Microsoft MVP성태의 닷넷 이야기
Linux: 123. eBPF (bpf2go) - BPF_PROG_TYPE_SOCKET_FILTER 예제 - SEC("socket") [링크 복사], [링크+제목 복사],
조회: 817
글쓴 사람
정성태 (seongtaejeong at gmail.com)
홈페이지
첨부 파일
 
(연관된 글이 1개 있습니다.)
(시리즈 글이 16개 있습니다.)
Linux: 86. Golang + bpf2go를 사용한 eBPF 기본 예제
; https://www.sysnet.pe.kr/2/0/13769

Linux: 94. eBPF - vmlinux.h 헤더 포함하는 방법 (bpf2go에서 사용)
; https://www.sysnet.pe.kr/2/0/13783

Linux: 95. eBPF - kprobe를 이용한 트레이스
; https://www.sysnet.pe.kr/2/0/13784

Linux: 96. eBPF (bpf2go) - fentry, fexit를 이용한 트레이스
; https://www.sysnet.pe.kr/2/0/13788

Linux: 100.  eBPF의 2가지 방식 - libbcc와 libbpf(CO-RE)
; https://www.sysnet.pe.kr/2/0/13801

Linux: 103. eBPF (bpf2go) - Tracepoint를 이용한 트레이스 (BPF_PROG_TYPE_TRACEPOINT)
; https://www.sysnet.pe.kr/2/0/13810

Linux: 105. eBPF - bpf2go에서 전역 변수 설정 방법
; https://www.sysnet.pe.kr/2/0/13815

Linux: 106. eBPF (bpf2go) - (BPF_MAP_TYPE_HASH) Map을 이용한 전역 변수 구현
; https://www.sysnet.pe.kr/2/0/13817

Linux: 107. eBPF - libbpf CO-RE의 CONFIG_DEBUG_INFO_BTF 빌드 여부에 대한 의존성
; https://www.sysnet.pe.kr/2/0/13819

Linux: 109. eBPF (bpf2go) - BPF_PERF_OUTPUT / BPF_MAP_TYPE_PERF_EVENT_ARRAY 사용법
; https://www.sysnet.pe.kr/2/0/13824

Linux: 110. eBPF (bpf2go) - BPF_RINGBUF_OUTPUT / BPF_MAP_TYPE_RINGBUF 사용법
; https://www.sysnet.pe.kr/2/0/13825

Linux: 115. eBPF (bpf2go) - ARRAY / HASH map 기본 사용법
; https://www.sysnet.pe.kr/2/0/13893

Linux: 116. eBPF (bpf2go) - BTF Style Maps 정의 구문과 데이터 정렬 문제
; https://www.sysnet.pe.kr/2/0/13894

Linux: 117. eBPF (bpf2go) - Map에 추가된 요소의 개수를 확인하는 방법
; https://www.sysnet.pe.kr/2/0/13895

Linux: 122. eBPF - __attribute__((preserve_access_index)) 사용법
; https://www.sysnet.pe.kr/2/0/14016

Linux: 123. eBPF (bpf2go) - BPF_PROG_TYPE_SOCKET_FILTER 예제 - SEC("socket")
; https://www.sysnet.pe.kr/2/0/14017




eBPF (bpf2go) - BPF_PROG_TYPE_SOCKET_FILTER 예제 - SEC("socket")

이번엔 소켓을 모니터링하는 eBPF 프로그램의 하나인 Socket Filter 유형(BPF_PROG_TYPE_SOCKET_FILTER)에 대해 알아보겠습니다.

Program type BPF_PROG_TYPE_SOCKET_FILTER
; https://docs.ebpf.io/linux/program-type/BPF_PROG_TYPE_SOCKET_FILTER/

아래의 글에서 이와 관련한 예제를 볼 수 있는데,

L7 Tracing with eBPF: HTTP and Beyond via Socket Filters and Syscall Tracepoints
; https://eunomia.dev/en/tutorials/23-http/

SEC("socket") 매크로를 사용하여 소켓 필터 프로그램을 정의하고 있습니다.

