5G O-RAN End-to-End Simulation

Bringing up Open5GS, srsRAN, and the O-RAN SC RIC as one working chain

01Problem

A 5G stack is four independent programs that only work in one order: core, RIC, base station, handset. Reading about the interfaces between them does not tell you what each one prints when it is ready, or which failure belongs to which layer. The goal of this lab was to bring the whole chain up on a shared server with no radio hardware, and to write down the exact command and the exact confirmation signal for every step.

02What I built

  • Brought up the four components in order — Open5GS 5GC under Docker Compose, the O-RAN SC RIC, an srsRAN gNB with E2 enabled, and srsUE in its own network namespace.
  • Ran the entire chain over ZMQ radio emulation on a remote lab server reached by SSH, using the upstream gnb_zmq.yaml and ue_zmq.conf configurations rather than hand-written ones.
  • Documented a gNB start-up failure end to end: the error, the four things I checked, and the fact that none of them was the cause.

03How it works

srsUE talks to an srsRAN gNB over a ZMQ radio link, so no SDR hardware is involved. The gNB connects north to the Open5GS core — the AMF address it dials has to match the NGAP address inside the core container's amf.yaml — and sideways to the O-RAN SC RIC over E2. The core and the RIC each run as their own Docker Compose stack, and the UE runs in a separate Linux network namespace so its traffic stays separable from the host's.

1Start the coredocker compose up 5gc in srsRAN_Project/docker starts Open5GS; docker exec into the open5gs_5gc container confirms the AMF NGAP address the gNB will need.
2Start the RICdocker compose up in oran-sc-ric brings up the RIC. The log line "RMR is ready now..." is the point at which it will accept an E2 connection.
3Start the gNB./gnb -c gnb_zmq.yaml e2 with the AMF address and local bind address set, using the configuration file downloaded from the srsRAN documentation.
4Attach the UEip netns add ue1 creates the namespace, then ./srsue ue_zmq.conf starts the handset; a successful attach completes the chain from core to UE.

04Results

  • The 5GC container reports its services running before the gNB is started, and amf.yaml inside the container shows the NGAP address the gNB configuration has to match.
  • The RIC prints "RMR is ready now...", which is the readiness signal for the E2 termination.
  • After the rebuild the gNB and the UE both start, and core, RIC, gNB, and UE run together as one chain.
Key takeaway The stack comes up in exactly one order, and each step has one line of output that says whether to continue. Recording those four lines is what turned a session of guesswork into a lab someone else can repeat.

05What went wrong

Symptom
The gNB exited during start-up with an INI error reporting that it could not read a section of cu_cp.amf, so no E2 session was ever established.
Hypotheses
The AMF address in the YAML was wrong; or the gnb_e2ap.pcap file that E2AP logging writes to was missing; or its filename in the YAML did not match the file on disk; or its path or permissions blocked the write.
Tests
Read amf.yaml inside the open5gs_5gc container and confirmed the NGAP address matched the configuration. Confirmed gnb_e2ap.pcap existed, compared the filename in the YAML against the file, relaxed the file permissions, and moved the file to another directory with the path updated. The same error appeared every time.
Fix
None of the four was the cause. The checked-out srsRAN source was out of date; rebuilding from the current upstream instructions made the same configuration work.
Takeaway
The error named a configuration section, so I spent the session inside the configuration. A correct configuration can still fail because of the environment around it, and the version of the program producing the message is now the first thing I check, not the last.

06Engineering decisions

  • Run every component in its own container or namespace instead of on the host. The core, the RIC, and the UE all want their own network stack, and the namespace is what keeps the UE's traffic separable.
  • Use ZMQ radio emulation instead of an SDR front end. There is no radio hardware in the lab, and ZMQ makes the whole chain reproducible over an SSH session on a shared server.
  • Take configuration files from the upstream documentation rather than writing my own. When the gNB failed that made my configuration the least likely explanation, which is exactly how the search ended up on the source version.

07Stack & artifacts