네트워크 - 3. Transport Layer (2)

cyw320712·2022년 4월 14일
1

3. Transport Layer


이번 챕터의 목표를 다시 확인하자

  • transport layer services의 원리 이해:
    • multiplexing, demultiplexing
    • reliable data transfer
    • flow control
    • congestion control
  • internet transport layer protocol 이해
    • UDP: connectionless transport
    • TCP: connection-oriented reliable transport
    • TCP congestion control



Principles of reliable data transfer


  • reliable한 data transfer는 networking에서 가장 중요한 개념이다.
    • 하위 계층에 상위 게층으로 데이터를 전송할 때, 전송된 데이터가 손상/손실 되지 않게 보장해줘야 함
    • 아래 layer가 쌓일수록 아래층의 layer는 unreliable할 확률이 높아진다.

      위 그림은 사용자 관점에서의 communication이다. 사용자 관점에서 보면 reliable한 것처럼 보인다. 하지만 실제로는

      이렇게 unreliabel channel을 통해서 reliable한 통신이 수행된다.
  • 이때 reliable data transfer protocol의 복잡성은 unreliable channel의 특성에 의존한다.
    • loss, corrupt, out-of-order 등
  • 또한 Sender나 receiver는 message를 통해 전달되지 않는 한 상대방의 "state"를 알 수가 없다.
    • message가 잘 도착 했는지? loss인지, 받을 준비가 되어있는지..
  • 신뢰성을 보장하기 위해서는 정상적으로 packet이 도착했음을 확인하고, 후에 다음으로 보낼 packet을 결정해야 한다.
  • Transport layer에서는 이를 해결하기 위해 RDT protocol을 사용한다.

Reliable data transfer protocol (rdt)

Why rdt?

  • unreliable channel에서 발생할 수 있는 다음 두 가지 문제를 해결하기 위해서 사용한다
    • bit error: packet에 문제가 생겨 기존의 bit와 달라짐
    • loss: packet이 중간에 유실됨

How rdt? (interfaces)

  • rdt_send(): 상위 layer에서 호출된다. receiver의 상위 layer로 전송될 data를 전달한다.
  • udt_send(): unreliable channel을 통해 receiver에게 packet을 전송하기 위해서 rdt 에 의해서 호출된다.
  • rdt_rcv(): packet이 channel의 receiver side에 도착할 때 호출된다
  • deliver_data(): 상위 layer에 data를 전달하기 위해서 rdt에 의해서 호출된다.
  • 이 모든 통신은 unreliable channel을 통한 양방향 통신이다.

What rdt?

  • rdt protocol의 sender/receiver side를 점진적으로 설명한다.
  • 단방향 data 전송만 고려한다
    • control 정보는 양방향으로 flow
  • Finite state machine을 이용해서 sender, receiver를 설명한다.

rdt1.0: reliable data transfer over a reliable channel

  • 하위 channel이 완벽하게 reliable하다고 가정 (no bit error, no packet loss)
  • sender와 receiver의 FSM을 각각 그려보자
    • sender는 underlying channel에 데이터를 보낸다.
    • receiver는 underlying channel에서 데이터를 받는다.

rdt2.0: bit error는 있을 수 있는 channel

  • underlying channel에서 packet 내부의 bit들이 flip될 수 있다.
  • receiver는 이를 어떻게 해결할까? (retransmit)
    • ACKs(Acknowledgements): receiver가 sender에게 packet을 잘 받았다고 말해줌
    • NAKs(Negative ACKs): receiver가 sender에게 packet에 에러가 있다고 말해줌
      • sender가 NAK을 받으면 packet을 다시 보낸다.
  • Stop and wait: sender는 packet 하나 보내고, receiver의 response를 기다린다!
  • 위의 람다는 아무것도 안한다는 뜻을 의미하며, corrupt는 방해됐음을 의미
  • 그러나 이는 ACK과 NAK packet의 loss는 고려하지 않았다.
    • 만약 이게 corrupted되면 sender는 receiver의 상황을 알 수가 없다.
    • 단순히 재전송해도 안되는 게, packet이 복사될 수 있다!
  • 즉, sender는 각 packet에 sequence number를 추가하고, 이를 보고 receiver가 pakcet을 discard할지 위 layer에 전달할지 결정한다.
  • 위에서 중요한 개념은 Sender에서 receiver에게 어떻게든 전달되지 않는 한, sender는 receiver의 상태를 알 수 없다는 것이다.
  • 이 때문에 우리는 protocol이 필요하다!
    • Network remote에 있는 누군가와 통신하는데, 상대방이 어떤 state인지 알기 위해서는 상대방이 보낸 message를 보고 알 수 있는 것.

