본문 바로가기

OSI Model

15. TCP Syn flooding (4계층 Transport Layer전송 계층)

(4계층 Transport Layer전송 계층)



TCP Syn flooding

왼쪽 그림은 정상적인 TCP 를 맺는 Flow 이다. 하지만 오른쪽 처럼 서버의 Syn + Ack에 대해 클라이언트쪽에서 ACK을 주지 않으면, 75초 동안 대기 후 커넷션을 초기화를 한다.

문제는 위와같이 수많은 요청이 발생 할 경우, 커넥션 마다 75초를 기다리고, 메모리의 백로그큐(backlog queue) 에 쌓이면서, 서버쪽에 설정된 backlog 값을 넘어 서면 더이상 Syn 요청을

받지 못하게 된다.

 

해결방안

서버syncookie 기능 활성화

+syncookie : 클라이언트 Syn 요청에 대해 서버에서 응답할때 Syn 패킷을 backlog에

저장하지 않고, Tcp Packet hearder의 option항목 (mss, timestamp 등) 에 Initiak Sequence Number(ISN) dmf 만들어 클라이언트에게 보냅니다.

클라이언트에서 ACK가 왔을때 다시 ISN 정보를 확인하고 클라이언트의 정보를 확인후 커넷션을 맺는다.

Syncookies 기능은 syn flooding 으로 backlog가 찼을때만 활성화 됩니다.

 

기본으로 해당 옵션을 켜는게 좋습니다

굳이 syncookies의 단점이라고 하면... 클라이언트 -> 서버로 ACK 패킷이 유실될 경우, 클라이언트쪽 해당 커넥션이 freezing 상태가 됩니다. 또한 정상적인 3way hancshake 면 packet hearder 의 option 항목을 참조하여 서버측 최적의 window scailing 값 등을 사용하여 성능을 높이지만, syncookie 가 활성화 되면 해당 정보를 참조할수 없어 성능면에선 좋진 않다고 하지만 큰 성능차는 나지 않을 것이다

 

 

커널 옵션 및 어플단 backlog 항목 설정

+커널옵션

- net,ipv4,tcp_syncookies = 1 (활성화시 /var/log/messages에 SYN flooding 로그가 남으며

Netstat 로 SYN_RECV 갯수가 비정상적으로 많으지 확인)

 

- net,ipv4,tcp_max_syn_backlog = 지정

-net,core,somaxconn = 지정

 

+어플옵션

-nginx : conf 파일에 backlog 옵션 지정

-apache : server,xml 옵션 지정

 

중간에 LB를 두어 구성

-LB도 서버와 마찬가지로 syncookie 옵션을 키면 앞단에서 손쉽게 막을 수 있다. 물론 LB에 해당 옵션을 켜야한다.

-서버와 마찬가지로 클라이언트 ACK을 받지 않는 이상 LB 세션테이블을 만들지 않는다.

이 외에도 방화벽이나 보안장비들을 앞단에 놓는 것도 방법이긴 하지만, 고가의 비용이 들어간다.

로드 밸런스에 대한 링크

https://leejoongwon.blogspot.com/2019/06/blog-post_28.html

 

TCP 트러블 슈팅

서버를 운영하다보면 TCP 기반의 서비스에서 reset이 왜 났지? 왜 계속 Retransmission을

요청하지? 하면서 트러블 슈팅을 진행 하는 경우가 많다. TCP 문제는 tcp dump을 추출 한 후 확인 하는게 최선의 방법이다.

 

Reset

reset은 커넥션 관리( time wait 최적화, 효율적인 커넥션 조정 ) 을 위한 TCP 매커니즘 이다

하지만 클라이언트 <-> 서버 간의 커넥션을 열고 통신이 잘되고 있는데, 어디선가 reset 을

날려 해당 커넥션의 요청이 실패하여 장애로 이어지는 경우가 문제가 된다

reset은 주로 서버쪽 응용이나 클라의 어플에서 발생 한다. 개발쪽 TCP관련 코딩의 경우가 대부분이나 , 네트워크 문제 또는 순단 등이 원인이 되는 경우도 있다

 

1. 네트워크 순단

-라우터나 스위치에선 reset을 보내는 기능이 없습니다. (하지만 LB에선 reset을 보낸다)

