Null Routing (Blackhole Routing)이란

  • 특정 IP 또는 특정 대역에서 과도한 스캐닝이나 공격시도가 감지된다면 해당 IP 또는 대역을 차단할 수 있는데, 이때마다 일일이 access list를 지우고 다시 설정하는 작업은 번거로운 일임
  • access list보다 CPU 부하가 적으면서도 손쉽게 필터링이 가능한 Null Routing을 사용
  • 특정 목적지IP 또는 IP대역에 대해 특정한 인터페이스로 라우팅 테이블을 강제로 지정 → 특정패킷이 라우터 내부로 들어올 수는 있지만 나갈 수는 없도록 하는 것으로 내부적으로 패킷을 필터링하는 것이 아니라 패킷을 Null 0 인터페이스로 포워딩하는 개념
  • blackhole filtering이라고도 함 → Null이라는 가상의 interface로 보내는것을 blackhole로 보내는 것과 비슷
  • 특정한 IP또는 IP대역에 대해서 null이라는 가상의 interface로 보냄
  • Null Interface는 특정주소를 목적지로 하는 트래픽을 차단(혹은 폐기)하기 위해서 사용
  • Null Interface는 ACL 과는 달리 라우터의 CPU 소모율(CPU 부하)이 낮기 때문에 효과적으로 라우터의 부하를 줄이는 방법
  • Null Interface는 Routing Loop를 방지하는데 도움을 줌


Null Routing 사용 이유

  • ACL보다 Router에 부하가 덜하고, 설정이 편리하단 점에선 ACL보단 편리함
  • Null Routing은 Dest IP나 IP대역에 대해서 막는건 쉽지만, 기능적으로 제한적임
  • Null Routing과 달리 ACL은 패킷에대해 포트나 Source IP, Dest IP에 대해 유연하게 제어가 가능
  • 네트워크대역에 대해 심각하게 IP스캔을 하거나 스팸을 뿌리는 IP가 보일 경우 Null Routing으로 차단 필요
  • 공격자는 대부분 차단된 것을 확인 후에는 더 이상의 시도를 하지 않게 되고 때때로 자동화된 스캔 프로그램을 실행해 두었는지 차단되고 있음에도 불구하고 계속시도를 하는 경우도 있음 → 공격한 패킷은 Null0으로 이동하기에 CPU 부하 X
  • Null Routing이란 소스 IP에 대한 라우팅이 아니라 목적지 IP에 대한 라우팅이므로 공격자의 IP를 Null0으로 설정하였을 경우 공격자의 요청(소스:공격자, 목적지:내부서버)은 라우터를 통과하여 서버까지 전달되지만 서버에서의 응답(소스:내부서버, 목적지:공격자)이 blackhole처럼 Null0로 보내어져 사라지게 함 → 패킷 부하 X
  • Null Routing 참고 사항
    • 공격자의 소스 IP를 차단하려면 access list를 사용해야함
    • Null Routing은 포트까지 제어할 수 있는 L4가 아니라 IP나 IP대역을 차단하는 L3
    • Null Routing은 특정한 포트를 차단할 수는 없음으로, 특정 포트를 차단하기 위해서는 extended access list 사용 필요


Null Routing 사용 형식

  • static routing을 이용하여 특정한 목적지 IP 또는 대역을 Null0라는(유닉스의 /dev/null과 유사) 가상의 쓰레기(garbage) 인터페이스에 강제적으로 보냄으로써 트래픽을 차단하는 방식
  • 라우터에서는 패킷이 필터링 될 때마다 해당 패킷의 소스 IP로 icmp unreachable messages를 발송 → 서비스 거부 공격이 이루어지고 있을 경우 필터링 되는 패킷으로 인하여 라우터에서 많은 icmp 패킷이 유발 (라우터에 과부하를 유발)
  • Null0 인터페이스에서 반응하지 않도록 no ip unreachables 설정 → 포트스캔이나 IP스캔의 결과를 지연시키거나 보이지 않도록 하는 효과
  • Null Routing 사용 형식
    interface Null0
    no ip unreachables
    !
    ip route <dest to drop> <mask> Null0

  • sender.com에서 receiver.com으로 telnet 접속을 시도
    1. 라우터 내부에는 receiver.com이라는 서버가 없거나 다운되어 연결할 수 없기 때문에 라우터에서 대신 icmp로 응답
    2. "no ip unreachables” 설정 후에는 라우터에서 반응 X
    3. ip unreachables를 반드시 사용하여야 할 특별한 이유가 있다면 “ip icmp rate-limit unreachable”을 이용하여 응답하는 비율을 제한
      sender.com.31504 > receiver.com.23: S
      router > sender.com:icmp: host receiver.com unreachables

  • Null Routing을 이용하여 231.1.1.1 IP를 차단

    1. static routing이므로 mask 적용 시 access list에서 사용하는 wildcard mask를 쓰지 않도록 주의 필요

      interface Null0
      no ip unreachables
      !
      ip route 231.1.1.1 255.255.255.255 Null0
    2. wildcard mask를 사용하여 231.1.1.0/24 대역을 차단

      Router(config)#ip route 231.1.1.0 255.255.255.0 Null0
    3. 설정한 내용을 해제하려면 설정내용 앞에 no가 필요



패킷트레이스 Null Routing 사용 예시

  • Null Routing 명령어 구조
    router(config)# ip route [dest ip | network] [subnet mask] null0

  • Null Routing 설정하지 않고 203.235.222.3 -------> 192.168.10.1으로 ping 테스트 (정상)

  • Null Routing 설정 (203.235.222.0/24 대역 차단) → -------> 192.168.10.1으로 ping 테스트 (일정 시간 초과하면 차단)
    # 양식
    R1(config)# ip route 203.235.222.0 255.255.255.0 null0

