From 6ef5a746e5036626e723475871298bd139d75703 Mon Sep 17 00:00:00 2001 From: Sukchan Lee Date: Mon, 25 May 2020 12:15:22 -0400 Subject: [PATCH] NGSetup is added --- configs/5gc.yaml.in | 85 + configs/{split.yaml.in => epc-custom.yaml.in} | 0 .../{installed.yaml.in => epc-fdconf.yaml.in} | 0 configs/{simple.yaml.in => epc.yaml.in} | 0 configs/meson.build | 8 +- configs/minimal.yaml.in | 89 + configs/open5gs/amf.yaml.in | 202 ++ configs/open5gs/meson.build | 3 +- configs/open5gs/nrf.yaml.in | 12 +- configs/open5gs/smf.yaml.in | 22 +- configs/open5gs/upf.yaml.in | 16 +- .../troubleshoot/02-now-in-github-issues.md | 26 +- lib/app/ogs-app.h | 7 +- lib/app/ogs-config.c | 15 +- lib/app/ogs-config.h | 8 +- lib/asn1c/util/conv.c | 20 + lib/asn1c/util/conv.h | 24 + lib/core/ogs-3gpp-types.c | 76 + lib/core/ogs-3gpp-types.h | 96 +- lib/core/ogs-macros.h | 4 + lib/gtp/types.h | 2 +- lib/nas/5gs/decoder.c | 2 +- lib/nas/5gs/encoder.c | 2 +- lib/nas/5gs/ies.c | 6 +- lib/nas/5gs/ies.h | 2 +- lib/nas/5gs/meson.build | 1 + lib/nas/5gs/message.h | 2 +- lib/nas/5gs/support/type-list.py | 4 +- lib/nas/5gs/types.c | 92 + lib/nas/5gs/types.h | 87 +- lib/nas/common/types.c | 70 - lib/nas/common/types.h | 65 +- lib/nas/eps/meson.build | 1 + lib/nas/eps/types.c | 91 + lib/nas/eps/types.h | 60 + lib/ngap/conv.c | 85 + lib/ngap/conv.h | 46 + lib/ngap/meson.build | 3 +- lib/ngap/message.c | 2 +- lib/ngap/message.h | 23 +- lib/ngap/ogs-ngap.h | 3 +- lib/s1ap/message.h | 21 - lib/sctp/ogs-sctp.h | 2 + src/amf/amf-sm.c | 415 +++ src/amf/amf-sm.h | 55 + src/amf/app.c | 40 + src/amf/context.c | 1044 +++++++ src/amf/context.h | 165 ++ src/amf/event.c | 139 + src/amf/event.h | 106 + src/amf/init.c | 134 + src/amf/meson.build | 68 + src/amf/nf-sm.c | 384 +++ src/amf/ngap-build.c | 2606 +++++++++++++++++ src/amf/ngap-build.h | 109 + src/amf/ngap-handler.c | 2063 +++++++++++++ src/amf/ngap-handler.h | 88 + src/amf/ngap-path.c | 546 ++++ src/amf/ngap-path.h | 105 + src/amf/ngap-sctp.c | 238 ++ src/amf/ngap-sm.c | 229 ++ src/amf/nnrf-build.c | 204 ++ src/amf/nnrf-build.h | 49 + src/amf/nnrf-handler.c | 278 ++ src/amf/nnrf-handler.h | 44 + src/amf/sbi-path.c | 199 ++ src/amf/sbi-path.h | 51 + src/amf/timer.c | 107 + src/amf/timer.h | 62 + src/meson.build | 3 +- src/mme/emm-build.c | 4 +- src/mme/emm-handler.c | 14 +- src/mme/mme-context.c | 65 +- src/mme/mme-context.h | 34 +- src/mme/mme-event.h | 4 +- src/mme/mme-sm.c | 2 +- src/mme/s1ap-handler.c | 20 +- src/mme/s1ap-path.c | 2 +- src/mme/sbc-handler.c | 4 +- src/mme/sbc-message.h | 2 +- src/sgw/sgw-context.h | 2 +- src/smf/context.c | 2 +- src/smf/nf-sm.c | 5 +- src/smf/nnrf-handler.c | 46 +- tests/app/5gc-init.c | 88 + tests/{epc/app-init.c => app/epc-init.c} | 84 +- tests/app/meson.build | 88 +- tests/app/test-5gc.h | 39 + tests/app/test-app.c | 93 - tests/app/test-app.h | 28 - tests/app/test-epc.h | 38 + tests/app/test-packet.c | 134 +- tests/app/test-packet.h | 23 +- tests/common/application.c | 172 ++ tests/common/application.h | 42 + tests/common/context.c | 451 +++ tests/common/context.h | 68 + tests/common/gtpu.c | 69 + tests/common/gtpu.h | 35 + tests/common/meson.build | 58 + tests/common/ngap-build.c | 143 + tests/common/ngap-build.h | 33 + tests/common/sctp.c | 102 + tests/common/sctp.h | 57 + tests/common/test-common.h | 49 + tests/common/test-ngap.h | 41 + tests/csfb/abts-main.c | 8 +- tests/csfb/crash-test.c | 2 +- tests/csfb/meson.build | 4 +- tests/csfb/mo-active-test.c | 2 +- tests/csfb/mo-idle-test.c | 2 +- tests/csfb/mo-sms-test.c | 2 +- tests/csfb/mt-active-test.c | 2 +- tests/csfb/mt-idle-test.c | 2 +- tests/csfb/mt-sms-test.c | 2 +- tests/cups/abts-main.c | 8 +- tests/cups/cups-test.c | 2 +- tests/cups/meson.build | 2 +- tests/{simple => epc-simple}/abts-main.c | 8 +- tests/{simple => epc-simple}/attach-test.c | 2 +- tests/{simple => epc-simple}/crash-test.c | 2 +- tests/{simple => epc-simple}/handover-test.c | 2 +- tests/{simple => epc-simple}/meson.build | 10 +- tests/{simple => epc-simple}/s1setup-test.c | 0 tests/{simple => epc-simple}/volte-test.c | 2 +- tests/epc/meson.build | 60 - tests/epc/test-epc.h | 17 - tests/meson.build | 7 +- tests/minimal/abts-main.c | 66 + tests/minimal/meson.build | 26 + tests/minimal/minimal-test.c | 281 ++ tests/mnc3/abts-main.c | 8 +- tests/mnc3/meson.build | 4 +- tests/mnc3/mnc3-test.c | 2 +- tests/unit/abts-main.c | 2 +- tests/unit/gtp-message-test.c | 2 +- tests/unit/meson.build | 2 +- tests/unit/nas-message-test.c | 10 +- tests/unit/sbi-message-test.c | 2 +- tests/volte/abts-main.c | 8 +- tests/volte/meson.build | 4 +- tests/volte/volte-test.c | 2 +- 142 files changed, 12739 insertions(+), 885 deletions(-) create mode 100644 configs/5gc.yaml.in rename configs/{split.yaml.in => epc-custom.yaml.in} (100%) rename configs/{installed.yaml.in => epc-fdconf.yaml.in} (100%) rename configs/{simple.yaml.in => epc.yaml.in} (100%) create mode 100644 configs/minimal.yaml.in create mode 100644 configs/open5gs/amf.yaml.in create mode 100644 lib/nas/5gs/types.c create mode 100644 lib/nas/eps/types.c create mode 100644 lib/ngap/conv.c create mode 100644 lib/ngap/conv.h create mode 100644 src/amf/amf-sm.c create mode 100644 src/amf/amf-sm.h create mode 100644 src/amf/app.c create mode 100644 src/amf/context.c create mode 100644 src/amf/context.h create mode 100644 src/amf/event.c create mode 100644 src/amf/event.h create mode 100644 src/amf/init.c create mode 100644 src/amf/meson.build create mode 100644 src/amf/nf-sm.c create mode 100644 src/amf/ngap-build.c create mode 100644 src/amf/ngap-build.h create mode 100644 src/amf/ngap-handler.c create mode 100644 src/amf/ngap-handler.h create mode 100644 src/amf/ngap-path.c create mode 100644 src/amf/ngap-path.h create mode 100644 src/amf/ngap-sctp.c create mode 100644 src/amf/ngap-sm.c create mode 100644 src/amf/nnrf-build.c create mode 100644 src/amf/nnrf-build.h create mode 100644 src/amf/nnrf-handler.c create mode 100644 src/amf/nnrf-handler.h create mode 100644 src/amf/sbi-path.c create mode 100644 src/amf/sbi-path.h create mode 100644 src/amf/timer.c create mode 100644 src/amf/timer.h create mode 100644 tests/app/5gc-init.c rename tests/{epc/app-init.c => app/epc-init.c} (55%) create mode 100644 tests/app/test-5gc.h delete mode 100644 tests/app/test-app.c delete mode 100644 tests/app/test-app.h create mode 100644 tests/app/test-epc.h create mode 100644 tests/common/application.c create mode 100644 tests/common/application.h create mode 100644 tests/common/context.c create mode 100644 tests/common/context.h create mode 100644 tests/common/gtpu.c create mode 100644 tests/common/gtpu.h create mode 100644 tests/common/meson.build create mode 100644 tests/common/ngap-build.c create mode 100644 tests/common/ngap-build.h create mode 100644 tests/common/sctp.c create mode 100644 tests/common/sctp.h create mode 100644 tests/common/test-common.h create mode 100644 tests/common/test-ngap.h rename tests/{simple => epc-simple}/abts-main.c (93%) rename tests/{simple => epc-simple}/attach-test.c (99%) rename tests/{simple => epc-simple}/crash-test.c (99%) rename tests/{simple => epc-simple}/handover-test.c (99%) rename tests/{simple => epc-simple}/meson.build (80%) rename tests/{simple => epc-simple}/s1setup-test.c (100%) rename tests/{simple => epc-simple}/volte-test.c (99%) delete mode 100644 tests/epc/meson.build delete mode 100644 tests/epc/test-epc.h create mode 100644 tests/minimal/abts-main.c create mode 100644 tests/minimal/meson.build create mode 100644 tests/minimal/minimal-test.c diff --git a/configs/5gc.yaml.in b/configs/5gc.yaml.in new file mode 100644 index 000000000..3c2a50c65 --- /dev/null +++ b/configs/5gc.yaml.in @@ -0,0 +1,85 @@ +db_uri: mongodb://localhost/open5gs + +logger: + +parameter: + +nrf: + sbi: + - addr: + - 127.0.0.1 + - ::1 + port: 7777 + +amf: + sbi: + - addr: 127.0.0.2 + port: 7777 + ngap: + guami: + - plmn_id: + mcc: 208 + mnc: 93 + amf_id: + region: 202 + set: 1016 + tai: + - plmn_id: + mcc: 901 + mnc: 70 + tac: 1 + plmn: + - plmn_id: + mcc: 901 + mnc: 70 + s_nssai: + - sst: 1 + security: + integrity_order : [ EIA1, EIA2, EIA0 ] + ciphering_order : [ EEA0, EEA1, EEA2 ] + network_name: + full: Open5GS + amf_name: amf.open5gs.org + +smf: + sbi: + - addr: 127.0.0.3 + port: 7777 + gtpc: + - addr: 127.0.0.3 + - addr: ::1 + pfcp: + - addr: 127.0.0.3 + pdn: + - addr: 10.45.0.1/16 + - addr: cafe::1/64 + dns: + - 8.8.8.8 + - 8.8.4.4 + - 2001:4860:4860::8888 + - 2001:4860:4860::8844 + mtu: 1400 + freeDiameter: + identity: pgw.open-ims.test + realm: open-ims.test + listen_on: 127.0.0.3 + load_extension: + - module: @freediameter_extensions_builddir@/dbg_msg_dumps.fdx + conf: 0x8888 + - module: @freediameter_extensions_builddir@/dict_rfc5777.fdx + - module: @freediameter_extensions_builddir@/dict_mip6i.fdx + - module: @freediameter_extensions_builddir@/dict_nasreq.fdx + - module: @freediameter_extensions_builddir@/dict_nas_mipv6.fdx + - module: @freediameter_extensions_builddir@/dict_dcca.fdx + - module: @freediameter_extensions_builddir@/dict_dcca_3gpp.fdx + connect: + - identity: pcrf.open-ims.test + addr: 127.0.0.5 +upf: + pfcp: + addr: 127.0.0.4 + gtpu: + - addr: + - 127.0.0.4 + - ::1 + pdn: diff --git a/configs/split.yaml.in b/configs/epc-custom.yaml.in similarity index 100% rename from configs/split.yaml.in rename to configs/epc-custom.yaml.in diff --git a/configs/installed.yaml.in b/configs/epc-fdconf.yaml.in similarity index 100% rename from configs/installed.yaml.in rename to configs/epc-fdconf.yaml.in diff --git a/configs/simple.yaml.in b/configs/epc.yaml.in similarity index 100% rename from configs/simple.yaml.in rename to configs/epc.yaml.in diff --git a/configs/meson.build b/configs/meson.build index 5176b5b88..e51947d85 100644 --- a/configs/meson.build +++ b/configs/meson.build @@ -34,9 +34,11 @@ else endif example_conf = ''' - simple.yaml - installed.yaml - split.yaml + 5gc.yaml + minimal.yaml + epc.yaml + epc-fdconf.yaml + epc-custom.yaml mnc3.yaml csfb.yaml volte.yaml diff --git a/configs/minimal.yaml.in b/configs/minimal.yaml.in new file mode 100644 index 000000000..f3cd48c2c --- /dev/null +++ b/configs/minimal.yaml.in @@ -0,0 +1,89 @@ +db_uri: mongodb://localhost/open5gs + +logger: + +parameter: + +nrf: + sbi: + - addr: + - 127.0.0.1 + - ::1 + port: 7777 + +amf: + sbi: + - addr: 127.0.0.2 + port: 7777 + ngap: + - addr: 127.0.0.1 + guami: + - plmn_id: + mcc: 208 + mnc: 93 + amf_id: + region: 202 + set: 1016 + tai: + - plmn_id: + mcc: 208 + mnc: 93 + tac: 1 + plmn: + - plmn_id: + mcc: 208 + mnc: 93 + s_nssai: + - sst: 1 + sd: 010203 + - sst: 1 + sd: 112233 + security: + integrity_order : [ EIA1, EIA2, EIA0 ] + ciphering_order : [ EEA0, EEA1, EEA2 ] + network_name: + full: Open5GS + amf_name: amf.open5gs.org + +smf: + sbi: + - addr: 127.0.0.3 + port: 7777 + gtpc: + - addr: 127.0.0.3 + - addr: ::1 + pfcp: + - addr: 127.0.0.3 + pdn: + - addr: 10.45.0.1/16 + - addr: cafe::1/64 + dns: + - 8.8.8.8 + - 8.8.4.4 + - 2001:4860:4860::8888 + - 2001:4860:4860::8844 + mtu: 1400 + freeDiameter: + identity: pgw.open-ims.test + realm: open-ims.test + listen_on: 127.0.0.3 + load_extension: + - module: @freediameter_extensions_builddir@/dbg_msg_dumps.fdx + conf: 0x8888 + - module: @freediameter_extensions_builddir@/dict_rfc5777.fdx + - module: @freediameter_extensions_builddir@/dict_mip6i.fdx + - module: @freediameter_extensions_builddir@/dict_nasreq.fdx + - module: @freediameter_extensions_builddir@/dict_nas_mipv6.fdx + - module: @freediameter_extensions_builddir@/dict_dcca.fdx + - module: @freediameter_extensions_builddir@/dict_dcca_3gpp.fdx + connect: + - identity: pcrf.open-ims.test + addr: 127.0.0.5 +upf: + pfcp: + - addr: 127.0.0.4 + gtpu: + - addr: + - 127.0.0.4 + - ::1 + pdn: diff --git a/configs/open5gs/amf.yaml.in b/configs/open5gs/amf.yaml.in new file mode 100644 index 000000000..a2fc69599 --- /dev/null +++ b/configs/open5gs/amf.yaml.in @@ -0,0 +1,202 @@ +# +# logger: +# +# o Set OGS_LOG_INFO to all domain level +# - If `level` is omitted, the default level is OGS_LOG_INFO) +# - If `domain` is omitted, the all domain level is set from 'level' +# (Nothing is needed) +# +# o Set OGS_LOG_ERROR to all domain level +# - `level` can be set with none, fatal, error, warn, info, debug, trace +# level: error +# +# o Set OGS_LOG_DEBUG to mme/emm domain level +# level: debug +# domain: mme,emm +# +# o Set OGS_LOG_TRACE to all domain level +# level: trace +# domain: core,pfcp,fd,gtp,amf,event,tlv,mem,sock +# +logger: + file: @localstatedir@/log/open5gs/amf.log +# +# amf: +# +# +# +# o SBI Server(http://:80) +# sbi: +# +# o SBI Server(http://:80) +# sbi: +# - addr: +# - 0.0.0.0 +# - ::0 +# port: 7777 +# +# o SBI Server(https://:443) +# sbi: +# tls: +# key: amf.key +# pem: amf.pem +# +# o SBI Server(https://127.0.0.2:443, http://[::1]:80) +# sbi: +# - addr: 127.0.0.2 +# tls: +# key: amf.key +# pem: amf.pem +# - addr: ::1 +# +# o SBI Server(http://amf.open5gs.org:80) +# sbi: +# name: amf.open5gs.org +# +# o SBI Server(http://127.0.0.2:7777) +# sbi: +# - addr: 127.0.0.2 +# port: 7777 +# +# o SBI Server(http://:80) +# sbi: +# dev: eth0 +# +amf: + sbi: + - addr: 127.0.0.2 + port: 7777 + ngap: + guami: + - plmn_id: + mcc: 901 + mnc: 70 + amf_id: + region: 2 + set: 1 + tai: + - plmn_id: + mcc: 901 + mnc: 70 + tac: 1 + plmn: + - plmn_id: + mcc: 901 + mnc: 70 + s_nssai: + - sst: 1 + security: + integrity_order : [ EIA1, EIA2, EIA0 ] + ciphering_order : [ EEA0, EEA1, EEA2 ] + network_name: + full: Open5GS + amf_name: amf.open5gs.org + +# +# nrf: +# +# > +# +# o SBI Client(http://127.0.0.1:7777) +# sbi: +# addr: 127.0.0.1 +# port: 7777 +# +# o SBI Client(https://127.0.0.1:443, http://nrf.open5gs.org:80) +# sbi: +# - addr: 127.0.0.1 +# tls: +# key: nrf.key +# pem: nrf.pem +# - name: nrf.open5gs.org +# +# o SBI Client(http://[fe80::1%@loopback_devname@]:80) +# If prefer_ipv4 is true, http://127.0.0.1:80 is selected. +# +# sbi: +# addr: +# - 127.0.0.1 +# - fe80::1%@loopback_devname@ +# +nrf: + sbi: + - addr: + - 127.0.0.1 + - ::1 + port: 7777 + +# +# parameter: +# +# o Number of output streams per SCTP associations. +# sctp_streams: 30 +# +# o Disable use of IPv4 addresses (only IPv6) +# no_ipv4: true +# +# o Disable use of IPv6 addresses (only IPv4) +# no_ipv6: true +# +# o Prefer IPv4 instead of IPv6 for estabishing new GTP connections. +# prefer_ipv4: true +# +# o Enable Multicast traffic to the UE +# multicast: true +# +# o Disable Stateless Address Autoconfiguration for IPv6 +# no_slaac: true +# +parameter: + +# +# max: +# +# o Maximum Number of gNB per AMF +# gnb: 32 +# o Maximum Number of UE per gNB +# ue: 128 +# +max: + +# +# pool: +# +# o The Number of Default Memory Pool Size +# +# - Pool-size 128 => 8192 Number +# - Pool-size 256 => 4096 Number +# - Pool-size 512 => 2048 Number +# - Pool-size 1024 => 1024 Number +# - Pool-size 2048 => 512 Number +# - Pool-size 8192 => 128 Number +# - Pool-size 1024*1024 => 8 Number +# +# 128: 8192 +# 256: 4096 +# 512: 2048 +# 1024: 1024 +# 2048: 512 +# 8192: 128 +# big: 8 +# +# o Memory of Packet Buffering in SGW +# - Maximum Number of packet(SDU size = 8Kbytes) pool in SGW +# - SGW Memory Usage : 65536 * 8Kbytes = 512Mbytes +# +# packet: 65536 +# +pool: + +# +# time: +# +# o NF Instance Heartbeat (Default : 3 seconds) +# +# o NF Instance Heartbeat (Disabled) +# nf_instance: +# heartbeat: 0 +# +# o NF Instance Heartbeat (5 seconds) +# nf_instance: +# heartbeat: 5 +time: diff --git a/configs/open5gs/meson.build b/configs/open5gs/meson.build index b8484428e..3b7829eb0 100644 --- a/configs/open5gs/meson.build +++ b/configs/open5gs/meson.build @@ -27,8 +27,9 @@ open5gs_conf = ''' pcrf.yaml nrf.yaml - smf.yaml upf.yaml + smf.yaml + amf.yaml '''.split() foreach file : open5gs_conf diff --git a/configs/open5gs/nrf.yaml.in b/configs/open5gs/nrf.yaml.in index 968e16c69..0c3690e81 100644 --- a/configs/open5gs/nrf.yaml.in +++ b/configs/open5gs/nrf.yaml.in @@ -98,15 +98,9 @@ parameter: # # max: # -# o Maximum Number of SGW per MME -# sgw: 32 -# o Maximum Number of PGW per MME -# pgw: 32 -# o Maximum Number of VLR per MME -# vlr: 32 -# o Maximum Number of eNodeB per MME -# enb: 32 -# o Maximum Number of UE per eNodeB +# o Maximum Number of gNB per MME +# gnb: 32 +# o Maximum Number of UE per gNB # ue: 128 # max: diff --git a/configs/open5gs/smf.yaml.in b/configs/open5gs/smf.yaml.in index 78c1cf632..04b189020 100644 --- a/configs/open5gs/smf.yaml.in +++ b/configs/open5gs/smf.yaml.in @@ -228,10 +228,10 @@ smf: # nrf: sbi: - addr: - - 127.0.0.1 - - ::1 - port: 7777 + - addr: + - 127.0.0.1 + - ::1 + port: 7777 # # upf: @@ -245,7 +245,7 @@ nrf: # upf: pfcp: - addr: 127.0.0.4 + - addr: 127.0.0.4 # # parameter: @@ -273,15 +273,9 @@ parameter: # # max: # -# o Maximum Number of SGW per MME -# sgw: 32 -# o Maximum Number of PGW per MME -# pgw: 32 -# o Maximum Number of VLR per MME -# vlr: 32 -# o Maximum Number of eNodeB per MME -# enb: 32 -# o Maximum Number of UE per eNodeB +# o Maximum Number of gNB per AMF +# gnb: 32 +# o Maximum Number of UE per gNB # ue: 128 # max: diff --git a/configs/open5gs/upf.yaml.in b/configs/open5gs/upf.yaml.in index 397c37af3..04bd8257b 100644 --- a/configs/open5gs/upf.yaml.in +++ b/configs/open5gs/upf.yaml.in @@ -59,7 +59,7 @@ logger: # upf: pfcp: - addr: 127.0.0.4 + - addr: 127.0.0.4 gtpu: - addr: - 127.0.0.4 @@ -78,7 +78,7 @@ upf: # smf: pfcp: - addr: 127.0.0.3 + - addr: 127.0.0.3 # # parameter: @@ -107,15 +107,9 @@ parameter: # # max: # -# o Maximum Number of SGW per MME -# sgw: 32 -# o Maximum Number of PGW per MME -# pgw: 32 -# o Maximum Number of VLR per MME -# vlr: 32 -# o Maximum Number of eNodeB per MME -# enb: 32 -# o Maximum Number of UE per eNodeB +# o Maximum Number of gNB per AMF +# gnb: 32 +# o Maximum Number of UE per gNB # ue: 128 # max: diff --git a/docs/_docs/troubleshoot/02-now-in-github-issues.md b/docs/_docs/troubleshoot/02-now-in-github-issues.md index 6b0b5674a..906a14de1 100644 --- a/docs/_docs/troubleshoot/02-now-in-github-issues.md +++ b/docs/_docs/troubleshoot/02-now-in-github-issues.md @@ -11,10 +11,10 @@ Sometimes you may get a message like the one below due to a problem with the fre $ meson test ... -5/8 open5gs:system / simple OK 7.69 s +5/8 open5gs:epc / simple OK 7.69 s --- command --- -08:06:23 /home/parallels/open5gs/build/tests/simple/simple +08:06:23 /home/parallels/open5gs/build/tests/epc-simple/simple --- stdout --- s1setup-test : SUCCESS attach-test : SUCCESS @@ -123,10 +123,10 @@ ninja: no work to do. 2/8 open5gs:unit / crypt OK 0.08 s 3/8 open5gs:system / sctp OK 1.09 s 4/8 open5gs:unit / unit OK 0.04 s -5/8 open5gs:system / simple OK 5.07 s -6/8 open5gs:system / mnc3 OK 1.18 s -7/8 open5gs:system / volte OK 2.99 s -8/8 open5gs:system / csfb OK 6.53 s +5/8 open5gs:epc / simple OK 5.07 s +6/8 open5gs:epc / mnc3 OK 1.18 s +7/8 open5gs:epc / volte OK 2.99 s +8/8 open5gs:epc / csfb OK 6.53 s Ok: 8 Expected Fail: 0 @@ -385,17 +385,17 @@ You can start MongoDB using systemctl. $ sudo systemctl start mongodb ``` -#### I have some error when running `./build/test/simple/simple` +#### I have some error when running `./build/test/epc-simple/simple` -Did you see the following error after executing `./build/test/simple/simple`? +Did you see the following error after executing `./build/test/epc-simple/simple`? ```bash -$ ./build/test/simple/simple +$ ./build/test/epc-simple/simple s1setup_test : SUCCESS attach_test : -Line 134: Condition is false, but expected true \04/09 15:49:09.285: [esm] FATAL: esm_handle_pdn_connectivity_request: Assertion `SECURITY_CONTEXT_IS_VALID(mme_ue)' failed. (esm_handler.c:29) /home/acetcom/Documents/git/open5gs/open5gs/lib/ogslib/src/core/.libs/libogscore-1.0.so.0(ogs_abort+0x2b)[0x7f608518271b] -/home/acetcom/Documents/git/open5gs/open5gs/test/.libs/testcomplex(+0x92121)[0x55dc9e274121] -/home/acetcom/Documents/git/open5gs/open5gs/test/.libs/testcomplex(+0x4f5b9)[0x55dc9e2315b9] +/home/acetcom/Documents/git/open5gs/open5gs/test/.libs/simple(+0x92121)[0x55dc9e274121] +/home/acetcom/Documents/git/open5gs/open5gs/test/.libs/simple(+0x4f5b9)[0x55dc9e2315b9] ``` @@ -415,9 +415,9 @@ $ sudo pkill -9 simple $ sudo pkill -9 open5gs-mmed ... ``` -Execute `./build/test/simple/simple` +Execute `./build/test/epc-simple/simple` ```bash -$ ./build/test/simple/simple +$ ./build/test/epc-simple/simple ``` #### My eNB does not support IPv6. diff --git a/lib/app/ogs-app.h b/lib/app/ogs-app.h index a9cc52ecb..c97abab18 100644 --- a/lib/app/ogs-app.h +++ b/lib/app/ogs-app.h @@ -60,11 +60,14 @@ void pcrf_terminate(void); int nrf_initialize(void); void nrf_terminate(void); +int upf_initialize(void); +void upf_terminate(void); + int smf_initialize(void); void smf_terminate(void); -int upf_initialize(void); -void upf_terminate(void); +int amf_initialize(void); +void amf_terminate(void); #ifdef __cplusplus } diff --git a/lib/app/ogs-config.c b/lib/app/ogs-config.c index 26e593299..367cb925a 100644 --- a/lib/app/ogs-config.c +++ b/lib/app/ogs-config.c @@ -138,7 +138,7 @@ static void recalculate_pool_size(void) #define MAX_NUM_OF_TUNNEL 3 /* Num of Tunnel per Bearer */ #define MAX_NUM_OF_PF 16 /* Num of PacketFilter per Bearer */ - self.pool.ue = self.max.ue * self.max.enb; + self.pool.ue = self.max.ue * self.max.gnb; self.pool.pfcp = ogs_max(self.max.smf, self.max.upf); self.pool.sbi = self.pool.pfcp; self.pool.sess = self.pool.ue * OGS_MAX_NUM_OF_SESS; @@ -156,15 +156,17 @@ static int config_prepare(void) #define MAX_NUM_OF_PGW 32 /* Num of PGW per MME */ #define MAX_NUM_OF_VLR 32 /* Num of VLR per MME */ #define MAX_NUM_OF_CSMAP 128 /* Num of TAI-LAI MAP per MME */ -#define MAX_NUM_OF_ENB 32 /* Num of eNodeB per MME */ -#define MAX_NUM_OF_UE 128 /* Num of UE per eNodeB */ + +#define MAX_NUM_OF_UE 128 /* Num of UE per gNB */ #define MAX_NUM_OF_SMF 32 /* Num of SMF per AMF */ #define MAX_NUM_OF_UPF 32 /* Num of PGW per AMF */ +#define MAX_NUM_OF_GNB 32 /* Num of gNB per AMF */ self.max.sgw = MAX_NUM_OF_SGW; self.max.pgw = MAX_NUM_OF_PGW; self.max.vlr = MAX_NUM_OF_VLR; self.max.csmap = MAX_NUM_OF_CSMAP; - self.max.enb = MAX_NUM_OF_ENB; + + self.max.gnb = MAX_NUM_OF_GNB; self.max.ue = MAX_NUM_OF_UE; self.max.smf = MAX_NUM_OF_SMF; self.max.upf = MAX_NUM_OF_UPF; @@ -323,9 +325,10 @@ int ogs_config_parse() if (!strcmp(max_key, "ue")) { const char *v = ogs_yaml_iter_value(&max_iter); if (v) self.max.ue = atoi(v); - } else if (!strcmp(max_key, "enb")) { + } else if (!strcmp(max_key, "gnb") || + !strcmp(max_key, "enb")) { const char *v = ogs_yaml_iter_value(&max_iter); - if (v) self.max.enb = atoi(v); + if (v) self.max.gnb = atoi(v); } else ogs_warn("unknown key `%s`", max_key); } diff --git a/lib/app/ogs-config.h b/lib/app/ogs-config.h index 27ee75a99..b2c9c4086 100644 --- a/lib/app/ogs-config.h +++ b/lib/app/ogs-config.h @@ -48,6 +48,11 @@ typedef struct ogs_config_s { int no_pgw; int no_pcrf; + int no_amf; + int no_smf; + int no_upf; + int no_nrf; + /* Network */ int no_ipv4; int no_ipv6; @@ -66,10 +71,11 @@ typedef struct ogs_config_s { int pgw; int vlr; int csmap; - int enb; + int ue; int smf; int upf; + int gnb; } max; struct { diff --git a/lib/asn1c/util/conv.c b/lib/asn1c/util/conv.c index 3e97e4a6e..e16db2357 100644 --- a/lib/asn1c/util/conv.c +++ b/lib/asn1c/util/conv.c @@ -38,6 +38,17 @@ void ogs_asn_uint16_to_OCTET_STRING( octet_string->buf[1] = uint16; } +void ogs_asn_uint24_to_OCTET_STRING( + ogs_uint24_t uint24, OCTET_STRING_t *octet_string) +{ + octet_string->size = 3; + octet_string->buf = CALLOC(octet_string->size, sizeof(uint8_t)); + + octet_string->buf[0] = uint24.v >> 16; + octet_string->buf[1] = uint24.v >> 8; + octet_string->buf[2] = uint24.v; +} + void ogs_asn_uint32_to_OCTET_STRING( uint32_t uint32, OCTET_STRING_t *octet_string) { @@ -50,6 +61,15 @@ void ogs_asn_uint32_to_OCTET_STRING( octet_string->buf[3] = uint32; } +void ogs_asn_buffer_to_OCTET_STRING( + void *buf, int size, OCTET_STRING_t *octet_string) +{ + octet_string->size = size; + octet_string->buf = CALLOC(octet_string->size, sizeof(uint8_t)); + + memcpy(octet_string->buf, buf, size); +} + int ogs_asn_BIT_STRING_to_ip(BIT_STRING_t *bit_string, ogs_ip_t *ip) { char buf[OGS_ADDRSTRLEN], buf2[OGS_ADDRSTRLEN]; diff --git a/lib/asn1c/util/conv.h b/lib/asn1c/util/conv.h index 8571cfce4..f511213d2 100644 --- a/lib/asn1c/util/conv.h +++ b/lib/asn1c/util/conv.h @@ -30,12 +30,36 @@ extern "C" { #endif +#define OGS_ASN_CLEAR_DATA(__dATA) \ + do { \ + ogs_assert((__dATA)); \ + if ((__dATA)->buf) { \ + FREEMEM((__dATA)->buf); \ + (__dATA)->buf = NULL; \ + (__dATA)->size = 0; \ + } \ + } while(0) +#define OGS_ASN_STORE_DATA(__dST, __sRC) \ + do { \ + ogs_assert((__sRC)); \ + ogs_assert((__sRC)->buf); \ + ogs_assert((__dST)); \ + OGS_ASN_CLEAR_DATA(__dST); \ + (__dST)->size = (__sRC)->size; \ + (__dST)->buf = CALLOC((__dST)->size, sizeof(uint8_t)); \ + memcpy((__dST)->buf, (__sRC)->buf, (__dST)->size); \ + } while(0) + void ogs_asn_uint8_to_OCTET_STRING( uint8_t uint8, OCTET_STRING_t *octet_string); void ogs_asn_uint16_to_OCTET_STRING( uint16_t uint16, OCTET_STRING_t *octet_string); +void ogs_asn_uint24_to_OCTET_STRING( + ogs_uint24_t uint24, OCTET_STRING_t *octet_string); void ogs_asn_uint32_to_OCTET_STRING( uint32_t uint32, OCTET_STRING_t *octet_string); +void ogs_asn_buffer_to_OCTET_STRING( + void *buf, int size, OCTET_STRING_t *octet_string); int ogs_asn_BIT_STRING_to_ip( BIT_STRING_t *bit_string, ogs_ip_t *ip); diff --git a/lib/core/ogs-3gpp-types.c b/lib/core/ogs-3gpp-types.c index a33750d85..52b14ffce 100644 --- a/lib/core/ogs-3gpp-types.c +++ b/lib/core/ogs-3gpp-types.c @@ -64,6 +64,82 @@ void *ogs_plmn_id_build(ogs_plmn_id_t *plmn_id, return plmn_id; } +uint32_t ogs_amf_id_hexdump(ogs_amf_id_t *amf_id) +{ + uint32_t hex; + + ogs_assert(amf_id); + + memcpy(&hex, amf_id, sizeof(ogs_amf_id_t)); + hex = be32toh(hex) >> 8; + + return hex; +} + +ogs_amf_id_t *ogs_amf_id_from_string(ogs_amf_id_t *amf_id, const char *hex) +{ + char hexbuf[sizeof(ogs_amf_id_t)]; + + ogs_assert(amf_id); + ogs_assert(hex); + + OGS_HEX(hex, strlen(hex), hexbuf); + + amf_id->region = hexbuf[0]; + amf_id->set1 = hexbuf[1]; + amf_id->set2 = (hexbuf[2] & 0xc0) >> 6; + amf_id->pointer = hexbuf[2] & 0x3f; + + return amf_id; +} + +char *ogs_amf_id_to_string(ogs_amf_id_t *amf_id, char *buf) +{ + int i; + ogs_assert(amf_id); + ogs_assert(buf); + + ogs_hex_to_ascii(amf_id, sizeof(ogs_amf_id_t), buf, OGS_AMFIDSTRLEN); + + /* I'd just like to use lower character */ + for (i = 0; buf[i]; i++) + if (buf[i] >= 'A' && buf[i] <= 'Z') + buf[i] = buf[i] + 'a' - 'A'; + + return buf; +} + +uint8_t ogs_amf_region_id(ogs_amf_id_t *amf_id) +{ + ogs_assert(amf_id); + return amf_id->region; +} +uint16_t ogs_amf_set_id(ogs_amf_id_t *amf_id) +{ + ogs_assert(amf_id); + return (amf_id->set1 << 2) + amf_id->set2; +} +uint8_t ogs_amf_pointer(ogs_amf_id_t *amf_id) +{ + ogs_assert(amf_id); + return amf_id->pointer; +} + +ogs_amf_id_t *ogs_amf_id_build(ogs_amf_id_t *amf_id, + uint8_t region, uint16_t set, uint8_t pointer) +{ + ogs_assert(amf_id); + ogs_assert(region); + ogs_assert(set); + + amf_id->region = region; + amf_id->set1 = set >> 2; + amf_id->set2 = set & 0x3; + amf_id->pointer = pointer; + + return amf_id; +} + int ogs_fqdn_build(char *dst, char *src, int length) { int i = 0, j = 0; diff --git a/lib/core/ogs-3gpp-types.h b/lib/core/ogs-3gpp-types.h index 9480e0995..231c9bb8c 100644 --- a/lib/core/ogs-3gpp-types.h +++ b/lib/core/ogs-3gpp-types.h @@ -28,8 +28,6 @@ extern "C" { #endif /* __cplusplus */ -#define OGS_MAX_FILEPATH_LEN 256 - #define OGS_MAX_NUM_OF_SESS 4 /* Num of APN(Session) per UE */ #define OGS_MAX_NUM_OF_RULE 4 /* Num of Rule per Session */ @@ -50,7 +48,11 @@ extern "C" { #define OGS_MAX_DNN_LEN 100 #define OGS_MAX_PCO_LEN 251 #define OGS_MAX_FQDN_LEN 256 -#define OGS_MAX_IFNAME_LEN 32 + +#define OGS_MAX_NUM_OF_SERVED_TAI 16 +#define OGS_MAX_NUM_OF_ALGORITHM 8 + +#define OGS_MAX_NUM_OF_BPLMN 6 #define OGS_NEXT_ID(__id, __min, __max) \ ((__id) = ((__id) == (__max) ? (__min) : ((__id) + 1))) @@ -63,8 +65,49 @@ extern "C" { #define OGS_NAS_PROCEDURE_TRANSACTION_IDENTITY_UNASSIGNED 0 -/********************************** - * PLMN_ID Structure */ +typedef struct ogs_uint24_s { + uint32_t v:24; +} __attribute__ ((packed)) ogs_uint24_t; + +static ogs_inline ogs_uint24_t ogs_be24toh(ogs_uint24_t x) +{ + uint32_t tmp = x.v; + tmp = be32toh(tmp); + x.v = tmp >> 8; + return x; +} + +static ogs_inline ogs_uint24_t ogs_htobe24(ogs_uint24_t x) +{ + uint32_t tmp = x.v; + tmp = htobe32(tmp); + x.v = tmp >> 8; + return x; +} + +static ogs_inline ogs_uint24_t ogs_uint24_from_string(char *str) +{ + ogs_uint24_t x; + + ogs_assert(str); + OGS_HEX(str, strlen(str), &x); + return ogs_be24toh(x); +} + +#define OGS_24BITSTRLEN (sizeof(ogs_uint24_t)*2+1) +static ogs_inline char *ogs_uint24_to_string(ogs_uint24_t x, char *str) +{ + ogs_assert(str); + + x = ogs_htobe24(x); + ogs_hex_to_ascii(&x, sizeof(x), str, OGS_24BITSTRLEN); + + return str; +} + +/************************************ + * PLMN_ID Structure */ +#define OGS_MAX_NUM_OF_PLMN 6 typedef struct ogs_plmn_id_s { ED2(uint8_t mcc2:4;, uint8_t mcc1:4;) @@ -83,18 +126,55 @@ uint16_t ogs_plmn_id_mnc_len(ogs_plmn_id_t *plmn_id); void *ogs_plmn_id_build(ogs_plmn_id_t *plmn_id, uint16_t mcc, uint16_t mnc, uint16_t mnc_len); -#define OGS_MAX_NUM_OF_TAI 16 +/************************************ + * AMF_ID Structure */ +typedef struct ogs_amf_id_s { + uint8_t region; + uint8_t set1; +ED2(uint8_t set2:2;, + uint8_t pointer:6;) +} __attribute__ ((packed)) ogs_amf_id_t; -typedef struct ogs_tai_s { +uint32_t ogs_amf_id_hexdump(ogs_amf_id_t *amf_id); + +#define OGS_AMFIDSTRLEN (sizeof(ogs_amf_id_t)*2+1) +ogs_amf_id_t *ogs_amf_id_from_string(ogs_amf_id_t *amf_id, const char *hex); +char *ogs_amf_id_to_string(ogs_amf_id_t *amf_id, char *buf); + +uint8_t ogs_amf_region_id(ogs_amf_id_t *amf_id); +uint16_t ogs_amf_set_id(ogs_amf_id_t *amf_id); +uint8_t ogs_amf_pointer(ogs_amf_id_t *amf_id); + +ogs_amf_id_t *ogs_amf_id_build(ogs_amf_id_t *amf_id, + uint8_t region, uint16_t set, uint8_t pointer); + +/************************************ + * TAI Structure */ +#define OGS_MAX_NUM_OF_TAI 16 +typedef struct ogs_eps_tai_s { ogs_plmn_id_t plmn_id; uint16_t tac; -} __attribute__ ((packed)) ogs_tai_t; +} __attribute__ ((packed)) ogs_eps_tai_t; + +typedef struct ogs_5gs_tai_s { + ogs_plmn_id_t plmn_id; + ogs_uint24_t tac; +} __attribute__ ((packed)) ogs_5gs_tai_t; typedef struct ogs_e_cgi_s { ogs_plmn_id_t plmn_id; uint32_t cell_id; /* 28 bit */ } __attribute__ ((packed)) ogs_e_cgi_t; +/************************************ + * S-NSSAI Structure */ +#define OGS_MAX_NUM_OF_S_NSSAI 8 +#define OGS_S_NSSAI_NO_SD_VALUE 0xffffff +typedef struct ogs_s_nssai_s { + uint8_t sst; + ogs_uint24_t sd; +} __attribute__ ((packed)) ogs_s_nssai_t; + /************************************************** * Common Structure * S1AP : 9.2.2.1 Transport Layer Address, See 36.414 diff --git a/lib/core/ogs-macros.h b/lib/core/ogs-macros.h index 3ecf11dd3..470e3b779 100644 --- a/lib/core/ogs-macros.h +++ b/lib/core/ogs-macros.h @@ -177,6 +177,10 @@ extern "C" { #define END }}} #endif +#define OGS_ARG_MAX 256 +#define OGS_MAX_FILEPATH_LEN 256 +#define OGS_MAX_IFNAME_LEN 32 + #ifdef __cplusplus } #endif diff --git a/lib/gtp/types.h b/lib/gtp/types.h index 8d4610604..0483d8dcd 100644 --- a/lib/gtp/types.h +++ b/lib/gtp/types.h @@ -344,7 +344,7 @@ typedef struct ogs_gtp_uli_s { ogs_gtp_uli_cgi_t cgi; ogs_gtp_uli_sai_t sai; ogs_gtp_uli_rai_t rai; - ogs_tai_t tai; + ogs_eps_tai_t tai; ogs_e_cgi_t e_cgi; ogs_gtp_uli_lai_t lai; } ogs_gtp_uli_t; diff --git a/lib/nas/5gs/decoder.c b/lib/nas/5gs/decoder.c index 4c5a320f3..8bfdb7ab3 100644 --- a/lib/nas/5gs/decoder.c +++ b/lib/nas/5gs/decoder.c @@ -28,7 +28,7 @@ /******************************************************************************* * This file had been created by nas-message.py script v0.2.0 * Please do not modify this file but regenerate it via script. - * Created on: 2020-05-22 16:56:22.013388 by acetcom + * Created on: 2020-05-24 17:29:31.606634 by acetcom * from 24501-g41.docx ******************************************************************************/ diff --git a/lib/nas/5gs/encoder.c b/lib/nas/5gs/encoder.c index 419679e66..240f48145 100644 --- a/lib/nas/5gs/encoder.c +++ b/lib/nas/5gs/encoder.c @@ -28,7 +28,7 @@ /******************************************************************************* * This file had been created by nas-message.py script v0.2.0 * Please do not modify this file but regenerate it via script. - * Created on: 2020-05-22 16:56:22.024260 by acetcom + * Created on: 2020-05-24 17:29:31.617845 by acetcom * from 24501-g41.docx ******************************************************************************/ diff --git a/lib/nas/5gs/ies.c b/lib/nas/5gs/ies.c index 59f96525c..355ffc1b1 100644 --- a/lib/nas/5gs/ies.c +++ b/lib/nas/5gs/ies.c @@ -28,7 +28,7 @@ /******************************************************************************* * This file had been created by nas-message.py script v0.2.0 * Please do not modify this file but regenerate it via script. - * Created on: 2020-05-22 16:56:21.995840 by acetcom + * Created on: 2020-05-24 17:29:31.588956 by acetcom * from 24501-g41.docx ******************************************************************************/ @@ -2713,7 +2713,7 @@ int ogs_nas_5gs_decode_5gs_tracking_area_identity(ogs_nas_5gs_tracking_area_iden ogs_assert(ogs_pkbuf_pull(pkbuf, size)); memcpy(tracking_area_identity, pkbuf->data - size, size); - tracking_area_identity->tac = be16toh(tracking_area_identity->tac); + tracking_area_identity->tac = ogs_be24toh(tracking_area_identity->tac); ogs_trace(" 5GS_TRACKING_AREA_IDENTITY - "); ogs_log_hexdump(OGS_LOG_TRACE, pkbuf->data - size, size); @@ -2727,7 +2727,7 @@ int ogs_nas_5gs_encode_5gs_tracking_area_identity(ogs_pkbuf_t *pkbuf, ogs_nas_5g ogs_nas_5gs_tracking_area_identity_t target; memcpy(&target, tracking_area_identity, size); - target.tac = htobe16(tracking_area_identity->tac); + target.tac = ogs_htobe24(tracking_area_identity->tac); ogs_assert(ogs_pkbuf_pull(pkbuf, size)); memcpy(pkbuf->data - size, &target, size); diff --git a/lib/nas/5gs/ies.h b/lib/nas/5gs/ies.h index 4b0870692..654907a62 100644 --- a/lib/nas/5gs/ies.h +++ b/lib/nas/5gs/ies.h @@ -28,7 +28,7 @@ /******************************************************************************* * This file had been created by nas-message.py script v0.2.0 * Please do not modify this file but regenerate it via script. - * Created on: 2020-05-22 16:56:21.993076 by acetcom + * Created on: 2020-05-24 17:29:31.586197 by acetcom * from 24501-g41.docx ******************************************************************************/ diff --git a/lib/nas/5gs/meson.build b/lib/nas/5gs/meson.build index 1a24eb6b4..6c49c5ef4 100644 --- a/lib/nas/5gs/meson.build +++ b/lib/nas/5gs/meson.build @@ -16,6 +16,7 @@ # along with this program. If not, see . libnas_5gs_sources = files(''' + types.c ies.c decoder.c encoder.c diff --git a/lib/nas/5gs/message.h b/lib/nas/5gs/message.h index 162ee3f14..c72ba7367 100644 --- a/lib/nas/5gs/message.h +++ b/lib/nas/5gs/message.h @@ -28,7 +28,7 @@ /******************************************************************************* * This file had been created by nas-message.py script v0.2.0 * Please do not modify this file but regenerate it via script. - * Created on: 2020-05-22 16:56:22.005526 by acetcom + * Created on: 2020-05-24 17:29:31.598549 by acetcom * from 24501-g41.docx ******************************************************************************/ diff --git a/lib/nas/5gs/support/type-list.py b/lib/nas/5gs/support/type-list.py index 94a68d19a..599f3eb61 100644 --- a/lib/nas/5gs/support/type-list.py +++ b/lib/nas/5gs/support/type-list.py @@ -17,9 +17,9 @@ # along with this program. If not, see . type_list["5GS tracking area identity"]["decode"] = \ -" tracking_area_identity->tac = be16toh(tracking_area_identity->tac);\n\n" +" tracking_area_identity->tac = ogs_be24toh(tracking_area_identity->tac);\n\n" type_list["5GS tracking area identity"]["encode"] = \ -" target.tac = htobe16(tracking_area_identity->tac);\n\n" +" target.tac = ogs_htobe24(tracking_area_identity->tac);\n\n" type_list["5GS mobile identity"]["decode"] = \ " if (mobile_identity->guti.type == OGS_NAS_5GS_MOBILE_IDENTITY_GUTI) {\n" \ diff --git a/lib/nas/5gs/types.c b/lib/nas/5gs/types.c new file mode 100644 index 000000000..1f8c1e4ac --- /dev/null +++ b/lib/nas/5gs/types.c @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2019,2020 by Sukchan Lee + * + * This file is part of Open5GS. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 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, see . + */ + +#include "ogs-nas-5gs.h" + +void ogs_nas_5gs_tai_list_build( + ogs_nas_5gs_tracking_area_identity_list_t *target, + ogs_5gs_tai0_list_t *source0, ogs_5gs_tai2_list_t *source2) +{ + int i = 0, j = 0, size = 0; + + ogs_5gs_tai0_list_t target0; + ogs_5gs_tai2_list_t target2; + ogs_nas_plmn_id_t ogs_nas_plmn_id; + + ogs_assert(target); + ogs_assert(source0); + ogs_assert(source2); + + memset(target, 0, sizeof(ogs_nas_5gs_tracking_area_identity_list_t)); + memset(&target0, 0, sizeof(ogs_5gs_tai0_list_t)); + memset(&target2, 0, sizeof(ogs_5gs_tai2_list_t)); + + for (i = 0; source0->tai[i].num; i++) { + ogs_assert(source0->tai[i].type == OGS_TAI0_TYPE); + target0.tai[i].type = source0->tai[i].type; + + /* target->num = source->num - 1 */ + ogs_assert(source0->tai[i].num < OGS_MAX_NUM_OF_TAI); + target0.tai[i].num = source0->tai[i].num - 1; + memcpy(&target0.tai[i].plmn_id, + ogs_nas_from_plmn_id(&ogs_nas_plmn_id, &source0->tai[i].plmn_id), + OGS_PLMN_ID_LEN); + + for (j = 0; j < source0->tai[i].num; j++) { + target0.tai[i].tac[j] = ogs_htobe24(source0->tai[i].tac[j]); + } + + size = (1 + 3 + 2 * source0->tai[i].num); + if ((target->length + size) > OGS_NAS_5GS_MAX_TAI_LIST_LEN) { + ogs_warn("Overflow: Ignore remained TAI LIST(length:%d, size:%d)", + target->length, size); + return; + } + memcpy(target->buffer + target->length, &target0.tai[i], size); + target->length += size; + } + + if (source2->num) { + memset(&target2, 0, sizeof(target2)); + + ogs_assert(source2->type == OGS_TAI1_TYPE || + source2->type == OGS_TAI2_TYPE); + target2.type = source2->type; + + /* target->num = source->num - 1 */ + ogs_assert(source2->num < OGS_MAX_NUM_OF_TAI); + target2.num = source2->num - 1; + + size = (1 + (3 + 2) * source2->num); + if ((target->length + size) > OGS_NAS_5GS_MAX_TAI_LIST_LEN) { + ogs_warn("Overflow: Ignore remained TAI LIST(length:%d, size:%d)", + target->length, size); + return; + } + for (i = 0; i < source2->num; i++) { + memcpy(&target2.tai[i].plmn_id, + ogs_nas_from_plmn_id(&ogs_nas_plmn_id, + &source2->tai[i].plmn_id), + OGS_PLMN_ID_LEN); + target2.tai[i].tac = ogs_htobe24(source2->tai[i].tac); + } + memcpy(target->buffer + target->length, &target2, size); + target->length += size; + } +} diff --git a/lib/nas/5gs/types.h b/lib/nas/5gs/types.h index bae9a637f..73dd7a5c2 100644 --- a/lib/nas/5gs/types.h +++ b/lib/nas/5gs/types.h @@ -28,6 +28,14 @@ extern "C" { #endif +/********************** + * NAS GUTI Structure */ +typedef struct ogs_nas_5gs_guti_s { + ogs_nas_plmn_id_t nas_plmn_id; + ogs_amf_id_t amf_id; + uint32_t m_tmsi; +} __attribute__ ((packed)) ogs_nas_5gs_guti_t; + /* 9.11.2.1A DNN * O TLV 3-102 */ typedef struct ogs_nas_dnn_s { @@ -48,11 +56,10 @@ typedef struct ogs_nas_eap_message_s { #define OGS_NAS_S_NSSAI_SST_AND_MAPPED_HPLMN_SST_LEN 2 #define OGS_NAS_S_NSSAI_SST_AND_SD 4 #define OGS_NAS_S_NSSAI_SST_SD_AND_MAPPED_HPLMN_SST_LEN 5 -#define OGS_NAS_MAX_S_NSSAI_LEN 8 typedef struct ogs_nas_s_nssai_s { uint8_t length; - uint32_t sst_sd; - uint32_t mapped_sst_sd; + ogs_s_nssai_t s_nssai; + ogs_s_nssai_t mapped_s_nssai; } __attribute__ ((packed)) ogs_nas_s_nssai_t; /* 9.11.3.1 5GMM capability @@ -133,20 +140,12 @@ ED3(uint8_t type:4;, /* 9.11.3.4 5GS mobile identity * M LV-E 6-n */ -#define OGS_NAS_GET_AMF_SET_ID(_iDentity) \ - ((_iDentity)->amf_set_id2 + ((_iDentity)->amf_set_id1 << 2)) -#define OGS_NAS_SET_AMF_SET_ID(_iDentity, _aMFSetId) \ - do { \ - ogs_assert((_iDentity)); \ - (_iDentity)->amf_set_id1 = (_aMFSetId >> 2) & 0x000f; \ - (_iDentity)->amf_set_id2 = _aMFSetId & 0x0003; \ - } while(0) typedef struct ogs_nas_5gs_mobile_identity_guti_s { ED3(uint8_t _0xf:4;, uint8_t spare:1;, uint8_t type:3;) ogs_nas_plmn_id_t nas_plmn_id; - uint16_t amf_region_id; + uint8_t amf_region_id; uint8_t amf_set_id1; ED2(uint8_t amf_set_id2:2;, uint8_t amf_pointer:6;) @@ -156,7 +155,7 @@ typedef struct ogs_nas_5gs_mobile_identity_s_tmsi_s { ED3(uint8_t _0xf:4;, uint8_t spare:1;, uint8_t type:3;) - uint16_t amf_region_id; + uint8_t amf_region_id; uint8_t amf_set_id1; ED2(uint8_t amf_set_id2:2;, uint8_t amf_pointer:6;) @@ -214,11 +213,54 @@ ED3(uint8_t type:4;, /* 9.11.3.8 5GS tracking area identity * O TV 6 */ -typedef ogs_nas_tracking_area_identity_t ogs_nas_5gs_tracking_area_identity_t; +typedef struct ogs_nas_5gs_tracking_area_identity_s { + ogs_nas_plmn_id_t nas_plmn_id; + ogs_uint24_t tac; +} __attribute__ ((packed)) ogs_nas_5gs_tracking_area_identity_t; + +typedef ogs_nas_5gs_tracking_area_identity_t ogs_nas_5gs_tai_t; /* 9.11.3.9 5GS tracking area identity list * O TLV 9-114 */ -typedef ogs_nas_tracking_area_identity_list_t ogs_nas_5gs_tracking_area_identity_list_t; +#define OGS_NAS_5GS_MAX_TAI_LIST_LEN 114 +typedef struct ogs_5gs_tai0_list_s { + struct { + ED3(uint8_t spare:1;, + uint8_t type:2;, + uint8_t num:5;) + /* + * Do not change 'ogs_plmn_id_t' to 'ogs_nas_plmn_id_t'. + * Use 'ogs_plmn_id_t' for easy implementation. + * ogs_nas_tai_list_build() changes to NAS format(ogs_nas_plmn_id_t) + * and is sent to the UE. + */ + ogs_plmn_id_t plmn_id; + ogs_uint24_t tac[OGS_MAX_NUM_OF_TAI]; + } __attribute__ ((packed)) tai[OGS_MAX_NUM_OF_TAI]; +} __attribute__ ((packed)) ogs_5gs_tai0_list_t; + +typedef struct ogs_5gs_tai2_list_s { +ED3(uint8_t spare:1;, + uint8_t type:2;, + uint8_t num:5;) + /* + * Do not change 'ogs_5gs_tai_t' to 'ogs_nas_tracking_area_identity_t'. + * Use 'ogs_5gs_tai_t' for easy implementation. + * ogs_nas_tai_list_build() changes to NAS + * format(ogs_nas_tracking_area_identity_t) + * and is sent to the UE. + */ + ogs_5gs_tai_t tai[OGS_MAX_NUM_OF_TAI]; +} __attribute__ ((packed)) ogs_5gs_tai2_list_t; + +typedef struct ogs_nas_5gs_tracking_area_identity_list_s { + uint8_t length; + uint8_t buffer[OGS_NAS_5GS_MAX_TAI_LIST_LEN]; +} __attribute__ ((packed)) ogs_nas_5gs_tracking_area_identity_list_t; + +void ogs_nas_5gs_tai_list_build( + ogs_nas_5gs_tracking_area_identity_list_t *target, + ogs_5gs_tai0_list_t *source0, ogs_5gs_tai2_list_t *source2); /* 9.11.3.9A 5GS update type * O TLV 3 */ @@ -381,10 +423,10 @@ ED2(uint8_t type:4;, /* 9.11.3.31B Mapped NSSAI * O TLV 3-42 */ -#define OGS_NAS_MAX_MAPPED_NSSAI_LEN 40 +#define OGS_NAX_MAX_NUM_OF_MAPPED_S_NSSAI 10 typedef struct ogs_nas_mapped_nssai_s { uint8_t length; - uint8_t buffer[OGS_NAS_MAX_MAPPED_NSSAI_LEN]; + ogs_s_nssai_t s_nssai[OGS_NAX_MAX_NUM_OF_MAPPED_S_NSSAI]; } ogs_nas_mapped_nssai_t; /* 9.11.3.33 message container @@ -405,10 +447,10 @@ ED4(uint8_t type:4;, /* 9.11.3.37 NSSAI * O TLV 4-72 */ -#define MAX_NAS_NSSAI_LEN 72 +#define OGS_NAS_MAX_NUM_OF_S_NSSAI 18 typedef struct ogs_nas_nssai_s { uint8_t length; - uint8_t buffer[MAX_NAS_NSSAI_LEN]; + ogs_s_nssai_t s_nssai[OGS_NAS_MAX_NUM_OF_S_NSSAI]; } __attribute__ ((packed)) ogs_nas_nssai_t; /* 9.11.3.37A NSSAI inclusion mode @@ -468,10 +510,13 @@ typedef ogs_nas_allowed_pdu_session_status_t ogs_nas_pdu_session_status_t; /* 9.11.3.46 Rejected NSSAI * O TLV 4-42 */ -#define OGS_MAX_NAS_REJECTED_NSSAI_LEN 40 typedef struct ogs_nas_rejected_nssai_s { uint8_t length; - uint8_t buffer[OGS_MAX_NAS_REJECTED_NSSAI_LEN]; + struct { +ED2(uint8_t len:4;, + uint8_t value:4;) + ogs_s_nssai_t s_nssai; + } rejected[OGS_MAX_NUM_OF_S_NSSAI]; } ogs_nas_rejected_nssai_t; /* 9.11.3.49 Service area list diff --git a/lib/nas/common/types.c b/lib/nas/common/types.c index c35c157d0..8c2fcc900 100644 --- a/lib/nas/common/types.c +++ b/lib/nas/common/types.c @@ -427,73 +427,3 @@ void eps_qos_build(ogs_nas_eps_quality_of_service_t *eps_qos, uint8_t qci, eps_qos->length = length*4+1; } - -void ogs_nas_tai_list_build( - ogs_nas_tracking_area_identity_list_t *target, - tai0_list_t *source0, tai2_list_t *source2) -{ - int i = 0, j = 0, size = 0; - - tai0_list_t target0; - tai2_list_t target2; - ogs_nas_plmn_id_t ogs_nas_plmn_id; - - ogs_assert(target); - ogs_assert(source0); - ogs_assert(source2); - - memset(target, 0, sizeof(ogs_nas_tracking_area_identity_list_t)); - memset(&target0, 0, sizeof(tai0_list_t)); - memset(&target2, 0, sizeof(tai2_list_t)); - - for (i = 0; source0->tai[i].num; i++) { - ogs_assert(source0->tai[i].type == TAI0_TYPE); - target0.tai[i].type = source0->tai[i].type; - - /* target->num = source->num - 1 */ - ogs_assert(source0->tai[i].num < OGS_MAX_NUM_OF_TAI); - target0.tai[i].num = source0->tai[i].num - 1; - memcpy(&target0.tai[i].plmn_id, - ogs_nas_from_plmn_id(&ogs_nas_plmn_id, &source0->tai[i].plmn_id), - OGS_PLMN_ID_LEN); - - for (j = 0; j < source0->tai[i].num; j++) { - target0.tai[i].tac[j] = htobe16(source0->tai[i].tac[j]); - } - - size = (1 + 3 + 2 * source0->tai[i].num); - if ((target->length + size) > OGS_NAS_MAX_TAI_LIST_LEN) { - ogs_warn("Overflow: Ignore remained TAI LIST(length:%d, size:%d)", - target->length, size); - return; - } - memcpy(target->buffer + target->length, &target0.tai[i], size); - target->length += size; - } - - if (source2->num) { - memset(&target2, 0, sizeof(target2)); - - ogs_assert(source2->type == TAI1_TYPE || source2->type == TAI2_TYPE); - target2.type = source2->type; - - /* target->num = source->num - 1 */ - ogs_assert(source2->num < OGS_MAX_NUM_OF_TAI); - target2.num = source2->num - 1; - - size = (1 + (3 + 2) * source2->num); - if ((target->length + size) > OGS_NAS_MAX_TAI_LIST_LEN) { - ogs_warn("Overflow: Ignore remained TAI LIST(length:%d, size:%d)", - target->length, size); - return; - } - for (i = 0; i < source2->num; i++) { - memcpy(&target2.tai[i].plmn_id, - ogs_nas_from_plmn_id(&ogs_nas_plmn_id, &source2->tai[i].plmn_id), - OGS_PLMN_ID_LEN); - target2.tai[i].tac = htobe16(source2->tai[i].tac); - } - memcpy(target->buffer + target->length, &target2, size); - target->length += size; - } -} diff --git a/lib/nas/common/types.h b/lib/nas/common/types.h index 830292f6d..0f052ccbb 100644 --- a/lib/nas/common/types.h +++ b/lib/nas/common/types.h @@ -97,13 +97,6 @@ void *ogs_nas_from_plmn_id( void *ogs_nas_to_plmn_id( ogs_plmn_id_t *plmn_id, ogs_nas_plmn_id_t *ogs_nas_plmn_id); -typedef struct ogs_nas_guti_s { - ogs_nas_plmn_id_t nas_plmn_id; - uint16_t mme_gid; - uint8_t mme_code; - uint32_t m_tmsi; -} __attribute__ ((packed)) ogs_nas_guti_t; - /* 9.9.2.0 Additional information * O TLV 3-n */ #define NAX_MAX_ADDITIONAL_INFORMATION_LEN 255 @@ -466,59 +459,13 @@ typedef struct ogs_nas_time_zone_and_time_s { uint8_t timezone; } ogs_nas_time_zone_and_time_t; -/* 9.9.3.32 Tracking area identity - * O TV 6 */ -typedef struct ogs_nas_tracking_area_identity_s { - ogs_nas_plmn_id_t nas_plmn_id; - uint16_t tac; -} __attribute__ ((packed)) ogs_nas_tracking_area_identity_t; - -typedef ogs_nas_tracking_area_identity_t ogs_nas_tai_t; - /* 9.9.3.33 Tracking area identity list - * M LV 7-97 */ -#define OGS_NAS_MAX_TAI_LIST_LEN 96 -#define TAI0_TYPE 0 -#define TAI1_TYPE 1 -#define TAI2_TYPE 2 -typedef struct tai0_list_s { - struct { - ED3(uint8_t spare:1;, - uint8_t type:2;, - uint8_t num:5;) - /* - * Do not change 'ogs_plmn_id_t' to 'ogs_nas_plmn_id_t'. - * Use 'ogs_plmn_id_t' for easy implementation. - * ogs_nas_tai_list_build() changes to NAS format(ogs_nas_plmn_id_t) - * and is sent to the UE. - */ - ogs_plmn_id_t plmn_id; - uint16_t tac[OGS_MAX_NUM_OF_TAI]; - } __attribute__ ((packed)) tai[OGS_MAX_NUM_OF_TAI]; -} __attribute__ ((packed)) tai0_list_t; - -typedef struct tai2_list_s { -ED3(uint8_t spare:1;, - uint8_t type:2;, - uint8_t num:5;) - /* - * Do not change 'ogs_tai_t' to 'ogs_nas_tracking_area_identity_t'. - * Use 'ogs_tai_t' for easy implementation. - * ogs_nas_tai_list_build() changes to NAS - * format(ogs_nas_tracking_area_identity_t) - * and is sent to the UE. - */ - ogs_tai_t tai[OGS_MAX_NUM_OF_TAI]; -} __attribute__ ((packed)) tai2_list_t; - -typedef struct ogs_nas_tracking_area_identity_list_s { - uint8_t length; - uint8_t buffer[OGS_NAS_MAX_TAI_LIST_LEN]; -} __attribute__ ((packed)) ogs_nas_tracking_area_identity_list_t; - -void ogs_nas_tai_list_build( - ogs_nas_tracking_area_identity_list_t *target, - tai0_list_t *source0, tai2_list_t *source2); + * M LV 7-97 + * 9.11.3.9 5GS tracking area identity list + * O TLV 9-114 */ +#define OGS_TAI0_TYPE 0 +#define OGS_TAI1_TYPE 1 +#define OGS_TAI2_TYPE 2 /* 9.9.3.34 UE network capability * M LV 3-14 diff --git a/lib/nas/eps/meson.build b/lib/nas/eps/meson.build index 89932abb3..306802235 100644 --- a/lib/nas/eps/meson.build +++ b/lib/nas/eps/meson.build @@ -16,6 +16,7 @@ # along with this program. If not, see . libnas_eps_sources = files(''' + types.c ies.c decoder.c encoder.c diff --git a/lib/nas/eps/types.c b/lib/nas/eps/types.c new file mode 100644 index 000000000..009efcfc2 --- /dev/null +++ b/lib/nas/eps/types.c @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2019,2020 by Sukchan Lee + * + * This file is part of Open5GS. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 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, see . + */ + +#include "ogs-nas-eps.h" + +void ogs_nas_tai_list_build(ogs_nas_tracking_area_identity_list_t *target, + ogs_eps_tai0_list_t *source0, ogs_eps_tai2_list_t *source2) +{ + int i = 0, j = 0, size = 0; + + ogs_eps_tai0_list_t target0; + ogs_eps_tai2_list_t target2; + ogs_nas_plmn_id_t ogs_nas_plmn_id; + + ogs_assert(target); + ogs_assert(source0); + ogs_assert(source2); + + memset(target, 0, sizeof(ogs_nas_tracking_area_identity_list_t)); + memset(&target0, 0, sizeof(ogs_eps_tai0_list_t)); + memset(&target2, 0, sizeof(ogs_eps_tai2_list_t)); + + for (i = 0; source0->tai[i].num; i++) { + ogs_assert(source0->tai[i].type == OGS_TAI0_TYPE); + target0.tai[i].type = source0->tai[i].type; + + /* target->num = source->num - 1 */ + ogs_assert(source0->tai[i].num < OGS_MAX_NUM_OF_TAI); + target0.tai[i].num = source0->tai[i].num - 1; + memcpy(&target0.tai[i].plmn_id, + ogs_nas_from_plmn_id(&ogs_nas_plmn_id, &source0->tai[i].plmn_id), + OGS_PLMN_ID_LEN); + + for (j = 0; j < source0->tai[i].num; j++) { + target0.tai[i].tac[j] = htobe16(source0->tai[i].tac[j]); + } + + size = (1 + 3 + 2 * source0->tai[i].num); + if ((target->length + size) > OGS_NAS_EPS_MAX_TAI_LIST_LEN) { + ogs_warn("Overflow: Ignore remained TAI LIST(length:%d, size:%d)", + target->length, size); + return; + } + memcpy(target->buffer + target->length, &target0.tai[i], size); + target->length += size; + } + + if (source2->num) { + memset(&target2, 0, sizeof(target2)); + + ogs_assert(source2->type == OGS_TAI1_TYPE || + source2->type == OGS_TAI2_TYPE); + target2.type = source2->type; + + /* target->num = source->num - 1 */ + ogs_assert(source2->num < OGS_MAX_NUM_OF_TAI); + target2.num = source2->num - 1; + + size = (1 + (3 + 2) * source2->num); + if ((target->length + size) > OGS_NAS_EPS_MAX_TAI_LIST_LEN) { + ogs_warn("Overflow: Ignore remained TAI LIST(length:%d, size:%d)", + target->length, size); + return; + } + for (i = 0; i < source2->num; i++) { + memcpy(&target2.tai[i].plmn_id, + ogs_nas_from_plmn_id(&ogs_nas_plmn_id, + &source2->tai[i].plmn_id), + OGS_PLMN_ID_LEN); + target2.tai[i].tac = htobe16(source2->tai[i].tac); + } + memcpy(target->buffer + target->length, &target2, size); + target->length += size; + } +} diff --git a/lib/nas/eps/types.h b/lib/nas/eps/types.h index c9dc7a78c..9f741c0d1 100644 --- a/lib/nas/eps/types.h +++ b/lib/nas/eps/types.h @@ -28,6 +28,15 @@ extern "C" { #endif +/********************** + * NAS GUTI Structure */ +typedef struct ogs_nas_eps_guti_s { + ogs_nas_plmn_id_t nas_plmn_id; + uint16_t mme_gid; + uint8_t mme_code; + uint32_t m_tmsi; +} __attribute__ ((packed)) ogs_nas_eps_guti_t; + /* 9.9.2.0A Device properties * See subclause 10.5.7.8 in 3GPP TS 24.008 [13]. * O TV 1 */ @@ -418,6 +427,57 @@ ED3(uint8_t type:4;, uint8_t tmsi_flag:1;) } __attribute__ ((packed)) ogs_nas_tmsi_status_t; +/* 9.9.3.32 Tracking area identity + * O TV 6 */ +typedef struct ogs_nas_tracking_area_identity_s { + ogs_nas_plmn_id_t nas_plmn_id; + uint16_t tac; +} __attribute__ ((packed)) ogs_nas_tracking_area_identity_t; + +typedef ogs_nas_tracking_area_identity_t ogs_nas_eps_tai_t; + +/* 9.9.3.33 Tracking area identity list + * M LV 7-97 */ +#define OGS_NAS_EPS_MAX_TAI_LIST_LEN 96 +typedef struct ogs_eps_tai0_list_s { + struct { + ED3(uint8_t spare:1;, + uint8_t type:2;, + uint8_t num:5;) + /* + * Do not change 'ogs_plmn_id_t' to 'ogs_nas_plmn_id_t'. + * Use 'ogs_plmn_id_t' for easy implementation. + * ogs_nas_tai_list_build() changes to NAS format(ogs_nas_plmn_id_t) + * and is sent to the UE. + */ + ogs_plmn_id_t plmn_id; + uint16_t tac[OGS_MAX_NUM_OF_TAI]; + } __attribute__ ((packed)) tai[OGS_MAX_NUM_OF_TAI]; +} __attribute__ ((packed)) ogs_eps_tai0_list_t; + +typedef struct ogs_eps_tai2_list_s { +ED3(uint8_t spare:1;, + uint8_t type:2;, + uint8_t num:5;) + /* + * Do not change 'ogs_eps_tai_t' to 'ogs_nas_tracking_area_identity_t'. + * Use 'ogs_eps_tai_t' for easy implementation. + * ogs_nas_tai_list_build() changes to NAS + * format(ogs_nas_tracking_area_identity_t) + * and is sent to the UE. + */ + ogs_eps_tai_t tai[OGS_MAX_NUM_OF_TAI]; +} __attribute__ ((packed)) ogs_eps_tai2_list_t; + +typedef struct ogs_nas_tracking_area_identity_list_s { + uint8_t length; + uint8_t buffer[OGS_NAS_EPS_MAX_TAI_LIST_LEN]; +} __attribute__ ((packed)) ogs_nas_tracking_area_identity_list_t; + +void ogs_nas_tai_list_build(ogs_nas_tracking_area_identity_list_t *target, + ogs_eps_tai0_list_t *source0, ogs_eps_tai2_list_t *source2); + + /* 9.9.3.35 UE radio capability information update needed * O TV 1 */ typedef struct ogs_nas_ue_radio_capability_information_update_needed_s { diff --git a/lib/ngap/conv.c b/lib/ngap/conv.c new file mode 100644 index 000000000..bc34a3620 --- /dev/null +++ b/lib/ngap/conv.c @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2019,2020 by Sukchan Lee + * + * This file is part of Open5GS. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 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, see . + */ + +#include "ogs-ngap.h" + +void ogs_ngap_uint32_to_GNB_ID(uint32_t gnb_id, NGAP_GNB_ID_t *gNB_ID) +{ + BIT_STRING_t *bit_string = NULL; + + ogs_assert(gNB_ID); + + bit_string = &gNB_ID->choice.gNB_ID; + ogs_assert(bit_string); + + gNB_ID->present = NGAP_GNB_ID_PR_gNB_ID; + bit_string->size = 3; + bit_string->buf = CALLOC(bit_string->size, sizeof(uint8_t)); + + bit_string->buf[0] = gnb_id >> 16; + bit_string->buf[1] = gnb_id >> 8; + bit_string->buf[2] = (gnb_id & 0xff); +} + +void ogs_ngap_GNB_ID_to_uint32(NGAP_GNB_ID_t *gNB_ID, uint32_t *gnb_id) +{ + uint8_t *buf = NULL; + + ogs_assert(gnb_id); + ogs_assert(gNB_ID); + + buf = gNB_ID->choice.gNB_ID.buf; + ogs_assert(buf); + + *gnb_id = (buf[0] << 16) + (buf[1] << 8) + buf[2]; +} + +void ogs_ngap_uint8_to_AMFRegionID( + uint8_t region, NGAP_AMFRegionID_t *aMFRegionID) +{ + ogs_assert(aMFRegionID); + + aMFRegionID->size = 1; + aMFRegionID->buf = CALLOC(aMFRegionID->size, sizeof(uint8_t)); + + aMFRegionID->buf[0] = region; +} +void ogs_ngap_uint16_to_NGAP_AMFSetID( + uint16_t set, NGAP_AMFSetID_t *aMFSetID) +{ + ogs_assert(aMFSetID); + + aMFSetID->size = 2; + aMFSetID->buf = CALLOC(aMFSetID->size, sizeof(uint8_t)); + aMFSetID->bits_unused = 6; + + aMFSetID->buf[0] = (set >> 2); + aMFSetID->buf[1] = ((set & 0x03) << 6); +} +void ogs_ngap_uint8_to_NGAP_NGAP_AMFPointer( + uint8_t pointer, NGAP_AMFPointer_t *aMFPointer) +{ + ogs_assert(aMFPointer); + + aMFPointer->size = 1; + aMFPointer->buf = CALLOC(aMFPointer->size, sizeof(uint8_t)); + aMFPointer->bits_unused = 2; + + aMFPointer->buf[0] = (pointer << 2); +} diff --git a/lib/ngap/conv.h b/lib/ngap/conv.h new file mode 100644 index 000000000..e039d3925 --- /dev/null +++ b/lib/ngap/conv.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2019,2020 by Sukchan Lee + * + * This file is part of Open5GS. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 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, see . + */ + +#if !defined(OGS_NGAP_INSIDE) && !defined(OGS_NGAP_COMPILATION) +#error "This header cannot be included directly." +#endif + +#ifndef OGS_NGAP_CONV_H +#define OGS_NGAP_CONV_H + +#ifdef __cplusplus +extern "C" { +#endif + +void ogs_ngap_uint32_to_GNB_ID(uint32_t gnb_id, NGAP_GNB_ID_t *gNB_ID); +void ogs_ngap_GNB_ID_to_uint32(NGAP_GNB_ID_t *gNB_ID, uint32_t *gnb_id); + +void ogs_ngap_uint8_to_AMFRegionID( + uint8_t region, NGAP_AMFRegionID_t *aMFRegionID); +void ogs_ngap_uint16_to_NGAP_AMFSetID( + uint16_t set, NGAP_AMFSetID_t *aMFSetID); +void ogs_ngap_uint8_to_NGAP_NGAP_AMFPointer( + uint8_t pointer, NGAP_AMFPointer_t *aMFPointer); + +#ifdef __cplusplus +} +#endif + +#endif /* OGS_NGAP_CONV_H */ + diff --git a/lib/ngap/meson.build b/lib/ngap/meson.build index ac07aeae6..a417d4f65 100644 --- a/lib/ngap/meson.build +++ b/lib/ngap/meson.build @@ -1,4 +1,4 @@ -# Copyright (C) 2019 by Sukchan Lee +# Copyright (C) 2019,2020 by Sukchan Lee # This file is part of Open5GS. @@ -16,6 +16,7 @@ # along with this program. If not, see . libngap_sources = files(''' + conv.c message.c '''.split()) diff --git a/lib/ngap/message.c b/lib/ngap/message.c index aad077748..1056176e8 100644 --- a/lib/ngap/message.c +++ b/lib/ngap/message.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 by Sukchan Lee + * Copyright (C) 2019,2020 by Sukchan Lee * * This file is part of Open5GS. * diff --git a/lib/ngap/message.h b/lib/ngap/message.h index 756f75170..8f6a916de 100644 --- a/lib/ngap/message.h +++ b/lib/ngap/message.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 by Sukchan Lee + * Copyright (C) 2019,2020 by Sukchan Lee * * This file is part of Open5GS. * @@ -28,27 +28,6 @@ extern "C" { #endif -/* Octets */ -#define OGS_NGAP_CLEAR_DATA(__dATA) \ - do { \ - ogs_assert((__dATA)); \ - if ((__dATA)->buf) { \ - FREEMEM((__dATA)->buf); \ - (__dATA)->buf = NULL; \ - (__dATA)->size = 0; \ - } \ - } while(0) -#define OGS_NGAP_STORE_DATA(__dST, __sRC) \ - do { \ - ogs_assert((__sRC)); \ - ogs_assert((__sRC)->buf); \ - ogs_assert((__dST)); \ - OGS_NGAP_CLEAR_DATA(__dST); \ - (__dST)->size = (__sRC)->size; \ - (__dST)->buf = CALLOC((__dST)->size, sizeof(uint8_t)); \ - memcpy((__dST)->buf, (__sRC)->buf, (__dST)->size); \ - } while(0) - typedef struct NGAP_NGAP_PDU ogs_ngap_message_t; int ogs_ngap_decode(ogs_ngap_message_t *message, ogs_pkbuf_t *pkbuf); diff --git a/lib/ngap/ogs-ngap.h b/lib/ngap/ogs-ngap.h index 6818304d8..92b31be64 100644 --- a/lib/ngap/ogs-ngap.h +++ b/lib/ngap/ogs-ngap.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 by Sukchan Lee + * Copyright (C) 2019,2020 by Sukchan Lee * * This file is part of Open5GS. * @@ -633,6 +633,7 @@ #define OGS_NGAP_INSIDE +#include "ngap/conv.h" #include "ngap/message.h" #undef OGS_NGAP_INSIDE diff --git a/lib/s1ap/message.h b/lib/s1ap/message.h index a447e285b..37a026989 100644 --- a/lib/s1ap/message.h +++ b/lib/s1ap/message.h @@ -28,27 +28,6 @@ extern "C" { #endif -/* Octets */ -#define OGS_S1AP_CLEAR_DATA(__dATA) \ - do { \ - ogs_assert((__dATA)); \ - if ((__dATA)->buf) { \ - FREEMEM((__dATA)->buf); \ - (__dATA)->buf = NULL; \ - (__dATA)->size = 0; \ - } \ - } while(0) -#define OGS_S1AP_STORE_DATA(__dST, __sRC) \ - do { \ - ogs_assert((__sRC)); \ - ogs_assert((__sRC)->buf); \ - ogs_assert((__dST)); \ - OGS_S1AP_CLEAR_DATA(__dST); \ - (__dST)->size = (__sRC)->size; \ - (__dST)->buf = CALLOC((__dST)->size, sizeof(uint8_t)); \ - memcpy((__dST)->buf, (__sRC)->buf, (__dST)->size); \ - } while(0) - typedef struct S1AP_S1AP_PDU ogs_s1ap_message_t; int ogs_s1ap_decode(ogs_s1ap_message_t *message, ogs_pkbuf_t *pkbuf); diff --git a/lib/sctp/ogs-sctp.h b/lib/sctp/ogs-sctp.h index aa311ee92..dd280a951 100644 --- a/lib/sctp/ogs-sctp.h +++ b/lib/sctp/ogs-sctp.h @@ -41,10 +41,12 @@ extern int __ogs_sctp_domain; #define OGS_S1AP_SCTP_PORT 36412 #define OGS_SGSAP_SCTP_PORT 29118 +#define OGS_NGAP_SCTP_PORT 38412 #define OGS_SCTP_S1AP_PPID 18 #define OGS_SCTP_X2AP_PPID 27 #define OGS_SCTP_SGSAP_PPID 0 +#define OGS_SCTP_NGAP_PPID 60 #if HAVE_USRSCTP diff --git a/src/amf/amf-sm.c b/src/amf/amf-sm.c new file mode 100644 index 000000000..591c01cc4 --- /dev/null +++ b/src/amf/amf-sm.c @@ -0,0 +1,415 @@ +/* + * Copyright (C) 2019,2020 by Sukchan Lee + * + * This file is part of Open5GS. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 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, see . + */ + +#include "sbi-path.h" +#include "nnrf-handler.h" +#include "ngap-path.h" + +void amf_state_initial(ogs_fsm_t *s, amf_event_t *e) +{ + amf_sm_debug(e); + + ogs_assert(s); + + OGS_FSM_TRAN(s, &amf_state_operational); +} + +void amf_state_final(ogs_fsm_t *s, amf_event_t *e) +{ + amf_sm_debug(e); + + ogs_assert(s); +} + +void amf_state_operational(ogs_fsm_t *s, amf_event_t *e) +{ + int rv; +#if 0 + ogs_pkbuf_t *recvbuf = NULL; +#endif + char buf[OGS_ADDRSTRLEN]; + + ogs_sock_t *sock = NULL; + ogs_sockaddr_t *addr = NULL; + amf_gnb_t *gnb = NULL; + uint16_t max_num_of_ostreams = 0; + + ogs_ngap_message_t ngap_message; + ogs_pkbuf_t *pkbuf = NULL; + int rc; + +#if 0 + ogs_nas_5gs_message_t nas_message; +#endif + + ogs_sbi_server_t *server = NULL; + ogs_sbi_session_t *session = NULL; + ogs_sbi_request_t *sbi_request = NULL; + + ogs_sbi_nf_instance_t *nf_instance = NULL; + ogs_sbi_subscription_t *subscription = NULL; + ogs_sbi_response_t *sbi_response = NULL; + ogs_sbi_message_t sbi_message; + + amf_sm_debug(e); + + ogs_assert(s); + + switch (e->id) { + case OGS_FSM_ENTRY_SIG: + rv = amf_sbi_open(); + if (rv != OGS_OK) { + ogs_fatal("Can't establish SBI path"); + } + + rv = ngap_open(); + if (rv != OGS_OK) { + ogs_error("Can't establish NGAP path"); + break; + } + + break; + + case OGS_FSM_EXIT_SIG: + ngap_close(); + amf_sbi_close(); + break; + + case AMF_EVT_SBI_SERVER: + sbi_request = e->sbi.request; + ogs_assert(sbi_request); + session = e->sbi.session; + ogs_assert(session); + server = e->sbi.server; + ogs_assert(server); + + rv = ogs_sbi_parse_request(&sbi_message, sbi_request); + if (rv != OGS_OK) { + /* 'sbi_message' buffer is released in ogs_sbi_parse_request() */ + ogs_error("cannot parse HTTP sbi_message"); + ogs_sbi_server_send_error(session, OGS_SBI_HTTP_STATUS_BAD_REQUEST, + NULL, "cannot parse HTTP sbi_message", NULL); + break; + } + + if (strcmp(sbi_message.h.api.version, OGS_SBI_API_VERSION) != 0) { + ogs_error("Not supported version [%s]", sbi_message.h.api.version); + ogs_sbi_server_send_error(session, OGS_SBI_HTTP_STATUS_BAD_REQUEST, + &sbi_message, "Not supported version", NULL); + ogs_sbi_message_free(&sbi_message); + break; + } + + SWITCH(sbi_message.h.service.name) + CASE(OGS_SBI_SERVICE_NAME_NRF_NFM) + + SWITCH(sbi_message.h.resource.name) + CASE(OGS_SBI_RESOURCE_NAME_NF_STATUS_NOTIFY) + SWITCH(sbi_message.h.method) + CASE(OGS_SBI_HTTP_METHOD_POST) + amf_nnrf_handle_nf_status_notify( + server, session, &sbi_message); + break; + + DEFAULT + ogs_error("Invalid HTTP method [%s]", + sbi_message.h.method); + ogs_sbi_server_send_error(session, + OGS_SBI_HTTP_STATUS_MEHTOD_NOT_ALLOWED, + &sbi_message, + "Invalid HTTP method", sbi_message.h.method); + END + break; + + DEFAULT + ogs_error("Invalid resource name [%s]", + sbi_message.h.resource.name); + ogs_sbi_server_send_error(session, + OGS_SBI_HTTP_STATUS_MEHTOD_NOT_ALLOWED, &sbi_message, + "Unknown resource name", sbi_message.h.resource.name); + END + break; + + DEFAULT + ogs_error("Invalid API name [%s]", sbi_message.h.service.name); + ogs_sbi_server_send_error(session, + OGS_SBI_HTTP_STATUS_MEHTOD_NOT_ALLOWED, &sbi_message, + "Invalid API name", sbi_message.h.resource.name); + END + + /* In lib/sbi/server.c, notify_completed() releases 'request' buffer. */ + ogs_sbi_message_free(&sbi_message); + break; + + case AMF_EVT_SBI_CLIENT: + ogs_assert(e); + + sbi_response = e->sbi.response; + ogs_assert(sbi_response); + rv = ogs_sbi_parse_response(&sbi_message, sbi_response); + if (rv != OGS_OK) { + ogs_error("cannot parse HTTP response"); + ogs_sbi_message_free(&sbi_message); + ogs_sbi_response_free(sbi_response); + break; + } + + if (strcmp(sbi_message.h.api.version, OGS_SBI_API_VERSION) != 0) { + ogs_error("Not supported version [%s]", sbi_message.h.api.version); + ogs_sbi_message_free(&sbi_message); + ogs_sbi_response_free(sbi_response); + break; + } + + SWITCH(sbi_message.h.service.name) + CASE(OGS_SBI_SERVICE_NAME_NRF_NFM) + + SWITCH(sbi_message.h.resource.name) + CASE(OGS_SBI_RESOURCE_NAME_NF_INSTANCES) + nf_instance = e->sbi.data; + ogs_assert(nf_instance); + ogs_assert(OGS_FSM_STATE(&nf_instance->sm)); + + e->sbi.message = &sbi_message; + ogs_fsm_dispatch(&nf_instance->sm, e); + + if (OGS_FSM_CHECK(&nf_instance->sm, amf_nf_state_exception)) { + ogs_error("State machine exception"); + } + break; + + CASE(OGS_SBI_RESOURCE_NAME_SUBSCRIPTIONS) + subscription = e->sbi.data; + ogs_assert(subscription); + + SWITCH(sbi_message.h.method) + CASE(OGS_SBI_HTTP_METHOD_POST) + if (sbi_message.res_status == OGS_SBI_HTTP_STATUS_CREATED || + sbi_message.res_status == OGS_SBI_HTTP_STATUS_OK) { + amf_nnrf_handle_nf_status_subscribe( + subscription, &sbi_message); + } else { + ogs_error("HTTP response error : %d", + sbi_message.res_status); + } + break; + + CASE(OGS_SBI_HTTP_METHOD_DELETE) + if (sbi_message.res_status == + OGS_SBI_HTTP_STATUS_NO_CONTENT) { + ogs_sbi_subscription_remove(subscription); + } else { + ogs_error("HTTP response error : %d", + sbi_message.res_status); + } + break; + + DEFAULT + ogs_error("Invalid HTTP method [%s]", sbi_message.h.method); + END + break; + + DEFAULT + ogs_error("Invalid resource name [%s]", + sbi_message.h.resource.name); + END + break; + + CASE(OGS_SBI_SERVICE_NAME_NRF_DISC) + SWITCH(sbi_message.h.resource.name) + CASE(OGS_SBI_RESOURCE_NAME_NF_INSTANCES) + if (sbi_message.res_status == OGS_SBI_HTTP_STATUS_OK) { + amf_nnrf_handle_nf_discover(&sbi_message); + } else { + ogs_error("HTTP response error : %d", + sbi_message.res_status); + } + break; + + DEFAULT + ogs_error("Invalid resource name [%s]", + sbi_message.h.resource.name); + END + break; + + DEFAULT + ogs_error("Invalid API name [%s]", sbi_message.h.service.name); + END + + ogs_sbi_message_free(&sbi_message); + ogs_sbi_response_free(sbi_response); + break; + + case AMF_EVT_SBI_TIMER: + ogs_assert(e); + + switch(e->timer_id) { + case AMF_TIMER_NF_INSTANCE_REGISTRATION_INTERVAL: + case AMF_TIMER_NF_INSTANCE_HEARTBEAT_INTERVAL: + case AMF_TIMER_NF_INSTANCE_HEARTBEAT: + case AMF_TIMER_NF_INSTANCE_VALIDITY: + nf_instance = e->sbi.data; + ogs_assert(nf_instance); + ogs_assert(OGS_FSM_STATE(&nf_instance->sm)); + + ogs_fsm_dispatch(&nf_instance->sm, e); + if (OGS_FSM_CHECK(&nf_instance->sm, amf_nf_state_de_registered)) { + amf_nf_fsm_fini(nf_instance); + ogs_sbi_nf_instance_remove(nf_instance); + + } else if (OGS_FSM_CHECK(&nf_instance->sm, + amf_nf_state_exception)) { + ogs_error("State machine exception"); + } + break; + + case AMF_TIMER_SUBSCRIPTION_VALIDITY: + subscription = e->sbi.data; + ogs_assert(subscription); + + ogs_info("Subscription validity expired [%s]", subscription->id); + ogs_sbi_subscription_remove(subscription); + + amf_sbi_send_nf_status_subscribe(subscription->client, + amf_self()->nf_type, subscription->nf_instance_id); + break; + + default: + ogs_error("Unknown timer[%s:%d]", + amf_timer_get_name(e->timer_id), e->timer_id); + } + break; + + case AMF_EVT_NGAP_LO_ACCEPT: + sock = e->ngap.sock; + ogs_assert(sock); + addr = e->ngap.addr; + ogs_assert(addr); + + ogs_info("gNB-N1 accepted[%s] in master_sm module", + OGS_ADDR(addr, buf)); + + gnb = amf_gnb_find_by_addr(addr); + if (!gnb) { + gnb = amf_gnb_add(sock, addr); + ogs_assert(gnb); + } else { + ogs_warn("gNB context duplicated with IP-address [%s]!!!", + OGS_ADDR(addr, buf)); + ogs_sock_destroy(sock); + ogs_warn("N1 Socket Closed"); + } + + break; + + case AMF_EVT_NGAP_LO_SCTP_COMM_UP: + sock = e->ngap.sock; + ogs_assert(sock); + addr = e->ngap.addr; + ogs_assert(addr); + + max_num_of_ostreams = e->ngap.max_num_of_ostreams; + + gnb = amf_gnb_find_by_addr(addr); + if (!gnb) { + gnb = amf_gnb_add(sock, addr); + ogs_assert(gnb); + } else { + ogs_free(addr); + } + + gnb->max_num_of_ostreams = + ogs_min(max_num_of_ostreams, gnb->max_num_of_ostreams); + + ogs_debug("gNB-N1 SCTP_COMM_UP[%s] Max Num of Outbound Streams[%d]", + OGS_ADDR(addr, buf), gnb->max_num_of_ostreams); + + break; + + case AMF_EVT_NGAP_LO_CONNREFUSED: + sock = e->ngap.sock; + ogs_assert(sock); + addr = e->ngap.addr; + ogs_assert(addr); + + gnb = amf_gnb_find_by_addr(addr); + ogs_free(addr); + + if (gnb) { + ogs_info("gNB-N1[%s] connection refused!!!", + OGS_ADDR(addr, buf)); + amf_gnb_remove(gnb); + } else { + ogs_warn("gNB-N1[%s] connection refused, Already Removed!", + OGS_ADDR(addr, buf)); + } + + break; + case AMF_EVT_NGAP_MESSAGE: + sock = e->ngap.sock; + ogs_assert(sock); + addr = e->ngap.addr; + ogs_assert(addr); + pkbuf = e->pkbuf; + ogs_assert(pkbuf); + + gnb = amf_gnb_find_by_addr(addr); + ogs_free(addr); + + ogs_assert(gnb); + ogs_assert(OGS_FSM_STATE(&gnb->sm)); + + rc = ogs_ngap_decode(&ngap_message, pkbuf); + if (rc == OGS_OK) { + e->gnb = gnb; + e->ngap.message = &ngap_message; + ogs_fsm_dispatch(&gnb->sm, e); + } else { + ogs_error("Cannot decode NGAP message"); +#if 0 + ngap_send_error_indication( + gnb, NULL, NULL, NGAP_Cause_PR_protocol, + NGAP_CauseProtocol_abstract_syntax_error_falsely_constructed_message); +#endif + } + + ogs_ngap_free(&ngap_message); + ogs_pkbuf_free(pkbuf); + break; + + case AMF_EVT_NGAP_TIMER: +#if 0 + gnb_ue = e->gnb_ue; + ogs_assert(gnb_ue); + gnb = e->gnb; + ogs_assert(gnb); + ogs_assert(OGS_FSM_STATE(&gnb->sm)); + + ogs_fsm_dispatch(&gnb->sm, e); +#else + ogs_fatal("Not implemeted"); +#endif + break; + + + default: + ogs_error("No handler for event %s", amf_event_get_name(e)); + break; + } +} diff --git a/src/amf/amf-sm.h b/src/amf/amf-sm.h new file mode 100644 index 000000000..f392a3b88 --- /dev/null +++ b/src/amf/amf-sm.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2019,2020 by Sukchan Lee + * + * This file is part of Open5GS. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 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, see . + */ + +#ifndef AMF_SM_H +#define AMF_SM_H + +#include "event.h" + +#ifdef __cplusplus +extern "C" { +#endif + +void amf_state_initial(ogs_fsm_t *s, amf_event_t *e); +void amf_state_final(ogs_fsm_t *s, amf_event_t *e); +void amf_state_operational(ogs_fsm_t *s, amf_event_t *e); + +void amf_nf_fsm_init(ogs_sbi_nf_instance_t *nf_instance); +void amf_nf_fsm_fini(ogs_sbi_nf_instance_t *nf_instance); + +void amf_nf_state_initial(ogs_fsm_t *s, amf_event_t *e); +void amf_nf_state_final(ogs_fsm_t *s, amf_event_t *e); +void amf_nf_state_will_register(ogs_fsm_t *s, amf_event_t *e); +void amf_nf_state_registered(ogs_fsm_t *s, amf_event_t *e); +void amf_nf_state_de_registered(ogs_fsm_t *s, amf_event_t *e); +void amf_nf_state_exception(ogs_fsm_t *s, amf_event_t *e); + +void ngap_state_initial(ogs_fsm_t *s, amf_event_t *e); +void ngap_state_final(ogs_fsm_t *s, amf_event_t *e); +void ngap_state_operational(ogs_fsm_t *s, amf_event_t *e); +void ngap_state_exception(ogs_fsm_t *s, amf_event_t *e); + +#define amf_sm_debug(__pe) \ + ogs_debug("%s(): %s", __func__, amf_event_get_name(__pe)) + +#ifdef __cplusplus +} +#endif + +#endif /* AMF_SM_H */ diff --git a/src/amf/app.c b/src/amf/app.c new file mode 100644 index 000000000..b6b208cad --- /dev/null +++ b/src/amf/app.c @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2019,2020 by Sukchan Lee + * + * This file is part of Open5GS. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 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, see . + */ + +#include "ogs-app.h" + +int app_initialize(const char *const argv[]) +{ + int rv; + + rv = amf_initialize(); + if (rv != OGS_OK) { + ogs_error("Failed to intialize AMF"); + return rv; + } + ogs_info("AMF initialize...done"); + + return OGS_OK; +} + +void app_terminate(void) +{ + amf_terminate(); + ogs_info("AMF terminate...done"); +} diff --git a/src/amf/context.c b/src/amf/context.c new file mode 100644 index 000000000..c73195af2 --- /dev/null +++ b/src/amf/context.c @@ -0,0 +1,1044 @@ +/* + * Copyright (C) 2019,2020 by Sukchan Lee + * + * This file is part of Open5GS. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 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, see . + */ + +#include "ngap-path.h" + +static amf_context_t self; + +int __amf_log_domain; + +static OGS_POOL(amf_gnb_pool, amf_gnb_t); + +static int context_initialized = 0; + +void amf_context_init(void) +{ + ogs_assert(context_initialized == 0); + + /* Initialize AMF context */ + memset(&self, 0, sizeof(amf_context_t)); + + ogs_log_install_domain(&__amf_log_domain, "amf", ogs_core()->log.level); + + ogs_list_init(&self.ngap_list); + ogs_list_init(&self.ngap_list6); + + /* Allocate TWICE the pool to check if maximum number of gNBs is reached */ + ogs_pool_init(&amf_gnb_pool, ogs_config()->max.gnb*2); + + ogs_list_init(&self.gnb_list); + self.gnb_addr_hash = ogs_hash_make(); + self.gnb_id_hash = ogs_hash_make(); + + ogs_pool_init(&self.m_tmsi, ogs_config()->pool.ue); + + context_initialized = 1; +} + +void amf_context_final(void) +{ + ogs_assert(context_initialized == 1); + + amf_gnb_remove_all(); + + ogs_assert(self.gnb_addr_hash); + ogs_hash_destroy(self.gnb_addr_hash); + ogs_assert(self.gnb_id_hash); + ogs_hash_destroy(self.gnb_id_hash); + + ogs_pool_final(&self.m_tmsi); + + ogs_pool_final(&amf_gnb_pool); + + context_initialized = 0; +} + +amf_context_t *amf_self(void) +{ + return &self; +} + +static int amf_context_prepare(void) +{ + self.nf_type = OpenAPI_nf_type_AMF; + + self.relative_capacity = 0xff; + + self.ngap_port = OGS_NGAP_SCTP_PORT; + + return OGS_OK; +} + +static int amf_context_validation(void) +{ + if (ogs_list_first(&self.ngap_list) == NULL && + ogs_list_first(&self.ngap_list6) == NULL) { + ogs_error("No amf.ngap in '%s'", ogs_config()->file); + return OGS_RETRY; + } + + if (self.num_of_served_guami == 0) { + ogs_error("No amf.guami in '%s'", ogs_config()->file); + return OGS_ERROR; + } + + if (self.num_of_served_tai == 0) { + ogs_error("No amf.tai in '%s'", ogs_config()->file); + return OGS_ERROR; + } + + if (self.num_of_plmn_support == 0) { + ogs_error("No amf.plmn in '%s'", ogs_config()->file); + return OGS_ERROR; + } + + if (self.plmn_support[0].num_of_s_nssai == 0) { + ogs_error("No amf.plmn.s_nssai in '%s'", ogs_config()->file); + return OGS_ERROR; + } + + if (self.served_tai[0].list0.tai[0].num == 0 && + self.served_tai[0].list2.num == 0) { + ogs_error("No amf.tai.plmn_id|tac in '%s'", ogs_config()->file); + return OGS_ERROR; + } + + if (self.amf_name == NULL) { + ogs_error("No amf.amf_name in '%s'", ogs_config()->file); + return OGS_ERROR; + } + + if (self.num_of_integrity_order == 0) { + ogs_error("No amf.security.integrity_order in '%s'", + ogs_config()->file); + return OGS_ERROR; + } + if (self.num_of_ciphering_order == 0) { + ogs_error("no amf.security.ciphering_order in '%s'", + ogs_config()->file); + return OGS_ERROR; + } + + + return OGS_OK; +} + +int amf_context_parse_config(void) +{ + int rv; + yaml_document_t *document = NULL; + ogs_yaml_iter_t root_iter; + + document = ogs_config()->document; + ogs_assert(document); + + rv = amf_context_prepare(); + if (rv != OGS_OK) return rv; + + ogs_yaml_iter_init(&root_iter, document); + while (ogs_yaml_iter_next(&root_iter)) { + const char *root_key = ogs_yaml_iter_key(&root_iter); + ogs_assert(root_key); + if (!strcmp(root_key, "amf")) { + ogs_yaml_iter_t amf_iter; + ogs_yaml_iter_recurse(&root_iter, &amf_iter); + while (ogs_yaml_iter_next(&amf_iter)) { + const char *amf_key = ogs_yaml_iter_key(&amf_iter); + ogs_assert(amf_key); + if (!strcmp(amf_key, "relative_capacity")) { + const char *v = ogs_yaml_iter_value(&amf_iter); + if (v) self.relative_capacity = atoi(v); + } else if (!strcmp(amf_key, "ngap")) { + ogs_yaml_iter_t ngap_array, ngap_iter; + ogs_yaml_iter_recurse(&amf_iter, &ngap_array); + do { + int family = AF_UNSPEC; + int i, num = 0; + const char *hostname[OGS_MAX_NUM_OF_HOSTNAME]; + uint16_t port = self.ngap_port; + const char *dev = NULL; + ogs_sockaddr_t *addr = NULL; + + if (ogs_yaml_iter_type(&ngap_array) == + YAML_MAPPING_NODE) { + memcpy(&ngap_iter, &ngap_array, + sizeof(ogs_yaml_iter_t)); + } else if (ogs_yaml_iter_type(&ngap_array) == + YAML_SEQUENCE_NODE) { + if (!ogs_yaml_iter_next(&ngap_array)) + break; + ogs_yaml_iter_recurse(&ngap_array, &ngap_iter); + } else if (ogs_yaml_iter_type(&ngap_array) == + YAML_SCALAR_NODE) { + break; + } else + ogs_assert_if_reached(); + + while (ogs_yaml_iter_next(&ngap_iter)) { + const char *ngap_key = + ogs_yaml_iter_key(&ngap_iter); + ogs_assert(ngap_key); + if (!strcmp(ngap_key, "family")) { + const char *v = ogs_yaml_iter_value(&ngap_iter); + if (v) family = atoi(v); + if (family != AF_UNSPEC && + family != AF_INET && family != AF_INET6) { + ogs_warn("Ignore family(%d) : " + "AF_UNSPEC(%d), " + "AF_INET(%d), AF_INET6(%d) ", + family, AF_UNSPEC, AF_INET, AF_INET6); + family = AF_UNSPEC; + } + } else if (!strcmp(ngap_key, "addr") || + !strcmp(ngap_key, "name")) { + ogs_yaml_iter_t hostname_iter; + ogs_yaml_iter_recurse( + &ngap_iter, &hostname_iter); + ogs_assert(ogs_yaml_iter_type(&hostname_iter) != + YAML_MAPPING_NODE); + + do { + if (ogs_yaml_iter_type(&hostname_iter) == + YAML_SEQUENCE_NODE) { + if (!ogs_yaml_iter_next(&hostname_iter)) + break; + } + + ogs_assert(num <= OGS_MAX_NUM_OF_HOSTNAME); + hostname[num++] = + ogs_yaml_iter_value(&hostname_iter); + } while ( + ogs_yaml_iter_type(&hostname_iter) == + YAML_SEQUENCE_NODE); + } else if (!strcmp(ngap_key, "port")) { + const char *v = ogs_yaml_iter_value(&ngap_iter); + if (v) port = atoi(v); + } else if (!strcmp(ngap_key, "dev")) { + dev = ogs_yaml_iter_value(&ngap_iter); + } else + ogs_warn("unknown key `%s`", ngap_key); + } + + addr = NULL; + for (i = 0; i < num; i++) { + rv = ogs_addaddrinfo(&addr, + family, hostname[i], port, 0); + ogs_assert(rv == OGS_OK); + } + + if (addr) { + if (ogs_config()->parameter.no_ipv4 == 0) + ogs_socknode_add( + &self.ngap_list, AF_INET, addr); + if (ogs_config()->parameter.no_ipv6 == 0) + ogs_socknode_add( + &self.ngap_list6, AF_INET6, addr); + ogs_freeaddrinfo(addr); + } + + if (dev) { + rv = ogs_socknode_probe( + ogs_config()->parameter.no_ipv4 ? + NULL : &self.ngap_list, + ogs_config()->parameter.no_ipv6 ? + NULL : &self.ngap_list6, + dev, port); + ogs_assert(rv == OGS_OK); + } + + } while (ogs_yaml_iter_type(&ngap_array) == + YAML_SEQUENCE_NODE); + + if (ogs_list_first(&self.ngap_list) == NULL && + ogs_list_first(&self.ngap_list6) == NULL) { + rv = ogs_socknode_probe( + ogs_config()->parameter.no_ipv4 ? + NULL : &self.ngap_list, + ogs_config()->parameter.no_ipv6 ? + NULL : &self.ngap_list6, + NULL, self.ngap_port); + ogs_assert(rv == OGS_OK); + } + } else if (!strcmp(amf_key, "guami")) { + ogs_yaml_iter_t guami_array, guami_iter; + ogs_yaml_iter_recurse(&amf_iter, &guami_array); + do { + const char *mcc = NULL, *mnc = NULL; + const char *region = NULL, *set = NULL; + const char *pointer = NULL; + ogs_assert(self.num_of_served_guami <= + MAX_NUM_OF_SERVED_GUAMI); + + if (ogs_yaml_iter_type(&guami_array) == + YAML_MAPPING_NODE) { + memcpy(&guami_iter, &guami_array, + sizeof(ogs_yaml_iter_t)); + } else if (ogs_yaml_iter_type(&guami_array) == + YAML_SEQUENCE_NODE) { + if (!ogs_yaml_iter_next(&guami_array)) + break; + ogs_yaml_iter_recurse(&guami_array, + &guami_iter); + } else if (ogs_yaml_iter_type(&guami_array) == + YAML_SCALAR_NODE) { + break; + } else + ogs_assert_if_reached(); + + while (ogs_yaml_iter_next(&guami_iter)) { + const char *guami_key = + ogs_yaml_iter_key(&guami_iter); + ogs_assert(guami_key); + if (!strcmp(guami_key, "plmn_id")) { + ogs_yaml_iter_t plmn_id_iter; + + ogs_yaml_iter_recurse(&guami_iter, + &plmn_id_iter); + while (ogs_yaml_iter_next(&plmn_id_iter)) { + const char *plmn_id_key = + ogs_yaml_iter_key(&plmn_id_iter); + ogs_assert(plmn_id_key); + if (!strcmp(plmn_id_key, "mcc")) { + mcc = ogs_yaml_iter_value( + &plmn_id_iter); + } else if (!strcmp(plmn_id_key, "mnc")) { + mnc = ogs_yaml_iter_value( + &plmn_id_iter); + } + } + + if (mcc && mnc) { + ogs_plmn_id_build( + &self.served_guami[ + self.num_of_served_guami]. + plmn_id, + atoi(mcc), atoi(mnc), strlen(mnc)); + } + } else if (!strcmp(guami_key, "amf_id")) { + ogs_yaml_iter_t amf_id_iter; + + ogs_yaml_iter_recurse(&guami_iter, + &amf_id_iter); + while (ogs_yaml_iter_next(&amf_id_iter)) { + const char *amf_id_key = + ogs_yaml_iter_key(&amf_id_iter); + ogs_assert(amf_id_key); + if (!strcmp(amf_id_key, "region")) { + region = ogs_yaml_iter_value( + &amf_id_iter); + } else if (!strcmp(amf_id_key, "set")) { + set = ogs_yaml_iter_value( + &amf_id_iter); + } else if (!strcmp(amf_id_key, "pointer")) { + pointer = ogs_yaml_iter_value( + &amf_id_iter); + } + } + + if (region && set) { + ogs_amf_id_build( + &self.served_guami[ + self.num_of_served_guami]. + amf_id, + atoi(region), atoi(set), + pointer ? atoi(pointer) : 0); + } + } else + ogs_warn("unknown key `%s`", guami_key); + } + + if (mnc && mcc && region && set) { + self.num_of_served_guami++; + } else { + ogs_warn("Ignore guami : " + "mcc(%s), mnc(%s), region(%s), set(%s)", + mcc, mnc, region, set); + } + } while (ogs_yaml_iter_type(&guami_array) == + YAML_SEQUENCE_NODE); + } else if (!strcmp(amf_key, "tai")) { + int num_of_list0 = 0; + ogs_5gs_tai0_list_t *list0 = NULL; + ogs_5gs_tai2_list_t *list2 = NULL; + + ogs_assert(self.num_of_served_tai <= + OGS_MAX_NUM_OF_SERVED_TAI); + list0 = &self.served_tai[self.num_of_served_tai].list0; + ogs_assert(list0); + list2 = &self.served_tai[self.num_of_served_tai].list2; + ogs_assert(list2); + + ogs_yaml_iter_t tai_array, tai_iter; + ogs_yaml_iter_recurse(&amf_iter, &tai_array); + do { + const char *mcc = NULL, *mnc = NULL; + ogs_uint24_t tac[OGS_MAX_NUM_OF_TAI]; + int num_of_tac = 0; + + if (ogs_yaml_iter_type(&tai_array) == + YAML_MAPPING_NODE) { + memcpy(&tai_iter, &tai_array, + sizeof(ogs_yaml_iter_t)); + } else if (ogs_yaml_iter_type(&tai_array) == + YAML_SEQUENCE_NODE) { + if (!ogs_yaml_iter_next(&tai_array)) + break; + ogs_yaml_iter_recurse(&tai_array, + &tai_iter); + } else if (ogs_yaml_iter_type(&tai_array) == + YAML_SCALAR_NODE) { + break; + } else + ogs_assert_if_reached(); + + while (ogs_yaml_iter_next(&tai_iter)) { + const char *tai_key = ogs_yaml_iter_key(&tai_iter); + ogs_assert(tai_key); + if (!strcmp(tai_key, "plmn_id")) { + ogs_yaml_iter_t plmn_id_iter; + + ogs_yaml_iter_recurse(&tai_iter, &plmn_id_iter); + while (ogs_yaml_iter_next(&plmn_id_iter)) { + const char *plmn_id_key = + ogs_yaml_iter_key(&plmn_id_iter); + ogs_assert(plmn_id_key); + if (!strcmp(plmn_id_key, "mcc")) { + mcc = ogs_yaml_iter_value( + &plmn_id_iter); + } else if (!strcmp(plmn_id_key, "mnc")) { + mnc = ogs_yaml_iter_value( + &plmn_id_iter); + } + } + } else if (!strcmp(tai_key, "tac")) { + ogs_yaml_iter_t tac_iter; + ogs_yaml_iter_recurse(&tai_iter, &tac_iter); + ogs_assert(ogs_yaml_iter_type(&tac_iter) != + YAML_MAPPING_NODE); + + do { + const char *v = NULL; + + ogs_assert(num_of_tac <= + OGS_MAX_NUM_OF_TAI); + if (ogs_yaml_iter_type(&tac_iter) == + YAML_SEQUENCE_NODE) { + if (!ogs_yaml_iter_next(&tac_iter)) + break; + } + + v = ogs_yaml_iter_value(&tac_iter); + if (v) { + tac[num_of_tac].v = atoi(v); + num_of_tac++; + } + } while ( + ogs_yaml_iter_type(&tac_iter) == + YAML_SEQUENCE_NODE); + } else + ogs_warn("unknown key `%s`", tai_key); + } + + if (mcc && mnc && num_of_tac) { + if (num_of_tac == 1) { + ogs_plmn_id_build( + &list2->tai[list2->num].plmn_id, + atoi(mcc), atoi(mnc), strlen(mnc)); + list2->tai[list2->num].tac.v = tac[0].v; + + list2->num++; + if (list2->num > 1) + list2->type = OGS_TAI2_TYPE; + else + list2->type = OGS_TAI1_TYPE; + } else if (num_of_tac > 1) { + int i; + ogs_plmn_id_build( + &list0->tai[num_of_list0].plmn_id, + atoi(mcc), atoi(mnc), strlen(mnc)); + for (i = 0; i < num_of_tac; i++) { + list0->tai[num_of_list0].tac[i].v = + tac[i].v; + } + + list0->tai[num_of_list0].num = num_of_tac; + list0->tai[num_of_list0].type = OGS_TAI0_TYPE; + + num_of_list0++; + } + } else { + ogs_warn("Ignore tai : mcc(%p), mnc(%p), " + "num_of_tac(%d)", mcc, mnc, num_of_tac); + } + } while (ogs_yaml_iter_type(&tai_array) == + YAML_SEQUENCE_NODE); + + if (list2->num || num_of_list0) { + self.num_of_served_tai++; + } + } else if (!strcmp(amf_key, "plmn")) { + ogs_yaml_iter_t plmn_array, plmn_iter; + ogs_yaml_iter_recurse(&amf_iter, &plmn_array); + do { + const char *mnc = NULL, *mcc = NULL; + ogs_assert(self.num_of_plmn_support <= + OGS_MAX_NUM_OF_PLMN); + + if (ogs_yaml_iter_type(&plmn_array) == + YAML_MAPPING_NODE) { + memcpy(&plmn_iter, &plmn_array, + sizeof(ogs_yaml_iter_t)); + } else if (ogs_yaml_iter_type(&plmn_array) == + YAML_SEQUENCE_NODE) { + if (!ogs_yaml_iter_next(&plmn_array)) + break; + ogs_yaml_iter_recurse(&plmn_array, + &plmn_iter); + } else if (ogs_yaml_iter_type(&plmn_array) == + YAML_SCALAR_NODE) { + break; + } else + ogs_assert_if_reached(); + + while (ogs_yaml_iter_next(&plmn_iter)) { + const char *plmn_key = + ogs_yaml_iter_key(&plmn_iter); + ogs_assert(plmn_key); + if (!strcmp(plmn_key, "plmn_id")) { + ogs_yaml_iter_t plmn_id_iter; + + ogs_yaml_iter_recurse(&plmn_iter, + &plmn_id_iter); + while (ogs_yaml_iter_next(&plmn_id_iter)) { + const char *plmn_id_key = + ogs_yaml_iter_key(&plmn_id_iter); + ogs_assert(plmn_id_key); + if (!strcmp(plmn_id_key, "mcc")) { + mcc = ogs_yaml_iter_value( + &plmn_id_iter); + } else if (!strcmp(plmn_id_key, "mnc")) { + mnc = ogs_yaml_iter_value( + &plmn_id_iter); + } + } + + if (mcc && mnc) { + ogs_plmn_id_build( + &self.plmn_support[ + self.num_of_plmn_support]. + plmn_id, + atoi(mcc), atoi(mnc), strlen(mnc)); + } + } else if (!strcmp(plmn_key, "s_nssai")) { + ogs_yaml_iter_t s_nssai_array, s_nssai_iter; + ogs_yaml_iter_recurse(&plmn_iter, + &s_nssai_array); + do { + ogs_s_nssai_t *s_nssai = NULL; + const char *sst = NULL, *sd = NULL; + ogs_assert( + self.plmn_support[ + self.num_of_plmn_support]. + num_of_s_nssai <= + OGS_MAX_NUM_OF_S_NSSAI); + s_nssai = &self.plmn_support[ + self.num_of_plmn_support].s_nssai[ + self.plmn_support[ + self.num_of_plmn_support]. + num_of_s_nssai]; + ogs_assert(s_nssai); + + if (ogs_yaml_iter_type(&s_nssai_array) == + YAML_MAPPING_NODE) { + memcpy(&s_nssai_iter, &s_nssai_array, + sizeof(ogs_yaml_iter_t)); + } else if (ogs_yaml_iter_type( + &s_nssai_array) == + YAML_SEQUENCE_NODE) { + if (!ogs_yaml_iter_next(&s_nssai_array)) + break; + ogs_yaml_iter_recurse(&s_nssai_array, + &s_nssai_iter); + } else if (ogs_yaml_iter_type( + &s_nssai_array) == + YAML_SCALAR_NODE) { + break; + } else + ogs_assert_if_reached(); + + while (ogs_yaml_iter_next(&s_nssai_iter)) { + const char *s_nssai_key = + ogs_yaml_iter_key(&s_nssai_iter); + ogs_assert(s_nssai_key); + if (!strcmp(s_nssai_key, "sst")) { + sst = ogs_yaml_iter_value( + &s_nssai_iter); + } else if (!strcmp( + s_nssai_key, "sd")) { + sd = ogs_yaml_iter_value( + &s_nssai_iter); + } + } + + if (sst) { + s_nssai->sst = atoi(sst); + if (sd) { + s_nssai->sd = + ogs_uint24_from_string( + (char*)sd); + } else { + s_nssai->sd.v = + OGS_S_NSSAI_NO_SD_VALUE; + } + + self.plmn_support[ + self.num_of_plmn_support]. + num_of_s_nssai++; + } + + } while (ogs_yaml_iter_type(&s_nssai_array) == + YAML_SEQUENCE_NODE); + } else + ogs_warn("unknown key `%s`", plmn_key); + } + + if (self.plmn_support[ + self.num_of_plmn_support].num_of_s_nssai && + mcc && mnc) { + self.num_of_plmn_support++; + } else { + ogs_warn("Ignore plmn : " + "s_nssai(%d) mcc(%s), mnc(%s)", + self.plmn_support[ + self.num_of_plmn_support].num_of_s_nssai, + mcc, mnc); + self.plmn_support[ + self.num_of_plmn_support].num_of_s_nssai = 0; + } + } while (ogs_yaml_iter_type(&plmn_array) == + YAML_SEQUENCE_NODE); + } else if (!strcmp(amf_key, "security")) { + ogs_yaml_iter_t security_iter; + ogs_yaml_iter_recurse(&amf_iter, &security_iter); + while (ogs_yaml_iter_next(&security_iter)) { + const char *security_key = + ogs_yaml_iter_key(&security_iter); + ogs_assert(security_key); + if (!strcmp(security_key, "integrity_order")) { + ogs_yaml_iter_t integrity_order_iter; + ogs_yaml_iter_recurse(&security_iter, + &integrity_order_iter); + ogs_assert(ogs_yaml_iter_type( + &integrity_order_iter) != + YAML_MAPPING_NODE); + + do { + const char *v = NULL; + + if (ogs_yaml_iter_type(&integrity_order_iter) == + YAML_SEQUENCE_NODE) { + if (!ogs_yaml_iter_next( + &integrity_order_iter)) + break; + } + + v = ogs_yaml_iter_value(&integrity_order_iter); + if (v) { + int integrity_index = + self.num_of_integrity_order; + if (strcmp(v, "EIA0") == 0) { + self.integrity_order[integrity_index] = + OGS_NAS_SECURITY_ALGORITHMS_EIA0; + self.num_of_integrity_order++; + } else if (strcmp(v, "EIA1") == 0) { + self.integrity_order[integrity_index] = + OGS_NAS_SECURITY_ALGORITHMS_128_EIA1; + self.num_of_integrity_order++; + } else if (strcmp(v, "EIA2") == 0) { + self.integrity_order[integrity_index] = + OGS_NAS_SECURITY_ALGORITHMS_128_EIA2; + self.num_of_integrity_order++; + } else if (strcmp(v, "EIA3") == 0) { + self.integrity_order[integrity_index] = + OGS_NAS_SECURITY_ALGORITHMS_128_EIA3; + self.num_of_integrity_order++; + } + } + } while ( + ogs_yaml_iter_type(&integrity_order_iter) == + YAML_SEQUENCE_NODE); + } else if (!strcmp(security_key, "ciphering_order")) { + ogs_yaml_iter_t ciphering_order_iter; + ogs_yaml_iter_recurse(&security_iter, + &ciphering_order_iter); + ogs_assert(ogs_yaml_iter_type( + &ciphering_order_iter) != YAML_MAPPING_NODE); + + do { + const char *v = NULL; + + if (ogs_yaml_iter_type(&ciphering_order_iter) == + YAML_SEQUENCE_NODE) { + if (!ogs_yaml_iter_next( + &ciphering_order_iter)) + break; + } + + v = ogs_yaml_iter_value(&ciphering_order_iter); + if (v) { + int ciphering_index = + self.num_of_ciphering_order; + if (strcmp(v, "EEA0") == 0) { + self.ciphering_order[ciphering_index] = + OGS_NAS_SECURITY_ALGORITHMS_EEA0; + self.num_of_ciphering_order++; + } else if (strcmp(v, "EEA1") == 0) { + self.ciphering_order[ciphering_index] = + OGS_NAS_SECURITY_ALGORITHMS_128_EEA1; + self.num_of_ciphering_order++; + } else if (strcmp(v, "EEA2") == 0) { + self.ciphering_order[ciphering_index] = + OGS_NAS_SECURITY_ALGORITHMS_128_EEA2; + self.num_of_ciphering_order++; + } else if (strcmp(v, "EEA3") == 0) { + self.ciphering_order[ciphering_index] = + OGS_NAS_SECURITY_ALGORITHMS_128_EEA3; + self.num_of_ciphering_order++; + } + } + } while ( + ogs_yaml_iter_type(&ciphering_order_iter) == + YAML_SEQUENCE_NODE); + } + } + } else if (!strcmp(amf_key, "network_name")) { + ogs_yaml_iter_t network_name_iter; + ogs_yaml_iter_recurse(&amf_iter, &network_name_iter); + + while (ogs_yaml_iter_next(&network_name_iter)) { + const char *network_name_key = + ogs_yaml_iter_key(&network_name_iter); + ogs_assert(network_name_key); + if (!strcmp(network_name_key, "full")) { + ogs_nas_network_name_t *network_full_name = + &self.full_name; + const char *c_network_name = + ogs_yaml_iter_value(&network_name_iter); + uint8_t size = strlen(c_network_name); + uint8_t i; + for (i = 0;iname[i*2] = 0; + network_full_name->name[(i*2)+1] = + c_network_name[i]; + + } + network_full_name->length = size*2+1; + network_full_name->coding_scheme = 1; + } else if (!strcmp(network_name_key, "short")) { + ogs_nas_network_name_t *network_short_name = + &self.short_name; + const char *c_network_name = + ogs_yaml_iter_value(&network_name_iter); + uint8_t size = strlen(c_network_name); + uint8_t i; + for (i = 0;iname[i*2] = 0; + network_short_name->name[(i*2)+1] = + c_network_name[i]; + + } + network_short_name->length = size*2+1; + network_short_name->coding_scheme = 1; + } + } + } else if (!strcmp(amf_key, "amf_name")) { + self.amf_name = ogs_yaml_iter_value(&amf_iter); + } else if (!strcmp(amf_key, "sbi")) { + /* handle config in sbi library */ + } else + ogs_warn("unknown key `%s`", amf_key); + } + } + } + + rv = amf_context_validation(); + if (rv != OGS_OK) return rv; + + return OGS_OK; +} + +amf_gnb_t *amf_gnb_add(ogs_sock_t *sock, ogs_sockaddr_t *addr) +{ + amf_gnb_t *gnb = NULL; + amf_event_t e; + + ogs_assert(sock); + ogs_assert(addr); + + ogs_pool_alloc(&amf_gnb_pool, &gnb); + ogs_assert(gnb); + memset(gnb, 0, sizeof *gnb); + + gnb->sock = sock; + gnb->addr = addr; + gnb->sock_type = amf_gnb_sock_type(gnb->sock); + + gnb->max_num_of_ostreams = DEFAULT_SCTP_MAX_NUM_OF_OSTREAMS; + gnb->ostream_id = 0; + if (ogs_config()->sockopt.sctp.max_num_of_ostreams) { + gnb->max_num_of_ostreams = + ogs_config()->sockopt.sctp.max_num_of_ostreams; + ogs_info("[ENB] max_num_of_ostreams : %d", gnb->max_num_of_ostreams); + } + + ogs_list_init(&gnb->gnb_ue_list); + + if (gnb->sock_type == SOCK_STREAM) { + gnb->poll = ogs_pollset_add(amf_self()->pollset, + OGS_POLLIN, sock->fd, ngap_recv_upcall, sock); + ogs_assert(gnb->poll); + } + + ogs_hash_set(self.gnb_addr_hash, gnb->addr, sizeof(ogs_sockaddr_t), gnb); + + e.gnb = gnb; + e.id = 0; + ogs_fsm_create(&gnb->sm, ngap_state_initial, ngap_state_final); + ogs_fsm_init(&gnb->sm, &e); + + ogs_list_add(&self.gnb_list, gnb); + + return gnb; +} + +int amf_gnb_remove(amf_gnb_t *gnb) +{ + amf_event_t e; + + ogs_assert(gnb); + ogs_assert(gnb->sock); + + ogs_list_remove(&self.gnb_list, gnb); + + e.gnb = gnb; + ogs_fsm_fini(&gnb->sm, &e); + ogs_fsm_delete(&gnb->sm); + + ogs_hash_set(self.gnb_addr_hash, gnb->addr, sizeof(ogs_sockaddr_t), NULL); + ogs_hash_set(self.gnb_id_hash, &gnb->gnb_id, sizeof(gnb->gnb_id), NULL); + +#if 0 + gnb_ue_remove_in_gnb(gnb); +#endif + + if (gnb->sock_type == SOCK_STREAM) { + ogs_pollset_remove(gnb->poll); + ogs_sctp_destroy(gnb->sock); + } + + ogs_free(gnb->addr); + + ogs_pool_free(&amf_gnb_pool, gnb); + + return OGS_OK; +} + +int amf_gnb_remove_all() +{ + amf_gnb_t *gnb = NULL, *next_gnb = NULL; + + ogs_list_for_each_safe(&self.gnb_list, next_gnb, gnb) + amf_gnb_remove(gnb); + + return OGS_OK; +} + +amf_gnb_t *amf_gnb_find_by_addr(ogs_sockaddr_t *addr) +{ + ogs_assert(addr); + return (amf_gnb_t *)ogs_hash_get(self.gnb_addr_hash, + addr, sizeof(ogs_sockaddr_t)); + + return NULL; +} + +amf_gnb_t *amf_gnb_find_by_gnb_id(uint32_t gnb_id) +{ + return (amf_gnb_t *)ogs_hash_get(self.gnb_id_hash, &gnb_id, sizeof(gnb_id)); +} + +int amf_gnb_set_gnb_id(amf_gnb_t *gnb, uint32_t gnb_id) +{ + ogs_assert(gnb); + + gnb->gnb_id = gnb_id; + ogs_hash_set(self.gnb_id_hash, &gnb->gnb_id, sizeof(gnb->gnb_id), gnb); + + return OGS_OK; +} + +int amf_gnb_sock_type(ogs_sock_t *sock) +{ + ogs_socknode_t *snode = NULL; + + ogs_assert(sock); + + ogs_list_for_each(&amf_self()->ngap_list, snode) + if (snode->sock == sock) return SOCK_SEQPACKET; + + ogs_list_for_each(&amf_self()->ngap_list6, snode) + if (snode->sock == sock) return SOCK_SEQPACKET; + + return SOCK_STREAM; +} + +int amf_find_served_tai(ogs_5gs_tai_t *tai) +{ + int i = 0, j = 0, k = 0; + + ogs_assert(tai); + + for (i = 0; i < self.num_of_served_tai; i++) { + ogs_5gs_tai0_list_t *list0 = &self.served_tai[i].list0; + ogs_assert(list0); + ogs_5gs_tai2_list_t *list2 = &self.served_tai[i].list2; + ogs_assert(list2); + + for (j = 0; list0->tai[j].num; j++) { + ogs_assert(list0->tai[j].type == OGS_TAI0_TYPE); + ogs_assert(list0->tai[j].num < OGS_MAX_NUM_OF_TAI); + + for (k = 0; k < list0->tai[j].num; k++) { + if (memcmp(&list0->tai[j].plmn_id, + &tai->plmn_id, OGS_PLMN_ID_LEN) == 0 && + list0->tai[j].tac[k].v == tai->tac.v) { + return i; + } + } + } + + if (list2->num) { + ogs_assert(list2->type == OGS_TAI1_TYPE || + list2->type == OGS_TAI2_TYPE); + ogs_assert(list2->num < OGS_MAX_NUM_OF_TAI); + + for (j = 0; j < list2->num; j++) { + if (memcmp(&list2->tai[j].plmn_id, + &tai->plmn_id, OGS_PLMN_ID_LEN) == 0 && + list2->tai[j].tac.v == tai->tac.v) { + return i; + } + } + } + } + + return -1; +} + +int amf_m_tmsi_pool_generate() +{ + int i, j; + int index = 0; + + ogs_trace("M-TMSI Pool try to generate..."); + for (i = 0; index < ogs_config()->pool.ue; i++) { + amf_m_tmsi_t *m_tmsi = NULL; + int conflict = 0; + + m_tmsi = &self.m_tmsi.array[index]; + ogs_assert(m_tmsi); + *m_tmsi = ogs_random32(); + + /* for mapped-GUTI */ + *m_tmsi |= 0xc0000000; + *m_tmsi &= 0xff00ffff; + + for (j = 0; j < index; j++) { + if (*m_tmsi == self.m_tmsi.array[j]) { + conflict = 1; + ogs_trace("[M-TMSI CONFLICT] %d:0x%x == %d:0x%x", + index, *m_tmsi, j, self.m_tmsi.array[j]); + break; + } + } + if (conflict == 1) { + continue; + } + + index++; + } + self.m_tmsi.size = index; + ogs_trace("M-TMSI Pool generate...done"); + + return OGS_OK; +} + +amf_m_tmsi_t *amf_m_tmsi_alloc() +{ + amf_m_tmsi_t *m_tmsi = NULL; + + ogs_pool_alloc(&self.m_tmsi, &m_tmsi); + ogs_assert(m_tmsi); + + return m_tmsi; +} + +int amf_m_tmsi_free(amf_m_tmsi_t *m_tmsi) +{ + ogs_assert(m_tmsi); + ogs_pool_free(&self.m_tmsi, m_tmsi); + + return OGS_OK; +} + +#if 0 +uint8_t amf_selected_int_algorithm(amf_ue_t *amf_ue) +{ + int i; + + ogs_assert(amf_ue); + + for (i = 0; i < amf_self()->num_of_integrity_order; i++) { + if (amf_ue->ue_network_capability.eia & + (0x80 >> amf_self()->integrity_order[i])) { + return amf_self()->integrity_order[i]; + } + } + + return 0; +} + +uint8_t amf_selected_enc_algorithm(amf_ue_t *amf_ue) +{ + int i; + + ogs_assert(amf_ue); + + for (i = 0; i < amf_self()->num_of_ciphering_order; i++) { + if (amf_ue->ue_network_capability.eea & + (0x80 >> amf_self()->ciphering_order[i])) { + return amf_self()->ciphering_order[i]; + } + } + + return 0; +} +#endif diff --git a/src/amf/context.h b/src/amf/context.h new file mode 100644 index 000000000..236d2c3e3 --- /dev/null +++ b/src/amf/context.h @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2019,2020 by Sukchan Lee + * + * This file is part of Open5GS. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 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, see . + */ + +#ifndef AMF_CONTEXT_H +#define AMF_CONTEXT_H + +#include "ogs-app.h" +#include "ogs-sbi.h" +#include "ogs-sctp.h" +#include "ogs-ngap.h" +#include "ogs-nas-5gs.h" + +#include "amf-sm.h" +#include "timer.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define MAX_NUM_OF_SERVED_GUAMI 8 + +extern int __amf_log_domain; + +#undef OGS_LOG_DOMAIN +#define OGS_LOG_DOMAIN __amf_log_domain + +typedef uint32_t amf_m_tmsi_t; + +typedef struct amf_context_s { + ogs_queue_t *queue; /* Queue for processing UPF control */ + ogs_timer_mgr_t *timer_mgr; /* Timer Manager */ + ogs_pollset_t *pollset; /* Poll Set for I/O Multiplexing */ + + OpenAPI_nf_type_e nf_type; + const char *amf_name; + + /* Served GUMME */ + uint8_t num_of_served_guami; + struct { + ogs_plmn_id_t plmn_id; + ogs_amf_id_t amf_id; + } served_guami[MAX_NUM_OF_SERVED_GUAMI]; + + /* Served TAI */ + uint8_t num_of_served_tai; + struct { + ogs_5gs_tai0_list_t list0; + ogs_5gs_tai2_list_t list2; + } served_tai[OGS_MAX_NUM_OF_SERVED_TAI]; + + /* PLMN Support */ + uint8_t num_of_plmn_support; + struct { + ogs_plmn_id_t plmn_id; + int num_of_s_nssai; + ogs_s_nssai_t s_nssai[OGS_MAX_NUM_OF_S_NSSAI]; + } plmn_support[OGS_MAX_NUM_OF_PLMN]; + + /* defined in 'nas_ies.h' + * #define NAS_SECURITY_ALGORITHMS_EIA0 0 + * #define NAS_SECURITY_ALGORITHMS_128_EEA1 1 + * #define NAS_SECURITY_ALGORITHMS_128_EEA2 2 + * #define NAS_SECURITY_ALGORITHMS_128_EEA3 3 */ + uint8_t num_of_ciphering_order; + uint8_t ciphering_order[OGS_MAX_NUM_OF_ALGORITHM]; + /* defined in 'nas_ies.h' + * #define NAS_SECURITY_ALGORITHMS_EIA0 0 + * #define NAS_SECURITY_ALGORITHMS_128_EIA1 1 + * #define NAS_SECURITY_ALGORITHMS_128_EIA1 2 + * #define NAS_SECURITY_ALGORITHMS_128_EIA3 3 */ + uint8_t num_of_integrity_order; + uint8_t integrity_order[OGS_MAX_NUM_OF_ALGORITHM]; + + /* Network Name */ + ogs_nas_network_name_t short_name; /* Network short name */ + ogs_nas_network_name_t full_name; /* Network Full Name */ + + /* M-TMSI Pool */ + OGS_POOL(m_tmsi, amf_m_tmsi_t); + + /* NGSetupResponse */ + uint8_t relative_capacity; + + uint16_t ngap_port; /* Default NGAP Port */ + + ogs_list_t ngap_list; /* AMF NGAP IPv4 Server List */ + ogs_list_t ngap_list6; /* AMF NGAP IPv6 Server List */ + + ogs_list_t gnb_list; /* ENB S1AP Client List */ + ogs_hash_t *gnb_addr_hash; /* hash table for GNB Address */ + ogs_hash_t *gnb_id_hash; /* hash table for GNB-ID */ +} amf_context_t; + +typedef struct amf_gnb_s { + ogs_lnode_t lnode; + + ogs_fsm_t sm; /* A state machine */ + + uint32_t gnb_id; /* eNB_ID received from eNB */ + int sock_type; /* SOCK_STREAM or SOCK_SEQPACKET */ + ogs_sock_t *sock; /* eNB S1AP Socket */ + ogs_sockaddr_t *addr; /* eNB S1AP Address */ + ogs_poll_t *poll; /* eNB S1AP Poll */ + + struct { + bool ng_setup_success; /* eNB S1AP Setup complete successfuly */ + } state; + + uint16_t max_num_of_ostreams;/* SCTP Max num of outbound streams */ + uint16_t ostream_id; /* gnb_ostream_id generator */ + + + uint8_t num_of_supported_ta_list; + ogs_5gs_tai_t supported_ta_list[OGS_MAX_NUM_OF_TAI*OGS_MAX_NUM_OF_BPLMN]; + + ogs_list_t gnb_ue_list; + +} amf_gnb_t; + +void amf_context_init(void); +void amf_context_final(void); +amf_context_t *amf_self(void); + +int amf_context_parse_config(void); + +amf_gnb_t *amf_gnb_add(ogs_sock_t *sock, ogs_sockaddr_t *addr); +int amf_gnb_remove(amf_gnb_t *gnb); +int amf_gnb_remove_all(void); +amf_gnb_t *amf_gnb_find_by_addr(ogs_sockaddr_t *addr); +amf_gnb_t *amf_gnb_find_by_gnb_id(uint32_t gnb_id); +int amf_gnb_set_gnb_id(amf_gnb_t *gnb, uint32_t gnb_id); +int amf_gnb_sock_type(ogs_sock_t *sock); + +int amf_find_served_tai(ogs_5gs_tai_t *tai); + +int amf_m_tmsi_pool_generate(void); +amf_m_tmsi_t *amf_m_tmsi_alloc(void); +int amf_m_tmsi_free(amf_m_tmsi_t *tmsi); + +#if 0 +uint8_t amf_selected_int_algorithm(amf_ue_t *amf_ue); +uint8_t amf_selected_enc_algorithm(amf_ue_t *amf_ue); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* AMF_CONTEXT_H */ diff --git a/src/amf/event.c b/src/amf/event.c new file mode 100644 index 000000000..4b4f9d708 --- /dev/null +++ b/src/amf/event.c @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2019,2020 by Sukchan Lee + * + * This file is part of Open5GS. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 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, see . + */ + +#include "event.h" +#include "context.h" + +#define EVENT_POOL 32 /* FIXME : 32 */ +static OGS_POOL(pool, amf_event_t); + +void amf_event_init(void) +{ + ogs_pool_init(&pool, EVENT_POOL); + + amf_self()->queue = ogs_queue_create(EVENT_POOL); + ogs_assert(amf_self()->queue); + amf_self()->timer_mgr = ogs_timer_mgr_create(); + ogs_assert(amf_self()->timer_mgr); + amf_self()->pollset = ogs_pollset_create(); + ogs_assert(amf_self()->pollset); +} + +void amf_event_final(void) +{ + if (amf_self()->pollset) + ogs_pollset_destroy(amf_self()->pollset); + if (amf_self()->timer_mgr) + ogs_timer_mgr_destroy(amf_self()->timer_mgr); + if (amf_self()->queue) + ogs_queue_destroy(amf_self()->queue); + + ogs_pool_final(&pool); +} + +amf_event_t *amf_event_new(amf_event_e id) +{ + amf_event_t *e = NULL; + + ogs_pool_alloc(&pool, &e); + ogs_assert(e); + memset(e, 0, sizeof(*e)); + + e->id = id; + + return e; +} + +void amf_event_free(amf_event_t *e) +{ + ogs_assert(e); + ogs_pool_free(&pool, e); +} + +const char *amf_event_get_name(amf_event_t *e) +{ + if (e == NULL) + return OGS_FSM_NAME_INIT_SIG; + + switch (e->id) { + case OGS_FSM_ENTRY_SIG: + return OGS_FSM_NAME_ENTRY_SIG; + case OGS_FSM_EXIT_SIG: + return OGS_FSM_NAME_EXIT_SIG; + + case AMF_EVT_SBI_SERVER: + return "AMF_EVT_SBI_SERVER"; + case AMF_EVT_SBI_CLIENT: + return "AMF_EVT_SBI_CLIENT"; + case AMF_EVT_SBI_TIMER: + return "AMF_EVT_SBI_TIMER"; + + case AMF_EVT_NGAP_MESSAGE: + return "AMF_EVT_NGAP_MESSAGE"; + case AMF_EVT_NGAP_TIMER: + return "AMF_EVT_NGAP_TIMER"; + case AMF_EVT_NGAP_LO_ACCEPT: + return "AMF_EVT_NGAP_LO_ACCEPT"; + case AMF_EVT_NGAP_LO_SCTP_COMM_UP: + return "AMF_EVT_NGAP_LO_SCTP_COMM_UP"; + case AMF_EVT_NGAP_LO_CONNREFUSED: + return "AMF_EVT_NGAP_LO_CONNREFUSED"; + + default: + break; + } + + return "UNKNOWN_EVENT"; +} + +void amf_sctp_event_push(amf_event_e id, + void *sock, ogs_sockaddr_t *addr, ogs_pkbuf_t *pkbuf, + uint16_t max_num_of_istreams, uint16_t max_num_of_ostreams) +{ + amf_event_t *e = NULL; + int rv; + + ogs_assert(id); + ogs_assert(sock); + ogs_assert(addr); + + e = amf_event_new(id); + ogs_assert(e); + + e->pkbuf = pkbuf; + + e->ngap.sock = sock; + e->ngap.addr = addr; + e->ngap.max_num_of_istreams = max_num_of_istreams; + e->ngap.max_num_of_ostreams = max_num_of_ostreams; + + rv = ogs_queue_push(amf_self()->queue, e); + if (rv != OGS_OK) { + ogs_warn("ogs_queue_push() failed:%d", (int)rv); + ogs_free(e->ngap.addr); + if (e->pkbuf) + ogs_pkbuf_free(e->pkbuf); + amf_event_free(e); + } +#if HAVE_USRSCTP + else { + ogs_pollset_notify(amf_self()->pollset); + } +#endif +} diff --git a/src/amf/event.h b/src/amf/event.h new file mode 100644 index 000000000..a14a81f57 --- /dev/null +++ b/src/amf/event.h @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2019,2020 by Sukchan Lee + * + * This file is part of Open5GS. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 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, see . + */ + +#ifndef AMF_EVENT_H +#define AMF_EVENT_H + +#include "ogs-core.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct ogs_sbi_server_s ogs_sbi_server_t; +typedef struct ogs_sbi_session_s ogs_sbi_session_t; +typedef struct ogs_sbi_request_s ogs_sbi_request_t; +typedef struct ogs_sbi_response_s ogs_sbi_response_t; +typedef struct ogs_sbi_message_s ogs_sbi_message_t; +typedef struct ogs_sbi_nf_instance_s ogs_sbi_nf_instance_t; +typedef struct ogs_sbi_subscription_s ogs_sbi_subscription_t; + +typedef enum { + AMF_EVT_BASE = OGS_FSM_USER_SIG, + + AMF_EVT_SBI_SERVER, + AMF_EVT_SBI_CLIENT, + AMF_EVT_SBI_TIMER, + + AMF_EVT_NGAP_MESSAGE, + AMF_EVT_NGAP_TIMER, + AMF_EVT_NGAP_LO_ACCEPT, + AMF_EVT_NGAP_LO_SCTP_COMM_UP, + AMF_EVT_NGAP_LO_CONNREFUSED, + + AMF_EVT_TOP, + +} amf_event_e; + +typedef struct amf_gnb_s amf_gnb_t; +typedef struct ogs_nas_5gs_message_s ogs_nas_5gs_message_t; +typedef struct NGAP_NGAP_PDU ogs_ngap_message_t; +typedef long NGAP_ProcedureCode_t; + +typedef struct amf_event_s { + int id; + ogs_pkbuf_t *pkbuf; + int timer_id; + + struct { + ogs_sock_t *sock; + ogs_sockaddr_t *addr; + uint16_t max_num_of_istreams; + uint16_t max_num_of_ostreams; + + NGAP_ProcedureCode_t code; + ogs_ngap_message_t *message; + } ngap; + + struct { + /* OGS_EVT_SBI_SERVER */ + ogs_sbi_request_t *request; + ogs_sbi_session_t *session; + ogs_sbi_server_t *server; + + /* OGS_EVT_SBI_CLIENT */ + ogs_sbi_response_t *response; + void *data; + + ogs_sbi_message_t *message; + } sbi; + + amf_gnb_t *gnb; +} amf_event_t; + +void amf_event_init(void); +void amf_event_final(void); + +amf_event_t *amf_event_new(amf_event_e id); +void amf_event_free(amf_event_t *e); + +const char *amf_event_get_name(amf_event_t *e); + +void amf_sctp_event_push(amf_event_e id, + void *sock, ogs_sockaddr_t *addr, ogs_pkbuf_t *pkbuf, + uint16_t max_num_of_istreams, uint16_t max_num_of_ostreams); + +#ifdef __cplusplus +} +#endif + +#endif /* AMF_EVENT_H */ diff --git a/src/amf/init.c b/src/amf/init.c new file mode 100644 index 000000000..1daef398e --- /dev/null +++ b/src/amf/init.c @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2019,2020 by Sukchan Lee + * + * This file is part of Open5GS. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 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, see . + */ + +#include "context.h" + +static ogs_thread_t *thread; +static void amf_main(void *data); +static int initialized = 0; + +int amf_initialize() +{ + int rv; + + amf_context_init(); + amf_event_init(); /* Create event with poll, timer */ + ogs_sbi_context_init(amf_self()->pollset, amf_self()->timer_mgr); + + rv = ogs_sbi_context_parse_config("amf", "nrf"); + if (rv != OGS_OK) return rv; + + rv = amf_context_parse_config(); + if (rv != OGS_OK) return rv; + + rv = ogs_log_config_domain( + ogs_config()->logger.domain, ogs_config()->logger.level); + if (rv != OGS_OK) return rv; + + thread = ogs_thread_create(amf_main, NULL); + if (!thread) return OGS_ERROR; + + initialized = 1; + + return OGS_OK; +} + +static ogs_timer_t *t_termination_holding = NULL; + +static void event_termination(void) +{ + ogs_sbi_nf_instance_t *nf_instance = NULL; + + /* Sending NF Instance De-registeration to NRF */ + ogs_list_for_each(&ogs_sbi_self()->nf_instance_list, nf_instance) + amf_nf_fsm_fini(nf_instance); + + /* Starting holding timer */ + t_termination_holding = ogs_timer_add(amf_self()->timer_mgr, NULL, NULL); + ogs_assert(t_termination_holding); +#define TERMINATION_HOLDING_TIME ogs_time_from_msec(300) + ogs_timer_start(t_termination_holding, TERMINATION_HOLDING_TIME); + + /* Sending termination event to the queue */ + ogs_queue_term(amf_self()->queue); + ogs_pollset_notify(amf_self()->pollset); +} + +void amf_terminate(void) +{ + if (!initialized) return; + + /* Daemon terminating */ + event_termination(); + ogs_thread_destroy(thread); + ogs_timer_delete(t_termination_holding); + + amf_context_final(); + ogs_sbi_context_final(); + + amf_event_final(); /* Destroy event */ +} + +static void amf_main(void *data) +{ + ogs_fsm_t amf_sm; + int rv; + + ogs_fsm_create(&amf_sm, amf_state_initial, amf_state_final); + ogs_fsm_init(&amf_sm, 0); + + for ( ;; ) { + ogs_pollset_poll(amf_self()->pollset, + ogs_timer_mgr_next(amf_self()->timer_mgr)); + + /* + * After ogs_pollset_poll(), ogs_timer_mgr_expire() must be called. + * + * The reason is why ogs_timer_mgr_next() can get the corrent value + * when ogs_timer_stop() is called internally in ogs_timer_mgr_expire(). + * + * You should not use event-queue before ogs_timer_mgr_expire(). + * In this case, ogs_timer_mgr_expire() does not work + * because 'if rv == OGS_DONE' statement is exiting and + * not calling ogs_timer_mgr_expire(). + */ + ogs_timer_mgr_expire(amf_self()->timer_mgr); + + for ( ;; ) { + amf_event_t *e = NULL; + + rv = ogs_queue_trypop(amf_self()->queue, (void**)&e); + ogs_assert(rv != OGS_ERROR); + + if (rv == OGS_DONE) + goto done; + + if (rv == OGS_RETRY) + break; + + ogs_assert(e); + ogs_fsm_dispatch(&amf_sm, e); + amf_event_free(e); + } + } +done: + + ogs_fsm_fini(&amf_sm, 0); + ogs_fsm_delete(&amf_sm); +} diff --git a/src/amf/meson.build b/src/amf/meson.build new file mode 100644 index 000000000..b14dc98f1 --- /dev/null +++ b/src/amf/meson.build @@ -0,0 +1,68 @@ +# Copyright (C) 2019,2020 by Sukchan Lee + +# This file is part of Open5GS. + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 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, see . + +libamf_sources = files(''' + context.c + event.c + timer.c + + nnrf-handler.c + nnrf-build.c + sbi-path.c + nf-sm.c + + ngap-sctp.c + ngap-build.c + ngap-handler.c + ngap-path.c + ngap-sm.c + + amf-sm.c + + init.c +'''.split()) + +libamf = static_library('amf', + sources : libamf_sources, + link_with : libipfw, + dependencies : [libapp_dep, + libsctp_dep, + libngap_dep, + libnas_5gs_dep, + libsbi_dep], + install : false) + +libamf_dep = declare_dependency( + link_with : libamf, + dependencies : [libapp_dep, + libsctp_dep, + libngap_dep, + libnas_5gs_dep, + libsbi_dep]) + +amf_sources = files(''' + app.c + ../main.c +'''.split()) + +executable('open5gs-amfd', + sources : amf_sources, + c_args : '-DDEFAULT_CONFIG_FILENAME="@0@/amf.yaml"'.format(open5gs_sysconfdir), + include_directories : srcinc, + dependencies : libamf_dep, + install_rpath : libdir, + install : true) diff --git a/src/amf/nf-sm.c b/src/amf/nf-sm.c new file mode 100644 index 000000000..e70452a44 --- /dev/null +++ b/src/amf/nf-sm.c @@ -0,0 +1,384 @@ +/* + * Copyright (C) 2019,2020 by Sukchan Lee + * + * This file is part of Open5GS. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 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, see . + */ + +#include "context.h" + +#include "sbi-path.h" +#include "nnrf-handler.h" + +void amf_nf_fsm_init(ogs_sbi_nf_instance_t *nf_instance) +{ + amf_event_t e; + + ogs_assert(nf_instance); + + e.sbi.data = nf_instance; + + ogs_fsm_create(&nf_instance->sm, + amf_nf_state_initial, amf_nf_state_final); + ogs_fsm_init(&nf_instance->sm, &e); +} + +void amf_nf_fsm_fini(ogs_sbi_nf_instance_t *nf_instance) +{ + amf_event_t e; + + ogs_assert(nf_instance); + e.sbi.data = nf_instance; + + ogs_fsm_fini(&nf_instance->sm, &e); + ogs_fsm_delete(&nf_instance->sm); +} + +void amf_nf_state_initial(ogs_fsm_t *s, amf_event_t *e) +{ + ogs_sbi_nf_instance_t *nf_instance = NULL; + + ogs_assert(s); + ogs_assert(e); + + amf_sm_debug(e); + + nf_instance = e->sbi.data; + ogs_assert(nf_instance); + ogs_assert(nf_instance->id); + + nf_instance->t_registration_interval = ogs_timer_add(amf_self()->timer_mgr, + amf_timer_nf_instance_registration_interval, nf_instance); + ogs_assert(nf_instance->t_registration_interval); + nf_instance->t_heartbeat_interval = ogs_timer_add(amf_self()->timer_mgr, + amf_timer_nf_instance_heartbeat_interval, nf_instance); + ogs_assert(nf_instance->t_heartbeat_interval); + nf_instance->t_heartbeat = ogs_timer_add(amf_self()->timer_mgr, + amf_timer_nf_instance_heartbeat, nf_instance); + ogs_assert(nf_instance->t_heartbeat); + nf_instance->t_validity = ogs_timer_add(amf_self()->timer_mgr, + amf_timer_nf_instance_validity, nf_instance); + ogs_assert(nf_instance->t_validity); + + if (NF_INSTANCE_IS_SELF(nf_instance->id)) { + OGS_FSM_TRAN(s, &amf_nf_state_will_register); + } else { + OGS_FSM_TRAN(s, &amf_nf_state_registered); + } +} + +void amf_nf_state_final(ogs_fsm_t *s, amf_event_t *e) +{ + ogs_sbi_nf_instance_t *nf_instance = NULL; + + ogs_assert(s); + ogs_assert(e); + + amf_sm_debug(e); + + nf_instance = e->sbi.data; + ogs_assert(nf_instance); + + ogs_timer_delete(nf_instance->t_registration_interval); + ogs_timer_delete(nf_instance->t_heartbeat_interval); + ogs_timer_delete(nf_instance->t_heartbeat); + ogs_timer_delete(nf_instance->t_validity); +} + +void amf_nf_state_will_register(ogs_fsm_t *s, amf_event_t *e) +{ + ogs_sbi_nf_instance_t *nf_instance = NULL; + ogs_sbi_client_t *client = NULL; + ogs_sbi_message_t *message = NULL; + ogs_sockaddr_t *addr = NULL; + + ogs_assert(s); + ogs_assert(e); + + amf_sm_debug(e); + + nf_instance = e->sbi.data; + ogs_assert(nf_instance); + + switch (e->id) { + case OGS_FSM_ENTRY_SIG: + ogs_timer_start(nf_instance->t_registration_interval, + amf_timer_cfg(AMF_TIMER_NF_INSTANCE_REGISTRATION_INTERVAL)-> + duration); + + amf_sbi_send_nf_register(nf_instance); + break; + + case OGS_FSM_EXIT_SIG: + ogs_timer_stop(nf_instance->t_registration_interval); + break; + + case AMF_EVT_SBI_CLIENT: + message = e->sbi.message; + ogs_assert(message); + + SWITCH(message->h.service.name) + CASE(OGS_SBI_SERVICE_NAME_NRF_NFM) + + SWITCH(message->h.resource.name) + CASE(OGS_SBI_RESOURCE_NAME_NF_INSTANCES) + + if (message->res_status == OGS_SBI_HTTP_STATUS_OK || + message->res_status == OGS_SBI_HTTP_STATUS_CREATED) { + amf_nnrf_handle_nf_register(nf_instance, message); + OGS_FSM_TRAN(s, &amf_nf_state_registered); + } else { + ogs_error("HTTP Response Status Code [%d]", + message->res_status); + OGS_FSM_TRAN(s, &amf_nf_state_exception); + } + break; + + DEFAULT + ogs_error("Invalid resource name [%s]", + message->h.resource.name); + END + break; + + DEFAULT + ogs_error("Invalid API name [%s]", message->h.service.name); + END + break; + + case AMF_EVT_SBI_TIMER: + switch(e->timer_id) { + case AMF_TIMER_NF_INSTANCE_REGISTRATION_INTERVAL: + client = nf_instance->client; + ogs_assert(client); + addr = client->addr; + ogs_assert(addr); + + ogs_warn("Retry to registration with NRF [%s]", nf_instance->id); + + ogs_timer_start(nf_instance->t_registration_interval, + amf_timer_cfg(AMF_TIMER_NF_INSTANCE_REGISTRATION_INTERVAL)-> + duration); + + amf_sbi_send_nf_register(nf_instance); + break; + + default: + ogs_error("Unknown timer[%s:%d]", + amf_timer_get_name(e->timer_id), e->timer_id); + } + break; + + default: + ogs_error("Unknown event %s", amf_event_get_name(e)); + break; + } +} + +void amf_nf_state_registered(ogs_fsm_t *s, amf_event_t *e) +{ + ogs_sbi_nf_instance_t *nf_instance = NULL; + ogs_sbi_client_t *client = NULL; + ogs_sbi_message_t *message = NULL; + ogs_assert(s); + ogs_assert(e); + + amf_sm_debug(e); + + nf_instance = e->sbi.data; + ogs_assert(nf_instance); + + switch (e->id) { + case OGS_FSM_ENTRY_SIG: + if (NF_INSTANCE_IS_SELF(nf_instance->id)) { + ogs_info("NF registered [%s]", nf_instance->id); + + client = nf_instance->client; + ogs_assert(client); + + if (nf_instance->time.heartbeat) { + ogs_timer_start(nf_instance->t_heartbeat_interval, + ogs_time_from_sec(nf_instance->time.heartbeat)); + ogs_timer_start(nf_instance->t_heartbeat, + ogs_time_from_sec(nf_instance->time.heartbeat * + OGS_SBI_HEARTBEAT_RETRYCOUNT)); + } + + amf_sbi_send_nf_status_subscribe(client, + amf_self()->nf_type, nf_instance->id); + } + + break; + + case OGS_FSM_EXIT_SIG: + if (NF_INSTANCE_IS_SELF(nf_instance->id)) { + ogs_info("NF de-registered [%s]", nf_instance->id); + + if (nf_instance->time.heartbeat) { + ogs_timer_stop(nf_instance->t_heartbeat_interval); + ogs_timer_stop(nf_instance->t_heartbeat); + } + + amf_sbi_send_nf_de_register(nf_instance); + } + break; + + case AMF_EVT_SBI_CLIENT: + message = e->sbi.message; + ogs_assert(message); + + SWITCH(message->h.service.name) + CASE(OGS_SBI_SERVICE_NAME_NRF_NFM) + + SWITCH(message->h.resource.name) + CASE(OGS_SBI_RESOURCE_NAME_NF_INSTANCES) + + if (message->res_status == OGS_SBI_HTTP_STATUS_NO_CONTENT || + message->res_status == OGS_SBI_HTTP_STATUS_OK) { + if (nf_instance->time.heartbeat) + ogs_timer_start(nf_instance->t_heartbeat, + ogs_time_from_sec(nf_instance->time.heartbeat * + OGS_SBI_HEARTBEAT_RETRYCOUNT)); + } else { + ogs_error("HTTP response error : %d", message->res_status); + } + + break; + + DEFAULT + ogs_error("Invalid resource name [%s]", + message->h.resource.name); + END + break; + + DEFAULT + ogs_error("Invalid API name [%s]", message->h.service.name); + END + break; + + case AMF_EVT_SBI_TIMER: + switch(e->timer_id) { + case AMF_TIMER_NF_INSTANCE_HEARTBEAT_INTERVAL: + if (nf_instance->time.heartbeat) { + ogs_timer_start(nf_instance->t_heartbeat_interval, + ogs_time_from_sec(nf_instance->time.heartbeat)); + } + + amf_sbi_send_nf_update(nf_instance); + break; + + case AMF_TIMER_NF_INSTANCE_HEARTBEAT: + OGS_FSM_TRAN(s, &amf_nf_state_will_register); + break; + + case AMF_TIMER_NF_INSTANCE_VALIDITY: + if (NF_INSTANCE_IS_OTHERS(nf_instance->id)) { + ogs_info("NF expired [%s]", nf_instance->id); + OGS_FSM_TRAN(s, &amf_nf_state_de_registered); + } + break; + + default: + ogs_error("Unknown timer[%s:%d]", + amf_timer_get_name(e->timer_id), e->timer_id); + break; + } + break; + + default: + ogs_error("Unknown event %s", amf_event_get_name(e)); + break; + } +} + +void amf_nf_state_de_registered(ogs_fsm_t *s, amf_event_t *e) +{ + ogs_sbi_nf_instance_t *nf_instance = NULL; + ogs_assert(s); + ogs_assert(e); + + amf_sm_debug(e); + + nf_instance = e->sbi.data; + ogs_assert(nf_instance); + + switch (e->id) { + case OGS_FSM_ENTRY_SIG: + if (NF_INSTANCE_IS_SELF(nf_instance->id)) { + ogs_info("NF de-registered [%s]", nf_instance->id); + } + break; + + case OGS_FSM_EXIT_SIG: + break; + + default: + ogs_error("Unknown event %s", amf_event_get_name(e)); + break; + } +} + +void amf_nf_state_exception(ogs_fsm_t *s, amf_event_t *e) +{ + ogs_sbi_nf_instance_t *nf_instance = NULL; + ogs_sbi_client_t *client = NULL; + ogs_sockaddr_t *addr = NULL; + ogs_assert(s); + ogs_assert(e); + + amf_sm_debug(e); + + nf_instance = e->sbi.data; + ogs_assert(nf_instance); + + switch (e->id) { + case OGS_FSM_ENTRY_SIG: + if (NF_INSTANCE_IS_SELF(nf_instance->id)) { + ogs_timer_start(nf_instance->t_registration_interval, + amf_timer_cfg(AMF_TIMER_NF_INSTANCE_REGISTRATION_INTERVAL)-> + duration); + } + break; + + case OGS_FSM_EXIT_SIG: + if (NF_INSTANCE_IS_SELF(nf_instance->id)) { + ogs_timer_stop(nf_instance->t_registration_interval); + } + break; + + case AMF_EVT_SBI_TIMER: + switch(e->timer_id) { + case AMF_TIMER_NF_INSTANCE_REGISTRATION_INTERVAL: + client = nf_instance->client; + ogs_assert(client); + addr = client->addr; + ogs_assert(addr); + + ogs_warn("Retry to registration with NRF [%s]", nf_instance->id); + + OGS_FSM_TRAN(s, &amf_nf_state_will_register); + break; + + default: + ogs_error("Unknown timer[%s:%d]", + amf_timer_get_name(e->timer_id), e->timer_id); + break; + } + break; + + default: + ogs_error("Unknown event %s", amf_event_get_name(e)); + break; + } +} diff --git a/src/amf/ngap-build.c b/src/amf/ngap-build.c new file mode 100644 index 000000000..4ef046c48 --- /dev/null +++ b/src/amf/ngap-build.c @@ -0,0 +1,2606 @@ +/* + * Copyright (C) 2019,2020 by Sukchan Lee + * + * This file is part of Open5GS. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 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, see . + */ + +#if 0 +#include "amf-kdf.h" +#endif +#include "ngap-build.h" + +ogs_pkbuf_t *ngap_build_setup_rsp(void) +{ + int i, j; + + NGAP_NGAP_PDU_t pdu; + NGAP_SuccessfulOutcome_t *successfulOutcome = NULL; + NGAP_NGSetupResponse_t *NGSetupResponse = NULL; + + NGAP_NGSetupResponseIEs_t *ie = NULL; + NGAP_AMFName_t *AMFName = NULL; + NGAP_ServedGUAMIList_t *ServedGUAMIList = NULL; + NGAP_RelativeAMFCapacity_t *RelativeAMFCapacity = NULL; + NGAP_PLMNSupportList_t *PLMNSupportList = NULL; + + memset(&pdu, 0, sizeof (NGAP_NGAP_PDU_t)); + pdu.present = NGAP_NGAP_PDU_PR_successfulOutcome; + pdu.choice.successfulOutcome = + CALLOC(1, sizeof(NGAP_SuccessfulOutcome_t)); + + successfulOutcome = pdu.choice.successfulOutcome; + successfulOutcome->procedureCode = NGAP_ProcedureCode_id_NGSetup; + successfulOutcome->criticality = NGAP_Criticality_reject; + successfulOutcome->value.present = + NGAP_SuccessfulOutcome__value_PR_NGSetupResponse; + + NGSetupResponse = &successfulOutcome->value.choice.NGSetupResponse; + + ie = CALLOC(1, sizeof(NGAP_NGSetupResponseIEs_t)); + ASN_SEQUENCE_ADD(&NGSetupResponse->protocolIEs, ie); + + ie->id = NGAP_ProtocolIE_ID_id_AMFName; + ie->criticality = NGAP_Criticality_reject; + ie->value.present = NGAP_NGSetupResponseIEs__value_PR_AMFName; + + AMFName = &ie->value.choice.AMFName; + + ie = CALLOC(1, sizeof(NGAP_NGSetupResponseIEs_t)); + ASN_SEQUENCE_ADD(&NGSetupResponse->protocolIEs, ie); + + ie->id = NGAP_ProtocolIE_ID_id_ServedGUAMIList; + ie->criticality = NGAP_Criticality_reject; + ie->value.present = NGAP_NGSetupResponseIEs__value_PR_ServedGUAMIList; + + ServedGUAMIList = &ie->value.choice.ServedGUAMIList; + + ie = CALLOC(1, sizeof(NGAP_NGSetupResponseIEs_t)); + ASN_SEQUENCE_ADD(&NGSetupResponse->protocolIEs, ie); + + ie->id = NGAP_ProtocolIE_ID_id_RelativeAMFCapacity; + ie->criticality = NGAP_Criticality_ignore; + ie->value.present = NGAP_NGSetupResponseIEs__value_PR_RelativeAMFCapacity; + + RelativeAMFCapacity = &ie->value.choice.RelativeAMFCapacity; + + ie = CALLOC(1, sizeof(NGAP_NGSetupResponseIEs_t)); + ASN_SEQUENCE_ADD(&NGSetupResponse->protocolIEs, ie); + + ie->id = NGAP_ProtocolIE_ID_id_PLMNSupportList; + ie->criticality = NGAP_Criticality_reject; + ie->value.present = NGAP_NGSetupResponseIEs__value_PR_PLMNSupportList; + + PLMNSupportList = &ie->value.choice.PLMNSupportList; + + ogs_asn_buffer_to_OCTET_STRING((char*)amf_self()->amf_name, + strlen(amf_self()->amf_name), AMFName); + + for (i = 0; i < amf_self()->num_of_served_guami; i++) { + NGAP_ServedGUAMIItem_t *ServedGUAMIItem = NULL; + NGAP_GUAMI_t *gUAMI = NULL; + NGAP_PLMNIdentity_t *pLMNIdentity = NULL; + NGAP_AMFRegionID_t *aMFRegionID = NULL; + NGAP_AMFSetID_t *aMFSetID = NULL; + NGAP_AMFPointer_t *aMFPointer = NULL; + + ServedGUAMIItem = (NGAP_ServedGUAMIItem_t *) + CALLOC(1, sizeof(NGAP_ServedGUAMIItem_t)); + gUAMI = &ServedGUAMIItem->gUAMI; + pLMNIdentity = &gUAMI->pLMNIdentity; + aMFRegionID = &gUAMI->aMFRegionID; + aMFSetID = &gUAMI->aMFSetID; + aMFPointer = &gUAMI->aMFPointer; + + ogs_asn_buffer_to_OCTET_STRING( + &amf_self()->served_guami[i].plmn_id, + OGS_PLMN_ID_LEN, pLMNIdentity); + ogs_ngap_uint8_to_AMFRegionID( + ogs_amf_region_id(&amf_self()->served_guami[i].amf_id), + aMFRegionID); + ogs_ngap_uint16_to_NGAP_AMFSetID( + ogs_amf_set_id(&amf_self()->served_guami[i].amf_id), + aMFSetID); + ogs_ngap_uint8_to_NGAP_NGAP_AMFPointer( + ogs_amf_pointer(&amf_self()->served_guami[i].amf_id), + aMFPointer); + + ASN_SEQUENCE_ADD(&ServedGUAMIList->list, ServedGUAMIItem); + } + + *RelativeAMFCapacity = amf_self()->relative_capacity; + + for (i = 0; i < amf_self()->num_of_plmn_support; i++) { + NGAP_PLMNSupportItem_t *NGAP_PLMNSupportItem = NULL; + NGAP_PLMNIdentity_t *pLMNIdentity = NULL; + NGAP_SliceSupportList_t *sliceSupportList = NULL; + + NGAP_PLMNSupportItem = (NGAP_PLMNSupportItem_t *) + CALLOC(1, sizeof(NGAP_PLMNSupportItem_t)); + pLMNIdentity = &NGAP_PLMNSupportItem->pLMNIdentity; + sliceSupportList = &NGAP_PLMNSupportItem->sliceSupportList; + + ogs_asn_buffer_to_OCTET_STRING( + &amf_self()->plmn_support[i].plmn_id, + OGS_PLMN_ID_LEN, pLMNIdentity); + for (j = 0; j < amf_self()->plmn_support[i].num_of_s_nssai; j++) { + NGAP_SliceSupportItem_t *NGAP_SliceSupportItem = NULL; + NGAP_S_NSSAI_t *s_NSSAI = NULL; + NGAP_SST_t *sST = NULL; + + NGAP_SliceSupportItem = (NGAP_SliceSupportItem_t *) + CALLOC(1, sizeof(NGAP_SliceSupportItem_t)); + s_NSSAI = &NGAP_SliceSupportItem->s_NSSAI; + sST = &s_NSSAI->sST; + + ogs_asn_uint8_to_OCTET_STRING( + amf_self()->plmn_support[i].s_nssai[j].sst, sST); + if (amf_self()->plmn_support[i].s_nssai[j].sd.v != + OGS_S_NSSAI_NO_SD_VALUE) { + s_NSSAI->sD = CALLOC(1, sizeof(ogs_uint24_t)); + ogs_asn_uint24_to_OCTET_STRING( + amf_self()->plmn_support[i].s_nssai[j].sd, s_NSSAI->sD); + } + + ASN_SEQUENCE_ADD(&sliceSupportList->list, NGAP_SliceSupportItem); + } + + ASN_SEQUENCE_ADD(&PLMNSupportList->list, NGAP_PLMNSupportItem); + } + + return ogs_ngap_encode(&pdu); +} + +ogs_pkbuf_t *ngap_build_setup_failure( + NGAP_Cause_PR group, long cause, long time_to_wait) +{ + NGAP_NGAP_PDU_t pdu; + NGAP_UnsuccessfulOutcome_t *unsuccessfulOutcome = NULL; + NGAP_NGSetupFailure_t *NGSetupFailure = NULL; + + NGAP_NGSetupFailureIEs_t *ie = NULL; + NGAP_Cause_t *Cause = NULL; + NGAP_TimeToWait_t *TimeToWait = NULL; + + ogs_debug(" Group[%d] Cause[%d] TimeToWait[%ld]", + group, (int)cause, time_to_wait); + + memset(&pdu, 0, sizeof (NGAP_NGAP_PDU_t)); + pdu.present = NGAP_NGAP_PDU_PR_unsuccessfulOutcome; + pdu.choice.unsuccessfulOutcome = + CALLOC(1, sizeof(NGAP_UnsuccessfulOutcome_t)); + + unsuccessfulOutcome = pdu.choice.unsuccessfulOutcome; + unsuccessfulOutcome->procedureCode = NGAP_ProcedureCode_id_NGSetup; + unsuccessfulOutcome->criticality = NGAP_Criticality_reject; + unsuccessfulOutcome->value.present = + NGAP_UnsuccessfulOutcome__value_PR_NGSetupFailure; + + NGSetupFailure = &unsuccessfulOutcome->value.choice.NGSetupFailure; + + ie = CALLOC(1, sizeof(NGAP_NGSetupFailureIEs_t)); + ASN_SEQUENCE_ADD(&NGSetupFailure->protocolIEs, ie); + + ie->id = NGAP_ProtocolIE_ID_id_Cause; + ie->criticality = NGAP_Criticality_ignore; + ie->value.present = NGAP_NGSetupFailureIEs__value_PR_Cause; + + Cause = &ie->value.choice.Cause; + + if (time_to_wait > -1) { + ie = CALLOC(1, sizeof(NGAP_NGSetupFailureIEs_t)); + ASN_SEQUENCE_ADD(&NGSetupFailure->protocolIEs, ie); + + ie->id = NGAP_ProtocolIE_ID_id_TimeToWait; + ie->criticality = NGAP_Criticality_ignore; + ie->value.present = NGAP_NGSetupFailureIEs__value_PR_TimeToWait; + + TimeToWait = &ie->value.choice.TimeToWait; + } + + Cause->present = group; + Cause->choice.radioNetwork = cause; + + if (TimeToWait) + *TimeToWait = time_to_wait; + + return ogs_ngap_encode(&pdu); +} + +#if 0 +ogs_pkbuf_t *ngap_build_downlink_nas_transport( + gnb_ue_t *gnb_ue, ogs_pkbuf_t *emmbuf) +{ + NGAP_NGAP_PDU_t pdu; + NGAP_InitiatingMessage_t *initiatingMessage = NULL; + NGAP_DownlinkNASTransport_t *DownlinkNASTransport = NULL; + + NGAP_DownlinkNASTransport_IEs_t *ie = NULL; + NGAP_AMF_UE_NGAP_ID_t *AMF_UE_NGAP_ID = NULL; + NGAP_ENB_UE_NGAP_ID_t *ENB_UE_NGAP_ID = NULL; + NGAP_NAS_PDU_t *NAS_PDU = NULL; + + ogs_assert(emmbuf); + ogs_assert(gnb_ue); + + ogs_debug("[AMF] DownlinkNASTransport"); + + memset(&pdu, 0, sizeof (NGAP_NGAP_PDU_t)); + pdu.present = NGAP_NGAP_PDU_PR_initiatingMessage; + pdu.choice.initiatingMessage = + CALLOC(1, sizeof(NGAP_InitiatingMessage_t)); + + initiatingMessage = pdu.choice.initiatingMessage; + initiatingMessage->procedureCode = + NGAP_ProcedureCode_id_downlinkNASTransport; + initiatingMessage->criticality = NGAP_Criticality_ignore; + initiatingMessage->value.present = + NGAP_InitiatingMessage__value_PR_DownlinkNASTransport; + + DownlinkNASTransport = + &initiatingMessage->value.choice.DownlinkNASTransport; + + ie = CALLOC(1, sizeof(NGAP_DownlinkNASTransport_IEs_t)); + ASN_SEQUENCE_ADD(&DownlinkNASTransport->protocolIEs, ie); + + ie->id = NGAP_ProtocolIE_ID_id_AMF_UE_NGAP_ID; + ie->criticality = NGAP_Criticality_reject; + ie->value.present = NGAP_DownlinkNASTransport_IEs__value_PR_AMF_UE_NGAP_ID; + + AMF_UE_NGAP_ID = &ie->value.choice.AMF_UE_NGAP_ID; + + ie = CALLOC(1, sizeof(NGAP_DownlinkNASTransport_IEs_t)); + ASN_SEQUENCE_ADD(&DownlinkNASTransport->protocolIEs, ie); + + ie->id = NGAP_ProtocolIE_ID_id_eNB_UE_NGAP_ID; + ie->criticality = NGAP_Criticality_reject; + ie->value.present = NGAP_DownlinkNASTransport_IEs__value_PR_ENB_UE_NGAP_ID; + + ENB_UE_NGAP_ID = &ie->value.choice.ENB_UE_NGAP_ID; + + ie = CALLOC(1, sizeof(NGAP_DownlinkNASTransport_IEs_t)); + ASN_SEQUENCE_ADD(&DownlinkNASTransport->protocolIEs, ie); + + ie->id = NGAP_ProtocolIE_ID_id_NAS_PDU; + ie->criticality = NGAP_Criticality_reject; + ie->value.present = NGAP_DownlinkNASTransport_IEs__value_PR_NAS_PDU; + + NAS_PDU = &ie->value.choice.NAS_PDU; + + ogs_debug(" ENB_UE_NGAP_ID[%d] AMF_UE_NGAP_ID[%d]", + gnb_ue->gnb_ue_ngap_id, gnb_ue->amf_ue_ngap_id); + + *AMF_UE_NGAP_ID = gnb_ue->amf_ue_ngap_id; + *ENB_UE_NGAP_ID = gnb_ue->gnb_ue_ngap_id; + + NAS_PDU->size = emmbuf->len; + NAS_PDU->buf = CALLOC(NAS_PDU->size, sizeof(uint8_t)); + memcpy(NAS_PDU->buf, emmbuf->data, NAS_PDU->size); + ogs_pkbuf_free(emmbuf); + + return ogs_ngap_encode(&pdu); +} + +ogs_pkbuf_t *ngap_build_initial_context_setup_request( + amf_ue_t *amf_ue, ogs_pkbuf_t *emmbuf) +{ + int rv; + + NGAP_NGAP_PDU_t pdu; + NGAP_InitiatingMessage_t *initiatingMessage = NULL; + NGAP_InitialContextSetupRequest_t *InitialContextSetupRequest = NULL; + + NGAP_InitialContextSetupRequestIEs_t *ie = NULL; + NGAP_AMF_UE_NGAP_ID_t *AMF_UE_NGAP_ID = NULL; + NGAP_ENB_UE_NGAP_ID_t *ENB_UE_NGAP_ID = NULL; + NGAP_UEAggregateMaximumBitrate_t *UEAggregateMaximumBitrate = NULL; + NGAP_E_RABToBeSetupListCtxtSUReq_t *E_RABToBeSetupListCtxtSUReq = NULL; + NGAP_UESecurityCapabilities_t *UESecurityCapabilities = NULL; + NGAP_SecurityKey_t *SecurityKey = NULL; + + gnb_ue_t *gnb_ue = NULL; + amf_sess_t *sess = NULL; + amf_bearer_t *bearer = NULL; + ogs_diam_s6a_subscription_data_t *subscription_data = NULL; + + ogs_assert(amf_ue); + gnb_ue = amf_ue->gnb_ue; + ogs_assert(gnb_ue); + subscription_data = &amf_ue->subscription_data; + ogs_assert(subscription_data); + + ogs_debug("[AMF] Initial context setup request"); + + memset(&pdu, 0, sizeof (NGAP_NGAP_PDU_t)); + pdu.present = NGAP_NGAP_PDU_PR_initiatingMessage; + pdu.choice.initiatingMessage = + CALLOC(1, sizeof(NGAP_InitiatingMessage_t)); + + initiatingMessage = pdu.choice.initiatingMessage; + initiatingMessage->procedureCode = + NGAP_ProcedureCode_id_InitialContextSetup; + initiatingMessage->criticality = NGAP_Criticality_reject; + initiatingMessage->value.present = + NGAP_InitiatingMessage__value_PR_InitialContextSetupRequest; + + InitialContextSetupRequest = + &initiatingMessage->value.choice.InitialContextSetupRequest; + + ie = CALLOC(1, sizeof(NGAP_InitialContextSetupRequestIEs_t)); + ASN_SEQUENCE_ADD(&InitialContextSetupRequest->protocolIEs, ie); + + ie->id = NGAP_ProtocolIE_ID_id_AMF_UE_NGAP_ID; + ie->criticality = NGAP_Criticality_reject; + ie->value.present = + NGAP_InitialContextSetupRequestIEs__value_PR_AMF_UE_NGAP_ID; + + AMF_UE_NGAP_ID = &ie->value.choice.AMF_UE_NGAP_ID; + + ie = CALLOC(1, sizeof(NGAP_InitialContextSetupRequestIEs_t)); + ASN_SEQUENCE_ADD(&InitialContextSetupRequest->protocolIEs, ie); + + ie->id = NGAP_ProtocolIE_ID_id_eNB_UE_NGAP_ID; + ie->criticality = NGAP_Criticality_reject; + ie->value.present = + NGAP_InitialContextSetupRequestIEs__value_PR_ENB_UE_NGAP_ID; + + ENB_UE_NGAP_ID = &ie->value.choice.ENB_UE_NGAP_ID; + + ie = CALLOC(1, sizeof(NGAP_InitialContextSetupRequestIEs_t)); + ASN_SEQUENCE_ADD(&InitialContextSetupRequest->protocolIEs, ie); + + ie->id = NGAP_ProtocolIE_ID_id_uEaggregateMaximumBitrate; + ie->criticality = NGAP_Criticality_reject; + ie->value.present = + NGAP_InitialContextSetupRequestIEs__value_PR_UEAggregateMaximumBitrate; + + UEAggregateMaximumBitrate = &ie->value.choice.UEAggregateMaximumBitrate; + + ie = CALLOC(1, sizeof(NGAP_InitialContextSetupRequestIEs_t)); + ASN_SEQUENCE_ADD(&InitialContextSetupRequest->protocolIEs, ie); + + ie->id = NGAP_ProtocolIE_ID_id_E_RABToBeSetupListCtxtSUReq; + ie->criticality = NGAP_Criticality_reject; + ie->value.present = + NGAP_InitialContextSetupRequestIEs__value_PR_E_RABToBeSetupListCtxtSUReq; + + E_RABToBeSetupListCtxtSUReq = &ie->value.choice.E_RABToBeSetupListCtxtSUReq; + + ogs_debug(" ENB_UE_NGAP_ID[%d] AMF_UE_NGAP_ID[%d]", + gnb_ue->gnb_ue_ngap_id, gnb_ue->amf_ue_ngap_id); + + *AMF_UE_NGAP_ID = gnb_ue->amf_ue_ngap_id; + *ENB_UE_NGAP_ID = gnb_ue->gnb_ue_ngap_id; + + asn_uint642INTEGER( + &UEAggregateMaximumBitrate->uEaggregateMaximumBitRateUL, + subscription_data->ambr.uplink); + asn_uint642INTEGER( + &UEAggregateMaximumBitrate->uEaggregateMaximumBitRateDL, + subscription_data->ambr.downlink); + + sess = amf_sess_first(amf_ue); + while (sess) { + bearer = amf_bearer_first(sess); + while (bearer) { + NGAP_E_RABToBeSetupItemCtxtSUReqIEs_t *item = NULL; + NGAP_E_RABToBeSetupItemCtxtSUReq_t *e_rab = NULL; + NGAP_GBR_QosInformation_t *gbrQosInformation = NULL; + NGAP_NAS_PDU_t *nasPdu = NULL; + + item = CALLOC( + 1, sizeof(NGAP_E_RABToBeSetupItemCtxtSUReqIEs_t)); + ASN_SEQUENCE_ADD(&E_RABToBeSetupListCtxtSUReq->list, item); + + item->id = NGAP_ProtocolIE_ID_id_E_RABToBeSetupItemCtxtSUReq; + item->criticality = NGAP_Criticality_reject; + item->value.present = NGAP_E_RABToBeSetupItemCtxtSUReqIEs__value_PR_E_RABToBeSetupItemCtxtSUReq; + + e_rab = &item->value.choice.E_RABToBeSetupItemCtxtSUReq; + + e_rab->e_RAB_ID = bearer->ebi; + e_rab->e_RABlevelQoSParameters.qCI = bearer->qos.qci; + + ogs_debug(" EBI[%d] QCI[%d] SGW-NGU-TEID[%d]", + bearer->ebi, bearer->qos.qci, bearer->sgw_s1u_teid); + + e_rab->e_RABlevelQoSParameters.allocationRetentionPriority. + priorityLevel = bearer->qos.arp.priority_level; + e_rab->e_RABlevelQoSParameters.allocationRetentionPriority. + pre_emptionCapability = + !(bearer->qos.arp.pre_emption_capability); + e_rab->e_RABlevelQoSParameters.allocationRetentionPriority. + pre_emptionVulnerability = + !(bearer->qos.arp.pre_emption_vulnerability); + + if (bearer->qos.mbr.downlink || bearer->qos.mbr.uplink || + bearer->qos.gbr.downlink || bearer->qos.gbr.uplink) { + if (bearer->qos.mbr.downlink == 0) + bearer->qos.mbr.downlink = MAX_BIT_RATE; + if (bearer->qos.mbr.uplink == 0) + bearer->qos.mbr.uplink = MAX_BIT_RATE; + if (bearer->qos.gbr.downlink == 0) + bearer->qos.gbr.downlink = MAX_BIT_RATE; + if (bearer->qos.gbr.uplink == 0) + bearer->qos.gbr.uplink = MAX_BIT_RATE; + + gbrQosInformation = + CALLOC(1, sizeof(struct NGAP_GBR_QosInformation)); + asn_uint642INTEGER(&gbrQosInformation->e_RAB_MaximumBitrateDL, + bearer->qos.mbr.downlink); + asn_uint642INTEGER(&gbrQosInformation->e_RAB_MaximumBitrateUL, + bearer->qos.mbr.uplink); + asn_uint642INTEGER(&gbrQosInformation-> + e_RAB_GuaranteedBitrateDL, bearer->qos.gbr.downlink); + asn_uint642INTEGER(&gbrQosInformation-> + e_RAB_GuaranteedBitrateUL, bearer->qos.gbr.uplink); + e_rab->e_RABlevelQoSParameters.gbrQosInformation = + gbrQosInformation; + } + + rv = ogs_asn_ip_to_BIT_STRING( + &bearer->sgw_s1u_ip, &e_rab->transportLayerAddress); + ogs_assert(rv == OGS_OK); + ogs_asn_uint32_to_OCTET_STRING( + bearer->sgw_s1u_teid, &e_rab->gTP_TEID); + + if (emmbuf && emmbuf->len) { + nasPdu = (NGAP_NAS_PDU_t *)CALLOC( + 1, sizeof(NGAP_NAS_PDU_t)); + nasPdu->size = emmbuf->len; + nasPdu->buf = CALLOC(nasPdu->size, sizeof(uint8_t)); + memcpy(nasPdu->buf, emmbuf->data, nasPdu->size); + e_rab->nAS_PDU = nasPdu; + ogs_pkbuf_free(emmbuf); + } + + bearer = amf_bearer_next(bearer); + } + sess = amf_sess_next(sess); + } + + ie = CALLOC(1, sizeof(NGAP_InitialContextSetupRequestIEs_t)); + ASN_SEQUENCE_ADD(&InitialContextSetupRequest->protocolIEs, ie); + + ie->id = NGAP_ProtocolIE_ID_id_UESecurityCapabilities; + ie->criticality = NGAP_Criticality_reject; + ie->value.present = + NGAP_InitialContextSetupRequestIEs__value_PR_UESecurityCapabilities; + + UESecurityCapabilities = &ie->value.choice.UESecurityCapabilities; + + UESecurityCapabilities->encryptionAlgorithms.size = 2; + UESecurityCapabilities->encryptionAlgorithms.buf = + CALLOC(UESecurityCapabilities->encryptionAlgorithms.size, + sizeof(uint8_t)); + UESecurityCapabilities->encryptionAlgorithms.bits_unused = 0; + UESecurityCapabilities->encryptionAlgorithms.buf[0] = + (amf_ue->ue_network_capability.eea << 1); + + UESecurityCapabilities->integrityProtectionAlgorithms.size = 2; + UESecurityCapabilities->integrityProtectionAlgorithms.buf = + CALLOC(UESecurityCapabilities-> + integrityProtectionAlgorithms.size, sizeof(uint8_t)); + UESecurityCapabilities->integrityProtectionAlgorithms.bits_unused = 0; + UESecurityCapabilities->integrityProtectionAlgorithms.buf[0] = + (amf_ue->ue_network_capability.eia << 1); + + ie = CALLOC(1, sizeof(NGAP_InitialContextSetupRequestIEs_t)); + ASN_SEQUENCE_ADD(&InitialContextSetupRequest->protocolIEs, ie); + + ie->id = NGAP_ProtocolIE_ID_id_SecurityKey; + ie->criticality = NGAP_Criticality_reject; + ie->value.present = + NGAP_InitialContextSetupRequestIEs__value_PR_SecurityKey; + + SecurityKey = &ie->value.choice.SecurityKey; + + SecurityKey->size = OGS_SHA256_DIGEST_SIZE; + SecurityKey->buf = + CALLOC(SecurityKey->size, sizeof(uint8_t)); + SecurityKey->bits_unused = 0; + memcpy(SecurityKey->buf, amf_ue->kgnb, SecurityKey->size); + + if (amf_ue->nas_eps.type == AMF_EPS_TYPE_EXTENDED_SERVICE_REQUEST && + AMF_P_TMSI_IS_AVAILABLE(amf_ue)) { + + /* Set CS-Fallback */ + NGAP_CSFallbackIndicator_t *CSFallbackIndicator = NULL; + NGAP_LAI_t *LAI = NULL; + + ie = CALLOC(1, sizeof(NGAP_InitialContextSetupRequestIEs_t)); + ASN_SEQUENCE_ADD(&InitialContextSetupRequest->protocolIEs, ie); + + ie->id = NGAP_ProtocolIE_ID_id_CSFallbackIndicator; + ie->criticality = NGAP_Criticality_reject; + ie->value.present = + NGAP_InitialContextSetupRequestIEs__value_PR_CSFallbackIndicator; + + CSFallbackIndicator = &ie->value.choice.CSFallbackIndicator; + ogs_assert(CSFallbackIndicator); + + *CSFallbackIndicator = NGAP_CSFallbackIndicator_cs_fallback_required; + + ie = CALLOC(1, sizeof(NGAP_InitialContextSetupRequestIEs_t)); + ASN_SEQUENCE_ADD(&InitialContextSetupRequest->protocolIEs, ie); + + ie->id = NGAP_ProtocolIE_ID_id_RegisteredLAI; + ie->criticality = NGAP_Criticality_ignore; + ie->value.present = + NGAP_InitialContextSetupRequestIEs__value_PR_LAI; + + LAI = &ie->value.choice.LAI; + ogs_assert(LAI); + + ogs_ngap_buffer_to_OCTET_STRING( + &amf_ue->tai.plmn_id, sizeof(ogs_plmn_id_t), &LAI->pLMNidentity); + ogs_assert(amf_ue->csmap); + ogs_assert(amf_ue->p_tmsi); + ogs_asn_uint16_to_OCTET_STRING(amf_ue->csmap->lai.lac, &LAI->lAC); + + } + + if (amf_ue->ueRadioCapability.buf && amf_ue->ueRadioCapability.size) { + /* Set UeRadioCapability if exists */ + NGAP_UERadioCapability_t *UERadioCapability = NULL; + + ie = CALLOC(1, sizeof(NGAP_InitialContextSetupRequestIEs_t)); + ASN_SEQUENCE_ADD(&InitialContextSetupRequest->protocolIEs, ie); + + ie->id = NGAP_ProtocolIE_ID_id_UERadioCapability; + ie->criticality = NGAP_Criticality_ignore; + ie->value.present = + NGAP_InitialContextSetupRequestIEs__value_PR_UERadioCapability; + + UERadioCapability = &ie->value.choice.UERadioCapability; + + ogs_assert(UERadioCapability); + ogs_ngap_buffer_to_OCTET_STRING( + amf_ue->ueRadioCapability.buf, amf_ue->ueRadioCapability.size, + UERadioCapability); + } + + return ogs_ngap_encode(&pdu); +} + +ogs_pkbuf_t *ngap_build_ue_context_modification_request(amf_ue_t *amf_ue) +{ + NGAP_NGAP_PDU_t pdu; + NGAP_InitiatingMessage_t *initiatingMessage = NULL; + NGAP_UEContextModificationRequest_t *UEContextModificationRequest = NULL; + + NGAP_UEContextModificationRequestIEs_t *ie = NULL; + + NGAP_AMF_UE_NGAP_ID_t *AMF_UE_NGAP_ID = NULL; + NGAP_ENB_UE_NGAP_ID_t *ENB_UE_NGAP_ID = NULL; + NGAP_UESecurityCapabilities_t *UESecurityCapabilities = NULL; + NGAP_SecurityKey_t *SecurityKey = NULL; + NGAP_CSFallbackIndicator_t *CSFallbackIndicator = NULL; + NGAP_LAI_t *LAI = NULL; + + gnb_ue_t *gnb_ue = NULL; + + ogs_assert(amf_ue); + gnb_ue = amf_ue->gnb_ue; + ogs_assert(gnb_ue); + + ogs_debug("[AMF] UE context modification request"); + + memset(&pdu, 0, sizeof (NGAP_NGAP_PDU_t)); + pdu.present = NGAP_NGAP_PDU_PR_initiatingMessage; + pdu.choice.initiatingMessage = + CALLOC(1, sizeof(NGAP_InitiatingMessage_t)); + + initiatingMessage = pdu.choice.initiatingMessage; + initiatingMessage->procedureCode = + NGAP_ProcedureCode_id_UEContextModification; + initiatingMessage->criticality = NGAP_Criticality_reject; + initiatingMessage->value.present = + NGAP_InitiatingMessage__value_PR_UEContextModificationRequest; + + UEContextModificationRequest = + &initiatingMessage->value.choice.UEContextModificationRequest; + + ie = CALLOC(1, sizeof(NGAP_UEContextModificationRequestIEs_t)); + ASN_SEQUENCE_ADD(&UEContextModificationRequest->protocolIEs, ie); + + ie->id = NGAP_ProtocolIE_ID_id_AMF_UE_NGAP_ID; + ie->criticality = NGAP_Criticality_reject; + ie->value.present = + NGAP_UEContextModificationRequestIEs__value_PR_AMF_UE_NGAP_ID, + + AMF_UE_NGAP_ID = &ie->value.choice.AMF_UE_NGAP_ID; + + ie = CALLOC(1, sizeof(NGAP_UEContextModificationRequestIEs_t)); + ASN_SEQUENCE_ADD(&UEContextModificationRequest->protocolIEs, ie); + + ie->id = NGAP_ProtocolIE_ID_id_eNB_UE_NGAP_ID; + ie->criticality = NGAP_Criticality_reject; + ie->value.present = + NGAP_UEContextModificationRequestIEs__value_PR_ENB_UE_NGAP_ID, + + ENB_UE_NGAP_ID = &ie->value.choice.ENB_UE_NGAP_ID; + + *AMF_UE_NGAP_ID = gnb_ue->amf_ue_ngap_id; + *ENB_UE_NGAP_ID = gnb_ue->gnb_ue_ngap_id; + + ogs_debug(" ENB_UE_NGAP_ID[%d] AMF_UE_NGAP_ID[%d]", + gnb_ue->gnb_ue_ngap_id, gnb_ue->amf_ue_ngap_id); + + if (amf_ue->nas_eps.type == AMF_EPS_TYPE_EXTENDED_SERVICE_REQUEST && + AMF_P_TMSI_IS_AVAILABLE(amf_ue)) { + ie = CALLOC(1, sizeof(NGAP_UEContextModificationRequestIEs_t)); + ASN_SEQUENCE_ADD(&UEContextModificationRequest->protocolIEs, ie); + + ie->id = NGAP_ProtocolIE_ID_id_CSFallbackIndicator; + ie->criticality = NGAP_Criticality_reject; + ie->value.present = + NGAP_UEContextModificationRequestIEs__value_PR_CSFallbackIndicator; + + CSFallbackIndicator = &ie->value.choice.CSFallbackIndicator; + ogs_assert(CSFallbackIndicator); + + *CSFallbackIndicator = NGAP_CSFallbackIndicator_cs_fallback_required; + + ie = CALLOC(1, sizeof(NGAP_UEContextModificationRequestIEs_t)); + ASN_SEQUENCE_ADD(&UEContextModificationRequest->protocolIEs, ie); + + ie->id = NGAP_ProtocolIE_ID_id_RegisteredLAI; + ie->criticality = NGAP_Criticality_ignore; + ie->value.present = + NGAP_UEContextModificationRequestIEs__value_PR_LAI; + + LAI = &ie->value.choice.LAI; + ogs_assert(LAI); + + ogs_ngap_buffer_to_OCTET_STRING( + &amf_ue->tai.plmn_id, sizeof(ogs_plmn_id_t), &LAI->pLMNidentity); + ogs_assert(amf_ue->csmap); + ogs_assert(amf_ue->p_tmsi); + ogs_asn_uint16_to_OCTET_STRING(amf_ue->csmap->lai.lac, &LAI->lAC); + + } else { + ie = CALLOC(1, sizeof(NGAP_UEContextModificationRequestIEs_t)); + ASN_SEQUENCE_ADD(&UEContextModificationRequest->protocolIEs, ie); + + ie->id = NGAP_ProtocolIE_ID_id_UESecurityCapabilities; + ie->criticality = NGAP_Criticality_reject; + ie->value.present = NGAP_UEContextModificationRequestIEs__value_PR_UESecurityCapabilities; + + UESecurityCapabilities = &ie->value.choice.UESecurityCapabilities; + + UESecurityCapabilities->encryptionAlgorithms.size = 2; + UESecurityCapabilities->encryptionAlgorithms.buf = + CALLOC(UESecurityCapabilities->encryptionAlgorithms.size, + sizeof(uint8_t)); + UESecurityCapabilities->encryptionAlgorithms.bits_unused = 0; + UESecurityCapabilities->encryptionAlgorithms.buf[0] = + (amf_ue->ue_network_capability.eea << 1); + + UESecurityCapabilities->integrityProtectionAlgorithms.size = 2; + UESecurityCapabilities->integrityProtectionAlgorithms.buf = + CALLOC(UESecurityCapabilities-> + integrityProtectionAlgorithms.size, sizeof(uint8_t)); + UESecurityCapabilities->integrityProtectionAlgorithms.bits_unused = 0; + UESecurityCapabilities->integrityProtectionAlgorithms.buf[0] = + (amf_ue->ue_network_capability.eia << 1); + + ie = CALLOC(1, sizeof(NGAP_UEContextModificationRequestIEs_t)); + ASN_SEQUENCE_ADD(&UEContextModificationRequest->protocolIEs, ie); + + ie->id = NGAP_ProtocolIE_ID_id_SecurityKey; + ie->criticality = NGAP_Criticality_reject; + ie->value.present = + NGAP_UEContextModificationRequestIEs__value_PR_SecurityKey; + + SecurityKey = &ie->value.choice.SecurityKey; + + SecurityKey->size = OGS_SHA256_DIGEST_SIZE; + SecurityKey->buf = + CALLOC(SecurityKey->size, sizeof(uint8_t)); + SecurityKey->bits_unused = 0; + memcpy(SecurityKey->buf, amf_ue->kgnb, SecurityKey->size); + } + + return ogs_ngap_encode(&pdu); +} + +ogs_pkbuf_t *ngap_build_ue_context_release_command( + gnb_ue_t *gnb_ue, NGAP_Cause_PR group, long cause) +{ + NGAP_NGAP_PDU_t pdu; + NGAP_InitiatingMessage_t *initiatingMessage = NULL; + NGAP_UEContextReleaseCommand_t *UEContextReleaseCommand = NULL; + + NGAP_UEContextReleaseCommand_IEs_t *ie = NULL; + NGAP_UE_NGAP_IDs_t *UE_NGAP_IDs = NULL; + NGAP_Cause_t *Cause = NULL; + + ogs_assert(gnb_ue); + + if (gnb_ue->amf_ue_ngap_id == 0) { + ogs_error("invalid amf ue ngap id"); + return NULL; + } + + memset(&pdu, 0, sizeof (NGAP_NGAP_PDU_t)); + pdu.present = NGAP_NGAP_PDU_PR_initiatingMessage; + pdu.choice.initiatingMessage = + CALLOC(1, sizeof(NGAP_InitiatingMessage_t)); + + initiatingMessage = pdu.choice.initiatingMessage; + initiatingMessage->procedureCode = NGAP_ProcedureCode_id_UEContextRelease; + initiatingMessage->criticality = NGAP_Criticality_reject; + initiatingMessage->value.present = + NGAP_InitiatingMessage__value_PR_UEContextReleaseCommand; + + UEContextReleaseCommand = + &initiatingMessage->value.choice.UEContextReleaseCommand; + + ie = CALLOC(1, sizeof(NGAP_UEContextReleaseCommand_IEs_t)); + ASN_SEQUENCE_ADD(&UEContextReleaseCommand->protocolIEs, ie); + + ie->id = NGAP_ProtocolIE_ID_id_UE_NGAP_IDs; + ie->criticality = NGAP_Criticality_reject; + ie->value.present = NGAP_UEContextReleaseCommand_IEs__value_PR_UE_NGAP_IDs; + + UE_NGAP_IDs = &ie->value.choice.UE_NGAP_IDs; + + ie = CALLOC(1, sizeof(NGAP_UEContextReleaseCommand_IEs_t)); + ASN_SEQUENCE_ADD(&UEContextReleaseCommand->protocolIEs, ie); + + ie->id = NGAP_ProtocolIE_ID_id_Cause; + ie->criticality = NGAP_Criticality_ignore; + ie->value.present = NGAP_UEContextReleaseCommand_IEs__value_PR_Cause; + + Cause = &ie->value.choice.Cause; + + if (gnb_ue->gnb_ue_ngap_id == INVALID_UE_NGAP_ID) { + UE_NGAP_IDs->present = NGAP_UE_NGAP_IDs_PR_mME_UE_NGAP_ID; + UE_NGAP_IDs->choice.mME_UE_NGAP_ID = gnb_ue->amf_ue_ngap_id; + } else { + UE_NGAP_IDs->present = NGAP_UE_NGAP_IDs_PR_uE_NGAP_ID_pair; + UE_NGAP_IDs->choice.uE_NGAP_ID_pair = + CALLOC(1, sizeof(NGAP_UE_NGAP_ID_pair_t)); + UE_NGAP_IDs->choice.uE_NGAP_ID_pair->mME_UE_NGAP_ID = + gnb_ue->amf_ue_ngap_id; + UE_NGAP_IDs->choice.uE_NGAP_ID_pair->eNB_UE_NGAP_ID = + gnb_ue->gnb_ue_ngap_id; + } + + Cause->present = group; + Cause->choice.radioNetwork = cause; + + return ogs_ngap_encode(&pdu); +} + + +ogs_pkbuf_t *ngap_build_e_rab_setup_request( + amf_bearer_t *bearer, ogs_pkbuf_t *esmbuf) +{ + int rv; + + NGAP_NGAP_PDU_t pdu; + NGAP_InitiatingMessage_t *initiatingMessage = NULL; + NGAP_E_RABSetupRequest_t *E_RABSetupRequest = NULL; + + NGAP_E_RABSetupRequestIEs_t *ie = NULL; + NGAP_AMF_UE_NGAP_ID_t *AMF_UE_NGAP_ID = NULL; + NGAP_ENB_UE_NGAP_ID_t *ENB_UE_NGAP_ID = NULL; + NGAP_E_RABToBeSetupListBearerSUReq_t *E_RABToBeSetupListBearerSUReq = NULL; + + NGAP_E_RABToBeSetupItemBearerSUReqIEs_t *item = NULL; + NGAP_E_RABToBeSetupItemBearerSUReq_t *e_rab = NULL; + NGAP_GBR_QosInformation_t *gbrQosInformation = NULL; + NGAP_NAS_PDU_t *nasPdu = NULL; + + amf_ue_t *amf_ue = NULL; + gnb_ue_t *gnb_ue = NULL; + + ogs_assert(esmbuf); + ogs_assert(bearer); + + amf_ue = bearer->amf_ue; + ogs_assert(amf_ue); + gnb_ue = amf_ue->gnb_ue; + ogs_assert(gnb_ue); + + memset(&pdu, 0, sizeof (NGAP_NGAP_PDU_t)); + pdu.present = NGAP_NGAP_PDU_PR_initiatingMessage; + pdu.choice.initiatingMessage = + CALLOC(1, sizeof(NGAP_InitiatingMessage_t)); + + initiatingMessage = pdu.choice.initiatingMessage; + initiatingMessage->procedureCode = NGAP_ProcedureCode_id_E_RABSetup; + initiatingMessage->criticality = NGAP_Criticality_reject; + initiatingMessage->value.present = + NGAP_InitiatingMessage__value_PR_E_RABSetupRequest; + + E_RABSetupRequest = &initiatingMessage->value.choice.E_RABSetupRequest; + + ie = CALLOC(1, sizeof(NGAP_E_RABSetupRequestIEs_t)); + ASN_SEQUENCE_ADD(&E_RABSetupRequest->protocolIEs, ie); + + ie->id = NGAP_ProtocolIE_ID_id_AMF_UE_NGAP_ID; + ie->criticality = NGAP_Criticality_reject; + ie->value.present = NGAP_E_RABSetupRequestIEs__value_PR_AMF_UE_NGAP_ID; + + AMF_UE_NGAP_ID = &ie->value.choice.AMF_UE_NGAP_ID; + + ie = CALLOC(1, sizeof(NGAP_E_RABSetupRequestIEs_t)); + ASN_SEQUENCE_ADD(&E_RABSetupRequest->protocolIEs, ie); + + ie->id = NGAP_ProtocolIE_ID_id_eNB_UE_NGAP_ID; + ie->criticality = NGAP_Criticality_reject; + ie->value.present = NGAP_E_RABSetupRequestIEs__value_PR_ENB_UE_NGAP_ID; + + ENB_UE_NGAP_ID = &ie->value.choice.ENB_UE_NGAP_ID; + + ie = CALLOC(1, sizeof(NGAP_E_RABSetupRequestIEs_t)); + ASN_SEQUENCE_ADD(&E_RABSetupRequest->protocolIEs, ie); + + ie->id = NGAP_ProtocolIE_ID_id_E_RABToBeSetupListBearerSUReq; + ie->criticality = NGAP_Criticality_reject; + ie->value.present = + NGAP_E_RABSetupRequestIEs__value_PR_E_RABToBeSetupListBearerSUReq; + + E_RABToBeSetupListBearerSUReq = + &ie->value.choice.E_RABToBeSetupListBearerSUReq; + + ogs_debug(" ENB_UE_NGAP_ID[%d] AMF_UE_NGAP_ID[%d]", + gnb_ue->gnb_ue_ngap_id, gnb_ue->amf_ue_ngap_id); + + *AMF_UE_NGAP_ID = gnb_ue->amf_ue_ngap_id; + *ENB_UE_NGAP_ID = gnb_ue->gnb_ue_ngap_id; + + item = CALLOC(1, sizeof(NGAP_E_RABToBeSetupItemBearerSUReqIEs_t)); + ASN_SEQUENCE_ADD(&E_RABToBeSetupListBearerSUReq->list, item); + + item->id = NGAP_ProtocolIE_ID_id_E_RABToBeSetupItemBearerSUReq; + item->criticality = NGAP_Criticality_reject; + item->value.present = NGAP_E_RABToBeSetupItemBearerSUReqIEs__value_PR_E_RABToBeSetupItemBearerSUReq; + + e_rab = &item->value.choice.E_RABToBeSetupItemBearerSUReq; + + e_rab->e_RAB_ID = bearer->ebi; + e_rab->e_RABlevelQoSParameters.qCI = bearer->qos.qci; + + ogs_debug(" EBI[%d] QCI[%d]", bearer->ebi, bearer->qos.qci); + + e_rab->e_RABlevelQoSParameters.allocationRetentionPriority. + priorityLevel = bearer->qos.arp.priority_level; + e_rab->e_RABlevelQoSParameters.allocationRetentionPriority. + pre_emptionCapability = !(bearer->qos.arp.pre_emption_capability); + e_rab->e_RABlevelQoSParameters.allocationRetentionPriority. + pre_emptionVulnerability = !(bearer->qos.arp.pre_emption_vulnerability); + + if (bearer->qos.mbr.downlink || bearer->qos.mbr.uplink || + bearer->qos.gbr.downlink || bearer->qos.gbr.uplink) { + if (bearer->qos.mbr.downlink == 0) + bearer->qos.mbr.downlink = MAX_BIT_RATE; + if (bearer->qos.mbr.uplink == 0) + bearer->qos.mbr.uplink = MAX_BIT_RATE; + if (bearer->qos.gbr.downlink == 0) + bearer->qos.gbr.downlink = MAX_BIT_RATE; + if (bearer->qos.gbr.uplink == 0) + bearer->qos.gbr.uplink = MAX_BIT_RATE; + + gbrQosInformation = CALLOC(1, sizeof(NGAP_GBR_QosInformation_t)); + asn_uint642INTEGER(&gbrQosInformation->e_RAB_MaximumBitrateDL, + bearer->qos.mbr.downlink); + asn_uint642INTEGER(&gbrQosInformation->e_RAB_MaximumBitrateUL, + bearer->qos.mbr.uplink); + asn_uint642INTEGER(&gbrQosInformation->e_RAB_GuaranteedBitrateDL, + bearer->qos.gbr.downlink); + asn_uint642INTEGER(&gbrQosInformation->e_RAB_GuaranteedBitrateUL, + bearer->qos.gbr.uplink); + e_rab->e_RABlevelQoSParameters.gbrQosInformation = gbrQosInformation; + } + + rv = ogs_asn_ip_to_BIT_STRING( + &bearer->sgw_s1u_ip, &e_rab->transportLayerAddress); + ogs_assert(rv == OGS_OK); + ogs_asn_uint32_to_OCTET_STRING(bearer->sgw_s1u_teid, &e_rab->gTP_TEID); + ogs_debug(" SGW-NGU-TEID[%d]", bearer->sgw_s1u_teid); + + nasPdu = &e_rab->nAS_PDU; + nasPdu->size = esmbuf->len; + nasPdu->buf = CALLOC(nasPdu->size, sizeof(uint8_t)); + memcpy(nasPdu->buf, esmbuf->data, nasPdu->size); + ogs_pkbuf_free(esmbuf); + + return ogs_ngap_encode(&pdu); +} + +ogs_pkbuf_t *ngap_build_e_rab_modify_request( + amf_bearer_t *bearer, ogs_pkbuf_t *esmbuf) +{ + NGAP_NGAP_PDU_t pdu; + NGAP_InitiatingMessage_t *initiatingMessage = NULL; + NGAP_E_RABModifyRequest_t *E_RABModifyRequest = NULL; + + NGAP_E_RABModifyRequestIEs_t *ie = NULL; + NGAP_AMF_UE_NGAP_ID_t *AMF_UE_NGAP_ID = NULL; + NGAP_ENB_UE_NGAP_ID_t *ENB_UE_NGAP_ID = NULL; + NGAP_E_RABToBeModifiedListBearerModReq_t + *E_RABToBeModifiedListBearerModReq = NULL; + + NGAP_E_RABToBeModifiedItemBearerModReqIEs_t *item = NULL; + NGAP_E_RABToBeModifiedItemBearerModReq_t *e_rab = NULL; + NGAP_GBR_QosInformation_t *gbrQosInformation = NULL; + NGAP_NAS_PDU_t *nasPdu = NULL; + + amf_ue_t *amf_ue = NULL; + gnb_ue_t *gnb_ue = NULL; + + ogs_assert(esmbuf); + ogs_assert(bearer); + + amf_ue = bearer->amf_ue; + ogs_assert(amf_ue); + gnb_ue = amf_ue->gnb_ue; + ogs_assert(gnb_ue); + + ogs_debug("[AMF] E-RAB modify request"); + memset(&pdu, 0, sizeof (NGAP_NGAP_PDU_t)); + pdu.present = NGAP_NGAP_PDU_PR_initiatingMessage; + pdu.choice.initiatingMessage = + CALLOC(1, sizeof(NGAP_InitiatingMessage_t)); + + initiatingMessage = pdu.choice.initiatingMessage; + initiatingMessage->procedureCode = NGAP_ProcedureCode_id_E_RABModify; + initiatingMessage->criticality = NGAP_Criticality_reject; + initiatingMessage->value.present = + NGAP_InitiatingMessage__value_PR_E_RABModifyRequest; + + E_RABModifyRequest = &initiatingMessage->value.choice.E_RABModifyRequest; + + ie = CALLOC(1, sizeof(NGAP_E_RABModifyRequestIEs_t)); + ASN_SEQUENCE_ADD(&E_RABModifyRequest->protocolIEs, ie); + + ie->id = NGAP_ProtocolIE_ID_id_AMF_UE_NGAP_ID; + ie->criticality = NGAP_Criticality_reject; + ie->value.present = NGAP_E_RABModifyRequestIEs__value_PR_AMF_UE_NGAP_ID; + + AMF_UE_NGAP_ID = &ie->value.choice.AMF_UE_NGAP_ID; + + ie = CALLOC(1, sizeof(NGAP_E_RABModifyRequestIEs_t)); + ASN_SEQUENCE_ADD(&E_RABModifyRequest->protocolIEs, ie); + + ie->id = NGAP_ProtocolIE_ID_id_eNB_UE_NGAP_ID; + ie->criticality = NGAP_Criticality_reject; + ie->value.present = NGAP_E_RABModifyRequestIEs__value_PR_ENB_UE_NGAP_ID; + + ENB_UE_NGAP_ID = &ie->value.choice.ENB_UE_NGAP_ID; + + ie = CALLOC(1, sizeof(NGAP_E_RABModifyRequestIEs_t)); + ASN_SEQUENCE_ADD(&E_RABModifyRequest->protocolIEs, ie); + + ie->id = NGAP_ProtocolIE_ID_id_E_RABToBeModifiedListBearerModReq; + ie->criticality = NGAP_Criticality_reject; + ie->value.present = + NGAP_E_RABModifyRequestIEs__value_PR_E_RABToBeModifiedListBearerModReq; + + E_RABToBeModifiedListBearerModReq = + &ie->value.choice.E_RABToBeModifiedListBearerModReq; + + ogs_debug(" ENB_UE_NGAP_ID[%d] AMF_UE_NGAP_ID[%d]", + gnb_ue->gnb_ue_ngap_id, gnb_ue->amf_ue_ngap_id); + + *AMF_UE_NGAP_ID = gnb_ue->amf_ue_ngap_id; + *ENB_UE_NGAP_ID = gnb_ue->gnb_ue_ngap_id; + + item = CALLOC(1, sizeof(NGAP_E_RABToBeModifiedItemBearerModReqIEs_t)); + ASN_SEQUENCE_ADD(&E_RABToBeModifiedListBearerModReq->list, item); + + item->id = NGAP_ProtocolIE_ID_id_E_RABToBeModifiedItemBearerModReq; + item->criticality = NGAP_Criticality_reject; + item->value.present = NGAP_E_RABToBeModifiedItemBearerModReqIEs__value_PR_E_RABToBeModifiedItemBearerModReq; + + e_rab = &item->value.choice.E_RABToBeModifiedItemBearerModReq; + + e_rab->e_RAB_ID = bearer->ebi; + e_rab->e_RABLevelQoSParameters.qCI = bearer->qos.qci; + + ogs_debug(" EBI[%d] QCI[%d]", bearer->ebi, bearer->qos.qci); + + e_rab->e_RABLevelQoSParameters.allocationRetentionPriority. + priorityLevel = bearer->qos.arp.priority_level; + e_rab->e_RABLevelQoSParameters.allocationRetentionPriority. + pre_emptionCapability = !(bearer->qos.arp.pre_emption_capability); + e_rab->e_RABLevelQoSParameters.allocationRetentionPriority. + pre_emptionVulnerability = !(bearer->qos.arp.pre_emption_vulnerability); + + if (bearer->qos.mbr.downlink || bearer->qos.mbr.uplink || + bearer->qos.gbr.downlink || bearer->qos.gbr.uplink) { + if (bearer->qos.mbr.downlink == 0) + bearer->qos.mbr.downlink = MAX_BIT_RATE; + if (bearer->qos.mbr.uplink == 0) + bearer->qos.mbr.uplink = MAX_BIT_RATE; + if (bearer->qos.gbr.downlink == 0) + bearer->qos.gbr.downlink = MAX_BIT_RATE; + if (bearer->qos.gbr.uplink == 0) + bearer->qos.gbr.uplink = MAX_BIT_RATE; + + gbrQosInformation = + CALLOC(1, sizeof(NGAP_GBR_QosInformation_t)); + asn_uint642INTEGER(&gbrQosInformation->e_RAB_MaximumBitrateDL, + bearer->qos.mbr.downlink); + asn_uint642INTEGER(&gbrQosInformation->e_RAB_MaximumBitrateUL, + bearer->qos.mbr.uplink); + asn_uint642INTEGER(&gbrQosInformation->e_RAB_GuaranteedBitrateDL, + bearer->qos.gbr.downlink); + asn_uint642INTEGER(&gbrQosInformation->e_RAB_GuaranteedBitrateUL, + bearer->qos.gbr.uplink); + e_rab->e_RABLevelQoSParameters.gbrQosInformation = gbrQosInformation; + } + + nasPdu = &e_rab->nAS_PDU; + nasPdu->size = esmbuf->len; + nasPdu->buf = CALLOC(nasPdu->size, sizeof(uint8_t)); + memcpy(nasPdu->buf, esmbuf->data, nasPdu->size); + ogs_pkbuf_free(esmbuf); + + return ogs_ngap_encode(&pdu); +} + +ogs_pkbuf_t *ngap_build_e_rab_release_command( + amf_bearer_t *bearer, ogs_pkbuf_t *esmbuf, + NGAP_Cause_PR group, long cause) +{ + NGAP_NGAP_PDU_t pdu; + NGAP_InitiatingMessage_t *initiatingMessage = NULL; + NGAP_E_RABReleaseCommand_t *E_RABReleaseCommand = NULL; + + NGAP_E_RABReleaseCommandIEs_t *ie = NULL; + NGAP_AMF_UE_NGAP_ID_t *AMF_UE_NGAP_ID = NULL; + NGAP_ENB_UE_NGAP_ID_t *ENB_UE_NGAP_ID = NULL; + NGAP_UEAggregateMaximumBitrate_t *UEAggregateMaximumBitrate = NULL; + NGAP_E_RABList_t *E_RABList = NULL; + NGAP_NAS_PDU_t *nasPdu = NULL; + + NGAP_E_RABItemIEs_t *item = NULL; + NGAP_E_RABItem_t *e_rab = NULL; + + amf_ue_t *amf_ue = NULL; + gnb_ue_t *gnb_ue = NULL; + ogs_diam_s6a_subscription_data_t *subscription_data = NULL; + + ogs_assert(esmbuf); + ogs_assert(bearer); + + amf_ue = bearer->amf_ue; + ogs_assert(amf_ue); + gnb_ue = amf_ue->gnb_ue; + ogs_assert(gnb_ue); + subscription_data = &amf_ue->subscription_data; + ogs_assert(subscription_data); + + ogs_debug("[AMF] E-RAB release command"); + + memset(&pdu, 0, sizeof (NGAP_NGAP_PDU_t)); + pdu.present = NGAP_NGAP_PDU_PR_initiatingMessage; + pdu.choice.initiatingMessage = + CALLOC(1, sizeof(NGAP_InitiatingMessage_t)); + + initiatingMessage = pdu.choice.initiatingMessage; + initiatingMessage->procedureCode = NGAP_ProcedureCode_id_E_RABRelease; + initiatingMessage->criticality = NGAP_Criticality_reject; + initiatingMessage->value.present = + NGAP_InitiatingMessage__value_PR_E_RABReleaseCommand; + + E_RABReleaseCommand = &initiatingMessage->value.choice.E_RABReleaseCommand; + + ie = CALLOC(1, sizeof(NGAP_E_RABReleaseCommandIEs_t)); + ASN_SEQUENCE_ADD(&E_RABReleaseCommand->protocolIEs, ie); + + ie->id = NGAP_ProtocolIE_ID_id_AMF_UE_NGAP_ID; + ie->criticality = NGAP_Criticality_reject; + ie->value.present = NGAP_E_RABReleaseCommandIEs__value_PR_AMF_UE_NGAP_ID; + + AMF_UE_NGAP_ID = &ie->value.choice.AMF_UE_NGAP_ID; + + ie = CALLOC(1, sizeof(NGAP_E_RABReleaseCommandIEs_t)); + ASN_SEQUENCE_ADD(&E_RABReleaseCommand->protocolIEs, ie); + + ie->id = NGAP_ProtocolIE_ID_id_eNB_UE_NGAP_ID; + ie->criticality = NGAP_Criticality_reject; + ie->value.present = NGAP_E_RABReleaseCommandIEs__value_PR_ENB_UE_NGAP_ID; + + ENB_UE_NGAP_ID = &ie->value.choice.ENB_UE_NGAP_ID; + + ie = CALLOC(1, sizeof(NGAP_E_RABReleaseCommandIEs_t)); + ASN_SEQUENCE_ADD(&E_RABReleaseCommand->protocolIEs, ie); + + ie->id = NGAP_ProtocolIE_ID_id_uEaggregateMaximumBitrate; + ie->criticality = NGAP_Criticality_reject; + ie->value.present = + NGAP_E_RABReleaseCommandIEs__value_PR_UEAggregateMaximumBitrate; + + UEAggregateMaximumBitrate = &ie->value.choice.UEAggregateMaximumBitrate; + + ie = CALLOC(1, sizeof(NGAP_E_RABReleaseCommandIEs_t)); + ASN_SEQUENCE_ADD(&E_RABReleaseCommand->protocolIEs, ie); + + ie->id = NGAP_ProtocolIE_ID_id_E_RABToBeReleasedList; + ie->criticality = NGAP_Criticality_ignore; + ie->value.present = NGAP_E_RABReleaseCommandIEs__value_PR_E_RABList; + + E_RABList = &ie->value.choice.E_RABList; + + ie = CALLOC(1, sizeof(NGAP_E_RABReleaseCommandIEs_t)); + ASN_SEQUENCE_ADD(&E_RABReleaseCommand->protocolIEs, ie); + + ie->id = NGAP_ProtocolIE_ID_id_NAS_PDU; + ie->criticality = NGAP_Criticality_ignore; + ie->value.present = NGAP_E_RABReleaseCommandIEs__value_PR_NAS_PDU; + + nasPdu = &ie->value.choice.NAS_PDU; + + ogs_debug(" ENB_UE_NGAP_ID[%d] AMF_UE_NGAP_ID[%d]", + gnb_ue->gnb_ue_ngap_id, gnb_ue->amf_ue_ngap_id); + + *AMF_UE_NGAP_ID = gnb_ue->amf_ue_ngap_id; + *ENB_UE_NGAP_ID = gnb_ue->gnb_ue_ngap_id; + + asn_uint642INTEGER( + &UEAggregateMaximumBitrate->uEaggregateMaximumBitRateUL, + subscription_data->ambr.uplink); + asn_uint642INTEGER( + &UEAggregateMaximumBitrate->uEaggregateMaximumBitRateDL, + subscription_data->ambr.downlink); + + item = CALLOC(1, sizeof(NGAP_E_RABItemIEs_t)); + ASN_SEQUENCE_ADD(&E_RABList->list, item); + + item->id = NGAP_ProtocolIE_ID_id_E_RABItem; + item->criticality = NGAP_Criticality_ignore; + item->value.present = NGAP_E_RABItemIEs__value_PR_E_RABItem; + + e_rab = &item->value.choice.E_RABItem; + + e_rab->e_RAB_ID = bearer->ebi; + e_rab->cause.present = group; + e_rab->cause.choice.radioNetwork = cause; + + ogs_debug(" EBI[%d] Gruop[%d] Cause[%d]", + bearer->ebi, group, (int)cause); + + nasPdu->size = esmbuf->len; + nasPdu->buf = CALLOC(nasPdu->size, sizeof(uint8_t)); + memcpy(nasPdu->buf, esmbuf->data, nasPdu->size); + ogs_pkbuf_free(esmbuf); + + return ogs_ngap_encode(&pdu); +} + +ogs_pkbuf_t *ngap_build_paging( + amf_ue_t *amf_ue, NGAP_CNDomain_t cn_domain) +{ + NGAP_NGAP_PDU_t pdu; + NGAP_InitiatingMessage_t *initiatingMessage = NULL; + NGAP_Paging_t *Paging = NULL; + + NGAP_PagingIEs_t *ie = NULL; + + NGAP_UEIdentityIndexValue_t *UEIdentityIndexValue = NULL; + NGAP_UEPagingID_t *UEPagingID = NULL; + NGAP_CNDomain_t *CNDomain = NULL; + NGAP_TAIList_t *TAIList = NULL; + + NGAP_TAIItemIEs_t *item = NULL; + NGAP_TAIItem_t *tai_item = NULL; + + uint16_t index_value; + uint64_t ue_imsi_value = 0; + int i = 0; + + ogs_assert(amf_ue); + + ogs_debug("[AMF] Paging"); + + memset(&pdu, 0, sizeof (NGAP_NGAP_PDU_t)); + pdu.present = NGAP_NGAP_PDU_PR_initiatingMessage; + pdu.choice.initiatingMessage = + CALLOC(1, sizeof(NGAP_InitiatingMessage_t)); + + initiatingMessage = pdu.choice.initiatingMessage; + initiatingMessage->procedureCode = NGAP_ProcedureCode_id_Paging; + initiatingMessage->criticality = NGAP_Criticality_ignore; + initiatingMessage->value.present = NGAP_InitiatingMessage__value_PR_Paging; + + Paging = &initiatingMessage->value.choice.Paging; + + ie = CALLOC(1, sizeof(NGAP_PagingIEs_t)); + ASN_SEQUENCE_ADD(&Paging->protocolIEs, ie); + + ie->id = NGAP_ProtocolIE_ID_id_UEIdentityIndexValue; + ie->criticality = NGAP_Criticality_ignore; + ie->value.present = NGAP_PagingIEs__value_PR_UEIdentityIndexValue; + + UEIdentityIndexValue = &ie->value.choice.UEIdentityIndexValue; + + ie = CALLOC(1, sizeof(NGAP_PagingIEs_t)); + ASN_SEQUENCE_ADD(&Paging->protocolIEs, ie); + + ie->id = NGAP_ProtocolIE_ID_id_UEPagingID; + ie->criticality = NGAP_Criticality_ignore; + ie->value.present = NGAP_PagingIEs__value_PR_UEPagingID; + + UEPagingID = &ie->value.choice.UEPagingID; + + ie = CALLOC(1, sizeof(NGAP_PagingIEs_t)); + ASN_SEQUENCE_ADD(&Paging->protocolIEs, ie); + + ie->id = NGAP_ProtocolIE_ID_id_CNDomain; + ie->criticality = NGAP_Criticality_ignore; + ie->value.present = NGAP_PagingIEs__value_PR_CNDomain; + + CNDomain = &ie->value.choice.CNDomain; + + ie = CALLOC(1, sizeof(NGAP_PagingIEs_t)); + ASN_SEQUENCE_ADD(&Paging->protocolIEs, ie); + + ie->id = NGAP_ProtocolIE_ID_id_TAIList; + ie->criticality = NGAP_Criticality_ignore; + ie->value.present = NGAP_PagingIEs__value_PR_TAIList; + + TAIList = &ie->value.choice.TAIList; + + /* Set UE Identity Index value : IMSI mod 4096 */ + UEIdentityIndexValue->size = 2; + UEIdentityIndexValue->buf = + CALLOC(UEIdentityIndexValue->size, sizeof(uint8_t)); + + /* Conver string to value */ + for (i = 0; i < strlen(amf_ue->imsi_bcd); i++) { + ue_imsi_value = ue_imsi_value*10 + (amf_ue->imsi_bcd[i] - '0'); + } + + /* index(10bit) = ue_imsi_value mod 1024 */ + index_value = ue_imsi_value % 1024; + UEIdentityIndexValue->buf[0] = index_value >> 2; + UEIdentityIndexValue->buf[1] = (index_value & 0x3f) << 6; + UEIdentityIndexValue->bits_unused = 6; + + /* Set Paging Identity */ + UEPagingID->present = NGAP_UEPagingID_PR_s_TMSI; + UEPagingID->choice.s_TMSI = + CALLOC(1, sizeof(NGAP_S_TMSI_t)); + ogs_asn_uint8_to_OCTET_STRING(amf_ue->guti.amf_code, + &UEPagingID->choice.s_TMSI->mMEC); + + ogs_asn_uint32_to_OCTET_STRING(amf_ue->guti.m_tmsi, + &UEPagingID->choice.s_TMSI->m_TMSI); + + ogs_debug(" AMF_CODE[%d] M_TMSI[0x%x]", + amf_ue->guti.amf_code, amf_ue->guti.m_tmsi); + ogs_debug(" CN_DOMAIN[%s]", + cn_domain == NGAP_CNDomain_cs ? "CS" : + cn_domain == NGAP_CNDomain_ps ? "PS" : "Unknown"); + + *CNDomain = cn_domain; + + item = CALLOC(1, sizeof(NGAP_TAIItemIEs_t)); + ASN_SEQUENCE_ADD(&TAIList->list, item); + + item->id = NGAP_ProtocolIE_ID_id_TAIItem; + item->criticality = NGAP_Criticality_ignore; + item->value.present = NGAP_TAIItemIEs__value_PR_TAIItem; + + tai_item = &item->value.choice.TAIItem; + + ogs_ngap_buffer_to_OCTET_STRING(&amf_ue->tai.plmn_id, sizeof(ogs_plmn_id_t), + &tai_item->tAI.pLMNidentity); + ogs_asn_uint16_to_OCTET_STRING(amf_ue->tai.tac, &tai_item->tAI.tAC); + + return ogs_ngap_encode(&pdu); +} + +ogs_pkbuf_t *ngap_build_amf_configuration_transfer( + NGAP_SONConfigurationTransfer_t *son_configuration_transfer) +{ + int rv; + + NGAP_NGAP_PDU_t pdu; + NGAP_InitiatingMessage_t *initiatingMessage = NULL; + NGAP_AMFConfigurationTransfer_t *AMFConfigurationTransfer = NULL; + + NGAP_AMFConfigurationTransferIEs_t *ie = NULL; + NGAP_SONConfigurationTransfer_t *SONConfigurationTransfer = NULL; + + ogs_assert(son_configuration_transfer); + + ogs_debug("[AMF] AMF Configuration Transfer"); + + memset(&pdu, 0, sizeof (NGAP_NGAP_PDU_t)); + pdu.present = NGAP_NGAP_PDU_PR_initiatingMessage; + pdu.choice.initiatingMessage = + CALLOC(1, sizeof(NGAP_InitiatingMessage_t)); + + initiatingMessage = pdu.choice.initiatingMessage; + initiatingMessage->procedureCode = + NGAP_ProcedureCode_id_AMFConfigurationTransfer; + initiatingMessage->criticality = NGAP_Criticality_ignore; + initiatingMessage->value.present = + NGAP_InitiatingMessage__value_PR_AMFConfigurationTransfer; + + AMFConfigurationTransfer = + &initiatingMessage->value.choice.AMFConfigurationTransfer; + + ie = CALLOC(1, sizeof(NGAP_AMFConfigurationTransferIEs_t)); + ASN_SEQUENCE_ADD(&AMFConfigurationTransfer->protocolIEs, ie); + + ie->id = NGAP_ProtocolIE_ID_id_SONConfigurationTransferMCT; + ie->criticality = NGAP_Criticality_ignore; + ie->value.present = + NGAP_AMFConfigurationTransferIEs__value_PR_SONConfigurationTransfer; + + SONConfigurationTransfer = &ie->value.choice.SONConfigurationTransfer; + + rv = ogs_asn_copy_ie(&asn_DEF_NGAP_SONConfigurationTransfer, + son_configuration_transfer, SONConfigurationTransfer); + ogs_assert(rv == OGS_OK); + + return ogs_ngap_encode(&pdu); +} + +ogs_pkbuf_t *ngap_build_path_switch_ack(amf_ue_t *amf_ue) +{ + NGAP_NGAP_PDU_t pdu; + NGAP_SuccessfulOutcome_t *successfulOutcome = NULL; + NGAP_PathSwitchRequestAcknowledge_t *PathSwitchRequestAcknowledge = NULL; + + NGAP_PathSwitchRequestAcknowledgeIEs_t *ie = NULL; + NGAP_AMF_UE_NGAP_ID_t *AMF_UE_NGAP_ID = NULL; + NGAP_ENB_UE_NGAP_ID_t *ENB_UE_NGAP_ID = NULL; + NGAP_SecurityContext_t *SecurityContext = NULL; + + gnb_ue_t *gnb_ue = NULL; + + ogs_assert(amf_ue); + gnb_ue = amf_ue->gnb_ue; + ogs_assert(gnb_ue); + + ogs_debug("[AMF] Path switch acknowledge"); + + memset(&pdu, 0, sizeof (NGAP_NGAP_PDU_t)); + pdu.present = NGAP_NGAP_PDU_PR_successfulOutcome; + pdu.choice.successfulOutcome = + CALLOC(1, sizeof(NGAP_SuccessfulOutcome_t)); + + successfulOutcome = pdu.choice.successfulOutcome; + successfulOutcome->procedureCode = NGAP_ProcedureCode_id_PathSwitchRequest; + successfulOutcome->criticality = NGAP_Criticality_reject; + successfulOutcome->value.present = + NGAP_SuccessfulOutcome__value_PR_PathSwitchRequestAcknowledge; + + PathSwitchRequestAcknowledge = + &successfulOutcome->value.choice.PathSwitchRequestAcknowledge; + + ie = CALLOC(1, sizeof(NGAP_PathSwitchRequestAcknowledgeIEs_t)); + ASN_SEQUENCE_ADD(&PathSwitchRequestAcknowledge->protocolIEs, ie); + + ie->id = NGAP_ProtocolIE_ID_id_AMF_UE_NGAP_ID; + ie->criticality = NGAP_Criticality_ignore; + ie->value.present = + NGAP_PathSwitchRequestAcknowledgeIEs__value_PR_AMF_UE_NGAP_ID; + + AMF_UE_NGAP_ID = &ie->value.choice.AMF_UE_NGAP_ID; + + ie = CALLOC(1, sizeof(NGAP_PathSwitchRequestAcknowledgeIEs_t)); + ASN_SEQUENCE_ADD(&PathSwitchRequestAcknowledge->protocolIEs, ie); + + ie->id = NGAP_ProtocolIE_ID_id_eNB_UE_NGAP_ID; + ie->criticality = NGAP_Criticality_ignore; + ie->value.present = + NGAP_PathSwitchRequestAcknowledgeIEs__value_PR_ENB_UE_NGAP_ID; + + ENB_UE_NGAP_ID = &ie->value.choice.ENB_UE_NGAP_ID; + + ie = CALLOC(1, sizeof(NGAP_PathSwitchRequestAcknowledgeIEs_t)); + ASN_SEQUENCE_ADD(&PathSwitchRequestAcknowledge->protocolIEs, ie); + + ie->id = NGAP_ProtocolIE_ID_id_SecurityContext; + ie->criticality = NGAP_Criticality_reject; + ie->value.present = + NGAP_PathSwitchRequestAcknowledgeIEs__value_PR_SecurityContext; + + SecurityContext = &ie->value.choice.SecurityContext; + + ogs_debug(" ENB_UE_NGAP_ID[%d] AMF_UE_NGAP_ID[%d]", + gnb_ue->gnb_ue_ngap_id, gnb_ue->amf_ue_ngap_id); + + *AMF_UE_NGAP_ID = gnb_ue->amf_ue_ngap_id; + *ENB_UE_NGAP_ID = gnb_ue->gnb_ue_ngap_id; + + SecurityContext->nextHopChainingCount = amf_ue->nhcc; + SecurityContext->nextHopParameter.size = OGS_SHA256_DIGEST_SIZE; + SecurityContext->nextHopParameter.buf = + CALLOC(SecurityContext->nextHopParameter.size, + sizeof(uint8_t)); + SecurityContext->nextHopParameter.bits_unused = 0; + memcpy(SecurityContext->nextHopParameter.buf, + amf_ue->nh, SecurityContext->nextHopParameter.size); + + return ogs_ngap_encode(&pdu); +} + +ogs_pkbuf_t *ngap_build_path_switch_failure( + uint32_t gnb_ue_ngap_id, uint32_t amf_ue_ngap_id, + NGAP_Cause_PR group, long cause) +{ + NGAP_NGAP_PDU_t pdu; + NGAP_UnsuccessfulOutcome_t *unsuccessfulOutcome = NULL; + NGAP_PathSwitchRequestFailure_t *PathSwitchRequestFailure = NULL; + + NGAP_PathSwitchRequestFailureIEs_t *ie = NULL; + NGAP_AMF_UE_NGAP_ID_t *AMF_UE_NGAP_ID = NULL; + NGAP_ENB_UE_NGAP_ID_t *ENB_UE_NGAP_ID = NULL; + NGAP_Cause_t *Cause = NULL; + + ogs_debug("[AMF] Path switch failure"); + + memset(&pdu, 0, sizeof (NGAP_NGAP_PDU_t)); + pdu.present = NGAP_NGAP_PDU_PR_unsuccessfulOutcome; + pdu.choice.unsuccessfulOutcome = + CALLOC(1, sizeof(NGAP_UnsuccessfulOutcome_t)); + + unsuccessfulOutcome = pdu.choice.unsuccessfulOutcome; + unsuccessfulOutcome->procedureCode = + NGAP_ProcedureCode_id_PathSwitchRequest; + unsuccessfulOutcome->criticality = NGAP_Criticality_reject; + unsuccessfulOutcome->value.present = + NGAP_UnsuccessfulOutcome__value_PR_PathSwitchRequestFailure; + + PathSwitchRequestFailure = + &unsuccessfulOutcome->value.choice.PathSwitchRequestFailure; + + ie = CALLOC(1, sizeof(NGAP_PathSwitchRequestFailureIEs_t)); + ASN_SEQUENCE_ADD(&PathSwitchRequestFailure->protocolIEs, ie); + + ie->id = NGAP_ProtocolIE_ID_id_AMF_UE_NGAP_ID; + ie->criticality = NGAP_Criticality_ignore; + ie->value.present = + NGAP_PathSwitchRequestFailureIEs__value_PR_AMF_UE_NGAP_ID; + + AMF_UE_NGAP_ID = &ie->value.choice.AMF_UE_NGAP_ID; + + ie = CALLOC(1, sizeof(NGAP_PathSwitchRequestFailureIEs_t)); + ASN_SEQUENCE_ADD(&PathSwitchRequestFailure->protocolIEs, ie); + + ie->id = NGAP_ProtocolIE_ID_id_eNB_UE_NGAP_ID; + ie->criticality = NGAP_Criticality_ignore; + ie->value.present = + NGAP_PathSwitchRequestFailureIEs__value_PR_ENB_UE_NGAP_ID; + + ENB_UE_NGAP_ID = &ie->value.choice.ENB_UE_NGAP_ID; + + ie = CALLOC(1, sizeof(NGAP_PathSwitchRequestFailureIEs_t)); + ASN_SEQUENCE_ADD(&PathSwitchRequestFailure->protocolIEs, ie); + + ie->id = NGAP_ProtocolIE_ID_id_Cause; + ie->criticality = NGAP_Criticality_ignore; + ie->value.present = NGAP_PathSwitchRequestFailureIEs__value_PR_Cause; + + Cause = &ie->value.choice.Cause; + + ogs_debug(" ENB_UE_NGAP_ID[%d] AMF_UE_NGAP_ID[%d]", + gnb_ue_ngap_id, amf_ue_ngap_id); + ogs_debug(" Group[%d] Cause[%d]", group, (int)cause); + + *AMF_UE_NGAP_ID = amf_ue_ngap_id; + *ENB_UE_NGAP_ID = gnb_ue_ngap_id; + Cause->present = group; + Cause->choice.radioNetwork = cause; + + return ogs_ngap_encode(&pdu); +} + +ogs_pkbuf_t *ngap_build_handover_command(gnb_ue_t *source_ue) +{ + int rv; + + NGAP_NGAP_PDU_t pdu; + NGAP_SuccessfulOutcome_t *successfulOutcome = NULL; + NGAP_HandoverCommand_t *HandoverCommand = NULL; + + NGAP_HandoverCommandIEs_t *ie = NULL; + NGAP_AMF_UE_NGAP_ID_t *AMF_UE_NGAP_ID = NULL; + NGAP_ENB_UE_NGAP_ID_t *ENB_UE_NGAP_ID = NULL; + NGAP_HandoverType_t *HandoverType = NULL; + NGAP_E_RABSubjecttoDataForwardingList_t + *E_RABSubjecttoDataForwardingList = NULL; + NGAP_Target_ToSource_TransparentContainer_t + *Target_ToSource_TransparentContainer = NULL; + + amf_ue_t *amf_ue = NULL; + amf_sess_t *sess = NULL; + amf_bearer_t *bearer = NULL; + + ogs_assert(source_ue); + amf_ue = source_ue->amf_ue; + + ogs_debug("[AMF] Handover command"); + + memset(&pdu, 0, sizeof (NGAP_NGAP_PDU_t)); + pdu.present = NGAP_NGAP_PDU_PR_successfulOutcome; + pdu.choice.successfulOutcome = + CALLOC(1, sizeof(NGAP_SuccessfulOutcome_t)); + + successfulOutcome = pdu.choice.successfulOutcome; + successfulOutcome->procedureCode = + NGAP_ProcedureCode_id_HandoverPreparation; + successfulOutcome->criticality = NGAP_Criticality_reject; + successfulOutcome->value.present = + NGAP_SuccessfulOutcome__value_PR_HandoverCommand; + + HandoverCommand = &successfulOutcome->value.choice.HandoverCommand; + ogs_assert(HandoverCommand); + + ie = CALLOC(1, sizeof(NGAP_HandoverCommandIEs_t)); + ASN_SEQUENCE_ADD(&HandoverCommand->protocolIEs, ie); + + ie->id = NGAP_ProtocolIE_ID_id_AMF_UE_NGAP_ID; + ie->criticality = NGAP_Criticality_reject; + ie->value.present = NGAP_HandoverCommandIEs__value_PR_AMF_UE_NGAP_ID; + + AMF_UE_NGAP_ID = &ie->value.choice.AMF_UE_NGAP_ID; + + ie = CALLOC(1, sizeof(NGAP_HandoverCommandIEs_t)); + ASN_SEQUENCE_ADD(&HandoverCommand->protocolIEs, ie); + + ie->id = NGAP_ProtocolIE_ID_id_eNB_UE_NGAP_ID; + ie->criticality = NGAP_Criticality_reject; + ie->value.present = NGAP_HandoverCommandIEs__value_PR_ENB_UE_NGAP_ID; + + ENB_UE_NGAP_ID = &ie->value.choice.ENB_UE_NGAP_ID; + + ie = CALLOC(1, sizeof(NGAP_HandoverCommandIEs_t)); + ASN_SEQUENCE_ADD(&HandoverCommand->protocolIEs, ie); + + ie->id = NGAP_ProtocolIE_ID_id_HandoverType; + ie->criticality = NGAP_Criticality_reject; + ie->value.present = NGAP_HandoverCommandIEs__value_PR_HandoverType; + + HandoverType = &ie->value.choice.HandoverType; + + *AMF_UE_NGAP_ID = source_ue->amf_ue_ngap_id; + *ENB_UE_NGAP_ID = source_ue->gnb_ue_ngap_id; + *HandoverType = source_ue->handover_type; + + ogs_debug(" ENB_UE_NGAP_ID[%d] AMF_UE_NGAP_ID[%d]", + source_ue->gnb_ue_ngap_id, source_ue->amf_ue_ngap_id); + + sess = amf_sess_first(amf_ue); + while (sess) { + bearer = amf_bearer_first(sess); + while (bearer) { + NGAP_E_RABDataForwardingItem_t *e_rab = NULL; + + if (AMF_HAVE_SGW_DL_INDIRECT_TUNNEL(bearer) || + AMF_HAVE_SGW_UL_INDIRECT_TUNNEL(bearer)) { + NGAP_E_RABDataForwardingItemIEs_t *item = NULL; + + if (E_RABSubjecttoDataForwardingList == NULL) { + ie = CALLOC(1, sizeof(NGAP_HandoverCommandIEs_t)); + ogs_assert(ie); + ASN_SEQUENCE_ADD(&HandoverCommand->protocolIEs, ie); + + ie->id = + NGAP_ProtocolIE_ID_id_E_RABSubjecttoDataForwardingList; + ie->criticality = NGAP_Criticality_ignore; + ie->value.present = + NGAP_HandoverCommandIEs__value_PR_E_RABSubjecttoDataForwardingList; + + E_RABSubjecttoDataForwardingList = + &ie->value.choice.E_RABSubjecttoDataForwardingList; + } + ogs_assert(E_RABSubjecttoDataForwardingList); + + item = CALLOC( + 1, sizeof(NGAP_E_RABDataForwardingItemIEs_t)); + ogs_assert(item); + ASN_SEQUENCE_ADD(&E_RABSubjecttoDataForwardingList->list, item); + + item->id = NGAP_ProtocolIE_ID_id_E_RABDataForwardingItem; + item->criticality = NGAP_Criticality_ignore; + item->value.present = + NGAP_E_RABDataForwardingItemIEs__value_PR_E_RABDataForwardingItem; + + e_rab = &item->value.choice.E_RABDataForwardingItem; + ogs_assert(e_rab); + + e_rab->e_RAB_ID = bearer->ebi; + } + + if (AMF_HAVE_SGW_DL_INDIRECT_TUNNEL(bearer)) { + ogs_assert(e_rab); + e_rab->dL_transportLayerAddress = + (NGAP_TransportLayerAddress_t *) + CALLOC(1, sizeof(NGAP_TransportLayerAddress_t)); + rv = ogs_asn_ip_to_BIT_STRING( + &bearer->sgw_dl_ip, e_rab->dL_transportLayerAddress); + ogs_assert(rv == OGS_OK); + + e_rab->dL_gTP_TEID = (NGAP_GTP_TEID_t *) + CALLOC(1, sizeof(NGAP_GTP_TEID_t)); + ogs_asn_uint32_to_OCTET_STRING( + bearer->sgw_dl_teid, e_rab->dL_gTP_TEID); + ogs_debug(" SGW-DL-TEID[%d]", bearer->sgw_dl_teid); + } + + if (AMF_HAVE_SGW_UL_INDIRECT_TUNNEL(bearer)) { + ogs_assert(e_rab); + e_rab->uL_TransportLayerAddress = + (NGAP_TransportLayerAddress_t *) + CALLOC(1, sizeof(NGAP_TransportLayerAddress_t)); + rv = ogs_asn_ip_to_BIT_STRING( + &bearer->sgw_ul_ip, e_rab->uL_TransportLayerAddress); + ogs_assert(rv == OGS_OK); + + e_rab->uL_GTP_TEID = (NGAP_GTP_TEID_t *) + CALLOC(1, sizeof(NGAP_GTP_TEID_t)); + ogs_asn_uint32_to_OCTET_STRING( + bearer->sgw_ul_teid, e_rab->uL_GTP_TEID); + ogs_debug(" SGW-UL-TEID[%d]", bearer->sgw_dl_teid); + } + + bearer = amf_bearer_next(bearer); + } + sess = amf_sess_next(sess); + } + + ie = CALLOC(1, sizeof(NGAP_HandoverCommandIEs_t)); + ASN_SEQUENCE_ADD(&HandoverCommand->protocolIEs, ie); + + ie->id = NGAP_ProtocolIE_ID_id_Target_ToSource_TransparentContainer; + ie->criticality = NGAP_Criticality_reject; + ie->value.present = + NGAP_HandoverCommandIEs__value_PR_Target_ToSource_TransparentContainer; + + Target_ToSource_TransparentContainer = + &ie->value.choice.Target_ToSource_TransparentContainer; + + ogs_ngap_buffer_to_OCTET_STRING(amf_ue->container.buf, + amf_ue->container.size, Target_ToSource_TransparentContainer); + + return ogs_ngap_encode(&pdu); +} + +ogs_pkbuf_t *ngap_build_handover_preparation_failure( + gnb_ue_t *source_ue, NGAP_Cause_t *cause) +{ + NGAP_NGAP_PDU_t pdu; + NGAP_UnsuccessfulOutcome_t *unsuccessfulOutcome = NULL; + NGAP_HandoverPreparationFailure_t *HandoverPreparationFailure = NULL; + + NGAP_HandoverPreparationFailureIEs_t *ie = NULL; + NGAP_AMF_UE_NGAP_ID_t *AMF_UE_NGAP_ID = NULL; + NGAP_ENB_UE_NGAP_ID_t *ENB_UE_NGAP_ID = NULL; + NGAP_Cause_t *Cause = NULL; + + ogs_assert(source_ue); + ogs_assert(cause); + + ogs_debug("[AMF] Handover preparation failure"); + + memset(&pdu, 0, sizeof (NGAP_NGAP_PDU_t)); + pdu.present = NGAP_NGAP_PDU_PR_unsuccessfulOutcome; + pdu.choice.unsuccessfulOutcome = + CALLOC(1, sizeof(NGAP_UnsuccessfulOutcome_t)); + + unsuccessfulOutcome = pdu.choice.unsuccessfulOutcome; + unsuccessfulOutcome->procedureCode = + NGAP_ProcedureCode_id_HandoverPreparation; + unsuccessfulOutcome->criticality = NGAP_Criticality_reject; + unsuccessfulOutcome->value.present = + NGAP_UnsuccessfulOutcome__value_PR_HandoverPreparationFailure; + + HandoverPreparationFailure = + &unsuccessfulOutcome->value.choice.HandoverPreparationFailure; + + ie = CALLOC(1, sizeof(NGAP_HandoverPreparationFailureIEs_t)); + ASN_SEQUENCE_ADD(&HandoverPreparationFailure->protocolIEs, ie); + + ie->id = NGAP_ProtocolIE_ID_id_AMF_UE_NGAP_ID; + ie->criticality = NGAP_Criticality_ignore; + ie->value.present = + NGAP_HandoverPreparationFailureIEs__value_PR_AMF_UE_NGAP_ID; + + AMF_UE_NGAP_ID = &ie->value.choice.AMF_UE_NGAP_ID; + + ie = CALLOC(1, sizeof(NGAP_HandoverPreparationFailureIEs_t)); + ASN_SEQUENCE_ADD(&HandoverPreparationFailure->protocolIEs, ie); + + ie->id = NGAP_ProtocolIE_ID_id_eNB_UE_NGAP_ID; + ie->criticality = NGAP_Criticality_ignore; + ie->value.present = + NGAP_HandoverPreparationFailureIEs__value_PR_ENB_UE_NGAP_ID; + + ENB_UE_NGAP_ID = &ie->value.choice.ENB_UE_NGAP_ID; + + ie = CALLOC(1, sizeof(NGAP_HandoverPreparationFailureIEs_t)); + ASN_SEQUENCE_ADD(&HandoverPreparationFailure->protocolIEs, ie); + + ie->id = NGAP_ProtocolIE_ID_id_Cause; + ie->criticality = NGAP_Criticality_ignore; + ie->value.present = NGAP_HandoverPreparationFailureIEs__value_PR_Cause; + + Cause = &ie->value.choice.Cause; + + ogs_debug(" ENB_UE_NGAP_ID[%d] AMF_UE_NGAP_ID[%d]", + source_ue->gnb_ue_ngap_id, source_ue->amf_ue_ngap_id); + ogs_debug(" Group[%d] Cause[%d]", + cause->present, (int)cause->choice.radioNetwork); + + *AMF_UE_NGAP_ID = source_ue->amf_ue_ngap_id; + *ENB_UE_NGAP_ID = source_ue->gnb_ue_ngap_id; + Cause->present = cause->present; + Cause->choice.radioNetwork = cause->choice.radioNetwork; + + return ogs_ngap_encode(&pdu); +} + +ogs_pkbuf_t *ngap_build_handover_request( + amf_ue_t *amf_ue, gnb_ue_t *target_ue, + NGAP_ENB_UE_NGAP_ID_t *gnb_ue_ngap_id, + NGAP_AMF_UE_NGAP_ID_t *amf_ue_ngap_id, + NGAP_HandoverType_t *handovertype, + NGAP_Cause_t *cause, + NGAP_Source_ToTarget_TransparentContainer_t + *source_totarget_transparentContainer) +{ + int rv; + + NGAP_NGAP_PDU_t pdu; + NGAP_InitiatingMessage_t *initiatingMessage = NULL; + NGAP_HandoverRequest_t *HandoverRequest = NULL; + + NGAP_HandoverRequestIEs_t *ie = NULL; + NGAP_AMF_UE_NGAP_ID_t *AMF_UE_NGAP_ID = NULL; + NGAP_HandoverType_t *HandoverType = NULL; + NGAP_Cause_t *Cause = NULL; + NGAP_UEAggregateMaximumBitrate_t *UEAggregateMaximumBitrate = NULL; + NGAP_E_RABToBeSetupListHOReq_t *E_RABToBeSetupListHOReq = NULL; + NGAP_Source_ToTarget_TransparentContainer_t + *Source_ToTarget_TransparentContainer = NULL; + NGAP_UESecurityCapabilities_t *UESecurityCapabilities = NULL; + NGAP_SecurityContext_t *SecurityContext = NULL; + + amf_sess_t *sess = NULL; + amf_bearer_t *bearer = NULL; + ogs_diam_s6a_subscription_data_t *subscription_data = NULL; + + ogs_assert(handovertype); + ogs_assert(cause); + ogs_assert(source_totarget_transparentContainer); + + ogs_assert(target_ue); + ogs_assert(amf_ue); + subscription_data = &amf_ue->subscription_data; + ogs_assert(subscription_data); + + ogs_debug("[AMF] Handover request"); + + memset(&pdu, 0, sizeof (NGAP_NGAP_PDU_t)); + pdu.present = NGAP_NGAP_PDU_PR_initiatingMessage; + pdu.choice.initiatingMessage = + CALLOC(1, sizeof(NGAP_InitiatingMessage_t)); + + initiatingMessage = pdu.choice.initiatingMessage; + initiatingMessage->procedureCode = + NGAP_ProcedureCode_id_HandoverResourceAllocation; + initiatingMessage->criticality = NGAP_Criticality_reject; + initiatingMessage->value.present = + NGAP_InitiatingMessage__value_PR_HandoverRequest; + + HandoverRequest = &initiatingMessage->value.choice.HandoverRequest; + + ie = CALLOC(1, sizeof(NGAP_HandoverRequestIEs_t)); + ASN_SEQUENCE_ADD(&HandoverRequest->protocolIEs, ie); + + ie->id = NGAP_ProtocolIE_ID_id_AMF_UE_NGAP_ID; + ie->criticality = NGAP_Criticality_reject; + ie->value.present = NGAP_HandoverRequestIEs__value_PR_AMF_UE_NGAP_ID; + + AMF_UE_NGAP_ID = &ie->value.choice.AMF_UE_NGAP_ID; + + ie = CALLOC(1, sizeof(NGAP_HandoverRequestIEs_t)); + ASN_SEQUENCE_ADD(&HandoverRequest->protocolIEs, ie); + + ie->id = NGAP_ProtocolIE_ID_id_HandoverType; + ie->criticality = NGAP_Criticality_reject; + ie->value.present = NGAP_HandoverRequestIEs__value_PR_HandoverType; + + HandoverType = &ie->value.choice.HandoverType; + + ie = CALLOC(1, sizeof(NGAP_HandoverRequestIEs_t)); + ASN_SEQUENCE_ADD(&HandoverRequest->protocolIEs, ie); + + ie->id = NGAP_ProtocolIE_ID_id_Cause; + ie->criticality = NGAP_Criticality_ignore; + ie->value.present = NGAP_HandoverRequestIEs__value_PR_Cause; + + Cause = &ie->value.choice.Cause; + + ie = CALLOC(1, sizeof(NGAP_HandoverRequestIEs_t)); + ASN_SEQUENCE_ADD(&HandoverRequest->protocolIEs, ie); + + ie->id = NGAP_ProtocolIE_ID_id_uEaggregateMaximumBitrate; + ie->criticality = NGAP_Criticality_reject; + ie->value.present = + NGAP_HandoverRequestIEs__value_PR_UEAggregateMaximumBitrate; + + UEAggregateMaximumBitrate = &ie->value.choice.UEAggregateMaximumBitrate; + + ie = CALLOC(1, sizeof(NGAP_HandoverRequestIEs_t)); + ASN_SEQUENCE_ADD(&HandoverRequest->protocolIEs, ie); + + ie->id = NGAP_ProtocolIE_ID_id_E_RABToBeSetupListHOReq; + ie->criticality = NGAP_Criticality_reject; + ie->value.present = + NGAP_HandoverRequestIEs__value_PR_E_RABToBeSetupListHOReq; + + E_RABToBeSetupListHOReq = &ie->value.choice.E_RABToBeSetupListHOReq; + + ie = CALLOC(1, sizeof(NGAP_HandoverRequestIEs_t)); + ASN_SEQUENCE_ADD(&HandoverRequest->protocolIEs, ie); + + ie->id = NGAP_ProtocolIE_ID_id_Source_ToTarget_TransparentContainer; + ie->criticality = NGAP_Criticality_reject; + ie->value.present = + NGAP_HandoverRequestIEs__value_PR_Source_ToTarget_TransparentContainer; + + Source_ToTarget_TransparentContainer = + &ie->value.choice.Source_ToTarget_TransparentContainer; + + ie = CALLOC(1, sizeof(NGAP_HandoverRequestIEs_t)); + ASN_SEQUENCE_ADD(&HandoverRequest->protocolIEs, ie); + + ie->id = NGAP_ProtocolIE_ID_id_UESecurityCapabilities; + ie->criticality = NGAP_Criticality_reject; + ie->value.present = + NGAP_HandoverRequestIEs__value_PR_UESecurityCapabilities; + + UESecurityCapabilities = &ie->value.choice.UESecurityCapabilities; + + ie = CALLOC(1, sizeof(NGAP_HandoverRequestIEs_t)); + ASN_SEQUENCE_ADD(&HandoverRequest->protocolIEs, ie); + + ie->id = NGAP_ProtocolIE_ID_id_SecurityContext; + ie->criticality = NGAP_Criticality_reject; + ie->value.present = + NGAP_HandoverRequestIEs__value_PR_SecurityContext; + + SecurityContext = &ie->value.choice.SecurityContext; + + *AMF_UE_NGAP_ID = target_ue->amf_ue_ngap_id; + *HandoverType = *handovertype; + Cause->present = cause->present; + Cause->choice.radioNetwork = cause->choice.radioNetwork; + + asn_uint642INTEGER( + &UEAggregateMaximumBitrate->uEaggregateMaximumBitRateUL, + subscription_data->ambr.uplink); + asn_uint642INTEGER( + &UEAggregateMaximumBitrate->uEaggregateMaximumBitRateDL, + subscription_data->ambr.downlink); + + sess = amf_sess_first(amf_ue); + while (sess) { + bearer = amf_bearer_first(sess); + while (bearer) { + NGAP_E_RABToBeSetupItemHOReqIEs_t *item = NULL; + NGAP_E_RABToBeSetupItemHOReq_t *e_rab = NULL; + NGAP_GBR_QosInformation_t *gbrQosInformation = NULL; + + item = CALLOC(1, sizeof(NGAP_E_RABToBeSetupItemHOReqIEs_t)); + ASN_SEQUENCE_ADD(&E_RABToBeSetupListHOReq->list, item); + + item->id = NGAP_ProtocolIE_ID_id_E_RABToBeSetupItemHOReq; + item->criticality = NGAP_Criticality_reject; + item->value.present = + NGAP_E_RABToBeSetupItemHOReqIEs__value_PR_E_RABToBeSetupItemHOReq; + + e_rab = &item->value.choice.E_RABToBeSetupItemHOReq; + + e_rab->e_RAB_ID = bearer->ebi; + e_rab->e_RABlevelQosParameters.qCI = bearer->qos.qci; + + e_rab->e_RABlevelQosParameters.allocationRetentionPriority. + priorityLevel = bearer->qos.arp.priority_level; + e_rab->e_RABlevelQosParameters.allocationRetentionPriority. + pre_emptionCapability = + !(bearer->qos.arp.pre_emption_capability); + e_rab->e_RABlevelQosParameters.allocationRetentionPriority. + pre_emptionVulnerability = + !(bearer->qos.arp.pre_emption_vulnerability); + + if (bearer->qos.mbr.downlink || bearer->qos.mbr.uplink || + bearer->qos.gbr.downlink || bearer->qos.gbr.uplink) { + if (bearer->qos.mbr.downlink == 0) + bearer->qos.mbr.downlink = MAX_BIT_RATE; + if (bearer->qos.mbr.uplink == 0) + bearer->qos.mbr.uplink = MAX_BIT_RATE; + if (bearer->qos.gbr.downlink == 0) + bearer->qos.gbr.downlink = MAX_BIT_RATE; + if (bearer->qos.gbr.uplink == 0) + bearer->qos.gbr.uplink = MAX_BIT_RATE; + + gbrQosInformation = + CALLOC(1, sizeof(struct NGAP_GBR_QosInformation)); + asn_uint642INTEGER(&gbrQosInformation->e_RAB_MaximumBitrateDL, + bearer->qos.mbr.downlink); + asn_uint642INTEGER(&gbrQosInformation->e_RAB_MaximumBitrateUL, + bearer->qos.mbr.uplink); + asn_uint642INTEGER(&gbrQosInformation-> + e_RAB_GuaranteedBitrateDL, bearer->qos.gbr.downlink); + asn_uint642INTEGER(&gbrQosInformation-> + e_RAB_GuaranteedBitrateUL, bearer->qos.gbr.uplink); + e_rab->e_RABlevelQosParameters.gbrQosInformation = + gbrQosInformation; + } + + rv = ogs_asn_ip_to_BIT_STRING( + &bearer->sgw_s1u_ip, &e_rab->transportLayerAddress); + ogs_assert(rv == OGS_OK); + ogs_asn_uint32_to_OCTET_STRING( + bearer->sgw_s1u_teid, &e_rab->gTP_TEID); + ogs_debug(" SGW-NGU-TEID[%d]", bearer->sgw_s1u_teid); + + bearer = amf_bearer_next(bearer); + } + sess = amf_sess_next(sess); + } + + ogs_ngap_buffer_to_OCTET_STRING( + source_totarget_transparentContainer->buf, + source_totarget_transparentContainer->size, + Source_ToTarget_TransparentContainer); + + UESecurityCapabilities->encryptionAlgorithms.size = 2; + UESecurityCapabilities->encryptionAlgorithms.buf = + CALLOC(UESecurityCapabilities->encryptionAlgorithms.size, + sizeof(uint8_t)); + UESecurityCapabilities->encryptionAlgorithms.bits_unused = 0; + UESecurityCapabilities->encryptionAlgorithms.buf[0] = + (amf_ue->ue_network_capability.eea << 1); + + UESecurityCapabilities->integrityProtectionAlgorithms.size = 2; + UESecurityCapabilities->integrityProtectionAlgorithms.buf = + CALLOC(UESecurityCapabilities-> + integrityProtectionAlgorithms.size, sizeof(uint8_t)); + UESecurityCapabilities->integrityProtectionAlgorithms.bits_unused = 0; + UESecurityCapabilities->integrityProtectionAlgorithms.buf[0] = + (amf_ue->ue_network_capability.eia << 1); + + SecurityContext->nextHopChainingCount = amf_ue->nhcc; + SecurityContext->nextHopParameter.size = OGS_SHA256_DIGEST_SIZE; + SecurityContext->nextHopParameter.buf = + CALLOC(SecurityContext->nextHopParameter.size, + sizeof(uint8_t)); + SecurityContext->nextHopParameter.bits_unused = 0; + memcpy(SecurityContext->nextHopParameter.buf, + amf_ue->nh, SecurityContext->nextHopParameter.size); + + return ogs_ngap_encode(&pdu); +} + +ogs_pkbuf_t *ngap_build_handover_cancel_ack(gnb_ue_t *source_ue) +{ + NGAP_NGAP_PDU_t pdu; + NGAP_SuccessfulOutcome_t *successfulOutcome = NULL; + NGAP_HandoverCancelAcknowledge_t *HandoverCancelAcknowledge = NULL; + + NGAP_HandoverCancelAcknowledgeIEs_t *ie = NULL; + NGAP_AMF_UE_NGAP_ID_t *AMF_UE_NGAP_ID = NULL; + NGAP_ENB_UE_NGAP_ID_t *ENB_UE_NGAP_ID = NULL; + + ogs_assert(source_ue); + + ogs_debug("[AMF] Handover cancel acknowledge"); + + memset(&pdu, 0, sizeof (NGAP_NGAP_PDU_t)); + pdu.present = NGAP_NGAP_PDU_PR_successfulOutcome; + pdu.choice.successfulOutcome = + CALLOC(1, sizeof(NGAP_SuccessfulOutcome_t)); + + successfulOutcome = pdu.choice.successfulOutcome; + successfulOutcome->procedureCode = NGAP_ProcedureCode_id_HandoverCancel; + successfulOutcome->criticality = NGAP_Criticality_reject; + successfulOutcome->value.present = + NGAP_SuccessfulOutcome__value_PR_HandoverCancelAcknowledge; + + HandoverCancelAcknowledge = + &successfulOutcome->value.choice.HandoverCancelAcknowledge; + + ie = CALLOC(1, sizeof(NGAP_HandoverCancelAcknowledgeIEs_t)); + ASN_SEQUENCE_ADD(&HandoverCancelAcknowledge->protocolIEs, ie); + + ie->id = NGAP_ProtocolIE_ID_id_AMF_UE_NGAP_ID; + ie->criticality = NGAP_Criticality_ignore; + ie->value.present = + NGAP_HandoverCancelAcknowledgeIEs__value_PR_AMF_UE_NGAP_ID; + + AMF_UE_NGAP_ID = &ie->value.choice.AMF_UE_NGAP_ID; + + ie = CALLOC(1, sizeof(NGAP_HandoverCancelAcknowledgeIEs_t)); + ASN_SEQUENCE_ADD(&HandoverCancelAcknowledge->protocolIEs, ie); + + ie->id = NGAP_ProtocolIE_ID_id_eNB_UE_NGAP_ID; + ie->criticality = NGAP_Criticality_ignore; + ie->value.present = + NGAP_HandoverCancelAcknowledgeIEs__value_PR_ENB_UE_NGAP_ID; + + ENB_UE_NGAP_ID = &ie->value.choice.ENB_UE_NGAP_ID; + + *AMF_UE_NGAP_ID = source_ue->amf_ue_ngap_id; + *ENB_UE_NGAP_ID = source_ue->gnb_ue_ngap_id; + + ogs_debug(" Source : ENB_UE_NGAP_ID[%d] AMF_UE_NGAP_ID[%d]", + source_ue->gnb_ue_ngap_id, source_ue->amf_ue_ngap_id); + + return ogs_ngap_encode(&pdu); +} + +ogs_pkbuf_t *ngap_build_amf_status_transfer( + gnb_ue_t *target_ue, + NGAP_ENB_StatusTransfer_TransparentContainer_t + *gnb_statustransfer_transparentContainer) +{ + int rv; + + NGAP_NGAP_PDU_t pdu; + NGAP_InitiatingMessage_t *initiatingMessage = NULL; + NGAP_AMFStatusTransfer_t *AMFStatusTransfer = NULL; + + NGAP_AMFStatusTransferIEs_t *ie = NULL; + NGAP_AMF_UE_NGAP_ID_t *AMF_UE_NGAP_ID = NULL; + NGAP_ENB_UE_NGAP_ID_t *ENB_UE_NGAP_ID = NULL; + NGAP_ENB_StatusTransfer_TransparentContainer_t + *ENB_StatusTransfer_TransparentContainer = NULL; + + ogs_assert(target_ue); + ogs_assert(gnb_statustransfer_transparentContainer); + + ogs_debug("[AMF] AMF status transfer"); + + memset(&pdu, 0, sizeof (NGAP_NGAP_PDU_t)); + pdu.present = NGAP_NGAP_PDU_PR_initiatingMessage; + pdu.choice.initiatingMessage = + CALLOC(1, sizeof(NGAP_InitiatingMessage_t)); + + initiatingMessage = pdu.choice.initiatingMessage; + initiatingMessage->procedureCode = NGAP_ProcedureCode_id_AMFStatusTransfer; + initiatingMessage->criticality = NGAP_Criticality_ignore; + initiatingMessage->value.present = + NGAP_InitiatingMessage__value_PR_AMFStatusTransfer; + + AMFStatusTransfer = &initiatingMessage->value.choice.AMFStatusTransfer; + + ie = CALLOC(1, sizeof(NGAP_AMFStatusTransferIEs_t)); + ASN_SEQUENCE_ADD(&AMFStatusTransfer->protocolIEs, ie); + + ie->id = NGAP_ProtocolIE_ID_id_AMF_UE_NGAP_ID; + ie->criticality = NGAP_Criticality_reject; + ie->value.present = NGAP_AMFStatusTransferIEs__value_PR_AMF_UE_NGAP_ID; + + AMF_UE_NGAP_ID = &ie->value.choice.AMF_UE_NGAP_ID; + + ie = CALLOC(1, sizeof(NGAP_AMFStatusTransferIEs_t)); + ASN_SEQUENCE_ADD(&AMFStatusTransfer->protocolIEs, ie); + + ie->id = NGAP_ProtocolIE_ID_id_eNB_UE_NGAP_ID; + ie->criticality = NGAP_Criticality_reject; + ie->value.present = NGAP_AMFStatusTransferIEs__value_PR_ENB_UE_NGAP_ID; + + ENB_UE_NGAP_ID = &ie->value.choice.ENB_UE_NGAP_ID; + + ie = CALLOC(1, sizeof(NGAP_AMFStatusTransferIEs_t)); + ASN_SEQUENCE_ADD(&AMFStatusTransfer->protocolIEs, ie); + + ie->id = NGAP_ProtocolIE_ID_id_eNB_StatusTransfer_TransparentContainer; + ie->criticality = NGAP_Criticality_reject; + ie->value.present = + NGAP_AMFStatusTransferIEs__value_PR_ENB_StatusTransfer_TransparentContainer; + + ENB_StatusTransfer_TransparentContainer = + &ie->value.choice.ENB_StatusTransfer_TransparentContainer; + + *AMF_UE_NGAP_ID = target_ue->amf_ue_ngap_id; + *ENB_UE_NGAP_ID = target_ue->gnb_ue_ngap_id; + + ogs_debug(" Target : ENB_UE_NGAP_ID[%d] AMF_UE_NGAP_ID[%d]", + target_ue->gnb_ue_ngap_id, target_ue->amf_ue_ngap_id); + + rv = ogs_asn_copy_ie( + &asn_DEF_NGAP_ENB_StatusTransfer_TransparentContainer, + gnb_statustransfer_transparentContainer, + ENB_StatusTransfer_TransparentContainer); + ogs_assert(rv == OGS_OK); + + return ogs_ngap_encode(&pdu); +} + +ogs_pkbuf_t *ngap_build_error_indication( + NGAP_AMF_UE_NGAP_ID_t *amf_ue_ngap_id, + NGAP_ENB_UE_NGAP_ID_t *gnb_ue_ngap_id, + NGAP_Cause_PR group, long cause) +{ + NGAP_NGAP_PDU_t pdu; + NGAP_InitiatingMessage_t *initiatingMessage = NULL; + NGAP_ErrorIndication_t *ErrorIndication = NULL; + + NGAP_ErrorIndicationIEs_t *ie = NULL; + NGAP_AMF_UE_NGAP_ID_t *AMF_UE_NGAP_ID = NULL; + NGAP_ENB_UE_NGAP_ID_t *ENB_UE_NGAP_ID = NULL; + NGAP_Cause_t *Cause = NULL; + + ogs_debug("[AMF] Error Indication"); + + memset(&pdu, 0, sizeof (NGAP_NGAP_PDU_t)); + pdu.present = NGAP_NGAP_PDU_PR_initiatingMessage; + pdu.choice.initiatingMessage = + CALLOC(1, sizeof(NGAP_InitiatingMessage_t)); + + initiatingMessage = pdu.choice.initiatingMessage; + initiatingMessage->procedureCode = NGAP_ProcedureCode_id_ErrorIndication; + initiatingMessage->criticality = NGAP_Criticality_ignore; + initiatingMessage->value.present = + NGAP_InitiatingMessage__value_PR_ErrorIndication; + + ErrorIndication = &initiatingMessage->value.choice.ErrorIndication; + + if (amf_ue_ngap_id) { + ie = CALLOC(1, sizeof(NGAP_ErrorIndicationIEs_t)); + ASN_SEQUENCE_ADD(&ErrorIndication->protocolIEs, ie); + + ie->id = NGAP_ProtocolIE_ID_id_AMF_UE_NGAP_ID; + ie->criticality = NGAP_Criticality_ignore; + ie->value.present = NGAP_ErrorIndicationIEs__value_PR_AMF_UE_NGAP_ID; + + AMF_UE_NGAP_ID = &ie->value.choice.AMF_UE_NGAP_ID; + + *AMF_UE_NGAP_ID = *amf_ue_ngap_id; + ogs_debug(" AMF_UE_NGAP_ID[%d]", (int)*amf_ue_ngap_id); + } + + if (gnb_ue_ngap_id) { + ie = CALLOC(1, sizeof(NGAP_ErrorIndicationIEs_t)); + ASN_SEQUENCE_ADD(&ErrorIndication->protocolIEs, ie); + + ie->id = NGAP_ProtocolIE_ID_id_eNB_UE_NGAP_ID; + ie->criticality = NGAP_Criticality_ignore; + ie->value.present = NGAP_ErrorIndicationIEs__value_PR_ENB_UE_NGAP_ID; + + ENB_UE_NGAP_ID = &ie->value.choice.ENB_UE_NGAP_ID; + + *ENB_UE_NGAP_ID = *gnb_ue_ngap_id; + ogs_debug(" ENB_UE_NGAP_ID[%d]", (int)*gnb_ue_ngap_id); + } + + ie = CALLOC(1, sizeof(NGAP_ErrorIndicationIEs_t)); + ASN_SEQUENCE_ADD(&ErrorIndication->protocolIEs, ie); + + ie->id = NGAP_ProtocolIE_ID_id_Cause; + ie->criticality = NGAP_Criticality_ignore; + ie->value.present = NGAP_ErrorIndicationIEs__value_PR_Cause; + + Cause = &ie->value.choice.Cause; + + Cause->present = group; + Cause->choice.radioNetwork = cause; + + ogs_debug(" Group[%d] Cause[%d]", + Cause->present, (int)Cause->choice.radioNetwork); + + return ogs_ngap_encode(&pdu); +} + +ogs_pkbuf_t *ngap_build_s1_reset( + NGAP_Cause_PR group, long cause, + NGAP_UE_associatedLogicalNG_ConnectionListRes_t *partOfNG_Interface) +{ + NGAP_NGAP_PDU_t pdu; + NGAP_InitiatingMessage_t *initiatingMessage = NULL; + NGAP_Reset_t *Reset = NULL; + + NGAP_ResetIEs_t *ie = NULL; + NGAP_Cause_t *Cause = NULL; + NGAP_ResetType_t *ResetType = NULL; + + ogs_debug("[AMF] Reset"); + + memset(&pdu, 0, sizeof (NGAP_NGAP_PDU_t)); + pdu.present = NGAP_NGAP_PDU_PR_initiatingMessage; + pdu.choice.initiatingMessage = + CALLOC(1, sizeof(NGAP_InitiatingMessage_t)); + + initiatingMessage = pdu.choice.initiatingMessage; + initiatingMessage->procedureCode = NGAP_ProcedureCode_id_Reset; + initiatingMessage->criticality = NGAP_Criticality_ignore; + initiatingMessage->value.present = + NGAP_InitiatingMessage__value_PR_Reset; + + Reset = &initiatingMessage->value.choice.Reset; + + ie = CALLOC(1, sizeof(NGAP_ResetIEs_t)); + ASN_SEQUENCE_ADD(&Reset->protocolIEs, ie); + + ie->id = NGAP_ProtocolIE_ID_id_Cause; + ie->criticality = NGAP_Criticality_ignore; + ie->value.present = NGAP_ResetIEs__value_PR_Cause; + + Cause = &ie->value.choice.Cause; + + ie = CALLOC(1, sizeof(NGAP_ResetIEs_t)); + ASN_SEQUENCE_ADD(&Reset->protocolIEs, ie); + + ie->id = NGAP_ProtocolIE_ID_id_ResetType; + ie->criticality = NGAP_Criticality_reject; + ie->value.present = NGAP_ResetIEs__value_PR_ResetType; + + ResetType = &ie->value.choice.ResetType; + + Cause->present = group; + Cause->choice.radioNetwork = cause; + + ogs_debug(" Group[%d] Cause[%d] partOfNG_Interface[%p]", + Cause->present, (int)Cause->choice.radioNetwork, partOfNG_Interface); + + if (partOfNG_Interface) { + ResetType->present = NGAP_ResetType_PR_partOfNG_Interface; + ResetType->choice.partOfNG_Interface = partOfNG_Interface; + } else { + ResetType->present = NGAP_ResetType_PR_s1_Interface; + ResetType->choice.s1_Interface = NGAP_ResetAll_reset_all; + } + + return ogs_ngap_encode(&pdu); +} + +ogs_pkbuf_t *ngap_build_s1_reset_partial( + NGAP_Cause_PR group, long cause, + NGAP_AMF_UE_NGAP_ID_t *amf_ue_ngap_id, + NGAP_ENB_UE_NGAP_ID_t *gnb_ue_ngap_id) +{ + NGAP_UE_associatedLogicalNG_ConnectionListRes_t *partOfNG_Interface = NULL; + NGAP_UE_associatedLogicalNG_ConnectionItemRes_t *ie2 = NULL; + NGAP_UE_associatedLogicalNG_ConnectionItem_t *item = NULL; + + partOfNG_Interface = CALLOC(1, + sizeof(NGAP_UE_associatedLogicalNG_ConnectionListRes_t)); + ogs_assert(partOfNG_Interface); + + ie2 = CALLOC(1, + sizeof(NGAP_UE_associatedLogicalNG_ConnectionItemRes_t)); + ASN_SEQUENCE_ADD(&partOfNG_Interface->list, ie2); + + ie2->id = NGAP_ProtocolIE_ID_id_UE_associatedLogicalNG_ConnectionItem; + ie2->criticality = NGAP_Criticality_reject; + ie2->value.present = NGAP_UE_associatedLogicalNG_ConnectionItemRes__value_PR_UE_associatedLogicalNG_ConnectionItem; + + item = &ie2->value.choice.UE_associatedLogicalNG_ConnectionItem; + item->mME_UE_NGAP_ID = amf_ue_ngap_id; + item->eNB_UE_NGAP_ID = gnb_ue_ngap_id; + + return ngap_build_s1_reset(group, cause, partOfNG_Interface); +} + +ogs_pkbuf_t *ngap_build_s1_reset_ack( + NGAP_UE_associatedLogicalNG_ConnectionListRes_t *partOfNG_Interface) +{ + NGAP_NGAP_PDU_t pdu; + NGAP_SuccessfulOutcome_t *successfulOutcome = NULL; + NGAP_ResetAcknowledge_t *ResetAcknowledge = NULL; + + NGAP_ResetAcknowledgeIEs_t *ie = NULL; + + ogs_debug("[AMF] Reset acknowledge"); + + memset(&pdu, 0, sizeof (NGAP_NGAP_PDU_t)); + pdu.present = NGAP_NGAP_PDU_PR_successfulOutcome; + pdu.choice.successfulOutcome = + CALLOC(1, sizeof(NGAP_SuccessfulOutcome_t)); + + successfulOutcome = pdu.choice.successfulOutcome; + successfulOutcome->procedureCode = NGAP_ProcedureCode_id_Reset; + successfulOutcome->criticality = NGAP_Criticality_reject; + successfulOutcome->value.present = + NGAP_SuccessfulOutcome__value_PR_ResetAcknowledge; + + ResetAcknowledge = &successfulOutcome->value.choice.ResetAcknowledge; + + if (partOfNG_Interface && partOfNG_Interface->list.count) { + int i = 0; + NGAP_UE_associatedLogicalNG_ConnectionListResAck_t *list = NULL; + + ie = CALLOC(1, sizeof(NGAP_ResetAcknowledgeIEs_t)); + ASN_SEQUENCE_ADD(&ResetAcknowledge->protocolIEs, ie); + + ie->id = + NGAP_ProtocolIE_ID_id_UE_associatedLogicalNG_ConnectionListResAck; + ie->criticality = NGAP_Criticality_ignore; + ie->value.present = NGAP_ResetAcknowledgeIEs__value_PR_UE_associatedLogicalNG_ConnectionListResAck; + + list = &ie->value.choice.UE_associatedLogicalNG_ConnectionListResAck; + + for (i = 0; i < partOfNG_Interface->list.count; i++) { + NGAP_UE_associatedLogicalNG_ConnectionItemRes_t *ie1 = NULL; + NGAP_UE_associatedLogicalNG_ConnectionItem_t *item1 = NULL; + + NGAP_UE_associatedLogicalNG_ConnectionItemResAck_t *ie2 = NULL; + NGAP_UE_associatedLogicalNG_ConnectionItem_t *item2 = NULL; + + ie1 = (NGAP_UE_associatedLogicalNG_ConnectionItemRes_t *) + partOfNG_Interface->list.array[i]; + ogs_assert(ie1); + + item1 = &ie1->value.choice.UE_associatedLogicalNG_ConnectionItem; + ogs_assert(item1); + + if (item1->mME_UE_NGAP_ID == NULL && + item1->eNB_UE_NGAP_ID == NULL) { + ogs_warn("No AMF_UE_NGAP_ID & ENB_UE_NGAP_ID"); + continue; + } + + ie2 = CALLOC(1, + sizeof(NGAP_UE_associatedLogicalNG_ConnectionItemResAck_t)); + ogs_assert(ie2); + ASN_SEQUENCE_ADD(&list->list, ie2); + + ie2->id = + NGAP_ProtocolIE_ID_id_UE_associatedLogicalNG_ConnectionItem; + ie2->criticality = NGAP_Criticality_ignore; + ie2->value.present = NGAP_UE_associatedLogicalNG_ConnectionItemResAck__value_PR_UE_associatedLogicalNG_ConnectionItem; + + item2 = &ie2->value.choice.UE_associatedLogicalNG_ConnectionItem; + ogs_assert(item2); + + if (item1->mME_UE_NGAP_ID) { + item2->mME_UE_NGAP_ID = CALLOC(1, + sizeof(NGAP_AMF_UE_NGAP_ID_t)); + ogs_assert(item2->mME_UE_NGAP_ID); + *item2->mME_UE_NGAP_ID = *item1->mME_UE_NGAP_ID; + } + + if (item1->eNB_UE_NGAP_ID) { + item2->eNB_UE_NGAP_ID = CALLOC(1, + sizeof(NGAP_ENB_UE_NGAP_ID_t)); + ogs_assert(item2->eNB_UE_NGAP_ID); + *item2->eNB_UE_NGAP_ID = *item1->eNB_UE_NGAP_ID; + } + + ogs_debug(" AMF_UE_NGAP_ID[%d] ENB_UE_NGAP_ID[%d]", + item2->mME_UE_NGAP_ID ? (int)*item2->mME_UE_NGAP_ID : -1, + item2->eNB_UE_NGAP_ID ? (int)*item2->eNB_UE_NGAP_ID : -1); + } + } + + return ogs_ngap_encode(&pdu); +} + +ogs_pkbuf_t *ngap_build_write_replace_warning_request(sbc_pws_data_t *sbc_pws) +{ + NGAP_NGAP_PDU_t pdu; + NGAP_InitiatingMessage_t *initiatingMessage = NULL; + NGAP_WriteReplaceWarningRequest_t *WriteReplaceWarningRequest = NULL; + + NGAP_WriteReplaceWarningRequestIEs_t *ie = NULL; + NGAP_MessageIdentifier_t *MessageIdentifier = NULL; + NGAP_SerialNumber_t *SerialNumber = NULL; + NGAP_RepetitionPeriod_t *RepetitionPeriod = NULL; + NGAP_NumberofBroadcastRequest_t *NumberofBroadcastRequest = NULL; + NGAP_DataCodingScheme_t *DataCodingScheme = NULL; + NGAP_WarningMessageContents_t *WarningMessageContents = NULL; + + ogs_debug("[AMF] Write-replace warning request"); + + ogs_assert(sbc_pws); + + memset(&pdu, 0, sizeof (NGAP_NGAP_PDU_t)); + pdu.present = NGAP_NGAP_PDU_PR_initiatingMessage; + pdu.choice.initiatingMessage = + CALLOC(1, sizeof(NGAP_InitiatingMessage_t)); + + initiatingMessage = pdu.choice.initiatingMessage; + initiatingMessage->procedureCode = + NGAP_ProcedureCode_id_WriteReplaceWarning; + initiatingMessage->criticality = NGAP_Criticality_reject; + initiatingMessage->value.present = + NGAP_InitiatingMessage__value_PR_WriteReplaceWarningRequest; + + WriteReplaceWarningRequest = + &initiatingMessage->value.choice.WriteReplaceWarningRequest; + + ie = CALLOC(1, sizeof(NGAP_WriteReplaceWarningRequestIEs_t)); + ASN_SEQUENCE_ADD(&WriteReplaceWarningRequest->protocolIEs, ie); + + ie->id = NGAP_ProtocolIE_ID_id_MessageIdentifier; + ie->criticality = NGAP_Criticality_reject; + ie->value.present = + NGAP_WriteReplaceWarningRequestIEs__value_PR_MessageIdentifier; + + MessageIdentifier = &ie->value.choice.MessageIdentifier; + + MessageIdentifier->size = (16 / 8); + MessageIdentifier->buf = + CALLOC(MessageIdentifier->size, sizeof(uint8_t)); + MessageIdentifier->bits_unused = 0; + MessageIdentifier->buf[0] = (sbc_pws->message_id >> 8) & 0xFF; + MessageIdentifier->buf[1] = sbc_pws->message_id & 0xFF; + + ie = CALLOC(1, sizeof(NGAP_WriteReplaceWarningRequestIEs_t)); + ASN_SEQUENCE_ADD(&WriteReplaceWarningRequest->protocolIEs, ie); + + ie->id = NGAP_ProtocolIE_ID_id_SerialNumber; + ie->criticality = NGAP_Criticality_reject; + ie->value.present = + NGAP_WriteReplaceWarningRequestIEs__value_PR_SerialNumber; + + SerialNumber = &ie->value.choice.SerialNumber; + + SerialNumber->size = (16 / 8); + SerialNumber->buf = + CALLOC(SerialNumber->size, sizeof(uint8_t)); + SerialNumber->bits_unused = 0; + SerialNumber->buf[0] = (sbc_pws->serial_number >> 8) & 0xFF; + SerialNumber->buf[1] = sbc_pws->serial_number & 0xFF; + + /* TODO: optional Warning Area List */ + + ie = CALLOC(1, sizeof(NGAP_WriteReplaceWarningRequestIEs_t)); + ASN_SEQUENCE_ADD(&WriteReplaceWarningRequest->protocolIEs, ie); + + ie->id = NGAP_ProtocolIE_ID_id_RepetitionPeriod; + ie->criticality = NGAP_Criticality_reject; + ie->value.present = + NGAP_WriteReplaceWarningRequestIEs__value_PR_RepetitionPeriod; + + RepetitionPeriod = &ie->value.choice.RepetitionPeriod; + + *RepetitionPeriod = sbc_pws->repetition_period; + + /* TODO: optional Extended Repetition Period */ + + ie = CALLOC(1, sizeof(NGAP_WriteReplaceWarningRequestIEs_t)); + ASN_SEQUENCE_ADD(&WriteReplaceWarningRequest->protocolIEs, ie); + + ie->id = NGAP_ProtocolIE_ID_id_NumberofBroadcastRequest; + ie->criticality = NGAP_Criticality_reject; + ie->value.present = + NGAP_WriteReplaceWarningRequestIEs__value_PR_NumberofBroadcastRequest; + + NumberofBroadcastRequest = &ie->value.choice.NumberofBroadcastRequest; + + *NumberofBroadcastRequest = sbc_pws->number_of_broadcast; + + /* TODO: optional Warnging Type */ + + /* TODO: optional Warning Security Information */ + + ie = CALLOC(1, sizeof(NGAP_WriteReplaceWarningRequestIEs_t)); + ASN_SEQUENCE_ADD(&WriteReplaceWarningRequest->protocolIEs, ie); + + ie->id = NGAP_ProtocolIE_ID_id_DataCodingScheme; + ie->criticality = NGAP_Criticality_reject; + ie->value.present = + NGAP_WriteReplaceWarningRequestIEs__value_PR_DataCodingScheme; + + DataCodingScheme = &ie->value.choice.DataCodingScheme; + + DataCodingScheme->size = (8 / 8); + DataCodingScheme->buf = + CALLOC(DataCodingScheme->size, sizeof(uint8_t)); + DataCodingScheme->bits_unused = 0; + DataCodingScheme->buf[0] = sbc_pws->data_coding_scheme & 0xFF; + + ie = CALLOC(1, sizeof(NGAP_WriteReplaceWarningRequestIEs_t)); + ASN_SEQUENCE_ADD(&WriteReplaceWarningRequest->protocolIEs, ie); + + ie->id = NGAP_ProtocolIE_ID_id_WarningMessageContents; + ie->criticality = NGAP_Criticality_reject; + ie->value.present = + NGAP_WriteReplaceWarningRequestIEs__value_PR_WarningMessageContents; + + WarningMessageContents = &ie->value.choice.WarningMessageContents; + + WarningMessageContents->size = sbc_pws->message_length;; + WarningMessageContents->buf = + CALLOC(WarningMessageContents->size, sizeof(uint8_t)); + memcpy(WarningMessageContents->buf, + sbc_pws->message_contents, WarningMessageContents->size); + + /* TODO: optional Concurrent Warning Message Indicator */ + + ogs_debug(" Message[%02x,%02x] Serial[%02x,%02x] " + "Repetition[%d] NumBroadcast[%d]", + MessageIdentifier->buf[0], MessageIdentifier->buf[1], + SerialNumber->buf[0], SerialNumber->buf[1], + (int)*RepetitionPeriod, (int)*NumberofBroadcastRequest); + + return ogs_ngap_encode(&pdu); +} + +ogs_pkbuf_t *ngap_build_kill_request(sbc_pws_data_t *sbc_pws) +{ + NGAP_NGAP_PDU_t pdu; + NGAP_InitiatingMessage_t *initiatingMessage = NULL; + NGAP_KillRequest_t *KillRequest = NULL; + + NGAP_KillRequestIEs_t *ie = NULL; + NGAP_MessageIdentifier_t *MessageIdentifier = NULL; + NGAP_SerialNumber_t *SerialNumber = NULL; + + ogs_debug("[AMF] Kill request"); + + ogs_assert(sbc_pws); + + memset(&pdu, 0, sizeof (NGAP_NGAP_PDU_t)); + pdu.present = NGAP_NGAP_PDU_PR_initiatingMessage; + pdu.choice.initiatingMessage = + CALLOC(1, sizeof(NGAP_InitiatingMessage_t)); + + initiatingMessage = pdu.choice.initiatingMessage; + initiatingMessage->procedureCode = NGAP_ProcedureCode_id_Kill; + initiatingMessage->criticality = NGAP_Criticality_reject; + initiatingMessage->value.present = + NGAP_InitiatingMessage__value_PR_KillRequest; + + KillRequest = &initiatingMessage->value.choice.KillRequest; + + ie = CALLOC(1, sizeof(NGAP_KillRequestIEs_t)); + ASN_SEQUENCE_ADD(&KillRequest->protocolIEs, ie); + + ie->id = NGAP_ProtocolIE_ID_id_MessageIdentifier; + ie->criticality = NGAP_Criticality_reject; + ie->value.present = NGAP_KillRequestIEs__value_PR_MessageIdentifier; + + MessageIdentifier = &ie->value.choice.MessageIdentifier; + + MessageIdentifier->size = (16 / 8); + MessageIdentifier->buf = + CALLOC(MessageIdentifier->size, sizeof(uint8_t)); + MessageIdentifier->bits_unused = 0; + MessageIdentifier->buf[0] = (sbc_pws->message_id >> 8) & 0xFF; + MessageIdentifier->buf[1] = sbc_pws->message_id & 0xFF; + + ie = CALLOC(1, sizeof(NGAP_KillRequestIEs_t)); + ASN_SEQUENCE_ADD(&KillRequest->protocolIEs, ie); + + ie->id = NGAP_ProtocolIE_ID_id_SerialNumber; + ie->criticality = NGAP_Criticality_reject; + ie->value.present = NGAP_KillRequestIEs__value_PR_SerialNumber; + + SerialNumber = &ie->value.choice.SerialNumber; + + SerialNumber->size = (16 / 8); + SerialNumber->buf = + CALLOC(SerialNumber->size, sizeof(uint8_t)); + SerialNumber->bits_unused = 0; + SerialNumber->buf[0] = (sbc_pws->serial_number >> 8) & 0xFF; + SerialNumber->buf[1] = sbc_pws->serial_number & 0xFF; + + /* TODO: optional Warning Area List */ + + ogs_debug(" Message[%02x,%02x] Serial[%02x,%02x]", + MessageIdentifier->buf[0], MessageIdentifier->buf[1], + SerialNumber->buf[0], SerialNumber->buf[1]); + + return ogs_ngap_encode(&pdu); +} +#endif diff --git a/src/amf/ngap-build.h b/src/amf/ngap-build.h new file mode 100644 index 000000000..dbd9e4d24 --- /dev/null +++ b/src/amf/ngap-build.h @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2019,2020 by Sukchan Lee + * + * This file is part of Open5GS. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 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, see . + */ + +#ifndef NGAP_BUILD_H +#define NGAP_BUILD_H + +#include "context.h" + +#ifdef __cplusplus +extern "C" { +#endif + +ogs_pkbuf_t *ngap_build_setup_rsp(void); +ogs_pkbuf_t *ngap_build_setup_failure( + NGAP_Cause_PR group, long cause, long time_to_wait); + +#if 0 +ogs_pkbuf_t *ngap_build_downlink_nas_transport( + gnb_ue_t *gnb_ue, ogs_pkbuf_t *emmbuf); + +ogs_pkbuf_t *ngap_build_initial_context_setup_request( + amf_ue_t *amf_ue, ogs_pkbuf_t *emmbuf); +ogs_pkbuf_t *ngap_build_ue_context_modification_request(amf_ue_t *amf_ue); +ogs_pkbuf_t *ngap_build_ue_context_release_command( + gnb_ue_t *gnb_ue, NGAP_Cause_PR group, long cause); + +ogs_pkbuf_t *ngap_build_e_rab_setup_request( + amf_bearer_t *bearer, ogs_pkbuf_t *esmbuf); +ogs_pkbuf_t *ngap_build_e_rab_modify_request( + amf_bearer_t *bearer, ogs_pkbuf_t *esmbuf); +ogs_pkbuf_t *ngap_build_e_rab_release_command( + amf_bearer_t *bearer, ogs_pkbuf_t *esmbuf, NGAP_Cause_PR group, long cause); + +ogs_pkbuf_t *ngap_build_paging( + amf_ue_t *amf_ue, NGAP_CNDomain_t cn_domain); + +ogs_pkbuf_t *ngap_build_amf_configuration_transfer( + NGAP_SONConfigurationTransfer_t *son_configuration_transfer); + +ogs_pkbuf_t *ngap_build_path_switch_ack(amf_ue_t *amf_ue); +ogs_pkbuf_t *ngap_build_path_switch_failure( + uint32_t gnb_ue_ngap_id, uint32_t amf_ue_ngap_id, + NGAP_Cause_PR group, long cause); + +ogs_pkbuf_t *ngap_build_handover_command(gnb_ue_t *source_ue); +ogs_pkbuf_t *ngap_build_handover_preparation_failure( + gnb_ue_t *source_ue, NGAP_Cause_t *cause); + +ogs_pkbuf_t *ngap_build_handover_request( + amf_ue_t *amf_ue, gnb_ue_t *target_ue, + NGAP_ENB_UE_NGAP_ID_t *gnb_ue_ngap_id, + NGAP_AMF_UE_NGAP_ID_t *amf_ue_ngap_id, + NGAP_HandoverType_t *handovertype, + NGAP_Cause_t *cause, + NGAP_Source_ToTarget_TransparentContainer_t + *source_totarget_transparentContainer); + +ogs_pkbuf_t *ngap_build_handover_cancel_ack( + gnb_ue_t *source_ue); + +ogs_pkbuf_t *ngap_build_amf_status_transfer( + gnb_ue_t *target_ue, + NGAP_ENB_StatusTransfer_TransparentContainer_t + *gnb_statustransfer_transparentContainer); + +ogs_pkbuf_t *ngap_build_error_indication( + NGAP_AMF_UE_NGAP_ID_t *amf_ue_ngap_id, + NGAP_ENB_UE_NGAP_ID_t *gnb_ue_ngap_id, + NGAP_Cause_PR group, long cause); + +ogs_pkbuf_t *ngap_build_s1_reset( + NGAP_Cause_PR group, long cause, + NGAP_UE_associatedLogicalS1_ConnectionListRes_t *partOfS1_Interface); + +ogs_pkbuf_t *ngap_build_s1_reset_partial( + NGAP_Cause_PR group, long cause, + NGAP_AMF_UE_NGAP_ID_t *amf_ue_ngap_id, + NGAP_ENB_UE_NGAP_ID_t *gnb_ue_ngap_id); + +ogs_pkbuf_t *ngap_build_s1_reset_ack( + NGAP_UE_associatedLogicalS1_ConnectionListRes_t *partOfS1_Interface); + +ogs_pkbuf_t *ngap_build_write_replace_warning_request( + sbc_pws_data_t *sbc_pws); + +ogs_pkbuf_t *ngap_build_kill_request(sbc_pws_data_t *sbc_pws); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* NGAP_BUILD_H */ diff --git a/src/amf/ngap-handler.c b/src/amf/ngap-handler.c new file mode 100644 index 000000000..1703bf733 --- /dev/null +++ b/src/amf/ngap-handler.c @@ -0,0 +1,2063 @@ +/* + * Copyright (C) 2019,2020 by Sukchan Lee + * + * This file is part of Open5GS. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 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, see . + */ + +#include "ngap-handler.h" +#include "ngap-path.h" + +static bool served_tai_is_found(amf_gnb_t *gnb) +{ + int i; + int served_tai_index; + + for (i = 0; i < gnb->num_of_supported_ta_list; i++) { + served_tai_index = amf_find_served_tai(&gnb->supported_ta_list[i]); + if (served_tai_index >= 0 && + served_tai_index < OGS_MAX_NUM_OF_SERVED_TAI) { + ogs_debug(" SERVED_TAI_INDEX[%d]", served_tai_index); + return true; + } + } + + return false; +} + +static bool maximum_number_of_gnbs_is_reached(void) +{ + amf_gnb_t *gnb = NULL, *next_gnb = NULL; + int number_of_gnbs_online = 0; + + ogs_list_for_each_safe(&amf_self()->gnb_list, next_gnb, gnb) { + if (gnb->state.ng_setup_success) { + number_of_gnbs_online++; + } + } + + return number_of_gnbs_online >= ogs_config()->max.gnb; +} + +void ngap_handle_ng_setup_request(amf_gnb_t *gnb, ogs_ngap_message_t *message) +{ + char buf[OGS_ADDRSTRLEN]; + int i, j; + + NGAP_InitiatingMessage_t *initiatingMessage = NULL; + NGAP_NGSetupRequest_t *NGSetupRequest = NULL; + + NGAP_NGSetupRequestIEs_t *ie = NULL; + NGAP_GlobalRANNodeID_t *GlobalRANNodeID = NULL; + NGAP_GlobalGNB_ID_t *globalGNB_ID = NULL; + NGAP_SupportedTAList_t *SupportedTAList = NULL; + NGAP_PagingDRX_t *PagingDRX = NULL; + + uint32_t gnb_id; + NGAP_Cause_PR group = NGAP_Cause_PR_NOTHING; + long cause = 0; + + ogs_assert(gnb); + ogs_assert(gnb->sock); + + ogs_assert(message); + initiatingMessage = message->choice.initiatingMessage; + ogs_assert(initiatingMessage); + NGSetupRequest = &initiatingMessage->value.choice.NGSetupRequest; + ogs_assert(NGSetupRequest); + + ogs_debug("[AMF] NG-Setup request"); + + for (i = 0; i < NGSetupRequest->protocolIEs.list.count; i++) { + ie = NGSetupRequest->protocolIEs.list.array[i]; + switch (ie->id) { + case NGAP_ProtocolIE_ID_id_GlobalRANNodeID: + GlobalRANNodeID = &ie->value.choice.GlobalRANNodeID; + break; + case NGAP_ProtocolIE_ID_id_SupportedTAList: + SupportedTAList = &ie->value.choice.SupportedTAList; + break; + case NGAP_ProtocolIE_ID_id_DefaultPagingDRX: + PagingDRX = &ie->value.choice.PagingDRX; + break; + default: + break; + } + } + + if (!GlobalRANNodeID) { + ogs_warn("No GlobalRANNodeID"); + group = NGAP_Cause_PR_protocol; + cause = NGAP_CauseProtocol_semantic_error; + ngap_send_ng_setup_failure(gnb, group, cause); + return; + } + + globalGNB_ID = GlobalRANNodeID->choice.globalGNB_ID; + if (!globalGNB_ID) { + ogs_warn("No globalGNB_ID"); + group = NGAP_Cause_PR_protocol; + cause = NGAP_CauseProtocol_semantic_error; + ngap_send_ng_setup_failure(gnb, group, cause); + return; + } + + if (!SupportedTAList) { + ogs_warn("No SupportedTAList"); + group = NGAP_Cause_PR_protocol; + cause = NGAP_CauseProtocol_semantic_error; + ngap_send_ng_setup_failure(gnb, group, cause); + return; + } + + ogs_ngap_GNB_ID_to_uint32(&globalGNB_ID->gNB_ID, &gnb_id); + ogs_debug(" IP[%s] GNB_ID[%x]", OGS_ADDR(gnb->addr, buf), gnb_id); + + if (PagingDRX) + ogs_debug(" PagingDRX[%ld]", *PagingDRX); + + amf_gnb_set_gnb_id(gnb, gnb_id); + + /* Parse Supported TA */ + gnb->num_of_supported_ta_list = 0; + for (i = 0; i < SupportedTAList->list.count; i++) { + NGAP_SupportedTAItem_t *SupportedTAItem = NULL; + NGAP_TAC_t *tAC = NULL; + + SupportedTAItem = (NGAP_SupportedTAItem_t *) + SupportedTAList->list.array[i]; + ogs_assert(SupportedTAItem); + tAC = &SupportedTAItem->tAC; + ogs_assert(tAC); + + for (j = 0; j < SupportedTAItem->broadcastPLMNList.list.count; j++) { + NGAP_BroadcastPLMNItem_t *BroadcastPLMNItem = NULL; + NGAP_PLMNIdentity_t *pLMNIdentity = NULL; + + BroadcastPLMNItem = (NGAP_BroadcastPLMNItem_t *) + SupportedTAItem->broadcastPLMNList.list.array[i]; + ogs_assert(BroadcastPLMNItem); + pLMNIdentity = (NGAP_PLMNIdentity_t *) + &BroadcastPLMNItem->pLMNIdentity; + ogs_assert(pLMNIdentity); + + memcpy(&gnb->supported_ta_list[gnb->num_of_supported_ta_list].tac, + tAC->buf, sizeof(ogs_uint24_t)); + gnb->supported_ta_list[gnb->num_of_supported_ta_list].tac = + ogs_be24toh( + gnb->supported_ta_list[gnb->num_of_supported_ta_list].tac); + memcpy(&gnb->supported_ta_list + [gnb->num_of_supported_ta_list].plmn_id, + pLMNIdentity->buf, sizeof(ogs_plmn_id_t)); + ogs_debug(" PLMN_ID[MCC:%d MNC:%d] TAC[%d]", + ogs_plmn_id_mcc(&gnb->supported_ta_list + [gnb->num_of_supported_ta_list].plmn_id), + ogs_plmn_id_mnc(&gnb->supported_ta_list + [gnb->num_of_supported_ta_list].plmn_id), + gnb->supported_ta_list[gnb->num_of_supported_ta_list].tac.v); + gnb->num_of_supported_ta_list++; + } + } + + if (maximum_number_of_gnbs_is_reached()) { + ogs_warn("NG-Setup failure:"); + ogs_warn(" Maximum number of gNBs reached"); + group = NGAP_Cause_PR_misc; + cause = NGAP_CauseMisc_control_processing_overload; + + ngap_send_ng_setup_failure(gnb, group, cause); + return; + } + + if (gnb->num_of_supported_ta_list == 0) { + ogs_warn("NG-Setup failure:"); + ogs_warn(" No supported TA exist in NG-Setup request"); + group = NGAP_Cause_PR_protocol; + cause = NGAP_CauseProtocol_message_not_compatible_with_receiver_state; + + ngap_send_ng_setup_failure(gnb, group, cause); + return; + } + + if (!served_tai_is_found(gnb)) { + ogs_warn("NG-Setup failure:"); + ogs_warn(" Cannot find Served TAI. Check 'amf.tai' configuration"); + group = NGAP_Cause_PR_misc; + cause = NGAP_CauseMisc_unknown_PLMN; + + ngap_send_ng_setup_failure(gnb, group, cause); + return; + } + + gnb->state.ng_setup_success = true; + ngap_send_ng_setup_response(gnb); +} + +#if 0 +void ngap_handle_initial_ue_message(amf_gnb_t *gnb, ogs_ngap_message_t *message) +{ + int i; + char buf[OGS_ADDRSTRLEN]; + + NGAP_InitiatingMessage_t *initiatingMessage = NULL; + NGAP_InitialUEMessage_t *InitialUEMessage = NULL; + + NGAP_InitialUEMessage_IEs_t *ie = NULL; + NGAP_ENB_UE_NGAP_ID_t *ENB_UE_NGAP_ID = NULL; + NGAP_NAS_PDU_t *NAS_PDU = NULL; + NGAP_TAI_t *TAI = NULL; + NGAP_EUTRAN_CGI_t *EUTRAN_CGI = NULL; + NGAP_S_TMSI_t *S_TMSI = NULL; + + NGAP_PLMNidentity_t *pLMNidentity = NULL; + NGAP_TAC_t *tAC = NULL; + NGAP_CellIdentity_t *cell_ID = NULL; + + gnb_ue_t *gnb_ue = NULL; + + ogs_assert(gnb); + ogs_assert(gnb->sock); + + ogs_assert(message); + initiatingMessage = message->choice.initiatingMessage; + ogs_assert(initiatingMessage); + InitialUEMessage = &initiatingMessage->value.choice.InitialUEMessage; + ogs_assert(InitialUEMessage); + + ogs_debug("[AMF] Initial UE Message"); + + for (i = 0; i < InitialUEMessage->protocolIEs.list.count; i++) { + ie = InitialUEMessage->protocolIEs.list.array[i]; + switch (ie->id) { + case NGAP_ProtocolIE_ID_id_eNB_UE_NGAP_ID: + ENB_UE_NGAP_ID = &ie->value.choice.ENB_UE_NGAP_ID; + break; + case NGAP_ProtocolIE_ID_id_NAS_PDU: + NAS_PDU = &ie->value.choice.NAS_PDU; + break; + case NGAP_ProtocolIE_ID_id_TAI: + TAI = &ie->value.choice.TAI; + break; + case NGAP_ProtocolIE_ID_id_EUTRAN_CGI: + EUTRAN_CGI = &ie->value.choice.EUTRAN_CGI; + break; + case NGAP_ProtocolIE_ID_id_S_TMSI: + S_TMSI = &ie->value.choice.S_TMSI; + break; + default: + break; + } + } + + ogs_debug(" IP[%s] ENB_ID[%d]", + OGS_ADDR(gnb->addr, buf), gnb->gnb_id); + + ogs_assert(ENB_UE_NGAP_ID); + gnb_ue = gnb_ue_find_by_gnb_ue_ngap_id(gnb, *ENB_UE_NGAP_ID); + if (!gnb_ue) { + gnb_ue = gnb_ue_add(gnb, *ENB_UE_NGAP_ID); + ogs_assert(gnb_ue); + + /* Find AMF_UE if S_TMSI included */ + if (S_TMSI) { + served_guamfi_t *served_guamfi = &amf_self()->served_guamfi[0]; + ogs_nas_5gs_guti_t nas_guti; + amf_ue_t *amf_ue = NULL; + + memset(&nas_guti, 0, sizeof(ogs_nas_5gs_guti_t)); + + /* Use the first configured plmn_id and amf group id */ + ogs_nas_from_plmn_id(&nas_guti.nas_plmn_id, &served_guamfi->plmn_id[0]); + nas_guti.amf_gid = served_guamfi->amf_gid[0]; + + /* size must be 1 */ + memcpy(&nas_guti.amf_code, S_TMSI->mMEC.buf, S_TMSI->mMEC.size); + /* size must be 4 */ + memcpy(&nas_guti.m_tmsi, S_TMSI->m_TMSI.buf, S_TMSI->m_TMSI.size); + nas_guti.m_tmsi = ntohl(nas_guti.m_tmsi); + + amf_ue = amf_ue_find_by_guti(&nas_guti); + if (!amf_ue) { + ogs_warn("Unknown UE by S_TMSI[G:%d,C:%d,M_TMSI:0x%x]", + nas_guti.amf_gid, nas_guti.amf_code, nas_guti.m_tmsi); + } else { + ogs_debug(" S_TMSI[G:%d,C:%d,M_TMSI:0x%x] IMSI:[%s]", + amf_ue->guti.amf_gid, + amf_ue->guti.amf_code, + amf_ue->guti.m_tmsi, + AMF_UE_HAVE_IMSI(amf_ue) + ? amf_ue->imsi_bcd : "Unknown"); + + /* If NAS(amf_ue_t) has already been associated with + * older S1(gnb_ue_t) context */ + if (ECM_CONNECTED(amf_ue)) { + /* Implcit S1 release */ + ogs_debug("Implicit S1 release"); + ogs_debug(" ENB_UE_NGAP_ID[%d] AMF_UE_NGAP_ID[%d]", + amf_ue->gnb_ue->gnb_ue_ngap_id, + amf_ue->gnb_ue->amf_ue_ngap_id); + gnb_ue_remove(amf_ue->gnb_ue); + } + amf_ue_associate_gnb_ue(amf_ue, gnb_ue); + } + } + } + + ogs_assert(TAI); + pLMNidentity = &TAI->pLMNidentity; + ogs_assert(pLMNidentity && pLMNidentity->size == sizeof(ogs_plmn_id_t)); + tAC = &TAI->tAC; + ogs_assert(tAC && tAC->size == sizeof(uint16_t)); + + memcpy(&gnb_ue->saved.tai.plmn_id, pLMNidentity->buf, + sizeof(gnb_ue->saved.tai.plmn_id)); + memcpy(&gnb_ue->saved.tai.tac, tAC->buf, sizeof(gnb_ue->saved.tai.tac)); + gnb_ue->saved.tai.tac = ntohs(gnb_ue->saved.tai.tac); + + ogs_assert(EUTRAN_CGI); + pLMNidentity = &EUTRAN_CGI->pLMNidentity; + ogs_assert(pLMNidentity && pLMNidentity->size == sizeof(ogs_plmn_id_t)); + cell_ID = &EUTRAN_CGI->cell_ID; + ogs_assert(cell_ID); + memcpy(&gnb_ue->saved.e_cgi.plmn_id, pLMNidentity->buf, + sizeof(gnb_ue->saved.e_cgi.plmn_id)); + memcpy(&gnb_ue->saved.e_cgi.cell_id, cell_ID->buf, + sizeof(gnb_ue->saved.e_cgi.cell_id)); + gnb_ue->saved.e_cgi.cell_id = (ntohl(gnb_ue->saved.e_cgi.cell_id) >> 4); + + ogs_debug(" ENB_UE_NGAP_ID[%d] AMF_UE_NGAP_ID[%d] TAC[%d]", + gnb_ue->gnb_ue_ngap_id, gnb_ue->amf_ue_ngap_id, gnb_ue->saved.tai.tac); + + ngap_send_to_nas(gnb_ue, + NGAP_ProcedureCode_id_initialUEMessage, NAS_PDU); +} + +void ngap_handle_uplink_nas_transport( + amf_gnb_t *gnb, ogs_ngap_message_t *message) +{ + char buf[OGS_ADDRSTRLEN]; + int i; + + NGAP_InitiatingMessage_t *initiatingMessage = NULL; + NGAP_UplinkNASTransport_t *UplinkNASTransport = NULL; + + NGAP_UplinkNASTransport_IEs_t *ie = NULL; + NGAP_ENB_UE_NGAP_ID_t *ENB_UE_NGAP_ID = NULL; + NGAP_NAS_PDU_t *NAS_PDU = NULL; + + gnb_ue_t *gnb_ue = NULL; + + ogs_assert(gnb); + ogs_assert(gnb->sock); + + ogs_assert(message); + initiatingMessage = message->choice.initiatingMessage; + ogs_assert(initiatingMessage); + UplinkNASTransport = &initiatingMessage->value.choice.UplinkNASTransport; + ogs_assert(UplinkNASTransport); + + ogs_debug("[AMF] Uplink NAS transport"); + + for (i = 0; i < UplinkNASTransport->protocolIEs.list.count; i++) { + ie = UplinkNASTransport->protocolIEs.list.array[i]; + switch (ie->id) { + case NGAP_ProtocolIE_ID_id_eNB_UE_NGAP_ID: + ENB_UE_NGAP_ID = &ie->value.choice.ENB_UE_NGAP_ID; + break; + case NGAP_ProtocolIE_ID_id_NAS_PDU: + NAS_PDU = &ie->value.choice.NAS_PDU; + break; + default: + break; + } + } + + ogs_debug(" IP[%s] ENB_ID[%d]", + OGS_ADDR(gnb->addr, buf), gnb->gnb_id); + + ogs_assert(ENB_UE_NGAP_ID); + gnb_ue = gnb_ue_find_by_gnb_ue_ngap_id(gnb, *ENB_UE_NGAP_ID); + ogs_expect_or_return(gnb_ue); + + ogs_debug(" ENB_UE_NGAP_ID[%d] AMF_UE_NGAP_ID[%d]", + gnb_ue->gnb_ue_ngap_id, gnb_ue->amf_ue_ngap_id); + + ngap_send_to_nas(gnb_ue, + NGAP_ProcedureCode_id_uplinkNASTransport, NAS_PDU); +} + +void ngap_handle_ue_capability_info_indication( + amf_gnb_t *gnb, ogs_ngap_message_t *message) +{ + char buf[OGS_ADDRSTRLEN]; + int i; + + NGAP_InitiatingMessage_t *initiatingMessage = NULL; + NGAP_UECapabilityInfoIndication_t *UECapabilityInfoIndication = NULL; + + NGAP_UECapabilityInfoIndicationIEs_t *ie = NULL; + NGAP_ENB_UE_NGAP_ID_t *ENB_UE_NGAP_ID = NULL; + NGAP_UERadioCapability_t *UERadioCapability = NULL; + + gnb_ue_t *gnb_ue = NULL; + + ogs_assert(gnb); + ogs_assert(gnb->sock); + + ogs_assert(message); + initiatingMessage = message->choice.initiatingMessage; + ogs_assert(initiatingMessage); + UECapabilityInfoIndication = + &initiatingMessage->value.choice.UECapabilityInfoIndication; + ogs_assert(UECapabilityInfoIndication); + + ogs_debug("[AMF] UE capability info indication"); + + for (i = 0; i < UECapabilityInfoIndication->protocolIEs.list.count; i++) { + ie = UECapabilityInfoIndication->protocolIEs.list.array[i]; + switch (ie->id) { + case NGAP_ProtocolIE_ID_id_eNB_UE_NGAP_ID: + ENB_UE_NGAP_ID = &ie->value.choice.ENB_UE_NGAP_ID; + break; + case NGAP_ProtocolIE_ID_id_UERadioCapability: + UERadioCapability = &ie->value.choice.UERadioCapability; + break; + default: + break; + } + } + + ogs_debug(" IP[%s] ENB_ID[%d]", + OGS_ADDR(gnb->addr, buf), gnb->gnb_id); + + ogs_assert(ENB_UE_NGAP_ID); + gnb_ue = gnb_ue_find_by_gnb_ue_ngap_id(gnb, *ENB_UE_NGAP_ID); + ogs_assert(gnb_ue); + + ogs_debug(" ENB_UE_NGAP_ID[%d] AMF_UE_NGAP_ID[%d]", + gnb_ue->gnb_ue_ngap_id, gnb_ue->amf_ue_ngap_id); + + if (gnb_ue->amf_ue) { + ogs_assert(UERadioCapability); + OGS_NGAP_STORE_DATA(&gnb_ue->amf_ue->ueRadioCapability, + UERadioCapability); + } +} + +void ngap_handle_initial_context_setup_response( + amf_gnb_t *gnb, ogs_ngap_message_t *message) +{ + int rv; + char buf[OGS_ADDRSTRLEN]; + int i; + + NGAP_SuccessfulOutcome_t *successfulOutcome = NULL; + NGAP_InitialContextSetupResponse_t *InitialContextSetupResponse = NULL; + + NGAP_InitialContextSetupResponseIEs_t *ie = NULL; + NGAP_ENB_UE_NGAP_ID_t *ENB_UE_NGAP_ID = NULL; + NGAP_E_RABSetupListCtxtSURes_t *E_RABSetupListCtxtSURes = NULL; + + amf_ue_t *amf_ue = NULL; + gnb_ue_t *gnb_ue = NULL; + + ogs_assert(gnb); + ogs_assert(gnb->sock); + + ogs_assert(message); + successfulOutcome = message->choice.successfulOutcome; + ogs_assert(successfulOutcome); + InitialContextSetupResponse = + &successfulOutcome->value.choice.InitialContextSetupResponse; + ogs_assert(InitialContextSetupResponse); + + ogs_debug("[AMF] Initial context setup response"); + + for (i = 0; i < InitialContextSetupResponse->protocolIEs.list.count; i++) { + ie = InitialContextSetupResponse->protocolIEs.list.array[i]; + switch (ie->id) { + case NGAP_ProtocolIE_ID_id_eNB_UE_NGAP_ID: + ENB_UE_NGAP_ID = &ie->value.choice.ENB_UE_NGAP_ID; + break; + case NGAP_ProtocolIE_ID_id_E_RABSetupListCtxtSURes: + E_RABSetupListCtxtSURes = + &ie->value.choice.E_RABSetupListCtxtSURes; + break; + default: + break; + } + } + + ogs_debug(" IP[%s] ENB_ID[%d]", + OGS_ADDR(gnb->addr, buf), gnb->gnb_id); + + ogs_assert(ENB_UE_NGAP_ID); + gnb_ue = gnb_ue_find_by_gnb_ue_ngap_id(gnb, *ENB_UE_NGAP_ID); + ogs_expect_or_return(gnb_ue); + amf_ue = gnb_ue->amf_ue; + ogs_assert(amf_ue); + + ogs_debug(" ENB_UE_NGAP_ID[%d] AMF_UE_NGAP_ID[%d]", + gnb_ue->gnb_ue_ngap_id, gnb_ue->amf_ue_ngap_id); + + ogs_assert(E_RABSetupListCtxtSURes); + for (i = 0; i < E_RABSetupListCtxtSURes->list.count; i++) { + NGAP_E_RABSetupItemCtxtSUResIEs_t *ie2 = NULL; + NGAP_E_RABSetupItemCtxtSURes_t *e_rab = NULL; + + amf_bearer_t *bearer = NULL; + + ie2 = (NGAP_E_RABSetupItemCtxtSUResIEs_t *) + E_RABSetupListCtxtSURes->list.array[i]; + ogs_assert(ie2); + + e_rab = &ie2->value.choice.E_RABSetupItemCtxtSURes; + ogs_assert(e_rab); + + bearer = amf_bearer_find_by_ue_ebi(amf_ue, e_rab->e_RAB_ID); + ogs_assert(bearer); + memcpy(&bearer->gnb_s1u_teid, e_rab->gTP_TEID.buf, + sizeof(bearer->gnb_s1u_teid)); + bearer->gnb_s1u_teid = ntohl(bearer->gnb_s1u_teid); + rv = ogs_asn_BIT_STRING_to_ip( + &e_rab->transportLayerAddress, &bearer->gnb_s1u_ip); + ogs_assert(rv == OGS_OK); + + ogs_debug(" EBI[%d] ENB-S1U-TEID[%d]", + bearer->ebi, bearer->gnb_s1u_teid); + + if (OGS_FSM_CHECK(&bearer->sm, esm_state_active)) { + ogs_debug(" NAS_EPS Type[%d]", amf_ue->nas_eps.type); + int uli_presence = 0; + if (amf_ue->nas_eps.type != AMF_EPS_TYPE_ATTACH_REQUEST) { + ogs_debug(" ### ULI PRESENT ###"); + uli_presence = 1; + } + amf_gtp_send_modify_bearer_request(bearer, uli_presence); + } + } + + if (SMS_SERVICE_INDICATOR(amf_ue)) { + sgsap_send_service_request(amf_ue, SGSAP_EMM_CONNECTED_MODE); + } + + CLEAR_SERVICE_INDICATOR(amf_ue); +} + +void ngap_handle_initial_context_setup_failure( + amf_gnb_t *gnb, ogs_ngap_message_t *message) +{ + char buf[OGS_ADDRSTRLEN]; + int i; + + NGAP_UnsuccessfulOutcome_t *unsuccessfulOutcome = NULL; + NGAP_InitialContextSetupFailure_t *InitialContextSetupFailure = NULL; + + NGAP_InitialContextSetupFailureIEs_t *ie = NULL; + NGAP_ENB_UE_NGAP_ID_t *ENB_UE_NGAP_ID = NULL; + NGAP_Cause_t *Cause = NULL; + + amf_ue_t *amf_ue = NULL; + gnb_ue_t *gnb_ue = NULL; + + ogs_assert(gnb); + ogs_assert(gnb->sock); + + ogs_assert(message); + unsuccessfulOutcome = message->choice.unsuccessfulOutcome; + ogs_assert(unsuccessfulOutcome); + InitialContextSetupFailure = + &unsuccessfulOutcome->value.choice.InitialContextSetupFailure; + ogs_assert(InitialContextSetupFailure); + + ogs_debug("[AMF] Initial context setup failure"); + + for (i = 0; i < InitialContextSetupFailure->protocolIEs.list.count; i++) { + ie = InitialContextSetupFailure->protocolIEs.list.array[i]; + switch (ie->id) { + case NGAP_ProtocolIE_ID_id_eNB_UE_NGAP_ID: + ENB_UE_NGAP_ID = &ie->value.choice.ENB_UE_NGAP_ID; + break; + case NGAP_ProtocolIE_ID_id_Cause: + Cause = &ie->value.choice.Cause; + break; + default: + break; + } + } + + ogs_debug(" IP[%s] ENB_ID[%d]", + OGS_ADDR(gnb->addr, buf), gnb->gnb_id); + + ogs_assert(ENB_UE_NGAP_ID); + ogs_assert(Cause); + + gnb_ue = gnb_ue_find_by_gnb_ue_ngap_id(gnb, *ENB_UE_NGAP_ID); + if (gnb_ue == NULL) { + ogs_warn("Initial context setup failure : " + "cannot find eNB-UE-NGAP-ID[%d]", (int)*ENB_UE_NGAP_ID); + return; + } + + ogs_debug(" ENB_UE_NGAP_ID[%d] AMF_UE_NGAP_ID[%d]", + gnb_ue->gnb_ue_ngap_id, gnb_ue->amf_ue_ngap_id); + ogs_debug(" Cause[Group:%d Cause:%d]", + Cause->present, (int)Cause->choice.radioNetwork); + + if (amf_ue) + CLEAR_SERVICE_INDICATOR(amf_ue); + + /* + * 19.2.2.3 in Spec 36.300 + * + * In case of failure, eNB and AMF behaviours are not mandated. + * + * Both implicit release (local release at each node) and + * explicit release (AMF-initiated UE Context Release procedure) + * may in principle be adopted. The eNB should ensure + * that no hanging resources remain at the eNB. + */ + amf_send_delete_session_or_gnb_ue_context_release(gnb_ue); +} + +void ngap_handle_ue_context_modification_response( + amf_gnb_t *gnb, ogs_ngap_message_t *message) +{ + char buf[OGS_ADDRSTRLEN]; + int i; + + NGAP_SuccessfulOutcome_t *successfulOutcome = NULL; + NGAP_UEContextModificationResponse_t *UEContextModificationResponse = NULL; + + NGAP_UEContextModificationResponseIEs_t *ie = NULL; + NGAP_ENB_UE_NGAP_ID_t *ENB_UE_NGAP_ID = NULL; + + amf_ue_t *amf_ue = NULL; + gnb_ue_t *gnb_ue = NULL; + + ogs_assert(gnb); + ogs_assert(gnb->sock); + + ogs_assert(message); + successfulOutcome = message->choice.successfulOutcome; + ogs_assert(successfulOutcome); + UEContextModificationResponse = + &successfulOutcome->value.choice.UEContextModificationResponse; + ogs_assert(UEContextModificationResponse); + + ogs_debug("[AMF] UE context modification response"); + + for (i = 0; i < UEContextModificationResponse->protocolIEs.list.count; i++) { + ie = UEContextModificationResponse->protocolIEs.list.array[i]; + switch (ie->id) { + case NGAP_ProtocolIE_ID_id_eNB_UE_NGAP_ID: + ENB_UE_NGAP_ID = &ie->value.choice.ENB_UE_NGAP_ID; + break; + default: + break; + } + } + + ogs_debug(" IP[%s] ENB_ID[%d]", + OGS_ADDR(gnb->addr, buf), gnb->gnb_id); + + ogs_assert(ENB_UE_NGAP_ID); + gnb_ue = gnb_ue_find_by_gnb_ue_ngap_id(gnb, *ENB_UE_NGAP_ID); + ogs_assert(gnb_ue); + amf_ue = gnb_ue->amf_ue; + ogs_assert(amf_ue); + + ogs_debug(" ENB_UE_NGAP_ID[%d] AMF_UE_NGAP_ID[%d]", + gnb_ue->gnb_ue_ngap_id, gnb_ue->amf_ue_ngap_id); + + CLEAR_SERVICE_INDICATOR(amf_ue); +} + +void ngap_handle_ue_context_modification_failure( + amf_gnb_t *gnb, ogs_ngap_message_t *message) +{ + char buf[OGS_ADDRSTRLEN]; + int i; + + NGAP_UnsuccessfulOutcome_t *unsuccessfulOutcome = NULL; + NGAP_UEContextModificationFailure_t *UEContextModificationFailure = NULL; + + NGAP_UEContextModificationFailureIEs_t *ie = NULL; + NGAP_ENB_UE_NGAP_ID_t *ENB_UE_NGAP_ID = NULL; + NGAP_Cause_t *Cause = NULL; + + gnb_ue_t *gnb_ue = NULL; + amf_ue_t *amf_ue = NULL; + + ogs_assert(gnb); + ogs_assert(gnb->sock); + + ogs_assert(message); + unsuccessfulOutcome = message->choice.unsuccessfulOutcome; + ogs_assert(unsuccessfulOutcome); + UEContextModificationFailure = + &unsuccessfulOutcome->value.choice.UEContextModificationFailure; + ogs_assert(UEContextModificationFailure); + + ogs_warn("[AMF] UE context modification failure"); + + for (i = 0; i < UEContextModificationFailure->protocolIEs.list.count; i++) { + ie = UEContextModificationFailure->protocolIEs.list.array[i]; + switch (ie->id) { + case NGAP_ProtocolIE_ID_id_eNB_UE_NGAP_ID: + ENB_UE_NGAP_ID = &ie->value.choice.ENB_UE_NGAP_ID; + break; + case NGAP_ProtocolIE_ID_id_Cause: + Cause = &ie->value.choice.Cause; + break; + default: + break; + } + } + + ogs_debug(" IP[%s] ENB_ID[%d]", + OGS_ADDR(gnb->addr, buf), gnb->gnb_id); + + ogs_assert(ENB_UE_NGAP_ID); + ogs_assert(Cause); + + gnb_ue = gnb_ue_find_by_gnb_ue_ngap_id(gnb, *ENB_UE_NGAP_ID); + if (gnb_ue == NULL) { + ogs_warn("Initial context setup failure : " + "cannot find eNB-UE-NGAP-ID[%d]", (int)*ENB_UE_NGAP_ID); + goto cleanup; + } + + ogs_debug(" ENB_UE_NGAP_ID[%d] AMF_UE_NGAP_ID[%d]", + gnb_ue->gnb_ue_ngap_id, gnb_ue->amf_ue_ngap_id); + ogs_debug(" Cause[Group:%d Cause:%d]", + Cause->present, (int)Cause->choice.radioNetwork); + +cleanup: + amf_ue = gnb_ue->amf_ue; + ogs_assert(amf_ue); + CLEAR_SERVICE_INDICATOR(amf_ue); +} + + +void ngap_handle_e_rab_setup_response( + amf_gnb_t *gnb, ogs_ngap_message_t *message) +{ + int rv; + char buf[OGS_ADDRSTRLEN]; + int i; + + NGAP_SuccessfulOutcome_t *successfulOutcome = NULL; + NGAP_E_RABSetupResponse_t *E_RABSetupResponse = NULL; + + NGAP_E_RABSetupResponseIEs_t *ie = NULL; + NGAP_ENB_UE_NGAP_ID_t *ENB_UE_NGAP_ID = NULL; + NGAP_E_RABSetupListBearerSURes_t *E_RABSetupListBearerSURes = NULL; + + gnb_ue_t *gnb_ue = NULL; + amf_ue_t *amf_ue = NULL; + + ogs_assert(gnb); + ogs_assert(gnb->sock); + + ogs_assert(message); + successfulOutcome = message->choice.successfulOutcome; + ogs_assert(successfulOutcome); + E_RABSetupResponse = &successfulOutcome->value.choice.E_RABSetupResponse; + ogs_assert(E_RABSetupResponse); + + ogs_debug("[AMF] E-RAB setup response"); + + for (i = 0; i < E_RABSetupResponse->protocolIEs.list.count; i++) { + ie = E_RABSetupResponse->protocolIEs.list.array[i]; + switch (ie->id) { + case NGAP_ProtocolIE_ID_id_eNB_UE_NGAP_ID: + ENB_UE_NGAP_ID = &ie->value.choice.ENB_UE_NGAP_ID; + break; + case NGAP_ProtocolIE_ID_id_E_RABSetupListBearerSURes: + E_RABSetupListBearerSURes = + &ie->value.choice.E_RABSetupListBearerSURes; + break; + default: + break; + } + } + + ogs_debug(" IP[%s] ENB_ID[%d]", + OGS_ADDR(gnb->addr, buf), gnb->gnb_id); + + ogs_assert(ENB_UE_NGAP_ID); + gnb_ue = gnb_ue_find_by_gnb_ue_ngap_id(gnb, *ENB_UE_NGAP_ID); + ogs_assert(gnb_ue); + amf_ue = gnb_ue->amf_ue; + ogs_assert(amf_ue); + + ogs_debug(" ENB_UE_NGAP_ID[%d] AMF_UE_NGAP_ID[%d]", + gnb_ue->gnb_ue_ngap_id, gnb_ue->amf_ue_ngap_id); + + ogs_assert(E_RABSetupListBearerSURes); + for (i = 0; i < E_RABSetupListBearerSURes->list.count; i++) { + NGAP_E_RABSetupItemBearerSUResIEs_t *ie2 = NULL; + NGAP_E_RABSetupItemBearerSURes_t *e_rab = NULL; + + amf_bearer_t *bearer = NULL; + + ie2 = (NGAP_E_RABSetupItemBearerSUResIEs_t *) + E_RABSetupListBearerSURes->list.array[i]; + ogs_assert(ie2); + + e_rab = &ie2->value.choice.E_RABSetupItemBearerSURes; + ogs_assert(e_rab); + + bearer = amf_bearer_find_by_ue_ebi(amf_ue, e_rab->e_RAB_ID); + ogs_assert(bearer); + + memcpy(&bearer->gnb_s1u_teid, e_rab->gTP_TEID.buf, + sizeof(bearer->gnb_s1u_teid)); + bearer->gnb_s1u_teid = ntohl(bearer->gnb_s1u_teid); + rv = ogs_asn_BIT_STRING_to_ip( + &e_rab->transportLayerAddress, &bearer->gnb_s1u_ip); + ogs_assert(rv == OGS_OK); + + ogs_debug(" EBI[%d]", bearer->ebi); + + if (OGS_FSM_CHECK(&bearer->sm, esm_state_active)) { + amf_bearer_t *linked_bearer = amf_linked_bearer(bearer); + ogs_assert(linked_bearer); + ogs_debug(" Linked-EBI[%d]", linked_bearer->ebi); + + if (bearer->ebi == linked_bearer->ebi) { + amf_gtp_send_modify_bearer_request(bearer, 0); + } else { + amf_gtp_send_create_bearer_response(bearer); + } + } + } +} + +void ngap_handle_ue_context_release_request( + amf_gnb_t *gnb, ogs_ngap_message_t *message) +{ + char buf[OGS_ADDRSTRLEN]; + int i; + + NGAP_InitiatingMessage_t *initiatingMessage = NULL; + NGAP_UEContextReleaseRequest_t *UEContextReleaseRequest = NULL; + + NGAP_UEContextReleaseRequest_IEs_t *ie = NULL; + NGAP_AMF_UE_NGAP_ID_t *AMF_UE_NGAP_ID = NULL; + NGAP_ENB_UE_NGAP_ID_t *ENB_UE_NGAP_ID = NULL; + NGAP_Cause_t *Cause = NULL; + + gnb_ue_t *gnb_ue = NULL; + + ogs_assert(gnb); + ogs_assert(gnb->sock); + + ogs_assert(message); + initiatingMessage = message->choice.initiatingMessage; + ogs_assert(initiatingMessage); + UEContextReleaseRequest = + &initiatingMessage->value.choice.UEContextReleaseRequest; + ogs_assert(UEContextReleaseRequest); + + ogs_debug("[AMF] UE Context release request"); + + for (i = 0; i < UEContextReleaseRequest->protocolIEs.list.count; i++) { + ie = UEContextReleaseRequest->protocolIEs.list.array[i]; + switch (ie->id) { + case NGAP_ProtocolIE_ID_id_AMF_UE_NGAP_ID: + AMF_UE_NGAP_ID = &ie->value.choice.AMF_UE_NGAP_ID; + break; + case NGAP_ProtocolIE_ID_id_eNB_UE_NGAP_ID: + ENB_UE_NGAP_ID = &ie->value.choice.ENB_UE_NGAP_ID; + break; + case NGAP_ProtocolIE_ID_id_Cause: + Cause = &ie->value.choice.Cause; + break; + default: + break; + } + } + + ogs_debug(" IP[%s] ENB_ID[%d]", + OGS_ADDR(gnb->addr, buf), gnb->gnb_id); + + ogs_assert(AMF_UE_NGAP_ID); + gnb_ue = gnb_ue_find_by_amf_ue_ngap_id(*AMF_UE_NGAP_ID); + if (!gnb_ue) { + ogs_warn("No ENB UE Context : AMF_UE_NGAP_ID[%d]", + (int)*AMF_UE_NGAP_ID); + ngap_send_error_indication(gnb, + AMF_UE_NGAP_ID, ENB_UE_NGAP_ID, + NGAP_Cause_PR_radioNetwork, + NGAP_CauseRadioNetwork_unknown_amf_ue_ngap_id); + return; + } + + ogs_debug(" ENB_UE_NGAP_ID[%d] AMF_UE_NGAP_ID[%d]", + gnb_ue->gnb_ue_ngap_id, gnb_ue->amf_ue_ngap_id); + + ogs_assert(Cause); + ogs_debug(" Cause[Group:%d Cause:%d]", + Cause->present, (int)Cause->choice.radioNetwork); + + switch (Cause->present) { + case NGAP_Cause_PR_radioNetwork: + case NGAP_Cause_PR_transport: + case NGAP_Cause_PR_protocol: + case NGAP_Cause_PR_misc: + break; + case NGAP_Cause_PR_nas: + ogs_warn("NAS-Cause[%d]", (int)Cause->choice.nas); + break; + default: + ogs_warn("Invalid cause group[%d]", Cause->present); + break; + } + + amf_send_release_access_bearer_or_ue_context_release(gnb_ue); +} + +void ngap_handle_ue_context_release_complete( + amf_gnb_t *gnb, ogs_ngap_message_t *message) +{ + int rv; + char buf[OGS_ADDRSTRLEN]; + int i; + + NGAP_SuccessfulOutcome_t *successfulOutcome = NULL; + NGAP_UEContextReleaseComplete_t *UEContextReleaseComplete = NULL; + + NGAP_UEContextReleaseComplete_IEs_t *ie = NULL; + NGAP_AMF_UE_NGAP_ID_t *AMF_UE_NGAP_ID = NULL; + + amf_ue_t *amf_ue = NULL; + gnb_ue_t *gnb_ue = NULL; + + ogs_assert(gnb); + ogs_assert(gnb->sock); + + ogs_assert(message); + successfulOutcome = message->choice.successfulOutcome; + ogs_assert(successfulOutcome); + UEContextReleaseComplete = + &successfulOutcome->value.choice.UEContextReleaseComplete; + ogs_assert(UEContextReleaseComplete); + + ogs_debug("[AMF] UE Context release complete"); + + for (i = 0; i < UEContextReleaseComplete->protocolIEs.list.count; i++) { + ie = UEContextReleaseComplete->protocolIEs.list.array[i]; + switch (ie->id) { + case NGAP_ProtocolIE_ID_id_AMF_UE_NGAP_ID: + AMF_UE_NGAP_ID = &ie->value.choice.AMF_UE_NGAP_ID; + break; + default: + break; + } + } + + ogs_debug(" IP[%s] ENB_ID[%d]", + OGS_ADDR(gnb->addr, buf), gnb->gnb_id); + + ogs_assert(AMF_UE_NGAP_ID); + gnb_ue = gnb_ue_find_by_amf_ue_ngap_id(*AMF_UE_NGAP_ID); + if (!gnb_ue) { + ogs_warn("No ENB UE Context : AMF_UE_NGAP_ID[%d]", + (int)*AMF_UE_NGAP_ID); + ngap_send_error_indication(gnb, + AMF_UE_NGAP_ID, NULL, + NGAP_Cause_PR_radioNetwork, + NGAP_CauseRadioNetwork_unknown_amf_ue_ngap_id); + return; + } + + amf_ue = gnb_ue->amf_ue; + + ogs_debug(" ENB_UE_NGAP_ID[%d] AMF_UE_NGAP_ID[%d]", + gnb_ue->gnb_ue_ngap_id, gnb_ue->amf_ue_ngap_id); + + switch (gnb_ue->ue_ctx_rel_action) { + case NGAP_UE_CTX_REL_S1_CONTEXT_REMOVE: + ogs_debug(" No Action"); + gnb_ue_remove(gnb_ue); + break; + case NGAP_UE_CTX_REL_S1_REMOVE_AND_UNLINK: + ogs_debug(" Action: S1 normal release"); + gnb_ue_remove(gnb_ue); + amf_ue_deassociate(amf_ue); + break; + case NGAP_UE_CTX_REL_UE_CONTEXT_REMOVE: + ogs_debug(" Action: UE context remove()"); + gnb_ue_remove(gnb_ue); + amf_ue_remove(amf_ue); + break; + case NGAP_UE_CTX_REL_DELETE_INDIRECT_TUNNEL: + ogs_debug(" Action: Delete indirect tunnel"); + + source_ue_deassociate_target_ue(gnb_ue); + gnb_ue_remove(gnb_ue); + + ogs_assert(amf_ue); + if (amf_ue_have_indirect_tunnel(amf_ue)) { + amf_gtp_send_delete_indirect_data_forwarding_tunnel_request( + amf_ue); + } else { + ogs_warn("Check your eNodeB"); + ogs_warn(" There is no INDIRECT TUNNEL"); + ogs_warn(" Packet could be dropped during S1-Handover"); + rv = amf_ue_clear_indirect_tunnel(amf_ue); + ogs_expect(rv == OGS_OK); + } + break; + default: + ogs_fatal("Invalid Action[%d]", gnb_ue->ue_ctx_rel_action); + ogs_assert_if_reached(); + break; + } +} + +void ngap_handle_path_switch_request( + amf_gnb_t *gnb, ogs_ngap_message_t *message) +{ + int rv; + char buf[OGS_ADDRSTRLEN]; + int i; + + NGAP_InitiatingMessage_t *initiatingMessage = NULL; + NGAP_PathSwitchRequest_t *PathSwitchRequest = NULL; + + NGAP_PathSwitchRequestIEs_t *ie = NULL; + NGAP_ENB_UE_NGAP_ID_t *ENB_UE_NGAP_ID = NULL; + NGAP_E_RABToBeSwitchedDLList_t *E_RABToBeSwitchedDLList = NULL; + NGAP_AMF_UE_NGAP_ID_t *AMF_UE_NGAP_ID = NULL; + NGAP_EUTRAN_CGI_t *EUTRAN_CGI = NULL; + NGAP_TAI_t *TAI = NULL; + NGAP_UESecurityCapabilities_t *UESecurityCapabilities = NULL; + + NGAP_PLMNidentity_t *pLMNidentity = NULL; + NGAP_CellIdentity_t *cell_ID = NULL; + NGAP_TAC_t *tAC = NULL; + NGAP_EncryptionAlgorithms_t *encryptionAlgorithms = NULL; + NGAP_IntegrityProtectionAlgorithms_t *integrityProtectionAlgorithms = NULL; + uint16_t eea = 0, eia = 0; + + gnb_ue_t *gnb_ue = NULL; + amf_ue_t *amf_ue = NULL; + ogs_pkbuf_t *ngapbuf = NULL; + + ogs_assert(gnb); + ogs_assert(gnb->sock); + + ogs_assert(message); + initiatingMessage = message->choice.initiatingMessage; + ogs_assert(initiatingMessage); + PathSwitchRequest = &initiatingMessage->value.choice.PathSwitchRequest; + ogs_assert(PathSwitchRequest); + + ogs_debug("[AMF] Path switch request"); + + for (i = 0; i < PathSwitchRequest->protocolIEs.list.count; i++) { + ie = PathSwitchRequest->protocolIEs.list.array[i]; + switch (ie->id) { + case NGAP_ProtocolIE_ID_id_eNB_UE_NGAP_ID: + ENB_UE_NGAP_ID = &ie->value.choice.ENB_UE_NGAP_ID; + break; + case NGAP_ProtocolIE_ID_id_E_RABToBeSwitchedDLList: + E_RABToBeSwitchedDLList = + &ie->value.choice.E_RABToBeSwitchedDLList; + break; + case NGAP_ProtocolIE_ID_id_SourceAMF_UE_NGAP_ID: + AMF_UE_NGAP_ID = &ie->value.choice.AMF_UE_NGAP_ID; + break; + case NGAP_ProtocolIE_ID_id_EUTRAN_CGI: + EUTRAN_CGI = &ie->value.choice.EUTRAN_CGI; + break; + case NGAP_ProtocolIE_ID_id_TAI: + TAI = &ie->value.choice.TAI; + break; + case NGAP_ProtocolIE_ID_id_UESecurityCapabilities: + UESecurityCapabilities = &ie->value.choice.UESecurityCapabilities; + break; + default: + break; + } + } + + ogs_debug(" IP[%s] ENB_ID[%d]", + OGS_ADDR(gnb->addr, buf), gnb->gnb_id); + + ogs_assert(EUTRAN_CGI); + pLMNidentity = &EUTRAN_CGI->pLMNidentity; + ogs_assert(pLMNidentity && pLMNidentity->size == sizeof(ogs_plmn_id_t)); + cell_ID = &EUTRAN_CGI->cell_ID; + ogs_assert(cell_ID); + + ogs_assert(TAI); + pLMNidentity = &TAI->pLMNidentity; + ogs_assert(pLMNidentity && pLMNidentity->size == sizeof(ogs_plmn_id_t)); + tAC = &TAI->tAC; + ogs_assert(tAC && tAC->size == sizeof(uint16_t)); + + ogs_assert(UESecurityCapabilities); + encryptionAlgorithms = + &UESecurityCapabilities->encryptionAlgorithms; + integrityProtectionAlgorithms = + &UESecurityCapabilities->integrityProtectionAlgorithms; + + ogs_assert(AMF_UE_NGAP_ID); + ogs_assert(ENB_UE_NGAP_ID); + gnb_ue = gnb_ue_find_by_amf_ue_ngap_id(*AMF_UE_NGAP_ID); + if (!gnb_ue) { + ogs_error("Cannot find UE from sourceAMF-UE-NGAP-ID[%d] and eNB[%s:%d]", + (int)*AMF_UE_NGAP_ID, OGS_ADDR(gnb->addr, buf), gnb->gnb_id); + + ngapbuf = ngap_build_path_switch_failure( + *ENB_UE_NGAP_ID, *AMF_UE_NGAP_ID, + NGAP_Cause_PR_radioNetwork, + NGAP_CauseRadioNetwork_unknown_amf_ue_ngap_id); + ogs_expect_or_return(ngapbuf); + + ogs_expect(OGS_OK == + ngap_send_to_gnb(gnb, ngapbuf, NGAP_NON_UE_SIGNALLING)); + return; + } + + ogs_debug(" ENB_UE_NGAP_ID[%d] AMF_UE_NGAP_ID[%d]", + gnb_ue->gnb_ue_ngap_id, gnb_ue->amf_ue_ngap_id); + + amf_ue = gnb_ue->amf_ue; + ogs_assert(amf_ue); + + if (SECURITY_CONTEXT_IS_VALID(amf_ue)) { + amf_ue->nhcc++; + amf_kdf_nh(amf_ue->kasme, amf_ue->nh, amf_ue->nh); + } else { + ngapbuf = ngap_build_path_switch_failure( + *ENB_UE_NGAP_ID, *AMF_UE_NGAP_ID, + NGAP_Cause_PR_nas, NGAP_CauseNas_authentication_failure); + ogs_expect_or_return(ngapbuf); + + ngap_send_to_gnb_ue(gnb_ue, ngapbuf); + return; + } + + gnb_ue->gnb_ue_ngap_id = *ENB_UE_NGAP_ID; + + memcpy(&gnb_ue->saved.tai.plmn_id, pLMNidentity->buf, + sizeof(gnb_ue->saved.tai.plmn_id)); + memcpy(&gnb_ue->saved.tai.tac, tAC->buf, sizeof(gnb_ue->saved.tai.tac)); + gnb_ue->saved.tai.tac = ntohs(gnb_ue->saved.tai.tac); + + memcpy(&gnb_ue->saved.e_cgi.plmn_id, pLMNidentity->buf, + sizeof(gnb_ue->saved.e_cgi.plmn_id)); + memcpy(&gnb_ue->saved.e_cgi.cell_id, cell_ID->buf, + sizeof(gnb_ue->saved.e_cgi.cell_id)); + gnb_ue->saved.e_cgi.cell_id = (ntohl(gnb_ue->saved.e_cgi.cell_id) >> 4); + + ogs_debug(" OLD TAI[PLMN_ID:%06x,TAC:%d]", + ogs_plmn_id_hexdump(&amf_ue->tai.plmn_id), + amf_ue->tai.tac); + ogs_debug(" OLD E_CGI[PLMN_ID:%06x,CELL_ID:%d]", + ogs_plmn_id_hexdump(&amf_ue->e_cgi.plmn_id), + amf_ue->e_cgi.cell_id); + ogs_debug(" TAI[PLMN_ID:%06x,TAC:%d]", + ogs_plmn_id_hexdump(&gnb_ue->saved.tai.plmn_id), + gnb_ue->saved.tai.tac); + ogs_debug(" E_CGI[PLMN_ID:%06x,CELL_ID:%d]", + ogs_plmn_id_hexdump(&gnb_ue->saved.e_cgi.plmn_id), + gnb_ue->saved.e_cgi.cell_id); + + /* Copy TAI and ECGI from gnb_ue */ + memcpy(&amf_ue->tai, &gnb_ue->saved.tai, sizeof(ogs_5gs_tai_t)); + memcpy(&amf_ue->e_cgi, &gnb_ue->saved.e_cgi, sizeof(ogs_e_cgi_t)); + + memcpy(&eea, encryptionAlgorithms->buf, sizeof(eea)); + eea = ntohs(eea); + amf_ue->ue_network_capability.eea = eea >> 9; + amf_ue->ue_network_capability.eea0 = 1; + + memcpy(&eia, integrityProtectionAlgorithms->buf, sizeof(eia)); + eia = ntohs(eia); + amf_ue->ue_network_capability.eia = eia >> 9; + amf_ue->ue_network_capability.eia0 = 0; + + ogs_assert(E_RABToBeSwitchedDLList); + for (i = 0; i < E_RABToBeSwitchedDLList->list.count; i++) { + NGAP_E_RABToBeSwitchedDLItemIEs_t *ie2 = NULL; + NGAP_E_RABToBeSwitchedDLItem_t *e_rab = NULL; + + amf_bearer_t *bearer = NULL; + + ie2 = (NGAP_E_RABToBeSwitchedDLItemIEs_t *) + E_RABToBeSwitchedDLList->list.array[i]; + ogs_assert(ie2); + + e_rab = &ie2->value.choice.E_RABToBeSwitchedDLItem; + ogs_assert(e_rab); + + bearer = amf_bearer_find_by_ue_ebi(amf_ue, e_rab->e_RAB_ID); + ogs_assert(bearer); + + memcpy(&bearer->gnb_s1u_teid, e_rab->gTP_TEID.buf, + sizeof(bearer->gnb_s1u_teid)); + bearer->gnb_s1u_teid = ntohl(bearer->gnb_s1u_teid); + rv = ogs_asn_BIT_STRING_to_ip( + &e_rab->transportLayerAddress, &bearer->gnb_s1u_ip); + ogs_expect(rv == OGS_OK); + + GTP_COUNTER_INCREMENT( + amf_ue, GTP_COUNTER_MODIFY_BEARER_BY_PATH_SWITCH); + + amf_gtp_send_modify_bearer_request(bearer, 1); + } + + /* Switch to gnb */ + gnb_ue_switch_to_gnb(gnb_ue, gnb); +} + +void ngap_handle_gnb_configuration_transfer( + amf_gnb_t *gnb, ogs_ngap_message_t *message, ogs_pkbuf_t *pkbuf) +{ + char buf[OGS_ADDRSTRLEN]; + int i; + + NGAP_InitiatingMessage_t *initiatingMessage = NULL; + NGAP_ENBConfigurationTransfer_t *ENBConfigurationTransfer = NULL; + + NGAP_ENBConfigurationTransferIEs_t *ie = NULL; + NGAP_SONConfigurationTransfer_t *SONConfigurationTransfer = NULL; + + ogs_assert(gnb); + ogs_assert(gnb->sock); + + ogs_assert(message); + initiatingMessage = message->choice.initiatingMessage; + ogs_assert(initiatingMessage); + ENBConfigurationTransfer = + &initiatingMessage->value.choice.ENBConfigurationTransfer; + ogs_assert(ENBConfigurationTransfer); + + ogs_debug("[AMF] ENB configuration transfer"); + for (i = 0; i < ENBConfigurationTransfer->protocolIEs.list.count; i++) { + ie = ENBConfigurationTransfer->protocolIEs.list.array[i]; + switch (ie->id) { + case NGAP_ProtocolIE_ID_id_SONConfigurationTransferECT: + SONConfigurationTransfer = + &ie->value.choice.SONConfigurationTransfer; + break; + default: + break; + } + } + + ogs_debug(" IP[%s] ENB_ID[%d]", + OGS_ADDR(gnb->addr, buf), gnb->gnb_id); + + if (SONConfigurationTransfer) { + NGAP_TargeteNB_ID_t *targeteNB_ID = + &SONConfigurationTransfer->targeteNB_ID; + NGAP_SourceeNB_ID_t *sourceeNB_ID = + &SONConfigurationTransfer->sourceeNB_ID; + + amf_gnb_t *target_gnb = NULL; + uint32_t source_gnb_id, target_gnb_id; + uint16_t source_tac, target_tac; + + ogs_ngap_ENB_ID_to_uint32( + &sourceeNB_ID->global_ENB_ID.eNB_ID, &source_gnb_id); + ogs_ngap_ENB_ID_to_uint32( + &targeteNB_ID->global_ENB_ID.eNB_ID, &target_gnb_id); + + memcpy(&source_tac, sourceeNB_ID->selected_TAI.tAC.buf, + sizeof(source_tac)); + source_tac = ntohs(source_tac); + memcpy(&target_tac, targeteNB_ID->selected_TAI.tAC.buf, + sizeof(target_tac)); + target_tac = ntohs(target_tac); + + ogs_debug(" Source : ENB_ID[%s:%d], TAC[%d]", + sourceeNB_ID->global_ENB_ID.eNB_ID.present == + NGAP_ENB_ID_PR_homeENB_ID ? "Home" : + sourceeNB_ID->global_ENB_ID.eNB_ID.present == + NGAP_ENB_ID_PR_macroENB_ID ? "Macro" : "Others", + source_gnb_id, source_tac); + ogs_debug(" Target : ENB_ID[%s:%d], TAC[%d]", + targeteNB_ID->global_ENB_ID.eNB_ID.present == + NGAP_ENB_ID_PR_homeENB_ID ? "Home" : + targeteNB_ID->global_ENB_ID.eNB_ID.present == + NGAP_ENB_ID_PR_macroENB_ID ? "Macro" : "Others", + target_gnb_id, target_tac); + + target_gnb = amf_gnb_find_by_gnb_id(target_gnb_id); + if (target_gnb == NULL) { + ogs_warn("eNB configuration transfer : cannot find target eNB-id[%d]", + target_gnb_id); + return; + } + + ngap_send_amf_configuration_transfer( + target_gnb, SONConfigurationTransfer); + } +} + +void ngap_handle_handover_required(amf_gnb_t *gnb, ogs_ngap_message_t *message) +{ + char buf[OGS_ADDRSTRLEN]; + int i; + + NGAP_InitiatingMessage_t *initiatingMessage = NULL; + NGAP_HandoverRequired_t *HandoverRequired = NULL; + + NGAP_HandoverRequiredIEs_t *ie = NULL; + NGAP_ENB_UE_NGAP_ID_t *ENB_UE_NGAP_ID = NULL; + NGAP_AMF_UE_NGAP_ID_t *AMF_UE_NGAP_ID = NULL; + NGAP_HandoverType_t *HandoverType = NULL; + NGAP_Cause_t *Cause = NULL; + NGAP_TargetID_t *TargetID = NULL; + NGAP_Source_ToTarget_TransparentContainer_t + *Source_ToTarget_TransparentContainer = NULL; + + ogs_assert(gnb); + ogs_assert(gnb->sock); + + ogs_assert(message); + initiatingMessage = message->choice.initiatingMessage; + ogs_assert(initiatingMessage); + HandoverRequired = &initiatingMessage->value.choice.HandoverRequired; + ogs_assert(HandoverRequired); + + gnb_ue_t *source_ue = NULL; + amf_ue_t *amf_ue = NULL; + amf_gnb_t *target_gnb = NULL; + uint32_t target_gnb_id = 0; + + ogs_debug("[AMF] Handover required"); + for (i = 0; i < HandoverRequired->protocolIEs.list.count; i++) { + ie = HandoverRequired->protocolIEs.list.array[i]; + switch (ie->id) { + case NGAP_ProtocolIE_ID_id_eNB_UE_NGAP_ID: + ENB_UE_NGAP_ID = &ie->value.choice.ENB_UE_NGAP_ID; + break; + case NGAP_ProtocolIE_ID_id_AMF_UE_NGAP_ID: + AMF_UE_NGAP_ID = &ie->value.choice.AMF_UE_NGAP_ID; + break; + case NGAP_ProtocolIE_ID_id_HandoverType: + HandoverType = &ie->value.choice.HandoverType; + break; + case NGAP_ProtocolIE_ID_id_Cause: + Cause = &ie->value.choice.Cause; + break; + case NGAP_ProtocolIE_ID_id_TargetID: + TargetID = &ie->value.choice.TargetID; + break; + case NGAP_ProtocolIE_ID_id_Source_ToTarget_TransparentContainer: + Source_ToTarget_TransparentContainer = + &ie->value.choice.Source_ToTarget_TransparentContainer; + break; + default: + break; + } + } + + ogs_debug(" IP[%s] ENB_ID[%d]", + OGS_ADDR(gnb->addr, buf), gnb->gnb_id); + + ogs_assert(TargetID); + switch (TargetID->present) { + case NGAP_TargetID_PR_targeteNB_ID: + ogs_ngap_ENB_ID_to_uint32( + &TargetID->choice.targeteNB_ID->global_ENB_ID.eNB_ID, + &target_gnb_id); + break; + default: + ogs_error("Not implemented(%d)", TargetID->present); + return; + } + + target_gnb = amf_gnb_find_by_gnb_id(target_gnb_id); + if (target_gnb == NULL) { + ogs_warn("Handover required : cannot find target eNB-id[%d]", + target_gnb_id); + return; + } + + ogs_assert(ENB_UE_NGAP_ID); + ogs_assert(AMF_UE_NGAP_ID); + source_ue = gnb_ue_find_by_gnb_ue_ngap_id(gnb, *ENB_UE_NGAP_ID); + ogs_assert(source_ue); + ogs_assert(source_ue->amf_ue_ngap_id == *AMF_UE_NGAP_ID); + + ogs_debug(" Source : ENB_UE_NGAP_ID[%d] AMF_UE_NGAP_ID[%d]", + source_ue->gnb_ue_ngap_id, source_ue->amf_ue_ngap_id); + + amf_ue = source_ue->amf_ue; + ogs_assert(amf_ue); + + if (SECURITY_CONTEXT_IS_VALID(amf_ue)) { + amf_ue->nhcc++; + amf_kdf_nh(amf_ue->kasme, amf_ue->nh, amf_ue->nh); + } else { + ogs_assert(Cause); + + ngap_send_handover_preparation_failure(source_ue, Cause); + + return; + } + + ogs_assert(HandoverType); + source_ue->handover_type = *HandoverType; + + ngap_send_handover_request(amf_ue, target_gnb, + ENB_UE_NGAP_ID, AMF_UE_NGAP_ID, + HandoverType, Cause, + Source_ToTarget_TransparentContainer); +} + +void ngap_handle_handover_request_ack(amf_gnb_t *gnb, ogs_ngap_message_t *message) +{ + int rv; + char buf[OGS_ADDRSTRLEN]; + int i; + + NGAP_SuccessfulOutcome_t *successfulOutcome = NULL; + NGAP_HandoverRequestAcknowledge_t *HandoverRequestAcknowledge = NULL; + + NGAP_HandoverRequestAcknowledgeIEs_t *ie = NULL; + NGAP_AMF_UE_NGAP_ID_t *AMF_UE_NGAP_ID = NULL; + NGAP_ENB_UE_NGAP_ID_t *ENB_UE_NGAP_ID = NULL; + NGAP_E_RABAdmittedList_t *E_RABAdmittedList = NULL; + NGAP_Target_ToSource_TransparentContainer_t + *Target_ToSource_TransparentContainer = NULL; + + gnb_ue_t *source_ue = NULL; + gnb_ue_t *target_ue = NULL; + amf_ue_t *amf_ue = NULL; + + ogs_assert(gnb); + ogs_assert(gnb->sock); + + ogs_assert(message); + successfulOutcome = message->choice.successfulOutcome; + ogs_assert(successfulOutcome); + HandoverRequestAcknowledge = + &successfulOutcome->value.choice.HandoverRequestAcknowledge; + ogs_assert(HandoverRequestAcknowledge); + + ogs_debug("[AMF] Handover request acknowledge"); + for (i = 0; i < HandoverRequestAcknowledge->protocolIEs.list.count; i++) { + ie = HandoverRequestAcknowledge->protocolIEs.list.array[i]; + switch (ie->id) { + case NGAP_ProtocolIE_ID_id_AMF_UE_NGAP_ID: + AMF_UE_NGAP_ID = &ie->value.choice.AMF_UE_NGAP_ID; + break; + case NGAP_ProtocolIE_ID_id_eNB_UE_NGAP_ID: + ENB_UE_NGAP_ID = &ie->value.choice.ENB_UE_NGAP_ID; + break; + case NGAP_ProtocolIE_ID_id_E_RABAdmittedList: + E_RABAdmittedList = &ie->value.choice.E_RABAdmittedList; + break; + case NGAP_ProtocolIE_ID_id_Target_ToSource_TransparentContainer: + Target_ToSource_TransparentContainer = + &ie->value.choice.Target_ToSource_TransparentContainer; + break; + default: + break; + } + } + ogs_debug(" IP[%s] ENB_ID[%d]", + OGS_ADDR(gnb->addr, buf), gnb->gnb_id); + + ogs_assert(AMF_UE_NGAP_ID); + ogs_assert(ENB_UE_NGAP_ID); + ogs_assert(E_RABAdmittedList); + ogs_assert(Target_ToSource_TransparentContainer); + + target_ue = gnb_ue_find_by_amf_ue_ngap_id(*AMF_UE_NGAP_ID); + ogs_assert(target_ue); + + target_ue->gnb_ue_ngap_id = *ENB_UE_NGAP_ID; + + source_ue = target_ue->source_ue; + ogs_assert(source_ue); + amf_ue = source_ue->amf_ue; + ogs_assert(amf_ue); + + ogs_debug(" Source : ENB_UE_NGAP_ID[%d] AMF_UE_NGAP_ID[%d]", + source_ue->gnb_ue_ngap_id, source_ue->amf_ue_ngap_id); + ogs_debug(" Target : ENB_UE_NGAP_ID[%d] AMF_UE_NGAP_ID[%d]", + target_ue->gnb_ue_ngap_id, target_ue->amf_ue_ngap_id); + + for (i = 0; i < E_RABAdmittedList->list.count; i++) { + NGAP_E_RABAdmittedItemIEs_t *ie2 = NULL; + NGAP_E_RABAdmittedItem_t *e_rab = NULL; + + amf_bearer_t *bearer = NULL; + + ie2 = (NGAP_E_RABAdmittedItemIEs_t *)E_RABAdmittedList->list.array[i]; + ogs_assert(ie2); + + e_rab = &ie2->value.choice.E_RABAdmittedItem; + ogs_assert(e_rab); + + bearer = amf_bearer_find_by_ue_ebi(amf_ue, e_rab->e_RAB_ID); + ogs_assert(bearer); + + memcpy(&bearer->target_s1u_teid, e_rab->gTP_TEID.buf, + sizeof(bearer->target_s1u_teid)); + bearer->target_s1u_teid = ntohl(bearer->target_s1u_teid); + rv = ogs_asn_BIT_STRING_to_ip( + &e_rab->transportLayerAddress, &bearer->target_s1u_ip); + ogs_assert(rv == OGS_OK); + + if (e_rab->dL_transportLayerAddress && e_rab->dL_gTP_TEID) { + ogs_assert(e_rab->dL_gTP_TEID->buf); + ogs_assert(e_rab->dL_transportLayerAddress->buf); + memcpy(&bearer->gnb_dl_teid, e_rab->dL_gTP_TEID->buf, + sizeof(bearer->gnb_dl_teid)); + bearer->gnb_dl_teid = ntohl(bearer->gnb_dl_teid); + rv = ogs_asn_BIT_STRING_to_ip( + e_rab->dL_transportLayerAddress, &bearer->gnb_dl_ip); + ogs_assert(rv == OGS_OK); + } + + if (e_rab->uL_TransportLayerAddress && e_rab->uL_GTP_TEID) { + ogs_assert(e_rab->uL_GTP_TEID->buf); + ogs_assert(e_rab->uL_TransportLayerAddress->buf); + memcpy(&bearer->gnb_ul_teid, e_rab->uL_GTP_TEID->buf, + sizeof(bearer->gnb_ul_teid)); + bearer->gnb_ul_teid = ntohl(bearer->gnb_ul_teid); + rv = ogs_asn_BIT_STRING_to_ip( + e_rab->uL_TransportLayerAddress, &bearer->gnb_ul_ip); + ogs_assert(rv == OGS_OK); + } + } + + OGS_NGAP_STORE_DATA(&amf_ue->container, + Target_ToSource_TransparentContainer); + + if (amf_ue_have_indirect_tunnel(amf_ue) == 1) { + amf_gtp_send_create_indirect_data_forwarding_tunnel_request( + amf_ue); + } else { + ngap_send_handover_command(source_ue); + } +} + +void ngap_handle_handover_failure(amf_gnb_t *gnb, ogs_ngap_message_t *message) +{ + char buf[OGS_ADDRSTRLEN]; + int i; + + NGAP_UnsuccessfulOutcome_t *unsuccessfulOutcome = NULL; + NGAP_HandoverFailure_t *HandoverFailure = NULL; + + NGAP_HandoverFailureIEs_t *ie = NULL; + NGAP_AMF_UE_NGAP_ID_t *AMF_UE_NGAP_ID = NULL; + NGAP_Cause_t *Cause = NULL; + + gnb_ue_t *target_ue = NULL; + gnb_ue_t *source_ue = NULL; + + ogs_assert(gnb); + ogs_assert(gnb->sock); + + ogs_assert(message); + unsuccessfulOutcome = message->choice.unsuccessfulOutcome; + ogs_assert(unsuccessfulOutcome); + HandoverFailure = &unsuccessfulOutcome->value.choice.HandoverFailure; + ogs_assert(HandoverFailure); + + ogs_debug("[AMF] Handover failure"); + for (i = 0; i < HandoverFailure->protocolIEs.list.count; i++) { + ie = HandoverFailure->protocolIEs.list.array[i]; + switch (ie->id) { + case NGAP_ProtocolIE_ID_id_AMF_UE_NGAP_ID: + AMF_UE_NGAP_ID = &ie->value.choice.AMF_UE_NGAP_ID; + break; + case NGAP_ProtocolIE_ID_id_Cause: + Cause = &ie->value.choice.Cause; + break; + default: + break; + } + } + + ogs_debug(" IP[%s] ENB_ID[%d]", + OGS_ADDR(gnb->addr, buf), gnb->gnb_id); + + ogs_assert(AMF_UE_NGAP_ID); + ogs_assert(Cause); + + target_ue = gnb_ue_find_by_amf_ue_ngap_id(*AMF_UE_NGAP_ID); + ogs_assert(target_ue); + + source_ue = target_ue->source_ue; + ogs_assert(source_ue); + + ogs_debug(" Source : ENB_UE_NGAP_ID[%d] AMF_UE_NGAP_ID[%d]", + source_ue->gnb_ue_ngap_id, source_ue->amf_ue_ngap_id); + ogs_debug(" Target : ENB_UE_NGAP_ID[%d] AMF_UE_NGAP_ID[%d]", + target_ue->gnb_ue_ngap_id, target_ue->amf_ue_ngap_id); + + ngap_send_handover_preparation_failure(source_ue, Cause); + + ngap_send_ue_context_release_command( + target_ue, NGAP_Cause_PR_radioNetwork, + NGAP_CauseRadioNetwork_ho_failure_in_target_EPC_eNB_or_target_system, + NGAP_UE_CTX_REL_DELETE_INDIRECT_TUNNEL, 0); +} + +void ngap_handle_handover_cancel(amf_gnb_t *gnb, ogs_ngap_message_t *message) +{ + char buf[OGS_ADDRSTRLEN]; + int i; + + NGAP_InitiatingMessage_t *initiatingMessage = NULL; + NGAP_HandoverCancel_t *HandoverCancel = NULL; + + NGAP_HandoverCancelIEs_t *ie = NULL; + NGAP_AMF_UE_NGAP_ID_t *AMF_UE_NGAP_ID = NULL; + NGAP_ENB_UE_NGAP_ID_t *ENB_UE_NGAP_ID = NULL; + NGAP_Cause_t *Cause = NULL; + + gnb_ue_t *source_ue = NULL; + gnb_ue_t *target_ue = NULL; + + ogs_assert(gnb); + ogs_assert(gnb->sock); + + ogs_assert(message); + initiatingMessage = message->choice.initiatingMessage; + ogs_assert(initiatingMessage); + HandoverCancel = &initiatingMessage->value.choice.HandoverCancel; + ogs_assert(HandoverCancel); + + ogs_debug("[AMF] Handover cancel"); + for (i = 0; i < HandoverCancel->protocolIEs.list.count; i++) { + ie = HandoverCancel->protocolIEs.list.array[i]; + switch (ie->id) { + case NGAP_ProtocolIE_ID_id_AMF_UE_NGAP_ID: + AMF_UE_NGAP_ID = &ie->value.choice.AMF_UE_NGAP_ID; + break; + case NGAP_ProtocolIE_ID_id_eNB_UE_NGAP_ID: + ENB_UE_NGAP_ID = &ie->value.choice.ENB_UE_NGAP_ID; + break; + case NGAP_ProtocolIE_ID_id_Cause: + Cause = &ie->value.choice.Cause; + break; + default: + break; + } + } + ogs_debug(" IP[%s] ENB_ID[%d]", + OGS_ADDR(gnb->addr, buf), gnb->gnb_id); + + ogs_assert(AMF_UE_NGAP_ID); + ogs_assert(ENB_UE_NGAP_ID); + ogs_assert(Cause); + + source_ue = gnb_ue_find_by_gnb_ue_ngap_id(gnb, *ENB_UE_NGAP_ID); + ogs_assert(source_ue); + ogs_assert(source_ue->amf_ue_ngap_id == *AMF_UE_NGAP_ID); + + target_ue = source_ue->target_ue; + ogs_assert(target_ue); + + ogs_debug(" Source : ENB_UE_NGAP_ID[%d] AMF_UE_NGAP_ID[%d]", + source_ue->gnb_ue_ngap_id, source_ue->amf_ue_ngap_id); + ogs_debug(" Target : ENB_UE_NGAP_ID[%d] AMF_UE_NGAP_ID[%d]", + target_ue->gnb_ue_ngap_id, target_ue->amf_ue_ngap_id); + + ngap_send_handover_cancel_ack(source_ue); + + ngap_send_ue_context_release_command( + target_ue, NGAP_Cause_PR_radioNetwork, + NGAP_CauseRadioNetwork_handover_cancelled, + NGAP_UE_CTX_REL_DELETE_INDIRECT_TUNNEL, + ogs_time_from_msec(300)); + + ogs_debug("[AMF] Handover Cancel : " + "UE[eNB-UE-NGAP-ID(%d)] --> eNB[%s:%d]", + source_ue->gnb_ue_ngap_id, + OGS_ADDR(gnb->addr, buf), gnb->gnb_id); +} + +void ngap_handle_gnb_status_transfer(amf_gnb_t *gnb, ogs_ngap_message_t *message) +{ + char buf[OGS_ADDRSTRLEN]; + int i; + + NGAP_InitiatingMessage_t *initiatingMessage = NULL; + NGAP_ENBStatusTransfer_t *ENBStatusTransfer = NULL; + + NGAP_ENBStatusTransferIEs_t *ie = NULL; + NGAP_AMF_UE_NGAP_ID_t *AMF_UE_NGAP_ID = NULL; + NGAP_ENB_UE_NGAP_ID_t *ENB_UE_NGAP_ID = NULL; + NGAP_ENB_StatusTransfer_TransparentContainer_t + *ENB_StatusTransfer_TransparentContainer = NULL; + + gnb_ue_t *source_ue = NULL, *target_ue = NULL; + + ogs_assert(gnb); + ogs_assert(gnb->sock); + + ogs_assert(message); + initiatingMessage = message->choice.initiatingMessage; + ogs_assert(initiatingMessage); + ENBStatusTransfer = &initiatingMessage->value.choice.ENBStatusTransfer; + ogs_assert(ENBStatusTransfer); + + ogs_debug("[AMF] ENB status transfer"); + for (i = 0; i < ENBStatusTransfer->protocolIEs.list.count; i++) { + ie = ENBStatusTransfer->protocolIEs.list.array[i]; + switch (ie->id) { + case NGAP_ProtocolIE_ID_id_AMF_UE_NGAP_ID: + AMF_UE_NGAP_ID = &ie->value.choice.AMF_UE_NGAP_ID; + break; + case NGAP_ProtocolIE_ID_id_eNB_UE_NGAP_ID: + ENB_UE_NGAP_ID = &ie->value.choice.ENB_UE_NGAP_ID; + break; + case NGAP_ProtocolIE_ID_id_eNB_StatusTransfer_TransparentContainer: + ENB_StatusTransfer_TransparentContainer = + &ie->value.choice.ENB_StatusTransfer_TransparentContainer; + break; + default: + break; + } + } + ogs_debug(" IP[%s] ENB_ID[%d]", + OGS_ADDR(gnb->addr, buf), gnb->gnb_id); + + ogs_assert(AMF_UE_NGAP_ID); + ogs_assert(ENB_UE_NGAP_ID); + ogs_assert(ENB_StatusTransfer_TransparentContainer); + + source_ue = gnb_ue_find_by_gnb_ue_ngap_id(gnb, *ENB_UE_NGAP_ID); + ogs_assert(source_ue); + ogs_assert(source_ue->amf_ue_ngap_id == *AMF_UE_NGAP_ID); + + target_ue = source_ue->target_ue; + ogs_assert(target_ue); + + ogs_debug(" Source : ENB_UE_NGAP_ID[%d] AMF_UE_NGAP_ID[%d]", + source_ue->gnb_ue_ngap_id, source_ue->amf_ue_ngap_id); + ogs_debug(" Target : ENB_UE_NGAP_ID[%d] AMF_UE_NGAP_ID[%d]", + target_ue->gnb_ue_ngap_id, target_ue->amf_ue_ngap_id); + + ngap_send_amf_status_transfer(target_ue, + ENB_StatusTransfer_TransparentContainer); +} + +void ngap_handle_handover_notification(amf_gnb_t *gnb, ogs_ngap_message_t *message) +{ + char buf[OGS_ADDRSTRLEN]; + int i; + + NGAP_InitiatingMessage_t *initiatingMessage = NULL; + NGAP_HandoverNotify_t *HandoverNotify = NULL; + + NGAP_HandoverNotifyIEs_t *ie = NULL; + NGAP_AMF_UE_NGAP_ID_t *AMF_UE_NGAP_ID = NULL; + NGAP_ENB_UE_NGAP_ID_t *ENB_UE_NGAP_ID = NULL; + NGAP_EUTRAN_CGI_t *EUTRAN_CGI = NULL; + NGAP_TAI_t *TAI = NULL; + + NGAP_PLMNidentity_t *pLMNidentity = NULL; + NGAP_CellIdentity_t *cell_ID = NULL; + NGAP_TAC_t *tAC = NULL; + + gnb_ue_t *source_ue = NULL; + gnb_ue_t *target_ue = NULL; + amf_ue_t *amf_ue = NULL; + amf_sess_t *sess = NULL; + amf_bearer_t *bearer = NULL; + + ogs_assert(gnb); + ogs_assert(gnb->sock); + + ogs_assert(message); + initiatingMessage = message->choice.initiatingMessage; + ogs_assert(initiatingMessage); + HandoverNotify = &initiatingMessage->value.choice.HandoverNotify; + ogs_assert(HandoverNotify); + + ogs_debug("[AMF] Handover notification"); + for (i = 0; i < HandoverNotify->protocolIEs.list.count; i++) { + ie = HandoverNotify->protocolIEs.list.array[i]; + switch (ie->id) { + case NGAP_ProtocolIE_ID_id_AMF_UE_NGAP_ID: + AMF_UE_NGAP_ID = &ie->value.choice.AMF_UE_NGAP_ID; + break; + case NGAP_ProtocolIE_ID_id_eNB_UE_NGAP_ID: + ENB_UE_NGAP_ID = &ie->value.choice.ENB_UE_NGAP_ID; + break; + case NGAP_ProtocolIE_ID_id_EUTRAN_CGI: + EUTRAN_CGI = &ie->value.choice.EUTRAN_CGI; + break; + case NGAP_ProtocolIE_ID_id_TAI: + TAI = &ie->value.choice.TAI; + break; + default: + break; + } + } + ogs_debug(" IP[%s] ENB_ID[%d]", + OGS_ADDR(gnb->addr, buf), gnb->gnb_id); + + ogs_assert(EUTRAN_CGI); + pLMNidentity = &EUTRAN_CGI->pLMNidentity; + ogs_assert(pLMNidentity && pLMNidentity->size == sizeof(ogs_plmn_id_t)); + cell_ID = &EUTRAN_CGI->cell_ID; + ogs_assert(cell_ID); + + ogs_assert(TAI); + pLMNidentity = &TAI->pLMNidentity; + ogs_assert(pLMNidentity && pLMNidentity->size == sizeof(ogs_plmn_id_t)); + tAC = &TAI->tAC; + ogs_assert(tAC && tAC->size == sizeof(uint16_t)); + + ogs_assert(ENB_UE_NGAP_ID); + ogs_assert(AMF_UE_NGAP_ID); + target_ue = gnb_ue_find_by_gnb_ue_ngap_id(gnb, *ENB_UE_NGAP_ID); + ogs_assert(target_ue); + ogs_assert(target_ue->amf_ue_ngap_id == *AMF_UE_NGAP_ID); + + source_ue = target_ue->source_ue; + ogs_assert(source_ue); + amf_ue = source_ue->amf_ue; + ogs_assert(amf_ue); + + ogs_debug(" Source : ENB_UE_NGAP_ID[%d] AMF_UE_NGAP_ID[%d]", + source_ue->gnb_ue_ngap_id, source_ue->amf_ue_ngap_id); + ogs_debug(" Target : ENB_UE_NGAP_ID[%d] AMF_UE_NGAP_ID[%d]", + target_ue->gnb_ue_ngap_id, target_ue->amf_ue_ngap_id); + + amf_ue_associate_gnb_ue(amf_ue, target_ue); + + memcpy(&target_ue->saved.tai.plmn_id, pLMNidentity->buf, + sizeof(target_ue->saved.tai.plmn_id)); + memcpy(&target_ue->saved.tai.tac, + tAC->buf, sizeof(target_ue->saved.tai.tac)); + target_ue->saved.tai.tac = ntohs(target_ue->saved.tai.tac); + + memcpy(&target_ue->saved.e_cgi.plmn_id, pLMNidentity->buf, + sizeof(target_ue->saved.e_cgi.plmn_id)); + memcpy(&target_ue->saved.e_cgi.cell_id, cell_ID->buf, + sizeof(target_ue->saved.e_cgi.cell_id)); + target_ue->saved.e_cgi.cell_id = + (ntohl(target_ue->saved.e_cgi.cell_id) >> 4); + + ogs_debug(" OLD TAI[PLMN_ID:%06x,TAC:%d]", + ogs_plmn_id_hexdump(&amf_ue->tai.plmn_id), + amf_ue->tai.tac); + ogs_debug(" OLD E_CGI[PLMN_ID:%06x,CELL_ID:%d]", + ogs_plmn_id_hexdump(&amf_ue->e_cgi.plmn_id), + amf_ue->e_cgi.cell_id); + ogs_debug(" TAI[PLMN_ID:%06x,TAC:%d]", + ogs_plmn_id_hexdump(&target_ue->saved.tai.plmn_id), + target_ue->saved.tai.tac); + ogs_debug(" E_CGI[PLMN_ID:%06x,CELL_ID:%d]", + ogs_plmn_id_hexdump(&target_ue->saved.e_cgi.plmn_id), + target_ue->saved.e_cgi.cell_id); + + /* Copy TAI and ECGI from gnb_ue */ + memcpy(&amf_ue->tai, &target_ue->saved.tai, sizeof(ogs_5gs_tai_t)); + memcpy(&amf_ue->e_cgi, &target_ue->saved.e_cgi, sizeof(ogs_e_cgi_t)); + + sess = amf_sess_first(amf_ue); + while (sess) { + bearer = amf_bearer_first(sess); + while (bearer) { + bearer->gnb_s1u_teid = bearer->target_s1u_teid; + memcpy(&bearer->gnb_s1u_ip, &bearer->target_s1u_ip, + sizeof(ogs_ip_t)); + + GTP_COUNTER_INCREMENT( + amf_ue, GTP_COUNTER_MODIFY_BEARER_BY_HANDOVER_NOTIFY); + + amf_gtp_send_modify_bearer_request(bearer, 1); + + bearer = amf_bearer_next(bearer); + } + sess = amf_sess_next(sess); + } +} + +void ngap_handle_s1_reset( + amf_gnb_t *gnb, ogs_ngap_message_t *message) +{ + char buf[OGS_ADDRSTRLEN]; + int i; + + NGAP_InitiatingMessage_t *initiatingMessage = NULL; + NGAP_Reset_t *Reset = NULL; + + NGAP_ResetIEs_t *ie = NULL; + NGAP_Cause_t *Cause = NULL; + NGAP_ResetType_t *ResetType = NULL; + NGAP_UE_associatedLogicalS1_ConnectionListRes_t *partOfS1_Interface = NULL; + + ogs_assert(gnb); + ogs_assert(gnb->sock); + + ogs_assert(message); + initiatingMessage = message->choice.initiatingMessage; + ogs_assert(initiatingMessage); + Reset = &initiatingMessage->value.choice.Reset; + ogs_assert(Reset); + + ogs_debug("[AMF] Reset"); + + for (i = 0; i < Reset->protocolIEs.list.count; i++) { + ie = Reset->protocolIEs.list.array[i]; + switch (ie->id) { + case NGAP_ProtocolIE_ID_id_Cause: + Cause = &ie->value.choice.Cause; + break; + case NGAP_ProtocolIE_ID_id_ResetType: + ResetType = &ie->value.choice.ResetType; + break; + default: + break; + } + } + + ogs_debug(" IP[%s] ENB_ID[%d]", + OGS_ADDR(gnb->addr, buf), gnb->gnb_id); + + ogs_assert(Cause); + ogs_debug(" Cause[Group:%d Cause:%d]", + Cause->present, (int)Cause->choice.radioNetwork); + + switch (Cause->present) { + case NGAP_Cause_PR_radioNetwork: + case NGAP_Cause_PR_transport: + case NGAP_Cause_PR_protocol: + case NGAP_Cause_PR_misc: + break; + case NGAP_Cause_PR_nas: + ogs_warn("NAS-Cause[%d]", (int)Cause->choice.nas); + break; + default: + ogs_warn("Invalid cause group[%d]", Cause->present); + break; + } + + ogs_assert(ResetType); + switch (ResetType->present) { + case NGAP_ResetType_PR_s1_Interface: + ogs_debug(" NGAP_ResetType_PR_s1_Interface"); + + gnb_ue_remove_in_gnb(gnb); + break; + case NGAP_ResetType_PR_partOfS1_Interface: + ogs_debug(" NGAP_ResetType_PR_partOfS1_Interface"); + + partOfS1_Interface = ResetType->choice.partOfS1_Interface; + ogs_assert(partOfS1_Interface); + for (i = 0; i < partOfS1_Interface->list.count; i++) { + NGAP_UE_associatedLogicalS1_ConnectionItemRes_t *ie2 = NULL; + NGAP_UE_associatedLogicalS1_ConnectionItem_t *item = NULL; + + gnb_ue_t *gnb_ue = NULL; + + ie2 = (NGAP_UE_associatedLogicalS1_ConnectionItemRes_t *) + partOfS1_Interface->list.array[i]; + ogs_assert(ie2); + + item = &ie2->value.choice.UE_associatedLogicalS1_ConnectionItem; + ogs_assert(item); + + ogs_debug(" AMF_UE_NGAP_ID[%d] ENB_UE_NGAP_ID[%d]", + item->mME_UE_NGAP_ID ? (int)*item->mME_UE_NGAP_ID : -1, + item->eNB_UE_NGAP_ID ? (int)*item->eNB_UE_NGAP_ID : -1); + + if (item->mME_UE_NGAP_ID) + gnb_ue = gnb_ue_find_by_amf_ue_ngap_id( + *item->mME_UE_NGAP_ID); + else if (item->eNB_UE_NGAP_ID) + gnb_ue = gnb_ue_find_by_gnb_ue_ngap_id(gnb, + *item->eNB_UE_NGAP_ID); + + if (gnb_ue == NULL) { + ogs_warn("Cannot find S1 Context " + "(AMF_UE_NGAP_ID[%d] ENB_UE_NGAP_ID[%d])", + item->mME_UE_NGAP_ID ? (int)*item->mME_UE_NGAP_ID : -1, + item->eNB_UE_NGAP_ID ? (int)*item->eNB_UE_NGAP_ID : -1); + continue; + } + + gnb_ue_remove(gnb_ue); + } + break; + default: + ogs_warn("Invalid ResetType[%d]", ResetType->present); + break; + } + + ngap_send_s1_reset_ack(gnb, partOfS1_Interface); +} + +void ngap_handle_write_replace_warning_response( + amf_gnb_t *gnb, ogs_ngap_message_t *message) +{ + char buf[OGS_ADDRSTRLEN]; + + NGAP_SuccessfulOutcome_t *successfulOutcome = NULL; + NGAP_WriteReplaceWarningResponse_t *WriteReplaceWarningResponse = NULL; + + ogs_assert(gnb); + ogs_assert(gnb->sock); + + ogs_assert(message); + successfulOutcome = message->choice.successfulOutcome; + ogs_assert(successfulOutcome); + WriteReplaceWarningResponse = + &successfulOutcome->value.choice.WriteReplaceWarningResponse; + ogs_assert(WriteReplaceWarningResponse); + + ogs_debug("[AMF] Write replace warning response"); + + ogs_debug(" IP[%s] ENB_ID[%d]", + OGS_ADDR(gnb->addr, buf), gnb->gnb_id); + +} + +void ngap_handle_kill_response( + amf_gnb_t *gnb, ogs_ngap_message_t *message) +{ + char buf[OGS_ADDRSTRLEN]; + + NGAP_SuccessfulOutcome_t *successfulOutcome = NULL; + NGAP_KillResponse_t *KillResponse = NULL; + + ogs_assert(gnb); + ogs_assert(gnb->sock); + + ogs_assert(message); + successfulOutcome = message->choice.successfulOutcome; + ogs_assert(successfulOutcome); + KillResponse = + &successfulOutcome->value.choice.KillResponse; + ogs_assert(KillResponse); + + ogs_debug("[AMF] Kill response"); + + ogs_debug(" IP[%s] ENB_ID[%d]", + OGS_ADDR(gnb->addr, buf), gnb->gnb_id); +} +#endif diff --git a/src/amf/ngap-handler.h b/src/amf/ngap-handler.h new file mode 100644 index 000000000..b1ff28e25 --- /dev/null +++ b/src/amf/ngap-handler.h @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2019,2020 by Sukchan Lee + * + * This file is part of Open5GS. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 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, see . + */ + +#ifndef NGAP_HANDLER_H +#define NGAP_HANDLER_H + +#include "context.h" + +#ifdef __cplusplus +extern "C" { +#endif + +void ngap_handle_ng_setup_request( + amf_gnb_t *gnb, ogs_ngap_message_t *message); +#if 0 +void ngap_handle_initial_ue_message( + amf_gnb_t *gnb, ogs_ngap_message_t *message); +void ngap_handle_uplink_nas_transport( + amf_gnb_t *gnb, ogs_ngap_message_t *message); +void ngap_handle_ue_capability_info_indication( + amf_gnb_t *gnb, ogs_ngap_message_t *message); +void ngap_handle_initial_context_setup_response( + amf_gnb_t *gnb, ogs_ngap_message_t *message); +void ngap_handle_initial_context_setup_failure( + amf_gnb_t *gnb, ogs_ngap_message_t *message); + +void ngap_handle_ue_context_modification_response( + amf_gnb_t *gnb, ogs_ngap_message_t *message); +void ngap_handle_ue_context_modification_failure( + amf_gnb_t *gnb, ogs_ngap_message_t *message); + +void ngap_handle_ue_context_release_request( + amf_gnb_t *gnb, ogs_ngap_message_t *message); +void ngap_handle_ue_context_release_complete( + amf_gnb_t *gnb, ogs_ngap_message_t *message); + +void ngap_handle_e_rab_setup_response( + amf_gnb_t *gnb, ogs_ngap_message_t *message); + +void ngap_handle_path_switch_request( + amf_gnb_t *gnb, ogs_ngap_message_t *message); + +void ngap_handle_handover_required( + amf_gnb_t *gnb, ogs_ngap_message_t *message); +void ngap_handle_handover_request_ack( + amf_gnb_t *gnb, ogs_ngap_message_t *message); +void ngap_handle_handover_failure( + amf_gnb_t *gnb, ogs_ngap_message_t *message); +void ngap_handle_handover_cancel( + amf_gnb_t *gnb, ogs_ngap_message_t *message); + +void ngap_handle_gnb_status_transfer( + amf_gnb_t *gnb, ogs_ngap_message_t *message); +void ngap_handle_gnb_configuration_transfer( + amf_gnb_t *gnb, ogs_ngap_message_t *message, ogs_pkbuf_t *pkbuf); +void ngap_handle_handover_notification( + amf_gnb_t *gnb, ogs_ngap_message_t *message); + +void ngap_handle_s1_reset( + amf_gnb_t *gnb, ogs_ngap_message_t *message); + +void ngap_handle_write_replace_warning_response( + amf_gnb_t *gnb, ogs_ngap_message_t *message); +void ngap_handle_kill_response( + amf_gnb_t *gnb, ogs_ngap_message_t *message); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* NGAP_HANDLER_H */ diff --git a/src/amf/ngap-path.c b/src/amf/ngap-path.c new file mode 100644 index 000000000..e25173d79 --- /dev/null +++ b/src/amf/ngap-path.c @@ -0,0 +1,546 @@ +/* + * Copyright (C) 2019,2020 by Sukchan Lee + * + * This file is part of Open5GS. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 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, see . + */ + +#include "ogs-sctp.h" + +#include "context.h" + +#include "ngap-build.h" +#include "ngap-path.h" + +int ngap_open(void) +{ + ogs_socknode_t *node = NULL; + + ogs_list_for_each(&amf_self()->ngap_list, node) + ngap_server(node); + + ogs_list_for_each(&amf_self()->ngap_list6, node) + ngap_server(node); + + return OGS_OK; +} + +void ngap_close() +{ + ogs_socknode_remove_all(&amf_self()->ngap_list); + ogs_socknode_remove_all(&amf_self()->ngap_list6); +} + +int ngap_send(ogs_sock_t *sock, ogs_pkbuf_t *pkbuf, + ogs_sockaddr_t *addr, uint16_t stream_no) +{ + int sent; + + ogs_assert(sock); + ogs_assert(pkbuf); + + sent = ogs_sctp_sendmsg(sock, pkbuf->data, pkbuf->len, + addr, OGS_SCTP_NGAP_PPID, stream_no); + if (sent < 0 || sent != pkbuf->len) { + ogs_error("ogs_sctp_sendmsg error (%d:%s)", errno, strerror(errno)); + return OGS_ERROR; + } + ogs_pkbuf_free(pkbuf); + + return OGS_OK; +} + +int ngap_send_to_gnb(amf_gnb_t *gnb, ogs_pkbuf_t *pkbuf, uint16_t stream_no) +{ + char buf[OGS_ADDRSTRLEN]; + int rv; + + ogs_assert(gnb); + ogs_assert(pkbuf); + ogs_assert(gnb->sock); + + ogs_debug(" IP[%s] ENB_ID[%d]", + OGS_ADDR(gnb->addr, buf), gnb->gnb_id); + + rv = ngap_send(gnb->sock, pkbuf, + gnb->sock_type == SOCK_STREAM ? NULL : gnb->addr, + stream_no); + if (rv != OGS_OK) { + ogs_error("ngap_send() failed"); + + ogs_pkbuf_free(pkbuf); + ngap_event_push(AMF_EVT_NGAP_LO_CONNREFUSED, + gnb->sock, gnb->addr, NULL, 0, 0); + } + + return rv; +} + +#if 0 +int ngap_send_to_gnb_ue(gnb_ue_t *gnb_ue, ogs_pkbuf_t *pkbuf) +{ + amf_gnb_t *gnb = NULL; + + ogs_assert(gnb_ue); + gnb = gnb_ue->gnb; + ogs_assert(gnb); + + return ngap_send_to_gnb(gnb, pkbuf, gnb_ue->gnb_ostream_id); +} + +int ngap_delayed_send_to_gnb_ue( + gnb_ue_t *gnb_ue, ogs_pkbuf_t *pkbuf, ogs_time_t duration) +{ + ogs_assert(gnb_ue); + ogs_assert(pkbuf); + + if (duration) { + amf_event_t *e = NULL; + + e = amf_event_new(AMF_EVT_NGAP_TIMER); + ogs_assert(e); + e->timer = ogs_timer_add( + amf_self()->timer_mgr, amf_timer_ng_delayed_send, e); + ogs_assert(e->timer); + e->pkbuf = pkbuf; + e->gnb_ue = gnb_ue; + e->gnb = gnb_ue->gnb; + + ogs_timer_start(e->timer, duration); + + return OGS_OK; + } else { + amf_gnb_t *gnb = NULL; + gnb = gnb_ue->gnb; + ogs_assert(gnb); + return ngap_send_to_gnb_ue(gnb_ue, pkbuf); + } +} + +int ngap_send_to_esm(amf_ue_t *amf_ue, ogs_pkbuf_t *esmbuf) +{ + int rv; + amf_event_t *e = NULL; + + ogs_assert(amf_ue); + ogs_assert(esmbuf); + + e = amf_event_new(AMF_EVT_ESM_MESSAGE); + ogs_assert(e); + e->amf_ue = amf_ue; + e->pkbuf = esmbuf; + rv = ogs_queue_push(amf_self()->queue, e); + if (rv != OGS_OK) { + ogs_warn("ogs_queue_push() failed:%d", (int)rv); + ogs_pkbuf_free(e->pkbuf); + amf_event_free(e); + } + + return rv; +} + +int ngap_send_to_nas(gnb_ue_t *gnb_ue, + NGAP_ProcedureCode_t procedureCode, NGAP_NAS_PDU_t *nasPdu) +{ + ogs_nas_eps_security_header_t *sh = NULL; + ogs_nas_security_header_type_t security_header_type; + + ogs_nas_emm_header_t *h = NULL; + ogs_pkbuf_t *nasbuf = NULL; + amf_event_t *e = NULL; + + ogs_assert(gnb_ue); + ogs_assert(nasPdu); + + /* The Packet Buffer(pkbuf_t) for NAS message MUST make a HEADROOM. + * When calculating AES_CMAC, we need to use the headroom of the packet. */ + nasbuf = ogs_pkbuf_alloc(NULL, OGS_NAS_HEADROOM+nasPdu->size); + ogs_pkbuf_reserve(nasbuf, OGS_NAS_HEADROOM); + ogs_pkbuf_put_data(nasbuf, nasPdu->buf, nasPdu->size); + + sh = (ogs_nas_eps_security_header_t *)nasbuf->data; + ogs_assert(sh); + + memset(&security_header_type, 0, sizeof(ogs_nas_security_header_type_t)); + switch(sh->security_header_type) { + case OGS_NAS_SECURITY_HEADER_PLAIN_NAS_MESSAGE: + break; + case OGS_NAS_SECURITY_HEADER_FOR_SERVICE_REQUEST_MESSAGE: + security_header_type.service_request = 1; + break; + case OGS_NAS_SECURITY_HEADER_INTEGRITY_PROTECTED: + security_header_type.integrity_protected = 1; + ogs_pkbuf_pull(nasbuf, 6); + break; + case OGS_NAS_SECURITY_HEADER_INTEGRITY_PROTECTED_AND_CIPHERED: + security_header_type.integrity_protected = 1; + security_header_type.ciphered = 1; + ogs_pkbuf_pull(nasbuf, 6); + break; + case OGS_NAS_SECURITY_HEADER_INTEGRITY_PROTECTED_AND_NEW_SECURITY_CONTEXT: + security_header_type.integrity_protected = 1; + security_header_type.new_security_context = 1; + ogs_pkbuf_pull(nasbuf, 6); + break; + case OGS_NAS_SECURITY_HEADER_INTEGRITY_PROTECTED_AND_CIPHTERD_WITH_NEW_INTEGRITY_CONTEXT: + security_header_type.integrity_protected = 1; + security_header_type.ciphered = 1; + security_header_type.new_security_context = 1; + ogs_pkbuf_pull(nasbuf, 6); + break; + default: + ogs_error("Not implemented(security header type:0x%x)", + sh->security_header_type); + return OGS_ERROR; + } + + if (gnb_ue->amf_ue) { + if (nas_eps_security_decode(gnb_ue->amf_ue, + security_header_type, nasbuf) != OGS_OK) { + ogs_error("nas_eps_security_decode failed()"); + return OGS_ERROR; + } + } + + h = (ogs_nas_emm_header_t *)nasbuf->data; + ogs_assert(h); + if (h->protocol_discriminator == OGS_NAS_PROTOCOL_DISCRIMINATOR_EMM) { + int rv; + e = amf_event_new(AMF_EVT_EMM_MESSAGE); + ogs_assert(e); + e->gnb_ue = gnb_ue; + e->ngap_code = procedureCode; + e->nas_type = security_header_type.type; + e->pkbuf = nasbuf; + rv = ogs_queue_push(amf_self()->queue, e); + if (rv != OGS_OK) { + ogs_warn("ogs_queue_push() failed:%d", (int)rv); + ogs_pkbuf_free(e->pkbuf); + amf_event_free(e); + } + return rv; + } else if (h->protocol_discriminator == OGS_NAS_PROTOCOL_DISCRIMINATOR_ESM) { + amf_ue_t *amf_ue = gnb_ue->amf_ue; + ogs_assert(amf_ue); + return ngap_send_to_esm(amf_ue, nasbuf); + } else { + ogs_error("Unknown/Unimplemented NAS Protocol discriminator 0x%02x", + h->protocol_discriminator); + return OGS_ERROR; + } +} +#endif + +void ngap_send_ng_setup_response(amf_gnb_t *gnb) +{ + ogs_pkbuf_t *ngap_buffer; + + ogs_debug("[AMF] NG-Setup response"); + ngap_buffer = ngap_build_setup_rsp(); + ogs_expect_or_return(ngap_buffer); + + ogs_expect(OGS_OK == + ngap_send_to_gnb(gnb, ngap_buffer, NGAP_NON_UE_SIGNALLING)); +} + +void ngap_send_ng_setup_failure( + amf_gnb_t *gnb, NGAP_Cause_PR group, long cause) +{ + ogs_pkbuf_t *ngap_buffer; + + ogs_debug("[AMF] NG-Setup failure"); + ngap_buffer = ngap_build_setup_failure(group, cause, NGAP_TimeToWait_v10s); + ogs_expect_or_return(ngap_buffer); + + ogs_expect(OGS_OK == + ngap_send_to_gnb(gnb, ngap_buffer, NGAP_NON_UE_SIGNALLING)); +} + +#if 0 +void ngap_send_initial_context_setup_request(amf_ue_t *amf_ue) +{ + int rv; + ogs_pkbuf_t *ngapbuf = NULL; + + ogs_assert(amf_ue); + + ngapbuf = ngap_build_initial_context_setup_request(amf_ue, NULL); + ogs_expect_or_return(ngapbuf); + + rv = nas_eps_send_to_gnb(amf_ue, ngapbuf); + ogs_expect(rv == OGS_OK); +} + +void ngap_send_ue_context_modification_request(amf_ue_t *amf_ue) +{ + int rv; + ogs_pkbuf_t *ngapbuf = NULL; + + ogs_assert(amf_ue); + + ngapbuf = ngap_build_ue_context_modification_request(amf_ue); + ogs_expect_or_return(ngapbuf); + + rv = nas_eps_send_to_gnb(amf_ue, ngapbuf); + ogs_expect(rv == OGS_OK); +} + +void ngap_send_ue_context_release_command( + gnb_ue_t *gnb_ue, NGAP_Cause_PR group, long cause, + uint8_t action, uint32_t delay) +{ + int rv; + ogs_pkbuf_t *ngapbuf = NULL; + + ogs_assert(gnb_ue); + + ogs_debug("[AMF] UE Context release command"); + ogs_debug(" ENB_UE_NGAP_ID[%d] AMF_UE_NGAP_ID[%d]", + gnb_ue->gnb_ue_ngap_id, gnb_ue->amf_ue_ngap_id); + + if (delay) { + ogs_assert(action != NGAP_UE_CTX_REL_INVALID_ACTION); + gnb_ue->ue_ctx_rel_action = action; + + ogs_debug(" Group[%d] Cause[%d] Action[%d] Delay[%d]", + group, (int)cause, action, delay); + + ngapbuf = ngap_build_ue_context_release_command(gnb_ue, group, cause); + ogs_expect_or_return(ngapbuf); + + rv = ngap_delayed_send_to_gnb_ue(gnb_ue, ngapbuf, delay); + ogs_expect(rv == OGS_OK); + } else { + ogs_assert(action != NGAP_UE_CTX_REL_INVALID_ACTION); + gnb_ue->ue_ctx_rel_action = action; + + ogs_debug(" Group[%d] Cause[%d] Action[%d] Delay[%d]", + group, (int)cause, action, delay); + + ngapbuf = ngap_build_ue_context_release_command(gnb_ue, group, cause); + ogs_expect_or_return(ngapbuf); + + rv = ngap_delayed_send_to_gnb_ue(gnb_ue, ngapbuf, 0); + ogs_expect(rv == OGS_OK); + } +} + +void ngap_send_paging(amf_ue_t *amf_ue, NGAP_CNDomain_t cn_domain) +{ + ogs_pkbuf_t *ngapbuf = NULL; + amf_gnb_t *gnb = NULL; + int i; + int rv; + + /* Find enB with matched TAI */ + ogs_list_for_each(&amf_self()->gnb_list, gnb) { + for (i = 0; i < gnb->num_of_supported_ta_list; i++) { + + if (memcmp(&gnb->supported_ta_list[i], &amf_ue->tai, + sizeof(ogs_5gs_tai_t)) == 0) { + + if (amf_ue->t3413.pkbuf) { + ngapbuf = amf_ue->t3413.pkbuf; + } else { + ngapbuf = ngap_build_paging(amf_ue, cn_domain); + ogs_expect_or_return(ngapbuf); + } + + amf_ue->t3413.pkbuf = ogs_pkbuf_copy(ngapbuf); + + rv = ngap_send_to_gnb(gnb, ngapbuf, NGAP_NON_UE_SIGNALLING); + ogs_expect(rv == OGS_OK); + } + } + } + + /* Start T3413 */ + ogs_timer_start(amf_ue->t3413.timer, + amf_timer_cfg(AMF_TIMER_T3413)->duration); +} + +void ngap_send_amf_configuration_transfer( + amf_gnb_t *target_gnb, + NGAP_SONConfigurationTransfer_t *SONConfigurationTransfer) +{ + int rv; + ogs_pkbuf_t *ngapbuf = NULL; + + ogs_assert(target_gnb); + ogs_assert(SONConfigurationTransfer); + + ngapbuf = ngap_build_amf_configuration_transfer(SONConfigurationTransfer); + ogs_expect_or_return(ngapbuf); + + rv = ngap_send_to_gnb(target_gnb, ngapbuf, NGAP_NON_UE_SIGNALLING); + ogs_expect(rv == OGS_OK); +} + +void ngap_send_path_switch_ack(amf_ue_t *amf_ue) +{ + int rv; + ogs_pkbuf_t *ngapbuf = NULL; + + ogs_assert(amf_ue); + + ngapbuf = ngap_build_path_switch_ack(amf_ue); + ogs_expect_or_return(ngapbuf); + + rv = nas_eps_send_to_gnb(amf_ue, ngapbuf); + ogs_expect(rv == OGS_OK); +} + +void ngap_send_handover_command(gnb_ue_t *source_ue) +{ + int rv; + ogs_pkbuf_t *ngapbuf = NULL; + + ogs_assert(source_ue); + + ngapbuf = ngap_build_handover_command(source_ue); + ogs_expect_or_return(ngapbuf); + + rv = ngap_send_to_gnb_ue(source_ue, ngapbuf); + ogs_expect(rv == OGS_OK); +} + +void ngap_send_handover_preparation_failure( + gnb_ue_t *source_ue, NGAP_Cause_t *cause) +{ + int rv; + ogs_pkbuf_t *ngapbuf = NULL; + + ogs_assert(source_ue); + ogs_assert(cause); + + ngapbuf = ngap_build_handover_preparation_failure(source_ue, cause); + ogs_expect_or_return(ngapbuf); + + rv = ngap_send_to_gnb_ue(source_ue, ngapbuf); + ogs_expect(rv == OGS_OK); +} + +void ngap_send_handover_cancel_ack(gnb_ue_t *source_ue) +{ + int rv; + ogs_pkbuf_t *ngapbuf = NULL; + + ogs_assert(source_ue); + + ngapbuf = ngap_build_handover_cancel_ack(source_ue); + ogs_expect_or_return(ngapbuf); + + rv = ngap_send_to_gnb_ue(source_ue, ngapbuf); + ogs_expect(rv == OGS_OK); +} + + +void ngap_send_handover_request( + amf_ue_t *amf_ue, + amf_gnb_t *target_gnb, + NGAP_ENB_UE_NGAP_ID_t *gnb_ue_ngap_id, + NGAP_AMF_UE_NGAP_ID_t *amf_ue_ngap_id, + NGAP_HandoverType_t *handovertype, + NGAP_Cause_t *cause, + NGAP_Source_ToTarget_TransparentContainer_t + *source_totarget_transparentContainer) +{ + int rv; + ogs_pkbuf_t *ngapbuf = NULL; + + gnb_ue_t *source_ue = NULL, *target_ue = NULL; + + ogs_debug("[AMF] Handover request"); + + ogs_assert(target_gnb); + + ogs_assert(amf_ue); + source_ue = amf_ue->gnb_ue; + ogs_assert(source_ue); + ogs_assert(source_ue->target_ue == NULL); + + target_ue = gnb_ue_add(target_gnb, INVALID_UE_NGAP_ID); + ogs_assert(target_ue); + + ogs_debug(" Source : ENB_UE_NGAP_ID[%d] AMF_UE_NGAP_ID[%d]", + source_ue->gnb_ue_ngap_id, source_ue->amf_ue_ngap_id); + ogs_debug(" Target : ENB_UE_NGAP_ID[Unknown] AMF_UE_NGAP_ID[%d]", + target_ue->amf_ue_ngap_id); + + source_ue_associate_target_ue(source_ue, target_ue); + + ngapbuf = ngap_build_handover_request(amf_ue, target_ue, + gnb_ue_ngap_id, amf_ue_ngap_id, + handovertype, cause, + source_totarget_transparentContainer); + ogs_expect_or_return(ngapbuf); + + rv = ngap_send_to_gnb_ue(target_ue, ngapbuf); + ogs_expect(rv == OGS_OK); +} + +void ngap_send_amf_status_transfer( + gnb_ue_t *target_ue, + NGAP_ENB_StatusTransfer_TransparentContainer_t + *gnb_statustransfer_transparentContainer) +{ + int rv; + ogs_pkbuf_t *ngapbuf = NULL; + + ogs_assert(target_ue); + + ngapbuf = ngap_build_amf_status_transfer(target_ue, + gnb_statustransfer_transparentContainer); + ogs_expect_or_return(ngapbuf); + + rv = ngap_send_to_gnb_ue(target_ue, ngapbuf); + ogs_expect(rv == OGS_OK); +} + +void ngap_send_error_indication( + amf_gnb_t *gnb, + NGAP_AMF_UE_NGAP_ID_t *amf_ue_ngap_id, + NGAP_ENB_UE_NGAP_ID_t *gnb_ue_ngap_id, + NGAP_Cause_PR group, long cause) +{ + int rv; + ogs_pkbuf_t *ngapbuf = NULL; + + ogs_assert(gnb); + + ngapbuf = ngap_build_error_indication( + amf_ue_ngap_id, gnb_ue_ngap_id, group, cause); + ogs_expect_or_return(ngapbuf); + + rv = ngap_send_to_gnb(gnb, ngapbuf, NGAP_NON_UE_SIGNALLING); + ogs_expect(rv == OGS_OK); +} + +void ngap_send_ng_reset_ack( + amf_gnb_t *gnb, + NGAP_UE_associatedLogicalNG_ConnectionListRes_t *partOfNG_Interface) +{ + int rv; + ogs_pkbuf_t *ngapbuf = NULL; + + ogs_assert(gnb); + + ngapbuf = ngap_build_ng_reset_ack(partOfNG_Interface); + ogs_expect_or_return(ngapbuf); + + rv = ngap_send_to_gnb(gnb, ngapbuf, NGAP_NON_UE_SIGNALLING); + ogs_expect(rv == OGS_OK); +} +#endif diff --git a/src/amf/ngap-path.h b/src/amf/ngap-path.h new file mode 100644 index 000000000..1365d49ad --- /dev/null +++ b/src/amf/ngap-path.h @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2019,2020 by Sukchan Lee + * + * This file is part of Open5GS. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 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, see . + */ + +#ifndef NGAP_PATH_H +#define NGAP_PATH_H + +#include "context.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define NGAP_NON_UE_SIGNALLING 0 + +#define ngap_event_push amf_sctp_event_push + +int ngap_open(void); +void ngap_close(void); + +ogs_sock_t *ngap_server(ogs_socknode_t *node); +void ngap_recv_upcall(short when, ogs_socket_t fd, void *data); +int ngap_send(ogs_sock_t *sock, + ogs_pkbuf_t *pkbuf, ogs_sockaddr_t *addr, uint16_t stream_no); + +int ngap_send_to_gnb( + amf_gnb_t *gnb, ogs_pkbuf_t *pkb, uint16_t stream_no); +#if 0 +int ngap_send_to_gnb_ue(gnb_ue_t *gnb_ue, ogs_pkbuf_t *pkbuf); +int ngap_delayed_send_to_gnb_ue(gnb_ue_t *gnb_ue, + ogs_pkbuf_t *pkbuf, ogs_time_t duration); +int ngap_send_to_nas(gnb_ue_t *gnb_ue, + NGAP_ProcedureCode_t procedureCode, NGAP_NAS_PDU_t *nasPdu); +int ngap_send_to_esm(amf_ue_t *amf_ue, ogs_pkbuf_t *esmbuf); +#endif + +void ngap_send_ng_setup_response(amf_gnb_t *gnb); +void ngap_send_ng_setup_failure( + amf_gnb_t *gnb, NGAP_Cause_PR group, long cause); + +#if 0 +void ngap_send_initial_context_setup_request(amf_ue_t *amf_ue); +void ngap_send_ue_context_modification_request(amf_ue_t *amf_ue); +void ngap_send_ue_context_release_command( + gnb_ue_t *gnb_ue, NGAP_Cause_PR group, long cause, + uint8_t action, uint32_t delay); + +void ngap_send_paging(amf_ue_t *amf_ue, NGAP_CNDomain_t cn_domain); + +void ngap_send_amf_configuration_transfer( + amf_gnb_t *target_gnb, + NGAP_SONConfigurationTransfer_t *SONConfigurationTransfer); + +void ngap_send_path_switch_ack(amf_ue_t *amf_ue); + +void ngap_send_handover_command(gnb_ue_t *source_ue); +void ngap_send_handover_preparation_failure( + gnb_ue_t *source_ue, NGAP_Cause_t *cause); + +void ngap_send_handover_request( + amf_ue_t *amf_ue, + amf_gnb_t *target_gnb, + NGAP_ENB_UE_NGAP_ID_t *gnb_ue_ngap_id, + NGAP_AMF_UE_NGAP_ID_t *amf_ue_ngap_id, + NGAP_HandoverType_t *handovertype, + NGAP_Cause_t *cause, + NGAP_Source_ToTarget_TransparentContainer_t + *source_totarget_transparentContainer); + +void ngap_send_handover_cancel_ack(gnb_ue_t *source_ue); + +void ngap_send_amf_status_transfer( + gnb_ue_t *target_ue, + NGAP_ENB_StatusTransfer_TransparentContainer_t + *gnb_statustransfer_transparentContainer); +void ngap_send_error_indication( + amf_gnb_t *gnb, + NGAP_AMF_UE_NGAP_ID_t *amf_ue_ngap_id, + NGAP_ENB_UE_NGAP_ID_t *gnb_ue_ngap_id, + NGAP_Cause_PR group, long cause); +void ngap_send_ng_reset_ack( + amf_gnb_t *gnb, + NGAP_UE_associatedLogicalNG_ConnectionListRes_t *partOfNG_Interface); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* NGAP_PATH_H */ diff --git a/src/amf/ngap-sctp.c b/src/amf/ngap-sctp.c new file mode 100644 index 000000000..e109e4c69 --- /dev/null +++ b/src/amf/ngap-sctp.c @@ -0,0 +1,238 @@ +/* + * Copyright (C) 2019,2020 by Sukchan Lee + * + * This file is part of Open5GS. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 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, see . + */ + +#include "ogs-sctp.h" + +#include "ngap-path.h" + +#if HAVE_USRSCTP +static void usrsctp_recv_handler(struct socket *socket, void *data, int flags); +#else +static void lksctp_accept_handler(short when, ogs_socket_t fd, void *data); +#endif + +void ngap_accept_handler(ogs_sock_t *sock); +void ngap_recv_handler(ogs_sock_t *sock); + +ogs_sock_t *ngap_server(ogs_socknode_t *node) +{ + char buf[OGS_ADDRSTRLEN]; + ogs_sock_t *sock = NULL; + + ogs_assert(node); + + ogs_socknode_sctp_option(node, &ogs_config()->sockopt); + ogs_socknode_nodelay(node, true); + +#if HAVE_USRSCTP + sock = ogs_sctp_server(SOCK_SEQPACKET, node); + ogs_assert(sock); + usrsctp_set_non_blocking((struct socket *)sock, 1); + usrsctp_set_upcall((struct socket *)sock, usrsctp_recv_handler, NULL); +#else + sock = ogs_sctp_server(SOCK_STREAM, node); + ogs_assert(sock); + node->poll = ogs_pollset_add(amf_self()->pollset, + OGS_POLLIN, sock->fd, lksctp_accept_handler, sock); +#endif + + ogs_info("ngap_server() [%s]:%d", + OGS_ADDR(node->addr, buf), OGS_PORT(node->addr)); + + return sock; +} + +void ngap_recv_upcall(short when, ogs_socket_t fd, void *data) +{ + ogs_sock_t *sock = NULL; + + ogs_assert(fd != INVALID_SOCKET); + sock = data; + ogs_assert(sock); + + ngap_recv_handler(sock); +} + +#if HAVE_USRSCTP +static void usrsctp_recv_handler(struct socket *socket, void *data, int flags) +{ + int events; + + while ((events = usrsctp_get_events(socket)) && + (events & SCTP_EVENT_READ)) { + ngap_recv_handler((ogs_sock_t *)socket); + } +} +#else +static void lksctp_accept_handler(short when, ogs_socket_t fd, void *data) +{ + ogs_assert(data); + ogs_assert(fd != INVALID_SOCKET); + + ngap_accept_handler(data); +} +#endif + +void ngap_accept_handler(ogs_sock_t *sock) +{ + char buf[OGS_ADDRSTRLEN]; + ogs_sock_t *new = NULL; + + ogs_assert(sock); + + new = ogs_sock_accept(sock); + if (new) { + ogs_sockaddr_t *addr = NULL; + + addr = ogs_calloc(1, sizeof(ogs_sockaddr_t)); + ogs_assert(addr); + memcpy(addr, &new->remote_addr, sizeof(ogs_sockaddr_t)); + + ogs_info("eNB-S1 accepted[%s]:%d in s1_path module", + OGS_ADDR(addr, buf), OGS_PORT(addr)); + + ngap_event_push(AMF_EVT_NGAP_LO_ACCEPT, + new, addr, NULL, 0, 0); + } else { + ogs_log_message(OGS_LOG_ERROR, ogs_socket_errno, "accept() failed"); + } +} + +void ngap_recv_handler(ogs_sock_t *sock) +{ + ogs_pkbuf_t *pkbuf; + int size; + ogs_sockaddr_t *addr = NULL; + ogs_sockaddr_t from; + ogs_sctp_info_t sinfo; + int flags = 0; + + ogs_assert(sock); + + pkbuf = ogs_pkbuf_alloc(NULL, OGS_MAX_SDU_LEN); + ogs_pkbuf_put(pkbuf, OGS_MAX_SDU_LEN); + size = ogs_sctp_recvmsg( + sock, pkbuf->data, pkbuf->len, &from, &sinfo, &flags); + if (size < 0) { + ogs_error("ogs_sctp_recvmsg(%d) failed(%d:%s)", + size, errno, strerror(errno)); + ogs_pkbuf_free(pkbuf); + return; + } + + if (flags & MSG_NOTIFICATION) { + union sctp_notification *not = + (union sctp_notification *)pkbuf->data; + + switch(not->sn_header.sn_type) { + case SCTP_ASSOC_CHANGE : + ogs_debug("SCTP_ASSOC_CHANGE:" + "[T:%d, F:0x%x, S:%d, I/O:%d/%d]", + not->sn_assoc_change.sac_type, + not->sn_assoc_change.sac_flags, + not->sn_assoc_change.sac_state, + not->sn_assoc_change.sac_inbound_streams, + not->sn_assoc_change.sac_outbound_streams); + + if (not->sn_assoc_change.sac_state == SCTP_COMM_UP) { + ogs_debug("SCTP_COMM_UP"); + + addr = ogs_calloc(1, sizeof(ogs_sockaddr_t)); + ogs_assert(addr); + memcpy(addr, &from, sizeof(ogs_sockaddr_t)); + + ngap_event_push(AMF_EVT_NGAP_LO_SCTP_COMM_UP, + sock, addr, NULL, + not->sn_assoc_change.sac_inbound_streams, + not->sn_assoc_change.sac_outbound_streams); + } else if (not->sn_assoc_change.sac_state == SCTP_SHUTDOWN_COMP || + not->sn_assoc_change.sac_state == SCTP_COMM_LOST) { + + if (not->sn_assoc_change.sac_state == SCTP_SHUTDOWN_COMP) + ogs_debug("SCTP_SHUTDOWN_COMP"); + if (not->sn_assoc_change.sac_state == SCTP_COMM_LOST) + ogs_debug("SCTP_COMM_LOST"); + + addr = ogs_calloc(1, sizeof(ogs_sockaddr_t)); + ogs_assert(addr); + memcpy(addr, &from, sizeof(ogs_sockaddr_t)); + + ngap_event_push(AMF_EVT_NGAP_LO_CONNREFUSED, + sock, addr, NULL, 0, 0); + } + break; + case SCTP_SHUTDOWN_EVENT : + case SCTP_SEND_FAILED : + if (not->sn_header.sn_type == SCTP_SHUTDOWN_EVENT) + ogs_debug("SCTP_SHUTDOWN_EVENT:[T:%d, F:0x%x, L:%d]", + not->sn_shutdown_event.sse_type, + not->sn_shutdown_event.sse_flags, + not->sn_shutdown_event.sse_length); + if (not->sn_header.sn_type == SCTP_SEND_FAILED) +#if HAVE_USRSCTP + ogs_error("SCTP_SEND_FAILED:[T:%d, F:0x%x, S:%d]", + not->sn_send_failed_event.ssfe_type, + not->sn_send_failed_event.ssfe_flags, + not->sn_send_failed_event.ssfe_error); +#else + ogs_error("SCTP_SEND_FAILED:[T:%d, F:0x%x, S:%d]", + not->sn_send_failed.ssf_type, + not->sn_send_failed.ssf_flags, + not->sn_send_failed.ssf_error); +#endif + + addr = ogs_calloc(1, sizeof(ogs_sockaddr_t)); + ogs_assert(addr); + memcpy(addr, &from, sizeof(ogs_sockaddr_t)); + + ngap_event_push(AMF_EVT_NGAP_LO_CONNREFUSED, + sock, addr, NULL, 0, 0); + break; + case SCTP_PEER_ADDR_CHANGE: + ogs_warn("SCTP_PEER_ADDR_CHANGE:[T:%d, F:0x%x, S:%d]", + not->sn_paddr_change.spc_type, + not->sn_paddr_change.spc_flags, + not->sn_paddr_change.spc_error); + break; + case SCTP_REMOTE_ERROR: + ogs_warn("SCTP_REMOTE_ERROR:[T:%d, F:0x%x, S:%d]", + not->sn_remote_error.sre_type, + not->sn_remote_error.sre_flags, + not->sn_remote_error.sre_error); + break; + default : + ogs_error("Discarding event with unknown flags:0x%x type:0x%x", + flags, not->sn_header.sn_type); + break; + } + } else if (flags & MSG_EOR) { + ogs_pkbuf_trim(pkbuf, size); + + addr = ogs_calloc(1, sizeof(ogs_sockaddr_t)); + ogs_assert(addr); + memcpy(addr, &from, sizeof(ogs_sockaddr_t)); + + ngap_event_push(AMF_EVT_NGAP_MESSAGE, sock, addr, pkbuf, 0, 0); + return; + } else { + ogs_assert_if_reached(); + } + + ogs_pkbuf_free(pkbuf); +} diff --git a/src/amf/ngap-sm.c b/src/amf/ngap-sm.c new file mode 100644 index 000000000..68f8eab82 --- /dev/null +++ b/src/amf/ngap-sm.c @@ -0,0 +1,229 @@ +/* + * Copyright (C) 2019 by Sukchan Lee + * + * This file is part of Open5GS. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 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, see . + */ + +#include "ngap-build.h" +#include "ngap-handler.h" +#include "ngap-path.h" + +void ngap_state_initial(ogs_fsm_t *s, amf_event_t *e) +{ + ogs_assert(s); + + amf_sm_debug(e); + + OGS_FSM_TRAN(s, &ngap_state_operational); +} + +void ngap_state_final(ogs_fsm_t *s, amf_event_t *e) +{ + ogs_assert(s); + + amf_sm_debug(e); +} + +void ngap_state_operational(ogs_fsm_t *s, amf_event_t *e) +{ + amf_gnb_t *gnb = NULL; +#if 0 + ogs_pkbuf_t *pkbuf = NULL; +#endif + + NGAP_NGAP_PDU_t *pdu = NULL; + NGAP_InitiatingMessage_t *initiatingMessage = NULL; + NGAP_SuccessfulOutcome_t *successfulOutcome = NULL; + NGAP_UnsuccessfulOutcome_t *unsuccessfulOutcome = NULL; + + ogs_assert(s); + ogs_assert(e); + + amf_sm_debug(e); + + gnb = e->gnb; + ogs_assert(gnb); + + switch (e->id) { + case OGS_FSM_ENTRY_SIG: + break; + case OGS_FSM_EXIT_SIG: + break; + case AMF_EVT_NGAP_MESSAGE: + pdu = e->ngap.message; + ogs_assert(pdu); + + switch (pdu->present) { + case NGAP_NGAP_PDU_PR_initiatingMessage : + initiatingMessage = pdu->choice.initiatingMessage; + ogs_assert(initiatingMessage); + + switch (initiatingMessage->procedureCode) { + case NGAP_ProcedureCode_id_NGSetup : + ngap_handle_ng_setup_request(gnb, pdu); + break; +#if 0 + case NGAP_ProcedureCode_id_initialUEMessage : + ngap_handle_initial_ue_message(gnb, pdu); + break; + case NGAP_ProcedureCode_id_uplinkNASTransport : + ngap_handle_uplink_nas_transport(gnb, pdu); + break; + case NGAP_ProcedureCode_id_UECapabilityInfoIndication : + ngap_handle_ue_capability_info_indication( gnb, pdu); + break; + case NGAP_ProcedureCode_id_UEContextReleaseRequest: + ngap_handle_ue_context_release_request( gnb, pdu); + break; + case NGAP_ProcedureCode_id_PathSwitchRequest: + ngap_handle_path_switch_request(gnb, pdu); + break; + case NGAP_ProcedureCode_id_eNBConfigurationTransfer: + pkbuf = e->pkbuf; + ogs_assert(pkbuf); + + ngap_handle_gnb_configuration_transfer(gnb, pdu, pkbuf); + break; + case NGAP_ProcedureCode_id_HandoverPreparation: + ngap_handle_handover_required(gnb, pdu); + break; + case NGAP_ProcedureCode_id_HandoverCancel: + ngap_handle_handover_cancel(gnb, pdu); + break; + case NGAP_ProcedureCode_id_eNBStatusTransfer: + ngap_handle_gnb_status_transfer(gnb, pdu); + break; + case NGAP_ProcedureCode_id_HandoverNotification: + ngap_handle_handover_notification(gnb, pdu); + break; + case NGAP_ProcedureCode_id_Reset: + ngap_handle_s1_reset(gnb, pdu); + break; +#endif + default: + ogs_warn("Not implemented(choice:%d, proc:%d)", + pdu->present, (int)initiatingMessage->procedureCode); + break; + } + break; + case NGAP_NGAP_PDU_PR_successfulOutcome : + successfulOutcome = pdu->choice.successfulOutcome; + ogs_assert(successfulOutcome); + + switch (successfulOutcome->procedureCode) { +#if 0 + case NGAP_ProcedureCode_id_InitialContextSetup: + ngap_handle_initial_context_setup_response(gnb, pdu); + break; + case NGAP_ProcedureCode_id_UEContextModification: + ngap_handle_ue_context_modification_response(gnb, pdu); + break; + case NGAP_ProcedureCode_id_UEContextRelease: + ngap_handle_ue_context_release_complete( + gnb, pdu); + break; + case NGAP_ProcedureCode_id_E_RABSetup: + ngap_handle_e_rab_setup_response(gnb, pdu); + break; + case NGAP_ProcedureCode_id_E_RABModify: + break; + case NGAP_ProcedureCode_id_E_RABRelease: + break; + case NGAP_ProcedureCode_id_HandoverResourceAllocation: + ngap_handle_handover_request_ack(gnb, pdu); + break; + case NGAP_ProcedureCode_id_WriteReplaceWarning: + ngap_handle_write_replace_warning_response(gnb, pdu); + break; + case NGAP_ProcedureCode_id_Kill: + ngap_handle_kill_response(gnb, pdu); + break; +#endif + default: + ogs_warn("Not implemented(choice:%d, proc:%d)", + pdu->present, (int)successfulOutcome->procedureCode); + break; + } + break; + case NGAP_NGAP_PDU_PR_unsuccessfulOutcome : + unsuccessfulOutcome = pdu->choice.unsuccessfulOutcome; + ogs_assert(unsuccessfulOutcome); + + switch (unsuccessfulOutcome->procedureCode) { +#if 0 + case NGAP_ProcedureCode_id_InitialContextSetup : + ngap_handle_initial_context_setup_failure(gnb, pdu); + break; + case NGAP_ProcedureCode_id_UEContextModification: + ngap_handle_ue_context_modification_failure(gnb, pdu); + break; + case NGAP_ProcedureCode_id_HandoverResourceAllocation : + ngap_handle_handover_failure(gnb, pdu); + break; +#endif + default: + ogs_warn("Not implemented(choice:%d, proc:%d)", + pdu->present, (int)unsuccessfulOutcome->procedureCode); + break; + } + break; + default: + ogs_warn("Not implemented(choice:%d)", pdu->present); + break; + } + + break; + case AMF_EVT_NGAP_TIMER: + switch (e->timer_id) { +#if 0 + case AMF_TIMER_S1_DELAYED_SEND: + ogs_assert(e->gnb_ue); + ogs_assert(e->pkbuf); + + ogs_expect(OGS_OK == ngap_send_to_gnb_ue(e->gnb_ue, e->pkbuf)); + ogs_timer_delete(e->timer); + break; +#endif + default: + ogs_error("Unknown timer[%s:%d]", + amf_timer_get_name(e->timer_id), e->timer_id); + break; + } + break; + default: + ogs_error("Unknown event %s", amf_event_get_name(e)); + break; + } +} + +void ngap_state_exception(ogs_fsm_t *s, amf_event_t *e) +{ + ogs_assert(s); + ogs_assert(e); + + amf_sm_debug(e); + + switch (e->id) { + case OGS_FSM_ENTRY_SIG: + break; + case OGS_FSM_EXIT_SIG: + break; + default: + ogs_error("Unknown event %s", amf_event_get_name(e)); + break; + } +} + diff --git a/src/amf/nnrf-build.c b/src/amf/nnrf-build.c new file mode 100644 index 000000000..73fd5e492 --- /dev/null +++ b/src/amf/nnrf-build.c @@ -0,0 +1,204 @@ +/* + * Copyright (C) 2019,2020 by Sukchan Lee + * + * This file is part of Open5GS. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 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, see . + */ + +#include "nnrf-build.h" + +ogs_sbi_request_t *amf_nnrf_build_nf_register( + ogs_sbi_nf_instance_t *nf_instance) +{ + ogs_sbi_message_t message; + ogs_sbi_request_t *request = NULL; + ogs_sbi_client_t *client = NULL; + + OpenAPI_nf_profile_t *NFProfile = NULL; + + ogs_assert(nf_instance); + client = nf_instance->client; + ogs_assert(client); + + memset(&message, 0, sizeof(message)); + message.h.method = (char *)OGS_SBI_HTTP_METHOD_PUT; + message.h.service.name = (char *)OGS_SBI_SERVICE_NAME_NRF_NFM; + message.h.api.version = (char *)OGS_SBI_API_VERSION; + message.h.resource.name = (char *)OGS_SBI_RESOURCE_NAME_NF_INSTANCES; + message.h.resource.id = ogs_sbi_self()->nf_instance_id; + + message.http.content_encoding = (char*)ogs_sbi_self()->content_encoding; + + NFProfile = ogs_sbi_nnrf_build_nf_profile(nf_instance); + ogs_assert(NFProfile); + + NFProfile->heart_beat_timer = nf_instance->time.heartbeat; + + message.NFProfile = NFProfile; + + request = ogs_sbi_build_request(&message); + ogs_assert(request); + + ogs_sbi_nnrf_free_nf_profile(NFProfile); + + return request; +} + +ogs_sbi_request_t *amf_nnrf_build_nf_update(ogs_sbi_nf_instance_t *nf_instance) +{ + ogs_sbi_message_t message; + ogs_sbi_request_t *request = NULL; + + OpenAPI_list_t *PatchItemList; + OpenAPI_patch_item_t item; + + ogs_assert(nf_instance); + + memset(&message, 0, sizeof(message)); + message.h.method = (char *)OGS_SBI_HTTP_METHOD_PATCH; + message.h.service.name = (char *)OGS_SBI_SERVICE_NAME_NRF_NFM; + message.h.api.version = (char *)OGS_SBI_API_VERSION; + message.h.resource.name = (char *)OGS_SBI_RESOURCE_NAME_NF_INSTANCES; + message.h.resource.id = ogs_sbi_self()->nf_instance_id; + message.http.content_type = (char *)OGS_SBI_CONTENT_PATCH_TYPE; + + PatchItemList = OpenAPI_list_create(); + ogs_assert(PatchItemList); + + memset(&item, 0, sizeof(item)); + item.op = OpenAPI_patch_operation_replace; + item.path = (char *)"/nfStatus"; + item.value = OpenAPI_nf_status_ToString(OpenAPI_nf_status_REGISTERED); + + OpenAPI_list_add(PatchItemList, &item); + + message.PatchItemList = PatchItemList; + + request = ogs_sbi_build_request(&message); + ogs_assert(request); + + OpenAPI_list_free(PatchItemList); + + return request; +} + +ogs_sbi_request_t *amf_nnrf_build_nf_de_register( + ogs_sbi_nf_instance_t *nf_instance) +{ + ogs_sbi_message_t message; + ogs_sbi_request_t *request = NULL; + + ogs_assert(nf_instance); + + memset(&message, 0, sizeof(message)); + message.h.method = (char *)OGS_SBI_HTTP_METHOD_DELETE; + message.h.service.name = (char *)OGS_SBI_SERVICE_NAME_NRF_NFM; + message.h.api.version = (char *)OGS_SBI_API_VERSION; + message.h.resource.name = (char *)OGS_SBI_RESOURCE_NAME_NF_INSTANCES; + message.h.resource.id = nf_instance->id; + + request = ogs_sbi_build_request(&message); + ogs_assert(request); + + return request; +} + +ogs_sbi_request_t *amf_nnrf_build_nf_status_subscribe( + ogs_sbi_subscription_t *subscription) +{ + ogs_sbi_message_t message; + ogs_sbi_request_t *request = NULL; + ogs_sbi_server_t *server = NULL; + + OpenAPI_subscription_data_t *SubscriptionData = NULL; + + ogs_assert(subscription); + ogs_assert(subscription->nf_type); + + memset(&message, 0, sizeof(message)); + message.h.method = (char *)OGS_SBI_HTTP_METHOD_POST; + message.h.service.name = (char *)OGS_SBI_SERVICE_NAME_NRF_NFM; + message.h.api.version = (char *)OGS_SBI_API_VERSION; + message.h.resource.name = (char *)OGS_SBI_RESOURCE_NAME_SUBSCRIPTIONS; + + SubscriptionData = ogs_calloc(1, sizeof(*SubscriptionData)); + ogs_assert(SubscriptionData); + + server = ogs_list_first(&ogs_sbi_self()->server_list); + ogs_assert(server); + + SubscriptionData->nf_status_notification_uri = ogs_sbi_server_uri(server, + OGS_SBI_SERVICE_NAME_NRF_NFM, OGS_SBI_API_VERSION, + OGS_SBI_RESOURCE_NAME_NF_STATUS_NOTIFY, NULL); + ogs_assert(SubscriptionData->nf_status_notification_uri); + + SubscriptionData->req_nf_type = subscription->nf_type; + SubscriptionData->req_nf_instance_id = subscription->nf_instance_id; + + message.SubscriptionData = SubscriptionData; + + request = ogs_sbi_build_request(&message); + ogs_assert(request); + + ogs_free(SubscriptionData->nf_status_notification_uri); + ogs_free(SubscriptionData); + + return request; +} + +ogs_sbi_request_t *amf_nnrf_build_nf_status_unsubscribe( + ogs_sbi_subscription_t *subscription) +{ + ogs_sbi_message_t message; + ogs_sbi_request_t *request = NULL; + + ogs_assert(subscription); + + memset(&message, 0, sizeof(message)); + message.h.method = (char *)OGS_SBI_HTTP_METHOD_DELETE; + message.h.service.name = (char *)OGS_SBI_SERVICE_NAME_NRF_NFM; + message.h.api.version = (char *)OGS_SBI_API_VERSION; + message.h.resource.name = (char *)OGS_SBI_RESOURCE_NAME_SUBSCRIPTIONS; + message.h.resource.id = subscription->id; + + request = ogs_sbi_build_request(&message); + ogs_assert(request); + + return request; +} + +ogs_sbi_request_t *amf_nnrf_build_nf_discover( + OpenAPI_nf_type_e target_nf_type, OpenAPI_nf_type_e requester_nf_type) +{ + ogs_sbi_message_t message; + ogs_sbi_request_t *request = NULL; + + ogs_assert(target_nf_type); + ogs_assert(requester_nf_type); + + memset(&message, 0, sizeof(message)); + message.h.method = (char *)OGS_SBI_HTTP_METHOD_GET; + message.h.service.name = (char *)OGS_SBI_SERVICE_NAME_NRF_DISC; + message.h.api.version = (char *)OGS_SBI_API_VERSION; + message.h.resource.name = (char *)OGS_SBI_RESOURCE_NAME_NF_INSTANCES; + + message.param.target_nf_type = target_nf_type; + message.param.requester_nf_type = requester_nf_type; + + request = ogs_sbi_build_request(&message); + ogs_assert(request); + + return request; +} diff --git a/src/amf/nnrf-build.h b/src/amf/nnrf-build.h new file mode 100644 index 000000000..b0eca2441 --- /dev/null +++ b/src/amf/nnrf-build.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2019,2020 by Sukchan Lee + * + * This file is part of Open5GS. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 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, see . + */ + +#ifndef AMF_NNRF_BUILD_H +#define AMF_NNRF_BUILD_H + +#include "ogs-sbi.h" +#include "context.h" + +#ifdef __cplusplus +extern "C" { +#endif + +ogs_sbi_request_t *amf_nnrf_build_nf_register( + ogs_sbi_nf_instance_t *nf_instance); +ogs_sbi_request_t *amf_nnrf_build_nf_update( + ogs_sbi_nf_instance_t *nf_instance); +ogs_sbi_request_t *amf_nnrf_build_nf_de_register( + ogs_sbi_nf_instance_t *nf_instance); + +ogs_sbi_request_t *amf_nnrf_build_nf_status_subscribe( + ogs_sbi_subscription_t *subscription); +ogs_sbi_request_t *amf_nnrf_build_nf_status_unsubscribe( + ogs_sbi_subscription_t *subscription); + +ogs_sbi_request_t *amf_nnrf_build_nf_discover( + OpenAPI_nf_type_e target_nf_type, OpenAPI_nf_type_e requester_nf_type); + +#ifdef __cplusplus +} +#endif + +#endif /* AMF_NNRF_BUILD_H */ diff --git a/src/amf/nnrf-handler.c b/src/amf/nnrf-handler.c new file mode 100644 index 000000000..e5c11d3c8 --- /dev/null +++ b/src/amf/nnrf-handler.c @@ -0,0 +1,278 @@ +/* + * Copyright (C) 2019,2020 by Sukchan Lee + * + * This file is part of Open5GS. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 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, see . + */ + +#include "sbi-path.h" +#include "nnrf-handler.h" + +void amf_nnrf_handle_nf_register( + ogs_sbi_nf_instance_t *nf_instance, ogs_sbi_message_t *message) +{ + OpenAPI_nf_profile_t *NFProfile = NULL; + ogs_sbi_client_t *client = NULL; + + ogs_assert(message); + ogs_assert(nf_instance); + client = nf_instance->client; + ogs_assert(client); + + NFProfile = message->NFProfile; + if (!NFProfile) { + ogs_error("No NFProfile"); + return; + } + + /* TIME : Update heartbeat from NRF */ + nf_instance->time.heartbeat = NFProfile->heart_beat_timer; +} + +void amf_nnrf_handle_nf_status_subscribe( + ogs_sbi_subscription_t *subscription, ogs_sbi_message_t *message) +{ + OpenAPI_subscription_data_t *SubscriptionData = NULL; + ogs_sbi_client_t *client = NULL; + + ogs_assert(message); + ogs_assert(subscription); + client = subscription->client; + ogs_assert(client); + + SubscriptionData = message->SubscriptionData; + if (!SubscriptionData) { + ogs_error("No SubscriptionData"); + return; + } + + if (!SubscriptionData->subscription_id) { + ogs_error("No SubscriptionId"); + return; + } + ogs_sbi_subscription_set_id( + subscription, SubscriptionData->subscription_id); + + if (SubscriptionData->validity_time) { + struct timeval tv; + struct tm local, next; + ogs_time_t diff, duration; + if (ogs_strptime(SubscriptionData->validity_time, + OGS_TIME_ISO8601_FORMAT, &next)) { + ogs_gettimeofday(&tv); + ogs_localtime(tv.tv_sec, &local); + diff = ogs_mktime(&next) - ogs_mktime(&local); +#define VALIDITY_MARGIN 5 /* 5 seconds */ +#define VALIDITY_MINIMUM 60 /* 60 seconds */ + duration = diff - (int)VALIDITY_MARGIN; + + if (duration < (int)VALIDITY_MINIMUM) { + ogs_warn("Validation period [%d seconds, %s] is too small", + (int)diff, SubscriptionData->validity_time); + duration = VALIDITY_MINIMUM; + ogs_warn("Forced to %d seconds", VALIDITY_MINIMUM); + } + + subscription->t_validity = ogs_timer_add(amf_self()->timer_mgr, + amf_timer_subscription_validity, subscription); + ogs_assert(subscription->t_validity); + ogs_timer_start( + subscription->t_validity, ogs_time_from_sec(duration)); + } + } +} + +bool amf_nnrf_handle_nf_status_notify(ogs_sbi_server_t *server, + ogs_sbi_session_t *session, ogs_sbi_message_t *message) +{ + ogs_sbi_response_t *response = NULL; + OpenAPI_notification_data_t *NotificationData = NULL; + OpenAPI_nf_profile_t *NFProfile = NULL; + ogs_sbi_nf_instance_t *nf_instance = NULL; + bool handled; + + ogs_assert(session); + ogs_assert(message); + + NotificationData = message->NotificationData; + if (!NotificationData) { + ogs_error("No NotificationData"); + ogs_sbi_server_send_error(session, OGS_SBI_HTTP_STATUS_BAD_REQUEST, + message, "No NotificationData", NULL); + return false; + } + + NFProfile = NotificationData->nf_profile; + if (!NFProfile) { + ogs_error("No NFProfile"); + ogs_sbi_server_send_error(session, OGS_SBI_HTTP_STATUS_BAD_REQUEST, + message, "No NFProfile", NULL); + return false; + } + + if (!NFProfile->nf_instance_id) { + ogs_error("No NFProfile.NFInstanceId"); + ogs_sbi_server_send_error(session, OGS_SBI_HTTP_STATUS_BAD_REQUEST, + message, "No NFProfile", "NFInstanceId"); + return false; + } + + if (!NFProfile->nf_instance_id) { + ogs_error("No NFProfile.NFInstanceId"); + ogs_sbi_server_send_error(session, OGS_SBI_HTTP_STATUS_BAD_REQUEST, + message, "No NFProfile", "NFInstanceId"); + return false; + } + + if (NF_INSTANCE_IS_SELF(NFProfile->nf_instance_id)) { + ogs_error("The notification is not allowed [%s]", + NFProfile->nf_instance_id); + ogs_sbi_server_send_error(session, OGS_SBI_HTTP_STATUS_FORBIDDEN, + message, "The notification is not allowed", + NFProfile->nf_instance_id); + return false; + } + + if (NotificationData->event == + OpenAPI_notification_event_type_NF_REGISTERED) { + ogs_sbi_client_t *client = NULL; + + nf_instance = ogs_sbi_nf_instance_find(NFProfile->nf_instance_id); + if (!nf_instance) { + nf_instance = ogs_sbi_nf_instance_add(NFProfile->nf_instance_id); + ogs_assert(nf_instance); + + amf_nf_fsm_init(nf_instance); + ogs_info("(NRF-notify) NF registered [%s]", nf_instance->id); + } else + ogs_warn("(NRF-notify) NF [%s] has already been added", + NFProfile->nf_instance_id); + + handled = ogs_sbi_nnrf_handle_nf_profile( + nf_instance, NFProfile, session, message); + if (!handled) return false; + + ogs_info("(NRF-notify) NF Profile updated [%s]", nf_instance->id); + + client = ogs_sbi_nf_instance_find_client(nf_instance); + if (!client) { + ogs_error("Cannot find client [%s]", nf_instance->id); + ogs_sbi_server_send_error(session, + OGS_SBI_HTTP_STATUS_BAD_REQUEST, + message, "Cannot find client", nf_instance->id); + return false; + } + amf_sbi_nf_associate_client(nf_instance, client); + + } else if (NotificationData->event == + OpenAPI_notification_event_type_NF_DEREGISTERED) { + nf_instance = ogs_sbi_nf_instance_find(NFProfile->nf_instance_id); + if (nf_instance) { + ogs_info("(NRF-notify) NF de-registered [%s]", nf_instance->id); + amf_nf_fsm_fini(nf_instance); + ogs_sbi_nf_instance_remove(nf_instance); + + /* FIXME : Remove unnecessary Client */ + } else { + ogs_warn("(NRF-notify) Not found [%s]", NFProfile->nf_instance_id); + ogs_sbi_server_send_error(session, + OGS_SBI_HTTP_STATUS_NOT_FOUND, + message, "Not found", message->h.resource.id); + return false; + } + } else { + char *eventstr = OpenAPI_notification_event_type_ToString( + NotificationData->event); + ogs_error("Not supported event [%d:%s]", + NotificationData->event, eventstr ? eventstr : "Unknown"); + ogs_sbi_server_send_error(session, OGS_SBI_HTTP_STATUS_BAD_REQUEST, + message, "Not supported event", + eventstr ? eventstr : "Unknown"); + return false; + } + + response = ogs_sbi_build_response(message); + ogs_assert(response); + ogs_sbi_server_send_response(session, response, + OGS_SBI_HTTP_STATUS_NO_CONTENT); + + return true; +} + +void amf_nnrf_handle_nf_discover(ogs_sbi_message_t *message) +{ + OpenAPI_search_result_t *SearchResult = NULL; + OpenAPI_lnode_t *node = NULL; + bool handled; + + ogs_assert(message); + + SearchResult = message->SearchResult; + if (!SearchResult) { + ogs_error("No SearchResult"); + return; + } + + OpenAPI_list_for_each(SearchResult->nf_instances, node) { + OpenAPI_nf_profile_t *NFProfile = NULL; + ogs_sbi_nf_instance_t *nf_instance = NULL; + ogs_sbi_client_t *client = NULL; + + if (!node->data) continue; + + NFProfile = node->data; + + nf_instance = ogs_sbi_nf_instance_find(NFProfile->nf_instance_id); + if (!nf_instance) { + nf_instance = ogs_sbi_nf_instance_add(NFProfile->nf_instance_id); + ogs_assert(nf_instance); + + amf_nf_fsm_init(nf_instance); + ogs_info("(NF-discover) NF registered [%s]", nf_instance->id); + } else + ogs_warn("(NF-discover) NF [%s] has already been added", + NFProfile->nf_instance_id); + + if (NF_INSTANCE_IS_OTHERS(nf_instance->id)) { + handled = ogs_sbi_nnrf_handle_nf_profile( + nf_instance, NFProfile, NULL, NULL); + if (!handled) { + ogs_error("ogs_sbi_nnrf_handle_nf_profile() failed [%s]", + nf_instance->id); + continue; + } + + client = ogs_sbi_nf_instance_find_client(nf_instance); + if (!client) { + ogs_error("Cannot find client [%s]", nf_instance->id); + continue; + } + amf_sbi_nf_associate_client(nf_instance, client); + + /* TIME : Update validity from NRF */ + if (SearchResult->validity_period) { + nf_instance->time.validity = SearchResult->validity_period; + + ogs_assert(nf_instance->t_validity); + ogs_timer_start(nf_instance->t_validity, + ogs_time_from_sec(nf_instance->time.validity)); + + } else + ogs_warn("NF Instance validity-time should not 0"); + + ogs_info("(NF-discover) NF Profile updated [%s]", nf_instance->id); + } + } +} diff --git a/src/amf/nnrf-handler.h b/src/amf/nnrf-handler.h new file mode 100644 index 000000000..7ef42d1cf --- /dev/null +++ b/src/amf/nnrf-handler.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2019,2020 by Sukchan Lee + * + * This file is part of Open5GS. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 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, see . + */ + +#ifndef AMF_NNRF_HANDLER_H +#define AMF_NNRF_HANDLER_H + +#include "ogs-sbi.h" +#include "context.h" + +#ifdef __cplusplus +extern "C" { +#endif + +void amf_nnrf_handle_nf_register( + ogs_sbi_nf_instance_t *nf_instance, ogs_sbi_message_t *message); +void amf_nnrf_handle_nf_status_subscribe( + ogs_sbi_subscription_t *subscription, ogs_sbi_message_t *message); + +bool amf_nnrf_handle_nf_status_notify(ogs_sbi_server_t *server, + ogs_sbi_session_t *session, ogs_sbi_message_t *message); + +void amf_nnrf_handle_nf_discover(ogs_sbi_message_t *message); + +#ifdef __cplusplus +} +#endif + +#endif /* AMF_NNRF_HANDLER_H */ diff --git a/src/amf/sbi-path.c b/src/amf/sbi-path.c new file mode 100644 index 000000000..e525c374d --- /dev/null +++ b/src/amf/sbi-path.c @@ -0,0 +1,199 @@ +/* + * Copyright (C) 2019,2020 by Sukchan Lee + * + * This file is part of Open5GS. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 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, see . + */ + +#include "context.h" +#include "sbi-path.h" + +static int server_cb(ogs_sbi_server_t *server, + ogs_sbi_session_t *session, ogs_sbi_request_t *request) +{ + amf_event_t *e = NULL; + int rv; + + ogs_assert(session); + ogs_assert(request); + + e = amf_event_new(AMF_EVT_SBI_SERVER); + ogs_assert(e); + + e->sbi.server = server; + e->sbi.session = session; + e->sbi.request = request; + + rv = ogs_queue_push(amf_self()->queue, e); + if (rv != OGS_OK) { + ogs_warn("ogs_queue_push() failed:%d", (int)rv); + amf_event_free(e); + return OGS_ERROR; + } + + return OGS_OK; +} + +static int client_cb(ogs_sbi_response_t *response, void *data) +{ + amf_event_t *e = NULL; + int rv; + + ogs_assert(response); + + e = amf_event_new(AMF_EVT_SBI_CLIENT); + ogs_assert(e); + e->sbi.response = response; + e->sbi.data = data; + + rv = ogs_queue_push(amf_self()->queue, e); + if (rv != OGS_OK) { + ogs_warn("ogs_queue_push() failed:%d", (int)rv); + amf_event_free(e); + return OGS_ERROR; + } + + return OGS_OK; +} + +int amf_sbi_open(void) +{ + ogs_sbi_nf_instance_t *nf_instance = NULL; + ogs_sbi_client_t *client = NULL; + + ogs_sbi_server_start_all(server_cb); + + ogs_list_for_each(&ogs_sbi_self()->client_list, client) { + ogs_sbi_nf_service_t *service = NULL; + + nf_instance = ogs_sbi_nf_instance_build_default( + amf_self()->nf_type, client); + ogs_assert(nf_instance); + + service = ogs_sbi_nf_service_build_default(nf_instance, + (char*)OGS_SBI_SERVICE_NAME_SMF_PDUSESSION, client); + ogs_assert(service); + ogs_sbi_nf_service_add_version(service, (char*)OGS_SBI_API_VERSION, + (char*)OGS_SBI_API_FULL_VERSION, NULL); + + amf_sbi_nf_associate_client(nf_instance, client); + amf_nf_fsm_init(nf_instance); + } + + return OGS_OK; +} + +void amf_sbi_close(void) +{ + ogs_sbi_server_stop_all(); +} + +void amf_sbi_nf_associate_client( + ogs_sbi_nf_instance_t *nf_instance, ogs_sbi_client_t *client) +{ + ogs_assert(nf_instance); + ogs_assert(client); + + OGS_SETUP_SBI_CLIENT(nf_instance, client); + client->cb = client_cb; +} + +void amf_sbi_send_nf_register(ogs_sbi_nf_instance_t *nf_instance) +{ + ogs_sbi_request_t *request = NULL; + ogs_sbi_client_t *client = NULL; + + ogs_assert(nf_instance); + client = nf_instance->client; + ogs_assert(client); + + request = amf_nnrf_build_nf_register(nf_instance); + ogs_assert(request); + ogs_sbi_client_send_request(client, request, nf_instance); +} + +void amf_sbi_send_nf_update(ogs_sbi_nf_instance_t *nf_instance) +{ + ogs_sbi_request_t *request = NULL; + ogs_sbi_client_t *client = NULL; + + ogs_assert(nf_instance); + client = nf_instance->client; + ogs_assert(client); + + request = amf_nnrf_build_nf_update(nf_instance); + ogs_assert(request); + ogs_sbi_client_send_request(client, request, nf_instance); +} + +void amf_sbi_send_nf_de_register(ogs_sbi_nf_instance_t *nf_instance) +{ + ogs_sbi_request_t *request = NULL; + ogs_sbi_client_t *client = NULL; + + ogs_assert(nf_instance); + client = nf_instance->client; + ogs_assert(client); + + request = amf_nnrf_build_nf_de_register(nf_instance); + ogs_assert(request); + ogs_sbi_client_send_request(client, request, nf_instance); +} + +void amf_sbi_send_nf_status_subscribe(ogs_sbi_client_t *client, + OpenAPI_nf_type_e nf_type, char *nf_instance_id) +{ + ogs_sbi_request_t *request = NULL; + ogs_sbi_subscription_t *subscription = NULL; + + ogs_assert(client); + + subscription = ogs_sbi_subscription_add(); + ogs_assert(subscription); + subscription->client = client; + subscription->nf_type = nf_type; + if (nf_instance_id) + subscription->nf_instance_id = ogs_strdup(nf_instance_id); + + request = amf_nnrf_build_nf_status_subscribe(subscription); + ogs_assert(request); + ogs_sbi_client_send_request(client, request, subscription); +} + +void amf_sbi_send_nf_status_unsubscribe(ogs_sbi_subscription_t *subscription) +{ + ogs_sbi_request_t *request = NULL; + ogs_sbi_client_t *client = NULL; + + ogs_assert(subscription); + client = subscription->client; + ogs_assert(client); + + request = amf_nnrf_build_nf_status_unsubscribe(subscription); + ogs_assert(request); + ogs_sbi_client_send_request(client, request, subscription); +} + +void amf_sbi_send_nf_discover(ogs_sbi_client_t *client, + OpenAPI_nf_type_e target_nf_type, OpenAPI_nf_type_e requester_nf_type) +{ + ogs_sbi_request_t *request = NULL; + + ogs_assert(client); + + request = amf_nnrf_build_nf_discover(target_nf_type, requester_nf_type); + ogs_assert(request); + ogs_sbi_client_send_request(client, request, NULL); +} diff --git a/src/amf/sbi-path.h b/src/amf/sbi-path.h new file mode 100644 index 000000000..c3ab0887f --- /dev/null +++ b/src/amf/sbi-path.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2019,2020 by Sukchan Lee + * + * This file is part of Open5GS. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 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, see . + */ + +#ifndef AMF_SBI_PATH_H +#define AMF_SBI_PATH_H + +#include "nnrf-build.h" + +#ifdef __cplusplus +extern "C" { +#endif + +int amf_sbi_open(void); +void amf_sbi_close(void); + +void amf_sbi_nf_associate_client( + ogs_sbi_nf_instance_t *nf_instance, ogs_sbi_client_t *client); + +void amf_sbi_send_nf_register(ogs_sbi_nf_instance_t *nf_instance); +void amf_sbi_send_nf_update(ogs_sbi_nf_instance_t *nf_instance); +void amf_sbi_send_nf_de_register(ogs_sbi_nf_instance_t *nf_instance); + +void amf_sbi_send_nf_status_subscribe(ogs_sbi_client_t *client, + OpenAPI_nf_type_e nf_type, char *nf_instance_id); +void amf_sbi_send_nf_status_unsubscribe(ogs_sbi_subscription_t *subscription); + +void amf_sbi_send_nf_discover(ogs_sbi_client_t *client, + OpenAPI_nf_type_e target_nf_type, OpenAPI_nf_type_e requester_nf_type); + + +#ifdef __cplusplus +} +#endif + +#endif /* AMF_SBI_PATH_H */ diff --git a/src/amf/timer.c b/src/amf/timer.c new file mode 100644 index 000000000..327cccb98 --- /dev/null +++ b/src/amf/timer.c @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2019,2020 by Sukchan Lee + * + * This file is part of Open5GS. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 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, see . + */ + +#include "context.h" + +static amf_timer_cfg_t g_amf_timer_cfg[MAX_NUM_OF_AMF_TIMER] = { + [AMF_TIMER_NF_INSTANCE_REGISTRATION_INTERVAL] = + { .duration = ogs_time_from_sec(3) }, +}; + +amf_timer_cfg_t *amf_timer_cfg(amf_timer_e id) +{ + ogs_assert(id < MAX_NUM_OF_AMF_TIMER); + return &g_amf_timer_cfg[id]; +} + +const char *amf_timer_get_name(amf_timer_e id) +{ + switch (id) { + case AMF_TIMER_NF_INSTANCE_REGISTRATION_INTERVAL: + return "AMF_TIMER_NF_INSTANCE_REGISTRATION_INTERVAL"; + case AMF_TIMER_NF_INSTANCE_HEARTBEAT_INTERVAL: + return "AMF_TIMER_NF_INSTANCE_HEARTBEAT_INTERVAL"; + case AMF_TIMER_NF_INSTANCE_HEARTBEAT: + return "AMF_TIMER_NF_INSTANCE_HEARTBEAT"; + case AMF_TIMER_NF_INSTANCE_VALIDITY: + return "AMF_TIMER_NF_INSTANCE_VALIDITY"; + case AMF_TIMER_SUBSCRIPTION_VALIDITY: + return "AMF_TIMER_SUBSCRIPTION_VALIDITY"; + default: + break; + } + + return "UNKNOWN_TIMER"; +} + +static void timer_send_event(int timer_id, void *data) +{ + int rv; + amf_event_t *e = NULL; + ogs_assert(data); + + switch (timer_id) { + case AMF_TIMER_NF_INSTANCE_REGISTRATION_INTERVAL: + case AMF_TIMER_NF_INSTANCE_HEARTBEAT_INTERVAL: + case AMF_TIMER_NF_INSTANCE_HEARTBEAT: + case AMF_TIMER_NF_INSTANCE_VALIDITY: + case AMF_TIMER_SUBSCRIPTION_VALIDITY: + e = amf_event_new(AMF_EVT_SBI_TIMER); + ogs_assert(e); + e->timer_id = timer_id; + e->sbi.data = data; + break; + default: + ogs_fatal("Unknown timer id[%d]", timer_id); + ogs_assert_if_reached(); + break; + } + + rv = ogs_queue_push(amf_self()->queue, e); + if (rv != OGS_OK) { + ogs_warn("ogs_queue_push() failed [%d] in %s", + (int)rv, amf_timer_get_name(e->timer_id)); + amf_event_free(e); + } +} + +void amf_timer_nf_instance_registration_interval(void *data) +{ + timer_send_event(AMF_TIMER_NF_INSTANCE_REGISTRATION_INTERVAL, data); +} + +void amf_timer_nf_instance_heartbeat_interval(void *data) +{ + timer_send_event(AMF_TIMER_NF_INSTANCE_HEARTBEAT_INTERVAL, data); +} + +void amf_timer_nf_instance_heartbeat(void *data) +{ + timer_send_event(AMF_TIMER_NF_INSTANCE_HEARTBEAT, data); +} + +void amf_timer_nf_instance_validity(void *data) +{ + timer_send_event(AMF_TIMER_NF_INSTANCE_VALIDITY, data); +} + +void amf_timer_subscription_validity(void *data) +{ + timer_send_event(AMF_TIMER_SUBSCRIPTION_VALIDITY, data); +} diff --git a/src/amf/timer.h b/src/amf/timer.h new file mode 100644 index 000000000..509e78fc7 --- /dev/null +++ b/src/amf/timer.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2019 by Sukchan Lee + * + * This file is part of Open5GS. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 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, see . + */ + +#ifndef AMF_TIMER_H +#define AMF_TIMER_H + +#include "ogs-core.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* forward declaration */ +typedef enum { + AMF_TIMER_BASE = 0, + + AMF_TIMER_NF_INSTANCE_REGISTRATION_INTERVAL, + AMF_TIMER_NF_INSTANCE_HEARTBEAT_INTERVAL, + AMF_TIMER_NF_INSTANCE_HEARTBEAT, + AMF_TIMER_NF_INSTANCE_VALIDITY, + AMF_TIMER_SUBSCRIPTION_VALIDITY, + + MAX_NUM_OF_AMF_TIMER, + +} amf_timer_e; + +typedef struct amf_timer_cfg_s { + int max_count; + ogs_time_t duration; +} amf_timer_cfg_t; + +amf_timer_cfg_t *amf_timer_cfg(amf_timer_e id); + +const char *amf_timer_get_name(amf_timer_e id); + +void amf_timer_nf_instance_registration_interval(void *data); +void amf_timer_nf_instance_heartbeat_interval(void *data); +void amf_timer_nf_instance_heartbeat(void *data); +void amf_timer_nf_instance_validity(void *data); +void amf_timer_subscription_validity(void *data); + +#ifdef __cplusplus +} +#endif + +#endif /* AMF_TIMER_H */ diff --git a/src/meson.build b/src/meson.build index fc366e9f4..546aa95e8 100644 --- a/src/meson.build +++ b/src/meson.build @@ -28,5 +28,6 @@ subdir('pgw') subdir('pcrf') subdir('nrf') -subdir('smf') subdir('upf') +subdir('smf') +subdir('amf') diff --git a/src/mme/emm-build.c b/src/mme/emm-build.c index 5568f6b05..67fc49fb7 100644 --- a/src/mme/emm-build.c +++ b/src/mme/emm-build.c @@ -70,7 +70,7 @@ ogs_pkbuf_t *emm_build_attach_accept( served_tai_index = mme_find_served_tai(&mme_ue->tai); ogs_debug(" SERVED_TAI_INDEX[%d]", served_tai_index); ogs_assert(served_tai_index >= 0 && - served_tai_index < MAX_NUM_OF_SERVED_TAI); + served_tai_index < OGS_MAX_NUM_OF_SERVED_TAI); ogs_nas_tai_list_build(&attach_accept->tai_list, &mme_self()->served_tai[served_tai_index].list0, &mme_self()->served_tai[served_tai_index].list2); @@ -368,7 +368,7 @@ ogs_pkbuf_t *emm_build_tau_accept(mme_ue_t *mme_ue) served_tai_index = mme_find_served_tai(&mme_ue->tai); ogs_debug(" SERVED_TAI_INDEX[%d]", served_tai_index); ogs_assert(served_tai_index >= 0 && - served_tai_index < MAX_NUM_OF_SERVED_TAI); + served_tai_index < OGS_MAX_NUM_OF_SERVED_TAI); ogs_nas_tai_list_build(&tau_accept->tai_list, &mme_self()->served_tai[served_tai_index].list0, &mme_self()->served_tai[served_tai_index].list2); diff --git a/src/mme/emm-handler.c b/src/mme/emm-handler.c index c32ec8071..0d94b7b11 100644 --- a/src/mme/emm-handler.c +++ b/src/mme/emm-handler.c @@ -39,7 +39,7 @@ int emm_handle_attach_request( int served_tai_index = 0; ogs_nas_eps_mobile_identity_guti_t *eps_mobile_identity_guti = NULL; - ogs_nas_guti_t nas_guti; + ogs_nas_eps_guti_t nas_guti; enb_ue_t *enb_ue = NULL; ogs_nas_eps_attach_type_t *eps_attach_type = @@ -101,7 +101,7 @@ int emm_handle_attach_request( enb_ue->saved.e_cgi.cell_id); /* Copy TAI and ECGI from enb_ue */ - memcpy(&mme_ue->tai, &enb_ue->saved.tai, sizeof(ogs_tai_t)); + memcpy(&mme_ue->tai, &enb_ue->saved.tai, sizeof(ogs_eps_tai_t)); memcpy(&mme_ue->e_cgi, &enb_ue->saved.e_cgi, sizeof(ogs_e_cgi_t)); /* Check TAI */ @@ -120,7 +120,7 @@ int emm_handle_attach_request( /* Store UE specific information */ if (attach_request->presencemask & OGS_NAS_EPS_ATTACH_REQUEST_LAST_VISITED_REGISTERED_TAI_PRESENT) { - ogs_nas_tracking_area_identity_t *last_visited_registered_tai = + ogs_nas_eps_tai_t *last_visited_registered_tai = &attach_request->last_visited_registered_tai; ogs_nas_to_plmn_id(&mme_ue->last_visited_plmn_id, @@ -426,7 +426,7 @@ int emm_handle_tau_request(mme_ue_t *mme_ue, int served_tai_index = 0; ogs_nas_eps_mobile_identity_guti_t *eps_mobile_identity_guti = NULL; - ogs_nas_guti_t nas_guti; + ogs_nas_eps_guti_t nas_guti; ogs_nas_eps_update_type_t *eps_update_type = &tau_request->eps_update_type; @@ -486,7 +486,7 @@ int emm_handle_tau_request(mme_ue_t *mme_ue, enb_ue->saved.e_cgi.cell_id); /* Copy TAI and ECGI from enb_ue */ - memcpy(&mme_ue->tai, &enb_ue->saved.tai, sizeof(ogs_tai_t)); + memcpy(&mme_ue->tai, &enb_ue->saved.tai, sizeof(ogs_eps_tai_t)); memcpy(&mme_ue->e_cgi, &enb_ue->saved.e_cgi, sizeof(ogs_e_cgi_t)); /* Check TAI */ @@ -503,7 +503,7 @@ int emm_handle_tau_request(mme_ue_t *mme_ue, /* Store UE specific information */ if (tau_request->presencemask & OGS_NAS_EPS_TRACKING_AREA_UPDATE_REQUEST_LAST_VISITED_REGISTERED_TAI_PRESENT) { - ogs_nas_tracking_area_identity_t *last_visited_registered_tai = + ogs_nas_eps_tai_t *last_visited_registered_tai = &tau_request->last_visited_registered_tai; ogs_nas_to_plmn_id(&mme_ue->last_visited_plmn_id, @@ -609,7 +609,7 @@ int emm_handle_extended_service_request(mme_ue_t *mme_ue, enb_ue->saved.e_cgi.cell_id); /* Copy TAI and ECGI from enb_ue */ - memcpy(&mme_ue->tai, &enb_ue->saved.tai, sizeof(ogs_tai_t)); + memcpy(&mme_ue->tai, &enb_ue->saved.tai, sizeof(ogs_eps_tai_t)); memcpy(&mme_ue->e_cgi, &enb_ue->saved.e_cgi, sizeof(ogs_e_cgi_t)); /* Check TAI */ diff --git a/src/mme/mme-context.c b/src/mme/mme-context.c index 5d45a733e..763617618 100644 --- a/src/mme/mme-context.c +++ b/src/mme/mme-context.c @@ -125,7 +125,7 @@ void mme_context_init() ogs_pool_init(&mme_csmap_pool, ogs_config()->max.csmap); /* Allocate TWICE the pool to check if maximum number of eNBs is reached */ - ogs_pool_init(&mme_enb_pool, ogs_config()->max.enb*2); + ogs_pool_init(&mme_enb_pool, ogs_config()->max.gnb*2); ogs_pool_init(&mme_ue_pool, ogs_config()->pool.ue); ogs_pool_init(&enb_ue_pool, ogs_config()->pool.ue); @@ -704,7 +704,7 @@ int mme_context_parse_config() ogs_plmn_id_t *plmn_id = NULL; const char *mcc = NULL, *mnc = NULL; ogs_assert(gummei->num_of_plmn_id <= - MAX_PLMN_ID); + OGS_MAX_NUM_OF_PLMN); plmn_id = &gummei->plmn_id[ gummei->num_of_plmn_id]; ogs_assert(plmn_id); @@ -833,11 +833,11 @@ int mme_context_parse_config() YAML_SEQUENCE_NODE); } else if (!strcmp(mme_key, "tai")) { int num_of_list0 = 0; - tai0_list_t *list0 = NULL; - tai2_list_t *list2 = NULL; + ogs_eps_tai0_list_t *list0 = NULL; + ogs_eps_tai2_list_t *list2 = NULL; ogs_assert(self.num_of_served_tai <= - MAX_NUM_OF_SERVED_TAI); + OGS_MAX_NUM_OF_SERVED_TAI); list0 = &self.served_tai[self.num_of_served_tai].list0; ogs_assert(list0); list2 = &self.served_tai[self.num_of_served_tai].list2; @@ -923,9 +923,9 @@ int mme_context_parse_config() list2->num++; if (list2->num > 1) - list2->type = TAI2_TYPE; + list2->type = OGS_TAI2_TYPE; else - list2->type = TAI1_TYPE; + list2->type = OGS_TAI1_TYPE; } else if (num_of_tac > 1) { int i; ogs_plmn_id_build( @@ -936,7 +936,7 @@ int mme_context_parse_config() } list0->tai[num_of_list0].num = num_of_tac; - list0->tai[num_of_list0].type = TAI0_TYPE; + list0->tai[num_of_list0].type = OGS_TAI0_TYPE; num_of_list0++; } @@ -1839,16 +1839,16 @@ void mme_csmap_remove_all(void) mme_csmap_remove(csmap); } -mme_csmap_t *mme_csmap_find_by_tai(ogs_tai_t *tai) +mme_csmap_t *mme_csmap_find_by_tai(ogs_eps_tai_t *tai) { mme_csmap_t *csmap = NULL; ogs_assert(tai); ogs_list_for_each(&self.csmap_list, csmap) { - ogs_nas_tai_t ogs_nas_tai; + ogs_nas_eps_tai_t ogs_nas_tai; ogs_nas_from_plmn_id(&ogs_nas_tai.nas_plmn_id, &tai->plmn_id); ogs_nas_tai.tac = tai->tac; - if (memcmp(&csmap->tai, &ogs_nas_tai, sizeof(ogs_nas_tai_t)) == 0) + if (memcmp(&csmap->tai, &ogs_nas_tai, sizeof(ogs_nas_eps_tai_t)) == 0) return csmap; } @@ -2134,11 +2134,11 @@ static int mme_ue_new_guti(mme_ue_t *mme_ue) /* MME has a VALID GUTI * As such, we need to remove previous GUTI in hash table */ ogs_hash_set(self.guti_ue_hash, - &mme_ue->guti, sizeof(ogs_nas_guti_t), NULL); + &mme_ue->guti, sizeof(ogs_nas_eps_guti_t), NULL); ogs_assert(mme_m_tmsi_free(mme_ue->m_tmsi) == OGS_OK); } - memset(&mme_ue->guti, 0, sizeof(ogs_nas_guti_t)); + memset(&mme_ue->guti, 0, sizeof(ogs_nas_eps_guti_t)); /* Use the first configured plmn_id and mme group id */ ogs_nas_from_plmn_id(&mme_ue->guti.nas_plmn_id, &served_gummei->plmn_id[0]); @@ -2149,7 +2149,7 @@ static int mme_ue_new_guti(mme_ue_t *mme_ue) ogs_assert(mme_ue->m_tmsi); mme_ue->guti.m_tmsi = *(mme_ue->m_tmsi); ogs_hash_set(self.guti_ue_hash, - &mme_ue->guti, sizeof(ogs_nas_guti_t), mme_ue); + &mme_ue->guti, sizeof(ogs_nas_eps_guti_t), mme_ue); return OGS_OK; } @@ -2258,7 +2258,7 @@ void mme_ue_remove(mme_ue_t *mme_ue) /* Clear hash table */ if (mme_ue->m_tmsi) { ogs_hash_set(self.guti_ue_hash, - &mme_ue->guti, sizeof(ogs_nas_guti_t), NULL); + &mme_ue->guti, sizeof(ogs_nas_eps_guti_t), NULL); ogs_assert(mme_m_tmsi_free(mme_ue->m_tmsi) == OGS_OK); } if (mme_ue->imsi_len != 0) @@ -2271,10 +2271,10 @@ void mme_ue_remove(mme_ue_t *mme_ue) CLEAR_SERVICE_INDICATOR(mme_ue); /* Free UeRadioCapability */ - OGS_S1AP_CLEAR_DATA(&mme_ue->ueRadioCapability); + OGS_ASN_CLEAR_DATA(&mme_ue->ueRadioCapability); /* Clear Transparent Container */ - OGS_S1AP_CLEAR_DATA(&mme_ue->container); + OGS_ASN_CLEAR_DATA(&mme_ue->container); /* Delete All Timers */ CLEAR_MME_UE_ALL_TIMERS(mme_ue); @@ -2319,12 +2319,12 @@ mme_ue_t *mme_ue_find_by_imsi(uint8_t *imsi, int imsi_len) return (mme_ue_t *)ogs_hash_get(self.imsi_ue_hash, imsi, imsi_len); } -mme_ue_t *mme_ue_find_by_guti(ogs_nas_guti_t *guti) +mme_ue_t *mme_ue_find_by_guti(ogs_nas_eps_guti_t *guti) { ogs_assert(guti); return (mme_ue_t *)ogs_hash_get( - self.guti_ue_hash, guti, sizeof(ogs_nas_guti_t)); + self.guti_ue_hash, guti, sizeof(ogs_nas_eps_guti_t)); } mme_ue_t *mme_ue_find_by_teid(uint32_t teid) @@ -2345,7 +2345,7 @@ mme_ue_t *mme_ue_find_by_message(ogs_nas_eps_message_t *message) ogs_nas_eps_mobile_identity_guti_t *eps_mobile_identity_guti = NULL; ogs_nas_mobile_identity_tmsi_t *mobile_identity_tmsi = NULL; served_gummei_t *served_gummei = NULL; - ogs_nas_guti_t ogs_nas_guti; + ogs_nas_eps_guti_t ogs_nas_guti; switch (message->emm.h.message_type) { case OGS_NAS_EPS_ATTACH_REQUEST: @@ -3079,20 +3079,20 @@ ogs_pdn_t *mme_default_pdn(mme_ue_t *mme_ue) return NULL; } -int mme_find_served_tai(ogs_tai_t *tai) +int mme_find_served_tai(ogs_eps_tai_t *tai) { int i = 0, j = 0, k = 0; ogs_assert(tai); for (i = 0; i < self.num_of_served_tai; i++) { - tai0_list_t *list0 = &self.served_tai[i].list0; + ogs_eps_tai0_list_t *list0 = &self.served_tai[i].list0; ogs_assert(list0); - tai2_list_t *list2 = &self.served_tai[i].list2; + ogs_eps_tai2_list_t *list2 = &self.served_tai[i].list2; ogs_assert(list2); for (j = 0; list0->tai[j].num; j++) { - ogs_assert(list0->tai[j].type == TAI0_TYPE); + ogs_assert(list0->tai[j].type == OGS_TAI0_TYPE); ogs_assert(list0->tai[j].num < OGS_MAX_NUM_OF_TAI); for (k = 0; k < list0->tai[j].num; k++) { @@ -3105,7 +3105,8 @@ int mme_find_served_tai(ogs_tai_t *tai) } if (list2->num) { - ogs_assert(list2->type == TAI1_TYPE || list2->type == TAI2_TYPE); + ogs_assert(list2->type == OGS_TAI1_TYPE || + list2->type == OGS_TAI2_TYPE); ogs_assert(list2->num < OGS_MAX_NUM_OF_TAI); for (j = 0; j < list2->num; j++) { @@ -3208,17 +3209,3 @@ uint8_t mme_selected_enc_algorithm(mme_ue_t *mme_ue) return 0; } - -bool mme_is_maximum_number_of_enbs_reached(void) -{ - mme_enb_t *enb = NULL, *next_enb = NULL; - int number_of_enbs_online = 0; - - ogs_list_for_each_safe(&self.enb_list, next_enb, enb) { - if (enb->state.s1_setup_success) { - number_of_enbs_online++; - } - } - - return number_of_enbs_online >= ogs_config()->max.enb; -} diff --git a/src/mme/mme-context.h b/src/mme/mme-context.h index c1091e4a1..bc15573a0 100644 --- a/src/mme/mme-context.h +++ b/src/mme/mme-context.h @@ -35,15 +35,10 @@ extern "C" { #endif -#define MAX_PLMN_ID 6 #define GRP_PER_MME 256 /* According to spec it is 65535 */ #define CODE_PER_MME 256 /* According to spec it is 256 */ -#define MAX_NUM_OF_SERVED_TAI 16 #define MAX_NUM_OF_SERVED_GUMMEI 8 -#define MAX_NUM_OF_ALGORITHM 8 - -#define MAX_NUM_OF_BPLMN 6 extern int __mme_log_domain; extern int __emm_log_domain; @@ -72,7 +67,7 @@ typedef enum { typedef struct served_gummei_s { uint32_t num_of_plmn_id; - ogs_plmn_id_t plmn_id[MAX_PLMN_ID]; + ogs_plmn_id_t plmn_id[OGS_MAX_NUM_OF_PLMN]; uint32_t num_of_mme_gid; uint16_t mme_gid[GRP_PER_MME]; @@ -117,9 +112,9 @@ typedef struct mme_context_s { /* Served TAI */ uint8_t num_of_served_tai; struct { - tai0_list_t list0; - tai2_list_t list2; - } served_tai[MAX_NUM_OF_SERVED_TAI]; + ogs_eps_tai0_list_t list0; + ogs_eps_tai2_list_t list2; + } served_tai[OGS_MAX_NUM_OF_SERVED_TAI]; /* defined in 'nas_ies.h' * #define NAS_SECURITY_ALGORITHMS_EIA0 0 @@ -127,14 +122,14 @@ typedef struct mme_context_s { * #define NAS_SECURITY_ALGORITHMS_128_EEA2 2 * #define NAS_SECURITY_ALGORITHMS_128_EEA3 3 */ uint8_t num_of_ciphering_order; - uint8_t ciphering_order[MAX_NUM_OF_ALGORITHM]; + uint8_t ciphering_order[OGS_MAX_NUM_OF_ALGORITHM]; /* defined in 'nas_ies.h' * #define NAS_SECURITY_ALGORITHMS_EIA0 0 * #define NAS_SECURITY_ALGORITHMS_128_EIA1 1 * #define NAS_SECURITY_ALGORITHMS_128_EIA1 2 * #define NAS_SECURITY_ALGORITHMS_128_EIA3 3 */ uint8_t num_of_integrity_order; - uint8_t integrity_order[MAX_NUM_OF_ALGORITHM]; + uint8_t integrity_order[OGS_MAX_NUM_OF_ALGORITHM]; /* S1SetupResponse */ uint8_t relative_capacity; @@ -209,7 +204,7 @@ typedef struct mme_vlr_s { typedef struct mme_csmap_s { ogs_lnode_t lnode; - ogs_nas_tai_t tai; + ogs_nas_eps_tai_t tai; ogs_nas_lai_t lai; mme_vlr_t *vlr; @@ -235,7 +230,7 @@ typedef struct mme_enb_s { uint8_t num_of_supported_ta_list; - ogs_tai_t supported_ta_list[OGS_MAX_NUM_OF_TAI * MAX_NUM_OF_BPLMN]; + ogs_eps_tai_t supported_ta_list[OGS_MAX_NUM_OF_TAI*OGS_MAX_NUM_OF_BPLMN]; ogs_list_t enb_ue_list; @@ -261,7 +256,7 @@ struct enb_ue_s { * * Save TAI and ECGI. And then, this will copy 'mme_ue_t' context later */ struct { - ogs_tai_t tai; + ogs_eps_tai_t tai; ogs_e_cgi_t e_cgi; } saved; @@ -317,7 +312,7 @@ struct mme_ue_s { mme_m_tmsi_t *m_tmsi; mme_p_tmsi_t p_tmsi; - ogs_nas_guti_t guti; + ogs_nas_eps_guti_t guti; int guti_present; uint32_t mme_s11_teid; /* MME-S11-TEID is derived from INDEX */ @@ -326,7 +321,7 @@ struct mme_ue_s { uint16_t vlr_ostream_id; /* SCTP output stream id for VLR */ /* UE Info */ - ogs_tai_t tai; + ogs_eps_tai_t tai; ogs_e_cgi_t e_cgi; ogs_plmn_id_t last_visited_plmn_id; @@ -659,7 +654,7 @@ mme_csmap_t *mme_csmap_add(mme_vlr_t *vlr); void mme_csmap_remove(mme_csmap_t *csmap); void mme_csmap_remove_all(void); -mme_csmap_t *mme_csmap_find_by_tai(ogs_tai_t *tai); +mme_csmap_t *mme_csmap_find_by_tai(ogs_eps_tai_t *tai); mme_csmap_t *mme_csmap_find_by_nas_lai(ogs_nas_lai_t *lai); mme_enb_t *mme_enb_add(ogs_sock_t *sock, ogs_sockaddr_t *addr); @@ -669,7 +664,6 @@ mme_enb_t *mme_enb_find_by_addr(ogs_sockaddr_t *addr); mme_enb_t *mme_enb_find_by_enb_id(uint32_t enb_id); int mme_enb_set_enb_id(mme_enb_t *enb, uint32_t enb_id); int mme_enb_sock_type(ogs_sock_t *sock); -bool mme_is_maximum_number_of_enbs_reached(void); enb_ue_t *enb_ue_add(mme_enb_t *enb, uint32_t enb_ue_s1ap_id); unsigned int enb_ue_count(void); @@ -688,7 +682,7 @@ void mme_ue_remove_all(void); mme_ue_t *mme_ue_find_by_imsi(uint8_t *imsi, int imsi_len); mme_ue_t *mme_ue_find_by_imsi_bcd(char *imsi_bcd); -mme_ue_t *mme_ue_find_by_guti(ogs_nas_guti_t *nas_guti); +mme_ue_t *mme_ue_find_by_guti(ogs_nas_eps_guti_t *nas_guti); mme_ue_t *mme_ue_find_by_teid(uint32_t teid); mme_ue_t *mme_ue_find_by_message(ogs_nas_eps_message_t *message); @@ -780,7 +774,7 @@ void mme_pdn_remove_all(mme_ue_t *mme_ue); ogs_pdn_t *mme_pdn_find_by_apn(mme_ue_t *mme_ue, char *apn); ogs_pdn_t *mme_default_pdn(mme_ue_t *mme_ue); -int mme_find_served_tai(ogs_tai_t *tai); +int mme_find_served_tai(ogs_eps_tai_t *tai); int mme_m_tmsi_pool_generate(void); mme_m_tmsi_t *mme_m_tmsi_alloc(void); diff --git a/src/mme/mme-event.h b/src/mme/mme-event.h index c9e284d3d..e7fddbda6 100644 --- a/src/mme/mme-event.h +++ b/src/mme/mme-event.h @@ -55,7 +55,7 @@ typedef enum { } mme_event_e; typedef long S1AP_ProcedureCode_t; -typedef struct S1AP_S1AP_PDU s1ap_message_t; +typedef struct S1AP_S1AP_PDU ogs_s1ap_message_t; typedef struct ogs_nas_eps_message_s ogs_nas_eps_message_t; typedef struct mme_vlr_s mme_vlr_t; typedef struct mme_enb_s mme_enb_t; @@ -77,7 +77,7 @@ typedef struct mme_event_s { uint16_t max_num_of_ostreams; S1AP_ProcedureCode_t s1ap_code; - s1ap_message_t *s1ap_message; + ogs_s1ap_message_t *s1ap_message; ogs_gtp_node_t *gnode; diff --git a/src/mme/mme-sm.c b/src/mme/mme-sm.c index 677cc1768..27687c775 100644 --- a/src/mme/mme-sm.c +++ b/src/mme/mme-sm.c @@ -103,7 +103,7 @@ void mme_state_operational(ogs_fsm_t *s, mme_event_t *e) mme_enb_t *enb = NULL; uint16_t max_num_of_ostreams = 0; - s1ap_message_t s1ap_message; + ogs_s1ap_message_t s1ap_message; ogs_pkbuf_t *pkbuf = NULL; int rc; diff --git a/src/mme/s1ap-handler.c b/src/mme/s1ap-handler.c index 88c06b556..35db36e2b 100644 --- a/src/mme/s1ap-handler.c +++ b/src/mme/s1ap-handler.c @@ -40,7 +40,8 @@ static bool served_tai_is_found(mme_enb_t *enb) for (i = 0; i < enb->num_of_supported_ta_list; i++) { served_tai_index = mme_find_served_tai(&enb->supported_ta_list[i]); - if (served_tai_index >= 0 && served_tai_index < MAX_NUM_OF_SERVED_TAI) { + if (served_tai_index >= 0 && + served_tai_index < OGS_MAX_NUM_OF_SERVED_TAI) { ogs_debug(" SERVED_TAI_INDEX[%d]", served_tai_index); return true; } @@ -60,7 +61,7 @@ static bool maximum_number_of_enbs_is_reached(void) } } - return number_of_enbs_online >= ogs_config()->max.enb; + return number_of_enbs_online >= ogs_config()->max.gnb; } void s1ap_handle_s1_setup_request(mme_enb_t *enb, ogs_s1ap_message_t *message) @@ -256,13 +257,14 @@ void s1ap_handle_initial_ue_message(mme_enb_t *enb, ogs_s1ap_message_t *message) /* Find MME_UE if S_TMSI included */ if (S_TMSI) { served_gummei_t *served_gummei = &mme_self()->served_gummei[0]; - ogs_nas_guti_t nas_guti; + ogs_nas_eps_guti_t nas_guti; mme_ue_t *mme_ue = NULL; - memset(&nas_guti, 0, sizeof(ogs_nas_guti_t)); + memset(&nas_guti, 0, sizeof(ogs_nas_eps_guti_t)); /* Use the first configured plmn_id and mme group id */ - ogs_nas_from_plmn_id(&nas_guti.nas_plmn_id, &served_gummei->plmn_id[0]); + ogs_nas_from_plmn_id(&nas_guti.nas_plmn_id, + &served_gummei->plmn_id[0]); nas_guti.mme_gid = served_gummei->mme_gid[0]; /* size must be 1 */ @@ -434,7 +436,7 @@ void s1ap_handle_ue_capability_info_indication( if (enb_ue->mme_ue) { ogs_assert(UERadioCapability); - OGS_S1AP_STORE_DATA(&enb_ue->mme_ue->ueRadioCapability, + OGS_ASN_STORE_DATA(&enb_ue->mme_ue->ueRadioCapability, UERadioCapability); } } @@ -1165,7 +1167,7 @@ void s1ap_handle_path_switch_request( enb_ue->saved.e_cgi.cell_id); /* Copy TAI and ECGI from enb_ue */ - memcpy(&mme_ue->tai, &enb_ue->saved.tai, sizeof(ogs_tai_t)); + memcpy(&mme_ue->tai, &enb_ue->saved.tai, sizeof(ogs_eps_tai_t)); memcpy(&mme_ue->e_cgi, &enb_ue->saved.e_cgi, sizeof(ogs_e_cgi_t)); memcpy(&eea, encryptionAlgorithms->buf, sizeof(eea)); @@ -1529,7 +1531,7 @@ void s1ap_handle_handover_request_ack(mme_enb_t *enb, ogs_s1ap_message_t *messag } } - OGS_S1AP_STORE_DATA(&mme_ue->container, + OGS_ASN_STORE_DATA(&mme_ue->container, Target_ToSource_TransparentContainer); if (mme_ue_have_indirect_tunnel(mme_ue) == 1) { @@ -1858,7 +1860,7 @@ void s1ap_handle_handover_notification(mme_enb_t *enb, ogs_s1ap_message_t *messa target_ue->saved.e_cgi.cell_id); /* Copy TAI and ECGI from enb_ue */ - memcpy(&mme_ue->tai, &target_ue->saved.tai, sizeof(ogs_tai_t)); + memcpy(&mme_ue->tai, &target_ue->saved.tai, sizeof(ogs_eps_tai_t)); memcpy(&mme_ue->e_cgi, &target_ue->saved.e_cgi, sizeof(ogs_e_cgi_t)); sess = mme_sess_first(mme_ue); diff --git a/src/mme/s1ap-path.c b/src/mme/s1ap-path.c index 481772040..05ad4cfa1 100644 --- a/src/mme/s1ap-path.c +++ b/src/mme/s1ap-path.c @@ -350,7 +350,7 @@ void s1ap_send_paging(mme_ue_t *mme_ue, S1AP_CNDomain_t cn_domain) for (i = 0; i < enb->num_of_supported_ta_list; i++) { if (memcmp(&enb->supported_ta_list[i], &mme_ue->tai, - sizeof(ogs_tai_t)) == 0) { + sizeof(ogs_eps_tai_t)) == 0) { if (mme_ue->t3413.pkbuf) { s1apbuf = mme_ue->t3413.pkbuf; diff --git a/src/mme/sbc-handler.c b/src/mme/sbc-handler.c index 0072c6a56..b408565c4 100644 --- a/src/mme/sbc-handler.c +++ b/src/mme/sbc-handler.c @@ -35,7 +35,7 @@ void sbc_handle_write_replace_warning_request(sbc_pws_data_t *sbc_pws) for (i = 0, flag = 0; i < enb->num_of_supported_ta_list; i++) { for (j = 0; j < sbc_pws->no_of_tai; j++) { if (!memcmp(&enb->supported_ta_list[i], - &sbc_pws->tai[j], sizeof(ogs_tai_t))) + &sbc_pws->tai[j], sizeof(ogs_eps_tai_t))) flag = 1; if (flag) break; @@ -70,7 +70,7 @@ void sbc_handle_stop_warning_request(sbc_pws_data_t *sbc_pws) for (i = 0, flag = 0; i < enb->num_of_supported_ta_list; i++) { for (j = 0; j < sbc_pws->no_of_tai; j++) { if (!memcmp(&enb->supported_ta_list[i], - &sbc_pws->tai[j], sizeof(ogs_tai_t))) + &sbc_pws->tai[j], sizeof(ogs_eps_tai_t))) flag = 1; if (flag) break; diff --git a/src/mme/sbc-message.h b/src/mme/sbc-message.h index 8e2c18a90..f3233c1df 100644 --- a/src/mme/sbc-message.h +++ b/src/mme/sbc-message.h @@ -38,7 +38,7 @@ typedef struct _sbc_pws_data_t { uint16_t message_id; uint16_t serial_number; uint32_t no_of_tai; - ogs_tai_t tai[16]; /* TODO: max 65535 */ + ogs_eps_tai_t tai[16]; /* TODO: max 65535 */ uint32_t repetition_period; uint32_t number_of_broadcast; uint8_t data_coding_scheme; diff --git a/src/sgw/sgw-context.h b/src/sgw/sgw-context.h index 06705b2a7..91d2c15c0 100644 --- a/src/sgw/sgw-context.h +++ b/src/sgw/sgw-context.h @@ -125,7 +125,7 @@ typedef struct sgw_bearer_s { uint8_t ebi; /* User-Lication-Info */ - ogs_tai_t tai; + ogs_eps_tai_t tai; ogs_e_cgi_t e_cgi; /* Pkts which will be buffered in case of UE-IDLE */ diff --git a/src/smf/context.c b/src/smf/context.c index 34b382925..073864bc5 100644 --- a/src/smf/context.c +++ b/src/smf/context.c @@ -503,7 +503,7 @@ int smf_context_parse_config(void) } else if (!strcmp(smf_key, "pdn")) { /* handle config in pfcp library */ } else if (!strcmp(smf_key, "sbi")) { - /* handle config in pfcp library */ + /* handle config in sbi library */ } else ogs_warn("unknown key `%s`", smf_key); } diff --git a/src/smf/nf-sm.c b/src/smf/nf-sm.c index 09da81df0..9cd7f38b4 100644 --- a/src/smf/nf-sm.c +++ b/src/smf/nf-sm.c @@ -201,11 +201,12 @@ void smf_nf_state_registered(ogs_fsm_t *s, smf_event_t *e) switch (e->id) { case OGS_FSM_ENTRY_SIG: - client = nf_instance->client; - ogs_assert(client); if (NF_INSTANCE_IS_SELF(nf_instance->id)) { ogs_info("NF registered [%s]", nf_instance->id); + client = nf_instance->client; + ogs_assert(client); + if (nf_instance->time.heartbeat) { ogs_timer_start(nf_instance->t_heartbeat_interval, ogs_time_from_sec(nf_instance->time.heartbeat)); diff --git a/src/smf/nnrf-handler.c b/src/smf/nnrf-handler.c index 810c1e11c..b0530b4ad 100644 --- a/src/smf/nnrf-handler.c +++ b/src/smf/nnrf-handler.c @@ -153,18 +153,8 @@ bool smf_nnrf_handle_nf_status_notify(ogs_sbi_server_t *server, if (!nf_instance) { nf_instance = ogs_sbi_nf_instance_add(NFProfile->nf_instance_id); ogs_assert(nf_instance); + smf_nf_fsm_init(nf_instance); - - client = ogs_sbi_nf_instance_find_client(nf_instance); - if (!client) { - ogs_error("Cannot find client [%s]", nf_instance->id); - ogs_sbi_server_send_error(session, - OGS_SBI_HTTP_STATUS_BAD_REQUEST, - message, "Cannot find client", nf_instance->id); - return false; - } - smf_sbi_nf_associate_client(nf_instance, client); - ogs_info("(NRF-notify) NF registered [%s]", nf_instance->id); } else ogs_warn("(NRF-notify) NF [%s] has already been added", @@ -176,6 +166,15 @@ bool smf_nnrf_handle_nf_status_notify(ogs_sbi_server_t *server, ogs_info("(NRF-notify) NF Profile updated [%s]", nf_instance->id); + client = ogs_sbi_nf_instance_find_client(nf_instance); + if (!client) { + ogs_error("Cannot find client [%s]", nf_instance->id); + ogs_sbi_server_send_error(session, + OGS_SBI_HTTP_STATUS_BAD_REQUEST, + message, "Cannot find client", nf_instance->id); + return false; + } + smf_sbi_nf_associate_client(nf_instance, client); } else if (NotificationData->event == OpenAPI_notification_event_type_NF_DEREGISTERED) { @@ -186,9 +185,13 @@ bool smf_nnrf_handle_nf_status_notify(ogs_sbi_server_t *server, ogs_sbi_nf_instance_remove(nf_instance); /* FIXME : Remove unnecessary Client */ - } else - ogs_warn("(NRF-notify) NF [%s] has already been removed", - NFProfile->nf_instance_id); + } else { + ogs_warn("(NRF-notify) Not found [%s]", NFProfile->nf_instance_id); + ogs_sbi_server_send_error(session, + OGS_SBI_HTTP_STATUS_NOT_FOUND, + message, "Not found", message->h.resource.id); + return false; + } } else { char *eventstr = OpenAPI_notification_event_type_ToString( NotificationData->event); @@ -235,18 +238,11 @@ void smf_nnrf_handle_nf_discover(ogs_sbi_message_t *message) if (!nf_instance) { nf_instance = ogs_sbi_nf_instance_add(NFProfile->nf_instance_id); ogs_assert(nf_instance); + smf_nf_fsm_init(nf_instance); - - client = ogs_sbi_nf_instance_find_client(nf_instance); - if (!client) { - ogs_error("Cannot find client [%s]", nf_instance->id); - continue; - } - smf_sbi_nf_associate_client(nf_instance, client); - - ogs_info("(NF-Discover) NF registered [%s]", nf_instance->id); + ogs_info("(NF-discover) NF registered [%s]", nf_instance->id); } else - ogs_warn("(NF-Discover) NF [%s] has already been added", + ogs_warn("(NF-discover) NF [%s] has already been added", NFProfile->nf_instance_id); if (NF_INSTANCE_IS_OTHERS(nf_instance->id)) { @@ -276,7 +272,7 @@ void smf_nnrf_handle_nf_discover(ogs_sbi_message_t *message) } else ogs_warn("NF Instance validity-time should not 0"); - ogs_info("(NF-Discover) NF Profile updated [%s]", nf_instance->id); + ogs_info("(NF-discover) NF Profile updated [%s]", nf_instance->id); } } } diff --git a/tests/app/5gc-init.c b/tests/app/5gc-init.c new file mode 100644 index 000000000..41456e121 --- /dev/null +++ b/tests/app/5gc-init.c @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2019,2020 by Sukchan Lee + * + * This file is part of Open5GS. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 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, see . + */ + +#include "test-5gc.h" + +static ogs_thread_t *nrf_thread = NULL; +static ogs_thread_t *smf_thread = NULL; +static ogs_thread_t *upf_thread = NULL; +#if 0 +static ogs_thread_t *amf_thread = NULL; +#endif + +int app_initialize(const char *const argv[]) +{ + int rv; + + const char *argv_out[OGS_ARG_MAX]; + bool user_config = false; + int i = 0; + + for (i = 0; argv[i]; i++) { + if (strcmp("-c", argv[i]) == 0) { + user_config = true; + } + argv_out[i] = argv[i]; + } + argv_out[i] = NULL; + + if (!user_config) { + argv_out[i++] = "-c"; + argv_out[i++] = DEFAULT_CONFIG_FILENAME; + argv_out[i] = NULL; + } + + if (ogs_config()->parameter.no_nrf == 0) + nrf_thread = test_child_create("nrf", argv_out); + if (ogs_config()->parameter.no_upf == 0) + upf_thread = test_child_create("upf", argv_out); + if (ogs_config()->parameter.no_smf == 0) + smf_thread = test_child_create("smf", argv_out); + + ogs_sctp_init(ogs_config()->usrsctp.udp_port); + + rv = amf_initialize(); + ogs_assert(rv == OGS_OK); + ogs_info("AMF initialize...done"); + + return OGS_OK;; +} + +void app_terminate(void) +{ + amf_terminate(); + + ogs_sctp_final(); + ogs_info("AMF terminate...done"); + + if (smf_thread) ogs_thread_destroy(smf_thread); + if (upf_thread) ogs_thread_destroy(upf_thread); + if (nrf_thread) ogs_thread_destroy(nrf_thread); +} + +void test_5gc_init(void) +{ + ogs_log_install_domain(&__ogs_sctp_domain, "sctp", OGS_LOG_ERROR); + ogs_log_install_domain(&__ogs_ngap_domain, "ngap", OGS_LOG_ERROR); + ogs_log_install_domain(&__ogs_dbi_domain, "dbi", OGS_LOG_ERROR); + ogs_log_install_domain(&__ogs_nas_domain, "nas", OGS_LOG_ERROR); + ogs_log_install_domain(&__ogs_sbi_domain, "sbi", OGS_LOG_ERROR); + + ogs_assert(ogs_mongoc_init(ogs_config()->db_uri) == OGS_OK); +} diff --git a/tests/epc/app-init.c b/tests/app/epc-init.c similarity index 55% rename from tests/epc/app-init.c rename to tests/app/epc-init.c index 1f6d219e0..34ddc514e 100644 --- a/tests/epc/app-init.c +++ b/tests/app/epc-init.c @@ -17,86 +17,13 @@ * along with this program. If not, see . */ -#include "ogs-sctp.h" - #include "test-epc.h" -#include "test-config-private.h" - -#define MAX_CHILD_PROCESS 8 -#define OGS_ARG_MAX 256 static ogs_thread_t *pcrf_thread = NULL; static ogs_thread_t *pgw_thread = NULL; static ogs_thread_t *sgw_thread = NULL; static ogs_thread_t *hss_thread = NULL; -static ogs_proc_t process[MAX_CHILD_PROCESS]; -static int process_num = 0; - -static void child_main(void *data) -{ - const char **commandLine = data; - ogs_proc_t *current = NULL; - FILE *out = NULL; - char buf[OGS_HUGE_LEN]; - int ret = 0, out_return_code = 0;; - - current = &process[process_num++]; - ret = ogs_proc_create(commandLine, - ogs_proc_option_combined_stdout_stderr| - ogs_proc_option_inherit_environment, - current); - ogs_assert(ret == 0); - out = ogs_proc_stdout(current); - ogs_assert(out); - - while(fgets(buf, OGS_HUGE_LEN, out)) { - printf("%s", buf); - } - - ret = ogs_proc_join(current, &out_return_code); - ogs_assert(ret == 0); - ogs_assert(out_return_code == 0); - - ret = ogs_proc_destroy(current); - ogs_assert(ret == 0); -} - -ogs_thread_t *test_child_create(const char *name, const char *const argv[]) -{ - ogs_thread_t *child = NULL; - const char *commandLine[OGS_ARG_MAX]; - int i = 0; - char command[OGS_MAX_FILEPATH_LEN]; - - while(argv[i] && i < 32) { - commandLine[i] = argv[i]; - i++; - } - commandLine[i] = NULL; - - /* buildroot/src/mme/open5gs-mmed */ - ogs_snprintf(command, sizeof command, "%s%s%s%sd", - MESON_BUILD_ROOT OGS_DIR_SEPARATOR_S "src" OGS_DIR_SEPARATOR_S, - name, OGS_DIR_SEPARATOR_S "open5gs-", name); - commandLine[0] = command; - - child = ogs_thread_create(child_main, commandLine); - ogs_msleep(50); - - return child; -} - -void test_child_terminate(void) -{ - int i; - ogs_proc_t *current = NULL; - for (i = 0; i < process_num; i++) { - current = &process[i]; - ogs_proc_terminate(current); - } -} - int app_initialize(const char *const argv[]) { int rv; @@ -149,3 +76,14 @@ void app_terminate(void) if (pgw_thread) ogs_thread_destroy(pgw_thread); if (pcrf_thread) ogs_thread_destroy(pcrf_thread); } + +void test_epc_init(void) +{ + ogs_log_install_domain(&__ogs_sctp_domain, "sctp", OGS_LOG_ERROR); + ogs_log_install_domain(&__ogs_s1ap_domain, "s1ap", OGS_LOG_ERROR); + ogs_log_install_domain(&__ogs_diam_domain, "diam", OGS_LOG_ERROR); + ogs_log_install_domain(&__ogs_dbi_domain, "dbi", OGS_LOG_ERROR); + ogs_log_install_domain(&__ogs_nas_domain, "nas", OGS_LOG_ERROR); + + ogs_assert(ogs_mongoc_init(ogs_config()->db_uri) == OGS_OK); +} diff --git a/tests/app/meson.build b/tests/app/meson.build index a645c6138..d6752da55 100644 --- a/tests/app/meson.build +++ b/tests/app/meson.build @@ -1,4 +1,4 @@ -# Copyright (C) 2019 by Sukchan Lee +# Copyright (C) 2019,2020 by Sukchan Lee # This file is part of Open5GS. @@ -14,25 +14,77 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see . - -libtestapp_sources = files(''' - test-packet.h - test-app.h - - test-packet.c - test-app.c -'''.split()) +# libtestapp_inc = include_directories('.') -libtestapp = static_library('testapp', - sources : libtestapp_sources, - c_args : testcore_cc_flags, - include_directories : libtestapp_inc, - dependencies : libtestepc_dep, +libtestepc_cc_args = '-DDEFAULT_CONFIG_FILENAME="@0@/configs/epc.yaml"'.format(meson.build_root()) + +libtestepc_sources = files(''' + test-packet.c + epc-init.c +'''.split()) + +libtestepc = static_library('testepc', + sources : [libtestepc_sources], + c_args : libtestepc_cc_args, + include_directories : [libtestapp_inc, srcinc], + link_with : [libmme, libhss, libsgw, libpgw, libpcrf], + dependencies : [libtestcommon_dep, + libmme_dep, + libhss_dep, + libsgw_dep, + libpgw_dep, + libpcrf_dep], install : false) -libtestapp_dep = declare_dependency( - link_with : libtestapp, - include_directories : libtestapp_inc, - dependencies : libtestepc_dep) +libtestepc_dep = declare_dependency( + link_with : libtestepc, + include_directories : [libtestapp_inc, srcinc], + dependencies : [libtestcommon_dep, + libmme_dep, + libhss_dep, + libsgw_dep, + libpgw_dep, + libpcrf_dep]) + +testepc_sources = files(''' + ../../src/main.c +'''.split()) + +executable('epc', + sources : [testepc_sources], + c_args : libtestepc_cc_args, + include_directories : srcinc, + dependencies : [libtestepc_dep]) + +libtest5gc_cc_args = '-DDEFAULT_CONFIG_FILENAME="@0@/configs/5gc.yaml"'.format(meson.build_root()) + +libtest5gc_sources = files(''' + 5gc-init.c +'''.split()) + +libtest5gc = static_library('test5gc', + sources : [libtest5gc_sources], + c_args : libtest5gc_cc_args, + include_directories : [libtestapp_inc, srcinc], + link_with : libamf, + dependencies : [libtestcommon_dep, + libamf_dep], + install : false) + +libtest5gc_dep = declare_dependency( + link_with : libtest5gc, + include_directories : [libtestapp_inc, srcinc], + dependencies : [libtestcommon_dep, + libamf_dep]) + +test5gc_sources = files(''' + ../../src/main.c +'''.split()) + +executable('5gc', + sources : [test5gc_sources], + c_args : libtest5gc_cc_args, + include_directories : srcinc, + dependencies : [libtest5gc_dep]) diff --git a/tests/app/test-5gc.h b/tests/app/test-5gc.h new file mode 100644 index 000000000..83acbdfc7 --- /dev/null +++ b/tests/app/test-5gc.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2019,2020 by Sukchan Lee + * + * This file is part of Open5GS. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 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, see . + */ + +#ifndef TEST_5GC_H +#define TEST_5GC_H + +#include "test-common.h" +#include "test-ngap.h" + +#include "amf/context.h" + +#ifdef __cplusplus +extern "C" { +#endif + +void test_5gc_init(void); +#define test_5gc_final ogs_mongoc_final + +#ifdef __cplusplus +} +#endif + +#endif /* TEST_5GC_H */ diff --git a/tests/app/test-app.c b/tests/app/test-app.c deleted file mode 100644 index 13a661a82..000000000 --- a/tests/app/test-app.c +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (C) 2019 by Sukchan Lee - * - * This file is part of Open5GS. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 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, see . - */ - -#include "test-app.h" -#include "test-config-private.h" - -static int connected_count = 0; -static void test_diam_logger_handler(enum fd_hook_type type, struct msg * msg, - struct peer_hdr * peer, void * other, struct fd_hook_permsgdata *pmd, - void * regdata) -{ - if (type == HOOK_PEER_CONNECT_SUCCESS) { - connected_count++; - } -} - -void test_app_run(int argc, const char *const argv[], - const char *name, void (*init)(const char * const argv[])) -{ - int rv; - bool user_config; - - /* '-f sample-XXXX.conf -e error' is always added */ - const char *argv_out[argc+4], *new_argv[argc+4]; - int argc_out; - - char conf_file[OGS_MAX_FILEPATH_LEN]; - - user_config = false; - for (argc_out = 0; argc_out < argc; argc_out++) { - if (strcmp("-c", argv[argc_out]) == 0) { - user_config = true; - } - argv_out[argc_out] = argv[argc_out]; - } - argv_out[argc_out] = NULL; - - if (!user_config) { - /* buildroot/configs/XXX-conf.yaml */ - ogs_snprintf(conf_file, sizeof conf_file, "%s%s", - MESON_BUILD_ROOT OGS_DIR_SEPARATOR_S - "configs" OGS_DIR_SEPARATOR_S, name); - argv_out[argc_out++] = "-c"; - argv_out[argc_out++] = conf_file; - argv_out[argc_out] = NULL; - } - - /* buildroot/src/open5gs-main */ - argv_out[0] = MESON_BUILD_ROOT OGS_DIR_SEPARATOR_S - "src" OGS_DIR_SEPARATOR_S "open5gs-main"; - - rv = abts_main(argc_out, argv_out, new_argv); - ogs_assert(rv == OGS_OK); - - ogs_diam_logger_register(test_diam_logger_handler); - - (*init)(new_argv); - - while(1) { - if (connected_count == 1) break; - ogs_msleep(50); - } - - ogs_msleep(500); /* Wait for listening all sockets */ -} - -void test_app_init(void) -{ - ogs_log_install_domain(&__ogs_sctp_domain, "sctp", OGS_LOG_ERROR); - ogs_log_install_domain(&__ogs_s1ap_domain, "s1ap", OGS_LOG_ERROR); - ogs_log_install_domain(&__ogs_diam_domain, "diam", OGS_LOG_ERROR); - ogs_log_install_domain(&__ogs_dbi_domain, "dbi", OGS_LOG_ERROR); - ogs_log_install_domain(&__ogs_nas_domain, "nas", OGS_LOG_ERROR); - - ogs_assert(ogs_mongoc_init(ogs_config()->db_uri) == OGS_OK); -} - diff --git a/tests/app/test-app.h b/tests/app/test-app.h deleted file mode 100644 index 8f037c26b..000000000 --- a/tests/app/test-app.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef TEST_APP_H -#define TEST_APP_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include "ogs-diameter-common.h" -#include "ogs-sctp.h" -#include "ogs-dbi.h" -#include "ogs-app.h" -#include "mme/mme-context.h" -#include "core/abts.h" - -#include "test-epc.h" -#include "test-packet.h" - -void test_app_run(int argc, const char *const argv[], - const char *name, void (*init)(const char * const argv[])); - -void test_app_init(void); -#define test_app_final ogs_mongoc_final - -#ifdef __cplusplus -} -#endif - -#endif /* TEST_APP_H */ diff --git a/tests/app/test-epc.h b/tests/app/test-epc.h new file mode 100644 index 000000000..b30c04a37 --- /dev/null +++ b/tests/app/test-epc.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2019,2020 by Sukchan Lee + * + * This file is part of Open5GS. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 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, see . + */ + +#ifndef TEST_EPC_H +#define TEST_EPC_H + +#include "test-packet.h" + +#include "mme/mme-context.h" + +#ifdef __cplusplus +extern "C" { +#endif + +void test_epc_init(void); +#define test_epc_final ogs_mongoc_final + +#ifdef __cplusplus +} +#endif + +#endif /* TEST_EPC_H */ diff --git a/tests/app/test-packet.c b/tests/app/test-packet.c index f68b88314..3e4a969a3 100644 --- a/tests/app/test-packet.c +++ b/tests/app/test-packet.c @@ -44,128 +44,13 @@ #include "mme/sgsap-path.h" -ogs_socknode_t *testsctp_server(const char *ipstr) -{ - int rv; - ogs_sockaddr_t *addr = NULL; - ogs_socknode_t *node = NULL; - - rv = ogs_getaddrinfo(&addr, AF_UNSPEC, ipstr, OGS_SGSAP_SCTP_PORT, 0); - ogs_assert(rv == OGS_OK); - - node = ogs_socknode_new(addr); - ogs_assert(node); - ogs_socknode_nodelay(node, true); - - ogs_sctp_server(SOCK_SEQPACKET, node); - ogs_assert(node->sock); - - return node; -} - -ogs_socknode_t *testsctp_client(const char *ipstr) -{ - int rv; - ogs_sockaddr_t *addr = NULL; - ogs_socknode_t *node = NULL; - - rv = ogs_getaddrinfo(&addr, AF_UNSPEC, ipstr, OGS_S1AP_SCTP_PORT, 0); - ogs_assert(rv == OGS_OK); - - node = ogs_socknode_new(addr); - ogs_assert(node); - ogs_socknode_nodelay(node, true); - - ogs_sctp_client(SOCK_STREAM, node); - ogs_assert(node->sock); - - return node; -} - -static ogs_sockaddr_t sctp_last_addr; - -ogs_pkbuf_t *testsctp_read(ogs_socknode_t *node, int type) -{ - int size; - ogs_pkbuf_t *recvbuf = NULL; - - ogs_assert(node); - ogs_assert(node->sock); - - recvbuf = ogs_pkbuf_alloc(NULL, OGS_MAX_SDU_LEN); - ogs_pkbuf_put(recvbuf, OGS_MAX_SDU_LEN); - - size = ogs_sctp_recvdata(node->sock, recvbuf->data, OGS_MAX_SDU_LEN, - type == 1 ? &sctp_last_addr : NULL, NULL); - if (size <= 0) { - ogs_error("sgsap_recv() failed"); - return NULL; - } - - ogs_pkbuf_trim(recvbuf, size); - return recvbuf;; -} - -int testenb_s1ap_send(ogs_socknode_t *node, ogs_pkbuf_t *sendbuf) -{ - return s1ap_send(node->sock, sendbuf, NULL, 0); -} - -int testvlr_sgsap_send(ogs_socknode_t *node, ogs_pkbuf_t *sendbuf) -{ - return sgsap_send(node->sock, sendbuf, &sctp_last_addr, 0); -} - -ogs_socknode_t *testenb_gtpu_server(const char *ipstr) -{ - int rv; - ogs_sockaddr_t *addr = NULL; - ogs_socknode_t *node = NULL; - ogs_sock_t *sock = NULL; - - rv = ogs_getaddrinfo(&addr, AF_UNSPEC, ipstr, OGS_GTPV1_U_UDP_PORT, 0); - ogs_assert(rv == OGS_OK); - - node = ogs_socknode_new(addr); - ogs_assert(node); - - sock = ogs_udp_server(node); - ogs_assert(sock); - - return node; -} - -ogs_pkbuf_t *testenb_gtpu_read(ogs_socknode_t *node) -{ - int rc = 0; - ogs_pkbuf_t *recvbuf = ogs_pkbuf_alloc(NULL, OGS_MAX_SDU_LEN); - ogs_pkbuf_put(recvbuf, OGS_MAX_SDU_LEN); - - ogs_assert(node); - ogs_assert(node->sock); - - while (1) { - rc = ogs_recv(node->sock->fd, recvbuf->data, recvbuf->len, 0); - if (rc <= 0) { - if (errno == EAGAIN) { - continue; - } - break; - } else { - break; - } - } - recvbuf->len = rc; - - return recvbuf; -} +#include "test-packet.h" bool test_no_mme_self = 0; int testenb_gtpu_send(ogs_socknode_t *node, ogs_pkbuf_t *sendbuf) { int rv; - ogs_hash_index_t *hi = NULL; mme_ue_t *mme_ue = NULL; mme_sess_t *sess = NULL; mme_bearer_t *bearer = NULL; @@ -213,17 +98,10 @@ int testenb_gtpu_send(ogs_socknode_t *node, ogs_pkbuf_t *sendbuf) return OGS_OK; } -void testenb_gtpu_close(ogs_socknode_t *node) -{ - ogs_socknode_free(node); -} - int tests1ap_build_setup_req( ogs_pkbuf_t **pkbuf, S1AP_ENB_ID_PR present, uint32_t enb_id, int tac, uint16_t mcc, uint16_t mnc, uint16_t mnc_len) { - int rv; - ogs_plmn_id_t plmn_id; S1AP_S1AP_PDU_t pdu; @@ -1187,12 +1065,9 @@ int tests1ap_build_ue_context_modification_response( ogs_pkbuf_t **pkbuf, uint32_t mme_ue_s1ap_id, uint32_t enb_ue_s1ap_id) { - int rv; - S1AP_S1AP_PDU_t pdu; S1AP_SuccessfulOutcome_t *successfulOutcome = NULL; S1AP_UEContextModificationResponse_t *UEContextModificationResponse = NULL; - ogs_sockaddr_t *addr = NULL; S1AP_UEContextModificationResponseIEs_t *ie = NULL; S1AP_MME_UE_S1AP_ID_t *MME_UE_S1AP_ID = NULL; @@ -2480,8 +2355,6 @@ static void build_bearer_resource_modification_request(ogs_pkbuf_t **pkbuf, ogs_gtp_tft_t tft; ogs_tlv_octet_t octet; - int len; - char tft_buf[OGS_GTP_MAX_TRAFFIC_FLOW_TEMPLATE]; ogs_ipsubnet_t ipsubnet; memset(&message, 0, sizeof(message)); @@ -2669,7 +2542,6 @@ int tests1ap_build_bearer_resource_modification_request( uint8_t tft_code, uint8_t qci, uint8_t ul_mbr, uint8_t dl_mbr, uint8_t ul_gbr, uint8_t dl_gbr) { - int rv; ogs_pkbuf_t *emmbuf = NULL; S1AP_S1AP_PDU_t pdu; @@ -2680,8 +2552,6 @@ int tests1ap_build_bearer_resource_modification_request( S1AP_MME_UE_S1AP_ID_t *MME_UE_S1AP_ID = NULL; S1AP_ENB_UE_S1AP_ID_t *ENB_UE_S1AP_ID = NULL; S1AP_NAS_PDU_t *NAS_PDU = NULL; - S1AP_EUTRAN_CGI_t *EUTRAN_CGI = NULL; - S1AP_TAI_t *TAI = NULL; memset(&pdu, 0, sizeof (S1AP_S1AP_PDU_t)); pdu.present = S1AP_S1AP_PDU_PR_initiatingMessage; @@ -3070,8 +2940,6 @@ int tests1ap_build_handover_request_ack( S1AP_MME_UE_S1AP_ID_t *MME_UE_S1AP_ID = NULL; S1AP_ENB_UE_S1AP_ID_t *ENB_UE_S1AP_ID = NULL; S1AP_E_RABAdmittedList_t *E_RABAdmittedList = NULL; - S1AP_E_RABFailedtoSetupListHOReqAck_t - *E_RABFailedtoSetupListHOReqAck = NULL; S1AP_Target_ToSource_TransparentContainer_t *Target_ToSource_TransparentContainer = NULL; diff --git a/tests/app/test-packet.h b/tests/app/test-packet.h index 39f04e3bb..9d5b0b58f 100644 --- a/tests/app/test-packet.h +++ b/tests/app/test-packet.h @@ -22,7 +22,8 @@ #include "ogs-s1ap.h" #include "mme/s1ap-build.h" -#include "core/abts.h" + +#include "test-common.h" #ifdef __cplusplus extern "C" { @@ -30,24 +31,10 @@ extern "C" { extern bool test_no_mme_self; -ogs_socknode_t *testsctp_server(const char *ipstr); -ogs_socknode_t *testsctp_client(const char *ipstr); -ogs_pkbuf_t *testsctp_read(ogs_socknode_t *node, int type); - -#define testenb_s1ap_client testsctp_client -#define testenb_s1ap_read(x) testsctp_read(x, 0); -int testenb_s1ap_send(ogs_socknode_t *node, ogs_pkbuf_t *sendbuf); -#define testenb_s1ap_close ogs_socknode_free - -#define testvlr_sgsap_server testsctp_server -#define testvlr_sgsap_read(x) testsctp_read(x, 1); -int testvlr_sgsap_send(ogs_socknode_t *node, ogs_pkbuf_t *sendbuf); -#define testvlr_sgsap_close ogs_socknode_free - -ogs_socknode_t *testenb_gtpu_server(const char *ipstr); -ogs_pkbuf_t *testenb_gtpu_read(ogs_socknode_t *node); +#define testenb_gtpu_server(x) test_gtpu_server(x, OGS_GTPV1_U_UDP_PORT) +#define testenb_gtpu_read(x) test_gtpu_read(x) int testenb_gtpu_send(ogs_socknode_t *node, ogs_pkbuf_t *sendbuf); -void testenb_gtpu_close(ogs_socknode_t *node); +#define testenb_gtpu_close(x) test_gtpu_close(x) int tests1ap_build_invalid_packet(ogs_pkbuf_t **pkbuf, int i); diff --git a/tests/common/application.c b/tests/common/application.c new file mode 100644 index 000000000..02f8fa331 --- /dev/null +++ b/tests/common/application.c @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2019,2020 by Sukchan Lee + * + * This file is part of Open5GS. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 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, see . + */ + +#include "test-config-private.h" +#include "test-common.h" + +static int connected_count = 0; +static void test_diam_logger_handler(enum fd_hook_type type, struct msg * msg, + struct peer_hdr * peer, void * other, struct fd_hook_permsgdata *pmd, + void * regdata) +{ + if (type == HOOK_PEER_CONNECT_SUCCESS) { + connected_count++; + } +} + +static void test_app_run(int argc, const char *const argv[], + const char *name, void (*init)(const char * const argv[])) +{ + int rv; + bool user_config; + + /* '-f sample-XXXX.conf -e error' is always added */ + const char *argv_out[argc+4], *new_argv[argc+4]; + int argc_out; + + char conf_file[OGS_MAX_FILEPATH_LEN]; + + user_config = false; + for (argc_out = 0; argc_out < argc; argc_out++) { + if (strcmp("-c", argv[argc_out]) == 0) { + user_config = true; + } + argv_out[argc_out] = argv[argc_out]; + } + argv_out[argc_out] = NULL; + + if (!user_config) { + /* buildroot/configs/XXX-conf.yaml */ + ogs_snprintf(conf_file, sizeof conf_file, "%s%s", + MESON_BUILD_ROOT OGS_DIR_SEPARATOR_S + "configs" OGS_DIR_SEPARATOR_S, name); + argv_out[argc_out++] = "-c"; + argv_out[argc_out++] = conf_file; + argv_out[argc_out] = NULL; + } + + /* buildroot/src/open5gs-main */ + argv_out[0] = MESON_BUILD_ROOT OGS_DIR_SEPARATOR_S + "src" OGS_DIR_SEPARATOR_S "open5gs-main"; + + rv = abts_main(argc_out, argv_out, new_argv); + ogs_assert(rv == OGS_OK); + + (*init)(new_argv); +} + +void test_epc_run(int argc, const char *const argv[], + const char *name, void (*init)(const char * const argv[])) +{ + ogs_diam_logger_register(test_diam_logger_handler); + + test_app_run(argc, argv, name, init); + + while(1) { + if (connected_count == 1) break; + ogs_msleep(50); + } + + ogs_msleep(500); /* Wait for listening all sockets */ +} + +void test_5gc_run(int argc, const char *const argv[], + const char *name, void (*init)(const char * const argv[])) +{ + int rv; + + test_app_run(argc, argv, name, init); + + test_context_init(); + + rv = test_context_parse_config(); + ogs_assert(rv == OGS_OK); + + ogs_msleep(500); /* Wait for listening all sockets */ +} + +#define MAX_CHILD_PROCESS 8 +#define OGS_ARG_MAX 256 + +static ogs_proc_t process[MAX_CHILD_PROCESS]; +static int process_num = 0; + +static void child_main(void *data) +{ + const char **commandLine = data; + ogs_proc_t *current = NULL; + FILE *out = NULL; + char buf[OGS_HUGE_LEN]; + int ret = 0, out_return_code = 0;; + + current = &process[process_num++]; + ret = ogs_proc_create(commandLine, + ogs_proc_option_combined_stdout_stderr| + ogs_proc_option_inherit_environment, + current); + ogs_assert(ret == 0); + out = ogs_proc_stdout(current); + ogs_assert(out); + + while(fgets(buf, OGS_HUGE_LEN, out)) { + printf("%s", buf); + } + + ret = ogs_proc_join(current, &out_return_code); + ogs_assert(ret == 0); + ogs_assert(out_return_code == 0); + + ret = ogs_proc_destroy(current); + ogs_assert(ret == 0); +} + +ogs_thread_t *test_child_create(const char *name, const char *const argv[]) +{ + ogs_thread_t *child = NULL; + const char *commandLine[OGS_ARG_MAX]; + int i = 0; + char command[OGS_MAX_FILEPATH_LEN]; + + while(argv[i] && i < 32) { + commandLine[i] = argv[i]; + i++; + } + commandLine[i] = NULL; + + /* buildroot/src/mme/open5gs-mmed */ + ogs_snprintf(command, sizeof command, "%s%s%s%sd", + MESON_BUILD_ROOT OGS_DIR_SEPARATOR_S "src" OGS_DIR_SEPARATOR_S, + name, OGS_DIR_SEPARATOR_S "open5gs-", name); + commandLine[0] = command; + + child = ogs_thread_create(child_main, commandLine); + ogs_msleep(50); + + return child; +} + +void test_child_terminate(void) +{ + int i; + ogs_proc_t *current = NULL; + for (i = 0; i < process_num; i++) { + current = &process[i]; + ogs_proc_terminate(current); + } +} diff --git a/tests/common/application.h b/tests/common/application.h new file mode 100644 index 000000000..3ed0dc6e4 --- /dev/null +++ b/tests/common/application.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2019,2020 by Sukchan Lee + * + * This file is part of Open5GS. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 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, see . + */ + +#if !defined(OGS_TEST_INSIDE) && !defined(OGS_TEST_COMPILATION) +#error "This header cannot be included directly." +#endif + +#ifndef TEST_COMMON_APPLICATION_H +#define TEST_COMMON_APPLICATION_H + +#ifdef __cplusplus +extern "C" { +#endif + +void test_epc_run(int argc, const char *const argv[], + const char *name, void (*init)(const char * const argv[])); +void test_5gc_run(int argc, const char *const argv[], + const char *name, void (*init)(const char * const argv[])); +void test_child_terminate(void); +ogs_thread_t *test_child_create(const char *name, const char *const argv[]); + +#ifdef __cplusplus +} +#endif + +#endif /* TEST_COMMON_APPLICATION_H */ diff --git a/tests/common/context.c b/tests/common/context.c new file mode 100644 index 000000000..60bf499b3 --- /dev/null +++ b/tests/common/context.c @@ -0,0 +1,451 @@ +/* + * Copyright (C) 2019,2020 by Sukchan Lee + * + * This file is part of Open5GS. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 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, see . + */ + +#include "test-config-private.h" +#include "test-common.h" + +static test_context_t self; + +static int context_initialized = 0; + +void test_context_init(void) +{ + ogs_assert(context_initialized == 0); + + /* Initialize AMF context */ + memset(&self, 0, sizeof(test_context_t)); + + context_initialized = 1; +} + +void test_context_final(void) +{ + ogs_assert(context_initialized == 1); + + context_initialized = 0; +} + +test_context_t *test_self(void) +{ + return &self; +} + +static int test_context_prepare(void) +{ + return OGS_OK; +} + +static int test_context_validation(void) +{ + return OGS_OK; +} + +int test_context_parse_config(void) +{ + int rv; + yaml_document_t *document = NULL; + ogs_yaml_iter_t root_iter; + + document = ogs_config()->document; + ogs_assert(document); + + rv = test_context_prepare(); + if (rv != OGS_OK) return rv; + + ogs_yaml_iter_init(&root_iter, document); + while (ogs_yaml_iter_next(&root_iter)) { + const char *root_key = ogs_yaml_iter_key(&root_iter); + ogs_assert(root_key); + if (!strcmp(root_key, "amf")) { + ogs_yaml_iter_t amf_iter; + ogs_yaml_iter_recurse(&root_iter, &amf_iter); + while (ogs_yaml_iter_next(&amf_iter)) { + const char *amf_key = ogs_yaml_iter_key(&amf_iter); + ogs_assert(amf_key); + if (!strcmp(amf_key, "guami")) { + ogs_yaml_iter_t guami_array, guami_iter; + ogs_yaml_iter_recurse(&amf_iter, &guami_array); + do { + const char *mcc = NULL, *mnc = NULL; + const char *region = NULL, *set = NULL; + const char *pointer = NULL; + ogs_assert(self.num_of_served_guami <= + MAX_NUM_OF_SERVED_GUAMI); + + if (ogs_yaml_iter_type(&guami_array) == + YAML_MAPPING_NODE) { + memcpy(&guami_iter, &guami_array, + sizeof(ogs_yaml_iter_t)); + } else if (ogs_yaml_iter_type(&guami_array) == + YAML_SEQUENCE_NODE) { + if (!ogs_yaml_iter_next(&guami_array)) + break; + ogs_yaml_iter_recurse(&guami_array, + &guami_iter); + } else if (ogs_yaml_iter_type(&guami_array) == + YAML_SCALAR_NODE) { + break; + } else + ogs_assert_if_reached(); + + while (ogs_yaml_iter_next(&guami_iter)) { + const char *guami_key = + ogs_yaml_iter_key(&guami_iter); + ogs_assert(guami_key); + if (!strcmp(guami_key, "plmn_id")) { + ogs_yaml_iter_t plmn_id_iter; + + ogs_yaml_iter_recurse(&guami_iter, + &plmn_id_iter); + while (ogs_yaml_iter_next(&plmn_id_iter)) { + const char *plmn_id_key = + ogs_yaml_iter_key(&plmn_id_iter); + ogs_assert(plmn_id_key); + if (!strcmp(plmn_id_key, "mcc")) { + mcc = ogs_yaml_iter_value( + &plmn_id_iter); + } else if (!strcmp(plmn_id_key, "mnc")) { + mnc = ogs_yaml_iter_value( + &plmn_id_iter); + } + } + + if (mcc && mnc) { + ogs_plmn_id_build( + &self.served_guami[ + self.num_of_served_guami]. + plmn_id, + atoi(mcc), atoi(mnc), strlen(mnc)); + } + } else if (!strcmp(guami_key, "amf_id")) { + ogs_yaml_iter_t amf_id_iter; + + ogs_yaml_iter_recurse(&guami_iter, + &amf_id_iter); + while (ogs_yaml_iter_next(&amf_id_iter)) { + const char *amf_id_key = + ogs_yaml_iter_key(&amf_id_iter); + ogs_assert(amf_id_key); + if (!strcmp(amf_id_key, "region")) { + region = ogs_yaml_iter_value( + &amf_id_iter); + } else if (!strcmp(amf_id_key, "set")) { + set = ogs_yaml_iter_value( + &amf_id_iter); + } else if (!strcmp(amf_id_key, "pointer")) { + pointer = ogs_yaml_iter_value( + &amf_id_iter); + } + } + + if (region && set) { + ogs_amf_id_build( + &self.served_guami[ + self.num_of_served_guami]. + amf_id, + atoi(region), atoi(set), + pointer ? atoi(pointer) : 0); + } + } else + ogs_warn("unknown key `%s`", guami_key); + } + + if (mnc && mcc && region && set) { + self.num_of_served_guami++; + } else { + ogs_warn("Ignore guami : " + "mcc(%s), mnc(%s), region(%s), set(%s)", + mcc, mnc, region, set); + } + } while (ogs_yaml_iter_type(&guami_array) == + YAML_SEQUENCE_NODE); + } else if (!strcmp(amf_key, "tai")) { + int num_of_list0 = 0; + ogs_5gs_tai0_list_t *list0 = NULL; + ogs_5gs_tai2_list_t *list2 = NULL; + + ogs_assert(self.num_of_served_tai <= + OGS_MAX_NUM_OF_SERVED_TAI); + list0 = &self.served_tai[self.num_of_served_tai].list0; + ogs_assert(list0); + list2 = &self.served_tai[self.num_of_served_tai].list2; + ogs_assert(list2); + + ogs_yaml_iter_t tai_array, tai_iter; + ogs_yaml_iter_recurse(&amf_iter, &tai_array); + do { + const char *mcc = NULL, *mnc = NULL; + ogs_uint24_t tac[OGS_MAX_NUM_OF_TAI]; + int num_of_tac = 0; + + if (ogs_yaml_iter_type(&tai_array) == + YAML_MAPPING_NODE) { + memcpy(&tai_iter, &tai_array, + sizeof(ogs_yaml_iter_t)); + } else if (ogs_yaml_iter_type(&tai_array) == + YAML_SEQUENCE_NODE) { + if (!ogs_yaml_iter_next(&tai_array)) + break; + ogs_yaml_iter_recurse(&tai_array, + &tai_iter); + } else if (ogs_yaml_iter_type(&tai_array) == + YAML_SCALAR_NODE) { + break; + } else + ogs_assert_if_reached(); + + while (ogs_yaml_iter_next(&tai_iter)) { + const char *tai_key = ogs_yaml_iter_key(&tai_iter); + ogs_assert(tai_key); + if (!strcmp(tai_key, "plmn_id")) { + ogs_yaml_iter_t plmn_id_iter; + + ogs_yaml_iter_recurse(&tai_iter, &plmn_id_iter); + while (ogs_yaml_iter_next(&plmn_id_iter)) { + const char *plmn_id_key = + ogs_yaml_iter_key(&plmn_id_iter); + ogs_assert(plmn_id_key); + if (!strcmp(plmn_id_key, "mcc")) { + mcc = ogs_yaml_iter_value( + &plmn_id_iter); + } else if (!strcmp(plmn_id_key, "mnc")) { + mnc = ogs_yaml_iter_value( + &plmn_id_iter); + } + } + } else if (!strcmp(tai_key, "tac")) { + ogs_yaml_iter_t tac_iter; + ogs_yaml_iter_recurse(&tai_iter, &tac_iter); + ogs_assert(ogs_yaml_iter_type(&tac_iter) != + YAML_MAPPING_NODE); + + do { + const char *v = NULL; + + ogs_assert(num_of_tac <= + OGS_MAX_NUM_OF_TAI); + if (ogs_yaml_iter_type(&tac_iter) == + YAML_SEQUENCE_NODE) { + if (!ogs_yaml_iter_next(&tac_iter)) + break; + } + + v = ogs_yaml_iter_value(&tac_iter); + if (v) { + tac[num_of_tac].v = atoi(v); + num_of_tac++; + } + } while ( + ogs_yaml_iter_type(&tac_iter) == + YAML_SEQUENCE_NODE); + } else + ogs_warn("unknown key `%s`", tai_key); + } + + if (mcc && mnc && num_of_tac) { + if (num_of_tac == 1) { + ogs_plmn_id_build( + &list2->tai[list2->num].plmn_id, + atoi(mcc), atoi(mnc), strlen(mnc)); + list2->tai[list2->num].tac.v = tac[0].v; + + list2->num++; + if (list2->num > 1) + list2->type = OGS_TAI2_TYPE; + else + list2->type = OGS_TAI1_TYPE; + } else if (num_of_tac > 1) { + int i; + ogs_plmn_id_build( + &list0->tai[num_of_list0].plmn_id, + atoi(mcc), atoi(mnc), strlen(mnc)); + for (i = 0; i < num_of_tac; i++) { + list0->tai[num_of_list0].tac[i].v = + tac[i].v; + } + + list0->tai[num_of_list0].num = num_of_tac; + list0->tai[num_of_list0].type = OGS_TAI0_TYPE; + + num_of_list0++; + } + } else { + ogs_warn("Ignore tai : mcc(%p), mnc(%p), " + "num_of_tac(%d)", mcc, mnc, num_of_tac); + } + } while (ogs_yaml_iter_type(&tai_array) == + YAML_SEQUENCE_NODE); + + if (list2->num || num_of_list0) { + self.num_of_served_tai++; + } + } else if (!strcmp(amf_key, "plmn")) { + ogs_yaml_iter_t plmn_array, plmn_iter; + ogs_yaml_iter_recurse(&amf_iter, &plmn_array); + do { + const char *mnc = NULL, *mcc = NULL; + ogs_assert(self.num_of_plmn_support <= + OGS_MAX_NUM_OF_PLMN); + + if (ogs_yaml_iter_type(&plmn_array) == + YAML_MAPPING_NODE) { + memcpy(&plmn_iter, &plmn_array, + sizeof(ogs_yaml_iter_t)); + } else if (ogs_yaml_iter_type(&plmn_array) == + YAML_SEQUENCE_NODE) { + if (!ogs_yaml_iter_next(&plmn_array)) + break; + ogs_yaml_iter_recurse(&plmn_array, + &plmn_iter); + } else if (ogs_yaml_iter_type(&plmn_array) == + YAML_SCALAR_NODE) { + break; + } else + ogs_assert_if_reached(); + + while (ogs_yaml_iter_next(&plmn_iter)) { + const char *plmn_key = + ogs_yaml_iter_key(&plmn_iter); + ogs_assert(plmn_key); + if (!strcmp(plmn_key, "plmn_id")) { + ogs_yaml_iter_t plmn_id_iter; + + ogs_yaml_iter_recurse(&plmn_iter, + &plmn_id_iter); + while (ogs_yaml_iter_next(&plmn_id_iter)) { + const char *plmn_id_key = + ogs_yaml_iter_key(&plmn_id_iter); + ogs_assert(plmn_id_key); + if (!strcmp(plmn_id_key, "mcc")) { + mcc = ogs_yaml_iter_value( + &plmn_id_iter); + } else if (!strcmp(plmn_id_key, "mnc")) { + mnc = ogs_yaml_iter_value( + &plmn_id_iter); + } + } + + if (mcc && mnc) { + ogs_plmn_id_build( + &self.plmn_support[ + self.num_of_plmn_support]. + plmn_id, + atoi(mcc), atoi(mnc), strlen(mnc)); + } + } else if (!strcmp(plmn_key, "s_nssai")) { + ogs_yaml_iter_t s_nssai_array, s_nssai_iter; + ogs_yaml_iter_recurse(&plmn_iter, + &s_nssai_array); + do { + ogs_s_nssai_t *s_nssai = NULL; + const char *sst = NULL, *sd = NULL; + ogs_assert( + self.plmn_support[ + self.num_of_plmn_support]. + num_of_s_nssai <= + OGS_MAX_NUM_OF_S_NSSAI); + s_nssai = &self.plmn_support[ + self.num_of_plmn_support].s_nssai[ + self.plmn_support[ + self.num_of_plmn_support]. + num_of_s_nssai]; + ogs_assert(s_nssai); + + if (ogs_yaml_iter_type(&s_nssai_array) == + YAML_MAPPING_NODE) { + memcpy(&s_nssai_iter, &s_nssai_array, + sizeof(ogs_yaml_iter_t)); + } else if (ogs_yaml_iter_type( + &s_nssai_array) == + YAML_SEQUENCE_NODE) { + if (!ogs_yaml_iter_next(&s_nssai_array)) + break; + ogs_yaml_iter_recurse(&s_nssai_array, + &s_nssai_iter); + } else if (ogs_yaml_iter_type( + &s_nssai_array) == + YAML_SCALAR_NODE) { + break; + } else + ogs_assert_if_reached(); + + while (ogs_yaml_iter_next(&s_nssai_iter)) { + const char *s_nssai_key = + ogs_yaml_iter_key(&s_nssai_iter); + ogs_assert(s_nssai_key); + if (!strcmp(s_nssai_key, "sst")) { + sst = ogs_yaml_iter_value( + &s_nssai_iter); + } else if (!strcmp( + s_nssai_key, "sd")) { + sd = ogs_yaml_iter_value( + &s_nssai_iter); + } + } + + if (sst) { + s_nssai->sst = atoi(sst); + if (sd) { + s_nssai->sd = + ogs_uint24_from_string( + (char*)sd); + } else { + s_nssai->sd.v = + OGS_S_NSSAI_NO_SD_VALUE; + } + + self.plmn_support[ + self.num_of_plmn_support]. + num_of_s_nssai++; + } + + } while (ogs_yaml_iter_type(&s_nssai_array) == + YAML_SEQUENCE_NODE); + } else + ogs_warn("unknown key `%s`", plmn_key); + } + + if (self.plmn_support[ + self.num_of_plmn_support].num_of_s_nssai && + mcc && mnc) { + self.num_of_plmn_support++; + } else { + ogs_warn("Ignore plmn : " + "s_nssai(%d) mcc(%s), mnc(%s)", + self.plmn_support[ + self.num_of_plmn_support].num_of_s_nssai, + mcc, mnc); + self.plmn_support[ + self.num_of_plmn_support].num_of_s_nssai = 0; + } + } while (ogs_yaml_iter_type(&plmn_array) == + YAML_SEQUENCE_NODE); + } else if (!strcmp(amf_key, "sbi")) { + /* handle config in sbi library */ + } else + ogs_warn("unknown key `%s`", amf_key); + } + } + } + + rv = test_context_validation(); + if (rv != OGS_OK) return rv; + + return OGS_OK; +} diff --git a/tests/common/context.h b/tests/common/context.h new file mode 100644 index 000000000..1e7084d85 --- /dev/null +++ b/tests/common/context.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2019,2020 by Sukchan Lee + * + * This file is part of Open5GS. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 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, see . + */ + +#if !defined(OGS_TEST_INSIDE) && !defined(OGS_TEST_COMPILATION) +#error "This header cannot be included directly." +#endif + +#ifndef TEST_COMMON_CONTEXT_H +#define TEST_COMMON_CONTEXT_H + +#ifdef __cplusplus +extern "C" { +#endif + +#define MAX_NUM_OF_SERVED_GUAMI 8 + +typedef struct test_context_s { + /* Served GUMME */ + uint8_t num_of_served_guami; + struct { + ogs_plmn_id_t plmn_id; + ogs_amf_id_t amf_id; + } served_guami[MAX_NUM_OF_SERVED_GUAMI]; + + /* Served TAI */ + uint8_t num_of_served_tai; + struct { + ogs_5gs_tai0_list_t list0; + ogs_5gs_tai2_list_t list2; + } served_tai[OGS_MAX_NUM_OF_SERVED_TAI]; + + /* PLMN Support */ + uint8_t num_of_plmn_support; + struct { + ogs_plmn_id_t plmn_id; + int num_of_s_nssai; + ogs_s_nssai_t s_nssai[OGS_MAX_NUM_OF_S_NSSAI]; + } plmn_support[OGS_MAX_NUM_OF_PLMN]; + +} test_context_t; + +void test_context_init(void); +void test_context_final(void); +test_context_t *test_self(void); + +int test_context_parse_config(void); + +#ifdef __cplusplus +} +#endif + +#endif /* TEST_COMMON_CONTEXT_H */ diff --git a/tests/common/gtpu.c b/tests/common/gtpu.c new file mode 100644 index 000000000..6e62f1068 --- /dev/null +++ b/tests/common/gtpu.c @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2019,2020 by Sukchan Lee + * + * This file is part of Open5GS. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 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, see . + */ + +#include "test-common.h" + +ogs_socknode_t *test_gtpu_server(const char *ipstr, int port) +{ + int rv; + ogs_sockaddr_t *addr = NULL; + ogs_socknode_t *node = NULL; + ogs_sock_t *sock = NULL; + + rv = ogs_getaddrinfo(&addr, AF_UNSPEC, ipstr, port, 0); + ogs_assert(rv == OGS_OK); + + node = ogs_socknode_new(addr); + ogs_assert(node); + + sock = ogs_udp_server(node); + ogs_assert(sock); + + return node; +} + +ogs_pkbuf_t *test_gtpu_read(ogs_socknode_t *node) +{ + int rc = 0; + ogs_pkbuf_t *recvbuf = ogs_pkbuf_alloc(NULL, OGS_MAX_SDU_LEN); + ogs_pkbuf_put(recvbuf, OGS_MAX_SDU_LEN); + + ogs_assert(node); + ogs_assert(node->sock); + + while (1) { + rc = ogs_recv(node->sock->fd, recvbuf->data, recvbuf->len, 0); + if (rc <= 0) { + if (errno == EAGAIN) { + continue; + } + break; + } else { + break; + } + } + recvbuf->len = rc; + + return recvbuf; +} + +void test_gtpu_close(ogs_socknode_t *node) +{ + ogs_socknode_free(node); +} diff --git a/tests/common/gtpu.h b/tests/common/gtpu.h new file mode 100644 index 000000000..385496833 --- /dev/null +++ b/tests/common/gtpu.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2019,2020 by Sukchan Lee + * + * This file is part of Open5GS. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 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, see . + */ + +#ifndef TEST_COMMON_GTPU_H +#define TEST_COMMON_GTPU_H + +#ifdef __cplusplus +extern "C" { +#endif + +ogs_socknode_t *test_gtpu_server(const char *ipstr, int port); +ogs_pkbuf_t *test_gtpu_read(ogs_socknode_t *node); +void test_gtpu_close(ogs_socknode_t *node); + +#ifdef __cplusplus +} +#endif + +#endif /* TEST_COMMON_GTPU_H */ diff --git a/tests/common/meson.build b/tests/common/meson.build new file mode 100644 index 000000000..82fbd4b9e --- /dev/null +++ b/tests/common/meson.build @@ -0,0 +1,58 @@ +# Copyright (C) 2019,2020 by Sukchan Lee + +# This file is part of Open5GS. + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 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, see . + +libtestcommon_conf = configuration_data() +libtestcommon_conf.set_quoted('MESON_BUILD_ROOT', meson.build_root()) +configure_file(output : 'test-config-private.h', + configuration : libtestcommon_conf) + +libtestcommon_sources = files(''' + sctp.c + gtpu.c + context.c + application.c + + ngap-build.c +'''.split()) + +libtestcommon_inc = include_directories('.') + +libtestcommon = static_library('testcomon', + sources : libtestcommon_sources, + c_args : testcore_cc_flags, + include_directories : [libtestcommon_inc, testinc], + dependencies : [libcore_dep, + libapp_dep, + libdbi_dep, + libsctp_dep, + libngap_dep, + libnas_eps_dep, + libnas_5gs_dep, + libdiameter_common_dep], + install : false) + +libtestcommon_dep = declare_dependency( + link_with : libtestcommon, + include_directories : [libtestcommon_inc, testinc], + dependencies : [libcore_dep, + libapp_dep, + libdbi_dep, + libsctp_dep, + libngap_dep, + libnas_eps_dep, + libnas_5gs_dep, + libdiameter_common_dep]) diff --git a/tests/common/ngap-build.c b/tests/common/ngap-build.c new file mode 100644 index 000000000..9ad2af1f8 --- /dev/null +++ b/tests/common/ngap-build.c @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2019,2020 by Sukchan Lee + * + * This file is part of Open5GS. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 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, see . + */ + +#include "test-ngap.h" + +int testngap_build_setup_req(ogs_pkbuf_t **pkbuf, uint32_t gnb_id) +{ + int i, j; + ogs_plmn_id_t *plmn_id = NULL; + ogs_uint24_t uint24; + + NGAP_NGAP_PDU_t pdu; + NGAP_InitiatingMessage_t *initiatingMessage = NULL; + NGAP_NGSetupRequest_t *NGSetupRequest = NULL; + + NGAP_NGSetupRequestIEs_t *ie = NULL; + NGAP_GlobalRANNodeID_t *GlobalRANNodeID = NULL; + NGAP_GlobalGNB_ID_t *globalGNB_ID = NULL; + NGAP_SupportedTAList_t *SupportedTAList = NULL; + NGAP_SupportedTAItem_t *SupportedTAItem = NULL; + NGAP_BroadcastPLMNItem_t *BroadcastPLMNItem = NULL; + NGAP_SliceSupportItem_t *SliceSupportItem = NULL; + NGAP_PLMNIdentity_t *pLMNIdentity = NULL; + NGAP_PagingDRX_t *PagingDRX = NULL; + + memset(&pdu, 0, sizeof (NGAP_NGAP_PDU_t)); + pdu.present = NGAP_NGAP_PDU_PR_initiatingMessage; + pdu.choice.initiatingMessage = + CALLOC(1, sizeof(NGAP_InitiatingMessage_t)); + + initiatingMessage = pdu.choice.initiatingMessage; + initiatingMessage->procedureCode = NGAP_ProcedureCode_id_NGSetup; + initiatingMessage->criticality = NGAP_Criticality_reject; + initiatingMessage->value.present = + NGAP_InitiatingMessage__value_PR_NGSetupRequest; + + NGSetupRequest = &initiatingMessage->value.choice.NGSetupRequest; + + ie = CALLOC(1, sizeof(NGAP_NGSetupRequestIEs_t)); + ASN_SEQUENCE_ADD(&NGSetupRequest->protocolIEs, ie); + + ie->id = NGAP_ProtocolIE_ID_id_GlobalRANNodeID; + ie->criticality = NGAP_Criticality_reject; + ie->value.present = NGAP_NGSetupRequestIEs__value_PR_GlobalRANNodeID; + + GlobalRANNodeID = &ie->value.choice.GlobalRANNodeID; + + ie = CALLOC(1, sizeof(NGAP_NGSetupRequestIEs_t)); + ASN_SEQUENCE_ADD(&NGSetupRequest->protocolIEs, ie); + + ie->id = NGAP_ProtocolIE_ID_id_SupportedTAList; + ie->criticality = NGAP_Criticality_reject; + ie->value.present = NGAP_NGSetupRequestIEs__value_PR_SupportedTAList; + + SupportedTAList = &ie->value.choice.SupportedTAList; + + ie = CALLOC(1, sizeof(NGAP_NGSetupRequestIEs_t)); + ASN_SEQUENCE_ADD(&NGSetupRequest->protocolIEs, ie); + + ie->id = NGAP_ProtocolIE_ID_id_DefaultPagingDRX; + ie->criticality = NGAP_Criticality_ignore; + ie->value.present = NGAP_NGSetupRequestIEs__value_PR_PagingDRX; + + PagingDRX = &ie->value.choice.PagingDRX; + + globalGNB_ID = CALLOC(1, sizeof(NGAP_GlobalGNB_ID_t)); + + plmn_id = &test_self()->plmn_support[0].plmn_id; + ogs_asn_buffer_to_OCTET_STRING( + plmn_id, OGS_PLMN_ID_LEN, &globalGNB_ID->pLMNIdentity); + + ogs_ngap_uint32_to_GNB_ID(gnb_id, &globalGNB_ID->gNB_ID); + + GlobalRANNodeID->present = NGAP_GlobalRANNodeID_PR_globalGNB_ID; + GlobalRANNodeID->choice.globalGNB_ID = globalGNB_ID; + + SupportedTAItem = CALLOC(1, sizeof(NGAP_SupportedTAItem_t)); + if (test_self()->served_tai[0].list2.num) + ogs_asn_uint24_to_OCTET_STRING( + test_self()->served_tai[0].list2.tai[0].tac, &SupportedTAItem->tAC); + else if (test_self()->served_tai[0].list0.tai[0].num) + ogs_asn_uint24_to_OCTET_STRING( + test_self()->served_tai[0].list0.tai[0].tac[0], + &SupportedTAItem->tAC); + else + ogs_assert_if_reached(); + + for (i = 0; i < test_self()->num_of_plmn_support; i++) { + plmn_id = &test_self()->plmn_support[i].plmn_id; + + BroadcastPLMNItem = CALLOC(1, sizeof(NGAP_BroadcastPLMNItem_t)); + + ogs_asn_buffer_to_OCTET_STRING( + plmn_id, OGS_PLMN_ID_LEN, &BroadcastPLMNItem->pLMNIdentity); + + for (j = 0; j < test_self()->plmn_support[i].num_of_s_nssai; j++) { + ogs_s_nssai_t *s_nssai = &test_self()->plmn_support[i].s_nssai[j]; + + SliceSupportItem = CALLOC(1, sizeof(NGAP_SliceSupportItem_t)); + ogs_asn_uint8_to_OCTET_STRING(s_nssai->sst, + &SliceSupportItem->s_NSSAI.sST); + if (s_nssai->sd.v != OGS_S_NSSAI_NO_SD_VALUE) { + SliceSupportItem->s_NSSAI.sD = CALLOC(1, sizeof(ogs_uint24_t)); + ogs_asn_uint24_to_OCTET_STRING( + s_nssai->sd, SliceSupportItem->s_NSSAI.sD); + } + + ASN_SEQUENCE_ADD(&BroadcastPLMNItem->tAISliceSupportList.list, + SliceSupportItem); + } + + ASN_SEQUENCE_ADD(&SupportedTAItem->broadcastPLMNList.list, + BroadcastPLMNItem); + } + + ASN_SEQUENCE_ADD(&SupportedTAList->list, SupportedTAItem); + + *PagingDRX = NGAP_PagingDRX_v64; + + *pkbuf = ogs_ngap_encode(&pdu); + if (*pkbuf == NULL) { + ogs_error("ogs_ngap_encode() failed"); + return OGS_ERROR; + } + return OGS_OK; +} + diff --git a/tests/common/ngap-build.h b/tests/common/ngap-build.h new file mode 100644 index 000000000..f080cc3e5 --- /dev/null +++ b/tests/common/ngap-build.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2019,2020 by Sukchan Lee + * + * This file is part of Open5GS. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 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, see . + */ + +#ifndef TEST_NGAP_BUILD_H +#define TEST_NGAP_BUILD_H + +#ifdef __cplusplus +extern "C" { +#endif + +int testngap_build_setup_req(ogs_pkbuf_t **pkbuf, uint32_t gnb_id); + +#ifdef __cplusplus +} +#endif + +#endif /* TEST_NGAP_BUILD_H */ diff --git a/tests/common/sctp.c b/tests/common/sctp.c new file mode 100644 index 000000000..1f86c1404 --- /dev/null +++ b/tests/common/sctp.c @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2019,2020 by Sukchan Lee + * + * This file is part of Open5GS. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 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, see . + */ + +#include "test-common.h" + +ogs_socknode_t *testsctp_server(const char *ipstr, int port) +{ + int rv; + ogs_sockaddr_t *addr = NULL; + ogs_socknode_t *node = NULL; + + rv = ogs_getaddrinfo(&addr, AF_UNSPEC, ipstr, port, 0); + ogs_assert(rv == OGS_OK); + + node = ogs_socknode_new(addr); + ogs_assert(node); + ogs_socknode_nodelay(node, true); + + ogs_sctp_server(SOCK_SEQPACKET, node); + ogs_assert(node->sock); + + return node; +} + +ogs_socknode_t *testsctp_client(const char *ipstr, int port) +{ + int rv; + ogs_sockaddr_t *addr = NULL; + ogs_socknode_t *node = NULL; + + rv = ogs_getaddrinfo(&addr, AF_UNSPEC, ipstr, port, 0); + ogs_assert(rv == OGS_OK); + + node = ogs_socknode_new(addr); + ogs_assert(node); + ogs_socknode_nodelay(node, true); + + ogs_sctp_client(SOCK_STREAM, node); + ogs_assert(node->sock); + + return node; +} + +ogs_sockaddr_t last_addr; + +ogs_pkbuf_t *testsctp_read(ogs_socknode_t *node, int type) +{ + int size; + ogs_pkbuf_t *recvbuf = NULL; + + ogs_assert(node); + ogs_assert(node->sock); + + recvbuf = ogs_pkbuf_alloc(NULL, OGS_MAX_SDU_LEN); + ogs_pkbuf_put(recvbuf, OGS_MAX_SDU_LEN); + + size = ogs_sctp_recvdata(node->sock, recvbuf->data, OGS_MAX_SDU_LEN, + type == 1 ? &last_addr : NULL, NULL); + if (size <= 0) { + ogs_error("sgsap_recv() failed"); + return NULL; + } + + ogs_pkbuf_trim(recvbuf, size); + return recvbuf;; +} + +int testsctp_send(ogs_socknode_t *node, ogs_pkbuf_t *pkbuf, + int ppid, uint16_t stream_no, int type) +{ + int sent; + + ogs_assert(node); + ogs_assert(node->sock); + ogs_assert(pkbuf); + + sent = ogs_sctp_sendmsg(node->sock, pkbuf->data, pkbuf->len, + type == 1 ? &last_addr : NULL, ppid, stream_no); + if (sent < 0 || sent != pkbuf->len) { + ogs_error("ogs_sctp_sendmsg error (%d:%s)", errno, strerror(errno)); + return OGS_ERROR; + } + ogs_pkbuf_free(pkbuf); + + return OGS_OK; +} diff --git a/tests/common/sctp.h b/tests/common/sctp.h new file mode 100644 index 000000000..20ecbba64 --- /dev/null +++ b/tests/common/sctp.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2019,2020 by Sukchan Lee + * + * This file is part of Open5GS. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 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, see . + */ + +#ifndef TEST_COMMON_SCTP_H +#define TEST_COMMON_SCTP_H + +#ifdef __cplusplus +extern "C" { +#endif + +extern ogs_sockaddr_t ogs_test_sctp_last_addr; + +ogs_socknode_t *testsctp_server(const char *ipstr, int port); +ogs_socknode_t *testsctp_client(const char *ipstr, int port); +int testsctp_send(ogs_socknode_t *node, ogs_pkbuf_t *pkbuf, + int ppid, uint16_t stream_no, int type); +ogs_pkbuf_t *testsctp_read(ogs_socknode_t *node, int type); + +#define testenb_s1ap_client(x) testsctp_client(x, OGS_S1AP_SCTP_PORT) +#define testenb_s1ap_read(x) testsctp_read(x, 0); +#define testenb_s1ap_send(x, y) \ + testsctp_send(x, y, OGS_SCTP_S1AP_PPID, 0, 0) +#define testenb_s1ap_close ogs_socknode_free + +#define testvlr_sgsap_server(x) testsctp_server(x, OGS_SGSAP_SCTP_PORT) +#define testvlr_sgsap_read(x) testsctp_read(x, 1); +#define testvlr_sgsap_send(x, y) \ + testsctp_send(x, y, OGS_SCTP_SGSAP_PPID, 0, 1) +#define testvlr_sgsap_close ogs_socknode_free + +#define testgnb_ngap_client(x) testsctp_client(x, OGS_NGAP_SCTP_PORT) +#define testgnb_ngap_read(x) testsctp_read(x, 0); +#define testgnb_ngap_send(x, y) \ + testsctp_send(x, y, OGS_SCTP_NGAP_PPID, 0, 0) +#define testgnb_ngap_close ogs_socknode_free + +#ifdef __cplusplus +} +#endif + +#endif /* TEST_COMMON_SCTP_H */ diff --git a/tests/common/test-common.h b/tests/common/test-common.h new file mode 100644 index 000000000..6fdbd105d --- /dev/null +++ b/tests/common/test-common.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2019,2020 by Sukchan Lee + * + * This file is part of Open5GS. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 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, see . + */ + +#ifndef TEST_COMMON_H +#define TEST_COMMON_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "ogs-app.h" +#include "ogs-dbi.h" +#include "ogs-sctp.h" +#include "ogs-diameter-common.h" +#include "ogs-nas-eps.h" +#include "ogs-nas-5gs.h" + +#include "core/abts.h" + +#define OGS_TEST_INSIDE + +#include "common/context.h" +#include "common/sctp.h" +#include "common/gtpu.h" +#include "common/application.h" + +#undef OGS_TEST_INSIDE + +#ifdef __cplusplus +} +#endif + +#endif /* TEST_COMMON_H */ diff --git a/tests/common/test-ngap.h b/tests/common/test-ngap.h new file mode 100644 index 000000000..3eb6182d4 --- /dev/null +++ b/tests/common/test-ngap.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2019,2020 by Sukchan Lee + * + * This file is part of Open5GS. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 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, see . + */ + +#ifndef TEST_NGAP_H +#define TEST_NGAP_H + +#include "ogs-ngap.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#include "test-common.h" + +#define OGS_TEST_INSIDE + +#include "common/ngap-build.h" + +#undef OGS_TEST_INSIDE + +#ifdef __cplusplus +} +#endif + +#endif /* TEST_NGAP_H */ diff --git a/tests/csfb/abts-main.c b/tests/csfb/abts-main.c index 942996701..a107635ca 100644 --- a/tests/csfb/abts-main.c +++ b/tests/csfb/abts-main.c @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -#include "test-app.h" +#include "test-epc.h" abts_suite *test_mo_idle(abts_suite *suite); abts_suite *test_mt_idle(abts_suite *suite); @@ -58,7 +58,7 @@ static void terminate(void) testvlr_sgsap_close(sgsap); ogs_sctp_final(); - test_app_final(); + test_epc_final(); ogs_info("MME terminate...done"); if (hss_thread) ogs_thread_destroy(hss_thread); @@ -81,7 +81,7 @@ static void initialize(const char *const argv[]) sgw_thread = test_child_create("sgw", argv); hss_thread = test_child_create("hss", argv); - test_app_init(); + test_epc_init(); ogs_sctp_init(ogs_config()->usrsctp.udp_port); sgsap = testvlr_sgsap_server("127.0.0.2"); @@ -98,7 +98,7 @@ int main(int argc, const char *const argv[]) abts_suite *suite = NULL; atexit(terminate); - test_app_run(argc, argv, "csfb.yaml", initialize); + test_epc_run(argc, argv, "csfb.yaml", initialize); for (i = 0; alltests[i].func; i++) suite = alltests[i].func(suite); diff --git a/tests/csfb/crash-test.c b/tests/csfb/crash-test.c index 8960e7821..f75e58b10 100644 --- a/tests/csfb/crash-test.c +++ b/tests/csfb/crash-test.c @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -#include "test-app.h" +#include "test-epc.h" extern ogs_socknode_t *sgsap; diff --git a/tests/csfb/meson.build b/tests/csfb/meson.build index cab6fd7dd..40d5e66f1 100644 --- a/tests/csfb/meson.build +++ b/tests/csfb/meson.build @@ -29,6 +29,6 @@ testcsfb_sources = files(''' testcsfb_exe = executable('csfb', sources : testcsfb_sources, c_args : testcore_cc_flags, - dependencies : libtestapp_dep) + dependencies : libtestepc_dep) -test('csfb', testcsfb_exe, is_parallel : false, suite: 'system') +test('csfb', testcsfb_exe, is_parallel : false, suite: 'epc') diff --git a/tests/csfb/mo-active-test.c b/tests/csfb/mo-active-test.c index 1117570cb..74864f3b2 100644 --- a/tests/csfb/mo-active-test.c +++ b/tests/csfb/mo-active-test.c @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -#include "test-app.h" +#include "test-epc.h" extern ogs_socknode_t *sgsap; diff --git a/tests/csfb/mo-idle-test.c b/tests/csfb/mo-idle-test.c index f369e3dce..83c905c20 100644 --- a/tests/csfb/mo-idle-test.c +++ b/tests/csfb/mo-idle-test.c @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -#include "test-app.h" +#include "test-epc.h" extern ogs_socknode_t *sgsap; diff --git a/tests/csfb/mo-sms-test.c b/tests/csfb/mo-sms-test.c index 0f345cdff..99c57b6c6 100644 --- a/tests/csfb/mo-sms-test.c +++ b/tests/csfb/mo-sms-test.c @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -#include "test-app.h" +#include "test-epc.h" extern ogs_socknode_t *sgsap; diff --git a/tests/csfb/mt-active-test.c b/tests/csfb/mt-active-test.c index 17e050086..e04607855 100644 --- a/tests/csfb/mt-active-test.c +++ b/tests/csfb/mt-active-test.c @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -#include "test-app.h" +#include "test-epc.h" extern ogs_socknode_t *sgsap; diff --git a/tests/csfb/mt-idle-test.c b/tests/csfb/mt-idle-test.c index 23edc30e7..7a93ae2eb 100644 --- a/tests/csfb/mt-idle-test.c +++ b/tests/csfb/mt-idle-test.c @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -#include "test-app.h" +#include "test-epc.h" extern ogs_socknode_t *sgsap; diff --git a/tests/csfb/mt-sms-test.c b/tests/csfb/mt-sms-test.c index 7eb1bc263..413998c4e 100644 --- a/tests/csfb/mt-sms-test.c +++ b/tests/csfb/mt-sms-test.c @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -#include "test-app.h" +#include "test-epc.h" extern ogs_socknode_t *sgsap; diff --git a/tests/cups/abts-main.c b/tests/cups/abts-main.c index d592c8a7f..ab15b50f6 100644 --- a/tests/cups/abts-main.c +++ b/tests/cups/abts-main.c @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -#include "test-app.h" +#include "test-epc.h" #include "pcscf-fd-path.h" @@ -56,7 +56,7 @@ static void terminate(void) ogs_sctp_final(); - test_app_final(); + test_epc_final(); ogs_app_terminate(); } @@ -77,7 +77,7 @@ static void initialize(const char *const argv[]) hss_thread = test_child_create("hss", argv); mme_thread = test_child_create("mme", argv); - test_app_init(); + test_epc_init(); ogs_sctp_init(ogs_config()->usrsctp.udp_port); rv = pcscf_fd_init(); @@ -90,7 +90,7 @@ int main(int argc, const char *const argv[]) abts_suite *suite = NULL; atexit(terminate); - test_app_run(argc, argv, "cups.yaml", initialize); + test_epc_run(argc, argv, "cups.yaml", initialize); for (i = 0; alltests[i].func; i++) suite = alltests[i].func(suite); diff --git a/tests/cups/cups-test.c b/tests/cups/cups-test.c index ffbc9ce8e..c72e2afc4 100644 --- a/tests/cups/cups-test.c +++ b/tests/cups/cups-test.c @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -#include "test-app.h" +#include "test-epc.h" #include "pcscf-fd-path.h" #define TEST1_PING 1 diff --git a/tests/cups/meson.build b/tests/cups/meson.build index 03e55fcf0..6db152471 100644 --- a/tests/cups/meson.build +++ b/tests/cups/meson.build @@ -27,6 +27,6 @@ testcups_exe = executable('cups', sources : testcups_sources, c_args : [testcore_cc_flags, '-DFD_EXT_DIR="@0@"'.format(freediameter_extensions_builddir)], - dependencies : libtestapp_dep) + dependencies : libtestepc_dep) test('cups', testcups_exe, is_parallel : false, suite: '5gc') diff --git a/tests/simple/abts-main.c b/tests/epc-simple/abts-main.c similarity index 93% rename from tests/simple/abts-main.c rename to tests/epc-simple/abts-main.c index dedb96e64..e0bd632c1 100644 --- a/tests/simple/abts-main.c +++ b/tests/epc-simple/abts-main.c @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -#include "test-app.h" +#include "test-epc.h" abts_suite *test_s1setup(abts_suite *suite); abts_suite *test_attach(abts_suite *suite); @@ -43,7 +43,7 @@ static void terminate(void) test_child_terminate(); app_terminate(); - test_app_final(); + test_epc_final(); ogs_app_terminate(); } @@ -53,7 +53,7 @@ static void initialize(const char *const argv[]) rv = ogs_app_initialize(NULL, argv); ogs_assert(rv == OGS_OK); - test_app_init(); + test_epc_init(); rv = app_initialize(argv); ogs_assert(rv == OGS_OK); @@ -65,7 +65,7 @@ int main(int argc, const char *const argv[]) abts_suite *suite = NULL; atexit(terminate); - test_app_run(argc, argv, "simple.yaml", initialize); + test_epc_run(argc, argv, "epc.yaml", initialize); for (i = 0; alltests[i].func; i++) suite = alltests[i].func(suite); diff --git a/tests/simple/attach-test.c b/tests/epc-simple/attach-test.c similarity index 99% rename from tests/simple/attach-test.c rename to tests/epc-simple/attach-test.c index 09e7067c2..77dbf49ad 100644 --- a/tests/simple/attach-test.c +++ b/tests/epc-simple/attach-test.c @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -#include "test-app.h" +#include "test-epc.h" /************************************************************** * eNB : MACRO diff --git a/tests/simple/crash-test.c b/tests/epc-simple/crash-test.c similarity index 99% rename from tests/simple/crash-test.c rename to tests/epc-simple/crash-test.c index 7ad253046..ecbd74633 100644 --- a/tests/simple/crash-test.c +++ b/tests/epc-simple/crash-test.c @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -#include "test-app.h" +#include "test-epc.h" static void crash_test1(abts_case *tc, void *data) { diff --git a/tests/simple/handover-test.c b/tests/epc-simple/handover-test.c similarity index 99% rename from tests/simple/handover-test.c rename to tests/epc-simple/handover-test.c index 64c787e4b..3e584b313 100644 --- a/tests/simple/handover-test.c +++ b/tests/epc-simple/handover-test.c @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -#include "test-app.h" +#include "test-epc.h" static void handover_test1(abts_case *tc, void *data) { diff --git a/tests/simple/meson.build b/tests/epc-simple/meson.build similarity index 80% rename from tests/simple/meson.build rename to tests/epc-simple/meson.build index 7a40f08f1..39cfdeda0 100644 --- a/tests/simple/meson.build +++ b/tests/epc-simple/meson.build @@ -15,7 +15,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -testsimple_sources = files(''' +testepc_simple_sources = files(''' abts-main.c s1setup-test.c attach-test.c @@ -25,9 +25,9 @@ testsimple_sources = files(''' '''.split()) -testsimple_exe = executable('simple', - sources : testsimple_sources, +testepc_simple_exe = executable('simple', + sources : testepc_simple_sources, c_args : testcore_cc_flags, - dependencies : libtestapp_dep) + dependencies : libtestepc_dep) -test('simple', testsimple_exe, is_parallel : false, suite: 'system') +test('simple', testepc_simple_exe, is_parallel : false, suite: 'epc') diff --git a/tests/simple/s1setup-test.c b/tests/epc-simple/s1setup-test.c similarity index 100% rename from tests/simple/s1setup-test.c rename to tests/epc-simple/s1setup-test.c diff --git a/tests/simple/volte-test.c b/tests/epc-simple/volte-test.c similarity index 99% rename from tests/simple/volte-test.c rename to tests/epc-simple/volte-test.c index 5449c1062..afc794a14 100644 --- a/tests/simple/volte-test.c +++ b/tests/epc-simple/volte-test.c @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -#include "test-app.h" +#include "test-epc.h" static void volte_test1(abts_case *tc, void *data) { diff --git a/tests/epc/meson.build b/tests/epc/meson.build deleted file mode 100644 index b4b7efa1c..000000000 --- a/tests/epc/meson.build +++ /dev/null @@ -1,60 +0,0 @@ -# Copyright (C) 2019 by Sukchan Lee - -# This file is part of Open5GS. - -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 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, see . -# - -testepc_conf = configuration_data() -testepc_conf.set_quoted('MESON_BUILD_ROOT', meson.build_root()) -configure_file(output : 'test-config-private.h', configuration : testepc_conf) - -testepc_cc_args = '-DDEFAULT_CONFIG_FILENAME="@0@/configs/simple.yaml"'.format(meson.build_root()) - -libtestepc_sources = files(''' - app-init.c -'''.split()) - -libtestepc_inc = include_directories('.') - -libtestepc = static_library('epc', - sources : [libtestepc_sources], - c_args : testepc_cc_args, - include_directories : [libtestepc_inc, srcinc], - link_with : [libmme, libhss, libsgw, libpgw, libpcrf], - dependencies : [libmme_dep, - libhss_dep, - libsgw_dep, - libpgw_dep, - libpcrf_dep], - install : false) - -libtestepc_dep = declare_dependency( - link_with : libtestepc, - include_directories : [libtestepc_inc, srcinc], - dependencies : [libmme_dep, - libhss_dep, - libsgw_dep, - libpgw_dep, - libpcrf_dep]) - -testepc_sources = files(''' - ../../src/main.c -'''.split()) - -executable('epc', - sources : [testepc_sources], - c_args : testepc_cc_args, - include_directories : srcinc, - dependencies : [libtestepc_dep]) diff --git a/tests/epc/test-epc.h b/tests/epc/test-epc.h deleted file mode 100644 index d806b0485..000000000 --- a/tests/epc/test-epc.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef TEST_EPC_H -#define TEST_EPC_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include "ogs-app.h" - -void test_child_terminate(void); -ogs_thread_t *test_child_create(const char *name, const char *const argv[]); - -#ifdef __cplusplus -} -#endif - -#endif /* TEST_EPC_H */ diff --git a/tests/meson.build b/tests/meson.build index 0a110665e..95bd6bba4 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -15,14 +15,17 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +testinc = include_directories('.') + subdir('core') subdir('crypt') subdir('sctp') -subdir('epc') +subdir('common') subdir('app') subdir('unit') -subdir('simple') +subdir('epc-simple') subdir('mnc3') subdir('volte') subdir('csfb') subdir('cups') +subdir('minimal') diff --git a/tests/minimal/abts-main.c b/tests/minimal/abts-main.c new file mode 100644 index 000000000..345262318 --- /dev/null +++ b/tests/minimal/abts-main.c @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2019,2020 by Sukchan Lee + * + * This file is part of Open5GS. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 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, see . + */ + +#include "test-5gc.h" + +abts_suite *test_minimal(abts_suite *suite); + +const struct testlist { + abts_suite *(*func)(abts_suite *suite); +} alltests[] = { + {test_minimal}, + {NULL}, +}; + +static void terminate(void) +{ + ogs_msleep(50); + + test_child_terminate(); + app_terminate(); + + test_5gc_final(); + ogs_app_terminate(); +} + +static void initialize(const char *const argv[]) +{ + int rv; + + rv = ogs_app_initialize(NULL, argv); + ogs_assert(rv == OGS_OK); + test_5gc_init(); + + rv = app_initialize(argv); + ogs_assert(rv == OGS_OK); +} + +int main(int argc, const char *const argv[]) +{ + int i; + abts_suite *suite = NULL; + + atexit(terminate); + test_5gc_run(argc, argv, "minimal.yaml", initialize); + + for (i = 0; alltests[i].func; i++) + suite = alltests[i].func(suite); + + return abts_report(suite); +} diff --git a/tests/minimal/meson.build b/tests/minimal/meson.build new file mode 100644 index 000000000..5fd3ac602 --- /dev/null +++ b/tests/minimal/meson.build @@ -0,0 +1,26 @@ +# Copyright (C) 2019,2020 by Sukchan Lee + +# This file is part of Open5GS. + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 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, see . + +testminimal_sources = files(''' + abts-main.c + minimal-test.c +'''.split()) + +testminimal_exe = executable('minimal', + sources : testminimal_sources, + c_args : testcore_cc_flags, + dependencies : libtest5gc_dep) diff --git a/tests/minimal/minimal-test.c b/tests/minimal/minimal-test.c new file mode 100644 index 000000000..b0295b9b8 --- /dev/null +++ b/tests/minimal/minimal-test.c @@ -0,0 +1,281 @@ +/* + * Copyright (C) 2019,2020 by Sukchan Lee + * + * This file is part of Open5GS. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 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, see . + */ + +#include "test-5gc.h" + +static void test1_func(abts_case *tc, void *data) +{ + int rv; + ogs_socknode_t *ngap; + ogs_socknode_t *gtpu; + ogs_pkbuf_t *sendbuf; + ogs_pkbuf_t *recvbuf; + ogs_ngap_message_t message; + int i; + int msgindex = 0; + + uint8_t tmp[OGS_MAX_SDU_LEN]; + const char *_ng_setup_request = + "0015002d00000300 1b00080002f83910 0001020066001500 000000010002f839" + "0001100801020310 0811223300154001 20"; + const char *_ng_setup_response = + "2015003d00000400 0100110700616d66 2e6f70656e356773 2e6f726700600008" + "000002f839cafe00 00564001ff005000 100002f839000110 0801020310081122" + "33"; + const char *_authentication_request = + "000b403b00000300 000005c00100009d 000800020001001a 0025240752002008" + "0c3818183b522614 162c07601d0d10f1 1b89a2a8de8000ad 0ccf7f55e8b20d"; + const char *_security_mode_command = + "000b4028" + "00000300000005c0 0100009d00080002 0001001a00121137 f497722900075d01" + "0005e060c04070c1"; + const char *_esm_information_request = + "000b402000000300 000005c00100009d 000800020001001a 000a092779012320" + "010221d9"; + const char *_initial_context_setup_request = + "00090080d8000006 00000005c0010000 9d00080002000100 42000a183e800000" + "603e800000001800 8086000034008080 450009200f807f00 0002000000017127" + "4db5d98302074202 49064000f1105ba0 00485221c1010909 08696e7465726e65" + "7405010a2d00025e 06fefeeeee030327 2980c22304030000 0480211002000010" + "8106080808088306 08080404000d0408 080808000d040808 0404500bf600f110" + "0002010000000153 12172c5949640125 006b000518000c00 00004900203311c6" + "03c6a6d67f695e5a c02bb75b381b693c 3893a6d932fd9182 3544e3e79b"; + const char *_emm_information = + "000b403b00000300 000005c00100009d 000800020001001a 002524271f9b491e" + "030761430f10004f 00700065006e0035 0047005347812072 11240563490100"; + + mongoc_collection_t *collection = NULL; + bson_t *doc = NULL; + int64_t count = 0; + bson_error_t error; + const char *json = + "{" + "\"_id\" : { \"$oid\" : \"597223158b8861d7605378c6\" }, " + "\"imsi\" : \"001010123456819\", " + "\"pdn\" : [" + "{" + "\"apn\" : \"internet\", " + "\"_id\" : { \"$oid\" : \"597223158b8861d7605378c7\" }, " + "\"ambr\" : {" + "\"uplink\" : { \"$numberLong\" : \"1024000\" }, " + "\"downlink\" : { \"$numberLong\" : \"1024000\" } " + "}," + "\"qos\" : { " + "\"qci\" : 9, " + "\"arp\" : { " + "\"priority_level\" : 8," + "\"pre_emption_vulnerability\" : 1, " + "\"pre_emption_capability\" : 1" + "} " + "}, " + "\"type\" : 2" + "}" + "]," + "\"ambr\" : { " + "\"uplink\" : { \"$numberLong\" : \"1024000\" }, " + "\"downlink\" : { \"$numberLong\" : \"1024000\" } " + "}," + "\"subscribed_rau_tau_timer\" : 12," + "\"network_access_mode\" : 2, " + "\"subscriber_status\" : 0, " + "\"access_restriction_data\" : 32, " + "\"security\" : { " + "\"k\" : \"465B5CE8 B199B49F AA5F0A2E E238A6BC\", " + "\"opc\" : \"E8ED289D EBA952E4 283B54E8 8E6183CA\", " + "\"amf\" : \"8000\", " + "\"sqn\" : { \"$numberLong\" : \"64\" }, " + "\"rand\" : \"20080C38 18183B52 2614162C 07601D0D\" " + "}, " + "\"__v\" : 0 " + "}"; + + /* gNB connects to AMF */ + ngap = testgnb_ngap_client("127.0.0.1"); + ABTS_PTR_NOTNULL(tc, ngap); + +#if 0 + /* eNB connects to SGW */ + gtpu = testgnb_gtpu_server("127.0.0.5"); + ABTS_PTR_NOTNULL(tc, gtpu); +#endif + + /* Send NG-Setup Reqeust */ + rv = testngap_build_setup_req(&sendbuf, 0x000102); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + ABTS_TRUE(tc, memcmp(sendbuf->data, + OGS_HEX(_ng_setup_request, strlen(_ng_setup_request), tmp), + sendbuf->len) == 0); + rv = testgnb_ngap_send(ngap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive NG-Setup Response */ + recvbuf = testgnb_ngap_read(ngap); + ABTS_PTR_NOTNULL(tc, recvbuf); + ABTS_TRUE(tc, memcmp(recvbuf->data, + OGS_HEX(_ng_setup_response, strlen(_ng_setup_response), tmp), + recvbuf->len) == 0); + ogs_pkbuf_free(recvbuf); + +#if 0 + collection = mongoc_client_get_collection( + ogs_mongoc()->client, ogs_mongoc()->name, "subscribers"); + ABTS_PTR_NOTNULL(tc, collection); + + /********** Insert Subscriber in Database */ + doc = bson_new_from_json((const uint8_t *)json, -1, &error);; + ABTS_PTR_NOTNULL(tc, doc); + ABTS_TRUE(tc, mongoc_collection_insert(collection, + MONGOC_INSERT_NONE, doc, NULL, &error)); + bson_destroy(doc); + + doc = BCON_NEW("imsi", BCON_UTF8("310014987654004")); + ABTS_PTR_NOTNULL(tc, doc); + do { + count = mongoc_collection_count ( + collection, MONGOC_QUERY_NONE, doc, 0, 0, NULL, &error); + } while (count == 0); + bson_destroy(doc); + + mme_self()->mme_ue_ngap_id = 27263233; + rv = testngap_build_initial_ue_msg(&sendbuf, msgindex); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + rv = testgnb_ngap_send(ngap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive Authentication Request */ + recvbuf = testgnb_ngap_read(ngap); + ABTS_PTR_NOTNULL(tc, recvbuf); + ABTS_TRUE(tc, memcmp(recvbuf->data, + OGS_HEX(_authentication_request, strlen(_authentication_request), tmp), + recvbuf->len) == 0); + ogs_pkbuf_free(recvbuf); + + /* Send Authentication Response */ + rv = testngap_build_authentication_response(&sendbuf, msgindex); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + rv = testgnb_ngap_send(ngap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive Security mode Command */ + recvbuf = testgnb_ngap_read(ngap); + ABTS_PTR_NOTNULL(tc, recvbuf); + ABTS_TRUE(tc, memcmp(recvbuf->data, + OGS_HEX(_security_mode_command, strlen(_security_mode_command), tmp), + recvbuf->len) == 0); + ogs_pkbuf_free(recvbuf); + + /* Send Security mode Complete */ + rv = testngap_build_security_mode_complete(&sendbuf, msgindex); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + rv = testgnb_ngap_send(ngap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive ESM Information Request */ + recvbuf = testgnb_ngap_read(ngap); + ABTS_PTR_NOTNULL(tc, recvbuf); + ABTS_TRUE(tc, memcmp(recvbuf->data, + OGS_HEX(_esm_information_request, strlen(_security_mode_command), tmp), + recvbuf->len) == 0); + ogs_pkbuf_free(recvbuf); + + /* Send ESM Information Response */ + rv = testngap_build_esm_information_response(&sendbuf, msgindex); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + rv = testgnb_ngap_send(ngap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive Initial Context Setup Request + + * Attach Accept + + * Activate Default Bearer Context Request */ + recvbuf = testgnb_ngap_read(ngap); + ABTS_PTR_NOTNULL(tc, recvbuf); + OGS_HEX(_initial_context_setup_request, + strlen(_initial_context_setup_request), tmp); + ABTS_TRUE(tc, memcmp(recvbuf->data, tmp, 62) == 0); + ABTS_TRUE(tc, memcmp(recvbuf->data+66, tmp+66, 78) == 0); + ABTS_TRUE(tc, memcmp(recvbuf->data+148, tmp+148, 50) == 0); + ogs_pkbuf_free(recvbuf); + + /* Send UE Capability Info Indication */ + rv = testngap_build_ue_capability_info_indication(&sendbuf, msgindex); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + rv = testgnb_ngap_send(ngap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + ogs_msleep(50); + + /* Send Initial Context Setup Response */ + rv = testngap_build_initial_context_setup_response(&sendbuf, + 27263233, 24, 5, 1, "127.0.0.5"); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + rv = testgnb_ngap_send(ngap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Send Attach Complete + Activate default EPS bearer cotext accept */ + rv = testngap_build_attach_complete(&sendbuf, msgindex); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + rv = testgnb_ngap_send(ngap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + ogs_msleep(50); + + /* Receive EMM information */ + recvbuf = testgnb_ngap_read(ngap); + ABTS_PTR_NOTNULL(tc, recvbuf); + OGS_HEX(_emm_information, strlen(_emm_information), tmp); + ABTS_TRUE(tc, memcmp(recvbuf->data, tmp, 28) == 0); + ABTS_TRUE(tc, memcmp(recvbuf->data+32, tmp+32, 20) == 0); + ogs_pkbuf_free(recvbuf); + + /* Send GTP-U ICMP Packet */ + rv = testgtpu_build_ping(&sendbuf, 1, "10.45.0.2", "10.45.0.1"); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + rv = testgnb_gtpu_send(gtpu, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive GTP-U ICMP Packet */ + recvbuf = testgnb_gtpu_read(gtpu); + ABTS_PTR_NOTNULL(tc, recvbuf); + ogs_pkbuf_free(recvbuf); + + /********** Remove Subscriber in Database */ + doc = BCON_NEW("imsi", BCON_UTF8("310014987654004")); + ABTS_PTR_NOTNULL(tc, doc); + ABTS_TRUE(tc, mongoc_collection_remove(collection, + MONGOC_REMOVE_SINGLE_REMOVE, doc, NULL, &error)) + bson_destroy(doc); + + mongoc_collection_destroy(collection); + + /* eNB disonncect from SGW */ + testgnb_gtpu_close(gtpu); +#endif + + /* gNB disonncect from AMF */ + testgnb_ngap_close(ngap); +} + +abts_suite *test_minimal(abts_suite *suite) +{ + suite = ADD_SUITE(suite) + + abts_run_test(suite, test1_func, NULL); + + return suite; +} diff --git a/tests/mnc3/abts-main.c b/tests/mnc3/abts-main.c index 880e64531..f0fca248f 100644 --- a/tests/mnc3/abts-main.c +++ b/tests/mnc3/abts-main.c @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -#include "test-app.h" +#include "test-epc.h" abts_suite *test_mnc3(abts_suite *suite); @@ -35,7 +35,7 @@ static void terminate(void) test_child_terminate(); app_terminate(); - test_app_final(); + test_epc_final(); ogs_app_terminate(); } @@ -45,7 +45,7 @@ static void initialize(const char *const argv[]) rv = ogs_app_initialize(NULL, argv); ogs_assert(rv == OGS_OK); - test_app_init(); + test_epc_init(); rv = app_initialize(argv); ogs_assert(rv == OGS_OK); @@ -57,7 +57,7 @@ int main(int argc, const char *const argv[]) abts_suite *suite = NULL; atexit(terminate); - test_app_run(argc, argv, "mnc3.yaml", initialize); + test_epc_run(argc, argv, "mnc3.yaml", initialize); for (i = 0; alltests[i].func; i++) suite = alltests[i].func(suite); diff --git a/tests/mnc3/meson.build b/tests/mnc3/meson.build index 618c48b8e..83cfd8b28 100644 --- a/tests/mnc3/meson.build +++ b/tests/mnc3/meson.build @@ -23,6 +23,6 @@ testmnc3_sources = files(''' testmnc3_exe = executable('mnc3', sources : testmnc3_sources, c_args : testcore_cc_flags, - dependencies : libtestapp_dep) + dependencies : libtestepc_dep) -test('mnc3', testmnc3_exe, is_parallel : false, suite: 'system') +test('mnc3', testmnc3_exe, is_parallel : false, suite: 'epc') diff --git a/tests/mnc3/mnc3-test.c b/tests/mnc3/mnc3-test.c index f99b0a861..96af5626a 100644 --- a/tests/mnc3/mnc3-test.c +++ b/tests/mnc3/mnc3-test.c @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -#include "test-app.h" +#include "test-epc.h" static void test1_func(abts_case *tc, void *data) { diff --git a/tests/unit/abts-main.c b/tests/unit/abts-main.c index 400d21ae0..42e01749d 100644 --- a/tests/unit/abts-main.c +++ b/tests/unit/abts-main.c @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -#include "test-app.h" +#include "test-epc.h" abts_suite *test_s1ap_message(abts_suite *suite); abts_suite *test_nas_message(abts_suite *suite); diff --git a/tests/unit/gtp-message-test.c b/tests/unit/gtp-message-test.c index 80e293689..ea63223de 100644 --- a/tests/unit/gtp-message-test.c +++ b/tests/unit/gtp-message-test.c @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -#include "test-app.h" +#include "test-epc.h" static void gtp_message_test1(abts_case *tc, void *data) { diff --git a/tests/unit/meson.build b/tests/unit/meson.build index c0fee43f2..e248c461b 100644 --- a/tests/unit/meson.build +++ b/tests/unit/meson.build @@ -28,6 +28,6 @@ testunit_sources = files(''' testunit_exe = executable('unit', sources : testunit_sources, c_args : [testcore_cc_flags, sbi_cc_flags], - dependencies : [libtestapp_dep, libsbi_dep]) + dependencies : [libtestepc_dep, libsbi_dep]) test('unit', testunit_exe, is_parallel : false, suite: 'unit') diff --git a/tests/unit/nas-message-test.c b/tests/unit/nas-message-test.c index d8ef8ea29..ee6c33e9b 100644 --- a/tests/unit/nas-message-test.c +++ b/tests/unit/nas-message-test.c @@ -67,8 +67,8 @@ static void ogs_nas_eps_message_test2(abts_case *tc, void *data) ogs_nas_eps_message_t message; ogs_nas_eps_attach_accept_t *attach_accept = &message.emm.attach_accept; - tai0_list_t tai0_list; - tai2_list_t tai2_list; + ogs_eps_tai0_list_t tai0_list; + ogs_eps_tai2_list_t tai2_list; ogs_plmn_id_t plmn_id; ogs_pkbuf_t *pkbuf = NULL; @@ -83,9 +83,9 @@ static void ogs_nas_eps_message_test2(abts_case *tc, void *data) OGS_NAS_GRPS_TIMER_UNIT_MULTIPLES_OF_1_MM; attach_accept->t3412_value.value = 3; - memset(&tai0_list, 0, sizeof(tai0_list_t)); - memset(&tai2_list, 0, sizeof(tai2_list_t)); - tai0_list.tai[0].type = TAI0_TYPE; + memset(&tai0_list, 0, sizeof(ogs_eps_tai0_list_t)); + memset(&tai2_list, 0, sizeof(ogs_eps_tai2_list_t)); + tai0_list.tai[0].type = OGS_TAI0_TYPE; tai0_list.tai[0].num = 1; ogs_plmn_id_build(&tai0_list.tai[0].plmn_id, 417, 99, 2); tai0_list.tai[0].tac[0] = 12345; diff --git a/tests/unit/sbi-message-test.c b/tests/unit/sbi-message-test.c index 5a847250f..002970b9d 100644 --- a/tests/unit/sbi-message-test.c +++ b/tests/unit/sbi-message-test.c @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -#include "test-app.h" +#include "test-epc.h" #include "ogs-sbi.h" diff --git a/tests/volte/abts-main.c b/tests/volte/abts-main.c index 3dd5e4a79..26ddad6ac 100644 --- a/tests/volte/abts-main.c +++ b/tests/volte/abts-main.c @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -#include "test-app.h" +#include "test-epc.h" #include "pcscf-fd-path.h" @@ -52,7 +52,7 @@ static void terminate(void) ogs_sctp_final(); - test_app_final(); + test_epc_final(); ogs_app_terminate(); } @@ -71,7 +71,7 @@ static void initialize(const char *const argv[]) hss_thread = test_child_create("hss", argv); mme_thread = test_child_create("mme", argv); - test_app_init(); + test_epc_init(); ogs_sctp_init(ogs_config()->usrsctp.udp_port); rv = pcscf_fd_init(); @@ -84,7 +84,7 @@ int main(int argc, const char *const argv[]) abts_suite *suite = NULL; atexit(terminate); - test_app_run(argc, argv, "volte.yaml", initialize); + test_epc_run(argc, argv, "volte.yaml", initialize); for (i = 0; alltests[i].func; i++) suite = alltests[i].func(suite); diff --git a/tests/volte/meson.build b/tests/volte/meson.build index 0908b8ea6..05bba6f03 100644 --- a/tests/volte/meson.build +++ b/tests/volte/meson.build @@ -27,6 +27,6 @@ testvolte_exe = executable('volte', sources : testvolte_sources, c_args : [testcore_cc_flags, '-DFD_EXT_DIR="@0@"'.format(freediameter_extensions_builddir)], - dependencies : libtestapp_dep) + dependencies : libtestepc_dep) -test('volte', testvolte_exe, is_parallel : false, suite: 'system') +test('volte', testvolte_exe, is_parallel : false, suite: 'epc') diff --git a/tests/volte/volte-test.c b/tests/volte/volte-test.c index 7e4e3e93f..ae34b558e 100644 --- a/tests/volte/volte-test.c +++ b/tests/volte/volte-test.c @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -#include "test-app.h" +#include "test-epc.h" #include "pcscf-fd-path.h" static void volte_test1(abts_case *tc, void *data)