Network/Unix Network Programming

[UNP] Transport Layer에 대한 이야기

Binceline 2017. 3. 22. 21:50

모든 것은 결국 Datagram 단위로 쪼개져 보내진다.


TCP가 Stream 방식이지만, 이건 sequence number를 이용해 순서를 정해주는 방식이다.


DSCP : 네트워크 트래픽에 서로 다른 수준의 서비스를 할당할 수 있도록 하는 IP 패킷의 한 필드


MTU(Maximum Transmission Unit) 

 - 네트워크 Layer에서 Segment 없이 보낼 수 있는 최대 데이터그램 크기. 즉, 한 번에 보낼 수 있는 크기.

 - 하드웨어에 따라 값이 다르다.

 - 우리가 주로 사용하는 이더넷의 MTU 값은 1500이다.

 - MSS(Maximum Segment Size)는 MTU에 영향을 조금 받는다. 

MTU = MSS + IP Header + TCP Header

MTU가 1500이라면, MSS = 1500 - ( IP Header(20 bytes) + TCP Header(20 bytes) ) = 1460bytes가 된다.

Connection에서 MSS 크기를 전송해 주는데, 이 시점에서부터 서로 어떤 크기로 전송해야 할 지 알 수 있게 된다.


 - 두 End point 사이의 네트워크 중에서 가장 작은 MTU를 가지는 네트워크를 Path MTU라고 부른다.

즉, 최단경로를 path MTU라고 한다.

하지만, 각 End point 에서의 MTU가 다를 때가 많다. 네트워크 Routing은 일반적으로 비대칭적이기 때문이다.

즉, A->B 와 B->A의 MTU값이 다를 경우가 많다는 것이다.


IP datagram이 외부로 전송될 때, 만약 MTU를 초과한다면 IPv4 / IPv6에 의해 Fragmentation(쪼개짐)이 일어난다.

최종 목적지에 도착할 때까지 다시 합쳐지지 않는다.


IPv4

 - Host가 Fragmentation

 - Router가 Fragmentation을 할 때도 있다.

 - IPv4 Header에 Fragmentation을 다루는 Field가 있다.

 - Header에 DF(Don't Forget) Field가 설정이 되면, 호스트나 Router에 의해 Fragmentation되는 것을 방지한다.

만약, 어떤 라우터에서 MTU 값보다 큰 크기 + DF설정된 Datagram을 전송받는다면, 

"destination unreachable, fragmentation needed but DF bit set" ICMPv4 에러를 발생시킨다.


IPv6

 - Host(시작할 때)만 Fragmentation 가능

 - IPv6 Header에 Fragmentation을 다루는 Field가 없고, 예외로 취급되기 때문에 이 정보를 option header에 담는다.

 - IPv6 Router(Host가 아님)는 Fragmentation을 하지 않기 때문에, Datagram에는 DF가 기본으로 설정되어 있다.

만약, 어떤 Router에서 MTU값보다 큰 크기의 Datagram을 전송받는다면, "Packet too big" ICMPv6 에러를 발생시킨다.


가끔 방화벽에서 패킷 내용을 검사하기 위해 Fragmentation된 패킷들을 합치는 작업을 하기도 하는데, 특정 공격들을 방어해 준다.


이 과정들은 TCP / UDP와는 다른 Layer이므로, TCP / UDP와는 무관하게 진행된다.


IPv4 / IPv6의 DF Field는 경로 탐색을 위해 쓰이기도 한다.

예를 들어, TCP 환경에서는 DF 설정 후 패킷 전송. 중간에 Router로부터 ICMP 에러를 받게 된다면, 최소 MTU를 사용해 재전송한다.

이 기능은 Path MTU Discovery라고 하는데,  IPv4에서는 옵션이고, IPv6에서는 기본 탑재된 기능이다.

UDP에서는 그냥 전송하는 방식이라 경로 탐색을 할 일이 없으니 PMTUD 기능이 존재하지 않는다. 

PMTUD 작업이 무의미해질 때가 있다. 점점 많은 방화벽들이 ICMP 메시지를 삭제해버리는 식으로 개발되기 때문에,

TCP는 자신이 보내는 메시지 크기(MTU)를 줄여야 한다는 메시지를 전달받지 못한다.


Minimum Reassembly Buffer Size

 - IPv4 : 576 Byte

 - IPv6 1500 Byte


대부분 Application에서 이 Size를 넘는 IP datagram의 생성을 제한한다.



시나리오

TCP -> PMTUD 과정으로 최적 MSS 가지는 네트워크 탐색 -> 처음에 서버 & 클라이언트가 서로 MSS 교환


UDP -> MSS를 모르므로 Fragmentation을 피하기 위해 애초에 Minimum Reassembly Buffer Size보다 작은 

크기의 데이터그램으로 통신한다.


UDP건 TCP건 DF Field 옵션 설정에 따라 다음 기능이 작동한다.


Packet Size가 MSS보다 클 경우,

-> IPv4의 경우 DF Field 설정 On : 라우터를 거치면서 Fragmentation O / 재전송 요청 X

-> IPv4의 경우 DF Field 설정 Off : 라우터를 거치면서 Fragmentation X / 재전송 요청 O


MSS의 목표 : Fragmentation을 피하자!

MSS값의 최대 : 16bit 65,535


IPv4에서 TCP Data의 최대 크기는 65535 - 20 - 20 = 65495.

IPv6에서는 Jumbo Payload 옵션이 존재한다. 

65,535 byte 를 초과하는 데이터를 보내기 위해서는 이 옵션을 사용해야 한다.

이 옵션을 사용하게 되면 Payload length 값은 0이 되어야 한다.


TCP 소켓에 데이터를 쏠 때 일어나는 일들.

1. Application이 write 함수 호출 -> Kernel이 Application Buffer의 데이터를 Socket Send Buffer로 복사.

Blocking Socket의 경우, 소켓의 Buffer 공간이 부족하면 Process가 Sleep된다.

Kernel은 Application의 데이터가 Socket Buffer에 전부 복사되기 전까지 Return되지 않는다. 

-> TCP Socket에서 write함수가 return된다는 건 Application의 버퍼를 재사용할 수 있는 상태라는 뜻이다.

2. TCP는 Send Buffer의 data를 TCP 전송 규칙에 따라 data를 송신한다.

1) TCP는 IP로 MSS 사이즈 이하의 크기인 data에 TCP Header를 segment마다 붙여서 전달한다. 

