-
TC / IPTables / Conntrack 이해해보기카테고리 없음 2025. 9. 14. 16:38
리눅스 환경에서 네트워크를 다루다 보면 tc, iptables, netfilter, conntrack 같은 개념을 자주 접하게 됩니다. 하지만 이 네 가지가 정확히 어떤 관계인지, 언제 쓰는지, 흐름이 어떻게 되는지 헷갈릴 수 있기때문에 이번 글에서는 해당 내용들을 정리해보려고 합니다. 즉, 우리가 보고자 하는 영역은 아래 그림에서 빨간색 네모친 부분입니다.
등장배경
리눅스 커널은 서버와 네트워크 장비의 역할을 동시에 수행할 수 있도록 발전해 왔고, 네트워크가 발달함과 동시에 아래와 같은 기능들이 요구되었고, 아래와 같은 기능들이 등장하게 되었고, 이를 통해 커널에서는 단순 패킷 전달을 넘어서 트래픽 관리·보안·상태 추적이 가능하게 되었습니다.
네트워크 트래픽 제어(QoS, shaping, policing)로 대역폭 제한, 지연, 우선순위를 주고 싶었다 → TC (Traffic Control)
사용자 공간에서 다룰 수 있는 패킷 필터링과 방화벽 → iptables (netfilter 프레임워크 활용)
연결 상태 기반 제어(stateful firewall, NAT) → conntrack (netfilter 프레임워크 활용)활용방법
TC
- 백업/배치 작업이 트래픽을 독점 → 속도 제한으로 전체 지연 방지
- 테스트용 지연/패킷손실/지터 주입(netem)으로 장애 재현
- 서비스별 우선순위(VoIP 우선 등)
Iptables
- 방화벽 정책 설정
- NAT/포트포워딩 (가정/클러스터 게이트웨이) 설정
- 간단한 패킷 마킹/리다이렉션/로그
Conntrack
- “ESTABLISHED만 허용” 같은 상태기반 방화벽
- 세션 매핑(NAT) 유지 → 리턴 패킷을 제자리로 돌려줌
- Kubernetes 세션 어피니티/서비스 연결 유지에도 관여
동작원리
각 모듈의 플로우
각 모듈은 아래와 같은 과정을 거치게 됩니다.
서버의 NIC 장치를 통해서 패킷이 들어오게 되면, TC -> Netfilter (IPTables/Conntrack) 을 조회하는 순서로 동작하는걸 이해하고 있어야합니다.Ingress(NIC RX) └─[선택] tc ingress/clsact: filter+action(Policing/Drop/Mirror/Redirect) └─ netfilter PREROUTING (raw→mangle→nat:DNAT) + conntrack(NEW/조회) └─ Routing 결정 ├─ 로컬이면 → INPUT(mangle→filter) + conntrack 조회 → 소켓 └─ 포워딩이면 → FORWARD(mangle→filter) + conntrack 조회 └─ POSTROUTING(mangle→nat:SNAT) + conntrack NAT정보 적용 └─ tc egress qdisc(Shaping/Queuing/Scheduling) → NIC TX(Egress)
TC
아래 그림을 보시면 알 수 있듯이 TC는 IP 스택 하위에 존재하고, QDisk(Queuing Discipline)를 가지고 있습니다.
QDisk는 간단하게 말하자면, 패킷을 우선순위에 따라 분류한다고 보시면 됩니다TC의 QDisk는 일반적으로는 아래와 같은 트리구조를 갖는데 각각은 아래와 같은 특징을 갖습니다.
- qdisc (root) 설정 → 전체 대역폭 제한
- class를 사용해서 자식(child) class를 만듦 → 기본 속도(rate) + 최대 허용 속도(ceil) 부여
- filter로 트래픽을 분류하여 해당 class 로 보내는 기준을 설정함 (예: 목적지 IP 기반)
- 자식 class 간의 대역폭 경쟁 발생시 부모 class에서 정의한 전체 대역폭 안에서 “borrow / 공유(borrowing)” 가능
위 그림을 설명해보자면, root 1:0 이라고 되어있는 부분은 ID를 의미하며, 1:1, 1:10, 1:30 에서 1은 부모의 ID를, 1,10,30은 부모 하위의 자녀의 ID를 의미합니다. 각각은 클래스로 나누어져 있고, 각 클래스는 자신이 허용할 수 있는 최대 전송비율을 가지고 있습니다. 예를들어, 트리 가장 하위의 1:10은 최대 80mbits를 전송할 수 있는거죠. 하지만, 1:10, 1:30이 각각 전송할 수 있는 최대 전송값 80mbits를 사용하게 된다면 어떻게될까요?
두 값의 합은 160mbits인데 최대 160mbits를 전송할 수 있는걸까요? 정답은 아닙니다. 부모 클래스의 최대 전송값이 100mbits이기 때문에, 설정에 따라 다르겠지만 1:10 클래스와 1:30 클래스는 비율에 따라 각각 최대 50mbits씩 전송 가능하게 됩니다. 추가로, 그림의 filter는 u1, u2호스트와의 통신시에는 각각 1:10, 1:30 클래스로 가게끔 if문이 정해져있는데,filter는 이와 같이 호스트를 각 클래스로 분류하는 역할을 하게 됩니다.결국 TC의 QDisk의 컴포넌트들은 아래와 같은 역할을 하게 됩니다.
- Class: 대역폭 제어 단위 → 기본 할당(rate) + 최대 허용(ceil) + 부모/자식 관계
- Filter: 트래픽 식별자 → 어떤 패킷이 어느 class 소속인지 결정
IPTables
iptables의 흐름을 이해하기 전에 Table과 Chain에 대한 이해가 필요합니다.
Table
iptables는 체인이라는 규칙을 연결한 테이블 구조로 되어 있습니다.
각 테이블은 자신만의 고유한 사슬 집합을 가지고 있지만,
사용자가 공통태그와 관련된 규칙집합을 만들기 위해 사용자 정의 사슬을 생성할 수 있으며.
아래와 같은 5개의 테이블이 있습니다.- filter : iptables의 기본 테이블로 패킷 필터링 담당
- nat : Network Address Translation, IP 주소 변환
- mangle : 패킷 데이터를 변경하는 특수 규칙을 적용하는 테이블, 성능향상을 위한 TOS(Type of Service) 설정
- raw : 넷필터의 연결추적 하위시스템과 독립적으로 동작해야 하는 규칙을 설정하는 테이블
- security : 리눅스 보안 모듈인 SELinux에 의해 사용되는 MAC(Mandatory Access Control) 네트워크 관련 규칙 적용
Chain
일치하는 패킷의 규칙을 연결하는 단위로, INPUT, OUTPUT, FORWARD가 내장 체인입니다.- INPUT : 시스템 외부에서 내부로 들어오는 체인
- OUTPUT : 시스템 내부에서 외부로 나가는 체인
- FORWARD : 시스템을 통과하는 체인
Packet은 Netfilter에서 설정한 여러 Hook들을 통과하게 되며,
Hook별로 iptables에서 정의한 각 Chain들의 Rule들을 체크하고, 해당 Rule에 매칭되면 특정 Action을 Trigger합니다.위의 그림에서 볼 수 있듯이 미리 정의해놓은 Hook들을 Packet들은 거쳐가게 됩니다.
Hook 별로 우리가 iptables 라는 명령어를 통해서 각 Chain에 Rule들을 세팅할 수 있고,
해당 Rule에 매칭이 될 경우 특정 Action을 수행하도록 합니다.전체적인 흐름은 아래와 같습니다.
주요 명령어
TC: Egress Shaping + Ingress Policing
# 통계 보며 상태 파악 tc -s qdisc show dev eth0 tc -s filter show dev eth0 parent ffff: # ingress 측 # egress: 100Mbit ceiling, 기본 클래스 50Mbit tc qdisc add dev eth0 root handle 1: htb default 10 tc class add dev eth0 parent 1: classid 1:10 htb rate 50mbit ceil 100mbit tc filter add dev eth0 parent 1: protocol ip prio 1 u32 \ match ip dport 443 0xffff flowid 1:10 # netem: 80ms 지연 + 0.5% 손실 tc qdisc replace dev eth0 root netem delay 80ms loss 0.5% # ingress 폴리싱/리다이렉트 → ifb 셰이핑 modprobe ifb ip link add ifb0 type ifb && ip link set ifb0 up tc qdisc add dev eth0 handle ffff: ingress tc filter add dev eth0 parent ffff: protocol ip prio 1 u32 match u32 0 0 \ action mirred egress redirect dev ifb0 tc qdisc add dev ifb0 root tbf rate 20mbit burst 32kbit latency 400ms
IPtables: stateful Policy + NAT
# 보기 iptables -L -v -n iptables -t nat -L -v -n # 기본 정책 iptables -P INPUT DROP iptables -P FORWARD DROP iptables -P OUTPUT ACCEPT # stateful 허용(반환/연관 트래픽 허용) iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT # 서비스 포트 허용 iptables -A INPUT -p tcp --dport 22 -j ACCEPT iptables -A INPUT -p tcp --dport 80 -j ACCEPT iptables -A INPUT -p tcp --dport 443 -j ACCEPT # DNAT(외부 443 → 내부 10.0.0.10:8443) iptables -t nat -A PREROUTING -p tcp --dport 443 \ -j DNAT --to-destination 10.0.0.10:8443 iptables -A FORWARD -p tcp -d 10.0.0.10 --dport 8443 -j ACCEPT # SNAT/MASQ(사설망 → 공인IP) iptables -t nat -A POSTROUTING -s 10.0.0.0/24 -j MASQUERADE
Conntrack: status/Event/Tunning
# 요약 통계/상태 conntrack -S conntrack -L | head # 특정 플로우 찾기(예: dport 8443) conntrack -L -p tcp --dport 8443 # 세션 강제 삭제(막힌 연결 해제) conntrack -D -p tcp --dport 8443 -d 10.0.0.10 # 실시간 이벤트(생성/종료) conntrack -E # 튜닝(최대 엔트리) sysctl -w net.netfilter.nf_conntrack_max=524288 # 영구화는 /etc/sysctl.d/*.conf