LabHub
배우기 러닝패스 코스

Network Fundamentals — Hands-on in a Linux VM

Catch the TCP Handshake in Three Lines

LabHub 에서 이어서 보기

한국어 원문으로 표시합니다.

이 실습은 VM 에서 돕니다

루프백(lo) 위에서 nc 로 서버와 클라이언트를 만들고 tcpdump 로 그 사이를 잡습니다. 바깥 네트워크가 필요 없어서 잡히는 패킷이 전부 내 것입니다.

목표

연결이 열리고 닫히는 과정을 SYN·SYN-ACK·ACK·FIN 플래그로 읽고, 닫힌 포트와 열린 포트의 차이, TIME-WAIT, 임시 포트, 그리고 포트를 잡은 프로세스를 찾는 법을 익힙니다.

왜 중요한가

"연결이 안 된다" 는 한 문장에 서로 다른 세 가지가 섞여 있습니다. SYN 을 보냈는데 RST 가 오면 상대는 살아 있고 포트만 닫힌 것이고, 아무것도 안 오면 방화벽이 버리거나 경로가 없는 것이고, SYN-ACK 까지 왔는데 데이터가 안 오면 애플리케이션 문제입니다. 이 셋은 사용자에게 똑같이 "안 됨" 으로 보이지만 고치는 사람은 완전히 다릅니다. 플래그를 읽을 줄 알면 첫 30초에 그 셋이 갈립니다.

단계

  1. nc -lk 127.0.0.1 9000 서버를 백그라운드로 띄우고(받은 데이터는 /root/tcp/recv.txt 로) ss -ltn 출력을 /root/tcp/listen.txt 에 저장하세요.
  2. lo 의 9000 포트를 tcpdump 로 잡으면서 hello 한 줄을 보내, 핸드셰이크와 종료가 담긴 캡처를 /root/tcp/handshake.txt 에 저장하세요.
  3. 아무도 듣지 않는 9001 포트로 접속을 시도해 오류 메시지와 그때의 캡처(RST 가 보이는)를 /root/tcp/refused.txt 에 함께 저장하세요.
  4. 9000 으로 한 번 더 접속하고 닫은 직후 ss -tan 에서 TIME-WAIT 줄을 /root/tcp/timewait.txt 에 저장하세요.
  5. 임시 포트 범위와 2단계 캡처에서 클라이언트가 쓴 포트를 /root/tcp/ports.txtrange=·client_port= 두 줄로 저장하세요.
  6. 파이썬으로 127.0.0.1:9000 에 한 번 더 bind 를 시도해 그 오류를 /root/tcp/inuse.txt 에 저장하세요.
  7. ss -ltnp 로 9000 을 잡은 프로세스를 찾아 그 줄을 /root/tcp/owner.txt 에 저장하세요.
  8. /root/tcp/report.mdclient_port=·server_port= 두 줄과 함께 세 단계 핸드셰이크와 TIME-WAIT 가 왜 있는지를 적으세요.

참고

듣는 소켓

nc -lk 127.0.0.1 9000 서버를 백그라운드로 띄우고(받은 데이터는 /root/tcp/recv.txt 로) ss -ltn 출력을 /root/tcp/listen.txt 에 저장하세요.

setsid nc -lk 127.0.0.1 9000 > /root/tcp/recv.txt 2>&1 < /dev/null &. ss -ltn 은 TCP(-t) 듣는 소켓(-l)을 숫자로(-n) 보여 줍니다. 127.0.0.1:9000 이 LISTEN 으로 보여야 합니다.

세 줄의 핸드셰이크

lo 의 9000 포트를 tcpdump 로 잡으면서 hello 한 줄을 보내, 핸드셰이크와 종료가 담긴 캡처를 /root/tcp/handshake.txt 에 저장하세요.

캡처를 먼저: timeout 6 tcpdump -i lo -nn -l 'tcp port 9000' > /root/tcp/handshake.txt 2>&1 &. 1초 뒤 echo hello | nc -q 1 127.0.0.1 9000. 파일에 [S], [S.], [.] 순서로 시작해 [F.] 로 끝나는 줄들이 있어야 하고, recv.txt 에 hello 가 들어옵니다.

닫힌 포트는 RST 로 답한다

아무도 듣지 않는 9001 포트로 접속을 시도해 오류 메시지와 그때의 캡처(RST 가 보이는)를 /root/tcp/refused.txt 에 함께 저장하세요.

캡처 timeout 5 tcpdump -i lo -nn -l 'tcp port 9001' >> /root/tcp/refused.txt 2>&1 & 를 걸고 nc -zv -w 1 127.0.0.1 9001 >> /root/tcp/refused.txt 2>&1. 파일에 Connection refusedFlags [R.] 둘 다 있어야 합니다.

TIME-WAIT

9000 으로 한 번 더 접속하고 닫은 직후 ss -tan 에서 TIME-WAIT 줄을 /root/tcp/timewait.txt 에 저장하세요.

echo again | nc -q 1 127.0.0.1 9000 직후 ss -tan | grep TIME-WAIT > /root/tcp/timewait.txt. 먼저 닫은 쪽(클라이언트)에 남습니다. 60초 뒤 사라지니 바로 보세요.

임시 포트

임시 포트 범위와 2단계 캡처에서 클라이언트가 쓴 포트를 /root/tcp/ports.txtrange=·client_port= 두 줄로 저장하세요.

범위는 sysctl -n net.ipv4.ip_local_port_range (두 숫자). 클라이언트 포트는 handshake.txt 의 [S] 줄에서 127.0.0.1.<포트> > 의 포트입니다. 그 값이 범위 안에 있어야 합니다.

포트는 하나가 잡는다

파이썬으로 127.0.0.1:9000 에 한 번 더 bind 를 시도해, 어디에 bind 했는지 한 줄과 그 오류를 /root/tcp/inuse.txt 에 함께 저장하세요.

{ echo 'bind 127.0.0.1:9000'; python3 -c 'import socket; s=socket.socket(); s.bind(("127.0.0.1", 9000))' 2>&1; } > /root/tcp/inuse.txt. Address already in use 가 나와야 합니다. 같은 주소·포트는 한 소켓만 가질 수 있습니다. 어느 포트에서 난 충돌인지가 파일에 남아 있어야 합니다 — 다른 포트의 충돌 메시지는 이 단계의 답이 아닙니다.

누가 잡았나

ss -ltnp 로 9000 을 잡은 프로세스를 찾아 그 줄을 /root/tcp/owner.txt 에 저장하세요.

ss -ltnp | grep ':9000 '. -pusers:(("nc",pid=…)) 를 붙여 줍니다. root 가 아니면 남의 프로세스는 안 보입니다.

무엇을 배웠나

/root/tcp/report.mdclient_port=·server_port= 두 줄과 함께 세 단계 핸드셰이크와 TIME-WAIT 가 왜 있는지를 적으세요.

client_port 는 5단계의 값, server_port 는 9000. 본문에 SYN 과 TIME-WAIT 라는 낱말이 들어가야 합니다.