- Today
- Total
Byeo
1. P4 - Basic 본문
이번에는 P4를 사용해서 ping을 성공시키는 작업을 진행해봅니다.
https://github.com/p4lang/tutorials/tree/master/exercises/basic의 step 2와 같습니다.
basic.p4의 todo를 채우러 가봅시다!
참고문서: https://p4.org/p4-spec/docs/P4-16-v1.2.0.html#sec-packet-parsing
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)
여기서 요구하는 내용은 다음과 같습니다.
- Sets the egress port for the next hop.
- Updates the ethernet destination address with the address of the next hop.
- Updates the ethernet source address with the address of the switch.
- 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을 조금 수정해야 합니다..
- Defines a table that will read an IPv4 destination address, and invoke either drop or ipv4_forward.
- 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/lecture06.html
'프로그래밍 (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 |