Oct 15, 2026BY SwollowS
public5G-SAOCUDUOpen5GSSDR
이동통신 LAB 구축 시리즈 O CUDU와 Open5GS 5G SA 구축

이 글은 범용 소프트웨어 무선 주변장치(Universal Software Radio Peripheral, USRP) B210, OCUDU
release_26_10, Open5GS 2.7.6으로 기본 5G 단독 구성(5G Standalone, 5G SA)을 구축하는 절차를 정리한다. 구축이 끝나면 사용자 장비(User Equipment, UE)가 5G 기지국인 gNB(next-generation Node B)를 통해 5G 코어(5G Core, 5GC)에 등록하고, internet 데이터 네트워크 이름(Data Network Name, DNN)의 프로토콜 데이터 단위(Protocol Data Unit, PDU) 세션을 생성하며, 외부 네트워크와 패킷을 주고받을 수 있다.제목의 O-CU/DU는 개방형 무선 접속망(Open Radio Access Network, O-RAN) 문맥에서 중앙 장치(Central Unit, CU)와 분산 장치(Distributed Unit, DU) 기능을 가리킨다. 이 글의 실제 기준선은 OCUDU의 CU와 DU 기능을 하나의 gNB 프로세스에서 실행하고 B210을 UHD로 직접 연결한 구성이다. B210을 O-RAN 무선 장치로 사용하거나 Open Fronthaul 7-2x를 검증한 구성은 아니다.
실제 랩에서 확인한 기준선은 다음과 같다.
| 항목 | 확인한 값 |
|---|---|
| 호스트 | Ubuntu 26.04.1, x86-64 |
| Docker | 29.1.3 |
| Docker Compose | 2.40.3 |
| USRP 하드웨어 드라이버(USRP Hardware Driver, UHD) | 4.9.0.1 |
| gNB | OCUDU release_26_10, 커밋 e0db566aac6e6376ddd60e83c84f58698fcc4d70 |
| 5GC | Open5GS 2.7.6 |
| 무선 장치 | USRP B210, USB 3.0, 2송신 2수신 |
| 무선 프로파일 | n78, 30 MHz, 30 kHz 부반송파 간격, 초당 30.72 백만 샘플 |
| 이동통신망 | 시험망 식별자 001/01, 추적 영역 7, 슬라이스 서비스 유형 1 |
| 검증 결과 | N2 초기 설정, 랜덤 액세스, 무선 자원 제어, 5G 등록, internet PDU 세션, 사용자 패킷 통과 |
2026년 10월 16일 서버를 다시 확인했을 때 이 기준선의
open5gs_5gc 컨테이너는 실행 중이 아니었고 마지막 상태는 Exited (139)였다. gNB도 중지 상태였다. 종료 코드 139는 일반적으로 치명적 신호로 프로세스가 종료됐음을 뜻하므로 재기동 전에 해당 컨테이너의 로그와 코어 덤프 유무를 확인해야 한다. 따라서 위 표는 현재 서비스 상태가 아니라 보존된 실행 결과와 서버 아티팩트에 대한 기록이다. 별도로 실행 중인 IP 멀티미디어 서브시스템(IP Multimedia Subsystem, IMS) 프로파일은 Open5GS 2.8.0-48-gf87da61 계열이므로 이 글의 2.7.6 기준선과 섞지 않는다.이 글은 설치, 설정, 기동, 검증 순서로 진행한다. 단문 메시지 서비스(Short Message Service, SMS), 멀티미디어 메시지 서비스(Multimedia Messaging Service, MMS), 5G NR 음성통화(Voice over New Radio, VoNR), 영상통화는 후속 글에서 다룬다.
시리즈 구성
- [이동통신 LAB 구축 시리즈] 이동통신 세대별 RAN, 코어 구조와 오픈소스 구현
- [이동통신 LAB 구축 시리즈] B210, O-CU/DU, Open5GS 5G SA 아키텍처
- 현재 글: [이동통신 LAB 구축 시리즈] O-CU/DU와 Open5GS 5G SA 구축
- [이동통신 LAB 구축 시리즈] IMS 등록과 SMS over IMS 구축
- [이동통신 LAB 구축 시리즈] MMS 구축
- [이동통신 LAB 구축 시리즈] VoNR 음성통화 구축
- [이동통신 LAB 구축 시리즈] IMS 영상통화 구축
- [이동통신 LAB 구축 시리즈] Galaxy S26 Ultra 종단간 검증
구축 결과를 먼저 이해하기
무선 접속망(Radio Access Network, RAN)은 UE와 5GC 사이의 무선 구간이다. OCUDU는 RAN의 gNB를 구현하고, Open5GS는 등록, 인증, 세션 제어, 사용자 패킷 전달에 필요한 5GC 기능을 구현한다. 소프트웨어 정의 무선(Software-Defined Radio, SDR) 장치인 B210은 무선주파수(Radio Frequency, RF) 신호와 동상, 직교(In-phase/Quadrature, I/Q) 샘플을 변환한다.
접속 및 이동성 관리 기능(Access and Mobility Management Function, AMF)은 UE 등록과 이동성을 관리한다. 세션 관리 기능(Session Management Function, SMF)은 PDU 세션을 만들고 사용자 평면 기능(User Plane Function, UPF)을 제어한다. UPF는 UE 패킷을 gNB와 외부 데이터망 사이에서 전달한다.
스트림 제어 전송 프로토콜(Stream Control Transmission Protocol, SCTP)은 N2에서 NG 응용 프로토콜(NG Application Protocol, NGAP)을 운반한다. GPRS 터널링 프로토콜 사용자 평면(GPRS Tunnelling Protocol User Plane, GTP-U)은 N3의 사용자 패킷을 터널링한다. 패킷 전달 제어 프로토콜(Packet Forwarding Control Protocol, PFCP)은 N4에서 SMF가 UPF에 규칙을 설치할 때 사용한다.

