포스트

10. Internet Engineering - Network Layer Data Plane (Final Review)

기말 정리 1: 포워딩과 라우팅, 데이터/제어 평면, 라우터 구조(LPM, TCAM, 스위칭 패브릭, HOL 차단, 버퍼와 스케줄링), IP 주소와 서브넷, CIDR, DHCP, NAT, 일반화된 포워딩과 미들박스. 서브넷 분할과 NAT 변환 테이블을 Python으로 실행

10. Internet Engineering - Network Layer Data Plane (Final Review)

학습 목표

  • 포워딩과 라우팅, 데이터 평면과 제어 평면의 차이
  • 인터넷의 최선형(best effort) 서비스 모델이 보장하지 않는 것
  • 라우터 내부 구조: 최장 프리픽스 매치, TCAM, 스위칭 패브릭, 입력/출력 큐잉, 패킷 스케줄링
  • IP 주소, 서브넷, CIDR, 계층적 주소 지정과 경로 집계
  • DHCP와 NAT의 동작과 장단점
  • 일반화된 포워딩(match + action), 미들박스, 인터넷의 설계 원칙

1. 네트워크 계층 개요

포워딩과 라우팅

구분포워딩 (forwarding)라우팅 (routing)
하는 일입력 링크에 들어온 패킷을 알맞은 출력 링크로 옮김출발지에서 목적지까지 패킷이 갈 경로를 결정
범위라우터 하나 안의 지역적 기능네트워크 전체의 전역적 기능
담당 평면데이터 평면제어 평면
비유교차로 하나를 지나는 일출발 전에 전체 여행 경로를 짜는 일
  • 데이터 평면은 포워딩 테이블에 적힌 대로 패킷을 옮기고, 제어 평면은 라우팅 알고리즘으로 그 테이블을 계산함
  • 제어 평면을 구현하는 방법은 두 가지
    • 라우터별 제어: 모든 라우터에 라우팅 알고리즘이 있고, 서로 메시지를 주고받으며 각자 테이블을 계산 (전통적 방식)
    • SDN: 원격 컨트롤러 하나가 테이블을 계산해서 각 라우터에 설치 (논리적 중앙 집중)

서비스 모델

  • 이론적으로 가능한 서비스: 전달 보장, 지연 상한 보장(예: 40 ms 이내), 순서 보장, 최소 대역폭 보장, 패킷 간격(jitter) 제한
  • 인터넷은 최선형(best effort): 위 어느 것도 보장하지 않음

(중요!!) best effort는 전달, 지연, 순서, 대역폭을 하나도 보장하지 않음. 단순한 덕분에 널리 퍼졌고, 부족한 부분(신뢰성, 혼잡 제어)은 종단의 TCP와 응용이 맡음


2. 라우터 내부 구조

라우터는 라우팅 프로세서(제어 평면, 소프트웨어, ms 단위)와 입력 포트, 스위칭 패브릭, 출력 포트(데이터 평면, 하드웨어, ns 단위)로 이루어짐.

입력 포트와 최장 프리픽스 매치

  • 입력 포트: 물리 계층(비트 수신) → 링크 계층(이더넷 등) → 조회와 포워딩
  • 각 입력 포트가 포워딩 테이블 사본을 가지고 직접 출력 포트를 찾는 분산 스위칭. 목표는 이 처리를 라인 속도로 끝내는 것
  • 목적지 기반 포워딩은 최장 프리픽스 매치를 씀: 목적지 주소와 맞는 프리픽스 중 가장 긴(가장 구체적인) 것을 고름
  • TCAM(Ternary Content Addressable Memory): 주소를 주고 내용을 찾는 일반 메모리와 반대로, 내용(목적지 주소)을 주면 맞는 엔트리를 한 클록에 병렬로 찾아 줌. 0, 1 말고 “상관없음(*)”도 저장할 수 있어 프리픽스 검색에 알맞음

스위칭 패브릭

입력 포트의 패킷을 출력 포트로 옮기는 부분. 입력이 N개면 라인 속도의 N배가 이상적인 스위칭 속도.

방식동작한계
메모리CPU가 패킷을 메모리에 복사했다가 출력 포트로 복사 (초기 라우터)패킷마다 메모리를 두 번 거치므로 메모리 대역폭에 묶임
버스공유 버스로 입력에서 출력으로 직접 전달한 번에 한 패킷만 → 버스 대역폭에 묶임
상호연결 네트워크크로스바 등 여러 경로를 둔 네트워크. 큰 패킷은 고정 크기 셀로 쪼개 보내고 출력에서 다시 합침입력과 출력이 겹치지 않으면 여러 패킷을 동시에 전달

