-
[Ribbon SBC] Core–Edge TLS/SRTP Hands on - 7 : 5초 통화 시험과 TLS/SRTP 증적 판독Ribbon Communications/Session Border controller 2026. 9. 15. 14:10
이번 편에서 할 일
드디어 통화를 걸어 보겠습니다. SIPp client가
2000번으로 호를 걸고 5초 동안 PCMU 음성을 보냅니다. SIPp server는 받은 음성을 그대로 되돌려 보냅니다. 이 호는 Core와 Edge를 차례로 지나갑니다.이번 편에서 제가 가장 신경 쓴 것은 "호가 됐다"와 "TLS/SRTP로 보호됐다"를 따로 증명하는 것입니다. 그래서 아래 네 가지 증적을 모아 하나씩 판독하고, 마지막에 함께 놓고 판정하겠습니다.
- 첫 호의 TLS 핸드셰이크
- Core 로그의 SDP(
RTP/SAVP,a=crypto) - Core–Edge 사이 **전선(wire)**에서 잡은 미디어 크기
- 양 끝 SIPp의 결과(end-to-end)
시험은 설정을 바꾸지 않고 두 번 했습니다. 두 호는 쓰임새가 다릅니다.
회차 시작 시각(KST, Rocky 시계) 이 회차에서 얻은 것 1회차 2026-09-15 13:02:20 end-to-end 결과, TLS 핸드셰이크, Core 로그 SDP, Edge 내부 캡처 2회차 2026-09-15 13:10:47 end-to-end 결과, Core–Edge 전선 캡처(SRTP 크기) 두 번째 호에서는 TLS 핸드셰이크가 다시 일어나지 않았습니다. 첫 호 때 맺은 TLS 연결을 그대로 재사용했기 때문입니다. 그래서 "핸드셰이크"와 "전선 SRTP 크기"를 한 호에서 동시에 얻지는 못했습니다. 이 점은 판정 절에서 한 번 더 짚겠습니다.
시험 환경
항목 값 비고 Core / Edge / Rocky 4·5·6편 구성, 2편 namespace 7.2.16 r174877 SIPp 3.7.7 기존 설치본 음원 tone440.pcmu(PCMU/8000)40,000 bytes, 8,000 samples/s의 5초 톤 패킷화 간격 20ms ( a=ptime:20)시나리오 SDP 통화 유지 5000ms (시나리오 pause)— 회차 2회 — 사전 조건
- [ ] 2편:
sipp-client,sipp-servernamespace, Rockyenp0s16캡처 NIC - [ ] 4편: Core 7단계 commit 완료
- [ ] 5·6편: Edge Signaling Group 두 개 Up

최초 호의 TLS 협상과 5초 RTP/SRTP 흐름
Core INFO 로그 준비
Core CLI(admin)에서 SDP를 수집할 동안만 INFO 수준으로 올렸습니다. 아래 명령은 이번 시험에서 실제 사용했습니다. 시험이 끝나면 8편의 major 복원 명령을 적용합니다.
configure private set oam eventLog typeAdmin debug filterLevel info commit check commit exit시험 시나리오
어디서: Rocky (
/home/sbclab/fresh-sbc-lab/edge-media/uac-rtp-5s.xml)SIPp 기본 UAC 시나리오를 바탕으로, ACK 뒤에 PCMU 파일을 5초 동안 흘리게 바꾼 것입니다. 경로의 계정 이름은 실제 랩 계정
sbclab이며 실행한 시나리오를 그대로 실었습니다.<?xml version="1.0" encoding="ISO-8859-1" ?> <!DOCTYPE scenario SYSTEM "sipp.dtd"> <!-- This program is free software; you can redistribute it and/or --> <!-- modify it under the terms of the GNU General Public License as --> <!-- published by the Free Software Foundation; either version 2 of the --> <!-- License, or (at your option) any later version. --> <!-- --> <!-- This program is distributed in the hope that it will be useful, --> <!-- but WITHOUT ANY WARRANTY; without even the implied warranty of --> <!-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the --> <!-- GNU General Public License for more details. --> <!-- --> <!-- You should have received a copy of the GNU General Public License --> <!-- along with this program; if not, write to the --> <!-- Free Software Foundation, Inc., --> <!-- 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA --> <!-- --> <!-- Sipp default 'uac' scenario. --> <!-- --> <scenario name="Edge PCMU RTP 5 seconds"> <!-- In client mode (sipp placing calls), the Call-ID MUST be --> <!-- generated by sipp. To do so, use [call_id] keyword. --> <send retrans="500"> <![CDATA[ INVITE sip:[service]@[remote_ip]:[remote_port] SIP/2.0 Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch] From: sipp <sip:sipp@[local_ip]:[local_port]>;tag=[pid]SIPpTag00[call_number] To: [service] <sip:[service]@[remote_ip]:[remote_port]> Call-ID: [call_id] CSeq: 1 INVITE Contact: sip:sipp@[local_ip]:[local_port] Max-Forwards: 70 Subject: Performance Test Content-Type: application/sdp Content-Length: [len] v=0 o=user1 53655765 2353687637 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [media_port] RTP/AVP 0 a=rtpmap:0 PCMU/8000 a=ptime:20 a=sendrecv ]]> </send> <recv response="100" optional="true"> </recv> <recv response="180" optional="true"> </recv> <recv response="183" optional="true"> </recv> <!-- By adding rrs="true" (Record Route Sets), the route sets --> <!-- are saved and used for following messages sent. Useful to test --> <!-- against stateful SIP proxies/B2BUAs. --> <recv response="200" rtd="true"> </recv> <!-- Packet lost can be simulated in any send/recv message by --> <!-- by adding the 'lost = "10"'. Value can be [1-100] percent. --> <send> <![CDATA[ ACK sip:[service]@[remote_ip]:[remote_port] SIP/2.0 Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch] From: sipp <sip:sipp@[local_ip]:[local_port]>;tag=[pid]SIPpTag00[call_number] To: [service] <sip:[service]@[remote_ip]:[remote_port]>[peer_tag_param] Call-ID: [call_id] CSeq: 1 ACK Contact: sip:sipp@[local_ip]:[local_port] Max-Forwards: 70 Subject: Performance Test Content-Length: 0 ]]> </send> <!-- This delay can be customized by the -d command-line option --> <!-- or by adding a 'milliseconds = "value"' option here. --> <nop><action><exec rtp_stream="/home/sbclab/fresh-sbc-lab/edge-media/tone440.pcmu,1,0,PCMU/8000"/></action></nop> <pause milliseconds="5000"/> <nop><action><exec rtp_stream="pause"/></action></nop> <!-- The 'crlf' option inserts a blank line in the statistics report. --> <send retrans="500"> <![CDATA[ BYE sip:[service]@[remote_ip]:[remote_port] SIP/2.0 Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch] From: sipp <sip:sipp@[local_ip]:[local_port]>;tag=[pid]SIPpTag00[call_number] To: [service] <sip:[service]@[remote_ip]:[remote_port]>[peer_tag_param] Call-ID: [call_id] CSeq: 2 BYE Contact: sip:sipp@[local_ip]:[local_port] Max-Forwards: 70 Subject: Performance Test Content-Length: 0 ]]> </send> <recv response="200" crlf="true"> </recv> <!-- definition of the response time repartition table (unit is ms) --> <ResponseTimeRepartition value="10, 20, 30, 40, 50, 100, 150, 200"/> <!-- definition of the call length repartition table (unit is ms) --> <CallLengthRepartition value="10, 50, 100, 500, 1000, 5000, 10000"/> </scenario>기본 UAC 시나리오와 달라진 부분만 보겠습니다.
위치 내용 이유 INVITE SDP m=audio [media_port] RTP/AVP 0PCMU 하나만 평문 RTP로 제안 client 구간은 평문. 코덱 선택지를 하나로 고정 a=ptime:2020ms 단위 패킷 수 기대값 계산의 기준 recv 100/180/183optional="true"중간 응답은 와도 되고 안 와도 됨 Core가 어떤 중간 응답을 보내든 시나리오가 멈추지 않게 exec rtp_stream="…tone440.pcmu,1,0,PCMU/8000"ACK 직후 PCMU 파일 송출(payload type 0) 실제 RTP를 흘리기 위해 pause milliseconds="5000"5초 유지 이번 시험의 통화 시간 exec rtp_stream="pause"송출 중지 BYE 전에 미디어를 멈춤 BYE 후 recv 200정상 종료 확인 200을 못 받으면 SIPp가 오류로 끝남 실행 스크립트와 캡처 지점
어디서: Rocky shell (
sudo로 실행)client·server 두 namespace에서 캡처를 켜고, UAS를 먼저 띄운 뒤 UAC로 한 호를 거는 스크립트입니다. 파일명은
run-chain-call.sh입니다. 계정 이름만sbclab로 바꿨습니다.#!/usr/bin/env bash set -uo pipefail if [ "$EUID" -ne 0 ]; then echo 'Run with sudo.'; exit 2; fi out="/home/sbclab/fresh-sbc-lab/results/fresh-chain-$(date +%Y%m%d-%H%M%S)" mkdir -p "$out"; cd "$out" ip netns exec sipp-client tcpdump -i enp0s9 -nn -U -w client-pkt0.pcap 'arp or ip' >client-capture.log 2>&1 & cap1=$! ip netns exec sipp-server tcpdump -i enp0s10 -nn -U -w server-pkt1.pcap 'arp or ip' >server-capture.log 2>&1 & cap2=$! cleanup() { kill -INT "$cap1" "$cap2" 2>/dev/null | — | true; wait "$cap1" "$cap2" 2>/dev/null | — | true; chown -R sbclab:sbclab "$out"; } trap cleanup EXIT ip netns exec sipp-server /usr/local/bin/sipp -sn uas -rtp_echo -i 10.77.30.10 -p 5060 -mi 10.77.30.10 -mp 6002 -m 1 -timeout 40s -timeout_error -trace_msg -trace_err -trace_stat -stf uas-stat.csv >uas-console.log 2>&1 & uas_pid=$! sleep 1 ip netns exec sipp-client /usr/local/bin/sipp 10.77.10.1:5060 -sf /home/sbclab/fresh-sbc-lab/edge-media/uac-rtp-5s.xml -i 10.77.10.10 -p 5060 -mi 10.77.10.10 -mp 6000 -s 2000 -m 1 -l 1 -r 1 -d 5000 -timeout 30s -timeout_error -trace_msg -trace_err -trace_stat -stf uac-stat.csv >uac-console.log 2>&1 uac_rc=$? wait "$uas_pid"; uas_rc=$? printf 'uac_exit=%s\nuas_exit=%s\n' "$uac_rc" "$uas_rc" | tee result.txt printf 'result_directory=%s\n' "$out" [ "$uac_rc" -eq 0 ] && [ "$uas_rc" -eq 0 ]SIPp 옵션
옵션 UAS (server ns) UAC (client ns) 이유 시나리오 -sn uas(내장)-sf uac-rtp-5s.xmlserver는 받기만, client는 위 시나리오 대상 — 10.77.10.1:5060Core pkt0sipSigPort 20-i/-p10.77.30.10 / 5060 10.77.10.10 / 5060 각 namespace 주소. Core ingressIpPrefix, Edge Federated IP와 맞음-mi/-mp10.77.30.10 / 6002 10.77.10.10 / 6000 미디어 주소·포트 -rtp_echo사용 — 받은 RTP를 그대로 돌려보내 반대 방향 미디어를 만듦 -s— 2000R-URI 사용자 부분 = 시험 번호 -m 11호 처리 후 종료 1호 발신 후 종료 회차당 정확히 한 호 -l 1 -r 1— 동시 1호, 초당 1호 — -d 5000— 기본 pause 5000ms 시나리오에 5000ms가 명시돼 있어 같은 값으로 -timeout … -timeout_error40s 30s 시간 안에 안 끝나면 오류 종료 코드로 끝남. 멈춘 시험이 성공처럼 보이지 않게 -trace_msg -trace_err -trace_stat -stf사용 사용 메시지·오류·통계 파일 UAS를 먼저 띄우고
sleep 1뒤에 UAC를 실행하는 이유는, 호가 도착했을 때 server가 이미 듣고 있어야 하기 때문입니다. 마지막 줄은 두 종료 코드가 모두 0일 때만 스크립트 자체도 0으로 끝나게 합니다.캡처 지점
# 위치 방법 보이는 것 회차 A client ns enp0s9위 스크립트 tcpdump client↔Core 평문 SIP·RTP 1, 2 B server ns enp0s10위 스크립트 tcpdump Edge↔server 평문 SIP·RTP 1, 2 C Edge 내부 캡처 Edge WebUI 진단 캡처를 호 발신 직전에 시작 Edge가 보는 패킷(TLS 핸드셰이크 포함) 1 D Rocky enp0s16(SBC-Lab-PKT1)수신 전용 NIC에서 Core(10.77.20.1)–Edge(10.77.20.2) 사이 캡처, 호 발신 전에 시작 전선 위의 TCP/TLS와 SRTP 2 Edge 내부 캡처 설정에서는 Media를 1, Other를 0으로 두고 Called에
2000,+12000을 넣었습니다.Core 쪽은 SDP를 보려고 시험 동안 디버그 로그 수준을 INFO로 올려 두었습니다. 끝난 뒤 되돌리는 절차는 8편에서 다룹니다.
원본 캡처와 로그 파일은 이 글에 첨부하지 않습니다. Core 로그의 SDP에는 SRTP 마스터 키(
a=crypto의inline:값)가 평문으로 남기 때문입니다. 아래 표와 발췌에서는 키를 가렸습니다.실행 결과: end-to-end
어디서: Rocky shell
sudo bash run-chain-call.sh두 회차 모두 마지막 출력은 같았습니다.
uac_exit=0 uas_exit=0확인 항목 기대값 1회차 2회차 판정 UAC 종료 코드 0 0 0 일치 UAS 종료 코드 0 0 0 일치 성공 호 / 시도 호 1 / 1 1 / 1 1 / 1 일치 ( -m 1+ 종료 코드 0)client 캡처의 ACK → BYE 간격 약 5초 5.010초 5.015초 일치 ACK → BYE 간격은 client 캡처(A)의 타임스탬프 차이입니다.
- 1회차: 13:02:26.613481 − 13:02:21.603556 = 5.010초
- 2회차: 13:10:53.191627 − 13:10:48.176306 = 5.015초
판독 ① 평문 구간 SIP 흐름
1회차 client ↔ Core (캡처 A, Rocky 시계, INVITE 기준 경과 시간)
경과(초) 방향 메시지 0.000 client → Core INVITE sip:2000@10.77.10.1:5060 SIP/2.00.076 Core → client SIP/2.0 100 Trying1.185 Core → client SIP/2.0 180 Ringing1.185 Core → client SIP/2.0 200 OK1.186 client → Core ACK sip:2000@10.77.10.1:5060 SIP/2.06.196 client → Core BYE sip:2000@10.77.10.1:5060 SIP/2.06.215 Core → client SIP/2.0 200 OK1회차 Edge ↔ server (캡처 B, 같은 Rocky 시계, client INVITE 기준)
경과(초) 방향 메시지 1.134 Edge → server INVITE sip:2000;phone-context=private@10.77.30.10:5060;user=phone SIP/2.01.134 server → Edge SIP/2.0 180 Ringing1.136 server → Edge SIP/2.0 200 OK1.146 Edge → server ACK sip:10.77.30.10:5060;transport=UDP SIP/2.06.227 Edge → server BYE sip:10.77.30.10:5060;transport=UDP SIP/2.06.228 server → Edge SIP/2.0 200 OKA와 B는 같은 Rocky VM에서 잡았으므로 시계가 같습니다. 그래서 두 표를 한 시간축으로 읽어도 됩니다. 이렇게 보면 client가 INVITE를 보내고 1.134초 뒤에 server에 INVITE가 도착했습니다. 그 사이에 Core, TLS 구간, Edge를 지났습니다.
관찰한 점을 정리하면 이렇습니다.
- 번호: server가 받은 R-URI 사용자 부분은
2000입니다. Core 로그에서도 Edge로 보낸 R-URI와 To가2000이었습니다. 번호 변환이나 메시지 조작 규칙 없이 번호가 그대로 전달됐습니다. - URI 파라미터: server가 받은 URI에는
;phone-context=private와;user=phone이 있습니다. Core가 Edge로 보내는 INVITE에도 이미 포함되어 있으므로 Edge가 처음 추가한 값으로 해석하지 않습니다. SIPp 내장 UAS는 이 형식을 받고 정상 응답했습니다. - 2회차: client INVITE에서 server INVITE까지 0.889초, 200 OK까지 0.922초였습니다. 흐름은 1회차와 같습니다.
판독 ② TLS 구간: 핸드셰이크
증적: 1회차 Edge 내부 캡처(C)
메시지 출발지 Version 필드 Cipher Suite Client Hello 10.77.20.1 (Core) 771 = 0x0303 — Server Hello 10.77.20.2 (Edge) 771 = 0x0303 49199 = 0xC02F Client Hello에서 Server Hello까지는 Edge 시계로 0.053초였습니다.
이 표를 읽을 때 함정이 하나 있습니다. Version 필드 0x0303만으로 TLS 1.2라고 단정하면 안 됩니다. TLS 1.3도 호환성 때문에 이 필드에 0x0303을 넣고, 실제 버전은 확장 필드로 따로 알립니다. 이번에 TLS 1.2라고 판정한 근거는 이렇습니다.
- Server Hello가 고른 암호군이 **0xC02F =
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256**입니다. 이 형식의 암호군은 TLS 1.2까지에서 쓰이는 것이고, TLS 1.3 암호군(0x13xx 계열)이 아닙니다. - Core TLS 프로파일에서
v1_3 disabled,v1_2 enabled로 설정되어 있습니다(4편).
이 두 가지를 합쳐 협상 버전은 TLS 1.2, 암호군은 0xC02F로 판정했습니다. 양쪽 프로파일에 이 암호군 하나만 넣었으니, 설정과도 맞는 결과입니다.
역할도 확인됩니다. Client Hello가 Core에서 나왔으므로 정방향 호에서 Core가 TLS client, Edge가 TLS server입니다(3편 설계와 같음).
그리고 이 캡처에서 Core–Edge 구간의 평문 SIP는 한 건도 발견되지 않았습니다. Edge 내부 캡처에 SIP로 해석된 메시지는 모두 Edge↔server(10.77.30.x) 구간 것이었습니다. 2회차 전선 캡처(D)에서도 SIP로 해석되는 패킷은 0건이었습니다.
캡처로 확인한 상호 TLS 인증
최초 호의 TCP 스트림을 순서대로 재조립해 인증서 교환 메시지를 확인했습니다. 실제 연결은 10.77.20.1:54319 → 10.77.20.2:5061입니다.
방향 실제 핸드셰이크 메시지 인증서 Edge → Core ServerHello, Certificate, ServerKeyExchange, CertificateRequest, ServerHelloDone CN=edge.sbc.test / issuer=SBC Fresh Lab Root CA Core → Edge ClientHello, Certificate, ClientKeyExchange, CertificateVerify CN=core.sbc.test / issuer=SBC Fresh Lab Root CA Edge가 클라이언트 인증서를 요청했고 Core가 자신의 인증서와 개인키 소유 증명인 CertificateVerify를 보냈습니다. 이어 TLS 협상과 호가 정상 완료됐습니다. 따라서 이번 결과의 상호 인증은 설정 화면뿐 아니라 실제 메시지로 확인했습니다. 상세 인증서 지문은 첨부
evidence/mutual-tls-proof.json에 있습니다.두 번째 호는 이 TLS 연결을 재사용하므로 두 번째 wire 캡처에 새 핸드셰이크가 없는 것이 정상입니다.
판독 ③ TLS 안의 SIP와 SDP
패킷 캡처로는 TLS 안의 SIP를 볼 수 없습니다. 그래서 TLS 구간 SDP는 Core 디버그 로그로 확인했습니다.
증적: 1회차 Core 로그
확인 항목 기대값 실측값 판정 Core → Edge INVITE의 SDP 미디어 프로파일(offer) RTP/SAVPRTP/SAVP일치 Edge → Core 200 OK의 SDP 미디어 프로파일(answer) RTP/SAVPRTP/SAVP일치 offer a=cryptosuiteAES_CM_128_HMAC_SHA1_80AES_CM_128_HMAC_SHA1_80일치 answer a=cryptosuiteAES_CM_128_HMAC_SHA1_80AES_CM_128_HMAC_SHA1_80일치 inline:키 값(공개하지 않음) <마스킹>— Edge로 보낸 R-URI / To 사용자 부분 20002000일치 RTP/SAVP는 "SRTP로 음성을 보내겠다"는 SDP 표기입니다.a=crypto에는 suite 이름과 그 호에서 쓸 마스터 키가 들어 있습니다. offer와 answer 양쪽에 같은 suite가 있어야 SDES-SRTP 협상이 성립한 것입니다. 한쪽만 있으면 제안만 하고 합의되지 않은 상태입니다.그리고 이 키가 평문으로 적혀 있다는 점이 TLS가 필요한 이유입니다. 이 SDP가 지나간 Core–Edge 구간은 판독 ②의 TLS 1.2 연결이었습니다.
평문 구간(client ↔ Core)의 SDP는 시나리오 파일에서 확인할 수 있습니다. SIPp가
m=audio … RTP/AVP 0으로 평문 RTP를 제안합니다.판독 ④ 미디어: 구간별 패킷 수
기대값 계산
- 통화 유지 5초, 패킷화 간격 20ms → 5 ÷ 0.02 = 250개 (방향별 근사치)
- PCMU 한 패킷의 음성 데이터: 8000Hz × 0.02초 × 1바이트 = 160바이트
- RTP 헤더 12바이트 + 160바이트 = UDP payload 172바이트
1회차 (캡처 A·B·C)
구간 방향 포트 패킷 수 지속(초) UDP payload PT A: client ↔ Core client → Core 10.77.10.10:6000 → 10.77.10.1:1024 250 4.980 172 0 A: client ↔ Core Core → client 10.77.10.1:1024 → 10.77.10.10:6000 249 5.002 172 0 C: Edge 내부, Core 구간 Core → Edge 10.77.20.1:1024 → 10.77.20.2:16384 249 4.977 172 0 C: Edge 내부, Core 구간 Edge → Core 10.77.20.2:16384 → 10.77.20.1:1024 250 5.024 172 0 C: Edge 내부, server 구간 Edge → server 10.77.30.1:16384 → 10.77.30.10:6002 249 4.961 172 0 C: Edge 내부, server 구간 server → Edge 10.77.30.10:6002 → 10.77.30.1:16384 249 4.961 172 0 B: Edge ↔ server Edge → server 10.77.30.1:16384 → 10.77.30.10:6002 249 4.960 172 0 B: Edge ↔ server server → Edge 10.77.30.10:6002 → 10.77.30.1:16384 249 4.960 172 0 2회차 (캡처 A·B·D)
구간 방향 포트 패킷 수 지속(초) UDP payload PT A: client ↔ Core client → Core 10.77.10.10:6000 → 10.77.10.1:1026 250 4.981 172 0 A: client ↔ Core Core → client 10.77.10.1:1026 → 10.77.10.10:6000 249 5.014 172 0 D: 전선, Core ↔ Edge Core → Edge 10.77.20.1:1026 → 10.77.20.2:16386 250 4.981 182 0 D: 전선, Core ↔ Edge Edge → Core 10.77.20.2:16386 → 10.77.20.1:1026 250 5.036 182 0 B: Edge ↔ server Edge → server 10.77.30.1:16386 → 10.77.30.10:6002 248 4.941 172 0 B: Edge ↔ server server → Edge 10.77.30.10:6002 → 10.77.30.1:16386 248 4.941 172 0 실측도: 2회차 미디어 경로
SIPp client ──RTP 172B (250 / 249)──► Core ══SRTP 182B (250 / 250)══► Edge ──RTP 172B (248 / 248)──► SIPp server 10.77.10.10:6000 10.77.10.1:1026 10.77.20.1:1026 10.77.20.2:16386 10.77.30.1:16386 10.77.30.10:6002 [캡처 A] [캡처 D: Rocky enp0s16] [캡처 B]모든 구간이 기대값 250개 근처(248~250)입니다. 구간마다 1~2개씩 차이가 나는 것은 사실 그대로 적었습니다. 차이의 원인은 따로 분석하지 않았습니다. 캡처 시작·종료 시점, SIPp 송출 타이밍, Edge의 DSP 미디어 처리 등이 후보입니다. 그래서 이 표로 "손실 0"이나 음질을 주장하지 않겠습니다. 이 표가 말해 주는 것은 5초 동안 네 구간 모두 양방향으로 미디어가 흘렀다는 것까지입니다.
PT 0은 분석 도구가 RTP 헤더의 payload type 필드를 읽은 값입니다. SRTP는 RTP 헤더를 암호화하지 않으므로, 전선 구간 D에서도 PT 0이 읽히는 것이 정상입니다. PT 0이 읽혔다는 사실이 평문이라는 뜻은 아닙니다.
두 번째 신규 호의 실제 패킷 수와 크기
172바이트와 182바이트
이번 편에서 가장 설명하고 싶었던 숫자입니다.
캡처 Core–Edge 구간 UDP payload C: Edge 내부 캡처(1회차) 172 D: Rocky 수신 전용 NIC, 전선(2회차) 182 차이 10바이트 SRTP suite
AES_CM_128_HMAC_SHA1_80을 계산식으로 풀면 이렇습니다.RTP 헤더 12 바이트 (SRTP에서도 평문으로 남음) 암호화된 음성 payload 160 바이트 (AES 카운터 모드는 길이가 변하지 않음) 인증 태그 10 바이트 (HMAC-SHA1을 80비트로 자름: 80 ÷ 8 = 10) ───────────────────────────────── 합계 182 바이트전선에서 잡은 182바이트는 평문 RTP 172바이트에 80비트 인증 태그 10바이트를 더한 값과 정확히 맞습니다. 반면 Edge 내부 캡처는 같은 Core–Edge 구간인데도 172바이트였습니다. 내부 캡처가 SRTP 처리의 어느 쪽에서 패킷을 뜨는지는 확인하지 않았습니다. 다만 내부 캡처의 172바이트는 전선 위의 모습이 아니므로 SRTP 증거로 쓸 수 없다는 점은 분명합니다. 2편에서 캡처 전용 NIC를 따로 붙인 이유가 바로 이것입니다.
그래도 182바이트 하나만으로 "SRTP로 암호화됐다"고 결론 내리지는 않습니다. 크기는 "그럴 수 있다"는 정합성일 뿐입니다. 이론적으로는 평문 RTP에 10바이트짜리 헤더 확장이나 패딩이 붙어도 같은 크기가 됩니다. 저는 전선 payload를 PCMU로 디코드해 보는 확인은 하지 않았습니다. 그래서 크기는 아래 판정에서 여러 근거 중 하나로만 씁니다.
TLS의 AES-GCM과 SRTP의 AES-CM은 다른 것
같은 AES라서 헷갈리기 쉽습니다. 이번 시험에서 협상된 두 암호를 나란히 놓겠습니다.
구분 TLS (신호) SRTP (미디어) 이번 결과 TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256(0xC02F)AES_CM_128_HMAC_SHA1_80확인한 곳 1회차 Server Hello 1회차 Core 로그 SDP a=crypto, 2회차 전선 크기보호 대상 Core–Edge TCP 연결 위의 SIP 전체 Core–Edge UDP 위의 음성 RTP 키 합의 ECDHE, 인증서(RSA)로 서명 SDES: SDP에 키를 적어 보냄(TLS가 보호) 암호화 AES-128 GCM (암호화와 무결성을 한 번에) AES-128 카운터 모드 무결성 GCM 태그 HMAC-SHA1 80비트 태그(패킷마다 10바이트) 설정 위치 Core FRESH_TLS, Edge TLS Profile 201Core FRESH_SRTP, Edge SDES-SRTP Profile 201TLS에서 GCM이 협상됐다고 SRTP도 GCM인 것은 아닙니다. 또 TLS 핸드셰이크가 성공했다고 해서 미디어가 SRTP라는 뜻도 아닙니다. 그래서 둘을 따로 확인했습니다.
종합 판정
# 판정 항목 기대값 실측값 판정 근거 1 UAC 종료 코드 0 0 / 0 (1·2회차) 일치 result.txt2 UAS 종료 코드 0 0 / 0 일치 result.txt3 성공 호 / 시도 호 전부 성공 1/1, 1/1 일치 종료 코드 + -m 14 통화 유지(ACK→BYE) 약 5초 5.010초, 5.015초 일치 캡처 A 5 협상 TLS 버전 TLS 1.2 TLS 1.2 (0xC02F 선택 + v1_3 비활성) 일치 1회차 캡처 C 6 협상 암호군 0xC02F 0xC02F 일치 1회차 캡처 C 7 Core–Edge 구간 평문 SIP 0건 0건 일치 캡처 C(1회차), D(2회차) 8 상호 인증 메시지 CertificateRequest 및 클라이언트 증명 양측 Certificate, CertificateRequest, CertificateVerify 확인 일치 mutual-tls-proof.json 9 이름 검증 켠 상태에서 성립 참 참 일치 4·5편 설정 + 1~6 10 TLS 구간 SDP 프로파일 offer·answer 모두 RTP/SAVP모두 RTP/SAVP일치 1회차 Core 로그 11 TLS 구간 a=cryptosuiteoffer·answer 모두 AESCM128HMACSHA1_80 모두 AESCM128HMACSHA1_80 일치 1회차 Core 로그 12 평문 구간 미디어 흐름 방향별 약 250개, 172B client 250/249, server 249/249·248/248 흐름 확인 캡처 A·B 13 전선 Core–Edge 미디어 크기 172 + 10 = 182B 182B, 방향별 250개 일치 2회차 캡처 D 14 Edge 내부 캡처 크기 (전선 증거 아님) 172B 참고만 1회차 캡처 C 15 Core→Edge TCP 목적지 포트 Edge TLS 5061 10.77.20.2:5061 일치 최초 호 TCP 스트림 16 수신 음원이 발신 음원과 같은 신호인지 — 미측정 — — 17 SBC 통계 화면의 SRTP 표시 — 미측정 — — 결론
아래 네 가지가 서로 맞물립니다.
- 첫 호의 TLS 핸드셰이크: Core–Edge는 TLS 1.2, 0xC02F로 연결됐고, 그 구간에 평문 SIP가 없었습니다.
- SDP: 그 TLS 연결 안에서 offer와 answer가 모두
RTP/SAVP와AES_CM_128_HMAC_SHA1_80에 합의했습니다. 게다가 양쪽 모두 평문 fallback을 허용하지 않는 설정입니다(CoreallowFallback disable, Edge Operation Option Required). - 전선 크기: 실제 선 위의 Core–Edge 미디어는 방향별 250개가 182바이트였습니다. 이는 80비트 인증 태그가 붙은 SRTP 크기와 정확히 맞습니다.
- end-to-end: 두 회차 모두 SIPp 양쪽이 종료 코드 0으로 끝났고, 양 끝 및 SBC 사이 구간에서 약 5초 동안 양방향 미디어가 흘렀습니다. 양쪽 모두 fallback을 막아 둔 설정이므로, SRTP 협상이 실패했다면 평문으로 대신 연결되는 대신 호가 실패하도록 기대되는 구성입니다. 이 실패 동작 자체는 부정 시험으로 확인하지 않았습니다.
그래서 이 랩 구성에서 정방향
2000번 호는 Core–Edge 구간에서 SIP는 TLS 1.2로, 음성은 AESCM128HMACSHA1_80 SRTP로 전달됐다고 판정합니다.단, 이 결론에는 조건이 붙습니다.
- 핸드셰이크·SDP(1회차)와 전선 크기(2회차)는 서로 다른 호에서 얻었습니다. 두 호 사이에 SBC 설정은 바꾸지 않았습니다. 2회차가 1회차의 TLS 연결을 재사용했다는 사실은 그 사이 두 SBC의 TLS 연결이 끊기지 않았음을 보여 줍니다.
- 상호 인증 메시지와 TCP 목적지 5061은 최초 호의 재조립한 TCP 스트림에서 직접 확인했습니다.
- 각 회차는 한 호씩입니다. 반복·부하 시험은 하지 않았습니다.
- 음질(MOS), 손실 0, 음원 일치 여부는 주장하지 않습니다.
시각을 맞춰 볼 때 주의할 점
1회차에서 Edge 내부 캡처(C)와 server 캡처(B)의 같은 메시지 시각을 비교해 보면 일정한 차이가 있습니다.
메시지 Edge 시계(C) Rocky 시계(B) 차이 INVITE (Edge → server) 04:02:20.324849 UTC 04:02:21.551088 UTC 1.226초 200 OK (server → Edge) 04:02:20.329592 UTC 04:02:21.553643 UTC 1.224초 ACK (Edge → server) 04:02:20.335607 UTC 04:02:21.562986 UTC 1.227초 BYE (Edge → server) 04:02:25.417556 UTC 04:02:26.644584 UTC 1.227초 같은 구간을 오가는 네 메시지가 모두 약 1.22초씩 어긋나 있습니다. 네트워크 지연이라면 방향에 따라 부호가 달라져야 합니다. 그런데 차이가 일정하므로 Edge와 Rocky 시계가 약 1.2초 어긋나 있다고 보는 것이 자연스럽습니다. 그래서 이 글에서는 서로 다른 장비의 캡처 시각을 한 시간축에 섞지 않았습니다. 따라 하실 때도 캡처 파일끼리 시간 순서를 비교하기 전에 이 차이부터 확인해 주세요.
정리
5초 통화를 두 번 걸었고, 두 번 모두 SIPp 양쪽이 종료 코드 0으로 끝났습니다. 첫 호에서는 TLS 1.2 / 0xC02F 핸드셰이크와 SDP의
RTP/SAVP+AES_CM_128_HMAC_SHA1_80합의를 확인했습니다. 두 번째 호에서는 전선 위 Core–Edge 미디어가 182바이트, 즉 평문 172바이트에 80비트 태그가 더해진 크기임을 확인했습니다. 어느 한 증적만으로 결론 내리지 않고, 네 가지를 합쳐서 판정했습니다.다음 편에서는 이번에 실제로 막혔던 지점과, 호가 안 될 때 어디서 멈췄는지 좁혀 가는 순서를 정리하겠습니다. 시험 뒤 로그 수준을 되돌리고 장비를 정리하는 절차도 함께 다루겠습니다.
'Ribbon Communications > Session Border controller' 카테고리의 다른 글