-순단으로 인해 응용쪽 처리가 밀리거나 O O M 발생시 응용 재시작이 되면서 reset를 보내는 경우

-LB에서 reset을 보내는 경우

 

 

Dsr 구조의 LB 사례

- 클라이언트쪽에서 30초간 요청이 아무것도 없다면, LB 세션테이블은 종료 되고 LB -> 클라&서버에 reset을 날린다. ( 만약 30초 이상 순단이 되면 서버는 LB로 부터 reset받아 커넥션이 종료 되지만, 클라이언트쪽은 좀비세션 으로 남을수도 있다.)

-서버쪽 keepalive 설정이 180초라 하고, 이를 기반으로 개발을 했는데 LB의 idle timeout

설정이 짧다면 LB로부터 reset을 받는다

-반대로 서버쪽 keepalive 설정값이 LB보다 짧다면 절대로 LB에서 먼저 idle 세션테이블을 끊지 않는다

-위와 같은 구조에서 LB쪽 세션테이블에 문제가 생기면, 서버->클라이언트쪽 통신은 되지만 반대로 클라이언트 ->서버쪽 통신이 되질 않는다. 이럴 경우 클라이언트쪽에선 해당 커넥션을 대기하면서 서비스에 지장을 주고, 서버 입장에선 클라이언트 요청이 없으니 keepalive가 적용되어 나중엔 서버 쪽에서 클라이언트로 reset를 날린다. LB세션 테이블에 문제가 생기면 LB에서 클라이언트 서버에 reset을 보낼때도 있고 아닌 경우도 있다(LB벤더사 마다 설정이 틀리고, VIP내에 서버가 전부 죽었는지? 한대라도 살아 있는지 여부에 따라 reset을 보낼지 말지 판단한다)

 

 

서버 레벨의 문제

-RX TX Ring buffer 크기가 작아서 발생

+buffer full이 발생하게 되면 패킷이 pause 상태가 될수 있다.

+ethtool 을 통해 버퍼사이즈를 최대로 늘린다. (구형 NIC 또는 구버전 펌웨어&드라이버인 경우 해당 설정값의 MAX치에 한계가 있을수 있다>)

 

-CPU receiving NIC interrpts

+NIC의 동작을 1개의 CPU쓰레드만 사용 하는 경우이다. 이럴 경우 CPU부하로 문제가 될수 있다.

이런 원인은 NIC driver 나 옵션설정, 또는 OS레벨에서의 irqbalance을 이용한다.

 

-NIC driver & Firmware

+이 부분은 확인 하기가 어렵다. NIC벤터사의 드라이버, 펌웨어 릴리즈노트를 보며 이슈를 확인 해보며, 우선 최신으로 업뎃후에 증상을 관찰 한다.

+서버에서 ifconfig 출력 후 RX, TX 에 dropped count가 늘어났을 경우 아래 3가지 사항을 확인한다.

+NIC카드 실제 장애

+lan 케이블 불량

+스위치 포트 불량

 

 

커넥션풀

커넥션풀 이란? 설정 한 만큼 TCP커넥션을 맺어두고 그 커넥션을 이용한다. 즉 매번 TCP을 연결 하게 되면 그 만큼 일도 많아지고 잡비용이 증가되므로, 일정한 커넥션 풀을 만들어 두고 그걸 사용한다.

아래 그림은 java기반의 app서버 <->DB 연동시 예제 이다 min/max 설정을 5로 했다고 가정 하고, app구동시 min 설정에 따라 기본적으로 5개의 커넥션풀을 생성 한다.

max값이 5를 넘으면.. 기존 커넥션 중 하나가 종료 될때 까지 대기 후 처리 한다.

.커넥션풀을 어떤걸 쓸지 결정 하는 옵션중 GeneriObjectpool 등 옵션으로 LIFO, FIFO 로

지정 할수 있다.

연결 단 중간에 LB나 GSLB등의 분산장치를 뒀는데, 유난히 한쪽 서버에 트래픽이 몰리는 현상이 있을 수 있다.

이는 LIFO로 설정되어 최근에 반납된 idle connection pool을 계속 재사용 하여 RR하게 분배가 안되고 한쪽으로 몰릴수 있다.( 예를 들면 하나의 스레드에서 루프를 돌면... 같은 커넥션으로 계속 붙게 될수 있음)