티스토리 뷰

프로그래밍/Network

Transport Layer (2)

국윤창 2017. 2. 24. 21:55

7.  TIME_WAIT State

TCP 프로그래밍에서 가장 많이 혼동하는 것 중 하나가 TIME_WAIT state이다.

[그림 1] TCP state transition diagram


그림 1을 보면 ESTABLISHED state에서 active close가 TIME_WAIT state를 거쳐가는 것을 볼 수 있다.

endpoint가 이 state에 머물러 있는 시간은 maximum segment lifetime(MSL)의 2배인데 이를 줄여서 2MSL이라고 부르기도 한다.

TCP를 구현할 때 반드시 MSL값을 정해줘야하는데 RFC1122에 나온 추천은 2분이다. 그렇지만 Berkeley-derived implementation에서는 30초라서 보통 이 2MSL은 1분에서 4분정도 된다.

MSL은 IP datagram이 네트워크에서 존재할 수 있는 시간이다.

모든 datagram은 8-bit hop을 포함하고 있기 때문에 IP datagram이 존재하는 은 이 8-bit hop 시간을 따른다 (당연한 말이지만 최대 255)

그리고 이 8-bit hop은 MSL을 넘지 못한다. (MSL이 2분이면 120을 넘지 못한다는 의미)


packet이 손실되는 이유는 라우팅이 변칙적이기 때문이다.

라우터에 충돌(crash)이 일어나거나 연결이 끊기면 라우팅 프로토콜은 이런 상황을 안정화 시키거나 대안을 찾기 위해 수초~수분의 시간을 사용한다.

이 시간동안 routing loop(router A가 B에 패킷을 전달하고 B는 다시 A에게 전달)가 일어날 수 있고, 그 때 패킷은 이 루프에 갇힐수도 있다.

그 사이에 TCP segment가 time out되면 대안(alternative path)을 통해 재전송된다.

만약 손실된 패킷이 출발한지 MSL 내에 routing loop가 복구되면 loop에 갇혔던 패킷이 목적지로 전송된다. 이 패킷을 lost duplicate 또는 wandering duplicate라고 한다.

TCP는 반드시 이러한 duplicate들을 처리해야한다.


TIME_WAIT state가 존재하는 이유는 2가지이다.

1) TCP의 full-duplex connection termination을 신뢰할 수 있게 구현하기위해

2) 네트워크에서 오래된 duplicate segment를 없애기 위해


[그림 2] Packet exchange for TCP connection


첫번째 이유는 그림 2로 설명할 수 있다.

그림 2의 마지막 ACK가 손실됐다고 가정했을 때 server는 FIN을 다시 보낼것이다. 물론 클라이언트는 FIN을 받고 ACK를 다시 보내는 상황을 위해 이 state(TIME_WAIT)를 유지할 것이다.에

만약 이 마지막 ACK가 도달하지 않는다면 않는다면 서버는 클라이언트에 RST(TCP segment의 일종)로 응답하고, 이 응답은 서버에서 에러로 인식된다.

(정확한 정보를 위해 검색해본 결과 이 ACK가 결국 서버에 도달하지 않으면, 이후에 연결을 시도하는 클라이언트에 서버는 RST로 응답한다. 이는 클라이언트에서 접속 오류 메시지로 나타난다.)

TCP가 양쪽 data-flow 종료(full-duplex close)에 필요한 모든 작업을 완벽히 수행하는 중이라면, TCP는 반드시 이 4개의 segment의 손실을 다뤄야한다.

위 그림 2의 예제는 active close를 수행하는 endpoint가 TIME_WAIT state로 남아있는 이유를 보여준다. 위에서 설명했지만 이 endpoint는 마지막 ACK를 재전송할 상황을 위해 TIME_WAIT state로 남아있는 것이다.


두번째 이유를 이해하기 위해 12.106.32.254에 포트 1500과 206.168.112.219에 포트 21이 TCP connection이 이루어져 있다고 가정하자.

그리고 이 connection이 종료되고 조금 뒤에 같은 ip와 port로 연결을 수립했다고 가정하자.

이 재연결을 이전 연결의 incarnation라고 부른다. 좀 전과 ip와 port가 같기 때문이다.

