| 검색 | ?

Socket filter

  • 작성자
    조재혁(Mminzkn@minzkn.com)

  • 고친과정
    2011년 4월 07일 : 처음씀

  • 문서의 작성기준 개발환경
    GNU/Linux

1.1. 개요

RAW socket을 이용하여 패킷을 수신하고자 할때 내가 원하는 패킷을 걸러내기 위해서는 많은 작업과 더불어 많은 성능손실을 감수해야 합니다. 그러나 Socket filter를 이용하여 수신하게 되면 커널레벨에서 내가 원하는 패킷만 응용프로그램으로 전달하므로써 이러한 성능손실을 개선할수 있습니다.

중요한것은 BPF(Berkeley Packet Filter) code의 이해가 필요하며 다행히도 잘 몰라도 "tcpdump -dd '<BPF표현문법>'" 명령을 통해서 얻을수 있습니다.

본 문서에서는 BPF라고 하는 것은 "cBPF (Classic Berkeley Packet Filter)" 을 의미하고 eBPF라고 하는 것은 "eBPF (Extended Berkeley Packet Filter)" 을 의미합니다.

cBPF (Classic BPF)는 1992년 부터 존재하였으나 이는 구식으로 취급되며 eBPF (Extended Berkeley Packet Filter)는 [https]Linux Kernel v3.18[] 부터 등장하였습니다. 현재는 cBPF 도 내부적으로 eBPF로 투명하게 변환되어 처리됩니다. eBPF로 작성된 프로그램을 "bpf() / ebpf()" 시스템 콜을 통해서 Kernel에 전달하면 Kernel내에서 Sandbox형태의 인터프리터 ( Interpreter : JIT virtual machine )로 동작하게 됩니다.

1.2. 예제소스

  • @socket_filter.tar.gz (1.79 KB) : Linux에서 Application이 Kernel의 Socket filter를 이용하는 방법 (정상적인 동작을 위해서는 root권한이 필요함)
  • 특정 ICMP reply 패킷을 필터링하여 수신하기 위한 BPF설정 예제
    #include <linux/filter.h> /* BPF : SO_ATTACH_FILTER */
    
    struct sock_filter s_bpf_code[] = { /* ident 가 다르거나 ICMP_ECHOREPLY 가 아닌 경우 버림 */
        BPF_STMT(BPF_LDX | BPF_B   | BPF_MSH, 0),          /* Skip IP header due BSD, see ping6. */
        BPF_STMT(BPF_LD  | BPF_H   | BPF_IND, 4),          /* Load icmp echo ident */
        BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0xAAAA, 1, 0), /* Ours? */
        BPF_STMT(BPF_RET | BPF_K, 0),                      /* Echo with wrong ident. Reject. */
        BPF_STMT(BPF_LD  | BPF_B   | BPF_IND, 0),          /* Load icmp type */
        BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ICMP_ECHOREPLY, 0, 1), /* Echo? */
        BPF_STMT(BPF_RET | BPF_K, ~0U),                    /* Yes, it passes. */
        BPF_STMT(BPF_RET | BPF_K, 0)                       /* Echo with wrong reply. Reject. */
    };
    struct sock_fprog s_filter;
    
    s_filter.len = (unsigned short)(sizeof(s_bpf_code) / sizeof(struct sock_filter));
    s_filter.filter = (struct sock_filter *)(&s_bpf_code[0]);
    
    s_bpf_code[2] = (struct sock_filter)BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, (uint32_t)(s_my_icmp_identification & 0xffffu), 1 /* true jump */, 0 /* false jump */);
    
    setsockopt(s_socket, SOL_SOCKET, (int)(SO_ATTACH_FILTER), (const void *)(&s_filter), (socklen_t)sizeof(s_filter));
    
  • 특정 ICMPv6 reply 패킷을 필터링하여 수신하기 위한 BPF설정 예제
    struct sock_filter s_bpf_code[] = { /* ident 가 다르거나 ICMP6_ECHO_REPLY 가 아닌 경우 버림 */
        BPF_STMT(BPF_LD  | BPF_H   | BPF_ABS, 4),          /* Load icmp echo ident */
        BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0xAAAA, 1, 0), /* Ours? */
        BPF_STMT(BPF_RET | BPF_K, 0),                      /* Echo with wrong ident. Reject. */
        BPF_STMT(BPF_LD  | BPF_B   | BPF_ABS, 0),          /* Load icmp type */
        BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ICMP6_ECHO_REPLY, 0, 1), /* Echo? */
        BPF_STMT(BPF_RET | BPF_K, ~0U),                    /* Yes, it passes. */
        BPF_STMT(BPF_RET | BPF_K, 0)                       /* Echo with wrong reply. Reject. */
    };
    struct sock_fprog s_filter;
    
    s_filter.len = (unsigned short)(sizeof(s_bpf_code) / sizeof(struct sock_filter));
    s_filter.filter = (struct sock_filter *)(&s_bpf_code[0]);
    
    s_bpf_code[1] = (struct sock_filter)BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, (uint32_t)(s_my_icmp_identification  & 0xffffu), 1 /* true jump */, 0 /* false jump */);
    
    setsockopt(s_socket, SOL_SOCKET, (int)(SO_ATTACH_FILTER), (const void *)(&s_filter), (socklen_t)sizeof(s_filter));
    


Copyright ⓒ MINZKN.COM
All Rights Reserved.