참고 URL : https://www.linux.co.kr/bbs/board.php?bo_table=lecture&wr_id=2601
참고 URL : https://secdata.tistory.com/18


Timeout이란

  • 프로그램이 특정한 시간 내에 성공적으로 수행되지 않아서 진행이 자동적으로 중단되는 현상
  • 응답을 무한정 기다릴 수 없기 때문에 기다릴 시간을 정해야 함


1. Connection Timeout

  • 클라이언트가 서버와 연결을 맺지 못하는 경우 발생하는 Timeout을 Connection Timeout이라고 함

  • 3-Way Handshake을 정상적으로 수행해야지 클라이언트와 서버가 정상적으로 연결을 맺었다고 말할 수 있음

  • 3-Way Handshake 연결(Connection)에 소요된 시간 → Connection Timeout은 연결을 하는데 소요되는 시간의 임계치

  • Connection time을 무한으로 설정할 수는 없으니까 일정 시간을 지정하여 무한정 연결을 제한


  • Connection Timeout 사례 예시

    • 히포가 유명맛집을 인터넷에서 검색하고 맛집에 찾아갑니다.
    • 맛집에는 이미 많은 사람들이 찾아와 북적이고 대기열도 한가득 있었습니다.
    • 성질급한 히포는 “이거하나 먹으려고 이리 오래 기다려야되나?" 라고 생각하고 “10분만 기다려보고 안되면 다른데 가야겠다”라고 결정을 내렸습니다.
    • 10분을 기다렸지만 대기열은 줄어들지 않아 히포는 맛집은 들어가보지도 못한채 다음에 와봐야겠다 생각하고 집으로 돌아갔습니다.
    • 서버(맛집)자체에 클라이언트(히포)가 어떤 사유로 접근을 실패했을시 적용되는것이 Connection timeout입니다.
    • 접근을 시도하는 시간제한이 Connection Timeout이 되는 것입니다.


2. Socket Timeout

  • 클라이언트와 서버가 정상적으로 연결된 이후에 발생
  • 서버는 클라이언트에게 응답 메세지를 전달할 때, 하나의 메세지를 여러 개의 패킷(packet)으로 나누어 전달
  • 각 패킷이 전송될 때 시간 갭이 발생할 수 있는데 시간이 초과된 것 → 서버에서 클라이언트 전달할 때 시간 간격인 Socket Timeout 발생
  • 클라이언트가 응답에 대한 패킷을 전달받을 때 시간 차이가 발생 → 발생하는 시간 차이의 임계치가 Socket Timeout
  • 전체 연결이 타임아웃이 발생한 것이 아니고 개별 패킷이 타임아웃이 발생



3. ConnectionTimeout과 SocketTimeout의 차이 (웹 브라우저에서 네이버에 접속)

  • URL을 호출할 때에는 ConnectionTimeout과 SocketTimeout 설정이 모두 필요
  • 두 가지 Timeout을 설정하지 않으면 URL 접속시 무한 대기 가능
  • OS 레벨에서 Timeout이 설정되어 있는 경우 그 설정값이 적용됨


3.1. 웹 브라우저가 네이버 서버에 접속하기 위해 서버와 연결

  • 연결을 구성하기 위해서 보통 TCP Connection과 동일하게 3-way Handshake 방식으로 수행
  • 3-way Handshake를 정상적으로 수행하게 되면 웹 브라우저와 네이버 서버는 연결된 상태가 됨
  • 3-way Handshake에 소요된 시간을 Connection에 소요된 시간이라고 함
  • ConnectionTimeout은 Connection을 구성하는데 소요되는 시간의 임계치를 의미


3.2. 클라이언트와 서버가 연결된 상태에서 서버는 데이터를 클라이언트로 전송

  • 하나의 패킷이 아니라 여러 개의 패킷으로 나눠서 전송
  • 여러 패킷을 전솔할 때, 각 패킷이 전송될 때 시간 Gap이 발생 가능
  • 패킷이 전송하는 시간의 임계치를 SocketTimeout


3.3. ConnectionTimeout과 SocketTimeout 비교




4. Read Timeout

  • Read Timeout도 Socket Timeout과 마찬가지로 서버와 정상적인 연결은 된 이후에 발생
  • 서버가 클라이언트의 요청을 받아 처리하는 시간이 길어지게 되는 경우 Read Timeout이 발생 → 요청에 대한 응답이 없어서 발생
  • 클라이언트가 특정 시간동안 서버로부터 요청에 대한 응답을 받지 못함
  • 주고 받는 데이터의 양이나 네트워크 속도에 따라서 대응을 다름
  • 데이터의 양이 크다면 이를 분할해서 받을 수 있도록 API 자체 Spec을 변경하거나 Retry 전략을 사용
  • 속도가 느려서 발생하는 상황이라면 전반적으로 네트워크 대역폭 증가를 위한 인프라 작업을 필요
  • Read Timeout 사례 예시
    • 히포가 대기열을 뚫고 맛집안까지 들어가서 메뉴를 주문하는데까진 성공을 했습니다.
    • 근데 워낙에 사람이 많다보니 이번엔 주문한 메뉴가 나오는데 많은 시간이 필요했습니다.
    • 주방에서 뭔짓을 하는지 아무리 기다려도 주문한 음식은 나오지가 않습니다.
    • 성질급한 히포는 “아오! 그냥 이시간에 딴걸할걸 바빠 죽겠는데” 하고 그냥 가게를 나와버립니다.
    • 나중에 히포이가 주문한 음식은 나왔지만 이미 음식을 먹을 사람은 사라지고 없어져, 결국 요리는 버려지게 됩니다.
    • client가 server에 접속을 성공 했으나 서버가 로직을 수행하는 시간이 너무 길어 제대로 응답을 못 준상태에서 client가 연결을 해제하는것이 Read timeout입니다.
    • client는 해당상황을 오류로 인지(요리가 안나옴)하고 server는 계속 로직(주문된 요리)을 수행하고 있어 성공으로 인지해 양사이드간에 싱크가 맞지 않아 문제가 발생할 확률이 높습니다.