TCP는 같은 ip, port로 재연결된 연결로부터 old duplicate가 다시 나타나는 것을 막아야하며, 이 old duplicate가 새 연결에 존재한다고 잘못 해석되어지는 것을 막아야한다.

이것을 위해 TCP는 TIME_WAIT state에 있는 연결에 대해서는 incarnation을 만들지 않는다. TIME_WAIT state의 시간이 MSL의 2배인 이유는 보낸 패킷이 손실되었을때, 보낸 패킷에 대한 reply패킷이 손실되었을 때를 고려한 것이다.

인터넷에서 참고를 하자면 위의 말은 TIME_WAIT state에 있는 연결에 TIME_WAIT시간이 짧아 old duplicate가 같은 주소와 포트로 재연결된 연결에 영향을 주지 않도록 충분히 길다는 의미이다. (MSL은 ip datagram이 회선에 존재할 수 있는 시간이기 때문에 2MSL이면 더이상 old duplicate가 존재하지 않는다. 어렵다..)


이 TIME_WAIT state의 규칙을 통해 TCP는 이전 연결에서 발생한 old duplicate들이 네트워크에서 만료되도록 하고, incarnation이 이런 old duplicate들에서 영향을 받지 않도록 보장할 수 있다.


추가로 이런 규칙에도 예외도 있다. Berkeley-derived 구현에선 TIME_WAIT state에 있어도 SYN이 ending sequence number보다 큰 숫자를 가지고 있으면 연결을 수립해준다. (당연히 ending sequence number 보다 크면 old duplicate들이 수신되도 만료된 것으로 처리할 수 있을 것이라 생각한다.)

다만 이러한 연결 수립은 SYN을 받는 쪽이 TIME_WAIT state가 돼야하므로, 서버가 active close를 수행하는 것을 요구한다.


8. SCTP Association Establishment and Termination

SCTP는 TCP처럼 connection-oriented이다. 따라서 SCTP도 association 설립과 종료를 위한 handshake를 가지고 있다.

그러나 SCTP의 handshake는 TCP와는 다르다.


1) Four-Way Handshake

다음의 시나리오는 SCTP association이 설립될 때 일어난다.

1> 서버는 도착하는 association을 받기 위해 준비하여야 한다. 이러한 준비는 보통 socket, bind, listen으로 이루어지며 passive open이라 불린다.

2> 클라이언트는 connect함수 호출을 하거나, 또는 암시적으로 association을 여는 메시지를 보냄으로써 active open을 하게 된다.

active open은 SCTP 클라이언트가 INIT 메시지를 서버에 보내도록 한다. INIT 메시지는 서버에게 IP주소 리스트, 초기 sequence number, association에서 모든 패킷들의 id가 되는 initiation tag, 클라이언트가 요청하는 출력스트림 개수, 그리고 클라이언트가 제공할 수 있는 입력스트림 개수를 알려준다.

3> 서버는 클라이언트의 INIT 메시지에 대한 알림으로 INIT-ACK메시지를 보내며, 이는 서버의 IP주소 리스트, initiation tag, 서버가 요청하는 출력 스트림 개수, 서버가 제공할 수 있는 입력스트림 개수, 그리고 state cookie를 담고 있다.

state cookie는 association이 유효하고 유효성이 보장될 수 있도록 디지털로 명시되었는지를 보장하는 모든 state를 담고 있다.

4> 클라이언트는 서버의 state cookie를 COOKIE-ECHO 메시지로 답장하며, 이 메시지는 state cookie와 같은 메시지 안에 유저 데이터를 담고 있을 수 있다.

5> 서버는 cookie가 유효하고 association이 설립되었다는 것을 COOKIE-ACK 메시지로 답장한다. 이 메시지는 state cookie에 유저데이터를 담은 것을 포함할 수 있다.


위의 시나리오와 같이 설립에 필요한 패킷을 최소 4개이며, 따라서 이러한 과정을 four-way handshake라고 부른다. 그림 3에서 이 네개의 segment를 확인할 수 있다.


[그림 3] SCTP four-way handshake