rdt 2.1: sender, handling garbled ACK/NAKs

  • rdt 2.0에 sequence number를 추가했다.
  • ACK과 NAK에는 sequence number가 들어갈 필요가 없는데, Stop and Wait방식을 사용하기 때문.

Sender FSM

  • Sender Flow
    • Sender는 0번 packet을 보내고 ACK, NAK 0을 기다린다.
    • NAK 0가 왔다면 0번 packet을 retransmit한다.
    • ACK 0가 왔다면 다음 packet인 1을 전송할 수 있는 상태로 기다린다.
    • 1번 packet을 보내고, ACK, NAK 1을 기다린다.
    • ACK, NAK 1에 대한 reaction은 0번과 같다.
    • 모든 패킷을 보낼때 까지 위 과정을 반복한다.
  • sequence number는 0과 1이면 충분하다!
    • Stop-and-wait 방식을 선택하기 때문에, ACK이나 NAK이 오기 전까진 packet을 보내지 않는다. (In-flight packet은 1개)
    • Inflight Packet: Sender에서 보내졌지만 receiver가 받기 전인 packet
    • packet이 중복인지 아닌지 판단하는 데는 0과 1이면 충분한 것.
  • ACK/NAK의 corrupt 여부를 확인해야한다.
  • sequence number 0, 1 packet 중 무엇을 기다릴지 기억해야하기 때문에 두 배의 state 필요

Receiver FSM

  • Receiver Flow
    • 0번 packet에 오류가 있으면 NAK 0를 보내고 state는 그대로
    • 0번 packet에 오류가 없으면 ACK 0를 보내고 1번 packet wait로 상태 변경
    • 1번 packet에 대한 reaction은 0번과 같다.
  • 받은 packet이 중복인지 확인해야 한다.
    • state는 기다리는 pakcet이 0번인지 1번인지를 나타냄
  • 기억해라: receiver는 ACK/NAK이 송신자에게 제대로 갔는지 여부를 알 수 없다!

rdt2.2: A NAK-free protocol

  • 2.1과 같은 기능을 갖지만, NAK 대신 receiver는 마지막으로 받은 packet에 대한 ACK을 보낸다.
    • receiver는 ACK된 pakcet의 sequence number를 반드시 명시적으로 포함해야 한다.
  • Sender의 duplicated ACK은 결과적으로 NAK과 같은 역할을 한다. (지금 packet을 재전송해라..)
  • 앞으로 확인하겠지만, TCP는 NAK-free를 위해 이와 같은 방식을 사용한다.
  • Example flow
    • 0번 packet을 보내고 제대로 송신되어서 ACK 0을 받음
    • 1번 packet을 보냈는데, receiver는 packet의 오류 발견, ACK 1이 아닌 ACK 0 재전송
    • Sender는 (기대 중인)ACK 1이 아닌 ACK 0을 받았으므로, 1번 packet 재전송