OCUDU gNB는 B210을 통해 UE와 연결되고, Open5GS의 AMF와 UPF에는 각각 N2와 N3으로 연결된다.
이 구성에서 확인해야 할 경로는 네 가지다.
| 경로 | 양쪽 종단 | 프로토콜 | 역할 |
|---|---|---|---|
| N2 | gNB와 AMF | SCTP 위 NGAP | 등록, 이동성, NAS 메시지 전달 |
| N3 | gNB와 UPF | UDP 위 GTP-U | UE 사용자 패킷 전달 |
| N4 | SMF와 UPF | PFCP | UPF의 패킷 처리 규칙 제어 |
| N6 | UPF와 외부 데이터망 | IP | UE 주소와 외부 네트워크 연결 |
설치 전에 경로를 고정한다
이 글의 명령은 실제 서버에 남은 소스, 설정, 이미지, 관리 스크립트를 바탕으로 정리했다. 과거 설치 시 입력한 모든 셸 명령의 원문은 보존돼 있지 않으므로, 아래 절차는 서버 아티팩트에서 재구성한 재현 절차다.
개인 홈 디렉터리는 공개 글에서
<home_folder>로 표시한다. 다음 환경변수를 먼저 설정한다.export LAB_HOME='<home_folder>'
export LAB_ROOT='<LAB_ROOT>'
export SA_ROOT="${LAB_HOME}/repos/5g-sa-all-in-one"
export STACK_SRC="${SA_ROOT}/ocudu"
export OCUDU_SRC="${LAB_HOME}/repos/5g-baselines/ocudu-release-26.10"
export RUNTIME_DIR="${SA_ROOT}/runtime"
export CORE_ENV="${RUNTIME_DIR}/ocudu-open5gs-00101.env"
export GNB_CONFIG="${RUNTIME_DIR}/ocudu-gnb-b210-00101-n78-30mhz-2t2r.yml"
export GNB_OVERLAY="${RUNTIME_DIR}/ocudu-26.10-docker-overlay.yml"
export CORE_MANAGER="${RUNTIME_DIR}/manage-ocudu-5g-sa.sh"
export COMPOSE_PROJECT_NAME='ota_base'
<LAB_ROOT>는 ota-fuzz/ 디렉터리가 있는 로컬 작업 트리의 루트다. 공개용 명령에는 실제 사용자명, 내부 IP 주소, 장치 시리얼을 넣지 않는다.서버에 보존된 실행 파일을 보호된 runtime 디렉터리로 복사한다.
install -d -m 0700 "${SA_ROOT}" "${RUNTIME_DIR}"
install -m 0644 \
"${LAB_ROOT}/ota-fuzz/ocudu-open5gs-00101.env" \
"${CORE_ENV}"
install -m 0644 \
"${LAB_ROOT}/ota-fuzz/ocudu-gnb-b210-00101-n78-30mhz-2t2r.yml" \
"${GNB_CONFIG}"
install -m 0644 \
"${LAB_ROOT}/ota-fuzz/ocudu-26.10-docker-overlay.yml" \
"${GNB_OVERLAY}"
if [[ ! -e "${RUNTIME_DIR}/subscriber_db.csv" ]]; then
install -m 0600 /dev/null "${RUNTIME_DIR}/subscriber_db.csv"
fi
설치

먼저 RF를 사용하지 않는 호스트, SDR, 코어 검사를 끝낸 뒤 gNB를 기동하고 계층별 증거를 확인한다.
호스트 패키지 설치
실제 랩은
ota-fuzz/install-remote-5g-lab-stack.sh로 패키지와 이미지를 준비했다. 이 스크립트는 다른 연구 도구도 함께 설치하므로 기본 5G SA 스택만 필요하다면 다음 패키지로 시작한다.sudo apt-get update
sudo apt-get install -y \
build-essential ca-certificates cmake curl \
docker.io docker-compose-v2 git jq ninja-build pkg-config ripgrep \
libboost-program-options-dev libfftw3-dev libgtest-dev \
libmbedtls-dev libsctp-dev libsoapysdr-dev libuhd-dev \
libusb-1.0-0-dev libyaml-cpp-dev python3-pip python3-venv \
soapysdr-tools uhd-host usbutils
sudo systemctl enable --now docker
sudo usermod -aG docker,dialout,plugdev "${USER}"
그룹 변경은 로그아웃 후 다시 로그인해야 반영된다. 도구 버전을 확인하고 UHD FPGA 이미지를 내려받는다.
docker version
docker compose version
uhd_config_info --version
sudo uhd_images_downloader
B210 연결 확인
USRP 하드웨어 드라이버(USRP Hardware Driver, UHD)는 OCUDU와 B210 사이에서 장치를 제어하고 I/Q 샘플을 전달한다. 현장 프로그래머블 게이트 어레이(Field-Programmable Gate Array, FPGA)는 B210 내부의 실시간 샘플 처리를 담당한다.
다음 명령은 gNB를 시작하지 않고 USB, UHD, FPGA 이미지, 송수신 채널을 확인한다.
lsusb
uhd_find_devices
uhd_usrp_probe --args 'type=b200'
확인할 항목은 USB 3.0 연결, 송신 채널 2개, 수신 채널 2개, FPGA 로딩, register loopback 통과 여부다. 실행 중인 다른 gNB가 B210을 점유하면
uhd_find_devices가 장치를 찾지 못할 수 있다.OCUDU 소스와 이미지 고정
소스 태그만 기록하면 같은 결과를 재현하기 어렵다. 태그와 40자 커밋 ID를 함께 고정한다.
install -d -m 0700 "${LAB_HOME}/repos/5g-baselines" "${SA_ROOT}" "${RUNTIME_DIR}"
git clone --depth 1 --branch release_26_10 \
https://gitlab.com/ocudu/ocudu.git \
"${OCUDU_SRC}"
test "$(git -C "${OCUDU_SRC}" rev-parse HEAD)" = \
'e0db566aac6e6376ddd60e83c84f58698fcc4d70'
고정한 소스로 gNB 이미지를 빌드한다.
docker build \
--tag ota-lab/ocudu-gnb:release-26.10 \
--build-arg OS=ubuntu \
--build-arg OS_VERSION=26.04 \
--build-arg NUM_JOBS="$(nproc)" \
--build-arg MARCH=native \
--file "${OCUDU_SRC}/docker/Dockerfile" \
"${OCUDU_SRC}"
docker run --rm \
ota-lab/ocudu-gnb:release-26.10 \
gnb --version
검증한 출력에는
OCUDU 5G gNB version 26.10.0과 커밋 축약 e0db566이 포함됐다.Open5GS 2.7.6 소스 고정
실제 랩은 OCUDU 릴리스 이미지와 5GC Compose 소스를 분리했다. Open5GS Dockerfile이
v2.7.6을 빌드하는 스냅샷은 다음 커밋이다.git clone https://gitlab.com/ocudu/ocudu.git "${STACK_SRC}"
git -C "${STACK_SRC}" checkout --detach \
'600357715ea3b3ef5339727357b2d7018a3ea024'
rg 'OPEN5GS_VERSION=v2\.7\.6' \
"${STACK_SRC}/docker/open5gs/Dockerfile"
test "$(git -C "${STACK_SRC}" rev-parse HEAD)" = \
'600357715ea3b3ef5339727357b2d7018a3ea024'
latest 이미지나 개발 브랜치를 사용하면 이 글의 기준선과 같은 결과라고 볼 수 없다.Open5GS 설정
네트워크 기능의 역할
Open5GS 올인원 이미지는 여러 네트워크 기능(Network Function, NF)을 한 컨테이너에서 실행한다.
| 약어 | 정식 명칭 | 역할 |
|---|---|---|
| AMF | Access and Mobility Management Function | UE 등록, 이동성, NAS 종단 |
| AUSF | Authentication Server Function | 5G 가입자 인증 |
| UDM | Unified Data Management | 가입자 신원과 인증 데이터 로직 |
| UDR | Unified Data Repository | 가입자와 정책 데이터 저장 |
| SMF | Session Management Function | PDU 세션 생성, IP 할당, UPF 제어 |
| UPF | User Plane Function | N3와 N6 사이의 사용자 패킷 전달 |
| NRF | Network Repository Function | NF 등록과 검색 |
| NSSF | Network Slice Selection Function | 네트워크 슬라이스 선택 |
| PCF | Policy Control Function | 세션과 서비스 정책 제공 |
비접속 계층(Non-Access Stratum, NAS)은 UE와 5GC 사이에서 등록, 인증, 이동성, 세션 제어 메시지를 운반한다.
코어 환경 파일 작성
공중 육상 이동통신망(Public Land Mobile Network, PLMN) 식별자는 이동 국가 코드(Mobile Country Code, MCC)와 이동망 코드(Mobile Network Code, MNC)로 구성된다. 이 랩의 시험값은 MCC
001, MNC 01이다.ocudu-open5gs-00101.env의 주소와 이름을 환경에 맞게 수정한다.MONGODB_IP=127.0.0.1
OPEN5GS_IP=<AMF_AND_UPF_CONTAINER_IP>
UE_IP_BASE=<UE_POOL_FIRST_THREE_OCTETS>
UPF_ADVERTISE_IP=<UPF_N3_IP>
DEBUG=false
SUBSCRIBER_DB=/open5gs/subscriber_db.csv
NETWORK_NAME_FULL=<LAB_NETWORK_NAME>
NETWORK_NAME_SHORT=<LAB_SHORT_NAME>
MCC=001
MNC=01
TZ=Asia/Seoul
SUBSCRIBER_DB에는 가입자 값을 직접 넣지 않는다. 컨테이너 안의 절대 경로를 지정하면 컨테이너를 다시 만들 때 가입자 목록을 유지할 수 있고, 엔트리포인트 로그에 인증 비밀값이 출력되는 문제도 피할 수 있다.SUCI 해소 설정
은폐 가입자 식별자(Subscription Concealed Identifier, SUCI)는 무선 구간에서 영구 가입자 식별자(Subscription Permanent Identifier, SUPI)를 감춘다. 가입자 식별자 은폐 해제 기능(Subscription Identifier De-concealing Function, SIDF)은 UDM에서 SUCI를 SUPI로 복원한다.
보존된 카드 판독 결과와 등록 로그에서 랩 카드는 SUCI 보호 방식 1과 홈 네트워크(Home Network, HNET) 공개키 ID 1을 사용했다. 해당 키 쌍이 USIM 프로파일과 일치하는지 확인한 다음 패치를 적용한다.
cd "${STACK_SRC}/docker/open5gs"
git apply --check \
"${LAB_ROOT}/ota-fuzz/open5gs-enable-example-hnet-id1.patch"
git apply \
"${LAB_ROOT}/ota-fuzz/open5gs-enable-example-hnet-id1.patch"
예제 개인키를 검증 없이 사용하면 SUCI 해소에 실패한다. 개인키 바이트와 전체 SUCI는 로그, 저장소, 공개 문서에 남기지 않는다.
MSISDN 확장은 기본망에 적용하지 않는다
국제 이동 가입자 전화번호(Mobile Station International Subscriber Directory Number, MSISDN)는 운영자가 가입자에게 할당하는 전화번호 식별자다.
open5gs-optional-msisdn.patch는 가입자 CSV에 선택적 MSISDN 열을 추가한다. 기본 데이터 세션에는 MSISDN이 필요하지 않으므로 이 기준선에는 패치를 적용하지 않는다. MSISDN이 필요한 가입자 설계와 패치 적용은 IMS와 SMS 구축 단계에서 수행한다.N6 TUN과 NAT 수정
터널(Tunnel, TUN) 인터페이스는 IP 패킷을 커널과 사용자 공간 사이에서 전달하는 가상 네트워크 장치다. Open5GS UPF는
ogstun을 통해 UE 주소 대역을 연결한다. 네트워크 주소 변환(Network Address Translation, NAT)은 UE의 사설 주소를 외부 통신에 사용할 주소로 변환한다.기본
setup_tun.py는 서브넷마다 TUN과 NAT 규칙을 반복 생성하고 외부 방향 패킷과 맞지 않는 출력 인터페이스 조건을 사용했다. 보존된 수정본은 TUN을 한 번만 만들고, 인접한 256개 서브넷을 하나의 요약 범위로 합치며, ogstun으로 들어온 UE 패킷에만 NAT를 적용한다.install -m 0644 \
"${LAB_ROOT}/ota-fuzz/runtime-sync/open5gs-internet/setup_tun.py" \
"${STACK_SRC}/docker/open5gs/setup_tun.py"
python3 -m py_compile \
"${STACK_SRC}/docker/open5gs/setup_tun.py"
<UE_POOL_FIRST_THREE_OCTETS>를 바꿨다면 생성 범위, 가입자 고정 IP, 방화벽 정책도 함께 검사한다.가입자 정보를 이미지 밖에 둔다
가입자 프로비저닝은 네트워크가 인증과 세션 정책을 적용할 수 있도록 가입자 레코드를 만드는 작업이다. 가입자 CSV는 이미지에 넣지 않고 런타임에 bind mount한다. bind mount는 호스트 파일을 컨테이너 경로에 연결하는 기능이다.
문서에는 다음 필드 구조만 기록한다.
# name,imsi,key,op_type,op_or_opc,amf,qci,ip_alloc
<MASKED_NAME>,<FULL_SUPI>,<K>,OPC,<OPC>,<AKA_AMF>,9,<UE_IP_OR_DYNAMIC>
USIM과 가입자 DB의 SUPI 또는 IMSI, 장기 인증키
K, OP 또는 OPc, 인증 관리 필드(Authentication Management Field, AMF)가 정확히 일치해야 한다. 여기서 AKA AMF는 인증 및 키 합의(Authentication and Key Agreement, AKA)에 쓰는 필드이며, 5GC의 Access and Mobility Management Function과는 다른 개념이다.서비스 품질 클래스 식별자(QoS Class Identifier, QCI)는 이 CSV 임포터가 서비스 품질(Quality of Service, QoS) 정책을 표현할 때 사용하는 필드다. 5G 서비스 품질 흐름은 5G QoS 식별자(5G QoS Identifier, 5QI)를 사용하지만, 이 임포터 스키마에는 기존
qci 필드가 남아 있다.순번(Sequence Number, SQN)은 인증값 재사용을 막는 카운터다. USIM과 UDM 또는 AUSF의 SQN이 허용 범위를 벗어나면 인증 동기화 실패 토큰(Authentication Synchronization Failure Token, AUTS)을 이용한 재동기화를 확인한다.
단일 네트워크 슬라이스 선택 지원 정보(Single Network Slice Selection Assistance Information, S-NSSAI)는 슬라이스를 식별한다. 이 랩은 슬라이스 서비스 유형(Slice/Service Type, SST) 1을 사용한다.
internet DNN은 SMF, UPF, 가입자 정책, UE 접속점 이름(Access Point Name, APN)에서 일치해야 한다.파일 권한은 내용 대신 메타데이터로 확인한다.
chmod 0600 "${RUNTIME_DIR}/subscriber_db.csv"
stat -c '%a %U %G %n' "${RUNTIME_DIR}/subscriber_db.csv"
이미지 빌드 때는 빈 일반 파일을 사용한다.
install -m 0600 /dev/null \
"${STACK_SRC}/docker/open5gs/subscriber_db.csv"
cd "${STACK_SRC}/docker"
OPEN_5GS_ENV_FILE="${CORE_ENV}" \
docker compose build 5gc
docker compose images 5gc
코어 관리 스크립트의 경로 수정
보존된
manage-ocudu-5g-sa.sh에는 설치 당시의 고정 경로가 있었다. 공개 가능한 실행본은 환경변수로 루트를 받도록 바꾼다.install -m 0755 \
"${LAB_ROOT}/ota-fuzz/manage-ocudu-5g-sa.sh" \
"${CORE_MANAGER}"
sed -i \
's|^root_dir=.*$|root_dir="${OTA_FUZZ_SA_ROOT:?set OTA_FUZZ_SA_ROOT}"|' \
"${CORE_MANAGER}"
rg -n '^root_dir=' "${CORE_MANAGER}"
원본 스크립트의
rf-up은 Compose에 지정된 gNB 이미지를 시작한다. 이 글에서는 정확한 릴리스 이미지를 보장하기 위해 rf-up 대신 뒤에서 제시하는 명시적 docker run을 사용한다.OCUDU gNB 설정
n78 30 MHz 2T2R 프로파일
5G 신무선(New Radio, NR)의 n78은 3.5 GHz 부근 시분할 이중화(Time Division Duplex, TDD) 대역이다. 부반송파 간격(Subcarrier Spacing, SCS)은 직교 주파수 분할 다중화(Orthogonal Frequency-Division Multiplexing, OFDM)의 부반송파 간격이다. 초당 백만 샘플(Mega-samples per second, Msps)은 SDR의 샘플 처리 속도를 나타낸다.
B210 2T2R 구성에서는 40 MHz 프로파일이 요구한 46.08 Msps를 실행하지 못했다. 검증한 기준은 30 MHz, 30.72 Msps, 2T2R이다.
추적 영역 코드(Tracking Area Code, TAC)는 이동성 관리 영역을 식별한다. 물리 셀 식별자(Physical Cell Identity, PCI)는 물리 계층에서 셀을 구분한다. 절대 무선주파수 채널 번호(Absolute Radio-Frequency Channel Number, ARFCN)는 캐리어 주파수를 숫자로 표현한다.
ocudu-gnb-b210-00101-n78-30mhz-2t2r.yml의 핵심 설정은 다음과 같다.cu_cp:
amf:
addrs: <AMF_N2_IP>
port: 38412
bind_addrs: <GNB_N2_N3_IP>
supported_tracking_areas:
- tac: 7
plmn_list:
- plmn: "00101"
tai_slice_support_list:
- sst: 1
ru_sdr:
device_driver: uhd
device_args: type=b200,num_recv_frames=64,num_send_frames=64
srate: 30.72
otw_format: sc12
tx_gain: <MEASURED_SAFE_TX_GAIN>
rx_gain: <MEASURED_RX_GAIN>
expert_cfg:
tx_mode: same-port
cell_cfg:
dl_arfcn: 632628
band: 78
channel_bandwidth_MHz: 30
common_scs: 30
nof_antennas_dl: 2
nof_antennas_ul: 2
plmn: "00101"
tac: 7
pci: 1
expert_phy:
pusch_channel_estimator_td_strategy: average
pusch_channel_estimator_cfo_compensation: true
log:
filename: stdout
all_level: info
broadcast_enabled: true
pcap:
mac_enable: true
mac_type: dlt
mac_filename: /tmp/gnb_mac.pcap
ngap_enable: true
ngap_filename: /tmp/gnb_ngap.pcap
검증 이력의 시험에서는 TX gain 80, RX gain 40을 사용했고 위 ARFCN에서 SSB ARFCN 631968이 파생됐다. 이 값은 B210 API의 장치 설정이며 안테나 단자의 절대 출력 전력이 아니다. 케이블, 감쇠기, 포트, 장치가 달라지면
<MEASURED_SAFE_TX_GAIN>과 <MEASURED_RX_GAIN>을 다시 측정해 정한다.tx_mode: same-port는 TDD 수신 시에도 각 채널의 TX/RX 커넥터를 선택한다. sc12는 USB 전송에서 각 I/Q 성분을 12비트로 표현한다. 물리 상향링크 공유 채널(Physical Uplink Shared Channel, PUSCH)은 UE의 상향 데이터와 제어 패킷을 운반한다. 반송파 주파수 오차(Carrier Frequency Offset, CFO)는 UE와 gNB의 반송파 사이에 생긴 주파수 차이다.패킷 캡처(Packet Capture, PCAP)는 프로토콜 패킷을 파일로 저장한다. 매체 접근 제어(Medium Access Control, MAC)는 무선 자원 스케줄링과 전송 블록 조립을 담당한다. 2T2R MAC PDU 캡처에는
mac_type: dlt를 사용했다.Docker 네트워크 오버레이
ocudu-26.10-docker-overlay.yml은 Compose 파일이 아니라 OCUDU의 추가 설정 파일이다. gNB 실행 시 두 번째 -c 인자로 전달한다.cu_cp:
amf:
addrs: <AMF_N2_IP>
bind_addrs: <GNB_N2_N3_IP>
metrics:
autostart_stdout_metrics: true
enable_json: true
remote_control:
bind_addr: 0.0.0.0
enabled: true
중앙 장치 제어 평면(Central Unit Control Plane, CU-CP)은 RRC와 NGAP을 처리한다. 중앙 장치 사용자 평면(Central Unit User Plane, CU-UP)은 SDAP와 PDCP 사용자 평면을 처리한다. 분산 장치(Distributed Unit, DU)는 RLC, MAC, PHY와 스케줄링을 처리한다. 이 랩은 세 기능을 하나의 gNB 프로세스에서 실행한다.
N2, N3, N4, N6 주소 계약
각 인터페이스에는 실제로 도달 가능한 주소를 지정해야 한다. 올인원 Open5GS 컨테이너 안의
127.0.0.x 주소는 NF 사이의 내부 통신용이므로 gNB에 광고하면 안 된다.| 경로 | 주소 소유자 | 필수 조건 |
|---|---|---|
| N2 | AMF 컨테이너 주소와 gNB 컨테이너 주소 | SCTP 38412 양방향 도달 |
| N3 | UPF 광고 주소와 gNB GTP-U 주소 | UDP 2152 양방향 도달 |
| N4 | SMF와 UPF 내부 주소 | PFCP association 성립, 이 올인원 구성에서는 컨테이너 내부 경로 사용 |
| N6 | ogstun과 컨테이너 외부 인터페이스 | IP forwarding, NAT, 반환 경로 정상 |
<AMF_N2_IP>, <UPF_N3_IP>, <GNB_N2_N3_IP>는 이 계약에 맞춰 정한다. 주소를 바꾼 뒤에는 설정 파일만 확인하지 말고 실제 소켓 바인드, 컨테이너 라우팅, 패킷 카운터를 함께 확인한다.기동
Open5GS 코어만 먼저 시작
gNB보다 먼저 5GC를 시작한다.
export OTA_FUZZ_SA_ROOT="${SA_ROOT}"
export OTA_FUZZ_CORE_ENV="${CORE_ENV}"
export OTA_FUZZ_GNB_CONFIG="${GNB_CONFIG}"
"${CORE_MANAGER}" core-up
"${CORE_MANAGER}" status
컨테이너 상태,
ogstun, IP forwarding, NAT, 소켓을 확인한다.docker inspect \
--format 'status={{.State.Status}} health={{.State.Health.Status}} restarts={{.RestartCount}}' \
open5gs_5gc
docker exec open5gs_5gc ip -brief address show ogstun
docker exec open5gs_5gc sysctl net.ipv4.ip_forward
docker exec open5gs_5gc nft list table ip nat
docker exec open5gs_5gc ss -lntup
docker exec open5gs_5gc ss -lnpA sctp
합격 조건은
running/healthy, 재시작 횟수 0, ogstun의 UE 풀 게이트웨이, IPv4 forwarding 활성화, UE 대역을 처리하는 NAT, 정상적인 NGAP, GTP-U, PFCP 소켓이다. 같은 NAT 규칙이 대량으로 중복돼 있으면 setup_tun.py 적용 여부부터 다시 확인한다.MongoDB의 가입자 개수는 문서 전체를 출력하지 않고 숫자만 확인한다.
docker exec open5gs_5gc \
mongosh --quiet --eval \
'db.getSiblingDB("open5gs").subscribers.countDocuments({})'
보호된 CSV의 데이터 행 수와 MongoDB 문서 수가 같아야 한다. 실행 중 CSV를 새 파일로 교체하면 file-level bind mount가 이전 inode를 가리킬 수 있다. 이 경우 5GC 컨테이너를 재생성하고 컨테이너 안의 파일과 데이터베이스 개수를 다시 확인한다.
gNB 실행 전 충돌 확인
하나의 B210은 동시에 하나의 gNB만 사용한다.
docker ps --format '{{.Names}} {{.Image}}'
pgrep -a 'gnb|nr-softmodem|srsran' || true
uhd_find_devices
docker network inspect "${COMPOSE_PROJECT_NAME}_ran"
ocudu_gnb, ocudu26_gnb, srsran25_gnb, OAI 기반 gNB 가운데 하나만 실행해야 한다. <GNB_N2_N3_IP>는 Docker RAN 네트워크에서 사용하지 않는 주소로 지정한다.OCUDU 릴리스 이미지 실행
장치 연결, 링크 손실, 감쇠량, 단말 입력 범위, 현지 규정과 시험 장소의 사용 조건을 먼저 확인한다. 다음 게이트는 실수로 gNB를 시작하지 않도록 명시적인 환경변수를 요구한다.
test "${ALLOW_RF_TX:-}" = 'YES' || {
printf '%s\n' 'RF authorization gate is closed.' >&2
exit 2
}
uhd_find_devices | rg -q 'type: b200'
test "$(docker inspect --format '{{.State.Health.Status}}' open5gs_5gc)" = 'healthy'
docker run -d \
--name ocudu26_gnb \
--restart no \
--privileged \
--cap-add SYS_NICE \
--cap-add SYS_PTRACE \
--network "${COMPOSE_PROJECT_NAME}_ran" \
--ip '<GNB_N2_N3_IP>' \
--mount type=bind,src=/dev/bus/usb,dst=/dev/bus/usb \
--mount type=bind,src=/usr/share/uhd/images,dst=/usr/share/uhd/images,readonly \
--mount type=bind,src="${GNB_CONFIG}",dst=/gnb_config.yml,readonly \
--mount type=bind,src="${GNB_OVERLAY}",dst=/gnb_compose_config.yml,readonly \
--mount type=volume,src=ocudu26_gnb_storage,dst=/tmp \
ota-lab/ocudu-gnb:release-26.10 \
gnb -c /gnb_config.yml -c /gnb_compose_config.yml
--privileged는 검증 환경에서 USB SDR과 필요한 네트워크 장치 접근을 단순화하기 위해 사용한 권한이다. 장기 운영 환경에서는 장치 cgroup, Linux capability, 네트워크 권한을 실제 필요 범위로 줄여야 한다.시작 직후 상태 확인
docker inspect \
--format 'running={{.State.Running}} status={{.State.Status}} restarts={{.RestartCount}}' \
ocudu26_gnb
docker logs --tail 300 ocudu26_gnb
로그는 다음 순서로 확인한다.
- OCUDU 26.10.0과 커밋 축약이 출력된다.
- B210이 USB 3.0으로 열리고 30.72 Msps가 설정된다.
- 채널 0과 1 모두
TX/RX포트를 사용한다. - n78, 30 MHz, 2T2R, PLMN
00101, TAC 7, PCI 1이 적용된다. - SCTP association이 성립한다.
NGSetupRequest뒤에NGSetupResponse가 나온다.- late, underflow, overflow, disconnect, fatal 오류가 반복되지 않는다.
NG Setup 성공은 N2와 PLMN, TAC, S-NSSAI가 맞는다는 뜻이다. UE 접속과 사용자 패킷 경로까지 성공했다는 뜻은 아니다.
종료
gNB를 먼저 중지하고 B210이 해제됐는지 확인한다.
docker stop --time 10 ocudu26_gnb
docker rm ocudu26_gnb
docker ps --filter name=ocudu26_gnb
uhd_find_devices
코어까지 종료하려면 다음 명령을 실행한다.
export OTA_FUZZ_SA_ROOT="${SA_ROOT}"
export OTA_FUZZ_CORE_ENV="${CORE_ENV}"
"${CORE_MANAGER}" down
검증
계층별로 판정한다
하나의 연결 표시로 전체 시스템을 합격 처리하지 않는다.
| 단계 | 필수 증거 | 아직 증명하지 못하는 것 |
|---|---|---|
| 이미지 | gnb --version, 소스 커밋, image ID | 설정과 무선 동작 |
| SDR | UHD probe, USB 3.0, 두 송신과 두 수신 채널 | 셀 방송 |
| 5GC | running/healthy, NF 소켓, PFCP association | gNB 연결 |
| N2 | NGSetupResponse | UE 접속 |
| 하향링크 | UE의 SSB와 SIB1 수신 | UE의 상향 전송 |
| 초기 접속 | PRACH, RAR, Msg3, RRCSetupComplete | 가입자 인증 |
| N2 UE 절차 | InitialUEMessage | 가입자 허용 |
| NAS | 인증, Security Mode, Registration Complete | PDU 세션 |
| 세션 | PDU Session Establishment Accept, PFCP 세션, QFI와 DRB 매핑, UE IP | 외부 통신 |
| N3 | GTP-U TEID가 있는 양방향 UE 패킷 | N6 반환 경로 |
| N6 | ogstun과 NAT 카운터, 인터페이스 고정 HTTP 응답 | IMS 서비스 |
동기화 신호 블록(Synchronization Signal Block, SSB)은 UE가 셀 시간과 주파수 동기를 얻는 하향 블록이다. 시스템 정보 블록 1(System Information Block Type 1, SIB1)은 PLMN, TAC, 셀 접근 조건, 랜덤 액세스 설정을 방송한다.
물리 랜덤 액세스 채널(Physical Random Access Channel, PRACH)은 UE가 최초 상향 접속을 시도하는 채널이다. 랜덤 액세스 응답(Random Access Response, RAR)은 gNB가 검출한 PRACH에 응답해 UE에 상향 자원을 제공한다. Msg3는 UE가 해당 자원으로 전송하는 첫 RRC 요청이다. 무선 자원 제어(Radio Resource Control, RRC)는 UE와 gNB 사이의 무선 연결을 관리한다.
InitialUEMessage는 UE의 첫 NAS 메시지를 gNB가 AMF로 전달하는 NGAP 메시지다. NAS Security Mode는 이후 NAS 메시지에 사용할 암호화와 무결성 알고리즘을 활성화한다.서비스 품질 흐름 식별자(QoS Flow Identifier, QFI)는 5G QoS 흐름을 식별한다. 데이터 무선 베어러(Data Radio Bearer, DRB)는 UE 데이터를 운반한다. 터널 종단점 식별자(Tunnel Endpoint Identifier, TEID)는 GTP-U 터널의 양 끝을 식별한다. UE에 IP 주소가 할당됐다는 사실만으로 N3와 N6 양방향 전달이 증명되지는 않는다.
종단간 검증 결과
검증에서는 OCUDU
release_26_10 이미지와 소스 커밋을 고정한 뒤 B210을 n78, 30 MHz, 30.72 Msps, 2T2R로 구동했다. Open5GS 2.7.6과 N2 NG Setup을 확인했으며, 새 실행에서 PRACH, RRC Setup, InitialUEMessage, NAS 등록, internet PDU 세션까지 이어지는 절차를 확인했다. 마지막으로 UPF 게이트웨이, DNS, NAT를 거쳐 UE의 셀룰러 인터페이스에 묶은 HTTP와 HTTPS 요청이 외부 응답을 받는 것까지 검증했다.장애와 수정 내역
| 증상 | 확인한 원인 | 적용한 수정 | 재검증 기준 |
|---|---|---|---|
| 40 MHz 2T2R 기동 실패 | B210 두 채널에서 46.08 Msps 마스터 클록 사용 불가 | 30 MHz와 30.72 Msps로 고정 | UHD가 2TX와 2RX를 열고 계속 실행 |
| UE가 셀을 보지만 접속하지 않음 | 초기 999/70 환경에서 PRACH 증거가 없음 | 001/01 기준선에서 계층별로 다시 확인 | PRACH, RRC, InitialUEMessage를 각각 확인 |
| Registration Reject cause 7 | UDM에 HNET 키 ID 1 매핑이 없음 | USIM 공개키와 짝이 맞는 개인키 매핑 | SUCI 해소 뒤 가입자 조회와 인증 진행 |
| 5GC 재생성 후 가입자 한 명만 남음 | SUBSCRIBER_DB에 가입자 한 명을 인라인으로 지정 | /open5gs/subscriber_db.csv 절대 경로 사용 | CSV 데이터 행 수와 DB 문서 수 일치 |
| N6 외부 패킷 실패 | 중복 NAT 규칙과 잘못된 oifname ogstun 조건 | TUN 하나, 요약 대역 하나, iifname ogstun NAT | HTTP와 HTTPS 응답, 양방향 카운터 증가 |
| PCAP 파일 미생성 | 호스트 bind 디렉터리에 컨테이너 UID 쓰기 권한이 없음 | 쓰기 가능한 격리 volume 사용 | 시작 직후 /tmp PCAP 생성 |
999/70과 001/01 결과만으로 특정 PLMN에서 OCUDU가 동작하지 않는다고 일반화할 수 없다. 이 실험이 보여 주는 범위는 특정 단말 펌웨어, USIM, PLMN 조합에서 접속 절차가 다르게 진행됐다는 사실이다.실행 증거를 남기는 기준
각 실행에는 다음 메타데이터를 남긴다.
- UTC와 KST 시각, 호스트 시각 동기 상태
- OCUDU, Open5GS, UHD, FPGA 버전
- 소스 커밋, 이미지 ID, 설정 파일 SHA-256
- 시험 구성, 연결 포트, 감쇠 구성
- 비밀값을 제거한 gNB와 5GC 로그
- MAC과 NGAP PCAP, 필요한 N3와 N6 캡처
- UE 모델, 모뎀 빌드, 운영체제 버전
256비트 보안 해시 알고리즘(Secure Hash Algorithm 256-bit, SHA-256)은 실행 사이에 파일이 바뀌었는지 확인하는 데 사용한다. PCAP과 원시 로그에는 SUPI, SUCI, 임시 UE 식별자, IP 주소가 들어갈 수 있으므로 원본은 보호된 저장소에 보관하고 공개본에는 레닥션을 적용한다.
이 기준선에 포함되지 않은 기능
다음 항목은 이 글의 완료 범위가 아니다.
- 새 호스트에서 의존성 설치부터 외부 통신까지 자동으로 다시 수행한 clean-room 재현
- 관리 스크립트의 완전한 경로 독립성과 릴리스 이미지 자동 고정
- 최소 capability만 사용하는 비특권 컨테이너 배포
- IPv6 PDU 세션과 IPv6 N6 경로
- 다수 UE 동시 세션, 장시간 부하, 재기동 내구성
- SMS, MMS, VoNR, IMS 영상통화
IP 멀티미디어 서브시스템(IP Multimedia Subsystem, IMS)은 데이터 연결 위에서 메시지와 통화 서비스를 제공한다. 세션 개시 프로토콜(Session Initiation Protocol, SIP)은 IMS 등록과 세션 제어에 사용한다. P-CSCF(Proxy-Call Session Control Function)는 UE의 첫 SIP 프록시이고, S-CSCF(Serving-CSCF)는 IMS 등록과 세션 라우팅을 담당한다. SMSC(Short Message Service Centre)는 SMS를 저장하고 전달하며, MMSC(Multimedia Messaging Service Centre)는 MMS 알림과 콘텐츠를 처리한다.
이 기능들은 기본
internet DNN을 만드는 것만으로 활성화되지 않는다. 후속 글에서 IMS 가입자, SIP 경로, SMS, MMS, 음성, 영상 기능을 순서대로 구축한다.재실행 체크리스트
- OCUDU 태그, 커밋, 이미지 ID를 기록한다.
- Open5GS Dockerfile의
v2.7.6고정과 적용 패치를 기록한다. - B210 점유 여부, USB 3.0, 2T2R, UHD 상태를 gNB 기동 전에 확인한다.
- PLMN, TAC, SST, DNN, SUCI 키 ID, 가입자 인증값의 소유자를 확인한다.
SUBSCRIBER_DB=/open5gs/subscriber_db.csv와 파일 모드0600을 확인한다.- 5GC의 health, N4,
ogstun, forwarding, NAT를 확인한다. - gNB 실행 전 링크 구성과 장치 입력 범위를 검토하고 명시적 실행 게이트를 연다.
- NG Setup, SSB와 SIB1, PRACH와 RRC, NGAP와 NAS, PDU 세션, N3와 N6를 각각 판정한다.
- UE에서 수행할 세부 시험은 Galaxy S26 Ultra 종단간 검증의 절차를 따른다.
- 시험 종료 후 gNB 컨테이너 제거, B210 해제, 코어 종료 여부를 확인한다.
참고 자료
이전: B210, O-CU/DU, Open5GS 5G SA 아키텍처
다음: IMS 등록과 SMS over IMS 구축
다음: IMS 등록과 SMS over IMS 구축