이 페이지의 이전 버전을 보고 있습니다. 현재 버전 보기.

현재와 비교 페이지 이력 보기

« 이전 버전 13 다음 »

PDCA


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


D
ECN 실습, QoS 실습, Firewall 실습, Link monitoring 실습, 단순한 계산기 구현


C




A




일일회고

23/02/20

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

23/02/21

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

23/02/22

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

23/02/23

  • Fact : 
  • Feelings : 
  • Finding : 
  • Future Action Plan : 
  • Feedbacks :

Memo

23/02/20

출처

내용

배운 점 및 기억해야할 점

비고

과학데이터교육 인공지능충북대 현장실습 직무교육
다양한 에이전트 아키텍처, 상태공간과 탐색문제1,2, 다양한 탐색 문제와 탐색 전략

23/02/21

출처

내용

배운 점 및 기억해야할 점

비고

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

23/02/22

출처

내용

배운 점 및 기억해야할 점

비고

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

23/02/23

출처

내용

배운 점 및 기억해야할 점

비고

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

배운 것 및 기억해야할 것

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

사전조건

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;

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

... 동일

실행결과

h2의 tos(diffserv+ecn)필드의 값이다. 중간에 h11→h22로의 UDP통신이 시작된 시점부터 0x3이 된 것을 볼 수 있는데 이때부터 네트워크가 혼잡하다는 것을 알 수 있고 UDP 통신이 끝난 후에 0x1로 바껴서 혼잡이 풀렸다는 것을 알 수 있다.

P4 튜토리얼 Quality of Service(QoS)의 이해

사전조건

h1과 h2 호스트에서 TCP패킷과 UDP패킷을 보낸다. 이때 헤더의 tos필드에는 TCP와 UDP의 각기 다른 우선순위에 따른 값이 부여된다.

코드리뷰

/* -*- P4_16 -*- */
#include <core.p4>
#include <v1model.p4>

const bit<16> TYPE_IPV4 = 0x800;

/* IP protocols */ ipv4 헤더의 poroto 필드에 들어가는 다음 계층 프로토콜의 값
const bit<8> IP_PROTOCOLS_ICMP       =   1;
const bit<8> IP_PROTOCOLS_IGMP       =   2;
const bit<8> IP_PROTOCOLS_IPV4       =   4;
const bit<8> IP_PROTOCOLS_TCP        =   6;
const bit<8> IP_PROTOCOLS_UDP        =  17;
const bit<8> IP_PROTOCOLS_IPV6       =  41;
const bit<8> IP_PROTOCOLS_GRE        =  47;
const bit<8> IP_PROTOCOLS_IPSEC_ESP  =  50;
const bit<8> IP_PROTOCOLS_IPSEC_AH   =  51;
const bit<8> IP_PROTOCOLS_ICMPV6     =  58;
const bit<8> IP_PROTOCOLS_EIGRP      =  88;
const bit<8> IP_PROTOCOLS_OSPF       =  89;
const bit<8> IP_PROTOCOLS_PIM        = 103;
const bit<8> IP_PROTOCOLS_VRRP       = 112;


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