입력 포트 큐잉과 HOL 차단

  • 스위칭 패브릭이 입력 포트들의 합친 속도보다 느리면 입력 큐에 패킷이 쌓임 → 지연, 버퍼 넘침으로 손실
  • HOL(Head-of-the-Line) 차단: 큐 맨 앞 패킷이 출력 포트 충돌로 기다리는 동안, 그 뒤의 패킷은 자기 출력 포트가 비어 있어도 못 나가는 현상

(중요!!) HOL 차단은 입력 포트 큐에서 생기는 문제. “출력 포트 큐에서만 발생한다”는 틀린 말

출력 포트 큐잉, 버퍼, 스케줄링

  • 패브릭에서 오는 속도가 링크 전송 속도보다 빠르면 출력 포트에 버퍼가 필요
  • 버퍼 크기: 경험 법칙은 \(RTT \times C\) (C는 링크 용량). 최근 권고는 흐름이 N개일 때 \(\frac{RTT \times C}{\sqrt{N}}\). 버퍼가 너무 크면 지연이 늘어 실시간 앱(게임, 화상 통화)에 나쁨
  • 드롭 정책: 버퍼가 꽉 찼을 때 무엇을 버릴지
    • tail drop: 새로 도착한 패킷을 버림
    • priority: 우선순위가 낮은 패킷부터 버림
  • 마킹: 버리기 전에 혼잡을 알리는 표시를 붙임 (ECN, RED)

패킷 스케줄링: 출력 링크로 다음에 보낼 패킷 고르기

방식동작
FCFS (FIFO)도착한 순서대로
우선순위클래스별 큐 중 가장 높은 우선순위 큐에 패킷이 있으면 그것부터. 같은 클래스 안에서는 FCFS
라운드 로빈(RR)클래스 큐를 차례로 돌며 하나씩
WFQ (가중 공정 큐잉)일반화된 RR. 클래스 i가 가중치 \(w_i\)에 비례하는 몫을 받아 최소 대역폭이 보장됨

WFQ에서 클래스 i가 받는 몫은 \(\frac{w_i}{\sum_j w_j}\)


3. IP 주소와 서브넷

IP 주소

  • 호스트나 라우터의 인터페이스마다 붙는 32비트 식별자. 라우터는 인터페이스가 여러 개라 주소도 여러 개
  • 점으로 구분한 10진수로 씀 (예: 223.1.1.1)

서브넷

  • 서브넷: 중간 라우터를 거치지 않고 서로 직접 닿을 수 있는 인터페이스들의 집합
  • 주소는 서브넷 부분(앞쪽 비트, 같은 서브넷이면 같음)과 호스트 부분(뒤쪽 비트)으로 나뉨
  • 223.1.3.0/24의 /24는 앞 24비트가 서브넷 부분이라는 뜻 (서브넷 마스크 255.255.255.0)

CIDR

  • 서브넷 부분의 길이를 아무 비트 수로나 정할 수 있게 한 방식. 형식은 a.b.c.d/x
  • 예: 200.23.16.0/23은 앞 23비트가 서브넷 부분
  • 예전의 클래스 주소(A, B, C 클래스의 /8, /16, /24)보다 주소를 낭비 없이 나눌 수 있음

주소는 어떻게 받는가

  • 호스트의 주소: 관리자가 직접 설정하거나 DHCP로 자동으로 받음
  • 네트워크의 주소(서브넷 부분): ISP가 가진 주소 블록의 일부를 받음
  • ISP는 ICANN이 관리하는 주소 공간을 지역 인터넷 등록 기관(RIR)을 통해 받음

서브넷 분할과 경로 집계 (실행)

ISP가 200.23.16.0/20을 받아 고객 8곳에 /23씩 나눠 주는 예. 3비트를 더 쓰므로 \(2^3 = 8\)개 블록이 나옴.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import ipaddress

# 1) ISP 블록 200.23.16.0/20을 /23 블록 8개로 나누기
isp = ipaddress.ip_network("200.23.16.0/20")
print(f"ISP 블록 {isp}: 주소 {isp.num_addresses}개 (호스트 부분 {32 - isp.prefixlen}비트)")
for i, sub in enumerate(isp.subnets(new_prefix=23)):
    print(f"  Organization {i}: {sub}  ({sub[0]} ~ {sub[-1]}, 사용 가능 호스트 {sub.num_addresses - 2}개)")

