NAT 심화 (Network Address Translation)

Linux 커널 NAT 서브시스템: nf_nat 아키텍처, SNAT/DNAT/MASQUERADE, conntrack 연동, nftables/iptables 규칙, NAT helper(ALG), CGNAT, NAT64/NAT46, Stateless NAT, Flowtable, IOMMU, SR-IOV, 전원관리, 성능 튜닝 종합 가이드.

관련 표준: RFC 1918 (사설 IP 대역), RFC 4787 (NAT UDP 동작), RFC 6888 (CGN 요구사항), RFC 791 (IPv4) — 커널 NAT 구현이 따르는 주소 변환 규격입니다. 종합 목록은 참고자료 — 표준 & 규격 섹션을 참고하세요.

NAT 개요

NAT(Network Address Translation)는 패킷의 IP 주소(및 포트)를 변환하여 사설 네트워크와 공인 네트워크를 연결하는 기술입니다. Linux 커널에서 NAT는 Netfilter 프레임워크의 nf_nat 모듈과 conntrack(연결 추적)을 기반으로 동작합니다.

NAT 유형변환 대상Netfilter 훅대표 사용 사례
SNAT 출발지 IP(:포트) POSTROUTING 사설 → 공인 IP 변환 (인터넷 게이트웨이)
MASQUERADE 출발지 IP(:포트) POSTROUTING 동적 IP 환경의 SNAT (PPPoE, DHCP WAN)
DNAT 목적지 IP(:포트) PREROUTING / OUTPUT 포트 포워딩, 로드밸런싱
REDIRECT 목적지 IP → 로컬 PREROUTING / OUTPUT 투명 프록시 (Squid, mitmproxy)
NETMAP 네트워크 대역 1:1 변환 PREROUTING / POSTROUTING 서브넷 간 주소 매핑
FULLCONENAT 출발지 IP:포트 (포트 고정) POSTROUTING / PREROUTING P2P, VoIP, 게임 서버 (모든 외부 호스트 접근 허용)
NAT의 핵심 원리: NAT는 첫 번째 패킷에서만 주소 변환 규칙을 결정합니다. 이후 같은 연결의 패킷은 conntrack 엔트리를 참조하여 자동으로 변환됩니다. 응답 패킷의 역변환도 conntrack이 자동 처리합니다 (SNAT의 응답에 자동 DNAT 적용).

NAT 아키텍처 (커널 내부)

Packet IN conntrack (lookup/create) nf_conntrack_in() DNAT (PREROUTING) nf_nat_ipv4_fn() Routing fwd FORWARD local INPUT → Local SNAT (POSTROUTING) nf_nat_ipv4_fn() conntrack confirm 해시 테이블에 엔트리 확정 Packet OUT conntrack 엔트리 구조 (nf_conn) Original: {src=192.168.1.100:5000, dst=8.8.8.8:443, proto=TCP} Reply: {src=8.8.8.8:443, dst=203.0.113.1:30000, proto=TCP} → SNAT 적용: 출발지 192.168.1.100:5000 → 203.0.113.1:30000 → 응답 패킷: 자동 역변환 dst 203.0.113.1:30000 → 192.168.1.100:5000 NAT 정보 (nf_nat_ext) manip: {type=SRC, range={min_addr, max_addr, min_proto, max_proto}} → 연결의 첫 패킷에서 결정, 이후 동일 연결은 conntrack 참조

NAT 커널 구조체

nf_nat_range2 (변환 범위)

/* include/uapi/linux/netfilter/nf_nat.h */
struct nf_nat_range2 {
    unsigned int          flags;
        /* NF_NAT_RANGE_MAP_IPS        — IP 주소 범위 지정 */
        /* NF_NAT_RANGE_PROTO_SPECIFIED — 포트 범위 지정 */
        /* NF_NAT_RANGE_PROTO_RANDOM   — 포트 무작위 선택 */
        /* NF_NAT_RANGE_PERSISTENT     — 동일 클라이언트 → 동일 매핑 */
        /* NF_NAT_RANGE_PROTO_OFFSET   — 포트 오프셋 (since 5.1) */
    union nf_inet_addr    min_addr;  /* 변환 IP 범위 시작 */
    union nf_inet_addr    max_addr;  /* 변환 IP 범위 끝 */
    union nf_conntrack_man_proto min_proto; /* 포트 범위 시작 */
    union nf_conntrack_man_proto max_proto; /* 포트 범위 끝 */
    union nf_conntrack_man_proto base_proto; /* 포트 오프셋 기준 */
};

nf_conn과 NAT 확장

/* conntrack 엔트리 (net/netfilter/nf_conntrack_core.c) */
struct nf_conn {
    struct nf_conntrack_tuple_hash  tuplehash[IP_CT_DIR_MAX];
        /* [IP_CT_DIR_ORIGINAL] — 원본 방향 튜플 */
        /* [IP_CT_DIR_REPLY]    — 응답 방향 튜플 (NAT 변환 반영) */
    unsigned long                  status;
        /* IPS_SRC_NAT         — SNAT 적용됨 */
        /* IPS_DST_NAT         — DNAT 적용됨 */
        /* IPS_SRC_NAT_DONE    — SNAT 변환 완료 */
        /* IPS_DST_NAT_DONE    — DNAT 변환 완료 */
        /* IPS_CONFIRMED       — 해시 테이블에 확정 */
    struct nf_ct_ext              *ext;
        /* NAT 확장: struct nf_conn_nat (변환 범위 저장) */
        /* Helper 확장: struct nf_conn_help (ALG 데이터) */
};

/* conntrack 튜플: 연결을 식별하는 5-tuple */
struct nf_conntrack_tuple {
    struct {
        union nf_inet_addr              u3;   /* 출발지 IP */
        union nf_conntrack_man_proto     u;    /* 출발지 포트 */
        u_int8_t                        l3num; /* AF_INET/AF_INET6 */
    } src;
    struct {
        union nf_inet_addr              u3;   /* 목적지 IP */
        union {
            __be16                      all;
            struct { __be16 port; }      tcp;
            struct { __be16 port; }      udp;
            struct { __be16 id; }        icmp;
        } u;
        u_int8_t                        protonum; /* IPPROTO_TCP 등 */
        u_int8_t                        dir;
    } dst;
};

NAT 처리 흐름 (커널 함수)

/* NAT 처리 핵심 함수 (net/netfilter/nf_nat_core.c) */

/* 1. NAT 초기화 — 첫 패킷에서 변환 결정 */
unsigned int nf_nat_setup_info(
    struct nf_conn *ct,
    const struct nf_nat_range2 *range,
    enum nf_nat_manip_type maniptype  /* NF_NAT_MANIP_SRC 또는 _DST */
);
/* → conntrack의 reply 튜플을 변환된 주소로 수정 */
/* → 포트 충돌 시 nf_nat_used_tuple()로 빈 포트 탐색 */

/* 2. 패킷 변환 — 실제 IP/포트 헤더 수정 */
unsigned int nf_nat_packet(
    struct nf_conn *ct,
    enum ip_conntrack_info ctinfo,
    unsigned int hooknum,
    struct sk_buff *skb
);
/* → l3proto->manip_pkt()로 IP 헤더 변경 */
/* → l4proto->manip_pkt()로 TCP/UDP 포트 + 체크섬 변경 */

/* 3. 체크섬 업데이트 — incremental checksum */
/* IP 체크섬: inet_proto_csum_replace4() */
/* TCP/UDP 체크섬: inet_proto_csum_replace2() (포트 변경 시) */
/* → 전체 재계산이 아닌 증분 업데이트로 성능 유지 */

SNAT & MASQUERADE

SNAT는 출발지 주소를 변환하여 사설 네트워크 호스트가 공인 IP로 인터넷에 접속할 수 있게 합니다. MASQUERADE는 SNAT의 변형으로, 출력 인터페이스의 현재 IP를 자동으로 사용합니다.

nftables SNAT 설정

# === nftables SNAT 기본 설정 ===

# NAT 테이블 생성
nft add table ip nat

# POSTROUTING 체인 생성 (srcnat 타입)
nft add chain ip nat postrouting { type nat hook postrouting priority srcnat \; }

