버전 비교

  • 이 줄이 추가되었습니다.
  • 이 줄이 삭제되었습니다.
  • 서식이 변경되었습니다.

...


전공교육개발실습부서교육부서업무지원기타
P과학데이터교육 일 3.2시간 이상 수강p4 tutorial실습


D과학데이터교육 일 3.2시간 이상 수강ECN 실습, QoS 실습, Firewall 실습, Link monitoring 실습, 단순한 계산기 구현


C과학데이터교육 일 3.2시간 이상 수강P4 튜토리얼을 끝내고 P4 언어로 간단한 계산기를 구현했다. 


A




일일회고

23/02/20

  • Fact : ECN, QoS, Firewall을 실습해보고 코드를 이해했다. 
  • Feelings : 글로만 배웠던 것들을 코딩을 통해 구현해 보니 신기했다.
  • Finding : QoS가 무엇인지 처음 알게 되었고 Firewall에 블룸 필터의 개념을 확실히 알게 되었다.
  • Future Action Plan : Link Monitoring 실습, P4 튜토리얼 내용 총 정리
  • Feedbacks : 

...

  • Fact : Link monitoring실습을 하고 이해했다.
  • Feelings : P4 튜토리얼을 모두 완료해서 뿌듯했다.
  • Finding : P4라는 언어를 통해 SDN을 구성할 수 있다는 것을 어느정도 알 수 있었고 기존 네트워크 계층의 스위치, 라우터와 같은 하드웨어의 기능을 프로그래밍으로 구현할 수 있다는것을 알았다.
  • Future Action Plan : 간단한 P4 프로그램 제작
  • Feedbacks :

Memo

23/02/

...

22

  • Fact : P4로 단순한 계산기를 구현해보고 실행해봤지만 생각대로 동작하지 않았다. 성과취합 추가분을 완성했다.
  • Feelings : 보던거랑 실제로 생각해서 구현하는 거랑 난이도가 너무 달랐다.
  • Finding : 아직 P4에 대해 1%도 알지 못한다는 것을 알았다.
  • Future Action Plan : P4로 만든 단순한 계산기를 생각대로 동작되도록 수정
  • Feedbacks :

23/02/23

  • Fact : 어제 만든 P4 계산기를 설계대로 동작하도록 수정했다.
  • Feelings : 문서를 찾아보는게 가장 정확한 방법이라는 것을 다시 한번 느꼇다.
  • Finding : p4가 문제인줄 알고 p4코드만 계속 수정하다가 문서를 봤는데 생각보다 간단한 문제였다. 역시 모를때는 문서를 보는게 제일 정확하다
  • Future Action Plan : 
  • Feedbacks :

23/02/24

  • Fact : 문서 정리
  • Feelings : 네트워크의 기본도 모르던 처음에 비해 많은 것을 얻은 것 같은 느낌이다.
  • Finding : 생각보다 많은 것을 실습했고 많은 것을 얻은 것을 알 수 있었다.
  • Future Action Plan : 
  • Feedbacks :

Memo

23/02/20

...

출처

내용

배운 점 및 기억해야할 점

비고

과학데이터교육 인공지능충북대 현장실습 직무교육
문제 해결과 탐색 알고리즘, 탐색 트리와 탐색 알고리즘, 너비 우선 탐색과 균일 비용 탐색, 깊이 우선과 깊이 우선 반복 심화 탐색, 양방향 탐색, 주요 탐색 전략 비교

배운 것 및 기억해야할 것

P4 튜토리얼 Explicit Congestion Notification(ECN)의 이해

사전조건

Image Removed

s1과 s2의 링크의 대역폭은 512kbps로 제한되어 있다. 이상태에서 s1과 s2의 통로를 h11↔h22가 UDP 통신을 하고 h1↔h2가 패킷을 주고 받는 조건이다.

대역폭이 512kbps인 링크를 UDP통신으로 많이 소모하고 있기 때문에 h1↔2h의 패킷교환은 느리게 이루어질 수 밖에 없는데 이때의 h1가 h2에게 보낸 패킷의 헤더의 ECN 값이 동적으로 바뀌는 내용이다.

코드리뷰


23/02/22

출처

내용

배운 점 및 기억해야할 점

비고

과학데이터교육 인공지능충북대 현장실습 직무교육
휴리스틱 탐색법과 최적,최고 우선 탐색, 탐욕적 탐색과 A*탐색, 적대적 탐색, 게임트리의 탐색, 지식 표현과 추론

23/02/23

23/02/24

  • 참고사항
    • -

배운 것 및 기억해야할 것

P4 튜토리얼 Explicit Congestion Notification(ECN)의 이해

사전조건

Image Added

s1과 s2의 링크의 대역폭은 512kbps로 제한되어 있다. 이상태에서 s1과 s2의 통로를 h11↔h22가 UDP 통신을 하고 h1↔h2가 패킷을 주고 받는 조건이다.

대역폭이 512kbps인 링크를 UDP통신으로 많이 소모하고 있기 때문에 h1↔2h의 패킷교환은 느리게 이루어질 수 밖에 없는데 이때의 h1가 h2에게 보낸 패킷의 헤더의 ECN 값이 동적으로 바뀌는 내용이다.

코드리뷰

코드 블럭
/* -*- P4_16 -*- */
#include <core.p4>
#include <v1model.p4>

const bit<8>  TCP_PROTOCOL = 0x06;
const bit<16> TYPE_IPV4 = 0x800;
const bit<19> ECN_THRESHOLD = 10;

/*
코드 블럭
/* -*- P4_16 -*- */
#include <core.p4>
#include <v1model.p4>

const bit<8>  TCP_PROTOCOL = 0x06;
const bit<16> TYPE_IPV4 = 0x800;
const bit<19> ECN_THRESHOLD = 10;