# 2) 서브넷 마스크와 네트워크/브로드캐스트 주소
net = ipaddress.ip_network("223.1.3.0/24")
print(f"\n{net}: netmask {net.netmask}, network {net.network_address}, broadcast {net.broadcast_address}")
print("223.1.3.27이 이 서브넷에 속하는가?", ipaddress.ip_address("223.1.3.27") in net)

# 3) 경로 집계와 더 구체적인 경로: 최장 프리픽스 매치로 다음 ISP 결정
routes = {
    ipaddress.ip_network("200.23.16.0/20"): "Fly-By-Night-ISP",   # 집계된 경로
    ipaddress.ip_network("199.31.0.0/16"):  "ISPs-R-Us",
    ipaddress.ip_network("200.23.18.0/23"): "ISPs-R-Us",          # Organization 1이 옮겨 간 뒤 광고된 더 구체적인 경로
}
def lookup(dst):
    dst = ipaddress.ip_address(dst)
    match = max((n for n in routes if dst in n), key=lambda n: n.prefixlen, default=None)
    return f"{dst} -> {match} -> {routes[match] if match else 'no route'}"
for d in ["200.23.20.5", "200.23.18.77", "199.31.4.1"]:
    print(lookup(d))

실행 결과

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
ISP 블록 200.23.16.0/20: 주소 4096개 (호스트 부분 12비트)
  Organization 0: 200.23.16.0/23  (200.23.16.0 ~ 200.23.17.255, 사용 가능 호스트 510개)
  Organization 1: 200.23.18.0/23  (200.23.18.0 ~ 200.23.19.255, 사용 가능 호스트 510개)
  Organization 2: 200.23.20.0/23  (200.23.20.0 ~ 200.23.21.255, 사용 가능 호스트 510개)
  Organization 3: 200.23.22.0/23  (200.23.22.0 ~ 200.23.23.255, 사용 가능 호스트 510개)
  Organization 4: 200.23.24.0/23  (200.23.24.0 ~ 200.23.25.255, 사용 가능 호스트 510개)
  Organization 5: 200.23.26.0/23  (200.23.26.0 ~ 200.23.27.255, 사용 가능 호스트 510개)
  Organization 6: 200.23.28.0/23  (200.23.28.0 ~ 200.23.29.255, 사용 가능 호스트 510개)
  Organization 7: 200.23.30.0/23  (200.23.30.0 ~ 200.23.31.255, 사용 가능 호스트 510개)

223.1.3.0/24: netmask 255.255.255.0, network 223.1.3.0, broadcast 223.1.3.255
223.1.3.27이 이 서브넷에 속하는가? True
200.23.20.5 -> 200.23.16.0/20 -> Fly-By-Night-ISP
200.23.18.77 -> 200.23.18.0/23 -> ISPs-R-Us
199.31.4.1 -> 199.31.0.0/16 -> ISPs-R-Us
  • /20 블록은 호스트 부분이 12비트라 \(2^{12} = 4096\)개 주소
  • /23 블록 하나는 \(2^9 = 512\)개 주소이고, 첫 주소(네트워크 주소)와 마지막 주소(브로드캐스트 주소)를 빼면 호스트에 쓸 수 있는 주소는 510개
  • 경로 집계: ISP는 고객 8곳의 블록을 따로 광고하지 않고 200.23.16.0/20 하나로 묶어 광고함 → 전 세계 라우팅 테이블이 작아짐
  • 더 구체적인 경로: Organization 1이 다른 ISP로 옮겨도 주소를 유지하려면, 새 ISP가 200.23.18.0/23을 광고함. 최장 프리픽스 매치 덕분에 그 주소로 가는 트래픽만 새 ISP로 감

4. DHCP

  • 호스트가 네트워크에 들어올 때 서버에서 IP 주소를 동적으로 받는 프로토콜. “플러그 앤 플레이”, 주소 재사용, 이동하는 사용자 지원
  • 4단계: Discover → Offer → Request → ACK (Discover와 Offer는 선택 사항)
  • Discover와 Request는 출발지 0.0.0.0, 목적지 255.255.255.255로 브로드캐스트
  • IP 주소 외에 첫 번째 홉 라우터 주소, DNS 서버 주소, 서브넷 마스크도 함께 받음
  • UDP 위에서 동작 (서버 67, 클라이언트 68)

