OCUDU Docker Split Deployment

Setup

The OCUDU repository is officially online, as SRS has transferred all development work to the new GitLab repository. In this article I am going to walk through deploying the Split Docker and the troubleshooting steps I went through to get the end-to-end connectivity from the UE to the Internet.

Pre requisites

  • OCUDU
  • Docker & Docker Compose
  • USRP B210 with GPSDO or External Clock Source
  • 5G Capable UE and Test SIM
    • Motorola Moto G Power 5G (2023)
    • IMSI: 001010000560128
    • K: FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
    • OPC: 9ED73ED8F0FD186430CA9D7ED728EA0F
    • APN: apn (IPv4)
  • Computer with Internet connectivity
    • OS: Ubuntu 22.04.5 LTS x86_64
    • Kernel: 6.8.0-100-generic

For this example, I am running everything on a single machine – gNB DU, gNB CU CP, gNB CU UP, and 5G Core

OCUDU Install

Clone the OCUDU repository, then move into the ~/ocudu directory

$ git clone https://gitlab.com/ocudu/ocudu.git
$ cd ocudu

Docker

Inspect the ~/ocudu/docker directory and you will see the following files

docker-compose.yml defines the following

  • RAN Network: docker_ran (10.53.1.0/24)
  • 5GC IP Address: 10.53.1.2

docker-compose.split.yml defines the following

  • F1-U Network: f1u_network (172.18.10.0/24)
    • DU F1-U IP Address: 172.18.10.3
    • CU UP F1-U IP Address: 172.18.10.2
  • gNB DU IP Address: 10.53.1.6
  • gNB CU CP IP Address: 10.53.1.4 (E1AP, F1AP, AMF Bind)
    • AMF address: 10.53.1.2
  • gNB CU UP IP Address: 10.53.1.5
  • Config Files
    • ${CUCP_CONFIG_PATH:-../configs/cu_cp.yml}
    • ${CUUP_CONFIG_PATH:-../configs/cu_up.yml}
    • ${DU_CONFIG_PATH:-../configs/du_rf_b200_tdd_n78_20mhz.yml}

Initial Build

If you want to run the Split configuration then you will use the following command from ~/ocudu

docker compose -f docker/docker-compose.yml -f docker/docker-compose.split.yml up

Out of the box, everything should start. The first time you run the docker compose up command will take awhile to build. At this point, the UE should be able to connect to the network….

  • If UE IMSI, K, and OPC, and APN are not configured in Open5gs, the UE will be rejected or PDU Session will not be setup
    • Access the webui @ localhost:9999 on the host
    • See 5GC additional configurations below to have changes be persistent (will require removing the open5gs image, then have it rebuilt with the docker compose up command)
  • CU UP NG-U interface does not seem to be configured properly and may require configuration changes
    • See cu_up.yml additional configurations below
  • The host requires routing configuration changes to allow incoming Downlink traffic to reach the ocudu network
    • See Host Network Configuration below

Additional Configurations

There are a few configurations I change locally – though it may not be necessary for you

  • ~/ocudu/configs/du_rf_b200_tdd_n78_20mhz.yml
    • all_level: debug
  • ~/ocudu/configs/cu_cp.yml
    • all_level: debug
  • ~/ocudu/configs/cu_up.yml
    • ngu > socket > bind_addr: 10.53.1.5
    • all_level: debug
  • ~/ocudu/docker/open5gs/add_users.py
    • opc=”63bfa50ee6523365ff14c1f45f88737d”, amf=”9001″, apn=”apn”, qci=”9″, ip_alloc=””):
  • ~/ocudu/docker/open5gs/open5gs.env
    • SUBSCRIBER_DB=<IMSI>,<K>,opc,<OPC>,8000,9,10.45.1.2
    • TZ=America/New_York

Changing the cu_up.yml to 10.53.1.5 has been the only way I have gotten the UE to have internet connectivity.

Host Network Configuration

When the docker containers are running, you will need to check the hosts network configurations with ifconfig

# docker_ran bridge interface
br-0fea5da6616c: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 10.53.1.1 netmask 255.255.255.0 broadcast 10.53.1.255
# What interface is connected to the internet
enp36s0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.2.226 netmask 255.255.255.0 broadcast 192.168.2.255

Now, from the host you will need to enter the following commands… Make sure to change <br-docker_ran> and <inet_interface> to your specific configuration

sudo ip route add 10.45.0.0/16 via 10.53.1.2 dev <br-docker_ran>

sudo iptables -t nat -A POSTROUTING -s 10.45.0.0/16 -o <inet_interface> -j MASQUERADE

for example, I would configure the following

sudo ip route add 10.45.0.0/16 via 10.53.1.2 dev br-0fea5da6616c
sudo iptables -t nat -A POSTROUTING -s 10.45.0.0/16 -o enp36s0 -j MASQUERADE

Now when your UE is connected to the network, it should be able to reach the internet.

Troubleshooting

For awhile I could not figure out why my UE could connect to the ocudu network, but would not get an internet connection…

I ran tcpdump on the host machine, and I ran it in open5gs core container as well. Since the open5gs container has internet connectivity, I was able to install tcpdump and run it in the container. Use the docker exec -it <container> bash command to open an interactive bash shell in the container.

docker exec -it open5gs_5gc bash

apt install tcpdump

tcpdump -i any -w docker_5gc.pcap

So now I had two packet captures to see where the data flow was failing.

The capture below is from the host –

  1. 1726: F1-U transfers UL NR RAN container from DU to CU UP (172.18.10.3 to 172.18.10.2)
  2. 1732: GTP-U PDU Session Container goes from the gNB CU UP to the 5G Core (10.53.1.5 to 10.35.1.2)
  3. 1742: The host receives the UE data to DNS server (10.45.1.2 to 8.8.8.8)
  4. 1744: The host sends the data to the internet (192.168.2.226 to 8.8.8.8)
  5. 1763: The host receives the UL data from the DNS server (8.8.8.8 to 192.168.2.226)
  6. 1764: The host packages the data to be sent to the UE (8.8.8.8 to 10.45.1.2)
  7. The packet stops here… There is no subsequent flow to the UPF

At the 5G Core side, we can see that the UL data is received (correlated to packet # 1732 on the host) though it never receives DL data from the host.

So now we can assume that there is something broken between the host and the 5GC on the docker_ran network.

This issue is fixed with the HOST NETWORK CONFIGURATION section which sets the route for UE traffic on 10.45.0.0/16 to reach the docker_ran network (10.53.1.1/16) where the 5GC is located. See the EXAMPLE DATA FLOW section below, to see what a working configuration looks like.

UE Packet Capture

If you wanted to go a step further, you can run tcpdump on rooted android devices. I use scrcpy to mirror android devices on my laptop, but if you connect your UE to your laptop and use adb you should be able to run tcpdump.

Verify if tcpdump is installed on the device and what the path is to tcpdump

adb devices
List of devices attached
ZY22HCLS7Z device
devonn:/ # which tcpdump
/system/bin/tcpdump

then exit back to your host CLI and run tcpdump on the UE.

adb shell su -c "/system/bin/tcpdump -i any -s 0 -w /sdcard/capture.pcap"
tcpdump: data link type LINUX_SLL2
tcpdump: listening on any, link-type LINUX_SLL2 (Linux cooked v2), snapshot length 262144 bytes

When you are finished with the capture, pull the pcap to your computer.

adb pull /storage/emulated/0/capture.pcap
/storage/emulated/0/capture.pcap: 1 file pulled, 0 skipped. 6.1 MB/s (24576 bytes in 0.004s)

Open5gs Connection Issues

If Open5gs is running on the host machine then you will see this error

Make sure all Open5gs services are stopped locally on the host, then you will need to run the docker compose down –remove-orphans command before re-running the docker compose up command again.

Example data flow

Docker Commands

The following docker commands are run on the host, and are helpful for observing configurations, logs, or interacting with the containers.

docker images

List current loaded images

docker rmi -f <image>

Forcefully removes loaded images

docker network ls

Lists configured docker networks

docket network inspect <network>

Displays details of container networks

docker network inspect docker_ran
[
{
"Name": "docker_ran",
"Id": "0fea5da6616c930c5fc45e73698ba57f183e3a925bee62f3b1dbd919c66f1d12",
"Created": "2026-02-20T10:44:42.206208235-05:00",
"Scope": "local",
"Driver": "bridge",
"EnableIPv4": true,
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": null,
"Config": [
{
"Subnet": "10.53.1.0/24",
"IPRange": "",
"Gateway": "10.53.1.1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Options": {},
"Labels": {
"com.docker.compose.config-hash": "0edf626a1c1e248ce1822695de1063c5aafee1350a92a54495ab8f992c516cb8",
"com.docker.compose.network": "ran",
"com.docker.compose.project": "docker",
"com.docker.compose.version": "5.0.1"
},
"Containers": {
"1c7b1a4eaa1f024ec654c4902dbb32f41cfad55cef9bb29a9315f2ecf1d88a66": {
"Name": "ocudu_du",
"EndpointID": "2c3847e91df4e15c600144aaec7e8217f348e6a0e61ac590a31186b5b2426c88",
"MacAddress": "86:6c:c1:24:b9:df",
"IPv4Address": "10.53.1.6/24",
"IPv6Address": ""
},
"6f1416e11f85f86e86abc1e472374fec6b02ff7ff43924cf306d1b20cc016190": {
"Name": "open5gs_5gc",
"EndpointID": "6a9939324857b0a8eb44eb092f6968d9e0ce6da41dd73d8225873cd2a7983853",
"MacAddress": "ea:75:5c:b4:70:7b",
"IPv4Address": "10.53.1.2/24",
"IPv6Address": ""
},
"9fe29132053e86a277d9b49973db556a2b9d878f49d1cbbdac19f94323fac33d": {
"Name": "ocudu_cu_up",
"EndpointID": "5cd10cb92b6b7a45d6645479360fac3365533391b5d14a50e2599d0a7aa24bbf",
"MacAddress": "aa:db:41:41:4f:5d",
"IPv4Address": "10.53.1.5/24",
"IPv6Address": ""
},
"c01bb0defdea15114556761ee81761e1391d83dcf5091517799b04c73d008dcf": {
"Name": "ocudu_cu_cp",
"EndpointID": "4421d977a794c81163d0e346380a5368298264bc3f6e7e55c337b03215b24d5d",
"MacAddress": "46:41:ca:39:c6:df",
"IPv4Address": "10.53.1.4/24",
"IPv6Address": ""
}
},
"Status": {
"IPAM": {
"Subnets": {
"10.53.1.0/24": {
"IPsInUse": 7,
"DynamicIPsAvailable": 249
}
}
}
}
}
]

docker container ls or docker contain ps

Both commands display the same or similar information about currently running containers

docker container stats

Displays real time container process information

docker exec -it <container> bash

Allows interaction with a container via the defined shell (bash, in this example). This is useful for viewing or copying logs, installing additional applications, viewing container network information, etc.

docker logs -f <container>

Displays the logs of the specified container, which are also displayed in the attached view after running the docker compose up command without the -d (detached) flag.

docker inspect <container>

Provides detailed information on the container

docker rm <container>

Removes a container

docker stop <container>

Stops a container from running

docker start <container>

Starts a container

Responses

  1. Hi,

    Great work, and thanks for sharing this blog post and video!
    I have a couple of questions:

    Could you provide more details about your testbed setup, particularly the hardware? Did you use a virtualized/cloud environment or a physical machine?

    You mention deploying functional split 7.2. However, in O-CU/DU (formerly srsRAN), it’s documented that a commercial RU is required to establish a real connection (compliant with FS 7.2) to a UE (mobile phone). Are you using such an RU? If not, how did you achieve this?

    1. Sorry for the confusion, it is actually just the split DU/CU configuration. Not true LLS 7.2 as I was not using an RU, just a USRP. So there is no O RAN FH CU data.

      This particular deployment was just on a physical machine.

Leave a Reply

Discover more from

Subscribe now to keep reading and get access to the full archive.

Continue reading