Fast Retransmit (빠른 재전송)

  • 타이머의 타임 아웃 기간이 상대적으로 너무 길어 타이머가 종료되기 전이라도 중복된(duplicate) ACK를 3번 받으면 바로 재전송을 하는 기능 → 수신측이 기다리는 순서번호의 세그먼트보다 큰 순서번호의 세그먼트가 3개 도착할 경우를 의미
  • 손실된 패킷을 재전송하기 전 발생하는 긴 지연시간을 줄여줌
  • Fast Retransmit는 또 다른 말로, Triple-Duplicate-ACK이라고 하며 3개의 중복된 ACK를 받으면 재전송하는 것
  • Duplicate ACK 수신을 통해 세그먼트가 손실되었음을 감지 → 손실된 경우 수신 측은 여러 중복 ACK들을 발생시킴
  • 송신자는 동일한 데이터에 대해 3개의 ACK를 수신하면 ACK된 데이터 이후의 세그먼트가 손실된 것으로 가정
    1. Duplicate ACK는 비정상적인 순서(out-of-order)의 패킷이 수신될 시 수신 측에서 발생시킴 → 현재까지 ACK가 완료된 패킷 다음 패킷이 손실 난 상태에서 다음 패킷들이 계속 수신되는 경우
    2. Host A에서 보낸 2번째 패킷이 손실되어 Host B는 3, 4, 5번째 패킷 수신 시에 duplicate ACK를 보내게 됨
    3. RTO 타이머가 종료되기까지 시간이 남았지만, Host A는 2번째 패킷이 손실된 것으로 가정하고 재전송함

Flow Control(흐름 제어)

  • TCP 송신자가 데이터를 너무 빠르게 또는 너무 많이 전송하여 수신버퍼가 오버플로우(Overflow)하는 것을 방지
  • Flow Control(흐름 제어)는 송신측과 수신측의 데이터처리 속도 차이를 해결하기 위한 기법
    1. 수신측이 송신측보다 속도가 빠른 것은 아무 문제가 되지 않음
    2. 송신측이 수신측보다 속도가 빠르면 문제가 발생
  • 수신측에서 수신된 데이터를 처리해서 윗 계층으로 서비스 하는 속도보다 송신측에서 보내는 데이터 속도가 더 빠르다면, 수신측에서 제한된 저장용량(일반적으로 큐)을 초과하여, 도착하는 데이터의 손실을 가져올 수있음
  • 속도 차이로 인해 발생한 데이터의 손실을 복구하기 위해 불필요하게 응답과 재전송의 데이터가 필요 → 다시 송신측과 수신측간에 이동
  • 수신측의 저장공간이 초과되어 발생하는 불필요한 재전송을 줄이기 위해 강제로 송신측의 데이터 전송을 줄임
  • Flow Control(흐름 제어)는 속도를 일치시키는 서비스 → 애플리케이션 프로세스의 읽는 속도 = 송신자가 데이터를 전송하는 속도
  • Receive Window(rwnd)

    1. 수신버퍼의 가용 바이트 수로, 송신 윈도우 크기를 결정

    2. 수신 버퍼보다 작아야함 → 초과하면 오버플로우 발생

      LastByteRcvd - LastByteRead <= RcvBuffer
      [밑에서 수신 받은 번호 - 위로 올려 보낸 번호 = 수신했지만 상위계층으로 전송되지 않은 데이터들]
      
      rwnd = RcvBuffer - (LastByteRcvd - LastByteRead)
       = RcvBuffer - (수신했지만 상위 계층으로 전송하지 않은 데이터들)

  • 수신자는 TCP헤더에 수신버퍼안에 있는 여유공간(Receive Window, rwnd) 값을 포함함으로써 버퍼에 공간이 있음을 송신자에게 알림

  • 수신버퍼의 크기는 소켓의 옵션을 통해 설정되는데 디폴트 값이 4096바이트

1. Stop and wait 방식

  • 매번 전송한 패킷에 대해 확인응답을 받아야만 그 다음 패킷을 전송하는 방법

2. 슬라이딩 윈도우 기법

  • 수신 측에서 설정한 윈도우 크기만큼 송신 측에서 확인 응답 없이 세그먼트를 전송할 수 있게 하여 데이터 흐름을 동적으로 조절하여 제어하는 기법
  • 슬라이딩 윈도우 기법을 통하여 송신 버퍼의 범위는 수신 측의 여유 버퍼 공간을 반영하여 동적으로 바뀜으로써 흐름제어를 수행


Congestion control(혼합 제어)

  • 호스트와 네트워크 상의 데이터처리를 효율적으로 하기 위한 기법
  • 송신측의 데이터 전달과 네트워크의 처리속도 차이를 해결
  • 송신측의 데이터는 지역망이나 인터넷으로 연결된 대형 네트워크를 통해 전달 → 네트워크 상의 라우터가 항상 바쁜 상태임
  • 하나의 라우터에 데이터가 몰려 혼잡한 경우에, 라우터는 자신에게 온 데이터를 모두 처리할 수 없음
  • 호스트들은 라우터에서 손실된 데이터를 재전송을 하게 되고 결국 혼잡을 가중시켜 오버플로우나 데이터 손실을 발생
  • 네트워크의 혼잡을 피하기 위해 송신측에서 보내는 데이터의 전송 속도를 강제로 줄이게 됨

