Silly Window Syndrome
수신 측에서 발생하는 신드롬과 송신 측에서 발생하는 신드롬이 각각있는데,
먼저 송신 측에서 발생하는 신드롬에 대해서 알아보자.
silly window syndrome에 대해서 먼저 알아보면,
어플리케이션에서 buffer로 데이터를 보내고, 이 buffer의 데이터를 전송하기 위해서 TCP header 20byte, IP header 20byte 또 MAC header도 달아야 한다. 만약 데이터가 1byte크기만 전송한다고 하면, 데이터 1byte를 전송하기 위해 40byte의 TCP/IP 헤더를 사용하게 되는 것이다. 상당히 비효율적이다.
이렇게 보내는 데이터의 헤더의 크기에 비해 너무 작아서 비효율적인 현상을 silly window syndrome이라고 한다.
Nagle 알고리즘
Nagle이 이러한 현상을 놓고 새로운 방법을 제안했다. 이것이 nagle 알고리즘이다.
보낼 데이터가 MSS(maximum segment size)로 정의된 크기만큼 쌓이면, 상대편에게 무조건 보내고,
보낼 데이터가 MSS보다 작을 경우, 이전에 보낸 데이터에 대한 ACK이 오기를 기다리고, ACK이 도달하면 보낼 데이터가 MSS보다 작더라도 상대에게 보낸다. (맨 처음 데이터는 그냥 보냄)
즉 데이터를 보내고 ACK이 오기 기다리는 동안 데이터를 대기시켜놓고, ACK이 도착했는데도 MSS만큼 모이지 않았다면 그대로 데이터를 전송한다.
수신 측에서도 이러한 신드롬이 발생한다.
receiveing buffer가 촥찬 상태에서 read로 1byte만 어플리케이션에서 읽었다고 하자. rwnd는 1이고, ACK이 갈 때 rwnd가 1로 전달되므로 상대방은 1byte의 데이터를 보내기 위해 40byte TCP/IP header를 포함해서 보낼 것이다. 이러한 비효율적인 상황은 수신 측에서 어플리케이션에 너무 적은 양을 보내서 생기는 일이다.
해결방법은 Clark 해결방법과 확인응답의 지연 두 가지이다.
Clark Solution
수신 측에서 buffer의 빈 공간이 MSS(maximum segment size)만큼 되거나, buffer의 절반 공간만큼은 되어야 ACK을 전송하고, 그렇지 않은 경우 window size로 0을 보내는 방법이다.
sender가 rwnd로 0을 받으면 window size가 0이므로 데이터를 보내지 않는다. 그러는 동안 receiver에서는 buffer의 빈 공간을 계속해서 축적한다. 축적한 양이 일정한 만큼 커지면 ACK에 rwnd를 빈 공간만큼 담아 보내서 sender로부터 데이터를 받기 시작한다.
확인 응답의 지연
buffer가 빈 공간이 적고 나머지가 차있는 상황이라고 가정하자. 이러한 상황에서 ACK을 아예 보내지 않는 방법이 확인 응답의 지연이다. ACK 자체의 전송을 지연하는 방법인 것이다.
buffer에 일정 크기 이상의 빈 공간이 생겼을 때 ACK을 보내고, 그 전까지는 ACK을 보내지 않으므로 sender쪽 sending buffer의 데이터는 window size만큼 쌓이고 더 이상 쌓이지 않고 멈춘다.
Clark 방법과 확인 응답의 지연의 차이는 Clark 해결방법의 경우 rwnd가 0인 ACK을 보내서 sender가 받는 순간 window size가 0으로 조정되므로 바로 멈추고, 확인 응답의 지연은 ACK이 올 때까지는 sender의 sending buffer는 window size만큼은 계속 쌓인다.
SYN Flooding
클라이언트가 SYN을 보냈을 때 SYN + ACK을 보내주는 것은 Server 소켓이고, 일반소켓은 listen을 통해서 서버소켓으로 만들어진다. listen으로 서버소켓을 만들 때 매개변수로 연결요청 대기 큐의 크기가 들어간다.
for(i=0; i<5; i++)
{
cint_sock=accept(serv_sock, (struct sockaddr* )&clnt_adr,&clnt_adr_sz);
if(ciInt_sock==-1)
error_handling("accept() error");
else
printf("Connected client %od \ n", i+1);
while((str_len=read(clnt_sock, message, BUF_SIZE))!=0)
write(clnt_sock, message, str_len);
close(cint_sock);
}
위 코드를 보면 클라이언트의 요청을 for문을 통해 순차적으로 처리한다. 이러한 것을 iteractive server라고 한다. 상대방의 FIN을 통해 서버가 close되어야 다음 클라이언트의 요청을 위해 accept을 한다.
위 코드를 다음 그림과 같이 볼 수 있다.
서버소켓에 client 1이 SYN을 보낸 상태이고, 서버는 클라이언트에게 SYN + ACK을 보내고 연결요청 대기 큐에 클라이언트를 담는다. 클라이언트에게서 ACK이 오면 accept함수로 인해 연결 요청이 수락되고, 새로운 소켓을 생성하여 client와 연결한다.
클라이언트의 주소가 165.246.34.51이고 포트번호가 3456인 클라이언트가 서버에게 SYN을 보내는 상황을 가정하자. 그런데 클라이언트가 자신의 IP를 잘못하고 167.5.6.7로 지정하면 서버로 보낼 때는 잘 전달되지만 답장을 받을 때 오류가 생긴다.
보통 클라이언트에서 IP는 header를 생성할 때 시스템이 알아서 처리해주지만, raw socket이라는 것을 통해 header를 manual로 만들 수 있다. 이러한 방법으로 클라이언트가 자신의 IP주소를 다른 주소로 적을 수 있는데, 그렇게 되면 서버가 SYN + ACK을 보내도 잘못된 클라이언트 IP로 보냈으므로 ACK이 서버에게 돌아오지 않는다. 따라서 연결요청 대기 큐에 클라이언트가 한 없이 대기상태가 되는 것이다.
이런식으로 연결요청 대기 큐가 꽉 차게되면 다음 client가 연결 요청으로 SYN을 보냈을 때, 넣을 연결요청 큐의 자리가 없으므로 서버는 SYN + ACK을 보낼 수 없는 상황이 된다.
이러한 상황을 SYN Flooding이라고 한다. 또 이러한 방식을 DoS(Denial of Service)라고 한다.
라우터의 특정 경로로 반복해서 오게 되면 네트워크 상에서 방화벽 등의 방법으로 버리면 되지만 한 경로가 아니라 여러 가지의 라우터 경로를 통해 동시다발적으로 SYN Flooding이 일어나게 되면, 즉 분산적(Distributed)으로 일어나게되면 막지 못하는데 이것이 바로 DDoS(Distributed Denial of Service)라고 한다.
Error Control
ACK은 응답으로 확인하지 않으므로 상대방이 받았는지 여부를 알 수 없다. 하지만 TCP는 데이터를 성공적으로 전달했는지에 대한 신뢰성이 중요하다. buffer에서 read를 통해 어플리케이션으로 올라가는 정보들도 TCP를 통해 전달한 것이므로 순서나 분실에 대한 신뢰성이 보장되었음을 전제로 한다.
ACK을 보내는 것에 대한 Normal operation을 알아보자. 이는 ACK의 개수를 줄여보려는 시도이다.
Rule1에 대해서 알아보자. seq 1201-1400에 대한 ACK이 1401로 가는 것이 보통이다.
ACK을 보내려 할 때 sending buffer을 찾아본다. 만약 server의 sending buffer에 보낼 데이터가 있다면, 데이터를 ACK과 함께 보내는 것이 Rule1이다.
Rule2의 경우, 서버로부터 seq 4001-5000에 대한 데이터가 왔으므로 ACK으로 5001을 보내야 하지만, Rule1을 고려하여 데이터가 올 것을 예상하고 50ms정도 기다린다.(그림은 500ms 이지만 50ms으로 알 것) 기다려도 데이터가 오지 않으면, 그때 ACK을 보낸다.
Rule3는 Rule2의 기다리는 상황에서 또 하나의 패킷이 올 경우, 더 이상 기다리지 않고 ACK을 보내는 것이다.
이와 같은 과정으로 ACK을 자주 보내는 상황을 줄여나가는 것이다. 이렇게 ACK을 보내기까지 받는 패킷 개수가 많아지만 ACK 하나를 잃어버릴 경우 손실되는 패킷의 수도 증가하므로 TCP 설계에 따라 2개까지만 설정한다.
Rule 4,5,6도 존재하는데, 이들은 특정 예외상황에서는 delay 없이 바로 전송한다는 규칙이다.
'개발 · 컴퓨터공학' 카테고리의 다른 글
네트워크 프로그래밍 - Error Control, Congestion control (0) | 2021.10.13 |
---|---|
UNIX - compile 명령어 정리, Makefile (0) | 2021.10.12 |
네트워크 프로그래밍 - Window, Flow Control (0) | 2021.10.10 |
UNIX - main function, process, fork, exec (0) | 2021.10.09 |
UNIX - current directory, sync / fsync, device files (0) | 2021.10.08 |