/************************************************************************************************************
*********************** 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<6>    diffserv;
    bit<2>    ecn;

    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 {
}

struct headers {
    ethernet_t   ethernet;
    ipv4_t       ipv4;
}

... 동일


/*************************************************************************
****************  E G R E S S   P R O C E S S I N G   *******************
*************************************************************************/

control MyEgress(inout headers hdr,
                 inout metadata meta,
                 inout standard_metadata_t standard_metadata) {
    action mark_ecn() {
        hdr.ipv4.ecn = 3;
    }
    apply {
		//패킷의 ecn이 1이나 2일 때 즉, 이 패킷이 ecn필드를 사용할때
        if (hdr.ipv4.ecn == 1 || hdr.ipv4.ecn == 2){
			//패킷이 나갈때 스위치의 대기열의 깊이가 ecn가중치보다 깊다면 즉, 패킷이 정체되고 있다면
            if (standard_metadata.enq_qdepth >= ECN_THRESHOLD){
				//헤더의 ecn필드의 값을 3으로 바꿔서 네트워크가 혼잡하다는 것을 나타낸다.
                mark_ecn();
            }
        }
    }
}

... 동일

...

코드 블럭
/* -*- P4_16 -*- */
#include <core.p4>
#include <v1model.p4>

/* CONSTANTS */

const bit<16> TYPE_IPV4 = 0x800;
const bit<8>  TYPE_TCP  = 6;

//블룸 필터를 사용해서 방화벽을 구현한다.
#define BLOOM_FILTER_ENTRIES 4096
#define BLOOM_FILTER_BIT_WIDTH 1

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

header tcp_t{
    bit<16> srcPort;
    bit<16> dstPort;
    bit<32> seqNo;
    bit<32> ackNo;
    bit<4>  dataOffset;
    bit<4>  res;
    bit<1>  cwr;
    bit<1>  ece;
    bit<1>  urg;
    bit<1>  ack;
    bit<1>  psh;
    bit<1>  rst;
    bit<1>  syn;
    bit<1>  fin;
    bit<16> window;
    bit<16> checksum;
    bit<16> urgentPtr;
}

struct metadata {
    /* empty */
}

struct headers {
    ethernet_t   ethernet;
    ipv4_t       ipv4;
    tcp_t        tcp;
}

...기본적인 헤더 파서와 동일

/*************************************************************************
**************  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) {

    register<bit<BLOOM_FILTER_BIT_WIDTH>>(BLOOM_FILTER_ENTRIES) bloom_filter_1;
    register<bit<BLOOM_FILTER_BIT_WIDTH>>(BLOOM_FILTER_ENTRIES) bloom_filter_2;
    bit<32> reg_pos_one; bit<32> reg_pos_two;
    bit<1> reg_val_one; bit<1> reg_val_two;
    bit<1> direction;

    action drop() {
        mark_to_drop(standard_metadata);
    }

    action compute_hashes(ip4Addr_t ipAddr1, ip4Addr_t ipAddr2, bit<16> port1, bit<16> port2){
       //Get register position 헤더의 정보를 이용해서 레지스터를 할당받는다.
       hash(reg_pos_one, HashAlgorithm.crc16, (bit<32>)0, {ipAddr1,
                                                           ipAddr2,
                                                           port1,
                                                           port2,
                                                           hdr.ipv4.protocol},
                                                           (bit<32>)BLOOM_FILTER_ENTRIES);

       hash(reg_pos_two, HashAlgorithm.crc32, (bit<32>)0, {ipAddr1,
                                                           ipAddr2,
                                                           port1,
                                                           port2,
                                                           hdr.ipv4.protocol},
                                                           (bit<32>)BLOOM_FILTER_ENTRIES);
    }

    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 ipv4_lpm {
        key = {
            hdr.ipv4.dstAddr: lpm;
        }
        actions = {
            ipv4_forward;
            drop;
            NoAction;
        }
        size = 1024;
        default_action = drop();
    }

    action set_direction(bit<1> dir) {
        direction = dir;
    }

    table check_ports {
        key = {
            standard_metadata.ingress_port: exact;
            standard_metadata.egress_spec: exact;
        }
        actions = {
            set_direction;
            NoAction;
        }
        size = 1024;
        default_action = NoAction();
    }

    apply {
        if (hdr.ipv4.isValid()){
            ipv4_lpm.apply();
            if (hdr.tcp.isValid()){
                direction = 0; // default
				//포워딩 테이블에서 목적지와 출발지가 같은 내용을 발견했다면
                if (check_ports.apply().hit) {
                    // test and set the bloom filter
                    if (direction == 0) {
                        compute_hashes(hdr.ipv4.srcAddr, hdr.ipv4.dstAddr, hdr.tcp.srcPort, hdr.tcp.dstPort);
                    }
                    else {
                        compute_hashes(hdr.ipv4.dstAddr, hdr.ipv4.srcAddr, hdr.tcp.dstPort, hdr.tcp.srcPort);
                    }
                    // Packet comes from internal network
                    if (direction == 0){
                        // If there is a syn we update the bloom filter and add the entry
                        if (hdr.tcp.syn == 1){
                            bloom_filter_1.write(reg_pos_one, 1);
                            bloom_filter_2.write(reg_pos_two, 1);
                        }
                    }
                    // Packet comes from outside
                    else if (direction == 1){
                        // Read bloom filter cells to check if there are 1's
                        bloom_filter_1.read(reg_val_one, reg_pos_one);
                        bloom_filter_2.read(reg_val_two, reg_pos_two);
                        // only allow flow to pass if both entries are set
                        if (reg_val_one != 1 || reg_val_two != 1){
                            drop();
                        }
                    }
                }
            }
        }
    }
}

/*************************************************************************
****************  E G R E S S   P R O C E S S I N G   *******************
*************************************************************************/

