BPF 이해

BPF 이해

다음을 사용하여 일부 패킷을 캡처해야 하는 경우 tcpdump:

tcpdump -i eth0 "dst host 192.168.1.0"

나는 항상 생각한다대상 호스트 192.168.1.0그 일부를 BPF(Berkeley Packet Filter)라고 합니다. 나에게 이것은 네트워크 패킷을 필터링하기 위한 간단한 언어입니다. 하지만 오늘 내 룸메이트는 BPF를 사용하여 성능 정보를 캡처할 수 있다고 말했습니다. 그의 설명에 따르면 이것은 perfmonWindows의 도구와 같습니다. 이거 진짜야? 질문 시작 부분에 언급한 BPF와 동일한가요?

답변1

BPF란 무엇입니까?

BPF(또는 더 일반적으로 확장 버전,전자 BPF)는 원래 패킷 필터링을 위해 특별히 설계된 언어이지만 그 이상의 기능을 수행합니다. Linux에서는 다음을 포함한 다양한 목적으로 사용될 수 있습니다.시스템 호출 필터안전상의 이유로,종료할 프로세스를 선택하세요.지적하신 대로 시스템 메모리가 부족한 경우와 복잡한 성능 모니터링이 가능합니다. Windows가 그렇긴 하지만eBPF 지원 추가perfmon, Windows 유틸리티에서는 사용 되지 않습니다 . Windows에는 eBPF에 대한 운영 체제 지원에 의존하는 비 Windows 유틸리티에 대한 호환성 지원만 추가되었습니다.

eBPF 프로그램은 사용자 공간에서 실행되지 않습니다. 대신 애플리케이션은 eBPF 프로그램을 생성하여 커널로 보낸 다음 이를 실행합니다. 이는 실제로 커널에서 인터프리터로 구현되는 가상 프로세서의 기계어 코드이지만, 사용할 수도 있습니다.적시 컴파일성능을 획기적으로 향상시킵니다. 이 프로그램은 성능 및 네트워킹과 관련된 인터페이스를 포함하여 커널의 일부 기본 인터페이스에 액세스할 수 있습니다. 그런 다음 eBPF 프로그램은 커널과 통신하여 계산 결과(예: 삭제된 패킷)를 제공합니다.

eBPF 프로젝트의 한계

서비스 거부 공격이나 예상치 못한 충돌을 방지하려면 커널이 먼저확인하다컴파일하기 전의 코드. 코드는 실행하기 전에 몇 가지 중요한 검사를 거쳐야 합니다.

  • 계획에는 다음이 포함됩니다.4096권한이 없는 사용자를 위한 일반 지침입니다.

  • 다음을 제외하고는 뒤로 점프할 수 없습니다.경계 루프그리고 함수 호출.

  • 결코 도달할 수 없는 지시는 없습니다.

결과적으로 검증자는 eBPF 프로그램이 중지되었음을 증명할 수 있어야 합니다. 아직 해결책이 발견되지 않았습니다질문 중지물론 이것이 바로 자체 프로그램만 허용하는 이유입니다.알다멈출 것이다. 이를 위해 프로그램을 다음과 같이 나타냅니다.방향성 비순환 그래프. 이 외에도 실제 작업을 차단하여 정보 유출 및 범위를 벗어난 메모리 접근을 방지하려고 합니다.제한된 작업을 허용하면서 포인터가 누출되는 것을 방지합니다.

  • 포인터는 확인 가능한 값으로 비교, 저장 또는 반환될 수 없습니다.

  • 포인터 연산은 스칼라(포인터에서 파생되지 않은 값)에서만 수행할 수 있습니다.

  • 포인터 연산을 수행하면 지정된 메모리 맵 외부를 가리키는 결과가 발생하지 않습니다.

검증인은꽤 복잡하다그 자체이지만 그 이상을 수행합니다.심각한 안전 곤충, 적어도 언제bpf(2)시스템 호출은권한이 없는 사용자에게는 비활성화됨.

코드 보기