SEC("socket")
int socket_handler(struct __sk_buff *skb)
{
    // ...[생략]...Your eBPF program logic here

    return skb->len; // 반드시 패킷의 길이를 반환해야 통신이 허용됩니다.
}

함수의 인자로 전달되는 __sk_buff 구조체는 sk_buff 구조체의 eBPF 표현인데요,

// (제 테스트 시스템 기준으로) sizeof(__sk_buff) == 184
// cat vmlinux.h | grep -A 38 "struct __sk_buff {"
struct __sk_buff {
        __u32 len;
        __u32 pkt_type;
        __u32 mark;
        __u32 queue_mapping;
        __u32 protocol; // This field indicates the Layer 3 protocol of the packet and is one of the ETH_P_* values defined in include/uapi/linux/if_ether.h.
        __u32 vlan_present;
        __u32 vlan_tci;
        __u32 vlan_proto;
        __u32 priority;
        __u32 ingress_ifindex;
        __u32 ifindex;
        __u32 tc_index;
        __u32 cb[5];
        __u32 hash;
        __u32 tc_classid;
        __u32 data;
        __u32 data_end;
        __u32 napi_id;
        __u32 family;
        __u32 remote_ip4;
        __u32 local_ip4;
        __u32 remote_ip6[4];
        __u32 local_ip6[4];
        __u32 remote_port;
        __u32 local_port;
        __u32 data_meta;
        union {
                struct bpf_flow_keys *flow_keys; 
        };
        __u64 tstamp;
        __u32 wire_len;
        __u32 gso_segs;
        union {
                struct bpf_sock *sk;
        };
        __u32 gso_size;
};

// cat vmlinux.h | grep -A 118 "struct sk_buff {"
struct sk_buff {
        union {
                struct {
                        struct sk_buff *next;
                        struct sk_buff *prev;
                        union {
                                struct net_device *dev;
                                long unsigned int dev_scratch;
                        };
                };
                struct rb_node rbnode;
                struct list_head list;
        };
        union {
                struct sock *sk;
                int ip_defrag_offset;
        };
        union {
                ktime_t tstamp;
                u64 skb_mstamp_ns;
        };
        char cb[48];
        union {
                struct {
                        long unsigned int _skb_refdst;
                        void (*destructor)(struct sk_buff *);
                };
                struct list_head tcp_tsorted_anchor;
                long unsigned int _sk_redir;
        };
        long unsigned int _nfct;
        unsigned int len;
        unsigned int data_len;
        __u16 mac_len;
        __u16 hdr_len;
        __u16 queue_mapping;
        __u8 __cloned_offset[0];
        __u8 cloned: 1;
        __u8 nohdr: 1;
        __u8 fclone: 2;
        __u8 peeked: 1;
        __u8 head_frag: 1;
        __u8 pfmemalloc: 1;
        __u8 pp_recycle: 1;
        __u8 active_extensions;
        __u32 headers_start[0];
        __u8 __pkt_type_offset[0];
        __u8 pkt_type: 3;
        __u8 ignore_df: 1;
        __u8 nf_trace: 1;
        __u8 ip_summed: 2;
        __u8 ooo_okay: 1;
        __u8 l4_hash: 1;
        __u8 sw_hash: 1;
        __u8 wifi_acked_valid: 1;
        __u8 wifi_acked: 1;
        __u8 no_fcs: 1;
        __u8 encapsulation: 1;
        __u8 encap_hdr_csum: 1;
        __u8 csum_valid: 1;
        __u8 __pkt_vlan_present_offset[0];
        __u8 vlan_present: 1;
        __u8 csum_complete_sw: 1;
        __u8 csum_level: 2;
        __u8 csum_not_inet: 1;
        __u8 dst_pending_confirm: 1;
        __u8 ndisc_nodetype: 2;
        __u8 ipvs_property: 1;
        __u8 inner_protocol_type: 1;
        __u8 remcsum_offload: 1;
        __u8 offload_fwd_mark: 1;
        __u8 offload_l3_fwd_mark: 1;
        __u8 tc_skip_classify: 1;
        __u8 tc_at_ingress: 1;
        __u8 redirected: 1;
        __u8 slow_gro: 1;
        __u8 scm_io_uring: 1;
        __u16 tc_index;
        union {
                __wsum csum;
                struct {
                        __u16 csum_start;
                        __u16 csum_offset;
                };
        };
        __u32 priority;
        int skb_iif;
        __u32 hash;
        __be16 vlan_proto;
        __u16 vlan_tci;
        union {
                unsigned int napi_id;
                unsigned int sender_cpu;
        };
        __u32 secmark;
        union {
                __u32 mark;
                __u32 reserved_tailroom;
        };
        union {
                __be16 inner_protocol;
                __u8 inner_ipproto;
        };
        __u16 inner_transport_header;
        __u16 inner_network_header;
        __u16 inner_mac_header;
        __be16 protocol;
        __u16 transport_header;
        __u16 network_header;
        __u16 mac_header;
        __u32 headers_end[0];
        sk_buff_data_t tail;
        sk_buff_data_t end;
        unsigned char *head;
        unsigned char *data;
        unsigned int truesize;
        refcount_t users;
        struct skb_ext *extensions;
};

