웹 브라우저나 curl 로 페이지를 열면 화면에는 결과만 보인다. 하지만 그 짧은 순간에
두 컴퓨터는 작은 메시지를 열 번쯤 주고받는다. 이 글에서는 그 메시지를 직접
붙잡아서 한 줄씩 읽어 본다.
네트워크를 처음 공부하는 사람을 기준으로 썼다. 용어는 나올 때마다 풀어서 설명한다.
다 따라 하면 이런 것을 눈으로 확인하게 된다.
| 상황 | 보게 되는 것 |
|---|---|
| 정상적으로 접속할 때 | 연결을 맺는 인사 세 번(3-way handshake), 요청과 응답, 연결을 끊는 인사 |
| 아무도 없는 포트에 접속할 때 | 서버가 곧바로 "없다" 고 거절한다 — Connection refused |
| 대답이 중간에 사라질 때 | 같은 인사를 몇 번이고 다시 보낸다 — Connection timed out |
글에 나오는 출력은 모두 집에 만든 실습용 가상 머신에서 실제로 잡은 것이다.
참고
집 내부망 주소는 예시 값으로 바꿔 적었다. 패킷을 잡는 실습 컴퓨터는
192.168.0.10, 접속할 웹 서버는192.168.0.20이다. 따라 할 때는 자기 주소로
바꿔 넣는다.
먼저 알아 둘 말 다섯 개#
이 다섯 개만 알면 글 전체를 읽을 수 있다.
| 말 | 쉽게 말하면 |
|---|---|
| 패킷 | 네트워크로 오가는 메시지 한 통. 큰 데이터도 작은 패킷 여러 개로 나뉘어 간다 |
| IP 주소 | 컴퓨터의 집 주소. 192.168.0.20 같은 숫자 네 덩어리 |
| 포트 | 그 집 안의 방 번호. 웹 서버는 보통 80번(HTTP), 443번(HTTPS) 방에서 손님을 받는다 |
| TCP | 패킷이 빠짐없이, 순서대로 도착하게 챙기는 약속(프로토콜). 웹, SSH 등 대부분이 이 위에서 돈다 |
| 캡처 | 오가는 패킷을 복사해서 기록해 두는 것. 이 글에서는 tcpdump 라는 프로그램이 한다 |
TCP 는 전화 통화와 비슷하다. 바로 용건부터 말하지 않고, 먼저 "여보세요?"
"네, 들려요" "네, 저도 들려요" 를 주고받아 서로 연결된 것을 확인한다. 이 세 번의
인사가 3-way handshake 다. 다 말한 뒤에는 "끊을게요" "네, 끊어요" 로 마무리한다.
이 글은 그 인사를 실제 패킷으로 보는 것이 목표다.
실습 환경#
컴퓨터 두 대가 같은 공유기에 연결돼 있다.
[실습 컴퓨터] ── 패킷 ──▶ [웹 서버]
192.168.0.10 192.168.0.20 (80번 포트)
tcpdump 를 여기서 실행
- 실습 컴퓨터: 리눅스면 무엇이든 된다. 여기서는 Proxmox 위에 만든 Ubuntu 24.04 가상 머신(2코어, 메모리 2 GB)을 썼다.
- 웹 서버: 같은 네트워크에서 80번 포트로 응답하는 것이면 된다. 집에 블로그나 NAS 가 있으면 그걸 쓴다. 없으면 다른 리눅스 컴퓨터에서
sudo python3 -m http.server 80한 줄로 임시 웹 서버를 띄울 수 있다.
명령은 모두 실습 컴퓨터의 터미널에 입력한다. 걸리는 시간은 30분 정도다.
참고
명령 앞의
sudo는 "관리자 권한으로 실행" 이라는 뜻이다. 패킷을 엿보는 일은 아무나
하면 안 되는 일이라 관리자 권한이 필요하다. 처음 쓸 때 내 계정 비밀번호를 묻는다.
1. 도구를 설치한다#
sudo apt update
sudo apt install -y tcpdump tshark nftables curl netcat-openbsd
apt 는 우분투의 프로그램 설치 도구다. 설치하는 다섯 가지는 이렇다.
| 프로그램 | 하는 일 |
|---|---|
tcpdump |
패킷을 잡아서 보여 준다. 이 글의 주인공 |
tshark |
잡은 패킷을 더 친절하게 풀이해 준다. 유명한 Wireshark 의 터미널판 |
nftables |
리눅스 방화벽. 6단계에서 패킷을 일부러 버릴 때 쓴다 |
curl |
터미널에서 웹 페이지를 요청한다. 브라우저 대신 쓴다 |
netcat (nc) |
특정 포트가 열려 있는지 두드려 본다 |
설치 도중 tshark 가 "Should non-superusers be able to capture packets?"(관리자가 아닌
사용자도 패킷을 잡게 할까요?) 라고 물으면 Yes 를 고른다. 그리고 내 계정을 그 권한
그룹에 넣는다.
sudo usermod -aG wireshark $USER
이 설정은 로그아웃했다 다시 로그인해야 적용된다. 설치된 버전은 이랬다.
tcpdump version 4.99.4
TShark (Wireshark) 4.2.2
curl 8.5.0
2. 네트워크 장치 이름을 확인한다#
tcpdump 에게 "어느 랜선을 지켜볼지" 알려 줘야 한다. 장치 이름부터 확인한다.
ip -4 -br addr
lo UNKNOWN 127.0.0.1/8
eth0 UP 192.168.0.10/24 metric 100
lo는 컴퓨터가 자기 자신과 이야기할 때 쓰는 가상 장치라서 무시한다.eth0이 실제 네트워크 장치고, 내 주소는192.168.0.10이다.
장치 이름은 컴퓨터마다 다르다. ens18, enp0s3 처럼 나오면 아래 명령의 eth0 을
그 이름으로 바꾼다.
3. 정상 접속을 잡는다#
순서는 이렇다. 녹화 시작 → 웹 페이지 요청 → 녹화 종료 → 녹화본 보기.
sudo tcpdump -i eth0 -nn -w http.pcap 'host 192.168.0.20 and tcp port 80' &
curl -s -o /dev/null -w 'http_code=%{http_code}n' http://192.168.0.20/
sudo pkill -INT tcpdump
한 줄씩 보면 이렇다.
첫 줄 — 녹화 시작
| 부분 | 뜻 |
|---|---|
tcpdump |
패킷 캡처 프로그램을 실행한다 |
-i eth0 |
eth0 장치를 지켜본다 |
-nn |
주소와 포트를 숫자 그대로 보여 준다. 이름으로 바꾸느라 생기는 추가 패킷이 섞이지 않는다 |
-w http.pcap |
화면에 보여 주는 대신 http.pcap 파일에 녹화한다 |
'host 192.168.0.20 and tcp port 80' |
필터. 이 서버의 80번 포트와 오가는 패킷만 녹화한다. 안 걸면 다른 패킷이 잔뜩 섞인다 |
맨 끝 & |
이 명령을 뒤에서 돌게 두고 다음 명령을 바로 입력할 수 있게 한다 |
둘째 줄 — 웹 페이지 요청. curl 로 서버에 접속하고, 받은 내용은 버리고
(-o /dev/null) 응답 코드만 찍는다. 화면에 이렇게 나온다.
http_code=301
301 은 "다른 주소로 가세요" 라는 응답이다. 이 서버는 HTTP 접속을 HTTPS 로 넘기도록
설정돼 있어서 그렇다. 응답 코드는 중요하지 않다. 연결이 맺어지고 끊어지는 과정을
보는 것이 목적이다.
셋째 줄 — 녹화 종료. 뒤에서 돌던 tcpdump 에게 멈추라고 신호를 보낸다.
이제 녹화본을 읽는다.
tcpdump -nn -r http.pcap
-r 은 "파일에서 읽기" 다. 출력은 패킷 하나가 한 줄이고, 모두 열 줄이 나온다.
한 줄이 길어서 뒷부분의 options [...] 은 … 로 줄였다. 그 부분은 몰라도 된다.
07:51:10.186372 IP 192.168.0.10.37738 > 192.168.0.20.80: Flags [S], seq 3850998404, win 64240, options […], length 0
07:51:10.186619 IP 192.168.0.20.80 > 192.168.0.10.37738: Flags [S.], seq 568641128, ack 3850998405, win 65160, options […], length 0
07:51:10.186659 IP 192.168.0.10.37738 > 192.168.0.20.80: Flags [.], ack 1, win 502, options […], length 0
07:51:10.186722 IP 192.168.0.10.37738 > 192.168.0.20.80: Flags [P.], seq 1:74, ack 1, win 502, options […], length 73: HTTP: GET / HTTP/1.1
07:51:10.186821 IP 192.168.0.20.80 > 192.168.0.10.37738: Flags [.], ack 74, win 509, options […], length 0
07:51:10.186913 IP 192.168.0.20.80 > 192.168.0.10.37738: Flags [P.], seq 1:364, ack 74, win 509, options […], length 363: HTTP: HTTP/1.1 301 Moved Permanently
07:51:10.186924 IP 192.168.0.10.37738 > 192.168.0.20.80: Flags [.], ack 364, win 501, options […], length 0
07:51:10.187062 IP 192.168.0.10.37738 > 192.168.0.20.80: Flags [F.], seq 74, ack 364, win 501, options […], length 0
07:51:10.187185 IP 192.168.0.20.80 > 192.168.0.10.37738: Flags [F.], seq 364, ack 75, win 509, options […], length 0
07:51:10.187197 IP 192.168.0.10.37738 > 192.168.0.20.80: Flags [.], ack 365, win 501, options […], length 0
처음 보면 암호 같지만, 한 줄만 뜯어보면 나머지는 같은 모양이다.
한 줄 뜯어보기#
첫 줄을 조각내 보면 이렇다.
07:51:10.186372 IP 192.168.0.10.37738 > 192.168.0.20.80: Flags [S], seq 3850998404, win 64240, length 0
| 조각 | 뜻 |
|---|---|
07:51:10.186372 |
패킷을 잡은 시각. 소수점 아래 여섯 자리라 백만분의 1초까지 나온다 |
IP |
IP 패킷이라는 표시 |
192.168.0.10.37738 |
보낸 쪽. 주소 192.168.0.10 의 37738번 포트. 마지막 숫자 덩어리가 포트다 |
> |
이 방향으로 갔다 |
192.168.0.20.80 |
받는 쪽. 웹 서버의 80번 포트 |
Flags [S] |
이 패킷의 종류. 아래 표에 정리했다 |
seq 3850998404 |
순서 번호. 아래 "순서 번호" 에서 설명한다 |
win 64240 |
"지금 이만큼 더 받을 수 있다" 는 여유 공간 |
length 0 |
실어 나르는 데이터의 크기(바이트). 0 이면 내용 없이 인사만 한 것이다 |
참고
내 쪽 포트
37738은 운영체제가 아무 빈 번호나 골라 준 것이다. 서버는 80번처럼
정해진 번호에서 기다리고, 접속하는 쪽은 매번 아무 번호나 쓴다. 그래서 따라 하면
이 숫자는 다르게 나온다.
Flags — 패킷의 종류#
| 표시 | 이름 | 통화로 치면 |
|---|---|---|
[S] |
SYN | "여보세요?" — 연결하고 싶다 |
[S.] |
SYN + ACK | "네, 들려요. 그쪽도 제 말 들리세요?" |
[.] |
ACK | "네, 잘 들려요" — 받았다는 확인 |
[P.] |
PUSH + ACK | 진짜 용건(데이터)을 담았다 |
[F.] |
FIN + ACK | "저는 할 말 다 했어요, 끊을게요" |
[R.] |
RST + ACK | "그런 사람 없어요" — 연결 거절. 5단계에서 나온다 |
점(.)은 ACK, 즉 "받았다" 는 표시다. 맨 처음 SYN 을 빼면 거의 모든 패킷에 붙어 있다.
열 줄을 우리말로 옮기면#
| 번호 | 방향 | Flags | 무슨 일 |
|---|---|---|---|
| 1 | 나 → 서버 | [S] |
여보세요? (연결 요청) |
| 2 | 서버 → 나 | [S.] |
네, 들려요. 그쪽도 들리세요? |
| 3 | 나 → 서버 | [.] |
네, 들려요. 여기까지가 3-way handshake |
| 4 | 나 → 서버 | [P.] |
웹 페이지 주세요 (GET / HTTP/1.1, 73바이트) |
| 5 | 서버 → 나 | [.] |
요청 잘 받았어요 |
| 6 | 서버 → 나 | [P.] |
여기 응답이요 (301 Moved Permanently, 363바이트) |
| 7 | 나 → 서버 | [.] |
응답 잘 받았어요 |
| 8 | 나 → 서버 | [F.] |
저는 끝났어요, 끊을게요 |
| 9 | 서버 → 나 | [F.] |
네, 저도 끊어요 |
| 10 | 나 → 서버 | [.] |
알겠어요. 연결 종료 |
1번부터 10번까지 걸린 시간을 시각으로 계산해 보면 0.001초가 안 된다. 같은 집
안의 컴퓨터끼리라 그렇다.
순서 번호(seq)와 확인 번호(ack)#
TCP 는 보낸 데이터에 번호를 매긴다. 긴 편지에 쪽 번호를 적어 보내는 것과 같다.
받는 쪽은 "몇 번까지 받았으니 그다음 번호를 보내 달라" 고 답한다. 이것이 ack 다.
4~5번 줄에서 확인할 수 있다.
4 나 → 서버 [P.], seq 1:74, length 73 ← 1번부터 73번까지 73바이트를 보낸다
5 서버 → 나 [.], ack 74 ← 73번까지 다 받았다. 다음은 74번부터
6~7번 줄도 같다. 서버가 363바이트를 보내고(seq 1:364), 내가 ack 364 로 답한다.
참고
1번 줄은
seq 3850998404처럼 큰 숫자인데 3번 줄부터는ack 1처럼 작아진다.
시작 번호는 보안 때문에 아무 큰 수에서 시작하는데, 읽기 힘드니 tcpdump 가 두 번째
패킷부터 "시작 번호에서 몇 번째인지" 로 바꿔 보여 주기 때문이다. 원래 숫자를
보려면-S를 붙인다.
bash
tcpdump -nn -S -r http.pcap
07:51:10.186659 IP 192.168.0.10.37738 > 192.168.0.20.80: Flags [.], ack 568641129, …2번 줄에서 서버가 보낸 시작 번호
568641128에 1을 더한 값이다. SYN 은 데이터가
없어도 번호를 하나 쓴다. 그래서 "SYN 잘 받았다" 는 답이 +1 이 된다. 8~9번 줄의
FIN 도 마찬가지라,seq 74로 보낸 FIN 에 서버가ack 75로 답한다.
4. tshark 로 다시 읽는다 (선택)#
같은 녹화본을 tshark 로 열면 더 읽기 쉽게 풀이해 준다.
tshark -r http.pcap -T fields -e frame.number -e frame.time_relative -e _ws.col.Info
-e 로 보고 싶은 칸을 고른다. 번호, 첫 패킷부터 지난 시간, 요약 설명 세 칸이다.
요약 칸 뒤쪽은 … 로 줄였다.
1 0.000000000 37738 → 80 [SYN] Seq=0 Win=64240 Len=0 …
2 0.000247000 80 → 37738 [SYN, ACK] Seq=0 Ack=1 Win=65160 Len=0 …
3 0.000287000 37738 → 80 [ACK] Seq=1 Ack=1 Win=64256 Len=0 …
4 0.000350000 GET / HTTP/1.1
5 0.000449000 80 → 37738 [ACK] Seq=1 Ack=74 Win=65152 Len=0 …
6 0.000541000 HTTP/1.1 301 Moved Permanently (text/html)
7 0.000552000 37738 → 80 [ACK] Seq=74 Ack=364 Win=64128 Len=0 …
8 0.000690000 37738 → 80 [FIN, ACK] Seq=74 Ack=364 Win=64128 Len=0 …
9 0.000813000 80 → 37738 [FIN, ACK] Seq=364 Ack=75 Win=65152 Len=0 …
10 0.000825000 37738 → 80 [ACK] Seq=75 Ack=365 Win=64128 Len=0 …
[S.] 대신 [SYN, ACK] 처럼 이름을 풀어 쓰고, 4번과 6번은 HTTP 내용으로 보여 준다.
순서 번호도 처음부터 0 으로 시작해서 더 보기 편하다.
팁
마우스로 보는 화면이 편하면
http.pcap파일을 내 PC 로 가져와 Wireshark 로 연다.
파일을 가져오는 방법은 SSH 키로 비밀번호 없이 서버 접속하기에
정리해 두었다. 이 글의 실습은 터미널만으로 충분하다.
5. 아무도 없는 포트를 두드린다 — Connection refused#
이번에는 서버에서 아무 프로그램도 기다리지 않는 81번 포트로 접속해 본다.
아무도 안 사는 방 문을 두드리는 셈이다.
sudo tcpdump -i eth0 -nn -w rst.pcap 'host 192.168.0.20 and tcp port 81' &
curl -sS -m 3 http://192.168.0.20:81/
sudo pkill -INT tcpdump
tcpdump -nn -r rst.pcap
curl 은 바로 실패한다.
curl: (7) Failed to connect to 192.168.0.20 port 81 after 0 ms: Couldn't connect to server
녹화본에는 딱 두 줄이 남는다.
07:51:25.364900 IP 192.168.0.10.57504 > 192.168.0.20.81: Flags [S], seq 1538215172, win 64240, options […], length 0
07:51:25.365196 IP 192.168.0.20.81 > 192.168.0.10.57504: Flags [R.], seq 0, ack 1538215173, win 0, length 0
"여보세요?"([S]) 에 서버가 곧바로 "그런 사람 없어요"([R.]) 라고 답했다.
nc 로 두드려 보면 더 익숙한 문장이 나온다.
nc -vz -w 2 192.168.0.20 81
nc: connect to 192.168.0.20 port 81 (tcp) failed: Connection refused
Connection refused 는 RST 를 받았다는 뜻이다. 서버 컴퓨터까지는 잘 도착했고,
서버가 대답도 했다. 다만 그 포트에서 기다리는 프로그램이 없다. 이런 오류를 보면
네트워크보다 서버 프로그램이 켜져 있는지, 포트 번호가 맞는지를 먼저 본다.
6. 대답을 버려서 재전송을 만든다 — Connection timed out#
연결이 안 되는 경우는 하나 더 있다. 대답이 아예 오지 않는 경우다. 보통 중간의
방화벽이 패킷을 조용히 버릴 때 이렇게 된다.
참고
방화벽은 패킷을 검사해서 통과시킬지 버릴지 정하는 문지기다. 리눅스에는
nftables라는 방화벽이 들어 있다. 이번 실습에서는 일부러 규칙을 하나 넣어서
이 상황을 만든다.
실습 컴퓨터에 "서버에서 오는 SYN-ACK 는 버려라" 라는 규칙을 넣는다. 서버는
"네, 들려요" 라고 대답하지만, 그 대답이 내 컴퓨터 문 앞에서 버려진다.
sudo nft add table inet lab
sudo nft add chain inet lab in '{ type filter hook input priority 0; policy accept; }'
sudo nft add rule inet lab in ip saddr 192.168.0.20 tcp sport 80 tcp flags '& (syn|ack) == syn|ack' counter drop
세 줄이 하는 일은 이렇다.
lab이라는 이름의 규칙 묶음(table)을 만든다.- 그 안에 들어오는 패킷(
hook input)을 검사하는 목록(chain)을 만든다. 기본값은 통과(policy accept)다. - 규칙 하나를 넣는다. 보낸 쪽이
192.168.0.20의 80번 포트이고, SYN 과 ACK 표시가 둘 다 켜진 패킷이면, 개수를 세고(counter) 버린다(drop).
잘 들어갔는지 확인한다.
sudo nft list table inet lab
table inet lab {
chain in {
type filter hook input priority filter; policy accept;
ip saddr 192.168.0.20 tcp sport 80 tcp flags syn,ack / syn,ack counter packets 0 bytes 0 drop
}
}
이제 녹화하면서 접속해 본다. -m 8 은 8초까지만 기다리라는 뜻이다.
sudo tcpdump -i eth0 -nn -w retx.pcap 'host 192.168.0.20 and tcp port 80' &
curl -sS -m 8 -o /dev/null http://192.168.0.20/
sudo pkill -INT tcpdump
이번에는 바로 실패하지 않고 8초를 기다린 뒤 포기한다.
curl: (28) Connection timed out after 8002 milliseconds
tshark 로 녹화본을 읽는다. 뒷부분은 줄였다.
tshark -r retx.pcap -T fields -e frame.number -e frame.time_relative -e _ws.col.Info
1 0.000000000 47244 → 80 [SYN] Seq=0 …
2 0.000237000 80 → 47244 [SYN, ACK] Seq=0 Ack=1 …
3 1.000877000 [TCP Retransmission] 80 → 47244 [SYN, ACK] Seq=0 Ack=1 …
4 1.044813000 [TCP Retransmission] 47244 → 80 [SYN] Seq=0 …
5 1.045084000 [TCP Retransmission] 80 → 47244 [SYN, ACK] Seq=0 Ack=1 …
6 2.068998000 [TCP Retransmission] 47244 → 80 [SYN] Seq=0 …
7 2.069344000 [TCP Retransmission] 80 → 47244 [SYN, ACK] Seq=0 Ack=1 …
8 3.092999000 [TCP Retransmission] 47244 → 80 [SYN] Seq=0 …
9 3.093322000 [TCP Retransmission] 80 → 47244 [SYN, ACK] Seq=0 Ack=1 …
10 4.116799000 [TCP Retransmission] 47244 → 80 [SYN] Seq=0 …
11 4.117096000 [TCP Retransmission] 80 → 47244 [SYN, ACK] Seq=0 Ack=1 …
12 5.140992000 [TCP Retransmission] 47244 → 80 [SYN] Seq=0 …
13 5.141390000 [TCP Retransmission] 80 → 47244 [SYN, ACK] Seq=0 Ack=1 …
14 7.145083000 [TCP Retransmission] 80 → 47244 [SYN, ACK] Seq=0 Ack=1 …
15 7.188976000 [TCP Retransmission] 47244 → 80 [SYN] Seq=0 …
16 7.189306000 [TCP Retransmission] 80 → 47244 [SYN, ACK] Seq=0 Ack=1 …
Retransmission 은 "재전송", 즉 같은 패킷을 다시 보냈다는 뜻이다. 통화로 옮기면
이렇다.
- 나: "여보세요?"
- 서버: "네, 들려요!" — 그런데 이 말이 내 방화벽에서 버려진다.
- 나: (대답이 없네…) 1초 뒤 다시 "여보세요?"
- 서버: (대답을 했는데 확인이 없네…) "네, 들려요!" — 또 버려진다.
- 이렇게 계속 반복하다가 8초가 지나 curl 이 포기한다.
몇 가지를 더 읽을 수 있다.
- 서버는 멀쩡하다. SYN-ACK 를 매번 제대로 보냈다. 받는 쪽에서 버렸을 뿐이다.
- 같은 SYN 을 다시 보낸 것이다.
Seq=0이 매번 같은 것이 "새 연결이 아니라 같은 인사의 반복" 이라는 표시다. - 다시 보내는 간격은 1, 2, 3, 4, 5초째에 한 번씩, 그다음은 7초째다. 처음 몇 번은 1초 간격으로 빠르게 다시 보내고, 그 뒤로는 간격을 점점 늘린다.
참고
버려지는 SYN-ACK 가 녹화본에 보이는 이유.
tcpdump는 패킷이 방화벽에 도착하기
전에 복사한다. 그래서 방화벽이 버린 패킷도 녹화된다. 이 성질 덕분에 "패킷은
도착했는데 방화벽이 막는 것인지" 를 가려낼 수 있다.
참고
교과서에는 재전송 간격이 1초, 2초, 4초… 로 두 배씩 늘어난다고 나온다. 여기서
처음에 1초 간격이 이어진 것은 최근 리눅스(6.6 이후)에 생긴 설정 때문이다.
$ sysctl net.ipv4.tcp_syn_linear_timeouts net.ipv4.tcp_syn_retries
net.ipv4.tcp_syn_linear_timeouts = 4
net.ipv4.tcp_syn_retries = 6처음 몇 번은 같은 간격으로 빨리 다시 보내고, 그래도 안 되면 간격을 두 배씩 늘린다.
포기하기 전까지 최대 6번 다시 보낸다.
실습이 끝나면 규칙을 지운다#
주의
이 규칙을 남겨 두면 이 컴퓨터에서 그 웹 서버로 영영 접속이 안 된다. 꼭 지운다.
지우기 전에 규칙이 몇 개를 버렸는지 본다.
sudo nft list chain inet lab in | grep counter
ip saddr 192.168.0.20 tcp sport 80 tcp flags syn,ack / syn,ack counter packets 9 bytes 540 drop
packets 9 — 9개를 버렸다. 녹화본의 SYN-ACK(2, 3, 5, 7, 9, 11, 13, 14, 16번)도
정확히 9개다. 이제 지운다.
sudo nft delete table inet lab
sudo nft list tables
마지막 명령의 결과에 lab 이 없으면 원래대로 돌아온 것이다.
확인#
다 따라 했다면 아래 명령의 결과가 오른쪽 # 뒤의 숫자와 같아야 한다.
tcpdump -nn -r http.pcap | wc -l # 10 — 정상 접속은 패킷 10개
tcpdump -nn -r http.pcap 'tcp[tcpflags] & tcp-fin != 0' | wc -l # 2 — 끊는 인사 FIN 은 양쪽에서 한 번씩
tcpdump -nn -r rst.pcap 'tcp[tcpflags] & tcp-rst != 0' | wc -l # 1 — 닫힌 포트는 RST 한 번
tshark -r retx.pcap -Y tcp.analysis.retransmission | wc -l # 14 — 재전송이 14번
sudo nft list tables | grep -c lab # 0 — 실습 규칙이 남아 있지 않다
wc -l 은 줄 수를 세는 명령이다. 패킷 수는 환경에 따라 한두 개 다를 수 있다.
정상 접속에서 [S] [S.] [.] 로 시작해 [F.] 두 개와 [.] 로 끝나면 된다.
정리#
- TCP 연결은 인사 세 번으로 시작한다.
[S]→[S.]→[.]를 3-way handshake 라고 부른다. 끊을 때도 양쪽이[F.]를 한 번씩 보낸다. Connection refused와Connection timed out은 원인이 다르다.- refused: RST 를 받았다. 서버까지는 갔다. 서버 프로그램과 포트 번호를 본다.
- timed out: 아무 대답도 못 받았다. 중간의 방화벽이나 경로를 본다.
- 어느 쪽인지 헷갈리면 tcpdump 로 한 번 잡아 보면 바로 갈린다.
- tcpdump 는 방화벽보다 앞에서 본다. 그래서 "패킷은 왔는데 막혔다" 를 확인할 수 있다.
용어 정리#
| 용어 | 뜻 |
|---|---|
| SYN | 연결하자는 첫 인사 |
| ACK | 받았다는 확인 |
| FIN | 할 말 다 했으니 끊자는 인사 |
| RST | 연결을 즉시 거절하거나 끊는 신호 |
| seq | 내가 보내는 데이터의 순서 번호 |
| ack (번호) | "여기까지 받았으니 다음은 이 번호부터" |
| 재전송 | 확인을 못 받아서 같은 패킷을 다시 보내는 것 |
| 필터 | 원하는 패킷만 골라 잡는 조건 (host …, tcp port …) |
다음 실습에서는 리눅스 안에 가상 컴퓨터 여러 대를 만들고, 서로 다른 네트워크를
라우터로 이어 본다. 오늘 쓴 tcpdump 로 그 사이를 오가는 패킷도 다시 들여다본다.

댓글