...동일

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

    /* 기본적인 우선순위 */
    action default_forwarding() {
        hdr.ipv4.diffserv = 0;
    }

    /* Expedited Forwarding */ //UDP 우선순위
    action expedited_forwarding() {
        hdr.ipv4.diffserv = 46;
    }

    /* Voice Admit */ //TCP 우선순위
    action voice_admit() {
        hdr.ipv4.diffserv = 44;
    }

    /* Assured Forwarding */
    /* Class 1 Low drop probability */
    action af_11() {
        hdr.ipv4.diffserv = 10;
    }

    /* Class 1 Med drop probability */
    action af_12() {
        hdr.ipv4.diffserv = 12;
    }

    /* Class 1 High drop probability */
    action af_13() {
        hdr.ipv4.diffserv = 14;
    }

    /* Class 2 Low drop probability */
    action af_21() {
        hdr.ipv4.diffserv = 18;
    }

    /* Class 2 Med drop probability */
    action af_22() {
        hdr.ipv4.diffserv = 20;
    }

    /* Class 2 High drop probability */
    action af_23() {
        hdr.ipv4.diffserv = 22;
    }

    /* Class 3 Low drop probability */
    action af_31() {
        hdr.ipv4.diffserv = 26;
    }

    /* Class 3 Med drop probability */
    action af_32() {
        hdr.ipv4.diffserv = 28;
    }

    /* Class 3 High drop probability */
    action af_33() {
        hdr.ipv4.diffserv = 30;
    }

    /* Class 4 Low drop probability */
    action af_41() {
        hdr.ipv4.diffserv = 34;
    }

    /* Class 4 Med drop probability */
    action af_42() {
        hdr.ipv4.diffserv = 36;
    }

    /* Class 4 High drop probability */
    action af_43() {
        hdr.ipv4.diffserv = 38;
    }

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

    apply {
        if (hdr.ipv4.isValid()) {//이 튜토리얼에서는 UDP와 TCP 패킷만 보내기 때문에 둘로만 나누어서 패킷의 우선순위를 정한다.
            if (hdr.ipv4.protocol == IP_PROTOCOLS_UDP) {
                expedited_forwarding();
            }
            else if (hdr.ipv4.protocol == IP_PROTOCOLS_TCP) {
                voice_admit();
            }
            ipv4_lpm.apply();
        }
    }
}

...동일

실행결과

     tos       = 0xb9
     tos       = 0xb9
     tos       = 0xb9
     tos       = 0xb9
     tos       = 0xb9
     tos       = 0xb9
     tos       = 0xb9
     tos       = 0xb9
     tos       = 0xb1
     tos       = 0xb1
     tos       = 0xb1
     tos       = 0xb1
     tos       = 0xb1
     tos       = 0xb1
     tos       = 0xb1
     tos       = 0xb1

위와 같이 h1에서 h2로 패킷을 보낼때 UDP로 보내면 tos의 값이 0xb9로 되는 것을 알 수 있고 TCP로 보내면 0xb1이 되는 것을 알 수 있다. 이 튜토리얼에서는 패킷의 헤더에 우선순위를 부여하는 것만 해보았는데 실제로는 각 우선순위에 맞는 대기열들을 따로 만들고 우선순위에 따른 작업을 진행함으로써 서비스 품질을 보장하게 된다.

P4 튜토리얼 firewall의 이해

사전조건

s1에는 firewall가 설치되어 있다. h1↔h2는 패킷이동이 자유롭고 h1과 h2에서 h3과 h4로의 패킷이동도 자유롭지만 h3이나 h4에서 h1과 h2로의 패킷이동은 자유롭지 못하도록 하는 방화벽구현이 목표이다.

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

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

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

실행 결과

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

P4 튜토리얼 Link monitoring의 이해

사전조건

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()

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

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

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

P4로 만든 간단한 계산기

설계

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

