From mboxrd@z Thu Jan 1 00:00:00 1970 From: Changan Song Subject: [PATCH] Documentation: Add HOWTO Korean translation into BPF and XDP Reference Guide. Date: Fri, 21 Sep 2018 13:22:38 +0900 Message-ID: <20180921042238.19582-1-csongxdp@gmail.com> Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: Changan Song To: netdev@vger.kernel.org Return-path: Received: from mail-pf1-f194.google.com ([209.85.210.194]:44801 "EHLO mail-pf1-f194.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725898AbeIUKJ5 (ORCPT ); Fri, 21 Sep 2018 06:09:57 -0400 Received: by mail-pf1-f194.google.com with SMTP id k21-v6so5364722pff.11 for ; Thu, 20 Sep 2018 21:22:55 -0700 (PDT) Sender: netdev-owner@vger.kernel.org List-ID: Signed-off-by: Changan Song --- Documentation/translations/ko_KR/bpf-xdp.txt | 3511 ++++++++++++++++++ 1 file changed, 3511 insertions(+) create mode 100644 Documentation/translations/ko_KR/bpf-xdp.txt diff --git a/Documentation/translations/ko_KR/bpf-xdp.txt b/Documentation/translations/ko_KR/bpf-xdp.txt new file mode 100644 index 000000000000..c9472c437fdc --- /dev/null +++ b/Documentation/translations/ko_KR/bpf-xdp.txt @@ -0,0 +1,3511 @@ +NOTE: + +This is a version of https://cilium.readthedocs.io/en/v1.0/bpf/ translated +into Korean. The latest version of the English documentation can be found +at https://cilium.readthedocs.io/en/latest/bpf/. This document is maintained +by Changan Song . + +If you find any difference between this document and the original file or +a problem with the translation, please contact the maintainer of this file. + +Please also note that the purpose of this file is to be easier to read for +non English (read: Korean) speakers and is not intended as a fork. So if +you have any comments or updates for this file please update the original +English file first. The English version is definitive, and readers should +look there if they have any doubt. + +=================================== +이 문서는 +https://cilium.readthedocs.io/en/v1.0/bpf/#bpf-guide +의 한글 번역입니다. 다소 이해를 돕기 위해서 의역도 이루어 졌음을 알려 +드립니다. +역자: 송창안 +=================================== + +참고:이 문서 과정은 기술적인 면에서 BPF와 XDP를 이해하고자 하는 개발자와 +사용자를 대상으로 만들어졌습니다. 현재의 참조안내서를 읽는 동안 Cilium에 +대한 이해하는데 있어서 도움이 될 수 있지만, Cilium을 사용하는 것에 있어서 +필수사항이 아닙니다. 더 높은 수준의 소개에 대해서는 시작 안내서 +(https://cilium.readthedocs.io/en/v1.0/gettingstarted/#gs-guide) +및개념(https://cilium.readthedocs.io/en/v1.0/concepts/#arch-guide)을 +참조하십시오. + +BPF는 Linux 커널에서 매우 유연하고 효율적인 가상 시스템과 같은 구조 이며, 다양한 방식 +으로 바이트 코드를 안전하게 실행할 수 있습니다. 또한, 많은 리눅스 커널 서브 시스템에서 +사용되며 네트워킹, 추적 및 보안 (예 : 보호된 영역 내에서 프로그램을 동작시키는 것을 뜻하며, +sandboxing 라고 합니다)이 가장 두드러집니다. BPF는 1992 년부터 존재 하였지만, +현재의 문서는 커널 3.18에 처음으로 등장한 버클리 패킷 필터 (eBPF) 버전을 다루고 있으며, +고전적인 BPF (cBPF)라고 불리는 의미에 대해서는 요즘은 거의 사용 되지 않습니다 + +cBPF는 많은 사람들에게 tcpdump가 사용하는 패킷 필터 언어로 친숙히 알려져 있습니다. +최신 Linux 커널은 eBPF만 실행하고 로드 된 cBPF 바이트 코드는 프로그램 실행 전에 커널에서 +eBPF 표현으로 편리하게 변환됩니다. 이 문서는 일반적으로 eBPF와 cBPF 간의 명시적인 차이점이 +따로 언급되지 않는 상태에서는 통상적으로 BPF라는 용어를 사용합니다. + +Berkeley Packet Filter라는 이름이 특정 목적을 필터링하는 패킷을 암시 하긴 하지만, +명령어 세트는 요즘에는 앞서 설명한 의미와는 별도로 BPF에 대한 많은 사용 사례가 있으며 +일반적이며 유연합니다. BPF를 사용하는 프로젝트 목록은 추가 자료 +(https://cilium.readthedocs.io/en/v1.0/bpf/#bpf-users)를 참조하십시오. + +Cilium은 BPF를 데이터 경로에 많이 사용하며, 자세한 내용은 개념 +(https://cilium.readthedocs.io/en/v1.0/concepts/#arch-guide) +을 참조하십시오. 이 장의 목표는 BPF에 대한 이해와 tc(트래픽 제어) 및 XDP(고속 데이터 경로)로 +BPF 프로그램을 로드하는 것을 포함한 네트워크 관련 용도 와 Cilium에서 BPF 템플릿을 개발 +하는데 도움이되는 BPF 참조 가이드를 제공하는 것입니다. + +* BPF 구조 +BPF는 명령 세트만 제공함에 있어서 다른 정의하지 않지만, 효율적인 키/값 저장소 역할을 하는 map이 +있으며, 커널 기능과 상호 작용하고 활용하는 도와주는 함수인 helper 함수, 다른 BPF 프로그램을 +호출하는 tail call, 보안 강화 기본 요소, 객체(map, 프로그램) 고정용 가상 파일 시스템, +BPF를 네트워크 카드와 같은 오프로드 허용하는 기반을 언급하고 있습니다. + +LLVM은 BPF 백엔드를 제공하므로 clang과 같은 도구를 사용하여 C를 BPF 객체 파일로 컴파일 +한 다음에 커널에 로드 할 수 있습니다. BPF는 리눅스 커널에 많은 연관 관계가 있으며, 원래의 +커널 성능을 저해시키지 않으면서 완전한 프로그래밍이 가능합니다. + +마지막으로, 그렇지만 앞에서 설명한 것과 마찬가지로 중요한 것은 BPF를 사용하는 커널 서브 시스템은 +BPF의 기반의 일부라는 것입니다. 이 문서에서 다루는 두 가지 주요 하위 시스템은 +BPF 프로그램을 연결할 수 있는 tc 및 XDP 입니다. +(첨언 : https://fosdem.org/2018/schedule/event/xdp/attachments/slides/ +2220/export/events/attachments/xdp/slides/2220/fosdem18_SdN_NFV_qmonnet_XDPoffload.pdf +10 번 슬라이드를 참고 부탁드립니다) XDP BPF 프로그램은 초기 네트워킹 드라이버 단계에 연결되어 +패킷 수신시 BPF 프로그램 실행을 유발하게 합니다. 정의에 따르면 패킷이 소프트웨어보다 더 빠른 시점에서 +처리 될 수 없으므로 최상의 패킷 처리 성능을 얻을 수 있습니다. 그러나 이러한 처리는 네트워킹 스택의 초기에 +발생하기 때문에 스택은 패킷에서 아직 메타 데이터를 추출하지 못했습니다. 반면에 tc BPF 프로그램은 커널 스택에서 +나중에 실행 되므로 더 많은 메타 데이터와 코어 커널 기능에 접근 할 수 있습니다. +tc 및 XDP 프로그램 외에도 추적 (kprobes, uprobes, tracepoints 등)와 같은 +BPF를 사용하는 다양한 다른 커널 하위 시스템이 있습니다. + +다음 하위 절에서는 앞에서 열거한 BPF 아키텍처의 개별적인 측면에 대해 자세히 설명합니다. + +# 명령어 집합 +BPF는 범용 RISC 명령어 집합이며 compiler 하위 단계에서 (예를 들어 LLVM)를 통해 +BPF 명령어로 컴파일 가능한 C언어로 프로그램을 작성하기 위해 설계 되었으며, +이후 커널은 나중에 커널 내부의 최적의 실행 성능을 위해 커널 내 JIT 컴파일러를 통해 고유의 +연산자 코드(opcode)로 매핑 할 수 있습니다. + +이러한 명령어들을 커널에 적용 하게되면 다음과 같은 장점이 있습니다: + +* 커널/사용자 영역 간 경계를 넘지 않아도 커널 프로그래밍 할 수 있습니다. 예를 들어, Cilium의 경우에서는 +네트워킹과 관련된 BPF 프로그램은 유연한 컨테이너 정책, 로드 밸런싱 구현이 가능 하며, 다른 의미로써 +패킷을 사용자 영역으로 이동시키고 커널로 다시 보낼 필요가 없다는 것을 의미합니다. +또한, BPF 프로그램과 커널/사용자 영역 사이의 상태는 필요할 때마다 맵을 통해 공유 할 수 있습니다. +(역자 : https://suchakra.wordpress.com/tag/ebpf/의 그림에서 +제일 마지막 그림중에 bpf()시스템콜을 받으면서 read maps 부분을 참고 부탁드립니다.) +(역자 참고 : 커널 v2.6 부터 v4.6 v4.9 까지의 BPF 프로파일링에 대한 변화를 확인하고 싶다면, +http://www.brendangregg.com/blog/2016-10-21/linux-efficient-profiler.html +을 참고 부탁드립니다) + +* 프로그램 가능한 데이터 경로의 유연성을 감안할 때, 프로그램은 사용 사례에는 요구하지 않는 기능에 대해 +컴파일 제외 하여 성능을 크게 향상시킬 수 있습니다. 예를 들어, 컨테이너에서 IPv4가 필요하지 않은 경우 +BPF 프로그램은 fast-path에서의 자원들을 절약 하기 위해 IPv6 만 처리하도록 구축 될 수 있습니다. + +* 네트워킹 (예 : tc 및 XDP)의 경우 BPF 프로그램은 커널, 시스템 서비스 또는 컨테이너를 다시 시작하지 않고 +트래픽 중단 없이 최소한으로 업데이트 할 수 있습니다. 또한 BPF 맵을 통해 모든 프로그램 상태를 업데이트 할 수 있습니다. + +* BPF는 사용자 영역에 대해 안정적인 ABI를 제공 하며 써드파티 커널 모듈을 따로 필요로 하지 않습니다. +BPF는 모든 곳에서 포함되는 Linux 커널의 핵심 부분(LTM 버젼으로 최소 v4.4 이상으로 판단)이며 +기존 BPF 프로그램이 최신 커널버전으로 계속 실행 될수 있도록 보장합니다. +앞서 설명한 보장은 사용자 공간 응용 프로그램과 관련하여 커널이 system 콜을 제공하는 것과 동일한 보장입니다. +또한 BPF 프로그램은 다른 아키텍처(ARM32, ARM64, Power, NFR 등등)에서 이식 가능합니다. + +* BPF 프로그램은 커널과 함께 작동하며 커널이 제공하는 안전 보장 뿐만 아니라 기존 커널 기반(예 : 드라이버, +net device, 터널, 프로토콜 스택, 소켓) 및 도구 (예 : iproute2)를 사용합니다. +커널 모듈과는 달리 BPF 프로그램으로 커널을 crash가 발생 할수 없도록 하며, 항상 종료 되도록 보장하기 위해 +커널 내 verifier 를 통해 검증 됩니다.예를 들어, XDP 프로그램은 기존의 커널 내부 드라이버를 재사용하며, +XDP 프로그램을 다른 모델처럼 사용자 또는 전체 드라이버를 사용자 공간에 공개하지 않고 xdp 패킷 프레임을 +포함하는 커널에서 제공된 DMA 버퍼에서 동작합니다. 또한 XDP 프로그램은 기존 프로토콜 스택을 bypass 하는 +대신에 다시 사용합니다. BPF는 특정 사용 사례를 해결하기 위한 프로그램을 만들기 위해 커널 기능에 대한 +일반적인 "Glue code" 로 고려되어 질수 있습니다. + +커널 내부의 BPF 프로그램 실행은 항상 이벤트 동작 입니다. 예를 들어, 들어오는 경로에 BPF 프로그램이 연결된 +네트워킹 장치는 패킷이 수신되면 프로그램의 실행을 트리거합니다. BPF 프로그램이 연결된 kprobes가 위치한 +커널 주소는 해당 주소의 코드가 도착하면 트랩 되며, 실행 한 다음 측정을 위해 kprobes 콜백 함수를 호출하며 +이후에 연결된 BPF 프로그램의 실행을 트리거 합니다. + +BPF는 11 개의 64 비트 레지스터와 32 비트 서브 레지스터로 구성 되며, +프로그램 카운터 및 512 바이트의 큰 BPF 스택 공간이 있습니다. BPF 레지스터의 이름은 "r0 - r10"입니다. +작동 방식는 기본적으로 64 비트이며 32 비트 하위 레지스터는 특수 ALU(산술 논리 장치) 작업을 통해서만 +액세스 할 수 있습니다. 32 비트 하위 하위 레지스터는 기록 될 때 64 비트로 zero-extend 됩니다. + +레지스터 r10은 읽기 전용이며 BPF 스택 공간에 액세스하기 위해 프레임 포인터 주소를 포함하는 유일한 +레지스터입니다. 나머지 r0 부터 r9 레지스터는 범용이며 읽기 / 쓰기 특성을 가지고 있습니다. +BPF 프로그램은 핵심 커널에 의해 정의 된 사전 정의 된 Helper 함수를 콜 할 수 있습니다 +(모듈별로 사용하지 않습니다). BPF 콜 규칙은 다음과 같이 정의됩니다. + +* r0 레지스터리는 helper 함수 콜의 반환 값이 포함됩니다. +* r1 - r5 레지스터리는 BPF 프로그램에서 커널 helper 기능에 대한 인수를 가지게 됩니다. +* r6 - r9 레지스터리는 helper 함수 콜시 callee가 저장하는 레지스터 입니다. + +BPF 호출 규약은 x86_64, arm64 및 기타 ABI 를 직접 매핑 할만큼 충분히 일반적이며, 따라서 +모든 BPF 레지스터들은 HW CPU 레지스터들과 1:1 매핑 되며, JIT는 호출 명령어를 단지 전달하며, +함수 인자들을 배치 하기 위한 추가적인 행동들은 없습니다.이 콜 규약은 성능 저하 없이 일반적인 호출 +상황은 포함하도록 모델링 되었습니다. 6 개 이상의 인자가 있는 콜은 현재 지원되지 않습니다. + +커널에서 BPF (BPF_call_0() 함수에서 BPF_call_5() 함수들)의 Helper 함수는 특별히 규칙을 +염두에 두고 고안되었습니다. (역자 : 위의서 설명한 BPF_call_0 에서 BPF_call_5() 함수는 아래의 +코드에서 확인 가능 합니다.) +https://github.com/torvalds/linux/blob/master/kernel/bpf/helpers.c) + +레지스터 r0은 BPF 프로그램의 종료 값을 포함하는 레지스터 이기도합니다. +종료 값의 의미는 프로그램 유형에 따라 정의됩니다. 커널에게 실행을 다시 전달 할때, 종료 값은 32비트 +값으로 전달됩니다. + +(역자 : https://github.com/iovisor/bpf-docs/blob/master/bpf-internals-2.md +에서 구조 부분을 참조 ) + +R0 – rax return value from function +R1 – rdi 1st argument +R2 – rsi 2nd argument +R3 – rdx 3rd argument +R4 – rcx 4th argument +R5 – r8 5th argument +R6 – rbx callee saved +R7 - r13 callee saved +R8 - r14 callee saved +R9 - r15 callee saved +R10 – rbp frame pointer ( read only ) + + +레지스터 r1 - r5는 스크래치 레지스터 이며, BPF 프로그램은 BPF 스택에 이들을 spilling +하거나 이러한 인수가 여러 helper 함수 콜에 걸쳐 재사용 될 경우 callee 저장 레지스터로 이동 +중 하나가 필요하게 됩니다. spilling은 레지스터의 변수가 BPF 스택으로 이동 하는 것을 뜻합니다. +변수를 BPF 스택에서 레지스터로 이동하는 반대 작업을 채우기라고합니다. +BPF 스택에서 레지스트로 변구를 이동하는 역동작을 가리켜 filling 이라고 합니다. +제한된 레지스터 수로 인해 spilling/filling 이 동작이 있는 이유 입니다. + +BPF 프로그램 실행을 시작하면 레지스터 r1은 처음에 프로그램의 컨텍스트를 포함합니다. +컨텍스트는 프로그램의 입력 인수 뜻하며, (일반적인 C 프로그램의 경우 argc/argv 쌍과 +유사하다고 이해하시면 됩니다). BPF는 단일 컨텍스트에서 작업하도록 제한됩니다. +컨텍스트는 프로그램의 유형에 따라 달리지며, 예를 들어, 네트워킹 프로그램은 +네트워크 패킷 (skb)의 kernel representation을 입력 인수로 가질 수 있습니다. + +BPF의 일반적인 연산은 포인터 연산을 수행하기 위해 64 비트 아키텍처의 natural model +을 따르는 64 비트이며, 포인터를 전달 할 뿐만 아니라 64 비트 값을 helper 함수에 전달하며, +그리고 64bit atomic operation을 허용합니다. + +프로그램 당 최대 명령어은 4096 BPF 명령어 으로 제한이 되며 이는 설계적으로 +모든 BPF 프로그램은 빨리 종료 되는것을 의미 합니다. 명령어 세트가 순방향 및 역방향 점프를 포함하지만 +커널 내 BPF verifier는 루프를 금지 하므로 종료가 항상 보장됩니다. BPF 프로그램은 커널 내부에서 +실행되기 때문에 verifier의 job는 시스템의 안정성에 영향을 주지 않고 실행이 안전하다는 것을 +확인하는 것입니다. 즉, 명령어 세트 관점에서 루프를 구현할 수 있지만 verifier는 이를 제한 합니다. +그러나 한 BPF 프로그램이 다른 BPF 프로그램으로 이동할 수 있도록 하는 tail 콜 개념도 있습니다. +(역자 : https://elixir.bootlin.com/…/l…/source/kernel/bpf/verifier.c +코드를 확인하면 좀더 자세히 verifier에 대한 내부 구조가 확인 가능합니다.) +"eBPF verifier statically determines that the program +terminates and safe to execute." :-) + +(오역의 가능성) +This, too, comes with an upper nesting limit of 32 calls, and is usually used to +decouple parts of the program logic, for example, into stages. +이것 또한, 32개의 상위 nesting 제한이 있으며, 일반적으로 프로그램의 논리 부분을 분리 하는데 사용되며, +예를 들어 다음 단계로 진입을 뜻합니다. (역자 : bpf syscall은 BPF_PROG_LOAD 가 되면서, JIT interpreter로 +진입을 뜻하는 것으로 판단해서 물론 로드된 byter code에 대해 verfier가 실행을 하기 위해 존재 +하며 따라서 위와 같은 번역을 하였습니다.) + +명령어 형식은 두 개의 피연산자 명령어들로 모델링 되며 JIT 단계에서 BPF 명령어를 기본 명령어에 매핑하는 +데 도움이 됩니다. 명령어 세트는 고정 크기이며 모든 명령어가 64 비트 인코딩을 가지는것을 의미합니다. +현재 87 개의 명령어가 구현 되었으며 인코딩을 통해 필요할 때 further 명령어을 사용하여 세트를 확장 +할 수 있습니다. big-endian 머신에서 단일 64 비트 명령어의 명령어 인코딩은 아래로 구성이 되며, +op:8, dst_reg:4, src_reg:4, off:16, imm:32, off 그리고 imm은 부호 타입 입니다. +인코딩은 커널 헤더의 일부이며 linux/bpf_common.h를 포함하는 linux/bpf.h 헤더 파일에 +정의되어 있습니다. + +msb lsb ++------------------------+----------------+----+----+--------+ +|immediate |offset |src |dst |opcode | +:%s/\s\+$//e ++------------------------+----------------+----+----+--------+ + +8 bit opcode +4 bit destination register (dst) +4 bit source register (src) +16 bit offset +32 bit immediate (imm) + +@/include/uapi/linux/bpf.h +struct bpf_insn { +__u8 code; /* opcode */ +__u8 dst_reg:4; /* dest register */ +__u8 src_reg:4; /* source register */ +__s16 off; /* signed offset */ +__s32 imm; /* signed immediate constant */ +}; + +https://github.com/iovisor/bpf-docs/blob/master/eBPF.md ( 참조) + +op는 수행 할 실제 작업을 정의합니다. op에 대한 대부분의 인코딩은 cBPF에서 다시 사용 되었습니다. +동작은 레지스터 또는 즉각적인 피연산자를 기반으로 할 수 있습니다. op 자체의 인코딩은 사용할 방식 +(레지스터 기반 동작을 표현하기 위한 BPF_X 그리고 즉각적인 기반 각각 동작을 위한 BPF_K)에 대한 +정보를 제공합니다. (역자 : opcode는 약 8 bit 이며, operation code(4 bits), Source bit(1bit), +instruction class(3bit)로 내부에 나누어 져 있습니다. operation code에는 BPF_MOV, BPF_JNE에 +해당하는 값이 포함되며, Source bit(1bit)에서는 BPF_X src_reg와 dst_reg 혹은 BPF_K dst_reg 와 +32bit imm 이 포함할수 있으며, instruction class(3bit)는 BPF_ALU, BPF_ALU64, BPF_JMP가 포함됩니다. +https://open-nfp.org/s/pdfs/dxdd-europe-bpf-and-xdp-explained.pdf 참조) +후자의 경우 대상 피연산자는 항상 레지스터 입니다. +dst_reg 및 src_reg는 모두 동작을 위해 사용될 레지스터 피연산자 (예 : r0 - r9)에 대한 추가 정보를 제공합니다. +off는 예를 들어 BPF에 이용 가능한 스택 또는 다른 버퍼 (예를 들어, 맵 값, 패킷 데이터 등)를 어드레싱하거나, +Jump 명령어에서 타겟을 점프 와 같은 상대 오프셋을 제공 하기 위해 일부 명령어 에서 사용됩니다. +imm은 상수 / 즉각적인 값을 포함합니다. + +(현재 아래의 두개의 그림은 현재의 번역하는 문서에는 없는 내용입니다. 하지만 이해를 돕기 위해서 아래의 페이지를 +참고하여 opcode 구조를 참조 하였습니다) +https://github.com/iovisor/bpf-docs/blob/master/eBPF.md + +ALU/ALU64/JMP opcode 구조: ++----------------+--------+--------------------+ +| 4 bits | 1 bit | 3 bits | +| operation code | source | instruction class | ++----------------+--------+--------------------+ +(MSB) (LSB) + +LD/LDX/ST/STX opcode 구조: ++------------+------------+--------------------+ +| 3 bit | 2 bit | 3 bits | +| mode | size | instruction class | ++------------+------------+--------------------+ +(MSB) (LSB) + +Least Significant Bit(LSB) +Most Significant Bit(MSB) + +사용 가능한 op 명령어는 다양한 명령어 클래스로 분류 할 수 있습니다. 이러한 클래스는 op 필드에도 인코딩 됩니다. +연산 필드는 최하위 비트(lsb) 부터 최상위 비트(msb)까지 이며, code:4 bits ,source:1 bits 그리고 +instruction class:3 bits 으로 나뉩니다. instruction class 필드는 일반적인 명령어 클래스이며, +operation code는 해당 클래스의 특정 연산 코드를 나타내며, source 필드는 소스 피연산자가 +레지스터인지 즉각적인 값 인지를 알려 줍니다. 가능한 명령 클래스는 다음과 같습니다: + +BPF_LD, BPF_LDX : 두개 모두 load 동작을 위한 클래스 입니다. +BPF_LD는 imm 필드의 32 bits 와 패킷 데이터의 byte/half-word/word 로드 하기 의한 +두개의 명령어를 포괄하는 특수 명령어 이며, double word를 로드 하는데 사용합니다. +이후 에는 주로 최적화 된 JIT 코드를 가지고 있기 때문에 cBPF를 BPF 변환으로 유지하기 위해 주로 +cBPF에서 가져와서 적용 되었습니다. 현재의 BPF의 경우에는 이러한 패킷 로드 명령어는 관련성이 낮습니다. +BPF_LDX 클래스는 메모리에서 byte/half-word/word/double-word 로드에 대한 명령어를 저장합니다. +이 컨텍스트의 메모리는 일반적이며 스택 메모리, 맵 값 데이터, 패킷 데이터 등 일 수 있습니다. + +mode 필드에서는 다음과 같이 포함될 수 있습니다: +BPF_IMM 0x00 /* used for 32-bit mov in classic BPF and 64-bit in eBPF */ +BPF_ABS 0x20 +BPF_IND 0x40 +BPF_MEM 0x60 +BPF_LEN 0x80 /* classic BPF only, reserved in eBPF */ +BPF_MSH 0xa0 /* classic BPF only, reserved in eBPF */ +BPF_XADD 0xc0 /* eBPF only, exclusive add */ + +BPF_IND 와 BPF_ABS에 대한 데이터 크기는 word (BPF_W), half-word (BPF_H) 혹은 byte (BPF_B) 로 지정 됩니다. +size 필드에서는 다음과 같이 포함될 수 있습니다: +BPF_W 0x00 /* word (4 byte) */ +BPF_H 0x08 /* half word (2 byte) */ +BPF_B 0x10 /* byte (1 byte) */ +BPF_DW 0x18 /* eBPF only, double word (8 byte) */ + +BPF_ST, BPF_STX: 두개 모두 저장 연산을 위한 클래스 입니다. +BPF_LDX와 비슷하게 BPF_STX는 마찬가지로 저장하는 역활을 하며 레지스터의 데이터를 메모리에 저장하는 데 사용되며 +다시 스택 메모리, 맵 값, 패킷 데이터 등이 될 수 있습니다. BPF_STX는 또한 예를 들어, 카운터에 사용할 수있는 단어 및 +double-word 기반 atomic 추가 연산을 수행하기위한 특수 명령어를 가지게 됩니다. +BPF_ST 클래스는 소스 피연산자가 즉각적인 값이라는 것을 메모리에 저장하기 위한 명령을 제공함으로써 BPF_STX와 유사합니다. + +BPF_ALU, BPF_ALU64: 두개의 모두 ALU 동작을 포함한 클래스 입니다. +일반적으로, BPF_ALU 동작은 32bit 방식 이며, BPF_ALU64는 64bit 방식 입니다. +두 ALU 클래스는 모두 레지스터 기반의 원본 피연산자와 즉각적인 기반의 피연산자로 기본 연산을 수행합니다. +add(+), sub(-), and(&), or(|), left shift(<<), +right shift (>>), xor(^), mul (*), div (/), +mod (%), neg (~) 연산을 두개의 class가 지원합니다. 또한 mov( := )는 두 피연산자 방식에서 +두 클래스의 특수한 ALU 연산으로 추가되었습니다. BPF_ALU64에는 signed right shift도 포함됩니다. +BPF_ALU는 주어진 source 레지스터에 대한 half-word word/double-word 에 대한 엔디안 변환 명령을 추가로 포함합니다. + +만약 BPF_CLASS(code) == BPF_ALU 혹은 BPF_ALU64 [ eBPF에서 ], BPF_OP(code)에는 다음 중 하나 입니다: + +BPF_ADD 0x00 +BPF_SUB 0x10 +BPF_MUL 0x20 +BPF_DIV 0x30 +BPF_OR 0x40 +BPF_AND 0x50 +BPF_LSH 0x60 +BPF_RSH 0x70 +BPF_NEG 0x80 +BPF_MOD 0x90 +BPF_XOR 0xa0 +BPF_MOV 0xb0 /* eBPF only: mov reg to reg */ +BPF_ARSH 0xc0 /* eBPF only: sign extending shift right */ +BPF_END 0xd0 /* eBPF only: endianness conversion */ + +BPF_JMP: 점프 동작 전용 클래스 입니다. 점프는 무조건 그리고 조건부 일 수 있습니다. +무조건 점프는 단순히 프로그램 카운터를 앞으로 이동시켜 현재 명령과 관련하여 실행될 다음 명령이 off + 1이 +되도록 하며, 여기서 off는 명령에 인코딩 된 상수 오프셋 입니다. +off가 signed 때문에 점프는 루프를 생성하지 않고 프로그램 범위 내에있는 한 다시 실행할 수 있습니다. +조건부 점프는 레지스터 기반 및 즉각적인 기반 소스 피연산자 모두에서 작동합니다. +점프 작업의 조건이 참일 경우 오프 + 1로 상대 점프가 수행되고, 그렇지 않으면 다음 명령 (0 + 1)이 수행됩니다. +이 fall-through jump 로직은 cBPF와 비교하여 다르므로 보다 자연스럽게 CPU 분기 예측 로직에 적합하므로 +더 나은 분기 예측이 가능하게 됩니다. 사용 가능한 조건은 jeq (==), jne (!=), jgt(>), jge (>=), +jsgt (signed >), jsge (signed >=), jlt (<), jle (<=), jslt (signed <), +jsle (signed <=) and jset (만약 DST & SRC 점프). +그 외에도 이 클래스에는 세 가지 특별한 점프 작업들이 있으며: 다시말해서, BPF 프로그램을 종료하고 +r0의 현재 값을 반환 코드로 반환하는 종료 명령, 사용 가능한 BPF helper function 중 하나로 함수 콜을 발행하는 +콜 명령어 및 다른 BPF 프로그램으로 점프하는 tail 콜 명령어가 있습니다. + +만약 BPF_CLASS(code) == BPF_JMP 인 경우에 BPF_OP(code)에는 다음 중 하나 입니다: +BPF_JA 0x00 +BPF_JEQ 0x10 +BPF_JGT 0x20 +BPF_JGE 0x30 +BPF_JSET 0x40 +BPF_JNE 0x50 /* eBPF only: jump != */ +BPF_JSGT 0x60 /* eBPF only: signed '>' */ +BPF_JSGE 0x70 /* eBPF only: signed '>=' */ +BPF_CALL 0x80 /* eBPF only: function call */ +BPF_EXIT 0x90 /* eBPF only: function return */ +BPF_JLT 0xa0 /* eBPF only: unsigned '<' */ +BPF_JLE 0xb0 /* eBPF only: unsigned '<=' */ +BPF_JSLT 0xc0 /* eBPF only: signed '<' */ +BPF_JSLE 0xd0 /* eBPF only: signed '<=' */ + +만약 BPF_CLASS(code) == BPF_ALU 또는 BPF_JMP 일 때, 4 번째 비트는 소스 피연산자를 인코딩 하게 됩니다. +BPF_K 0x00 +BPF_X 0x08 + +예를 들어서 +BPF_IND | BPF_W | BPF_LD 의미는: + +R0 = ntohl(*(u32 *) (((struct sk_buff *) R6)->data + src_reg + imm32)) +그리고 R1 - R5 스크래치 됬었습니다. + +기본 BPF 명령어 세트와 달리, eBPF는 일반적인 로드/저장 연산을 가지고 있습니다 : + +BPF_MEM | | BPF_STX: *(size *) (dst_reg + off) = src_reg +BPF_MEM | | BPF_ST: *(size *) (dst_reg + off) = imm32 +BPF_MEM | | BPF_LDX: dst_reg = *(size *) (src_reg + off) +BPF_XADD | BPF_W | BPF_STX: lock xadd *(u32 *)(dst_reg + off16) += src_reg +BPF_XADD | BPF_DW | BPF_STX: lock xadd *(u64 *)(dst_reg + off16) += src_reg + +Linux 커널은 BPF 명령어로 어셈블된 프로그램을 실행하는 BPF 인터프리터와 함께 제공됩니다. +아직 cBPF JIT와 함께 제공되고 아직 eBPF JIT로 마이그레이션되지 않은 아키텍처를 제외하고는 cBPF +프로그램도 커널에서 투명하게 eBPF 프로그램으로 변환됩니다. 현재 x86_64, arm64, ppc64, s390x, mips64, +sparc64 및 arm 아키텍처에는 커널 내 eBPF JIT 컴파일러가 제공됩니다. + +프로그램을 커널에 로드하거나 BPF 맵을 작성하는 것와 같은 모든 BPF 처리는 중앙 bpf() 시스템 콜을 통해 관리됩니다. +또한 맵 엔트리 (lookup/update/delete)를 관리하고, 프로그램뿐만 아니라 고정 된 맵을 +BPF 파일 시스템에 고정시키는 데에도 사용됩니다. + +#Helper 함수 + +Helper 함수는 BPF 프로그램이 코어 커널에 정의된 함수 콜 참조하여 커널에서 데이터를 +검색/푸시 할수있게 해주는 개념입니다. 사용 가능한 Helper 기능은 각 BPF 프로그램 유형 마다 다를 수 있으며, +예를 들어, 소켓에 연결된 BPF 프로그램은 TC 계층에 연결된 BPF 프로그램과 비교하여 Helper 하위 집합 만 콜 할 수 있습니다. +경량 터널링을 위한 캡슐화 및 캡슐해제 Helper는 tc 레이어 아래쪽에서 사용할 수 있는 기능의 예를 구성하는 +반면 사용자 공간에 알림을 푸시 하기위한 event output Helper는 tc 및 XDP 프로그램에서 사용 할 수 있습니다. + +각 helper 함수는 시스템 콜과 유사한 공통적으로 공유되는 함수 대표로 구현됩니다. 대표는 다음과 같이 정의됩니다: +u64 fn(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5) + +이전 섹션에서 설명한대로 콜 규칙은 모든 BPF helper 함수에 적용됩니다. +커널은 helper 함수를 매크로 "BPF_call_0 ()에서 BPF_call_5 ()까지 시스템 콜과 유사하게 규격화 하였습니다. +다음 예제는 map 구현에 해당하는 콜백을 콜하여 맵 요소를 업데이트 하는 helper 함수를 발췌하였습니다. + +BPF_call_4(bpf_map_update_elem, struct bpf_map *, map, void *, key, +void *, value, u64, flags) +{ +WARN_ON_ONCE(!rcu_read_lock_held()); +return map->ops->map_update_elem(map, key, value, flags); +} + +const struct bpf_func_proto bpf_map_update_elem_proto = { +.func = bpf_map_update_elem, +.gpl_only = false, +.ret_type = RET_INTEGER, +.arg1_type = ARG_CONST_MAP_PTR, +.arg2_type = ARG_PTR_TO_MAP_KEY, +.arg3_type = ARG_PTR_TO_MAP_VALUE, +.arg4_type = ARG_ANYTHING, +}; + +이러한 접근에는 여러 가지 장점이 있습니다: 기존의 cBPF는 보조 helper 함수를 호출 +하기 위해 무리한 패킷 오프셋에서 데이터를 가져 오기 위해 로드 명령어를 오버로드 했으며, +각 cBPF JIT는 이러한 cBPF 확장에 대한 지원을 구현해야했습니다. 하지만 eBPF의 경우, 새로 추가된 +각 helper함수는 JIT 가 투명하고 효율적인 컴파일된 컴파일이 되며, 이러한 의미는 JIT 컴파일러는 +레지스터 매핑과 같은 방식으로 이루어지기 때문에 단지 호출 명령을 내보내며 되며, +레지스터 매핑과 같은 방식은 BPF 레지스터 할당은 이미 기본 아키텍처의 호출 개념과 일치합니다. +이렇게 하면 새로운 helper 기능으로 core 커널을 쉽게 확장 할 수 있습니다. +하지만 모든 BPF helper 함수는 core 커널의 일부이며 커널 모듈을 통해 확장하거나 추가 할 수 없습니다. + +앞서 언급한 함수 시그니처는 또한 verifier가 유형 검사를 수행 하도록 허용합니다. +구조체 bpf_func_proto는 helper에 대해 알 필요가 있는 모든 필요한 정보를 verifier에게 +넘겨주기 위해 사용되며, 따라서 verifier는 helper가 BPF 프로그램의 분석 된 레지스터의 +현재 내용과 일치에서 예상되는 유형을 확인할 수 있습니다. + +인수 유형은 모든 종류의 값 전달에서 BPF 스택 버퍼에 대한 포인터/크기 쌍 +(예를 들어서, helper에서 읽거나 쓸 내용)까지 다양합니다. 후자의 경우, verifier는 예를 들어 버퍼가 +이전에 초기화되었는지 여부와 같은 추가 검사를 수행 할 수도 있습니다. 사용 가능한 BPF helper +함수 목록은 다소 길며 지속적으로 증가하고 있으며, 예를 들어 글을 쓰는 시점에서, BPF 프로그램은 38 개의 +BPF helper 중에서 선택할 수 있습니다. (역자 주 : 커널 코드 내 에서 helper 함수에 대해서는 +git grep 'FN(' include/uapi/linux/bpf.h 명령을 토대로 +helper 함수 리스트를 확인 할 수 있습니다. 4.17.0-rc7 기준으로 80개의 helper 함수를 확인 할 수 +있었습니다. 자세한 내용은 +https://github.com/iovisor/bcc/blob/master/docs/kernel-versions.md#helpers +를 참조 하였습니다.) 커널 구조체 인 bpf_verifier_ops는 제공된 BPF 프로그램 타입을 위한 사용 +가능 한 helper 중 하나에 특정 enum 키워드인 bpf_func_id를 매핑을 제공하는 +get_func_proto 콜백 함수가 있습니다. + +@include/linux/bpf.h +.... +229 struct bpf_verifier_ops { +230 /* return eBPF function prototype for verification */ +231 const struct bpf_func_proto * +232 (*get_func_proto)(enum bpf_func_id func_id, +233 const struct bpf_prog *prog); +.... + +#Maps +map은 커널 공간에 있는 효율적인 키/값 저장소입니다. 여러 BPF 프로그램 콜간에 상태를 유지하기 위해 +BPF 프로그램에서 액세스 할 수 있습니다. 또한 사용자 공간의 파일 설명자를 통해 액세스 할 수 있으며 다른 +BPF 프로그램이나 사용자 공간 응용 프로그램과 마음대로 공유 할 수 있습니다. 서로 map을 공유하는 BPF 프로그램은 +동일한 프로그램 유형이 아니어야 하며, 예를 들어, trace 프로그램은 네트워크 프로그램과 map을 공유 할 수 있습니다. +단일 BPF 프로그램은 현재 최대 64 개의 다른 map에 직접 액세스 할 수 있습니다. +map 구현은 코어 커널에 의해 제공됩니다. 마음대로 데이터를 읽고 쓸 수있는 각 CPU 마다 일반적인 map 및 각 CPU 마다 +아닌 일반적인 map들 있지만 helper 함수와 함께 사용 되는 일부 일반적 이지 않는 map도 있습니다. +(역자 주 : 커널 코드내 에서 map에 관련된 내용에 대해서는 +git grep -W 'bpf_map_type {' include/uapi/linux/bpf.h 명령을 토대로 리스트가 확인이 가능합니다.) + +지금 사용 가능한 일반적인 map은 BPF_MAP_TYPE_HASH, BPF_MAP_TYPE_ARRAY, BPF_MAP_TYPE_PERCPU_HASH, +BPF_MAP_TYPE_PERCPU_ARRAY, BPF_MAP_TYPE_LRU_HASH, BPF_MAP_TYPE_LRU_PERCPU_HASH and +BPF_MAP_TYPE_LPM_TRIE 가 있습니다. 그들은 서로 다른 의미와 성능 특성을 지닌 다른 백엔드를 구현되었지만 +조회, 업데이트 또는 삭제 작업을 수행하기 위해 BPF herlper 함수들의 동일한 공통 세트를 사용합니다. +일반적이지 않는 map은 현재 커널에서 BPF_MAP_TYPE_PROG_ARRAY, BPF_MAP_TYPE_PERF_EVENT_ARRAY, +BPF_MAP_TYPE_CGROUP_ARRAY, BPF_MAP_TYPE_STACK_TRACE, BPF_MAP_TYPE_ARRAY_OF_MAPS, +BPF_MAP_TYPE_HASH_OF_MAPS 가 있습니다. 예를 들어, BPF_MAP_TYPE_PROG_ARRAY는 다른 BPF 프로그램을 +저장하는 배열 map이며, BPF_MAP_TYPE_ARRAY_OF_MAPS 및 BPF_MAP_TYPE_HASH_OF_MAPS는 런타임시 +전체 BPF 맵을 원자적 으로 대체 할 수 있도록 다른 맵에 대한 포인터를 보유합니다. +이러한 유형의 map은 BPF 프로그램 콜을 통해 추가(비-데이터) 상태가 유지되어야 하기 때문에 +BPF helper 함수를 통해 구현 하기에는 부적합한 문제를 해결 합니다. + +#객체 고정 + +BPF 맵과 프로그램은 커널 리소스 역할을 하며 커널의 익명 inode가 지원하는 파일 디스크립터를 통해서만 +액세스 할 수 있으며, 장점 인것 뿐만 아니라 여러 가지 단점이 있습니다: +사용자 공간 응용 프로그램은 대부분의 파일 디스크립터 관련 API, Unix 도메인 소켓을 통과하는 +파일 디스크립터 등을 투명하게 사용할 수 있지만 동시에 파일 디스크립터는 프로세스의 수명에만 제한되기 +때문에 map 공유와 같은 옵션이 동작하는 것은 어려워집니다. 따라서 iproute2 에서는 tc 또는 XDP는 +커널에 프로그램을 로드하고 결국 종료되는 것 같은 특정 사용 사례에서는 여러가지 복잡한 문제가 발생합니다. +앞의 문제를 포함하여, 또한 데이터 경로의 ingress 및 engress 위치 사이에서 맵을 공유하는 경우 와 +같이 사용자 공간 측면에서도 map에 액세스 할 수 없습니다. 또한 사용자 공간 측면에서 map에 대한 +액세스를 사용할 수 없습니다. 또한 타사 응용 프로그램은 BPF 프로그램 런타임 중에 map +내용에 대해 모니터링 하거나 업데이트 하려고 할수 있습니다. 이러한 한계를 극복 하기 위해서 +BPF map 과 프로그램은 객체 고정 이라고 불리는 고정이 될수 있는 최소한의 커널 공간 BPF 파일 시스템 +(/sys/fs/bpf)이 구현 되었습니다. 따라서 BPF 시스템 콜은 이전에 고정 된 +객체를 고정(BPF_OBJ_PIN)하거나 검색(BPF_OBJ_GET) 할 수있는 두 개의 새로운 명령으로 확장 되었습니다. + +kernel/bpf/syscall.c +... +static int bpf_obj_pin(const union bpf_attr *attr) +{ +if (CHECK_ATTR(BPF_OBJ) || attr->file_flags != 0) +return -EINVAL; + +return bpf_obj_pin_user(attr->bpf_fd, u64_to_user_ptr(attr->pathname)); +} + +static int bpf_obj_get(const union bpf_attr *attr) +{ +if (CHECK_ATTR(BPF_OBJ) || attr->bpf_fd != 0 || +attr->file_flags & ~BPF_OBJ_FLAG_MASK) +return -EINVAL; + +return bpf_obj_get_user(u64_to_user_ptr(attr->pathname), +attr->file_flags); +} +... + +예를 들어 tc와 같은 도구는 진입 및 이탈에서 map를 공유하기 위해 이러한 구조를 사용합니다. +BPF 관련 파일 시스템은 싱글톤 패턴이 아니며 다중 마운트 인스턴스, 하드 및 소프트 링크 등을 지원합니다. + +* Tail 콜 + +BPF와 함께 사용할 수있는 또 다른 개념을 tail 콜이라고합니다. +Tail 콜은 하나의 BPF 프로그램이 이전 프로그램으로 돌아 가지 않고 다른 프로그램을 콜 할수 있게 해주는 +메커니즘으로 보여질수 있습니다. 이러한 콜은 함수 콜과 달리 최소한의 오버 헤드를 가지며 동일한 +스택 프레임을 재사용하여 긴 점프로 구현됩니다. 이러한 프로그램은 서로 관계없이 확인되므로 CPU 마다 맵을 +스크래치 버퍼로 전송하거나 tc 프로그램의 경우 cb[] 영역과 같은 skb 필드를 사용해야합니다. +동일한 유형의 프로그램만 tail 콜 할 수 있으며, 또한 JIT 컴파일과 관련하여 일치해야하므로 +JIT 컴파일되거나 해석 된 프로그램 만 콜 할 수 있지만 함께 혼재되서 구동 할 수는 없습니다. +tail 콜을 수행하는 데는 두 가지 구성 요소가 필요하며: 첫 번째 요소은 사용자 공간에서 +키/값으로 덧붙일수 있는 프로그램 배열(BPF_MAP_TYPE_PROG_ARRAY)이라는 특수한 맵을 설정해야 하며, +여기서 값은 tail 콜 된 BPF 프로그램이라는 파일 디스크립터 이며, 두 번째 요소는 컨텍스트 +다시 말해 프로그램 배열에 대한 참조 및 조회 키가 전달되는 bpf_tail_call() helper입니다. +(역자 주: https://lwn.net/Articles/645169/ "bpf: introduce +bpf_tail_call() helper" 참조 부탁 드립니다. +또한 tail 콜 에 대한 예제는 +https://github.com/torvalds/linux/blob/master/samples/bpf/sockex3_kern.c 이며 +BPF_MAP_TYPE_PROG_ARRAY의 BPF map을 제공 한 후, bpf_tail_call () helper를 사용하면 +구성이 가능합니다. http://yunazuno.hatenablog.com/entry/2017/05/09/102154 +참조 하였습니다.) + +그런 다음 커널은 이 helper 콜을 특수화 된 BPF 명령어로 직접 인라인 합니다. +이러한 프로그램 배열은 현재 사용자 공간 측면에서 쓰기 전용입니다. +커널은 전달 된 파일 디스크립터에서 관련 BPF 프로그램을 찾고 지정된 map 슬롯에서 프로그램 포인터를 +원자적으로 대체합니다. 제공된 키에서 map 항목을 찾지 못한다면, 커널은 "fall-through" 및 +bpf_taill_call()다음에 오는 명령으로 이전 프로그램의 실행을 계속합니다. taill 콜은 강력한 유틸리티 이며, +예를 들어 파싱 네트워크 헤더는 taill 콜을 통해 구조화 될 수 있습니다. +런타임 중에는 기능을 원자적으로 추가하거나 대체 할 수 있으므로 BPF 프로그램의 실행 동작이 변경됩니다. +(역자 주 : https://lwn.net/Articles/741773/ ) + +#BPF 호출에서 BPF 호출 +BPF helper 콜과 BPF tail 콜 외에도 BPF 핵심 구조에 추가 된 최신 기능은 BPF 호출에서 BPF 호출 입니다. +이 기능이 커널에 도입되기 전에, 일반적인 BPF C 프로그램은 다음과 같은 재사용 가능한 코드를 선언 해야 했으며, +예를 들어, 헤더에 always_inline으로 존재하며 LLVM이 컴파일 하고 BPF 객체 모든 함수들이 인라인이 되며, +그리하여 생성된 오프젝트 파일에 여러번 복제되어 인위적으로 코드 크기가 늘어나게됩니다. + +#include + +#ifndef __section +# define __section(NAME) \ +__attribute__((section(NAME), used)) +#endif + +#ifndef __inline +# define __inline \ +inline __attribute__((always_inline)) +#endif + +static __inline int foo(void) +{ +return XDP_DROP; +} + +__section("prog") +int xdp_drop(struct xdp_md *ctx) +{ +return foo(); +} + +char __license[] __section("license") = "GPL"; + +이것이 필요했던 주된 이유는 BPF 프로그램 로더뿐만 아니라 verifier, 인터프리터 및 JIT에서 함수 콜 지원이 부족했기 때문입니다. +Linux 커널 4.16 및 LLVM 6.0부터이 제한이 해제 되었으며 BPF 프로그램은 더 이상 always_inline을 사용할 필요가 없습니다. +따라서 이전에 표시된 BPF 예제 코드는 다음과 같이 자연스럽게 다시 작성 될 수 있습니다: + +#include + +#ifndef __section +# define __section(NAME) \ +__attribute__((section(NAME), used)) +#endif + +static int foo(void) +{ +return XDP_DROP; +} + +__section("prog") +int xdp_drop(struct xdp_md *ctx) +{ +return foo(); +} + +char __license[] __section("license") = "GPL"; +x86_64 및 arm64와 같은 메인스트림 BPF JIT 컴파일러는 BPF 에서 BPF 콜을 지원합니다. +BPF에서 BPF 콜은 생성 된 BPF 코드 크기를 많이 줄여 CPU의 명령어 캐시에 더 우호적이기 때문에 중요한 성능 최적화입니다. +BPF helper 함수에서 알려진 콜 규칙은 BPF에서 BPF 콜에도 적용되며, 이 의미는 r1 ~ r5는 콜 수신자에게 인수를 전달하기위한 것이며 +결과는 r0에 리턴됩니다. r1 ~ r5는 스크래치 레지스터이며 r6 ~ r9는 일반적인 콜을 통해 유지됩니다. 각각 허용 된 콜 +프레임의 최대 중첩 콜 수는 8입니다. 콜 송신자는 콜 송신자의 포인터 (예를 들어서, 콜 송신자의 스택 프레임에 대한 포인터)를 콜 +수신자에게 전달할 수 있지만 역방향으로는 동작하지 않습니다. BPF에서 BPF 콜은 현재 BPF tail 콜과 호환되지 않으며, +후자는 현상태의 스택 설정을 그대로 재사용 할 것을 요구하기 때문에, 전자는 추가 스택 프레임을 추가하므로 tail 콜의 +예상된 레이아웃을 변경이됩니다. BPF JIT 컴파일러는 각 함수 본체에 대해 별도의 이미지를 내보내고 나중에 +최종 JIT 경로에서 이미지의 함수 콜 주소를 수정합니다. BPF 에 BPF 콜은 일반적인 BPF helper 콜로 처리 할 수 있다는 점에서 +JIT에 대한 최소한의 변경이 필요하다는 것이 입증되었습니다. + +#JIT +64 비트 x86_64, arm64, ppc64, s390x, mips64, sparc64 및 32 비트 arm 아키텍처는 모두 커널 내 eBPF +JIT 컴파일러와 함께 제공되며 모든 기능이 동일하며 다음을 통해 활성화 할 수 있습니다: + +# echo 1 > /proc/sys/net/core/bpf_jit_enable + +32 비트 mips, ppc 및 sparc 아키텍처에는 현재 cBPF JIT 컴파일러가 있습니다. +cBPF JIT와 BPF JIT 컴파일러가 없는 Linux 커널이 지원하는 나머지 아키텍처는 +모두 커널 내 인터프리터를 통해 eBPF 프로그램을 실행할 필요가 있습니다. 커널의 소스 트리에서 +eBPF JIT 지원은 HAVE_EBPF_JIT에 대한 grep을 통해 쉽게 확인할 수 있습니다: + +# git grep HAVE_EBPF_JIT arch/ +arch/arm/Kconfig: select HAVE_EBPF_JIT if !CPU_ENDIAN_BE32 +arch/arm64/Kconfig: select HAVE_EBPF_JIT +arch/powerpc/Kconfig: select HAVE_EBPF_JIT if PPC64 +arch/mips/Kconfig: select HAVE_EBPF_JIT if (64BIT && !CPU_MICROMIPS) +arch/s390/Kconfig: select HAVE_EBPF_JIT if PACK_STACK && HAVE_MARCH_Z196_FEATURES +arch/sparc/Kconfig: select HAVE_EBPF_JIT if SPARC64 +arch/x86/Kconfig: select HAVE_EBPF_JIT if X86_64 + +(역자주 : 4.17.0-rc7 기준으로 아래와 같습니다. +# git grep HAVE_EBPF_JIT arch/ +arch/arm/Kconfig: select HAVE_EBPF_JIT if !CPU_ENDIAN_BE32 +arch/arm64/Kconfig: select HAVE_EBPF_JIT +arch/mips/Kconfig: select HAVE_EBPF_JIT if (64BIT && !CPU_MICROMIPS) +arch/mips/Kconfig: depends on BPF_JIT && HAVE_EBPF_JIT +arch/powerpc/Kconfig: select HAVE_EBPF_JIT if PPC64 +arch/s390/Kconfig: select HAVE_EBPF_JIT if PACK_STACK && HAVE_MARCH_Z196_FEATURES +arch/sparc/Kconfig: select HAVE_EBPF_JIT if SPARC64 +arch/x86/Kconfig: select HAVE_EBPF_JIT +) + +JIT 컴파일러는 BPF 프로그램의 실행 속도를 인터프리터와 비교하여 명령 마다 비용을 크게 줄여주므로 +속도를 크게 높입니다. 명령어는 종종 기본 아키텍처의 기본 명령어로 1:1로 매핑 될 수 있습니다. +이렇게 하면 실행 가능 이미지 크기가 줄어들며, CPU에 우호적인 더 많은 명령어 캐시가 됩니다. +특히 x86과 같은 CISC 명령어 세트의 경우 JITs는 주어진 명령어에 대해 가능한 가장 +짧은 opcode를 변환하여 프로그램 변환에 필요한 총 크기를 줄이기 위해 최적화됩니다. + +#Hardening +BPF는 코드 잠재적 손상을 방지하기 위해 프로그램의 실행 동안 커널에서 읽기 전용으로 JIT 컴파일 된 이미지 ( +struct bpf_binary_header)뿐만 아니라 BPF 인터프리터 이미지(struct bpf_prog)를 잠급니다. +이러한 시점에서 일어난 corruption, 예를 들어, 일부 커널 버그로 인해 general protection fault가 발생하고 +따라서 corruption이 자동으로 일어나는 것을 허용하지 않고 커널을 crash 시킵니다. +이미지 메모리를 읽기 전용으로 설정하는 것을 지원하는 아키텍처는 다음을 통해 결정될 수 있습니다: +$ git grep ARCH_HAS_SET_MEMORY | grep select +arch/arm/Kconfig: select ARCH_HAS_SET_MEMORY +arch/arm64/Kconfig: select ARCH_HAS_SET_MEMORY +arch/s390/Kconfig: select ARCH_HAS_SET_MEMORY +arch/x86/Kconfig: select ARCH_HAS_SET_MEMORY + +CONFIG_ARCH_HAS_SET_MEMORY 옵션은 설정 가능 하지 않으며, 보호 기능이 항상 내장되어 있습니다. +다른 아키텍처들은 향후 동일 할 수 있습니다.x86_64 JIT 컴파일러의 경우, tail 콜을 사용하여, +간접 점프의 JiTing은 CONFIG_RETPOLINE이 설정 되었을 때, +가장 최신의 리눅스 배포판에서 retpoline 을 통해 실행됩니다. + +/proc/sys/net/core/bpf_jit_harden 의 값이 1로 설정된 경우 JIT 컴파일에 대한 추가적인 hardening 단계로 +권한이 없는 사용자들에게 적용됩니다. + +(역자 주: 추가적으로 옵션은 아래와 같은 설정이 가능합니다 ) + +bpf_jit_harden +-------------- +BPF JIT 컴파일러을 위한 hardening을 활성화 합니다. 현재 지원되는 것은 eBPF JIT 백엔드입니다. +hardening 기능을 사용하면 성능이 저하 되지만 JIT spraying를 완화 할 수 있습니다. + +Values : +0 - JIT hardening 비활성화 (기본값) +1 - 권한이 없는 사용자들에 대한 JIT hardening 활성화 +2 - 모든 사용자들에 대한 JIT hardening 활성화 + +이것은 시스템에서 동작하는 신뢰할 수없는 사용자에 대해서 (잠재적인)공격 지점을 줄임으로써 성능을 실제적으로 약간 저하 됩니다. +이러한 프로그램 실행의 축소는 여전히 인터프리터로 전환하는 것 보다 더 나은 성능을 나타냅니다. +현재 hardening 기능을 사용하면 원시 opcode를 직접적인 값으로 주입하는 JIT spraying 공격을 방지하기 위해 +JIT 컴파일시 BPF 프로그램의 모든 32 비트 및 64 비트 상수를 제공받지 못하게됩니다. +이러한 직접적인 값은 실행 가능한 커널 메모리에 있기 때문에 문제가 되며, 따라서 일부 커널 버그로 인해 발생할 수있는 점프는 +직접적인 값의 시작으로 이동 한 다음 기본 명령어로 실행합니다. + +JIT 상수 블라인드는 실제 명령어를 무작위로 지정하여 방지 하며, 이것은 직접적인 기반을 둔 소스 피연산자에서 +레지스터 기반을 둔 실제 피연산자로 값의 실제 로드를 두 단계로 나누어 명령을 다시 작성하는 방식으로 변환됩니다: + +1) 블라인드 된 직접 값 rnd ^ imm을 레지스터에 로드 하며, 2) 본래의 imm immediatie가 레지스터에 상주하고 실제 작업에 +사용될 수 있도록 rnd로 등록하는 xoring을 합니다. +이 예제는 로드 동작을 위해 제공 되었지만, 실제로 모든 일반 동작은 블라인드입니다. + +hardening이 비활성화 된 프로그램의 JITing 예제 : +# echo 0 > /proc/sys/net/core/bpf_jit_harden + +ffffffffa034f5e9 + : +[...] +39: mov $0xa8909090,%eax +3e: mov $0xa8909090,%eax +43: mov $0xa8ff3148,%eax +48: mov $0xa89081b4,%eax +4d: mov $0xa8900bb0,%eax +52: mov $0xa810e0c1,%eax +57: mov $0xa8908eb4,%eax +5c: mov $0xa89020b0,%eax +[...] + +같은 프로그램이 경우 hardening이 활성화 된 권한이 없는 사용자를 BPF를 통해 로드 될 때 상수가 블라인드가됩니다: +# echo 1 > /proc/sys/net/core/bpf_jit_harden + +ffffffffa034f1e5 + : +[...] +39: mov $0xe1192563,%r10d +3f: xor $0x4989b5f3,%r10d +46: mov %r10d,%eax +49: mov $0xb8296d93,%r10d +4f: xor $0x10b9fd03,%r10d +56: mov %r10d,%eax +59: mov $0x8c381146,%r10d +5f: xor $0x24c7200e,%r10d +66: mov %r10d,%eax +69: mov $0xeb2a830e,%r10d +6f: xor $0x43ba02ba,%r10d +76: mov %r10d,%eax +79: mov $0xd9730af,%r10d +7f: xor $0xa5073b1f,%r10d +86: mov %r10d,%eax +89: mov $0x9a45662b,%r10d +8f: xor $0x325586ea,%r10d +96: mov %r10d,%eax +[...] + +두 프로그램 모두 의미 상 동일 하며, 단지 두 번째 프로그램의 디스어셈블리에서 더 이상 원래의 직접 값이 보이지 않습니다. +동시에 hardening는 JIT 이미지 주소가 /proc/kallsyms에 더 이상 노출되지 않도록 권한있는 사용자에 대한 JIT +kallsyms 노출에 대해 비활성화합니다. + +(역자주 : 해당 옵션에 대해서 세부 적인 내용을 확인 하였습니다.) +bpf_jit_kallsyms +---------------- +BPF JIT 컴파일러가 활성화 되면, 컴파일된 이미지는 커널에 알려지지 않는 주소 이며, 이것은 추적이나 /proc/kallsyms에 +나타나지 않는 것을 의미합니다. 이 파라메터를 활성화 하면, 디버깅 및 추적에 사용 할수 있는 주소를 내보낼수 있습니다. +만약 bpf_jit_harden 파라메터가 활성화 되었다면, 이 기능은 비 활성화 됩니다. +Values : +0 - disable JIT kallsyms 내보내기 비 활성화 (기본값) +1 - 허가된 유저들만 JIT kallsyms 내보내기 활성화 + +또한 Linux 커널은 전체 BPF 인터프리터를 커널에서 제거하고 JIT 컴파일러를 영구적으로 활성화하는 +CONFIG_BPF_JIT_ALWAYS_ON 옵션을 제공합니다. 이것은 Spectre v2의 상황에서 VM 기반 설정에서 +사용될 때 게스트 커널이 공격이 증가 할때 호스트 커널의 BPF 인터프리터를 재사용하지 않기 위해 개발되었습니다. +컨테이너 기반 환경의 경우 CONFIG_BPF_JIT_ALWAYS_ON +구성 옵션은 선택 사항이지만, JIT가 활성화되어있는 경우 인터프리터는 커널의 복잡성을 줄이기 위해 컴파일에서 +제외 될 수 있습니다. 따라서 x86_64 및 arm64와 같은 메인 스트림 아키텍처의 경우 널리 사용되는 JITs를 +일반적으로 권장됩니다. + +마지막으로, 커널은 /proc/sys/kernel/unprivileged_bpf_disabled sysctl 설정를 통해 권한이없는 +사용자에게 bpf(2)시스템콜을 사용하지 못하게 하는 옵션을 제공합니다. 일회성 kill 스위치 이며, 한번 1로 설정이되면, +새로운 커널을 부팅 할때까지 다시 0으로 재 설정하는 옵션은 없습니다. +초기 네임스페이스에서 CAP_SYS_ADAMIN 권한이 설정된 프로세서만 설정하면, 그 시점 이후 bpf(2) +시스템콜을 사용 할수 있습니다. cilium은 이 설정에 대해서 1로 설정합니다. +# echo 1 > /proc/sys/kernel/unprivileged_bpf_disabled + +(역자 주 : https://lwn.net/Articles/660331/ Unprivileged bpf()를 참조 부탁드리며, +패치된 내용은 아래에 있습니다. https://git.kernel.org/pub/scm/linux/kernel +/git/stable/linux.git/commit/?h=v4.17.8&id=c1bf5fe03184f782f2a6827cf314ae58834865da) + +#Offloads + +BPF의 네트워킹 프로그램, 특히 tc와 XDP의 경우 NIC에서 직접 BPF 코드를 실행하기 위해 커널의 하드웨어에 +대한 오프로드 인터페이스가 있습니다. 현재 Netronome의 nfp 드라이버는 BPF 명령어를 NIC에 대해 +구현된 명령어 세트로 변환하여 JIT 컴파일러를 통해 BPF를 오프로드 할 수 있도록 지원합니다. 여기에는 NIC에 +대한 BPF 맵의 offloading 포함되므로 offload된 BPF 프로그램은 맵 조회, 업데이트 및 삭제를 수행 할 수 있습니다. +(역자 주 : 관심이 있으신 분은 "eBPF Offload Getting Started Guide - Netronome" 라는 키워드를 통해 +구글을 통해서 설정 및 간단한 예제가 포함된 문서를 찾으실 수 있습니다.) + +#툴체인 + +현재 사용자 공간에서의 도구, BPF 관련 자가 검사 방법 및 BPF 관련의 커널 설정 제어는 +이 섹션에서 논의됩니다. BPF 관련된 도구 및 구조는 여전히 빠르게 발전하고 있으므로, 사용 가능한 +모든 도구에 대한 전체 그림을 제공하지 못할 수도 있습니다. + +개발환경 +Fedora와 Ubuntu 모두에서 BPF 개발 환경을 설정하는 단계별 가이드는 아래에 나와 있습니다. +이것은 iproute2 빌드 및 설치는 물론 개발 커널의 빌드, 설치 및 테스트를 안내합니다. +iproute2 및 Linux 커널을 수동으로 빌드하는 단계는 일반적으로 주요 배포판이 이미 최신 커널을 기본적으로 제공하기 +때문에 필요하지 않지만 +리스크가 있는 가장 최신의 버전을 테스트하거나 BPF 패치에 기여하는 iproute2 및 리눅스 커널에 필요합니다. +마찬가지로, 디버깅 및 자가 검사를 위해 bpftool을 빌드하는 것은 선택 사항이지만 권장됩니다. + +페도라 +페도라 25 이상의 버젼에 적용됩니다: + +$ sudo dnf install -y git gcc ncurses-de음el elfutils-libelf-devel bc \ +openssl-devel libcap-devel clang llvm graphviz bison flex glibc-static + +노트 : 다른 페도라 파생 버젼을 사용하는 경우, dnf 명령어가 없는 경우 yum을 사용해 보세요. + +우분트 +우분투 17.04 이상의 버젼에 적용 됩니다: + +$ sudo apt-get install -y make gcc libssl-dev bc libelf-dev libcap-dev \ +clang gcc-multilib llvm libncurses5-dev git pkg-config libmnl bison flex \ +graphviz + +커널 컴파일 +리눅스 커널을 위한 새로운 BPF 기능의 개발은 net-next git 트리 안에서 이루어지며, 최신 BPF는 net +트리에서 fix됩니다.다음 명령은 git을 통해 net-next 트리의 커널 소스를 얻습니다: + +$ git clone git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next.git + +자식 커밋 기록에 대해 관심이 없다면 "--depth 1"은 자식 커밋 기록을 가장 최근의 커밋으로 제외함으로써 훨씬 +빠르게 트리를 복제합니다. + +net 트리에 관심이 있는 경우에는 다음 URL에서 복제 할수 있습니다: +$ git clone git://git.kernel.org/pub/scm/linux/kernel/git/davem/net.git + +인터넷에 리눅스 커널을 만드는 방법에 대한 수십 가지 튜토리얼이 있으며, 하나의 좋은 자료는 Kernel Newbies +웹 사이트 (https://kernelnewbies.org/KernelBuild) 이며, 위에서 언급 한 두 가지 자식 트리 중 +하나를 따라 리눅스 커널을 구성 합니다. + +생성 된 .config 파일에 BPF를 실행하기 위한 다음 CONFIG_ * 항목이 포함되어 있는지 확인하십시오. +이 항목은 Cilium에도 필요합니다. +CONFIG_CGROUP_BPF=y +CONFIG_BPF=y +CONFIG_BPF_SYSCALL=y +CONFIG_NET_SCH_INGRESS=m +CONFIG_NET_CLS_BPF=m +CONFIG_NET_CLS_ACT=y +CONFIG_BPF_JIT=y +CONFIG_LWTUNNEL_BPF=y +CONFIG_HAVE_EBPF_JIT=y +CONFIG_BPF_EVENTS=y +CONFIG_TEST_BPF=m + +일부 항목은 make menuconfig를 통해 조정할 수 없습니다. 예를 들어 CONFIG_HAVE_EBPF_JIT는 지정된 아키텍처에 +eBPF JIT가 있는 경우 자동으로 선택됩니다. 이 경우 CONFIG_HAVE_EBPF_JIT는 선택 사항이지만 적극 권장됩니다. +eBPF JIT 컴파일러가 없는 아키텍처는 커널 내 인터프리터로 대체하여 BPF 명령어를 실행되며 효율적이지 않습니다. + +설정확인 +새로 컴파일 된 커널로 부팅 한 후, BPF 기능을 테스트하기 위해 BPF 셀프 테스트 그룹으로 이동합니다 (현재 작업 디렉토리가 +복제 된 git 트리의 루트를 가리킵니다). + +$ cd tools/testing/selftests/bpf/ +$ make +$ sudo ./test_verifier + +verifier 프로그램 테스트는 수행중인 모든 현재 검사를 출력합니다: +Summary: 847 PASSED, 0 SKIPPED, 0 FAILED + +노트: +커널 릴리즈 4.16 이상 버젼에서 BPF 셀프 테스트는 더 이상 인라인 될 필요가 없는 BPF 함수 호출로 인해 LLVM 6.0 이상 +버젼에 의존됩니다.자세한 정보는 BPF에서 BPF 호출에 대한 항목을 참조하거나 커널 패치 (https://lwn.net/Articles/741773/) +에서 커버 레터 메일을 참조하십시오. 이 새로운 기능을 사용하지 않으면 모든 BPF 프로그램이 LLVM 6.0 이상에 의존적이지 않습니다. +배포판에서 LLVM 6.0 이상을 제공하지 않으면 LLVM 섹션의 지침에 따라 컴파일 할 수 있습니다. + +모든 BPF 셀프 테스트를 실행하려면 다음 명령이 필요합니다: +$ sudo make run_tests + +iproute2 컴파일 + +net(fixes 전용)과 net-next(새로운 기능) 커널 트리와 비슷하게 iproute2 git 트리에는 master와 net-next라는 +두 가지 분기가 있습니다. 마스터 분기는 net 트리를 기반으로 하며 net-next 분기는 net-next 커널 트리를 기반으로 합니다. +헤더 파일의 변경 사항을 iproute2 트리에서 동기화 할 수 있도록 하려면이 작업이 필요합니다. +iproute2 마스터 분기를 복제 하려면 다음 명령을 사용할 수 있습니다: +$ git clone git://git.kernel.org/pub/scm/linux/kernel/git/iproute2/iproute2.git + +마찬가지로 iproute2의 언급 된 net-next 분기에 복제 하려면 다음을 실행하십시오. +$ git clone -b net-next git://git.kernel.org/pub/scm/linux/kernel/git/iproute2/iproute2.git + +그런 다음 빌드 및 설치를 진행하십시오: + +$ cd iproute2/ +$ ./configure --prefix=/usr +TC schedulers +ATM no + +libc has setns: yes +SELinux support: yes +ELF support: yes +libmnl support: no +Berkeley DB: no + +docs: latex: no +WARNING: no docs can be built from LaTeX files +sgml2html: no +WARNING: no HTML docs can be built from SGML +$ make +[...] +$ sudo make install + +iproute2가 LLVM의 BPF 백엔드에서 ELF 파일을 처리 할 수 있도록 configure 스크립트에 +"ELF support : yes"가 표시거 되는지 확인하십시오.libelf는 이전에 Fedora와 Ubuntu의 경우 의존성 +설치 지침에 나열되었습니다. + +bpftool 컴파일 +bpftool은 BPF 프로그램 및 맵의 디버깅 및 내부 검사에 필수적인 도구입니다. 커널 트리의 일부이며 +tools/bpf/bpftool/ 아래에 위치해 있습니다.앞에서 설명한 것처럼 net 또는 net-next 커널 트리를 복제 +되었는지 확인하십시오. + +bpftool을 빌드하고 설치하려면 다음 단계가 필요합니다: +$ cd /tools/bpf/bpftool/ +$ make +Auto-detecting system features: +... libbfd: [ on ] +... disassembler-four-args: [ OFF ] + +CC xlated_dumper.o +CC prog.o +CC common.o +CC cgroup.o +CC main.o +CC json_writer.o +CC cfg.o +CC map.o +CC jit_disasm.o +CC disasm.o +make[1]: Entering directory '/home/foo/trees/net/tools/lib/bpf' + +Auto-detecting system features: +... libelf: [ on ] +... bpf: [ on ] + +CC libbpf.o +CC bpf.o +CC nlattr.o +LD libbpf-in.o +LINK libbpf.a +make[1]: Leaving directory '/home/foo/trees/bpf/tools/lib/bpf' +LINK bpftool +$ sudo make install + +LLVM + +LLVM은 현재 BPF 백엔드를 제공하는 유일한 컴파일러 모음입니다. 현재 gcc는 BPF를 지원하지 않습니다. +BPF 백엔드는 LLVM의 3.7 릴리즈로 병합되었습니다. +주요 배포판은 LLVM을 패키징 할 때 기본적으로 BPF 백엔드를 사용 가능하게하므로 +가장 최근의 배포판에서 clang 및 llvm을 설치하면 C를 BPF 오브젝트 파일로 컴파일 하기에 충분합니다. +일반적인 워크 플로우는 BPF 프로그램이 C로 작성되고 LLVM에 의해 object/ELF 파일로 컴파일되며 +사용자 공간 BPF ELF 로더(예 : iproute2 또는 기타)로 구문 분석되고 커널 BPF 시스템 호출을 통해 커널에 푸시됩니다. +커널은 BPF 명령어 와 JIT를 검증하고, 프로그램을 위한 새로운 파일 디스크립터를 리턴하고, 서브 시스템 +(예 : 네트워킹)에 소속 할 수 있습니다. +만약 지원된다면, 서브 시스템은 BPF 프로그램을 하드웨어 (예컨대, NIC)로 더 오프로드 할 수 있습니다. + +LLVM의 경우 BPF 대상 지원은 다음을 통해 확인할 수 있습니다: +$ llc --version +LLVM (http://llvm.org/): +LLVM version 3.8.1 +Optimized build. +Default target: x86_64-unknown-linux-gnu +Host CPU: skylake + +Registered Targets: +[...] +bpf - BPF (host endian) +bpfeb - BPF (big endian) +bpfel - BPF (little endian) +[...] + +기본적으로 bpf 타겟은 컴파일되는 CPU의 엔디안을 사용하며, 즉, CPU의 엔디안이 리틀 엔디안인 경우 프로그램은 +리틀 엔디안 형식으로 표시되며 CPU의 엔디안이 빅 엔디안 인 경우 프로그램 빅 엔디안 형식으로 표현됩니다. +이것은 BPF의 런타임 동작과도 일치 하며, BPF는 일반적인 형식이며, 어떤 형식의 아키텍처에도 불리하지 않도록하기 위해 +실행되는 CPU의 엔디안을 사용합니다. + +크로스 컴파일의 경우, 두개의 타겟 bpfeb 와 bpfel이 도입 덕분에, BPF 프로그램은 x86에서의 리틀 엔디안에서 실행되는 노드에서 +컴파일 할수 있으며,arm에서의 빅 엔디안 형식의 노드에서 실행 할 수 있습니다. 프런트 엔드(clang)는 타겟 엔디안 에서도 실행 해야 합니다. +타겟으로 bpf사용하는 것은 엔디안이 혼재되지 않는 상황에서 선호 되는 방법입니다. + +예를 들어, x86_64에서의 컴파일은 little endian이기 때문에 타겟 bpf 및 bpfel에 대해 동일한 결과물 로써, 컴파일을 +트리거하는 스크립트에서는 endian을 인식 할 필요가 없습니다. +최소한의 독립실행형 XDP drop 프로그램은 다음 예제 (xdp-example.c)와 같습니다. + +#include + +#ifndef __section +# define __section(NAME) \ +__attribute__((section(NAME), used)) +#endif + +__section("prog") +int xdp_drop(struct xdp_md *ctx) +{ +return XDP_DROP; +} + +char __license[] __section("license") = "GPL"; + +그런 다음 다음과 같이 컴파일하고 커널에 로드 할 수 있습니다. +$ clang -O2 -Wall -target bpf -c xdp-example.c -o xdp-example.o +# ip link set dev em1 xdp obj xdp-example.o + +NOTE: 위와 같이 네트워크 장치에 XDP BPF 프로그램을 연결하려면 XDP를 지원하는 장치가있는 Linux 4.11 혹은 Linux 4.12 이 요구됩니다. +생성 된 오브젝트 파일의 경우 LLVM (> = 3.9)은 공식적인 BPF 시스템 값, 즉 EM_BPF (10진수:247/16진수:0xf7)를 사용합니다. +이 예제에서 프로그램은 x86_64에서 bpf 타겟으로 컴파일 되었으므로 LSB(MSB와 반대)가 endian와 관련하여 표시됩니다. + +$ file xdp-example.o +xdp-example.o: ELF 64-bit LSB relocatable, *unknown arch 0xf7* version 1 (SYSV), not stripped + +readelf -a xdp-example.o 은 ELF 파일에 대한 추가 정보를 덤프 하며, 생성 된 섹션 헤더, 재배치 항목 및 +기호 테이블을 자가검사 할 때 유용 할 수 있습니다. +Clang 및 LLVM을 처음부터 컴파일 해야하는 경우에는 다음 명령을 사용할 수 있습니다: + +$ git clone http://llvm.org/git/llvm.git +$ cd llvm/tools +$ git clone --depth 1 http://llvm.org/git/clang.git +$ cd ..; mkdir build; cd build +$ cmake .. -DLLVM_TARGETS_TO_BUILD="BPF;X86" -DBUILD_SHARED_LIBS=OFF -DCMAKE_BUILD_TYPE=Release -DLLVM_BUILD_RUNTIME=OFF +$ make -j $(getconf _NPROCESSORS_ONLN) + +$ ./bin/llc --version +LLVM (http://llvm.org/): +LLVM version x.y.zsvn +Optimized build. +Default target: x86_64-unknown-linux-gnu +Host CPU: skylake + +Registered Targets: +bpf - BPF (host endian) +bpfeb - BPF (big endian) +bpfel - BPF (little endian) +x86 - 32-bit X86: Pentium-Pro and above +x86-64 - 64-bit X86: EM64T and AMD64 + +$ export PATH=$PWD/bin:$PATH # add to ~/.bashrc + +--version 정보에 Optimized build가 포함되어 있는지 확인해야 하며, 그렇지 않으면 디버깅 방식에서 LLVM을 사용하는 +프로그램의 컴파일 시간이 크게 늘어납니다 (예:10 배 이상). + +디버깅을 위해 clang에서는 다음과 같이 어셈블러 출력을 생성 할 수 있습니다. + +$ clang -O2 -S -Wall -target bpf -c xdp-example.c -o xdp-example.S +$ cat xdp-example.S +.text +.section prog,"ax",@progbits +.globl xdp_drop +.p2align 3 +xdp_drop: # @xdp_drop +# BB#0: +r0 = 1 +exit + +.section license,"aw",@progbits +.globl __license # @__license +__license: +.asciz "GPL" + +또한 최신 LLVM 버전 (>= 4.0)은 디버그 정보를 dwarf 형식으로 객체 파일에 저장할 수 있습니다. +이것은 컴파일 -g 파라메터를 추가하여 일반적인 워크플로우를 통해 수행 할 수 있습니다. + +$ clang -O2 -g -Wall -target bpf -c xdp-example.c -o xdp-example.o +$ llvm-objdump -S -no-show-raw-insn xdp-example.o + +xdp-example.o: file format ELF64-BPF + +Disassembly of section prog: +xdp_drop: +; { +0: r0 = 1 +; return XDP_DROP; +1: exit + +llvm-objdump 도구는 컴파일러에서 사용 된 원본 C 코드로 어셈블러 출력에 주석을 추가 할 수 있습니다. +이 경우 간단한 예제는 C 코드를 많이 포함하지 않지만 0 : 및 1: 로 표시된 줄 번호는 커널의 verifier 로그에 직접 해당합니다. + +즉, BPF 프로그램이 verifier에 의해 거부되는 경우 llvm-objdump는 명령을 원래의 +C코드와 연관시키는 데 도움을 줄 수 있으며 이는 분석하는데 있어서 매우 유용합니다. + +# ip link set dev em1 xdp obj xdp-example.o verb + +Prog section 'prog' loaded (5)! +- Type: 6 +- Instructions: 2 (0 over limit) +- License: GPL + +Verifier analysis: + +0: (b7) r0 = 1 +1: (95) exit +processed 2 insns + +verifier 분석에서 볼 수 있듯이, llvm-objdump 출력 값은 커널과 동일한 BPF 어셈블러 코드를 덤프 합니다. +-no-show-raw-insn 옵션을 제외하면 raw struct bpf_insn을 어셈블리 앞에 16 지수로 덤프 합니다: + +$ llvm-objdump -S xdp-example.o + +xdp-example.o: file format ELF64-BPF + +Disassembly of section prog: +xdp_drop: +; { +0: b7 00 00 00 01 00 00 00 r0 = 1 +; return foo(); +1: 95 00 00 00 00 00 00 00 exit + + +LLVM IR(중간 표현) 디버깅인 경우, BPF 컴파일 프로세스를 두 단계로 나뉠 수 있으며, 나중에 llc에 전달 될수 있는 +바이너리 LLVM IR(중간 표현) 중간 파일인 xdp-example.bc 생성 합니다. +$ clang -O2 -Wall -target bpf -emit-llvm -c xdp-example.c -o xdp-example.bc +$ llc xdp-example.bc -march=bpf -filetype=obj -o xdp-example.o + +생성 된 LLVM IR(중간 표현)은 또한 다음을 통해 사람이 읽을 수있는 형식으로 덤프 될 수 있습니다: +$ clang -O2 -Wall -emit-llvm -S -c xdp-example.c -o - + +LLVM의 BPF 백 엔드는 현재 BPF의 32비트 하위 레지스터를 사용하는 코드 생성을 지원하지 않습니다. +현재 BPF 용 인라인 어셈블리도 지원되지 않습니다. +또한 BPF 어셈블리 파서가 없기 때문에 현재 BPF 어셈블리 +(예를 들어, llvm-mc xdp-example.S -arch bpf -filetype = obj -o xdp-example.o) +에서 컴파일이 지원되지 않습니다. + +LLVM은 기본적으로 코드를 생성하기 위해 BPF 기본 명령어 세트를 사용하여 생성 된 오브젝트 파일이 +long-term stable kernel(예 : 4.9 이상)과 같은 이전의 커널로 로드 될 수 있는지 확인합니다. +그러나 LLVM에는 BPF 명령어 세트의 다른 버전을 선택하기 위해 BPF 백엔드에 대한 -mcpu 선택 옵션이 있어서, +보다 효율적이고 작은 코드를 생성하기 위해 BPF 기본 명령어 세트의 맨 위에 있는 명령어 세트 확장을 사용합니다. + +사용 가능한 -mcpu 옵션은 다음을 통해 쿼리 할 수 있습니다: +$ llc -march bpf -mcpu=help +Available CPUs for this target: + +generic - Select the generic processor. +probe - Select the probe processor. +v1 - Select the v1 processor. +v2 - Select the v2 processor. +[...] + +일반 프로세서는 기본 프로세서이며 BPF의 기본 명령어 세트 v1 입니다. +옵션 v1과 v2는 일반적으로 BPF 프로그램이 크로스 컴파일 되는 환경에서 유용하며, 프로그램이 로드 되는 +대상 호스트와 컴파일 된 대상 호스트가 다릅니다(따라서 사용 가능한 BPF 커널 기능도 다를 수 있음). + +Cilium에서 내부적으로 사용하는 -mcpu 권장 옵션은 -mcpu=probe 입니다! +여기서 LLVM BPF 백엔드는 BPF 명령어 세트 확장의 가용성을 커널에 쿼리하며, 커널에서 사용 가능한 것으로 확인되면 +LLVM은 필요할 때마다 BPF 프로그램을 컴파일 하기 위해 옵션을 사용합니다. + +llc의 -mcpu = probe를 사용한 전체 명령 행 예제: +$ clang -O2 -Wall -target bpf -emit-llvm -c xdp-example.c -o xdp-example.bc +$ llc xdp-example.bc -march=bpf -mcpu=probe -filetype=obj -o xdp-example.o + +일반적으로 LLVM IR 생성은 아키텍처 독립적입니다. +그러나 clang -target bpf를 -target bpf를 제외하고 사용하면 몇 가지 차이점이 있으며, +기본 아키텍처에 따라 x86_64, arm64 또는 기타 등의 clang의 기본 대상을 사용할 수 있습니다. + +커널의 Documentation/bpf/bpf_devel_QA.txt에서 인용: + +그러나 clang -target bpf를 사용하는 경우 와 -target bpf 를 사용하지 않을 때 +clang의 기본 대상을 사용하는 경우에는 기본 아키텍처에 따라 x86_64, arm64 또는 기타와 같은 몇 가지 차이점이 있습니다. + +* BPF 프로그램은 파일 범위 인라인 어셈블리 코드가 있는 헤더 파일을 재귀적으로 포함 할 수 있습니다. +기본 타켓은 잘 처리 할 수 있지만, bpf 백엔드 어셈블러가 이러한 어셈블리 코드를 이해하지 못하면 +대부분의 경우에 bpf 타겟이 실패 할 수 있습니다. + +* -g 옵션 없이 컴파일하면 예를 들어 .eh_frame 및 .rela.eh_frame 과 같은 추가 elf 섹션이 +기본 타겟을 가지는 오프젝트 파일에 존재 할 수 있으며, bpf 타겟과는 다릅니다. + +* 기본 타겟은 C언어 switch 문을 switch table lookup 및 Jump 동작으로 변환 할 수 있습니다. +switch table이 전역 읽기 전용 섹션에 있으므로 bpf 프로그램이 로드 되지 않습니다. +bpf 타겟은 switch table 최적화를 지원하지 않습니다. clang 옵션 -fno-jump-tables를 사용하여 switch table 생성을 비 활성화 할 수 있습니다. + +* clang -target bpf 의 경우, 포인터 또는 long/unsigned long 타입은 +기본 clang 바이너리 또는 기본 타겟(또는 커널)이 32 비트인지 여부에 관계없이 항상 64 비트의 크기를 가집니다. +그러나 네이티브 clang 타겟이 사용될 때 기본 아키텍처의 규칙에 따라 이러한 유형을 컴파일 하며, +32 비트 아키텍처의 경우 포인터 또는 long/unsigned long 타입을 의미 하며, +BPF 컨텍스트 구조는 32 비트의 너비를 가지며, BPF LLVM 백엔드는 여전히 64 비트로 작동합니다. + +* 네이티브 타겟은 CPU 레지스터 또는 CPU의 레지스터 너비가 중요한 다른 커널 구조를 매핑하는 커널의 구조체 +pt_regs 이동 하는 경우 추적에 주로 필요합니다. +네트워킹과 같은 다른 모든 경우에는 clang -target bpf를 사용하는 것이 좋습니다. + +BPF를 위한 C 프로그램을 작성 할때, C를 사용하여 일반적인 어플리케이션 개발과 비교 되며, 알고 있어야 할 몇 가지 +저지르기 쉬운 실수가 있습니다.다음 항목에서는 BPF 모델의 몇 가지 차이점에 대해 설명합니다: + +1. 모든 것이 인라인 될 필요가 있으며, 함수 콜 (구 LLVM 버전에서)이나 공유 라이브러리 호출이 없습니다. +공유 라이브러리 등은 BPF와 함께 사용할 수 없습니다. +그러나 BPF 프로그램에서 사용되는 공통 라이브러리 코드는 헤더 파일에 배치되고 주 프로그램에 포함될 수 있습니다. +예를 들어, Cilium은 이것을 많이 사용합니다 (bpf/lib/ 참조). 그러나 이것은 여전히 헤더 파일을 포함 할 수 있으며, +예를 들어 커널이나 다른 라이브러리에서 가져 와서 정적 인라인 함수 나 매크로/정의를 재사용 할 수 있습니다. +BPF에서 BPF 함수 콜이 지원되는 최신 커널 (4.16+)과 LLVM (6.0+)이 사용하지 않는 경우에 +LLVM은 전체 코드를 컴파일하고 주어진 프로그램 섹션에 대한 BPF 명령어의 flat sequence로 인라인 해야합니다. + +이 경우 아래에 표시된 것처럼 모든 라이브러리 함수에 대해 __inline과 같은 주석을 사용하는 것이 가장 좋습니다. +always_inline을 사용하는 것이 추천하며, 컴파일러는 여전히 inline 으로 주석 처리 된 큰 함수의 uninline 할 수 있기 때문입니다. +후자가 발생하면 LLVM은 ELF 파일로 재배치 엔트리를 생성하며, 이 엔트리는 iproute2와 같은 BPF ELF 로더가 해석 할 수 없으며 +따라서 BPF 맵만이 로더가 처리 할 수있는 유효한 재배치 엔트리이기 때문에 오류가 발생합니다. + +#include + +#ifndef __section +# define __section(NAME) \ +__attribute__((section(NAME), used)) +#endif + +#ifndef __inline +# define __inline \ +inline __attribute__((always_inline)) +#endif + +static __inline int foo(void) +{ +return XDP_DROP; +} + +__section("prog") +int xdp_drop(struct xdp_md *ctx) +{ +return foo(); +} + +char __license[] __section("license") = "GPL"; + +2.여러 프로그램은 서로 다른 섹션의 단일 C 파일 내에 상주 할 수 있습니다. +BPF 용 C 프로그램은 섹션 주석을 많이 사용합니다. C 파일은 일반적으로 3 개 이상의 섹션으로 +구성됩니다.BPF ELF 로더는 이 이름들을 사용하여 bpf 시스템 콜을 통해 프로그램 과 맵을 +로드하기 위해 관련 정보를 추출하고 준비합니다. +예를 들어, iproute2는 맵과 라이센스를 기본 섹션 이름으로 사용하여 맵 작성에 필요한 +메타 데이터와 BPF 프로그램에 대한 라이센스를 각각 찾습니다.프로그램 생성시 마지막에 +커널에 푸시가 되며, 프로그램이 GPL호환 라이센스를 보유한 경우에만 GPL로 노출되는 일부 Helper 기능 +들이 활성화 되며,예를 들어 bpf_ktime_get_ns(), bfp_probe_read() 및 기타가 해당이 됩니다. +나머지 섹션 이름은 BPF 프로그램 코드이며, 예를 들어 아래 코드는 두 개의 프로그램 섹션 인 +ingress 와 egress 를 포함하도록 수정되었습니다.toy 예제 코드는 둘 다 map 와 account_data () 함수와 +같은 일반적인 정적 인라인 helper를 공유 할 수 있음을 보여 줍니다. +xdp-example.c 예제는 tc로 로드되고 netdevice의 ingress 및 +egress hook에 연결될 수 있는 tc-example.c 예제로 수정되었습니다. +전송 된 바이트를 두 개의 map 슬롯을 가지고 있으며, 하나는 ingress hook에 있는 트래픽 용이고 다른 하나는 +egress hook에 있는 acc_map이라는 맵에 기록 됩니다. + +#include +#include +#include +#include + +#ifndef __section +# define __section(NAME) \ +__attribute__((section(NAME), used)) +#endif + +#ifndef __inline +# define __inline \ +inline __attribute__((always_inline)) +#endif + +#ifndef lock_xadd +# define lock_xadd(ptr, val) \ +((void)__sync_fetch_and_add(ptr, val)) +#endif + +#ifndef BPF_FUNC +# define BPF_FUNC(NAME, ...) \ +(*NAME)(__VA_ARGS__) = (void *)BPF_FUNC_##NAME +#endif + +static void *BPF_FUNC(map_lookup_elem, void *map, const void *key); + +struct bpf_elf_map acc_map __section("maps") = { +.type = BPF_MAP_TYPE_ARRAY, +.size_key = sizeof(uint32_t), +.size_value = sizeof(uint32_t), +.pinning = PIN_GLOBAL_NS, +.max_elem = 2, +}; + +static __inline int account_data(struct __sk_buff *skb, uint32_t dir) +{ +uint32_t *bytes; + +bytes = map_lookup_elem(&acc_map, &dir); +if (bytes) +lock_xadd(bytes, skb->len); + +return TC_ACT_OK; +} + +__section("ingress") +int tc_ingress(struct __sk_buff *skb) +{ +return account_data(skb, 0); +} + +__section("egress") +int tc_egress(struct __sk_buff *skb) +{ +return account_data(skb, 1); +} + +char __license[] __section("license") = "GPL"; + +이 예제는 또한 프로그램을 개발할 때 알아두면 유용한 몇 가지 다른 것들을 보여줍니다. +이 코드에는 커널 헤더, 표준 C 헤더 및 struct bpf_elf_map의 정의가 포함 된 iproute2 특정 헤더가 포함됩니다. +iproute2는 일반적인 BPF ELF 로더를 가지고 있으며 그리고 그러한 구조체 bpf_elf_map의 정의는 XDP 및 tc +타입 프로그램에 대해 매우 동일합니다.struct bpf_elf_map 항목은 프로그램에서 map을 정의하며 +두 BPF 프로그램에서 사용되는 map을 생성하는 데 필요한 모든 관련 정보 (예를 들어 키/값 크기 등)를 포함합니다. +구조체는 로더가 찾을 수 있도록 map 섹션에 배치해야 합니다. +다른 변수 이름을 가진이 타입의 map 선언이 여러 개있을 수 있지만, 모두 __section( "maps")으로 주석을 추가해야 합니다. + +구조체 bpf_elf_map은 iproute2에만 적용됩니다. +다른 BPF ELF 로더는 다른 형식을 가질 수 있으며, 예를 들어, perf에 의해 주로 사용되는 커널 소스 트리의 +libbpf는 다른 사양을 갖습니다. iproute2는 struct bpf_elf_map에 대한 하위 호환성을 보장합니다. +Cilium은 iproute2 모델을 따릅니다. 이 예제는 또한 BPF helper 함수가 C 코드로 매핑되고 사용되는 방법을 보여줍니다. + +여기에서 map_lookup_elem() 함수는 uapi/linux/bpf.h 에서 helper로 표시되는 +BPF_FUNC_map_lookup_elem 열거 형 값에 매핑하여 정의합니다.프로그램이 나중에 커널에 로드 될 때, +verifier는 전달 된 인수가 예상되는 유형인지 확인하고 helper 호출을 실제 함수 호출로 재 지정합니다. +또한 map_lookup_elem() 함수는 map를 BPF helper 함수에 전달하는 방법을 보여줍니다. +여기서 maps 섹션의 &acc_map은 map_lookup_elem() 함수의 첫 번째 인수로 전달됩니다. +정의 된 배열 map이 전역 이므로, 어카운팅은 lock_xadd()로 정의되는 atomic operation을 사용해야합니다. + +LLVM은 __sync_fetch_and_add()를 내장 함수로 매핑하여 word 크기에 대해 +BPF atomic add 명령어 즉 BPF_STX | BPF_XADD | BPF_W를 매핑합니다. +마지막으로, bpf_elf_map 구조체는 map이 PIN_GLOBAL_NS로 고정 되도록 나타냅니다. +즉, tc는 map을 BPF pseudo file system 에 노드 로 고정 합니다. +기본적으로 주어진 예제에서는 /sys/fs/bpf/tc/globalals/acc_map에 고정됩니다. +PIN_GLOBAL_NS로 인해 map은 /sys/fs/bpf/tc/globals/에 위치 하게 됩니다. +전역은 개체 파일에 걸쳐있는 전역 네임 스페이스 역할을합니다. 예제에서 PIN_OBJECT_NS를 사용하면 +tc는 오브젝트 파일에 대한 로컬 디렉토리를 작성합니다. + +예를 들어, BPF 코드가있는 다른 C 파일은 위의 PIN_GLOBAL_NS 고정과 동일한 acc_map +정의를 가질 수 있습니다.이 경우 map은 다양한 오브젝트 파일에서 비롯된 BPF 프로그램 간에 +공유됩니다. PIN_NONE은 map가 BPF 파일 시스템에 노드로 배치 되지 않으며, 결과적으로 tc가 종료 된 후 +사용자 공간에서 액세스 할 수 없음을 의미합니다.또한 tc가 각 프로그램에 대해 두 개의 개별 맵 인스턴스를 생성 +한다는 것은 해당 이름으로 이전에 고정 된 맵을 검색 할 수 없기 때문입니다. 언급 된 경로의 acc_map 부분은 소스 +코드에 지정된 map의 이름입니다. + +따라서, ingress 프로그램을 로딩 할 때, tc는 BPF 파일 시스템에 그러한 맵이 존재하지 않으며 +새로운 맵을 생성한다는 것을 알게 될 것입니다.성공하면 map가 고정되어 egress 프로그램이 tc를 통해 로드 될 때, +해당 map이 BPF 파일 시스템에 이미 있음 을 알게되고 egress 프로그램에 재사용 하게 됩니다. +또한 로더는 맵의 속성 (키/값 크기 등)이 일치하는 동일한 이름의 map이 있는 경우에도 확인합니다. + +tc가 같은 map을 검색 할 수있는 것처럼, 타사 응용 프로그램은 bpf 시스템 콜에서 BPF_OBJ_GET 명령을 사용하여 +동일한 맵 인스턴스를 가리키는 새 파일 디스크립터를 생성 할 수 있으며,이 디스크립터는 맵 요소를 검색/갱신/삭제 +하는데 사용 될수 있습니다. + +다음과 같이 코드를 컴파일하고 iproute2를 통해 로드 할 수 있습니다: +$ clang -O2 -Wall -target bpf -c tc-example.c -o tc-example.o + +# tc qdisc add dev em1 clsact +# tc filter add dev em1 ingress bpf da obj tc-example.o sec ingress +# tc filter add dev em1 egress bpf da obj tc-example.o sec egress + +# tc filter show dev em1 ingress +filter protocol all pref 49152 bpf +filter protocol all pref 49152 bpf handle 0x1 tc-example.o:[ingress] direct-action id 1 tag c5f7825e5dac396f + +# tc filter show dev em1 egress +filter protocol all pref 49152 bpf +filter protocol all pref 49152 bpf handle 0x1 tc-example.o:[egress] direct-action id 2 tag b2fd5adc0f262714 + +# mount | grep bpf +sysfs on /sys/fs/bpf type sysfs (rw,nosuid,nodev,noexec,relatime,seclabel) +bpf on /sys/fs/bpf type bpf (rw,relatime,mode=0700) + +# tree /sys/fs/bpf/ +/sys/fs/bpf/ ++-- ip -> /sys/fs/bpf/tc/ ++-- tc +| +-- globals +| +-- acc_map ++-- xdp -> /sys/fs/bpf/tc/ + +4 directories, 1 file + +패킷이 em1 장치를 통과 하자마자 BPF map의 카운터가 증가합니다. + +3. 허용되는 전역 변수가 없습니다. +1 에서 언급 한 이유로 BPF는 일반적인 C 프로그램에서 자주 사용되는 전역 변수를 가질 수 없습니다. +그러나 프로그램에서 단순히 BPF_MAP_TYPE_PERCPU_ARRAY 타입의 BPF 맵을 임의의 값 크기의 +단일 슬롯과 함께 사용할 수 있다는 해결 방법이 있습니다.이것은 실행 중에 BPF 프로그램이 결코 커널에 의해 선점되지 +않도록 보장되므로 단일 맵 항목을 임시 데이터를 위한 스크래치 버퍼로 사용할 수 있으며, 예를 들어서, 스택 제한 범위를 +넘어서는 확장 입니다. 이것은 또한 선점과 관련하여 동일한 보장이 있기 때문에, tail call에 건너서 동작 합니다. +그렇지 않으면 여러 BPF 프로그램 실행에서 상태를 유지하기 위해 정상적인 BPF 맵을 사용할 수 있습니다. + +4. const 문자열이나 배열이 허용되지 않습니다. +BPF C 프로그램에서 const 문자열 이나 다른 배열을 정의하는 것은 섹션 1과 3에서 지적한 내용과 같이 +즉, 로더쪽으로 ABI의 일부가 아니기 때문에 로더가 거부하는 ELF 파일에 재배치 항목이 생성 되는 이유로 동작 하지 않습니다 +(로더는 이미 컴파일 된 BPF 순서를 다시 작성 해야하므로 이러한 항목을 수정할 수 없습니다). +앞으로 LLVM은 이러한 발생을 감지하고 사용자에게 오류를 일찍 알수 있습니다. + +trace_printk()과 같은 helper 함수는 다음과 같이 처리 할 수 있습니다: + +static void BPF_FUNC(trace_printk, const char *fmt, int fmt_size, ...); + +#ifndef printk +# define printk(fmt, ...) \ +({ \ +char ____fmt[] = fmt; \ +trace_printk(____fmt, sizeof(____fmt), ##__VA_ARGS__); \ +}) +#endif + +그런 다음 프로그램은 printk("skb len:%u\n", skb->len); 와 같이 자연스럽게 매크로를 사용할 수 있습니다. +그러면 출력이 추적 파이프에 기록됩니다. tc exec bpf dbg를 사용하여 거기에서 메시지를 검색 할 수 있습니다. +trace_printk() helper 함수의 사용에는 몇 가지 단점이 있으므로 프로덕션 용도로 권장되지 않습니다. +helper 함수가 호출 될 때마다 "skb len:%u\n"과 같은 상수 문자열을 BPF 스택에 로드 해야 하지만 +BPF helper 함수는 최대 5 개의 인수로 제한됩니다.이것은 dumping을 위해 전달 될 수 있는 추가 변수 +3 개만 남겨 둡니다. + +따라서 빠른 디버깅에 도움이 되지만 네트워킹 프로그램에서 skb_event_output() 또는 +xdp_event_output() helper 함수를 사용하는 것이 좋습니다. +BPF 프로그램의 사용자 지정 구조체를 선택적 패킷 샘플과 함께 perf 이벤트 링 버퍼로 전달할 수 있습니다. +예를 들어, Cilium의 모니터는 디버깅 프레임 워크, 네트워크 정책 위반에 대한 알림 등을 구현하기 +위해 이 helper 함수 를 사용합니다.이러한 helper 함수들은 잠금없는 메모리 매핑 된 CPU 당 성능 링 버퍼를 통해 +데이터를 전달 하므로 trace_printk()보다 훨씬 빠릅니다. + +5. memset()/memcpy()/memmove()/memcmp()에 LLVM 내장 함수를 사용합니다. +BPF 프로그램은 BPF helper 가 아닌 다른 함수 호출을 수행 할 수 없기 때문에 공용 라이브러리 코드를 인라인 함수로 구현해야합니다. +또한 LLVM은 프로그램이 일정한 크기(여기서는 n)로 사용할 수있는 내장 함수를 제공 라며, 이 함수는 항상 인라인 됩니다: + +#ifndef memset +# define memset(dest, chr, n) __builtin_memset((dest), (chr), (n)) +#endif + +#ifndef memcpy +# define memcpy(dest, src, n) __builtin_memcpy((dest), (src), (n)) +#endif + +#ifndef memmove +# define memmove(dest, src, n) __builtin_memmove((dest), (src), (n)) +#endif + +memcmp() 빌트인은 백엔드에서 LLVM 문제로 인해 인라인이 발생하지 않는 일부 corner case가 있으므로 문제가 해결 될 때까지는 +사용하지 않는 것이 좋습니다. + +6.사용할 수 있는 루프가 없습니다(미완성). +커널의 BPF verifier는 BPF 프로그램이 다른 제어 흐름 그래프 검증 외에 가능한 모든 프로그램 경로의 깊이 우선 검색을 수행하여 +루프를 포함하고 있지 않은지 확인합니다.목적은 프로그램이 항상 종료되도록 보장하는 것입니다. +#pragma unroll 지시문을 사용하여 상한 루프 경계에서 매우 제한된 루핑 형식을 사용할 수 있습니다. +BPF로 컴파일 된 예제 코드: +#pragma unroll +for (i = 0; i < IPV6_MAX_HEADERS; i++) { +switch (nh) { +case NEXTHDR_NONE: +return DROP_INVALID_EXTHDR; +case NEXTHDR_FRAGMENT: +return DROP_FRAG_NOSUPPORT; +case NEXTHDR_HOP: +case NEXTHDR_ROUTING: +case NEXTHDR_AUTH: +case NEXTHDR_DEST: +if (skb_load_bytes(skb, l3_off + len, &opthdr, sizeof(opthdr)) < 0) +return DROP_INVALID; + +nh = opthdr.nexthdr; +if (nh == NEXTHDR_AUTH) +len += ipv6_authlen(&opthdr); +else +len += ipv6_optlen(&opthdr); +break; +default: +*nexthdr = nh; +return len; +} +} +또 다른 가능성은 같은 프로그램에 다시 호출하고 로컬 스크래치 공간을 갖는 BPF_MAP_TYPE_PERCPU_ARRAY 맵을 +사용하여 tail call을 사용하는 것입니다.동적 인 반면,이 루핑 형식은 최대 32 회 반복으로 제한됩니다. 앞으로 BPF는 +네이티브이지만 제한된 형태의 구현 루프를 가질 수 있습니다. + +7. tail call로 프로그램 분할. +tail call은 하나의 BPF 프로그램에서 다른 BPF 프로그램으로 점프하여 런타임 중에 프로그램 동작을 +atomically으로 변경할 수있는 유연성을 제공합니다. 다음 프로그램을 선택하기 위해 tail call은 프로그램 배열 map +(BPF_MAP_TYPE_PROG_ARRAY)을 사용하고 map뿐만 아니라 다음 프로그램에게 인덱스를 +다음 프로그램으로 점프 되도록 인덱스를 전달 합니다. 점프가 수행 된 이후에는 이전 프로그램으로 리턴되지 않으며 주어진 +map 인덱스에 프로그램이 없는 경우 처음 수행한 프로그램에서 실행을 계속됩니다. 예를 들어, 이것은 파서의 다양한 단계를 +구현하는 데 사용할 수 있으며, 이러한 단계는 런타임 중에 새로운 구문 분석 기능으로 업데이트 될 수 있습니다. 또 다른 사용 +사례는 이벤트 알림 이며, 예를 들어, Cilium은 런타임 중에 패킷 드랍 알림을 선택 할 수 있으며, +skb_event_output() 함수 콜은 tail called 프로그램 안에 위치합니다. 따라서 일반적인 동작에서는 프로그램이 관련 +map 인덱스에 추가되지 않으면 fall-through 경로가 항상 실행 되며, 여기에서 프로그램은 메타 데이터를 준비하고 +사용자 공간에 있는 데몬에 이벤트 통지를 트리거 합니다. 프로그램 배열 map은 매우 유연하므로 각 map 인덱스에 있는 +프로그램에 대해 개별 액션을 구현할 수 있습니다. + +예를 들어서, XDP 혹은 tc에 연결된 최상위 프로그램은 프로그램 배열 맵의 인덱스 0에서 초기 +tail call을 수행하여, 트래픽 샘플링을 수행 한 다음 프로그램 배열 맵의 인덱스 1로 이동하며, +여기서 방화벽 정책을 적용하며, 프로그램 맵 배열 인덱스 2에서 패킷에 대해 drop 또는 추후 처리중 하나를 선택하며, +여기서 패킷이 mangled 되고, 인터페이스에서 다시 전동 됩니다. + +물론 프로그램 배열 맵의 점프는 임의적 일 수 있습니다. 최대 taill call 제한에 도달하면 커널은 결국 +fall-through path 를 실행합니다. tail call 을 사용하는 최소 예제 발쵀는 아래와 같습니다: + +[...] + +#ifndef __stringify +# define __stringify(X) #X +#endif + +#ifndef __section +# define __section(NAME) \ +__attribute__((section(NAME), used)) +#endif + +#ifndef __section_tail +# define __section_tail(ID, KEY) \ +__section(__stringify(ID) "/" __stringify(KEY)) +#endif + +#ifndef BPF_FUNC +# define BPF_FUNC(NAME, ...) \ +(*NAME)(__VA_ARGS__) = (void *)BPF_FUNC_##NAME +#endif + +#define BPF_JMP_MAP_ID 1 + +static void BPF_FUNC(tail_call, struct __sk_buff *skb, void *map, +uint32_t index); + +struct bpf_elf_map jmp_map __section("maps") = { +.type = BPF_MAP_TYPE_PROG_ARRAY, +.id = BPF_JMP_MAP_ID, +.size_key = sizeof(uint32_t), +.size_value = sizeof(uint32_t), +.pinning = PIN_GLOBAL_NS, +.max_elem = 1, +}; + +__section_tail(JMP_MAP_ID, 0) +int looper(struct __sk_buff *skb) +{ +printk("skb cb: %u\n", skb->cb[0]++); +tail_call(skb, &jmp_map, 0); +return TC_ACT_OK; +} + +__section("prog") +int entry(struct __sk_buff *skb) +{ +skb->cb[0] = 0; +tail_call(skb, &jmp_map, 0); +return TC_ACT_OK; +} + +char __license[] __section("license") = "GPL"; + +이 toy 프로그램을 로드 할 때, tc는 프로그램 배열을 생성하고 jmp_map 아래의 전역 네임 스페이스의 BPF 파일 시스템에 고정 시킵니다. +또한 iproute2의 BPF ELF 로더는 __section_tail()으로 표시 된 섹션을 인식합니다. +struct bpf_elf_map의 제공된 ID는 __section_tail()의 ID 마커, 즉 JMP_MAP_ID와 일치하므로 +프로그램은 사용자 지정 프로그램 배열 map 인덱스 (이 예제에서는 0)에서 로드 됩니다. +결과적으로 제공된 모든 tail call 섹션은 iproute2 로더에 의해 해당 맵에 채워 집니다. +이 메커니즘은 tc에만 해당되는 것이 아니라 iproute2가 지원하는 다른 BPF 프로그램 유형(예 :XDP, lwt)에도 적용 할 수 있습니다. + +생성 된 elf에는 map ID와 해당 map 내의 항목을 설명하는 섹션 헤더가 있습니다: +$ llvm-objdump -S --no-show-raw-insn prog_array.o | less +prog_array.o: file format ELF64-BPF + +Disassembly of section 1/0: +looper: +0: r6 = r1 +1: r2 = *(u32 *)(r6 + 48) +2: r1 = r2 +3: r1 += 1 +4: *(u32 *)(r6 + 48) = r1 +5: r1 = 0 ll +7: call -1 +8: r1 = r6 +9: r2 = 0 ll +11: r3 = 0 +12: call 12 +13: r0 = 0 +14: exit +Disassembly of section prog: +entry: +0: r2 = 0 +1: *(u32 *)(r1 + 48) = r2 +2: r2 = 0 ll +4: r3 = 0 +5: call 12 +6: r0 = 0 +7: exi + +이 경우 section 1/0 은 looper()함수 가 0 위치의 map ID 1 에 상주 함을 나타냅니다. +고정 된 map은 새로운 프로그램으로 맵을 업데이트하기 위해 사용자 공간 애플리케이션(예 : Cilium 데몬)에 +의해 검색 될뿐만 아니라 tc 자체에 의해 검색 될 수 있습니다. +업데이트는 atomically 으로 발생하며, 다양한 하위 시스템에서 처음 트리거되는 +초기 엔트리 프로그램도 자동 업데이트 됩니다. + +tail call map 업데이트를 수행하는 tc의 예: +# tc exec bpf graft m:globals/jmp_map key 0 obj new.o sec foo + +iproute2가 고정 된 프로그램 배열을 갱신 할 경우, graft 명령을 사용할 수 있습니다. +globals/jmp_map을 가리키면 tc는 인덱스/키 0의 맵을 foo 섹션 아래 new.o 오브젝트 파일에 있는 +새 프로그램으로 갱신합니다. + +8. 최대 512 바이트의 제한된 스택 공간 +BPF 프로그램의 스택 공간은 512 바이트로 제한되어 있으므로 C로 BPF 프로그램을 구현할 때는 +신중하게 고려해야합니다. 그러나 앞의 3 번 항목에서 설명한 것처럼 스크래치 버퍼 공간을 확장하기 위해 +단일 항목이있는 BPF_MAP_TYPE_PERCPU_ARRAY 맵을 사용할 수 있습니다. + +9.BPF 인라인 어셈블리를 사용할 수 있습니다. +또한 LLVM은 필요할 수있는 드문 경우를 위해 BPF 용 인라인 어셈블리를 사용할 수 있습니다. +다음 (말도 안되는) toy 예제는 64 비트 atomic 추가를 보여 줍니다. +문서가 없기 때문에 lib/Target/BPF/BPFInstrInfo.td 및 test/CodeGen/BPF/ 에있는 +LLVM 소스 코드가 몇 가지 추가 예제를 제공하는 데 도움이 될 수 있습니다. + +테스트 코드: +#include + +#ifndef __section +# define __section(NAME) \ +__attribute__((section(NAME), used)) +#endif + +__section("prog") +int xdp_test(struct xdp_md *ctx) +{ +__u64 a = 2, b = 3, *c = &a; +/* just a toy xadd example to show the syntax */ +asm volatile("lock *(u64 *)(%0+0) += %1" : "=r"(c) : "r"(b), "0"(c)); +return a; +} + +char __license[] __section("license") = "GPL"; + +위의 프로그램은 다음과 같은 BPF 명령어 순서로 컴파일 됩니다: +Verifier analysis: + +0: (b7) r1 = 2 +1: (7b) *(u64 *)(r10 -8) = r1 +2: (b7) r1 = 3 +3: (bf) r2 = r10 +4: (07) r2 += -8 +5: (db) lock *(u64 *)(r2 +0) += r1 +6: (79) r0 = *(u64 *)(r10 -8) +7: (95) exit +processed 8 insns (limit 131072), stack depth 8 + +iproute2 +bcc, perf, iproute2 및 기타와 같은 BPF 프로그램을 커널에 로드 하기위한 다양한 프런트 엔드가 있습니다. +Linux 커널 소스 트리는 tools/lib/bpf/ 디렉토리 아래에 사용자 공간 라이브러리를 제공하는데, 이는 주로 +BPF 추적 프로그램을 커널에 로드하기 위해 perf에 의해 사용됩니다. 그러나 라이브러리 자체는 +범용이며 perf에만 제한되지 않습니다. bcc는 BPF C 코드가 내장 된 Python 인터페이스를 통해 +ad-hoc으로 로드 되는 많은 유용한 BPF 프로그램을 주로 제공하는 툴킷 입니다. +BPF 프로그램을 구현하기위한 구문 및 의미는 일반적으로 프론트 엔드간에 약간 다릅니다. +게다가, 생성된 오브젝트 파일을 구문 분석 하고 코드를 시스템 콜 인터페이스에 직접 로드 하는 +BPF 샘플 코드들이 커널 소스 트리 samples/bpf/ 아래에 있습니다. Cilium 프로그램은 주로 BPF 로더에 대해 구현 되므로, +현재 세션 및 이전 섹션은 주로 XDP, tc 또는 lwt 유형의 네트워킹 프로그램을 로드 하기 위한 +iproute2 제품군의 BPF 프런트 엔드에 중점을 둡니다. 앞으로 Cilium에는 기본 BPF 로더가 갖추어져 있지만, +개발 및 디버깅을 용이하게 하기 위해 프로그램은 iproute2 제품군을 통해 로드 될 수 있도록 여전히 호환됩니다. +iproute2가 지원하는 모든 BPF 프로그램 타입은 공통 로더 백엔드를 라이브러리 +(iproute2 소스 트리의 lib/bpf.c)로 구현 하므로 동일한 BPF 로더 로직을 공유 합니다. +LLVM의 이전 섹션에서는 BPF C 프로그램 작성과 관련된 일부 iproute2 부분을 다루었으며, +이 문서의 뒷부분은 프로그램 작성시 tc 및 XDP 특정 측면과 관련이 있습니다. 따라서 현재 섹션에서는 로더의 일반 +메커니즘 뿐만 아니라 iproute2로 객체 파일을 로드하는 데 사용 예제에 초점을 맞춥니다. +모든 세부 사항을 완벽하게 다루지는 않지만 시작하기에 충분합니다. + +1. XDP BPF 오브젝트 파일 로드. +BPF 객체 파일 prog.o가 XDP 용으로 컴파일 된 경우, 다음 명령을 사용하여 em1이라는 XDP 지원 netdevice에 +ip 명령어를 통해 로드 할 수 있습니다: +# ip link set dev em1 xdp obj prog.o + +위의 명령은 프로그램 코드가 XDP의 경우 prog 라고하는 기본 섹션에 있다고 가정합니다. +이런 경우가 아니고 섹션의 이름이 다르게 지정되면 (예 : foobar) 프로그램을 다음과 같이 로드 해야 합니다: + +# ip link set dev em1 xdp obj prog.o sec foobar + +네트워크 인터페이스에 XDP 프로그램이 이미 연결되어 있어 실수로 덮어 쓰지 않도록 기본적으로 ip 명령어는 +오류를 발생 시킵니다. +현재 실행중인 XDP 프로그램을 새 프로그램으로 바꾸려면 -force 옵션을 사용해야합니다: + +# ip -force link set dev em1 xdp obj prog.o + +오늘날 대부분의 XDP 지원 드라이버는 트래픽 중단 없이 기존 프로그램을 새로운 것으로 대체합니다. +여기에는 성능상의 이유로 XDP 지원 드라이버에 연결된 단일 프로그램 만 있으므로 프로그램 체인이 +지원되지 않습니다. 그러나 이전 섹션에서 설명한 것처럼 필요한 경우 유사한 사례를 얻기 위해 tail call +을 통해 프로그램을 나뉠 수 있습니다. 인터페이스에 XDP 프로그램이 연결되어 있으면 +ip link 명령에 xdp 플래그가 표시됩니다. 따라서 "ip link | grep xdp"를 사용하여 XDP가 실행되는 +모든 인터페이스를 찾을 수 있습니다. ip link 덤프에 표시된 BPF 프로그램 ID를 기반으로 +연결된 프로그램에 대한 정보를 검색하는 데 ip -d 링크가있는 자세히 보기를 통해 +추가 자가 검사 기능이 제공 되며 bpftool을 사용하여 이 정보를 검색 할 수 있습니다. + +인터페이스에서 기존 XDP 프로그램을 제거하려면 다음 명령을 실행해야합니다: +# ip link set dev em1 xdp off + +드라이버의 동작 방식을 non-XDP에서 네이티브 XDP로 또는 그 반대로 전환하는 경우, +일반적으로 드라이버는 수신 된 패킷이 BPF가 읽고 쓸 수있는 단일 페이지 내에서 선형적으로 +설정되도록 수신(및 전송) 링을 재구성해야합니다. +그러나, 일단 완료되면, 대부분의 드라이버는 BPF 프로그램을 스왑 하도록 요청할 때 +프로그램 자체의 atomic 교체를 수행하면 됩니다. + +전체적으로 XDP는 iproute2가 구현하는 세 가지 작동 방식을 지원 합니다 : xdpdrv, xdpoffload, xdpgeneric. +xdpdrv는 네이티브 XDP를 의미하며, 즉, BPF 프로그램은 소프트웨어의 가능한 가장 빠른 시점에 +드라이버의 수신 경로에서 직접 실행됩니다.이것은 일반/일반 XDP 방식이며, XDP 지원을 구현 하는 데 드라이버가 필요하며, +업스트림 리눅스 커널의 모든 주요 10G/40G/+ 네트워킹 드라이버가 이미 제공합니다. + +xdpgeneric은 일반 XDP를 나타내며 아직 네이티브 XDP를 지원하지 않는 드라이버를 위한 실험용 테스트 베드로 사용됩니다. +진입 경로의 일반적인 XDP hook이 패킷이 이미 스택의 메인 수신 경로인 skb로 들어 가는 훨씬 늦은 시점에 제공되며, +xdpdrv 방식에서 처리하는 것보다 성능이 훨씬 낮습니다.그러므로 xdpgeneric은 대부분 실험적인 측면에서만 흥미롭고 +실제 운영 환경 에서는 적습니다. + +마지막으로, xdpoffload 모드는 Netronome의 nfp 드라이버가 지원하는 SmartNIC에서 구현되며 전체 BPF/XDP 프로그램을 +하드웨어로 오프로드 할 수 있으므로 프로그램은 카드의 각 패킷 수신시 직접 실행됩니다.네이티브 XDP 와 비교하여 모든 BPF map 유형 +또는 BPF helper 함수를 사용할 수 있는 것은 아니지만 네이티브 XDP에서 실행하는 것보다 훨씬 높은 성능을 제공합니다. +이 경우 BPF verifier는 프로그램을 거부하고 그리고 지원되지 않는 것을 사용자에게 보고합니다. +지원되는 BPF 기능 및 도우미 기능의 영역에 머무르는 것 외에 BPF C 프로그램을 작성할 때는 특별한 주의를 기울이 지 않아도됩니다 + +ip link set dev em1 xdp obj [...] 와 같은 명령이 사용되면 커널은 먼저 프로그램을 기본 XDP로 로드 하려고 시도하고 +그리고 드라이버가 네이티브 XDP를 지원하지 않으면 자동으로 일반 XDP로 되돌아갑니다.따라서 예를 들어 xdp 대신 명시적으로 xdpdrv를 +사용하면 커널은 프로그램을 raw XDP로 로드 하려고 시도하고 드라이버가 지원하지 않는 경우 실패 하여 일반 XDP가 모두 회피 되는 보장을 제공 합니다. + +네이티브 XDP 방식에서 로드 할 BPF/XDP 프로그램 실행, 링크 세부 정보 덤프 및 프로그램 언로드 예: + +# ip -force link set dev em1 xdpdrv obj prog.o +# ip link show +[...] +6: em1: mtu 1500 xdp qdisc mq state UP mode DORMANT group default qlen 1000 +link/ether be:08:4d:b6:85:65 brd ff:ff:ff:ff:ff:ff +prog/xdp id 1 tag 57cd311f2e27366b +[...] +# ip link set dev em1 xdpdrv off + +드라이버가 기본 XDP를 지원하고 bpftool을 통해 삽입된 더미 프로그램의 BPF 명령어를 추가로 덤프하는 경우에도 일반 +XDP를 강제 실행하는 것과 같은 예제가 있습니다: + +# ip -force link set dev em1 xdpgeneric obj prog.o +# ip link show +[...] +6: em1: mtu 1500 xdpgeneric qdisc mq state UP mode DORMANT group default qlen 1000 +link/ether be:08:4d:b6:85:65 brd ff:ff:ff:ff:ff:ff +prog/xdp id 4 tag 57cd311f2e27366b <-- BPF 프로그램 ID 4 +[...] +# bpftool prog dump xlated id 4 <-- em1에서 실행중인 명령어 덤프 +0: (b7) r0 = 1 +1: (95) exit +# ip link set dev em1 xdpgeneric off + +마지막이긴 하나 중요한 오프로드 된 XDP는 일반 메타 데이터 검색을 위해 bpftool을 통해 프로그램 정보를 추가로 덤프합니다: + +# ip -force link set dev em1 xdpoffload obj prog.o +# ip link show +[...] +6: em1: mtu 1500 xdpoffload qdisc mq state UP mode DORMANT group default qlen 1000 +link/ether be:08:4d:b6:85:65 brd ff:ff:ff:ff:ff:ff +prog/xdp id 8 tag 57cd311f2e27366b +[...] +# bpftool prog show id 8 +8: xdp tag 57cd311f2e27366b dev em1 <-- 또한 em1에 오프로드 된 BPF 프로그램을 나타냅니다. +loaded_at Apr 11/20:38 uid 0 +xlated 16B not jited memlock 4096B +# ip link set dev em1 xdpoffload off + +xdpdrv와 xdpgeneric 또는 다른 방식를 동시에 사용할 수는 없으며, 이는 XDP 동작 방식 중 하나만 선택해야 함을 의미합니다. +다른 XDP 모드 간의 전환은 예를 들어 generic에서 native로 또는 그 반대로도 atomically으로 가능하지 않습니다: + +# ip -force link set dev em1 xdpgeneric obj prog.o +# ip -force link set dev em1 xdpoffload obj prog.o +RTNETLINK answers: File exists +# ip -force link set dev em1 xdpdrv obj prog.o +RTNETLINK answers: File exists +# ip -force link set dev em1 xdpgeneric obj prog.o <-- xdpgeneric로 인해 성공. +# + +방식을 전환하려면 먼저 현재 작동 모드를 종료 한 후 새로운 방식으로 전환해야합니다: +# ip -force link set dev em1 xdpgeneric obj prog.o +# ip -force link set dev em1 xdpgeneric off +# ip -force link set dev em1 xdpoffload obj prog.o +# ip l +[...] +6: em1: mtu 1500 xdpoffload qdisc mq state UP mode DORMANT group default qlen 1000 +link/ether be:08:4d:b6:85:65 brd ff:ff:ff:ff:ff:ff +prog/xdp id 17 tag 57cd311f2e27366b +[...] +# ip -force link set dev em1 xdpoffload off + +2. tc BPF 오브젝트 파일의 로딩. +prog.o가 tc 용으로 컴파일 된 BPF 오브젝트 파일이 고려될때, tc 명령을 통해 netdevice에 로드 할 수 있습니다. +XDP와 달리 장치에 BPF 프로그램을 연결하는 데 필요한 드라이버 의존성이 없습니다. +여기서 netdevice는 em1이라고하며 다음 명령을 사용하여 프로그램을 em1의 네트워킹 ingress 경로에 연결할 수 있습니다: + +# tc qdisc add dev em1 clsact +# tc filter add dev em1 ingress bpf da obj prog.o + +첫 번째 단계는 clsact qdisc (리눅스 대기 행렬의 규칙)를 설정하는 것입니다. +clsact는 ingress qdisc와 비슷한 더미 qdisc이며, classifier 및 action 만 보유 할 수 있지만 +실제 대기열을 작동 하지는 않습니다. bpf verifier를 연결하기 위해 필요합니다. clsact qdisc는 +ingress 및 egress 라고 하는 두 개의 특수 hook를 제공 하며, 이 hook는 classifier를 연결할 수 있습니다. +ingress 및 egress hook은 장치의 모든 패킷이 통과하는 네트워킹 데이터 경로의 중심 수신 및 전송 위치에 있습니다. + +ingress hook은 커널의 함수의 __netif_receive_skb_core () -> sch_handle_ingress () 호출되며, +egress hook은 __dev_queue_xmit () -> sch_handle_egress () 에서 호출됩니다. + +net/core/dev.c(ingress hook 커널 함수 호출 흐름이며, 4.17.0-rc7 기반에서 확인한 내용입니다.) +... +static int __netif_receive_skb_core(struct sk_buff *skb, bool pfmemalloc) +{ +... +if (pfmemalloc) +goto skip_taps; +... +skip_taps: +#ifdef CONFIG_NET_INGRESS +if (static_branch_unlikely(&ingress_needed_key)) { +skb = sch_handle_ingress(skb, &pt_prev, &ret, orig_dev); +if (!skb) +goto out; + +if (nf_ingress(skb, &pt_prev, &ret, orig_dev) < 0) +goto out; +} +#endif +.... +} + +static inline struct sk_buff * +sch_handle_ingress(struct sk_buff *skb, struct packet_type **pt_prev, int *ret, +struct net_device *orig_dev) +{ +.... +switch (tcf_classify(skb, miniq->filter_list, &cl_res, false)) { +case TC_ACT_OK: +case TC_ACT_RECLASSIFY: +skb->tc_index = TC_H_MIN(cl_res.classid); +break; +case TC_ACT_SHOT: +mini_qdisc_qstats_cpu_drop(miniq); +kfree_skb(skb); +return NULL; +case TC_ACT_STOLEN: +case TC_ACT_QUEUED: +case TC_ACT_TRAP: +consume_skb(skb); +return NULL; +case TC_ACT_REDIRECT: +/* skb_mac_header check was done by cls/act_bpf, so +* we can safely push the L2 header back before +* redirecting to another netdev +*/ +__skb_push(skb, skb->mac_len); +skb_do_redirect(skb); +return NULL; +default: +break; +} +.... +} + +net/core/dev.c(egress hook 커널 함수 호출 흐름이며, 4.17.0-rc7 기반에서 확인한 내용입니다.) +static int __dev_queue_xmit(struct sk_buff *skb, void *accel_priv) +{ +... +# ifdef CONFIG_NET_EGRESS +if (static_branch_unlikely(&egress_needed_key)) { +skb = sch_handle_egress(skb, &rc, dev); +if (!skb) +goto out; +} +... +} + +... + +static struct sk_buff * +sch_handle_egress(struct sk_buff *skb, int *ret, struct net_device *dev) +{ +... +switch (tcf_classify(skb, miniq->filter_list, &cl_res, false)) { +case TC_ACT_OK: +case TC_ACT_RECLASSIFY: +skb->tc_index = TC_H_MIN(cl_res.classid); +break; +case TC_ACT_SHOT: +mini_qdisc_qstats_cpu_drop(miniq); +*ret = NET_XMIT_DROP; +kfree_skb(skb); +return NULL; +case TC_ACT_STOLEN: +case TC_ACT_QUEUED: +case TC_ACT_TRAP: +*ret = NET_XMIT_SUCCESS; +consume_skb(skb); +return NULL; +case TC_ACT_REDIRECT: +/* No need to push/pop skb's mac_header here on egress! */ +skb_do_redirect(skb); +*ret = NET_XMIT_SUCCESS; +return NULL; +default: +break; +} + +egress hook에 프로그램을 연결하는 것에 해당하는 것은 다음과 같습니다 : +# tc filter add dev em1 egress bpf da obj prog.o + +clsact qdisc는 ingress 및 egress 방향에서 lockless로 처리 되며, 컨테이너를 연결하는 veth 장치와 같은 가상, +대기열없는 장치 에도 연결할 수 있습니다. hook 다음에, tc filter 명령은 da(직접 실행, direct-action mode) +방식에서 사용 할 bpf를 선택합니다. da mode가 권장이 되며 그리고 항상 명시 해야 합니다. +그것은 기본적으로 모든 작업 패킷 mangling, forwarding 또는 다른 종류의 이미 있는 하나의 +BPF 프로그램 내부에서 수행 할 수 있기 때문에 BPF 분류는,어쨌든 BPF 필요하지 않은 외부 TC 액션 모듈로 호출 할 필요가 +없음을 의미 따라서 연결이 상당히 빨라집니다. + +기본적으로 bpf classifier는 모든 패킷 mangling 및 포워딩 또는 다른 동작이 이미 +single bpf 프로그램 내부에서 수행 될 수 있기 때문에 외부 tc action 모듈을 호출 할 필요가 없다는 것을 의미 +하며, 연결이 될때 상당히 빨라 집니다. 이 시점에서 프로그램이 연결되어 패킷이 장치를 통과하면 프로그램이 실행됩니다. +XDP처럼, 기본 섹션 이름을 사용하지 않으면 로드 중에 foobar 섹션과 같이 지정할 수 있습니다: +# tc filter add dev em1 egress bpf da obj prog.o sec foobar + +iproute2의 BPF 로더는 프로그램 타입에 따라 동일한 명령 행 구문을 사용할 수 있으므로 obj prog.o sec foobar는 +앞에서 언급 한 XDP와 같은 구문입니다. 연결 된 프로그램은 다음 명령을 통해 나열 될 수 있습니다: + +# tc filter show dev em1 ingress +filter protocol all pref 49152 bpf +filter protocol all pref 49152 bpf handle 0x1 prog.o:[ingress] direct-action id 1 tag c5f7825e5dac396f + +# tc filter show dev em1 egress +filter protocol all pref 49152 bpf +filter protocol all pref 49152 bpf handle 0x1 prog.o:[egress] direct-action id 2 tag b2fd5adc0f262714 + +prog.o : [ingress] 의 출력은 프로그램 섹션 ingress가 prog.o 파일에서 로드 되었으며, +bpf가 직접-실행(direct-action mode) 방식에서 동작 하는 것을 나타냅니다.프로그램 id 및 tag 는 각각 값이며, 마지막 명령어 스트림에 대한 +hash 나타내며, 오브젝트 파일 혹은 stack trace가 있는 perf reports 등과 같은 관련을 될수 있습니다. 마지막이긴 하나 중요한 id는 +bpftool과 함께 사용하여 첨부 된 BPF 프로그램을 추가로 검사하거나 덤프 할 수있는 시스템 전체의 고유 한 BPF 프로그램 식별자를 나타냅니다. + +tc는 단 하나의 BPF 프로그램 이상을 연결 할 수 있으며, tc는 함께 chained 할 수 있는 다양한 다른 +classifier들을 제공합니다.하지만, BPF 프로그램 자체가 이미 TC_ACT_OK, TC_ACT_SHOT 및 기타와 +같은 tc 작업 결정을 반환한다는 것을 의미하는 da(직접-실행, direct-action)방식 덕분에 +모든 패킷 조작이 프로그램 자체에 포함될 수 있기 때문에 단일 BPF 프로그램을 연결 만으로 충분합니다. +최적의 성능과 유연성을 위해 권장되는 사용법 입니다. + +위의 show 명령에서 tc는 BPF 관련 출력 옆에 pref 49152 및 handle 0x1도 표시합니다. +둘 다 명령 줄을 통해 명시적으로 제공되지 않는 경우 자동 생성됩니다. +pref는 우선 순위 번호를 나타내며, 이는 다중 classifier들이 연결 된 경우 오름차순 우선 순위에 따라 실행되는 것을 의미하며, +handle은 같은 classifier의 다중 인스턴스가 동일한 pref 아래 로드 된 경우 식별자를 나타냅니다. +BPF의 경우 하나의 프로그램으로 충분하기 때문에 일반적으로 pref와 handle을 무시할 수 있습니다. +연결된 BPF 프로그램들을 atomically하게 교체할 계획이 있는 경우 에만, 초기 로드시 연역적으로 +pref 및 handle을 명시적으로 지정하는 것을 추천하며, 따라서 나중에 교체 작업을 위해 따로 쿼리할 필요는 없습니다. +따라서 창작물은 다음과 같이 됩니다: + +# tc filter add dev em1 ingress pref 1 handle 1 bpf da obj prog.o sec foobar + +# tc filter show dev em1 ingress +filter protocol all pref 1 bpf +filter protocol all pref 1 bpf handle 0x1 prog.o:[foobar] direct-action id 1 tag c5f7825e5dac396f + +그리고 atomic 교체을 위해 foobar 섹션의 prog.o 파일에서 새로운 BPF 프로그램으로 ingress hook에서 +기존 프로그램을 업데이트하기 위해 다음을 발행 할 수 있습니다: +# tc filter replace dev em1 ingress pref 1 handle 1 bpf da obj prog.o sec foobar + +마지막으로 중요한 것은 첨부 된 각 프로그램을 ingress 및 egress hook에서 모두 제거하려면 다음을 사용할 수 있습니다: +# tc filter del dev em1 ingress +# tc filter del dev em1 egress + +netdevice에서 clsact qdisc 전체 즉 암시적으로 ingress 및 egress hook에 모든 연결된 프로그램을 +제거하려면 다음과 같은 명령이 제공 됩니다 : +# tc qdisc del dev em1 clsact + +tc BPF 프로그램은 만약 NIC 및 드라이버가 XDP BPF 프로그램과 유사하게 지원 되는 경우 오프로드 할 수 있습니다. +Netronome의 nfp 지원 NIC는 두 가지 유형의 BPF 오프로드를 제공합니다. +# tc qdisc add dev em1 clsact +# tc filter replace dev em1 ingress pref 1 handle 1 bpf skip_sw da obj prog.o +Error: TC offload is disabled on net device. +We have an error talking to the kernel + +위의 오류가 표시되면 ethtool의 hw-tc-offload 설정을 통해 장치의 tc 하드웨어 오프로드를 먼저 활성화 해야 합니다. +# ethtool -K em1 hw-tc-offload on +# tc qdisc add dev em1 clsact +# tc filter replace dev em1 ingress pref 1 handle 1 bpf skip_sw da obj prog.o +# tc filter show dev em1 ingress +filter protocol all pref 1 bpf +filter protocol all pref 1 bpf handle 0x1 prog.o:[classifier] direct-action skip_sw in_hw id 19 tag 57cd311f2e27366b + +in_hw 플래그는 프로그램이 NIC로 오프로드 되었음을 확인합니다. +tc와 XDP의 BPF 오프로드를 동시에 로드 할 수 없으므로 tc 또는 XDP 오프로드 옵션을 선택해야합니다. + +3. netdevsim 드라이버를 통해 BPF 오프로드 인터페이스를 테스트합니다. +Linux 커널의 일부인 netdevsim 드라이버는 XDP BPF 및 tc BPF 프로그램 용 오프로드 인터페이스를 +구현하는 더미 드라이버를 제공하며 커널의 UAPI에 대해 직접 control plane을 구현하는 커널 변경 또는 +low-level 사용자 공간 프로그램 테스트를 가능 하게합니다. + +netdevsim 장치는 다음과 같이 생성 될 수 있습니다 : +# ip link add dev sim0 type netdevsim +# ip link set dev sim0 up +# ethtool -K sim0 hw-tc-offload on +# ip l +[...] +7: sim0: mtu 1500 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000 +link/ether a2:24:4c:1c:c2:b3 brd ff:ff:ff:ff:ff:ff + +** 역자주 : 커널 컴파일시 , 만약 hw 오프로드 관련 테스트 하고자 한다면, 아래 옵션에 대해서 활성화를 해야 합니다. +미 활성화 인 경우, 인터페이스는 생성되지 않습니다. +CONFIG_NETDEVSIM: +│ +│ This driver is a developer testing tool and software model that can +│ be used to test various control path networking APIs, especially +│ HW-offload related. +│ +│ To compile this driver as a module, choose M here: the module +│ will be called netdevsim. +│ +│ Symbol: NETDEVSIM [=n] +│ Type : tristate +│ Prompt: Simulated networking device +│ Location: +│ -> Device Drivers +│ -> Network device support (NETDEVICES [=y]) +│ Defined at drivers/net/Kconfig:502 +│ Depends on: NETDEVICES [=y] && DEBUG_FS [=y] && MAY_USE_DEVLINK [=y] + + +이 단계가 끝난 후 XDP BPF 또는 tc BPF 프로그램은 앞서 다양한 예제에 표시된대로 로드 할 수 있습니다. +# ip -force link set dev sim0 xdpoffload obj prog.o +# ip l +[...] +7: sim0: mtu 1500 xdpoffload qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000 +link/ether a2:24:4c:1c:c2:b3 brd ff:ff:ff:ff:ff:ff +prog/xdp id 20 tag 57cd311f2e27366b + +..... + +이 두 워크 플로는 iproute2를 사용하여 XDP BPF 및 tc BPF 프로그램을 로드하는 기본 작업입니다. +BPF 로더에는 XDP와 tc에 모두 적용되는 여러 가지 고급 옵션이 있으며 그 중 일부는 여기에 나열되어 있습니다. +예제에서 XDP는 단순화를 위해 제시된 것입니다. + +1. 성공시에도 자세한 로그 출력. +오류가 발생하지 않은 경우에도 Verifier 로그를 덤프하기 위해 verb 옵션을 추가하여 프로그램을 로드 할 수 있습니다. + +# ip link set dev em1 xdp obj xdp-example.o verb + +Prog section 'prog' loaded (5)! +- Type: 6 +- Instructions: 2 (0 over limit) +- License: GPL + +Verifier analysis: + +0: (b7) r0 = 1 +1: (95) exit +processed 2 insns + +2. BPF 파일 시스템에 이미 고정되어 있는 프로그램을 로드 하십시오. + +오브젝트 파일에서 프로그램을 로드하는 대신에, iproute2는 BPF 파일 시스템에서 프로그램을 검색 할 수 있으며, 외부 엔티티가 거기에 +고정시키고 장치에 연결하는 경우입니다: +# ip link set dev em1 xdp pinned /sys/fs/bpf/prog + +iproute2는 감지 된 BPF 파일 시스템의 마운트 지점과 관련된 짧은 형식을 사용할 수도 있습니다: +# ip link set dev em1 xdp pinned m:prog + +BPF 프로그램을 로드 할 때, iproute2는 노드의 고정을 수행하기 위해 마운트 된 파일 시스템 인스턴스를 자동으로 감지합니다. +마운트 된 BPF 파일 시스템 인스턴스가 없는 경우, tc는 이를 자동으로 /sys/fs/bpf/ 아래의 기본 위치에 마운트 합니다. + +인스턴스가 이미 발견 된 경우, 인스턴스가 사용되며 추가 마운트가 수행되지 않습니다. +# mkdir /var/run/bpf +# mount --bind /var/run/bpf /var/run/bpf +# mount -t bpf bpf /var/run/bpf +# tc filter add dev em1 ingress bpf da obj tc-example.o sec prog +# tree /var/run/bpf +/var/run/bpf ++-- ip -> /run/bpf/tc/ ++-- tc +| +-- globals +| +-- jmp_map ++-- xdp -> /run/bpf/tc/ + +4 directories, 1 file + +기본적으로 tc는 위와 같이 모든 서브 시스템 사용자는 전역 이름 공간에 대한 기호 링크를 통해 동일한 위치를 가리키는 초기 디렉토리 구조를 생성하며, +고정 된 BPF 맵을 iproute2의 다양한 BPF 프로그램 유형간에 재사용 할 수 있습니다. +파일 시스템 인스턴스가 이미 마운트 되었고 기존 구조가 이미 존재하는 경우, tc는 파일 시스템 인스턴스를 오버라이드 하지 않습니다. + +이는 전역 파일을 공유하지 않기 위해 lwt, tc 및 xdp map을 분리하는 경우가 발생 할 수 있습니다. +이전 LLVM 섹션에서 간단히 설명 했듯이, iproute2는 설치 시 BPF 프로그램에 의해 표준 include 경로를 통해 포함될 수있는 헤더 파일을 설치합니다. +#include + +이 헤더 파일의 목적은 프로그램에 사용되는 map과 기본 섹션 이름에 대한 API를 제공하는 것입니다. 그것은 iproute2와 BPF 프로그램 사이의 안정적인 계약입니다. +iproute2에 대한 map 정의는 bpf_elf_map 구조체 입니다. 그것의 구성은 이 문서의 LLVM 부분에서 더 일찍 다루어졌습니다. +BPF 객체 파일을 구문 분석 할 때 iproute2 로더는 모든 ELF 섹션을 처리합니다. 처음에는 map 및 라이센스 와 같은 보조 섹션을 가져옵니다. +map의 경우 struct bpf_elf_map 배열의 유효성을 검사하고 필요할 때마다 호환성 우회 방법이 수행 됩니다. +결과적으로 모든 map은 사용자가 제공 한 정보로 생성되며, 고정 된 객체로 검색되거나 새로 생성 된 다음 BPF 파일 시스템에 고정됩니다. +다음으로 로더는 map에 대한 ELF 재배치 엔트리가 포함된 프로그램 세션을 처리 하며, 이 의미는 map 파일 디스크립터를 레지스터리에 +로드하는 BPF 명령어로 다시 작성되어, 해당 map 파일 디스크립터가 immediate 값으로 인코딩이 되고, 이는 커널이 나중에 커널 포인터로 +변환 할 수 있도록 하기 위해서 입니다. 이후 모든 프로그램 자체가 BPF 시스템 호출을 통해 생성되고 tail 호출된 map이 있는 경우, +이 프로그램의 파일 디스크립터로 업데이트 됩니다. + +bpftool +bpftool은 BPF 주변의 주요 자가 검사 및 디버깅 도구이며 tools/bpf/bpftool/ 아래의 Linux 커널 트리와 함께 개발 및 제공됩니다. +이 도구는 현재 시스템에 로드 된 모든 BPF 프로그램과 맵을 덤프하거나, 특정 프로그램에서 사용하는 모든 BPF 맵을 나열하고 연관시킬 수 있습니다. + +또한 전체 맵의 키/값 쌍을 덤프하거나 개별 맵을 조회, 업데이트, 삭제할 수있을 뿐만 아니라 맵에서 키의 인접 키를 검색 할 수 있습니다. +이러한 작업은 BPF 프로그램 또는 맵 ID를 기반으로 수행하거나, BPF 파일 시스템 고정 프로그램 또는 맵의 위치를 지정하여 수행 할 수 있습니다. +또한 이 도구는 map을 고정하는 옵션 또는 프로그램을 BPF 파일 시스템에 제공합니다. + +현재 호스트에 로드 된 모든 BPF 프로그램의 개요 내용을 확인하려면 다음 명령을 호출하십시오: +# bpftool prog +398: sched_cls tag 56207908be8ad877 +loaded_at Apr 09/16:24 uid 0 +xlated 8800B jited 6184B memlock 12288B map_ids 18,5,17,14 +399: sched_cls tag abc95fb4835a6ec9 +loaded_at Apr 09/16:24 uid 0 +xlated 344B jited 223B memlock 4096B map_ids 18 +400: sched_cls tag afd2e542b30ff3ec +loaded_at Apr 09/16:24 uid 0 +xlated 1720B jited 1001B memlock 4096B map_ids 17 +401: sched_cls tag 2dbbd74ee5d51cc8 +loaded_at Apr 09/16:24 uid 0 +xlated 3728B jited 2099B memlock 4096B map_ids 17 +[...] + +마찬가지로 모든 활성 map의 개요를 보려면 다음을 수행하십시오: +# bpftool map +5: hash flags 0x0 +key 20B value 112B max_entries 65535 memlock 13111296B +6: hash flags 0x0 +key 20B value 20B max_entries 65536 memlock 7344128B +7: hash flags 0x0 +key 10B value 16B max_entries 8192 memlock 790528B +8: hash flags 0x0 +key 22B value 28B max_entries 8192 memlock 987136B +9: hash flags 0x0 +key 20B value 8B max_entries 512000 memlock 49352704B +[...] + +각 명령에 대해 bpftool은 명령 줄 끝에 --json을 추가하여 json 기반 출력을 지원합니다. 추가적인 --pretty 는 +사람이 읽을 수 있는 출력을 향상 시킵니다. +# bpftool prog --json --pretty + +특정 BPF 프로그램의 verifier 후 BPF 명령어 이미지를 덤프 하는 경우, 예를 들어 tc ingress hook에 연결된 특정 프로그램을 검사 할 수 있습니다. +# tc filter show dev cilium_host egress +filter protocol all pref 1 bpf chain 0 +filter protocol all pref 1 bpf chain 0 handle 0x1 bpf_host.o:[from-netdev] \ +direct-action not_in_hw id 406 tag e0362f5bd9163a0a jited + +객체 파일 bpf_host.o 프로그램은 from-netdev 섹션에서 id 406에 표시 된대로, 프로그램 ID 406을 가지고 있습니다. +이 정보를 바탕으로 bpftool은 프로그램과 관련된 몇 가지 상위 레벨 메타 데이터를 제공 할 수 있습니다: + +# bpftool prog show id 406 +406: sched_cls tag e0362f5bd9163a0a +loaded_at Apr 09/16:24 uid 0 +xlated 11144B jited 7721B memlock 12288B map_ids 18,20,8,5,6,14 + +ID 406의 프로그램은 sched_cls(BPF_PROG_TYPE_SCHED_CLS) 타입이며, e0362f5bd9163a0a(명령 시퀀스에 대한 sha 합계) +tag를 가지고 있으며, Apr 09/16:24 에 root uid 0에 의해 로드 되었습니다. BPF 명령 시퀀스는 11,144 바이트 이며, 주입된 이미지는 7,721 바이트입니다. +프로그램 자체 (map 제외)는 사용자 uid 0에 대해 계산된/소비된 accounted/charged 12,288 바이트를 사용합니다. +그리고 BPF 프로그램은 ID가 18, 20, 8, 5, 6 및 14 인 BPF 맵을 사용 합니다. 이후의 ID는 정보를 얻거나 맵을 덤프 하는 데 더 사용될 수 있습니다. +또한 bpftool은 프로그램이 실행하는 BPF 명령의 덤프 요청을 실행 할 수 있습니다. +# bpftool prog dump xlated id 406 +0: (b7) r7 = 0 +1: (63) *(u32 *)(r1 +60) = r7 +2: (63) *(u32 *)(r1 +56) = r7 +3: (63) *(u32 *)(r1 +52) = r7 +[...] +47: (bf) r4 = r10 +48: (07) r4 += -40 +49: (79) r6 = *(u64 *)(r10 -104) +50: (bf) r1 = r6 +51: (18) r2 = map[id:18] <-- BPF map id 18 +53: (b7) r5 = 32 +54: (85) call bpf_skb_event_output#5656112 <-- BPF helper 호출 +55: (69) r1 = *(u16 *)(r6 +192) +[...] + +bpftool은 BPF helper 또는 다른 BPF 프로그램에 대한 호출 뿐만 아니라 위의 그림과 같이 BPF map ID를 명령 스트림에 상관 관계가 있습니다. +명령 덤프는 커널의 BPF verifier와 동일한 'pretty-printer'를 다시 사용 합니다. +위의 xlated 명령에서 생성된 프로그램이 주입 되어 실제 JIT 이미지가 실행 되므로 bpftool을 통해서 덤프 될 수 있습니다. + +# bpftool prog dump jited id 406 +0: push %rbp +1: mov %rsp,%rbp +4: sub $0x228,%rsp +b: sub $0x28,%rbp +f: mov %rbx,0x0(%rbp) +13: mov %r13,0x8(%rbp) +17: mov %r14,0x10(%rbp) +1b: mov %r15,0x18(%rbp) +1f: xor %eax,%eax +21: mov %rax,0x20(%rbp) +25: mov 0x80(%rdi),%r9d +[...] + +BPF JIT 개발자를 중심으로, 실제 네이티브 opcode로 디스 어셈블리를 interleave하는 옵션도 있습니다: +# bpftool prog dump jited id 406 opcodes +0: push %rbp +55 +1: mov %rsp,%rbp +48 89 e5 +4: sub $0x228,%rsp +48 81 ec 28 02 00 00 +b: sub $0x28,%rbp +48 83 ed 28 +f: mov %rbx,0x0(%rbp) +48 89 5d 00 +13: mov %r13,0x8(%rbp) +4c 89 6d 08 +17: mov %r14,0x10(%rbp) +4c 89 75 10 +1b: mov %r15,0x18(%rbp) +4c 89 7d 18 +[...] + +커널에서 디버깅 할 때 유용 할 수있는 일반적인 BPF 명령어에 대해 동일한 interleave을 수행 할 수 있습니다: +# bpftool prog dump xlated id 406 opcodes +0: (b7) r7 = 0 +b7 07 00 00 00 00 00 00 +1: (63) *(u32 *)(r1 +60) = r7 +63 71 3c 00 00 00 00 00 +2: (63) *(u32 *)(r1 +56) = r7 +63 71 38 00 00 00 00 00 +3: (63) *(u32 *)(r1 +52) = r7 +63 71 34 00 00 00 00 00 +4: (63) *(u32 *)(r1 +48) = r7 +63 71 30 00 00 00 00 00 +5: (63) *(u32 *)(r1 +64) = r7 +63 71 40 00 00 00 00 00 +[...] + +graphviz의 도움을 받아 프로그램의 기본 블록을 시각화 할 수도 있습니다. +이 목적을 위해 bpftool은 나중에 png 파일로 변환 할 수 있는 일반 BPF xlated 명령 덤프 대신 도트 파일을 생성하는 +시각적 덤프 모드를 가지고 있습니다: + +# bpftool prog dump xlated id 406 visual &> output.dot +$ dot -Tpng output.dot -o output.png + +또 다른 옵션은 dot 파일을 뷰어인 dotty로 전달 하는 것이며, 즉 dotty output.dot 하는 경우 , +bpg_host.o 프로그램의 결과는 다음과 같습니다(작은 발췌). xlated 명령어 덤프는 만약 BPF 인터프리터를 통해 +실행될 경우와 같은 명령어를 덤프한다는 의미의 verifier후 BPF 명령어 이미지를 제공합니다. +커널에서 verifier는 BPF 로더가 제공 한 본래 명령어의 다양한 다시 쓰기을 수행합니다. + +다시 쓰기의 한가지 예는 런타임 성능을 향상 시키기 위해, helper 함수의 인라이닝(inline) 이며, +여기 hash 테이블에 대한 map 조회 경우 입니다: +# bpftool prog dump xlated id 3 +0: (b7) r1 = 2 +1: (63) *(u32 *)(r10 -4) = r1 +2: (bf) r2 = r10 +3: (07) r2 += -4 +4: (18) r1 = map[id:2] <-- BPF map id 2 +6: (85) call __htab_map_lookup_elem#77408 <-+ BPF helper inlined rewrite +7: (15) if r0 == 0x0 goto pc+2 | +8: (07) r0 += 56 | +9: (79) r0 = *(u64 *)(r0 +0) <-+ +10: (15) if r0 == 0x0 goto pc+24 +11: (bf) r2 = r10 +12: (07) r2 += -4 +[...] + +bpftool은 모듈이 참조할 수 있는 커널 내부의 함수나 변수의 심볼 정보인 kallsyms를 통해, helper 함수 +또는 BPF 호출에서 BPF 호출에 연결 합니다. 따라서 주입되어 BPF 프로그램이 kallsyms(bpf_jit_kallsyms 파라메터를 통해)에 +노출 되어 있고, kallsyms주소가 난독화되지 않는지 확인하십시오 호출은 call bpf_unspec#0 로 표시됩니다): + +# echo 0 > /proc/sys/kernel/kptr_restrict +(역자주 : kptr_restrict 파라메터 설명 +이 토글은 /proc 및 다른 인터페이스를 통해 커널 주소를 노출하는 데 제한이 있는지 여부를 나타냅니다. +kptr_restrict가 0(기본값)으로 설정되면 출력하기 전에 주소가 hash됩니다. (이것은 % p와 동일합니다.) +kptr_restrict를 (1)로 설정하면 커널 포인터는 %pK를 사용하여 사용자가 +CAP_SYSLOG 권한을 가지고 있지 않으면 형식 지정자는 "0" 으로 바뀝니다. 유효 사용자 및 그룹 ID는 실제 ID와 같습니다. +이것은 %pK 확인은 open() 시간 보다는 read () 시간에 완료 되었기 때문 이며, 따라서 open()과 read() 사이에서 권한이 +상승하면(예 :setuid 바이너리) 그런 다음 %pK는 권한이 없는 사용자에게 커널 포인터를 누설하지 않습니다 +이것은 일시적인 해결책 일뿐입니다. 올바른 장기적인 해결책은 open() 시간에 권한 검사를 수행하는 것입니다. +권한이없는 사용자에 대한 커널 포인터 누설 값이 문제가되는 경우 dmesg_restrict를 사용하여 +dmesg(8)에서 %pK 사용을 방지하려면 %pK를 사용하는 파일에서 전역 읽기 사용 권한을 제거하는 것이 좋습니다. +kptr_restrict가 (2)로 설정되면 %pK를 사용하여 출력된 커널 포인터는 권한에 관계없이 "0"으로 바뀝니다. +) + +# echo 1 > /proc/sys/net/core/bpf_jit_kallsyms +(역자주 : bpf_jit_kallsyms 파라메터 설명 +bpf: make jited programs visible in traces commit 메세지 참조 +https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=74451e66d516c55e3 + +Berkeley Packet Filter Just in Time 컴파일러가 활성화 되면 컴파일된 이미지는 커널에 알려지지 않는 주소이며, +이것은 추적 이나 /proc/kallsyms에 나타나지 않는 것을 의미 합니다. + +이것을 활성화 하면 디버깅/추적에 사용할 수 있는 이러한 주소를 내보낼수 있습니다. +bpf_jit_harden 파라메터 값이 활성화 된 경우에는 이 기능은 비활성화 됩니다. +설정값 : +0 - JIT kallsyms 내보내기 비활성화(기본값) +1 - 인가받은 사용자들에 대해서 JIT kallsyms 내보내기 활성화 +) + +BPF 호출에서 BPF 호출은 JIT 경우뿐만 아니라 인터프리터와 상관 관계가 있습니다. +뒤에서는 서브 프로그램의 태그가 호출 대상으로 표시됩니다. +각각의 경우, pc+2는 호출 대상의 pc-상대 오프셋이며, 서브 프로그램을 나타냅니다. +# bpftool prog dump xlated id 1 +0: (85) call pc+2#__bpf_prog_run_args32 +1: (b7) r0 = 1 +2: (95) exit +3: (b7) r0 = 2 +4: (95) exit + +덤프의 주입된 변형: +# bpftool prog dump xlated id 1 +0: (85) call pc+2#bpf_prog_3b185187f1855c4c_F +1: (b7) r0 = 1 +2: (95) exit +3: (b7) r0 = 2 +4: (95) exit + +tail 호출의 경우, 커널은 내부적으로 하나의 명령어로 매핑을 하며, bpftool은 디버깅을 쉽게하기 위해 +helper 호출과 연관 시킵니다: + +# bpftool prog dump xlated id 2 +[...] +10: (b7) r2 = 8 +11: (85) call bpf_trace_printk#-41312 +12: (bf) r1 = r6 +13: (18) r2 = map[id:1] +15: (b7) r3 = 0 +16: (85) call bpf_tail_call#12 +17: (b7) r1 = 42 +18: (6b) *(u16 *)(r6 +46) = r1 +19: (b7) r0 = 0 +20: (95) exit + +# bpftool map show id 1 +1: prog_array flags 0x0 +key 4B value 4B max_entries 1 memlock 4096B + +map dump 서브 명령을 사용하여, 현재의 모든 맵 요소를 반복하고 키/값 쌍으로 16진 수로 덤프하는 +전체 맵을 덤프할 수 있습니다. 곧 나오는 BTF(BPF 타입 포멧), 맵은 키/값 구조로 디버깅 정보를 가지고 있으며, +bpftool은 16 진수 덤프 외에도 내용을 'pretty-print' 할수 있습니다: +# bpftool map dump id 5 +key: +f0 0d 00 00 00 00 00 00 0a 66 00 00 00 00 8a d6 +02 00 00 00 +value: +00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +key: +0a 66 1c ee 00 00 00 00 00 00 00 00 00 00 00 00 +01 00 00 00 +value: +00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +[...] +Found 6 elements + +특정 키에 대한 map에서 조회, 업데이트, 삭제 및 다음 키 얻기 작업은 bpftool을 통해서도 수행 할 수 있습니다. + +BPF sysctls +리눅스 커널에서는 몇가지 BPF와 관련된 sysctl에 대한 제공을 하며, 이 장에서는 이 내용을 다룹니다. + +* /proc/sys/net/core/bpf_jit_enable: BPF JIT 컴파일러를 활성화 혹은 비활성화 하도록 설정합니다. +설정값: +0 - JIT를 비활성화 하며, 인터프리터만 사용합니다 (커널 기본값) +1 - JIT 컴파일러를 활성화 합니다. +2 - JIT를 활성화 하며, 커널 로그에 emit 디버깅 추적을 남깁니다. + +다음 절에서 설명하는 것 처럼, bpf_jit_disasm 도구는 JIT 컴파일러가 디버깅 모드(옵션 2)로 +설정된 경우 디버깅 추적을 처리하는 데 사용 할 수 있습니다. + +* /proc/sys/net/core/bpf_jit_harden: BPF JIT 하드닝를 활성화 혹은 비활성화 하도록 설정합니다. +하드닝 기능을 사용하면 성능이 저하되지만, BPF 프로그램의 즉각적인 값을 무시함으로써 +JIT 스프레이 공격에 대해 완화 할 수 있습니다. 인터프리터를 통해 처리되는 프로그램의 경우 +즉시 값의 블라인드가 필요하지 않거나 수행되지 않습니다. + +설정값: +0 - JIT 하드닝 비활성화 (커널의 기본값) +1 - 허가받은 사용자들에 대해서 JIT 하드닝 활성화 +2 - 모든 사용자들에 대해서 JIT 하드닝 활성화 + +* /proc/sys/net/core/bpf_jit_kallsyms: 주입된 프로그램을 /proc/kallsyms에 대한 커널 +심볼로 내보내기를 활성화하거나 비활성화 하도록 설정하며, perf 도구와 함께 사용할 수 있으며 이러한 주소가 +스택 해제를 위해 커널에 인식 되며, 예를 들어 스택 추적을 덤프라는 데 사용됩니다. +심볼 이름에는 BPF 프로그램 태그 (bpf_prog_)가 포함됩니다. +만약 bpf_jit_harden 파라메터 값이 활성화 되어 있다면, 이 기능은 비활성화 됩니다. + +설정값: +0 - JIT kallsyms 내보내기 비활성화(커널 기본값) +1 - 인가받은 사용자들에 대해서 JIT kallsyms 내보내기 활성화 + +* /proc/sys/kernel/unprivileged_bpf_disabled: +bpf(2) 시스템 호출의 권한없는 사용을 활성화하거나 비활성화 하도록 설정합니다. +Linux 커널은 기본적으로 bpf(2)를 사용하는 권한 없는 사용에 대해서 활성화 되어 있으며, +이 옵션이 바뀌면, 다음 재부팅 때까지 권한이 없는 사용이 영구적으로 비 활성화됩니다. +이 sysctl 설정은 일회성 스위치 이며, 즉 일단 설정되면, 응용프로그램이나 관리자가 더 이상 +값을 재설정 할수 없습니다. 이 설정은 프로그램을 커널에 로드하기 위해 bpf(2) 시스템 호출을 사용하지 않는 +seccomp 또는 기존 소켓 필터와 같은 cBPF 프로그램에는 영향을 주지 않습니다. +설정값: +0 - 권한없는 bpf 시스템 호출 사용 활성화 (커널 기본값) +1 - 권한없는 bpf 시스템 호출 사용 비활성화 + +Kernel Testing +Linux 커널은 커널 소스 트리에서 tools/testing/selftest/bpf 아래에 BPF +자가 테스트 모음을 제공 합니다. + +$ cd tools/testing/selftests/bpf/ +$ make +# make run_tests + +테스트 모음는 BPF verifier에 대한 테스트 케이스, 프로그램 태그, BPF map 인터페이스 및 +map 유형에 대한 다양한 테스트를 포함합니다. LLVM 백엔드 확인을위한 C 코드와 인터프리터 및 JIT 테스트를 위해 +커널에서 실행되는 cBPF asm 코드 뿐만 아니라 eBPF의 다양한 런타임 테스트가 포함되어 있습니다. + +JIT 디버깅 + +감사를 수행 하거나 확장 기능을 수행하는 JIT 개발자의 경우, 각 컴파일러 실행은 생성된 JIT 이미지를 다음을 통해 커널 로그에 출력 할수 있습니다: +# echo 2 > /proc/sys/net/core/bpf_jit_enable + +새 BPF 프로그램이 로드 될 때마다 JIT 컴파일러는 출력을 덤프하고 dmesg를 통해 점검 할 수 있으며, 예를 들면 다음과 같습니다: +[ 3389.935842] flen=6 proglen=70 pass=3 image=ffffffffa0069c8f from=tcpdump pid=20583 +[ 3389.935847] JIT code: 00000000: 55 48 89 e5 48 83 ec 60 48 89 5d f8 44 8b 4f 68 +[ 3389.935849] JIT code: 00000010: 44 2b 4f 6c 4c 8b 87 d8 00 00 00 be 0c 00 00 00 +[ 3389.935850] JIT code: 00000020: e8 1d 94 ff e0 3d 00 08 00 00 75 16 be 17 00 00 +[ 3389.935851] JIT code: 00000030: 00 e8 28 94 ff e0 83 f8 01 75 07 b8 ff ff 00 00 +[ 3389.935852] JIT code: 00000040: eb 02 31 c0 c9 c3 + +flen는 BPF 프로그램 여기서는 6개 BPF 명령어이며, proglen는 opcode 이미지에 대해 JIT에서 생성된 70 byte +를 알려 줍니다. pass는 이미지가 3 단계 compiler passes 에서 생성 된 것을 의미 하며, 예를 들어 x86_64는 가능한 경우에 +이미지 크기를 줄이기 위해 다양한 최적화 단계를 가질수 있습니다. image는 생성 된 JIT 이미지의 주소를 포함하고 있으며, +from및 pid는 컴파일 프로세스를 트리거 한 사용자 영역 응용 프로그램 이름과 PID입니다. eBPF 및 cBPF JIT의 덤프 출력은 동일한 형식입니다. + +tools/bpf/ 아래의 커널 트리에는 bpf_jit_disasm 불리는 도구가 있습니다. 최신 덤프를 읽고 추가 검사를 위해 디스 어셈블리를 출력합니다: +# ./bpf_jit_disasm +70 bytes emitted from JIT compiler (pass:3, flen:6) +ffffffffa0069c8f + : +0: push %rbp +1: mov %rsp,%rbp +4: sub $0x60,%rsp +8: mov %rbx,-0x8(%rbp) +c: mov 0x68(%rdi),%r9d +10: sub 0x6c(%rdi),%r9d +14: mov 0xd8(%rdi),%r8 +1b: mov $0xc,%esi +20: callq 0xffffffffe0ff9442 +25: cmp $0x800,%eax +2a: jne 0x0000000000000042 +2c: mov $0x17,%esi +31: callq 0xffffffffe0ff945e +36: cmp $0x1,%eax +39: jne 0x0000000000000042 +3b: mov $0xffff,%eax +40: jmp 0x0000000000000044 +42: xor %eax,%eax +44: leaveq +45: retq + +또는 이 도구는 디스어셈블 와 함께 관련 opcode를 덤프 할 수도 있습니다. +# ./bpf_jit_disasm -o +70 bytes emitted from JIT compiler (pass:3, flen:6) +ffffffffa0069c8f + : +0: push %rbp +55 +1: mov %rsp,%rbp +48 89 e5 +4: sub $0x60,%rsp +48 83 ec 60 +8: mov %rbx,-0x8(%rbp) +48 89 5d f8 +c: mov 0x68(%rdi),%r9d +44 8b 4f 68 +10: sub 0x6c(%rdi),%r9d +44 2b 4f 6c +14: mov 0xd8(%rdi),%r8 +4c 8b 87 d8 00 00 00 +1b: mov $0xc,%esi +be 0c 00 00 00 +20: callq 0xffffffffe0ff9442 +e8 1d 94 ff e0 +25: cmp $0x800,%eax +3d 00 08 00 00 +2a: jne 0x0000000000000042 +75 16 +2c: mov $0x17,%esi +be 17 00 00 00 +31: callq 0xffffffffe0ff945e +e8 28 94 ff e0 +36: cmp $0x1,%eax +83 f8 01 +39: jne 0x0000000000000042 +75 07 +3b: mov $0xffff,%eax +b8 ff ff 00 00 +40: jmp 0x0000000000000044 +eb 02 +42: xor %eax,%eax +31 c0 +44: leaveq +c9 +45: retq +c3 + + +보다 최근에 bpftool은 이미 시스템에 로드 된 BPF 프로그램 ID를 기반으로 BPF JIT 이미지를 덤프하는 것과 동일한 기능을 +채택했습니다 (bpftool 섹션 참조 부탁드립니다). 주입된 BPF 프로그램의 성능 분석을 위해 일반적으로 perf를 사용할 수 있습니다. +전제 조건으로, 주입된 프로그램은 kallsyms 인프라를 통해 export 되어야 합니다. + +# echo 1 > /proc/sys/net/core/bpf_jit_enable +# echo 1 > /proc/sys/net/core/bpf_jit_kallsyms + +bpf_jit_kallsyms 파라메터를 활성화 또는 비 활성화에는 관련 BPF 프로그램을 다시 로드 할 필요가 없습니다. +다음으로 BPF 프로그램을 프로파일링하기 위한 작은 작업 흐름 예제가 제공됩니다. 만들어진 tc BPF 프로그램은 +bpf_clone_redirect() helper 함수에서 perf가 실패한 할당을 기록하는 데 사용됩니다. 직접 쓰기를 사용하기 때문에, +bpf_try_make_head_writable() 함수에 실패하여 복제 된 skb를 다시 해제하고 오류 메시지와 함께 리턴합니다. +perf는 모든 kfree_skb 이벤트를 기록합니다. +# tc qdisc add dev em1 clsact +# tc filter add dev em1 ingress bpf da obj prog.o sec main +# tc filter show dev em1 ingress +filter protocol all pref 49152 bpf +filter protocol all pref 49152 bpf handle 0x1 prog.o:[main] direct-action id 1 tag 8227addf251b7543 + +# cat /proc/kallsyms +[...] +ffffffffc00349e0 t fjes_hw_init_command_registers [fjes] +ffffffffc003e2e0 d __tracepoint_fjes_hw_stop_debug_err [fjes] +ffffffffc0036190 t fjes_hw_epbuf_tx_pkt_send [fjes] +ffffffffc004b000 t bpf_prog_8227addf251b7543 + +# perf record -a -g -e skb:kfree_skb sleep 60 +# perf script --kallsyms=/proc/kallsyms +[...] +ksoftirqd/0 6 [000] 1004.578402: skb:kfree_skb: skbaddr=0xffff9d4161f20a00 protocol=2048 location=0xffffffffc004b52c +7fffb8745961 bpf_clone_redirect (/lib/modules/4.10.0+/build/vmlinux) +7fffc004e52c bpf_prog_8227addf251b7543 (/lib/modules/4.10.0+/build/vmlinux) +7fffc05b6283 cls_bpf_classify (/lib/modules/4.10.0+/build/vmlinux) +7fffb875957a tc_classify (/lib/modules/4.10.0+/build/vmlinux) +7fffb8729840 __netif_receive_skb_core (/lib/modules/4.10.0+/build/vmlinux) +7fffb8729e38 __netif_receive_skb (/lib/modules/4.10.0+/build/vmlinux) +7fffb872ae05 process_backlog (/lib/modules/4.10.0+/build/vmlinux) +7fffb872a43e net_rx_action (/lib/modules/4.10.0+/build/vmlinux) +7fffb886176c __do_softirq (/lib/modules/4.10.0+/build/vmlinux) +7fffb80ac5b9 run_ksoftirqd (/lib/modules/4.10.0+/build/vmlinux) +7fffb80ca7fa smpboot_thread_fn (/lib/modules/4.10.0+/build/vmlinux) +7fffb80c6831 kthread (/lib/modules/4.10.0+/build/vmlinux) +7fffb885e09c ret_from_fork (/lib/modules/4.10.0+/build/vmlinux) + +perf로 기록 된 스택 추적은 함수 호출 추적의 일부로 bpf_prog_8227addf251b7543() 심볼을 표시하며. +태그 8227addf251b7543을 가진 BPF 프로그램이 kfree_skb 이벤트와 관련이 있다는 것을 의미하며, +앞서 말한 프로그램은 tc에 표시된 것처럼 ingress hook의 netdevice em1에 연결되었습니다. + +Introspection(자가 검사) +리눅스 커널은 BPF 와 XDP 주변의 다양한 tracepoint들을 제공하는데, 이는 추가적인 introspection를 뜻하며, +예를 들어 사용자 영역 프로그램과 bpf 시스템 콜의 대화를 추적 할 수 있습니다. + +BPF를 위한 tracepoint: +# perf list | grep bpf: +bpf:bpf_map_create [Tracepoint event] +bpf:bpf_map_delete_elem [Tracepoint event] +bpf:bpf_map_lookup_elem [Tracepoint event] +bpf:bpf_map_next_key [Tracepoint event] +bpf:bpf_map_update_elem [Tracepoint event] +bpf:bpf_obj_get_map [Tracepoint event] +bpf:bpf_obj_get_prog [Tracepoint event] +bpf:bpf_obj_pin_map [Tracepoint event] +bpf:bpf_obj_pin_prog [Tracepoint event] +bpf:bpf_prog_get_type [Tracepoint event] +bpf:bpf_prog_load [Tracepoint event] +bpf:bpf_prog_put_rcu [Tracepoint event] + +perf를 사용한 예제 사용법(여기에 사용 된 sleep 예제 대신 tc 와 같은 특정 응용 프로그램을 사용할 수 있습니다): +# perf record -a -e bpf:* sleep 10 +# perf script +sock_example 6197 [005] 283.980322: bpf:bpf_map_create: map type=ARRAY ufd=4 key=4 val=8 max=256 flags=0 +sock_example 6197 [005] 283.980721: bpf:bpf_prog_load: prog=a5ea8fa30ea6849c type=SOCKET_FILTER ufd=5 +sock_example 6197 [005] 283.988423: bpf:bpf_prog_get_type: prog=a5ea8fa30ea6849c type=SOCKET_FILTER +sock_example 6197 [005] 283.988443: bpf:bpf_map_lookup_elem: map type=ARRAY ufd=4 key=[06 00 00 00] val=[00 00 00 00 00 00 00 00] +[...] +sock_example 6197 [005] 288.990868: bpf:bpf_map_lookup_elem: map type=ARRAY ufd=4 key=[01 00 00 00] val=[14 00 00 00 00 00 00 00] +swapper 0 [005] 289.338243: bpf:bpf_prog_put_rcu: prog=a5ea8fa30ea6849c type=SOCKET_FILTER + +BPF 프로그램의 경우, 개별 프로그램 태그가 표시됩니다. +디버깅을 위해 XDP에는 예외가 발생할 때 트리거되는 tracepoint도 있습니다: +# perf list | grep xdp: +xdp:xdp_exception [Tracepoint event] + +예외는 다음 시나리오에서 트리거 됩니다: +* BPF 프로그램은 유효하지 않은/알 수없는 XDP 액션 코드를 반환 했습니다. +* BPF 프로그램은 비정상 종료를 나타내는 XDP_ABORTED를 반환 했습니다. +* BPF 프로그램은 XDP_TX와 함께 반환 되었지만, 예를 들어 포트가 작동하지 않거나, +transmit ring이 가득 차거나, 할당 실패 로 인해, 전송 시 오류가 발생했습니다. + +두개의 tracepoint class는 BPF 프로그램 자체가 하나 이상의 tracepoint에 연결되어 검사될 수 있으며, +예를 들어, bpf_perf_event_output() helper 함수를 통해 map에 추가 정보를 수집하거나 +사용자 영역 수집기에 이러한 이벤트를 punting 할수 있습니다. + +Miscellaneous + +BPF 프로그램과 map는 RLIMIT_MEMLOCK/* 최대 잠긴 기억 장소 주소 공간 */을 기준으로 한 메모리 이며, perf 와 비슷합니다. +메모리에 잠길 수 있는 시스템 페이지 단위의 현재 사용 가능한 크기는 ulimit -l을 통해 검사 할 수 있습니다. +setrlimit 시스템 콜 man 페이지는 자세한 내용을 제공합니다. + +BPF 시스템 콜이 EPERM의 errno와 함께 반환 될 수 있으며, 보통은 더 복잡한 프로그램이나 큰 BPF 맵을 로드하는 데는 +기본 한계가 충분하지 않습니다.더 복잡한 프로그램이나 큰 BPF map을 로드하는 데에는 기본 한계값은 충분하지 않으며, +그래서 BPF 시스템 콜은 EPERM 의 에러 번호(errno)를 반환활 것 입니다. +이런 상황에서는 ulimit -l ulimited 혹은 충분히 큰 제한 값을 수행이 될수 있습니다. +RLMIT_MEMLOCK는 주로 권한이 없는 사용자에게 제한이 적용됩니다. +설정에 따라, 권한있는 사용자에 대해 더 높은 제한을 설정하는 것이 종종 허용 됩니다. + +프로그램 유형 +이 글을 쓸 당시, 사용 가능한 18 가지의 다른 BPF 프로그램의 유형이 있으며, 네트워킹을 위한 주요 유형 중 두가지는 +XDP BPF 프로그램 과 tc BPF 프로그램에 대해 하위 세션에서 자세히 설명합니다. +LLVM, iproute2 또는 기타 도구에 대한 두 가지 프로그램 유형에 대한 광범위한 사용 예는 툴체인 섹션에 전달되어 있으며 +여기서는 다루지 않습니다. 대신이 이 섹션에서는 아키텍처, 개념 및 사용 사례에 중점을 둡니다. + +XDP +XDP는 eXpress Data Path의 약자이며 리눅스 커널에서 고성능 프로그램 가능한 패킷 처리를 +가능하게 하는 BPF를 위한 프레임 워크를 제공합니다.XDP는 BPF 프로그램을 소프트웨어의 가장 빠른 시점, +즉 네트워크 드라이버가 패킷을 받는 순간 실행합니다. 고속 경로의 이시점에서, 드라이버는 패킷등을 +GRO(generic receive offload)로 푸시 하지 않고, 패킷을 네트워킹 스택 보다 더 위로 올리기 위한 +skb 할당과 같은 비용이 많이 드는 작업을 수행하지 않고, +수신링에서 패킷을 수신합니다. 따라서, XDP BPF 프로그램은 처리를 위해 CPU에서 사용 할수 있는 가장 빠른 시점에 실행됩니다. +XDP는 리눅스 커널 및 인프라와 함께 동작 하므로, 사용자 영역에서만 작동하는 다양한 네트워크 프레임 워크 +(dpdk, netmap,등등)처럼 커널을 우회하는 동작을 하지 않습니다. 패킷을 커널 공간에 유지하면 다음과 같은 몇 가지 주요 이점이 있습니다: + +* XDP는 BPF helper 호출 자체에서 라우팅 테이블, 소켓등과 같이 업스트립에서 개발된 모든 커널 드라이버, +사용자 영역 툴 또는 다른 사용 가능한 커널 내부 구조를 재사용 할수 있습니다. +* 커널 공간에 존재하는 XDP는 하드웨어에 접근을 위한 커널의 나머지 부분과 동일한 보안 모델을 가지고 있습니다. +* 처리 된 패킷이 이미 커널에 있기 때문에 컨테이너 혹은 커널의 네트워킹 스택 자체에서 사용되는 네임 스페이스와 +같은 다른 커널 내부 엔터티로 패킷을 유연하게 전달할 수 있으므로 커널/사용자 영역 경계를 넘을 필요가 없습니다. +이것은 멜트다운과 스펙터의 시기와 관련이 있습니다. +* XDP에서 커널의 robust로 패킷을 퍼팅(punting)하여 광범위하게 사용되는 TCP/IP 스택을 쉽게 사용할 수 있으며, +완전히 재사용 할 수 있으며 사용자 공간 프레임 워크와 별도로 TCP/IP 스택을 유지 관리 할 필요가 없습니다. +* BPF를 이용하여 커널의 시스템 call ABI 호출하는 것과 동일한 naver-break-user-space 보장을 통해 +안정적인 ABI를 유지하며, 모듈과 비교하여 커널 동작의 안정성을 보장하는 BPF verifier 덕분에 +안정 장치를 제공하는 완전한 프로그래밍 기능을 사용 할수 있습니다. +* XDP를 사용하면 네트워크 트래픽 중단이나 커널/시스템 재부팅 없이 런타임에 프로그램을 atomically하게 +스왑할 수 있습니다. +* XDP를 사용하면 커널에 통합된 워크로드를 유연한 구조화 할 수 있습니다. 예를 들어, "busy polling"또는 +"interrupt driven"모드에서 작동 할 수 있습니다. 명시 적으로 CPU를 XDP 전용으로 지정할 필요는 없습니다. +특별한 하드웨어 요구 사항은 없으며, hugepages에 의존하지 않습니다. +* XDP에는 타사 커널 모듈이나 라이센스가 필요하지 않습니다. 이것은 장기적인 아키텍처 솔루션으로 리눅스 커널의 +핵심 부분이며 커널 커뮤니티에 의해 개발되었습니다. +* XDP는 4.8 이상에 해당하는 커널을 실행하는 주요 배포판을 통해 모든 곳에서 이미 활성화되고 제공되며 대부분의 주요 +10G 이상의 네트워킹 드라이버를 지원합니다(XDP native mode). + +드라이버에서 BPF를 실행하기 위한 프레임워크로서, XDP는 패킷이 선형적으로 배치되어 BPF 프로그램이 읽고 쓸 수 +있는 단일 DMA의 페이지에 맞도록 보장합니다. 또한 XDP는 bpf_xdp_adjust_head() BPF helper 함수를 사용하여, +사용자 정의 캡슐화 헤더를 구현하거나 bpf_xdp_adjust_meta()를 통해 패킷 앞에 사용자 정의 메타 데이터를 추가하는 +프로그램에서 256 바이트의 추가 헤드룸을 사용할 수 있도록합니다. + +프레임워크에서 BPF 프로그램이 드라이버에게 패킷 진행 방법을 지시하기 위해 반환 할 수 있는 XDP 액션 코드가 +아래의 절에 자세히 설명되어 있으며, XDP 계층에서 실행되는 BPF 프로그램을 atomically 하게 바꿀 수 있습니다. +XDP는 고성능을 위해 디자인 되었습니다. BPF는 '직접 패킷 액세스(direct packet access)'를 통해 패킷 +데이터에 액세스 할 수 있으며, 이는 프로그램이 레지스터에서 직접 데이터 포인터를 보유하고, +내용을 레지스터에 로드하고, 레지스터로 부터 패킷으로 씁니다. + +BPF 컨텍스트로 BPF 프로그램에 전달 된 XDP의 패킷 표현은 다음과 같습니다: +struct xdp_buff { +void *data; +void *data_end; +void *data_meta; +void *data_hard_start; +struct xdp_rxq_info *rxq; +}; + +data는 페이지의 패킷 데이터의 시작을 가르키며, 이름에서 알 수 있듯이 data_end는 패킷 +데이터의 끝을 가르킵니다. XDP는 헤드 룸을 허용하기 때문에 data_hard_start는 페이지에서 +가능한 최대 헤드 룸 시작을 가리 키며, 즉, 패킷을 캡슐화 해야 할 때 +데이터가 bpf_xdp_adjust_head()를 통해 data_hard_start쪽으로 더 가까이 이동합니다. +같은 BPF 도우미 함수는 데이터를 data_hard_start에서 더 멀리 이동시키는 경우 역캡슐화를 허용합니다. +data_meta는 처음에는 data와 같은 위치를 가리키고 bpf_xdp_adjust_meta()는 일반적인 커널 네트워킹 +스택에서는 볼 수 없는 사용자 정의 메타 데이터 공간을 제공하기 위해 포인터를 data_hard_start방향으로 +이동할 수 있지만, tc BPF 프로그램은 XDP에서 skb로 전송되기 때문에 읽을 수 있습니다. 그 반대의 경우에는 +data_hard_start에서 data_meta를 다시 이동하여, 동일한 BPF helper 함수를 통해, +사용자 정의 메타 데이터의 크기를 제거하거나 줄일 수 있습니다. data_meta는 tc BPF 프로그램에서 액세스 할 수있는 +skb->cb[] 제어 블럭의 경우와 유사하게 tail 호출 간에 상태를 전달하는 용도로만 사용할 수도 있습니다. +이것은 구조체 xdp_buff 패킷포인터에 대해 각가 다음과 같은 관계를 갖습니다: +data_hard_start <= data_meta <= data < data_end + +(역자주: 유태희님, 송태웅 님의 공유 하신 XDP 패킷 구조에 대해서 +분석된 내용을 바탕으로 아래의 구조를 설명 하였습니다. + +HEADROOM MAC HEADER IP HEADER TAIL/TAILROOM END +| | | | | | +| | | | | | +|---------------------|-------|--------------|------------------------------------------------|---------------------| +| | | | | | +| | | | | | +| | | | | | +xdp->data_hard_start | xdp->data +xdp_frame | skb->data +| xdp->data_meta(이전 위치) +| +|xdp->data_meta(현재 위치): bpf_xdp_adjust_head() 함수를 통해, xdp->data_meta의 위치를 이동합니다. + + +@net/core/filter.c (4.17-rc7 기준) +커널에 선언된 bpf_xdp_adjust_head 함수 입니다. +.... +BPF_CALL_2(bpf_xdp_adjust_head, struct xdp_buff *, xdp, int, offset) +{ +void *xdp_frame_end = xdp->data_hard_start + sizeof(struct xdp_frame); +unsigned long metalen = xdp_get_metalen(xdp); +void *data_start = xdp_frame_end + metalen; +void *data = xdp->data + offset; + +if (unlikely(data < data_start || +data > xdp->data_end - ETH_HLEN)) +return -EINVAL; + +if (metalen) +memmove(xdp->data_meta + offset, +xdp->data_meta, metalen); +xdp->data_meta += offset; +xdp->data = data; + +return 0; +} +.... + +rxq 필드는 링 설정 시간(XDP 런타임이 아님)에 채워지는 수신 큐 메타 데이터 당 몇 가지 추가 정보를 가리 킵니다: +struct xdp_rxq_info { +struct net_device *dev; +u32 queue_index; +u32 reg_state; +} ____cacheline_aligned; +BPF 프로그램은 ifindex와 같은 netdevice 자체에서 추가 데이터 뿐만 아니라, queue_index를 검색 할 수 있습니다. + +BPF program return codes +XDP BPF 프로그램을 실행 한 후 드라이버에게 다음 패킷 처리 방법을 알리기 위해 프로그램에서 결정이 반환됩니다. +linux/bpf.h 시스템 헤더 파일에서 사용 가능한 모든 반환 결과가 나열됩니다: + +enum xdp_action { +XDP_ABORTED = 0, +XDP_DROP, +XDP_PASS, +XDP_TX, +XDP_REDIRECT, +}; + +이름에서 알 수 있듯이 XDP_DROP은 추가 리소스를 낭비하지 않고 드라이버 수준에서 바로 패킷을 삭제합니다. +이것은 특히 DDoS 완화 메커니즘 또는 방화벽을 일반적으로 구현하는 BPF 프로그램에 유용합니다. +XDP_PASS 리턴 코드는 패킷이 커널의 네트워킹 스택에 전달 될 수 있음을 의미합니다. +이 의미는 이 패킷을 처리하고 있던 현재 CPU는 이제 skb를 할당하고 채우고 GRO 엔진으로 전달합니다. +이것은 XDP가 없는 기본 패킷 처리 동작과 동일합니다. XDP_TX를 사용하면 BPF 프로그램은 방금 도착한 동일한 NIC에서 +네트워크 패킷을 전송할 수 있는 효율적인 옵션을 제공합니다. +이는 일반적으로 클러스터에서 후속 로드 밸런싱을 사용하는 방화벽과 같이, XDP BFP에서 재작성 후 들어오는 패킷을 다시 스위치로 +밀어 넣는 헤어핀(loopback) 로드 밸런서의 역활을 하는 몇 개의 노드가 구현되는 경우에 유용합니다. +XDP_REDIRECT는 XDP 패킷을 전송할 수 있지만 다른 NIC를 통해 전송할 수 있다는 점에서 XDP_TX와 유사합니다. +XDP_REDIRECT의 또 다른 옵션은 BPF cpumap으로 리디렉션하는 것이며, 이 의미는, NIC의 수신 대기열에서 XDP를 제공하는 +CPU는 계속해서 상위 커널 스택을 처리하기 위해 패킷을 원격 CPU로 푸시 할 수 있습니다. +이는 XDP_PASS와 유사하지만 XDP BPF 프로그램이 상위 계층으로 푸시하기 위해, 일시적으로 현재 패킷에 대한 작업을 수행하는 +것과 달리 들어오는 높은 부하를 계속 서비스 할 수있는 기능을 갖추고 있습니다. +마지막으로 프로그램에서 상태와 같은 예외를 표시하는 역할을 하는 XDP_ABORTED가 있으며, XDP_DROP와 동일한 동작을하며, +XDP_ABORTED는 추가로 잘못 감지 된 trace_xdp_exception tracepoint 을 전달합니다. + +XDP 사용 사례 +XDP의 주요 사용 사례 중 일부 이 하위 섹션에 나와 있습니다. +이 목록은 포괄적인 것은 아니며 XDP 및 BPF가 프로그래밍 할 수있는 효율성과 효율성을 고려할 때 매우 구체적인 사용 사례를 해결하기 +위해 쉽게 적용 할 수 있습니다. + +DDoS 완화, 방화벽 기능 +기본적인 XDP BPF 기능 중 하나이며 드라이버가 초기 단계에서 XDP_DROP를 사용하여 패킷을 drop하도록 지시하는 것으로 +패킷 당 비용이 매우 낮아 모든 종류의 효율적인 네트워크 정책 시행을 허용합니다. +이것은 모든 종류의 DDoS 공격애 대처할 필요가 있을 때 이상적이며, BPF에서 오버레드가 거의 없는 모든 종류의 방화벽 정책을 구현 할 수 있으며, +예를 들어 독립형 어플라이언스 (예를 들어, XDP_TX를 통한 'clean' 트래픽 제거) 또는 마지막 호스트 자체를 보호하는 노드 +(XDP_PASS 또는 cpumap XDP_REDIRECT를 통한 양호한 트래픽)에 널리 배치 됩니다. +오프로드 된 XDP는 라인 속도로 처리하면서, 이미 패킷 당 비용을 전체적으로 NIC로 완전히 옮김으로써 한 단계 더 나아갑니다. + +포워딩 및 로드밸런싱 +XDP의 또 다른 주요 사용 사례는 XDP_TX 또는 XDP_REDIRECT 동작을 통한 패킷 전달 및 로드 밸런싱 입니다. +패킷은 XDP 계층에서 실행되는 BPF 프로그램에 의해 중간에 mangle 될수 있으며, BPF 도우미 함수 기능은 패킷을 캡슐화 해제하고 +다시 캡슐화 하기 전에 캡슐화하기 위해패킷의 headroom을 늘리거나 줄일 수 있습니다. 원래 도착한 동일한 네트워크 장치에서 패킷을 +푸시 하는 XDP_TX hairpinned(혹은 loopback) 로드 밸런서를 사용하거나 XDP_REDIRECT 동작을 통해 전송을 위해 다른 +NIC로 전달할 수 있습니다. 이후의 리턴 코드는 BPF의 cpumap과 함께 로컬 스택을 통과 하기위한 패킷을 로드 밸런싱하기 위해 사용될 수 있지만 +원격의 non-XDP 프로세싱 CPU에서는 사용할 수 없습니다. + +사전 스택 필터링 / 처리 +정책 적용 외에도 XDP는 XDP_DROP의 경우, 커널의 네트워크 스택을 강화하는 데 사용 할 수 있으며, +이 의미는 네트워크 스택이 알기 전에 로컬 노트에 대해 관련성 없는 패킷을 가능한 빨리 삭제 할 수 있으며, +예를 들어 노드가 TCP 트래픽 만 처리하는 것을 알면, 모든 UDP, SCTP 또는 기타 L4 트래픽을 버릴 수 있습니다. 이것은 패킷이 GRO 엔진 과 같은 커널의 +흐름 분석기 및 다른 것들을 가로지를 필요가 없이 패킷을 버릴수 있기 때문에 커널의 공격 영역을 줄일 수 있다는 이점이 있습니다. +XDP의 조기 처리 단계 덕분에, 이것은 효과적으로 커널의 네트워킹 스택을 가장하며, 이러한 패킷은 네트워킹 장치에서 확인된적은 없습니다. +또한 스택의 수신 경로에 잠재적 인 버그가 발견되어 'ping of death'같은 시나리오가 발생하면 XDP를 사용하여 커널을 +재부팅하거나 서비스를 다시 시작하지 않고도 해당 패킷을 즉시 삭제할 수 있습니다. 이러한 프로그램을 atomically으로 스왑하여 +불량 패킷을 버리는 기능으로 인해 호스트에서 네트워크 트래픽이 중단되지 않습니다.사전 스택 처리를 위한 또 다른 사용 사례는 커널이 아직 패킷에 +대한 skb를 할당하지 않은 경우, BPF 프로그램은 패킷을 수정 할 수 있으며, 이 방법으로 네트워크 장비로 부터 스택에 수신된 패킷 인척 합니다. +이렇게하면 GRO 는 사용자 정의 프로토콜를 인식 못하기 때문에 어떠한 종료의 통계도 수행 +할수 없으며, GRO aggregation을 들어가기 전에 패킷을 캡슐화 해제할 수있는 사용자 정의 패킷 맹 글링 및 캡슐화 프로토콜이 있는 경우가 허용됩니다. +이는 일반적인 커널 스택에 '보이지'않으며, GRO(메타 데이터 매칭을 위해)를 집계하고, 나중에 예를 들어 사용할 수있는 다양한 skb 필드를 설정 하여, +skb 컨텍스트를 갖는 tc ingress BPF 프로그램과 함께 처리 할 수 있습니다. + +flow 샘플링, 모니터링 +또한 XDP는 패킷 모니터링, 샘플링 또는 기타 네트워크 분석과 같은 경우에 사용할 수 있으며, 예를 들어 경로의 중간 노드 +또는 끝 호스트에서 앞서 언급 한 사용 사례와 함께 사용할 수 있습니다.복잡한 패킷 분석을 위해 XDP는 네트워크 패킷 +(생략된 또는 전체 페이로드 포함) 및 사용자 지정 메타 데이터를 Linux perf 인프라에서 사용자 공간 응용 프로그램으로 제공되는 CPU 메모리 +매핑 링 버퍼 당 빠른 lock없이 신속하게 푸시 할 수있는 기능을 제공합니다. 이는 또한 흐름의 초기 데이터 만 분석하고 모니터링을 우회하는 양호한 +트래픽으로 판단되는 경우도 허용합니다. BPF가 제공하는 유연성으로 인해 사용자 정의 모니터링이나 샘플링을 구현할 수 있습니다. XDP BPF +프로덕션 사용의 한 예로 L4로드 균형 조정 및 DDoS 대응을 구현하는 Facebook SHIV및 Droplet infrastructure 가 있습니다. + +프로덕션 인프라를 Netfilter의 IPVS (IP Virtual Server)에서 XDP BPF로 마이그레이션하면 이전 IPVS 설정에 비해 속도가 10 배 빨라졌습니다. +이것은 netdev 2.1 컨퍼런스에서 처음 발표되었습니다: + +Slides: https://www.netdevconf.org/2.1/slides/apr6/zhou-netdev-xdp-2017.pdf +Video: https://youtu.be/YEU2ClcGqts + +iptables의 사용으로 인해 이것은 사용자 영역 우회 솔루션이 필요하다고 생각되는 공격 하에서 심각한 성능 문제를 일으켰지 만 NIC를 바쁘게 폴링하고 커널 +스택에 비용이 많이 드는 패킷을 다시 주입해야하는 등의 단점이있었습니다.eBPF와 XDP 로의 이전은 고성능 프로그래머블 패킷 처리를 커널 내부에서 +직접 수행함으로써 두 영역의 장점을 결합했습니다: + +Slides: https://www.netdevconf.org/2.1/slides/apr6/bertin_Netdev-XDP.pdf +Video: https://youtu.be/7OuOukmuivg + +XDP 동작 모드 +XDP에는 세 가지 작동 모드가 있습니다. '네이티브'XDP가 기본 모드입니다. XDP에 관해 이야기 할 때 일반적으로 이 모드가 암시됩니다. + +네이티브 XDP +이 모드는 XDP BPF 프로그램이 네트워킹 드라이버의 초기 수신 경로에서 직접 실행되는 기본 모드입니다. 10G 이상을 위해 널리 사용되는 NIC는 이미 네이티브 XDP를 지원합니다. + +오프로드 XDP +오프로드 XDP 모드에서 XDP BPF 프로그램은 호스트 CPU에서 실행되는 대신에 NIC로 직접 오프로드됩니다. +따라서 패킷 당 비용이 매우 낮아 호스트 CPU에서 완전히 푸시되고 NIC에서 실행되므로 네이티브 XDP에서 실행하는 것보다 훨씬 높은 성능을 제공합니다. +이 오프로드는 일반적으로 커널 내 JIT 컴파일러가 BPF를 후자의 기본 명령어로 변환하는 멀티 스레드, 멀티 코어 플로우 프로세서를 포함하는 SmartNIC에 의해 구현됩니다. +또한 오프로드 XDP를 지원하는 드라이버는 일부 BPF helper가 아직 또는 기본 모드에서만 사용할 수없는 경우에 대비해 네이티브 XDP를 지원합니다. + +일반 XDP + +네이티브 또는 오프로드 XDP를 아직 구현되지 않는 드라이버의 경우, 커널은 네트워킹 스택에서 많이 늦는 시점에서 실행되는 드라이버 변경이 필요없는 일반 XDP 옵션을 제공합니다. +이 설정은 주로 커널의 XDP API에 대해 프로그램을 작성 및 테스트하고 원시 또는 오프로드 모드의 성능 속도로 동작하지 않는 개발자를 대상으로합니다. +프로덕션 환경에서의 XDP 사용의 경우 네이티브 또는 오프로드 모드가 더 적합하며 XDP를 실행하는 데 권장되는 방법입니다. + +드라이버 지원 + +BPF와 XDP는 기능 및 드라이버 지원 측면에서 빠르게 진화하고 있기 때문에 다음은 커널 4.17에서의 네이티브 및 오프로드 된 XDP 드라이버 목록입니다. + +네이티브 XDP를 지원하는 드라이버 + +Drivers supporting native XDP + +* Broadcom +- bnxt + +* Cavium +- thunderx + +* Intel +- ixgbe +- ixgbevf +- i40e + +* Mellanox +- mlx4 +- mlx5 + +* Netronome +- nfp + +* Others +- tun +- virtio_net + +* Qlogic +- qede + +* Solarflare +- sfc [1] + +Drivers supporting offloaded XDP + +* Netronome +- nfp [2] + +XDP 프로그램 작성 및 로드 예제는 해당 도구 아래의 도구 모음 섹션에 포함되어 있습니다. +[1] sfc에 대한 XDP는 커널 4.17에서 트리 드라이버를 통해 사용할 수 있지만 곧 업스트림에 반영 예정에 있습니다. +[2] (1, 2) 현재 CPU 번호 검색과 같은 일부 BPF helper 기능은 오프로드 된 설정에서 사용할 수 없습니다. + +tc (트래픽 제어) +XDP와 같은 다른 프로그램 유형 과는 별도로 BPF는 네트워킹 데이터 경로의 커널 tc(트래픽 제어) 계층에서도 사용할 수 있습니다. +상위 수준에서 XDP BPF 프로그램과 tc BPF 프로그램을 비교할 때 세 가지 주요 차이점이 있습니다: + +*BPF 입력 컨텍스트는 xdp_buff가 아닌 sk_buff입니다. 커널의 네트워킹 스택이 패킷을 받으면 XDP 계층 다음에 버퍼를 할당하고 +패킷을 구문 분석하여 패킷에 대한 메타 데이터를 저장합니다. 이 표현을 sk_buff라고합니다.이후 이 구조는 BPF 입력 컨텍스트에서 노출되므로 +tc ingress 레이어의 BPF 프로그램이 스택에서 패킷에서 추출한 메타 데이터를 사용할 수 있습니다.이것은 유용 할 수 있지만,이러한 할당 및 메타 데이터 +추출을 수행하는 스택의 관련 비용과 함께 tc hook에 도달 할 때까지 패킷을 처리합니다. 정의에 따르면 xdp_buff는이 작업이 완료되기 전에, +XDP hook가 호출되기 때문에 이 메타 데이터에 액세스 할 수 없습니다. 이는 XDP와 tc hook 간의 성능 차이에 크게 관여됩니다. + +따라서 tc BPF hook에 연결 된 BPF 프로그램은 예를 들어, 읽기 혹은 쓰기 skb의 mark, pkt_type, protocol, priority, queue_mapping, +napi_id, cb[] array, hash, tc_classid or tc_index, vlan 메타데이터, XDP로 전송 된 사용자 정의 메타 데이터 및 다양한 다른 정보를 제 +공 할 수 있습니다. tc BPF에서 사용되는 struct __sk_buff BPF 컨텍스트의 모든 멤버는 linux/bpf.h 시스템 헤더에 정의되어 있습니다. +일반적으로 sk_buff는 장점과 단점이 모두 있는 xdp_buff 와 완전히 다릅니다. 예를 들어 sk_buff의 경우에는 연관된 메타 데이터를 mangle하는 것이 +훨씬 간단하지만, 많은 프로토콜 관련 정보 (예 : GSO 관련 상태) 를 가진 sk_buff가 단순히 패킷 데이터를 다시 작성하여 프로토콜을 간단하게 전환하는 되는 +것은 어렵습니다. 이것은 매번 패킷 내용에 액세스하는 비용이 들지 않고 메타 데이터를 기반으로 패킷을 처리하는 스택 때문입니다. 따라서 sk_buff 내부가 +올바르게 변환되도록 주의하면서, BPF helper 함수에서 추가 변환이 필요합니다. 그러나 xdp_buff의 경우 커널이 아직 sk_buff를 할당하지 않은 처음 +단계에 있기 때문에 이러한 문제는 발생하지 않으므로 모든 종류의 패킷 재 작성을 쉽게 실현할 수 있습니다. 그러나 xdp_buff의 경우에는이 단계에서 +mangling에 sk_buff 메타 데이터를 사용할 수 없다는 단점이 있습니다.후자는 XDP BPF에서 tc BPF로 사용자 지정 메타 데이터를 전달함으로써 +해결이 됩니다. 이러한 방식으로, 각 프로그램 유형의 한계는 사용 사례를 요구하는 두 유형의 보완 프로그램을 동작하는 것으로 해결 될 수 있습니다. + +* XDP와 비교하여 네트워킹 데이터 경로의 ingress 및 egress 지점에서 tc BPF 프로그램이 트리거 될 수 있으며, XDP의 경우는 반대로 ingress 만 가능합니다. +커널에서 sch_handle_ingress() 및 sch_handle_egress()라는 두 개의 hook 지점은 각각 __netif_receive_skb_core() 및 __dev_queue_xmit()에서 +트리거 됩니다. 후자의 두 가지는 데이터 경로의 주요 수신 및 전송 기능으로, XDP를 별도로 설정하면 노드에서 들어오고 나가는 모든 네트워크 패킷에 대해 트리거되어 +이러한 hook 지점에서 tc BPF 프로그램을 완벽하게 볼 수 있습니다. + +* tc BPF 프로그램은 네트워킹 스택의 일반 레이어에서 연결 지점에서 실행 되므로 드라이버 변경이 필요하지 않습니다. 따라서 모든 유형의 +네트워킹 장치에 연결할 수 있습니다. 이는 유연성을 제공하지만 네이티브 XDP 계층에서 실행하는 것과 비교하여 성능을 저하 시킵니다. +그러나 tc BPF 프로그램은 GRO이 실행 된 후 모든 프로토콜 처리, 기존 iptables 방화벽이 실행되기 전에 일반 커널의 네트워킹 데이터 경로의 가장 초기 단계에 +있으며, 예를 들어, iptables PREROUTING 또는 nftables ingress hooks 또는 다른 패킷 처리 와 같은 항목이 발생합니다. +마찬가지로 egress에서 tc BPF 프로그램은 전송을 위해 패킷을 드라이버에 전달 하기전 마지막 부분에서 실행이 되며 이 의미는 iptables POSTROUTING과 +같은 전통적인 iptables firewall hook 이후 패킷을 커널의 GSO 엔진에게 넘겨주기 전 부분을 설명하는내용입니다. 그러나 드라이버 변결을 요구하는 예외 +사항 중 하나는 tc BPF 프로그램을 오프로드 하는 것이며, 일반적으로 BPF 입력 컨텍스트, helper 기능 및 결정 코드의 차이로 인해 기능이 다르기 때문에 +오프로드된 XDP와 유사한 방식으로 SmartNIC에서 제공합니다. + +tc 계층에서 실행되는 BPF 프로그램은 cls_bpf classifier에서 실행됩니다. tc 용어는 BPF 연결 지점을 "classifier"로 설명하지만 cls_bpf가 수행 +할 수 있는 것을 충분하지 않는 표현을 하기 때문에 다소 오해의 소지가 있습니다. 즉, 완전히 프로그래밍 가능한 패킷 프로세서는 skb 메타 데이터 및 패킷 데이터를 +읽을 수있을뿐만 아니라 임의로 mangle하고 동작 판정으로 tc 처리를 종료 할 수 있습니다. 따라서 cls_bpf는 tc BPF 프로그램을 관리하고 실행하는 독립적인 엔티티로 +간주 될 수 있습니다. cls_bpf는 하나 이상의 tc BPF 프로그램을 보유 할 수 있습니다. Cilium이 cls_bpf 프로그램을 배포하는 경우 직접 실행 모드에서 지정된 hook에 +대해 단일 프로그램 만 연결합니다. 일반적으로 전통적인 TC 체계에는 classfier와 일치하는 하나 이상의 동작이 연결 된 classfier 와 동작 모듈간에 구분이 있으며, +classfier 에게는 일치가 있는 경우 트리거됩니다. 소프트웨어 데이터 경로에서 tc를 사용하는 현대 세계에서 이 모델은 복잡한 패킷 처리에 대해서는 확장 되지 않습니다. +cls_bpf에 연결 된 tc BPF 프로그램이 완전히 독립적 인 경우, 파싱 및 액션 프로세스를 효과적으로 단일 단위로 통합 합니다. cls_bpf의 직접 행동 모드(direct-action mode) +덕분에, 그것은 tc 액션 결정을 반환하고, 처리 파이프 라인을 즉시 종료합니다. 이를 통해 작업의 선형 반복을 피함으로써 네트워킹 데이터 경로에서 확장 가능한 프로그래밍 +가능한 패킷 처리를 구현할 수 있습니다. cls_bpf는 이러한 고속 경로가 가능한 tc 계층의 유일한 "classifier" 모듈 입니다. XDP BPF 프로그램과 마찬가지로 +tc BPF 프로그램은 네트워크 트래픽을 방해하거나 서비스를 다시 시작하지 않고 cls_bpf를 통해 런타임에 atomically하게 업데이트 될 수 있습니다. + +cls_bpf 자체가 연결 될 수 있는 tc ingress 와 egress 훅은 sch_clsact 라는 preseudo qdisc에 의해 관리됩니다. 이는 ingress 및 egress tc hook를 +모두 관리 할 수 있으므로, ingress qdisc의 drop-in 대체 및 적절한 상위 집합 입니다. __dev_queue_xmit()에서 tc의 egress hook의 경우 커널의 q +disc root lock 아래에서 실행되지 않는다고 강조하는 것이 중요합니다. 따라서, tc ingress 및 egress hook은 고속 경로에서 잠금없는 방식으로 실행됩니다. +두 경우 모두 선점이 비활성화되고 RCU 읽기 측에서 실행이 발생합니다. + +일반적으로 egress에는 넷디바이스에 연결된 qdisc가 있으며,예를 들어 sch_mq, sch_fq, sch_fq_codel 또는 sch_htb와 같이 하위클래스 를 포함하는 +classical qdisc 인 패킷을 역다중화 판정을 결정하는 패킷 분류 메커니즘이 필요합니다. + +이것은 tc classier를 호출하는 tcf_classify() 호출에 의해 처리 됩니다.이러한 경우 cls_bpf를 연결하여 사용할 수도 있습니다. +이러한 작업은 대부분 qdisc root lock 아래에서 수행 되며 lock 경합의 영향을 받을 수 있습니다. sch_clsact qdisc의 egress hook는 훨씬 빠르지 만, +기존의 egress qdisc와는 완전히 독립적으로 작동합니다. 따라서 sch_htb와 같은 경우 sch_clsact qdisc는 qdisc root lock 외부의 tc BPF를 통해 +과도한 패킷 분류를 수행 할 수 있습니다. skb-> mark 또는 skb-> priority를 설정하면 sch_htb는 root lock 아래 비용이 많이 발생하는 패킷 분류 없이 +평면 매핑 만 필요 하므로 경합이 줄어 듭니다. 오프로드 된 tc BPF 프로그램은 이전 로드 된 BPF 프로그램이 SmartNIC 드라이버에서 주입되어 NIC에서 +기본적으로 실행되는 cls_bpf와 함께 sch_clsact의 경우에 지원됩니다. 직접 실행 모드에서 작동하는 cls_bpf 프로그램 만이 오프로드 되도록 지원됩니다. +cls_bpf는 단일 프로그램의 오프로딩 만 지원하며 여러 프로그램을 오프로드 할 수 없습니다. 또한 ingress hook만으로 BPF 프로그램을 오프로드 할 수 있습니다. +하나의 cls_bpf 인스턴스는 여러 tc BPF 프로그램을 내부적으로 가질수 있습니다. 이 경우 TC_ACT_UNSPEC 프로그램 리턴 코드는 해당 목록의 다음 +tc BPF 프로그램으로 계속 실행됩니다. 그러나 이는 여러 프로그램이 패킷을 반복해서 구문 분석해야 성능이 저하된다는 단점이 있습니다. + +BPF program return codes +tc ingress 및 egress hook는 tc BPF 프로그램이 사용할 수있는 동일한 작업 결정 반환 공유합니다. +이 내용에 대해서는 linux/pkt_cls.h 시스템 헤더에 정의되어 있습니다 : + +#define TC_ACT_UNSPEC (-1) +#define TC_ACT_OK 0 +#define TC_ACT_SHOT 2 +#define TC_ACT_STOLEN 4 +#define TC_ACT_REDIRECT 7 + +두개의 hook에서도 사용되는 시스템 헤더 파일에서 사용 할수 있는 몇가지 액션 TC_ACT_* 결정이 더 있습니다. 그러나 +언급한 몇가지 행동은 위의 것과 동일한 의미를 공유합니다.(역자 주: 몇가지 더 있는 액션에 대해서 찾아 보았습니다.) +@include/uapi/linux/pkt_cls.h +#define TC_ACT_UNSPEC (-1) +#define TC_ACT_OK 0 +#define TC_ACT_RECLASSIFY 1 +#define TC_ACT_SHOT 2 +#define TC_ACT_PIPE 3 +#define TC_ACT_STOLEN 4 +#define TC_ACT_QUEUED 5 +#define TC_ACT_REPEAT 6 +#define TC_ACT_REDIRECT 7 +#define TC_ACT_TRAP 8 /* 하드웨어 경로의 경우 이는 "trap to cpu"를 의미하며 +* 하드웨어에서 프레임을 더 이상 처리하지 않습니다. +* 소프트웨어 경로의 경우 이값은 TC_ACT_STOLEN +* 와 같은 것을 의미 하며, skb를 버리고, +* 모든것이 정상적인 것 처럼 행동 합니다. +*/ +#define TC_ACT_VALUE_MAX TC_ACT_TRAP + +tc BPF 관점에서 의미는 TC_ACT_OK 와 TC_ACT_RECLASSIFY는 +세 가지 TC_ACT_STOLEN, TC_ACT_QUEUED 및 TC_ACT_TRAP opcode와 동일한 의미를 가집니다. +따라서 이 경우 두 그룹에 대한 TC_ACT_OK 및 TC_ACT_STOLEN 연산 코드만 설명합니다. + +결정 코드에 대해서 TC_ACT_UNSPEC으로 시작합니다. 이것은 "지정되지 않은 동작"의 의미를 가지며 다음과 같은 +세 가지 경우에 사용됩니다. +i) 오프로드 된 tc BPF 프로그램이 연결되고 오프로드 된 프로그램에 대한 cls_bpf 표현이 +TC_ACT_UNSPEC을 반환하는 tc ingress hook이 실행되는 경우, +ii) 다중 프로그램의 경우 cls_bpf에있는 다음 tc BPF 프로그램을 계속 진행되는 경우, +후자는 오프로드 된 경우에만 실행되는 다음 +tc BPF 프로그램에서 TC_ACT_UNSPEC이 계속되는 시점인 i)에서 오프로드 된 tc +BPF 프로그램과 함께 작동합니다. + +마지막으로 중요한 것은, iii) TC_ACT_UNSPEC은 단일 프로그램의 경우에도 커널에 부가적인 부작용없이 skb +계속 진행 하도록 알려 주는 용도로 사용됩니다. TC_ACT_UNSPEC은 TC_ACT_OK 결정 코드 와 매우 유사 하며, +즉, ingress 에서 스택의 상위 레이어로 skb를 전달하거나, egress에서 전송하기 위해 네트워킹 +디바이스 드라이버로 내리는 행동을 합니다. + +TC_ACT_OK의 유일한 차이점은 TC_ACT_OK 가 tc BPF 프로그램 세트의 classid를 기반으로 skb->tc_index 를 설정한다는 것입니다. +후자는 BPF 컨텍스트에서 skb->tc_classid를 통해 tc BPF 프로그램 자체에서 설정됩니다. TC_ACT_SHOT 명령은 커널에게 +패킷을 버리게 하며, 즉 네트워킹 스택의 상위 레이어는 진입시skb를 전혀 볼 수 없으며 유사하게 패킷은 egress에서 전송을 위해 +제출되지 않습니다. + +TC_ACT_SHOT 및 TC_ACT_STOLEN은 근본적으로 유사하지만 몇 가지 차이점이 있습니다: +TC_ACT_SHOT는 kfree_skb()를 통해 skb가 해제 되었고, 즉각적인 피드백을 위해 발신자에게 NET_XMIT_DROP를 반환하지만 +TC_ACT_STOLEN은 consume_skb()를 통해 skb를 해제하고 NET_XMIT_SUCCESS를 통해 전송이 성공했다고 상위 계층처럼 가장합니다. + +따라서 kfree_skb()의 흔적을 기록하는 perf 모니터는 TC_ACT_STOLEN의 버리는 표시를 보지 않으며, 이 의미는 skb가 " +사용"되거나 대기하지만 확실히 "삭제"되지 않았기 때문에 입니다. + +마지막으로 tc BPF 프로그램에서 사용할 수있는 TC_ACT_REDIRECT 동작도 있습니다. 이렇게 하면 skb를 bpf_redirect() helper와 함께 +동일한 또는 다른 장치의 ingress 또는 egress 경로로 리디렉션 할 수 있습니다. 패킷을 다른 장치의 ingress 또는 egress 방향으로 주입 +할 수 있기 때문에 BPF를 사용한 패킷 전달에서 완전한 유연성을 얻을 수 있습니다. +네트워킹 장치 자체가 아닌 대상 네트워킹 장치에 대한 요구 사항이 없으므로, 대상 장치에서 cls_bpf의 다른 인스턴스 또는 다른 제한 사항을 +실행할 필요가 없습니다. + +tc BPF FAQ +이 절에는 자주 질문하는 tc BPF 프로그램과 관련된 몇 가지 질문과 대답이 있습니다. + +질문 : 액션 모듈로 "act_bpf"는 무엇입니까? 여전히 관련이 있습니까? +답변 : 그렇지 않습니다. cls_bpf와 act_bpf는 tc BPF 프로그램에 대해 동일한 기능을 공유하지만 +cls_bpf는 act_bpf의 적절한 수퍼 세트 이며, 더 유연합니다. tc가 작동하는 방식은 tc 액션을 tc 분류 자에 연결되어야 한다는 것입니다. +cls_bpf와 동일한 유연성을 얻으려면 act_bpf를 cls_matchall 분류 자에 연결 해야합니다. 이름에서 알 수 있듯이, 이것은 연결 된 +tc 작업 처리를 위해 모든 패킷에서 일치합니다. act_bpf의 경우 직접 실행 모드에서 cls_bpf를 직접 사용하는 것보다 패킷 처리가 덜 효율적 입니다. +act_bpf가 cls_bpf 또는 cls_matchall이 아닌 다른 분류 자의 설정에서 사용 되면, tc 분류 자의 동작 특성으로 인해 더 나쁜 성능을 보입니다. +분류자 A가 불일치 하는 경우, 패킷은 분류자 B로 전달되며, 패킷등을 다시 패싱 되므로, 일반적인 경우에 패킷은 이러한 일치를 찾아 act_bpf를 +실행하기 위해 최악의 경우 N개의 분류기를 통과해야 하는 선형 처리가 있게 됩니다. 따라서 act_bpf는 그다지 중요하지 않습니다. +또한 act_bpf는 cls_bpf와 비교하여 tc 오프 로딩 인터페이스를 제공하지 않습니다. + +질문 : 직접 행동 모드(direct-action mode)가 아닌 cls_bpf를 사용하는 것이 좋습니다? +답변 : 아니요. 답변은 복잡한 처리를 위해 확장 할 수 없다는 점에서 위의 것과 비슷합니다. +tc BPF는 이미 효율적으로 자체적으로 필요한 모든 것을 수행 할 수 있으므로 직접 실행 모드 이외의 다른 기능은 필요하지 않습니다. + +질문 : 오프로드 된 cls_bpf와 오프로드 된 XDP에서 성능 차이가 있습니까? +답변 : 아니요. 둘 다 SmartNIC에 대한 오프로드를 처리하는 커널의 동일한 컴파일러를 통해 주입이 됩니다. 두 가지 모두의 로딩 메커니즘도 비슷합니다. +따라서 BPF 프로그램은 NIC에서 기본적으로 실행될 수 있도록 동일한 대상 명령 세트로 변환됩니다. +두 개의 tc BPF 및 XDP BPF 프로그램 유형은 서로 다른 기능 세트를 가지므로 사용 사례에 따라 다른 서비스 유형 (예 : 오프로드 경우)의 가용성으로 인해 다른 서비스 유형을 선택할 수 있습니다. + +tc BPF의 사용 사례 + +tc BPF 프로그램의 주요 사용 사례 중 일부 내용이 하위 절에 나와 있습니다. +또한 여기에서는 목록이 전체적이지는 않지만, 프로그래밍 가능성 과 효율성을 감안할 때 매우 특정한 사용자 사례를 해결하기 위해 +오케스트레이션 시스템에 맞춤화되고 통합될 수 있습니다. XDP 사용 사례가 일부 겹칠 수 있지만 tc BPF와 XDP BPF는 상호 보완적이며 동시에 해결할 주어진 문제에 +가장 적합한 두 가지 방법을 주어진 문제를 풀기에 가장 적합하게 동시에 또는 한 번에 사용할 수 있습니다. + +* 컨테이너에 대한 정책 시행 +tc BPF 프로그램이 적합한 한 응용 프로그램은 정책 집행, 사용자 정의 방화벽 또는 컨테이너 또는 포드에 대한 유사한 보안 대책을 각각 구현하는 것입니다. +이전의 경우, 컨테이너 격리는 호스트의 초기 네임 스페이스와 전용 컨테이너의 네임 스페이스를 연결하는 네트워크 장치가 있는 네트워크 네임 스페이스를 통해 구현됩니다. +veth 인터페이스 쌍의 한쪽 끝은 컨테이너의 네임 스페이스로 이동 되었지만 다른 쪽 끝은 호스트의 초기 네임 스페이스에 남아 있기 때문에, 컨테이너로부터의 모든 네트워크 트래픽은 +호스트 측면의 tc ingress 및 egress hook에 연결 가능한 veth 디바이스를 통과 해야 합니다. 컨테이너로 들어오는 네트워크 트래픽은 호스트를 향한 경로로 전달되지만, +컨테이너에서 들어오는 네트워크 트래픽은 호스트를 향한 경로로 전달됩니다. 컨테이너로 들어오는 네트워크 트래픽은 호스트 측면 veth의 egress hook을 통과하지만 컨테이너에서 +오는 네트워크 트래픽은 호스트 측면 veth의 ingress hook을 통과합니다. +veth 장치와 같은 가상 장치의 경우, XDP는 커널이 여기의 skb에서만 작동하고 일반 XDP는 복제 된 skb와 함께 작동하지 않는 몇 가지 제한 사항이 있으므로 이 경우 적합하지 않습니다. +후자는 generic XDP hook이 단순히 우회되는 재전송을 위한 데이터 세그먼트를 유지하기 위해 TCP/IP 스택에서 많이 사용됩니다. 또한 generic XDP는 전체 skb를 선형화 해야 성능이 +크게 저하됩니다. 반면에 tc BPF는 skb 입력 컨텍스트 케이스를 전문으로 하므로 더 유연하며, 따라서 generic XDP의 한계에 염두할 필요가 없습니다. + +* 포워딩 그리고 로드벨런싱 +포워딩 및로드 밸런싱 사용 사례는 서버와 클라이언트 간의 트래픽 보다는 컨테이너 와 컨테이너 사이의 워크로드를 타겟으로 하며, +(두 기술이 어느 경우에도 사용될 수 있지만,) XDP 와 매우 유사합니다. XDP는 ingress 측에서만 사용할 수 있으므로 tc BPF 프로그램은 특히 egress에서 적용되는 +추가 사용 사례를 허용 하며, 예를 들어 컨테이너 기반 트래픽은 초기 네임 스페이스에서 BPF를 통해 송신 측에서 이미 NAT된 및 로드 밸런싱 될 수 있으며, 이것은 +컨테이너 자체에 투명하게 이루어 집니다. egress 트래픽은 커널의 네트워킹 스택의 특성 때문에 sk_buff 구조체를 기반으로 하므로 패킷 재작성 및 리디렉션는 +tc BPF에서 적합합니다. BPF는 bpf_redirect() helper 함수를 활용하여 포워딩 로직를 대신 받아 패킷을 다른 네트워킹 장치의 ingress 또는 egress 경로로 +푸시 할 수 있습니다. 따라서 모든 브리지 형 장치는 forwarding fabric으로 tc BPF 활용으로 불필요하게 됩니다 + +* 흐름 샘플링, 모니터링 +XDP의 경우와 마찬가지로 플로우 샘플링 및 모니터링은 BPF 프로그램이 맞춤 데이터, 전체 또는 절단 된 패킷 내용, 또는 사용자 공간 응용 프로그램에 이르기까지 푸시 +할 수있는 고성능의 잠금없는 CPU 당 메모리 매핑 된 perf ring 버퍼를 통해 실현 될 수 있습니다. tc BPF 프로그램에서 이것은 bpf_xdp_event_output()과 +같은 대표 함수 와 의미를 가진 bpf_skb_event_output() BPF help 함수를 통해 실현됩니다. 주어진 tc BPF 프로그램은 XDP BPF 사용 사례에서 ingress 만 +아닌 ingress와 egress에 연결 될수 있으며, 두 개의 tc 후크는 (일반) 네트워킹 스택의 가장 낮은 계층에 있으며,이를 통해 특정 노드의 모든 네트워크 트래픽을 양방향 +모니터링 할 수 있습니다. 이것은 tcpdump와 wireshark가 skb를 복제할 필요 없으며, 프로그래밍 가능성 측면에서 보다 유연한 cBPF 사용 사례와 다소 관련될 수 있으며, +예를 들어 BPF는 이미 링 버퍼에 푸시 된 패킷에 대해 사용자 공간 뿐만 아니라 사용자 annotations까지 모두를 푸시하는 대신에 커널 내 aggregation을 수행 할 수 있습니다. +후자는 또한 Cilium에서 많이 사용되는데, 패킷 레이블에 컨테이너 레이블을 연관 시키려면 패킷 주석을 추가로 주석을 붙여야하며, 다양한 패킷을 처리해야하는 +이유(정책 위반 등) 때문에 다양한 context을 제공 해야합니다. + +* 패킷 스케줄러 사전 처리 +sch_clsact의 egress hook 인 sch_handle_egress()는 커널의 qdisc 루트 잠금을 취하기 바로 전에 실행 되며, +따라서 tc BPF 프로그램은 패킷이 sch_htb와 같은 진짜 완전한 qdisc로 전송되기 전에 heavy lifting packet classification 및 +mangling을 동작하는 데 사용될 수 있습니다. 이러한 sch_clsact 와 sch_htb 와 같은 실제 qdisc와의 상호 작용은 나중에 sch_clsact의 +egress hook가 잠금을 사용하지 않고 실행되기 때문에 전송 단계에서 나중에 발생하는 lock contention을 줄일 수 있습니다. + +tc BPF뿐만 아니라 XDP BPF 프로그램의 한 구체적인 예는 Cilium입니다. Cilium은 Docker 및 Kubernetes와 같은 Linux 컨테이너 관리 플랫폼을 +사용하여 배포 된 응용 프로그램 서비스 간의 네트워크 연결을 투명하게 보호 하기위한 오픈 소스 소프트웨어이며 Layer 3 및 Layer 7에서 작동합니다. +Cilium의 핵심은 BPF를 사용하여 정책 시행과 로드 밸런싱 및 모니터링을 구현합니다. + +Slides: https://www.slideshare.net/ThomasGraf5/dockercon-2017-cilium-network-and-application-security-with-bpf-and-xdp +Video: https://youtu.be/ilKlmTDdFgk +Github: https://github.com/cilium/cilium + +드라이버 지원 + +tc BPF 프로그램은 커널의 네트워킹 스택에서 시작되고 드라이버에서 직접 시작되지 않으므로 추가 드라이버 수정이 필요하지 않으므로 모든 네트워킹 장치에서 +실행할 수 있습니다. 아래 나열된 유일한 예외는 tc BPF 프로그램을 NIC에 오프 로딩하는 것을 뜻합니다. + +오프로드 된 tc BPF를 지원하는 드라이버 + +Netronome +nfp[2] + +tc BPF 프로그램을 작성하고 로딩하는 예제는 각 도구 아래의 toolchain 섹션에 포함되어 있습니다. + +더 읽을 거리 + +문서, 프로젝트, 대담, 논문 및 추가 자료의 언급 목록은 완전하지 않을 수 있습니다. 따라서, 목록을 완료하기 위해 pull 요청을 열어 주시기 바랍니다. + +커널 개발자 자주 묻는 질문 +Documentation/bpf/ 에서 Linux 커널은 주로 BPF 하위 시스템과 관련된 커널 개발자를 대상으로하는 두 개의 FAQ 파일을 제공합니다. + + +BPF Devel 자주 묻는 질문 :이 문서는 주로 패치 제출 과정 주변 정보와 BPF 커널 트리, 안정 트리 및 버그보고 워크 플로우, LLVM 등을 통해 BPF의 +확장성 및 상호 작용의 주변에 관련된 질문을 제공합니다. +https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/bpf/bpf_devel_QA.txt + +BPF Design 자주 묻는 질문 :이 문서는 명령어 세트, 검증에 관련된 BPF 설계 결정, 호출 규칙, JITs 등 주변 자주 묻는 질문에 대한 답변을 하려고 합니다. +https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/bpf/bpf_design_QA.txt + +BPF를 사용하는 프로젝트 + +다음 목록에는 BPF를 사용하는 오픈 소스 프로젝트 중 하나가 포함되어 있으며 각각 BPF에 대한 도구을 제공합니다. 이 문맥에서 eBPF 명령어 세트는 기존 +cBPF를 활용하는 프로젝트 대신 특별히 의미가 있습니다. + +Tracing + +* BCC +BCC는 BPF Compiler Collection의 약자로, 핵심 기능은 kprobes, kretprobes, tracepoints, uprobes, uretprobes 및 사용자 레벨에서 정적으로 +정의 된 추적(USDT) probe를 기반으로하는 커널 인프라에 BPF 프로그램을 연결하여 사용하기 쉽고 효율적인 커널 추적 유틸리티 세트를 제공하는 것입니다. +이 컬렉션은 응용 프로그램, 시스템 라이브러리, 다양한 커널 하위 시스템에 이르기까지 스택의 여러 계층을 대상으로하는 수백 가지 도구를 제공하여 시스템의 성능 특성 +또는 문제를 분석합니다. 또한 BCC는 다른 프로젝트의 라이브러리로 사용하기 위해 API를 제공합니다. + +https://github.com/iovisor/bcc + +* bpftrace + +bpftrace는 Linux 용 DTrace 스타일의 동적 추적 도구이며 LLVM을 백엔드로 사용하여 스크립트를 BPF 바이트 코드로 컴파일하고, BCC를 사용하여 커널의 +BPF 추적 인프라와 상호 작용합니다.원시 BCC와 비교하여 추적 스크립트를 구현하기 위한 고급 언어를 제공합니다. + +https://github.com/ajor/bpftrace + +* perf + +Linux 커널 커뮤니티에서 커널 소스 트리의 일부로 개발 한 perf 도구는 BPF의 수집된 된 데이터를 검색하고 처리 할 수있는 기존의 perf 레코드 하위 명령을 통해 +추적 BPF 프로그램을 로드하는 방법을 제공 하며, 예를 들어 perf 스크립트 및 다른 방법을 통해 perf.data에 저장됩니다. + +https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/tools/perf + +* ply + +ply는 yore의 'Little Language'접근 방식을 따르는 추적 도구이며 커널의 kprobes 및 tracepoint에 연결된 Linux BPF 프로그램에 +ply 스크립트를 컴파일합니다. 스크립트는 DTrace와 확장 awk에 크게 영향을받은 C와 유사한 구문을 사용합니다. ply는 의존성을 매우 최소한으로 유지하고 빌드시 +flex 및 bison 만 필요하며 런타임에는 libc 만 필요합니다. + +https://github.com/wkz/ply + +* systemtap +systemtap은 성능 또는 기능적 문제를 진단하고 분석하기 위해 데이터를 추출, 필터링 및 요약하기위한 스크립트 언어 및 도구입니다. +stapbpf라는 BPF 백엔드는 추가 컴파일러가 필요없이 스크립트를 BPF로 직접 변환하고 프로브를 커널에 주입합니다. 따라서 stap의 커널 모듈과 달리 +외부 종속성이 없으며, 커널 모듈을 따로 로드 할 필요도 없습니다. + +https://sourceware.org/git/gitweb.cgi?p=systemtap.git;a=summary + +* PCP + +Performance Co-Pilot (PCP)은 수집 된 시스템의 성능 측정을 실시간으로 분석하거나 기록 데이터를 사용하여 다양한 에이전트를 통해 +측정을 수집 할 수있는 시스템 성능 및 분석 프레임 워크입니다. +pmdabcc를 사용하면 PCP에는 BPF 및 BCC를 통해 커널에서 데이터를 추출하는 BCC 기반 성능 측정 도메인 에이전트가 있습니다. + +https://github.com/performancecopilot/pcp + +* Weave Scope + +Weave Scope는 kprobes와 함께 BPF를 사용하여 프로세스, 네트워킹 연결 또는 기타 시스템 데이터에 +대한 데이터를 수집하는 클라우드 모니터링 도구입니다. Weave Scope는 BPF ELF 파일을 커널에 로드하기 위해 gobpf 라이브러리의 맨 위에서 작동하며 +TCP 이벤트를 추적하기 위해 연결, 승인 및 닫기 호출을 모니터링하는 tcptracer-bpf 도구와 함께 제공됩니다. + +https://github.com/weaveworks/scope + +Networking + +* Cilium + +Cilium은 응용 프로그램 컨테이너 또는 프로세스와 같은 응용 프로그램 작업 부하간에 +네트워크 연결 및 로드 벨런스 조정을 제공하고 투명하게 보호합니다. Cilium은 레이어 3/4에서 작동하여 기존 네트워킹 및 +보안 서비스 뿐만 아니라 레이어 7을 제공하여 HTTP, gRPC 및 Kafka와 같은 최신 응용 프로그램 프로토콜의 +보호 및 보안을 유지합니다. Kubernetes 및 Mesos와 같은 오케스트레이션 프레임 워크에 통합되어 있으며, +BPF는 커널의 네트워킹 데이터 경로에서 작동하는 Cilium의 기본 부분입니다. + +https://github.com/cilium/cilium + +* Suricata + +Suricata는 네트워크 IDS, IPS 및 NSM 엔진이며, BPF를 기반으로 하는 로드 밸런서로서 특정 패킷을 처리하거나, 우회하기 위해 +BPF 필터로서 프로그래밍 가능한 세 가지 영역에서 BPF와 XDP를 활용합니다. 로드 밸런싱 및 XDP가 높은 패킷 속도로 바이 패스 또는 +drop 메커니즘을 구현할 수 있습니다. + +http://suricata.readthedocs.io/ko/latest/capture-hardware/ebpf-xdp.html +https://github.com/OISF/suricata + +* systemd +systemd는 IPv4/v6 주소을 허용하고, BPF의 cgroup ingress 및 egress hook을 기반으로하는 +systemd 장치에 대한 네트워크 액세스 제어를 구현합니다. 주소는 패킷/바이트를 기반으로하며 +허용/거부 규칙에 대한 주소 접두어로 ACL을 지정할 수 있습니다. 자세한 정보는 다음에서 찾을 수 있습니다 : + +http : / /pointer.net/blog/ip-accounting-and-access-lists-with-systemd.html +https://github.com/systemd/systemd + +* iproute2 + +iproute2는 LLVM이 ELF 파일을 커널에 생성 할 때 BPF 프로그램을 로드하는 기능을 제공합니다. +iproute2는 일반적인 BPF 로더 백엔드를 통해 tc BPF 프로그램뿐만 아니라 XDP BPF 프로그램도 지원합니다. +tc 및 ip 명령 행 유틸리티는 사용자에게 로더 및 자가 검사 기능을 사용을 가능하게 합니다. + +https://git.kernel.org/pub/scm/network/iproute2/iproute2.git/ + +* p4c-xdp + +p4c-xdp는 BPF 및 XDP를 대상으로하는 P4 컴파일러 백엔드를 제공합니다. P4는 NIC, 어플라이언스 또는 스위치와 같은 +프로그램 가능한 네트워크 요소의 데이터 플레인에 의해 패킷이 처리되는 방법을 설명하고 +p4c-xdp의 도움으로 도메인 에 따른 언어입니다. P4 프로그램은 다음에 의해 컴파일 될 수 있는 BPF C 프로그램으로 변환 될 수 있습니다. +clang/LLVM을 사용하고 고성능 패킷 처리를 위해 XDP 계층의 커널에 BPF 프로그램으로 로드됩니다. + +https://github.com/vmware/p4c-xdp + +기타 + +* LLVM + +clang/LLVM은 C BPF 프로그램을 ELF 파일에 포함 된 BPF 명령어로 컴파일하기 위해 BPF 백엔드를 제공합니다. +LLVM BPF 백엔드는 Linux 커널의 BPF 핵심 인프라와 함께 개발되며 동일한 커뮤니티에서 관리합니다. +clang/LLVM은 BPF 프로그램 개발을 위한 툴체인의 핵심 부분입니다. + +https://llvm.org/ + +* libbpf + +libbpf는 커널 소스 트리의 일부로 Linux 커널 커뮤니티에서 개발 한 일반 BPF 라이브러리이며, LLVM에서 생성 된 ELF 파일의 BPF 프로그램을 로드하여 커널에 연결 할 수 있습니다. +라이브러리는 perf 및 bpftool과 같은 다른 커널 프로젝트에서 사용됩니다. + +https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/tools/lib/bpf + +* bpftool + +bpftool은 BPF 프로그램과 BPF 맵을 자가 검사 하고 디버깅 하기위한 주요 도구이며, libbpf는 리눅스 커널 커뮤니티에 의해 개발되었습니다. +시스템의 모든 활성 BPF 프로그램 및 맵을 덤프하고 프로그램에서 BPF 또는 주입된 BPF 명령어를 덤프 및 역어셈블 하고 시스템의 BPF 맵을 덤프 및 조작 할 수 있습니다. +bpftool은 BPF 파일 시스템과의 상호 작용을 지원하며 객체 파일에서 커널에 다양한 프로그램 타입을 로드 합니다. + +https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/tools/bpf/bpftool + +* gobpf + +gobpf는 ELF 파일에서 BPF 프로그램을 로드하고 사용하기 위해 저수준 루틴 뿐 아니라 bcc 프레임 워크에 대한 바인딩을 제공합니다. + +https://github.com/iovisor/gobpf + + +* ebpf_asm + +ebpf_asm은 Intel 계열 어셈블리 구문으로 작성된 BPF 프로그램 용 어셈블러를 제공 하므로, +clang/LLVM 도구 체인을 이용하지 않고, 프로그램이 작고 단순한 경우 어셈블리에서 BPF 프로그램을 직접 작성할 수있는 대안을 제공합니다. + +https://github.com/solarflarecom/ebpf_asm + +XDP Newbies + +David S. Miller가 xdp-newbies 메일 링리스트 (http://vger.kernel.org/vger-lists.html#xdp-newbies)에 대한 XDP 및 BPF 다양한 파트에 대해 자세히 설명한 게시물이 있습니다: + +4. May 2017, +BPF Verifier Overview, David S. Miller, https://www.spinics.net/lists/xdp-newbies/msg00185.html + +3. May 2017, +Contextually speaking…, David S. Miller, https://www.spinics.net/lists/xdp-newbies/msg00181.html + +2. May 2017, +bpf.h and you…, David S. Miller, https://www.spinics.net/lists/xdp-newbies/msg00179.html + +1. Apr 2017, +XDP example of the day, David S. Miller, https://www.spinics.net/lists/xdp-newbies/msg00009.html + +BPF 뉴스 레터 +Alexander Alemayhu는 주 마다 대략 한 번 BPF를 관련된 뉴스 레터를 작성하여 Linux 커널 영역 와 사용자 영역 의 주변 에코시스템에 대한 BPF 관련 최신 개발 내용을 다루었습니다. +모든 BPF 업데이트 뉴스 레터 (01 - 12)는 다음에서 찾을 수 있습니다. +https://cilium.io/blog/categories/BPF%20Newsletter + +팟케스트 + +부분적으로 BPF를 다루는 많은 팟케스트가 있었습니다. +불완전한 목록: + +5. Feb 2017, +Linux Networking Update from Netdev Conference, Thomas Graf, Software Gone Wild, Show 71, +http://blog.ipspace.net/2017/02/linux-networking-update-from-netdev.html http://media.blubrry.com/ipspace/stream.ipspace.net/nuggets/podcast/Show_71-NetDev_Update.mp3 + +4. Jan 2017, +The IO Visor Project, Brenden Blanco, OVS Orbit, Episode 23, https://ovsorbit.org/#e23 https://ovsorbit.org/episode-23.mp3 + +3. Oct 2016, +Fast Linux Packet Forwarding, Thomas Graf, Software Gone Wild, Show 64, http://blog.ipspace.net/2016/10/fast-linux-packet-forwarding-with.html http://media.blubrry.com/ipspace/stream.ipspace.net/nuggets/podcast/Show_64-Cilium_with_Thomas_Graf.mp3 + +2. Aug 2016, +P4 on the Edge, John Fastabend, OVS Orbit, Episode 11, https://ovsorbit.org/#e11 https://ovsorbit.org/episode-11.mp3 + +1. May 2016, +Cilium, Thomas Graf, OVS Orbit, Episode 4, https://ovsorbit.org/#e4 https://ovsorbit.benpfaff.org/episode-4.mp3 + +블로그 게시물 +다음 (불완전한)목록에는 BPF, XDP 및 관련 프로젝트 관련 블로그 게시물이 포함됩니다: + +35. May 2017, +An entertaining eBPF XDP adventure, Suchakra Sharma, https://suchakra.wordpress.com/2017/05/23/an-entertaining-ebpf-xdp-adventure/ + +34. May 2017, +eBPF, part 2: Syscall and Map Types, Ferris Ellis, https://ferrisellis.com/posts/ebpf_syscall_and_maps/ + +33. May 2017, +Monitoring the Control Plane, Gary Berger, http://firstclassfunc.com/2017/05/monitoring-the-control-plane/ + +32. Apr 2017, +USENIX/LISA 2016 Linux bcc/BPF Tools, Brendan Gregg, http://www.brendangregg.com/blog/2017-04-29/usenix-lisa-2016-bcc-bpf-tools.html + +31. Apr 2017, +Liveblog: Cilium for Network and Application Security with BPF and XDP, Scott Lowe, http://blog.scottlowe.org//2017/04/18/black-belt-cilium/ + +30. Apr 2017, +eBPF, part 1: Past, Present, and Future, Ferris Ellis, https://ferrisellis.com/posts/ebpf_past_present_future/ + +29. Mar 2017, +Analyzing KVM Hypercalls with eBPF Tracing, Suchakra Sharma, https://suchakra.wordpress.com/2017/03/31/analyzing-kvm-hypercalls-with-ebpf-tracing/ + +28. Jan 2017, +Golang bcc/BPF Function Tracing, Brendan Gregg, http://www.brendangregg.com/blog/2017-01-31/golang-bcc-bpf-function-tracing.html + +27. Dec 2016, +Give me 15 minutes and I’ll change your view of Linux tracing, Brendan Gregg, http://www.brendangregg.com/blog/2016-12-27/linux-tracing-in-15-minutes.html + +26. Nov 2016, +Cilium: Networking and security for containers with BPF and XDP, Daniel Borkmann, https://opensource.googleblog.com/2016/11/cilium-networking-and-security.html + +25. Nov 2016, +Linux bcc/BPF tcplife: TCP Lifespans, Brendan Gregg, http://www.brendangregg.com/blog/2016-11-30/linux-bcc-tcplife.html + +24. Oct 2016, +DTrace for Linux 2016, Brendan Gregg, http://www.brendangregg.com/blog/2016-10-27/dtrace-for-linux-2016.html + +23. Oct 2016, +Linux 4.9’s Efficient BPF-based Profiler, Brendan Gregg, http://www.brendangregg.com/blog/2016-10-21/linux-efficient-profiler.html + +22. Oct 2016, +Linux bcc tcptop, Brendan Gregg, http://www.brendangregg.com/blog/2016-10-15/linux-bcc-tcptop.html + +21. Oct 2016, +Linux bcc/BPF Node.js USDT Tracing, Brendan Gregg, http://www.brendangregg.com/blog/2016-10-12/linux-bcc-nodejs-usdt.html + +20. Oct 2016, +Linux bcc/BPF Run Queue (Scheduler) Latency, Brendan Gregg, http://www.brendangregg.com/blog/2016-10-08/linux-bcc-runqlat.html + +19. Oct 2016, +Linux bcc ext4 Latency Tracing, Brendan Gregg, http://www.brendangregg.com/blog/2016-10-06/linux-bcc-ext4dist-ext4slower.html + +18. Oct 2016, +Linux MySQL Slow Query Tracing with bcc/BPF, Brendan Gregg, http://www.brendangregg.com/blog/2016-10-04/linux-bcc-mysqld-qslower.html + +17. Oct 2016, +Linux bcc Tracing Security Capabilities, Brendan Gregg, http://www.brendangregg.com/blog/2016-10-01/linux-bcc-security-capabilities.html + +16. Sep 2016, +Suricata bypass feature, Eric Leblond, https://www.stamus-networks.com/2016/09/28/suricata-bypass-feature/ + +15. Aug 2016, +Introducing the p0f BPF compiler, Gilberto Bertin, https://blog.cloudflare.com/introducing-the-p0f-bpf-compiler/ + +14. Jun 2016, +Ubuntu Xenial bcc/BPF, Brendan Gregg, http://www.brendangregg.com/blog/2016-06-14/ubuntu-xenial-bcc-bpf.html + +13. Mar 2016, +Linux BPF/bcc Road Ahead, March 2016, Brendan Gregg, http://www.brendangregg.com/blog/2016-03-28/linux-bpf-bcc-road-ahead-2016.html + +12. Mar 2016, +Linux BPF Superpowers, Brendan Gregg, http://www.brendangregg.com/blog/2016-03-05/linux-bpf-superpowers.html + +11. Feb 2016, +Linux eBPF/bcc uprobes, Brendan Gregg, http://www.brendangregg.com/blog/2016-02-08/linux-ebpf-bcc-uprobes.html + +10. Feb 2016, +Who is waking the waker? (Linux chain graph prototype), Brendan Gregg, http://www.brendangregg.com/blog/2016-02-05/ebpf-chaingraph-prototype.html + +9. Feb 2016, +Linux Wakeup and Off-Wake Profiling, Brendan Gregg, http://www.brendangregg.com/blog/2016-02-01/linux-wakeup-offwake-profiling.html + +8. Jan 2016, +Linux eBPF Off-CPU Flame Graph, Brendan Gregg, http://www.brendangregg.com/blog/2016-01-20/ebpf-offcpu-flame-graph.html + +7. Jan 2016, +Linux eBPF Stack Trace Hack, Brendan Gregg, http://www.brendangregg.com/blog/2016-01-18/ebpf-stack-trace-hack.html + +6. Sep 2015, +Linux Networking, Tracing and IO Visor, a New Systems Performance Tool for a Distributed World, Suchakra Sharma, https://thenewstack.io/comparing-dtrace-iovisor-new-systems-performance-platform-advance-linux-networking-virtualization/ + +5. Aug 2015, +BPF Internals - II, Suchakra Sharma, https://suchakra.wordpress.com/2015/08/12/bpf-internals-ii/ + +4. May 2015, +eBPF: One Small Step, Brendan Gregg, http://www.brendangregg.com/blog/2015-05-15/ebpf-one-small-step.html + +3. May 2015, +BPF Internals - I, Suchakra Sharma, https://suchakra.wordpress.com/2015/05/18/bpf-internals-i/ + +2. Jul 2014, +Introducing the BPF Tools, Marek Majkowski, https://blog.cloudflare.com/introducing-the-bpf-tools/ + +1. May 2014, +BPF - the forgotten bytecode, Marek Majkowski, https://blog.cloudflare.com/bpf-the-forgotten-bytecode/ + +대담 + +다음 (불완전한) 목록에는 BPF 및 XDP와 관련된 대담 및 회의 논문이 포함됩니다: + +44. May 2017, +PyCon 2017, Portland, Executing python functions in the linux kernel by transpiling to bpf, Alex Gartrell, https://www.youtube.com/watch?v=CpqMroMBGP4 + +43. May 2017, +gluecon 2017, Denver, Cilium + BPF: Least Privilege Security on API Call Level for Microservices, Dan Wendlandt, http://gluecon.com/#agenda + +42. May 2017, +Lund Linux Con, Lund, XDP - eXpress Data Path, Jesper Dangaard Brouer, http://people.netfilter.org/hawk/presentations/LLC2017/XDP_DDoS_protecting_LLC2017.pdf + +41. May 2017, +Polytechnique Montreal, Trace Aggregation and Collection with eBPF, Suchakra Sharma, http://step.polymtl.ca/~suchakra/eBPF-5May2017.pdf + +40. Apr 2017, +DockerCon, Austin, Cilium - Network and Application Security with BPF and XDP, Thomas Graf, https://www.slideshare.net/ThomasGraf5/dockercon-2017-cilium-network-and-application-security-with-bpf-and-xdp + +39. Apr 2017, +NetDev 2.1, Montreal, XDP Mythbusters, David S. Miller, https://www.netdevconf.org/2.1/slides/apr7/miller-XDP-MythBusters.pdf + +38. Apr 2017, +NetDev 2.1, Montreal, Droplet: DDoS countermeasures powered by BPF + XDP, Huapeng Zhou, Doug Porter, Ryan Tierney, Nikita Shirokov, https://www.netdevconf.org/2.1/slides/apr6/zhou-netdev-xdp-2017.pdf + +37. Apr 2017, +NetDev 2.1, Montreal, XDP in practice: integrating XDP in our DDoS mitigation pipeline, Gilberto Bertin, https://www.netdevconf.org/2.1/slides/apr6/bertin_Netdev-XDP.pdf + +36. Apr 2017, +NetDev 2.1, Montreal, XDP for the Rest of Us, Andy Gospodarek, Jesper Dangaard Brouer, https://www.netdevconf.org/2.1/slides/apr7/gospodarek-Netdev2.1-XDP-for-the-Rest-of-Us_Final.pdf + +35. Mar 2017, +SCALE15x, Pasadena, Linux 4.x Tracing: Performance Analysis with bcc/BPF, Brendan Gregg, https://www.slideshare.net/brendangregg/linux-4x-tracing-performance-analysis-with-bccbpf + +34. Mar 2017, +XDP Inside and Out, David S. Miller, https://github.com/iovisor/bpf-docs/raw/master/XDP_Inside_and_Out.pdf + +33. Mar 2017, +OpenSourceDays, Copenhagen, XDP - eXpress Data Path, Used for DDoS protection, Jesper Dangaard Brouer, https://github.com/iovisor/bpf-docs/raw/master/XDP_Inside_and_Out.pdf + +32. Mar 2017, +source{d}, Infrastructure 2017, Madrid, High-performance Linux monitoring with eBPF, Alfonso Acosta, https://www.youtube.com/watch?v=k4jqTLtdrxQ + +31. Feb 2017, +FOSDEM 2017, Brussels, Stateful packet processing with eBPF, an implementation of OpenState interface, Quentin Monnet, https://fosdem.org/2017/schedule/event/stateful_ebpf/ + +30. Feb 2017, +FOSDEM 2017, Brussels, eBPF and XDP walkthrough and recent updates, Daniel Borkmann, http://borkmann.ch/talks/2017_fosdem.pdf + +29. Feb 2017, +FOSDEM 2017, Brussels, Cilium - BPF & XDP for containers, Thomas Graf, https://fosdem.org/2017/schedule/event/cilium/ + +28. Jan 2017, +linuxconf.au, Hobart, BPF: Tracing and more, Brendan Gregg, https://www.slideshare.net/brendangregg/bpf-tracing-and-more + +27. Dec 2016, +USENIX LISA 2016, Boston, Linux 4.x Tracing Tools: Using BPF Superpowers, Brendan Gregg, https://www.slideshare.net/brendangregg/linux-4x-tracing-tools-using-bpf-superpowers + +26. Nov 2016, +Linux Plumbers, Santa Fe, Cilium: Networking & Security for Containers with BPF & XDP, Thomas Graf, http://www.slideshare.net/ThomasGraf5/clium-container-networking-with-bpf-xdp + +25. Nov 2016, +OVS Conference, Santa Clara, Offloading OVS Flow Processing using eBPF, William (Cheng-Chun) Tu, http://openvswitch.org/support/ovscon2016/7/1120-tu.pdf + +24. Oct 2016, +One.com, Copenhagen, XDP - eXpress Data Path, Intro and future use-cases, Jesper Dangaard Brouer, http://people.netfilter.org/hawk/presentations/xdp2016/xdp_intro_and_use_cases_sep2016.pdf + +23. Oct 2016, +Docker Distributed Systems Summit, Berlin, Cilium: Networking & Security for Containers with BPF & XDP, Thomas Graf, http://www.slideshare.net/Docker/cilium-bpf-xdp-for-containers-66969823 + +22. Oct 2016, +NetDev 1.2, Tokyo, Data center networking stack, Tom Herbert, http://netdevconf.org/1.2/session.html?tom-herbert + +21. Oct 2016, +NetDev 1.2, Tokyo, Fast Programmable Networks & Encapsulated Protocols, David S. Miller, http://netdevconf.org/1.2/session.html?david-miller-keynote + +20. Oct 2016, +NetDev 1.2, Tokyo, XDP workshop - Introduction, experience, and future development, Tom Herbert, http://netdevconf.org/1.2/session.html?herbert-xdp-workshop + +19. Oct 2016, +NetDev1.2, Tokyo, The adventures of a Suricate in eBPF land, Eric Leblond, http://netdevconf.org/1.2/slides/oct6/10_suricata_ebpf.pdf + +18. Oct 2016, +NetDev1.2, Tokyo, cls_bpf/eBPF updates since netdev 1.1, Daniel Borkmann, http://borkmann.ch/talks/2016_tcws.pdf + +17. Oct 2016, +NetDev1.2, Tokyo, Advanced programmability and recent updates with tc’s cls_bpf, Daniel Borkmann, http://borkmann.ch/talks/2016_netdev2.pdf http://www.netdevconf.org/1.2/papers/borkmann.pdf + +16. Oct 2016, +NetDev 1.2, Tokyo, eBPF/XDP hardware offload to SmartNICs, Jakub Kicinski, Nic Viljoen, http://netdevconf.org/1.2/papers/eBPF_HW_OFFLOAD.pdf + +15. Aug 2016, +LinuxCon, Toronto, What Can BPF Do For You?, Brenden Blanco, https://events.linuxfoundation.org/sites/events/files/slides/iovisor-lc-bof-2016.pdf + +14. Aug 2016, +LinuxCon, Toronto, Cilium - Fast IPv6 Container Networking with BPF and XDP, Thomas Graf, https://www.slideshare.net/ThomasGraf5/cilium-fast-ipv6-container-networking-with-bpf-and-xdp + +13 .Aug 2016, +P4, EBPF and Linux TC Offload, Dinan Gunawardena, Jakub Kicinski, https://de.slideshare.net/Open-NFP/p4-epbf-and-linux-tc-offload + +12. Jul 2016, +Linux Meetup, Santa Clara, eXpress Data Path, Brenden Blanco, http://www.slideshare.net/IOVisor/express-data-path-linux-meetup-santa-clara-july-2016 + +11. Jul 2016, +Linux Meetup, Santa Clara, CETH for XDP, Yan Chan, Yunsong Lu, http://www.slideshare.net/IOVisor/ceth-for-xdp-linux-meetup-santa-clara-july-2016 + +10. May 2016, +P4 workshop, Stanford, P4 on the Edge, John Fastabend, https://schd.ws/hosted_files/2016p4workshop/1d/Intel%20Fastabend-P4%20on%20the%20Edge.pdf + +9. Mar 2016, +Performance @Scale 2016, Menlo Park, Linux BPF Superpowers, Brendan Gregg, https://www.slideshare.net/brendangregg/linux-bpf-superpowers + +8. Mar 2016, +eXpress Data Path, Tom Herbert, Alexei Starovoitov, https://github.com/iovisor/bpf-docs/raw/master/Express_Data_Path.pdf + +7. Feb 2016, +NetDev1.1, Seville, On getting tc classifier fully programmable with cls_bpf, Daniel Borkmann, http://borkmann.ch/talks/2016_netdev.pdf http://www.netdevconf.org/1.1/proceedings/papers/On-getting-tc-classifier-fully-programmable-with-cls-bpf.pdf + +6. Jan 2016, +FOSDEM 2016, Brussels, Linux tc and eBPF, Daniel Borkmann, http://borkmann.ch/talks/2016_fosdem.pdf + +5. Oct 2015, +LinuxCon Europe, Dublin, eBPF on the Mainframe, Michael Holzheu, https://events.linuxfoundation.org/sites/events/files/slides/ebpf_on_the_mainframe_lcon_2015.pdf + +4. Aug 2015, +Tracing Summit, Seattle, LLTng’s Trace Filtering and beyond (with some eBPF goodness, of course!), Suchakra Sharma, https://github.com/iovisor/bpf-docs/raw/master/ebpf_excerpt_20Aug2015.pdf + +3. Jun 2015, +LinuxCon Japan, Tokyo, Exciting Developments in Linux Tracing, Elena Zannoni, https://events.linuxfoundation.org/sites/events/files/slides/tracing-linux-ezannoni-linuxcon-ja-2015_0.pdf + +2. Feb 2015, +Collaboration Summit, Santa Rosa, BPF: In-kernel Virtual Machine, Alexei Starovoitov, https://events.linuxfoundation.org/sites/events/files/slides/bpf_collabsummit_2015feb20.pdf + +1. Feb 2015, +NetDev 0.1, Ottawa, BPF: In-kernel Virtual Machine, Alexei Starovoitov, http://netdevconf.org/0.1/sessions/15.html + +0. Feb 2014, +DevConf.cz, Brno, tc and cls_bpf: lightweight packet classifying with BPF, Daniel Borkmann, http://borkmann.ch/talks/2014_devconf.pdf + +추가 문서 + +Dive into BPF: a list of reading material, Quentin Monnet (https://qmonnet.github.io/whirl-offload/2016/09/01/dive-into-bpf/) +XDP - eXpress Data Path, Jesper Dangaard Brouer (https://prototype-kernel.readthedocs.io/en/latest/networking/XDP/index.html) -- 2.17.1