SCTP의 four-way handshake는 TCP three-way handshake와 비슷하지만 SCTP는 cookie generation이 필수적으로 존재한다.

INIT segment는 INIT의 많은 파라미터와 덧붙여 검증 태그(verification tag : Ta), 초기 sequence number(initial sequence number : J)를 담아서 보낸다.

태그 Ta는 association이 존재하는 동안 peer(server)가 전송하는 모든 packet에 존재해야한다. 초기 sequence number인 J는 DATA chunks라고 불리는 DATA message에 대한 시작 sequence number로 사용된다.

INIT segment를 수신한 peer(client)는 INIT-ACK를 알림으로 보내게 되는데, INIT-ACK segment는 검증 태그(verification tag : Tz), 초기 sequence number(initial sequence number : K)에 덧붙여 cookie C를 같이 보낸다.

peer(client)는 검증 태그 Tz를 association이 존재하는 동안 peer(client)가 보내는 모든 packet에 존재하도록 한다.

cookie는 server의 SCTP stack이 associating client에 대한 정보를 유지할 필요 없도록 하기 위해, SCTP association을 설립하기 위한 모든 state들을 담고 있어야한다.(association의 설립에 대한 더 자세한 사항은 챕터4에 나온다고 한다.)

four-way handshake에서 각 endpoint는 primary destination address를 가지고 있어야 한다. primary destination address는 default destination이 된다.

SCTP에서 four-way handshake는 서비스 거부 공격(denial-of-service)을 보호하기 위해 사용된다. (나중에 챕터4에 나온다.)


four-way handshake에서 사용되는 쿠키는 서비스 거부 공격의 방어 방법으로 공식화 되어있다. 구현된 대부분의 TCP에서는 비슷한 방법을 사용하는데 가장 큰 차이는 TCP는 cookie state를 sequence number에 오로지 32-bit만 가지고 인코딩 해놓는다는 점이다. SCTP는 임의적인 길이를 가지고 있고 암호화된 보안을 사용한다.


2) Association Termination

TCP와 다르게 SCTP는 half-closed를 허락하지 않는다. 한 endpoint가 assciation을 닫으면 다른 endpoint는 반드시 데이터 전송을 중단해야한다. shutdown 요청을 받은 endpoint는 queue에 만약 데이터가 있다면 전송하고 shutdown을 완료한다. (queue에 이미 존재하는 데이터만 보내고, application에서 send하는 것은 안된다.) 아래의 그림 4에 이러한 과정이 나와있다.

[그림 4 Packets exchanged when an SCTP association is closed]


TCP와 다르게 SCTP는 TIME_WAIT state가 없다. 모든 chunk는 INIT chunk에 딸려오는 태그를 명시하도록 돼있는데, 새로운 연결에서는 다른 tag값을 가지게 될 것이다. 따라서 old duplicate들이 와도 tag가 맞지 않으므로 처리되지 않는다. 

따라서 SCTP는 TIME_WAIT에서 연결을 유지하는 대신 verification tag를 가지고 있는 것이다.


3) SCTP State Transition Datagram

SCTP의 assocation 설립, 종료와 관련된 연산은 state transition diagram으로 명시될 수 있다.

아래 그림 5에서 확인할 수 있다.

[그림 5] SCTP state transition diagram

그림 5의 state는 현재 state나 수신받은 chunk에 따라 SCTP 규칙대로 이동한다. 예를 들어 CLOSED state에서 active open을 수행하면 SCTP는 INIT chunk를 전송하고 COOKIE-WAIT state가 된다. 그 다음에 SCTP가 INIT ACK를 수신하면 COOKIE ECHO를 전송하고 COOKIE-ECHOED state가 된다. 그 다음에 SCTP가 COOKIE ACK를 수신하면 ESTABLISHED state가 된다. ESTABLISHED state는 데이터 전송이 가장 많이 일어나는 state이다.

ESTABLISHED state로부터 나가는 두개의 화살표는 association 종료와 관련있다. 만약 application이 SHUTDOWN을 수신하기 전에 close를 한다면(active close) SHUTDOWN-PENDING state가 되고, ESTABLISHED state에서 SHUTDOWN을 수신하면(passive close) SHUTDOWN-RECEIVED state가 된다.


