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, 전원관리, 성능 튜닝 종합 가이드.
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 커널 구조체
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
- 고정 공인 IP →
SNAT사용. 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
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 적용
- 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 어려움, 보안 강력 |
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를 구현할 수 있지만, 대규모 환경에서는 성능 튜닝이 핵심입니다.
| 항목 | 일반 NAT | CGNAT |
|---|---|---|
| 가입자 수 | 수십~수백 | 수천~수만 |
| 공인 IP당 동시 연결 | ~65,535 | 수십만 (포트 블록 할당) |
| conntrack 테이블 | 수만 엔트리 | 수백만 엔트리 (4GB+ 메모리) |
| 포트 할당 | 동적 (per-connection) | 포트 블록 (per-subscriber) |
| 주소 대역 | RFC 1918 | 100.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 시도
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 서버
- IPv6 클라이언트가
www.example.com의 AAAA 레코드 요청 - DNS64 서버: A 레코드(
93.184.216.34)만 존재하면 합성 AAAA 생성 →64:ff9b::93.184.216.34 - 클라이언트가
64:ff9b::93.184.216.34로 IPv6 패킷 전송 - NAT64 게이트웨이: IPv6 헤더 제거, IPv4 헤더 생성, 출발지를 NAT64 풀 주소로 변환
- 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 변환 + 포워딩을 커널 개입 없이 수행
- 소프트웨어 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
- 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=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 아키텍처
# === 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>
- 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 expressionnet/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 passthroughdrivers/cpuidle/— C-state 관리drivers/cpufreq/— CPU 주파수 거버너
- 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또는 정책 라우팅으로 대칭 경로 보장