control MyEgress(inout headers hdr,
                 inout metadata meta,
                 inout standard_metadata_t standard_metadata) {
    apply {  }
}

/*************************************************************************
*************   C H E C K S U M    C O M P U T A T I O N   **************
*************************************************************************/

control MyComputeChecksum(inout headers  hdr, inout metadata meta) {
     apply {
        update_checksum(
            hdr.ipv4.isValid(),
            { hdr.ipv4.version,
              hdr.ipv4.ihl,
              hdr.ipv4.diffserv,
              hdr.ipv4.totalLen,
              hdr.ipv4.identification,
              hdr.ipv4.flags,
              hdr.ipv4.fragOffset,
              hdr.ipv4.ttl,
              hdr.ipv4.protocol,
              hdr.ipv4.srcAddr,
              hdr.ipv4.dstAddr },
            hdr.ipv4.hdrChecksum,
            HashAlgorithm.csum16);
    }
}

/*************************************************************************
***********************  D E P A R S E R  *******************************
*************************************************************************/

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

*********/

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

/*************************************************************************
***********************  S W I T C H  *******************************
*************************************************************************/

V1Switch(
MyParser(),
MyVerifyChecksum(),
MyIngress(),
MyEgress(),
MyComputeChecksum(),
MyDeparser()
) main;

실행 결과

Image Added

h1과 h2는 통신이 되며 h1에서 h3로는 통신이 되지만 h3에서 h1로는 통신이 되지 않는다.

P4 튜토리얼 Link monitoring의 이해

사전조건

Image Added

probe 패킷이라는 경로 상의 스위치 정보 및 패킷 이동 경로에 대한 정보를 담는 패킷으로 모든 스위치를 순회해본다.

코드 리뷰

코드 블럭
/* -*- P4_16 -*- */
#include <core.p4>
#include <v1model.p4>

const bit<16> TYPE_IPV4  = 0x800;
const bit<16> TYPE_PROBE = 0x812;

#define MAX_HOPS 10
#define MAX_PORTS 8

/*************************************************************************
*********************** H E A D E R S  ***********************************
*************************************************************************/

typedef bit<9>  egressSpec_t;
typedef bit<48> macAddr_t;
typedef bit<32> ip4Addr_t;

typedef bit<48> time_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;
}

// Top-level probe header, indicates how many hops this probe
// packet has traversed so far.
header probe_t {
    bit<8> hop_cnt;
}

// The data added to the probe by each switch at each hop.
header probe_data_t {
    bit<1>    bos;
    bit<7>    swid;
    bit<8>    port;
    bit<32>   byte_cnt;
    time_t    last_time;
    time_t    cur_time;
}

// Indicates the egress port the switch should send this probe
// packet out of. There is one of these headers for each hop.
header probe_fwd_t {
    bit<8>   egress_spec;
}

struct parser_metadata_t {
    bit<8>  remaining;
}

struct metadata {
    bit<8> egress_spec;
    parser_metadata_t parser_metadata;
}

struct headers {
    ethernet_t              ethernet;
    ipv4_t                  ipv4;
    probe_t                 probe;
    probe_data_t[MAX_HOPS]  probe_data;
    probe_fwd_t[MAX_HOPS]   probe_fwd;
}

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

	// 이더넷 헤더에서 ipv4 패킷인지 프로브 패킷인지 구분해서 헤더를 추출한다.
    state parse_ethernet {
        packet.extract(hdr.ethernet);
        transition select(hdr.ethernet.etherType) {
            TYPE_IPV4: parse_ipv4;
            TYPE_PROBE: parse_probe;
            default: accept;
        }
    }

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

	//프로브 패킷이라면 probe 헤더에서 hop_cnt필드로 지나온 hop의 개수를 저장한다.
    state parse_probe {
        packet.extract(hdr.probe);
        meta.parser_metadata.remaining = hdr.probe.hop_cnt + 1;
        transition select(hdr.probe.hop_cnt) {
            0: parse_probe_fwd;
            default: parse_probe_data;
        }
    }
	//지나온 홉에 개수에 따라 probe_data 헤더를 추출한다. probe_data의 bos가 1 즉, 마지막 probe_data까지 추출했다면 probe_fwd를 추출한다.
    state parse_probe_data {
        packet.extract(hdr.probe_data.next);
        transition select(hdr.probe_data.last.bos) {
            1: parse_probe_fwd;
            default: parse_probe_data;
        }
    }
	//probe_fwd를 추출한다. probe_fwd는 이 패킷이 지나갈 경로를 나타내는 헤더이다. 현재 지나온 hop의 개수에 맞게 다음 경로(port)를 추출하는 것이다. 
    state parse_probe_fwd {
        packet.extract(hdr.probe_fwd.next);
        meta.parser_metadata.remaining = meta.parser_metadata.remaining - 1;
        // extract the forwarding data
        meta.egress_spec = hdr.probe_fwd.last.egress_spec;
        transition select(meta.parser_metadata.remaining) {
            0: accept;
            default: parse_probe_fwd;
        }
    }
}

/*************************************************************************
************   C H E C K S U M    V E R I F I C A T I O N   *************
*************************************************************************/

control MyVerifyChecksum(inout headers hdr, inout metadata meta) {
    apply {  }
}