# SNAT: 사설 대역 → 고정 공인 IP
nft add rule ip nat postrouting \
    ip saddr 192.168.1.0/24 oifname "eth0" \
    snat to 203.0.113.1

# SNAT: IP 풀 (여러 공인 IP로 분산)
nft add rule ip nat postrouting \
    ip saddr 10.0.0.0/8 oifname "eth0" \
    snat to 203.0.113.1-203.0.113.10

# SNAT: 포트 범위 지정 (CGNAT 환경)
nft add rule ip nat postrouting \
    ip saddr 10.0.0.0/8 oifname "eth0" \
    snat to 203.0.113.1:1024-65535

# MASQUERADE: 동적 IP 환경 (PPPoE, DHCP WAN)
nft add rule ip nat postrouting \
    ip saddr 192.168.1.0/24 oifname "ppp0" \
    masquerade

# MASQUERADE: 포트 범위 무작위 선택 (보안 강화)
nft add rule ip nat postrouting \
    ip saddr 192.168.1.0/24 oifname "ppp0" \
    masquerade to :32768-65535 random

# persistent 매핑 (동일 클라이언트 → 동일 외부 IP)
nft add rule ip nat postrouting \
    ip saddr 10.0.0.0/8 oifname "eth0" \
    snat to 203.0.113.1-203.0.113.10 persistent

iptables SNAT 설정 (레거시)

# iptables SNAT
iptables -t nat -A POSTROUTING \
    -s 192.168.1.0/24 -o eth0 \
    -j SNAT --to-source 203.0.113.1

# iptables MASQUERADE
iptables -t nat -A POSTROUTING \
    -s 192.168.1.0/24 -o ppp0 \
    -j MASQUERADE

# MASQUERADE 포트 범위 + 무작위
iptables -t nat -A POSTROUTING \
    -s 192.168.1.0/24 -o eth0 \
    -j MASQUERADE --to-ports 32768-65535 --random

# IP 포워딩 활성화 (필수)
sysctl -w net.ipv4.ip_forward=1
SNAT vs MASQUERADE 선택 기준:
  • 고정 공인 IPSNAT 사용. conntrack 엔트리에 변환 IP를 캐시하여 더 효율적
  • 동적 IP (PPPoE/DHCP)MASQUERADE 사용. 인터페이스 IP 변경 시 기존 conntrack 엔트리 자동 폐기
  • MASQUERADE는 매 패킷마다 인터페이스 IP를 조회하므로 고트래픽 환경에서 SNAT보다 약간 느림

DNAT & 포트 포워딩

DNAT는 목적지 주소를 변환하여 외부에서 내부 서버로 트래픽을 전달합니다. 포트 포워딩, 로드밸런싱, 투명 프록시의 핵심입니다.

nftables DNAT 설정

# === nftables DNAT / 포트 포워딩 ===

# PREROUTING 체인 생성 (dstnat 타입)
nft add chain ip nat prerouting { type nat hook prerouting priority dstnat \; }

# 기본 포트 포워딩: 외부:80 → 내부:8080
nft add rule ip nat prerouting \
    iifname "eth0" tcp dport 80 \
    dnat to 192.168.1.100:8080

# 포트 범위 포워딩
nft add rule ip nat prerouting \
    iifname "eth0" tcp dport 8000-8100 \
    dnat to 192.168.1.100:8000-8100

# 다중 백엔드 로드밸런싱 (라운드 로빈)
nft add rule ip nat prerouting \
    iifname "eth0" tcp dport 443 \
    dnat to numgen inc mod 3 map { \
        0 : 192.168.1.101, \
        1 : 192.168.1.102, \
        2 : 192.168.1.103 \
    }

# 해시 기반 로드밸런싱 (같은 클라이언트 → 같은 서버)
nft add rule ip nat prerouting \
    iifname "eth0" tcp dport 443 \
    dnat to jhash ip saddr mod 3 map { \
        0 : 192.168.1.101, \
        1 : 192.168.1.102, \
        2 : 192.168.1.103 \
    }

# REDIRECT: 투명 프록시 (로컬 포트로 전환)
nft add rule ip nat prerouting \
    iifname "eth0" tcp dport 80 \
    redirect to :3128

# OUTPUT 체인 DNAT (로컬 프로세스 출력에서 변환)
nft add chain ip nat output { type nat hook output priority -100 \; }
nft add rule ip nat output \
    ip daddr 169.254.169.254 tcp dport 80 \
    dnat to 127.0.0.1:8080

iptables DNAT 설정 (레거시)

# 기본 포트 포워딩
iptables -t nat -A PREROUTING \
    -i eth0 -p tcp --dport 80 \
    -j DNAT --to-destination 192.168.1.100:8080

# REDIRECT (투명 프록시)
iptables -t nat -A PREROUTING \
    -i eth0 -p tcp --dport 80 \
    -j REDIRECT --to-ports 3128

# Hairpin NAT (내부 → 외부IP → 다시 내부)
# 내부 클라이언트가 공인IP로 내부 서버 접속 시 필요
iptables -t nat -A POSTROUTING \
    -s 192.168.1.0/24 -d 192.168.1.100 -p tcp --dport 8080 \
    -j MASQUERADE
Hairpin NAT (NAT loopback): 내부 클라이언트가 공인 IP로 내부 서버에 접속할 때 발생하는 문제입니다. DNAT로 목적지가 내부 서버로 변환되지만, 출발지가 같은 서브넷이면 응답이 직접 전달되어 비대칭 경로가 됩니다. 해결: 내부→내부 트래픽에 추가 SNAT/MASQUERADE를 적용하여 패킷이 반드시 NAT 장비를 경유하게 합니다.

conntrack과 NAT 연동 상세

NAT conntrack 생명주기

/* NAT conntrack 엔트리의 생명주기 */

/* 1. NEW — 첫 번째 패킷 (SYN) 도착 */
/*    → nf_conntrack_in(): unconfirmed 리스트에 nf_conn 생성 */
/*    → original 튜플: {192.168.1.100:5000 → 8.8.8.8:443} */

/* 2. NAT 결정 (POSTROUTING) */
/*    → nf_nat_setup_info(): reply 튜플 수정 */
/*    → reply 튜플:   {8.8.8.8:443 → 203.0.113.1:30000} */
/*    → 포트 30000은 nf_nat_used_tuple()로 미사용 확인 */

/* 3. CONFIRMED — POSTROUTING 끝에서 확정 */
/*    → nf_conntrack_confirm(): 해시 테이블 삽입 */
/*    → 이후 동일 연결 패킷은 해시 lookup으로 처리 */

/* 4. ESTABLISHED — 양방향 트래픽 확인됨 */
/*    → 응답 패킷: nf_nat_packet()이 자동 역변환 */
/*    → dst 203.0.113.1:30000 → 192.168.1.100:5000 */

/* 5. 타임아웃/종료 — 엔트리 삭제 */
/*    → TCP FIN/RST: 120초 후 삭제 */
/*    → TCP ESTABLISHED 타임아웃: 기본 432000초 (5일) */
/*    → UDP 타임아웃: 기본 30초 (스트림: 120초) */
/*    → ICMP 타임아웃: 기본 30초 */

conntrack 관리 명령어

# 현재 NAT 연결 목록 (conntrack-tools)
conntrack -L --src-nat
conntrack -L --dst-nat

# NAT 연결 상세 출력 (튜플 포함)
conntrack -L -o extended
# ipv4  tcp  6 431999 ESTABLISHED src=192.168.1.100 dst=8.8.8.8
# sport=5000 dport=443 src=8.8.8.8 dst=203.0.113.1
# sport=443 dport=30000 [ASSURED] mark=0 use=1

# 특정 NAT 엔트리 삭제
conntrack -D --orig-src 192.168.1.100 --orig-dst 8.8.8.8

# 모든 NAT 엔트리 삭제 (서비스 중단 주의!)
conntrack -F

# conntrack 통계
conntrack -S
# cpu=0 found=1234 invalid=56 insert=789 insert_failed=0 drop=0
#       early_drop=0 error=0 search_restart=12

# conntrack 엔트리 수 실시간 모니터링
watch -n 1 'cat /proc/sys/net/netfilter/nf_conntrack_count'