이 명령의 구성요소 dst host 192.168.1.0는 BPF가 아닙니다. 그것은 단지 사용된 구문일 뿐입니다 tcpdump. 그러나 사용자가 제공하는 명령은 BPF 프로그램을 생성한 다음 이를 커널로 보내는 데 사용됩니다. 이 예에서는 eBPF가 사용되지 않고 이전 cBPF가 사용됩니다. 가지다몇 가지 중요한 차이점그 사이 어딘가에 있습니다(커널이 내부적으로 cBPF를 eBPF로 변환하지만). 이 -d플래그는 커널로 전송되는 cBPF 코드를 보는 데 사용할 수 있습니다.

# tcpdump -i eth0 "dst host 192.168.1.0" -d
(000) ldh      [12]
(001) jeq      #0x800           jt 2    jf 4
(002) ld       [30]
(003) jeq      #0xc0a80100      jt 8    jf 9
(004) jeq      #0x806           jt 6    jf 5
(005) jeq      #0x8035          jt 6    jf 9
(006) ld       [38]
(007) jeq      #0xc0a80100      jt 8    jf 9
(008) ret      #262144
(009) ret      #0

필터가 더 복잡할수록 바이트코드도 더 복잡해집니다. -d커널에 로드될 바이트코드를 확인하려면 맨페이지 및 추가 플래그의 몇 가지 예제를 시도해 보십시오 . 디스어셈블리 코드를 읽는 방법을 알아보려면 다음을 확인하세요.BPF 필터 문서. eBPF 프로그램을 읽고 있다면 한 번 살펴보세요.eBPF 명령어 세트가상 CPU의 경우.

코드 이해

단순화를 위해 대상 IP 192.168.1.0 대신 192.168.1.1을 지정하고 IPv4만 일치시키려고 한다고 가정하겠습니다. 그러면 더 이상 IPv6를 처리할 필요가 없으므로 코드가 크게 줄어듭니다.

# tcpdump -i eth0 "dst host 192.168.1.1 and ip" -d
(000) ldh      [12]
(001) jeq      #0x800           jt 2    jf 5
(002) ld       [30]
(003) jeq      #0xc0a80101      jt 4    jf 5
(004) ret      #262144
(005) ret      #0

위의 바이트코드가 실제로 무엇인지 살펴보겠습니다.하다. BPF 바이트코드는 지정된 인터페이스에서 패킷이 수신될 때마다 실행됩니다. 패킷 내용(해당하는 경우 이더넷 헤더 포함)은 BPF 코드에 액세스할 수 있는 버퍼에 배치됩니다. 이 코드는 패킷이 필터와 일치하면 캡처 버퍼의 크기(기본값은 262144바이트)를 반환하고, 그렇지 않으면 0을 반환합니다.

이 필터를 실행 중이고 192.168.1.142에서 192.168.1.1까지 빈 페이로드가 포함된 ICMP 메시지를 보내는 패킷을 수신한다고 가정해 보겠습니다. 소스 MAC는 aa:aa:aa:aa:aa:aa이고 대상 MAC는 bb:bb:bb:bb:bb:bb입니다. 이더넷 프레임의 내용(16진수)은 다음과 같습니다.

aa aa aa aa aa aa bb bb bb bb bb bb 08 00 45 00
00 1c 77 71 40 00 40 01 3f 92 c0 a8 01 8e c0 a8
01 01 08 00 c1 c0 36 0e 00 01

첫 번째 명령어는 입니다 ldh [12]. 이는 패킷의 오프셋 12바이트에 있는 하프워드(2바이트)를 A 레지스터로 로드합니다. 이는 0x0800 값입니다(네트워크 데이터는 항상 빅 엔디안임을 기억하세요). 두 번째 명령은 즉 jeq #0x800치값을 A 레지스터의 값과 비교하는 것입니다. 동일하면 명령어 2로 점프하고, 그렇지 않으면 명령어 5로 점프합니다. 이더넷 프레임의 이 오프셋에서 값 0x800은 IPv4 프로토콜을 지정합니다. 비교가 참이므로 이제 코드는 명령어 2로 점프합니다. 페이로드가 IPv4가 아닌 경우 명령 5로 점프합니다.