4) Watching the Packets

아래 그림 6은 SCTP에서 패킷 교환과 그에 따른 state변화를 보여를준다. association 설립과 데이터 전송, 종료가 나와 있다. 

[그림 6] Packet exchange for SCTP association


그림 6의 예제에서 클라이언트는 첫 번째 데이터 chunk를 COOKIE ECHO에 실어서 보내고 있고 서버도 데이터를 포함한 COOKIE ACK로 응답한다. 일반적으로 COOKIE ECHO는 DATA chunk를 하나이상 가지고 있을 수 있는데 application이 일대다 인터페이스 스타일을 사용할 때 그렇다. (챕터 9에서 일대일, 일대다 인터페이스 스타일에 대해 다룬다.)

이제 와서 설명하는거지만 chunk는 SCTP 패킷 안에 존재하는 단위를 말한다. chunk는 그 자체로 자기 자신을 서술할 수 있는데, chunk type, flags, length를 담고 있어서 그렇다. 이러한 특징은 chunk 여러개를 하나의 SCTP 패킷에 간단히 묶을 수 있게 도와준다. (chunk여러개를 묶는 것을 chunk bundling이라고 하는데 chunk bundling과 이 데이터 전송 과정은 [Stewart and Xie 2001]의 챕터 5에 나온다고 한다.... 이걸 읽을 일이 있을까?)


5) SCTP Options (이해하기 어려워 해석만 해놓음)

SCTP는 optional feature를 이용가능하기 위해 parameter와 chunk를 사용한다. 새로운 feature들은 parameter와 chunk에 feature를 추가함으로써, 그리고 일반적인 SCTP 처리과정이 unknown parameter와 unknown chunk를 처리할 수 있도록 함으로써 정의된다. parameter space와 chunk space의 상위 2비트는 SCTP receiver가 이런 unknown parameter와 unknown chunk를 수신했을 때 무엇을 해야하는지를 정의한다. (더 자세한 건 [Stewart and Xie 2001]의 Section 3.1에 나와있다.....)

현재 SCTP를 위한 두개의 확장이 개발됐다.

1> dynamic address extension: SCTP endpoint가 association으로부터 동적으로 IP 주소를 추가하거나 제거할 수 있도록 해준다.

2> partial reliability extension: SCTP endpoint가 application의 지시에 따라 데이터 재전송을 제한하도록 한다. application이 판단하에 데이터가 전송하기에 너무 오래됐으면 재전송을 하지 않는다. 이는 모든 데이터가 association의 다른 endpoint에 전송된다는 확신을 없애는 것이다.


9. Port Numbers

앞에서 다룬 UDP, TCP, SCTP는 모두 다중 처리가 가능하다. 이 세개의 transport layer들은 프로세스들을 구분하기 위해 16-bit 정수 port를 사용한다.

클라이언트가 서버에 접속하고 싶을 때, 클라이언트는 어떤 port로 연결하고 싶은지 반드시 명시해야한다. TCP, UDP, SCTP는 well-known service들에 대한 well-known port를 정의하였다. 예를 들어 FTP를 지원하는 모든 TCP/IP 구현은 FTP 서버에 well-known port로 21을 부여한다. UDP에서는 Trivial File Transfer Protocol(TFTP) 서버에 69를 부여한다.

반면에 클라이언트들은 ephemeral port를 사용하는데, 이는 수명이 짧은 port를 뜻한다. 이러한 port는 보통 transport protocol에 의해 자동으로 클라이언트에 부여된다. 클라이언트들은 보통 ephemeral 포트를 신경 안써도 된다. 단지 client host에 고유 port로 존재하면 된다. transport protocol은 이런 고유성을 보장해준다.

