공인 인터넷풀부터 VM NIC까지 트래픽 추적 디버깅
광역 공인 인터넷풀, 엣지 라우팅, 내부 L3/방화벽, Virtual Router/NAT, 하이퍼바이저 브리지와 VM NIC까지 같은 흐름으로 따라가며 패킷이 어디서 사라지는지 좁히는 운영 절차입니다.
클라우드 네트워크 장애에서 “공인 IP는 열려 있다”, “방화벽 정책도 있다”, “VM도 켜져 있다”는 말은 충분한 판정이 아닙니다. 광역 공인 인터넷풀에서 시작한 패킷이 엣지, 내부 L3, NAT/Virtual Router, 하이퍼바이저 가상 스위치, VM NIC 중 어디까지 도착했는지 같은 시간대의 증거로 끊어 봐야 합니다.
이 글은 운영 중 트래픽 추적 메모를 공개 가능한 형태로 정리한 절차입니다. 실제 고객명, 내부 도메인, 실 IP, 장비명, 티켓 번호는 쓰지 않고 문서용 예시 대역만 사용합니다. 명령은 본인에게 권한이 있는 시스템과 승인된 장애 대응 범위 안에서만 실행해야 합니다.
1. 전체 경로를 한 줄로 고정한다
처음부터 장비별 담당자에게 흩어져 물으면 시간이 오래 걸립니다. 먼저 패킷이 지나야 하는 경로를 한 줄로 적고, 각 구간마다 “봤다/못 봤다”를 채우는 방식으로 좁혀야 합니다.
Internet client
-> ISP / external routing
-> edge router / upstream firewall
-> public internet pool / VIP / floating IP
-> internal L3 gateway or firewall
-> Virtual Router / NAT gateway external interface
-> DNAT / SNAT / policy route / conntrack
-> Virtual Router / NAT gateway internal interface
-> hypervisor bridge / OVS / tap-vif
-> VM NIC
-> OS firewall / service socket이 경로를 써 놓으면 “공인풀 문제인지”, “내부 방화벽 문제인지”, “가상화 구간 문제인지”를 감으로 말하지 않게 됩니다. 같은 5-tuple을 각 지점에서 확인하는 것이 핵심입니다.
2. 추적 튜플과 관측 지점을 먼저 정한다
장애 설명을 “접속 안 됨”으로 두면 캡처 결과를 서로 비교할 수 없습니다. 출발지, 목적 공인 IP, 포트, DNAT 후 사설 IP, 가상 NIC의 MAC, 하이퍼바이저 브리지를 먼저 고정합니다.
client : 198.51.100.50
public pool : 203.0.113.0/24
public IP : 203.0.113.10
protocol/port : tcp/443
internal transit : 172.16.10.0/24
nat external : eth2
nat internal : eth0
vm private IP : 10.10.0.25
vm mac : 52:54:00:aa:bb:cc
hypervisor bridge : br-int / qbr / vif
service : 10.10.0.25:443특정 출발지만 실패하는지, 전체 출발지가 실패하는지, 같은 공인 풀의 다른 IP는 정상인지도 같이 적습니다. 이 세 가지가 없으면 라우팅, 방화벽, NAT, VM 서비스 중 어디를 먼저 봐야 하는지 흔들립니다.
3. 공인 인터넷풀과 엣지에서 먼저 본다
공인 IP가 클라우드 포털에 할당되어 있어도 외부 경로에서 그 prefix가 제대로 광고되고, 엣지 장비가 해당 IP를 알고, 상단 방화벽 세션이 만들어지는지는 별도 문제입니다. 먼저 공인풀과 엣지에서 패킷 진입 여부를 확인합니다.
CLIENT=198.51.100.50
PUBLIC_IP=203.0.113.10
PUBLIC_POOL=203.0.113.0/24
PORT=443
# 외부 테스트 지점에서 증상을 먼저 고정한다.
traceroute -n -T -p "${PORT}" "${PUBLIC_IP}"
mtr -n -T -P "${PORT}" "${PUBLIC_IP}"
nc -vz -w 3 "${PUBLIC_IP}" "${PORT}"
# Linux edge 또는 route reflector에서 보는 기본 확인 예시.
ip route get "${PUBLIC_IP}"
ip route show table all | grep -E "203\.0\.113|default"
ip neigh show | grep "${PUBLIC_IP}" || true
# 네트워크 장비 CLI는 장비별 문법이 다르므로 같은 항목을 확인한다.
show ip route 203.0.113.10
show ip bgp 203.0.113.0/24
show arp | include 203.0.113.10
show firewall session source 198.51.100.50 destination 203.0.113.10 destination-port 443여기서 중요한 것은 “라우트가 있다” 하나로 끝내지 않는 것입니다. prefix 광고, next-hop, ARP/ND, 방화벽 세션, deny counter, return route를 같이 봐야 합니다. 특히 outbound return route가 다른 장비로 빠지면 외부에서는 timeout으로만 보입니다.
4. 엣지 outside/inside는 동시에 캡처한다
엣지 outside에서 SYN이 보이는데 inside로 안 넘어가면 엣지 방화벽, VIP/FIP, NAT, 정책 라우팅 문제입니다. outside에서도 안 보이면 클라이언트, ISP, upstream route 쪽입니다. 두 지점을 같은 시각에 잡아야 말이 갈리지 않습니다.
CLIENT=198.51.100.50
PUBLIC_IP=203.0.113.10
PORT=443
STAMP=$(date +%Y%m%d_%H%M%S)
timeout 90 tcpdump -nn -s0 -B 8192 -i edge-outside \
"host ${CLIENT} and host ${PUBLIC_IP} and tcp port ${PORT}" \
-w /var/tmp/edge-outside-${STAMP}.pcap &
timeout 90 tcpdump -nn -s0 -B 8192 -i edge-inside \
"host ${CLIENT} and host ${PUBLIC_IP} and tcp port ${PORT}" \
-w /var/tmp/edge-inside-${STAMP}.pcap &
wait
tcpdump -nn -r /var/tmp/edge-outside-${STAMP}.pcap | head -n 40
tcpdump -nn -r /var/tmp/edge-inside-${STAMP}.pcap | head -n 40캡처는 화면 몇 줄보다 pcap으로 남기는 편이 낫습니다. 장애가 끝난 뒤에도 SYN, SYN-ACK, RST, ICMP fragmentation needed, TTL 변화를 다시 볼 수 있기 때문입니다.
5. 내부 L3/방화벽 구간은 정책 라우팅과 역방향을 본다
엣지 안쪽까지 들어온 패킷이 NAT/Virtual Router로 가지 않으면 내부 구간입니다. 이때는 목적지로 가는 forward route만 보지 말고, 출발지로 돌아가는 return path와 policy routing을 같이 봐야 합니다.
PUBLIC_IP=203.0.113.10
PRIVATE_IP=10.10.0.25
TRANSIT_SRC=172.16.10.10
PORT=443
ip route get "${PRIVATE_IP}" from "${TRANSIT_SRC}"
ip rule show
ip route show table all | grep -E "203\.0\.113|172\.16\.10|10\.10\.0"
nft list ruleset | grep -E "203\.0\.113|172\.16\.10|10\.10\.0|dport ${PORT}" || true
iptables-save -t nat | grep -E "203\.0\.113|10\.10\.0|dport ${PORT}" || true
iptables-save -t filter | grep -E "203\.0\.113|10\.10\.0|dport ${PORT}|DROP|REJECT" || true
conntrack -L -p tcp 2>/dev/null | grep -E "203\.0\.113\.10|10\.10\.0\.25|dport=${PORT}|sport=${PORT}" | head -n 80비대칭 경로는 흔한 함정입니다. 들어오는 길은 A 방화벽인데 나가는 길은 B 라우터라면 conntrack 기반 장비는 세션을 못 맞추고 drop할 수 있습니다. 내부 ACL counter가 증가하는지, default route보다 더 구체적인 route가 있는지도 같이 봅니다.
6. NAT/Virtual Router에서는 NAT 전후를 분리한다
Virtual Router나 NAT gateway에서는 external interface의 공인 IP 흐름과 internal interface의 사설 IP 흐름이 같은 요청인지 연결해야 합니다. external에만 있고 internal에 없으면 NAT, filter, interface mapping, conntrack 문제로 좁혀집니다.
CLIENT=198.51.100.50
PUBLIC_IP=203.0.113.10
PRIVATE_IP=10.10.0.25
PORT=443
STAMP=$(date +%Y%m%d_%H%M%S)
ip -br addr
ip route
timeout 90 tcpdump -nn -s0 -B 8192 -i eth2 \
"host ${CLIENT} and host ${PUBLIC_IP} and tcp port ${PORT}" \
-w /root/nat-ext-${STAMP}.pcap &
timeout 90 tcpdump -nn -s0 -B 8192 -i eth0 \
"host ${CLIENT} and host ${PRIVATE_IP} and tcp port ${PORT}" \
-w /root/nat-int-${STAMP}.pcap &
wait
tcpdump -nn -r /root/nat-ext-${STAMP}.pcap | head -n 40
tcpdump -nn -r /root/nat-int-${STAMP}.pcap | head -n 40동시에 rule과 counter도 봅니다. rule 존재 여부만 보면 안 됩니다. 테스트 전후로 packet/byte counter가 증가해야 실제 트래픽이 그 rule을 탄 것입니다.
PUBLIC_IP=203.0.113.10
PRIVATE_IP=10.10.0.25
PORT=443
iptables-save -t nat | grep -E "${PUBLIC_IP}|${PRIVATE_IP}|dport ${PORT}|--dport ${PORT}"
iptables-save -t filter | grep -E "${PUBLIC_IP}|${PRIVATE_IP}|dport ${PORT}|--dport ${PORT}|DROP|REJECT"
iptables -t nat -L PREROUTING -nv --line-numbers | grep -E "${PUBLIC_IP}|${PORT}"
iptables -L -nv --line-numbers | grep -E "${PRIVATE_IP}|${PORT}|DROP|REJECT"
cat /proc/sys/net/netfilter/nf_conntrack_count 2>/dev/null
cat /proc/sys/net/netfilter/nf_conntrack_max 2>/dev/null
conntrack -S 2>/dev/null || true
conntrack -L -p tcp 2>/dev/null | grep -E "${PUBLIC_IP}|${PRIVATE_IP}|dport=${PORT}|sport=${PORT}" | head -n 80운영 중에는 오래된 interface 정보나 stale NAT rule도 확인해야 합니다. 공인 subnet은 현재 eth2에 있는데 DNAT rule이 과거 eth3 기준으로 만들어져 있으면 rule은 있어도 패킷이 안 맞습니다. conntrack table이 가득 찬 상황도 포트포워딩 장애처럼 보일 수 있습니다.
7. 하이퍼바이저 가상구간은 bridge, OVS, tap/vif까지 본다
NAT internal에서 VM 사설 IP로 나간 패킷이 하이퍼바이저 브리지에서 보이는데 VM NIC에서 안 보이면 가상 스위치 구간입니다. KVM이면 tap/qbr/br-int, OVS면 port와 flow, Xen 계열이면 vif 상태와 host 로그를 봅니다.
PRIVATE_IP=10.10.0.25
VM_MAC=52:54:00:aa:bb:cc
PORT=443
ip -br link
bridge link show
bridge fdb show | grep -i "${VM_MAC}" || true
ovs-vsctl show
ovs-ofctl dump-flows br-int | grep -Ei "10\.10\.0\.25|52:54:00:aa:bb:cc|nw_dst|nw_src" || true
timeout 60 tcpdump -nn -s0 -B 8192 -i br-int \
"host ${PRIVATE_IP} and tcp port ${PORT}" \
-w /var/tmp/hv-br-int-${PRIVATE_IP}.pcap
# tap/vif/qbr 이름을 찾은 뒤 VM 포트 직전에서도 한 번 더 본다.
timeout 60 tcpdump -nn -s0 -B 8192 -i <tap-or-vif-interface> \
"host ${PRIVATE_IP} and tcp port ${PORT}" \
-w /var/tmp/hv-vif-${PRIVATE_IP}.pcap여기서는 VM IP만으로 부족할 수 있습니다. VM MAC, tap/vif 이름, security group chain, OVS flow, bridge FDB를 묶어 봐야 “브리지까지 왔는데 VM 포트로 못 갔다”를 증명할 수 있습니다.
8. VM NIC까지 들어오면 OS와 서비스 문제로 전환한다
VM의 eth0에서 SYN이 보이고 VM이 RST를 보내면 네트워크 경로는 VM까지 도달한 것입니다. 그 다음은 OS firewall, listen socket, 서비스 바인딩, 애플리케이션 로그입니다.
CLIENT=198.51.100.50
PRIVATE_IP=10.10.0.25
PORT=443
ip -br addr
ip route
ss -lntp | grep ":${PORT}\b" || true
nft list ruleset | grep -E "dport ${PORT}|${CLIENT}|${PRIVATE_IP}|drop|reject" || true
iptables -S | grep -E "dport ${PORT}|${CLIENT}|${PRIVATE_IP}|DROP|REJECT" || true
timeout 60 tcpdump -nn -s0 -B 8192 -i eth0 \
"host ${CLIENT} and tcp port ${PORT}" \
-w /tmp/vm-eth0-${PORT}.pcap
curl -vk "https://127.0.0.1:${PORT}/" --connect-timeout 3
curl -vk "https://${PRIVATE_IP}:${PORT}/" --connect-timeout 3VM 안에서 tcpdump가 보이는데 서비스가 응답하지 않으면 포털의 공인 IP 설정이나 상단 라우팅을 계속 볼 이유가 줄어듭니다. 반대로 VM 안에서는 아무것도 안 보이는데 하이퍼바이저 브리지에는 보이면 가상 NIC 전 단계로 돌아갑니다.
9. 관측 결과로 구간을 바로 판정한다
아래 표처럼 관측 결과를 구간 판정으로 바꿔야 다음 액션이 빨라집니다. 중요한 것은 한 지점의 로그가 아니라, 같은 시간대의 여러 지점 캡처가 서로 이어지는지입니다.
| 관측 결과 | 우선 의심 구간 | 다음 확인 |
|---|---|---|
| edge outside에서 SYN이 안 보임 | client, ISP, upstream route | 출발지 NAT, 외부 라우팅, prefix 광고, 상단 ACL |
| edge outside에는 보이고 edge inside에는 안 보임 | edge firewall/NAT/policy | 세션 테이블, deny counter, VIP/FIP 매핑 |
| internal transit에는 보이고 NAT external에는 안 보임 | 내부 L3/방화벽 | 정책 라우팅, return route, zone policy |
| NAT external에는 보이고 NAT internal에는 안 보임 | NAT/filter/conntrack | DNAT rule, interface binding, counter, conntrack 상태 |
| 하이퍼바이저 브리지에는 보이고 VM NIC에는 안 보임 | tap/vif/bridge/security group | OVS flow, bridge FDB, port state, host firewall |
| VM NIC까지 들어오고 VM이 RST 응답 | VM OS 또는 서비스 | listen socket, OS firewall, application log |
| SYN/SYN-ACK는 보이는데 client timeout | return path, asymmetry, MTU | 역방향 캡처, policy route, MSS/fragment, ICMP 차단 |
10. SYN은 보이는데 끊기면 MTU와 fragment를 본다
터널, VXLAN, Geneve, GRE, IPSec, 방화벽 장비가 섞인 구간에서는 handshake 일부만 보이고 payload에서 멈추는 장애가 나옵니다. 이때는 MTU, MSS clamp, ICMP fragmentation needed 차단 여부를 같이 봅니다.
PUBLIC_IP=203.0.113.10
PRIVATE_IP=10.10.0.25
ping -M do -s 1472 "${PUBLIC_IP}"
tracepath -n "${PUBLIC_IP}"
tcpdump -nn -s0 -i any \
"icmp or (host ${PUBLIC_IP} and (tcp[tcpflags] & (tcp-syn|tcp-rst) != 0))"
# 터널/VXLAN/오버레이 구간이면 VM MSS clamp 또는 path MTU blackhole 여부를 같이 본다.
ip link show | grep -E "mtu|vxlan|geneve|gre|tun"MTU 문제는 “가끔 됨”, “작은 응답은 됨”, “TLS handshake에서 멈춤”처럼 보일 수 있습니다. 단순 포트 오픈 확인으로는 놓치기 쉽습니다.
11. 장애 보고는 구간과 근거로 끝낸다
보고서는 길게 쓰는 것보다, 어느 구간까지 봤고 어디서 끊겼는지를 명확히 남기는 것이 중요합니다. 아래 형식이면 네트워크, 방화벽, 가상화, OS 담당자가 같은 증거를 기준으로 움직일 수 있습니다.
증상
- 198.51.100.50 -> 203.0.113.10:443 접속 실패
- 같은 공인 풀의 다른 IP 또는 다른 출발지는 정상 여부:
고정한 경로
- public pool / public IP:
- edge outside / inside:
- internal transit:
- NAT external / internal:
- hypervisor bridge / VM port:
- VM NIC / service:
동시 캡처 판정
- edge outside:
- edge inside:
- internal L3:
- NAT external:
- NAT internal:
- hypervisor bridge:
- VM NIC:
rule/counter/session
- route or policy route:
- firewall/ACL counter:
- DNAT/SNAT rule and counter:
- conntrack state:
- OVS/bridge port state:
- VM listen/firewall:
결론
- 끊기는 구간:
- 근거:
- 임시 조치:
- 영구 조치:
- 재현 테스트 결과:정리
공인 인터넷풀부터 내부 구간, 가상구간까지 이어지는 장애는 포털 화면이나 단일 장비 로그만으로 끝내면 계속 되돌아옵니다. 먼저 5-tuple과 전체 경로를 고정하고, 엣지 outside/inside, 내부 L3, NAT external/internal, 하이퍼바이저 브리지, VM NIC를 같은 시각에 확인합니다. 패킷이 안 보이는 첫 지점이 우선 조치 구간이고, 패킷이 보이는데 응답이 틀어진 지점이 다음 분석 구간입니다.