1. Slow start

  • 윈도우 크기를 2배로 늘림
  • 혼잡현상이 발생하면 창 크기를 1로 떨어뜨림
  • 혼잡현상이 발생했던 창 크기의 절반까지는 이전처럼 지수 함수 꼴로(2배로) 창 크기를 증가시키고 그 이후부터는 완만하게 1씩 증가시킴

2. Fast Recovery

  • 혼잡한 상태가 되면 창 크기를 1로 줄이지 않고 반으로 줄이고 선형 증가시키는 방식


참고 URL : https://pcmc.tistory.com/entry/190618-TCP-Retransmission
참고 URL : https://movefast.tistory.com/36
참고 URL : https://dlee0129.tistory.com/204

  • 링 버퍼(Ring Buffer)는 원형 큐로 약간의 개량해서 사용
  • 링 버퍼는 가변적인 데이터를 처리
  • 기본적으로 네트워크의 처리 단위 : 1/1000 초
  • 기본적으로 CPU의 처리 단위 : 1/1000000 초 이하
  • 네트워크와 CPU는 처리 속도가 수 백에서 수 만 배 차이 발생
  • 네트워크의 통신에서 패킷의 크기가 고정되어 있으면 처리가 쉬움
  • 필요 없는 데이터까지 포함한 가변적인 크기를 가진 패킷을 사용하는데 가변 크기를 가진 패킷을 저장하기 위한 링 버퍼(Ring Buffer)라는 자료구조가 사용 → 필요 없는 데이터까지 입/출력에 포함되어 있어서, 가변 데이터를 처리할 때 네트워크의 성능을 떨어짐
  • 링 버퍼는 분해된 패킷을 합치거나 완성되지 않은 자투리 패킷을 완전한 패킷으로 만들 수 있는데 편리
  • 링 버퍼는 Byte 단위로 저장할 공간만 있으면 동작함.
  • FIFO 기반의 일반적인 선형 큐의 메모리가 꽉 차게되면 Overflow가 발생 하지만 링 버퍼(Ring Buffer)는 head와 tail이 붙어 있는 형태의 큐를 구성으로 dropped 되어 유실되는 packet을 방지 가능

Network Ring Buffer Size 설정

  • Ring Buffer는 네트워크 카드의 버퍼 공간
  • 스위치에서 서버로 전달된 패킷의 전달 과정
    1. 네트워크 카드 내 Ring Buffer 에 보관 (NIC 카드 → Ring Buffer)
    2. 커널의 Socket RCV Buffer로 이동 (Ring Buffer → Socket RCV Buffer)
    3. User Application의 read 함수를 통해 전달 (Socket RCV Buffer → Application의 read()가 값 읽음)
  • Kernel의 Socket RCV Buffer가 여유가 있더라도 Ring Buffer Size가 작다면 중간 병목(bottleneck)이 발생 → Ring Buffer는 가능하다면 Maximum 값으로 설정해주는 것이 권장

  • 리눅스에서는 패킷 송수신시 1계층 물리적 케이블을 통하여 NIC로 packet이 수신 되면 가장 먼저 Ring Buffer 라는 영역에 보관한 뒤에 처리


  • Linux 상에서 NIC의 Ring Buffer(Current, Maximum) 값은 ethtool -g 명령어를 통해 확인 가능

    1. eth1 maximum으로 처리 가능한 ring buffer size는 4096임

    2. eth1 Current으로 처리 가능한 ring buffer size는 512으로 설정

    3. 리눅스 상에서packet 의 drop 과 error 를 줄이기 위해서 maximum 과 Current 동일하게 설정하는 것이 좋음

      # interface의 경우 최대 값이 4,096이지만 현재 설정 값은 512
      $ ethtool -g em1
      Ring parameters for em1:
      Pre-set maximums:
      RX:             4096
      RX Mini:        0
      RX Jumbo:       0
      TX:             4096
      Current hardware settings:
      RX:             512
      RX Mini:        0
      RX Jumbo:       0
      TX:             512

  • ethtool -G 명령어에 해당 파라미터를 maximum 값과 동일하게 설정한 뒤 설정 값을 변경

    # ethtool -G 명령어를 통해 ring buffer를 최대 값으로 설정
    $ ethtool -G em1 rx 4096
    $ ethtool -G em1 tx 4096
    
    # 변경된 ring buffer 내용 확인
    $ ethtool -g em1
    Ring parameters for em1:
    Pre-set maximums:
    RX:             4096
    RX Mini:        0
    RX Jumbo:       0
    TX:             4096
    Current hardware settings:
    RX:             4096
    RX Mini:        0
    RX Jumbo:       0
    TX:             4096



Network Ring Buffer Size 확대 영구적으로 등록

  • 대부분의 Linux 설정과 같이 명령어는 현재 메모리 상의 값을 변경하기 때문에 재부팅 시에는 이전 값으로 원복됨

  • 영구적으로 변경하기 위해서는 Network script 내 해당 Interface에 관련 설정 값을 추가해주는 것이 필요

  • 아래와 같이 스크립트 파일 내 ETHTOOL_OPTS 옵션을 추가한 후 해당 설정 값을 입력 → rx/tx의 최대 값을 확인 후 작성 필요

    $ cat /etc/sysconfig/network-scripts/ifcfg-em1
    TYPE=Ethernet
    BOOTPROTO=static
    DEFROUTE=yes
    NAME=em1
    DEVICE=em1
    ONBOOT=yes
    IPADDR=1.255.156.21
    NETMASK=255.255.255.224
    GATEWAY=1.255.156.1
    ETHTOOL_OPTS="-G ${DEVICE} rx 4096 ; -G {$DEVICE} tx 4096"

  • 재부팅시에 Network Ring Buffer Size 확대 명령어 실행 → rc-local.service 사용

    $ vi /etc/rc.local
    # 아래의 값이 등록
    ethtool -G eth1 rx 4096
    ethtool -G eth1 tx 4096