Internet Assigned Numbers Authority(IANA)에 port number의 리스트가 존재한다. 이 부여된 port number들은 RFC로 발행되었는데, RFC 1700[Reynolds and Postel 1994]이 마지막으로 발행되었다. RFC3232[Reynolds 2002]는 RFC1700의 대안으로 port 리스트의 온라인 데이터베이스 위치를 제공해준다.(http://www.iana.org/)


port number는 3개의 범위로 구분된다.

1> well-known ports: 0~1023. 이 port number는 IANA에 의해 부여된다. 가능하다면 UDP, TCP, SCTP의 같은 service에 대해 같은 port number를 부여한다. 예를 들어 port 80은 TCP, UDP 모두 Web service를 위해 할당되어있다. (비록 Web service의 모든 구현은 TCP만 사용하지만..)

2> registered ports: 1024~49151. IANA에 의해 관리되지 않지만 IANA는 편의를 위해 이 port의 사용을 커뮤니티에 등록하고 리스트화한다. 가능하면 같은 서비스에 대한 같은 포트를 TCP와 UDP에 부여한다. 예를 들면, port 6000~6063은 X Window server를 위해 TCP, UDP에 부여된다. 최고 높은 port인 49151는 ephemeral port의 범위를 허용하기 위해 사용된다. RFC 1700 [Reynolds and Postel 1994]는 ephemeral port의 범위 65535까지 리스트화 해놓았다.

3> dynamic ports 또는 private ports: 49152~65535. IANA에 의해 알려진게 없다. 이 port들은 ephemeral ports라 불린다.  (49152는 65536의 4분의 3이다.)


아래 그림 7은 위의 3개 범위를 보여준다.

[그림 7] Allocation of port numbers


위 그림 7로부터 아래와 같은 특징을 알 수 있다.

1> 유닉스 시스템은 0~1023의 reserved port라는 개념을 가지고 있다. 이 port들은 적당한 권한을 가진 프로세스에 의해 소켓에 부여된다. 모든 IANA well-known port들은 reserved port이다. 그래서 이 port가 할당된 서버(FTP 서버 등)는 시작 시 반드시 superuser권한을 가지고 있어야 한다.

2> 역사적으로  Berkeley-derived implementation(4.3BSD부터)들은 1024~5000까지 ephemeral port로 할당해왔다. 1980년대 까지는 이런 방법이 괜찮았지만 요즘은 호스트가 이 포트들을 3977개 이상을 제공해주는걸 쉽게 볼 수 있다. 그래서 새로나온 시스템들은 더 많은 port들을 제공해주기 위해 ephemeral port들을 기존과 다르게 부여한다. IANA에서 정의된 ephemeral port 범위를 사용하거나 그림 7 아래의 Solaris처럼 더 큰 범위를 사용한다.

3> 몇몇 클라이언트들은 reserved port를 클라이언트/서버 증명(authentication) 파트로 요청한다.(예를들면 rlogin, rsh 클라이언트) 이러한 클라이언트들은 rresvport라는 함수를 이용해 TCP소켓을 만들고 그 소켓에 513~1023 중 이용되지 않은 port를 부여한다. rresvport 함수는 처음에 port 1023을 부여하는 것을 시도하고 만약 실패하면 1022, 또 실패하면 1021, 이런방식으로 최고 높은 포트부터 하나씩 감소하면서 513까지 시도한다.


1) Socket Pair

TCP 연결에서 socket pair는 4개의 tuple(four-tuple)이다. 이 4개의 tuple은 2개의 endpoint를 의미한다.(local ip, port / foreign ip, port) socket pair는 모든 TCP 연결마다 고유의 식별(identity)을 갖게 한다. SCTP에서는 local ip 주소 집합, local port, 그리고 foreign ip 주소 집합, foreign port를 이용해 association을 식별한다. SCTP의 association이 가장 간단한 형태로 association의 두 endpoint 모두 multihome이 안돼있으면 TCP처럼 four-tuple socket pair를 갖게 된다. 그러나 endpoint 둘 중 하나라도 multihome이 돼있으면, 다수의 four-tuple(port는 같지만 ip주소가 다른)이 하나의 association을 식별한다.


10. TCP Port Numbers and Concurrent Servers

서버가 루프를 돌며 새 연결마다 child를 생성하는 concurrent server일 때, 만약 child가 긴 시간동안 well-known port로 서비스를 요청하면 어떻게 될까?
서버가 호스트 freebsd에서 multihome된 ip주소 12.106.32.254, 192.168.42.1로 시작하고, 서버가 well-known port 21로 passive open을 했다고 가정하자. 이제 서버는 아래 그림 8처럼 클라이언트를 기다린다.

