eBPF(Extended Berkeley Packet Filter)란?
XDP를 설명하기 전에 eBPF를 설명한다.
먼저, eBPF를 잘 모르더라도 XDP를 다루는 것에는 어려움은 없다. (필자의 경험담ㅎㅎ)
왜냐하면 BPF나 XDP 공개 레포지토리에 XDP 사용 방법에 대한 튜토리얼이 매우 잘 정리되어 있기 때문이다.
그래도 XDP는 BPF 위에서 동작함으로 한번 알아보도록 하자.
XDP는 eBPF라는 Linux Kernel 내에 존재하는 가상 환경(?), 샌드 박스(?) 환경에서 동작한다.
eBPF는 다음과 같은 특징을 갖는다고 한다.
- 리눅스 커널에서 발생하는 여러 이벤트들에 대해 사용자 정의 함수를 통해 샌드박스 환경에서 동작시킴
- 커널 코드를 수정하거나 모듈을 추가할 필요 없이 프로그램을 운영체제의 커널 공간에서 실행시킬 수 있음
필자도 eBPF에 대해서 정확히는 잘 모르겠지만 XDP는 eBPF에 정의된 Hook을 통해 패킷을 제어할 수 있도록 지원해주는 것으로 알고 있다. 그리고 이러한 eBPF Hook을 통해 프로세싱 되는 패킷은 기존 네트워크 스택을 거치는 환경보다 빠른 속도로 수행될 수 있다.
필자는 eBPF가 올려진 리눅스 커널 위에서 XDP만 컴파일하고 다루고 있어서 BPF 쪽은 큰 관심이 없다. 이것을 알면 XDP를 다루는데 더욱이 도움이 되겠지만, 아직은 XDP에 익숙해지는 것이 더 중요한 단계임으로 BPF는 나중에 공부하려고 한다.
eBPF는 v4.16 커널 버전 이상부터 지원한다.
그리고 앞에서도 설명하였지만, eBPF를 제대로 몰라도 BPF 및 XDP 라이브러리가 Github의 공개 레포지토리를 통해 잘 제공되어 있어서 XDP를 다루는 것에는 큰 불편함은 없다.
eBPF가 무엇인지 대충은 알게되었고 또, XDP를 다루는데 eBPF를 너무 빠삭히 알 필요는 없는 것을 알게되었음으로, 다음 섹션에서 부터는 XDP에 대해서 설명을 진행한다.
XDP(eXpress Data Path)란?
XDP는 eXpress Data Path의 약자로 직역하면, 고속의 데이터 경로이다.
이러한 고속의 데이터 경로를 달성하기 위해 XDP는 네트워크 스택을 우회하는 방법을 사용한다.
그리고 네트워크 스택을 우회하는 방법을 사용하기 위해서 XDP는 eBPF Hook을 이용한다.
또한, XDP는 패킷 처리의 속도를 높이기 위해 최소한의 Copy로 동작한다.
기존 네트워크 스택을 경유하는 패킷들은 각 Layer를 거칠때 마다 패킷을 Encapsulation/Decapsulation한다.
그리고 이 과정에서 Copy가 발생하는데, 이것은 네트워크 패킷 프로세싱에 오버헤드를 발생시켜 결론적으로 패킷 프로세싱에 성능 저하가 일어난다.
XDP는 이러한 불필요한 Copy 과정을 최대한 제거하여 기존 네트워크 스택을 거치는 것보다 더 빠른 속도로 패킷을 프로세싱할 수 있도록 지원해준다.
위 그림은 구글에 Netfilter-Packet-Flow를 검색하면 나오는 그림이다.
다음 그림을 보면 알 수 있듯이 XDP는 수신되는 패킷을 Network Driver의 alloc_skb()가 수행되기 전에 프로세싱한다.
Raw Socket을 통해 커널을 우회시키는 AF_PACKET 또한 alloc_skb() 이후에 프로세싱되는 것을 확인할 수 있다.
alloc_skb()는 sk_buff 구조체로 패킷을 규격화시키는 동작을 한다.
sk_buff 구조체는 리눅스 네트워크 스택 내부에서 패킷을 프로세싱하기 위해 사용하는 구조체이다.
본 페이지에서는 sk_buff 구조체에 대해서는 다루진 않는다.
다시 본론으로 돌아와 XDP는 alloc_skb()가 수행되기 전에 일어나는데 이것은 다시 말해, 리눅스 네트워크 스택 자체를 아예 경유하지 않을 수 있다는 것을 의미한다.
XDP는 sk_buff 구조체가 아닌 xdp_buff 구조체를 사용하여 패킷을 프로세싱한다.
즉, XDP를 사용하면 NIC(네트워크 카드)로 수신되는 패킷을 아무런 프로세싱을 하지 않고 곧바로 유저 영역으로 끌어올릴 수 있다는 뜻이다. 그것도 매우 고속으로!
그런데, 고속의 패킷 프로세싱이라는 장점이 존재하는 것처럼 반대로 단점도 존재한다.
XDP는 NIC로 수신되는 L2 패킷(Frame)을 네트워크 스택을 경유시키지 않고 곧바로 유저 영역으로 올린다고 설명하였는데 이것은 반대로 네트워크 스택(Layer 2, 3, 4)에서 처리되어야 하는 패킷 프로세싱을 유저 영역에서 모두 처리해주어야 한다는 점이다.
즉, XDP는 고속의 패킷 프로세싱을 위해 사용하지만 그만큼 불편함(기존에 제공해주는 Layer별 서비스를 유저 영역에서 제공해야 함)도 감수해야 한다는 것이다.
Data Plane에서 사용하기에 매우 적절하다.
또한, XDP를 완전히 고속으로 사용하기 위해서는 NIC가 XDP의 Native 모드를 지원해야 한다.
시중에 판매하는 NIC들이 모두 XDP Native 모드를 지원하진 않으며, 특정한 NIC만이 지원을 해준다.
이것과 관련된 설명은 아래 섹션에서 설명한다.
XDP Native/Generic 모드
XDP에는 아래 두가지 모드가 존재한다.
- Native(=DRV) : 최소한의 Copy로 패킷을 고속으로 처리하는 XDP 모드, 특정 NIC에서만 지원되는 모드
- Generic(=SKB) : XDP가 잘 동작하는지 테스트로 사용하는 모드이며, 모든 NIC에서 지원하는 모드
XDP는 Native와 Generic 두가지 모드 중 Native 모드로 동작시켜야 고속으로 패킷을 처리할 수 있다.
Generic 모드는 단순히 XDP를 테스트하거나 학습하기 위해 활용하는 목적으로 사용된다.
Generic 모드는 기존 네트워크 스택을 경유하는 것보다 더 느리게 동작한다. 해당 모드는 단순히 테스트 목적으로 사용할 수 있다.
XDP Native 모드를 지원하는 Network Driver들은 아래와 같다.
- mlx4
- mlx5
- i40e
- ixgbe
- igb
- ixgbevf
- nfp
- bnxt
- thunder
- dpaa2
- qede
- tun
- veth
- virtio_net
- netsec
위 Network Driver들이 XDP Native 모드를 지원해준다.
주로 Intel 사에서 제공하는 NIC(Network Driver 이름 앞에 i가 들어가는)들이 Native 모드를 지원하며, 또한 리눅스 가상화 인터페이스(veth)에서 XDP Native 모드를 지원해주고 있다.
즉, 위 Network Driver를 지원하는 NIC를 사용해야지만 온전히 XDP의 고속 패킷 프로세싱이 가능하다는 것이다.
본 섹션에서는 XDP를 고속 패킷 프로세싱하기 위해서는 어떠한 Network Driver를 지원하는 NIC를 사용해야하는지 알아보았다.
앞서 두번째 섹션(XDP란?)에서 XDP가 패킷을 곧바로 유저 영역으로 올려 고속의 패킷 프로세싱이 가능하다는 것을 설명했었는데, XDP는 수신된 패킷을 곧바로 유저 영역으로 올리는 것만이 아닌 다른 Network Driver로 포워딩시키거나 기존의 네트워크 스택을 경유하게끔도 해줄 수 있다.
이것과 관련한 설명은 다음 섹션에서 설명한다.
XDP 패킷 포워딩 타입
XDP는 패킷을 유저 영역으로 포워딩하거나 다른 Network Driver로 포워딩시키는 것을 지원한다.
그러한 타입에는 아래 5가지가 존재한다.
- XDP_ABORTED : 패킷을 Drop하고 xdp_exception을 발생시킴
- XDP_DROP : 패킷을 Drop
- XDP_PASS : 패킷을 네트워크 스택으로 보냄 (sk_buff로 변환되어 네트워크 스택에서 처리 됨)
- XDP_TX : 패킷을 수신한 드라이버에서 다시 해당 패킷을 내보냄 (어떠한 처리를 하고 되돌려 보내는 용도)
- XDP_REDIRECT : 패킷을 다른 드라이버로 포워딩하거나 유저 영역으로 포워딩
이러한 5가지 타입으로 XDP는 특정 드라이버로 수신된 패킷에 대한 드랍, 재전송 및 포워딩을 결정한다.
그리고 XDP_REDIRECT는 다른 드라이버로 패킷을 포워딩하거나 유저 영역으로 포워딩해주는데 유저 영역으로 패킷 포워딩 시킬 때 유저 영역에서는 AF_XDP 타입으로 정의된 소켓을 사용해 패킷을 드라이버로부터 수신받을 수 있다.
XDP 소켓을 줄여서 XSK라고 부르기도 한다. (XDP 쪽 코드를 보면은 XSK가 많이 있음으로 알아두는게 좋다.)
XDP의 개요는 이 섹션에서 마무리한다.
아래 섹션은 XDP 튜토리얼과 관련된 내용을 작성해두었다.
XDP 튜토리얼
XDP를 어떻게 다루는지 간단한 튜토리얼이 Github의 공개 레포지토리를 통해 제공되고 있으며, URL은 아래와 같다.
그리고 BPF 공개 레포지토리에는 XSK(XDP 소켓, AF_XDP) 관련 예제도 존재한다.
다음 XDP 관련 블로그 글로는 XDP 튜토리얼을 어떻게 빌드시키는지 또 XDP를 어떻게 다룰 수 있는지에 대해서 설명할 예정이다.
끝으로..
XDP와 비슷한 기능을 하는 라이브러리에는 DPDK라는 것이 존재한다.
아마도 DPDK가 더 Data Plane 개발자들에게 더 익숙하다고 생각한다.
인텔에서 직접적으로 제공하는 패킷 프로세싱 라이브러리 임으로 더 익숙하지 않을까..?하는 단순한 나의 추측이다.
필자는 DPDK는 접해보진 않았으며, XDP만 사용해보았다.
그런데, XDP 관련 내용을 찾을 때마다 DPDK라는 단어가 보여서 이게 어떠한 것인지 찾아보게 되었다.
DPDK란 Data Plane Development Kit의 약자로, 직역하자면 Data 평면 개발 키트이다.
이렇게 설명하면 잘 이해가 안될 수 있지만, DPDK가 사용되는 목적 자체는 매우 간단하다.
DPDK의 목적은 고속으로 패킷을 처리하기 위해 사용한다.
XDP와 유사한 목적으로 사용되는 건가 보다.
그리고 DPDK는 인텔 x86 프로세서를 위한 라이브러리 형태로 제공되고 있으며, DPDK를 통해 개발자는 패킷 처리 응용 프로그램을 빠르게 개발할 수 있도록 지원해준다.
DPDK는 고속 패킷 처리를 위해 Kernel Space에 존재하는 네트워크 스택을 우회하여 곧바로 User Space의 응용 프로그램으로 도달할 수 있도록 한다.
즉, DPDK는 NIC(네트워크 카드)에 응용 프로그램이 곧바로 연결되어 고속의 패킷 처리를 수행할 수 있게끔 한다.
찾아보니 DPDK는 인텔 x86 아키텍처에서 고속으로 패킷을 처리하기 위해 사용되는 전용 라이브러리인 것으로 간단히 이해하면 될 것 같다.
References
https://en.wikipedia.org/wiki/Express_Data_Path
https://www.monitorapp.com/ko/ebpf_xdp/
https://ko.wikipedia.org/wiki/DPDK
https://velog.io/@hellonewtry/eBPF-%EC%82%B4%ED%8E%B4%EB%B3%B4%EA%B8%B0
https://github.com/xdp-project/xdp-project/blob/master/areas/drivers/README.org