# conntrack 이벤트 실시간 감시
conntrack -E
# [NEW] tcp  6 120 SYN_SENT src=192.168.1.100 ...
# [UPDATE] tcp  6 431999 ESTABLISHED src=192.168.1.100 ...
# [DESTROY] tcp  6 src=192.168.1.100 ...

NAT Helper (ALG)

일부 프로토콜은 페이로드 내에 IP 주소/포트 정보를 포함합니다. NAT는 IP/TCP/UDP 헤더만 변환하므로, 이런 프로토콜은 페이로드도 수정해야 합니다. NAT Helper(Application Layer Gateway, ALG)가 이 역할을 합니다.

프로토콜커널 모듈문제Helper 동작
FTP nf_nat_ftp PORT/PASV 명령에 IP:포트 포함 FTP 데이터 채널 IP/포트를 NAT 변환된 값으로 수정. RELATED conntrack 생성
SIP nf_nat_sip SDP에 미디어 주소/포트 포함 SIP/SDP 헤더의 IP/포트를 NAT 주소로 수정. RTP 포트 RELATED 생성
TFTP nf_nat_tftp 서버가 클라이언트 IP로 직접 응답 TFTP 연결에 대한 RELATED conntrack 기대값(expectation) 생성
IRC (DCC) nf_nat_irc DCC CHAT/SEND에 IP:포트 포함 DCC 명령의 IP/포트를 NAT 주소로 수정
PPTP nf_nat_pptp GRE Call ID로 세션 식별 PPTP 제어 메시지의 Call ID를 변환. GRE NAT 지원
H.323 nf_nat_h323 ASN.1 인코딩된 주소 정보 H.225/H.245 시그널링의 주소 변환
Amanda nf_nat_amanda 백업 데이터 포트 협상 Amanda 데이터 연결 포트 RELATED 생성
# NAT Helper 모듈 로드
modprobe nf_nat_ftp
modprobe nf_nat_sip

# nftables에서 helper 설정
nft add table ip raw
nft add chain ip raw prerouting { type filter hook prerouting priority -300 \; }
nft add ct helper ip raw ftp-helper { type "ftp" protocol tcp \; }
nft add rule ip raw prerouting tcp dport 21 ct helper set "ftp-helper"

# iptables에서 helper 설정
iptables -t raw -A PREROUTING -p tcp --dport 21 -j CT --helper ftp

# 로드된 helper 확인
cat /proc/net/nf_conntrack_expect
# conntrack expectation (RELATED 연결 기대값) 표시

# 보안 주의: helper 자동 할당 비활성화 (권장)
sysctl -w net.netfilter.nf_conntrack_helper=0
# → 명시적으로 CT --helper로 지정한 트래픽만 helper 적용
NAT Helper 보안 주의:
  • Helper는 페이로드를 파싱하므로 취약점 공격 표면이 됩니다. 불필요한 helper는 로드하지 마세요
  • nf_conntrack_helper=0으로 자동 할당을 비활성화하고, 필요한 포트에만 명시적으로 할당하세요
  • FTP는 가능하면 FTPS(포트 990) 또는 SFTP로 대체하여 helper 의존을 제거하세요
  • SIP helper는 복잡한 SIP 시나리오(등록 서버, 포크, 재전송)에서 오동작할 수 있습니다. SBC(Session Border Controller) 사용 권장

NAT 유형 상세 비교

NAT 동작 모델 (RFC 4787)

NAT 유형매핑 규칙필터링 규칙특징
Full Cone
(EIM + EIF)
Endpoint-Independent Endpoint-Independent 외부 어떤 호스트든 매핑된 포트로 접근 가능. P2P에 최적, 보안 취약
Restricted Cone
(EIM + ARF)
Endpoint-Independent Address-Restricted 내부에서 먼저 접촉한 외부 IP만 응답 가능 (포트 무관)
Port Restricted
(EIM + APRF)
Endpoint-Independent Address+Port-Restricted 내부에서 먼저 접촉한 외부 IP:포트만 응답 가능. Linux 기본 동작
Symmetric
(APDM + APRF)
Address+Port-Dependent Address+Port-Restricted 목적지마다 다른 외부 포트 할당. P2P 어려움, 보안 강력
Linux NAT 기본 동작: Linux의 nf_nat은 기본적으로 Port Restricted Cone과 유사하게 동작합니다. 같은 내부 소스에서 나가는 패킷은 가능하면 같은 외부 포트를 재사용하지만(Endpoint-Independent Mapping), conntrack이 응답 패킷을 필터링합니다(Address+Port-Restricted Filtering). NF_NAT_RANGE_PROTO_RANDOM_FULLY 플래그를 사용하면 Symmetric NAT에 가까운 동작을 합니다.

NETMAP (1:1 서브넷 매핑)

# NETMAP: 서브넷 간 1:1 주소 변환
# 10.0.1.x → 10.0.2.x (호스트 부분 유지)

# nftables
nft add rule ip nat prerouting \
    ip daddr 10.0.2.0/24 \
    dnat ip prefix to ip daddr map { 10.0.2.0/24 : 10.0.1.0/24 }
nft add rule ip nat postrouting \
    ip saddr 10.0.1.0/24 \
    snat ip prefix to ip saddr map { 10.0.1.0/24 : 10.0.2.0/24 }

# iptables
iptables -t nat -A PREROUTING -d 10.0.2.0/24 -j NETMAP --to 10.0.1.0/24
iptables -t nat -A POSTROUTING -s 10.0.1.0/24 -j NETMAP --to 10.0.2.0/24

CGNAT (Carrier-Grade NAT)

CGNAT(RFC 6888)는 ISP 수준에서 여러 가입자가 하나의 공인 IP를 공유하는 대규모 NAT입니다. Linux에서는 nf_nat을 활용하여 CGNAT를 구현할 수 있지만, 대규모 환경에서는 성능 튜닝이 핵심입니다.

항목일반 NATCGNAT
가입자 수수십~수백수천~수만
공인 IP당 동시 연결~65,535수십만 (포트 블록 할당)
conntrack 테이블수만 엔트리수백만 엔트리 (4GB+ 메모리)
포트 할당동적 (per-connection)포트 블록 (per-subscriber)
주소 대역RFC 1918100.64.0.0/10 (RFC 6598, 공유 주소 공간)
로깅선택적법적 의무 (수사 요청 대응)
# === CGNAT 설정 예시 (nftables) ===

# CGNAT 전용 주소 대역: 100.64.0.0/10 (RFC 6598)

# 가입자별 포트 블록 할당 (1024개씩)
# 가입자 A: 포트 1024-2047
# 가입자 B: 포트 2048-3071
# ...

nft add rule ip nat postrouting \
    ip saddr 100.64.0.1 oifname "eth0" \
    snat to 203.0.113.1:1024-2047

nft add rule ip nat postrouting \
    ip saddr 100.64.0.2 oifname "eth0" \
    snat to 203.0.113.1:2048-3071

# 대규모 환경에서 nft map 활용
nft add map ip nat subscriber_map { type ipv4_addr : interval . inet_service \; }
nft add element ip nat subscriber_map { \
    100.64.0.1 : 203.0.113.1 . 1024-2047, \
    100.64.0.2 : 203.0.113.1 . 2048-3071, \
    100.64.0.3 : 203.0.113.1 . 3072-4095 \
}

# === conntrack 튜닝 (CGNAT 필수) ===

# 최대 연결 수 증가 (엔트리당 ~320바이트)
sysctl -w net.netfilter.nf_conntrack_max=2000000
# 2M 엔트리 × 320B ≈ 640MB 메모리

# 해시 테이블 크기 (부팅 시 또는 모듈 파라미터)
# echo 524288 > /sys/module/nf_conntrack/parameters/hashsize

# 타임아웃 축소 (CGNAT 환경)
sysctl -w net.netfilter.nf_conntrack_tcp_timeout_established=3600
sysctl -w net.netfilter.nf_conntrack_tcp_timeout_time_wait=30
sysctl -w net.netfilter.nf_conntrack_udp_timeout=30
sysctl -w net.netfilter.nf_conntrack_udp_timeout_stream=60
sysctl -w net.netfilter.nf_conntrack_icmp_timeout=10

