한양과학기술고등학교

에듀테크 프로젝트 대시보드

스패닝 트리 프로토콜 (STP)

스패닝 트리 프로토콜 — 루프를 잘라 나무를 만든다

스위치를 여러 대 연결하다 보면 케이블이 고리(루프)를 만든다. 2계층에는 IP의 TTL 같은 "수명"이 없어서, 브로드캐스트 프레임 하나가 고리를 타고 영원히 돌며 복제된다 — 이게 브로드캐스트 폭풍이다. STP(IEEE 802.1D)는 물리적으로는 고리가 있어도, 일부 포트를 차단(Blocking)해서 논리적으로는 루프 없는 나무 한 그루만 남긴다.

한 줄 요약: ① 가장 "번호가 작은" 스위치를 루트 브리지로 뽑고 → ② 각 스위치는 루트로 가는 가장 싼 포트 1개(루트 포트)를 열고 → ③ 각 케이블 구간마다 대표 포트(지정 포트) 1개를 열고 → ④ 그 외 나머지 포트는 전부 막는다.

1. 왜 루프가 위험한가

스위치는 목적지를 모르는 프레임(브로드캐스트, 또는 아직 안 배운 유니캐스트)을 들어온 포트만 빼고 전부로 내보낸다(플러딩). 고리가 있으면 이 프레임이 옆 스위치로 갔다가 되돌아오고, 다시 뿌려지고… 끝이 없다. 게다가 지나갈 때마다 갈림길에서 2배, 4배로 불어난다. 몇 초 만에 회선이 꽉 차고 스위치 CPU가 마비된다.

2. 직접 해보기 — 스위치 4대, 케이블 5개

스위치 4대가 케이블 5개로 연결돼 있다(고리가 여러 개 있다). 각 스위치에는 브리지 ID(BID)가 있고, SW1은 우선순위를 4096으로 낮춰 놨다(나머지는 기본 32768). 아래에서 STP를 켜면 루트 선출 → 포트 역할 계산이 애니메이션으로 진행된다. 케이블의 동그란 버튼을 누르면 그 링크를 끊어(장애) STP가 어떻게 다시 수렴하는지 볼 수 있다.

R = 루트 포트 (열림) D = 지정 포트 (열림) B = 차단 포트 (막힘) 루트 브리지

스위치별 상태

스위치브리지 ID루트까지
비용
루트 포트

각 링크 비용 = 19 (100 Mbps 기준). 대각선 SW1–SW4 링크만 느린 회선이라 비용 100. 비용이 같으면 BID가 작은 쪽이 이긴다.

「STP 계산」을 눌러 루트 선출부터 포트 차단까지 관찰하세요.

3. 브리지 ID — 선거의 기준

루트 브리지는 BID가 가장 작은 스위치가 된다. BID는 이렇게 생겼다:

브리지 우선순위 (2바이트)확장 시스템 ID (VLAN)스위치 MAC 주소 (6바이트)
0 ~ 61440 (4096 단위)
기본값 32768
+ VLAN 번호공장에서 박힌 고유값
(동점일 때 최종 판정)
관리자가 "이 스위치를 루트로 쓰고 싶다"면 우선순위를 4096이나 0으로 낮춘다. 보통 망 중앙의 고성능 스위치를 루트로 지정해야, 트래픽이 이상하게 가장자리 스위치로 우회하지 않는다.

4. 포트 역할이 정해지는 순서

순서역할규칙상태
1루트 브리지BID 최소. 루트의 모든 포트는 지정 포트Forwarding
2루트 포트 (RP)루트가 아닌 스위치마다 1개. 루트까지 누적 비용 최소인 포트
(동점 → 상대 BID 최소 → 상대 포트번호 최소)
Forwarding
3지정 포트 (DP)케이블 구간(세그먼트)마다 1개. 그 구간에서 루트까지 비용이
더 낮은 쪽 포트 (동점 → BID 최소)
Forwarding
4비지정 / 대체 포트RP도 DP도 아닌 나머지 전부Blocking

5. 의사코드

def spanning_tree(switches, links):
    root = min(switches, key=lambda s: s.bid)      # 1) 루트 선출

    # 2) 루트로부터 최소 비용 트리 (BPDU가 퍼지며 계산됨)
    cost = {root: 0}
    for s in dijkstra_order(root, links):
        best = min(neighbors(s),
                   key=lambda n: (cost[n] + link_cost(s, n), n.bid, n.port))
        cost[s]      = cost[best] + link_cost(s, best)
        s.root_port  = port_to(s, best)          # Forwarding

    # 3) 세그먼트마다 지정 포트 하나
    for (a, b) in links:
        winner = a if (cost[a], a.bid) < (cost[b], b.bid) else b
        port_between(winner, other).role = "designated"   # Forwarding

    # 4) 나머지는 전부 차단
    for p in all_ports:
        if p.role not in ("root", "designated"):
            p.state = "BLOCKING"          # 프레임 안 보내고 안 받음(BPDU만 들음)

6. 포트가 열리기까지 — 그리고 RSTP

고전 STP에서 막혔던 포트가 갑자기 "열림"으로 바뀌면 안 된다. 잠깐이라도 루프가 생기기 때문이다. 그래서 단계를 밟는다:

상태하는 일기본 시간
BlockingBPDU만 듣는다. 데이터 X—
ListeningBPDU 주고받으며 역할 결정. 데이터 X15초
LearningMAC 주소 학습. 데이터 X15초
Forwarding정상 전송—

장애가 나면 최대 30~50초 멈춘다. 너무 느려서 나온 게 RSTP(802.1w) — 포트 타입(엣지/P2P) 구분과 능동적 핸드셰이크로 1초 이내에 수렴한다. 요즘 스위치의 "spanning-tree mode rstp"가 그것이고, VLAN마다 트리를 따로 두는 MSTP / PVST+도 널리 쓰인다.

이어서 보면 좋은 글: OSI 7계층 모델 · ARP · 최소 신장 트리 (MST) · 로드 밸런싱