강의 그림은 Offer와 ACK도 255.255.255.255로 보내지만, Wireshark 실습의 실제 트레이스에서는 클라이언트에게 줄 주소로 유니캐스트되었음. 클라이언트가 보낸 Bootp flags(브로드캐스트 비트)에 따라 서버가 정함


5. NAT

  • 사설 네트워크의 여러 장치가 공인 IPv4 주소 하나를 같이 쓰게 해 주는 기술
  • 사설 주소 대역: 10/8, 172.16/12, 192.168/16. 로컬 네트워크 안에서만 의미가 있음
  • 나가는 패킷: (출발지 IP, 포트)를 (NAT 공인 IP, 새 포트)로 바꾸고 그 쌍을 NAT 변환 테이블에 기록
  • 들어오는 패킷: 목적지 (공인 IP, 포트)를 테이블에서 찾아 원래 (사설 IP, 포트)로 바꿔서 전달

NAT 변환 테이블 (실행)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import itertools

class NAT:
    """NAT 라우터의 변환 테이블: (사설 IP, 포트) <-> (공인 IP, 새 포트)"""
    def __init__(self, public_ip, first_port=5001):
        self.public_ip = public_ip
        self.ports = itertools.count(first_port)
        self.out_map, self.in_map = {}, {}

    def outgoing(self, src, dst):
        if src not in self.out_map:                       # 처음 나가는 연결이면 새 포트 배정
            pub = (self.public_ip, next(self.ports))
            self.out_map[src], self.in_map[pub] = pub, src
        return self.out_map[src], dst                     # 출발지만 바뀜

    def incoming(self, src, dst):
        if dst not in self.in_map:                        # 테이블에 없으면 버림
            return None
        return src, self.in_map[dst]                      # 목적지만 원래대로 복구

nat = NAT("138.76.29.7")
s1 = nat.outgoing(("10.0.0.1", 3345), ("128.119.40.186", 80))
s2 = nat.outgoing(("10.0.0.2", 3345), ("128.119.40.186", 80))   # 같은 포트를 쓰는 다른 내부 호스트
print("나가는 패킷 1:", s1)
print("나가는 패킷 2:", s2)
print("들어오는 응답 :", nat.incoming(("128.119.40.186", 80), ("138.76.29.7", 5001)))
print("요청 없이 들어온 패킷:", nat.incoming(("1.2.3.4", 4444), ("138.76.29.7", 8080)))
print("변환 테이블:", nat.out_map)

실행 결과

1
2
3
4
5
나가는 패킷 1: (('138.76.29.7', 5001), ('128.119.40.186', 80))
나가는 패킷 2: (('138.76.29.7', 5002), ('128.119.40.186', 80))
들어오는 응답 : (('128.119.40.186', 80), ('10.0.0.1', 3345))
요청 없이 들어온 패킷: None
변환 테이블: {('10.0.0.1', 3345): ('138.76.29.7', 5001), ('10.0.0.2', 3345): ('138.76.29.7', 5002)}
  • 내부 호스트 두 대가 같은 포트(3345)를 써도 NAT가 서로 다른 공인 포트(5001, 5002)를 붙여 구분함
  • 테이블에 없는 연결로 들어온 패킷은 전달할 곳이 없음 → 외부에서 내부 서버로 먼저 연결하기 어려움(NAT 통과 문제). 동시에 외부에서 내부를 직접 볼 수 없어 보안상 이점이 되기도 함
장점논란(단점)
ISP에서 공인 주소 하나만 받으면 됨라우터는 3계층까지만 처리해야 하는데 포트 번호(4계층)를 바꿈
외부에 알리지 않고 내부 주소를 바꿀 수 있음종단 간(end-to-end) 원칙 위반
ISP를 바꿔도 내부 주소는 그대로주소 부족은 IPv6로 풀어야 한다는 주장
내부 장치가 외부에 직접 노출되지 않음NAT 뒤의 서버에 외부에서 접속하기 어려움

현실적으로는 가정, 기업, 4G/5G 셀룰러 네트워크에서 널리 쓰이고 있음.


6. 일반화된 포워딩과 미들박스