지침 2(제3조)는 입니다 ld [30]. 그러면 오프셋 30에 있는 전체 4바이트 워드가 A 레지스터로 로드됩니다. 이더넷 프레임에서는 0xc0a80101입니다. 다음 명령어는 jeq #0xc0a80101즉치값을 A 레지스터의 내용과 비교하여 참이면 4로 점프하고, 그렇지 않으면 5로 점프합니다. 값은 대상 주소입니다(0xc0a80101은 192.168.1.1의 빅엔디안 표현입니다). 값이 일치하므로 이제 프로그램 카운터가 4로 설정됩니다.

명령어 4는 ret #262144BPF 프로그램을 종료하고 호출 프로그램에 정수 262144를 반환합니다. 이 경우 이는 호출 프로그램에 tcpdump패킷이 필터에 의해 포착되었음을 알리므로 커널에서 패킷의 내용을 요청하고 더 철저하게 디코딩한 후 해당 정보를 터미널에 기록합니다. 대상 주소가 필터가 찾고 있는 주소와 일치하지 않거나 프로토콜 유형이 IPv4가 아닌 경우 코드는 해당 주소가 발견되는 명령 5로 점프합니다 ret #0. 일치하지 않고 종료됩니다.

이는 패킷의 오프셋 12에 있는 하프워드가 0x800이고 오프셋 30에 있는 단어가 0xc0a80101인 경우 262144를 반환하는 방법일 뿐이며, 그렇지 않으면 0이 반환됩니다. 이 작업은 모두 커널에서 수행되기 때문에(선택적으로 JIT 엔진에 의해 네이티브 기계 코드로 변환된 후) 비용이 많이 드는 컨텍스트 전환이나 커널 공간과 사용자 공간 사이에 버퍼를 전달할 필요가 없으므로 필터는 yes입니다.빠르게.

고급 예

BPF 코드는 사용에만 국한되지 않습니다 tcpdump. 다른 많은 유틸리티에서 이를 사용할 수 있습니다. 이 xt_bpf모듈을 사용하여 BPF 필터로 iptables 규칙을 생성 할 수도 있습니다 ! 그러나 바이트코드를 생성할 때는 tcpdump -dddiptables가 지원하지 않는 레이어 2 헤더가 필요하므로 주의해야 합니다 . 호환되도록 하려면 오프셋을 조정해야 합니다.

또한 패킷 길이, 페이로드 시작 오프셋, 패킷을 수신하는 CPU, NetFilter 플래그 등과 같이 원시 패킷 내용을 읽어서 얻을 수 없는 정보를 제공하는 다양한 보조 기능이 제공됩니다. 필터 문서:

Linux 커널에는 k 매개변수를 음수 오프셋 + 특정 확장 오프셋으로 "오버로드"하여 로드 명령 클래스와 함께 작동하는 일부 BPF 확장도 있습니다. 이 BPF 확장의 결과는 A에 로드됩니다.

지원되는 BPF 확장은 다음과 같습니다.

확장하다 설명하다
런던 skb->렌
원기 skb->프로토콜
유형 skb->pkt_type
퍼프 페이로드 시작 오프셋
이피딕스 skb->dev->ifindex
엔라 유형 X 및 오프셋 A의 Netlink 속성
엔란 X 유형의 중첩된 Netlink 속성, 오프셋 A
표시 skb->마크
대기줄 skb->큐 매핑
하드타입 skb->개발자->유형
해시값 수신 skb->해시
CPU raw_smp_processor_id()
vlan_tci skb_vlan_tag_get(skb)
vlan_avail skb_vlan_tag_present(skb)
vlan_tpid skb->vlan_proto
랜드 무작위_u32()

예를 들어 CPU 3에서 수신된 모든 패킷을 일치시키려면 다음을 수행할 수 있습니다.

    ld #cpu
    jneq #3, drop
    ret #262144
drop:
    ret #0

이는 다음과 호환되는 BPF 어셈블리 구문을 사용하고 있음에 유의하세요.bpf_asm, 여기서 다른 어셈블리 목록은 tcpdump구문을 사용합니다. 주요 차이점은 전자 구문은 명명된 레이블을 사용하는 반면 후자 BPF 구문은 줄 번호를 사용하여 각 명령어를 표시한다는 것입니다. 이 어셈블리는 다음 바이트코드(쉼표로 구분된 지침)로 변환됩니다.

