Skip to content

feat(mocn): add multi-PLMN MOCN support for OAI gNB#271

Open
kevin8023c wants to merge 7 commits into
duranta-project:developfrom
kevin8023c:pr/mocn-2-cores
Open

feat(mocn): add multi-PLMN MOCN support for OAI gNB#271
kevin8023c wants to merge 7 commits into
duranta-project:developfrom
kevin8023c:pr/mocn-2-cores

Conversation

@kevin8023c

@kevin8023c kevin8023c commented Jul 6, 2026

Copy link
Copy Markdown

Summary

This PR fills in the missing NR gNB-side support needed to run an end-to-end 5G MOCN setup in OAI.

The main goal is to allow one OAI gNB to advertise multiple PLMNs in SIB1 and let each UE select the appropriate PLMN based on its IMSI. The base OAI/Duranta code already has selected-PLMN-aware NGAP/AMF routing support. This PR wires up the missing NR gNB-side multi-PLMN broadcast/configuration and UE-selection path so that the selected PLMN can be used to route each UE to the Core serving that PLMN.

In other words, this PR completes the key NR gNB-side path needed for a working 5G MOCN deployment in OAI. The changes were ported onto the latest Duranta/OAI weekly develop branch 2026.w27 and validated end-to-end with two independent L25GC+ cores (a free5GC-derived 5G Core).

Main changes

F1AP served-cell information

  • Extend served-cell information to carry per-PLMN slice information.
  • Add served PLMN structures for multiple PLMNs.
  • Update F1AP encode/decode/copy/equal helpers.
  • Update the corresponding F1AP unit test.

gNB config and SIB1 broadcasting

  • Parse all configured plmn_list entries and their snssaiList.
  • Propagate the served PLMN list into the MAC/RRC SIB1 configuration path.
  • Remove the previous hard-coded single-PLMN assumption so SIB1 can advertise multiple PLMNs.

CU/DU PLMN validation

  • Replace the legacy single-PLMN CU/DU check with a MOCN-aware validation.
  • The DU/cell is accepted only if every DU-advertised PLMN is present in the CU/gNB configured PLMN list.
  • This preserves meaningful validation for misconfigured non-MOCN cases while allowing a CU to support more PLMNs than a given DU/cell advertises.

nrUE PLMN selection

  • Let nrUE walk the SIB1 plmn-IdentityInfoList.
  • Match PLMN entries against the UE IMSI MCC/MNC.
  • Set selected_plmn_identity according to the matched PLMN.

RRC / selected PLMN propagation

  • Carry the UE-selected serving PLMN through the NR gNB UE context path.
  • The existing selected-PLMN-aware NGAP/AMF routing support in the base code can then route UE signalling to the AMF/Core serving the selected PLMN.
  • This PR therefore provides the missing multi-PLMN broadcast/configuration and UE-selection pieces needed to exercise that routing end-to-end in a 5G MOCN setup.

O1 / telnet exporter adaptation

  • Update the O1/telnet exporter to read slice information from the new served-PLMN list layout after the F1AP served-cell structure change.

Reference configs

  • Include the gNB/UE/UE2 configuration used for the two-core validation setup.
  • These config changes are mainly provided as a reproducibility reference, so reviewers can see exactly how the setup was run.
  • If maintainers prefer not to modify default configs, these can be moved to example files or described only in the PR body.

Validation

This PR was built and validated on the latest Duranta/OAI weekly develop branch 2026.w27 using a two-core L25GC+ testbed.

L25GC+ is a free5GC-derived 5G Core with UPF-side optimizations. In this validation setup, two independent L25GC+ cores were used to emulate two operators / two core networks.

Test topology:

  • node0: OAI gNB and two OAI nrUE instances.
    • Both nrUE instances run on the same OAI node as the gNB.
    • The UEs are separated using the OAI multi-UE namespace setup.
  • node1: L25GC+ Core1 / PLMN 20893 / UPF1.
  • node2: L25GC+ Core2 / PLMN 20894 / UPF2.
  • node3: DN1 for Core1.
  • node4: DN2 for Core2.

Validation results:

  • The gNB establishes NGAP/SCTP connections with both Core1 AMF and Core2 AMF.
  • UE1 selects PLMN 20893 based on its IMSI.
  • UE1 attaches through Core1, obtains a Core1-assigned IP address, and reaches DN1 through Core1 UPF/GTP-U.
  • UE2 selects PLMN 20894 based on its IMSI.
  • UE2 attaches through Core2, obtains a Core2-assigned IP address, and reaches DN2 through Core2 UPF/GTP-U.
  • UE1 → DN1 ping succeeds.
  • UE2 → DN2 ping succeeds.
  • UE1 → DN1 and UE2 → DN2 simultaneous iperf3 tests succeed.

