네트워크 패킷 계산 : BPF 소켓 필터에서 패킷 데이터 읽기 실패

알렉세이 S.

들어오는 네트워크 패킷을 계산하고 각 TOS 값에 대해 바이트 단위로 len입니다. 두 개의 맵을 만들었습니다. 첫 번째 맵에는 각 TOS 값의 패킷 수를 포함하는 256 개의 항목이 있고 두 번째 맵은 패킷 바이트가 있습니다. 그래서 다음 eBPF 소켓 필터를 작성했습니다.

struct bpf_insn prog[]{
  BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),

  //we use dgram socket, so packet starts directly from IP header
  // BPF_LD_ABS(BPF_H, offsetof(struct ethhdr, h_proto)), // r0 = header type
  // BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, ETH_P_IP, 2),        // if (r0 == IPv4) skip 2
  // BPF_MOV64_IMM(BPF_REG_0, 0),                         // r0 = 0
  // BPF_EXIT_INSN(),                                     // return

  //check for IP version, we only interested in v4
  BPF_LD_ABS(BPF_B, 0),                           // R0 = ip->vers: offsetof(struct iphdr, version)
  BPF_ALU64_IMM(BPF_AND, BPF_REG_0, 0xF0),        // r0 = r0 & 0xF0
  BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0x40, 2),       // if (r0 == 0x40) goto pc+2
  BPF_MOV64_IMM(BPF_REG_0, 0),                    // r0 = 0
  BPF_EXIT_INSN(),                                // return

  // load packet TOS value
  BPF_LD_ABS(BPF_B, offsetof(struct iphdr, tos)), // R0 = ip->tos
  BPF_STX_MEM(BPF_W, BPF_REG_10, BPF_REG_0, -4),  // *(u32 *)(fp - 4) = r0
  BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),           // r2 = fp
  BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4),          // r2 = fp - 4
  //first map with packet counters
  BPF_LD_MAP_FD(BPF_REG_1, map_cnt_fd),           // r1 = map_fd
  BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,
               BPF_FUNC_map_lookup_elem),         // r0 = map_lookup(r1, r2)
  BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 2),          // if (r0 == 0) goto pc+2
  BPF_MOV64_IMM(BPF_REG_1, 1),                    // r1 = 1
  BPF_RAW_INSN(BPF_STX | BPF_XADD | BPF_DW,
               BPF_REG_0, BPF_REG_1, 0, 0),       // xadd r0 += r1

  BPF_LD_ABS(BPF_B, offsetof(struct iphdr, tos)), // R0 = ip->tos
  BPF_STX_MEM(BPF_W, BPF_REG_10, BPF_REG_0, -4),  // *(u32 *)(fp - 4) = r0
  BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
  BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4),          // r2 = fp - 4
  //second map with packet bytes
  BPF_LD_MAP_FD(BPF_REG_1, map_bytes_fd),         // r1 = map_fd
  BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,
               BPF_FUNC_map_lookup_elem),         // r0 = map_lookup(r1, r2)
  BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 2),          // if (r0 == 0) goto pc+2
  // FIXME big endian
  BPF_LDX_MEM(BPF_H, BPF_REG_1, BPF_REG_6,
              offsetof(struct iphdr, tot_len)),   // r1 = tot_len
  BPF_RAW_INSN(BPF_STX | BPF_XADD | BPF_DW,
               BPF_REG_0, BPF_REG_1, 0, 0),       // xadd r0 += r1

  BPF_MOV64_IMM(BPF_REG_0, 0),                    // r0 = 0
  BPF_EXIT_INSN(),
};

맵은 오류없이 생성되고 소켓 필터 프로그램은 잘 생성되며 패킷 카운터 부분은 정상적으로 작동합니다. 그러나 바이트 카운터는 항상 0입니다. 그 코드의 문제점은 무엇입니까?

간단한 를 작성해 보았습니다 . 컴파일하려면 bpf_insn.h 를 포함 하면 됩니다.

Qeole

문제점 : 소켓 버퍼에서 읽기

BPF_REG_1프로그램이 시작되기 전에 배치 된 컨텍스트 는 데이터 시작에 대한 포인터가 아닙니다. 대신 다음과 같이 UAPI 헤더에struct __sk_buff 정의 된 포인터입니다 .

struct __sk_buff {
    __u32 len;
    ...
}

따라서 IP 헤더에서 데이터를 읽으려고 할 때 :

  BPF_LDX_MEM(BPF_H, BPF_REG_1, BPF_REG_6, offsetof(struct iphdr, tot_len)),

실제로 오프셋 2에서 2 바이트를 읽고 있습니다 struct __sk_buff(포인터를 호출합시다 skb). 시스템이 리틀 엔디안이므로 skb->len2 ^ 16 바이트보다 큰 패킷이 없으면 0 인에 대한 최상위 비트에 해당합니다 .

여기에 두 가지 가능한 해결책이 있습니다.

해결 방법 1 : 절대 부하 사용

올바른 위치에서 IP 길이를 읽도록 프로그램을 업데이트 할 수 있습니다. BPF_LDX_MEM()소켓 필터 가 직접 패킷 액세스를 허용하지 않기 때문에 나는 이것이 가능하지 않다고 생각 합니다 . 해결 방법은 대신 절대로드를 사용하는 것입니다. 프로그램은 다음과 같습니다.

struct bpf_insn prog[]{
  BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),

  // ... packet number counter, skipped for brevity

  // Read IP length and store to r7 (preserved during helper calls)
  BPF_LD_ABS(BPF_H,
             offsetof(struct iphdr, tot_len)),    // r0 = tot_len
  BPF_MOV64_REG(BPF_REG_7, BPF_REG_0),            // r7 = r0

  // No need to parse ToS a second time here, skipped
  BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
  BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4),          // r2 = fp - 4
  //second map with packet bytes
  BPF_LD_MAP_FD(BPF_REG_1, map_bytes_fd),         // r1 = map_fd
  BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,
               BPF_FUNC_map_lookup_elem),         // r0 = map_lookup(r1, r2)
  BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 2),          // if (r0 == 0) goto pc+2

  // Now add length to the counter
  BPF_STX_XADD(BPF_DW, BPF_REG_0, BPF_REG_7, 0),  // xadd r0 += r7

  BPF_MOV64_IMM(BPF_REG_0, 0),                    // r0 = 0
  BPF_EXIT_INSN(),
};

해결책 2 : 그냥 사용 skb->len

다른 해결책은 skb커널이 이미 우리를 위해 계산했기 때문에에서 길이를 얻는 것입니다. 이것은 당신이 가졌던 하중의 오프셋과 길이를 고정하는 문제이며 당신은 다음과 같이 BPF_STX_MEM(), BPF_XADD()될 것입니다.

  BPF_LDX_MEM(BPF_W, BPF_REG_1, BPF_REG_6,
              offsetof(struct __sk_buff, len)),         // r1 = skb->len
  BPF_STX_XADD(BPF_DW, BPF_REG_0, BPF_REG_1, 0),        // xadd r0 += r1

이 기사는 인터넷에서 수집됩니다. 재 인쇄 할 때 출처를 알려주십시오.

침해가 발생한 경우 연락 주시기 바랍니다[email protected] 삭제

에서 수정
0

몇 마디 만하겠습니다

0리뷰
로그인참여 후 검토

관련 기사

분류에서Dev

소켓 패킷 C #에서 데이터의 실제 크기를 얻는 방법

분류에서Dev

네트워크 연결없이 패킷 보내기 (무선 어댑터)

분류에서Dev

데이터 링크 계층에서 패킷 차단

분류에서Dev

무선 네트워크에서 MAC 주소 당 패킷 수

분류에서Dev

Node.js 웹 소켓 클라이언트-서버 패킷

분류에서Dev

GIOP 네트워크 패킷 이해

분류에서Dev

파이썬에서 DNS 패킷 읽기

분류에서Dev

일정 기간 동안 실시간 소형 패킷 데이터에 TCP 사용

분류에서Dev

모든 네트워크 인터페이스에서 m-search 패킷 보내기

분류에서Dev

클라이언트에 데이터 패킷 보내기