4,32 0 0 4294963236,21 0 1 1,6 0 0 262144,6 0 0 0,

그런 다음 iptables모듈과 함께 사용할 수 있습니다 xt_bpf.

iptables -A INPUT -m bpf --bytecode "4,32 0 0 4294963236,21 0 1 1,6 0 0 262144,6 0 0 0," -j CPU3

이는 CPU3해당 CPU에서 수신된 모든 패킷의 대상 체인으로 이동합니다.

이것이 강력해 보인다면 이것이 모두 cBPF라는 것을 기억하십시오. cBPF는 내부적으로 eBPF로 변환되지만 이것이 전부입니다.아무것도 없다원래 eBPF의 기능과 비교해보세요!

더 많은 정보를 알고 싶다면

꼭 읽어보시길 권합니다이 기사cBPF 사용 방법을 알아보세요 tcpdump.

읽어보신 후 꼭 읽어주세요이 설명표현식을 바이트코드로 변환하는 방법 tcpdump.

그것에 대한 다른 모든 것을 알고 싶다면 언제든지 확인할 수 있습니다.소스 코드!

답변2

eBPF 프로그램은 사용자 공간에서 실행되지 않습니다. 대신 애플리케이션은 eBPF 프로그램을 생성하여 커널로 보낸 다음 이를 실행합니다.

@forest의 좋은 답변을 보완하기 위해 이러한 절차가 수행되는 방법을 자세히 설명할 수 있습니다.

tcpdump가 사용하는 cBPF에는 후크가 거의 없습니다.소켓에 연결, 을 위한패킷이 도착하면 실행(이것은 tcpdump가 수행하는 작업입니다. 소켓에서 수신된 패킷을 필터링하고 필요한 패킷만 사용자 공간에 전달합니다.)seccomp 후크, 시스템 호출 및 해당 매개변수에 대해 일부 필터링을 수행합니다.

의 중요한 특징 중 하나전자 BPF네, 첨부 가능합니다더 넓은 후크 선택커널에서(seccomp를 수행하지는 않지만) 네트워크의 경우 다음이 있습니다.소켓, 그러나 또한TC(교통 통제) 후크,XDP(빠른 네트워킹을 위한 드라이버 수준 후크) 또는 기타. 귀하의 질문에 관하여: 프로그램은 다음에 첨부될 수도 있습니다.추적점커널(시스템 호출이나 커널의 "중요" 함수와 같은 일부 특정 함수에 대해 미리 정의된 후크) 또는 커널 프로브(k 프로브), 이를 통해모든 기능 추적커널에서(컴파일 타임에 인라인되지 않은 경우) 그 다음에다른 유형예를 들어 보안 사용 사례를 위한 LSM이 있습니다.

일반적으로 의존추적점 또는 kprobeeBPF 프로그램을 함수에 연결하고 커널에서 함수가 호출될 때마다 실행합니다. 프로그램은 함수의 매개변수 또는 (종료 시 첨부된 경우) 반환 값에 액세스할 수 있습니다. 사용하여지도, 배열이나 해시 맵과 같은 특수 커널 메모리 영역으로,데이터 공유eBPF 프로그램 및/또는 사용자 공간 사이에서 프로그램은 측정항목을 수집하거나 공유할 수 있습니다.상태연속 실행 사이.

예를 들어,모니터링 켜기open()from BCC는 시스템 호출의 시작 및 종료 추적 지점에 추가됩니다 openat(). 진입 시 열려는 파일의 경로와 파일을 연 프로세스의 PID를 수집하여 해시 맵에 저장합니다. 시스템 호출이 종료되면 두 번째 프로브는 반환 값을 수집하고 PID를 기반으로 해시 맵에서 관련 항목을 업데이트합니다. 그런 다음 사용자 공간은 해시 맵의 모든 항목을 수집하고 덤프하여 어떤 프로세스가 어떤 파일을 열었는지, 반환 값이 무엇인지 표시할 수 있습니다.

https://ebpf.io/eBPF 사용을 시작하기에 좋은 곳입니다.

관련 정보