rdt3.0: error와 loss가 모두 있을 수 있는 channel

  • rdt2.0/2.1/2.2는 packet eror에는 대응할 수 있으나, ACK/NAK 및 Packet의 loss에 대해서는 대응할 수 없음 (애초에 packet loss가 없다고 상정하고 만들었으니까..)
  • Sender는 reasonable한 시간동안만 ACK을 기다린다.
    • 이 시간동안 ACK을 못 받으면 packet 다시보냄
    • 만약 packet이 그냥 지연되고 있던거라면..
      • retransmission은 duplicate되지만, 이는 sequence number로 handle할 수 있다
      • receiver는 ACK될 packet의 sequence number를 지정해야만 한다.
    • Timer가 너무 짧으면..
      • 빠르게 loss에 대한 recover가 가능하다
      • 그러나 네트워크에 중복된 packet이 너무 많이 전송된다. (오버헤드 증가)
    • Timer가 너무 길면..
      • 데이터 전송에 충분한 시간을 주기 때문에 중복된 패킷이 전송될 일이 적어짐
      • 그러나 loss에 대한 반응이 느려짐...

  • 정상적인 상황만 고려했을 때 Sender의 FSM
    • duplicated ACK을 고려하지 않음.
    • timeout 발생시 어떻게 할지 명시되지 않음

모든 상황을 고려한 Sender의 FSM

  • duplicated ACK은 그냥 ignore -> 가만히 두면 Timeout에 의해서 처리됨
  • Timeout시 packet을 다시 보내고 timer는 다시 켠다.
  • above에서 1을 call하길 기다리는 상황에서는 receiver에서 오는 packet들을 받을 이유가 없기 때문에 (아마 garbage값) 그냥 ignore

모든 상황에 대한 flow

  • rdt3.0의 가장 큰 문제점: ACK신호가 지연된다면, (d) 상황처럼 전송이 꼬이는 상황이 발생한다.
    • 또한 데이터 하나를 보내고 ACK이 올 때까지 데이터를 보내지 않기 때문에 성능이 안좋음

rdt3.0: Pipelined protocol operation

  • rdt3.0의 성능
    • U(sender): utilization - sender가 보내는데 필요한 시간의 비율
    • example: 1Gbps link, 15ms prop delay일때 8000bit짜리 packet에 대해
      • channel로 packet이 전송되는 시간 D = L/R = 8000bits / 1Gbps = 8msec
    • packet이 전송되는 시간의 비율은 단 0.00027 == 미친듯한 비효율
  • 이를 극복하기 위해서 Pipelined protocol operation 추가
  • Pipelining: sender는 아직 acknowledge되지 않은 여러 in-flight packet을 보낼 수 있음
    • sequence number들의 범위는 증가해야만 한다
    • sender와 receiver는 buffer를 가지고 있어야 한다.
  • 대표적인 두 가지 protocol에 Go-Back-NSelective Repeat가 있다.

Go-Back-N

Sender

  • Sender는 한 번에 전송할 Packet의 개수(window size)를 정한다.
  • buffer에 그 개수만큼 packet을 저장해서 전송
  • buffer를 Window, buffer size를 Window size라고 한다.
  • buffer의 시작점인 send_base와 다음에 보낼 packet의 번호인 next_seq_num을 갖는다.
  • Cumulative ACK(n): sequence number n을 포함하는 지금까지의 모든 packet에 대한 ACK
    • ACK(n)을 받으면 window를 n+1까지 이동한다.
  • 가장 옛날 in-flight packet이 timer의 기준
  • timeout(n): window에 있는 모든 sequence number n이상의 packet들 재전송

Receiver

  • ACK-only: 해당 시점까지 제대로 받은 packet 중에서, 가장 높은 sequence number로 ACK을 보냄.
    • duplicate ACK 발생 가능
    • rcv_base만 기억하면 된다.
  • out-of-order packet에 대해서는..
    • 버리거나, buffer에 저장해두거나 구현에 따라서 달라짐.
    • buffering할 수도 있고 안 할 수도 있음.

Action flow

  • 아래 Timeout 발생시 window에 있는 모든 packet을 다시 보내느 모습을 확인할 수 있음.
    • buffering하는 경우 3, 4, 5를 처리해줘야됨..

Selective repeat

  • Go-Back-N의 비효율성을 해결하기 위한 방식
  • receiver는 제대로 받은 모든 packet들에 대해서 개별적으로 ACK를 날림 (not cumulative)
    • 상위 layer로의 최종적 in-order delivery를 위해 필요에 따라 packet을 buffering한다.
  • Sender는 unACKed packet에 대해서 각각 timeout/retransmit 한다.
    • sedner는 각 unACKed packet당 timer를 유지해줘야 한다.
    • 그러나 timer는 OS에서 아주 귀중한 resource임...
  • Sender window
    • 연속인 sequence number들 (~N)
    • 전송된, unACKed packet의 sequence number를 제한한다.

  • ACK이 loss되어 timeout되는 경우 sender는 해당 packet만 retransmit
  • receiver에게 잘 도달하면, 연속된 sequence number 중 가장 높은 수로 window 이동

Sender

  • 만약 window에서 다음으로 가능한 sequence number라면 packet을 보낸다.
  • timeout(n): packet n만 다시 보내고, timer restart
  • [sendbase, sendbase+N]안의 ACK(n)이 오면, packet n이 받아졌음을 마킹한다.
    • 만약 n이 가장 작은 unACKed packet이라면, window base를 다음 unACKed sequence number로 이동시킨다.
    • 즉, sendbase는 UnACKed 된 packet 중 가장 작은 값.

Recevier

  • Packet n이 [rcvbase, rcvbase+N-1]안에 있다면...
    • ACK(n)을 보낸다.
    • 순서가 바꼇다면, buffer에 저장
    • 제 순서인게 왔다면, 위로 올려주고(deliver buffered), window를 다음 not-yet-received packet으로 옮겨준다.
  • [rcvbase-N, rcvbase-1]안의 packet이 왔다면 ACK이 loss됐음을 의미하기 때문에 ACK(n)을 다시 보내줌.
  • 다른 경우는 무시 (ACK packet이 loss된 경우. packet은 ignore하고 reACK(n))
  • Go-Back-N보다 효율적으로 보이지만, 모든 unACKed packet에 timer가 있어야하기 때문에 overhead가 더 크다.

Action flow

  • ack2가 도착하면 sender window가 한 번에 6까지 옮겨진다. (send_base가 6)
  • Sender측의 sequence number가 0 1 2 3 0 1 2로 반복되어도, 0과 그 다음 0은 다른 0으로 취급할 수 있다.
    • 그러나 만약 receiver에서 sequence number가 반복된다면? 0, 1, 2에 대한 ACK이 loss되어서 sender는 첫 번째 0 packet을 보내도, receiver 측에선 그걸 두 번째 0으로 생각할 수 있다!
    • 즉, 새로운 0에 해당하는 packet이 들어왔다고 생각하는 것.

  • 이를 막기 위해선 sequence number / 2 >= window size 여야만 한다.



Connection-oriented transport: TCP - segment structure, reliable data transfer


TCP overview

  • point to point: one sender, one recevier. (1대1 통신)
  • reliable, in-order byte stream
    • byte만 처리하며, message는 application layer가 처리
    • message boundary들이 없음
  • full duplex(bi-directional) dta:
    • 같은 connection에서는 양방향 data flow
    • MSS: Maximum segment size가 존재
      • 보통 1460 byte
        == MTU(Maximum Transfer Unit) 1500byte - Header 40byte (TCP 20, IP 20)
  • cumulative ACKs
  • pipelining: TCP congestion과 flow control은 window size를 설정한다
    • congestion window size와 flow control window size가 1개씩 존재
  • Connection-oriented
    • data 교환 전에 sender와 receiver는 handshaking으로 connection을 초기화한다.
  • Flow controlled
    • sender는 receiver를 압도하는 양을 보내지 않는다.

TCP segment

  • port 확인은 TCP/UDP가 동일하다
  • Sequence number: packet에 담긴 byte의 bytestream에서의 시작 위치 (#번째 segment를 가리키는 것이 아니다!)
  • RST, SYN, FIN: connection management용
    • 각각 reset(close channel), syncronize, finish를 의미
  • Acknowledgements: 다음으로 받고싶은 (기대중인) byte의 시작 위치
    • cumulative ACK임
  • Q. receiver는 out-of-order packet을 어떻게 handle?
    A. TCP spec은 그런거 모름. 구현자에 따라 달림

piggy backing

  • 데이터 'C'가 1byte라서 Seq #와 ACK #이 1씩 오르는 모습을 확인할 수 있다.
  • 그건 그렇고 ACK packet에 data가 같이 들어있는 모습도 볼 수 있는데
    • 이를 piggy backing이라고 한다.
  • receiver가 data를 보내야하는 상황에서 발생하며, TCP segment에 ACK flag field 덕분에 이것이 가능한 것!
    • ACK flag가 set이면 그건 ACK packet으로 여겨지며, application data field에는 ACK과 무관하게 데이터를 담을 수 있다.

TCP round trip time, timeout

Q. TCP timeout 값은 어떻게 설정할까?

  • RTT보단 길어야되는데, RTT가 너무 다양하다!
  • Too Short: 일찍 timeout되어 불필요한 재전송이 많아짐
  • Too Long: segment loss에 대한 reaction이 느려짐

Q. RTT는 어떻게 계산할까?

SampleRTT

  • segment 전송부터 ACK 수신까지의 시간을 측정
  • retransmittion은 무시해야됨. ( retransmit하는 동안 ACK이 도착할 수 있으니까.. )
  • SampleRTT는 다양할 것이기 때문에 RTT를 좀 더 부드럽게 계산하고자 함
    • 단지 지금의 SampleRTT가 아니라 몇몇 최근의 측정값들을 평균내서...

EstimatedRTT

  • EstimatedRTT = ( 1 - a ) * previous_EstimatedRTT + a * current_SampleRTT
  • exponential weighted moving average(간단히 이동평균)을 써서 계산
  • 최근 값이 영향을 더 많이주고, 과거의 값은 점점 영향력이 떨어진다는 점이 포인트
  • 보통 a값은 0.125정도를 준다.

TimeoutInterval

  • EstimatedRTT에 safety margin을 더한 값.
  • TimeoutInterval = EstimatedRTT + 4 * DevRTT
  • DevRTT = (1-b) * DevRTT + b * |SampleRTT - EsitmatedRTT| (b는 보통 0.25)
    • DevRTT는 EstimatedRTT에서 파생된 SampleRTT의 이동평균임

TCP scenario

TCP Sender

  • Event: application으로부터 data가 도착함
    • sequence number와 함께 segment를 만듦
    • sequence number는 segment의 첫 번째 bte의 byte-stream number임
    • 이미 작동 중인게 아니라면, timer를 시작
      • oldest unACKed segment에 대해서 실행중인 timer를 생각해서..
      • expiration interval은 TimeoutInterval
  • Event: timeout
    • timeout을 일으킨 segment 재전송 후 timer 재시작
  • Event: ACK 도착
    • 만약 이전까지 unACKed segment인 ACK이 도착한거라면 ACKed로 업데이트
    • 여전히 unACKed segment가 있다면 timer 시작

TCP Receiver

  • Event: In-order segment가 도착
    • cumulative ACK을 보냄. (이전까진 모두 ACK을 보낸것)
  • Event: In-order segment가 왔는데, pending중인 아직 ACK이 안된 segment가 있음
    • in-order segment 각각에 대해 single cumulative ACK을 전송
  • Event: Out-of-order segment가 도착
    • 이전의 duplicated ACK을 보냄
  • Event: 부분이나, 갭 전체를 채워주는 segment 도착
    • ACK을 보내고, segment start를 갭의 끝에서 가장 낮은 번호로 보냄.
  • 중요한 것은 Packet이 오면 바로 ACK하는게 아니라, 좀 기다렸다가 마지막꺼에만 ACK을 보낸다. (cumulative ACK)
  • 실제로 2개씩 packet을 받아서 ACK한다고 함..



Connection-oriented transport: TCP - flow control, connection management


TCP flow control

  • Q. 만약 network layer에서 전달되는 data의 속도가 application layer에서 socket buffer를 비우는 속도보다 빠르면 어떻게 되냐?
    A. Buffer overflow (Loss)
  • Socker buffer에서 overflow로 인해 drop되면 cost가 너무 높아짐..
  • TCP segment header에서 flow control field가 여기에 사용됨

Flow Control

  • receiver는 sender를 control한다. 그래서 sender는 너무 많이 보내서 receiver의 buffer가 overflow되지 않도록 너무 많이, 너무 빨리 보내지는 않는다.
  • TCP receiver는 TCP header의 rwnd field 안의 free buffer space 값을 전달한다.
    • RcvBuffer는 socket option을 통해서 사이즈를 설정할 수 있다. (default는 4096)
    • 많은 OS는 RcvBuffer를 자동으로 조절한다.
  • sender는 rwnd 받아 unACKed in-flight data의 총량을 제한한다. (window size 조절로써 제한)
  • receive buffer가 overflow되지 않음을 보장

TCP connection management

  • data를 교환하기 전에, sender/receiver는 handshake를 한다.
    • connection 설립을 합의하고 (둘 다 connection하고 싶은지)
    • connection parameter들의 합의함 (starting sequence number 등)

Why not 2-way handshake?

  • delay가 다양하며, message loss로 인한 message 재전송, message reordering의 문제가 있다.
  • 결정적으로 상대방의 state를 못 보는 상황에서, 2-way로는 정보가 너무 부족하다.
  • half open connection problem이나 dup data accept problem 가능..

TCP 3-way handshake

  • 처음 client는 TCP SYN meg를 보낸다. (TCP segment header에 있던 SYN flag set
    • client는 SYNSENT 상태 (NOT ESTAB)
  • server는 SYN packet을 받으면 SYNACK을 날린다.
    • server는 SYN RCVD상태 (NOT ESTAB)
  • client는 SYNACK에 대한 ACK 패킷을 날린다.
    • 이때 client에는 connection ESTAB
    • 해당 packet에는 data 포함 가능 (piggy backing)
      • 보통은 첫 연결에 client가 데이터 포함할 일이 없음...
  • server가 ACK을 받으면 server 또한 connection ESTAB!

Closing TCP connection

  • client와 server는 connection의 자신 쪽을 각자 닫는다.
    • FIN bit가 1로 set된 TCP segment를 날림
  • 도착한 FIN에 ACK으로 응답함
    • FIN을 받았을 때, 그에 대한 응답에 FIN과 ACK을 합쳐서 날릴 수 있음
  • 동시에 FIN을 교환하는 상황도 handle 가능!



Transport layer Discussion Problem


Estimating RTT

Q. RTT 계산 시, retransmitted segment를 고려하면 안되는 이유?

A. 아까도 말했지만, TCP가 RTT를 계산할 떄 retransmitted segment는 고려하면 안된다. 실제로, 한 번 전송된 segment에 대해서만 SampleRTT를 측정한다. 단순히 delay 떄문에 ACK이 늦어지는 동안 Retransmit을 보낼 수 있기 때문이다..

SYN flood attack

서버 TCP는 클라이언트 TCP로부터 SYN 세그먼트를 수신하면 SYNACK 세그먼트를 전송하고 SYN_RECV 상태로 이동하여 ACK 세그먼트를 기다린다. (half-open connection) 수신된 SYN에 응답하여 연결 변수와 버퍼를 할당하고 초기화하므로 half-open connection의 수를 늘려 서버가 다른 SYN에 응답할 수 없게 된다. denial-of-server attack 중 하나인 "SYN flooad attack"이 이 특성을 이용한다.

Q. 이 공격을 어떻게 방어할 수 있을까요?

A. Cookie를 보고 Server는 Client가 공격자가 아님을 판단한다. 그 후에 메모리를 할당하도록 설계하면 된다.

0개의 댓글