# 조기 드롭 활성화 (테이블 포화 시 가장 오래된 unassured 엔트리 삭제)
# 커널 기본 동작: conntrack_max 도달 시 early_drop 시도
CGNAT 로깅 (법적 요구사항): NAT 매핑 로그는 많은 국가에서 법적 의무입니다. Linux에서는 다음과 같이 구현합니다:
  • nft log 또는 iptables -j LOG로 NAT 이벤트 기록 (성능 비용 높음)
  • conntrack -E -o timestamp로 conntrack 이벤트 수집 (ulogd2 연동)
  • Deterministic NAT — 가입자 IP로부터 외부 포트 범위를 결정론적으로 계산하면 연결별 로깅 불필요

NAT64 / NAT46 (IPv4-IPv6 변환)

IPv6 전환 과정에서 IPv4/IPv6 간 통신을 가능하게 하는 NAT 기술입니다.

기술방향설명Linux 구현
NAT64 IPv6 → IPv4 IPv6 전용 클라이언트가 IPv4 서버에 접속. DNS64와 함께 사용 Jool (커널 모듈), tayga (유저 공간)
NAT46 IPv4 → IPv6 IPv4 클라이언트가 IPv6 전용 서버에 접속 Jool, map646
CLAT IPv4 → IPv6 (클라이언트측) 464XLAT 아키텍처의 클라이언트 사이드. 앱이 IPv4 사용 → CLAT가 IPv6로 변환 clatd (유저 공간), Android clatd
SIIT 양방향 (stateless) Stateless IP/ICMP Translation. 1:1 주소 매핑 Jool (SIIT 모드)
MAP-E/MAP-T IPv4-in-IPv6 ISP IPv6 네트워크 위에서 IPv4 서비스 제공 (포트 셋 할당) 커널 ip6_tunnel + nftables 조합
# === NAT64 with Jool (커널 모듈) ===

# Jool 커널 모듈 로드
modprobe jool

# Stateful NAT64 인스턴스 생성
jool instance add "nat64_inst" --iptables --pool6 64:ff9b::/96
# 64:ff9b::/96 = Well-Known Prefix (RFC 6052)
# IPv6 클라이언트가 64:ff9b::8.8.8.8 으로 접속 → IPv4 8.8.8.8로 변환

# IPv4 풀 설정 (NAT64 출발지 주소)
jool pool4 add --tcp 203.0.113.1 1024-65535
jool pool4 add --udp 203.0.113.1 1024-65535
jool pool4 add --icmp 203.0.113.1 0-65535

# DNS64 설정 (BIND9 예시)
# dns64 64:ff9b::/96 {
#     clients { any; };
#     mapped { !rfc1918; any; };
# };

# === 464XLAT (CLAT + NAT64) ===
# 모바일 네트워크에서 보편적 (IPv6-only + CLAT)
# IPv4 앱 → CLAT(IPv4→IPv6) → ISP(IPv6) → PLAT/NAT64(IPv6→IPv4) → IPv4 서버
NAT64/DNS64 작동 원리:
  1. IPv6 클라이언트가 www.example.com의 AAAA 레코드 요청
  2. DNS64 서버: A 레코드(93.184.216.34)만 존재하면 합성 AAAA 생성 → 64:ff9b::93.184.216.34
  3. 클라이언트가 64:ff9b::93.184.216.34로 IPv6 패킷 전송
  4. NAT64 게이트웨이: IPv6 헤더 제거, IPv4 헤더 생성, 출발지를 NAT64 풀 주소로 변환
  5. IPv4 서버(93.184.216.34)가 응답 → NAT64 역변환 → IPv6 클라이언트에 전달

Stateless NAT

Stateless NAT는 conntrack 없이 패킷의 주소를 변환합니다. 상태를 추적하지 않으므로 고성능이지만, 포트 변환이나 여러 호스트의 IP 공유는 불가능합니다.

# === tc (Traffic Control)를 이용한 Stateless NAT ===
# 커널의 TC action으로 패킷 헤더를 직접 수정

# 인바운드: dst 203.0.113.1 → 10.0.1.1
tc qdisc add dev eth0 ingress
tc filter add dev eth0 ingress protocol ip \
    flower dst_ip 203.0.113.1 \
    action pedit ex munge ip dst set 10.0.1.1 \
    action csum ip

# 아웃바운드: src 10.0.1.1 → 203.0.113.1
tc qdisc add dev eth0 root handle 1: prio
tc filter add dev eth0 parent 1: protocol ip \
    flower src_ip 10.0.1.1 \
    action pedit ex munge ip src set 203.0.113.1 \
    action csum ip

# === ip rule/route를 이용한 Stateless NAT ===
# (커널 CONFIG_IP_ROUTE_CLASSID 필요)

# === nftables notrack + header 수정 ===
# raw 테이블에서 NOTRACK으로 conntrack 바이패스
nft add table ip raw
nft add chain ip raw prerouting { type filter hook prerouting priority -300 \; }
nft add chain ip raw output { type filter hook output priority -300 \; }
nft add rule ip raw prerouting ip daddr 203.0.113.1 notrack
nft add rule ip raw output ip saddr 10.0.1.1 notrack

# === XDP/BPF를 이용한 고성능 Stateless NAT ===
# XDP 프로그램에서 직접 IP 헤더 수정 (최고 성능)
# → BPF/XDP 페이지 참조
방식성능포트 변환다중 호스트 공유사용 사례
Stateful (nf_nat) 중간 (conntrack 오버헤드) 지원 지원 일반 게이트웨이, CGNAT
TC action 높음 제한적 (수동) 1:1만 DC 내부, L3 주소 리매핑
XDP/BPF 최고 (드라이버 레벨) 커스텀 구현 커스텀 구현 초고성능 NAT, CDN, 클라우드

Flowtable (NAT 하드웨어 가속)

Flowtable은 nftables의 소프트웨어 기반 NAT 가속 기능입니다. 확립된(ESTABLISHED) 연결의 패킷을 Netfilter 훅을 거치지 않고 빠른 경로(fastpath)로 전달합니다. 하드웨어 오프로드를 지원하는 NIC에서는 NAT 변환까지 하드웨어에서 처리합니다.

# === Flowtable 설정 ===

# flowtable 생성 (하드웨어 오프로드 활성화)
nft add table ip filter
nft add flowtable ip filter f { \
    hook ingress priority 0 \; \
    devices = { eth0, eth1 } \; \
    flags offload \; \
}

# FORWARD 체인에서 established 연결을 flowtable으로 오프로드
nft add chain ip filter forward { type filter hook forward priority 0 \; }
nft add rule ip filter forward \
    ct state established \
    flow add @f

# flowtable 상태 확인
nft list flowtable ip filter f
conntrack -L -o label  # offloaded 플래그 확인

# === 동작 원리 ===
# 1. 첫 패킷 (NEW): 일반 Netfilter 경로 → conntrack + NAT 규칙 적용
# 2. 확립 (ESTABLISHED): flowtable에 flow 등록
# 3. 이후 패킷: ingress에서 직접 flowtable lookup → 빠른 전달
#    → Netfilter 훅(PREROUTING/FORWARD/POSTROUTING) 전부 건너뜀
# 4. HW offload 시: NIC 하드웨어가 flow를 인식하여
#    NAT 변환 + 포워딩을 커널 개입 없이 수행
Flowtable 성능 효과:
  • 소프트웨어 fastpath: 일반 Netfilter 대비 2~3배 처리량 향상
  • 하드웨어 오프로드: 10~100배 처리량 향상 (NIC 의존)
  • 지원 NIC: Memory Technology (MT) 시리즈, Marvell, Realtek 일부 등 (ethtool -k eth0 | grep hw-tc-offload)
  • 제약: flowtable된 패킷은 Netfilter 규칙(필터, 로깅 등)을 통과하지 않음

NAT 성능 튜닝

sysctl 튜닝 파라미터

# === conntrack 관련 ===

# 최대 conntrack 엔트리 수 (기본: nf_conntrack_buckets × 4)
sysctl -w net.netfilter.nf_conntrack_max=1048576

# 해시 버킷 수 (모듈 로드 시 또는 sysfs)
echo 262144 > /sys/module/nf_conntrack/parameters/hashsize
# 최적: conntrack_max / 4 (체인 길이 ~4)