대충 이런 식으로 접근해 사용할 수 있습니다.

SEC("socket")
int socket_handler(struct __sk_buff *skb)
{
    __u16 protocol = skb->protocol;

    char isIPv4Packet = (protocol == bpf_htons(ETH_P_IP)); // ETH_P_IP == 0x0800(BE) == 8(LE)
    char isIPv6Packet = (protocol == bpf_htons(ETH_P_IPV6)); // ETH_P_IPV6 == 0x86DD(BE) == dd86(LE)

    if (isIPv4Packet == 0 && isIPv6Packet == 0) {
        return skb->len;
    }

    // Your eBPF program logic here

    return skb->len;
}

하지만, 무시할 수 없는 제약이 좀 있는데요, 우선, 1) 오직 ingoing 패킷에 대해서만 작동하고, 2) 무엇보다도 특정 socket descriptor에 attach 시키는 식으로만 활성화할 수 있기 때문에,

remoteAddr, err := net.ResolveTCPAddr("tcp", "192.168.100.50:80")

// 이렇게 Socket 관련 File Descriptor를 얻어온 다음,
conn, err := net.DialTCP("tcp", nil, remoteAddr)
if err != nil {
	return
}
defer conn.Close()

// 해당 FD에 eBPF 프로그램을 attach 시켜야 합니다.
err = link.AttachSocketFilter(conn, bpfObj.SocketHandler)
if err != nil {
	fmt.Printf("link.AttachNetfilter: %v\n", err)
	return
}
defer func(tcpConn *net.TCPConn) {
	_ = link.DetachSocketFilter(tcpConn)
}(conn)

(일반적인 다른 eBPF 프로그램들이 기본적으로 전역 모니터링할 수 있었다는 점과 비교해) 로컬 프로세스 내의 정보만을 얻을 수 있다는 점이 아쉽습니다. 즉, in-process 내부의 소켓에 대해 Input 패킷만을 모니터링할 수 있을 뿐입니다.

아마도 그런 점 때문에, "Program type BPF_PROG_TYPE_SOCKET_FILTER" 문서에 다음과 같은 주석이 있는 것 같습니다.

Socket filters pre-date eBPF itself, socket filters were the first ever prototype in the original BPF implementation, now referred to as cBPF (classic BPF). In fact, usage of this program type was the reason for inventing the whole system1.


참고로, 전에 SEC("socket") 매크로를 전역 변수를 제어하기 위한 함수를 위해서도 사용한 적이 있는데요, 이것이 BPF_PROG_TYPE_SOCKET_FILTER 유형의 프로그램이라는 것을 알았음에도... 왜 유독 SEC("socket") 매크로만 유효했는지 여전히 의문이 남는군요. ^^




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]

[연관 글]






[최초 등록일: ]
[최종 수정일: 9/29/2025]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 