Screenshots / figures included in this PR:

  1. Testbed topology showing node0–node4, PLMN/Core mapping, UE namespaces, and DN paths.
  2. Concurrent UE1/UE2 ping tests.
  3. Concurrent UE1/UE2 iperf3 tests.
  4. Two-AMF NGAP setup success.
image 853223007d33cec70ec9672e977fd9d1 89cd3d7f31ed38a2fb1afca023c7e927 b3963feb0d3c35900ab5907f7f60c2e0

Copilot AI review requested due to automatic review settings July 6, 2026 13:50
@linux-foundation-easycla

linux-foundation-easycla Bot commented Jul 6, 2026

Copy link
Copy Markdown

CLA Not Signed

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds end-to-end NR gNB-side support for 5G MOCN by enabling multi-PLMN broadcast in SIB1, carrying per-PLMN slice info in F1AP served-cell structures, and letting nrUE select a PLMN from SIB1 based on IMSI so the already-existing selected-PLMN-aware NGAP/AMF routing can be exercised.

Changes:

  • Extend F1AP served-cell information to represent multiple served PLMNs, each with its own slice list, and update encode/decode/copy/equality helpers plus unit tests.
  • Wire multi-PLMN configuration into gNB config → MAC/RRC SIB1 generation, removing the previous single-PLMN assumption.
  • Implement nrUE PLMN selection from SIB1 using IMSI MCC/MNC, and propagate selected PLMN into gNB UE context (F1 UE Context Setup).

Reviewed changes

Copilot reviewed 16 out of 16 changed files in this pull request and generated 10 comments.

Show a summary per file
File Description
targets/PROJECTS/GENERIC-NR-5GC/CONF/ue2.conf Adds a second UE reference config for the two-PLMN validation setup.
targets/PROJECTS/GENERIC-NR-5GC/CONF/ue.conf Updates UE reference config to use the new PLMN/IMSI and slice parameters used in validation.
targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.conf Updates gNB reference config for multi-PLMN broadcast and dual-AMF connectivity.
openair2/RRC/NR/rrc_gNB.c Sends the UE-selected serving PLMN in F1 UE Context Setup instead of cell default PLMN.
openair2/RRC/NR/rrc_gNB_du.c Replaces single-PLMN CU/DU validation with subset validation for MOCN.
openair2/RRC/NR_UE/rrc_UE.c Adds nrUE PLMN selection by matching IMSI MCC/MNC against SIB1 PLMN list.
openair2/LAYER2/NR_MAC_gNB/nr_radio_config.h Extends get_SIB1_NR() API to accept a served-PLMN list.
openair2/LAYER2/NR_MAC_gNB/nr_radio_config.c Populates SIB1 with multiple PLMNs from the served-PLMN list.
openair2/LAYER2/NR_MAC_gNB/mac_proto.h Extends nr_mac_configure_sib1() prototype to pass served-PLMN list.
openair2/LAYER2/NR_MAC_gNB/config.c Passes served-PLMN list into SIB1 generation for SA mode.
openair2/GNB_APP/gnb_config.c Parses plmn_list entries and per-PLMN slice lists into served-cell info for SIB1/F1.
openair2/F1AP/tests/f1ap_lib_test.c Updates F1AP served-cell unit test to the new served-PLMN layout.
openair2/F1AP/lib/f1ap_lib_common.c Updates served-cell equality helper to compare per-PLMN slice lists.
openair2/F1AP/lib/f1ap_interface_management.c Encodes/decodes served-cell info as multiple served PLMNs with per-PLMN slice extensions.
openair2/COMMON/f1ap_messages_types.h Introduces served-PLMN list structures and a max-PLMN constant for F1AP.
common/utils/telnetsrv/telnetsrv_o1.c Adapts O1/telnet exporter to read slice info from the new served-PLMN structure.