/*************************************************************************
**************  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 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 ipv4_lpm {
        key = {
            hdr.ipv4.dstAddr: lpm;
        }
        actions = {
            ipv4_forward;
            drop;
            NoAction;
        }
        size = 1024;
        default_action = drop();
    }

    apply {
        if (hdr.ipv4.isValid()) {
            ipv4_lpm.apply();
        }
        else if (hdr.probe.isValid()) {
			//다음 경로(port)를 설정한다.
            standard_metadata.egress_spec = (bit<9>)meta.egress_spec;
			//지나온 hop의 개수를 증가시킨다.
            hdr.probe.hop_cnt = hdr.probe.hop_cnt + 1;
        }
    }
}

/*************************************************************************
****************  E G R E S S   P R O C E S S I N G   ********************
*************************************************************************/

control MyEgress(inout headers hdr,
                 inout metadata meta,
                 inout standard_metadata_t standard_metadata) {

    // count the number of bytes seen since the last probe
	//register는 각각의 스위치의 저장공간에 해당된다. 
    register<bit<32>>(MAX_PORTS) byte_cnt_reg;
    // remember the time of the last probe
    register<time_t>(MAX_PORTS) last_time_reg;

    action set_swid(bit<7> swid) {
        hdr.probe_data[0].swid = swid;
    }

    table swid {
        actions = {
            set_swid;
            NoAction;
        }
        default_action = NoAction();
    }

    apply {
        bit<32> byte_cnt;
        bit<32> new_byte_cnt;
        time_t last_time;
        time_t cur_time = standard_metadata.egress_global_timestamp;
        // increment byte cnt for this packet's port
        byte_cnt_reg.read(byte_cnt, (bit<32>)standard_metadata.egress_port);
        byte_cnt = byte_cnt + standard_metadata.packet_length;
        // reset the byte count when a probe packet passes through
        new_byte_cnt = (hdr.probe.isValid()) ? 0 : byte_cnt;
        byte_cnt_reg.write((bit<32>)standard_metadata.egress_port, new_byte_cnt);

        if (hdr.probe.isValid()) {
            // fill out probe fields
            hdr.probe_data.push_front(1);
            hdr.probe_data[0].setValid();
            if (hdr.probe.hop_cnt == 1) {
                hdr.probe_data[0].bos = 1;
            }
            else {
                hdr.probe_data[0].bos = 0;
            }
            // set switch ID field
            swid.apply();
            hdr.probe_data[0].port = (bit<8>)standard_metadata.egress_port;
            hdr.probe_data[0].byte_cnt = byte_cnt;
            // read / update the last_time_reg
            last_time_reg.read(last_time, (bit<32>)standard_metadata.egress_port);
            last_time_reg.write((bit<32>)standard_metadata.egress_port, cur_time);
            hdr.probe_data[0].last_time = last_time;
            hdr.probe_data[0].cur_time = cur_time;
        }
    }
}

/*************************************************************************
*************   C H E C K S U M    C O M P U T A T I O N   ***************
*************************************************************************/

control MyComputeChecksum(inout headers  hdr, inout metadata meta) {
     apply {
        update_checksum(
            hdr.ipv4.isValid(),
            { hdr.ipv4.version,
              hdr.ipv4.ihl,
              hdr.ipv4.diffserv,
              hdr.ipv4.totalLen,
              hdr.ipv4.identification,
              hdr.ipv4.flags,
              hdr.ipv4.fragOffset,
              hdr.ipv4.ttl,
              hdr.ipv4.protocol,
              hdr.ipv4.srcAddr,
              hdr.ipv4.dstAddr },
            hdr.ipv4.hdrChecksum,
            HashAlgorithm.csum16);
    }
}

/*************************************************************************
***********************  D E P A R S E R  *******************************
*************************************************************************/

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

/*************************************************************************
***********************  S W I T C H  *******************************
*************************************************************************/

V1Switch(
MyParser(),
MyVerifyChecksum(),
MyIngress(),
MyEgress(),
MyComputeChecksum(),
MyDeparser()
) main;

실행결과

코드 블럭
#!/usr/bin/env python3
import sys
import time

from probe_hdrs import *


def main():

    probe_pkt = Ether(dst='ff:ff:ff:ff:ff:ff', src=get_if_hwaddr('eth0')) / \ #패킷을 보내기전에 경로를 설정해줬다.
                Probe(hop_cnt=0) / \
                ProbeFwd(egress_spec=4) / \
                ProbeFwd(egress_spec=1) / \
                ProbeFwd(egress_spec=4) / \
                ProbeFwd(egress_spec=1) / \
                ProbeFwd(egress_spec=3) / \
                ProbeFwd(egress_spec=2) / \
                ProbeFwd(egress_spec=3) / \
                ProbeFwd(egress_spec=2) / \
                ProbeFwd(egress_spec=1)

    while True:
        try:
            sendp(probe_pkt, iface='eth0')
            time.sleep(1)
        except KeyboardInterrupt:
            sys.exit()

if __name__ == '__main__':
    main()

Image Added

위의 코드로 패킷의 경로를 설정한 후에 h1에서 실행을 하면 저 경로로 패킷이 갔다가 다시 h1로 돌아오는 것을 알 수 있다. 이때 h1에서 돌아온 패킷을 검사하는 ./receive.py를 실행하면 위 사진과 같은 결과를 볼 수 있다. 

첫 줄부터 보면 s1의 port1을 통해 패킷이 나온 것을 확인 할 수 있으며 마지막 프로브 패킷이 포트에서 전송된 이후 s1의 port1에서 전송된 바이트 수를 알 수 있다.

맨 밑줄이 프로브 패킷의 첫 시작이므로 윗 줄로 갈수록 각 포트에서 전송된 바이트가 점점 증가하는 것을 볼 수 있다.

P4로 만든 간단한 계산기

설계

Image Added

코드 블럭
header calc_t{
    bit<2> operator;
    bit<7> operand1;
    bit<7> operand2;
    bit<7> result;
    bit<1> padding;
}

