Profile picture

[네트워크] 와이어샤크(Wireshark)로 TCP/UDP 패킷 분석해보기

JaehyoJJAng2024년 06월 03일

실습 정보

  • SERVER: Ubuntu 22.04
    • IP: 192.168.219.113
    • PORT: 65432
  • CLIENT: Windows 11
    • IP: 192.168.219.164
    • PORT: 51112

TCP 패킷 분석

TCP 헤더 구조

image

  • Source Port/ Detination Port: 출발지 포트와 목적지 포트를 나타냄
  • Sequence Number: 올바른 순서로 데이터를 보내기 위한 고유 일련 번호
  • Acknowledgement number: 다음 세그먼트를 수신할 준비가 된 상태를 나타내는 번호
  • Header length: Header 길이를 32bit 단위로 나타낸다. 최소 필드 값은 20 bytes 이다.
  • Window size Field: 흐름 제어를 위해 송신 시스템의 가용 가능 버퍼 크기를 나타냄 (Byte 단위)
  • Checksum Field: TCP segment의 내용이 유효한지 검증

TCP 연결을 위한 준비

  • TCP 연결을 테스트해보기 위해 파이썬으로 간단한 TCP 소켓 프로그래밍을 해볼 것이다.

server.py

import socket

# 서버 IP, 포트 설정
HOST :str = "192.168.219.113"
PORT :int = 65432

# 소켓 객체 생성
with socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM) as s:
    # 소켓을 HOST와 PORT에 바인딩
    s.bind((HOST, PORT))
    
    # 클라이언트의 연결 요청 대기
    s.listen()
    print("서버가 시작됨. 클라이언트의 연결을 기다리는 중 ..")
    
    while True:
        # 연결 수락
        conn, addr = s.accept()
        with conn:
            print('연결 되었음!', addr)
            while True:
                data = conn.recv(1024)
                if not data:
                    break
                print("받은 데이터: ", data.decode())
                conn.sendall(data) # 받은 데이터를 그대로 클라이언트에 전송

client.py

import socket

# 서버 IP, 포트 설정
HOST :str = "192.168.219.113" 
PORT :int = 65432

# 소켓 객체 생성
with socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM) as s:
    # 서버에 연결 요청
    s.connect((HOST,PORT))
    
    # 메시지 생성
    message :bytes = input("input string ..\n:").encode()

    # 서버에 메시지 전송
    s.sendall(message)
    
    data = s.recv(1024)

print("받은 데이터: ", data.decode())

  • Client와 Server 측 코드를 위와 같이 작성하였다.
  • 위 프로그램을 실행하여 패킷을 캡쳐해보자.
  • 위 방법 말고도, 웹 사이트에 접속하는 것만으로도 3-way, 4-way Handshake 과정 패킷 캡쳐가 가능하다.

코드를 실행하고 Client 측에서 포트를 확인해보자.
image
왼쪽 192.168.219.164(Client), 오른쪽 192.168.219.113(Server) 연결이 서로 성립(Established) 된 것을 볼 수 있다.

이제 패킷을 확인해보자.


TCP 패킷

image


3-Way Handshake 과정 - 연결 수립

image
사진에 나온 패킷 순서대로 살펴보도록 하자.

  • 9. 192.168.219.164(Client)측에서 192.168.219.113(Server)와 연결을 위해 SYN 패킷을 보내고 있다.
  • 10. .113(Server).164(Client)의 연결을 받았다는 ACK 응답을 하였다. 그리고 .113(Server)도 연결 요청(SYN)을 보낸다.
  • 11. .164(client)는 최종적으로 ACK 응답을 하게 됨으로써 서로 1:1 연결이 되었다.

Data Send/Receive 과정

image

  • 55. PSH flag의 의미는 데이터를 버퍼에 쌓지 말고 즉시 보내라 라는 의미이다.
    • 단순히 ACK만을 보내면 중간중간 데이터가 버퍼에 쌓이기 시작하여 PSH flag와 함께 보내면 즉시 보내지게 된다.

클라이언트단에서 "Hello?" 라는 문자열을 전송하였다. image
패킷을 자세히 확인해보자.


image

  • ACK, PSH flag가 Set이 된 상태이다
  • 또한 내가 Hello? 라는 문자열을 보낸 상태이고 Data Size는 6 bytes 인 것을 확인할 수 있다.
  • 처음 Seq Number를 보면 1로 설정되어 있고 다음 Seq number는 Data Size(=6)를 더한 7을 다음 Seq number로 맞추고 있다.

image

  • 56. 192.168.219.113(Server)192.168.219.164(Client)가 보낸 메시지를 잘 받았다는 ACK 응답을 보내고 있다.
    • ACK 번호를 7로 맞추고 있음.

  • 57. 192.168.219.113(Server)192.168.219.164(Client)에게로 메시지를 보내고 있다. 55번 패킷과 유사하게 PSH, ACK flag도 함께 보내고 있다.

4-Way Handshake 과정 - 연결 해제

image

  • 58. .164(Client).113(Server)에게 연결 종료를 위해 FIN/ACK 패킷을 보내고 있다.
  • 59. .113(Server).164(Client)에게 ACK 확인 응답을 보내고 세션이 끝날 때까지 기다린다 (CLOSE_WAIT)
  • 60. 마찬가지로 통신이 끝나면 Server도 Client에게 FIN/ACK 패킷을 보낸다.
  • 61. Client에서 수신 되었다는 ACK 메시지를 보내며 통신이 종료된다.

UDP 패킷 분석

UDP 연결을 위한 준비

  • UDP 연결을 테스트해보기 위해 파이썬으로 간단한 UDP 소켓 프로그래밍을 해볼 것이다.

server.py

import socket

# 서버 IP, 포트 설정
UDP_IP :str = "192.168.219.113"
UDP_PORT :int = 5005

# UDP 소켓 생성
server_socket = socket.socket(family=socket.AF_INET, type=socket.SOCK_DGRAM)

# 소켓을 주소에 바인딩
server_socket.bind((UDP_IP, UDP_PORT))
print("UDP 서버 시작됨")

while True:
    # 클라이언트로부터 데이터 수신
    data, addr = server_socket.recvfrom(1024)
    print(f"수신된 데이터 : {data.decode()}")
    
    # 클라이언트에게 데이터 전송
    server_socket.sendto(data, addr)

client.py

import socket

# 서버 IP, 포트 설정
UDP_IP :str = "192.168.219.113"
UDP_PORT :int = 5005

# UDP 소켓 생성
with socket.socket(family=socket.AF_INET, type=socket.SOCK_DGRAM) as s:
    while True:
        message :str = input("전송할 메시지 입력\n:")
        
        # 서버에 데이터 전송
        s.sendto(message.encode(), (UDP_IP, UDP_PORT))
        
        # 서버로부터 데이터 수신
        data, addr = s.recvfrom(1024)
        print(f"서버로부터 수신된 데이터: {data.decode()}")

  • Client와 Server 측 코드를 위와 같이 작성하였다.
  • 위 프로그램을 실행하여 패킷을 캡쳐해보자.

image

  • client 측에서 문자열을 입력하고 Server에 전달하면 서버는 그대로 반환하는 일종의 Echo system 형태이다.
  • client에서 hi, hello를 보내고 서버로부터 그대로 반환 받았다.

UDP 패킷

image

  • UDP 패킷의 경우 TCP 패킷과 다르게 매우 단순하다. TCP 에서는 연결 수립과 해제에서 handshake 과정이 필요하였다.
  • 그러나 UDP의 경우 handshake 과정 없이 data 만을 전송한다.

Loading script...