# TCP 타임아웃 조정 (NAT 게이트웨이 환경)
sysctl -w net.netfilter.nf_conntrack_tcp_timeout_established=7200  # 2시간
sysctl -w net.netfilter.nf_conntrack_tcp_timeout_time_wait=30
sysctl -w net.netfilter.nf_conntrack_tcp_timeout_close_wait=30
sysctl -w net.netfilter.nf_conntrack_tcp_timeout_fin_wait=30

# UDP 타임아웃
sysctl -w net.netfilter.nf_conntrack_udp_timeout=30
sysctl -w net.netfilter.nf_conntrack_udp_timeout_stream=60

# ICMP 타임아웃
sysctl -w net.netfilter.nf_conntrack_icmp_timeout=10

# conntrack 느슨한 TCP 추적 (비정상 시작 연결 허용)
sysctl -w net.netfilter.nf_conntrack_tcp_loose=1

# === NAT 포트 범위 ===
# 로컬 포트 범위 (MASQUERADE에서 사용)
sysctl -w net.ipv4.ip_local_port_range="1024 65535"

# === IP 포워딩 ===
sysctl -w net.ipv4.ip_forward=1
sysctl -w net.ipv6.conf.all.forwarding=1

# === 성능 최적화 ===
# conntrack 이벤트 비활성화 (로깅 불필요 시)
sysctl -w net.netfilter.nf_conntrack_events=0

# conntrack 타임스탬프 비활성화
sysctl -w net.netfilter.nf_conntrack_timestamp=0

# conntrack accounting 비활성화
sysctl -w net.netfilter.nf_conntrack_acct=0

커널 설정 (Kconfig)

# NAT 필수 모듈
CONFIG_NF_CONNTRACK=m               # conntrack 코어
CONFIG_NF_NAT=m                     # NAT 코어
CONFIG_NF_NAT_MASQUERADE=y          # MASQUERADE 지원
CONFIG_NF_NAT_REDIRECT=y            # REDIRECT 지원

# nftables NAT
CONFIG_NFT_NAT=m                    # nft nat expression
CONFIG_NFT_MASQ=m                   # nft masquerade
CONFIG_NFT_REDIR=m                  # nft redirect

# iptables NAT (레거시)
CONFIG_IP_NF_NAT=m                  # iptables nat table
CONFIG_IP_NF_TARGET_MASQUERADE=m
CONFIG_IP_NF_TARGET_REDIRECT=m
CONFIG_IP_NF_TARGET_NETMAP=m

# NAT helpers (ALG)
CONFIG_NF_NAT_FTP=m
CONFIG_NF_NAT_SIP=m
CONFIG_NF_NAT_TFTP=m
CONFIG_NF_NAT_IRC=m
CONFIG_NF_NAT_PPTP=m

# Flowtable 하드웨어 오프로드
CONFIG_NF_FLOW_TABLE=m
CONFIG_NF_FLOW_TABLE_INET=m
CONFIG_NFT_FLOW_OFFLOAD=m

# conntrack 기능
CONFIG_NF_CONNTRACK_EVENTS=y        # conntrack 이벤트 알림
CONFIG_NF_CONNTRACK_TIMESTAMP=y     # 연결 타임스탬프
CONFIG_NF_CONNTRACK_LABELS=y        # 연결 레이블
CONFIG_NF_CT_NETLINK=m              # Netlink로 conntrack 관리

NAT 디버깅

# === NAT 규칙 확인 ===

# nftables NAT 규칙 전체 출력
nft list table ip nat

# iptables NAT 테이블 (카운터 포함)
iptables -t nat -L -v -n --line-numbers

# === conntrack 디버깅 ===

# NAT가 적용된 연결만 필터
conntrack -L --src-nat -o extended
conntrack -L --dst-nat -o extended

# 특정 IP의 NAT 연결 추적
conntrack -L --orig-src 192.168.1.100
conntrack -L --reply-src 8.8.8.8

# conntrack 이벤트 실시간 감시 (NAT 전용)
conntrack -E -e NEW,DESTROY --src-nat

# conntrack 통계 (insert_failed가 0이 아니면 문제)
conntrack -S

# === Netfilter 추적 (TRACE) ===
# 패킷이 어떤 규칙을 통과하는지 단계별 추적

# nftables trace
nft add table ip raw
nft add chain ip raw prerouting { type filter hook prerouting priority -300 \; }
nft add rule ip raw prerouting ip saddr 192.168.1.100 meta nftrace set 1
nft monitor trace

# iptables TRACE
iptables -t raw -A PREROUTING -s 192.168.1.100 -j TRACE
iptables -t raw -A OUTPUT -d 192.168.1.100 -j TRACE
# → dmesg 또는 /var/log/kern.log에서 TRACE 출력 확인

# === 일반적인 NAT 문제 디버깅 ===

# 1. NAT가 작동하지 않을 때
# → ip_forward 확인
sysctl net.ipv4.ip_forward
# → FORWARD 체인에서 DROP되는지 확인
iptables -L FORWARD -v -n
nft list chain ip filter forward

# 2. conntrack 테이블 포화
cat /proc/sys/net/netfilter/nf_conntrack_count
cat /proc/sys/net/netfilter/nf_conntrack_max
dmesg | grep "nf_conntrack: table full"

# 3. NAT 포트 고갈
conntrack -L --src-nat | wc -l
# 65535에 근접하면 공인 IP 추가 또는 타임아웃 축소 필요

# 4. 비대칭 라우팅 문제
# → 응답 패킷이 다른 경로로 돌아와 conntrack 불일치
sysctl -w net.netfilter.nf_conntrack_tcp_be_liberal=1
# → rp_filter 확인 (strict 모드가 비대칭 패킷 드롭)
sysctl net.ipv4.conf.all.rp_filter

컨테이너 환경의 NAT

Docker, Kubernetes 등 컨테이너 환경에서 NAT는 네트워킹의 핵심 구성 요소입니다.

환경NAT 구현특징
Docker (bridge) iptables MASQUERADE + DNAT docker0 브릿지에서 컨테이너 IP를 호스트 IP로 SNAT. 포트 매핑은 DNAT
Docker (host) NAT 없음 호스트 네트워크 네임스페이스 공유
Kubernetes (kube-proxy iptables) iptables DNAT + MASQUERADE Service ClusterIP → Pod IP DNAT. NodePort는 추가 DNAT. 규칙 수가 O(n²)으로 증가
Kubernetes (kube-proxy IPVS) IPVS (L4 로드밸런서) + conntrack iptables보다 확장성 우수. O(1) 룩업. 여전히 SNAT은 iptables 사용
Cilium (eBPF) eBPF 기반 NAT iptables/conntrack 완전 대체. TC/XDP 레벨에서 NAT 처리. 대규모 클러스터에 적합
# Docker NAT 규칙 확인
iptables -t nat -L -v -n
# Chain POSTROUTING
# MASQUERADE  all  -- 172.17.0.0/16  0.0.0.0/0
# Chain DOCKER
# DNAT  tcp  -- 0.0.0.0/0  0.0.0.0/0  tcp dpt:8080 to:172.17.0.2:80

# Kubernetes Service NAT 규칙 확인
iptables -t nat -L KUBE-SERVICES -n
# → ClusterIP → KUBE-SVC-xxx 체인 → KUBE-SEP-xxx (endpoint DNAT)

# Kubernetes IPVS 모드
ipvsadm -Ln
# TCP  10.96.0.1:443 rr
#   -> 192.168.1.10:6443  Masq  1  0  0
#   -> 192.168.1.11:6443  Masq  1  0  0

# Cilium eBPF NAT 테이블 확인
cilium bpf nat list
컨테이너 NAT 성능 고려사항:
  • iptables 규칙 폭발 — Kubernetes iptables 모드에서 Service 수 × Endpoint 수만큼 규칙 증가. 5,000+ Service 환경에서 심각한 지연
  • conntrack 경합 — 많은 컨테이너가 동일 conntrack 테이블 공유. insert_failed 카운터 모니터링 필요
  • SNAT 포트 고갈 — 다수의 Pod이 동일 노드 IP로 SNAT. net.ipv4.ip_local_port_range 확대 또는 외부 IP 추가
  • eBPF 대안 — Cilium, Calico eBPF 모드는 conntrack/iptables 오버헤드를 제거하여 대규모 클러스터에서 유리

