SCTP
고친 사람 github-actions[bot]
SCTP 는 한 연결 안에 줄을 여러 개 두고 메시지를 나르는 전송 프로토콜입니다. 한 줄에서 패킷 하나가 사라져도 나머지 줄은 멈추지 않고 갑니다. 연결 하나에 주소를 여러 개 걸어 둡니다. 쓰던 길이 끊기면 걸어 둔 다른 주소로 옮겨 갑니다.
쉽고 빠른 이해
무슨 일을 하나 — 보낸 메시지를 잃지 않고 순서대로 건네줍니다. 그 순서를 지키는 줄을 여러 개 둡니다. 채팅 프로그램이 파일 전송과 대화 메시지를 한 연결 안의 다른 줄에 실어 서로 안 막게 하는 식입니다.
왜 이렇게 하나 — 줄이 하나뿐이면 맨 앞 조각이 사라졌을 때 뒤에 도착한 것들이 다 기다립니다. 받는 쪽이 순서를 지켜 올려 주기로 했으니 앞을 비워 둘 수 없기 때문입니다. 줄을 갈라 두면 기다리는 범위가 그 줄 하나로 줄어듭니다.
어떻게 도나
- 양쪽이 메시지를 네 번 주고받아 연결을 세웁니다. 이때 서로가 쓸 수 있는 주소 목록도 함께 넘깁니다
- 보낼 메시지마다 어느 줄에 실을지 고릅니다. 받는 쪽은 줄마다 따로 순서를 맞춰 올려 줍니다
- 쓰던 길이 답을 안 하면 연결을 유지한 채 넘겨받은 다른 주소로 보내기 시작합니다
대가 — 중간 장비가 이 프로토콜을 모르면 패킷을 통과시키지 않고 버립니다. 그래서 공개 인터넷에서는 널리 통하는 다른 전송 프로토콜 안에 한 번 싸서 보내는 방법을 씁니다. 그만큼 헤더가 늘어납니다.
상세
SCTP(Stream Control Transmission Protocol, 스트림 제어 전송 프로토콜)는 메시지를 잃지 않고 순서대로 건네주는 전송 프로토콜입니다. TCP(Transmission Control Protocol, 전송 제어 프로토콜)가 하는 일과 여기까지는 같습니다. 다른 점은 순서를 지키는 범위입니다. TCP 는 연결 하나에 줄이 하나지만, SCTP 는 한 연결 안에 줄을 여러 개 둡니다.
사무실 사이를 오가는 운반차로 치면 TCP 는 짐칸이 하나입니다. 앞에 실린 상자가 빠지면 뒤 상자도 같이 멈춰 섭니다. SCTP 는 같은 차에 칸막이를 세워 짐칸을 여럿으로 나눕니다. 한 칸이 막혀도 다른 칸의 짐은 내려집니다.
주소 목록을 함께 잡는 결합
SCTP 가 세우는 연결을 결합(association)이라고 부릅니다. 결합 하나는 양쪽의 포트 한 쌍과 주소 목록으로 정해집니다. 주소가 하나가 아니라 목록인 것이 TCP 와 갈리는 첫 대목입니다.
TCP 연결은 IP(Internet Protocol, 인터넷 프로토콜) 주소 하나와 포트 하나를 양쪽에서 짝지어 잡습니다. 그 주소가 달린 회선이 죽으면 연결도 같이 끝납니다. 통신을 이어 가려면 응용 프로그램이 연결을 새로 맺어야 합니다.
SCTP 는 결합을 열 때 자기가 쓸 수 있는 주소를 상대에게 다 알려 줍니다. 그래서 주소 하나가 죽어도 결합 자체는 살아 있습니다. 보내는 쪽이 목록에 있는 다른 주소로 옮겨 보내면 됩니다. 이렇게 주소를 여러 개 물고 있는 것을 멀티호밍이라고 합니다.
flowchart TD
subgraph A["결합 하나"]
direction TB
L["여는 쪽 · 주소 목록"]
R["받는 쪽 · 주소 목록"]
L -->|"주 경로"| R
L -.->|"예비 경로"| R
end
목록에 오른 주소 가운데 평소 데이터가 나가는 길 하나가 주 경로입니다. 나머지는 예비 경로로 남아 있다가 주 경로가 죽었을 때 대신 쓰입니다.
결합을 여는 네 번의 주고받기
결합을 열 때는 메시지가 네 번 오갑니다. 받는 쪽이 세 번째 메시지를 받기 전까지는 아무것도 기억해 두지 않는다는 것이 이 절차의 요점입니다.
sequenceDiagram
participant 여는쪽
participant 받는쪽
여는쪽->>받는쪽: INIT · 내 주소 목록과 검증 태그
받는쪽->>여는쪽: INIT ACK · 쿠키와 내 주소 목록
Note over 받는쪽: 아직 결합을 만들지 않는다
여는쪽->>받는쪽: COOKIE ECHO · 받은 쿠키 되보냄
받는쪽->>여는쪽: COOKIE ACK · 결합 열림
그림의 네 메시지가 하는 일은 이렇습니다. INIT(Initiation, 결합을 열자는 요청)은 여는 쪽의 주소 목록과 검증 태그를 싣습니다. 검증 태그는 이 결합을 가리키는 표식입니다. 뒤에 오는 패킷이 이 결합의 것인지 가리는 데 씁니다.
INIT ACK(Initiation Acknowledgement, 시작 확인)은 받는 쪽의 주소 목록과 함께 쿠키를 돌려줍니다. 쿠키는 결합에 필요한 정보를 받는 쪽이 자기만 알아볼 수 있게 묶어 만든 값입니다.
받는 쪽은 기억해 둘 것을 이 쿠키에 담아 상대에게 보내 버립니다. 자기 메모리에는 아무것도 남기지 않습니다.
여는 쪽이 COOKIE ECHO(Cookie Echo, 쿠키 되보내기)로 그 값을 그대로 돌려주면, 받는 쪽은 쿠키를 풀어 결합을 만듭니다. 그리고 COOKIE ACK(Cookie Acknowledgement, 쿠키 확인)으로 답합니다. 쿠키가 돌아왔다는 것은 여는 쪽이 자기가 적어 낸 주소로 실제로 답을 받고 있다는 뜻입니다.
TCP 는 연결을 열 때 메시지를 세 번 주고받습니다. SCTP 는 그에 견주어 왕복이 한 번 더 있습니다. 그 한 번 덕분에 남의 주소를 적어 넣은 연결 요청을 쏟아 부어도 받는 쪽 메모리가 안 찹니다.
sequenceDiagram
participant 공격자
participant 받는쪽
participant 남의주소
공격자->>받는쪽: INIT · 남의 주소를 적어 넣는다
받는쪽->>남의주소: INIT ACK · 쿠키
Note over 받는쪽: 아무것도 기억하지 않는다
Note over 남의주소: 되보내 줄 쪽이 없어 여기서 끝난다
쿠키는 공격자가 적어 낸 남의 주소로 날아갑니다. 그 주소에는 되보내 줄 쪽이 없어 거기서 끝납니다. 받는 쪽은 쿠키가 돌아오기 전까지 아무것도 기억하지 않으므로 쌓이는 것이 없습니다.
TCP 는 연결을 열자는 첫 신호를 SYN(Synchronize, 순서 맞춤)이라고 부릅니다. 이 신호만 쏟아 부어 상대 메모리를 채우는 공격이 SYN 플러드입니다. SCTP 의 쿠키가 막는 것이 그 수법입니다.
메시지 경계를 지키는 전달
TCP 는 보낸 데이터를 바이트가 줄줄이 이어진 흐름 하나로 다룹니다. 보내는 쪽이 백 바이트를 세 번 썼어도 받는 쪽은 삼백 바이트 덩어리 하나로 읽을 수 있습니다. 어디까지가 한 메시지인지는 응용 프로그램이 길이를 붙여 스스로 표시해야 합니다.
SCTP 는 UDP(User Datagram Protocol, 사용자 데이터그램 프로토콜)처럼 메시지 단위로 주고받습니다. 보낸 쪽이 한 번에 넘긴 덩어리는 받는 쪽에도 한 덩어리로 올라옵니다. 이 성질을 메시지 경계가 보존된다고 말합니다.
flowchart TD
subgraph W["보낸 쪽 · 백 바이트를 세 번 썼다"]
direction TB
A1["덩어리 1 · 백 바이트"]
A2["덩어리 2 · 백 바이트"]
A3["덩어리 3 · 백 바이트"]
end
subgraph T["TCP 로 받은 쪽 · 메시지 경계가 사라진다"]
direction TB
B1["삼백 바이트 한 덩어리"]
end
subgraph S["SCTP 로 받은 쪽 · 메시지 경계가 남는다"]
direction TB
C1["덩어리 1 · 백 바이트"]
C2["덩어리 2 · 백 바이트"]
C3["덩어리 3 · 백 바이트"]
end
W --> T
W --> S
같은 세 번의 쓰기가 TCP 에서는 한 덩어리로 뭉쳐 올라옵니다. SCTP 에서는 쓴 모양 그대로 세 덩어리로 올라옵니다.
한 패킷에 안 들어가는 큰 메시지는 SCTP 가 여러 조각으로 잘라 보냅니다. 받는 쪽은 조각이 다 모이면 원래 메시지로 붙여 한 번에 올립니다. 자르고 붙이는 일을 응용 프로그램이 안 해도 됩니다.
순서를 지키는 범위를 가르는 스트림
앞에서 줄이라고 부른 것이 스트림입니다. 결합 하나 안에는 스트림이 여러 개 있습니다. 스트림은 순서를 지켜야 하는 범위 하나입니다. 보내는 쪽은 메시지마다 어느 스트림에 실을지 번호로 고릅니다.
받는 쪽은 스트림마다 따로 순서를 맞춥니다. 그래서 한 스트림에서 조각이 사라져도 다른 스트림의 메시지는 기다리지 않고 응용 프로그램으로 올라갑니다.
번호 두 가지가 이 일을 나눠 맡습니다. 전송 순서 번호는 결합 전체에 하나씩 붙어 무엇이 사라졌는지 가려냅니다. 스트림 순서 번호는 스트림 안에서 올려 줄 차례를 정합니다.
flowchart TD
subgraph A["결합 하나 · 전송 순서 번호는 여기에 하나씩 붙는다"]
subgraph S0["스트림 0 · 대화 메시지"]
C10["전송 순서 번호 10 · 스트림 순서 번호 0"]
C13["전송 순서 번호 13 · 스트림 순서 번호 1"]
end
subgraph S1["스트림 1 · 파일 조각"]
C11["전송 순서 번호 11 · 스트림 순서 번호 0 · 사라짐"]
C12["전송 순서 번호 12 · 스트림 순서 번호 1"]
end
subgraph S2["스트림 2 · 상태 갱신"]
C14["전송 순서 번호 14 · 스트림 순서 번호 0"]
end
end
S0 --> U0["바로 올라간다"]
S1 -.-> U1["스트림 순서 번호 0 을 기다린다"]
S2 --> U2["바로 올라간다"]
전송 순서 번호에는 11 이 빠져 구멍이 났습니다. 그래도 스트림 0 과 스트림 2 의 스트림 순서 번호는 끊기지 않아 그 메시지들은 바로 올라갑니다. 멈춘 것은 스트림 1 하나뿐입니다.
맨 앞의 것 하나 때문에 뒤가 전부 서는 일을 헤드 오브 라인 블로킹이라고 합니다. 스트림을 가르면 그 범위가 스트림 하나로 좁아집니다.
순서를 안 지켜도 되는 메시지는 순서 없는 전달로 보낼 수 있습니다. 이 표시가 붙은 메시지는 도착하는 대로 응용 프로그램에 올라갑니다. 앞의 것을 기다리지 않습니다.
패킷 하나에 묶여 실리는 청크
SCTP 패킷은 공통 헤더 하나와 그 뒤에 이어 붙는 청크(chunk) 여럿으로 이루어집니다. 청크는 자기 종류와 길이를 스스로 밝히는 조각입니다.
block-beta columns 3 h["공통 헤더 · 출발 포트 · 도착 포트 · 검증 태그 · 체크섬"]:3 c1["데이터 청크"]:1 c2["확인 응답 청크"]:1 c3["하트비트 청크"]:1
성격이 다른 청크가 한 패킷에 같이 탑니다. 보낼 데이터와 확인 응답, 경로 점검 신호를 따로 보내지 않고 한 번에 묶어 내보낼 수 있습니다.
공통 헤더에는 출발 포트와 도착 포트, 검증 태그, 체크섬이 들어갑니다. 체크섬은 패킷이 오는 길에 망가졌는지 가려내는 값입니다. 받는 쪽이 같은 방법으로 다시 셈합니다. 값이 다르면 그 패킷을 버립니다.
앞서 본 검증 태그도 여기 실립니다. 이 값이 안 맞는 패킷은 다른 결합의 것이거나 지어낸 것이라고 보고 버립니다. 앞선 결합에서 늦게 도착한 패킷이 새 결합에 섞여 드는 것도 이 값이 막습니다.
데이터 청크는 사용자 메시지 조각을 싣습니다. 하트비트 청크는 예비 경로가 아직 살아 있는지 물어보는 점검 신호입니다.
확인 응답 청크는 어디까지 받았는지를 보내는 쪽에 알립니다. 「스무 번까지는 차례대로 다 받았다. 스물둘과 스물셋도 받았다」가 그 답입니다. 차례대로 받은 끝이 어디인지와 그 뒤로 띄엄띄엄 받은 구간이 어디인지를 함께 적습니다.
이렇게 골라서 알려 주는 방식이라 SACK(Selective Acknowledgement, 선택적 확인 응답)이라고 부릅니다. 보내는 쪽은 그 답을 보고 빠진 조각만 골라 재전송합니다. 위 예에서는 스물한 번째만 다시 보내면 됩니다.
한꺼번에 얼마나 내보낼지는 혼잡 제어와 흐름 제어가 정합니다. 받는 쪽이 알려 준 여유 공간과 망이 견디는 정도를 넘지 않게 조절합니다. 이 대목은 TCP 와 같은 뼈대입니다.
길이 끊길 때 갈아타는 경로
주 경로가 죽어도 결합은 끝나지 않습니다. 예비 경로로 갈아타면 됩니다. 언제 갈아탈지는 경로마다 살아 있는지를 세어 정합니다.
예비 경로가 아직 살아 있는지는 하트비트 청크로 확인합니다. 한쪽이 예비 주소로 하트비트를 보내면 상대가 같은 값을 실어 답합니다. 답이 안 오는 횟수를 세어 그 경로를 죽은 것으로 표시합니다.
stateDiagram-v2
state "주 경로가 살아 있음" as A1
state "답 없는 횟수가 쌓임" as A2
state "주 경로를 죽은 것으로 표시" as A3
state "예비 경로로 바꿔 보냄" as A4
[*] --> A1
A1 --> A2: 보낸 것에 답이 안 온다
A2 --> A3: 답 없는 횟수가 정해진 수를 넘는다
A3 --> A4: 예비 주소로 갈아탄다
주 경로로 보낸 데이터가 거듭 답을 못 받으면 보내는 쪽은 예비 주소로 바꿔 보내기 시작합니다. 결합은 살아 있습니다. 응용 프로그램은 연결을 다시 맺지 않습니다. 이렇게 길을 갈아타는 것을 페일오버라고 합니다.
닫을 때는 양쪽이 서로 할 말을 다 끝내야 합니다. 한쪽이 닫겠다고 알리면 남아 있던 데이터를 다 보낸 뒤 결합이 닫힙니다. 한쪽만 먼저 닫은 채 반대 방향으로는 계속 받는 반쪽 닫기가 없습니다.
쓰이는 곳과 눈에 덜 띄는 까닭
전화를 걸 때는 통화 내용과 따로 오가는 제어 신호가 있습니다. 누구에게 거는지, 상대가 받았는지 같은 것을 알리는 신호입니다. 전화망 안에서 이 신호를 나르는 체계를 SS7(Signaling System No. 7, 7번 신호 방식)이라고 부릅니다.
같은 신호를 IP 망 위로 나르는 것이 SIGTRAN(Signaling Transport, 신호 전송)입니다. SCTP 를 쓰는 곳이 여기입니다. 이 신호를 나르는 데 필요한 것이 메시지 단위 전달과 여러 경로였습니다. 그 요구에 맞춰 만들어진 프로토콜입니다.
브라우저 안에서도 돕니다. 브라우저끼리 직접 데이터를 주고받는 WebRTC(Web Real-Time Communication, 웹 실시간 통신)의 데이터 채널이 SCTP 를 씁니다. 이때는 SCTP 패킷을 UDP 안에 싸서 내보냅니다. 겉으로는 UDP 통신이라 중간 장비가 통과시킵니다.
block-beta columns 4 ip["IP 헤더"] udp["UDP 헤더"] sctp["SCTP 공통 헤더"] ck["청크들"] space:2 inner["UDP 안에 담긴 SCTP 패킷"]:2
받는 쪽은 UDP 헤더를 벗겨 안에 든 SCTP 패킷을 꺼냅니다. 헤더가 한 겹 더 붙는 만큼 한 패킷에 실을 수 있는 데이터가 줄어듭니다.
싸서 보내는 까닭이 곧 SCTP 를 공개 인터넷에서 그대로 쓰기 어려운 까닭입니다. NAT(Network Address Translation, 네트워크 주소 변환) 장비와 방화벽은 TCP 와 UDP 를 전제로 만들어져 있습니다. 모르는 번호가 달린 패킷은 통과시키지 않고 버리는 경우가 많습니다.
멀티호밍도 주소를 바꿔치기하는 장비와 부딪힙니다. 결합을 열 때 알려 주는 주소 목록은 보내는 쪽이 자기 주소로 아는 값입니다. 주소 변환을 지나면 밖에서 보이는 주소가 달라집니다. 상대가 목록에 적힌 주소로 보내면 닿지 않습니다.
관련 항목
SCTP 결합을 이루는 구성 요소
청크 · 공통 헤더 · 검증 태그 · 스트림 · 전송 순서 번호 · 스트림 순서 번호 · 포트 번호 · 체크섬
SCTP 와 같은 전송 계층에서 겨루는 프로토콜
TCP · UDP · QUIC · DCCP · 전송 계층 · IP
SCTP 가 데이터를 끊어 담는 단위와 맞세워지는 단위
메시지 경계 · 바이트 스트림 · 데이터그램 · 세그먼트 · 패킷 · 메시지
SCTP 가 신뢰성을 지키려고 쓰는 장치
확인 응답 · 선택적 확인 응답 · 재전송 · 재전송 타임아웃 · 혼잡 제어 · 흐름 제어 · 슬라이딩 윈도 · 신뢰성
SCTP 가 여러 경로를 다루는 수단
멀티호밍 · 하트비트 · 페일오버 · 경로 MTU 탐색 · MTU · 라우팅
SCTP 결합을 열고 닫을 때 오가는 신호
핸드셰이크 · 쿠키 · SYN 쿠키 · 하프 클로즈 · 킵얼라이브
SCTP 가 비켜 가거나 맞닥뜨리는 문제
헤드 오브 라인 블로킹 · 패킷 손실 · 패킷 재정렬 · SYN 플러드 · 지연 · 타임아웃
SCTP 를 실제로 채택한 시스템과 프로토콜
WebRTC · SIGTRAN · SS7 · Diameter · 데이터 채널
SCTP 패킷을 걸러 내거나 바꾸는 중간 장비
NAT · 방화벽 · 미들박스 · 로드 밸런서 · UDP 캡슐화
SCTP 를 응용 프로그램이 부르는 인터페이스
소켓 · 소켓 API · 시스템 콜 · 파일 서술자 · 커널
SCTP 가 속하는 상위 분류
프로토콜 · 네트워크 · 인터넷 프로토콜 스위트 · OSI 모형 · 표준
다른 이름: Stream Control Transmission Protocol · 스트림 제어 전송 프로토콜