※ rc-local.service를 실행하는 방법

  1. rc-local.service를 실행할 수 있는 환경 적용
    $ echo -e "\n[Install]\nWantedBy=multi-user.target" >> /usr/lib/systemd/system/rc-local.service
    $ chmod u+x /etc/rc.d/rc.local
  1. rc-local.service 실행

    /bin/systemctl daemon-reload
    /bin/systemctl restart rc-local.service
    /bin/systemctl enable rc-local.service
    
    # rc-local.service가 rebooting에도 실행되는 지 확인
    /bin/systemctl list-unit-files | grep rc.local

  • TCP는 신뢰성 있는 통신을 위해 자신이 보낸 패킷에 대해 ACK 확인 → 보낸 쪽에서는 반드시 받는 쪽이 잘 받았다는 신호를 주어야 다음 전송을 하게 됨

  • TCP는 송신에 대한 응답이 오지않는 경우 일정시간을 대기 후에 다시 재전송함

  • 송신자는 패킷을 송신하고 나면 별도로 정의된 Timer를 시작 → Timer의 시간이 만료되면 다시 패킷을 전송


  • TCP Retransmission과 RTO(Retransmission Timeout)과 RTT(Round Trip Time)를 학습하는 이유

    1. 서버와 네트워크 장비의 성능이 좋은 환경에서는 사실 200ms의 RTO_MIN 값은 너무 큰 값일 수 있음
    2. RTO_MIN이 너무 작으면 재전송이 너무 자주 일어나게 됨 → 재전송이 자주 발생하면, 부하를 일으켜 시스템의 성능과 서비스 응답 속도에 악영향을 끼칠 수 있음
    3. 서버와 네트워크 환경에 따라서 RTO 값을 조절 필요


  • ACK 패킷 손실에 따른 재전송
    1. Client가 보낸 SYN에 대한 ACK를 받지 못하면 보냈던 SYN을 다시 보내게 됨
    2. Retransmission Timer라는 커널 타이머에 의해 동작 SYN 재전송 시간 결정

Retransmission 종류

  • Retransmission이 발생하는 3가지 상황에서 공통점 → "Timeout 시간 안에 ACK를 받지 못했다."
  • Retransmission이 발생하는 경우에 TCP Protocol이 할 수 있는 최선의 방법은 Timeout 시간을 정의하는 것
    1. Timeout 시간이 지나치게 짧으면 재전송 패킷의 낭비 발생
    2. Timeout 시간이 지나치게 길면 패킷이 손실되었을 때 에도 기다려야하는 시간이 너무 길어짐
  • Timeout 기간에 대한 기준을 정의할 때 적절한 기준 시간 → RTO(Rtransmission Timeout)라는 개념을 사용

1. Retransmission : Packet lost

  • 재전송이 발생할 수 있는 첫 번째 상황
  • 송신자는 패킷을 보냈지만, 어떤 이유에서인지 중간에 유실되어 수신측에 도달하지 못함
  • 수신측은 당연히 패킷이 도착하지 않았으므로 ACK를 보내줄리가 없고, 송신자의 Timer는 만료됨

2. Rertransmssion : ACK lost

  • 재전송이 발생할 수 있는 두 번째 상황
  • 송신자가 패킷을 보냈고, 수신 측 또한 응답으로 ACK를 보냄
  • 수신자의 ACK 응답이 중간에 유실되어 송신자에게 도착하지 못하고, 송신자의 Timer는 만료됨

3. Retransmission : Early Timeout

  • 재전송이 발생할 수 있는 세 번째 상황.
  • 송신자가 패킷을 보냈고, 수신 측에서 응답으로 ACK를 보냄
  • 네트워크 지연이 발생해서 송신자의 Timer가 만료되어버린 후에 ACK가 송신자에게 도착함


RTO(Retransmission Timeout)과 RTT(Round Trip Time)

  • RTO(Retransmission Timeout) → 타이머가 작동하는 시간을 의미

  • RTT(Round Trip Time) → 네트워크 통신을 하는 두 노드 간에 패킷이 전달되는데 소요된 시간을 의미

  • 종단 간 거리가 멀면 멀수록 RTT 값을 커지게 됨

  • RTT는 RTO의 값을 결정하는데 아주 중요한 요소가 됨


  • RTO는 내부적으로 복잡한 계산식을 거친 후 동적으로 설정 → RTT 값을 기반으로 계산함

    1. 두 종단 간 통신이 이루어지는 첫 번째 SYN 패킷에 대한 RTO는 어떻게 결정되는가?
    2. TCP Handshake를 하기 위한 첫 번째 SYN 패킷은 상대방과의 RTT에 대한 정보가 전혀 없기 때문에 계산이 불가능 → 지정된 값을 사용
    3. 최초의 RTO 값을 InitRTO이라고 부름 → OS 마다 값이 조금씩 다르지만, Linux에서는 1초로 되어 있음
    4. 1초 → 2초 → 4초 이런 식으로 InitRTO 값은 2의 제곱수로 늘어남
    5. RTO는 운영체제 커널에 정의 → 운영체제 별로 초기값을 갖고 있지만, 모든 네트워크 상황은 다르므로 RTO는 동적으로 변함
    6. RTO 참고 자료 → https://www.rfc-editor.org/rfc/rfc6298.txt