[그림 8] TCP server with a passive open on port 21


그림 8에서 { *:21, *:* } 표기법은 서버의 socket pair를 나타내기 위해 사용한다. 서버는 port21에서 local interface에 대한 connection request를 기다린다. foreign IP 주소와 foreign port는 명시되지 않고 이것을 *.*로 나타낸다.

이것을 listening socket이라고 부른다.

IP 주소를 별표로 표시하는 것을 wildcard character라고 한다. 서버가 작동하고 있는 호스트가 multihome이면, 서버는 새로운 연결을 지정한 local interface에 도달하도록 명시할 수 있다. 서버에서는 이것을 one-or-any choice라고 한다.

서버는 다수의 주소를 명시할 수 없다. local address를 wildcard로 표시하는 것은 'any' 선택이다. wildcard address는 socket address를 세팅할 때 IP주소를 INADDR_ANY로 명시함으로써 설정된다.

그 후 IP주소 206.168.112.219인 클라이언트가 IP 주소 12.106.32.254인 서버로 active open을 하고, ephemeral port는 1500이라고 가정해보자. 아래 그림 9에 이러한 socket pair가 나타나있다.

[그림 9] Connection request from client to server


서버가 클라이언트의 연결 요청(connection request)을 수락(accept)하면 서버는 자기 자신의 복사본을 fork하게 되고, 복사된 child는 아래 그림 10처럼 client를 다루게 된다.(fork 함수는 챕터4의 section 7에서 다룬다.)

[그림 10] Connection request from client to server


이 때 서버 호스트에 존재하는 listening socket과 connected socket은 꼭 구분해야한다. connected socket는 listening socket과 같은 local port(21)를 쓰는 점을 알아두자. 또한 multihome 서버에서 연결이 수립되면 connected socket(12.106.32.254)의 local address가 채워지는 점을 알아두자.

클라이언트 호스트에 존재하는 또다른 클라이언트 프로세스가 같은 서버에 연결을 요청한다고 가정해보자. 클라이언트 호스트의 TCP 코드는 새로 만들어진 client socket에 사용되지 않은 ephemeral port를 부여할 것이다. 이 소켓에는 1501이 부여됐다고 가정하자. 이 시나리오는 아래 그림 11과 같이 나타낼 수 있다.

[그림 11] Second client connection with same server


위에서 설명한대로 클라이언트의 TCP는 이 새로만들어진 두번째 연결에 사용되지 않은 ephemeral port(1501)를 부여했기때문에, 첫번째 연결의 socket pair와 두번째 연결의 socket pair는 서로 다르고 이 말은 두 연결이 구분됨을 의미한다.

이 예제에서 TCP는 단순히 목적지 port만 가지고 도착하는 segment들을 구분(demultiplex)할 수 없다는 점을 알아두자. TCP는 반드시 socket pair의 4가지 요소를 모두 이용해야 어떤 endpoint가 도착하는 segment의 주인인지를 구분할 수 있다. 위 그림 11에서 세개의 소켓이 같은 local port(21)을 가지고 있음을 알 수 있다. 만약 IP 206.168.112.219 / port 1500으로부터 IP 12.106.32.254 / port 21로 segment가 전달되면 첫번째 child로 전달될 것이다. IP 206.168.112.219 / port 1501으로부터 IP 12.106.32.254 / port 21로 segment가 전달되면 두번째 child로 전달될 것이다. IP 12.106.32.254 / port 21로 오는 나머지 것들은 전부 서버의 listening socket으로 전달된다.

정리하자면 local ip / port로 전달되는 segment들은 foreign ip / port로 구분돼서 소켓에 전달된다.

'프로그래밍 > Network' 카테고리의 다른 글

Socket Introduction (2)  (0) 2017.05.22
Socket Introduction (1)  (0) 2017.04.05
Transport Layer (3)  (0) 2017.03.13
Transport Layer (2)  (0) 2017.02.24
Transport Layer (1)  (0) 2017.02.21
TCP UDP 통신  (0) 2017.01.17
댓글
댓글쓰기 폼