... 121  122  123  124  125  126  127  128  129  130  131  132  133  134  [135]  ...
NoWriterDateCnt.TitleFile(s)
1778정성태10/10/201422469오류 유형: 247. The Netlogon service could not create server share C:\Windows\SYSVOL\sysvol\[도메인명]\SCRIPTS.
1777정성태10/10/201425433오류 유형: 246. The processing of Group Policy failed. Windows attempted to read the file \\[도메인]\sysvol\[도메인]\Policies\{...GUID...}\gpt.ini
1776정성태10/10/201422126오류 유형: 245. 이벤트 로그 - Name resolution for the name _ldap._tcp.dc._msdcs.[도메인명]. timed out after none of the configured DNS servers responded.
1775정성태10/9/201423671오류 유형: 244. Visual Studio 디버깅 (2) - Unable to break execution. This process is not currently executing the type of code that you selected to debug.
1774정성태10/9/201430148개발 환경 구성: 246. IIS 작업자 프로세스의 20분 자동 재생(Recycle)을 끄는 방법
1773정성태10/8/201433715.NET Framework: 471. 웹 브라우저로 다운로드가 되는 파일을 왜 C# 코드로 하면 안되는 걸까요? [1]
1772정성태10/3/201422626.NET Framework: 470. C# 3.0의 기본 인자(default parameter)가 .NET 1.1/2.0에서도 실행될까? [3]
1771정성태10/2/201431046개발 환경 구성: 245. 실행된 프로세스(EXE)의 명령행 인자를 확인하고 싶다면 - Sysmon [4]
1770정성태10/2/201425610개발 환경 구성: 244. 매크로 정의를 이용해 파일 하나로 C++과 C#에서 공유하는 방법 [1]파일 다운로드1
1769정성태10/1/201428514개발 환경 구성: 243. Scala 개발 환경 구성(JVM, 닷넷) [1]
1768정성태10/1/201422994개발 환경 구성: 242. 배치 파일에서 Thread.Sleep 효과를 주는 방법 [5]
1767정성태10/1/201428107VS.NET IDE: 94. Visual Studio 2012/2013에서의 매크로 구현 - Visual Commander [2]
1766정성태10/1/201426688개발 환경 구성: 241. 책 "프로그래밍 클로저: Lisp"을 읽고 나서. [1]
1765정성태9/30/201430280.NET Framework: 469. Unity3d에서 transform을 변수에 할당해 사용하는 특별한 이유가 있을까요?
1764정성태9/30/201426887오류 유형: 243. 파일 삭제가 안 되는 경우 - The action can't be comleted because the file is open in System
1763정성태9/30/201428181.NET Framework: 468. PDB 파일을 연동해 소스 코드 라인 정보를 알아내는 방법파일 다운로드1
1762정성태9/30/201429817.NET Framework: 467. 닷넷에서 EIP/RIP 레지스터 값을 구하는 방법 [1]파일 다운로드1
1761정성태9/29/201426648.NET Framework: 466. 윈도우 운영체제의 보안 그룹 이름 및 설명 문자열을 바꾸는 방법파일 다운로드1
1760정성태9/28/201424536.NET Framework: 465. ICorProfilerInfo::GetILToNativeMapping 메서드가 0x80131358을 반환하는 경우
1759정성태9/27/201435401개발 환경 구성: 240. Visual C++ / x64 환경에서 inline-assembly를 매크로 어셈블리로 대체하는 방법파일 다운로드1
1758정성태9/23/201442303개발 환경 구성: 239. 원격 데스크톱 접속(RDP)을 기존의 콘솔 모드처럼 사용하는 방법 [1]
1757정성태9/23/201422984오류 유형: 242. Lync로 모임 참여 시 소리만 들리지 않는 경우 - 두 번째 이야기
1756정성태9/23/201431809기타: 48. NVidia 제품의 과다한 디스크 사용 [2]
1755정성태9/22/201438536오류 유형: 241. Unity Web Player를 설치해도 여전히 설치하라는 화면이 나오는 경우 [4]
1754정성태9/22/201429220VC++: 80. 내 컴퓨터에서 C++ AMP 코드가 실행이 될까요? [1]
1753정성태9/22/201424465오류 유형: 240. Lync로 세미나 참여 시 소리만 들리지 않는 경우 [1]
... 121  122  123  124  125  126  127  128  129  130  131  132  133  134  [135]  ...