본문 바로가기
장인으로의 여정/유용한 TIP

🚀 XDP 기반 세션 관리 방법

by 유기농프로그래밍 2025. 2. 23.
반응형

XDP를 활용하여 패킷을 수집한 후, 세션을 추적(Session Tracking)하는 방법입니다.


🔍 1. XDP에서 세션(Session)이란?

세션(Session)이란?

  • 클라이언트(Client)와 서버(Server) 간의 연결을 유지하면서 상태를 추적하는 것
  • TCP/UDP 패킷에서 5-Tuple (출발지 IP, 목적지 IP, 출발지 포트, 목적지 포트, 프로토콜)을 기반으로 식별 가능

XDP에서 세션 관리가 어려운 이유?

  • XDP는 무상태(Stateless) 방식으로 동작하며, 상태를 유지하는 기능이 없음
  • 패킷을 처리하고 바로 드롭(XDP_DROP)하거나 전달(XDP_PASS)하기 때문에, 이전 패킷 정보를 기억할 수 없음
  • 세션을 추적하려면 커널 내부에서 패킷 데이터를 저장하는 방식이 필요

해결 방법: eBPF 맵을 활용하여 세션 상태 유지

  • eBPF Hash Map을 사용하여 세션 정보를 저장
  • XDP가 패킷을 받을 때, 기존 세션이 존재하는지 확인하고 새로운 세션이면 추가
  • 일정 시간이 지나면 유휴 세션(Idle Session)을 삭제

🔎 2. XDP 세션 관리 아키텍처

┌──────────────────────────────────┐
│       🌍 네트워크 패킷 흐름      │
├──────────────────────────────────┤
│ 1️⃣ XDP에서 패킷 수집 (Zero-Copy)  │
│ 2️⃣ eBPF Hash Map을 사용해 세션 저장 │
│ 3️⃣ 기존 세션 여부 확인 및 업데이트 │
│ 4️⃣ 일정 시간 후 만료된 세션 삭제  │
│ 5️⃣ 세션 유지된 패킷만 XDP_PASS    │
└──────────────────────────────────┘

🔥 성능 최적화

  • eBPF Hash Map을 사용하여 세션을 추적 (O(1) 조회 속도)
  • 만료된 세션을 제거하여 메모리 절약
  • Zero-Copy 방식으로 커널 네트워크 스택을 거치지 않음

🔎 3. XDP 기반 세션 관리 코드

(1) eBPF XDP 코드: TCP/UDP 세션 저장 및 관리

📌 xdp_session.c (XDP + eBPF 기반 세션 관리 코드)

#include <linux/bpf.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/udp.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_endian.h>

// 🔥 eBPF Hash Map을 사용하여 세션 관리
struct bpf_map_def SEC("maps") session_map = {
    .type        = BPF_MAP_TYPE_HASH,
    .key_size    = sizeof(__u64),  // 5-Tuple 해시 키 (IP + 포트 + 프로토콜)
    .value_size  = sizeof(__u64),  // 타임스탬프 (세션 만료 추적)
    .max_entries = 10240,          // 최대 10,240개의 세션 저장 가능
};

// 🔥 5-Tuple 세션 키 생성 함수
static __u64 create_session_key(__u32 saddr, __u32 daddr, __u16 sport, __u16 dport, __u8 proto) {
    return ((__u64)saddr << 32) | ((__u64)daddr << 16) | ((__u64)sport << 8) | ((__u64)dport) | ((__u64)proto);
}

// 🔥 XDP 프로그램 (TCP/UDP 패킷 필터링 및 세션 추적)
SEC("xdp")
int xdp_session_tracking(struct __sk_buff *skb) {
    struct ethhdr *eth = bpf_hdr_pointer(skb, 0, sizeof(*eth));
    if (!eth) return XDP_PASS;

    struct iphdr *ip = bpf_hdr_pointer(skb, sizeof(*eth), sizeof(*ip));
    if (!ip) return XDP_PASS;

    if (ip->protocol != IPPROTO_TCP && ip->protocol != IPPROTO_UDP) {
        return XDP_PASS;  // TCP/UDP가 아니면 패킷을 커널로 전달
    }

    __u16 sport = 0, dport = 0;
    if (ip->protocol == IPPROTO_TCP) {
        struct tcphdr *tcp = bpf_hdr_pointer(skb, sizeof(*eth) + sizeof(*ip), sizeof(*tcp));
        if (!tcp) return XDP_PASS;
        sport = bpf_ntohs(tcp->source);
        dport = bpf_ntohs(tcp->dest);
    } else if (ip->protocol == IPPROTO_UDP) {
        struct udphdr *udp = bpf_hdr_pointer(skb, sizeof(*eth) + sizeof(*ip), sizeof(*udp));
        if (!udp) return XDP_PASS;
        sport = bpf_ntohs(udp->source);
        dport = bpf_ntohs(udp->dest);
    }

    // 5-Tuple을 사용하여 세션 키 생성
    __u64 session_key = create_session_key(ip->saddr, ip->daddr, sport, dport, ip->protocol);
    __u64 ts = bpf_ktime_get_ns();  // 현재 타임스탬프 (나노초)

    // 🔥 세션이 존재하는지 확인
    __u64 *session_ts = bpf_map_lookup_elem(&session_map, &session_key);
    if (session_ts) {
        // 세션이 이미 존재하는 경우 → 타임스탬프 업데이트
        *session_ts = ts;
    } else {
        // 새로운 세션 등록
        bpf_map_update_elem(&session_map, &session_key, &ts, BPF_ANY);
    }

    return XDP_PASS;  // 세션이 존재하는 패킷만 허용
}

char LICENSE[] SEC("license") = "GPL";

 

설명

  • session_map: eBPF Hash Map을 사용하여 세션 정보를 저장
  • create_session_key(): 5-Tuple (출발지 IP, 목적지 IP, 출발지 포트, 목적지 포트, 프로토콜) 기반 해시 생성
  • bpf_map_lookup_elem(): 기존 세션이 있는지 확인
  • bpf_map_update_elem(): 새로운 세션을 추가하거나 기존 세션 업데이트
  • XDP_PASS: 세션이 유지된 패킷만 허용

(2) eBPF XDP 프로그램 실행 (Python)

📌 load_xdp.py (XDP 프로그램 실행)

from bcc import BPF

bpf_code = """
#include <linux/bpf.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <linux/tcp.h>

SEC("xdp")
int xdp_filter(struct __sk_buff *skb) {
    struct ethhdr *eth = bpf_hdr_pointer(skb, 0, sizeof(*eth));
    if (!eth) return XDP_PASS;

    struct iphdr *ip = bpf_hdr_pointer(skb, sizeof(*eth), sizeof(*ip));
    if (!ip) return XDP_PASS;

    __u32 client_ip = bpf_htonl(0xC0A80101);
    __u32 server_ip = bpf_htonl(0xC0A80102);

    if (ip->saddr == client_ip || ip->daddr == server_ip) {
        return XDP_PASS;
    }
    return XDP_DROP;
}
"""

bpf = BPF(text=bpf_code)
fn = bpf.load_func("xdp_filter", BPF.XDP)
bpf.attach_xdp("eth0", fn, 0)
print("XDP 세션 관리 프로그램 로드 완료.")
try:
    bpf.trace_print()
except KeyboardInterrupt:
    pass
bpf.remove_xdp("eth0", 0)

 

설명

  • eBPF XDP 프로그램을 eth0 인터페이스에 적용
  • 세션 정보가 존재하면 패킷을 허용(XDP_PASS)
  • 세션이 존재하지 않으면 차단 가능

🚀 결론: XDP 기반 세션 관리

 

기능 역할 효과
eBPF Hash Map 세션 정보 저장 초고속 O(1) 조회
5-Tuple 해시 세션 추적 네트워크 연결 유지
XDP_PASS 기존 세션 유지 합법적인 트래픽 처리

 

🚀 결론:
XDP 기반 eBPF Hash Map을 활용하면 초고속 세션 관리를 구현 가능!

➡ 네트워크 환경(AWS, GCP, Kubernetes 등)을 알려주시면 추가 최적화 가능! 🚀

반응형

댓글