사진과 같은 간단한 토폴로지를 이용해서 h1에서 h2로 연산자(0→ 더하기, 1→ 빼기, 2→ 곱하기)와 operand1,2를 헤더에 담아 보내면 s1에서 result에 해당하는 연산의 값을 저장하여 h2에 전달한다.

코드 구현

코드 블럭
/* -*- P4_16 -*- */
#include <core.p4>
#include <v1model.p4>

//새로운 헤더의 임시 타입넘버
const bit<16> TYPE_CALC = 0x1213;

/*************************************************************************
*********************** H SE WA ID TE CR HS  ***********************************
*************************************************************************/

V1Switch(
MyParser(),
MyVerifyChecksum(),
MyIngress(),
MyEgress(),
MyComputeChecksum(),
MyDeparser()
) main;

실행 결과

Image Removed

h1과 h2는 통신이 되며 h1에서 h3로는 통신이 되지만 h3에서 h1로는 통신이 되지 않는다.

P4 튜토리얼 Link monitoring의 이해

사전조건

Image Removed

probe 패킷이라는 경로 상의 스위치 정보 및 패킷 이동 경로에 대한 정보를 담는 패킷으로 모든 스위치를 순회해본다.

코드 리뷰

코드 블럭
/* -*- P4_16 -*- */
#include <core.p4>
#include <v1model.p4>

const bit<16> TYPE_IPV4  = 0x800;
const bit<16> TYPE_PROBE = 0x812;

#define MAX_HOPS 10
#define MAX_PORTS 8typedef 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 calc_t{
    bit<2> operator;
    bit<7> operand1;
    bit<7> operand2;
    bit<7> result;
    bit<1> padding;
}
struct metadata {
    /* empty */
}

struct headers {
    ethernet_t   ethernet;
    calc_t       calc; 
}

/*************************************************************************
*********************** HP EA AR DS E R S  ***********************************
*************************************************************************/

typedef bit<9>  egressSpec_t;
typedef bit<48> macAddr_t;
typedef bit<32> ip4Addr_t;

typedef bit<48> time_t;

header ethernet_t {
    macAddr_t dstAddr;
    macAddr_t srcAddr;
    bit<16>   etherType;
}

header ipv4_t {*****/

parser MyParser(packet_in packet,
    bit<4>    version;
    bit<4>    ihl;
 out   bit<8>    diffserv;headers hdr,
    bit<16>   totalLen;
    bit<16>   identification;
  inout  bit<3>    flags;metadata meta,
    bit<13>   fragOffset;
    bit<8>    ttl;
    bit<8>    protocol;
    bit<16>   hdrChecksum;
    ip4Addr_t srcAddr;
    ip4Addr_t dstAddr;
}

// Top-level probe header, indicates how many hops this probe
// packet has traversed so far.
header probe_tinout standard_metadata_t standard_metadata) {

    state start {
    bit<8> hop_cnt;
}

// The data added to the probe by each switch at each hop.
header probe_data_ttransition parse_ethernet;
    }

    state parse_ethernet {
    bit<1>    bospacket.extract(hdr.ethernet);
    bit<7>    swid;transition select(hdr.ethernet.etherType){
    bit<8>     port;
    bit<32>TYPE_CALC:parse_calc;
    byte_cnt;
    time_t    last_timedefault:accept;
    time_t    cur_time;
}

// Indicates the egress port the switch should send this probe
// packet out of. There is one of these headers for each hop.
header probe_fwd_t {
    bit<8>   egress_spec;
}

struct parser_metadata_t {
    bit<8>  remaining;
}

struct metadata {
    bit<8> egress_spec;
    parser_metadata_t parser_metadata;
}

struct headers {
    ethernet_t              ethernet;
    ipv4_t                  ipv4;
    probe_t                 probe;
    probe_data_t[MAX_HOPS]  probe_data;
    probe_fwd_t[MAX_HOPS]   probe_fwd;
}

/******** }

    state parse_calc{
        packet.extract(hdr.calc);
        transition accept;
    }

}

/*************************************************************************
************   C H E C K S U M    V E R I F I C A T I O N   *************
*************************************************************************/

control MyVerifyChecksum(inout headers hdr, inout metadata meta) {
    apply {  }
}


/*****************************************************************
********
***************  I PN AG R E S S   P R O C E R S S I N G   ***********************************
*************************************************************************/

parsercontrol MyParser(packet_in packetMyIngress(inout headers hdr,
                  outinout headersmetadata hdrmeta,
                  inout standard_metadata_t standard_metadata) meta,{
    action drop() {
          inout standard_metadata_t mark_to_drop(standard_metadata) {;

    state start {
  }
	
	//입력값에 맞는 연산을 하고 저장한다.
    action  transition parse_ethernet;
    }

	// 이더넷 헤더에서 ipv4 패킷인지 프로브 패킷인지 구분해서 헤더를 추출한다.calc_operand(macAddr_t dstAddr,egressSpec_t port, bit<2> operator) {
        if (operator==0){
    state parse_ethernet {
        packet.extract(hdr.ethernet)hdr.calc.result=hdr.calc.operand1+hdr.calc.operand2;
        transition select(hdr.ethernet.etherType) { }
         else   TYPE_IPV4: parse_ipv4;if (operator==1){
            TYPE_PROBE: parse_probe hdr.calc.result=hdr.calc.operand1-hdr.calc.operand2;
            default: accept;}
        }
 else   }

    state parse_ipv4 {
if (operator==2){
             packet.extract(hdr.ipv4)hdr.calc.result=hdr.calc.operand1*hdr.calc.operand2;
        transition accept;}
    }

	//프로브 패킷이라면 probe 헤더에서 hop_cnt필드로 지나온 hop의 개수를 저장한다.standard_metadata.egress_spec = port;
    state parse_probe {
  hdr.ethernet.srcAddr =     packet.extract(hdr.probe)hdr.ethernet.dstAddr;
        metahdr.parser_metadataethernet.remainingdstAddr = hdr.probe.hop_cnt + 1dstAddr;
    }

    transition select(hdr.probe.hop_cnt)table operator_exact {
        key    0: parse_probe_fwd;= {
            defaulthdr.calc.operator: parse_probe_dataexact;
        }
    }
	//지나온 홉에 개수에 따라 probe_data 헤더를 추출한다. probe_data의 bos가 1 즉, 마지막 probe_data까지 추출했다면 probe_fwd를 추출한다.
    state parse_probe_data {
    actions = {
            calc_operand;
            packet.extract(hdr.probe_data.next)drop;
          transition select(hdr.probe_data.last.bos) { NoAction;
        }
    1: parse_probe_fwd;
    size = 1024;
        default: parse_probe_data_action = drop();
    }

    apply }{
    }
	//probe_fwd를 추출한다. probe_fwd는 이 패킷이 지나갈 경로를 나타내는 헤더이다. 현재 지나온 hop의 개수에 맞게 다음 경로(port)를 추출하는 것이다. 
    state parse_probe_fwd {
        packet.extract(hdr.probe_fwd.next);
        meta.parser_metadata.remaining = meta.parser_metadata.remaining - 1;
        // extract the forwarding data    if (hdr.calc.isValid()) {
            operator_exact.apply();
        }
    }
}

/*************************************************************************
****************  E G R E S S   P R O C E S S I N G   *******************
*************************************************************************/

control MyEgress(inout headers hdr,
        meta.egress_spec = hdr.probe_fwd.last.egress_spec;
       inout transitionmetadata select(meta.parser_metadata.remaining) {meta,
            0: accept;
            default: parse_probe_fwd;
  inout standard_metadata_t standard_metadata) {
    apply { }
    }
}

/*************************************************************************
*************   C H E C K S U M    VC EO RM IP FU I CT A T I O N   **************
*************************************************************************/

control MyVerifyChecksumMyComputeChecksum(inout headers  hdr, inout metadata meta) {
     apply {  }
}


/*************************************************************************
***********************  I N G R D E SP SA   P R O C E S S IE NR G    *******************************
*************************************************************************/

control MyIngressMyDeparser(inout headers hdr,
                  inout metadata meta,
                  inout standard_metadata_t standard_metadata) {

    action drop() {
        mark_to_drop(standard_metadata);
    }

    action ipv4_forward(macAddr_t dstAddr, egressSpec_t port)packet_out packet, in headers hdr) {
    apply {
        standard_metadata.egress_spec = portpacket.emit(hdr.ethernet);
        hdr.ethernet.srcAddr = hdr.ethernet.dstAddrpacket.emit(hdr.calc);
        hdr.ethernet.dstAddr = dstAddr;
        hdr.ipv4.ttl = hdr.ipv4.ttl - 1;
    }

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

    apply {
        if (hdr.ipv4.isValid()) {
            ipv4_lpm.apply();
        }
        else if (hdr.probe.isValid()) {
			//다음 경로(port)를 설정한다.
            standard_metadata.egress_spec = (bit<9>)meta.egress_spec;
			//지나온 hop의 개수를 증가시킨다.
            hdr.probe.hop_cnt = hdr.probe.hop_cnt + 1;
        }
    }
}}
}