match + action

  • 패킷 헤더의 비트를 매치하고, 결과에 따라 액션을 수행한다는 추상화
  • 목적지 기반 포워딩은 “목적지 IP만 매치하고 포워딩만 하는” 특수한 경우
  • 일반화된 포워딩: 링크, 네트워크, 전송 계층의 여러 헤더 필드를 매치하고, 다양한 액션을 수행
플로우 테이블 항목내용
매치헤더 필드 값의 패턴 (스위치 포트, MAC 주소, IP 주소, TCP/UDP 포트 등)
액션drop, forward, modify, copy, 컨트롤러로 전송
우선순위패턴이 겹칠 때 어느 규칙을 먼저 쓸지
카운터처리한 바이트 수, 패킷 수
  • 이 하나의 추상화로 라우터(목적지 IP 매치 → 포워딩), 스위치(MAC 매치 → 포워딩), 방화벽(포트 매치 → 차단), NAT(IP와 포트 매치 → 수정)를 모두 표현할 수 있음
  • 대표적인 예가 OpenFlow. 네트워크 전체 동작을 프로그래밍할 수 있게 해 줌

미들박스

  • 정의(RFC 3234): 출발지와 목적지 사이 경로에서 IP 라우터의 표준 기능 외의 일을 하는 모든 장치
  • 예: NAT, 방화벽, IDS(침입 탐지), 로드 밸런서, 캐시, 응용별 미들박스
  • 변화: 전용 하드웨어 → 개방형 API를 갖춘 화이트박스 하드웨어, SDN으로 중앙에서 관리, NFV(네트워크 기능 가상화)로 일반 서버에서 소프트웨어로 실행

인터넷의 설계 원칙

  • RFC 1958: 목표는 연결성, 도구는 IP, 지능은 네트워크 안이 아니라 종단에 있음
  • IP는 모든 것을 잇는 “얇은 허리(thin waist)”
  • 종단 간 원칙: 어떤 기능이 종단 응용의 지식으로만 완전하고 정확하게 구현될 수 있다면, 그 기능을 네트워크 안에 넣는 것으로는 완전하게 제공할 수 없음. 그래서 신뢰성 있는 전송, 혼잡 제어는 종단(TCP)에서 함

정리 노트에서 정정한 부분 정리

위치원래정정이유
데이터 플레인라우팅 테이블에 적힌 대로 포워딩포워딩 테이블에 적힌 대로 포워딩데이터 평면이 보는 것은 포워딩 테이블
DHCP 과정Offer의 목적지는 255.255.255.255브로드캐스트일 수도, 유니캐스트일 수도 있음실습 트레이스에서는 유니캐스트였음 (Bootp flags에 따라 결정)
종단 간 원칙통신은 간단하고 그 안에서 완벽하게 구현되어야 함네트워크 안은 단순하게 두고, 완전한 기능은 종단에서 구현종단 간 원칙은 네트워크 안에서 완벽하게 구현할 수 없다는 주장
메모리 스위칭메모리 대역폭에 의해 속도가 제한패킷마다 메모리를 두 번(쓰기, 읽기) 거쳐서 제한됨원인 보충

최종 핵심 정리

  1. 포워딩은 라우터 안의 지역적 기능(데이터 평면), 라우팅은 네트워크 전체의 경로 결정(제어 평면)
  2. 인터넷은 best effort라서 전달, 지연, 순서, 대역폭을 하나도 보장하지 않음
  3. 목적지 기반 포워딩은 최장 프리픽스 매치를 쓰고, TCAM으로 한 클록에 병렬 검색함
  4. 스위칭 패브릭은 메모리, 버스, 상호연결 네트워크 방식이 있고, HOL 차단은 입력 포트 큐에서 생김
  5. 출력 버퍼가 차면 tail drop이나 우선순위로 버리고, 스케줄링은 FCFS, 우선순위, RR, WFQ(최소 대역폭 보장)가 있음
  6. CIDR a.b.c.d/x는 앞 x비트가 서브넷 부분이고, ISP는 큰 블록을 나눠 주고 하나로 묶어 광고함(경로 집계)
  7. DHCP는 Discover, Offer, Request, ACK로 주소, 게이트웨이, DNS, 마스크를 주고, NAT는 (IP, 포트) 변환 테이블로 공인 주소 하나를 공유하게 함
  8. 일반화된 포워딩(match + action)은 라우터, 스위치, 방화벽, NAT를 하나의 추상화로 묶고, OpenFlow와 SDN의 바탕이 됨

End.

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.