/*************************************************************************
*********************** 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_CALC:parse_calc;
            default:accept;
        }
    }

    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 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 calc_operand(macAddr_t dstAddr,egressSpec_t port, bit<2> operator) {
        if (operator==0){
             hdr.calc.result=hdr.calc.operand1+hdr.calc.operand2;
         }
         else if (operator==1){
             hdr.calc.result=hdr.calc.operand1-hdr.calc.operand2;
         }
         else if (operator==2){
             hdr.calc.result=hdr.calc.operand1*hdr.calc.operand2;
         }
        standard_metadata.egress_spec = port;
        hdr.ethernet.srcAddr = hdr.ethernet.dstAddr;
        hdr.ethernet.dstAddr = dstAddr;
    }

    table operator_exact {
        key = {
            hdr.calc.operator: exact;
        }
        actions = {
            calc_operand;
            drop;
            NoAction;
        }
        size = 1024;
        default_action = drop();
    }

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

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

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

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

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

1차 실행결과

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

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

코드 수정

simple_calc.p4 
simple_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 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 calc_operand(macAddr_t dstAddr,egressSpec_t port, bit<8> operator) {//operator의 bit를 1byte로 수정
        if (operator==0){
            hdr.calc.result=hdr.calc.operand1+hdr.calc.operand2;
        }
        else if (operator==1){
            hdr.calc.result=hdr.calc.operand1-hdr.calc.operand2;
        }
        else if (operator==2){
            hdr.calc.result=hdr.calc.operand1*hdr.calc.operand2;
        }
        else if (operator==3){
            hdr.calc.result=hdr.calc.operand1+hdr.calc.operand2;
        }
        standard_metadata.egress_spec = port;
        hdr.ethernet.srcAddr = hdr.ethernet.dstAddr;
        hdr.ethernet.dstAddr = dstAddr;
    }

 ...동일
send.py

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

send.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():
        if "eth0" in i:
            iface=i
            break;
    if not iface:
        print("Cannot find eth0 interface")
        exit(1)
    return iface

def main():
    parser = argparse.ArgumentParser()
    parser.add_argument('--operator', type=int, help="0:+, 1:-, 2:*, 3:/")
    parser.add_argument('--oper1', type=int, help="operand1")
    parser.add_argument('--oper2', type=int, help='operand2')
    args = parser.parse_args()
    operator = args.operator
    oper1 = args.oper1
    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()
    sendp(pkt, iface=iface, verbose=False)


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

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

#!/usr/bin/env python3
import os
import sys

from scapy.all import (
    TCP,
    FieldLenField,
    FieldListField,
    IntField,
    IPOption,
    ShortField,
    get_if_list,
    sniff
)
from scapy.layers.inet import _IPOption_HDR
from Calc_header import Calc


def get_if():
    ifs=get_if_list()
    iface=None
    for i in get_if_list():
        if "eth0" in i:
            iface=i
            break;
    if not iface:
        print("Cannot find eth0 interface")
        exit(1)
    return 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헤더의 필드에 맞게 파싱하여 출력할수 있다.

Calc_header.py
from scapy.all import *

TYPE_MYTUNNEL = 0x1213

class Calc(Packet):
    name = "Calc"
    fields_desc = [
        ByteField("operator", 0),
        ByteField("operand1", 0),
        ByteField("operand2", 0),
        ByteField("result", 0)
    ]

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

설계한 토폴로지

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.1.10 dev eth0",
                           "arp -i eth0 -s 10.0.1.10 08:00:00:00:01:00"]}
    },
    "switches": {
        "s1": { "runtime_json" : "pod-topo/s1-runtime.json" }
    },
    "links": [
        ["h1", "s1-p1"], ["h2","s1-p2"]
    ]
}

s1-runtime.json

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

s1-runtime.json
{
  "target": "bmv2",
  "p4info": "build/basic.p4.p4info.txt",
  "bmv2_json": "build/basic.json",
  "table_entries": [
    {
      "table": "MyIngress.operator_exact",
      "default_action": true,
      "action_name": "MyIngress.drop",
      "action_params": { }
    },
    {
      "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": {
        "dstAddr": "08:00:00:00:02:22",
        "port": 2,
        "operator":1
      }
    },
    {
      "table": "MyIngress.operator_exact",
      "match": {
        "hdr.calc.operator": 2
      },
      "action_name": "MyIngress.calc_operand",
      "action_params": {
        "dstAddr": "08:00:00:00:02:22",
        "port": 2,
        "operator":2
      }
    },
    {
      "table": "MyIngress.operator_exact",
      "match": {
        "hdr.calc.operator": 3
      },
      "action_name": "MyIngress.calc_operand",
      "action_params": {
        "dstAddr": "08:00:00:00:02:22",
        "port": 2,
        "operator":3
      }
    }
  ]
}

2차 결과

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

operator를 1로 했을 때는 뺴기연산, operator를 2로 했을 때는 곱하기 연산의 결과가 result필드에 들어간것을 볼 수 있다. 

  • 레이블 없음