서론

이번 글에서는 클라이언트-서버 간 통신을 하기 전 , 서로의 상태를 확인하는 과정인 3-way hadnshake에 대해 알아보고, 

실제 인터페이스의 패킷을 캡쳐해 해당 과정을 확인해보겠습니다.

 

먼저, TCP/IP 프로토콜 스택에서의 4계층은 TCP와 UDP가 담당하고 있습니다.

여기서 4계층의 목적은 목적지를 찾아가는 주소가 아니라 
(이 과정은 이미 2계층-MAC주소과 3계층-IP주소에서 이루어집니다)
 애플리케이션에서 사용하는 프로세스를 정확히 찾아가고 데이터를 분할한 패킷을 잘 분해하고 조립하는 것입니다.

 

TCP 프로토콜은 정보유실 없는 통신을 보장하기 위해 패킷에 번호(Sequence Number)를 부여하고, 잘 전송되었는지에 대해 응답(Acknowledge Number) 합니다. 

 

또한, 한꺼번에 얼마나 보내야 수신자가 잘 받아 처리할 수 있는지 전송 크기(Window Size)까지 고려해 통신합니다.

( TCP/IP 송수신에 대해선 https://yunja.tistory.com/28에 다루었습니다. )

 

본론

TCP에서는 위에서 설명했듯이 유실없는 안전한 통신을 위해 통신 시작 전, 사전 연결작업을 진행합니다.

 

패킷에 순서를 부여하는 것을 Sequence Number, 응답 번호를 부여하는 것을 ACK 번호라고 부른다고 했는데요. 두 번호가 상호작용해 순서가 바뀌거나 중간에 패킷이 손실된 것을 파악할 수 있습니다. 대략적인 과정은 아래와 같습니다.

 

위의 과정으로 HTTP 요청 시, 서버 측의 응답하려는 데이터를 패킷 단위로 쪼개어 보내게 됩니다.

  1. 출발지 송신 측에서 시퀀스 번호를 0으로 보냅니다.
  2. 수신 측에서는 송신측의 0번 패킷을 잘 받았다는 표시로 응답 번호(ACK)에 1을 적어 응답합니다.
  3. 수신 측의 패킷을 받은 송신 측은 시퀀스 번호를 1로 , ACK 번호는 상대방의 0번 시퀀스를 잘 받았다는 의미로 시퀀스 번호를 다시 1로 부여해 송신 합니다.

위의 과정대로 TCP에서의 Sequence Number(Seq), 응답번호 (ACK)가 상호작용하여 데이터를 주고 받는 것을 살펴보았는데요.

 

이것을 토대로, 오늘의 주제인 위 과정이 실행되기 전의 사전 연결작업인 3방향 핸드셰이크에 대해 알아보겠습니다.

패킷 네트워크에서는 동시에 많은 상대방과 통신하므로 정확한 통신을 위해서 각 통신에 필요한 리소스를 미리 확보하는 작업이 중요합니다.

TCP에서는 3번의 패킷을 주고받음으로써 통신을 서로 준비(송,수신 측이 현재 패킷을 주고 받을 수 있는 상태인지)하기 때문에 3방향 핸드셰이크라고 부릅니다.

 

 

  1. 통신 시도시 송신자는 플래그에 있는 SYN 필드를 1로 표기해 패킷을 보냄
    • 이때 자신이 사용할 첫 seq no(시퀀스 번호)를 적어 보냄.
  2. SYN 패킷을 받은 수신자는 SYN과 ACK 비트를 플래그에 1로 표기해 응답함.
    • 첫 패킷이므로 SYN을 1로 표기하고, 송신자가 보냈던 패킷의 응답이기도 하니, ACK 도 함께 1로 써서 보냄
  3. ACK 번호는 10번까지 잘 받았으니 다음에는 10+1번을 달라는 의미임.
  4. 수신자의 응답을 받은 송신자는 연결 확립을 위해 다시 한번 응답 메시지를 보냄.
    • 수신자가 ACK 번호를 11로 표기했으니, seq no 를 11로 표기해 응답함.
    • 동시에 수신자의 시퀀스 번호 20에대한 응답이니, ACK번호를 2로 보냄(20번까지 잘 받았음, 다음꺼 보내줘)
    • ACK 번호를 비교해가며, 패킷 유실이 있으면 파악 후, 메모리에 유지해놓은 데이터를 이용해 재전송함.

대략적인 과정을 통해 3방향 핸드 셰이크 패킷이 어떻게 주고 받는지 알아봤으니, 직접 패킷을 캡쳐해 확인해보도록 하겠습니다.

curl www.google.com을 통해 GET 요청을 보낸 후, 캡쳐한 패킷 (*와이어샤크)

 

$  sudo tcpdump -i en0 -w /Users/yunsmac/Desktop/google.pcap tcp port 80

위 명령은 현재 퍼블릭 네트워크로 통신 중인 제 MAC 주소를 가진 인터페이스에 대해, tcp프로토콜 port 80인 패킷만 캡쳐해 제 로컬에 저장하겠다는 의미입니다. HTTP 요청시 사용하는 포트번호는 기본적으로 80이며, 이는 Well known 포트라고 합니다.
  1. 첫 번째 줄의 Source(출발지 주소)는 제 ip 주소, Destination(목적지 주소)는 google.com의 IP 주소입니다.
    1. 제가 클라이언트의 입장으로 서버에서 데이터를 받아오기 위해 연결을 시작한다는 의미이죠.
    2. 송신측에선 첫 번째 패킷이니 SYN 플래그를 1, 시퀀스 번호를 0으로 사용해 패킷을 전송합니다.
  2. 두 번째 줄에서 출발지 ip주소와 목적지 ip주소가 변경되어 google 서버에서 SYN, ACK 플래그로 응답해주는 모습입니다.
    1. 이 때, 수신측에서 보내주는 첫 패킷이니 시퀀스 번호는 0, 응답 번호는 수신 측의 시퀀스 번호에 1을 더해 보내줍니다.
    2. 수신측의 응답을 잘 받았으니 송신측은 송신 측의 Ack 번호를 시퀀스 번호로 설정하고, 응답 번호는 수신 측의 시퀀스 번호에 1을 더합니다.
  3. 네 번째 줄에서 서로 간 통신을 하기 위한 사전 연결 작업이 끝났으니, 송신 측에서는 그제서야 HTTP GET 요청을 보냅니다.

  4. 5 ~ 15번째 줄에선 서버 측에서 요청에 대한 응답 데이터를 보내는 과정입니다.

  5. 16 ~ 17 번째 줄은 클라이언트 측의 Window Size( 현재 받을 수 있는 패킷의 양)을 보내줍니다- [TCP Window Update].
    1. 이후 클라이언트 측의 윈도우 사이즈를 확인한 서버 측은 클라이언트 측이 윈도우 사이즈가 넉넉하니, 계속해서 데이터를 전송합니다.
    2. 이때, 서버 측은 패킷을 전송할 때, TCP 헤더에 PSH 플래그를 설정해 보냅니다.
    3. PSH 플래그는 서버 측에서 전송할 데이터가 없거나 데이터를 버퍼링 없이 응용 프로그램으로 즉시 전달할 것을 지시할 때 사용됩니다.
    4. 또한, HTTP status 200 으로 성공적으로 응답이 완료되었다는 패킷도 함께 전송합니다.
  6. 27 ~ 31 번째 줄 . 모든 패킷이 잘 분해되고 조립되어 클라이언트 - 서버 측 간 연결을 종료하게 됩니다. (4방향 핸드셰이크)  
    1. 이때는 FIN 플래그를 사용하게 됩니다.
    2. FIN 플래그는 연결 종료 시 1로 표시되며, 위와 같이 데이터 전송을 마친 후 정상적으로 양방향 종료 시 사용됩니다.

 

결론 및 참고

이로써, 클라이언트 - 서버 간 통신 상황에서 3방향 핸드셰이크가 어떤 과정으로 이루어지는 지 직접 확인해보았습니다.

 

참고로 정상적으로 양방향 종료 시, FIN 플래그가 1로 표시 되어 사용된다고 했는데 실제 서비스를 운영하는 과정에서 서버 측에서 프로세스가 종료 되었거나 의도치 않는 동작을 했을 경우, 서버 측에서 보낸 패킷에서 RST 플래그가 표시 될 수도 있습니다. 

 

RST 플래그 또한 연결 종료 시 1로 표시되어 사용되지만, FIN 플래그와 달리 연결 강제 종료를 위해 연결을 일방적으로 끊을 때 사용됩니다.(서버 측이던, 클라이언트 측이던 무엇인가 문제가 생겼다는 뜻이겠죠 ? )

 

이번 글에서는 3방향 핸드 셰이크 과정을 보기 위해 해당 과정 패킷을 중점적으로 보았지만,

 

이런식으로 패킷을 캡쳐하여 어떤 부분에서 문제가 일어났는지 분석을 할 수 있다면 , 서비스 운영 및 여러 문제 해결에 도움을 더 줄 수 있을 것 같습니다. 끝으로 지인 분께서 저한테 해주셨던 말씀으로 이 글을 마치도록 하겠습니다.

패킷은 절대 거짓말을 하지 않는다.
- 어느 훌륭한 네트워크 엔지니어(CCIE) - 

'Network' 카테고리의 다른 글

TCP 송/수신 원리  (8) 2023.09.03
DHCP ? (Dynamic Host Configuration Protocol)  (5) 2023.08.09

오늘은 TCP Segment 송수신 원리에 대해 알아보겠습니다.

 

클라이언트와 서버 간 서로 통신을 위해 TCP로 연결 된 모습을 간략하게 나타낸 그림입니다.

TCP 프로토콜로 연결하기 전, 3-way handshake 과정은 다음번에 더 자세히 다루도록 하겠습니당.

간단하게 설명하자면 3-way handshake는 클라이언트와 서버 간 데이터를 주고 받기 전에 서로간의 연결 상태(SYN, ACK)를 확인하는 과정을 말합니다.

먼저, 위 그림 같은 상황에서 서버가 클라이언트로 응답 데이터를 보낼때 서버에서 일어나는 상황을 볼까요 ?

 

서버의 프로세스에서 클라이언트 쪽으로 하드웨어 영역에 있는 파일을 전송한다 가정했을 때, 서버 쪽 프로세스는 해당 파일을 처리하기 위해 메모리에 적재를 합니다.

하드웨어 영역의 파일은 커널의 파일 시스템에 의해 관리되며, 하드 디스크는 소프트웨어 영역의 하드 디스크 드라이버에 의해 관리되겠죠.

 

이 때, 할당되는 메모리가 전송하려는 파일 크기보다 작다면, 할당된 메모리 크기에 맞춰 파일을 분할하여 메모리에 적재하게 됩니다.

(메모리만 받쳐준다면...많이 적재하는게 좋긴 하겠죠 ..ㅎㅎ) 

 

바로 아래 그림과 같이 말이죠.

전송하려는 파일 크기는 1.4MB, 할당된 메모리는 64KB이며 64KB 단위로 분할

이후, 서버 측 프로세스에선 데이터를 전송하기 위해 TCP/IP 프로토콜 스택에서 TCP Buffer에 현재 메모리에 적재되어 있는 분할된 데이터를 담게 됩니다. 

분할된 데이터가 메모리에서 TCP Buffer로 카피된다고 볼 수 있죠.

이 과정을 Buffered I/O라고 부릅니다.

 

TCP 버퍼에 카피된 데이터가 Internet 계층으로 인캡슐레이션 과정이 일어나게 되고, 이 과정에서 데이터가 세그먼트로 분할 되며 , 각각의 세그먼트들에게는 번호가 부여되게 됩니다. ( 예시 : 1번 세그먼트 + 2번 세그먼트 + 3번 세그먼트 = 보내려는 데이터 )

응답 서버 측 입장에선 4-3-2-1 순으로 패킷이 인캡슐레이션 된다.

분할된 세그먼트를 순서대로 패킷에 담아 스위치와 라우터 등을 거쳐 클라이언트 쪽으로 전송하게 됩니다. 

 

이 때, 당연히 해당 패킷에는 목적지 주소 정보도 같이 포함되어 있습니다.

 

성공적으로 패킷이 클라이언트 측으로 도착하면, 인캡슐레이션 된 패킷을 디캡슐레이션을 하게 됩니다. (택배 상자가 오면 하나하나 까듯이요 )

  1. 클라이언트 측 Internet 레이어 수준에서 패킷을 분해합니다. 
  2. 클라이언트 측 Transport 레이어 수준에서 세그먼트를 확인합니다.

클라이언트 측 OS 모델

서버 측에서 분할된 세그먼트는 다시 클라이언트 측 TCP 버퍼에 그대로 적재되게 됩니다. 

 

예시 상황으로 위 과정을 다시 쉽게 설명드리자면

  1. 서버 측에서 분할된 데이터 ( 1, 2번 세그먼트) 를 패킷에 적재합니다.
  2. 해당 패킷에 목적지 주소 정보와 포트 번호를 함께 붙입니다.
  3. 서버 -> 클라이언트로 패킷을 전송 후, 클라이언트에선 다시 패킷을 분해해 클라이언트의 TCP/IP 프로토콜 스택 상위 계층으로 올려보냅니다.
  4. 클라이언트 측은 1,2번 세그먼트를 성공적으로 받았다는 응답 (ACK)을 서버측으로 보내게 됩니다.

그 이후 서버 측에서 만약 더 보내야할 데이터가 있을 경우에는 위의 4번 과정의 ACK를 Wait하게 됩니다.

 

(왜냐하면 1, 2번 세그먼트를 클라이언트에서 성공적으로 받았다는 응답이 있어야 3번 세그먼트를 전송할 수 있기 때문이지요)

 

여기서 중요한건, 위의 4번 과정에서 서버 측으로 ACK를 보낼 때, 현재 TCP Buffer에서 남아있는 공간 ( Window Size)도 같이 전송하게 됩니다. (수신의 경우 버퍼는 OS에서 관리하는 수신 버퍼를 사용합니다)

클라이언트 측의 세그먼트를 처리할 수 있는 공간이 얼마 남았다, 라는 것을 보내준다는 의미라고 생각하시면 됩니다.

 

여기서 클라이언트의 ACK( 1, 2번 세그먼트 잘 받았어 3번 보내줘) 응답을 받은 서버는 3번 세그먼트를 보내기 전, 클라이언트의 윈도우 사이즈를 확인하고, 보낼지 말지 결정하게 됩니다.

 

만약, 보내려는 세그먼트 사이즈가 클라이언트 측의 윈도우 사이즈 보다 작으면 당연히 보내게 되는데, 이 반대의 상황이라면

Wait가 걸려버리게 됩니다.

여기서 서버측의 Wait가 발생하는 상황은 여러가지가 있을 수 있겠지만,

이 경우에서는 클라이언트 측의 파일 처리 속도가 네트워크 데이터를 수신하는 속도보다 느릴 경우, 클라이언트 측의 충분한 버퍼 사이즈가 확보되지 않아 발생할 수 있습니다.

그래서 보통 클라이언트 측에서는 데이터를 읽자마자 처리 루틴을 별도 스레드로 분리해 대응한다고 합니다.

만약 데이터를 읽고 처리하는 코드를 한 스레드 안에 묶어버리면 위와 같이 장애 가능성이 매우 높아집니다.

 

여기까지 서버와 클라이언트 측의 데이터가 어떤 식으로 전송되는지 알아보았습니다 !

 

ref : https://youtu.be/K9L9YZhEjC0?si=vMtodPcxR6AAQLdS

'Network' 카테고리의 다른 글

3-way handshake  (1) 2023.11.07
DHCP ? (Dynamic Host Configuration Protocol)  (5) 2023.08.09

오늘은 네트워크 주요 기술 중 하나인 DHCP에 대해 알아보겠습니다.

 

그전에 앞서, 호스트가 네트워크와 통신 시에 필요한 것들을 살펴볼까요?

 

첫 번째로, 물리적 네트워크가 구성이 되어 있어야 합니다. (외부망이든 내부망이든)

 

두 번째로, IP 주소, 서브넷 마스크, 게이트웨이, DNS 주소 설정 등이 더 필요하죠

 

여기서 IP 주소, 서브넷 마스크, 게이트웨이, DNS 주소 설정 등을 자동으로 해주는 것을 가리켜 정적 할당이라 하고,

 

그 외에 자동으로 설정하는 것을 동적 할당이라고 합니다.

 

최근에는 동적 할당 방식을 많이 이용하고 있는데, 이는 보안이 강화될뿐더러 저희가 수동으로 관리를 하지 않으니 관리가 쉬워진다는 장점 때문입니다

 

또한 IP를 별도로 설정하지 않아도 네트워크에 쉽게 접속이 가능합니다.

 

이걸 가능케 하는 프로토콜이 있는데요. 바로 오늘 소개할 프로토콜인 DHCP 프로토콜입니다.

 

DHCP 프로토콜은 앞서 설명했다시피, IP를 동적으로 할당하는 데 사용되는 프로토콜입니다.

 

해당 프로토콜을 사용하면 네트워크와 통신 시에 필요한 각종 설정들을 자동으로 할당받아 사용할 수 있습니다.

 

요약하자면,

  1. 별도의 IP 설정 작업이 필요 없어 사용자와 관리자 모두 편하게 네트워크에 접속이 가능

  2. 사용하지 않는 IP 정보는 회수되며 사용하는 경우에만 재할당이 됩니다

  3. 자동으로 할당되니 사용자가 직접 입력하며 발생할 수 있는 사소한 실수까지 예방이 가능합니다.

 

DHCP 동작방식

Discover 부터 DHCP 서버로의 Ack 과정

그럼 우리 클라이언트가 DHCP 서버에서 어떤 식으로 동적할당을 받는지 한 번 알아보겠습니다.

 

1. DHCP Discover ( 서버야 어딨니, 나 뭐 쓸지 좀 알려줘)

DHCP 클라이언트는 DHCP 서버를 찾기 위해 DHCP Discover 메시지를 연결된 네트워크상에서 브로드캐스트로 전송합니다.

  • 요청을 보낼 때, 출발지 MAC 주소는 클라이언트 본인으로 설정합니다.
  • 목적지 MAC 주소와 IP는 브로드캐스트 방식으로 요청합니다.
  • 아직 IP를 할당받은 상태가 아니니 Src IP는 0.0.0.0을 사용합니다.
  • IP를 할당받는 과정이므로 , 패킷을 정상적으로 주고받을 수 없으니 TCP가 아닌 UDP를 사용합니다.
  • 결국 첫 번째 과정은 DHCP 서버를 찾기 위한 메시징의 과정이라 볼 수 있습니다.

 

2. DHCP Offer (나 여깄어, 이거 쓰면 돼)

  • DHCP 서버는 DHCP Discover를 수신합니다.
  • DHCP 서버는 클라이언트에 할당할 IP 주소와 서브넷, 게이트웨이, DNS 정보, Lease Time( 해당 정보 *임대 시간  )의 정보를 포함한 DHCP 메시지를 클라이언트로 전송을 해줍니다.
    • 여기서 클라이언트에 할당할 IP 주소는 DHCP IP Pool 중에서 선택하게 됩니다. ( DHCP 서버가 보관하는 IP 리스트라 생각하시면 됩니다. )
    • IP 리스트들은 우리가 임의로 설정도 가능합니다. ( MacOS 기준 시스템 설정 -> 네트워크 -> 고급 사항)
*임대 시간 ?

클라이언트가 DHCP 서버로부터 할당 받은 정보들을 사용(임대) 할 수 있는 시간을 뜻합니다. 만료가 된다면 다시 1번의 과정을 거쳐야 할 것 같지만, 

실제로 저렇진 않고, 쓰는 도중에 새로 갱신을 합니다. ( 마치 AccessToken과 RefreshToken 처럼요 . )

리프레쉬 과정은 임대 시간중 50%가 지날 때 쯔음 진행되며

DHCP Discover(1번 과정)과 Offer(2번 과정)을 생략 한 채, 클라이언트 요청 ( Request ) 서버의 응답( Ack)를 통해 갱신됩니다.

1, 2번 과정을 생략하기 때문에 불필요한 브로드캐스트가 발생하지 않고, 유니캐스트로 진행되기 때문에 시간 또한 짧습니다.

만약 리프레쉬 과정에서 클라이언트가 응답하지 않는다면 사용하던 IP를 다시 서버에 반납하게 됩니다.

 

3. DHCP Request ( ㅇㅋ 이거 지금부터 쓴다 그럼?)

  • DHCP 클라이언트는 DHCP 서버로부터 제안받은 IP 주소와 DHCP 서버 정보를 포함한 요청 메시지를 브로드 캐스트로 다시 전송합니다.
여기서 한 가지 의문이 생기실 수 있습니다. 2번째 과정에서 클라이언트는 서버로부터 받은 메시지 안에 정보들이 담겨있을 텐데, 왜 유니캐스트로 전달하지 않고, 브로드 캐스트로 전달할까? 

여기에는 세 가지 이유가 있습니다. 

1.  DHCP 서버 여러 대가 동작하는 환경 일 수 있기 때문에 이를 위해 기본적으로 브로드 캐스트 방식을 사용합니다.

2. DHCP 서버 여러 대가 운용 중인 환경에서 클라이언트는 서버로부터 Offer 메시지를 여러 개를 수신 받고, 하나만 골라 Request 메시지를 보냅니다.

3. 위의 과정에서 브로드캐스트가 수행되며 여러 서버에서는 해당 Request 메시지가 자기것인지 확인하고 아니라면 패킷을 드랍시킵니다.

 

 

4. DHCP Acknowledgement ( ㅇㅇ 지금 부터 쓰는 거 확인했엉, 기록해둘게 )

  • 3번 과정을 통해 DHCP 클라이언트가 제공해준 IP 주소를 사용하겠다는 요청을 받습니다.
  • DHCP 서버는 해당 요청을 수신 후, 해당 IP를 누가, 언제부터 사용했는지를 기록합니다 ( 아마 Pool 때문에 그런 것 같습니다.)
  • 그 후, DHCP 클라이언트에게 확인했다는 메시지를 보내게 됩니다.
더보기

🛎️ 짤막 상식 !

 

IP Pool 에서 할당 가능한 IP가 모두 할당이 된 경우에는 새로운 클라이언트에서 IP 요청이 들어와도 IP를 내 줄 수 없게 되는 상태가 벌어집니다.

 

이를 악의적으로 이용해 DHCP 서버에서 가용한 모든IP를 가짜로 할당받아,

 

실제 클라이언트가 IP 주소를 할당받지 못하게 하는 공격 방식을 DHCP Starvation(기아 상태) 공격이라고 합니다 

 

 

여기까지 DHCP란 무엇이며, DHCP의 동작 방식을 살펴보았습니다.

 

'Network' 카테고리의 다른 글

3-way handshake  (1) 2023.11.07
TCP 송/수신 원리  (8) 2023.09.03

+ Recent posts