/*************************************************************************
***********************  S W I T C H  *******************************
*************************************************************************/

V1Switch(
MyParser(),
MyVerifyChecksum(),
MyIngress(),
MyEgress(),
MyComputeChecksum(),
MyDeparser()
) main;

1차 실행결과

h1에서 h2로 패킷을 보내도 h2에서 받은 패킷의 헤더를 파싱해보면 result에 초기값이 들어있다. 

패킷을 보내는 과정을 제대로 손봐야 할것 같다.

코드 수정

simple_calc.p4 
코드 블럭
titlesimple_calc.p4
/* -*- P4_16 -*- */
#include <core.p4>
#include <v1model.p4>

const bit<16> TYPE_CALC = 0x1213;

/*************************************************************************
*********************** 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 calc_t{//각 필드의 비트값을 1byte로 수정
    bit<8> operator;
    bit<8> operand1;
    bit<8> operand2;
    bit<8> result;
}
struct metadata {
    /* empty */
}

struct headers {
    ethernet_t   ethernet;
    calc_t       calc; 
}

...동일

/*************************************************************************
****************  I EN G R E S S   P R O C E S S I N G   ********************
*************************************************************************/

control MyEgressMyIngress(inout headers hdr,
                  inout metadata meta,
                  inout standard_metadata_t standard_metadata) {
    action drop() {


        mark_to_drop(standard_metadata);
    //}

 count the number of bytes seen since the last probe
	//register는 각각의 스위치의 저장공간에 해당된다. action calc_operand(macAddr_t dstAddr,egressSpec_t port, bit<8> operator) {//operator의 bit를 1byte로 수정
    register<bit<32>>(MAX_PORTS) byte_cnt_reg;
    // remember the time of the last probe
if (operator==0){
           register<time_t>(MAX_PORTS) last_time_reg;

 hdr.calc.result=hdr.calc.operand1+hdr.calc.operand2;
     action set_swid(bit<7> swid) {}
        hdr.probe_data[0].swid = swid;else if (operator==1){
    }

    table swid {
  hdr.calc.result=hdr.calc.operand1-hdr.calc.operand2;
      actions = {}
        else    set_swid;if (operator==2){
            NoActionhdr.calc.result=hdr.calc.operand1*hdr.calc.operand2;
        }
        default_actionelse =if NoAction(operator==3);{
    }

       apply { hdr.calc.result=hdr.calc.operand1+hdr.calc.operand2;
        bit<32> byte_cnt;}
        bit<32> new_byte_cntstandard_metadata.egress_spec = port;
        time_t last_timehdr.ethernet.srcAddr = hdr.ethernet.dstAddr;
        time_t cur_timehdr.ethernet.dstAddr = standard_metadata.egress_global_timestampdstAddr;
        // increment byte cnt for this packet's port
        byte_cnt_reg.read(byte_cnt, (bit<32>)standard_metadata.egress_port);
        byte_cnt = byte_cnt + standard_metadata.packet_length;}

 ...동일
send.py

h1에서 h2로 패킷을 보낼 때 사용하는 파이썬 스크립트

코드 블럭
titlesend.py
#!/usr/bin/env python3
import argparse
import random
import socket

from Calc_header import Calc
from scapy.all import *

TYPE_CALC = 0x1213

def get_if():
    ifs=get_if_list()
    iface=None # "h1-eth0"
    for i in get_if_list():
        // reset the byte count when a probe packet passes through
if "eth0" in i:
               new_byte_cnt = (hdr.probe.isValid()) ? 0 : byte_cnt;
iface=i
             byte_cnt_reg.write((bit<32>)standard_metadata.egress_port, new_byte_cnt)break;

    if not iface:
  if (hdr.probe.isValid()) {
    print("Cannot find  eth0 interface")
    // fill out probe fieldsexit(1)
    return iface

def main():
    parser = hdr.probe_data.push_front(1);argparse.ArgumentParser()
    parser.add_argument('--operator', type=int, help="0:+, 1:-, 2:*, 3:/")
   hdr parser.probe_data[0].setValid();add_argument('--oper1', type=int, help="operand1")
    parser.add_argument('--oper2', type=int, help='operand2')
      if (hdr.probe.hop_cnt == 1) {args = parser.parse_args()
    operator = args.operator
    oper1 = args.oper1
    hdr.probe_data[0].bos = 1;
    oper2=args.oper2
    iface = get_if()

    pkt =   }Ether(src=get_if_hwaddr(iface), dst='ff:ff:ff:ff:ff:ff')
    pkt = pkt / Calc(operator=operator,operand1=oper1,operand2=oper2)

    pkt.show2()
 else {
  sendp(pkt, iface=iface, verbose=False)


if __name__ == '__main__':
    main()
receive.py

h2에서 h1의 패킷을 받아서 출력하는 스크립트 파일

코드 블럭
#!/usr/bin/env python3
import os
import   hdr.probe_data[0].bos = 0;sys

from scapy.all import (
    TCP,
    FieldLenField,
    }FieldListField,
    IntField,
    IPOption,
    //ShortField,
 set switch ID field get_if_list,
    sniff
)
from scapy.layers.inet import _IPOption_HDR
from Calc_header import   swid.applyCalc


def get_if();:
            hdr.probe_data[0].port = (bit<8>)standard_metadata.egress_port;ifs=get_if_list()
    iface=None
    for i in  hdr.probe_data[0].byte_cnt = byte_cnt;
get_if_list():
        if "eth0" in i:
    //    read / update the last_time_reg iface=i
            last_time_reg.read(last_time, (bit<32>)standard_metadata.egress_port)break;
    if not iface:
        last_time_reg.write((bit<32>)standard_metadata.egress_port, cur_time);
print("Cannot find eth0 interface")
        exit(1)
    return  hdr.probe_data[0].last_time = last_time;
            hdr.probe_data[0].cur_time = cur_time;
        }
    }
}

/*************************************************************************
*************   C H E C K S U M    C O M P U T A T I O N   ***************
*************************************************************************/

control MyComputeChecksum(inout headers  hdr, iface

def handle_pkt(pkt):
    print("got a packet")
    pkt.show2()

def main():
    ifaces = [i for i in os.listdir('/sys/class/net/') if 'eth' in i]
    iface = ifaces[0]
    print("sniffing on %s" % iface)
    sys.stdout.flush()
    sniff(iface = iface,
          prn = lambda x: handle_pkt(x))

if __name__ == '__main__':
    main()

calc_header.py

simplecalc.p4에서 구현한대로 만들 헤더, 이를 사용해서 send.py에서는 임의로 만든 calc헤더가 담긴 패킷을 보낼 수 있으며 receive.py에서는 받은 패킷을 clac헤더의 필드에 맞게 파싱하여 출력할수 있다.

코드 블럭
titleCalc_header.py
from scapy.all import *

TYPE_MYTUNNEL = 0x1213

class Calc(Packet):
    name = "Calc"
    fields_desc = [
        ByteField("operator", 0),
        ByteField("operand1", 0),inout metadata meta) {
     apply {
        update_checksum(
ByteField("operand2", 0),
        ByteField("result", 0)
    ]

bind_layers(Ether,  hdrCalc, type=TYPE_MYTUNNEL)
topology.json

설계한 토폴로지

코드 블럭
titletopology.json
{.ipv4.isValid(),
            { hdr.ipv4.version,"hosts": {
        "h1":      hdr.ipv4.ihl,
  {"ip": "10.0.1.1/24", "mac": "08:00:00:00:01:11",
            hdr.ipv4.diffserv,
   "commands":["route add default gw 10.0.1.10 dev  eth0",
    hdr.ipv4.totalLen,
              hdr.ipv4.identification,
         "arp -i eth0 -s  hdr.ipv4.flags10.0.1.10 08:00:00:00:01:00"]},
        "h2":      hdr.ipv4.fragOffset,
  {"ip": "10.0.2.2/24", "mac": "08:00:00:00:02:22",
            hdr.ipv4.ttl,
   "commands":["route add default gw 10.0.1.10 dev eth0",
     hdr.ipv4.protocol,
              hdr.ipv4.srcAddr,
        "arp -i eth0    hdr.ipv4.dstAddr },-s 10.0.1.10 08:00:00:00:01:00"]}
    },
        hdr.ipv4.hdrChecksum,"switches": {
        "s1": { "runtime_json"  HashAlgorithm.csum16);: "pod-topo/s1-runtime.json" }
    },
    "links": [
        ["h1", "s1-p1"], ["h2","s1-p2"]
    ]
}

s1-runtime.json

s1의 포워딩 테이블 정적 정의

코드 블럭
titles1-runtime.json
{
  "target": "bmv2",
  "p4info": "build/basic.p4.p4info.txt",
  "bmv2_json": "build/basic.json",
  "table_entries": [
    {
}

/*************************************************************************
***********************  D E P A R S E R  *******************************
*************************************************************************/

control MyDeparser(packet_out packet, in headers hdr) {
    apply {
        packet.emit(hdr.ethernet);
        packet.emit(hdr.ipv4);"table": "MyIngress.operator_exact",
        packet.emit(hdr.probe);"default_action": true,
        packet.emit(hdr.probe_data);"action_name": "MyIngress.drop",
      "action_params":  packet.emit(hdr.probe_fwd);{ }
    }
}

/*************************************************************************
***********************  S W I T C H  *******************************
*************************************************************************/

V1Switch(
MyParser(),
MyVerifyChecksum(),
MyIngress(),
MyEgress(),
MyComputeChecksum(),
MyDeparser()
) main;

실행결과

코드 블럭
#!/usr/bin/env python3
import sys
import time

from probe_hdrs import *


def main():

    probe_pkt = Ether(dst='ff:ff:ff:ff:ff:ff', src=get_if_hwaddr('eth0')) / \ #패킷을 보내기전에 경로를 설정해줬다.
                Probe(hop_cnt=0) / \
       ,
    {
      "table": "MyIngress.operator_exact",
      "match": {
        "hdr.calc.operator": 0
      },
      "action_name": "MyIngress.calc_operand",
      "action_params": {
        "dstAddr": "08:00:00:00:02:22",
        "port": 2,
        "operator":0
      }
    },
    {
      "table": "MyIngress.operator_exact",
      "match": {
        "hdr.calc.operator": 1
      },
      "action_name": "MyIngress.calc_operand",
      "action_params": {
         ProbeFwd(egress_spec=4) / \
"dstAddr": "08:00:00:00:02:22",
        "port": 2,
         ProbeFwd(egress_spec=1) / \
"operator":1
      }
    },
    {
     ProbeFwd(egress_spec=4) / \ "table": "MyIngress.operator_exact",
      "match": {
         ProbeFwd(egress_spec=1) / \
"hdr.calc.operator": 2
      },
        "action_name": "MyIngress.calc_operand",
    ProbeFwd(egress_spec=3) / \  "action_params": {
        "dstAddr": "08:00:00:00:02:22",
       ProbeFwd(egress_spec=2) / \ "port": 2,
        "operator":2
      }
  ProbeFwd(egress_spec=3) / \},
    {
      "table": "MyIngress.operator_exact",
     ProbeFwd(egress_spec=2) / \ "match": {
        "hdr.calc.operator": 3
       ProbeFwd(egress_spec=1)

},
     while True:
  "action_name": "MyIngress.calc_operand",
      try:"action_params": {
            sendp(probe_pkt, iface='eth0')"dstAddr": "08:00:00:00:02:22",
            time.sleep(1)"port": 2,
        except KeyboardInterrupt:"operator":3
      }
    }
  sys.exit()]

if __name__ == '__main__':
    main()

Image Removed

}

2차 결과

Image AddedImage Added

h1에서 h2로 clac헤더의 operator필드에 1, operand1필드에 10 operand2필드에 6을 넣어서 패킷을 보냈을 때 h2에서 받은 패킷의 calc헤더의 result 필드가 16으로 바뀐것을 볼수있다.

operator를 1로 했을 때는 뺴기연산, operator를 2로 했을 때는 곱하기 연산의 결과가 result필드에 들어간것을 위의 코드로 패킷의 경로를 설정한 후에 h1에서 실행을 하면 저 경로로 패킷이 갔다가 다시 h1로 돌아오는 것을 알 수 있다. 이때 h1에서 돌아온 패킷을 검사하는 ./receive.py를 실행하면 위 사진과 같은 결과를 볼 수 있다. 

첫 줄부터 보면 s1의 port1을 통해 패킷이 나온 것을 확인 할 수 있으며 마지막 프로브 패킷이 포트에서 전송된 이후 s1의 port1에서 전송된 바이트 수를 알 수 있다.

수정 내용

p4에서 정의한 헤더와 보내는 패킷이 다른 것이 문제였다. p4의 파서는 패킷의 헤더를 처음부터 파싱하며 정의한 헤더에 맞게 파싱하기 때문에 헤더 정의를 잘못 했거나 정의에 맞지 않는 패킷을 보내면 p4에서는 패킷의 헤더를 잘못 파싱 할 수밖에 없다

그렇기 때문에 지금까지 bos같은 스택의 마지막인지를 확인하는 필드를 넣거나 몇개의 헤더가 추가적으로 있는지에 대한 필드를 넣거나해서 개수에 맞게 파싱을 한것을 알 수 있었다. 맨 밑줄이 프로브 패킷의 첫 시작이므로 윗 줄로 갈수록 각 포트에서 전송된 바이트가 점점 증가하는 것을 볼 수 있다.