IOMMU와 NAT

IOMMU(Input-Output Memory Management Unit)는 디바이스의 DMA 접근을 가상 주소로 변환하여 메모리 보호와 격리를 제공합니다. NAT 게이트웨이에서 고성능 패킷 처리를 위해 NIC의 DMA를 이해하는 것이 중요합니다.

IOMMU 기본 개념

항목설명NAT 관련 영향
DMA Remapping 디바이스 DMA 주소를 물리 주소로 변환 (IOVA → PA) NIC의 패킷 DMA 전송 시 IOMMU 변환 오버헤드 발생. 고트래픽 NAT에서 지연 증가 가능
IOMMU 그룹 동일 PCI 버스/브릿지를 공유하는 디바이스 묶음 NAT NIC과 다른 디바이스가 같은 그룹이면 VFIO passthrough 시 함께 할당해야 함
Passthrough 모드 iommu=pt — DMA를 1:1 매핑 (보호 없음, 최고 성능) NAT 전용 장비에서 IOMMU 오버헤드를 제거하여 패킷 처리 속도 극대화
Strict 모드 iommu.strict=1 — IOTLB 즉시 무효화 보안 강화, 성능 저하. NAT 게이트웨이에서는 lazy 모드 권장
Bounce Buffering IOMMU가 매핑하지 못하는 메모리 영역의 DMA를 중간 버퍼로 복사 SWIOTLB 사용 시 패킷 복사 오버헤드 발생. 대규모 NAT에서 병목
# === IOMMU 설정 및 확인 ===

# IOMMU 활성화 확인
dmesg | grep -i iommu
# DMAR: IOMMU enabled   (Intel VT-d)
# AMD-Vi: AMD IOMMUv2   (AMD-Vi)

# IOMMU 그룹 확인 (NAT NIC이 속한 그룹)
find /sys/kernel/iommu_groups/ -type l | sort -V
# /sys/kernel/iommu_groups/15/devices/0000:03:00.0  ← NIC

# 특정 디바이스의 IOMMU 그룹
ls -la /sys/bus/pci/devices/0000:03:00.0/iommu_group/devices/

# === 커널 부트 파라미터 (NAT 성능 최적화) ===

# Passthrough 모드 (최고 성능, DMA 보호 없음)
# GRUB: intel_iommu=on iommu=pt
# 또는: amd_iommu=on iommu=pt

# Lazy IOTLB invalidation (성능 우선)
# GRUB: intel_iommu=on iommu.strict=0

# SWIOTLB 크기 조정 (bounce buffer 부족 방지)
# GRUB: swiotlb=65536

# === IOMMU DMA 매핑 통계 ===
# CONFIG_DMA_API_DEBUG=y 필요
cat /sys/kernel/debug/dma-api/num_errors
cat /sys/kernel/debug/dma-api/num_free
cat /sys/kernel/debug/dma-api/driver_filter

IOMMU가 NAT 성능에 미치는 영향

/* NIC 드라이버의 DMA 매핑 (패킷 수신 경로) */
/* drivers/net/ethernet/ 내 대부분의 드라이버 */

/* 1. 패킷 수신 버퍼 DMA 매핑 */
dma_addr_t dma = dma_map_single(dev, buf, len, DMA_FROM_DEVICE);
/* IOMMU 활성: IOVA 할당 + 페이지 테이블 업데이트 (비용 발생) */
/* IOMMU=pt: 물리 주소 직접 사용 (오버헤드 최소) */

/* 2. 패킷 수신 완료 후 언매핑 */
dma_unmap_single(dev, dma, len, DMA_FROM_DEVICE);
/* IOMMU strict: 즉시 IOTLB invalidate (비용 높음) */
/* IOMMU lazy: 배치 invalidate (비용 낮음) */

/* 3. NAT 변환 후 전송 버퍼 DMA 매핑 */
dma = dma_map_single(dev, skb->data, skb->len, DMA_TO_DEVICE);
/* → 수신 + 전송 양방향 DMA 매핑이 발생하므로 IOMMU 비용이 2배 */

/* NAT 게이트웨이에서의 IOMMU 오버헤드:
 * - iommu=pt (passthrough): ~0ns 추가 지연
 * - iommu.strict=0 (lazy): ~50-100ns per DMA map/unmap
 * - iommu.strict=1: ~200-500ns per DMA map/unmap
 * - 10Mpps NAT 처리 시 strict 모드에서 ~5%+ CPU 오버헤드 */
NAT 게이트웨이 IOMMU 권장 설정:
  • 전용 NAT 장비iommu=pt로 최고 성능 확보. VM/컨테이너 격리가 불필요한 경우
  • 가상화 환경 (KVM) — IOMMU 필수. iommu.strict=0으로 lazy 모드 사용하여 성능/보안 균형
  • DMA 디버깅CONFIG_DMA_API_DEBUG=y로 빌드 후 /sys/kernel/debug/dma-api/에서 매핑 오류 확인
  • SWIOTLB 모니터링cat /sys/kernel/debug/swiotlb/io_tlb_used로 bounce buffer 사용량 확인. 고사용 시 swiotlb= 증가

SR-IOV와 NAT

SR-IOV(Single Root I/O Virtualization)는 하나의 물리 NIC을 여러 가상 함수(VF)로 분할하여 각 VM이나 컨테이너에 독립적인 NIC을 제공합니다. NAT 환경에서 SR-IOV는 가상화 오버헤드를 제거하고, 하드웨어 수준의 패킷 분류와 오프로드를 가능하게 합니다.

SR-IOV 아키텍처

Physical NIC (PF: Physical Function) eSwitch (HW) — MAC/VLAN 필터, Flow Rules, NAT 오프로드 VF0 (VM1) VF1 (VM2) VF2 (Container) VF3 (NAT GW) ... VFn VM1 (Guest) VF0 passthrough VFIO / vDPA VM2 (Guest) VF1 passthrough 직접 DMA Container VF2 netdev macvlan / netns NAT Gateway VF3 + Flowtable HW NAT offload Hypervisor / Host OS — switchdev 드라이버, tc flower, OVS offload eSwitch 규칙 관리: tc, devlink, OVS-DPDK External Network SR-IOV + NAT 오프로드 흐름: 1. 외부 패킷 → PF eSwitch (HW flow 매칭) 2. 매칭 flow: HW에서 NAT 변환 + VF로 전달 3. 미매칭: slowpath → Host → nf_nat → VF 4. ESTABLISHED → flowtable → HW offload 등록 5. 이후 패킷: 완전 HW 처리 (zero CPU)
# === SR-IOV 설정 (NAT 게이트웨이) ===

# 1. SR-IOV 지원 확인
lspci -vvv -s 03:00.0 | grep "SR-IOV"
# Capabilities: [160] Single Root I/O Virtualization (SR-IOV)

# VF 수 확인
cat /sys/class/net/eth0/device/sriov_totalvfs
# 64

# 2. VF 생성
echo 4 > /sys/class/net/eth0/device/sriov_numvfs
# → VF0~VF3 생성. lspci에 Virtual Function으로 표시

# 생성된 VF 확인
ip link show eth0
# eth0: ... master ovs-system
#     vf 0 ..., link-state auto
#     vf 1 ..., link-state auto
#     vf 2 ..., link-state auto
#     vf 3 ..., link-state auto

# 3. VF MAC/VLAN 설정
ip link set eth0 vf 0 mac 00:11:22:33:44:00 vlan 100
ip link set eth0 vf 1 mac 00:11:22:33:44:01 vlan 200
ip link set eth0 vf 3 mac 00:11:22:33:44:03  # NAT GW VF

# 4. VF Rate Limiting (QoS)
ip link set eth0 vf 0 max_tx_rate 1000  # 1Gbps 제한
ip link set eth0 vf 3 max_tx_rate 10000  # NAT GW: 10Gbps

# 5. VF Trust 모드 (promiscuous, 멀티캐스트 허용)
ip link set eth0 vf 3 trust on  # NAT GW는 trust 필요

# 6. VF Spoofcheck (보안)
ip link set eth0 vf 0 spoofchk on   # 일반 VM: MAC 위조 방지
ip link set eth0 vf 3 spoofchk off  # NAT GW: 변환된 MAC/IP 허용