1. SRTT(SampleRTT)

  • RTO는 RTT에 의해서 변하는데, RTT는 호스트 간 송신에 대한 응답을 받기까지 걸리는 시간을 의미
  • SampleRTT를 측정된 RTT → 송신자의 Original Transmission에 대한 ACK를 받기까지의 시간을 SampleRTT로 설정
  • ACK 받기까지 시간이 오래 걸려 재전송이 발생했을 경우, 응답해온 ACK가 초기 응답인지 재전송에 대한 응답인지 구분하기가 어렵움 → Retranmission에 대한 ACK인지 송신자는 판단 불가

2. Karn / Partridge Algorithm

  • RTO Timer가 만료되면 RTO 값을 2배로 늘어남 → Exponential Backoff 라고함
  • SampleRTT는 송/수신이 완료된 전송에 대해서만 값을 기록
  • SampleRTT 값을 Computed RTO를 도출하는 데 사용



애플리케이션 타임아웃

  • InitRTO의 경우는 1초로 설정되어 있기 때문에 애플리케이션의 타임아웃은 1초보다 큰 값으로 설정 필요 (InitRTO는 TCP의 시간)
  • 세션이 연결된 상황에서 재전송은 RTT를 기반으로 생성되기 때문에 대부분 1초를 넘기지 않음
  • 세션이 연결되어 있지 않는 상황에서는 TCP Handshake를 맺는 과정에서의 재전송은 최소 1초 소요되기 때문에 애플리케이션에서 타임아웃을 1초로 설정한다면 재전송으로 커버 가능한 통신을 타임아웃 에러로 끊어 버리게 됨
  • 커넥션 풀 방식으로 네트워크 세션을 미리 만들어 두고 통신하는 경우에는 InitRTO가 발생할 일이 없기 때문에 더 작은 값으로 설정해도 됨


RTO_MIN

  • RTO 값에 영향을 주는 다른 요소 → RTO_MIN 값으로 RTO의 최솟값을 의미

  • 별도로 설정하지 않았다면, RTO_MIN의 기본 값은 200ms로 되어 있음 → RTT가 아무리 작아도 RTO값은 200ms 밑으로 내려갈 수 없음을 의미

  • RTO_MIN 변경 URL : https://unix.stackexchange.com/questions/210367/changing-the-tcp-rto-value-in-linux

  • 현재 설정되어 있는 RTO값 확인 → Linux에서는 ss -i 명령을 이용해서 현재 통신 중인 세션의 RTO 값을 확인 가능(RTO 값이 235)

    $ ss -i
    tcp    ESTAB      0      2384                                                        211.212.37.175:snapenetio                                                                 1.214.216.250:linktest-s
           cubic wscale:8,7 rto:235 rtt:34.161/18.592 ato:40 mss:1460 rcvmss:1168 advmss:1460 cwnd:8 ssthresh:7 bytes_acked:25341 bytes_received:11268 segs_out:275 segs_in:383 send 2.7Mbps lastsnd:2 lastrcv:6 lastack:6 pacing_rate 5.5Mbps unacked:8 retrans:0/1 rcv_space:29200

  • RTO_MIN 변경은 ip route 명령어를 통해 변경 → 100ms로 RTO_MIN을 변경한 후에 ss -i 명령으로 RTO 값이 변해있는 것을 확인(RTO값이 120)

    1. ip route 명령어를 통해 기존 게이트웨이 rto_min 확인
      $ ip route
      default via 211.212.37.161 dev eno3 proto static metric 100
      172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1
      192.168.122.0/24 dev virbr0 proto kernel scope link src 192.168.122.1
      211.212.37.160/27 dev eno3 proto kernel scope link src 211.212.37.175 metric 100
    2. ip route 명령어를 통해 기존 게이트웨이의 rto_min 변경
      # 형식 : ip route change default via <default gw> dev <ethernet> rto_min 100
      $ ip route change default via 211.212.37.161 dev eno3 proto static metric 100 rto_min 100ms
    3. ip route를 통해 게이트웨이 RTO_MIN 추가 확인
      $ ip route
      default via 211.212.37.161 dev eno3 proto static metric 100 rto_min lock 100ms
      172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1
      192.168.122.0/24 dev virbr0 proto kernel scope link src 192.168.122.1
      211.212.37.160/27 dev eno3 proto kernel scope link src 211.212.37.175 metric 100
    4. RTO_MIN이 100ms으로 변경하였기에 RTO 값이 기본 RTO_MIN값인 200ms보다 떨어져 있음을 확인
      $ ss -i
      tcp    ESTAB      0      1168                                                        211.212.37.175:snapenetio                                                                 1.214.216.250:linktest-s
        cubic wscale:8,7 rto:145 rtt:44.372/5.203 ato:40 mss:1460 rcvmss:1168 advmss:1460 cwnd:6 ssthresh:5 bytes_acked:103277 bytes_received:63924 segs_out:1502 segs_in:2153 send 1.6Mbps lastrcv:8 pacing_rate 3.2Mbps unacked:5 retrans:0/2 rcv_rtt:535816 rcv_space:29364

참고 URL : https://pcmc.tistory.com/entry/190618-TCP-Retransmission
참고 URL : https://brunch.co.kr/@alden/15

  • 이더넷 인터페이스 기능 중에 TOE(TCP offload engine)가 있음
  • TOE는 NIC(Network Interface Cards)에서 사용되는 기술로, 보통 운영체제 상에서 처리되는 TCP/IP 스택을 네트워크 컨트롤러로 내려서 처리
  • 네트워크 처리가 크게 필요한 기가비트나 10G 기가비트같은 고속네트워크에서 유용하게 사용
  • 처음 TCP는 저속의 네트워크 속도에서 이용되었는데, 지금의 통신속도와 비교해 보면 통신속도는 크게 증가
  • 시스템에서 동작할 수 있는 여유자원은 상당히 제한적