분류에서Dev

AAC 패킷 크기

분류에서Dev

패킷 손실없이 고주파 데이터 기록

분류에서Dev

Rust의 구조체로 데이터 패킷 읽기

분류에서Dev

특정 네트워크의 우분투에서 네트워크 패킷 손실

분류에서Dev

네트워크 어댑터 아래에 나열되지 않은 매직 패킷 옵션을 기다립니다.

분류에서Dev

(대상 네트워크에서 데이터 패킷이 없습니다!) aircrack-ng ubuntu의 오류

분류에서Dev

커널 모듈에서 소켓으로 패킷 보내기

분류에서Dev

Python 소켓에서 사용자 지정 Scapy 패킷 받기

분류에서Dev

소켓을 통해 전송 된 패킷의 크기

분류에서Dev

C의 원시 소켓 패킷 수신기, 이상한 출력

분류에서Dev

C- 서버 소켓이 잘못된 패킷이 됨

분류에서Dev

eBPF 패킷 모니터가 'ping -f'시 일부 패킷 손실

분류에서Dev

네트워크 패킷에 애플리케이션 유형 태그 지정

분류에서Dev

패킷 크기 증가시 RTT 감소

분류에서Dev

OpenSSL : 애플리케이션 데이터의 패킷 크기 지정

분류에서Dev

올바른 필터링 된 패킷을 얻기 위해 원시 AF_PACKET 소켓을 비우는 방법

분류에서Dev

네트워크 인터페이스 내에서 보류중인 패킷은 몇 개입니까?

분류에서Dev

iOS에서 네트워크 패킷 차단

분류에서Dev

동일한 네트워크에서 패킷 전달

Related 관련 기사

  1. 1

    소켓 패킷 C #에서 데이터의 실제 크기를 얻는 방법

  2. 2

    네트워크 연결없이 패킷 보내기 (무선 어댑터)

  3. 3

    데이터 링크 계층에서 패킷 차단

  4. 4

    무선 네트워크에서 MAC 주소 당 패킷 수

  5. 5

    Node.js 웹 소켓 클라이언트-서버 패킷

  6. 6

    GIOP 네트워크 패킷 이해

  7. 7

    파이썬에서 DNS 패킷 읽기

  8. 8

    일정 기간 동안 실시간 소형 패킷 데이터에 TCP 사용

  9. 9

    모든 네트워크 인터페이스에서 m-search 패킷 보내기

  10. 10

    클라이언트에 데이터 패킷 보내기

  11. 11

    AAC 패킷 크기

  12. 12

    패킷 손실없이 고주파 데이터 기록

  13. 13

    Rust의 구조체로 데이터 패킷 읽기

  14. 14

    특정 네트워크의 우분투에서 네트워크 패킷 손실

  15. 15

    네트워크 어댑터 아래에 나열되지 않은 매직 패킷 옵션을 기다립니다.

  16. 16

    (대상 네트워크에서 데이터 패킷이 없습니다!) aircrack-ng ubuntu의 오류

  17. 17

    커널 모듈에서 소켓으로 패킷 보내기

  18. 18

    Python 소켓에서 사용자 지정 Scapy 패킷 받기

  19. 19

    소켓을 통해 전송 된 패킷의 크기

  20. 20

    C의 원시 소켓 패킷 수신기, 이상한 출력

  21. 21

    C- 서버 소켓이 잘못된 패킷이 됨

  22. 22

    eBPF 패킷 모니터가 'ping -f'시 일부 패킷 손실

  23. 23

    네트워크 패킷에 애플리케이션 유형 태그 지정

  24. 24

    패킷 크기 증가시 RTT 감소

  25. 25

    OpenSSL : 애플리케이션 데이터의 패킷 크기 지정

  26. 26

    올바른 필터링 된 패킷을 얻기 위해 원시 AF_PACKET 소켓을 비우는 방법

  27. 27

    네트워크 인터페이스 내에서 보류중인 패킷은 몇 개입니까?

  28. 28

    iOS에서 네트워크 패킷 차단

  29. 29

    동일한 네트워크에서 패킷 전달

뜨겁다태그

보관