Switchdev 모드와 NAT 오프로드

# === Switchdev 모드 (eSwitch HW 오프로드) ===
# Mellanox ConnectX-5+, Intel E810+ 등 지원

# devlink으로 eSwitch 모드 전환
devlink dev eswitch set pci/0000:03:00.0 mode switchdev
# → VF의 representor netdev 생성 (eth0_0, eth0_1, ...)

# eSwitch 모드 확인
devlink dev eswitch show pci/0000:03:00.0
# pci/0000:03:00.0: mode switchdev inline-mode none encap-mode basic

# === tc flower를 이용한 HW NAT 오프로드 ===
# tc flower 규칙을 eSwitch HW에 직접 설치

# DNAT 오프로드: 외부:80 → VF2(Container):8080
tc filter add dev eth0 ingress protocol ip \
    flower ip_proto tcp dst_port 80 \
    action pedit ex munge ip dst set 192.168.1.102 \
    action pedit ex munge tcp dport set 8080 \
    action csum ip tcp \
    action mirred egress redirect dev eth0_2 \
    skip_hw  # 또는 skip_sw (HW만 사용)

# SNAT 오프로드: VF → 외부 (출발지 변환)
tc filter add dev eth0_2 ingress protocol ip \
    flower ip_proto tcp \
    action pedit ex munge ip src set 203.0.113.1 \
    action csum ip tcp \
    action mirred egress redirect dev eth0

# === nftables Flowtable + SR-IOV HW 오프로드 ===
# Flowtable이 SR-IOV eSwitch의 HW flow table에 규칙 설치
nft add flowtable ip filter f { \
    hook ingress priority 0 \; \
    devices = { eth0, eth0_0, eth0_1, eth0_2, eth0_3 } \; \
    flags offload \; \
}
nft add rule ip filter forward ct state established flow add @f

# HW 오프로드 상태 확인
tc -s filter show dev eth0 ingress
# → in_hw 또는 in_hw_count: HW에 설치된 규칙 수
ethtool -S eth0 | grep offload

VFIO와 VM NAT Passthrough

# === VFIO: VF를 VM에 직접 할당 ===
# IOMMU 필수 (intel_iommu=on 또는 amd_iommu=on)

# 1. VF의 PCI 주소 확인
lspci | grep "Virtual Function"
# 03:00.2 Ethernet controller: ... (VF0)

# 2. VF를 vfio-pci 드라이버에 바인딩
echo 0000:03:00.2 > /sys/bus/pci/devices/0000:03:00.2/driver/unbind
echo "vfio-pci" > /sys/bus/pci/devices/0000:03:00.2/driver_override
echo 0000:03:00.2 > /sys/bus/pci/drivers/vfio-pci/bind

# 3. QEMU/KVM에서 VF passthrough
qemu-system-x86_64 \
    -device vfio-pci,host=03:00.2 \
    -netdev user,id=nat_net \
    # VM 내부에서 VF NIC이 직접 보임 (virtio 오버헤드 없음)

# 4. libvirt XML 설정
# <hostdev mode='subsystem' type='pci' managed='yes'>
#   <source>
#     <address domain='0x0000' bus='0x03' slot='0x00' function='0x2'/>
#   </source>
# </hostdev>
SR-IOV NAT 성능 비교:
  • virtio (소프트웨어) — vhost-net 사용 시 ~2-4 Mpps. 모든 NAT 처리를 호스트 CPU에서 수행
  • SR-IOV VF passthrough — ~10-14 Mpps. DMA 직접 접근으로 호스트 오버헤드 제거. VM 내부에서 NAT 처리
  • SR-IOV + HW offload — 25+ Mpps. eSwitch가 NAT 변환까지 HW에서 수행. CPU 사용 거의 없음
  • 제약 — HW offload는 단순 NAT(SNAT/DNAT)만 지원. ALG, 복잡한 nftables 규칙은 SW fallback
구성IOMMU 필요NAT 처리 위치성능유연성
virtio + host NAT 불필요 Host 커널 (nf_nat) 낮음 높음 (모든 NAT 기능)
VF passthrough + guest NAT 필수 Guest 커널 (nf_nat) 높음 높음 (모든 NAT 기능)
Switchdev + tc offload 불필요 eSwitch HW 최고 제한적 (단순 NAT만)
Switchdev + Flowtable offload 불필요 첫 패킷: SW, 이후: HW 최고 중간 (첫 패킷만 SW 규칙 적용)

전원관리와 NAT

전원관리(Power Management)는 NAT 게이트웨이의 성능과 지연에 직접적인 영향을 미칩니다. 절전 기능이 패킷 처리 지연을 증가시키거나, 인터럽트 전달을 지연시켜 NAT 성능을 저하시킬 수 있습니다. 고성능 NAT 환경에서는 절전 기능을 적절히 제어해야 합니다.

CPU C-states와 패킷 처리 지연

C-state상태복귀 지연NAT 영향
C0 활성 (실행 중) 0ns 없음 — 최적 상태
C1/C1E Halt (클럭 정지) ~1-10μs 미미 — 대부분 허용 가능
C3 Sleep (L1/L2 캐시 일부 플러시) ~50-100μs 패킷 처리 jitter 증가. UDP 실시간 트래픽에 영향
C6 Deep Sleep (전압 차단) ~100-500μs 심각 — 패킷 처리 지연, conntrack 타임아웃 정확도 저하
C7+ Package C-state (전체 코어) ~1ms+ 치명적 — 고트래픽 NAT에서 패킷 드롭 유발 가능
# === C-state 제어 (NAT 게이트웨이) ===

# 현재 C-state 설정 확인
cat /sys/devices/system/cpu/cpu0/cpuidle/state*/name
cat /sys/devices/system/cpu/cpu0/cpuidle/state*/latency  # μs
cat /sys/devices/system/cpu/cpu0/cpuidle/state*/usage

# 특정 C-state 비활성화 (C3 이상 차단)
for cpu in /sys/devices/system/cpu/cpu*/cpuidle/state3/disable; do
    echo 1 > $cpu
done

# 부팅 시 C-state 제한 (권장: NAT 전용 서버)
# GRUB: intel_idle.max_cstate=1 processor.max_cstate=1
# → C1까지만 허용, C3 이상 차단

# 또는 완전 비활성화 (최저 지연, 최고 전력 소모)
# GRUB: idle=poll
# → CPU가 유휴 시에도 busy-wait. 전력 소모 매우 높음

# PM QoS로 런타임 제어 (프로그래밍 방식)
# /dev/cpu_dma_latency에 최대 허용 지연(μs) 기록
# 0을 쓰면 C0 유지 (파일이 열려 있는 동안)
exec 3> /dev/cpu_dma_latency
echo -ne '\x00\x00\x00\x00' >&3  # 0μs 제한
# → fd 3이 열려 있는 동안 C0 유지

# tuned 프로파일 (Red Hat 계열)
tuned-adm profile latency-performance
# 또는
tuned-adm profile network-latency

P-states와 CPU 주파수 관리

# === CPU 주파수 거버너 (NAT 성능 최적화) ===

# 현재 거버너 확인
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor

# performance 거버너 설정 (항상 최대 주파수)
for gov in /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor; do
    echo performance > $gov
done

# Intel P-state 드라이버 제어
cat /sys/devices/system/cpu/intel_pstate/min_perf_pct
echo 100 > /sys/devices/system/cpu/intel_pstate/min_perf_pct
# → 최소 성능을 100%로 설정 (항상 최대 주파수)

# Turbo Boost 활성화 확인
cat /sys/devices/system/cpu/intel_pstate/no_turbo
# 0 = Turbo 활성, 1 = Turbo 비활성

# AMD: amd-pstate 드라이버
# GRUB: amd_pstate=active
cat /sys/devices/system/cpu/amd_pstate/status

NIC 전원관리와 인터럽트

# === NIC 절전 기능 (NAT에 미치는 영향) ===

# PCIe ASPM (Active State Power Management) 확인
lspci -vvv -s 03:00.0 | grep "ASPM"
# LnkCap: ASPM L0s L1
# LnkCtl: ASPM L0s L1 Enabled

