May 19, 2025

[NetWork] RX #3 네트워크 스택

[NetWork] RX #3  네트워크 스택

NIC에서 받은 정보로 산출된 sk_buff는 패킷단위로 되어 있습니다. 이 패킷을 네트워크의 스택에 따라 진행합니다.

GRO

napi_gro_receive() 가 작은 패킷들을 묶어 큰 패킷으로 합칩니다. 이름에서 알 수 있듯이 NAPI poll 함수 안에서 실행되지만, skb를 만들고 나서 실행됩니다.

skb를 하나 만든 뒤 GRO 엔진이 연속된 flow(IP,TCP,SEQ 등)을 확인해서 "super skb - gro_list"에 페이로드를 붙이고 새로운 skb를 바로 free 합니다.
(skb의 수를 줄입니다. 드라이버가 skb 없이 직접 하기도 합니다. )

GRO는 항상 호출하나요?

    1. 일단 GRO의 옵션을 끌 수 있습니다.
      GRO를 키면 MTU보다 큰 패킷을 TCPDUMP에서 볼 수 있습니다.

LRO는 또 뭔가요?

    1. LRO는 기본적으로 TCP 전용으로 NIC에서 직접 패킷을 합쳐서 올립니다.
      GRO는 UDP도 포함됩니다.
    2. NIC에서 하다보니 패킷 캡쳐 시 원본 세그먼트가 사라집니다.
      (라우터/브리지/VPN 재태그 같은 L2/L3 재가공 시 사용 불가합니다. )

L2 - RPS (Receive Packet Steering)

RPS

NIC의 RX 큐가 1개일 경우에는 1개의 CPU만 계속 사용합니다.
RSS로 분산이 될 때에는 큐마다 다른 CPU에 고정됩니다.

RPS를 켜게 되면 패킷마다 hash 값으로 같은 flow는 같은 cpu로 패킷을 보내 캐시 히트율을 높일 수 있습니다.

헤더 파싱

L2 Ethernet 헤더 길이만큼 포인터를 앞당겨서 L3부터 보게 합니다.
802.1Q VLAN 태그가 있으면 제거합니다.

AF_PACKET, tcpdump,eBPF,XDP 등 등록된 과정이 지금 단계에서 복사하거나 hook이 실행됩니다.

L3 - PRE_Routing

Pre Routing

네트워크 스택에는 "훅(hook)" 이 있습니다. 패킷이 각 시점에서 hook에 따라 패킷이 처리됩니다. Pre Routing은 L2 처리를 끝내고 라우팅 직전에 하는 처리들을 진행합니다.

넷필터 테이블에서 훅과 이름이 같은 체인 등을 읽고 주소, 포트를 바꾸거나 라벨, NotTrack을 설정합니다.

hook은 어떤 게 있나요?

    1. 5개의 표준 훅이 하드코딩되어 있습니다.
      (PREROUTING, INPUT, FORWARD, OUTPUT, POSTROUTING)
    2. 하드코딩 되어 있어, 새 훅 포인트를 만드려면 커널 코드 패치가 필요합니다.
      1. PREROUTING ip_rcv(): L2 처리를 끝낸 시점
      2. INPUT ip_local_deliver(): 목적지가 로컬로 라우팅 후
      3. FORWARD ip_forward(): 다른 호스트로 라우팅 된 패킷
      4. OUTPUT __ip_local_out(): 내부에서 직접 만든 패킷
      5. POSTROUTING ip_output(): ARP 완료 후 송출 직전

테이블은 어떤 게 있나요?

    1. raw -> mangle -> nat -> filter -> security 로 있고, 정해진 순서로 스캔합니다.
    2. 모든 테이블에 모든 체인이 연결되어 있지는 않습니다.
      1. raw: Conntrack 전 패킷 패스/우회 결정
      2. mangle: 헤더 조작/마킹/TTL/DSCP 수정
        └(모든 패킷 검사)
        ┌(한 커넥션 첫 패킷만 검사 최적화)
      1. nat: 주소/포트 변환(DNAT/SNAT/REDIRECT)
      1. filter: 패킷 허용/차단
      2. security: LSM 정책 매핑

훅을 추가하고 싶어요.

    1. 새 훅 포인트는 만들 수 없지만, 훅에 새로운 규칙, 모듈 을 등록하면서 우선순위를 제공할 수 있습니다.

Conntrack(Connection Tracking)

Pre Routing이 끝나면 Conntrack가 실행됩니다. Conntrack는 훅이 아니라 callback과 같은 개념의 함수입니다.

L3

IP 헤더 무결성 -> PREROUTING 이후에 라우팅된(DNAT 처리) 패킷으로 처리를 진행합니다.

  1. TTL-1 / Hop Limit-1 (0이면 ICMP 를 발송합니다. )
  2. IP 옵션(RR,TS,SEC) 해석 & 검증
  3. ip 재조립
  4. 최종 행선지 결정
  5. ICMP/오류 제어
  6. 포워딩 or 재전송

INPUT / FORWARD

목적지가 로컬일 경우에는 INPUT 훅이 작동하고, 목적지가 원격일 경우에는 FORWARD -> POSTROUTING으로 처리 된 후 L4 스택이 작동하지 않습니다.

L4

ip_local_deliver()가 IP Protocol(6=TCP, 17=UDP …)을 보고 L4 핸들러를 호출합니다. 서버가 호출한 소캣 큐에 skb를 넣습니다.

socket < bind < listen 으로 호출이 진행됩니다.
socket(): 빈 socket을 생성합니다.
bind(): 생성한 socket에 ip, port 필드를 입력합니다. (bind-hash)
listen(): SYN 패킷 전용 큐를 생성합니다.
connect(): 소켓에 5-tuple 완성

UDP는 bind까지만 해도 수신이 가능하지만, TCP는 listen까지 하지 않으면 받지 못합니다.

마무리

NIC에서 받은 sk_buff를 처리하는 네트워크 스택을 알아봤습니다.
개념이 부족하다 보니 L2에서 헤더 파싱하고, PREROUTRING하고, L3 하고 L4까지 알아야 할 것들이 매우 많더군요.

알아가면서 최대한 위의 Layer로 넘기지 않고, 밑에서 처리할 수 있는 부분은 직접 처리하고자 하는 느낌을 받았습니다. 처음에는 eBPF를 알기 위해서 시작했는데, 마찬가지 이겠지요.

Comments