들어오는 네트워크 패킷을 계산하고 각 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 를 포함 하면 됩니다.
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->len
2 ^ 16 바이트보다 큰 패킷이 없으면 0 인에 대한 최상위 비트에 해당합니다 .
여기에 두 가지 가능한 해결책이 있습니다.
올바른 위치에서 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(),
};
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] 삭제
몇 마디 만하겠습니다