Comment on lines +159 to +160
prnt(" \"" SD "\": %d,\n", cell_info->served_plmn_list[0].nssai[0].sd);
prnt(" \"" SST "\": %d\n", cell_info->served_plmn_list[0].nssai[0].sst);
int num_plmn,
const f1ap_served_plmn_info_t *served_plmn_list)
{
AssertFatal(cellID < (1l << 36), "cellID must fit within 36 bits, but is %lu\n", cellID);
Comment on lines +469 to +470
// Backwards-compat: if num_plmn==0 (legacy single-PLMN setup), fall back to c->plmn with no slices.
int num_splmn = c->num_plmn > 0 ? c->num_plmn : 1;
Comment on lines 897 to +901
f1ap_served_cell_info_t dst = {
.plmn = src->plmn,
.nr_cellid = src->nr_cellid,
.nr_pci = src->nr_pci,
.num_ssi = src->num_ssi,
.num_plmn = src->num_plmn,
Comment on lines +43 to 52
_EQ_CHECK_INT(a->num_plmn, b->num_plmn);
for (int i = 0; i < a->num_plmn; ++i) {
if (!eq_f1ap_plmn(&a->served_plmn_list[i].plmn, &b->served_plmn_list[i].plmn))
return false;
_EQ_CHECK_INT(a->served_plmn_list[i].num_nssai, b->served_plmn_list[i].num_nssai);
for (int j = 0; j < a->served_plmn_list[i].num_nssai; ++j) {
_EQ_CHECK_INT(a->served_plmn_list[i].nssai[j].sst, b->served_plmn_list[i].nssai[j].sst);
_EQ_CHECK_INT(a->served_plmn_list[i].nssai[j].sd, b->served_plmn_list[i].nssai[j].sd);
}
}
}

NR_PLMN_IdentityInfoList_t *info_list = &sib1->cellAccessRelatedInfo.plmn_IdentityInfoList;
for (int i = 0; i < info_list->list.count && !plmn_matched; i++) {
Comment on lines +7 to +9
dnn= "internet";
nssai_sst=1;
nssai_sd=0x010203;
Comment on lines +1 to +7
uicc0 = {
imsi = "208940000000004";
key = "8baf473f2f8fd09487cccbd7097c6862";
opc= "8e27b6af0e692e750f32667a3b14605d";
dnn= "internet";
nssai_sst=1;
nssai_sd=0x010204;
{ mcc = 208; mnc = 93; mnc_length = 2; snssaiList = ({ sst = 1; sd = 0x010203; }) },
{ mcc = 208; mnc = 94; mnc_length = 2; snssaiList = ({ sst = 1; sd = 0x010204; }) }
);
# { mcc = 208; mnc = 94; mnc_length = 2; snssaiList = ({ sst = 1; sd = 0x010204; }) }
Comment on lines +164 to +168
amf_ip_address = ({ ipv4 = "128.105.145.152"; }, { ipv4 = "128.105.145.158"; });

NETWORK_INTERFACES :
{
GNB_IPV4_ADDRESS_FOR_NG_AMF = "192.168.70.129/24";
GNB_IPV4_ADDRESS_FOR_NGU = "192.168.70.129/24";
GNB_IPV4_ADDRESS_FOR_NG_AMF = "128.105.145.157/24";
@rorsc

rorsc commented Jul 7, 2026

Copy link
Copy Markdown
Member

Thanks for the contribution.

We already discussed this in the past, and you had this already(!) in https://github.com/OPENAIRINTERFACE/openairinterface5g/pull/3/ (has been closed, no pull requests accepted and now that we can do this here, it's better). Please split out the commits into individual changes, e.g.:

  • F1AP forward (enc/dec/test)
  • Read from config
  • Fill F1AP message
  • UE read
  • gNB RRC change
  • O1 adapter, ...

If you don't have them anymore: I have them, let me know.

overall the changes are good, but I'd like to have a second look once this is done, and also test it.

Extend F1AP served-cell information to carry multiple served PLMNs and
per-PLMN slice information (38.473 §9.3.1.10).

- Add f1ap_served_plmn_info_t and F1AP_MAX_NB_PLMNS; replace the single
  slice list on f1ap_served_cell_info_t with a served-PLMN list.
- Update F1AP encode/decode to emit and parse one ServedPLMNs item per
  broadcast PLMN with its own slice list, with legacy single-PLMN
  fallback.
- Update copy/equal helpers and F1AP lib tests accordingly.

Signed-off-by: Yuanhao Chang <changyuanhao15@gmail.com>
Read all configured plmn_list entries and each PLMN's snssaiList, and
store them in the served-PLMN list carried in the F1AP served-cell info.

- Populate info->served_plmn_list[] with every configured PLMN and its
  slices; keep info->plmn as the primary PLMN for backward compatibility.
- Pass the served-PLMN list into nr_mac_configure_sib1() so SIB1 can
  advertise all PLMNs.

Signed-off-by: Yuanhao Chang <changyuanhao15@gmail.com>
Carry the configured served-PLMN list into the MAC/RRC SIB1 configuration
path so SIB1 can advertise multiple PLMNs.

- Extend nr_mac_configure_sib1() and get_SIB1_NR() to accept the served
  PLMN list and count.
- Remove the hard-coded single-PLMN assumption in SIB1 generation and
  emit one plmn-IdentityList entry per served PLMN.

Signed-off-by: Yuanhao Chang <changyuanhao15@gmail.com>
Replace the legacy single-PLMN CU/DU validation with a MOCN-aware subset
check and carry the UE-selected serving PLMN through the UE context.

- Accept a DU/cell only if every DU-advertised served PLMN is present in
  the CU/gNB configured PLMN list, with legacy single-PLMN fallback.
- Use the UE serving PLMN (instead of the cell's primary PLMN) when
  filling the F1AP UE context setup request.

Signed-off-by: Yuanhao Chang <changyuanhao15@gmail.com>
Select the serving PLMN by matching the UE IMSI against the PLMNs
advertised in SIB1.

- Walk SIB1 plmn-IdentityInfoList / plmn-IdentityList and compare each
  entry's MCC/MNC against the UE IMSI (honouring the 2/3-digit MNC).
- Set selected_plmn_identity (1-based) from the matched entry, falling
  back to the first PLMN when no match is found.

Signed-off-by: Yuanhao Chang <changyuanhao15@gmail.com>
Update the O1/telnet exporter to read slice information from the new
served-PLMN list layout of the F1AP served-cell structure.

- Read slice info from served_plmn_list[0].nssai[0] instead of the
  removed top-level nssai[0].
- Pass the served-PLMN list and count into nr_mac_configure_sib1().

This is a mechanical adapter change required by the F1AP served-cell
structure update.

Signed-off-by: Yuanhao Chang <changyuanhao15@gmail.com>
Add the gNB/UE/UE2 configuration files used to validate the two-core
MOCN setup, included as reproduction references.

- gNB config broadcasts two PLMNs (20893 and 20894).
- ue.conf selects PLMN 20893 (Core1); ue2.conf selects PLMN 20894
  (Core2).

Signed-off-by: Yuanhao Chang <changyuanhao15@gmail.com>
@github-actions

github-actions Bot commented Jul 9, 2026

Copy link
Copy Markdown

Validation:
The following commit(s) are missing signature:

5a04e32, 339bd38, 51e6de0, e1e59c5, a35daae, 39516a8, b10a759

Please use 'git commit -S' to sign your commits.
For detailed instructions, refer to the CONTRIBUTING.md file at the root of this repository or GitHub Docs

@kevin8023c

Copy link
Copy Markdown
Author

Thanks for the contribution.

We already discussed this in the past, and you had this already(!) in https://github.com/OPENAIRINTERFACE/openairinterface5g/pull/3/ (has been closed, no pull requests accepted and now that we can do this here, it's better). Please split out the commits into individual changes, e.g.:

  • F1AP forward (enc/dec/test)
  • Read from config
  • Fill F1AP message
  • UE read
  • gNB RRC change
  • O1 adapter, ...

If you don't have them anymore: I have them, let me know.

overall the changes are good, but I'd like to have a second look once this is done, and also test it.

Hi Robert,

Thanks a lot for the feedback. I updated this PR by splitting the MOCN changes into layered commits following the structure you suggested.

I remember the earlier PR/branch you mentioned. That version was based on a develop branch from a few months ago, and the old PR is no longer usable for this contribution. To make sure this contribution works with the current Duranta/OAI codebase, I re-ported the MOCN changes onto the current weekly develop branch, validated the end-to-end two-core setup again, and then split the tested changes into review-friendly commits.

The PR is now organized into:

  • F1AP served-cell multi-PLMN information
  • gNB config parsing
  • NR gNB/SIB1 served-PLMN propagation
  • gNB RRC MOCN handling
  • nrUE PLMN selection
  • O1/telnet adaptation
  • two-core validation reference configs

The final tree is identical to the previously tested one-commit version, so the final code content is unchanged. I hope this makes the review easier. Please let me know if you would prefer any commit to be split or combined differently.

Best,
Yuanhao

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants