NGFW 하드웨어 오프로드

차세대 방화벽(NGFW) 하드웨어 오프로드 아키텍처, Fast/Slow/Exception 경로, Stateful 방화벽·NAT·ACL·QoS·터널·IPSec 오프로드, 상용 NGFW 비교, Linux 커널 기반 NGFW 파이프라인, 세션 오프로드 결정 트리 종합 가이드

초보자 가이드

이 문서를 읽기 전에:

공항 보안에 비유한 NGFW 오프로드

NGFW 하드웨어 오프로드는 공항 보안 검색에 비유할 수 있습니다:

NGFW의 핵심 과제는 보안 수준을 유지하면서 첫 패킷 검사가 끝난 세션을 얼마나 빠르게 처리할 수 있는가입니다. 하드웨어 오프로드는 이 "PreCheck 경로"를 NIC/SmartNIC 칩에서 직접 처리하여 CPU 부하를 극적으로 줄입니다.

핵심 요약:
  • NGFW는 전통 방화벽(L3/L4 ACL)에 DPI, IPS, App-ID, SSL 검사를 추가한 차세대 방화벽
  • Fast Path: 검증 완료된 세션을 HW(eSwitch) 또는 SW(flowtable)에서 고속 전달
  • Slow Path: 새 세션의 첫 패킷을 전체 Netfilter + DPI 파이프라인으로 검사
  • Exception Path: 오프로드된 세션에서 이상 탐지 시 Slow Path로 복귀
  • Linux 커널에서는 nf_flowtable + TC flower + eSwitch + NFQUEUE 조합으로 구현

단계별 이해

단계학습 내용관련 섹션난이도
1NGFW가 전통 FW와 어떻게 다른지 이해개요입문
2Fast/Slow/Exception 3계층 경로 구분데이터 플레인 아키텍처입문
3세션이 오프로드되는 조건과 결정 트리세션 오프로드 생명주기중급
4Stateful FW/NAT/ACL 오프로드 메커니즘Stateful 방화벽 오프로드중급
5QoS/터널/IPSec 오프로드 파이프라인QoS/터널/IPSec 오프로드중급
6상용 NGFW 칩 아키텍처 비교상용 NGFW HW 아키텍처참고
7커널 서브시스템 매핑커널 빌딩 블록중급
8실전 nftables + TC flower 규칙구현 패턴고급
9보안 vs 성능 트레이드오프 분석보안 vs 오프로드 트레이드오프고급
10성능 튜닝과 운영성능 튜닝, 운영 가이드고급
11QUIC/TLS 1.3/ECH의 NGFW 도전과제QUIC/ECH와 NGFW 오프로드고급
12멀티 테넌트 격리와 zone 분리멀티 테넌트 NGFW고급
13eBPF 기반 차세대 NGFW 파이프라인eBPF 기반 차세대 NGFW고급
14흔한 실수와 안티패턴 회피흔한 실수중급
15실습 (flowtable, DPI, XDP)실습 가이드중급

선수 지식 수준 가이드

지식 영역필수/권장수준학습 자료
iptables/nftables 기초필수체인, 테이블, 규칙 작성Netfilter 프레임워크
conntrack 이해필수NEW/EST/REL 상태, conntrack -L 사용conntrack 헬퍼
TC (Traffic Control)권장qdisc, filter, flower classifier 기초TC 심화
NIC 드라이버 기초권장ethtool, ring buffer, offload 개념Network Device 드라이버
Linux 네트워킹 전반권장sk_buff, Netfilter 훅, 포워딩 경로sk_buff 구조

핵심 용어 정리

용어설명
Fast Path이미 검사가 완료된 세션의 후속 패킷을 최소 비용(HW 또는 SW)으로 전달하는 경로
Slow Path새 세션의 패킷이 전체 보안 파이프라인(ACL → conntrack → DPI → IPS)을 통과하는 경로
Exception Path오프로드된 세션이 비정상 상황(FIN/RST, ALG, 정책변경)으로 CPU로 복귀하는 경로
conntrack커널의 연결 추적 시스템. TCP/UDP 세션 상태(NEW, ESTABLISHED, RELATED)를 관리
flowtablenf_flowtable. ESTABLISHED 세션을 Netfilter 훅을 건너뛰고 직접 전달하는 SW 오프로드
eSwitch FDBNIC 내장 하드웨어 스위치의 Forwarding Database. TC flower 규칙을 하드웨어에서 실행
TC flowerTraffic Control의 flower 분류기. L2~L4 필드 매칭으로 패킷을 분류하고 HW offload 가능
ct_stateconntrack 상태 플래그. +est(established), +new(new), +trk(tracked), +inv(invalid) 등
NFQUEUENetfilter Queue. 커널 패킷을 유저스페이스 프로그램(DPI 엔진)에 전달하는 메커니즘
CPSConnections Per Second. 초당 새 연결 수립 수. NGFW Slow Path의 핵심 성능 지표
App-IDApplication Identification. 포트번호가 아닌 실제 L7 프로토콜/애플리케이션을 식별하는 기술
DPIDeep Packet Inspection. 패킷 페이로드를 분석하여 프로토콜, 시그니처, 악성 패턴을 탐지
ALGApplication Level Gateway. FTP/SIP 등 동적 포트를 사용하는 프로토콜의 NAT traversal 지원
SmartNIC/DPU프로그래머블 NIC. eSwitch, TC flower offload, IPSec crypto offload 등을 하드웨어에서 실행

NGFW HW 오프로드 개요

NGFW의 정의

차세대 방화벽(Next-Generation Firewall, NGFW)은 전통적인 L3/L4 패킷 필터링 방화벽에 다음 기능을 통합한 보안 장비입니다:

이러한 기능은 패킷당 처리 비용을 크게 증가시키므로, 하드웨어 오프로드를 통해 검증이 완료된 세션의 후속 패킷을 CPU를 거치지 않고 직접 전달하는 것이 성능의 핵심입니다.

NGFW 세대별 진화

세대시기핵심 기술오프로드 방식처리량
1세대 (패킷 필터)1990sL3/L4 ACL (stateless)ASIC 전체 오프로드1~10 Gbps
2세대 (Stateful FW)2000sconntrack + 상태 기반 ACL세션 테이블 ASIC10~40 Gbps
3세대 (UTM)2005~FW + IPS + AV + VPN 통합보안 프로세서(SP) 보조1~10 Gbps
4세대 (NGFW)2010~App-ID + DPI + SSL 검사검증 세션 선택적 오프로드10~100 Gbps
5세대 (SmartNIC NGFW)2020~eBPF/XDP + eSwitch + flowtable프로그래머블 HW offload100~400 Gbps

5세대의 핵심 차이점은 프로그래머블 오프로드입니다. 기존 ASIC은 벤더가 설계한 고정 파이프라인만 지원했지만, SmartNIC/DPU는 TC flower, eBPF, P4 등으로 오프로드 로직을 사용자가 정의할 수 있습니다.

NGFW 배치 모델

NGFW는 네트워크 내 위치와 동작 방식에 따라 여러 배치 모델이 있으며, 각 모델에서 HW offload의 적용 범위가 달라집니다:

배치 모델동작 방식HW 오프로드 적용장점단점
Inline (L3 Routed) 라우터처럼 L3 포워딩 경로에 위치. 패킷이 NGFW를 반드시 통과 flowtable + eSwitch FDB 전체 활용 가능 차단/허용 즉시 적용, NAT 연동 단일 장애점, 지연 추가
Inline (L2 Transparent) 브리지 모드로 L2에서 투명하게 검사. IP 주소 불필요 eSwitch bridge offload + TC flower 활용 네트워크 구조 변경 불필요 NAT 불가, 라우팅 정책 제한
TAP/SPAN (Passive) 트래픽 사본을 수신하여 탐지만 수행 (IDS 모드) HW offload 불필요 (패킷 전달 없음) 무중단, 성능 영향 없음 차단 불가, 실시간 대응 지연
Proxy (Explicit/Transparent) TCP 연결을 중간에서 종료/재수립 (SSL Proxy) 프록시 세션 간 TCP splice offload 가능 완전한 L7 검사, SSL 복호화 높은 CPU 부하, 세션 오프로드 제한
DPU 기반 (Bump-in-the-Wire) SmartNIC/DPU가 호스트 앞에서 독립적으로 NGFW 실행 DPU 내 eSwitch + flowtable 전용 활용 호스트 CPU 부하 제로, 독립 보안 DPU 비용, 관리 복잡성
배치 모델과 오프로드 효율: Inline L3 Routed 모델이 HW offload를 가장 효과적으로 활용합니다. flowtable의 L3 포워딩 + NAT rewrite가 모두 하드웨어에서 실행되기 때문입니다. L2 Transparent 모드는 bridge offload에 의존하므로 NIC 지원 범위가 더 제한적입니다. 이 문서의 구현 패턴은 주로 Inline L3 모델을 기준으로 설명합니다.

전통 FW vs NGFW 비교

특성전통 방화벽NGFW
검사 계층L3/L4 (IP, Port)L3~L7 (App-ID, DPI)
정책 기준5-tuple (src/dst IP, port, proto)5-tuple + App + User + Content
상태 추적conntrack (선택적)conntrack 필수 + DPI 세션 상태
IPS/IDS별도 장비통합 (inline IPS)
SSL 검사미지원SSL/TLS Proxy 또는 인라인 복호화
처리량 병목PPS (초당 패킷)CPS (초당 새 연결) + DPI 처리량
HW 오프로드 대상전체 ACL검증 완료 세션만 (selective offload)
오프로드 비율90%+ (대부분 정적 규칙)40~80% (DPI 결과에 따라 가변)

오프로드 필요성

NGFW에서 모든 패킷을 CPU에서 처리하면 다음과 같은 병목이 발생합니다:

100Gbps 이상 인터페이스에서 NGFW를 운영하려면, ESTABLISHED 세션의 후속 패킷을 하드웨어에서 처리하고 CPU는 NEW 세션과 예외 상황에만 집중해야 합니다.

트래픽 프로파일과 오프로드 효과

실제 네트워크 트래픽에서 세션의 특성을 분석하면 오프로드의 효과를 정량적으로 이해할 수 있습니다:

트래픽 특성일반적 비율패킷 비율바이트 비율오프로드 영향
장수명 대용량 세션 (영상, 다운로드)세션 5%60%80%오프로드 시 가장 큰 효과
중간 세션 (웹 브라우징)세션 20%25%15%DPI 분류 후 오프로드
단수명 세션 (DNS, API)세션 75%15%5%CPS에 영향, 오프로드 효과 적음

핵심 인사이트: 세션 수로는 단수명 세션이 압도적이지만, 실제 트래픽 볼륨(바이트)의 80%는 소수의 장수명 세션이 차지합니다. 이 장수명 세션을 HW offload하면 전체 CPU 부하의 대부분을 해소할 수 있습니다.

Elephant Flow vs Mouse Flow:
  • Elephant Flow: 100KB+ 세션. 비디오 스트리밍, 파일 다운로드, 백업. 오프로드 1순위 대상
  • Mouse Flow: 10KB 미만 세션. DNS 쿼리, API 호출, 헬스체크. 오프로드 효과 미미하지만 CPS에 영향
  • NGFW 오프로드의 목표: Elephant Flow를 모두 Fast Path로 → CPU는 Mouse Flow의 DPI에만 집중

NGFW 핵심 성능 지표

NGFW 성능을 평가할 때 반드시 구분해야 하는 핵심 메트릭입니다. 벤더 데이터시트는 대부분 "방화벽 처리량"만 강조하지만, 실제 NGFW 성능은 모든 보안 기능을 활성화한 상태에서 측정해야 합니다:

메트릭의미측정 단위병목 원인HW 오프로드 효과
FW 처리량 L3/L4 ACL + NAT + conntrack만 적용한 최대 처리량 Gbps 패킷 처리 오버헤드, PPS flowtable/eSwitch로 100Gbps+ 가능
NGFW 처리량 App-ID + IPS + DPI를 모두 활성화한 처리량 Gbps DPI 엔진 CPU, 시그니처 DB 크기 EST 세션 오프로드로 DPI 부하 분산
Threat Prevention 처리량 IPS + AV + 안티봇 + URL 필터 전체 적용 처리량 Gbps 시그니처 매칭, 파일 검사 제한적 (첫 패킷/새 세션은 항상 검사)
SSL Inspection 처리량 SSL/TLS 복호화 후 전체 검사 처리량 Gbps PKI 연산 (RSA/ECDHE) Crypto offload (QAT, NIC inline crypto)
CPS 초당 새 연결 수립 수 connections/s conntrack NEW + DPI 첫 분류 간접적 (CPU 여유분이 CPS에 할당)
CC 동시 연결 수 (Concurrent Connections) sessions conntrack 테이블 + DPI 세션 메모리 HW flow 테이블 활용으로 메모리 절감
지연 (Latency) 패킷이 NGFW를 통과하는 데 걸리는 시간 μs 파이프라인 단계 수, 큐잉 HW Fast Path: <5μs, SW: 50~200μs
벤더 데이터시트 주의점: 상용 NGFW 벤더의 "최대 처리량"은 대부분 L4 FW만 활성화하고 64-byte UDP 패킷으로 측정한 값입니다. 실제 환경에서 IPS + SSL Inspection을 활성화하면 처리량이 1/5 ~ 1/10 이하로 떨어집니다. RFC 9411은 보안 기능별 처리량을 분리 측정하도록 규정합니다.
Fast Path (HW/SW Offload) eSwitch FDB Rule (HW) / nf_flowtable (SW) ESTABLISHED 세션 → conntrack bypass → L2/L3 forwarding → NAT rewrite → TX 100Gbps+ 처리 CPU 부하 없음 Slow Path (Full Inspection) Netfilter hooks → conntrack NEW → ACL → NFQUEUE → DPI/IPS → App-ID → Verdict 첫 패킷 + 아직 분류되지 않은 세션 → 전체 보안 파이프라인 통과 1~10Gbps 처리 CPU 집약적 Exception Path ALG 프로토콜, IP fragment, TCP RST/FIN, ICMP error, 정책 변경 재분류 오프로드 불가 또는 오프로드 해제가 필요한 패킷 → Slow Path로 복귀 개별 처리 GC / 상태 전이 EST + ALLOW → offload RST/FIN/ALG → teardown 일반적 트래픽 분포: Fast Path 60~80% | Slow Path 15~30% | Exception 5~10%

데이터 플레인 아키텍처

Fast Path (HW + SW)

Fast Path는 이미 보안 검사가 완료된 세션의 후속 패킷을 최소 비용으로 전달하는 경로입니다. 두 가지 수준이 있습니다:

HW Fast Path (eSwitch FDB)

eSwitch의 switchdev 모드에서 TC flower 규칙으로 ct_state +est +trk 매칭 후 FDB(Forwarding Database) 룰을 NIC 하드웨어에 설치합니다. 패킷이 NIC에 도착하면 CPU를 거치지 않고 직접 전달됩니다.

HW Fast Path의 동작을 단계별로 보면:

  1. 패킷 도착: 이더넷 프레임이 NIC의 RX 큐에 도달
  2. eSwitch FDB lookup: NIC 내장 하드웨어가 5-tuple + ct_state로 FDB 테이블을 검색
  3. 매칭 시 HW 처리: NAT rewrite (IP/Port/Checksum), TTL 감소, 터널 encap/decap, VLAN push/pop
  4. 직접 TX: 처리된 패킷이 대상 포트의 TX 큐로 직접 전달 (DMA to DMA, CPU interrupt 없음)

SW Fast Path (nf_flowtable)

nf_flowtable은 커널 소프트웨어에서 conntrack을 bypass하고 직접 L3 forwarding을 수행합니다. HW offload가 불가능한 경우(예: NIC 미지원, 복잡한 NAT, 특정 터널)의 폴백 경로입니다.

SW Fast Path는 커널의 Netfilter ingress 훅(priority -10)에서 동작하여, 일반 Netfilter 체인(PREROUTING → FORWARD → POSTROUTING)을 완전히 건너뜁니다. 이를 통해 nftables 규칙 평가, conntrack 상세 추적, NFQUEUE 전달 등의 오버헤드를 제거합니다.

Fast Path 전환의 커널 내부 흐름

패킷이 Fast Path로 처리되는 과정을 커널 코드 수준에서 살펴보면:

/* net/netfilter/nf_flow_table_ip.c */
/* flowtable ingress 훅에서 호출되는 핵심 함수 */
static unsigned int
nf_flow_offload_ip_hook(void *priv, struct sk_buff *skb,
                        const struct nf_hook_state *state)
{
    struct flow_offload_tuple_rhash *tuplehash;
    struct nf_flowtable *flow_table = priv;
    struct flow_offload_tuple tuple = {};
    enum flow_offload_tuple_dir dir;
    struct flow_offload *flow;

    /* 1. skb에서 5-tuple 추출 */
    if (nf_flow_tuple_ip(skb, state->in, &tuple) < 0)
        return NF_ACCEPT;  /* 추출 실패 → Slow Path */

    /* 2. flowtable에서 lookup */
    tuplehash = flow_offload_lookup(flow_table, &tuple);
    if (!tuplehash)
        return NF_ACCEPT;  /* 미등록 플로우 → Slow Path */

    flow = container_of(tuplehash, struct flow_offload,
                        tuplehash[dir]);

    /* 3. TEARDOWN 상태면 Slow Path로 복귀 */
    if (flow_offload_stale_flow(flow))
        return NF_ACCEPT;

    /* 4. NAT rewrite 적용 */
    if (flow->flags & FLOW_OFFLOAD_SNAT)
        nf_flow_snat_ip(skb, thoff, ...);
    if (flow->flags & FLOW_OFFLOAD_DNAT)
        nf_flow_dnat_ip(skb, thoff, ...);

    /* 5. TTL 감소 + routing 적용 */
    ip_decrease_ttl(iph);
    skb_dst_set_noref(skb, &rt->dst);

    /* 6. 직접 전달 (Netfilter 훅 체인 건너뜀) */
    ip_output(state->net, state->sk, skb);
    return NF_STOLEN;  /* skb 소유권 가져감 → 훅 종료 */
}
코드 설명
  • 8-10행 flow_offload_tuple은 5-tuple(src IP, dst IP, src port, dst port, protocol)을 담는 구조체입니다. flowtable의 해시 키로 사용됩니다.
  • 13-14행 skb에서 IP/TCP/UDP 헤더를 파싱하여 5-tuple을 추출합니다. 실패하면 일반 Netfilter 경로로 진행합니다.
  • 17-18행 flow_offload_lookup()은 rhashtable 기반 해시 테이블에서 O(1) 조회합니다. 미스 시 NF_ACCEPT로 Slow Path 진입합니다.
  • 24-25행 TEARDOWN 플래그가 설정된 플로우(TCP FIN/RST 수신)는 GC 대기 중이므로 Slow Path로 보내 정상 종료를 처리합니다.
  • 28-31행 NAT 오프로드: SNAT/DNAT 플래그에 따라 IP/포트를 직접 rewrite합니다. conntrack을 거치지 않으므로 매우 빠릅니다.
  • 34행 라우터 기능: TTL을 1 감소시킵니다. TTL=0이면 ICMP Time Exceeded를 생성하고 패킷을 DROP합니다.
  • 37-38행 NF_STOLEN을 반환하면 Netfilter가 이 패킷의 소유권을 flowtable에 넘깁니다. 이후 FORWARD/POSTROUTING 훅이 호출되지 않습니다.

flow_offload 구조체

/* include/net/netfilter/nf_flow_table.h */
struct flow_offload_tuple {
    union {
        struct in_addr   src_v4;   /* IPv4 소스 주소 */
        struct in6_addr  src_v6;   /* IPv6 소스 주소 */
    };
    union {
        struct in_addr   dst_v4;
        struct in6_addr  dst_v6;
    };
    struct {
        __be16  src;  /* 소스 포트 */
        __be16  dst;  /* 목적지 포트 */
    } port;
    u8      l3proto;    /* AF_INET / AF_INET6 */
    u8      l4proto;    /* IPPROTO_TCP / IPPROTO_UDP */
    struct dst_entry  *dst_cache; /* 라우팅 캐시 */
    int     iifidx;     /* ingress 인터페이스 인덱스 */
    int     oifidx;     /* egress 인터페이스 인덱스 */
};

struct flow_offload {
    struct flow_offload_tuple_rhash tuplehash[2]; /* [ORIGINAL] + [REPLY] */
    struct nf_conn  *ct;         /* 대응하는 conntrack 엔트리 */
    unsigned long    flags;      /* SNAT, DNAT, TEARDOWN 등 */
    u32              timeout;    /* GC 타임아웃 (jiffies) */
};

하나의 flow_offload는 양방향 플로우를 나타냅니다. tuplehash[0]은 ORIGINAL 방향, tuplehash[1]은 REPLY 방향의 5-tuple을 저장합니다. NAT가 적용된 경우 두 방향의 IP/포트가 다릅니다.

Slow Path (전체 검사)

새로운 연결의 첫 패킷(또는 아직 DPI 분류가 완료되지 않은 초기 패킷)은 전체 보안 파이프라인을 통과합니다. Slow Path는 NGFW의 보안 정밀도를 담당하며, 여기서의 결정이 세션의 나머지 수명 동안의 처리 방식을 결정합니다.

Slow Path에서의 패킷 처리 시간은 일반적으로 100us~5ms 범위입니다. DPI 엔진의 시그니처 수, 패킷 페이로드 크기, CPU 캐시 히트율에 따라 달라집니다.

처리 순서:

  1. Ingress ACL — nftables/TC filter 기반 정적 규칙 (IP/Port/Protocol 매칭)
  2. conntrack — 연결 추적 테이블에 NEW 엔트리 생성 또는 기존 상태 갱신
  3. NAT — SNAT/DNAT/MASQUERADE 주소 변환
  4. NFQUEUE — DPI 엔진으로 패킷 전달 (NFQUEUE & DPI 엔진 통합)
  5. DPI / App-ID — 프로토콜 분류, 시그니처 매칭, 애플리케이션 식별
  6. IPS — 시그니처 기반 공격 탐지
  7. Verdict — ACCEPT/DROP/QUEUE 최종 판정

CPS 병목: Slow Path의 처리량은 주로 초당 새 연결(CPS)으로 측정됩니다. DPI 엔진에 따라 코어당 10K~100K CPS 범위이며, 이는 NGFW의 실질적 성능 한계를 결정합니다.

Slow Path 커널 호출 체인

/* Slow Path에서 새 패킷이 거치는 커널 함수 호출 순서 */

/* 1. PREROUTING: conntrack 입구 */
nf_conntrack_in()
  → resolve_normal_ct()
    → nf_conntrack_find_get()    /* 해시 테이블에서 기존 ct 검색 */
    → init_conntrack()            /* 미발견 시 NEW ct 생성 */
      → __nf_conntrack_alloc()   /* slab에서 nf_conn 할당 */
      → nf_ct_helper_find()      /* ALG 헬퍼 자동 할당 (SIP/FTP 등) */

/* 2. FORWARD: nftables 체인 평가 */
nft_do_chain()
  → nft_lookup_eval()            /* set/map 기반 ACL 매칭 */
  → nft_ct_eval()                /* ct state 조건 평가 */
  → nft_queue_eval()             /* NFQUEUE verdict → DPI 전달 */

/* 3. NFQUEUE: 유저스페이스 DPI 전달 */
nf_queue()
  → nf_queue_entry_get_refs()    /* 참조 카운터 증가 */
  → nfnetlink_queue_enqueue()    /* netlink 소켓으로 전달 */
  /* ... Suricata/nDPI가 패킷을 분석 ... */
  → nf_reinject()                /* verdict 반환 후 재주입 */

/* 4. POSTROUTING: NAT 적용 */
nf_nat_ipv4_out()
  → nf_nat_packet()
    → nf_nat_manip_pkt()         /* SNAT/DNAT 주소 변환 */

/* 5. 전송 */
ip_output()
  → ip_finish_output()
    → neigh_output()             /* L2 헤더 추가 후 NIC TX */

CPS 병목 분석

NGFW에서 Slow Path의 CPS가 전체 성능을 제한하는 원인을 세분화하면:

단계CPU 비용 (cycles/pkt)병목 원인최적화 방법
conntrack alloc~500slab 할당 + 해시 삽입conntrack_buckets 증가, NUMA 로컬 할당
nftables 평가~200규칙 수에 비례set/map 기반 매칭 (O(1) lookup)
NFQUEUE 전달~1000netlink 소켓, 컨텍스트 스위치NFQUEUE batch mode, fanout 분산
DPI 분석~5000~50000패턴 매칭, SSL 파싱멀티코어 분산, 시그니처 최적화
verdict 반환~500재주입 + 훅 체인 재개BPF verdict 캐시
NAT~300conntrack NAT tuple 확인1:1 NAT 단순화

DPI가 전체 Slow Path 비용의 70~90%를 차지합니다. 따라서 DPI 완료 후 세션을 즉시 Fast Path로 전환하는 것이 핵심 최적화입니다.

Exception Path

이미 오프로드된 세션이라도 다음 경우에는 Fast Path에서 CPU로 복귀합니다:

Exception Path 처리의 커널 내부

Exception Path의 핵심은 flow_offload_stale_flow() 검사와 HW trap 메커니즘입니다:

/* net/netfilter/nf_flow_table_core.c */
static bool flow_offload_stale_flow(struct flow_offload *flow)
{
    /* TEARDOWN 플래그: TCP FIN/RST 수신 시 설정 */
    if (flow->flags & FLOW_OFFLOAD_TEARDOWN)
        return true;

    /* DYING 플래그: GC에 의해 만료 대기 중 */
    if (flow->flags & FLOW_OFFLOAD_DYING)
        return true;

    /* conntrack이 삭제된 경우 */
    if (!nf_ct_is_confirmed(flow->ct))
        return true;

    return false;
}

/* GC 워커: 주기적으로 만료된 플로우 정리 */
static void nf_flow_offload_gc_step(struct nf_flowtable *flow_table)
{
    struct flow_offload_tuple_rhash *tuplehash;
    struct rhashtable_iter hti;

    rhashtable_walk_enter(&flow_table->rhashtable, &hti);
    rhashtable_walk_start(&hti);

    while ((tuplehash = rhashtable_walk_next(&hti))) {
        struct flow_offload *flow = ...;
        if (nf_flow_has_expired(flow) ||
            nf_ct_is_dying(flow->ct)) {
            /* HW rule 삭제 → 카운터 동기화 → ct 삭제 */
            flow_offload_teardown(flow);
            nf_flow_offload_del(flow_table, flow);
        }
    }
}

ALG 프로토콜의 Exception 처리

FTP, SIP, H.323 등 ALG(Application Level Gateway) 프로토콜은 제어 채널에서 동적으로 데이터 채널 포트를 협상합니다. 이 과정에서 conntrack helper가 페이로드를 파싱해야 하므로 오프로드가 불가능합니다.

ALG 프로토콜Helper 모듈동적 포트오프로드 불가 이유
FTPnf_conntrack_ftpPORT/PASV 명령으로 데이터 포트 협상제어 채널 파싱 + RELATED 세션 생성
SIPnf_conntrack_sipSDP에서 RTP 포트 협상SDP 메시지 파싱 + NAT 주소 치환
H.323nf_conntrack_h323H.245에서 미디어 포트 협상ASN.1 디코딩 + NAT traversal
TFTPnf_conntrack_tftp서버가 임의 포트에서 응답RELATED expectation 추적
PPTPnf_conntrack_pptpGRE 터널 세션 생성GRE call ID 추적 + NAT
ALG와 NGFW 오프로드의 공존: ALG 제어 채널(예: FTP 21번 포트)은 항상 Slow Path에서 처리하되, ALG에 의해 생성된 데이터 채널(예: FTP PASV 포트)이 ESTABLISHED 상태가 되면 해당 데이터 채널은 flowtable으로 오프로드할 수 있습니다. 다만 이를 위해서는 conntrack helper가 expectation을 확인(confirm)한 이후여야 합니다.
기능HW Fast PathSW Fast PathSlow PathException Path
L3/L4 ACLHW 매칭bypassnftables chainnftables chain
conntrack 상태HW ct_statebypass (aging)전체 추적전체 추적
NAT rewriteHW rewriteSW rewritenf_natnf_nat
DPI / App-ID미수행미수행NFQUEUE → DPI재분류 가능
IPS 시그니처미수행미수행인라인 검사재검사
TCP 윈도우 검증미수행미수행conntrack 수행conntrack 수행
터널 encap/decapVXLAN/GRE/Geneve제한적전체 지원전체 지원
TTL/Hop LimitHW 감소SW 감소SW 감소SW 감소
통계/카운터HW counterSW counterper-chain counterper-chain counter
처리량 (100G NIC)100Gbps10~40Gbps1~10Gbps개별 처리
NIC RX XDP DDoS 사전 필터 eSwitch HW FDB match? HW Fast Path conntrack flowtable SW Fast Path nftables ACL + NAT NFQUEUE DPI / IPS App-ID, 시그니처 Verdict ACCEPT/DROP NIC TX match miss EST NEW ALLOW → offload 등록 Fast Path (HW/SW offload) Slow Path (전체 검사) 오프로드 등록 경로 유저스페이스 DPI 엔진 100Gbps 10~40Gbps 1~5Gbps (DPI) NGFW 전체 데이터 플레인 파이프라인

세션 오프로드 생명주기

첫 패킷 처리

TCP SYN 또는 첫 UDP 패킷이 도착하면 다음 과정을 거칩니다:

  1. conntrack NEWnf_conntrack_in()에서 새 conntrack 엔트리 생성
  2. 전체 Netfilter 훅 — PREROUTING → FORWARD → POSTROUTING 체인의 모든 규칙 평가
  3. NFQUEUE 전달 — DPI 엔진(Suricata, nDPI 등)이 패킷을 수신하여 프로토콜 분석 시작
  4. App-ID 분류 — 첫 패킷만으로 부족하면 DPI 엔진이 추가 패킷 요청 (보통 3~10패킷)
  5. Verdict 반환 — DPI 엔진이 ACCEPT 또는 DROP verdict를 반환

이 단계에서 오프로드는 발생하지 않습니다. 모든 검사가 완료될 때까지 Slow Path에 머뭅니다.

첫 패킷 conntrack 처리 상세

/* net/netfilter/nf_conntrack_core.c - 새 연결 생성 */
static struct nf_conntrack_tuple_hash *
init_conntrack(struct net *net, struct nf_conn *tmpl,
               const struct nf_conntrack_tuple *tuple,
               struct sk_buff *skb)
{
    struct nf_conn *ct;

    /* conntrack_max 초과 시 early drop (LRU) */
    if (nf_conntrack_max &&
        atomic_read(&net->ct.count) >= nf_conntrack_max) {
        if (!early_drop(net, hash))
            return ERR_PTR(-ENOMEM);  /* DROP: 테이블 가득 참 */
    }

    /* nf_conn 구조체 할당 (slab 캐시) */
    ct = __nf_conntrack_alloc(net, zone, tuple, ...);

    /* L4 프로토콜별 초기 상태 설정 */
    /* TCP: SYN_SENT, UDP: UNREPLIED */
    l4proto->new(ct, skb, dataoff);

    /* ALG helper 자동 할당 (포트 기반) */
    if (net->ct.sysctl_auto_assign_helper)
        nf_ct_helper_find(ct, tuple);

    /* unconfirmed list에 추가 (아직 해시에 삽입하지 않음) */
    /* POSTROUTING에서 confirm되면 해시 테이블에 삽입 */
    nf_ct_add_to_unconfirmed_list(ct);

    return &ct->tuplehash[IP_CT_DIR_ORIGINAL];
}
코드 설명
  • 10-13행 nf_conntrack_max 한계에 도달하면 early_drop()이 가장 오래된 UNREPLIED 연결을 삭제하여 공간을 확보합니다. 이 한계는 NGFW에서 매우 중요합니다.
  • 16행 nf_conn 구조체는 conntrack 엔트리 하나를 나타냅니다. slab 캐시에서 할당되어 메모리 단편화를 줄입니다.
  • 19-20행 TCP는 SYN_SENT 상태로 시작하여 3-way handshake 완료 시 ESTABLISHED로 전환됩니다. UDP는 첫 응답 수신 시 ASSURED 상태가 됩니다.
  • 27-28행 conntrack 엔트리는 처음에 unconfirmed list에 들어갑니다. 패킷이 성공적으로 POSTROUTING까지 도달하면 nf_conntrack_confirm()에서 해시 테이블에 삽입됩니다. 이 2단계 삽입은 DROP된 패킷의 conntrack 엔트리가 테이블을 오염시키는 것을 방지합니다.

DPI 엔진의 패킷 분류 과정

NFQUEUE를 통해 유저스페이스 DPI 엔진(Suricata/nDPI)에 전달된 패킷의 분류 과정입니다:

패킷 번호DPI 동작App-ID 상태Verdict
1 (SYN)TCP 핸드셰이크 감지UNKNOWNACCEPT (통과)
2 (SYN-ACK)서버 응답 확인UNKNOWNACCEPT
3 (ACK)연결 수립 확인UNKNOWNACCEPT
4 (Client Hello)TLS SNI 추출TLS (SNI 확인중)ACCEPT
5 (Server Hello)서버 인증서/프로토콜 분석HTTPS (example.com)ACCEPT
6~ (Application Data)분류 완료, IPS 시그니처 검사CLASSIFIEDACCEPT → 오프로드 가능

일반적으로 3~10개 패킷이면 App-ID 분류가 완료됩니다. TLS 1.3에서 Encrypted Client Hello(ECH)가 사용되면 SNI가 암호화되어 더 많은 패킷이 필요하거나, SSL 복호화 없이는 분류가 불가능합니다.

ESTABLISHED 전환

TCP 3-way handshake가 완료되면 conntrack 상태가 ESTABLISHED로 전환됩니다. 이 시점에서 오프로드 여부를 결정합니다.

오프로드 결정 기준 4가지:

  1. DPI 분류 완료 — App-ID가 결정되고 ALLOW verdict가 나왔는가?
  2. ALG 프로토콜 아닌가 — FTP/SIP/H.323 등 동적 포트 할당이 필요한 프로토콜은 오프로드 불가
  3. IP 단편화 아닌가 — 단편화된 패킷 스트림은 오프로드 불가
  4. HW 지원 여부 — NIC/SmartNIC이 해당 플로우의 오프로드를 지원하는가? (터널 타입, NAT 유형 등)

오프로드 등록 커널 코드

nftables에서 flow add @ft가 실행되면 내부적으로 다음이 호출됩니다:

/* net/netfilter/nft_flow_offload.c */
static void nft_flow_offload_eval(const struct nft_expr *expr,
                                   struct nft_regs *regs,
                                   const struct nft_pktinfo *pkt)
{
    struct nf_conn *ct;
    enum ip_conntrack_info ctinfo;

    ct = nf_ct_get(pkt->skb, &ctinfo);

    /* 오프로드 가능 조건 확인 */
    if (nf_ct_is_dying(ct))
        goto err;
    if (nf_ct_helper(ct))        /* ALG helper 있으면 불가 */
        goto err;

    /* flow_offload 구조체 생성 */
    struct flow_offload *flow = flow_offload_alloc(ct);

    /* 라우팅 캐시 설정 */
    flow_offload_route_init(flow, &route);

    /* flowtable 해시에 등록 */
    flow_offload_add(&ft->ft, flow);

    /* HW offload 플래그가 있으면 NIC에도 등록 */
    if (ft->ft.flags & NF_FLOWTABLE_HW_OFFLOAD)
        nf_flow_offload_hw_add(net, flow, ct);
        /* → TC flower ct 규칙 → NIC eSwitch FDB 삽입 */
}

nf_flow_offload_hw_add()는 내부적으로 TC flower API를 호출하여 eSwitch에 FDB 규칙을 삽입합니다. 이 과정은 워크큐에서 비동기적으로 수행되므로, HW 규칙 설치까지 수 밀리초의 지연이 발생할 수 있습니다. 이 사이에 도착하는 패킷은 SW flowtable에서 처리됩니다.

오프로드 결정 트리

세션 오프로드 결정 트리 패킷 도착 conntrack ESTABLISHED? Slow Path (전체 검사) No DPI verdict = ALLOW? Yes DROP 또는 계속 검사 No ALG/Fragment 프로토콜? Yes SW Slow Path 유지 Yes SmartNIC HW 지원? No HW Offload Yes SW Offload (flowtable) No
conntrack 상태DPI 결과ALGNIC HW 지원오프로드 결과
NEW미분류--Slow Path (전체 검사)
ESTABLISHEDALLOWNoYesHW Offload (eSwitch FDB)
ESTABLISHEDALLOWNoNoSW Offload (flowtable)
ESTABLISHEDALLOWYes (FTP/SIP)-Slow Path 유지 (ALG 필요)
ESTABLISHEDDROP--즉시 DROP
ESTABLISHED미완료--Slow Path (DPI 계속)
RELATED-Yes-Slow Path (헬퍼 추적)
INVALID---DROP (기본 정책)
NGFW 세션 오프로드 상태 머신 NEW Slow Path (전체 검사) DPI_ANALYZING NFQUEUE → App-ID 분류중 CLASSIFIED DPI ALLOW → 오프로드 대기 SW_OFFLOADED flowtable (커널 Fast Path) HW_OFFLOADED eSwitch FDB (NIC Fast Path) TEARDOWN GC 대기 → 리소스 해제 DROPPED RECHECK 주기적 재검사 (샘플링) SYN → conntrack NEW App-ID 완료 + ALLOW DPI DROP flow add @ft NIC HW 지원 → FDB 설치 TCP FIN/RST FIN/RST/timeout 바이트 임계치 초과 ALLOW 재검사 DROP → TEARDOWN Slow Path DPI 처리중 Fast Path (오프로드) 종료/차단 주기적 샘플링 경로

오프로드 해제

오프로드된 세션은 다음 상황에서 해제됩니다:

  1. TCP FIN/RST — 연결 종료 신호가 감지되면 flowtable에서 DYING 상태로 전환
  2. GC 타이머nf_flow_offload_gc_step()이 주기적으로 만료된 엔트리 정리
  3. HW rule 삭제nf_flow_offload_hw_del()이 eSwitch FDB 규칙을 제거
  4. conntrack 삭제 — 관련 conntrack 엔트리도 정리하여 리소스 해제
/* flowtable 오프로드 해제 흐름 */
TCP FIN/RST 수신
  → flow_offload_teardown()       /* FLOW_OFFLOAD_TEARDOWN 플래그 설정 */
    → nf_flow_offload_gc_step()   /* GC 워크큐가 DYING 플로우 정리 */
      → nf_flow_table_offload_del() /* HW rule 삭제 (TC flower del) */
        → nf_ct_delete()          /* conntrack 엔트리 삭제 */

GC 메커니즘 상세

flowtable의 Garbage Collection은 두 가지 메커니즘으로 동작합니다:

GC 유형트리거동작지연
Passive GC패킷 도착 시 flow_offload_stale_flow() 확인stale 플로우 감지 → Slow Path 복귀즉시 (패킷 기반)
Active GC워크큐가 주기적 실행 (nf_flow_offload_work)만료된 플로우 정리 + HW rule 삭제1초 주기
Forced GCconntrack destroy 이벤트conntrack 삭제 시 연관 플로우 즉시 정리즉시

HW 카운터 동기화: HW offload된 플로우가 삭제될 때, NIC에서 처리된 패킷/바이트 카운터를 SW 카운터에 동기화합니다. 이를 통해 conntrack -L이나 nft list counter에서 정확한 통계를 확인할 수 있습니다.

/* HW 카운터 동기화 (net/netfilter/nf_flow_table_offload.c) */
static void nf_flow_offload_stats(struct flow_offload *flow,
                                    struct flow_stats *stats)
{
    struct nf_conn_acct *acct = nf_conn_acct_find(flow->ct);

    /* HW에서 읽어온 패킷/바이트 카운터를 conntrack에 반영 */
    atomic64_add(stats[0].pkts, &acct->counter[IP_CT_DIR_ORIGINAL].packets);
    atomic64_add(stats[0].bytes, &acct->counter[IP_CT_DIR_ORIGINAL].bytes);
    atomic64_add(stats[1].pkts, &acct->counter[IP_CT_DIR_REPLY].packets);
    atomic64_add(stats[1].bytes, &acct->counter[IP_CT_DIR_REPLY].bytes);

    /* 타임스탬프 갱신 → GC 타임아웃 리셋 */
    flow->timeout = nf_flowtable_time_stamp + flow_offload_get_timeout(flow);
}
타임아웃과 GC의 상호작용: HW offload된 플로우는 NIC에서 패킷 카운터가 주기적으로 SW에 보고됩니다. 이 보고가 있는 한 플로우의 타임아웃이 갱신되어 GC 대상이 되지 않습니다. NIC 보고 주기(보통 1초)보다 짧은 타임아웃을 설정하면 활성 플로우가 불필요하게 정리될 수 있으므로, nf_flowtable_tcp_timeout은 최소 30초 이상으로 설정해야 합니다.

Stateful 방화벽 오프로드

ACL HW 오프로드

TC flower를 사용하여 ct_state 매칭과 함께 ACL 규칙을 eSwitch에 설치할 수 있습니다. 이를 통해 conntrack 상태 기반 방화벽 규칙이 하드웨어에서 직접 평가됩니다.

# eSwitch switchdev 모드에서 TC flower ct 규칙 예시
# 1. ct zone 설정 및 conntrack 추적 시작
tc filter add dev eth0_rep0 ingress prio 1 \
  protocol ip flower \
  ct_state -trk \
  action ct zone 1

# 2. ESTABLISHED+TRACKED 세션 → HW offload forward
tc filter add dev eth0_rep0 ingress prio 2 \
  protocol ip flower \
  ct_state +trk+est \
  action ct zone 1 \
  action mirred egress redirect dev eth1_rep0

# 3. NEW+TRACKED 세션 → CPU (Slow Path)
tc filter add dev eth0_rep0 ingress prio 3 \
  protocol ip flower \
  ct_state +trk+new \
  action pass # CPU로 전달하여 전체 검사

# 4. INVALID → DROP
tc filter add dev eth0_rep0 ingress prio 4 \
  protocol ip flower \
  ct_state +trk+inv \
  action drop
코드 설명
  • 3-5행 아직 conntrack에 의해 추적되지 않은(-trk) 패킷에 대해 ct zone 1에서 conntrack 추적을 시작합니다.
  • 8-12행 이미 추적되고 ESTABLISHED 상태인 패킷(+trk+est)을 하드웨어에서 직접 다른 representor 포트로 리다이렉트합니다. 이것이 HW Fast Path의 핵심입니다.
  • 15-18행 새로운 연결(+trk+new)은 pass로 CPU에 전달하여 nftables → NFQUEUE → DPI 전체 검사를 수행합니다.
  • 21-24행 INVALID 상태(+trk+inv) 패킷은 하드웨어에서 즉시 DROP합니다.

conntrack HW 오프로드

NF_FLOWTABLE_HW_OFFLOAD 플래그가 활성화되면, flowtable에 등록된 플로우가 NIC 하드웨어의 conntrack 테이블에도 설치됩니다.

# flowtable HW offload 활성화
nft add flowtable inet filter ft \
  { hook ingress priority 0\; devices = { eth0, eth1 }\; \
    flags offload\; }

# forward chain에서 established 세션을 flowtable으로 등록
nft add rule inet filter forward \
  ct state established \
  flow add @ft

# 오프로드 상태 실시간 확인
conntrack -L --status OFFLOAD
# 출력 예:
# tcp  6 300 ESTABLISHED src=10.0.0.1 dst=192.168.1.1
#   sport=45678 dport=443 [OFFLOAD] mark=0 use=2

HW offload 내부 메커니즘

flowtable HW offload는 다음 과정으로 NIC에 규칙을 설치합니다:

/* HW offload 규칙 설치 흐름 */
nf_flow_offload_hw_add()
  → nf_flow_offload_tuple()
    → flow_offload_mangle()      /* NAT rewrite 액션 구성 */
    → flow_action_entry_set()    /* TC flow_action 구조체 채움 */
  → nf_flow_table_offload_add()
    → flow_block_cb()            /* NIC 드라이버 콜백 호출 */
      → mlx5e_tc_offload_fdb_rules()  /* mlx5: FDB rule 삽입 */
      → ice_tc_flower_action()         /* ice: TC flower 규칙 */

NIC 드라이버는 FLOW_BLOCK_BIND/FLOW_BLOCK_UNBIND 이벤트를 통해 flowtable과 연결됩니다. 드라이버가 지원하지 않는 플로우 타입(예: 특수 NAT, 미지원 터널)에 대해서는 -EOPNOTSUPP를 반환하고, 이 경우 SW flowtable에서만 처리됩니다.

HW conntrack의 한계:

conntrack HW offload 호환성 매트릭스

기능NVIDIA CX-6 DxNVIDIA CX-7Intel E810Broadcom P2100
CT offload (TCP)지원지원지원지원
CT offload (UDP)지원지원지원지원
CT offload (IPv6)지원지원제한적미지원
CT zone 지원지원지원제한적미지원
CT NAT actionSNAT/DNATSNAT/DNATSNAT/DNATDNAT만
CT mark action지원지원미지원미지원
최대 HW flow 엔트리~1M~4M~64K~128K
flowtable HW offload지원지원지원 (5.15+)미지원

NAT 오프로드

SNAT/DNAT 주소 변환은 flowtable과 eSwitch 모두에서 오프로드할 수 있습니다. (NAT 아키텍처 참고)

기능eSwitch HWflowtable SW비고
SNAT (Source NAT)지원지원NF_FLOW_SNAT 플래그
DNAT (Destination NAT)지원지원NF_FLOW_DNAT 플래그
MASQUERADE제한적지원인터페이스 주소 변경 시 flush 필요
CGNAT (Large-scale NAT)NIC 의존지원포트 범위 제한 시 SW 폴백
1:1 NAT지원지원가장 효율적인 HW offload 대상
Port Forwarding (REDIRECT)지원지원단일 목적지 DNAT
NAT64/NAT46미지원제한적IPv6↔IPv4 변환은 주로 SW
conntrack 기반 NAT지원지원CT action에서 NAT 정보 전달

NAT 오프로드의 커널 구현

flowtable NAT offload는 conntrack의 NAT 정보를 flow_offload 구조체에 복사하여, 이후 패킷을 conntrack 없이 직접 변환합니다:

/* net/netfilter/nf_flow_table_ip.c - NAT 오프로드 적용 */
static int nf_flow_nat_ip(const struct flow_offload *flow,
                           struct sk_buff *skb,
                           unsigned int thoff,
                           enum flow_offload_tuple_dir dir)
{
    struct iphdr *iph = ip_hdr(skb);

    /* SNAT: 소스 IP/포트 교체 */
    if (flow->flags & FLOW_OFFLOAD_SNAT) {
        /* ORIGINAL 방향: src → NAT 주소로 변경 */
        /* REPLY 방향: dst → 원래 주소로 복원 */
        nf_flow_snat_port(skb, thoff,
            flow->tuplehash[!dir].tuple.src_v4.s_addr,
            flow->tuplehash[!dir].tuple.port.src);
        /* IP 체크섬 + L4 체크섬 재계산 */
        csum_replace4(&iph->check, iph->saddr, new_addr);
    }

    /* DNAT: 목적지 IP/포트 교체 (위와 유사) */
    if (flow->flags & FLOW_OFFLOAD_DNAT)
        nf_flow_dnat_port(skb, thoff, ...);

    return 0;
}

CGNAT 대규모 NAT 시나리오

통신사(ISP) 환경의 CGNAT(Carrier-Grade NAT)에서는 수만 개의 내부 IP를 소수의 공인 IP로 변환합니다. 이 경우:

# CGNAT nftables 설정 예시
table ip cgnat {
    chain postrouting {
        type nat hook postrouting priority 100; policy accept;

        # 내부 대역별 공인 IP 분배 (Deterministic NAT)
        ip saddr 100.64.0.0/24 snat to 203.0.113.1:1024-32767
        ip saddr 100.64.1.0/24 snat to 203.0.113.1:32768-65535
        ip saddr 100.64.2.0/24 snat to 203.0.113.2:1024-32767

        # 폴백: 나머지 → 공인 IP 풀에서 동적 할당
        ip saddr 100.64.0.0/10 snat to 203.0.113.1-203.0.113.10
    }
}

# CGNAT 세션도 flowtable 오프로드 적용
nft add rule ip cgnat forward \
  ct state established flow add @ft

QoS/터널/IPSec 오프로드

DSCP/QoS 오프로드

TC flower로 DSCP 필드를 매칭하고 QoS 큐에 매핑하는 규칙을 eSwitch에 설치할 수 있습니다.

# DSCP EF (46) → TC 큐 0 (높은 우선순위)
tc filter add dev eth0_rep0 ingress prio 1 \
  protocol ip flower \
  ip_tos 0xb8/0xfc \
  action skbedit priority 0

# DSCP AF11 (10) → TC 큐 2 (낮은 우선순위)
tc filter add dev eth0_rep0 ingress prio 2 \
  protocol ip flower \
  ip_tos 0x28/0xfc \
  action skbedit priority 2

DSCP/QoS 매핑 상세

NGFW에서 QoS는 보안 정책과 연계됩니다. DPI 결과(App-ID)에 따라 DSCP 값을 재마킹하고, HW QoS 큐에 매핑하는 패턴입니다:

DSCP 클래스IP ToS 바이트용도HW 큐 매핑
EF (Expedited Forwarding)460xB8VoIP, 실시간 미디어TC 0 (strict priority)
AF41340x88화상회의, 인터랙티브TC 1
AF31260x68스트리밍 비디오TC 2
AF21180x48주요 비즈니스 앱TC 3
AF11100x28일반 트래픽TC 4 (WRR)
BE (Best Effort)00x00기본 트래픽TC 5 (WRR)
CS1 (Scavenger)80x20백업, 대용량 전송TC 6 (lowest)
# NGFW에서 DPI 결과 기반 DSCP 재마킹
# nftables에서 App-ID → DSCP 매핑 (DPI 엔진이 ct mark에 App-ID 기록)
table inet qos_mark {
    map app_dscp {
        type mark : verdict
        elements = {
            0x0001 : accept,    # VoIP → DSCP EF (DPI에서 이미 마킹)
            0x0002 : accept,    # Video → DSCP AF41
            0x0003 : accept,    # Web → DSCP AF11
        }
    }

    chain forward {
        type filter hook forward priority 50; policy accept;
        # DPI 엔진이 ct mark에 기록한 App-ID → DSCP 재마킹
        ct mark 0x0001 ip dscp set ef
        ct mark 0x0002 ip dscp set af41
        ct mark 0x0003 ip dscp set af11
    }
}

# HW QoS 큐 설정 (TC MQPRIO)
tc qdisc add dev eth0 root mqprio \
  num_tc 4 map 3 3 2 2 1 1 0 0 0 0 0 0 0 0 0 0 \
  queues 1@0 1@1 2@2 4@4 hw 1

터널 오프로드

NGFW에서 터널은 두 가지 역할을 합니다: 사이트 간 VPN오버레이 네트워크. eSwitch는 다음 터널 타입의 encap/decap을 하드웨어에서 수행합니다:

터널 + NGFW 오프로드 결합

터널 decap → 내부 패킷 DPI → 오프로드의 전체 흐름을 TC flower로 구성합니다:

# VXLAN 터널 디바이스 생성
ip link add vxlan0 type vxlan id 100 \
  local 10.0.0.1 dport 4789 nolearning external

# eSwitch에서 VXLAN decap + conntrack + forward 규칙
# chain 0: 외부 헤더로 VXLAN 매칭 → decap
tc filter add dev eth0_rep0 ingress chain 0 prio 1 \
  protocol ip flower \
  enc_dst_ip 10.0.0.1 enc_dst_port 4789 enc_key_id 100 \
  action tunnel_key unset pipe \
  action goto chain 1

# chain 1: decap된 내부 패킷에 conntrack 적용
tc filter add dev eth0_rep0 ingress chain 1 prio 1 \
  protocol ip flower ct_state -trk \
  action ct zone 1 pipe \
  action goto chain 2

# chain 2: EST → HW forward, NEW → CPU
tc filter add dev eth0_rep0 ingress chain 2 prio 1 \
  protocol ip flower ct_state +trk+est \
  action ct zone 1 nat pipe \
  action mirred egress redirect dev eth1_rep0

tc filter add dev eth0_rep0 ingress chain 2 prio 2 \
  protocol ip flower ct_state +trk+new \
  action pass # CPU → nftables → NFQUEUE → DPI

IPSec 인라인 크립토 오프로드

IPSec & xfrm의 HW 오프로드를 NGFW 파이프라인에 통합하면, 암호화/복호화를 NIC에서 수행하고 평문 패킷만 DPI 엔진에 전달할 수 있습니다.

# IPSec crypto offload 활성화 (xfrm)
ip xfrm state add src 10.0.0.1 dst 10.0.0.2 \
  proto esp spi 0x1001 reqid 1 mode tunnel \
  enc 'aes' 0x$(head -c 16 /dev/urandom | xxd -p) \
  offload dev eth0 dir out

IPSec 오프로드 모드 비교

모드암호화ESP 헤더라우팅성능NIC 요구사항
Full SWCPUCPUCPU1~5 Gbps/core없음
Crypto offloadNICCPUCPU10~25 Gbpsinline crypto 지원
Packet offloadNICNICCPU25~50 Gbpspacket offload 지원
Full offloadNICNICNIC50~100 GbpseSwitch + xfrm full offload

NGFW와의 통합 포인트: IPSec 수신 시 NIC이 복호화를 수행하고, 평문(decrypted) 패킷이 커널에 전달됩니다. 이 평문 패킷에 대해 conntrack → flowtable → DPI 파이프라인이 정상 작동합니다. 송신 시에는 flowtable이 평문 패킷을 NIC에 전달하고, NIC이 ESP 캡슐화와 암호화를 인라인으로 수행합니다.

# IPSec + flowtable NGFW 조합 설정
# 1. xfrm SA에 crypto offload 설정
ip xfrm state add src 10.0.0.1 dst 10.0.0.2 \
  proto esp spi 0x1001 reqid 1 mode tunnel \
  aead 'rfc4106(gcm(aes))' 0x$(head -c 20 /dev/urandom | xxd -p) 128 \
  offload crypto dev eth0 dir out

ip xfrm state add src 10.0.0.2 dst 10.0.0.1 \
  proto esp spi 0x1002 reqid 1 mode tunnel \
  aead 'rfc4106(gcm(aes))' 0x$(head -c 20 /dev/urandom | xxd -p) 128 \
  offload crypto dev eth0 dir in

# 2. xfrm 정책
ip xfrm policy add src 10.1.0.0/24 dst 10.2.0.0/24 \
  dir out tmpl src 10.0.0.1 dst 10.0.0.2 \
  proto esp reqid 1 mode tunnel

# 3. flowtable에서 decrypted 패킷 오프로드
nft add flowtable inet ngfw ft_ipsec \
  { hook ingress priority 0\; devices = { eth0 }\; flags offload\; }

nft add rule inet ngfw forward \
  ct state established \
  ipsec in reqid 1 \
  flow add @ft_ipsec accept

# 확인: IPSec HW 오프로드 상태
ip xfrm state show | grep -A1 offload
ethtool -S eth0 | grep ipsec

NGFW에서 VXLAN 오버레이 + 보안 통합

데이터센터 NGFW에서 VXLAN 오버레이 네트워크와 보안 검사를 통합하는 아키텍처입니다. eSwitch가 VXLAN 터널의 encap/decap을 하드웨어에서 수행하고, 내부 패킷에 대해 conntrack + DPI를 적용합니다.

# VXLAN + NGFW 통합 구성 (eSwitch offload)

# 1. VXLAN 터널 엔드포인트 생성
ip link add vxlan100 type vxlan id 100 \
  local 10.0.0.1 dport 4789 nolearning external

# 2. nftables에서 decap된 내부 트래픽에 NGFW 정책 적용
table inet vxlan_ngfw {
    flowtable ft_vxlan {
        hook ingress priority 0
        devices = { vxlan100, eth1 }
        flags offload
    }

    chain forward {
        type filter hook forward priority 0; policy drop;
        ct state invalid drop

        # VXLAN 내부 트래픽: EST → offload
        iifname "vxlan100" ct state established,related \
            flow add @ft_vxlan accept

        # VXLAN 내부 트래픽: NEW → DPI
        iifname "vxlan100" ct state new \
            queue num 0-3 fanout

        # 내부 → VXLAN: EST → offload
        oifname "vxlan100" ct state established,related \
            flow add @ft_vxlan accept
    }
}

# 3. eSwitch에서 VXLAN + conntrack 통합 규칙
# 외부 헤더로 VXLAN 패킷 식별 → decap → conntrack → forward
tc filter add dev eth0_rep0 ingress chain 0 prio 1 \
  protocol ip flower \
  enc_dst_ip 10.0.0.1 enc_dst_port 4789 enc_key_id 100 \
  action tunnel_key unset pipe \
  action ct zone 2 pipe \
  action goto chain 1

# chain 1에서 EST/NEW 분기 (위 §8 패턴과 동일)
tc filter add dev eth0_rep0 ingress chain 1 prio 1 \
  protocol ip flower ct_state +trk+est \
  action ct zone 2 nat pipe \
  action mirred egress redirect dev eth1_rep0

tc filter add dev eth0_rep0 ingress chain 1 prio 2 \
  protocol ip flower ct_state +trk+new \
  action pass
VXLAN offload의 성능 효과: eSwitch가 VXLAN decap을 HW에서 수행하면, 내부 패킷의 conntrack + flowtable offload가 자연스럽게 동작합니다. VXLAN encap/decap의 CPU 오버헤드(패킷당 ~200ns)를 완전히 제거할 수 있으며, 이는 소규모 패킷에서 특히 큰 성능 차이를 만듭니다.
IPSec + 터널 + QoS 통합 파이프라인 Ingress (수신) NIC RX IPSec Decrypt (HW crypto) Tunnel Decap VXLAN/GRE/Geneve NAT (DNAT) ACL/DPI QoS 분류 내부 전달 Egress (송신) 내부 출발 ACL NAT (SNAT) QoS 마킹 Tunnel Encap VXLAN/GRE/Geneve IPSec Encrypt (HW crypto) NIC TX SmartNIC/DPU HW Offload 범위 (IPSec + Tunnel + NAT + QoS) CPU 필요 (DPI)
터널 타입NVIDIA ConnectX-6+Intel E810Fortinet NP7Linux SW
VXLANHW encap/decapHW encap/decapHW encap/decap지원
GREHW encap/decapSWHW encap/decap지원
GeneveHW encap/decapHW encap/decap제한적지원
IPSec ESPinline cryptoinline cryptoNP7 내장xfrm SW
WireGuard미지원미지원미지원커널 SW
L2TP미지원미지원HW지원
MPLSHW push/pop미지원HW지원

상용 NGFW HW 아키텍처

Fortinet NP7 / SP5

Fortinet FortiGate는 자체 ASIC 칩을 사용하여 데이터 플레인을 가속합니다:

NP7 세션 오프로드 프로세스

FortiGate에서 세션이 NP7으로 오프로드되는 과정:

  1. 첫 패킷: CPU(IPS Engine)가 수신 → conntrack + UTM(IPS/AV/App Control) 전체 검사
  2. 세션 수립: 검사 통과 시 CPU가 ISF(Iterate Session Filter)에 오프로드 결정 쿼리
  3. NP7 등록: session_offload_add() → NP7 Session Table에 5-tuple + NAT 정보 + QoS 태그 기록
  4. 후속 패킷: NP7 ASIC이 Session Table lookup → NAT rewrite → QoS → forwarding (CPU bypass)
  5. 세션 종료: TCP FIN/RST → NP7이 CPU에 알림 → Session Table 삭제 → 통계 동기화
FortiGate 구성 요소역할성능
NP7 ASICL2~L4 세션 오프로드, NAT, IPSec, VXLAN, QoS200Gbps (단일 칩)
SP5 (Security Processor)SSL/TLS 가속 (복호화/암호화)SSL 검사 시 CPU 부하 감소
CP9 (Content Processor)AV 시그니처, IPS 패턴 매칭 보조CPU DPI 성능 ~2배 향상
CPU (FortiOS)정책 결정, App Control, DLP, 관리모델별 차이
# FortiGate 진단 명령
diagnose npu np7 session-stats         # NP7 오프로드 세션 통계
diagnose npu np7 sse-stats all         # Session Search Engine 통계
diagnose npu np7 port-list             # NP7 포트 매핑 확인
diagnose sys session filter dport 443  # 특정 포트 세션 확인
get system performance firewall statistics # 전체 성능 통계

# NP7 오프로드 비율 확인
diagnose npu np7 session-stats
# 출력 예:
# NP7 offload sessions: 1,234,567
# CPU sessions: 12,345
# Offload ratio: 99.0%

# 특정 세션의 오프로드 상태 확인
diagnose sys session filter src 10.0.0.1
diagnose sys session list
# 출력에서 npu_state=offloaded 확인
FortiGate 오프로드 제외 조건: UTM(AV/IPS/DLP/App Control)이 활성화된 정책의 세션은 첫 패킷 검사 이후에만 오프로드됩니다. 단, SSL 딥 인스펙션이 적용된 세션은 SP5가 전 구간 복호화/암호화를 수행하므로, 패킷당 처리지만 NP7 수준의 세션 오프로드는 불가합니다. 이 경우 SP5의 SSL 가속이 병목 해소의 핵심입니다.
Fortinet 참고 문서:

Palo Alto Networks FPGA

Palo Alto PA 시리즈는 FPGA 기반 SP3 (Security Processing) 아키텍처를 사용합니다:

Single-Pass 아키텍처 상세

Palo Alto의 핵심 차별점은 Single-Pass Parallel Processing (SP3) 아키텍처입니다:

전통적 방화벽Palo Alto SP3
패킷이 FW → IPS → AV → URL 순서로 직렬 통과FW, App-ID, IPS, AV, URL이 단일 패스에서 병렬 처리
각 엔진마다 패킷 복사/버퍼링스트림 기반 처리 (패킷 복사 최소화)
지연 = 각 엔진 지연의 합지연 = 가장 느린 엔진의 지연
엔진 추가 시 성능 선형 감소엔진 추가 시 성능 영향 최소

FE FPGA의 역할: FPGA는 패킷 분류(5-tuple lookup), 세션 테이블 검색, 디캡슐레이션, NAT rewrite를 수행합니다. CPU는 App-ID 엔진만 실행하므로, FPGA가 처리할 수 있는 세션(이미 분류된 ESTABLISHED)은 CPU를 완전히 bypass합니다.

# Palo Alto CLI 진단 명령
show running resource-monitor           # CPU/메모리/세션 사용률
show session all filter state active    # 활성 세션 목록
show session info                       # 세션 통계 (CPS, CC)
show counter global filter delta yes \
  packet-filter yes                     # 실시간 카운터
debug dataplane packet-diag set filter \
  match src 10.0.0.1                    # 특정 IP 패킷 추적
Palo Alto 참고 문서:

Check Point SecureXL / Maestro

Check Point는 소프트웨어 기반 가속 아키텍처를 사용합니다:

SecureXL 처리 경로 상세

SecureXL은 3가지 처리 경로(Accelerated Path, Medium Path, Firewall Path)로 패킷을 분류합니다:

경로처리 위치대상 트래픽성능 영향
Accelerated Path커널 (SecureXL)Accept Template에 등록된 ESTABLISHED 세션최고 성능
Medium Path커널 (SecureXL + SXL 엔진)NAT, VPN 적용 필요한 세션Accelerated의 60~80%
Firewall Path유저스페이스 (fwd daemon)NEW 연결, IPS/DLP/App Control 검사가장 느림

Accept Template은 Linux conntrack의 ESTABLISHED + flowtable 개념과 유사합니다. conntrack이 세션 상태를 추적하고, Accept Template이 검사가 완료된 세션의 5-tuple을 커널 가속 테이블에 등록합니다. Drop Template은 이미 DROP 판정이 난 소스 IP/포트를 캐시하여, 동일한 악성 트래픽이 재전송될 때 CPU까지 가지 않고 즉시 DROP합니다.

CoreXL / SND 아키텍처

Check Point의 멀티코어 아키텍처는 Linux의 RSS/RPS와 유사하지만 보안에 최적화되어 있습니다:

# Check Point 진단 명령 (Gaia OS)
fwaccel stat                            # SecureXL 가속 통계
fwaccel stats -s                        # 경로별 패킷/바이트 통계
fwaccel conns                           # Accept Template 연결 수
fw ctl multik stat                      # CoreXL 인스턴스별 통계
cpview                                  # 실시간 성능 모니터 (TUI)

# SecureXL 비활성화/활성화 (성능 비교 테스트)
fwaccel off     # 가속 비활성화 → 전체 Firewall Path
fwaccel on      # 가속 재활성화

# 특정 세션의 처리 경로 확인
fw ctl zdebug + drop    # 디버그 로그
Check Point 참고 문서:

Juniper Express Path

Juniper SRX 시리즈의 Express Path는 세션의 첫 패킷만 flow daemon이 처리하고, 이후 패킷은 NPU(Network Processing Unit)에서 직접 전달합니다:

Express Path 동작 원리

Juniper SRX의 패킷 처리 아키텍처:

  1. NP (Network Processor): 모든 패킷의 1차 수신. 세션 테이블에서 lookup
  2. Express Path 히트: 기존 세션 매칭 → NP에서 직접 forwarding + NAT rewrite (flowd bypass)
  3. Express Path 미스: 새 세션 → flowd(flow daemon)로 전달 → 정책 평가 + IPS/AppID
  4. 세션 설치: flowd가 정책 통과 시 세션을 NP Express Path 테이블에 등록
# Juniper SRX 진단 명령
show security flow statistics           # 플로우 통계 (Express Path 비율)
show security monitoring fpc 0          # FPC별 세션/처리량
show security flow session              # 세션 테이블
show security flow session summary      # 세션 요약 (활성/최대)

# Express Path 상태 확인
show pfe statistics traffic
# express-path-packets, slow-path-packets 비교

# Services Offload Engine 상태
show chassis fpc pic-status
Juniper 참고 문서:

Linux 커널 기반 NGFW

Linux 커널과 오픈소스 도구를 조합하여 NGFW를 구축하는 접근법입니다:

Linux NGFW 스택 구성

Linux 커널 기반 NGFW를 상용 수준으로 구축하기 위한 전체 스택:

계층구성 요소역할대안
HW Fast PatheSwitch FDB (mlx5/ice)EST 세션 라인레이트 전달OVS-DPDK TC offload
SW Fast Pathnf_flowtableHW 미지원 EST 세션 가속VPP session table
Stateful FWnftables + nf_conntrackACL, NAT, 세션 추적iptables (레거시)
DPI / IPSSuricata (NFQUEUE mode)시그니처 기반 탐지/차단nDPI, Snort 3
App-IDnDPI 라이브러리L7 프로토콜 분류Suricata App-Layer
SSL 검사mitmproxy / Suricata TLSSSL/TLS 프록시SSLsplit
DDoS Pre-filterXDP BPFL3/L4 사전 필터tc-bpf
QoSTC qdisc (HTB/fq_codel)대역폭 제어, 우선순위CAKE
VPNStrongSwan (xfrm) + WireGuard사이트 간/원격 접속 VPNLibreswan
HAconntrackd + Keepalived세션 동기화 + VIP failoverPacemaker
관리nftables API + Prometheus정책 관리 + 모니터링Firewalld

VPP 기반 NGFW 데이터 플레인

FD.io VPP를 사용하면 커널 Netfilter 대신 유저스페이스에서 패킷 처리하여 더 높은 성능을 달성합니다:

# VPP 기반 NGFW 설정 예시
# /etc/vpp/startup.conf
dpdk {
  dev 0000:03:00.0 { name eth0 }
  dev 0000:03:00.1 { name eth1 }
}

# VPP CLI에서 ACL + session 설정
vppctl acl-plugin acl add permit+reflect \
  src 10.0.0.0/8 dst 0.0.0.0/0 proto tcp
vppctl set acl-plugin interface eth0 input acl 0

# 새 세션만 TAP으로 전달 (DPI)
vppctl create tap id 0
vppctl set interface state tap0 up
Linux NGFW 참고 문서:
상용 NGFW 아키텍처 비교 Fortinet NP7 ASIC SP5 Crypto CP9 DPI CPU (FW) 전용 ASIC 기반 Palo Alto FE FPGA NPC Card CPU (Single-Pass App-ID/IPS) FPGA + CPU 병렬 Check Point SecureXL CoreXL Maestro HyperScale 순수 SW 가속 Linux NGFW eSwitch HW flowtable NFQUEUE nftables SmartNIC + 커널 아래 테이블에서 상세 비교 →
특성Fortinet (NP7)Palo Alto (FPGA)Check PointJuniper SRXLinux SmartNIC
Fast Path 방식ASIC session tableFPGA forwarding engineSecureXL Accept TemplateExpress Path NPUeSwitch FDB + flowtable
Fast Path 처리량200Gbps+모델 의존 (100Gbps)SW 기반 (제한적)NPU 의존NIC 라인레이트
DPI 방식CPU + CP9 보조CPU (Single-Pass)CPU (CoreXL)CPU (flowd)NFQUEUE + Suricata
IPSec 가속NP7 내장FPGA/CPUSWServices EngineNIC inline crypto
SSL/TLS 가속SP5 전용 칩CPUCPUCPUkTLS + CPU
NAT 오프로드NP7 HWFPGA HWSecureXL SWNPUflowtable/eSwitch
세션 테이블 크기수천만수백만수백만수백만NIC 의존 (수만~수십만 HW)
수평 확장HA clusterHA clusterMaestroChassis cluster커널 네임스페이스/VRF
커스터마이즈제한적 (벤더 종속)제한적중간제한적완전 자유
라이선스 비용높음매우 높음높음높음하드웨어 비용만

상용 vs Linux NGFW 선택 기준

어떤 접근을 선택해야 하는가?
  • 상용 NGFW 선택: 규제 준수 인증(CC, FIPS) 필수, 벤더 기술 지원 필요, IT 인력 제한, 빠른 도입 필요
  • Linux NGFW 선택: 커스텀 파이프라인 필요, 대규모(100G+) 환경, 특수 프로토콜/DPI 요구, 벤더 종속 탈피, 비용 최적화
  • 하이브리드: 상용 NGFW의 관리 플레인 + Linux/SmartNIC의 데이터 플레인 (일부 벤더가 이 모델 채택 중)

성능 비교 벤치마크 (참고 수치)

다음은 공개된 데이터시트와 독립 벤치마크(NSS Labs, RFC 9411 기반)에서 추출한 참고 수치입니다. 실제 환경에서는 정책 복잡도, 트래픽 믹스, DPI 시그니처 수에 따라 크게 달라집니다:

메트릭Fortinet 7081F (NP7)PA-5440CP 28000 (Maestro)Linux + CX-7 (200G)
FW 처리량 (L4)700 Gbps75 Gbps64 Gbps200 Gbps (HW offload)
NGFW 처리량 (DPI+IPS)60 Gbps30 Gbps15 Gbps10~20 Gbps (Suricata)
Threat Protection40 Gbps22 Gbps12 Gbps5~15 Gbps
SSL Inspection35 Gbps (SP5)12 Gbps7 Gbps3~8 Gbps (kTLS)
CPS1.5M450K300K200K~500K
동시 세션45M16M10Mconntrack_max 설정 의존

Linux NGFW의 NGFW 처리량은 Suricata DPI에 의해 제한되지만, FW 처리량(L4 stateful)에서는 SmartNIC HW offload로 상용 제품에 필적하는 성능을 달성합니다. 핵심은 오프로드 비율을 높여 DPI 부하를 최소화하는 것입니다.

벤치마크 수치 출처와 주의사항:
  • Fortinet 7081F: FortiGate 7000 Series Data Sheet 기준. FW 처리량 700Gbps는 NP7 ASIC 다중 장착 시 합산 값
  • PA-5440: PA-5400 Series Data Sheet 기준. App-ID + IPS + logging 활성화 시 30Gbps
  • CP 28000: Quantum Gateway 모델 비교 기준. Maestro 클러스터 시 수평 확장 가능
  • Linux + CX-7: NVIDIA ConnectX-7 Product Brief 기준 NIC 라인레이트. NGFW 처리량은 Suricata 멀티코어 벤치마크 참고
  • 모든 벤더 수치는 최적 조건(단순 트래픽 믹스, 최소 정책)에서의 데이터시트 값이며, 실제 환경에서는 RFC 9411 방법론에 따라 독립적으로 측정해야 합니다

커널 NGFW 빌딩 블록

Linux 커널에서 NGFW를 구축할 때 사용하는 주요 서브시스템과 NGFW 기능 간의 매핑입니다. 각 서브시스템은 독립적으로 발전해 왔지만, NGFW에서는 이들을 하나의 통합 파이프라인으로 결합해야 합니다. 이 절에서는 각 서브시스템의 역할과 연동 포인트를 정리합니다.

커널 서브시스템 선택의 핵심 원칙:
  • XDP: 가장 빠른 경로 (NIC 드라이버 직후). DDoS pre-filter, 명백한 악성 패킷 DROP
  • TC flower + eSwitch: HW Fast Path. ESTABLISHED 세션의 라인레이트 전달
  • nf_flowtable: SW Fast Path. HW 미지원 세션의 커널 내 가속
  • nftables + conntrack: Slow Path. 새 세션의 정책 평가, 상태 추적
  • NFQUEUE: 유저스페이스 DPI 연동. Suricata/nDPI로 패킷 전달
  • xfrm: IPSec 처리. HW crypto offload 가능
  • TC qdisc: QoS. 대역폭 제어, 우선순위 큐잉
NGFW 기능 ↔ 커널 서브시스템 매핑 NGFW 기능 Stateful ACL NAT (SNAT/DNAT) 세션 오프로드 DPI / IPS DDoS Pre-filter HW Fast Path IPSec VPN QoS / Traffic Shaping 커널 서브시스템 nftables + conntrack nf_nat nf_flowtable NFQUEUE + Suricata XDP / BPF TC flower + eSwitch xfrm (IPSec) TC qdisc (HTB/TBF) 실선: 1:1 매핑 | 점선: 추가 연관 (eSwitch가 ACL과 오프로드 모두 담당)
NGFW 기능커널 서브시스템HW 오프로드관련 페이지
Stateful ACL (L3/L4)nftables + nf_conntrackTC flower ct_state → eSwitchNetfilter
NAT (SNAT/DNAT)nf_natflowtable NAT offload / eSwitch NAT actionNAT
세션 오프로드 (Fast Path)nf_flowtableNF_FLOWTABLE_HW_OFFLOAD → NIC flow tableFlowtable
DPI / IPSNFQUEUE + 유저스페이스 DPI미오프로드 (CPU 처리)NFQUEUE & DPI
DDoS Pre-filterXDP BPF 프로그램XDP HW offload (지원 NIC)BPF/XDP
HW Fast PathTC flower + mlx5/ice eSwitcheSwitch FDB rule 삽입eSwitch
IPSec VPNxfrmNIC inline crypto offloadIPSec & xfrm
QoS / Traffic ShapingTC qdisc (HTB, TBF, fq_codel)TC flower + skbedit → HW QoS queueTC
ALG 프로토콜nf_conntrack_helper미오프로드 (CPU 필수)conntrack 헬퍼
네트워크 격리network namespace / VRFeSwitch VF representor네트워크 네임스페이스

드라이버 오프로드 계약 (ndo_setup_tc)

NIC 드라이버가 TC flower offload를 지원하기 위해 구현해야 하는 인터페이스입니다. NGFW 파이프라인의 HW offload는 이 계약에 의존합니다. (Network Device 드라이버 참고)

/* include/linux/netdevice.h */
struct net_device_ops {
    /* TC flower offload 진입점 */
    int (*ndo_setup_tc)(struct net_device *dev,
                        enum tc_setup_type type,
                        void *type_data);
};

/* 드라이버가 처리해야 하는 TC 타입 */
enum tc_setup_type {
    TC_SETUP_QDISC_MQPRIO,   /* MQPRIO QoS 큐 설정 */
    TC_SETUP_CLSFLOWER,       /* TC flower 분류기 규칙 */
    TC_SETUP_FT,              /* flowtable offload */
    TC_SETUP_BLOCK,           /* 블록 바인딩 (CT offload) */
};

/* CLSFLOWER 콜백에서 처리하는 명령 */
enum tc_fl_command {
    TC_CLSFLOWER_REPLACE,    /* 규칙 추가/교체 */
    TC_CLSFLOWER_DESTROY,    /* 규칙 삭제 */
    TC_CLSFLOWER_STATS,      /* HW 카운터 읽기 */
};
코드 설명
  • 4-6행 ndo_setup_tc는 TC 서브시스템이 NIC 드라이버에 오프로드 규칙을 전달하는 단일 진입점입니다. type 파라미터로 어떤 종류의 오프로드인지 구분합니다.
  • 10-14행 TC_SETUP_FT는 flowtable offload 전용 타입으로, nf_flow_offload_hw_add()가 이 타입으로 드라이버를 호출합니다. TC_SETUP_BLOCK은 conntrack action offload에 사용됩니다.
  • 18행 TC_CLSFLOWER_STATS는 HW에서 처리된 패킷/바이트 카운터를 읽어옵니다. GC가 주기적으로 이 명령을 호출하여 타임아웃을 갱신합니다.

nf_flowtable 내부 구조

/* include/net/netfilter/nf_flow_table.h */
struct nf_flowtable {
    struct list_head   list;        /* flowtable 전역 리스트 */
    struct rhashtable  rhashtable;  /* 5-tuple 해시 테이블 */
    int                priority;    /* Netfilter 훅 우선순위 */
    unsigned int       flags;       /* NF_FLOWTABLE_HW_OFFLOAD 등 */
    struct nf_flowtable_type *type;
    struct delayed_work gc_work;    /* GC 워크큐 */
    struct flow_block  flow_block;  /* HW offload 블록 */
    struct mutex       flow_block_lock;
    possible_net_t     net;
};

/* rhashtable 파라미터: 자동 크기 조절 */
static const struct rhashtable_params nf_flow_offload_rhash_params = {
    .head_offset     = offsetof(struct flow_offload_tuple_rhash, node),
    .key_offset      = offsetof(struct flow_offload_tuple_rhash, tuple),
    .key_len         = sizeof(struct flow_offload_tuple),
    .automatic_shrinking = true,  /* 엔트리 감소 시 자동 축소 */
};

flowtable은 rhashtable(Resizable Hash Table)을 사용합니다. 엔트리 수에 따라 버킷이 자동으로 확장/축소되며, RCU 기반 읽기로 lock-free lookup을 보장합니다. 이는 NGFW에서 높은 CPS 환경에서도 Fast Path lookup의 일관된 성능을 유지하는 핵심입니다.

핵심 커널 API 흐름

/* NGFW 파이프라인의 커널 API 호출 흐름 (개념도) */

/* 1. 패킷 수신 → Netfilter PREROUTING 훅 */
nf_hook(NFPROTO_IPV4, NF_INET_PRE_ROUTING, skb);

/* 2. conntrack: 세션 추적 */
struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
if (ctinfo == IP_CT_NEW) {
    /* 새 세션 → Slow Path: 전체 규칙 평가 */
    nft_do_chain(chain, skb);  /* nftables 규칙 평가 */
    nf_queue(skb, queue_num);  /* NFQUEUE → DPI 엔진 */
}

/* 3. flowtable: ESTABLISHED 세션 Fast Path */
if (ctinfo == IP_CT_ESTABLISHED) {
    struct flow_offload *flow = flow_offload_lookup(ft, skb);
    if (flow) {
        /* Fast Path: conntrack bypass → 직접 전달 */
        nf_flow_offload_ip_hook(skb);  /* NAT rewrite + forward */
        return NF_STOLEN;  /* Netfilter 훅 체인 종료 */
    }
}

/* 4. HW offload 등록 */
flow_offload_add(ft, flow);
if (ft->flags & NF_FLOWTABLE_HW_OFFLOAD)
    nf_flow_offload_hw_add(net, flow, ct);  /* eSwitch FDB rule 설치 */
코드 설명
  • 3행 (nf_hook PREROUTING) 패킷이 처음 도착하면 PREROUTING 훅에서 모든 등록된 콜백이 실행됩니다. conntrack, nftables, flowtable이 이 훅에 등록되어 있습니다.
  • 6행 (nf_ct_get) skb에 연결된 conntrack 엔트리를 가져옵니다. ctinfo는 현재 패킷의 방향(ORIGINAL/REPLY)과 상태(NEW/ESTABLISHED)를 나타냅니다.
  • 7-10행 (IP_CT_NEW) 새로운 세션의 첫 패킷입니다. nftables 체인 전체를 평가하고, NFQUEUE로 DPI 엔진에 전달합니다.
  • 13-19행 (IP_CT_ESTABLISHED) 이미 수립된 세션의 패킷입니다. flowtable에서 플로우를 찾으면 NAT rewrite + forward를 직접 수행하고 NF_STOLEN으로 Netfilter 체인을 종료합니다.
  • 22-24행 (HW offload) flowtable에 등록하고, HW offload 플래그가 있으면 NIC eSwitch에도 FDB 규칙을 설치합니다. 이후 동일 플로우의 패킷은 NIC에서 직접 처리됩니다.

NFQUEUE 연동 API

DPI 엔진이 NFQUEUE에서 패킷을 수신하고 verdict를 반환하는 과정의 유저스페이스 API입니다:

/* libnetfilter_queue를 사용한 DPI 엔진 기본 구조 */
#include <libnetfilter_queue/libnetfilter_queue.h>

static int nfq_callback(struct nfq_q_handle *qh,
                        struct nfgenmsg *nfmsg,
                        struct nfq_data *nfa,
                        void *data)
{
    struct nfqnl_msg_packet_hdr *ph;
    unsigned char *payload;
    int payload_len;
    uint32_t id;

    ph = nfq_get_msg_packet_hdr(nfa);
    id = ntohl(ph->packet_id);
    payload_len = nfq_get_payload(nfa, &payload);

    /* DPI 분석 수행 */
    int verdict = analyze_packet(payload, payload_len);

    /* verdict 반환: NF_ACCEPT 또는 NF_DROP */
    if (verdict == DPI_ALLOW)
        return nfq_set_verdict(qh, id, NF_ACCEPT, 0, NULL);
    else
        return nfq_set_verdict(qh, id, NF_DROP, 0, NULL);
}

/* 메인 루프: NFQUEUE 수신 + DPI 콜백 */
int main(void)
{
    struct nfq_handle *h = nfq_open();
    struct nfq_q_handle *qh = nfq_create_queue(h, 0,
                                                 &nfq_callback, NULL);

    /* 배치 verdict 활성화 (성능 향상) */
    nfq_set_queue_flags(qh, NFQA_CFG_F_GSO, NFQA_CFG_F_GSO);

    /* 소켓에서 패킷 수신 루프 */
    int fd = nfq_fd(h);
    char buf[65536];
    int rv;
    while ((rv = recv(fd, buf, sizeof(buf), 0)) > 0)
        nfq_handle_packet(h, buf, rv);

    return 0;
}

오프로드 구현 패턴

Suricata NFQUEUE 통합 설정

Suricata를 NGFW의 DPI/IPS 엔진으로 사용할 때의 최적화된 설정입니다:

# /etc/suricata/suricata.yaml (NGFW 최적화)

# NFQUEUE 모드 설정
nfq:
  mode: repeat          # verdict 후 재주입 (NFQUEUE → nftables 계속)
  repeat-mark: 1        # 재주입된 패킷 마킹 (중복 큐잉 방지)
  repeat-mask: 1
  bypass: yes           # Suricata 과부하 시 패킷 bypass (가용성 우선)
  fail-open: yes        # Suricata 장애 시 패킷 통과 (HA 환경)
  batchcount: 20        # 배치 verdict (성능 향상)

# 멀티 스레드 설정 (CPU 코어 4~16개 환경)
threading:
  set-cpu-affinity: yes
  cpu-affinity:
    - management-cpu-set:
        cpu: [ 0 ]
    - receive-cpu-set:
        cpu: [ 1, 2, 3, 4 ]    # NFQUEUE 수신 스레드
    - worker-cpu-set:
        cpu: [ 5, 6, 7, 8, 9, 10, 11, 12 ]  # 분석 워커

# App-Layer 프로토콜 분석 (NGFW App-ID)
app-layer:
  protocols:
    tls:
      enabled: yes
      detection-ports:
        dp: 443, 8443
      ja3-fingerprints: yes   # JA3/JA3S 핑거프린트 (TLS 분류)
    http:
      enabled: yes
      server-body-limit: 1mb  # DLP를 위한 바디 검사 한계
    dns:
      enabled: yes
    smb:
      enabled: yes
    ssh:
      enabled: yes
      hassh: yes              # HASSH 핑거프린트

# 플로우 타임아웃 (flowtable과 동기화)
flow-timeouts:
  default:
    new: 30
    established: 300          # nf_flowtable_tcp_timeout과 일치
    closed: 10
    bypassed: 300             # bypass된 세션 (오프로드됨)
  tcp:
    new: 60
    established: 600
    closed: 60

# bypass 설정 (오프로드 연동)
stream:
  bypass: yes                 # 분류 완료 후 스트림 바이패스
  max-sessions: 1000000       # 최대 동시 세션
코드 설명
  • 5행 (mode: repeat) repeat 모드에서 Suricata는 verdict(ACCEPT/DROP) 후 패킷을 Netfilter에 재주입합니다. 이를 통해 nftables의 나머지 규칙(flowtable 등록 등)이 계속 실행됩니다.
  • 9행 (fail-open: yes) Suricata 프로세스가 크래시하거나 응답하지 않을 때 패킷을 자동으로 통과시킵니다. NGFW에서 가용성이 보안보다 중요한 경우 필수입니다.
  • 10행 (batchcount: 20) NFQUEUE에서 20개 패킷을 모아서 한 번에 verdict를 전송합니다. syscall 횟수를 줄여 ~30% 성능 향상이 가능합니다.
  • 34행 (ja3-fingerprints) JA3 핑거프린트는 TLS Client Hello의 특성으로 클라이언트 애플리케이션을 식별합니다. SNI 없이도 App-ID가 가능합니다.
  • 55행 (stream: bypass) Suricata가 세션 분류를 완료하면 이후 패킷의 스트림 재조립을 건너뜁니다. nftables의 flowtable 오프로드와 함께 사용하면 DPI 완료 세션이 Fast Path로 전환됩니다.

nftables NGFW 설정

완전한 nftables 규칙셋으로 NGFW 데이터 플레인을 구성하는 예시입니다. flowtable으로 ESTABLISHED 세션을 오프로드하고, NEW 세션은 NFQUEUE로 DPI 검사를 수행합니다.

#!/usr/sbin/nft -f
# NGFW nftables 규칙셋 (flowtable + NFQUEUE DPI)

table inet ngfw {
    # flowtable 정의 (HW offload 활성화)
    flowtable ft {
        hook ingress priority 0
        devices = { eth0, eth1 }
        flags offload  # NF_FLOWTABLE_HW_OFFLOAD
    }

    # forward chain: NGFW 핵심 파이프라인
    chain forward {
        type filter hook forward priority 0; policy drop;

        # 1. INVALID 패킷 즉시 DROP
        ct state invalid drop

        # 2. ESTABLISHED/RELATED → flowtable offload
        ct state established,related flow add @ft accept

        # 3. NEW 세션 → 기본 ACL 통과 후 NFQUEUE로 DPI
        ct state new ip protocol tcp \
            tcp dport { 80, 443, 8080, 8443 } \
            queue num 0-3 fanout  # 4개 DPI 워커로 분산

        ct state new ip protocol udp \
            udp dport { 53, 443 } \
            queue num 0-3 fanout

        # 4. 허용된 ICMP
        ct state new icmp type { echo-request, echo-reply } accept

        # 5. 나머지 NEW → DROP (기본 정책)
    }

    # input chain: 관리 인터페이스 보호
    chain input {
        type filter hook input priority 0; policy drop;
        ct state established,related accept
        iifname "lo" accept
        tcp dport 22 accept  # SSH 관리
    }
}
코드 설명
  • 6-10행 flowtable ft를 정의합니다. flags offload로 HW offload를 활성화하면, SmartNIC이 지원하는 경우 eSwitch FDB 규칙이 자동 설치됩니다.
  • 17행 INVALID 상태 패킷은 즉시 DROP합니다. conntrack이 유효하지 않다고 판단한 패킷(시퀀스 번호 불일치 등)입니다.
  • 20행 핵심 오프로드 규칙: ESTABLISHED/RELATED 세션을 flowtable에 등록하고 accept합니다. 이후 패킷은 Fast Path로 처리됩니다.
  • 23-25행 새로운 TCP 연결(특정 포트)을 NFQUEUE 0~3번 큐로 fanout 분산합니다. 각 큐에 Suricata 워커가 연결되어 DPI를 수행합니다.
  • 27-29행 DNS(53)와 QUIC(443/UDP) 트래픽도 DPI로 전달합니다.

TC flower ACL 오프로드

eSwitch switchdev 모드에서 TC flower를 사용하여 ACL 규칙을 하드웨어에 직접 설치합니다. 이 방식은 nftables의 SW 경로와 병행하여, NEW 패킷은 CPU(nftables → NFQUEUE → DPI)에서 처리하고 ESTABLISHED 패킷은 eSwitch HW에서 직접 전달합니다.

eSwitch TC flower vs nftables flowtable 비교:
  • TC flower 직접 규칙: 관리자가 명시적으로 CT 매칭 규칙을 설치. 세밀한 제어 가능하지만 관리 복잡도 증가
  • nftables flowtable + HW offload: flow add @ft로 자동 등록. 간편하지만 커널이 자동 결정
  • 권장 조합: nftables flowtable으로 기본 오프로드 + TC flower로 특수 규칙(IP 차단, QoS 등) 추가
# 1. eSwitch switchdev 모드 전환
devlink dev eswitch set pci/0000:03:00.0 mode switchdev

# 2. VF representor에서 hw-tc-offload 활성화
ethtool -K eth0_rep0 hw-tc-offload on

# 3. conntrack zone 설정 + 추적 시작
tc filter add dev eth0_rep0 ingress prio 1 \
  protocol ip flower \
  ct_state -trk \
  action ct zone 1

# 4. EST+TRK → NAT + forward (HW offload)
tc filter add dev eth0_rep0 ingress prio 2 \
  protocol ip flower \
  ct_state +trk+est \
  action ct zone 1 nat \
  action mirred egress redirect dev eth1_rep0

# 5. NEW+TRK → pass to CPU (Slow Path)
tc filter add dev eth0_rep0 ingress prio 3 \
  protocol ip flower \
  ct_state +trk+new \
  action pass

# 6. 특정 IP 대역 차단 (HW에서 DROP)
tc filter add dev eth0_rep0 ingress prio 0 \
  protocol ip flower \
  src_ip 192.168.100.0/24 \
  action drop

DPI 오프로드 바이패스 패턴

DPI 엔진이 세션을 분류하면 이후 패킷은 NFQUEUE를 bypass해야 합니다. eBPF map을 활용한 캐시 패턴입니다:

eBPF DPI 결과 캐시 (완전한 구현)

/* dpi_cache.bpf.c — eBPF DPI 결과 캐시 프로그램 */
#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/udp.h>

/* 5-tuple 플로우 키 */
struct flow_key {
    __be32 src_ip;
    __be32 dst_ip;
    __be16 src_port;
    __be16 dst_port;
    __u8   protocol;
    __u8   pad[3];
};

/* DPI 결과 값 */
struct dpi_result {
    __u32 verdict;      /* 0=ACCEPT, 1=DROP, 2=CONTINUE */
    __u32 app_id;       /* 애플리케이션 ID (DPI 엔진이 설정) */
    __u64 timestamp;    /* 마지막 갱신 (nsec) */
    __u64 bytes;        /* 바이트 카운터 (샘플링용) */
};

/* LRU 해시 맵: 자동으로 가장 오래된 엔트리 퇴출 */
struct {
    __uint(type, BPF_MAP_TYPE_LRU_HASH);
    __uint(max_entries, 500000);
    __type(key, struct flow_key);
    __type(value, struct dpi_result);
} dpi_cache SEC(".maps");

/* 통계 맵 */
struct {
    __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
    __uint(max_entries, 4);
    __type(key, __u32);
    __type(value, __u64);
} stats SEC(".maps");
/* stats[0]=cache_hit, stats[1]=cache_miss,
   stats[2]=bypass_accept, stats[3]=bypass_drop */

static __always_inline int
extract_flow_key(struct __sk_buff *skb, struct flow_key *key)
{
    void *data = (void *)(long)skb->data;
    void *data_end = (void *)(long)skb->data_end;
    struct iphdr *iph = data + sizeof(struct ethhdr);

    if ((void *)(iph + 1) > data_end)
        return -1;

    key->src_ip = iph->saddr;
    key->dst_ip = iph->daddr;
    key->protocol = iph->protocol;

    if (iph->protocol == IPPROTO_TCP) {
        struct tcphdr *tcph = (void *)iph + (iph->ihl * 4);
        if ((void *)(tcph + 1) > data_end) return -1;
        key->src_port = tcph->source;
        key->dst_port = tcph->dest;
    } else if (iph->protocol == IPPROTO_UDP) {
        struct udphdr *udph = (void *)iph + (iph->ihl * 4);
        if ((void *)(udph + 1) > data_end) return -1;
        key->src_port = udph->source;
        key->dst_port = udph->dest;
    }
    return 0;
}

SEC("netfilter")
int dpi_bypass(struct bpf_nf_ctx *ctx)
{
    struct flow_key key = {};
    __u32 idx;
    __u64 *counter;

    if (extract_flow_key(ctx->skb, &key) < 0)
        return NF_ACCEPT;

    struct dpi_result *result = bpf_map_lookup_elem(&dpi_cache, &key);
    if (result) {
        /* 캐시 히트 */
        idx = 0; counter = bpf_map_lookup_elem(&stats, &idx);
        if (counter) (*counter)++;

        /* 바이트 카운터 갱신 (샘플링 판단용) */
        __sync_fetch_and_add(&result->bytes, ctx->skb->len);

        /* 10MB 초과 시 캐시 무효화 → 재검사 */
        if (result->bytes > 10485760) {
            bpf_map_delete_elem(&dpi_cache, &key);
            return NF_ACCEPT;  /* NFQUEUE로 전달 */
        }

        if (result->verdict == 0) {
            idx = 2; counter = bpf_map_lookup_elem(&stats, &idx);
            if (counter) (*counter)++;
            return NF_ACCEPT;   /* DPI ALLOW → bypass */
        } else if (result->verdict == 1) {
            idx = 3; counter = bpf_map_lookup_elem(&stats, &idx);
            if (counter) (*counter)++;
            return NF_DROP;     /* DPI DROP → 즉시 차단 */
        }
    }
    /* 캐시 미스 → NFQUEUE로 전달 (DPI 검사) */
    idx = 1; counter = bpf_map_lookup_elem(&stats, &idx);
    if (counter) (*counter)++;
    return NF_ACCEPT;
}

char _license[] SEC("license") = "GPL";
코드 설명
  • 11-18행 (flow_key) 5-tuple 플로우 키 구조체. pad[3]은 구조체를 8바이트 정렬하여 해시 성능을 최적화합니다.
  • 21-26행 (dpi_result) DPI 결과에 바이트 카운터를 추가하여 주기적 샘플링 임계치를 판단합니다. app_id는 모니터링/로깅에 활용됩니다.
  • 29-33행 (LRU_HASH) LRU 해시 맵은 max_entries에 도달하면 가장 오래된 엔트리를 자동 퇴출합니다. 50만 엔트리로 대부분의 활성 세션을 커버합니다.
  • 36-41행 (PERCPU_ARRAY) per-CPU 카운터로 캐시 히트/미스 통계를 lock-free로 수집합니다. bpftool map dump으로 확인 가능합니다.
  • 85-89행 (바이트 임계치) 10MB 초과 시 캐시 엔트리를 삭제하여 DPI 재검사를 강제합니다. 이는 §9의 주기적 샘플링을 eBPF 수준에서 구현한 것입니다.

DPI 엔진에서 eBPF 맵 업데이트

Suricata의 output 플러그인에서 verdict를 eBPF 맵에 기록합니다:

/* Suricata output 플러그인 (유저스페이스): verdict를 eBPF 맵에 기록 */
#include <bpf/libbpf.h>
#include <bpf/bpf.h>

int map_fd;  /* bpf_obj_get()으로 pinned map 열기 */

void update_dpi_cache(struct Packet *p, int verdict)
{
    struct flow_key key = {
        .src_ip   = p->src.addr_data32[0],
        .dst_ip   = p->dst.addr_data32[0],
        .src_port = p->sp,
        .dst_port = p->dp,
        .protocol = p->proto,
    };
    struct dpi_result result = {
        .verdict   = (verdict == VERDICT_PASS) ? 0 : 1,
        .app_id    = p->flow->alproto,
        .timestamp = get_timestamp_ns(),
        .bytes     = 0,
    };

    bpf_map_update_elem(map_fd, &key, &result, BPF_ANY);

    /* 역방향 플로우도 등록 (양방향 캐시) */
    struct flow_key rev_key = {
        .src_ip = key.dst_ip, .dst_ip = key.src_ip,
        .src_port = key.dst_port, .dst_port = key.src_port,
        .protocol = key.protocol,
    };
    bpf_map_update_elem(map_fd, &rev_key, &result, BPF_ANY);
}
# eBPF 프로그램 로드 및 nftables 연동
# 1. 컴파일
clang -O2 -target bpf -c dpi_cache.bpf.c -o dpi_cache.bpf.o

# 2. 로드 및 맵 핀닝
bpftool prog load dpi_cache.bpf.o /sys/fs/bpf/dpi_bypass \
  type netfilter

# 3. nftables에서 eBPF 프로그램 연결
# (nftables 커널 5.18+에서 BPF verdict 지원)
nft add rule inet ngfw forward \
  ct state new \
  meta mark != 0xff \
  queue num 0-3 fanout  # BPF 캐시 미스만 NFQUEUE로 전달

# 4. 캐시 통계 확인
bpftool map dump pinned /sys/fs/bpf/dpi_bypass/stats
# key: 0  value (per-cpu): [12345, 12340, ...]  ← cache_hit
# key: 1  value (per-cpu): [567, 570, ...]      ← cache_miss

NGFW 시스템 서비스 구성

Linux NGFW를 프로덕션에 배포하기 위한 systemd 서비스 구성입니다:

# /etc/systemd/system/ngfw-nftables.service
[Unit]
Description=NGFW nftables Rules
Before=suricata.service
After=network-pre.target

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/sbin/nft -f /etc/nftables-ngfw.conf
ExecReload=/usr/sbin/nft -f /etc/nftables-ngfw.conf
ExecStop=/usr/sbin/nft flush ruleset

[Install]
WantedBy=multi-user.target
# /etc/systemd/system/ngfw-eswitch.service
[Unit]
Description=NGFW eSwitch TC flower Rules
After=ngfw-nftables.service
Requires=ngfw-nftables.service

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStartPre=/usr/bin/devlink dev eswitch set pci/0000:03:00.0 mode switchdev
ExecStartPre=/usr/bin/sleep 2
ExecStartPre=/usr/sbin/ethtool -K eth0_rep0 hw-tc-offload on
ExecStart=/usr/local/bin/ngfw-tc-rules.sh
ExecStop=/usr/sbin/tc filter del dev eth0_rep0 ingress

[Install]
WantedBy=multi-user.target
# /etc/systemd/system/ngfw-tuning.service
[Unit]
Description=NGFW Kernel Tuning
Before=ngfw-nftables.service

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/bin/sh -c '\
  sysctl -w net.netfilter.nf_conntrack_max=2000000; \
  sysctl -w net.netfilter.nf_conntrack_buckets=500000; \
  sysctl -w net.netfilter.nf_conntrack_tcp_timeout_established=300; \
  sysctl -w net.netfilter.nf_flowtable_tcp_timeout=300; \
  sysctl -w net.core.optmem_max=81920; \
  sysctl -w net.core.netdev_budget=600; \
  sysctl -w net.core.netdev_max_backlog=10000; \
  sysctl -w net.ipv4.ip_forward=1; \
  ethtool -C eth0 rx-usecs 50 rx-frames 64 adaptive-rx on; \
  ethtool -G eth0 rx 4096 tx 4096; \
  /usr/local/bin/set_irq_affinity.sh 0-3 eth0 \
'

[Install]
WantedBy=multi-user.target
# 서비스 활성화 및 시작 순서
systemctl enable ngfw-tuning ngfw-nftables ngfw-eswitch
systemctl enable suricata conntrackd

# 시작 순서: tuning → nftables → eswitch → suricata → conntrackd
systemctl start ngfw-tuning
systemctl start ngfw-nftables
systemctl start ngfw-eswitch
systemctl start suricata
systemctl start conntrackd

# 상태 확인
systemctl status ngfw-nftables ngfw-eswitch suricata conntrackd

멀티 테이블 파이프라인

TC chain을 사용하여 다단계 매칭 파이프라인을 구성합니다:

# chain 0: conntrack 추적 및 분기
tc filter add dev eth0_rep0 ingress chain 0 prio 1 \
  protocol ip flower ct_state -trk \
  action ct zone 1 pipe \
  action goto chain 1

# chain 1: 상태 기반 분기
tc filter add dev eth0_rep0 ingress chain 1 prio 1 \
  protocol ip flower ct_state +trk+est \
  action goto chain 2  # ESTABLISHED → Fast Path chain

tc filter add dev eth0_rep0 ingress chain 1 prio 2 \
  protocol ip flower ct_state +trk+new \
  action pass  # NEW → CPU (Slow Path)

# chain 2: Fast Path (NAT + forward)
tc filter add dev eth0_rep0 ingress chain 2 prio 1 \
  protocol ip flower \
  action ct zone 1 nat \
  action mirred egress redirect dev eth1_rep0

XDP DDoS Pre-filter 패턴

NGFW의 가장 앞단에서 XDP를 사용하여 DDoS/스캔 트래픽을 CPU 부하 없이 처리합니다. 이 패턴은 conntrack 테이블 포화를 방지하는 핵심 방어선입니다.

/* xdp_ddos_filter.c — NGFW XDP DDoS Pre-filter */
#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <linux/tcp.h>

/* 차단 IP 세트 (관리 도구에서 동적 업데이트) */
struct {
    __uint(type, BPF_MAP_TYPE_LPM_TRIE);
    __uint(max_entries, 100000);
    __uint(map_flags, BPF_F_NO_PREALLOC);
    __type(key, struct lpm_key);
    __type(value, __u32);
} blocklist SEC(".maps");

/* SYN flood 속도 제한 (per-source IP) */
struct {
    __uint(type, BPF_MAP_TYPE_LRU_HASH);
    __uint(max_entries, 1000000);
    __type(key, __be32);      /* source IP */
    __type(value, __u64);     /* timestamp + count */
} syn_rate SEC(".maps");

SEC("xdp")
int xdp_ddos_filter(struct xdp_md *ctx)
{
    void *data = (void *)(long)ctx->data;
    void *data_end = (void *)(long)ctx->data_end;

    struct ethhdr *eth = data;
    if ((void *)(eth + 1) > data_end) return XDP_DROP;
    if (eth->h_proto != __constant_htons(ETH_P_IP)) return XDP_PASS;

    struct iphdr *iph = (void *)(eth + 1);
    if ((void *)(iph + 1) > data_end) return XDP_DROP;

    /* 1. IP 차단 리스트 확인 (LPM Trie: CIDR 매칭) */
    struct lpm_key key = { .prefixlen = 32, .addr = iph->saddr };
    if (bpf_map_lookup_elem(&blocklist, &key))
        return XDP_DROP;  /* 차단 IP → 즉시 DROP (최소 지연) */

    /* 2. SYN flood 감지 (TCP SYN 패킷만) */
    if (iph->protocol == IPPROTO_TCP) {
        struct tcphdr *tcph = (void *)iph + (iph->ihl * 4);
        if ((void *)(tcph + 1) > data_end) return XDP_DROP;

        if (tcph->syn && !tcph->ack) {
            /* SYN 속도 제한: 소스 IP당 초당 100 SYN */
            __u64 *rate = bpf_map_lookup_elem(&syn_rate, &iph->saddr);
            __u64 now = bpf_ktime_get_ns();
            if (rate) {
                __u64 last = *rate & 0xFFFFFFFF00000000ULL;
                __u32 count = *rate & 0xFFFFFFFF;
                if (now - last < 1000000000ULL) { /* 1초 이내 */
                    if (count > 100)
                        return XDP_DROP; /* 속도 초과 → DROP */
                    *rate = last | (count + 1);
                } else {
                    *rate = (now & 0xFFFFFFFF00000000ULL) | 1;
                }
            } else {
                __u64 val = (now & 0xFFFFFFFF00000000ULL) | 1;
                bpf_map_update_elem(&syn_rate, &iph->saddr, &val, BPF_ANY);
            }
        }
    }

    return XDP_PASS;  /* 정상 → 커널 네트워크 스택으로 전달 */
}

char _license[] SEC("license") = "GPL";
코드 설명
  • 9-14행 (LPM_TRIE) LPM(Longest Prefix Match) Trie는 CIDR 블록(예: 192.168.0.0/16)을 효율적으로 매칭합니다. IP 차단 리스트를 bpftool map으로 동적 업데이트할 수 있습니다.
  • 40-42행 차단 IP가 발견되면 XDP_DROP을 반환하여 NIC 드라이버 수준에서 즉시 패킷을 폐기합니다. skb 할당도 발생하지 않으므로 최소한의 CPU 비용만 소모합니다.
  • 47-66행 (SYN 속도 제한) 소스 IP당 초당 SYN 패킷 수를 추적합니다. 100 SYN/초를 초과하면 DROP합니다. 이를 통해 SYN flood가 conntrack 테이블에 도달하기 전에 차단합니다.
# XDP DDoS 필터 운영 명령
# 프로그램 로드
ip link set dev eth0 xdp obj xdp_ddos_filter.o sec xdp

# IP 차단 리스트 관리 (bpftool)
bpftool map update pinned /sys/fs/bpf/xdp_ddos/blocklist \
  key hex 20 00 00 00 c0 a8 64 00 \
  value hex 01 00 00 00  # 192.168.100.0/32 차단

# SYN flood 상위 공격자 확인
bpftool map dump pinned /sys/fs/bpf/xdp_ddos/syn_rate | \
  sort -t: -k2 -rn | head -20

# XDP 통계 확인
ip -s link show dev eth0 | grep -A5 xdp

conntrackd HA (세션 동기화)

Active-Standby NGFW 클러스터에서 failover 시 기존 세션을 유지하려면 conntrack 테이블을 실시간 동기화해야 합니다:

# /etc/conntrackd/conntrackd.conf (NGFW HA)
Sync {
    Mode FTFW {          # Fault Tolerant (ack 기반 신뢰성)
        DisableExternalCache Off
        CommitTimeout 1800
        PurgeTimeout 5
    }

    # 동기화 프로토콜
    Multicast {
        IPv4_address 225.0.0.50
        Group 3780
        IPv4_interface 192.168.100.1  # HA 전용 인터페이스
        Interface eth2
        SndSocketBuffer 1249280
        RcvSocketBuffer 1249280
        Checksum on
    }
}

General {
    Nice -20                  # 높은 우선순위
    HashSize 32768
    HashLimit 131072          # 최대 동기화 엔트리

    # 동기화 대상 필터 (flowtable 오프로드 세션 포함)
    Filter From Kernelspace {
        Protocol Accept {
            TCP
            UDP
        }
        Address Ignore {
            IPv4_address 127.0.0.1   # 로컬 제외
            IPv4_address 224.0.0.0/4 # 멀티캐스트 제외
        }
    }
}
flowtable과 conntrackd의 상호작용: flowtable에 오프로드된 세션은 conntrack 테이블에도 남아있으므로 conntrackd가 정상적으로 동기화합니다. 다만 HW offload된 세션의 패킷/바이트 카운터는 GC 주기(1초)마다만 SW에 반영되므로, failover 시점에 최대 1초분의 통계가 손실될 수 있습니다. HW FDB 규칙은 Standby 노드의 NIC에 새로 설치해야 합니다.
# HA failover 스크립트 (Keepalived notify 스크립트)
#!/bin/bash
# /usr/local/bin/ngfw-failover.sh

case "$1" in
  "MASTER")
    # Active 전환: flowtable HW offload 재등록
    conntrackd -c           # 외부 캐시 → 커널 동기화
    conntrackd -k           # 커널 conntrack 동기화 요청

    # eSwitch FDB 규칙 재설치 (기존 EST 세션)
    nft flush flowtable inet ngfw ft
    nft add flowtable inet ngfw ft \
      { hook ingress priority 0\; devices = { eth0, eth1 }\; flags offload\; }

    # 기존 conntrack의 EST 세션을 flowtable에 재등록
    # (자동으로 다음 패킷이 도착하면 flow add @ft에 의해 등록됨)
    ;;
  "BACKUP")
    # Standby 전환: HW offload 정리
    conntrackd -f           # 외부 캐시 플러시
    nft flush flowtable inet ngfw ft
    ;;
esac
nftables + flowtable + TC flower 연동 패킷 RX TC flower (eSwitch) ct_state +est → HW fwd ct_state +new → pass CPU HW Fast Path nftables (CPU) NEW → ACL 검사 EST → flow add @ft Slow Path NFQUEUE Suricata DPI/IPS flowtable SW/HW offload TX HW match → 직접 전달 miss/NEW QUEUE offload 등록 ALLOW → offload Fast Path (HW/SW) Slow Path 오프로드 등록

보안 vs 오프로드 트레이드오프

트레이드오프 모델

NGFW에서 가장 중요한 설계 결정은 어느 수준의 보안 검사를 활성화하고, 어느 지점에서 오프로드할 것인가입니다. 이 결정이 전체 시스템의 처리량을 결정합니다.

보안 수준 vs 오프로드 비율 vs 처리량 보안 수준 → L4 ACL +IPS +App-ID +SSL Insp. +Full DPI+DLP 0% 50% 100% 오프로드율 95% 80% 75% 40% 20% ~95G ~76G ~72G ~42G ~24G 오프로드 비율 실효 처리량 (100G NIC)

보안 기능을 추가할수록 오프로드 가능한 트래픽 비율이 줄어듭니다. 대략적인 모델:

오프로드 비율 측정

# conntrack 전체 세션 수
conntrack -C

# flowtable 오프로드된 세션 수
conntrack -L -p tcp --state ESTABLISHED | wc -l

# flowtable 통계 (nftables)
nft list flowtable inet ngfw ft

# ethtool HW offload 카운터
ethtool -S eth0 | grep offload

# 오프로드 비율 계산
# ratio = (flowtable_sessions / total_established) * 100
보안 기능오프로드 영향오프로드 비율비고
L3/L4 Stateful ACL영향 없음~95%HW ct_state 매칭
IPS (시그니처 검사)첫 패킷만 영향~80%ALLOW 후 오프로드
App-ID (L7 분류)초기 3~10 패킷~75%분류 완료 후 오프로드
SSL Inspection전체 세션 영향~40%복호화 세션은 CPU
URL FilteringHTTP/DNS만 영향~70%첫 요청 검사 후 오프로드
DLP (Data Loss Prevention)전체 페이로드 검사~30%대부분 CPU 처리
주기적 재검사 (Sampling)오프로드 해제 주기~60%바이트 임계치 기반

위협 회피 대응

공격자가 오프로드 메커니즘을 악용하여 검사를 회피할 수 있는 시나리오와 대응:

회피 기법설명대응
첫 패킷 위장정상 프로토콜로 시작 후 ESTABLISHED에서 악성 페이로드 전송주기적 샘플링 (N번째 패킷마다 DPI 재검사)
프로토콜 변경HTTP로 시작 후 터널링으로 프로토콜 변경바이트 카운터 감시, 임계치 초과 시 재분류
저속 공격 (Low & Slow)오프로드 유지하며 느린 속도로 데이터 유출세션 지속 시간 제한, 바이트 감사
단편화 공격IP fragment로 DPI 우회 시도단편화 패킷은 Exception Path에서 재조립 후 검사
세션 하이재킹오프로드된 세션의 5-tuple을 스푸핑하여 주입TCP 시퀀스 번호 검증 (SW 샘플링)
암호화 터널링정상 HTTPS 내부에 VPN 터널 생성바이트 패턴 분석, JA3 핑거프린트

주기적 샘플링 구현

오프로드된 세션에 대한 주기적 재검사(sampling)는 위협 회피를 방지하는 핵심 메커니즘입니다:

# nftables에서 바이트 기반 샘플링 구현
table inet ngfw_sampling {
    chain forward {
        type filter hook forward priority -50; policy accept;

        # 오프로드된 세션의 패킷이 N번째마다 CPU로 복귀
        # (flowtable timeout이 만료되면 자동으로 이 체인을 거침)

        # 10MB 이상 전송된 세션 → DPI 재검사
        ct state established \
            ct bytes gt 10485760 \
            ct mark != 0xfe \
            queue num 4 bypass  # 재검사용 별도 큐

        # 1시간 이상 지속된 세션 → 재분류
        ct state established \
            ct original packets gt 100000 \
            ct mark != 0xfe \
            queue num 4 bypass

        # 재검사 완료 마킹 (중복 방지)
        ct state established ct mark 0xfe accept
    }
}
샘플링과 성능의 균형: 샘플링 주기가 너무 짧으면(예: 1MB마다) Fast Path의 이점이 사라집니다. 권장 설정은 세션당 10~100MB 또는 5~60분 주기입니다. 고위험 Zone(Untrust → Trust)에서는 짧은 주기, 저위험 Zone(Trust → Trust)에서는 긴 주기 또는 비활성화합니다.

정책 설계 가이드

Zone-based 오프로드 정책 매트릭스

네트워크 Zone 간 트래픽 특성에 따라 오프로드 수준을 차별화합니다:

소스 Zone목적지 Zone검사 수준오프로드 정책예상 오프로드율
Trust (내부)Trust (내부)L4 ACL만즉시 HW offload95%+
TrustDMZL4 ACL + App-ID분류 후 SW offload80%
TrustUntrust (인터넷)Full DPI + IPSDPI 완료 후 offload60~70%
UntrustDMZ (서버)Full DPI + IPS + WAFDPI 완료 후 offload50~60%
UntrustTrustFull DPI + IPS + DLPDPI 완료 후 offload (제한적)40~50%
GuestUntrustURL Filter + IPSURL 검사 후 offload65%
IoTCloudApp-ID + 주기적 재검사조건부 offload (샘플링)70%

nftables Zone 구현 예시

# Zone-based NGFW nftables 설정
table inet ngfw_zones {
    # Zone별 flowtable (오프로드 수준 차별화)
    flowtable ft_trust {
        hook ingress priority 0
        devices = { eth0 }    # 내부 네트워크
        flags offload          # HW offload 활성화
    }

    flowtable ft_untrust {
        hook ingress priority 0
        devices = { eth1 }    # 인터넷 연결
        # flags offload 미설정 → SW offload만
    }

    chain forward {
        type filter hook forward priority 0; policy drop;
        ct state invalid drop

        # Trust → Trust: 즉시 HW offload (DPI 건너뜀)
        iifname "eth0" oifname "eth0" \
            ct state established,related \
            flow add @ft_trust accept

        # Trust → Untrust: DPI 검사 후 SW offload
        iifname "eth0" oifname "eth1" \
            ct state established,related \
            flow add @ft_untrust accept

        # Untrust → DMZ: 반드시 DPI 검사
        iifname "eth1" oifname "eth2" \
            ct state new \
            queue num 0-3 fanout

        # Untrust → DMZ: DPI 완료 후에만 offload
        iifname "eth1" oifname "eth2" \
            ct state established ct mark 0xff \
            flow add @ft_untrust accept
    }
}

오프로드 비율 정량 모델

NGFW의 전체 처리량은 Fast Path와 Slow Path의 가중 합으로 모델링됩니다:

처리량 공식: Total_Throughput = (Offload_Ratio × Fast_Path_BW) + ((1 - Offload_Ratio) × Slow_Path_BW)

예시: 오프로드 70%, Fast Path 100Gbps, Slow Path 5Gbps인 경우:
Total = (0.70 × 100) + (0.30 × 5) = 70 + 1.5 = 71.5 Gbps

오프로드 비율이 10% 증가하면(70% → 80%):
Total = (0.80 × 100) + (0.20 × 5) = 80 + 1.0 = 81.0 Gbps (+13%)

이 모델에서 Slow Path 처리량(DPI 성능)보다 오프로드 비율을 높이는 것이 전체 성능에 훨씬 큰 영향을 미칩니다. 오프로드 비율 1% 향상은 Slow Path 성능 20% 향상과 동등한 효과를 가집니다.

보안 수준별 성능 영향 시뮬레이션

보안 프로파일활성 기능오프로드율100G NIC 실효 처리량CPS 영향
MinimalL4 Stateful ACL95%~95 Gbps200K+ CPS
Standard+ IPS + App-ID75%~76 Gbps100K CPS
Enhanced+ SSL Inspection45%~47 Gbps30K CPS
Maximum+ DLP + AV + 주기적 재검사20%~24 Gbps10K CPS

성능 튜닝과 벤치마크

RFC 9411 메트릭

NGFW 성능 측정에 사용되는 주요 메트릭 (RFC 9411 — Benchmarking Methodology for Network Security Device Performance):

IRQ Affinity / NUMA 최적화

NGFW에서 CPU 코어 할당은 성능에 결정적입니다. NIC IRQ, Netfilter, DPI 엔진을 올바른 NUMA 노드와 코어에 배치해야 합니다:

# NUMA 토폴로지 확인
lscpu | grep -E "NUMA|Socket|Core"
# NUMA node0 CPU(s): 0-7    (NIC PCI 슬롯과 같은 노드)
# NUMA node1 CPU(s): 8-15

# NIC의 NUMA 노드 확인
cat /sys/class/net/eth0/device/numa_node
# 0  ← NIC이 NUMA 노드 0에 연결

# IRQ Affinity: NIC IRQ를 같은 NUMA 노드 코어에 바인딩
# eth0의 IRQ 번호 확인
grep eth0 /proc/interrupts | awk '{print $1}' | tr -d ':'
# IRQ 42, 43, 44, 45 (4개 큐)

# 각 IRQ를 NUMA node0 코어에 1:1 바인딩
echo 1 > /proc/irq/42/smp_affinity   # CPU 0
echo 2 > /proc/irq/43/smp_affinity   # CPU 1
echo 4 > /proc/irq/44/smp_affinity   # CPU 2
echo 8 > /proc/irq/45/smp_affinity   # CPU 3

# 또는: mlx5/ice 드라이버의 set_irq_affinity 스크립트
/usr/sbin/set_irq_affinity_cpulist.sh 0-3 eth0

NGFW 코어 배치 가이드

역할코어 할당NUMA 요구이유
NIC IRQ (NAPI poll)0~3NIC과 같은 노드DMA 버퍼가 로컬 메모리에 위치
XDP pre-filter0~3 (IRQ와 동일)NIC과 같은 노드XDP는 NAPI 컨텍스트에서 실행
conntrack / nftables0~3 (softirq)NIC과 같은 노드skb가 생성된 코어에서 처리
Suricata DPI 워커4~11NIC과 같은 노드 우선NFQUEUE 패킷 접근 locality
conntrackd (HA)12어느 노드든netlink 이벤트 수신
관리 (SSH, 모니터링)13~15어느 노드든데이터 플레인과 분리

CPS/처리량/지연 최적화

# ===== 1. conntrack 최적화 =====
sysctl -w net.netfilter.nf_conntrack_max=2000000
sysctl -w net.netfilter.nf_conntrack_buckets=500000
# 해시 충돌 최소화: buckets ≥ max/4

# conntrack 타임아웃 단축 (빠른 리소스 회수)
sysctl -w net.netfilter.nf_conntrack_tcp_timeout_established=300
sysctl -w net.netfilter.nf_conntrack_tcp_timeout_time_wait=30
sysctl -w net.netfilter.nf_conntrack_tcp_timeout_close_wait=30

# ===== 2. flowtable 최적화 =====
sysctl -w net.netfilter.nf_flowtable_tcp_timeout=300
sysctl -w net.netfilter.nf_flowtable_udp_timeout=30

# ===== 3. NFQUEUE 최적화 =====
sysctl -w net.core.optmem_max=81920
# NFQUEUE 소켓 버퍼 증가 → 버스트 트래픽 대응
sysctl -w net.core.rmem_max=16777216
sysctl -w net.core.wmem_max=16777216

# ===== 4. NIC RSS/ethtool 최적화 =====
# RSS 큐를 IRQ affinity 코어 수에 맞춤
ethtool -L eth0 combined 4

# Interrupt Coalescing (배치 처리로 IRQ 빈도 감소)
ethtool -C eth0 rx-usecs 50 rx-frames 64 \
  tx-usecs 50 tx-frames 64 adaptive-rx on

# Ring Buffer 크기 증가 (버스트 흡수)
ethtool -G eth0 rx 4096 tx 4096

# HW offload 기능 활성화
ethtool -K eth0 hw-tc-offload on
ethtool -K eth0 ntuple on

# ===== 5. XDP DDoS pre-filter =====
ip link set dev eth0 xdp obj xdp_ddos_filter.o sec xdp

# ===== 6. eSwitch 최적화 =====
devlink dev eswitch set pci/0000:03:00.0 mode switchdev
devlink dev eswitch set pci/0000:03:00.0 inline-mode transport

# ===== 7. 커널 파라미터 =====
sysctl -w net.core.netdev_budget=600
sysctl -w net.core.netdev_max_backlog=10000
sysctl -w net.ipv4.ip_forward=1

# NAPI busy_poll (지연 최소화)
sysctl -w net.core.busy_read=50
sysctl -w net.core.busy_poll=50

ethtool Interrupt Coalescing 상세

NGFW에서 Interrupt Coalescing은 처리량과 지연 사이의 균형을 결정합니다:

설정rx-usecsrx-frames적합 시나리오영향
Low Latency108VoIP, 금융 트래픽IRQ 빈도 높음, CPU 사용률 증가
Balanced5064일반 NGFW적절한 지연/처리량 균형
High Throughput100128대용량 파일 전송 위주지연 증가, 처리량 최대화
Adaptiveadaptive-rx on-트래픽 변동이 큰 환경NIC이 트래픽에 따라 자동 조절
단계별 처리량 상한선 (Ceiling) 및 병목 0 Gbps 25 Gbps 50 Gbps 75 Gbps 100 Gbps 100G NIC RX 80G XDP 40G conntrack 40G flowtable 10G NFQUEUE 5G DPI/IPS BOTTLENECK 100G TX DPI가 병목 → ESTABLISHED 세션을 Fast Path로 오프로드하여 DPI 부하 최소화

벤치마크 방법론 (RFC 9411)

NGFW 성능을 올바르게 측정하기 위한 RFC 9411 기반 방법론입니다:

테스트 유형측정 대상트래픽 프로파일도구
CPS 측정초당 새 TCP 연결1-packet HTTP 요청 (MSS 크기)TRex, iperf3 --parallel
처리량 (Throughput)Mbps/GbpsESTABLISHED 대량 UDP/TCP 스트림TRex, DPDK pktgen
Latency 측정패킷 지연고정 크기 패킷 스트림moongen, TRex
NGFW 처리량DPI 활성 시 처리량HTTP/HTTPS 혼합 실제 트래픽TRex ASTF (app-layer)
SSL 처리량SSL 인스펙션 시 처리량TLS 1.2/1.3 혼합TRex + nginx SSL 백엔드
혼합 부하CPS + 처리량 동시실제 트래픽 PCAP 재생TRex 리플레이 모드
# TRex를 사용한 NGFW 벤치마크 예시

# 1. CPS 측정 (TCP SYN flood → 세션 수립률 측정)
./t-rex-64 -f cap2/http_simple.yaml \
  --duration 60 --multiplier 100000 \
  --latency 1000

# 2. 처리량 측정 (기존 세션에 대량 트래픽)
./t-rex-64 -f cap2/imix_64_594_1518.yaml \
  --duration 120 --multiplier 1 \
  --astf  # Application-layer TRex

# 3. 결과에서 Fast Path / Slow Path 분리 확인
# TRex 실행 중 DUT에서:
conntrack -C                          # 전체 세션 수
conntrack -L --status OFFLOAD | wc -l # 오프로드 세션
ethtool -S eth0 | grep tx_packets_phy # HW 처리 패킷
메트릭HW Fast PathSW Fast PathSlow Path (DPI)
처리량 (64B 패킷)148.8 Mpps (100GbE)10~20 Mpps1~3 Mpps
처리량 (1518B 패킷)8.1 Mpps (100GbE)3~5 Mpps0.5~1 Mpps
지연 (Latency)<5 us10~50 us100~500 us
CPS (초당 새 연결)N/A (오프로드만)N/A50K~200K/core
동시 세션NIC 의존 (1M~)conntrack_maxconntrack_max
CPU 코어 사용01~2 코어4~16 코어

패킷 크기별 성능 특성

NGFW 성능은 패킷 크기에 크게 의존합니다. 작은 패킷은 PPS(초당 패킷)에 의해, 큰 패킷은 BPS(초당 비트)에 의해 제한됩니다:

패킷 크기HW offload PPSHW offload BPSSW flowtable PPSDPI (Slow Path) PPS주요 트래픽
64B148.8 Mpps76.2 Gbps15 Mpps2 MppsTCP ACK, SYN
128B84.5 Mpps86.5 Gbps12 Mpps1.5 MppsVoIP (G.711)
256B45.3 Mpps92.8 Gbps8 Mpps1 MppsDNS 응답
512B23.5 Mpps96.3 Gbps5 Mpps0.8 Mpps웹 요청
1024B12.0 Mpps98.3 Gbps3.5 Mpps0.6 Mpps파일 전송
1518B (MTU)8.1 Mpps98.4 Gbps3 Mpps0.5 Mpps대용량 전송
IMIX (혼합)~30 Mpps~80 Gbps~8 Mpps~1 Mpps실제 트래픽

IMIX(Internet Mix)는 실제 인터넷 트래픽을 모사한 혼합 패킷 크기 분포입니다 (7:4:1 비율로 64B:594B:1518B). NGFW 벤치마크에서는 IMIX 또는 HTTP 애플리케이션 트래픽을 사용하는 것이 실제 환경에 가장 가깝습니다.

성능 최적화 체크리스트

NGFW 성능 최적화 우선순위 (효과 순):
  1. 오프로드 비율 극대화 — flowtable + HW offload로 EST 세션 비율 70%+ 달성 (가장 큰 효과)
  2. NFQUEUE 최적화 — fanout 분산 + batchcount + fail-open으로 DPI 병목 해소
  3. IRQ/NUMA 최적화 — NIC IRQ를 같은 NUMA 노드 코어에 바인딩
  4. conntrack 튜닝 — 해시 버킷 최적화 + 타임아웃 단축으로 테이블 효율 향상
  5. Interrupt Coalescing — ethtool -C로 IRQ 빈도 최적화
  6. XDP pre-filter — DDoS/스캔 트래픽을 가장 빠른 경로에서 DROP
  7. DPI 시그니처 최적화 — 불필요한 시그니처 비활성화로 Suricata 부하 감소

진단과 모니터링

모니터링 아키텍처

NGFW의 각 구성 요소에서 메트릭을 수집하여 중앙 모니터링 시스템으로 전달하는 아키텍처입니다:

NGFW 모니터링 아키텍처 데이터 소스 ethtool -S (NIC HW) conntrack -L (세션) nft list (flowtable) tc -s filter (TC flower) Suricata stats.log bpftrace (커널 이벤트) 메트릭 수집기 node_exporter + textfile collector ngfw-metrics.sh (1초 주기 크론) Prometheus TSDB 저장 Alertmanager 임계치 알림 Grafana 대시보드 알림 조건: conntrack > 80%, NFQUEUE drops > 0 offload ratio < 50%, CPS > threshold

핵심 알림 규칙

알림조건심각도대응
conntrack 포화entries/max > 0.8Warningnf_conntrack_max 증가 또는 타임아웃 단축
conntrack 가득 참entries/max > 0.95Critical즉시 타임아웃 단축 + early_drop 활성화
NFQUEUE 드롭drops/s > 0WarningSuricata 워커 증가 또는 batchcount 조정
오프로드 비율 저하ratio < 0.5Warningflowtable 설정 확인, ALG 세션 분석
HW offload 실패devlink trap 증가InfoNIC 펌웨어/드라이버 업데이트 확인
DPI 엔진 과부하Suricata kernel_drops > 0Critical워커 스레드 추가, 규칙 최적화

flowtable 진단

# flowtable 오프로드 상태 확인
nft list flowtable inet ngfw ft

# conntrack 테이블에서 offloaded 세션 확인
conntrack -L --status OFFLOAD

# conntrack 이벤트 실시간 모니터
conntrack -E -e NEW,DESTROY

# TC flower 오프로드 규칙 통계
tc -s filter show dev eth0_rep0 ingress

# eSwitch FDB 규칙 확인
tc -s filter show dev eth0_rep0 ingress chain 0

# ethtool 오프로드 카운터
ethtool -S eth0 | grep -E '(offload|flower|ct)'

# devlink 트랩 (오프로드 실패 원인)
devlink trap show pci/0000:03:00.0

bpftrace / perf 진단

# flowtable offload 이벤트 추적
bpftrace -e 'kprobe:nf_flow_offload_add { printf("flow offload add: %s\n", comm); }'

# conntrack NEW 이벤트 빈도 측정 (CPS 추정)
bpftrace -e 'kprobe:__nf_conntrack_alloc { @cps = count(); }
             interval:s:1 { printf("CPS: %d\n", @cps); clear(@cps); }'

# NFQUEUE 지연 측정
perf probe -a 'nf_queue_entry_get_refs'
perf stat -e probe:nf_queue_entry_get_refs -a sleep 10

# Netfilter 훅 처리 시간 분포
bpftrace -e 'kprobe:nf_hook_slow { @start[tid] = nsecs; }
             kretprobe:nf_hook_slow /@start[tid]/ {
               @latency_us = hist((nsecs - @start[tid]) / 1000);
               delete(@start[tid]);
             }'

Prometheus 메트릭 수집

NGFW의 성능을 지속적으로 모니터링하기 위한 주요 메트릭과 수집 방법입니다:

#!/bin/bash
# /usr/local/bin/ngfw-metrics.sh
# node_exporter textfile collector용 메트릭 생성

METRICS_DIR="/var/lib/node_exporter/textfile_collector"

{
  # conntrack 메트릭
  ct_count=$(conntrack -C 2>/dev/null || echo 0)
  ct_max=$(sysctl -n net.netfilter.nf_conntrack_max)
  echo "ngfw_conntrack_entries $ct_count"
  echo "ngfw_conntrack_max $ct_max"
  echo "ngfw_conntrack_utilization $(echo "scale=4; $ct_count/$ct_max" | bc)"

  # flowtable 오프로드 세션 수
  offloaded=$(conntrack -L --status OFFLOAD 2>/dev/null | wc -l)
  echo "ngfw_flowtable_offloaded_sessions $offloaded"

  # 오프로드 비율
  established=$(conntrack -L -p tcp --state ESTABLISHED 2>/dev/null | wc -l)
  if [ "$established" -gt 0 ]; then
    ratio=$(echo "scale=4; $offloaded/$established" | bc)
    echo "ngfw_offload_ratio $ratio"
  fi

  # NIC HW offload 카운터 (mlx5)
  for stat in tx_packets_phy rx_packets_phy \
              rx_vport_rdma_unicast_packets \
              tx_vport_rdma_unicast_packets; do
    val=$(ethtool -S eth0 2>/dev/null | grep "$stat" | awk '{print $2}')
    [ -n "$val" ] && echo "ngfw_nic_${stat} $val"
  done

  # TC flower 규칙 수
  tc_rules=$(tc -s filter show dev eth0_rep0 ingress 2>/dev/null | grep -c "filter")
  echo "ngfw_tc_flower_rules $tc_rules"

  # NFQUEUE 드롭 (과부하 지표)
  nfq_drops=$(cat /proc/net/netfilter/nfnetlink_queue 2>/dev/null | \
    awk '{sum+=$5} END {print sum+0}')
  echo "ngfw_nfqueue_drops $nfq_drops"

} > "$METRICS_DIR/ngfw.prom.$$"
mv "$METRICS_DIR/ngfw.prom.$$" "$METRICS_DIR/ngfw.prom"

실시간 성능 대시보드 스크립트

#!/bin/bash
# /usr/local/bin/ngfw-dashboard.sh — 실시간 NGFW 상태
watch -n 1 '
echo "===== NGFW Status ====="
echo ""
echo "--- Conntrack ---"
printf "  Entries:    %s / %s\n" $(conntrack -C) $(sysctl -n net.netfilter.nf_conntrack_max)
printf "  Offloaded:  %s\n" $(conntrack -L --status OFFLOAD 2>/dev/null | wc -l)
echo ""
echo "--- Flowtable ---"
nft list flowtable inet ngfw ft 2>/dev/null | tail -5
echo ""
echo "--- TC flower (HW rules) ---"
tc -s filter show dev eth0_rep0 ingress 2>/dev/null | \
  grep -E "(filter|action|Sent)" | head -20
echo ""
echo "--- NIC Offload Stats ---"
ethtool -S eth0 2>/dev/null | grep -E "(offload|flower|ct)" | head -10
echo ""
echo "--- NFQUEUE ---"
cat /proc/net/netfilter/nfnetlink_queue 2>/dev/null
'

트러블슈팅 체크리스트

오프로드가 작동하지 않을 때 점검 사항:
  1. eSwitch 모드 확인: devlink dev eswitch showmode switchdev인가?
  2. hw-tc-offload 활성화: ethtool -k eth0 | grep hw-tc-offloadon인가?
  3. flowtable flags offload: nftables flowtable에 flags offload가 설정되었는가?
  4. NIC 드라이버 지원: mlx5/ice 등 TC flower ct offload를 지원하는 드라이버인가?
  5. conntrack 상태: conntrack -L --status OFFLOAD에 엔트리가 있는가?
  6. devlink trap 확인: HW offload 실패 이유가 trap으로 보고되는가?
  7. 커널 버전: flowtable HW offload는 커널 5.13+, CT offload는 5.7+ 필요
  8. 펌웨어 버전: NIC 펌웨어가 CT offload를 지원하는 버전인가?

자주 발생하는 문제와 해결

증상원인해결 방법
오프로드 비율이 0% flowtable에 flags offload 미설정 nft list flowtable에서 flags 확인 → offload 추가
HW offload는 되지만 NAT가 안 됨 TC flower에서 ct nat action 누락 TC 규칙에 action ct zone 1 nat 추가
conntrack 테이블이 빠르게 가득 참 DDoS/스캔으로 인한 NEW 세션 폭주 XDP pre-filter 활성화 + conntrack 타임아웃 단축
NFQUEUE에서 패킷 드롭 Suricata 처리 속도 부족 워커 스레드 증가, batchcount 최적화, bypass 활성화
flowtable 세션이 빠르게 만료 nf_flowtable_tcp_timeout이 너무 짧음 300초 이상으로 증가 (conntrack EST 타임아웃과 동기화)
특정 세션이 오프로드되지 않음 ALG helper가 할당된 세션 conntrack -L | grep helper로 확인 → 정상 동작 (ALG는 오프로드 불가)
eSwitch 전환 후 네트워크 끊김 switchdev 모드 전환 시 VF 재설정 필요 VF representor 재생성 + IP 재설정
TC flower 규칙이 HW에 설치되지 않음 NIC가 해당 매칭 조건 미지원 dmesg | grep tc로 에러 확인 → SW 폴백 또는 규칙 단순화

디버깅용 커널 트레이싱

# flowtable 이벤트 전체 추적 (디버깅 시에만 사용)
# 주의: 프로덕션에서는 성능 영향 있음

# 1. ftrace로 flowtable 함수 추적
echo 'nf_flow_offload*' > /sys/kernel/debug/tracing/set_ftrace_filter
echo function > /sys/kernel/debug/tracing/current_tracer
echo 1 > /sys/kernel/debug/tracing/tracing_on
cat /sys/kernel/debug/tracing/trace_pipe | head -100
echo 0 > /sys/kernel/debug/tracing/tracing_on

# 2. perf로 Netfilter 훅 프로파일링
perf top -g -e cycles:k --filter='nf_*'

# 3. bpftrace: flowtable HW offload 성공/실패 추적
bpftrace -e '
kprobe:nf_flow_offload_hw_add {
    printf("HW add: ct=%p\n", arg2);
}
kretprobe:nf_flow_offload_hw_add /retval != 0/ {
    printf("HW add FAILED: err=%d\n", retval);
}
kprobe:nf_flow_offload_hw_del {
    printf("HW del: flow=%p\n", arg1);
}'

# 4. conntrack 이벤트와 flowtable 이벤트 상관관계 추적
bpftrace -e '
kprobe:nf_conntrack_in {
    @ct_in = count();
}
kprobe:nf_flow_offload_ip_hook {
    @flow_hit = count();
}
interval:s:5 {
    printf("conntrack_in: %d, flow_hit: %d, ratio: ",
           @ct_in, @flow_hit);
    printf("%d%%\n",
           @flow_hit * 100 / (@ct_in + @flow_hit + 1));
    clear(@ct_in); clear(@flow_hit);
}'

커널 설정

커널 버전 요구사항

NGFW HW offload의 각 기능에 필요한 최소 커널 버전입니다:

기능최소 커널권장 커널관련 커밋/패치
nf_flowtable 기본4.165.13+nf_flow_table 모듈 도입
flowtable HW offload5.136.1+NF_FLOWTABLE_HW_OFFLOAD 플래그
TC flower ct action5.75.13+NET_ACT_CT 모듈
mlx5 CT offload5.75.15+MLX5_TC_CT 설정
ice eSwitch switchdev5.156.1+ICE_SWITCHDEV 설정
nftables BPF verdict5.186.1+BPF netfilter 프로그램 타입
xfrm packet offload6.16.6+XFRM_OFFLOAD 확장
flowtable VLAN offload5.176.1+VLAN push/pop 지원
conntrack zone offload5.105.15+CT zone HW 지원
권장 커널: NGFW HW offload의 안정적 운영을 위해 커널 6.1 LTS 이상을 권장합니다. 이 버전에서 flowtable HW offload, TC flower CT, IPSec packet offload가 모두 안정화되었습니다.

필수 CONFIG_* 옵션

# Netfilter / conntrack
CONFIG_NF_CONNTRACK=y
CONFIG_NF_TABLES=y
CONFIG_NF_TABLES_INET=y
CONFIG_NFT_FLOW_OFFLOAD=y        # flowtable offload 지원
CONFIG_NF_FLOW_TABLE=y            # nf_flowtable 코어
CONFIG_NF_FLOW_TABLE_INET=y
CONFIG_NETFILTER_NETLINK_QUEUE=y  # NFQUEUE

# HW offload (eSwitch/TC)
CONFIG_NET_SWITCHDEV=y            # switchdev 프레임워크
CONFIG_NET_CLS_FLOWER=y           # TC flower classifier
CONFIG_NET_ACT_CT=y               # TC conntrack action
CONFIG_NET_ACT_MIRRED=y           # TC mirred (redirect)
CONFIG_NET_ACT_PEDIT=y            # TC packet edit
CONFIG_NET_ACT_TUNNEL_KEY=y       # TC tunnel encap/decap

# NIC 드라이버 (예: NVIDIA ConnectX)
CONFIG_MLX5_CORE=y
CONFIG_MLX5_ESWITCH=y
CONFIG_MLX5_TC_CT=y               # mlx5 CT offload

# XDP
CONFIG_XDP_SOCKETS=y
CONFIG_BPF_SYSCALL=y
CONFIG_BPF_JIT=y

# IPSec HW offload
CONFIG_XFRM=y
CONFIG_XFRM_OFFLOAD=y
CONFIG_INET_ESP_OFFLOAD=y

# conntrack 헬퍼 (ALG 프로토콜)
CONFIG_NF_CONNTRACK_FTP=m       # FTP ALG
CONFIG_NF_CONNTRACK_SIP=m       # SIP ALG
CONFIG_NF_CONNTRACK_H323=m      # H.323 ALG
CONFIG_NF_CONNTRACK_TFTP=m      # TFTP ALG
CONFIG_NF_CONNTRACK_PPTP=m      # PPTP ALG
CONFIG_NF_CT_NETLINK=y          # conntrackd 통신

# NAT 관련
CONFIG_NF_NAT=y
CONFIG_NF_NAT_MASQUERADE=y
CONFIG_NFT_NAT=y
CONFIG_NFT_MASQ=y

# NFQUEUE 관련
CONFIG_NETFILTER_NETLINK_QUEUE=y
CONFIG_NFT_QUEUE=y              # nft queue 표현식

# Intel E810 드라이버 (대안 NIC)
CONFIG_ICE=y
CONFIG_ICE_SWITCHDEV=y          # ice eSwitch 지원

# QoS 관련
CONFIG_NET_SCH_HTB=y            # HTB qdisc
CONFIG_NET_SCH_FQ_CODEL=y       # fq_codel (AQM)
CONFIG_NET_SCH_MQPRIO=y         # HW QoS 큐 매핑
CONFIG_NET_SCH_INGRESS=y        # TC ingress qdisc

모듈 로딩 순서와 검증

NGFW 관련 커널 모듈은 의존성 순서대로 로드해야 합니다. 다음은 systemd 서비스 이전에 실행할 모듈 로딩 스크립트입니다:

#!/bin/bash
# /usr/local/sbin/ngfw-modules-load.sh
# NGFW 커널 모듈 로딩 (의존성 순서)

set -e

# 1단계: 기본 Netfilter 프레임워크
modprobe nf_conntrack
modprobe nf_tables
modprobe nft_chain_nat
modprobe nft_ct              # nft ct 표현식

# 2단계: flowtable 오프로드
modprobe nf_flow_table
modprobe nf_flow_table_inet
modprobe nft_flow_offload    # nft flow offload 표현식

# 3단계: NFQUEUE (DPI 엔진 연동)
modprobe nfnetlink_queue
modprobe nft_queue           # nft queue 표현식

# 4단계: TC offload 프레임워크
modprobe act_ct              # TC conntrack action
modprobe act_mirred          # TC redirect action
modprobe act_pedit           # TC packet edit
modprobe cls_flower          # TC flower classifier

# 5단계: conntrack 헬퍼 (필요한 ALG만 선택 로드)
# modprobe nf_conntrack_ftp
# modprobe nf_conntrack_sip

# 6단계: IPSec offload (필요시)
# modprobe esp4_offload
# modprobe esp6_offload

echo "NGFW modules loaded successfully"
코드 설명
  • 7-10행 nf_conntracknf_tables가 모든 NGFW 기능의 기반입니다. nft_ct는 nftables에서 conntrack 상태를 매칭하는 표현식을 제공합니다.
  • 13-15행 nf_flow_table은 flowtable 코어 모듈이고, nft_flow_offload은 nftables에서 flow offload @ft 구문을 사용할 수 있게 합니다. 반드시 nf_conntrack 다음에 로드.
  • 18-19행 NFQUEUE는 Suricata 등 유저스페이스 DPI 엔진과의 통신을 담당합니다. nfnetlink_queue가 커널 측, libnetfilter_queue가 유저 측입니다.
  • 22-25행 TC flower는 eSwitch HW offload의 핵심입니다. act_ct가 TC에서 conntrack 조회를 가능하게 하고, act_mirred가 패킷 리다이렉트를 수행합니다.

모듈 로딩 후 다음 명령으로 검증합니다:

# 모듈 로딩 확인
lsmod | grep -E "nf_flow|nft_flow|nf_conntrack|cls_flower|act_ct"

# 기대 출력 예시:
# nft_flow_offload     20480  1
# nf_flow_table_inet   16384  1 nft_flow_offload
# nf_flow_table        36864  1 nf_flow_table_inet
# nf_conntrack        172032  5 nf_flow_table,nft_ct,...
# cls_flower           40960  0
# act_ct               20480  0

# dmesg에서 초기화 로그 확인
dmesg | grep -iE "flow.table|conntrack|switchdev"

# NIC eSwitch 상태 확인
devlink dev eswitch show pci/0000:03:00.0 2>/dev/null || echo "eSwitch not available"

# NIC hw-tc-offload 기능 확인
ethtool -k eth0 | grep hw-tc-offload

커널 부트 파라미터

# /etc/default/grub의 GRUB_CMDLINE_LINUX에 추가
# NGFW 최적화 커널 부트 파라미터

# IOMMU (SmartNIC SR-IOV/eSwitch 필수)
intel_iommu=on iommu=pt

# CPU 격리: 데이터 플레인 코어를 커널 스케줄러에서 분리
isolcpus=4-11                  # Suricata 전용 코어 격리
nohz_full=4-11                 # 격리 코어의 타이머 틱 제거
rcu_nocbs=4-11                 # RCU 콜백을 다른 코어에서 실행

# Hugepages (VPP/DPDK 사용 시)
default_hugepagesz=1G hugepagesz=1G hugepages=4

# NIC MMIO 매핑 최적화
pci=assign-busses              # PCI 버스 재할당

# 네트워크 스택 최적화
net.ifnames=0                  # 예측 가능한 인터페이스 이름 비활성화 (선택)

sysctl 튜닝 파라미터

파라미터기본값NGFW 권장값설명
nf_conntrack_max2621442000000최대 conntrack 엔트리 수
nf_conntrack_buckets65536500000conntrack 해시 테이블 버킷 수
nf_conntrack_tcp_timeout_established432000300TCP ESTABLISHED 타임아웃 (초)
nf_flowtable_tcp_timeout30300flowtable TCP 타임아웃
nf_flowtable_udp_timeout3030flowtable UDP 타임아웃
net.core.optmem_max2048081920NFQUEUE 소켓 옵션 메모리
net.core.netdev_budget300600NAPI poll 버짓
net.core.netdev_max_backlog100010000입력 큐 크기
net.ipv4.ip_forward01IP 포워딩 활성화
net.netfilter.nf_conntrack_helper00자동 헬퍼 비활성화 (보안)

운영 가이드

NGFW 초기 배포 절차

Linux 기반 NGFW를 처음부터 배포하는 단계별 가이드입니다:

단계작업명령/설정검증
1 커널 설정 확인 CONFIG_NF_FLOW_TABLE, CONFIG_MLX5_ESWITCH 등 확인 zcat /proc/config.gz | grep NF_FLOW
2 NIC 드라이버/펌웨어 mlx5/ice 드라이버 로드, 펌웨어 업데이트 ethtool -i eth0, devlink dev info
3 eSwitch switchdev 모드 devlink dev eswitch set pci/... mode switchdev devlink dev eswitch show
4 커널 파라미터 튜닝 sysctl 설정 (conntrack_max, flowtable timeout 등) sysctl net.netfilter.nf_conntrack_max
5 IRQ/NUMA 바인딩 NIC IRQ를 로컬 NUMA 코어에 바인딩 cat /proc/interrupts | grep eth0
6 nftables 규칙 설치 flowtable + forward chain + NFQUEUE 규칙 nft list ruleset
7 TC flower 규칙 ct_state 매칭 + HW forward 규칙 tc -s filter show dev eth0_rep0 ingress
8 Suricata DPI 설정 NFQUEUE 모드, 멀티스레드, App-Layer 설정 suricatasc -c uptime
9 conntrackd HA (선택) FTFW 모드, 멀티캐스트 동기화 conntrackd -s
10 모니터링 설정 Prometheus + Grafana + 알림 규칙 대시보드 접근 확인

일상 운영 명령 요약

# ===== 상태 확인 =====
# 전체 NGFW 상태 요약
echo "=== conntrack ===" && conntrack -C && \
echo "=== offloaded ===" && conntrack -L --status OFFLOAD 2>/dev/null | wc -l && \
echo "=== nft flowtable ===" && nft list flowtable inet ngfw ft 2>/dev/null && \
echo "=== suricata ===" && systemctl is-active suricata

# ===== 긴급 대응 =====
# DDoS 감지 시 XDP 긴급 필터 적용
ip link set dev eth0 xdp obj /usr/local/lib/xdp/emergency_block.o sec xdp

# conntrack 테이블 긴급 정리 (가득 참 상황)
sysctl -w net.netfilter.nf_conntrack_tcp_timeout_established=60
sysctl -w net.netfilter.nf_conntrack_tcp_timeout_time_wait=5
conntrack -F  # 전체 플러시 (주의: 모든 세션 끊김)

# 특정 IP 긴급 차단
nft add element inet ngfw blocklist { 203.0.113.50 }

# ===== 정책 변경 =====
# 규칙 변경 시 기존 오프로드 세션 재평가
nft flush flowtable inet ngfw ft  # flowtable 초기화
# 이후 패킷이 도착하면 자동으로 새 규칙에 따라 재등록

# nftables 규칙 리로드 (서비스 무중단)
systemctl reload ngfw-nftables

# ===== 유지보수 =====
# Suricata 시그니처 업데이트
suricata-update
systemctl reload suricata

# NIC 펌웨어 업데이트 (다운타임 필요)
mlxfwmanager --online-activate

장애 복구 시나리오

장애 시나리오증상복구 절차
Suricata 크래시 NFQUEUE에 패킷 쌓임 → 지연 증가 fail-open: yes로 자동 bypass. systemctl restart suricata
conntrack 테이블 가득 새 연결 불가 (dmesg에 "nf_conntrack: table full") 타임아웃 단축 + nf_conntrack_max 증가. XDP DDoS 필터 확인
eSwitch HW 오류 HW offload 중단, SW 폴백으로 성능 저하 devlink trap show로 원인 확인. NIC 재설정 또는 펌웨어 업데이트
HA failover Active 노드 장애 → Standby 승격 conntrackd가 자동 동기화. flowtable은 다음 패킷부터 자동 재등록
메모리 부족 slab 할당 실패, conntrack alloc 실패 conntrack_max 감소, flowtable 타임아웃 단축, 불필요 규칙 정리

업그레이드/롤백 절차

NGFW 커널이나 NIC 펌웨어 업그레이드 시 무중단 또는 최소 중단을 위한 절차입니다:

구성요소업그레이드 방법중단 시간롤백 방법
Suricata 시그니처 suricata-update && systemctl reload suricata 0초 (graceful reload) suricata-update --force로 이전 버전 재적용
nftables 규칙 nft -f /etc/ngfw/ruleset-v2.nft (atomic replace) 0초 (원자적 교체) nft -f /etc/ngfw/ruleset-v1.nft 이전 규칙셋 복원
TC flower 규칙 새 규칙 추가 후 구 규칙 삭제 (순서 중요) 개별 규칙 <1ms 삭제한 규칙 재추가. HW 규칙은 NIC이 캐시 유지
NIC 펌웨어 mlxfwmanager --online-activate 10~30초 (NIC 리셋) 이전 펌웨어 이미지로 재플래시. HA 구성 시 failover 후 수행
커널 업그레이드 새 커널 설치 → GRUB 업데이트 → 재부팅 2~5분 (재부팅) GRUB에서 이전 커널 선택. grubby --set-default
eBPF/XDP 프로그램 ip link set dev eth0 xdp obj new_prog.o (atomic) 0초 (원자적 교체) ip link set dev eth0 xdp off로 제거 또는 이전 프로그램 로드
# ===== 안전한 커널 업그레이드 절차 (HA 구성) =====

# 1단계: Standby 노드에서 먼저 업그레이드
conntrackd -n  # Standby 역할 확인

# 2단계: 새 커널 설치
dnf install kernel-6.6.x  # 또는 dpkg -i linux-image-6.6.x

# 3단계: Standby 재부팅 (Active가 트래픽 처리 중)
reboot

# 4단계: 재부팅 후 검증
uname -r                    # 새 커널 버전 확인
lsmod | grep nf_flow_table  # 모듈 로딩 확인
conntrackd -s               # HA 동기화 상태 확인
suricatasc -c uptime        # Suricata 정상 기동 확인

# 5단계: Failover → 이 노드를 Active로 승격
conntrackd -c commit        # 캐시된 세션 커밋
conntrackd -k               # Active 역할 요청

# 6단계: 구 Active 노드도 동일 절차로 업그레이드

로그 관리와 감사

NGFW는 보안 장비이므로 로그 관리가 법적 요구사항일 수 있습니다. 주요 로그 소스와 관리 방안입니다:

로그 소스경로/방법포함 정보보존 기간 (권장)
nftables 로그 nft log → syslog 또는 nflog → ulogd2 차단/허용 verdict, 5-tuple, 카운터 90일+
conntrack 이벤트 conntrack -E 또는 conntrackd의 stats 로그 NEW/DESTROY 이벤트, 세션 지속 시간, 바이트 30일
Suricata EVE JSON /var/log/suricata/eve.json IPS alert, App-ID, DNS 쿼리, TLS 핸드셰이크, HTTP 메타데이터 90일+ (SIEM 전송)
flowtable 통계 nft list flowtable, ethtool -S 오프로드된 플로우 수, 바이트, 패킷 Prometheus에 시계열 저장
커널 메시지 dmesg, /var/log/kern.log conntrack table full, NIC 오류, OOM 30일
# /etc/rsyslog.d/ngfw.conf — NGFW 로그 분리 저장
# nftables LOG 타겟 로그
:msg, contains, "NGFW_" /var/log/ngfw/firewall.log
& stop

# conntrackd 로그
:programname, isequal, "conntrackd" /var/log/ngfw/conntrackd.log
& stop

# logrotate 설정
# /etc/logrotate.d/ngfw
# /var/log/ngfw/*.log {
#     daily
#     rotate 90
#     compress
#     delaycompress
#     missingok
#     notifempty
#     sharedscripts
#     postrotate
#         systemctl reload rsyslog
#     endscript
# }

용량 계획

NGFW 장비의 리소스 소요를 사전에 계획하기 위한 가이드입니다:

리소스계산 공식예시 (100K 동시 세션)
conntrack 메모리세션 수 × ~320 bytes/entry100K × 320B = ~32 MB
flowtable 메모리오프로드 세션 × ~256 bytes/entry70K × 256B = ~18 MB
conntrack 해시 버킷max_entries / 4 (권장)100K / 4 = 25K 버킷
NIC HW flow 엔트리NIC 의존 (CX-6: ~1M, E810: ~64K)70K (HW 한계 내)
Suricata 메모리기본 ~2GB + 시그니처 ~1GB + 세션 캐시~4 GB
DPI CPU 코어DPI 처리량 / 코어당 1~3 Gbps10 Gbps DPI → 4~10 코어

QUIC/TLS 1.3과 NGFW 오프로드

QUIC 프로토콜과 TLS 1.3의 Encrypted Client Hello(ECH)는 기존 NGFW의 DPI 기반 트래픽 분류 체계를 근본적으로 무력화합니다. 이 섹션에서는 QUIC/ECH 환경에서 NGFW 오프로드를 유지하기 위한 전략을 분석합니다.

QUIC 프로토콜이 NGFW에 미치는 영향

QUIC(RFC 9000)은 UDP 기반에 TLS 1.3 암호화를 내장한 전송 프로토콜입니다. 기존 TCP+TLS 스택과 비교하면 NGFW에 다음과 같은 도전을 제시합니다:

특성TCP + TLS 1.2/1.3QUICNGFW 영향
전송 프로토콜TCP (proto 6)UDP (proto 17)포트 기반 프로토콜 식별 불가 (UDP 443)
헤더 가시성TCP 헤더 평문, TLS record 헤더 평문Short Header: CID + 패킷 번호만 (암호화)DPI가 활용할 평문 메타데이터 극소
SNI 가시성Client Hello의 SNI 평문 (TLS 1.2/1.3)Initial Packet에만 SNI (ECH 시 암호화)URL Filtering 제한 또는 불가
세션 식별5-tuple 고정Connection ID (가변 길이)conntrack 5-tuple 키 무효화 가능
핸드셰이크TCP 3-way + TLS (2-3 RTT)1-RTT (0-RTT 재연결)NEW 세션 탐지 시점 불명확
연결 마이그레이션미지원 (새 TCP 연결 필요)지원 (CID 유지)flowtable 엔트리 갱신 불가
다중화연결당 하나의 스트림 (HTTP/2 제외)단일 연결에 다중 스트림하나의 conntrack 엔트리에 다중 논리 세션
오프로드 가능성높음 (EST 후 flowtable)제한적 (CID 추적 필요)CID 기반 오프로드 미지원
QUIC의 시장 점유율: 2025년 기준 전체 웹 트래픽의 약 30~40%가 QUIC을 사용합니다(Google, Meta, Cloudflare 서비스). Chrome은 기본적으로 QUIC을 활성화하며, HTTP/3는 QUIC 위에서 동작합니다. NGFW가 QUIC을 올바르게 처리하지 못하면 전체 트래픽의 상당 부분에 대해 보안 가시성을 잃게 됩니다.

QUIC 트래픽 탐지와 분류

QUIC 패킷은 첫 번째 바이트(Header Form bit)로 Long Header와 Short Header를 구분합니다. NGFW에서는 Initial Packet의 Long Header를 분석하여 QUIC 연결을 식별합니다.

패킷 유형Header Form첫 바이트 패턴가시 필드NGFW 활용
InitialLong (1)0b1100xxxx (0xC0~0xCF)Version, DCID, SCID, Token, LengthQUIC 탐지, 버전 확인, CID 추출
0-RTTLong (1)0b1101xxxx (0xD0~0xDF)Version, DCID, SCID, Length재연결 탐지
HandshakeLong (1)0b1110xxxx (0xE0~0xEF)Version, DCID, SCID, Length핸드셰이크 진행 확인
RetryLong (1)0b1111xxxx (0xF0~0xFF)Version, DCID, SCID, Retry TokenDoS 방어 확인
1-RTT (Short)Short (0)0b01xxxxxx (0x40~0x7F)DCID만 (나머지 암호화)CID로 기존 세션 매핑만 가능

nftables에서 QUIC 트래픽을 탐지하고 분류하는 규칙 예시입니다:

#!/usr/sbin/nft -f
# QUIC 트래픽 탐지 및 분류 nftables 규칙

table inet quic_detect {
    # QUIC Initial Packet 탐지 set (rate limiting용)
    set quic_new_conn {
        type ipv4_addr . inet_service
        flags dynamic,timeout
        timeout 30s
    }

    chain forward {
        type filter hook forward priority 0; policy accept;

        # UDP 443 트래픽 필터링
        udp dport 443 jump quic_classify

        # UDP 443 + UDP 80 (alt-svc 기반 QUIC 업그레이드)
        udp dport 80 @th,0,8 & 0x80 == 0x80 jump quic_classify
    }

    chain quic_classify {
        # Long Header 판별: 첫 번째 바이트 bit7 = 1
        @th,0,8 & 0x80 == 0x00 jump quic_short_header

        # QUIC v1 Initial Packet: first_byte & 0xF0 == 0xC0, version == 0x00000001
        @th,0,8 & 0xF0 == 0xC0 @th,8,32 == 0x00000001 \
            jump quic_initial

        # QUIC v2 Initial: version == 0x6b3343cf
        @th,0,8 & 0xF0 == 0xC0 @th,8,32 == 0x6b3343cf \
            jump quic_initial

        # 0-RTT Packet (재연결): first_byte & 0xF0 == 0xD0
        @th,0,8 & 0xF0 == 0xD0 \
            log prefix "QUIC-0RTT: " counter

        # Handshake Packet: first_byte & 0xF0 == 0xE0
        @th,0,8 & 0xF0 == 0xE0 counter accept
    }

    chain quic_initial {
        # 새 QUIC 연결 rate limiting (DDoS 방어)
        update @quic_new_conn { ip saddr . udp dport } \
            limit rate over 100/second burst 50 packets drop

        # 로깅 후 Slow Path로 전달 (DPI 검사)
        log prefix "QUIC-NEW: " counter accept
    }

    chain quic_short_header {
        # Short Header: 이미 확립된 QUIC 세션
        # conntrack EST 상태이면 flowtable으로 오프로드 가능
        ct state established flow add @ft counter accept
        ct state established counter accept
    }
}
코드 설명
  • 5-9행 QUIC 새 연결을 추적하는 동적 set입니다. 30초 타임아웃으로 자동 만료되며, DDoS rate limiting에 사용됩니다.
  • 15행 QUIC은 주로 UDP 443을 사용합니다. 이 규칙으로 잠재적 QUIC 트래픽을 필터링합니다.
  • 18행 @th,0,8은 transport header offset 0부터 8비트를 읽습니다. UDP 페이로드의 첫 바이트를 검사하여 Long Header 여부를 판별합니다.
  • 25-26행 Initial Packet은 0xC0~0xCF 범위이고, 바이트 1~4가 QUIC 버전입니다. v1(0x00000001)을 확인합니다.
  • 42-43행 새 QUIC 연결에 대해 source IP + port 조합으로 초당 100개까지 허용하고 초과분을 DROP합니다. QUIC flood 공격 방어입니다.
  • 50-51행 Short Header 패킷은 이미 QUIC 연결이 확립된 상태이므로 conntrack ESTABLISHED이면 flowtable 오프로드 대상입니다.

TLS 1.3 Encrypted Client Hello (ECH)

TLS 1.3의 ECH(RFC 9460 초안)는 Client Hello 메시지 내의 SNI(Server Name Indication)를 완전히 암호화합니다. ECH 이전에는 TLS 핸드셰이크의 Client Hello에서 SNI를 평문으로 읽어 URL Filtering과 트래픽 분류에 활용할 수 있었지만, ECH가 활성화되면 이 방법이 완전히 무력화됩니다.

기술ECH 이전ECH 이후대체 식별 방법
SNI 기반 URL FilteringClient Hello에서 SNI 평문 추출외부 SNI만 가시 (CDN 도메인)DNS 쿼리 모니터링, IP 평판 DB
인증서 기반 식별Server Hello의 인증서 평문TLS 1.3부터 인증서도 암호화OCSP stapling 분석, CT 로그
App-ID (DPI)SNI + ALPN + 인증서 조합ALPN만 부분 가시JA4 핑거프린트, 트래픽 패턴 분석
카테고리 필터링SNI → 도메인 → 카테고리 DB외부 SNI만으로는 정확도 저하DNS Response Policy Zone (RPZ)
SSL InspectionMITM 프록시로 복호화ECH 키 없이는 MITM 불가엔드포인트 에이전트, WARP 방식
JA3/JA4 핑거프린팅보조 식별 수단주요 식별 수단으로 격상JA4+ (JA4S, JA4H, JA4X 확장)
JA4 핑거프린트 구성: JA4는 TLS 핸드셰이크 특성을 t13d1516h2_8daaf6152771_e5627efa2ab1 형태의 해시로 요약합니다. 첫 부분은 프로토콜/버전/SNI유무/cipher수/extension수, 나머지는 cipher suite와 extension의 해시입니다. ECH로 SNI가 가려져도 브라우저·OS·앱별로 고유한 핑거프린트가 생성되어 식별이 가능합니다.

QUIC/ECH 환경의 NGFW 전략

QUIC과 ECH가 확산되는 환경에서 NGFW의 트래픽 가시성을 유지하기 위한 4가지 전략을 분석합니다:

전략 1: QUIC 차단/제어 (정책적 접근)

전략 2: 초기 핸드셰이크 기반 분류 후 오프로드

전략 3: DNS 기반 사전 분류 (DoH/DoT 제어)

전략 4: 엔드포인트 기반 (QUIC Client Hello 파싱)

다음은 nftables + Suricata를 조합하여 QUIC을 처리하는 종합 규칙 예시입니다:

#!/usr/sbin/nft -f
# QUIC/ECH 대응 NGFW nftables 규칙 (전략 1+2+3 조합)

table inet ngfw_quic {
    # 정책별 QUIC 처리 모드
    # 0: block (TCP fallback 유도)
    # 1: inspect (Initial만 DPI)
    # 2: allow (분류 후 오프로드)

    # DNS 기반 IP→카테고리 매핑 (외부 스크립트로 갱신)
    set dns_blocked_ips {
        type ipv4_addr
        flags interval
        # 예: DNS RPZ에서 차단 도메인의 IP를 자동 추가
    }

    set quic_allowed_dsts {
        type ipv4_addr
        flags interval
        # 예: 업무용 SaaS IP 대역 (QUIC 허용)
    }

    flowtable ft {
        hook ingress priority 0;
        devices = { eth0, eth1 };
        flags offload;
    }

    chain forward {
        type filter hook forward priority 0; policy drop;

        # 1. conntrack EST 세션 → flowtable 오프로드
        ct state established,related flow add @ft counter accept

        # 2. DNS 기반 사전 차단 (전략 3)
        ip daddr @dns_blocked_ips counter drop

        # 3. QUIC 처리 (UDP 443)
        udp dport 443 jump quic_policy

        # 4. 일반 TCP/TLS → DPI (NFQUEUE)
        tcp dport 443 ct state new \
            queue num 0-3 fanout,bypass

        # 5. 기타 허용 규칙
        ct state established,related accept
    }

    chain quic_policy {
        # 허용된 목적지 → QUIC 허용 (오프로드 대상)
        ip daddr @quic_allowed_dsts accept

        # 나머지 QUIC → 정책에 따라 처리
        # 전략 1: 차단하여 TCP fallback 유도
        # icmp type port-unreachable 응답으로 빠른 fallback
        reject with icmpx port-unreachable

        # 전략 2를 사용할 경우 (위 reject 대신):
        # @th,0,8 & 0x80 == 0x80 queue num 4-5 fanout,bypass
        # @th,0,8 & 0x80 == 0x00 ct state established accept
    }
}
# Suricata QUIC 탐지 규칙 (suricata.rules에 추가)
# QUIC Initial Packet 탐지 (App-Layer)
alert quic $HOME_NET any -> $EXTERNAL_NET 443 \
  (msg:"QUIC Initial Packet detected"; \
   quic.version; content:"|00 00 00 01|"; \
   sid:3000001; rev:1;)

# QUIC 0-RTT 재연결 탐지 (Early Data 위험)
alert quic $HOME_NET any -> $EXTERNAL_NET 443 \
  (msg:"QUIC 0-RTT Early Data"; \
   quic.header.type; content:"|01|"; \
   sid:3000002; rev:1;)

# JA4 핑거프린트 기반 브라우저 식별 예시
alert tls $HOME_NET any -> $EXTERNAL_NET 443 \
  (msg:"TLS JA4 - Chrome Browser detected"; \
   ja4.hash; content:"t13d1516h2"; startswith; \
   sid:3000003; rev:1;)
QUIC vs TCP+TLS — NGFW 가시성 비교 TCP + TLS 1.3 TCP Header (평문) SrcPort | DstPort | Seq | Ack | Flags | Window 가시 TLS Record Header (평문) Content-Type | Version | Length 가시 Client Hello → SNI (평문) server_name = "example.com" | ALPN = "h2" 가시 Application Data (암호화) HTTP/2 요청/응답 → 암호화된 페이로드 불가 NGFW 활용 가능: 5-tuple 추적 ✓ | SNI 기반 분류 ✓ | conntrack EST 오프로드 ✓ 5-tuple 고정 → conntrack 추적 안정 10.0.0.1:45678 → 93.184.216.34:443 (TCP) QUIC (UDP + 내장 TLS) UDP Header (평문) SrcPort | DstPort(443) | Length | Checksum 최소 QUIC Long Header (Initial만) Form(1b) | Type | Version | DCID | SCID | Token 제한 QUIC Short Header (1-RTT) Form(0) | DCID만 (패킷번호+페이로드 암호화) 불가 CRYPTO Frame → ECH Client Hello SNI 암호화 | Outer SNI만 노출 (CDN 도메인) 불가 NGFW 한계: DPI 제한 ✗ | SNI 암호화 ✗ | CID 변경 시 세션 추적 ✗ 5-tuple 변경 가능 → conntrack 무효화 CID=0x1a2b (유지) | IP 10.0.0.1 → 10.0.0.5 (변경)

멀티 테넌트 NGFW 아키텍처

클라우드 환경과 MSSP(Managed Security Service Provider)에서는 단일 NGFW 인스턴스에서 다수의 테넌트(고객/VPC/네임스페이스)를 격리하여 운영해야 합니다. 이 섹션에서는 Linux 커널의 격리 메커니즘을 활용한 멀티 테넌트 NGFW 아키텍처를 분석합니다.

멀티 테넌시 격리 모델

Linux 커널에서 제공하는 네트워크 격리 기술을 NGFW 관점에서 비교합니다:

격리 방식격리 수준conntrack 격리flowtable 격리HW offload장점단점
네트워크 네임스페이스 완전 격리 (독립 네트워크 스택) 네임스페이스별 독립 conntrack 네임스페이스별 독립 flowtable VF를 네임스페이스에 이동 가능 가장 강력한 격리, 충돌 없음 리소스 오버헤드, 관리 복잡성
VRF (Virtual Routing & Forwarding) L3 라우팅 격리 공유 (zone으로 분리 필요) VRF별 설정 가능 TC flower에서 VRF 매칭 가능 라우팅 격리, 단일 스택 유지 L2 격리 불가, conntrack 추가 설정 필요
conntrack zone conntrack 테이블 격리 zone ID별 완전 격리 zone별 flowtable 분리 가능 TC flower ct zone 지원 경량, 기존 인프라 유지 nftables 규칙 복잡도 증가
eSwitch VF representor HW 수준 격리 VF별 conntrack zone 매핑 VF별 독립 flowtable VF당 독립 FDB 규칙 HW 격리, 최고 성능 VF 수 제한 (NIC 의존), SmartNIC 필요
nftables table/chain 분리 논리적 격리 (정책 분리) 공유 (mark/zone으로 구분) 공유 flowtable 공유 설정 단순, 유연한 정책 격리 보장 약함, 버그 시 누출

conntrack zone 기반 테넌트 격리

conntrack zone은 동일 커널 내에서 conntrack 테이블을 논리적으로 분리하는 가장 효율적인 방법입니다. 각 테넌트에 고유한 zone ID를 할당하면 동일한 5-tuple을 가진 세션도 충돌 없이 독립적으로 추적됩니다.

#!/usr/sbin/nft -f
# 멀티 테넌트 conntrack zone 설정
# 테넌트 A: zone 100, 테넌트 B: zone 200

table inet multi_tenant {
    # 테넌트별 독립 flowtable
    flowtable ft_tenant_a {
        hook ingress priority 0;
        devices = { eth0, eth1 };
        flags offload;
    }

    flowtable ft_tenant_b {
        hook ingress priority 0;
        devices = { eth0, eth2 };
        flags offload;
    }

    # 테넌트 분류 체인 (ingress에서 mark 설정)
    chain prerouting {
        type filter hook prerouting priority -150; policy accept;

        # 서브넷 기반 테넌트 분류
        ip saddr 10.100.0.0/16 meta mark set 100
        ip saddr 10.200.0.0/16 meta mark set 200

        # VLAN 기반 테넌트 분류 (대안)
        # vlan id 100 meta mark set 100
        # vlan id 200 meta mark set 200

        # mark → conntrack zone 매핑
        meta mark 100 ct zone set 100
        meta mark 200 ct zone set 200
    }

    chain forward {
        type filter hook forward priority 0; policy drop;

        # 테넌트 A: zone 100의 EST 세션 → 전용 flowtable
        meta mark 100 ct zone 100 ct state established \
            flow add @ft_tenant_a counter accept

        # 테넌트 B: zone 200의 EST 세션 → 전용 flowtable
        meta mark 200 ct zone 200 ct state established \
            flow add @ft_tenant_b counter accept

        # 새 세션: 테넌트별 DPI 큐 분리
        meta mark 100 ct zone 100 ct state new \
            queue num 0-3 fanout,bypass

        meta mark 200 ct zone 200 ct state new \
            queue num 4-7 fanout,bypass

        # RELATED 세션 허용
        ct state related accept
    }
}
코드 설명
  • 7-16행 테넌트별 독립 flowtable을 생성합니다. 각 flowtable은 자체 devices를 가지며 독립적으로 HW offload됩니다.
  • 23-24행 소스 IP 서브넷으로 테넌트를 분류하여 nfmark를 설정합니다. VLAN, 인터페이스, DSCP 등 다양한 기준 사용 가능합니다.
  • 32-33행 ct zone set으로 패킷의 conntrack zone을 설정합니다. 같은 5-tuple이라도 zone이 다르면 별도 conntrack 엔트리가 생성됩니다.
  • 40-41행 테넌트 A의 ESTABLISHED 세션을 전용 flowtable ft_tenant_a에 오프로드합니다. zone 100 내에서만 매칭됩니다.
  • 49-53행 새 세션을 테넌트별 NFQUEUE 범위에 분리하여 전달합니다. Suricata 인스턴스를 테넌트별로 분리 운영할 수 있습니다.

TC flower에서도 conntrack zone을 지정하여 HW 수준의 테넌트 격리를 구현할 수 있습니다:

# TC flower: 테넌트별 conntrack zone + HW offload

# 테넌트 A (VF0, zone 100): untracked → ct zone 100 시작
tc filter add dev enp4s0f0v0 ingress prio 1 \
  protocol ip flower \
  ct_state -trk \
  action ct zone 100

# 테넌트 A: EST → HW forward to uplink
tc filter add dev enp4s0f0v0 ingress prio 2 \
  protocol ip flower \
  ct_state +trk+est \
  ct_zone 100 \
  action ct zone 100 \
  action mirred egress redirect dev enp4s0f0

# 테넌트 B (VF1, zone 200): untracked → ct zone 200 시작
tc filter add dev enp4s0f0v1 ingress prio 1 \
  protocol ip flower \
  ct_state -trk \
  action ct zone 200

# 테넌트 B: EST → HW forward to uplink
tc filter add dev enp4s0f0v1 ingress prio 2 \
  protocol ip flower \
  ct_state +trk+est \
  ct_zone 200 \
  action ct zone 200 \
  action mirred egress redirect dev enp4s0f0

# 테넌트별 conntrack 상태 확인
conntrack -L --zone 100  # 테넌트 A 세션
conntrack -L --zone 200  # 테넌트 B 세션

VRF + eSwitch VF 기반 격리

VRF(Virtual Routing and Forwarding)는 L3 라우팅을 테넌트별로 완전 격리합니다. eSwitch의 VF representor와 결합하면 HW 수준의 라우팅 + 방화벽 격리가 가능합니다.

#!/bin/bash
# VRF + eSwitch VF 기반 멀티 테넌트 설정

# 1. VRF 생성 (테넌트별)
ip link add vrf-tenant-a type vrf table 100
ip link set vrf-tenant-a up
ip link add vrf-tenant-b type vrf table 200
ip link set vrf-tenant-b up

# 2. VF representor를 VRF에 바인딩
ip link set enp4s0f0v0 master vrf-tenant-a
ip link set enp4s0f0v1 master vrf-tenant-b

# 3. 업링크 서브인터페이스를 VRF에 바인딩
ip link add link enp4s0f0 name enp4s0f0.100 type vlan id 100
ip link set enp4s0f0.100 master vrf-tenant-a
ip addr add 10.100.0.1/24 dev enp4s0f0.100

ip link add link enp4s0f0 name enp4s0f0.200 type vlan id 200
ip link set enp4s0f0.200 master vrf-tenant-b
ip addr add 10.200.0.1/24 dev enp4s0f0.200

# 4. VRF별 라우팅 테이블
ip route add default via 10.100.0.254 table 100
ip route add default via 10.200.0.254 table 200

# 5. sysctl: VRF별 forwarding 활성화
sysctl -w net.ipv4.conf.all.forwarding=1
sysctl -w net.ipv4.conf.vrf-tenant-a.forwarding=1
sysctl -w net.ipv4.conf.vrf-tenant-b.forwarding=1
#!/usr/sbin/nft -f
# VRF 기반 테넌트별 nftables 방화벽 + flowtable

table inet vrf_ngfw {
    flowtable ft_a {
        hook ingress priority 0;
        devices = { enp4s0f0v0, enp4s0f0.100 };
        flags offload;
    }

    flowtable ft_b {
        hook ingress priority 0;
        devices = { enp4s0f0v1, enp4s0f0.200 };
        flags offload;
    }

    chain forward {
        type filter hook forward priority 0; policy drop;

        # VRF 인터페이스로 테넌트 구분
        iifname "enp4s0f0v0" jump tenant_a_rules
        iifname "enp4s0f0v1" jump tenant_b_rules

        # 반환 트래픽
        oifname "enp4s0f0v0" ct state established,related accept
        oifname "enp4s0f0v1" ct state established,related accept
    }

    chain tenant_a_rules {
        # 테넌트 A 보안 정책
        ct state established flow add @ft_a counter accept
        ct state new tcp dport { 80, 443 } accept
        ct state new udp dport 53 accept
        counter drop
    }

    chain tenant_b_rules {
        # 테넌트 B 보안 정책 (더 제한적)
        ct state established flow add @ft_b counter accept
        ct state new tcp dport 443 accept
        counter drop
    }
}

Kubernetes 환경의 NGFW

Kubernetes에서는 CNI(Container Network Interface) 플러그인이 Pod 간 네트워킹을 관리합니다. NGFW 기능을 K8s 환경에 통합하려면 CNI의 데이터 플레인과 flowtable offload의 호환성을 고려해야 합니다.

CNI 플러그인데이터 플레인NetworkPolicyconntrack offloadflowtable 호환비고
CiliumeBPF (tc-bpf + XDP)L3/L4/L7 (자체 구현)eBPF CT map (자체)미지원 (자체 fast path)커널 conntrack 우회, 자체 CT + LB
Calico (eBPF)eBPF (tc-bpf)L3/L4eBPF CT (자체)미지원 (자체 구현)kube-proxy 대체, eBPF conntrack
Calico (iptables)iptables/nftablesL3/L4커널 conntrack가능 (nftables 모드)nftables 모드에서 flowtable 연동 가능
OVN-KubernetesOVS + GeneveL3/L4 (OVS ACL)OVS conntrackOVS-DPDK TC offloadSmartNIC OVS offload 시 HW 가속
AntreaOVSL3/L4/L7 (일부)OVS conntrackOVS HW offloadOVS bridge offload 활용
kube-routeriptables + IPVSL3/L4커널 conntrack가능 (수동 설정)flowtable 수동 구성 필요
Cilium + Host Firewall: Cilium의 Host Firewall 기능은 eBPF 기반으로 노드 수준의 방화벽을 제공합니다. 이는 커널 conntrack 대신 eBPF CT map을 사용하므로 nftables flowtable과 직접 연동되지 않습니다. 그러나 Cilium의 eBPF datapath 자체가 이미 Fast Path 역할을 수행하여, 별도의 flowtable 없이도 높은 처리 성능을 달성합니다. Pod-to-external 트래픽에 대해 호스트의 nftables flowtable을 추가로 활성화하면 이중 가속이 가능합니다.
멀티 테넌트 NGFW — conntrack zone + VRF + eSwitch 격리 SmartNIC eSwitch VF0 (Tenant A) VF1 (Tenant B) PF Uplink HW FDB Rules zone 100: VF0↔PF zone 200: VF1↔PF HW Flow Counters per-tenant 통계 Linux Kernel conntrack zone 100 Tenant A 세션 테이블 flowtable ft_a (HW offload) conntrack zone 200 Tenant B 세션 테이블 flowtable ft_b (HW offload) VRF table 100 Tenant A 라우팅 VRF table 200 Tenant B 라우팅 nftables (per-tenant chains) tenant_a_rules | tenant_b_rules 독립 ACL + DPI 큐 분리 NFQUEUE (DPI) Q0-3: Tenant A Q4-7: Tenant B Userspace (per-tenant) Suricata (Tenant A) NFQUEUE 0-3 시그니처 Set A 정책: Web + Email Suricata (Tenant B) NFQUEUE 4-7 시그니처 Set B 정책: HTTPS only 관리 플레인 (Multi-Tenant Manager) 테넌트 프로비저닝 | 정책 배포 | 로그 분리 conntrack zone 할당 | VRF 생성 | flowtable 관리 API: REST / gRPC / Netconf 모니터링 (per-tenant) HW counter → Prometheus exporter conntrack -L --zone N → 세션 통계 Suricata eve.json → 테넌트별 로그 분리

eBPF 기반 차세대 NGFW

eBPF(extended Berkeley Packet Filter)는 커널 내에서 안전하게 실행되는 프로그램을 통해 네트워크 패킷 처리를 프로그래밍할 수 있는 기술입니다. 전통적인 nftables/iptables 기반 방화벽을 넘어, eBPF는 더 유연하고 고성능인 NGFW 파이프라인을 구현할 수 있게 합니다.

eBPF NGFW의 장점과 한계

특성iptablesnftableseBPF (tc-bpf/XDP)BPF netfilter (6.4+)
성능 (패킷/초)낮음 (순차 체인)중간 (집합 기반)높음 (JIT 컴파일)높음 (JIT + 훅 직결)
유연성고정 매칭 (모듈 확장)표현식 기반 (유연)최고 (C 프로그램)높음 (BPF 프로그램)
HW offload미지원flowtable (TC flower)XDP HW offload (제한적)미지원 (SW only)
conntrack 연동nf_conntrack 직접nf_conntrack 직접자체 CT map 또는 kfuncnf_conntrack kfunc
flowtable 연동불가flow add @ft불가 (자체 fast path)가능 (kfunc)
동적 업데이트전체 체인 교체규칙 단위 추가/삭제맵 업데이트 (무중단)맵 업데이트 (무중단)
디버깅LOG 타겟log 액션bpftrace, bpf_printkbpftrace, bpf_printk
학습 곡선낮음중간높음 (C + BPF 지식)높음
안정성매우 높음 (수십 년)높음중간 (verifier 의존)아직 초기
eBPF vs nftables 선택 기준: 일반적인 L3/L4 방화벽 + flowtable offload가 목적이라면 nftables가 적합합니다. 커스텀 프로토콜 파싱, 머신러닝 기반 탐지, 복잡한 상태 머신이 필요하거나 Kubernetes 환경에서 수만 개의 Pod 정책을 관리해야 한다면 eBPF가 유리합니다. 실무에서는 nftables(기본 방화벽) + eBPF(XDP DDoS 필터, 커스텀 DPI)를 조합하는 하이브리드 접근이 가장 효과적입니다.

BPF netfilter 프로그램 (커널 6.4+)

Linux 커널 6.4에서 도입된 BPF_PROG_TYPE_NETFILTER는 Netfilter 훅 포인트에 BPF 프로그램을 직접 연결할 수 있게 합니다. 이를 통해 nftables 규칙 대신 BPF 프로그램으로 방화벽 로직을 구현할 수 있습니다.

/* BPF netfilter 프로그램 예시 — 세션 추적 + 오프로드 결정 */
/* 커널 6.4+ 필요, libbpf 사용 */

#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>
#include <linux/netfilter.h>

/* conntrack kfunc 선언 (커널 6.4+) */
extern struct nf_conn *
bpf_xdp_ct_lookup(struct xdp_md *ctx,
                  struct bpf_sock_tuple *tuple, u32 len,
                  struct bpf_ct_opts *opts, u32 opts_len) __ksym;
extern void
bpf_ct_release(struct nf_conn *ct) __ksym;

/* 세션별 DPI 결과를 저장하는 BPF 맵 */
struct {
    __uint(type, BPF_MAP_TYPE_LRU_HASH);
    __uint(max_entries, 1000000);
    __type(key, struct flow_key);    /* 5-tuple */
    __type(value, struct flow_info);  /* DPI 결과 + 상태 */
} flow_map SEC(".maps");

/* DPI 완료 + ALLOW된 세션을 기록하는 맵 */
struct {
    __uint(type, BPF_MAP_TYPE_LRU_HASH);
    __uint(max_entries, 500000);
    __type(key, struct flow_key);
    __type(value, __u64);  /* 오프로드 타임스탬프 */
} offload_map SEC(".maps");

/* 통계 카운터 */
struct {
    __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
    __uint(max_entries, 4);  /* 0:pass, 1:drop, 2:offload, 3:dpi */
    __type(key, __u32);
    __type(value, __u64);
} stats SEC(".maps");

struct flow_key {
    __be32 saddr;
    __be32 daddr;
    __be16 sport;
    __be16 dport;
    __u8   proto;
};

struct flow_info {
    __u32 app_id;        /* DPI 분류 결과 */
    __u32 verdict;       /* 0=pending, 1=allow, 2=deny */
    __u64 pkt_count;     /* 패킷 수 */
    __u64 byte_count;    /* 바이트 수 */
    __u64 last_seen;     /* 마지막 패킷 타임스탬프 */
};

SEC("netfilter")
int ngfw_filter(struct bpf_nf_ctx *ctx)
{
    struct sk_buff *skb = ctx->skb;
    struct flow_key key = {};
    struct flow_info *info;
    __u32 stat_key;

    /* 5-tuple 추출 */
    if (extract_flow_key(skb, &key) < 0)
        return NF_ACCEPT;

    /* flow_map에서 기존 세션 조회 */
    info = bpf_map_lookup_elem(&flow_map, &key);

    if (!info) {
        /* 새 세션: flow_map에 등록하고 DPI로 전달 */
        struct flow_info new_info = {
            .verdict = 0,  /* pending */
            .pkt_count = 1,
        };
        bpf_map_update_elem(&flow_map, &key, &new_info, BPF_ANY);
        stat_key = 3;  /* DPI로 전달 */
        update_stats(&stats, stat_key);
        return NF_ACCEPT;  /* Slow Path로 진행 */
    }

    /* DPI 결과에 따른 처리 */
    switch (info->verdict) {
    case 1:  /* ALLOW */
        info->pkt_count++;
        info->byte_count += skb->len;
        info->last_seen = bpf_ktime_get_ns();

        /* 10패킷 이상이면 오프로드 대상으로 마킹 */
        if (info->pkt_count > 10 &&
            !bpf_map_lookup_elem(&offload_map, &key)) {
            __u64 ts = bpf_ktime_get_ns();
            bpf_map_update_elem(&offload_map, &key, &ts, BPF_ANY);
            stat_key = 2;
            update_stats(&stats, stat_key);
        }

        stat_key = 0;
        update_stats(&stats, stat_key);
        return NF_ACCEPT;

    case 2:  /* DENY */
        stat_key = 1;
        update_stats(&stats, stat_key);
        return NF_DROP;

    default:  /* PENDING — 아직 DPI 진행 중 */
        info->pkt_count++;
        return NF_ACCEPT;
    }
}

char _license[] SEC("license") = "GPL";
코드 설명
  • 8-14행 커널 6.4+의 kfunc(kernel function) 인터페이스로 BPF 프로그램에서 커널 conntrack을 직접 조회할 수 있습니다. __ksym은 커널 심볼 참조를 나타냅니다.
  • 17-22행 BPF_MAP_TYPE_LRU_HASH는 100만 엔트리의 세션 테이블입니다. LRU 정책으로 가득 차면 가장 오래된 엔트리가 자동 제거됩니다.
  • 55행 SEC("netfilter")는 이 프로그램이 Netfilter 훅에 연결되는 BPF_PROG_TYPE_NETFILTER 타입임을 선언합니다.
  • 69-77행 새 세션을 flow_map에 등록하고 NF_ACCEPT를 반환하여 이후 nftables NFQUEUE → DPI 경로로 진행하게 합니다.
  • 84-91행 DPI가 ALLOW 판정을 내린 세션에서 10패킷 이상 처리되면 offload_map에 등록합니다. 유저스페이스 관리 데몬이 이 맵을 폴링하여 실제 flowtable 오프로드를 수행합니다.

Cilium의 eBPF 방화벽 아키텍처

Cilium은 Kubernetes 환경에서 가장 널리 사용되는 eBPF 기반 CNI + 방화벽 솔루션입니다. 커널의 nf_conntrack과 nftables를 사용하지 않고, eBPF 맵으로 자체 conntrack과 정책 엔진을 구현합니다.

구성요소구현 방식BPF 맵 타입역할
CT map (conntrack)BPF_MAP_TYPE_LRU_HASHper-CPU LRU hash세션 추적, 상태 관리, NAT 매핑
Policy mapBPF_MAP_TYPE_HASHidentity → policy 매핑K8s NetworkPolicy 평가
NAT mapBPF_MAP_TYPE_LRU_HASH원본 → 변환 IP/portClusterIP, NodePort DNAT
Endpoints mapBPF_MAP_TYPE_HASHIP → endpoint identityPod 식별, 보안 ID 할당
Metrics mapBPF_MAP_TYPE_PERCPU_HASHreason → counterdrop/forward 사유별 통계
Events mapBPF_MAP_TYPE_PERF_EVENT_ARRAYring bufferHubble 이벤트 전달 (모니터링)

Cilium의 데이터 플레인 파이프라인은 다음과 같은 순서로 실행됩니다:

/* Cilium CT map 구조 (간소화) */
/* bpf/lib/conntrack.h 참고 */

struct ct_entry {
    __u64 rx_packets;        /* 수신 패킷 수 */
    __u64 rx_bytes;          /* 수신 바이트 */
    __u64 tx_packets;        /* 송신 패킷 수 */
    __u64 tx_bytes;          /* 송신 바이트 */
    __u32 lifetime;          /* 남은 수명 (초) */
    __u16 rx_closing:1;      /* FIN 수신 */
    __u16 tx_closing:1;      /* FIN 송신 */
    __u16 nat46:1;           /* NAT46 변환 */
    __u16 lb_loopback:1;     /* LB 루프백 */
    __u16 seen_non_syn:1;    /* SYN 이후 패킷 확인 */
    __u16 node_port:1;       /* NodePort 플래그 */
    __u8  rev_nat_index;     /* 역NAT 인덱스 */
    __u8  slave;             /* LB backend 슬롯 */
    __u16 ifindex;           /* 출력 인터페이스 */
    __u32 src_sec_id;        /* 소스 보안 identity */
};

/* CT GC (Garbage Collection) — 유저스페이스 에이전트 */
/* cilium-agent가 주기적으로 CT map을 순회하며 만료 엔트리 삭제 */
/* GC 주기: 기본 12초, conntrack-gc-interval 옵션으로 조절 */
/* GC 전략: */
/*   1. lifetime이 0인 엔트리 삭제 */
/*   2. TCP FIN/RST 후 grace period 만료 엔트리 삭제 */
/*   3. NAT map의 orphan 엔트리 정리 */
Cilium의 bpf_redirect_peer(): 이 BPF 헬퍼 함수는 veth 피어를 통해 패킷을 직접 전달하여 호스트 측 네트워크 스택의 ingress 경로를 완전히 우회합니다. 이는 nftables flowtable의 NF_STOLEN 반환과 유사한 효과를 제공하며, Pod-to-Pod 트래픽에서 ~30% 성능 향상을 달성합니다. 커널 5.10+에서 사용 가능합니다.

tc-bpf vs XDP vs BPF netfilter 비교

eBPF 기반 NGFW를 구현할 때 사용할 수 있는 3가지 BPF 프로그램 타입의 특성을 비교합니다:

특성XDP (eXpress Data Path)tc-bpf (Traffic Control)BPF netfilter (6.4+)
실행 시점NIC 드라이버 직후 (skb 생성 전)TC ingress/egress (skb 존재)Netfilter 훅 (conntrack 후)
성능최고 (~100Mpps)높음 (~40Mpps)nftables와 유사 (~20Mpps)
skb 접근제한적 (xdp_md만)전체 skb 접근전체 skb + nf_hook_state
conntrack 접근kfunc (6.4+)kfunc (6.4+)자동 (훅 위치에 따라)
NAT수동 rewrite수동 rewritenf_nat 통합 가능
redirectbpf_redirect(), bpf_redirect_map()bpf_redirect(), bpf_redirect_peer()NF_ACCEPT/DROP만
HW offload제한적 (Netronome 등)미지원미지원
NGFW 역할DDoS pre-filter, rate limitL3/L4 정책, CT, LB, redirectNetfilter 훅 대체, DPI 연동
대표 프로젝트Cloudflare Magic TransitCilium, Calico eBPF아직 초기 (실험적)

각 BPF 타입의 NGFW에서의 역할을 종합하면:

eBPF NGFW 파이프라인 (XDP → tc-bpf → BPF CT → Policy → Redirect) NIC RX 패킷 수신 XDP DDoS Pre-filter Rate Limiter XDP_DROP tc-bpf ingress 정책 identity lookup BPF CT map 세션 조회/생성 LRU Hash Policy map ALLOW/DENY identity 기반 NAT/LB DNAT rewrite backend 선택 TX redirect TC_ACT_SHOT BPF Maps (커널 메모리) XDP Blocklist LPM_TRIE: IP 차단 Rate: per-src 제한 CT map LRU_HASH: 1M entries 5-tuple → ct_entry Policy map HASH: identity 기반 src_id+dst_id → allow/deny NAT map LRU_HASH: DNAT 매핑 ClusterIP → PodIP Events/Metrics PERF_EVENT: Hubble PERCPU: 드롭 통계 Userspace Agent 맵 업데이트 (정책 변경) CT GC (만료 엔트리 정리) 이벤트 소비 (모니터링) cilium-agent | bpftool | prometheus-exporter

흔한 실수와 안티패턴

NGFW 오프로드를 구현할 때 자주 발생하는 실수와 안티패턴 10가지를 정리합니다. 각 실수에 대한 증상, 원인, 올바른 해결 방법을 포함합니다.

#실수증상올바른 방법
1flowtable에 flags offload 없이 HW offload 기대모든 세션이 SW fast path에서만 처리flags offload 명시 + NIC 지원 확인
2nf_conntrack_max 부족새 연결 불가, nf_conntrack: table full 로그예상 동시 세션의 1.5배로 설정
3NFQUEUE fail-open 미설정Suricata 장애 시 전체 트래픽 차단queue ... bypass 옵션 사용
4flowtable timeout > conntrack timeout유령 플로우 (이미 종료된 세션의 HW 규칙 잔존)flowtable timeout < conntrack timeout
5ALG 프로토콜(FTP/SIP)을 무조건 오프로드데이터 채널 생성 실패, 통화 끊김ALG 제어 채널은 오프로드 제외
6eSwitch switchdev 전환 시 VF 미재설정VF가 legacy 모드에서 멈춤, offload 불가VF unbind → switchdev 전환 → VF rebind
7IRQ affinity 미설정단일 코어 과부하, 다른 코어 유휴RX 큐별 IRQ를 별도 코어에 바인딩
8XDP + NFQUEUE 조합 시 순서 실수XDP에서 DROP한 패킷이 DPI에 미도달XDP는 pre-filter만, DPI 대상은 반드시 PASS
9conntrackd 동기화 없이 HA failoverfailover 시 모든 세션 손실conntrackd + VRRP/keepalived 동기화
10HW flow table 크기 초과 무시SW 폴백으로 성능 급락 (HW→SW 전환 시 지연)HW 테이블 사용률 모니터링 + 알림

실수 1: flowtable에 flags offload 누락

# ❌ 잘못된 설정: flags offload 누락
nft add flowtable inet filter ft \
  { hook ingress priority 0; devices = { eth0, eth1 }; }
# → SW fast path만 동작, HW offload 없음

# ✅ 올바른 설정
nft add flowtable inet filter ft \
  { hook ingress priority 0; devices = { eth0, eth1 }; \
    flags offload; }

# 검증: HW offload 확인
conntrack -L --status OFFLOAD 2>/dev/null | wc -l
# 0이면 HW offload 동작하지 않음

# NIC 지원 여부 확인
ethtool -k eth0 | grep hw-tc-offload
# hw-tc-offload: on → HW offload 가능

실수 2: nf_conntrack_max 부족

# 현재 conntrack 사용량 확인
cat /proc/sys/net/netfilter/nf_conntrack_count
cat /proc/sys/net/netfilter/nf_conntrack_max

# ❌ 기본값(65536)은 NGFW에 부족
# 증상: dmesg에 "nf_conntrack: table full, dropping packet" 출력
dmesg | grep "table full"

# ✅ 예상 동시 세션의 1.5배로 설정
# 예: 동시 세션 200K 예상 → 300K으로 설정
sysctl -w net.netfilter.nf_conntrack_max=300000

# 해시 테이블 크기도 함께 조정 (max / 4 권장)
# 부팅 시 모듈 파라미터로 설정 (런타임 변경 불가)
echo "options nf_conntrack hashsize=75000" > \
  /etc/modprobe.d/nf_conntrack.conf

# 메모리 계산: 300K × ~320 bytes = ~96 MB
# 영구 설정
echo "net.netfilter.nf_conntrack_max = 300000" >> /etc/sysctl.d/99-ngfw.conf

실수 3: NFQUEUE fail-open 미설정

# ❌ 잘못된 설정: bypass 없음
# Suricata가 죽으면 모든 트래픽이 DROP됨
nft add rule inet filter forward \
  ct state new queue num 0

# ✅ 올바른 설정: bypass 옵션으로 fail-open
nft add rule inet filter forward \
  ct state new queue num 0-3 fanout,bypass

# fanout: 여러 NFQUEUE에 분산 (Suricata 멀티스레드)
# bypass: NFQUEUE가 비어있으면 자동으로 ACCEPT

# 검증: Suricata 프로세스 상태 확인
ss -nlp | grep suricata
# Suricata가 NFQUEUE를 바인딩하고 있는지 확인

# Suricata 설정 (suricata.yaml)
# nfqueue:
#   mode: accept   ← fail-open (NFQUEUE 바인딩 해제 시 accept)
#   fail-open: yes

실수 4: flowtable timeout > conntrack timeout

# ❌ 위험: conntrack timeout이 먼저 만료되면
# conntrack 엔트리 삭제 → flowtable 엔트리 고아화
# → HW에 유령 규칙 잔존 → 보안 위험

# conntrack timeout 확인
sysctl net.netfilter.nf_conntrack_tcp_timeout_established
# 기본: 432000 (5일)

# ✅ flowtable timeout은 conntrack timeout보다 짧게
# flowtable 기본 timeout: 30초 (자동 갱신)
# 문제가 되는 경우: conntrack timeout을 짧게 줄인 경우
# 예: UDP conntrack timeout을 30초로 줄이면
sysctl -w net.netfilter.nf_conntrack_udp_timeout_stream=60
# flowtable aging은 이보다 짧은 주기로 동작해야 함

# HW flow 엔트리 상태 확인 (mlx5)
tc -s filter show dev eth0 ingress
# → 각 규칙의 packets/bytes 카운터 확인
# 카운터가 오래 정지된 규칙 = 유령 플로우 의심

실수 5: ALG 프로토콜 무조건 오프로드

# ❌ 잘못된 설정: 모든 EST 세션을 무조건 오프로드
nft add rule inet filter forward \
  ct state established flow add @ft

# ✅ 올바른 설정: ALG helper가 있는 세션은 제외
nft add rule inet filter forward \
  ct state established \
  ct helper "" \
  flow add @ft

# ct helper ""는 helper가 없는 세션만 매칭
# FTP, SIP, H.323 등 ALG 세션은 항상 Slow Path 유지

# 현재 ALG helper가 할당된 세션 확인
conntrack -L | grep helper
# 예: tcp  6 300 ESTABLISHED ... helper=ftp

실수 6: eSwitch switchdev 전환 시 VF 미재설정

# ❌ 잘못된 순서: VF가 바인딩된 상태에서 switchdev 전환
# 결과: 전환 실패 또는 VF가 legacy 모드에 잔류

# ✅ 올바른 순서
# 1. VF unbind (모든 VF)
for vf in /sys/bus/pci/devices/0000:04:00.0/virtfn*; do
    pci_addr=$(basename $(readlink $vf))
    echo $pci_addr > /sys/bus/pci/drivers/mlx5_core/unbind 2>/dev/null
done

# 2. switchdev 모드 전환
devlink dev eswitch set pci/0000:04:00.0 mode switchdev

# 3. VF rebind
for vf in /sys/bus/pci/devices/0000:04:00.0/virtfn*; do
    pci_addr=$(basename $(readlink $vf))
    echo $pci_addr > /sys/bus/pci/drivers/mlx5_core/bind 2>/dev/null
done

# 4. 검증
devlink dev eswitch show pci/0000:04:00.0
# mode switchdev inline-mode transport encap-mode basic

# VF representor 확인
ip link show | grep "enp4s0f0v"

실수 7: IRQ affinity 미설정

# 증상: mpstat으로 확인하면 CPU0만 100%, 나머지 idle
mpstat -P ALL 1

# 현재 IRQ 분포 확인
cat /proc/interrupts | grep eth0

# ✅ IRQ affinity 자동 설정 스크립트
# Mellanox NIC의 경우
if [ -x /usr/sbin/mlnx_affinity ]; then
    /usr/sbin/mlnx_affinity
fi

# 수동 설정: RX 큐별 IRQ를 별도 코어에 바인딩
# 예: 8개 RX 큐를 CPU 0-7에 각각 바인딩
for i in $(seq 0 7); do
    irq=$(grep "eth0-$i" /proc/interrupts | awk '{print $1}' | tr -d ':')
    echo $((1 << $i)) > /proc/irq/$irq/smp_affinity
done

# irqbalance 서비스 비활성화 (NGFW에서는 수동 제어 권장)
systemctl stop irqbalance
systemctl disable irqbalance

# RPS (Receive Packet Steering) 설정 (SW fallback)
echo "ff" > /sys/class/net/eth0/queues/rx-0/rps_cpus

실수 8: XDP + NFQUEUE 순서 실수

# ❌ 문제: XDP에서 DPI 대상까지 DROP
# XDP는 Netfilter 이전에 실행되므로
# XDP에서 DROP한 패킷은 NFQUEUE(DPI)에 도달 불가

# ✅ 올바른 설계:
# XDP: 명백한 공격만 차단 (rate limit, blocklist)
# Netfilter/NFQUEUE: DPI 대상 패킷 처리

# XDP에서 DPI 대상 트래픽은 반드시 XDP_PASS
# XDP 프로그램 내 분류 로직:
#   blocklist match → XDP_DROP
#   rate limit 초과 → XDP_DROP
#   그 외 모두 → XDP_PASS (Netfilter로 진행)

# XDP 프로그램 로드
ip link set dev eth0 xdpgeneric obj xdp_prefilter.o sec xdp

# XDP 통계 확인 (DROP된 패킷이 의도한 것인지 검증)
bpftool prog show
bpftool map dump name xdp_stats_map

실수 9: conntrackd 동기화 없이 HA failover

# ❌ conntrackd 없이 VRRP failover 시:
# 모든 기존 세션이 새 마스터에서 NEW로 처리
# → DPI 재검사, flowtable 재등록 → 수초간 성능 저하

# ✅ conntrackd 설정 (/etc/conntrackd/conntrackd.conf)
# Sync {
#     Mode FTFW {            ← Fault Tolerant, Full Write
#         ResendQueueSize 131072
#         PurgeTimeout 60
#     }
#     UDP {
#         IPv4_address 10.0.0.1
#         IPv4_Destination_Address 10.0.0.2
#         Port 3780
#         Interface eth2      ← 동기화 전용 인터페이스
#     }
# }

# conntrackd 시작
systemctl start conntrackd

# VRRP failover 스크립트에 추가
# /etc/keepalived/notify_master.sh:
conntrackd -C /etc/conntrackd/conntrackd.conf -c  # 커밋
conntrackd -C /etc/conntrackd/conntrackd.conf -f  # 플러시
conntrackd -C /etc/conntrackd/conntrackd.conf -R  # 수신 테이블 복원

# 동기화 상태 확인
conntrackd -s
# cache-internal: 150000 entries
# cache-external: 148500 entries  ← 피어와 거의 동기

실수 10: HW flow table 크기 초과 무시

# NIC별 HW flow table 최대 엔트리
# Mellanox CX-6 Dx: ~1M flows
# Intel E810: ~64K flows
# Broadcom P2100: ~500K flows

# ❌ HW 테이블 초과 시 증상:
# - dmesg: "mlx5_core: Failed to add TC flower rule (-ENOSPC)"
# - 새 오프로드 실패 → SW flowtable 폴백 → 성능 급락
# - 기존 HW 규칙은 유지되지만 새 세션은 모두 SW 처리

# ✅ 모니터링 설정
# Prometheus exporter를 위한 HW flow 사용률 체크 스크립트

# 현재 HW offload 세션 수
hw_flows=$(conntrack -L --status OFFLOAD 2>/dev/null | wc -l)
echo "ngfw_hw_flows_current $hw_flows"

# TC filter 통계
tc -s filter show dev eth0 ingress | grep -c "in_hw"
# in_hw = HW에 설치된 규칙 수

# 알림 임계값: HW 한계의 80%에서 경고
hw_max=1000000  # CX-6 Dx
threshold=$(( hw_max * 80 / 100 ))
if [ $hw_flows -gt $threshold ]; then
    logger -p daemon.warning "NGFW: HW flow table usage $hw_flows/$hw_max (${threshold} threshold)"
fi
안티패턴 종합 점검: 위 10가지 실수를 한 번에 점검하는 스크립트를 작성하여 NGFW 배포 전 자동 검증에 활용하세요. 특히 실수 3(fail-open)실수 9(HA 동기화)는 장애 시 서비스 전체에 영향을 미치므로 반드시 사전에 검증해야 합니다. 프로덕션 배포 전에 네트워크 네임스페이스 기반 가상 환경에서 모든 시나리오를 테스트하세요.

실습 가이드

이 섹션에서는 실제 Linux 환경에서 NGFW 오프로드를 단계별로 구현하는 실습을 제공합니다. 네트워크 네임스페이스를 활용하여 물리 장비 없이도 핵심 개념을 실험할 수 있습니다.

실습 환경 구성

요구사항최소 사양권장 사양비고
커널 버전5.13+ (flowtable)6.1+ (full CT offload)uname -r로 확인
nftables0.9.8+1.0.6+nft --version
Suricata6.0+7.0+suricata --build-info
iproute25.10+6.1+ip -V
NIC (Lab 3용)-Mellanox CX-5+ / Intel E810HW offload 실습 시 필요
메모리2 GB8 GB+Suricata + conntrack 테이블
CPU2 코어4+ 코어NFQUEUE 병렬 처리
도구iperf3, curl+ hping3, bpftool, tcpdump테스트 및 디버깅

Lab 1 — 기본 flowtable 오프로드

네트워크 네임스페이스로 라우터를 구성하고, nftables flowtable을 설정한 후 성능을 측정합니다.

#!/bin/bash
# Lab 1: 기본 flowtable 오프로드 실습
# 토폴로지: [client] ---veth--- [router] ---veth--- [server]

set -e

# ============ 1. 네임스페이스 생성 ============
ip netns add client
ip netns add router
ip netns add server

# ============ 2. veth 쌍 생성 ============
# client ↔ router
ip link add c-eth0 type veth peer name r-eth0
ip link set c-eth0 netns client
ip link set r-eth0 netns router

# router ↔ server
ip link add r-eth1 type veth peer name s-eth0
ip link set r-eth1 netns router
ip link set s-eth0 netns server

# ============ 3. IP 설정 ============
# client: 10.0.1.0/24
ip netns exec client ip addr add 10.0.1.10/24 dev c-eth0
ip netns exec client ip link set c-eth0 up
ip netns exec client ip link set lo up
ip netns exec client ip route add default via 10.0.1.1

# router: 게이트웨이
ip netns exec router ip addr add 10.0.1.1/24 dev r-eth0
ip netns exec router ip addr add 10.0.2.1/24 dev r-eth1
ip netns exec router ip link set r-eth0 up
ip netns exec router ip link set r-eth1 up
ip netns exec router ip link set lo up
ip netns exec router sysctl -w net.ipv4.ip_forward=1

# server: 10.0.2.0/24
ip netns exec server ip addr add 10.0.2.20/24 dev s-eth0
ip netns exec server ip link set s-eth0 up
ip netns exec server ip link set lo up
ip netns exec server ip route add default via 10.0.2.1

# ============ 4. 연결 테스트 ============
ip netns exec client ping -c 2 10.0.2.20

echo "=== 네트워크 설정 완료 ==="
# ============ 5. nftables flowtable 설정 (router) ============

ip netns exec router nft -f - <<'EOF'
flush ruleset

table inet ngfw_lab {
    flowtable ft {
        hook ingress priority 0;
        devices = { r-eth0, r-eth1 };
    }

    chain forward {
        type filter hook forward priority 0; policy drop;

        # ESTABLISHED 세션 → flowtable (SW offload)
        ct state established,related flow add @ft counter accept

        # 새 세션 허용 (테스트용 전체 허용)
        ct state new counter accept

        # INVALID 차단
        ct state invalid counter drop
    }

    # NAT (옵션: SNAT 테스트)
    chain postrouting {
        type nat hook postrouting priority 100; policy accept;
        oifname "r-eth1" masquerade
    }
}
EOF

echo "=== nftables 설정 완료 ==="
# ============ 6. 성능 측정 ============

# server에서 iperf3 서버 시작
ip netns exec server iperf3 -s -D

# client에서 iperf3 테스트 (10초, TCP)
echo "=== flowtable 활성 상태 성능 ==="
ip netns exec client iperf3 -c 10.0.2.20 -t 10

# ============ 7. 오프로드 확인 ============

# conntrack 상태 확인 (router)
ip netns exec router conntrack -L
# 예상 출력:
# tcp  6 300 ESTABLISHED src=10.0.1.10 dst=10.0.2.20
#   sport=45678 dport=5201 [OFFLOAD] ...

# flowtable 엔트리 확인
ip netns exec router conntrack -L --status ASSURED
# OFFLOAD 상태가 표시되면 SW flowtable에서 처리 중

# nftables 카운터 확인
ip netns exec router nft list ruleset
# forward chain의 counter: 첫 몇 패킷만 카운트
# (나머지는 flowtable에서 바이패스)

# ============ 8. flowtable 비활성 비교 ============

# flowtable 규칙 제거
ip netns exec router nft delete rule inet ngfw_lab forward handle $(
  ip netns exec router nft -a list chain inet ngfw_lab forward | \
  grep "flow add" | awk '{print $NF}'
)

echo "=== flowtable 비활성 상태 성능 ==="
ip netns exec client iperf3 -c 10.0.2.20 -t 10

# 두 결과 비교: flowtable 활성 시 처리량 향상 확인
# ============ 정리 스크립트 ============
ip netns exec server pkill iperf3 2>/dev/null
ip netns del client
ip netns del router
ip netns del server
echo "=== Lab 1 정리 완료 ==="

Lab 2 — NFQUEUE + Suricata DPI 통합

Lab 1의 환경에 Suricata DPI 엔진을 추가하여, 새 세션은 DPI 검사를 거치고 분류 완료된 세션만 오프로드되도록 구성합니다.

# Lab 2: NFQUEUE + Suricata DPI 통합
# 사전 조건: Lab 1의 네트워크 환경 구성 완료
# Suricata 설치: apt install suricata (또는 dnf install suricata)

# ============ 1. Suricata 설정 ============
# /etc/suricata/suricata.yaml 수정 (주요 항목)

cat > /tmp/suricata-nfqueue.yaml <<'EOF'
# NFQUEUE 모드 설정
nfq:
  mode: accept          # 기본 verdict: accept (fail-open)
  repeat-mark: 1
  repeat-mask: 1
  bypass-mark: 8        # DPI 분류 완료 시 이 mark 설정
  bypass-mask: 8
  fail-open: yes        # Suricata 장애 시 트래픽 허용

# NFQUEUE 수신 설정
nfqueue:
  - queue-num: 0
    threads: 2
  - queue-num: 1
    threads: 2

# 출력: eve.json 로깅
outputs:
  - eve-log:
      enabled: yes
      filename: /var/log/suricata/eve.json
      types:
        - alert
        - http
        - dns
        - tls
EOF
# ============ 2. nftables 규칙 (DPI 통합) ============

ip netns exec router nft -f - <<'EOF'
flush ruleset

table inet ngfw_lab2 {
    flowtable ft {
        hook ingress priority 0;
        devices = { r-eth0, r-eth1 };
    }

    chain forward {
        type filter hook forward priority 0; policy drop;

        # 1. DPI 완료 + bypass mark가 있는 EST 세션 → flowtable
        ct state established meta mark & 0x08 == 0x08 \
            flow add @ft counter accept

        # 2. EST/RELATED (아직 DPI 진행 중) → 통과
        ct state established,related counter accept

        # 3. 새 세션 → NFQUEUE (Suricata DPI)
        ct state new queue num 0-1 fanout,bypass

        # 4. INVALID 차단
        ct state invalid counter drop
    }

    chain postrouting {
        type nat hook postrouting priority 100; policy accept;
        oifname "r-eth1" masquerade
    }
}
EOF

echo "=== DPI 통합 nftables 설정 완료 ==="
# ============ 3. Suricata 시작 및 테스트 ============

# Suricata를 router 네임스페이스에서 NFQUEUE 모드로 시작
ip netns exec router suricata -c /tmp/suricata-nfqueue.yaml \
  -q 0 -q 1 -D

# 잠시 대기 (초기화)
sleep 3

# ============ 4. DPI 탐지 테스트 ============

# HTTP 요청 (DPI가 HTTP로 분류해야 함)
ip netns exec server python3 -m http.server 80 &
sleep 1
ip netns exec client curl -s http://10.0.2.20/

# Suricata 로그 확인
ip netns exec router cat /var/log/suricata/eve.json | \
  python3 -m json.tool | grep -A5 "\"http\""

# conntrack 확인: bypass mark(8)가 설정되었는지 확인
ip netns exec router conntrack -L | grep mark
# mark=8 → DPI 완료, 오프로드 대상

# iperf3 성능 테스트 (DPI 후 오프로드 확인)
ip netns exec server iperf3 -s -D
ip netns exec client iperf3 -c 10.0.2.20 -t 10

# 결과: 첫 몇 패킷은 NFQUEUE 경유, 이후 flowtable 오프로드

echo "=== Lab 2 완료 ==="

Lab 3 — TC flower eSwitch HW offload (SmartNIC 필요)

주의: 이 Lab은 Mellanox ConnectX-5 이상 또는 Intel E810 시리즈 SmartNIC이 필요합니다. 가상 환경에서는 실행할 수 없습니다. SR-IOV VF가 생성되어 있어야 합니다.
#!/bin/bash
# Lab 3: TC flower eSwitch HW offload
# 사전 조건: SmartNIC (CX-5+/E810), SR-IOV VF 생성됨

NIC="enp4s0f0"  # PF 이름 (환경에 맞게 변경)
PCI="0000:04:00.0"  # PCI 주소

# ============ 1. eSwitch switchdev 전환 ============

# 현재 모드 확인
devlink dev eswitch show pci/$PCI

# VF unbind
for vf in /sys/bus/pci/devices/$PCI/virtfn*; do
    pci_addr=$(basename $(readlink $vf))
    echo $pci_addr > /sys/bus/pci/drivers/mlx5_core/unbind 2>/dev/null || true
done

# switchdev 모드 전환
devlink dev eswitch set pci/$PCI mode switchdev

# VF rebind
for vf in /sys/bus/pci/devices/$PCI/virtfn*; do
    pci_addr=$(basename $(readlink $vf))
    echo $pci_addr > /sys/bus/pci/drivers/mlx5_core/bind 2>/dev/null || true
done

# TC HW offload 활성화
ethtool -K $NIC hw-tc-offload on

echo "=== eSwitch switchdev 전환 완료 ==="

# VF representor 확인
ip link show | grep "${NIC}v"
# ============ 2. TC flower ct 규칙 설치 ============

VF_REP="${NIC}v0"  # VF0 representor

# 기존 규칙 초기화
tc qdisc del dev $VF_REP ingress 2>/dev/null || true
tc qdisc add dev $VF_REP ingress

tc qdisc del dev $NIC ingress 2>/dev/null || true
tc qdisc add dev $NIC ingress

# VF → Uplink 방향: conntrack 추적 시작
tc filter add dev $VF_REP ingress prio 1 \
  protocol ip flower \
  ct_state -trk \
  action ct zone 1

# VF → Uplink: EST 세션 → HW forward
tc filter add dev $VF_REP ingress prio 2 \
  protocol ip flower \
  ct_state +trk+est \
  action ct zone 1 \
  action mirred egress redirect dev $NIC

# VF → Uplink: NEW 세션 → CPU
tc filter add dev $VF_REP ingress prio 3 \
  protocol ip flower \
  ct_state +trk+new \
  action pass

# Uplink → VF 방향 (동일 패턴)
tc filter add dev $NIC ingress prio 1 \
  protocol ip flower \
  ct_state -trk \
  action ct zone 1

tc filter add dev $NIC ingress prio 2 \
  protocol ip flower \
  ct_state +trk+est \
  action ct zone 1 \
  action mirred egress redirect dev $VF_REP

tc filter add dev $NIC ingress prio 3 \
  protocol ip flower \
  ct_state +trk+new \
  action pass

echo "=== TC flower ct 규칙 설치 완료 ==="
# ============ 3. HW offload 확인 ============

# TC 규칙이 HW에 설치되었는지 확인
tc -s filter show dev $VF_REP ingress
# 출력에서 "in_hw" 플래그 확인
# in_hw in_hw_count 1 → HW에 성공적으로 설치됨

# conntrack 오프로드 확인
conntrack -L --status OFFLOAD
# [OFFLOAD] 상태의 세션 확인

# HW 카운터 확인
tc -s filter show dev $VF_REP ingress
# packets/bytes 카운터가 증가하면 HW에서 처리 중

# ethtool 통계
ethtool -S $NIC | grep -E "tx_packets|rx_packets|offload"

echo "=== Lab 3 HW offload 확인 완료 ==="

Lab 4 — XDP DDoS Pre-filter

XDP 프로그램을 작성하여 NIC 드라이버 수준에서 SYN flood 공격을 차단합니다. 이 프로그램은 nftables NGFW 파이프라인 앞에 배치됩니다.

/* xdp_synflood_filter.c — XDP SYN flood 방어 프로그램 */
/* 컴파일: clang -O2 -target bpf -c xdp_synflood_filter.c -o xdp_synflood_filter.o */

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

#define MAX_ENTRIES  100000
#define SYN_RATE_LIMIT 100   /* 소스 IP당 초당 SYN 최대 100개 */
#define WINDOW_NS  (1000000000ULL)  /* 1초 (나노초) */

/* 소스 IP별 SYN 카운터 */
struct syn_count {
    __u64 count;           /* 윈도우 내 SYN 수 */
    __u64 window_start;    /* 현재 윈도우 시작 시간 */
};

struct {
    __uint(type, BPF_MAP_TYPE_LRU_HASH);
    __uint(max_entries, MAX_ENTRIES);
    __type(key, __be32);            /* 소스 IP */
    __type(value, struct syn_count);
} syn_rate_map SEC(".maps");

/* IP 차단 리스트 (관리자가 bpftool로 추가) */
struct {
    __uint(type, BPF_MAP_TYPE_LPM_TRIE);
    __uint(max_entries, 10000);
    __uint(map_flags, BPF_F_NO_PREALLOC);
    __type(key, struct lpm_key);
    __type(value, __u32);           /* 1=block */
} blocklist SEC(".maps");

/* 통계 */
struct {
    __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
    __uint(max_entries, 3);  /* 0:pass, 1:drop_rate, 2:drop_block */
    __type(key, __u32);
    __type(value, __u64);
} xdp_stats SEC(".maps");

struct lpm_key {
    __u32 prefixlen;
    __be32 addr;
};

SEC("xdp")
int xdp_syn_filter(struct xdp_md *ctx)
{
    void *data = (void *)(long)ctx->data;
    void *data_end = (void *)(long)ctx->data_end;
    struct ethhdr *eth = data;
    struct iphdr *iph;
    struct tcphdr *tcph;
    __u32 stat_key;
    __u64 *stat_val;

    /* 경계 검사 */
    if ((void *)(eth + 1) > data_end)
        return XDP_PASS;

    if (eth->h_proto != bpf_htons(ETH_P_IP))
        return XDP_PASS;

    iph = (struct iphdr *)(eth + 1);
    if ((void *)(iph + 1) > data_end)
        return XDP_PASS;

    /* 1. IP 차단 리스트 확인 */
    struct lpm_key lpm = {
        .prefixlen = 32,
        .addr = iph->saddr,
    };
    if (bpf_map_lookup_elem(&blocklist, &lpm)) {
        stat_key = 2;
        stat_val = bpf_map_lookup_elem(&xdp_stats, &stat_key);
        if (stat_val) (*stat_val)++;
        return XDP_DROP;
    }

    /* TCP SYN만 rate limit */
    if (iph->protocol != IPPROTO_TCP)
        goto pass;

    tcph = (struct tcphdr *)((void *)iph + (iph->ihl * 4));
    if ((void *)(tcph + 1) > data_end)
        goto pass;

    /* SYN 패킷만 필터링 (SYN=1, ACK=0) */
    if (!(tcph->syn && !tcph->ack))
        goto pass;

    /* 2. SYN rate limiting */
    __u64 now = bpf_ktime_get_ns();
    struct syn_count *sc;

    sc = bpf_map_lookup_elem(&syn_rate_map, &iph->saddr);
    if (sc) {
        if (now - sc->window_start > WINDOW_NS) {
            /* 새 윈도우 시작 */
            sc->count = 1;
            sc->window_start = now;
        } else {
            sc->count++;
            if (sc->count > SYN_RATE_LIMIT) {
                /* rate limit 초과 → DROP */
                stat_key = 1;
                stat_val = bpf_map_lookup_elem(&xdp_stats, &stat_key);
                if (stat_val) (*stat_val)++;
                return XDP_DROP;
            }
        }
    } else {
        struct syn_count new_sc = {
            .count = 1,
            .window_start = now,
        };
        bpf_map_update_elem(&syn_rate_map, &iph->saddr, &new_sc, BPF_ANY);
    }

pass:
    stat_key = 0;
    stat_val = bpf_map_lookup_elem(&xdp_stats, &stat_key);
    if (stat_val) (*stat_val)++;
    return XDP_PASS;
}

char _license[] SEC("license") = "GPL";
# ============ XDP 프로그램 로드 및 테스트 ============

# 1. 컴파일
clang -O2 -g -target bpf \
  -I /usr/include/$(uname -m)-linux-gnu \
  -c xdp_synflood_filter.c -o xdp_synflood_filter.o

# 2. XDP 프로그램 로드 (generic 모드: 모든 NIC 지원)
ip netns exec router \
  ip link set dev r-eth0 xdpgeneric obj xdp_synflood_filter.o sec xdp

# 확인
ip netns exec router ip link show r-eth0
# → "xdpgeneric ... prog/xdp" 표시

# 3. SYN flood 테스트 (hping3 사용)
# hping3 설치: apt install hping3
ip netns exec client hping3 -S -p 80 --flood 10.0.2.20 &
FLOOD_PID=$!
sleep 5
kill $FLOOD_PID

# 4. XDP 통계 확인
ip netns exec router bpftool map dump name xdp_stats
# key: 0 (pass), 1 (drop_rate), 2 (drop_block)
# drop_rate 카운터가 증가했으면 rate limiting 작동

# 5. IP 차단 리스트에 추가 (bpftool 사용)
# bpftool map update name blocklist \
#   key hex 20 00 00 00 0a 00 01 0a \
#   value hex 01 00 00 00
# → 10.0.1.10/32 차단

# 6. XDP 프로그램 제거
ip netns exec router ip link set dev r-eth0 xdpgeneric off

echo "=== Lab 4 완료 ==="
실습 확장 아이디어:
  • Lab 5: conntrackd + keepalived로 HA failover 테스트 (네임스페이스 2개의 라우터)
  • Lab 6: conntrack zone 기반 멀티 테넌트 격리 (Lab 1에 zone 추가)
  • Lab 7: Prometheus + Grafana로 NGFW 메트릭 대시보드 구성
  • Lab 8: nftables + Suricata + XDP 삼중 파이프라인 전체 통합
실습 환경 토폴로지 (Lab 1~4) client (netns) c-eth0: 10.0.1.10/24 iperf3 -c / curl / hping3 gw: 10.0.1.1 router (netns) r-eth0: 10.0.1.1 r-eth1: 10.0.2.1 XDP (L4) nftables flowtable NFQUEUE Suricata DPI (Q0-1) SNAT Lab 4 Lab 1 Lab 1 Lab 2 server (netns) s-eth0: 10.0.2.20/24 iperf3 -s / python3 http gw: 10.0.2.1 veth pair veth pair Lab 3: HW Offload (SmartNIC) eSwitch switchdev + TC flower ct 물리 NIC(CX-5+/E810) 필요 VF rep → HW FDB → PF uplink

참고자료

커널 공식 문서

벤더 기술 문서

DPI/IPS 엔진

표준/벤치마크

고가용성/운영

다음 학습: