/var/log/messages
3-way handshaking 본문
3-way handshaking은 TCP/IP 프로토콜을 사용하여 통신을 시작하는 데 사용되는 중요한 프로세스입니다. 이 과정은 클라이언트와 서버 간에 안정적인 연결을 설정하기 위해 사용됩니다. 이 글에서는 3-way handshaking의 과정을 단계별로 설명하고, 그 중요성을 강조합니다.
클라이언트는 임의의 시퀀스 번호가(100) 포함된 SYN(동기화) 패킷을 서버로 보냅니다.
서버는 임의의 시퀀스 번호와(200) 클라이언트의 시퀀스 번호를 승인하는 ACK 번호가(101) 포함된 SYN-ACK 패킷을 다시 보냅니다. 클라이언트는 서버의 시퀀스 번호를 승인하는 ACK 번호를(201) 서버로 보냅니다.
이 과정을 더 심오하게 보면 각 노드의 상태가 어떤지와, 어떠한 시스템 콜 호출이 되는지를 알아야 합니다.
제가 제공한 그림을 기준으로, 확인해 보겠습니다. 이때 주의할 점은, 그림에 Client, Server라고 적혀 있다고, 무의식으로 무조건 Client, Server 간의 통신이 아닌 Node간의 통신에서 일어나는 것이라고 인지해야 합니다.
일단, SYN과 ACK가 무엇이고, 어떤 의미인지 알아보겠습니다.
SYN은 "Synchronize Sequence Numbers"의 약자로, 연결을 시작할 때 사용됩니다.
클라이언트가 서버에 연결을 요청할 때 첫 번째 단계로 SYN 패킷을 보내고, 이 패킷에는 연결 시작을 알리는 데 사용되는 초기 시퀀스 번호가 포함됩니다. SYN 패킷을 전송하기 전, SYN Queue에서 대기를 하게 됩니다.
localhost:~ # cat /proc/sys/net/ipv4/tcp_max_syn_backlog
4096
현재 제 시스템에서는 SYN Queue의 크기가 4096 Byte라는 것을 확인할 수 있습니다.
ACK는 "Acknowledge"의 약자로, 받은 패킷이나 시퀀스 번호를 확인할 때 사용됩니다.
SYN 패킷을 받은 서버는 SYN과 ACK 플래그가 설정된 패킷으로 응답하여 클라이언트의 요청을 받았다고 알리고, 자신의 시퀀스 번호도 함께 보냅니다. 이후 클라이언트도 ACK 패킷을 보내어 서버의 SYN을 확인합니다. 이 과정을 통해 양쪽 모두 서로에 대한 정보를 확인하고 연결이 설정됩니다. 원래는 ack 패킷을 받고, aceept() 시스템 콜 호출이 되어야 연결이 성립이 되는 것이라, 해당 시스템 콜 호출이 되기 전, Accept Queue에서 대기하게 됩니다.
localhost:~ # sysctl net.core.somaxconn
net.core.somaxconn = 4096
현재 제 시스템에서는 Accept Queue의 크기가 4096 Byte라는 것을 확인할 수 있습니다.
이 값에 의해서 backlog 값이 결정됩니다.
이제 SYN과 ACK가 무엇인지 알았으니, 각 단계에서 어떠한 일이 일어나는지를 간단하게 알아보겠습니다.
Listen():
시스템은 소켓의 상태를 확인하고, 연결 대기 상태로 변경합니다.
소켓이 이미 LISTEN 상태인 경우, backlog 크기만 조정할 수 있습니다.
서버 입장, SYN 패킷을 수신:
multicast, broadcast 주소로 보내진 것인지를 확인 후, Accept Queue가 가득 찼는지 확인합니다.
서버가 클라이언트에게 SYN+ACK 패킷을 전송:
송신 경로를 설정 후, 전송합니다. 전송에 실패할 경우, net.ipv4.tcp_synack_retries 값을 통해 최대 재전송 횟수를 설정할 수 있습니다.
localhost:~ # sysctl net.ipv4.tcp_synack_retries
net.ipv4.tcp_synack_retries = 5
서버가 클라이언트에게 ACK를 받았을 때:
클라이언트로부터 ACK 패킷을 받으면, 이를 확인하고 연결 요청에 대한 최종 승인 절차를 진행합니다.
진행 후, 서버는 '완전한' 소켓을 생성합니다. 소켓이 만들어지면 Accept 큐에 추가됩니다.
Accept 큐는 accept() 함수 호출을 통해 애플리케이션에 전달될 준비가 된 연결들을 저장합니다.
마지막으로, 서버는 소켓의 상태를 'ESTABLISHED'(연결 수립)으로 변경합니다.
Accept():
accept() 함수는 대기 중인 연결 요청들 중 하나를 수락합니다.
새 소켓은 연결이 완전히 수립된 상태로 변경되며, 이제부터 데이터 송수신이 가능해집니다. 새로운 소켓은 애플리케이션에 할당되고, 애플리케이션은 이를 통해 클라이언트와 통신할 수 있습니다. 연결이 수락되면, 관련 리소스(예: 메모리)도 적절히 관리됩니다. 예를 들어, 새 소켓에 대한 메모리 사용량이 추적되고, 필요한 경우 리소스 사용량이 조정됩니다.
이 과정에서 SYN Flooding이라는 공격을 당할 수가 있습니다.
SYN Flooding이란, SYN+ACK를 받은 노드는, ACK를 전송해야 하는데, ACK를 전송하지 않는 것입니다.
그러면서 수많은 SYN를 처음 SYN+ACK를 받은 노드에게 계속 전송합니다.
그렇게 되니, /proc/sys/net/ipv4/tcp_max_syn_backlog이 감당을 할 수 없을 정도로 많은 패킷이 쌓이게 됩니다.
결국 SYN Drop이 발생합니다. 그렇게 되면 연결이 끊어지게 됩니다.
그럼에도 불구하고, 커널은 SYN Drop이 발생하고 있는 경우에도, 연결을 위한 best effort를 수행합니다.
바로 ‘net.ipv4.tcp_syncookies’라는 커널변수가 이 기능을 수행하게 합니다. 이 값이 1이라면, 해당 기능을 활성화 하겠다는 것입니다. 물론 속도는 느려지긴 합니다.
localhost:~ # sysctl net.ipv4.tcp_syncookies
net.ipv4.tcp_syncookies = 1
또한 dmesg로도 syn flooding이 있었는지 확인 할 수 있습니다.
localhost:~ # dmesg -T | grep -i "syn flooding"
localhost:~ #
그러나, 커널은 ack flooding 공격을 받고 있다는 판단을 할 수 없습니다.
왜냐하면, 커널이 syn flooding을 당했다고 판단 할 수 있는 근거는 syn_recv 상태가 예상보다 많을 때, 이 공격을 받고 있다고 생각할 수 있습니다.
localhost:~ # ss -tuna | grep –i "syn-recv"
localhost:~ #
비록 출력이 된 결과는 없지만, 이 상태도 감지할 수 있다는 것을 증명하기 위해서 ss의 man page 일부 발췌를 했습니다.
STATE-FILTER STATE-FILTER allows to construct arbitrary set of states to match. Its syntax is sequence of keywords state and exclude followed by identifier of state. Available identifiers are: All standard TCP states: established, syn-sent, syn-recv, fin-wait-1, fin-wait-2, time-wait, closed, close-wait, last-ack, listening and closing. ... bucket - states, which are maintained as minisockets, i.e. time-wait and syn-recv big - opposite to bucket |
그러나, 위에서 제공한 자료를 보면 ack-recv란 상태는 없습니다.
즉, 커널이 ack flooding을 받고 있다고 판단할 근거가 없기 때문에, ack flooding을 감지할 수 없습니다.
다음 글에는 4-way handshaking에 대해서 설명하겠습니다.
'SUSE' 카테고리의 다른 글
dmesg가 출력할 수 있는 메시지 종류-Hardware Error (0) | 2024.04.02 |
---|---|
4-way handshaking (0) | 2024.04.01 |
error: cluster.join: [Errno 2] No such file or directory: '/etc/corosync/corosync.conf (0) | 2024.03.18 |
xfs 파일시스템 '안전하게' 복구하기 (0) | 2024.02.05 |
파티션 테이블 복제(백업)하기 (0) | 2024.01.30 |