TCP 프로토콜을 처리하는데 있어서 오버헤드를 가져오는 요소

  1. 3 way 핸드쉐이크 과정을 통한 연결
  2. 체크섬, 시퀀스 넘버 계산
  3. Packet acknowledgement 와 congestion 제어를 위한 슬라이딩 윈도우 계산
  4. 연결 해제


TCP 프로토콜의 오버헤드 기능을 전용 하드웨어 구현 → TCP offload engine

  • CPU 는 다른 작업을 할 수 있도록 여유를 줌
  • 과거에는 몇몇 제한적인 NIC 만이 TOE 를 지원하였지만 지금은 대부분 NIC에서 TOE를 지원
  • TOE 구현은 오프로드 정도에 따라 부분적 오프로딩과 전체(Full offloading)오프로딩으로 구분
    1. 전체 오프로딩
      • TCP/IP 패킷을 처리하기 위한 TCP연결설정, 타임아웃,오류처리 등과 같이 TCP/IP 스택을 하드웨어로 구현
    2. 부분적 오프로딩
      • 체크섬 및 데이터 송수신 관련한 기능만을 구현
  • 일반적으로 TCP/IP 의 1bit/s 를 처리하는데는 CPU 1 헤르츠가 필요
  • 5Gbit/s (메가로 환산하면 625MB/s) 네트워크 트래픽을 처리하기 위해서는 5GHz CPU 가 필요
  • 5Gbit/s 를 처리하기 위해서는 2.5GHz 듀얼코어 CPU 가 필요
  • TOE 기능을 이용해 CPU 자원 사용을 줄여주게 되어 TCP/IP 처리가 많은 경우에는 TOE 를 지원하는 기능이 그렇지 않은 경우보다 더 많은 작업(시스템 관점에서)을 처리 가능


기본적으로 리눅스 커널에서는 TOE 하드웨어를 지원 X

  • 커널상에서 직접 지원하지 않는 이유
    1. 보안적 이슈 → TOE 펌웨어는 소스가 공개되어 있지 않아, 보안적으로 문제가 발생할 경우 해결할 방법이 없음
    2. 복잡성도 증가
    3. 하드웨어적인 여러 제약 발생

참고 : https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=hymne&logNo=220945826028

점보프레임(Jumbo Frame)

  • 기본적으로 사용하는 MTU(Maximum Transmission Unit) 값은 1500 바이트
  • 실제 전송시에는 프레임 헤더를 붙이면 18 바이트가 추가되어 기본적으로는 1518 바이트가 됨 → 이더넷 헤더가 18 바이트(Link Layer)
  • 점보프레임은 프레임 크기를 크게 늘려주는 것으로, 9000 바이트까지 MTU를 확장시켜 줌
  • 처음 이더넷 프레임으로 1500 바이트가 사용된 것은 과거 낮은 통신속도와 비교적 높게 발생되었던 에러 비율 때문임 → 데이터를 전송하다가 에러가 발생되면 단지 1500 바이트 부분만 에러를 정정하여 재전송하면 됨
  • 각 프레임은 네트워크 하드웨어와 소프트웨어적으로 프로세싱하는 것이 필요 → 프레임 사이즈가 커진다면 시스템에서 전송하기 위해 사용되는 처리 CPU가 더 줄어듬
  • 9000 바이트 이상으로 커진다면 데이터를 자르는 기준이 적어지므로 빠른 속도로 전달 가능 → 한번에 1500 바이트가 아닌 9000 바이트를 전송
  • 하드웨어 성능은 워낙 좋아졌기 때문에 점보프레임으로 인한 성능 향상은 그리 크지 않음
  • 점보프레임이 2000년대 초에는 어느 정도 효과를 볼 수 있었을지 몰라도 빠르게 발전하는 하드웨어 속도에 비하면 프레임을 나누기 위한 CPU 사용은 적음
  • 점보프레임(Jumbo Frame)을 사용할 때 효율적인 성능 향상를 얻을 수 있는 방법은 좋은 하드웨어를 사용하지 않는 것 → 현재 하드웨어 발전 상황에서는 큰 차이 X
  • 점보프레임을 사용하기 위해서는 스위치, NIC 카드등에서 지원 필요 → 요즘 사용되는 장비들은 대부분 지원


점보프레임이 설정된 패킷을 덤프

  • 데이터 크기가 8920 바이트나 되는 것을 확인 가능

시스템상에서의 설정은 MTU 값을 변경

  • 설정 전에는 MTU가 1500
  • 리눅스의 경우 커널 2.6.17 이상이면 지원됨
  • ip route 명령어를 통해 MTU 확인

    $ ip route get 111.6.0.25
    111.6.0.25 dev bond1  src 111.6.0.30
      cache  mtu 1500 advmss 1460 hoplimit 64

  • ifconfig 명령어를 통해 MTU 확인

    $ ifconfig eth6
    eth6      Link encap:Ethernet  HWaddr [삭제]
            UP BROADCAST RUNNING SLAVE MULTICAST  MTU:1500  Metric:1
            RX packets:2892 errors:0 dropped:0 overruns:0 frame:0
            TX packets:2728 errors:0 dropped:0 overruns:0 carrier:0
            collisions:0 txqueuelen:1000
            RX bytes:256450 (250.4 KiB)  TX bytes:2785780 (2.6 MiB)
            Interrupt:32 Memory:d8000000-d8012800

  • ifconfig 명령어를 통해 인터페이스의 MTU를 변경

    $ ifconfig eth6 mtu 9000

  • ip route 명령어를 통해 MTU 확인

    $ ip route get 111.6.0.25
    111.6.0.25 dev bond1  src 111.6.0.30
      cache  mtu 9000 advmss 8960 hoplimit 64

  • ifconfig 명령어를 통해 MTU 확인

    $ ifconfig eth6
    eth6      Link encap:Ethernet  HWaddr [삭제]
            UP BROADCAST RUNNING SLAVE MULTICAST  MTU:9000  Metric:1
            RX packets:2892 errors:0 dropped:0 overruns:0 frame:0
            TX packets:2728 errors:0 dropped:0 overruns:0 carrier:0
            collisions:0 txqueuelen:1000
            RX bytes:256450 (250.4 KiB)  TX bytes:2785780 (2.6 MiB)
            Interrupt:32 Memory:d8000000-d8012800



tcpdump 명령어로 트래픽을 캡쳐 설정

  • 캡쳐 크기를 늘려줘야 패킷을 캡처할 수 있기에, -s 옵션으로 9000 바이트를 지정
  • tcpdump 명령어는 기본 MTU이 1500임으로 점보 프레임을 패킷 캡처를 위해서는 MTU 9000으로 변경 필요
    $ tcpdump -i bond1 -s 9000 -n -w jumbo.pcap
    tcpdump: listening on bond1, link-type EN10MB (Ethernet), capture size 9000 bytes


tshark 명령어로 프레임 크기 확인

  • 1500 바이트 이상의 크기가 많이 보이는 것을 확인 가능
    $ tshark -i bond1 -e frame.len -Tfields
    9014
    9014
    66
    9014
    9014
    66
    9014
    430
    66
    114
    4210
    66
    114
    9014
    7550
    66
    114
    114
    66
    4210
    9014
    66
    9014
    9014
    66
    6038
    114
    9014
    ^C1947 packets captured


네트워크 인터페이스에 MTU=9000으로 지정

  • CentOS, 레드햇 계열은 /etc/sysconfig/network-script/ifcfg-eth0 파일에 MTU=9000으로 설정 추가 후 네트워크 재실행
    $ vi /etc/sysconfig/network-script/ifcfg-eth0
    # 중략
    # 맨 아래에 추가
    MTU=9000

참고 : http://www.packetinside.com/2012/03/jumbo-frame.html

  • 패킷 덤프를 하다보면 MTU 이상의 패킷 사이즈를 보는 경우가 있음
  • 10G 네트워크 링크에서 MTU가 1500 바이트이면 대략 초당 800,000개 패킷 통신 → 패킷처리를 위해 CPU 오버헤드를 발생 가능
  • 오버헤드를 줄이기 위해 MTU 증가 필요 → 점보프레임인 9000 바이트로 늘리면 한번에 더 큰 패킷을 처리 가능하지만, 인터넷에 연결되어 있는 모든 구간이 MTU 값을 9000 바이트로 사용 X
  • 보통 일반적인 네트워크는 MTU를 1500 바이트로 사용 → 내부 네트워크라면 모르지만 인터넷 구간에서는 현실적으로 점보프레임을 사용이 힘듬
  • 네트워크 어뎁터에서 TCP 세그먼테이션 오프로드 기능 사용→ TSO(TCP Segmentaion Offload)
  • TSO 호환 가능 어뎁터를 통해 커널상에서 아웃바운드 데이터(밖으로 패킷)를 보낼때 데이터를 재-세그먼트 하여 작게 보냄 → 호스트 컴퓨터의 CPU 자원 사용면에서도 효율적
  • TSO 외에 GSO(Generic segmentation offload)도 있음
  • GSO는 TCP에만 한정을 두지 않는다는 장점이 있지만 데이터 전송에서만 동작하고 받는쪽에서는 동작 X
  • 추가적으로 LRO(Large Receive Offload)도 있음 → 들어오는 패킷에 대해 한번에 Merge를 진행 (리눅스 기반의 10G 드라이버들이 LRO 를 넓게 사용)


TOE(TCP Offload Engine)이란

  • MTU 이상의 크기와 연관 있는 기능 → generic-receive-offload가 on

  • generic-receive-offload가 on으로 오프로드 기능이 동작하여 패킷을 덤프할 시에 패킷의 사이즈가 다르게 보임

  • GRO(generic-receive-offload)을 ON하면 네트워크 어뎁터에서 패킷을 처리해 주기에, 패킷 캡쳐 프로그램에서는 크기가 큰 패킷 사이즈를 확인 가능


  • ethtool 명령어에 -k 옵션을 통해 네트워크 인터페이스의 정보를 확인

    $ ethtool -k eth0
    Offload parameters for eth0:
    rx-checksumming: on
    tx-checksumming: off
    scatter-gather: off
    tcp-segmentation-offload: off
    udp-fragmentation-offload: off
    generic-segmentation-offload: off
    generic-receive-offload: on
    large-receive-offload: off
    rx-vlan-offload: on
    tx-vlan-offload: on
    ntuple-filters: off
    receive-hashing: off

1. GRO (generic-receive-offload) 기능 ON

  • GRO 기능이 On 되어 있는 경우는 아래 그림과 같이 패킷 사이즈가 2762, 4017, 3538 과 같이 MTU 값 이상으로 출력

2. GRO (generic-receive-offload) 기능 OFF

  • gro 기능을 off 하고 패킷 캡쳐를 다시 해보면 패킷 데이터 전송크기가 1414로 동일

    # ethtool -K eth0 gro off
    # ethtool -k eth0
    Offload parameters for eth0:
    rx-checksumming: on
    tx-checksumming: off
    scatter-gather: off
    tcp-segmentation-offload: off
    udp-fragmentation-offload: off
    generic-segmentation-offload: off
    generic-receive-offload: off
    large-receive-offload: off
    rx-vlan-offload: on
    tx-vlan-offload: on
    ntuple-filters: off
    receive-hashing: off

  • GRO 기능 off 그림


참고 : http://www.packetinside.com/2013/02/mtu-1500.html

+ Recent posts