IP Layer에 전달하기 전에 fragmentation을 할 수 있지만, 초반에 MSS 값 교환과 PMTUD 기능으로 최대한 피하는 것이 이상적이다.

2) MSS 크기는 Peer로부터 받는다. 받지 못한다면, 최소 MSS인 536(IPv4일 경우) 으로 결정된다.

3) 송신 측은 상대방으로부터 ACK를 받기 전까지 데이터의 복사본을 가지고 있어야 하고, 그 때까지 Send Buffer의 데이터를 지우지 않는다.

3. IP는 전달받은 data에 Header를 붙이고, 목적지 IP 주소에 대한 Routing Table(송신 가능한 목적지가 적혀 있음 : 공유기 기능이기도 하당)을 검색하고,

DataLink Layer로 데이터를 전달한다.

4. Datalink는 Output Queue를 가지고 있는데, 이 Queue가 꽉 차면 패킷이 손실되고 Protocol Stack을 따라 에러가 반환된다.

datalink -> IP -> TCP 순으로 반환되는데, TCP에서 이 에러를 핸들링하게 되면 Segment 재전송을 시도하게 된다.



UDP


1. Application -> UDP

UDP socket은 Send Buffer가 없다. Send Buffer의 크기를 가지고 있긴 하지만 이는 UDP 소켓에 쏠 수 있는 최대 Datagram 크기를 의미한다.

만약 Application이 send buffer의 크기보다 큰 데이터그램을 보내면 EMSGSIZE가 리턴된다.

TCP와 달리 ACK가 올 때까지 data의 복사본을 가지고 있을 필요가 없고, 실제로 send buffer가 필요가 없다.

2. UDP -> IP

UDP는 8yte짜리 Header를 data에 붙여서 IP로 Datagram을 보낸다.

IP Layer에서 Routing Table을 참조해 송신 가능한 목적지를 참조하고, data가 MTU보다 작으면 바로 Datalink Output Queue로 Datagram을 전송하고,

MTU보다 크면 Fragmentation을 수행한 후 Output Queue로 각 Fragment를 전송한다.

만약 큰 크기의 data가 전달되면 Fragmentation을 수행한다. TCP는 애초에 MSS로 Fragmentation을 피한다. UDP는 그런 게 없다.


UDP Socket에 대한 write 함수가 반환되었다는 것은 datagram 또는 datagram의 모든 fragment들이 datalink output queue에 추가되었다는 뜻이다.

queue가 꽉 차서 아예 write 함수가 실행되지 않고 반환되었다면, ENOBUFS 에러가 Application으로 반환된다.

가끔 ENOBUFS 에러 반환을 해 주지 않는 UDP 구현 버젼들 이 있다. 그럴 경우 그냥 폐기되고 끝이다.


SCTP

TCP처럼 Send Buffer를 가지고 있다. TCP처럼 SO_SNDBUF 소켓 옵션으로 버퍼 크기를 정할 수 있다.

지금은 Blocking Socket이라 가정한다.

1. Application write() 호출 -> Send Buffer로 data 전달(TCP/IP 4계층 중 Transport Layer(3계층)에 존재함)

이 때, 다음의 경우 sleep된다(blocking 방식일 때)

1) Send Buffer 크기보다 data의 크기가 더 크다

2) Send Buffer에 data가 남아 있다.(아직 data를 다 전송하지 못했다)


Kernel은 application buffer의 모든 data가 send buffer에 전달될 때까지 write()함수를 반환하지 않는다.

즉, wirte() 함수가 반환된다면 이는 application buffer가 다시 사용할 수 있는 상태가 되었다는 것을 의미한다.

주의할 것은, wirte() 함수가 반환되었다는 것이 peer에 데이터가 전달되었다는 것을 의미하지는 않는다는 것이다.

2. 그 후, SCTP 전송 규약([Stewart and Xie 2001]의 챕터 5에서 설명되어 있다)에 따라 전송된다. 

- 복잡한가보다;;


Standard Internet Services

TCP/IP 표준으로 제공되는 기능들이다. UDP TCP 둘 다 지원한다.

- time

- echo

- discard

- chargen

- daytime


보통 Unix Host의 inetd daemon에 의해 제공되는데, 요즘은 해킹에 쓰일 수 있으므로 비활성화 되어 있다...

-> 제공이 안 된다는 뜻이겠지?


참고 : HTTP는 내부적으로 TCP만을 사용한다.

자세한 건 윤창이 페이지에 그림을 보거나 Unix Network Programming 책에... 이... 부분을 보자.

Protocol usage of various common internet applications

반응형