# ASPM 비활성화 (NAT 게이트웨이 필수)
# GRUB: pcie_aspm=off
# 또는 런타임:
echo 0 > /sys/bus/pci/devices/0000:03:00.0/link/l1_aspm

# NIC PCI 런타임 PM 비활성화
echo on > /sys/bus/pci/devices/0000:03:00.0/power/control
# "on" = 항상 활성, "auto" = 자동 절전 (NAT에서는 "on" 권장)

# === 인터럽트 코얼레싱 (Interrupt Coalescing) ===
# NIC이 인터럽트를 모아서 전달 → 처리량↑, 지연↑

# 현재 설정 확인
ethtool -c eth0
# rx-usecs: 50   ← 50μs 동안 인터럽트 대기
# rx-frames: 64  ← 64프레임 모일 때까지 대기

# 저지연 NAT 설정 (인터럽트 빈도 증가)
ethtool -C eth0 rx-usecs 10 rx-frames 16
ethtool -C eth0 tx-usecs 10 tx-frames 16

# 적응형 인터럽트 (트래픽 패턴에 따라 자동 조정)
ethtool -C eth0 adaptive-rx on adaptive-tx on

# === Energy Efficient Ethernet (EEE) ===
# 유휴 시 링크 속도 감소 → 복귀 시 지연 발생

# EEE 상태 확인
ethtool --show-eee eth0

# EEE 비활성화 (NAT 게이트웨이 권장)
ethtool --set-eee eth0 eee off

# === Wake-on-LAN (WoL) ===
# NAT 게이트웨이에서 WoL은 일반적으로 불필요
ethtool -s eth0 wol d  # WoL 비활성화

NAPI Busy Polling과 전원 효율

# === NAPI와 Busy Polling ===
# 일반 NAPI: 인터럽트 → softirq → poll (절전 친화)
# Busy Poll: 소켓 poll에서 NIC을 직접 폴링 (저지연, 전력↑)

# Busy Poll 전역 활성화
sysctl -w net.core.busy_poll=50       # poll() 시 50μs busy-wait
sysctl -w net.core.busy_read=50       # read() 시 50μs busy-wait

# NAPI 가중치 (한 번의 poll에서 처리할 최대 패킷)
sysctl -w net.core.netdev_budget=600          # 기본 300
sysctl -w net.core.netdev_budget_usecs=8000   # 기본 2000μs
# → NAT 게이트웨이: 높은 값으로 설정하여 한 번에 많은 패킷 처리

# === RPS/RFS (Receive Packet/Flow Steering) ===
# 멀티코어에서 패킷 처리를 분산 — 특정 코어가 유휴 방지

# RPS 설정: 모든 코어에 분산 (8코어 예시)
echo ff > /sys/class/net/eth0/queues/rx-0/rps_cpus

# RFS 설정: 소켓이 동작하는 코어에서 패킷 처리
echo 32768 > /proc/sys/net/core/rps_sock_flow_entries
echo 4096 > /sys/class/net/eth0/queues/rx-0/rps_flow_cnt

# === IRQ Affinity (NAT 전용 코어 할당) ===
# NIC 인터럽트를 특정 코어에 고정 → C-state 진입 방지

# NIC IRQ 번호 확인
grep eth0 /proc/interrupts
# 32: ... eth0-TxRx-0
# 33: ... eth0-TxRx-1

# IRQ를 코어 0, 1에 고정
echo 1 > /proc/irq/32/smp_affinity
echo 2 > /proc/irq/33/smp_affinity

# irqbalance 비활성화 (수동 IRQ 관리 시)
systemctl stop irqbalance
systemctl disable irqbalance

NAT 게이트웨이 전원관리 체크리스트

항목저지연 NAT 설정절전 균형 설정확인 방법
C-state max_cstate=1 max_cstate=3 cat /sys/devices/system/cpu/cpu*/cpuidle/state*/disable
CPU Governor performance schedutil cat /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor
Turbo Boost 활성 (no_turbo=0) 활성 cat /sys/devices/system/cpu/intel_pstate/no_turbo
PCIe ASPM pcie_aspm=off default lspci -vvv | grep ASPM
NIC PCI PM power/control=on auto cat /sys/bus/pci/devices/*/power/control
EEE 비활성 활성 ethtool --show-eee eth0
인터럽트 코얼레싱 rx-usecs 10 adaptive-rx on ethtool -c eth0
NAPI Budget 600+ 300 (기본) sysctl net.core.netdev_budget
IRQ Affinity 수동 고정 irqbalance cat /proc/irq/*/smp_affinity
전원관리 관련 핵심 주의사항:
  • C-state과 conntrack 타이머 — 깊은 C-state에서 복귀 시 타이머 정확도가 떨어져 conntrack 엔트리의 타임아웃 처리가 지연될 수 있습니다. CGNAT 환경에서 특히 문제
  • PCIe ASPM과 DMA — ASPM L1에서 복귀하는 동안 NIC의 DMA 전송이 지연됩니다. 고속 NAT에서 pcie_aspm=off 필수
  • EEE와 링크 지연 — EEE 활성 시 유휴→활성 전환에 16~30μs 추가 지연. 버스트 트래픽 시 첫 패킷 지연 발생
  • Thermal Throttling — 절전 비활성화로 발열 증가 시 CPU가 자동으로 클럭을 낮춤. 충분한 냉각 확보 필수. cat /sys/devices/platform/coretemp.0/hwmon/hwmon*/temp*_input
  • 전력 비용idle=poll + performance 거버너 조합은 서버당 50-100W+ 추가 전력 소모. 데이터센터 규모에서 비용 고려
  • SR-IOV VF와 전원관리 — VF passthrough 환경에서 Guest OS가 자체 전원관리를 할 수 있음. Host의 PM 설정이 Guest에 전파되지 않으므로 양쪽 모두 설정 필요

참고 사항

커널 소스 참고 경로:
  • net/netfilter/nf_nat_core.c — NAT 코어 (변환 결정, 패킷 수정)
  • net/netfilter/nf_nat_masquerade.c — MASQUERADE 구현
  • net/netfilter/nf_nat_redirect.c — REDIRECT 구현
  • net/netfilter/nf_conntrack_core.c — conntrack 코어
  • net/netfilter/nf_conntrack_proto_tcp.c — TCP 연결 추적
  • net/netfilter/nft_nat.c — nftables NAT expression
  • net/netfilter/nf_flow_table_core.c — Flowtable 코어
  • include/uapi/linux/netfilter/nf_nat.h — NAT uAPI 구조체
  • include/net/netfilter/nf_nat.h — NAT 내부 헤더
  • drivers/iommu/ — IOMMU 드라이버 (intel, amd, arm-smmu)
  • drivers/pci/iov.c — SR-IOV PCI 코어
  • drivers/net/ethernet/mellanox/mlx5/core/eswitch*.c — eSwitch/switchdev 구현
  • drivers/vfio/pci/ — VFIO PCI passthrough
  • drivers/cpuidle/ — C-state 관리
  • drivers/cpufreq/ — CPU 주파수 거버너
NAT 운영 시 핵심 주의사항:
  • conntrack 테이블 포화nf_conntrack_max에 도달하면 모든 새 연결이 드롭됩니다. 모니터링 필수
  • 포트 고갈 — 하나의 공인 IP당 TCP 동시 연결 상한은 ~65,000. 초과 시 공인 IP 추가 또는 포트 범위 확대
  • NAT와 IPSec — ESP 프로토콜은 포트 개념이 없어 NAT 통과 불가. NAT-T(UDP 4500 캡슐화) 필수
  • Path MTU 문제 — NAT가 IP 헤더를 변경하면 PMTUD ICMP 에러가 내부 호스트에 도달하지 못할 수 있음. MSS clamping 권장: iptables -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu
  • FTP/SIP 등 ALG — NAT helper가 페이로드를 수정하므로 TLS 암호화와 충돌. FTPS, SRTP 환경에서는 helper 비활성화 필요
  • 비대칭 라우팅 — 멀티호밍 환경에서 요청/응답 경로가 다르면 conntrack이 불일치하여 패킷 드롭. nf_conntrack_tcp_be_liberal=1 또는 정책 라우팅으로 대칭 경로 보장