diff --git a/configs/23504.yaml.in b/configs/23504.yaml.in index dd11de5b5..630f25354 100644 --- a/configs/23504.yaml.in +++ b/configs/23504.yaml.in @@ -12,6 +12,13 @@ parameter: # no_udm: true # no_udr: true +nrf: + sbi: + addr: + - 127.0.0.1 + - ::1 + port: 7777 + mme: freeDiameter: identity: mme.localdomain @@ -52,6 +59,63 @@ mme: network_name: full: Open5GS +sgwc: + gtpc: + addr: 127.0.0.6 + pfcp: + addr: 127.0.0.6 + +sgwu: + gtpu: + addr: 127.0.0.7 + pfcp: + addr: 127.0.0.7 + +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: smf.localdomain + realm: localdomain + 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.localdomain + addr: 127.0.0.5 +upf: + pfcp: + - addr: 127.0.0.4 + gtpu: + - addr: + - 127.0.0.4 + - ::1 + pdn: + - addr: 10.45.0.1/16 + - addr: cafe::1/64 + hss: freeDiameter: identity: hss.localdomain @@ -70,50 +134,6 @@ hss: - identity: mme.localdomain addr: 127.0.0.2 -sgw: - gtpc: - addr: 127.0.0.2 - gtpu: - addr: 127.0.0.2 - -pgw: - freeDiameter: - identity: pgw.localdomain - realm: localdomain - 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: - connect: - - identity: pcrf.localdomain - addr: 127.0.0.5 - - gtpc: - - addr: - - 127.0.0.3 - - ::1 - - addr: - - 127.0.0.4 - gtpu: - - addr: 127.0.0.3 - - addr: ::1 - 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 - pcrf: freeDiameter: identity: pcrf.localdomain @@ -129,5 +149,5 @@ pcrf: - module: @freediameter_extensions_builddir@/dict_dcca.fdx - module: @freediameter_extensions_builddir@/dict_dcca_3gpp.fdx connect: - - identity: pgw.localdomain + - identity: smf.localdomain addr: 127.0.0.3 diff --git a/configs/app.yaml.in b/configs/app.yaml.in new file mode 100644 index 000000000..fe04fff6d --- /dev/null +++ b/configs/app.yaml.in @@ -0,0 +1,145 @@ +db_uri: mongodb://localhost/open5gs + +logger: + +parameter: + no_ipv6: true + +nrf: + sbi: + addr: + - 127.0.0.1 + - ::1 + port: 7777 + +mme: + freeDiameter: + identity: mme.localdomain + realm: localdomain + listen_on: 127.0.0.2 + 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: hss.localdomain + addr: 127.0.0.4 + + s1ap: + addr: 127.0.0.1 + gtpc: + addr: 127.0.0.1 + gummei: + plmn_id: + mcc: 901 + mnc: 70 + mme_gid: 2 + mme_code: 1 + tai: + plmn_id: + mcc: 901 + mnc: 70 + tac: 1 + security: + integrity_order : [ EIA1, EIA2, EIA0 ] + ciphering_order : [ EEA0, EEA1, EEA2 ] + + network_name: + full: Open5GS + +sgwc: + gtpc: + addr: 127.0.0.6 + pfcp: + addr: 127.0.0.6 + +sgwu: + gtpu: + addr: 127.0.0.7 + pfcp: + addr: 127.0.0.7 + +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: smf.localdomain + realm: localdomain + 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.localdomain + addr: 127.0.0.5 +upf: + pfcp: + - addr: 127.0.0.4 + gtpu: + - addr: + - 127.0.0.4 + - ::1 + pdn: + - addr: 10.45.0.1/16 + - addr: cafe::1/64 + +hss: + freeDiameter: + identity: hss.localdomain + realm: localdomain + listen_on: 127.0.0.4 + 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: mme.localdomain + addr: 127.0.0.2 +pcrf: + freeDiameter: + identity: pcrf.localdomain + realm: localdomain + listen_on: 127.0.0.5 + 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: smf.localdomain + addr: 127.0.0.3 diff --git a/configs/csfb.yaml.in b/configs/csfb.yaml.in index ec57f96b0..914d81bcf 100644 --- a/configs/csfb.yaml.in +++ b/configs/csfb.yaml.in @@ -5,6 +5,13 @@ logger: parameter: no_ipv6: true +nrf: + sbi: + addr: + - 127.0.0.1 + - ::1 + port: 7777 + mme: freeDiameter: identity: mme.localdomain @@ -79,6 +86,63 @@ mme: network_name: full: Open5GS +sgwc: + gtpc: + addr: 127.0.0.6 + pfcp: + addr: 127.0.0.6 + +sgwu: + gtpu: + addr: 127.0.0.7 + pfcp: + addr: 127.0.0.7 + +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: smf.localdomain + realm: localdomain + 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.localdomain + addr: 127.0.0.5 +upf: + pfcp: + - addr: 127.0.0.4 + gtpu: + - addr: + - 127.0.0.4 + - ::1 + pdn: + - addr: 10.45.0.1/16 + - addr: cafe::1/64 + hss: freeDiameter: identity: hss.localdomain @@ -97,47 +161,6 @@ hss: - identity: mme.localdomain addr: 127.0.0.2 -sgw: - gtpc: - addr: 127.0.0.2 - gtpu: - addr: 127.0.0.2 - -pgw: - freeDiameter: - identity: pgw.localdomain - realm: localdomain - 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.localdomain - addr: 127.0.0.5 - - gtpc: - - addr: - - 127.0.0.3 - - ::1 - gtpu: - - addr: 127.0.0.3 - - addr: ::1 - 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 - pcrf: freeDiameter: identity: pcrf.localdomain @@ -153,5 +176,5 @@ pcrf: - module: @freediameter_extensions_builddir@/dict_dcca.fdx - module: @freediameter_extensions_builddir@/dict_dcca_3gpp.fdx connect: - - identity: pgw.localdomain + - identity: smf.localdomain addr: 127.0.0.3 diff --git a/configs/cups.yaml.in b/configs/cups.yaml.in index b7df8db13..7ee72afa4 100644 --- a/configs/cups.yaml.in +++ b/configs/cups.yaml.in @@ -3,11 +3,27 @@ db_uri: mongodb://localhost/open5gs logger: parameter: +# no_nrf: true +# no_amf: true +# no_smf: true +# no_upf: true +# no_ausf: true +# no_udm: true +# no_udr: true + +nrf: + sbi: + addr: + - 127.0.0.1 + - ::1 + port: 7777 mme: freeDiameter: identity: mme.open-ims.test realm: open-ims.test +# port: 3868 +# sec_port: 5868 listen_on: 127.0.0.2 load_extension: - module: @freediameter_extensions_builddir@/dbg_msg_dumps.fdx @@ -21,6 +37,7 @@ mme: connect: - identity: hss.open-ims.test addr: 127.0.0.4 +# port: 3868 s1ap: addr: 127.0.0.1 @@ -41,29 +58,17 @@ mme: integrity_order : [ EIA1, EIA2, EIA0 ] ciphering_order : [ EEA0, EEA1, EEA2 ] -hss: - freeDiameter: - identity: hss.open-ims.test - realm: open-ims.test - listen_on: 127.0.0.4 - 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: mme.open-ims.test - addr: 127.0.0.2 - -sgw: +sgwc: gtpc: - addr: 127.0.0.2 + addr: 127.0.0.6 + pfcp: + addr: 127.0.0.6 + +sgwu: gtpu: - addr: 127.0.0.2 + addr: 127.0.0.7 + pfcp: + addr: 127.0.0.7 smf: sbi: @@ -84,8 +89,10 @@ smf: - 2001:4860:4860::8844 mtu: 1400 freeDiameter: - identity: pgw.open-ims.test + identity: smf.open-ims.test realm: open-ims.test +# port: 3868 +# sec_port: 5868 listen_on: 127.0.0.3 load_extension: - module: @freediameter_extensions_builddir@/dbg_msg_dumps.fdx @@ -99,12 +106,7 @@ smf: connect: - identity: pcrf.open-ims.test addr: 127.0.0.5 -nrf: - sbi: - addr: - - 127.0.0.1 - - ::1 - port: 7777 +# port: 3868 upf: pfcp: @@ -114,6 +116,26 @@ upf: - 127.0.0.4 - ::1 pdn: + - addr: 10.45.0.1/16 + - addr: cafe::1/64 + +hss: + freeDiameter: + identity: hss.open-ims.test + realm: open-ims.test + listen_on: 127.0.0.4 + 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: mme.open-ims.test + addr: 127.0.0.2 pcrf: freeDiameter: @@ -130,7 +152,7 @@ pcrf: - module: @freediameter_extensions_builddir@/dict_dcca.fdx - module: @freediameter_extensions_builddir@/dict_dcca_3gpp.fdx connect: - - identity: pgw.open-ims.test + - identity: smf.open-ims.test addr: 127.0.0.3 - identity: pcscf.open-ims.test addr: 127.0.0.1 diff --git a/configs/epc.yaml.in b/configs/epc.yaml.in index 6692fb370..ee8d1abff 100644 --- a/configs/epc.yaml.in +++ b/configs/epc.yaml.in @@ -5,6 +5,13 @@ logger: parameter: no_ipv6: true +nrf: + sbi: + addr: + - 127.0.0.1 + - ::1 + port: 7777 + mme: freeDiameter: identity: mme.localdomain @@ -45,6 +52,63 @@ mme: network_name: full: Open5GS +sgwc: + gtpc: + addr: 127.0.0.6 + pfcp: + addr: 127.0.0.6 + +sgwu: + gtpu: + addr: 127.0.0.7 + pfcp: + addr: 127.0.0.7 + +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: smf.localdomain + realm: localdomain + 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.localdomain + addr: 127.0.0.5 +upf: + pfcp: + - addr: 127.0.0.4 + gtpu: + - addr: + - 127.0.0.4 + - ::1 + pdn: + - addr: 10.45.0.1/16 + - addr: cafe::1/64 + hss: freeDiameter: identity: hss.localdomain @@ -63,47 +127,6 @@ hss: - identity: mme.localdomain addr: 127.0.0.2 -sgw: - gtpc: - addr: 127.0.0.2 - gtpu: - addr: 127.0.0.2 - -pgw: - freeDiameter: - identity: pgw.localdomain - realm: localdomain - 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.localdomain - addr: 127.0.0.5 - - gtpc: - addr: - - 127.0.0.3 - - ::1 - gtpu: - - addr: 127.0.0.3 - - addr: ::1 - 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 - pcrf: freeDiameter: identity: pcrf.localdomain @@ -119,5 +142,5 @@ pcrf: - module: @freediameter_extensions_builddir@/dict_dcca.fdx - module: @freediameter_extensions_builddir@/dict_dcca_3gpp.fdx connect: - - identity: pgw.localdomain + - identity: smf.localdomain addr: 127.0.0.3 diff --git a/configs/meson.build b/configs/meson.build index 094c51b4b..abc7d708f 100644 --- a/configs/meson.build +++ b/configs/meson.build @@ -47,6 +47,7 @@ example_conf = ''' volte.yaml srslte.yaml cups.yaml + app.yaml '''.split() foreach file : example_conf diff --git a/configs/mnc3.yaml.in b/configs/mnc3.yaml.in index 3cf77fa7c..4d5a84c47 100644 --- a/configs/mnc3.yaml.in +++ b/configs/mnc3.yaml.in @@ -3,7 +3,13 @@ db_uri: mongodb://localhost/open5gs logger: parameter: - no_ipv6: true + +nrf: + sbi: + addr: + - 127.0.0.1 + - ::1 + port: 7777 mme: freeDiameter: @@ -45,6 +51,63 @@ mme: network_name: full: Open5GS +sgwc: + gtpc: + addr: 127.0.0.6 + pfcp: + addr: 127.0.0.6 + +sgwu: + gtpu: + addr: 127.0.0.7 + pfcp: + addr: 127.0.0.7 + +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: smf.localdomain + realm: localdomain + 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.localdomain + addr: 127.0.0.5 +upf: + pfcp: + - addr: 127.0.0.4 + gtpu: + - addr: + - 127.0.0.4 + - ::1 + pdn: + - addr: 10.45.0.1/16 + - addr: cafe::1/64 + hss: freeDiameter: identity: hss.localdomain @@ -62,52 +125,6 @@ hss: connect: - identity: mme.localdomain addr: 127.0.0.2 - -sgw: - gtpc: - addr: 127.0.0.2 - gtpu: - addr: 127.0.0.2 - -pgw: - freeDiameter: - identity: pgw.localdomain - realm: localdomain - 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: - connect: - - identity: pcrf.localdomain - addr: 127.0.0.5 - - gtpc: - - addr: - - 127.0.0.3 - - ::1 - - addr: - - 127.0.0.4 - apn: starent.com - gtpu: - - addr: 127.0.0.3 - - addr: ::1 - 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 - pcrf: freeDiameter: identity: pcrf.localdomain @@ -123,5 +140,5 @@ pcrf: - module: @freediameter_extensions_builddir@/dict_dcca.fdx - module: @freediameter_extensions_builddir@/dict_dcca_3gpp.fdx connect: - - identity: pgw.localdomain + - identity: smf.localdomain addr: 127.0.0.3 diff --git a/configs/open5gs/meson.build b/configs/open5gs/meson.build index 4e2101b9c..c7d7a2623 100644 --- a/configs/open5gs/meson.build +++ b/configs/open5gs/meson.build @@ -23,6 +23,8 @@ open5gs_conf = ''' mme.yaml hss.yaml sgw.yaml + sgwc.yaml + sgwu.yaml pgw.yaml pcrf.yaml diff --git a/configs/open5gs/mme.yaml.in b/configs/open5gs/mme.yaml.in index a4c39d6ec..2d934537d 100644 --- a/configs/open5gs/mme.yaml.in +++ b/configs/open5gs/mme.yaml.in @@ -226,7 +226,7 @@ mme: mme_name: open5gs-mme0 # -# sgw: +# sgwc: # # # @@ -236,18 +236,18 @@ mme: # If prefer_ipv4 is not true, [fe80::2%@loopback_devname@] is selected. # gtpc: # addr: -# - 127.0.0.2 +# - 127.0.0.6 # - fe80::2%@loopback_devname@ # # o Two SGW are defined. MME selects SGW with round-robin manner per UE # gtpc: -# - addr: 127.0.0.2 +# - addr: 127.0.0.6 # - addr: fe80::2%@loopback_devname@ # # o Three SGW are defined. MME selects SGW with round-robin manner per UE # gtpc: # - addr -# - 127.0.0.2 +# - 127.0.0.6 # - fe80::2%@loopback_devname@ # - addr # - 127.0.0.12 @@ -259,7 +259,7 @@ mme: # o Round-Robin # # gtpc: -# addr: 127.0.0.2 +# addr: 127.0.0.6 # addr: 127.0.2.2 # addr: 127.0.4.2 # @@ -267,7 +267,7 @@ mme: # (either single TAC or multiple TACs, DECIMAL representation) # # gtpc: -# - addr: 127.0.0.2 +# - addr: 127.0.0.6 # tac: 26000 # - addr: 127.0.2.2 # tac: [25000, 27000, 28000] @@ -276,40 +276,40 @@ mme: # (either single or multiple e_cell_id, HEX representation) # # gtpc: -# - addr: 127.0.0.2 +# - addr: 127.0.0.6 # e_cell_id: abcde01 # - addr: 127.0.2.2 # e_cell_id: [12345, a9413, 98765] # -sgw: +sgwc: gtpc: - addr: 127.0.0.2 + addr: 127.0.0.6 # -# pgw: +# smf: # # # -# o By default, the PGW uses the first PGW node. -# - To use a different APN for each PGW, specify gtpc.apn as the APN name. -# - If the HSS uses WebUI to set the PGW IP for each UE, -# you can use a specific PGW node for each UE. +# o By default, the SMF uses the first SMF node. +# - To use a different APN for each SMF, specify gtpc.apn as the APN name. +# - If the HSS uses WebUI to set the SMF IP for each UE, +# you can use a specific SMF node for each UE. # -# o Two PGW are defined. 127.0.0.3:2123 is used. +# o Two SMF are defined. 127.0.0.3:2123 is used. # [fe80::3%@loopback_devname@]:2123 is ignored. # gtpc: # - addr: 127.0.0.3 # - addr: fe80::3%@loopback_devname@ # -# o One PGW is defined. if prefer_ipv4 is not true, +# o One SMF is defined. if prefer_ipv4 is not true, # [fe80::3%@loopback_devname@] is selected. # gtpc: # - addr: # - 127.0.0.3 # - fe80::3%@loopback_devname@ # -# o Two PGW are defined with a different APN. -# - Note that if PGW IP for UE is configured in HSS, +# o Two SMF are defined with a different APN. +# - Note that if SMF IP for UE is configured in HSS, # the following configurion for this UE is ignored. # gtpc: # - addr: 127.0.0.3 @@ -317,12 +317,12 @@ sgw: # - addr: 127.0.0.5 # apn: volte # -# o If APN is omitted, the default APN uses the first PGW node. +# o If APN is omitted, the default APN uses the first SMF node. # gtpc: # - addr: 127.0.0.3 # - addr: 127.0.0.5 # apn: volte -pgw: +smf: gtpc: addr: - 127.0.0.3 diff --git a/configs/open5gs/sgw.yaml.in b/configs/open5gs/sgw.yaml.in index 6c0c44317..2bd7f7d9d 100644 --- a/configs/open5gs/sgw.yaml.in +++ b/configs/open5gs/sgw.yaml.in @@ -26,21 +26,21 @@ logger: # # # -# o GTP-C Server(127.0.0.2:2123, [fe80::2%@loopback_devname@]:2123) +# o GTP-C Server(127.0.0.6:2123, [fe80::2%@loopback_devname@]:2123) # gtpc: # addr: -# - 127.0.0.2 +# - 127.0.0.6 # - fe80::2%@loopback_devname@ # -# o On SGW, Same Configuration(127.0.0.2:2123, +# o On SGW, Same Configuration(127.0.0.6:2123, # [fe80::2%@loopback_devname@]:2123) as below. # gtpc: -# - addr: 127.0.0.2 +# - addr: 127.0.0.6 # - addr: fe80::2%@loopback_devname@ # # # -# o GTP-U Server(all address avaiable) +# o GTP-U Server(all address available) # gtpu: # # o Provide custom SGW GTP-U address to be advertised inside S1AP messages @@ -53,14 +53,14 @@ logger: # advertise_name: sgw1.epc.mnc001.mcc001.3gppnetwork.org # # gtpu: -# dev: ens3 -# advertise_name: sgw1.epc.mnc001.mcc001.3gppnetwork.org +# dev: ens3 +# advertise_name: sgw1.epc.mnc001.mcc001.3gppnetwork.org # sgw: gtpc: - addr: 127.0.0.2 + addr: 127.0.0.6 gtpu: - addr: 127.0.0.2 + addr: 127.0.0.6 # # parameter: diff --git a/configs/open5gs/sgwc.yaml.in b/configs/open5gs/sgwc.yaml.in new file mode 100644 index 000000000..97e437f9e --- /dev/null +++ b/configs/open5gs/sgwc.yaml.in @@ -0,0 +1,172 @@ +# +# 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,gtp,sgw,event,tlv,mem,sock +# +logger: + file: @localstatedir@/log/open5gs/sgwc.log + +# +# sgwc: +# +# +# +# o GTP-C Server(127.0.0.6:2123, [fe80::2%@loopback_devname@]:2123) +# gtpc: +# addr: +# - 127.0.0.6 +# - fe80::2%@loopback_devname@ +# +# o On SGW, Same Configuration(127.0.0.6:2123, +# [fe80::2%@loopback_devname@]:2123) as below. +# gtpc: +# - addr: 127.0.0.6 +# - addr: fe80::2%@loopback_devname@ +# +# +# +# o PFCP Server(127.0.0.6:8805, ::1:8805) +# pfcp: +# - addr: 127.0.0.6 +# - addr: ::1 +# +sgwc: + gtpc: + addr: 127.0.0.6 + pfcp: + addr: 127.0.0.6 + +# +# sgwu: +# +# > +# +# o PFCP Client(127.0.0.7:8805) +# +# pfcp: +# addr: 127.0.0.7 +# +# +# +# o Round-Robin +# sgwu: +# pfcp: +# - addr: 127.0.0.7 +# - addr: 127.0.0.12 +# +# o UPF selection by eNodeB TAC +# (either single TAC or multiple TACs, DECIMAL representation) +# +# sgwu: +# pfcp: +# - addr: 127.0.0.7 +# tac: 1 +# - addr: 127.0.0.12 +# tac: [3,5,8] +# +# o UPF selection by UE's DNN/APN (either single DNN/APN or multiple DNNs/APNs) +# +# sgwu: +# pfcp: +# - addr: 127.0.0.7 +# dnn: ims +# - addr: 127.0.0.12 +# apn: [internet, web] +# +# o UPF selection by CellID(e_cell_id: 28bit, nr_cell_id: 36bit) +# (either single enb_id or multiple enb_ids, HEX representation) +# +# sgwu: +# pfcp: +# - addr: 127.0.0.7 +# e_cell_id: 463 +# - addr: 127.0.0.12 +# nr_cell_id: [123456789, 9413] +# +sgwu: + pfcp: + addr: 127.0.0.7 + +# +# 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: + no_ipv6: true + +# +# 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 +# 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 UPF/SGW +# - Maximum Number of packet(SDU size = 8Kbytes) pool in UPF/SGW +# - UPF/SGW Memory Usage : 65536 * 8Kbytes = 512Mbytes +# +# packet: 65536 +# +pool: diff --git a/configs/open5gs/sgwu.yaml.in b/configs/open5gs/sgwu.yaml.in new file mode 100644 index 000000000..ffb0452a6 --- /dev/null +++ b/configs/open5gs/sgwu.yaml.in @@ -0,0 +1,137 @@ +# +# 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,gtp,sgw,event,tlv,mem,sock +# +logger: + file: @localstatedir@/log/open5gs/sgwc.log + +# +# sgwu: +# +# +# +# o GTP-U Server(all address available) +# gtpu: +# +# o Provide custom SGW GTP-U address to be advertised inside S1AP messages +# gtpu: +# addr: 10.4.128.21 +# advertise_addr: 172.24.15.30 +# +# gtpu: +# addr: 10.4.128.21 +# advertise_name: sgw1.epc.mnc001.mcc001.3gppnetwork.org +# +# gtpu: +# dev: ens3 +# advertise_name: sgw1.epc.mnc001.mcc001.3gppnetwork.org +# +# +# +# o PFCP Server(127.0.0.7:8805, ::1:8805) +# pfcp: +# - addr: 127.0.0.7 +# - addr: ::1 +# +sgwu: + gtpu: + addr: 127.0.0.7 + pfcp: + addr: 127.0.0.7 + +# +# sgwc: +# +# > +# +# o PFCP Client(127.0.0.6:8805) +# +# pfcp: +# addr: 127.0.0.6 +# +sgwc: + +# +# 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: + no_ipv6: true + +# +# 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 +# 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 UPF/SGW +# - Maximum Number of packet(SDU size = 8Kbytes) pool in UPF/SGW +# - UPF/SGW Memory Usage : 65536 * 8Kbytes = 512Mbytes +# +# packet: 65536 +# +pool: diff --git a/configs/open5gs/upf.yaml.in b/configs/open5gs/upf.yaml.in index 0eb4e356b..d54b09900 100644 --- a/configs/open5gs/upf.yaml.in +++ b/configs/open5gs/upf.yaml.in @@ -77,8 +77,6 @@ upf: # addr: 127.0.0.3 # smf: - pfcp: - - addr: 127.0.0.3 # # parameter: diff --git a/configs/srslte.yaml.in b/configs/srslte.yaml.in index 343779d63..4f0253cf0 100644 --- a/configs/srslte.yaml.in +++ b/configs/srslte.yaml.in @@ -5,6 +5,13 @@ logger: parameter: no_ipv6: true +nrf: + sbi: + addr: + - 127.0.0.1 + - ::1 + port: 7777 + mme: freeDiameter: identity: mme.localdomain @@ -45,6 +52,64 @@ mme: network_name: full: Open5GS +sgwc: + gtpc: + addr: 127.0.0.6 + pfcp: + addr: 127.0.0.6 + +sgwu: + gtpu: + addr: 127.0.0.7 + pfcp: + addr: 127.0.0.7 + +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: smf.localdomain + realm: localdomain + 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.localdomain + addr: 127.0.0.5 +upf: + pfcp: + - addr: 127.0.0.4 + gtpu: + - addr: + - 127.0.0.4 + - ::1 + pdn: + - addr: 10.45.0.1/16 + - addr: cafe::1/64 + + hss: freeDiameter: identity: hss.localdomain @@ -63,47 +128,6 @@ hss: - identity: mme.localdomain addr: 127.0.0.2 -sgw: - gtpc: - addr: 127.0.0.2 - gtpu: - addr: 127.0.0.2 - -pgw: - freeDiameter: - identity: pgw.localdomain - realm: localdomain - 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.localdomain - addr: 127.0.0.5 - - gtpc: - addr: - - 127.0.0.3 - - ::1 - gtpu: - - addr: 127.0.0.3 - - addr: ::1 - 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 - pcrf: freeDiameter: identity: pcrf.localdomain @@ -119,5 +143,5 @@ pcrf: - module: @freediameter_extensions_builddir@/dict_dcca.fdx - module: @freediameter_extensions_builddir@/dict_dcca_3gpp.fdx connect: - - identity: pgw.localdomain + - identity: smf.localdomain addr: 127.0.0.3 diff --git a/configs/volte.yaml.in b/configs/volte.yaml.in index cf88ca92e..db0b8f378 100644 --- a/configs/volte.yaml.in +++ b/configs/volte.yaml.in @@ -3,7 +3,20 @@ db_uri: mongodb://localhost/open5gs logger: parameter: +# no_nrf: true +# no_amf: true +# no_smf: true +# no_upf: true +# no_ausf: true +# no_udm: true +# no_udr: true +nrf: + sbi: + addr: + - 127.0.0.1 + - ::1 + port: 7777 mme: freeDiameter: identity: mme.open-ims.test @@ -44,6 +57,67 @@ mme: integrity_order : [ EIA1, EIA2, EIA0 ] ciphering_order : [ EEA0, EEA1, EEA2 ] +sgwc: + gtpc: + addr: 127.0.0.6 + pfcp: + addr: 127.0.0.6 + +sgwu: + gtpu: + addr: 127.0.0.7 + pfcp: + addr: 127.0.0.7 + +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: smf.open-ims.test + realm: open-ims.test +# port: 3868 +# sec_port: 5868 + 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 +# port: 3868 + +upf: + pfcp: + - addr: 127.0.0.4 + gtpu: + - addr: + - 127.0.0.4 + - ::1 + pdn: + - addr: 10.45.0.1/16 + - addr: cafe::1/64 + hss: freeDiameter: identity: hss.open-ims.test @@ -65,60 +139,6 @@ hss: addr: 127.0.0.2 # port: 3868 -sgw: - gtpc: - addr: 127.0.0.2 - gtpu: - addr: 127.0.0.2 - -pgw: - freeDiameter: - identity: pgw.open-ims.test - realm: open-ims.test -# port: 3868 -# sec_port: 5868 - 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 -# port: 3868 - - gtpc: - addr: - - 127.0.0.3 - - ::1 - gtpu: - - addr: 127.0.0.3 - - addr: ::1 - 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 - -# -# -# -# o Proxy Call Session Control Function -# -# p-cscf: -# - 127.0.0.1 -# - ::1 -# - pcrf: freeDiameter: identity: pcrf.open-ims.test @@ -136,7 +156,7 @@ pcrf: - module: @freediameter_extensions_builddir@/dict_dcca.fdx - module: @freediameter_extensions_builddir@/dict_dcca_3gpp.fdx connect: - - identity: pgw.open-ims.test + - identity: smf.open-ims.test addr: 127.0.0.3 - identity: pcscf.open-ims.test addr: 127.0.0.1 diff --git a/docs/_docs/new/03-moving-4g-epc-to-cups.md b/docs/_docs/new/03-moving-4g-epc-to-cups.md new file mode 100644 index 000000000..d6182f3fb --- /dev/null +++ b/docs/_docs/new/03-moving-4g-epc-to-cups.md @@ -0,0 +1,407 @@ +--- +title: Moving 4G EPC to the CUPS +head_inline: "" +--- + +This post explains how to compile and install the source code on **Debian/Ubuntu** based Linux Distribution. +{: .blue} + +### Getting MongoDB +--- + +Install MongoDB with package manager. It is used as database for the HSS and PCRF. + +```bash +$ sudo apt update +$ sudo apt install mongodb +$ sudo systemctl start mongodb (if '/usr/bin/mongod' is not running) +$ sudo systemctl enable mongodb (ensure to automatically start it on system boot) +``` + +### Setting up TUN device (not persistent after rebooting) +--- + +Create the TUN device with the interface name `ogstun`. + +```bash +$ sudo ip tuntap add name ogstun mode tun +$ sudo ip addr add 10.45.0.1/16 dev ogstun +$ sudo ip addr add cafe::1/64 dev ogstun +$ sudo ip link set ogstun up +``` + +**Tip:** The script provided in [$GIT_REPO/misc/netconf.sh](https://github.com/{{ site.github_username }}/open5gs/blob/master/misc/netconf.sh) makes it easy to configure the TUN device as follows: +`$ sudo ./misc/netconf.sh` +{: .notice--info} + +### Building Open5GS +--- + +Install the dependencies for building the source code. + +```bash +$ sudo apt install python3-pip python3-setuptools python3-wheel ninja-build build-essential flex bison git libsctp-dev libgnutls28-dev libgcrypt-dev libssl-dev libidn11-dev libmongoc-dev libbson-dev libyaml-dev libmicrohttpd-dev libcurl4-gnutls-dev meson +``` + +Git clone. + +```bash +$ git clone https://github.com/{{ site.github_username }}/open5gs +``` + +To compile with meson: + +```bash +$ cd open5gs +$ meson build --prefix=`pwd`/install +$ ninja -C build +``` + +Check whether the compilation is correct. +```bash +$ meson test +``` + +**Tip:** You can also check the result of `ninja -C build test` with a tool that captures packets. If you are running `wireshark`, select the `loopback` interface and set FILTER to `s1ap || gtpv2 || diameter || pfcp || gtp`. You can see the virtually created packets. [[testcups.pcapng]]({{ site.url }}{{ site.baseurl }}/assets/pcapng/testcups.pcapng) +{: .notice--info} + +You need to perform the **installation process**. +```bash +$ cd build +$ ninja install +$ cd ../ +``` + +### Configure Open5GS +--- + +Modify [install/etc/open5gs/mme.yaml](https://github.com/{{ site.github_username }}/open5gs/blob/master/configs/open5gs/mme.yaml.in) to set the S1AP IP address, PLMN ID, and TAC. + +In the below example we + +- use MCC-MNC of 901-70, as this is the home network of the default IMSIs of the sysmoUSIM-SJS1 cards. +- use 192.168.0.100 for the S1AP/GTP-U connection of MME/SGW-U to the eNB + +```diff +diff -u /etc/open5gs/mme.yaml.old /etc/open5gs/mme.yaml +--- mme.yaml.old 2018-04-15 18:28:31.000000000 +0900 ++++ mme.yaml 2018-04-15 19:53:10.000000000 +0900 +@@ -204,20 +204,20 @@ logger: + mme: + freeDiameter: @sysconfdir@/freeDiameter/mme.conf + s1ap: +- addr: 127.0.0.1 ++ addr: 192.168.0.100 + gtpc: + addr: 127.0.0.1 + gummei: + plmn_id: +- mcc: 001 +- mnc: 01 ++ mcc: 901 ++ mnc: 70 + mme_gid: 2 + mme_code: 1 + tai: + plmn_id: +- mcc: 001 +- mnc: 01 +- tac: 1 ++ mcc: 901 ++ mnc: 70 ++ tac: 7 + security: + integrity_order : [ EIA1, EIA2, EIA0 ] + ciphering_order : [ EEA0, EEA1, EEA2 ] + +``` + +Modify [install/etc/open5gs/sgwu.yaml](https://github.com/{{ site.github_username }}/open5gs/blob/master/configs/open5gs/sgwu.yaml.in) to set the GTP-U IP address. +```diff +diff -u /etc/open5gs/sgwu.yaml.old /etc/open5gs/sgwu.yaml +--- sgwu.yaml.old 2018-04-15 18:30:25.000000000 +0900 ++++ sgwu.yaml 2018-04-15 18:30:30.000000000 +0900 +@@ -51,7 +51,7 @@ logger: + # + sgwu: + gtpu: +- addr: 127.0.0.7 ++ addr: 192.168.0.100 + pfcp: + addr: 127.0.0.7 +``` + +If you modify the config files while Open5GS daemons are running, please restart them + + +### Running Open5GS +--- + +Specify the absolute path to the sharead library as follows. + +```bash +$ echo $(cd $(dirname ./install/lib/x86_64-linux-gnu/) && pwd -P)/$(basename ./install/lib/x86_64-linux-gnu/) +/home/acetcom/Documents/git/open5gs/install/lib/x86_64-linux-gnu +$ export LD_LIBRARY_PATH=/home/acetcom/Documents/git/open5gs/install/lib/x86_64-linux-gnu +$ ldd ./install/bin/open5gs-mmed +... + libogsapp.so.1 => /home/acetcom/Documents/git/open5gs/install/lib/x86_64-linux-gnu/libogsapp.so.1 (0x00007f161ab51000) + libogscore.so.1 => /home/acetcom/Documents/git/open5gs/install/lib/x86_64-linux-gnu/libogscore.so.1 (0x00007f161a922000) + libogssctp.so.1 => /home/acetcom/Documents/git/open5gs/install/lib/x86_64-linux-gnu/libogssctp.so.1 (0x00007f161a71d000) + libogss1ap.so.1 => /home/acetcom/Documents/git/open5gs/install/lib/x86_64-linux-gnu/libogss1ap.so.1 (0x00007f161a519000) +... +``` + +If you want to set the shared library path permanently, you can use ldconfig. +```bash +$ sudo sh -c "echo /home/acetcom/Documents/git/open5gs/install/lib/x86_64-linux-gnu > /etc/ld.so.conf.d/open5gs.conf" +$ sudo ldconfig +``` + +Now let's get started. + +```bash +$ ./install/bin/open5gs-nrfd +Open5GS daemon v1.3.0-194-gc674984+ + +08/12 19:34:39.277: [app] INFO: Configuration: '/home/acetcom/Documents/git/open5gs/install/etc/open5gs/nrf.yaml' (../src/main.c:54) +08/12 19:34:39.277: [app] INFO: File Logging: '/home/acetcom/Documents/git/open5gs/install/var/log/open5gs/nrf.log' (../src/main.c:57) +08/12 19:34:39.278: [app] INFO: NRF initialize...done (../src/nrf/app.c:31) +08/12 19:34:39.278: [sbi] INFO: sbi_server() [127.0.0.1]:7777 (../lib/sbi/server.c:298) +08/12 19:34:39.278: [sbi] INFO: sbi_server() [::1]:7777 (../lib/sbi/server.c:298 + +$ ./install/bin/open5gs-mmed +Open5GS daemon v1.3.0-194-gc674984+ + +08/12 19:35:00.505: [app] INFO: Configuration: '/home/acetcom/Documents/git/open5gs/install/etc/open5gs/mme.yaml' (../src/main.c:54) +08/12 19:35:00.506: [app] INFO: File Logging: '/home/acetcom/Documents/git/open5gs/install/var/log/open5gs/mme.log' (../src/main.c:57) +08/12 19:35:00.560: [gtp] INFO: gtp_server() [127.0.0.1]:2123 (../lib/gtp/path.c:32) +08/12 19:35:00.560: [app] INFO: MME initialize...done (../src/mme/app-init.c:33) +08/12 19:35:00.560: [gtp] INFO: gtp_connect() [127.0.0.6]:2123 (../lib/gtp/path.c:59) +08/12 19:35:00.560: [mme] INFO: s1ap_server() [127.0.0.1]:36412 (../src/mme/s1ap-sctp.c:57) + +$ ./install/bin/open5gs-sgwcd +Open5GS daemon v1.3.0-194-gc674984+ + +08/12 19:35:15.866: [app] INFO: Configuration: '/home/acetcom/Documents/git/open5gs/install/etc/open5gs/sgwc.yaml' (../src/main.c:54) +08/12 19:35:15.866: [app] INFO: File Logging: '/home/acetcom/Documents/git/open5gs/install/var/log/open5gs/sgwc.log' (../src/main.c:57) +08/12 19:35:15.872: [app] INFO: SGW-C initialize...done (../src/sgwc/app.c:31) +08/12 19:35:15.873: [gtp] INFO: gtp_server() [127.0.0.6]:2123 (../lib/gtp/path.c:32) +08/12 19:35:15.873: [pfcp] INFO: pfcp_server() [127.0.0.6]:8805 (../lib/pfcp/path.c:32) +08/12 19:35:15.873: [pfcp] INFO: ogs_pfcp_connect() [127.0.0.7]:8805 (../lib/pfcp/path.c:60) + +$ ./install/bin/open5gs-sgwud +Open5GS daemon v1.3.0-194-gc674984+ + +08/12 19:35:37.768: [app] INFO: Configuration: '/home/acetcom/Documents/git/open5gs/install/etc/open5gs/sgwu.yaml' (../src/main.c:54) +08/12 19:35:37.768: [app] INFO: File Logging: '/home/acetcom/Documents/git/open5gs/install/var/log/open5gs/sgwc.log' (../src/main.c:57) +08/12 19:35:37.771: [pfcp] INFO: pfcp_server() [127.0.0.7]:8805 (../lib/pfcp/path.c:32) +08/12 19:35:37.771: [app] INFO: SGW-U initialize...done (../src/sgwu/app.c:31) +08/12 19:35:37.773: [gtp] INFO: gtp_server() [127.0.0.7]:2152 (../lib/gtp/path.c:32) + +$ ./install/bin/open5gs-smfd +Open5GS daemon v1.3.0-194-gc674984+ + +08/12 19:35:50.948: [app] INFO: Configuration: '/home/acetcom/Documents/git/open5gs/install/etc/open5gs/smf.yaml' (../src/main.c:54) +08/12 19:35:50.948: [app] INFO: File Logging: '/home/acetcom/Documents/git/open5gs/install/var/log/open5gs/smf.log' (../src/main.c:57) +08/12 19:35:50.999: [gtp] INFO: gtp_server() [127.0.0.3]:2123 (../lib/gtp/path.c:32) +08/12 19:35:50.999: [app] INFO: SMF initialize...done (../src/smf/app.c:31) +08/12 19:35:50.999: [gtp] INFO: gtp_server() [::1]:2123 (../lib/gtp/path.c:32) +08/12 19:35:50.999: [pfcp] INFO: pfcp_server() [127.0.0.3]:8805 (../lib/pfcp/path.c:32) +08/12 19:35:50.999: [pfcp] INFO: pfcp_server() [::1]:8805 (../lib/pfcp/path.c:32) +08/12 19:35:50.999: [pfcp] INFO: ogs_pfcp_connect() [127.0.0.4]:8805 (../lib/pfcp/path.c:60) +08/12 19:35:50.999: [sbi] INFO: sbi_server() [127.0.0.3]:7777 (../lib/sbi/server.c:298) + +$ ./install/bin/open5gs-upfd +Open5GS daemon v1.3.0-194-gc674984+ + +08/12 19:36:03.980: [app] INFO: Configuration: '/home/acetcom/Documents/git/open5gs/install/etc/open5gs/upf.yaml' (../src/main.c:54) +08/12 19:36:03.981: [app] INFO: File Logging: '/home/acetcom/Documents/git/open5gs/install/var/log/open5gs/upf.log' (../src/main.c:57) +08/12 19:36:03.984: [pfcp] INFO: pfcp_server() [127.0.0.4]:8805 (../lib/pfcp/path.c:32) +08/12 19:36:03.984: [app] INFO: UPF initialize...done (../src/upf/app.c:31) +08/12 19:36:03.984: [gtp] INFO: gtp_server() [127.0.0.4]:2152 (../lib/gtp/path.c:32) +08/12 19:36:03.984: [gtp] INFO: gtp_server() [::1]:2152 (../lib/gtp/path.c:32) + +$ ./install/bin/open5gs-hssd +Open5GS daemon v1.3.0-194-gc674984+ + +08/12 19:36:15.670: [app] INFO: Configuration: '/home/acetcom/Documents/git/open5gs/install/etc/open5gs/hss.yaml' (../src/main.c:54) +08/12 19:36:15.670: [app] INFO: File Logging: '/home/acetcom/Documents/git/open5gs/install/var/log/open5gs/hss.log' (../src/main.c:57) +08/12 19:36:15.671: [dbi] INFO: MongoDB URI: 'mongodb://localhost/open5gs' (../lib/dbi/ogs-mongoc.c:99) +08/12 19:36:15.693: [app] INFO: HSS initialize...done (../src/hss/app-init.c:31) + +$ ./install/bin/open5gs-pcrfd +Open5GS daemon v1.3.0-194-gc674984+ + +08/12 19:36:30.356: [app] INFO: Configuration: '/home/acetcom/Documents/git/open5gs/install/etc/open5gs/pcrf.yaml' (../src/main.c:54) +08/12 19:36:30.356: [app] INFO: File Logging: '/home/acetcom/Documents/git/open5gs/install/var/log/open5gs/pcrf.log' (../src/main.c:57) +08/12 19:36:30.358: [dbi] INFO: MongoDB URI: 'mongodb://localhost/open5gs' (../lib/dbi/ogs-mongoc.c:99) +08/12 19:36:30.387: [app] INFO: PCRF initialize...done (../src/pcrf/app-init.c:31) +``` + +Several command line options are provided. + +```bash +$ ./install/bin/open5gs-mmed -h +Usage: ./install/bin/open5gs-mmed [options] +Options: + -c filename : set configuration file + -l filename : set logging file + -e level : set global log-level (default:info) + -m domain : set log-domain (e.g. mme:sgw:gtp) + -d : print lots of debugging information + -t : print tracing information for developer + -D : start as a daemon + -v : show version number and exit + -h : show this message and exit +``` + +You can also copy the binaries to /usr/bin to be able to run them from anywhere on the system. +```bash +$ cp open5gs* /usr/bin/ +``` + +For convenience, you can execute all network elements at once by using the following command. +```bash +$ cd build +$ vi configs/app.yaml ## check the configuration +$ ./test/app/app ## run all network elements +``` + +### Building the WebUI of Open5GS +--- + +[Node.js](https://nodejs.org/) is required to build WebUI of Open5GS + +```bash +$ sudo apt install curl +$ curl -sL https://deb.nodesource.com/setup_12.x | sudo -E bash - +$ sudo apt install nodejs +``` + +Install the dependencies to run WebUI + +```bash +$ cd webui +$ npm install +``` + +The WebUI runs as an [npm](https://www.npmjs.com/) script. + +```bash +$ npm run dev +``` + +### Register Subscriber Information +--- + +Connect to `http://localhost:3000` and login with **admin** account. + +> Username : admin +> Password : 1423 + +**Note:** +You can change the password in _Account_ Menu. +{: .notice--info} + +To add subscriber information, you can do WebUI operations in the following order: + + 1. Go to `Subscriber` Menu. + 2. Click `+` Button to add a new subscriber. + 3. Fill the IMSI, security context(K, OPc, AMF), and APN of the subscriber. + 4. Click `SAVE` Button + +**Tip:** This addition immediately affects Open5GS without restarting any daemon. +{: .notice--warning} + +### IP routing + NAT for UE internet connectivity +--- + +To allow your phones to connect to the internet, you must run the following command on the host running Open5GS-PGW: + +```bash +### Check IP Table 'forward' +$ sudo iptables -L +Chain INPUT (policy ACCEPT) +target prot opt source destination + +Chain FORWARD (policy ACCEPT) +target prot opt source destination + +Chain OUTPUT (policy ACCEPT) +target prot opt source destination + +### Check IP Table 'nat' +$ sudo iptables -L -t nat +Chain PREROUTING (policy ACCEPT) +target prot opt source destination + +Chain INPUT (policy ACCEPT) +target prot opt source destination + +Chain OUTPUT (policy ACCEPT) +target prot opt source destination + +Chain POSTROUTING (policy ACCEPT) +target prot opt source destination + +### Enable IPv4 Forwarding +$ sudo sh -c "echo 1 > /proc/sys/net/ipv4/ip_forward" + +### Add NAT Rule +$ sudo iptables -t nat -A POSTROUTING -s 10.45.0.0/16 ! -o ogstun -j MASQUERADE +``` + +**Note:** The above assumes you do not have any existing rules in the filter and nat tables. If a program such as docker has already set up rules, you may need to add the Open5GS related rules differently. +{: .notice--danger} + +### Turn on your eNodeB and Phone +--- + +- You can see actual traffic through wireshark -- [[srsenb.pcapng]]({{ site.url }}{{ site.baseurl }}/assets/pcapng/srsenb.pcapng). +- You can view the log at `$INSTALL_PREFIX/var/log/open5gs/*.log`. + +### Troubleshooting +--- + +Debugging tools can help you troubleshoot problems. + +- [GDB](https://www.gnu.org/software/gdb/) can be used as below: + + ```bash + $ gdb ./open5gs-mmed + ``` + +- On *Mac OS X*, you can use the [LLDB](https://lldb.llvm.org/). + + ```bash + $ lldb ./open5gs-mmed + ``` + +You can use the command line option[`-d`] to record more logs. + +```bash +$ ./install/bin/open5gs-mmed -d +Open5GS daemon v1.3.0-194-gc674984+ + +08/12 19:38:40.968: [app] INFO: Configuration: '/home/acetcom/Documents/git/open5gs/install/etc/open5gs/mme.yaml' (../src/main.c:54) +08/12 19:38:40.968: [app] INFO: File Logging: '/home/acetcom/Documents/git/open5gs/install/var/log/open5gs/mme.log' (../src/main.c:57) +08/12 19:38:40.968: [app] INFO: LOG-LEVEL: 'debug' (../src/main.c:60) +08/12 19:38:41.076: [thread] DEBUG: [0x7ff5d26adb18] worker signal (../lib/core/ogs-thread.c:66) +08/12 19:38:41.076: [mme] DEBUG: mme_state_initial(): INIT + (../src/mme/mme-sm.c:82) +08/12 19:38:41.076: [thread] DEBUG: [0x7ff5d26adb18] thread started (../lib/core/ogs-thread.c:101) +08/12 19:38:41.076: [mme] DEBUG: mme_state_operational(): ENTRY + (../src/mme/mme-sm.c:128) +08/12 19:38:41.076: [app] INFO: MME initialize...done (../src/mme/app-init.c:33) +08/12 19:38:41.076: [sock] DEBUG: socket create(2:2:17) (../lib/core/ogs-socket.c:92) +08/12 19:38:41.076: [sock] DEBUG: udp_socket() family:2 (../lib/core/ogs-udp.c:31) +08/12 19:38:41.076: [sock] DEBUG: socket bind 127.0.0.1:2123 (../lib/core/ogs-socket.c:117) +08/12 19:38:41.076: [sock] DEBUG: udp_server() [127.0.0.1]:2123 (../lib/core/ogs-udp.c:55) +08/12 19:38:41.076: [gtp] INFO: gtp_server() [127.0.0.1]:2123 (../lib/gtp/path.c:32) +08/12 19:38:41.076: [gtp] INFO: gtp_connect() [127.0.0.6]:2123 (../lib/gtp/path.c:59) +08/12 19:38:41.076: [sock] DEBUG: socket create(2:1:132) (../lib/core/ogs-socket.c:92) +08/12 19:38:41.076: [sock] DEBUG: socket bind 127.0.0.1:36412 (../lib/core/ogs-socket.c:117) +08/12 19:38:41.076: [sock] DEBUG: sctp_server() [127.0.0.1]:36412 (../lib/sctp/ogs-lksctp.c:100) +08/12 19:38:41.076: [mme] INFO: s1ap_server() [127.0.0.1]:36412 (../src/mme/s1ap-sctp.c:57) +``` diff --git a/docs/_pages/docs.md b/docs/_pages/docs.md index 6b6e1bd17..cc2992c1c 100644 --- a/docs/_pages/docs.md +++ b/docs/_pages/docs.md @@ -7,6 +7,7 @@ head_inline: "" - What's New? - [Installing 5G Core](new/02-installing-5g-core) + - [Moving 4G EPC to the CUPS](new/03-moving-4g-epc-to-cups) - User's Guide - [Quickstart](guide/01-quickstart) diff --git a/docs/assets/pcapng/testcups.pcapng b/docs/assets/pcapng/testcups.pcapng new file mode 100644 index 000000000..811ecfa70 Binary files /dev/null and b/docs/assets/pcapng/testcups.pcapng differ diff --git a/lib/app/ogs-app.h b/lib/app/ogs-app.h index 017b0b459..c8a3f2f0f 100644 --- a/lib/app/ogs-app.h +++ b/lib/app/ogs-app.h @@ -51,6 +51,12 @@ void hss_terminate(void); int sgw_initialize(void); void sgw_terminate(void); +int sgwc_initialize(void); +void sgwc_terminate(void); + +int sgwu_initialize(void); +void sgwu_terminate(void); + int pgw_initialize(void); void pgw_terminate(void); diff --git a/lib/app/ogs-config.c b/lib/app/ogs-config.c index 1c47fa16b..5d731b4ac 100644 --- a/lib/app/ogs-config.c +++ b/lib/app/ogs-config.c @@ -339,6 +339,15 @@ int ogs_config_parse() if (!strcmp(parameter_key, "no_hss")) { self.parameter.no_hss = ogs_yaml_iter_bool(¶meter_iter); + } else if (!strcmp(parameter_key, "no_mme")) { + self.parameter.no_mme = + ogs_yaml_iter_bool(¶meter_iter); + } else if (!strcmp(parameter_key, "no_sgwu")) { + self.parameter.no_sgwu = + ogs_yaml_iter_bool(¶meter_iter); + } else if (!strcmp(parameter_key, "no_sgwc")) { + self.parameter.no_sgwc = + ogs_yaml_iter_bool(¶meter_iter); } else if (!strcmp(parameter_key, "no_sgw")) { self.parameter.no_sgw = ogs_yaml_iter_bool(¶meter_iter); diff --git a/lib/app/ogs-config.h b/lib/app/ogs-config.h index d32be8697..1d041ed06 100644 --- a/lib/app/ogs-config.h +++ b/lib/app/ogs-config.h @@ -43,8 +43,11 @@ typedef struct ogs_config_s { struct { /* Element */ + int no_mme; int no_hss; int no_sgw; + int no_sgwc; + int no_sgwu; int no_pgw; int no_pcrf; diff --git a/lib/gtp/message.c b/lib/gtp/message.c index 8b3deeca9..506de6f4d 100644 --- a/lib/gtp/message.c +++ b/lib/gtp/message.c @@ -20,7 +20,7 @@ /******************************************************************************* * This file had been created by gtp-tlv.py script v0.1.0 * Please do not modify this file but regenerate it via script. - * Created on: 2020-07-08 16:42:53.180060 by acetcom + * Created on: 2020-08-11 20:17:53.518172 by acetcom * from 29274-g30.docx ******************************************************************************/ @@ -2711,8 +2711,10 @@ int ogs_gtp_parse_msg(ogs_gtp_message_t *gtp_message, ogs_pkbuf_t *pkbuf) if (h->teid_presence) gtp_message->h.teid = be32toh(gtp_message->h.teid); - if (pkbuf->len == 0) + if (pkbuf->len == 0) { + ogs_assert(ogs_pkbuf_push(pkbuf, size)); return OGS_OK; + } switch(gtp_message->h.type) { case OGS_GTP_ECHO_REQUEST_TYPE: @@ -2844,6 +2846,8 @@ int ogs_gtp_parse_msg(ogs_gtp_message_t *gtp_message, ogs_pkbuf_t *pkbuf) break; } + ogs_assert(ogs_pkbuf_push(pkbuf, size)); + return rv; } diff --git a/lib/gtp/message.h b/lib/gtp/message.h index f3af07427..a6c3d5002 100644 --- a/lib/gtp/message.h +++ b/lib/gtp/message.h @@ -20,7 +20,7 @@ /******************************************************************************* * This file had been created by gtp-tlv.py script v0.1.0 * Please do not modify this file but regenerate it via script. - * Created on: 2020-07-08 16:42:53.173072 by acetcom + * Created on: 2020-08-11 20:17:53.511069 by acetcom * from 29274-g30.docx ******************************************************************************/ diff --git a/lib/gtp/support/README.md b/lib/gtp/support/README.md index 75d0609c8..09b961474 100644 --- a/lib/gtp/support/README.md +++ b/lib/gtp/support/README.md @@ -8,10 +8,9 @@ user@host ~/Documents/git/open5gs/lib/gtp/support$ \ sudo pip install python-docx * Change the format of standard specification - from 29274-d80.doc to 29274-d80.docx + from 29274-g30.doc to 29274-g30.docx using Microsoft Office 2007+ * Generate TLV support files user@host ~/Documents/git/open5gs/lib/s1ap/support$ \ - python gtp-tlv.py -f 29274-d80.docx -o .. python gtp-tlv.py -f 29274-g30.docx -o .. diff --git a/lib/gtp/support/gtp-tlv.py b/lib/gtp/support/gtp-tlv.py index 799ac0384..29a4eea88 100644 --- a/lib/gtp/support/gtp-tlv.py +++ b/lib/gtp/support/gtp-tlv.py @@ -650,8 +650,10 @@ f.write("""int ogs_gtp_parse_msg(ogs_gtp_message_t *gtp_message, ogs_pkbuf_t *pk if (h->teid_presence) gtp_message->h.teid = be32toh(gtp_message->h.teid); - if (pkbuf->len == 0) + if (pkbuf->len == 0) { + ogs_assert(ogs_pkbuf_push(pkbuf, size)); return OGS_OK; + } switch(gtp_message->h.type) { """) @@ -666,6 +668,8 @@ f.write(""" default: break; } + ogs_assert(ogs_pkbuf_push(pkbuf, size)); + return rv; } diff --git a/lib/gtp/xact.h b/lib/gtp/xact.h index c226d401e..ea4996b59 100644 --- a/lib/gtp/xact.h +++ b/lib/gtp/xact.h @@ -80,7 +80,12 @@ typedef struct ogs_gtp_xact_s { ogs_timer_t *tm_holding; /**< Timer waiting for holding message */ uint8_t holding_rcount; - struct ogs_gtp_xact_s *assoc_xact; /**< Associated transaction */ + void *assoc_xact; /**< Associated GTP transaction */ + void *pfcp_xact; /**< Associated PFCP transaction */ + +#define OGS_GTP_MODIFY_TFT_UPDATE ((uint64_t)1<<0) +#define OGS_GTP_MODIFY_QOS_UPDATE ((uint64_t)1<<1) + uint64_t update_flags; } ogs_gtp_xact_t; int ogs_gtp_xact_init(ogs_timer_mgr_t *timer_mgr, int size); diff --git a/lib/ipfw/meson.build b/lib/ipfw/meson.build index 9a35c2e04..2aaf854e2 100644 --- a/lib/ipfw/meson.build +++ b/lib/ipfw/meson.build @@ -78,8 +78,10 @@ libipfw = library('ogsipfw', version : libogslib_version, c_args : ['-include', 'glue.h', ipfw_cc_flags], include_directories : libipfw_inc, + dependencies : libcore_dep, install : true) libipfw_dep = declare_dependency( link_with : libipfw, - include_directories : libinc) + include_directories : libinc, + dependencies : libcore_dep) diff --git a/lib/ipfw/ogs-ipfw.c b/lib/ipfw/ogs-ipfw.c index bde585b50..9049c29ea 100644 --- a/lib/ipfw/ogs-ipfw.c +++ b/lib/ipfw/ogs-ipfw.c @@ -24,18 +24,6 @@ #endif #endif -#ifndef OGS_ERROR -#define OGS_ERROR -1 -#endif - -#ifndef OGS_OK -#define OGS_OK 0 -#endif - -#ifndef OGS_IPV6_LEN -#define OGS_IPV6_LEN 16 -#endif - #include "ipfw2.h" #include "objs/include_e/netinet/ip_fw.h" @@ -46,7 +34,7 @@ void compile_rule(char *av[], uint32_t *rbuf, int *rbufsize, void *tstate); -int ogs_ipfw_compile_rule(ogs_ipfw_rule_t *ipfw_rule, char *description) +int ogs_ipfw_compile_rule(ogs_ipfw_rule_t *ipfw_rule, char *flow_description) { ogs_ipfw_rule_t zero_rule; char *token, *dir; @@ -61,10 +49,10 @@ int ogs_ipfw_compile_rule(ogs_ipfw_rule_t *ipfw_rule, char *description) int l; ipfw_insn *cmd; - if (!ipfw_rule) { - fprintf(stderr, "ipfw_rule is NULL\n"); - return OGS_ERROR; - } + char *description = NULL; + + ogs_assert(ipfw_rule); + ogs_assert(flow_description); rbufsize = sizeof(rulebuf); memset(rulebuf, 0, rbufsize); @@ -72,29 +60,31 @@ int ogs_ipfw_compile_rule(ogs_ipfw_rule_t *ipfw_rule, char *description) av[0] = NULL; /* ACTION */ - if (!description) { /* FIXME : OLD gcc generates uninitialized warning */ - fprintf(stderr, "description is NULL\n"); - return OGS_ERROR; - } - token = strtok_r(description, " ", &saveptr); + + description = ogs_strdup(flow_description); + ogs_assert(description); + + token = ogs_strtok_r(description, " ", &saveptr); if (strcmp(token, "permit") != 0) { - fprintf(stderr, "Not begins with reserved keyword : 'permit'"); + ogs_error("Not begins with reserved keyword : 'permit'"); + ogs_free(description); return OGS_ERROR; } av[1] = token; /* Save DIRECTION */ - dir = token = strtok_r(NULL, " ", &saveptr); + dir = token = ogs_strtok_r(NULL, " ", &saveptr); if (strcmp(token, "out") != 0) { - fprintf(stderr, "Not begins with reserved keyword : 'permit out'"); + ogs_error("Not begins with reserved keyword : 'permit out'"); + ogs_free(description); return OGS_ERROR; } /* ADDR */ - token = strtok_r(NULL, " ", &saveptr); + token = ogs_strtok_r(NULL, " ", &saveptr); while (token != NULL) { av[i++] = token; - token = strtok_r(NULL, " ", &saveptr); + token = ogs_strtok_r(NULL, " ", &saveptr); } /* Add DIRECTION */ @@ -168,9 +158,177 @@ int ogs_ipfw_compile_rule(ogs_ipfw_rule_t *ipfw_rule, char *description) memset(&zero_rule, 0, sizeof(ogs_ipfw_rule_t)); if (memcmp(ipfw_rule, &zero_rule, sizeof(ogs_ipfw_rule_t)) == 0) { - fprintf(stderr, "Cannot find Flow-Description"); + ogs_error("Cannot find Flow-Description"); + ogs_free(description); return OGS_ERROR; } + ogs_free(description); return OGS_OK; } + +char *ogs_ipfw_encode_flow_description(ogs_ipfw_rule_t *ipfw_rule) +{ + char flow_description[OGS_HUGE_LEN]; + char *p, *last; + char buf[OGS_ADDRSTRLEN]; + ogs_sockaddr_t sa; + int prefixlen = 0; + + p = flow_description; + last = flow_description + OGS_HUGE_LEN; + + ogs_assert(ipfw_rule); + p = ogs_slprintf(p, last, "permit out"); + + if (ipfw_rule->proto) { + p = ogs_slprintf(p, last, " %d", ipfw_rule->proto); + } else { + p = ogs_slprintf(p, last, " ip"); + } + +#define IPV4_BITLEN (OGS_IPV4_LEN * 8) +#define IPV6_BITLEN (OGS_IPV6_LEN * 8) + + p = ogs_slprintf(p, last, " from"); + memset(&sa, 0, sizeof(sa)); + + if (ipfw_rule->ipv4_local) { + sa.ogs_sa_family = AF_INET; + memcpy(&sa.sin.sin_addr, + ipfw_rule->ip.local.addr, sizeof(struct in_addr)); + + OGS_ADDR(&sa, buf); + prefixlen = contigmask( + (uint8_t *)ipfw_rule->ip.local.mask, IPV4_BITLEN); + + if (prefixlen < 0) { + ogs_error("Invalid mask[%x:%x:%x:%x]", + ipfw_rule->ip.local.mask[0], + ipfw_rule->ip.local.mask[1], + ipfw_rule->ip.local.mask[2], + ipfw_rule->ip.local.mask[3]); + return NULL; + } else if (prefixlen == 0) { + p = ogs_slprintf(p, last, " any"); + } else if (prefixlen > 0 && prefixlen < IPV4_BITLEN) { + p = ogs_slprintf(p, last, " %s/%d", buf, prefixlen); + } else if (prefixlen == IPV4_BITLEN) { + p = ogs_slprintf(p, last, " %s", buf); + } else { + ogs_fatal("Invalid prefixlen[%d]", prefixlen); + ogs_assert_if_reached(); + } + + } else if (ipfw_rule->ipv6_local) { + sa.ogs_sa_family = AF_INET6; + memcpy(&sa.sin6.sin6_addr, + ipfw_rule->ip.local.addr, sizeof(struct in6_addr)); + + OGS_ADDR(&sa, buf); + prefixlen = contigmask( + (uint8_t *)ipfw_rule->ip.local.mask, IPV6_BITLEN); + + if (prefixlen < 0) { + ogs_error("Invalid mask[%x:%x:%x:%x]", + ipfw_rule->ip.local.mask[0], + ipfw_rule->ip.local.mask[1], + ipfw_rule->ip.local.mask[2], + ipfw_rule->ip.local.mask[3]); + return NULL; + } else if (prefixlen == 0) { + p = ogs_slprintf(p, last, " any"); + } else if (prefixlen > 0 && prefixlen < IPV6_BITLEN) { + p = ogs_slprintf(p, last, " %s/%d", buf, prefixlen); + } else if (prefixlen == IPV6_BITLEN) { + p = ogs_slprintf(p, last, " %s", buf); + } else { + ogs_fatal("Invalid prefixlen[%d]", prefixlen); + ogs_assert_if_reached(); + } + } else + p = ogs_slprintf(p, last, " any"); + + if (ipfw_rule->port.local.low == ipfw_rule->port.local.high) { + if (ipfw_rule->port.local.low == 0) { + /* Nothing */ + } else { + p = ogs_slprintf(p, last, " %d", ipfw_rule->port.local.low); + } + } else { + p = ogs_slprintf(p, last, " %d-%d", + ipfw_rule->port.local.low, ipfw_rule->port.local.high); + } + + p = ogs_slprintf(p, last, " to"); + memset(&sa, 0, sizeof(sa)); + + if (ipfw_rule->ipv4_remote) { + sa.ogs_sa_family = AF_INET; + memcpy(&sa.sin.sin_addr, + ipfw_rule->ip.remote.addr, sizeof(struct in_addr)); + + OGS_ADDR(&sa, buf); + prefixlen = contigmask( + (uint8_t *)ipfw_rule->ip.remote.mask, IPV4_BITLEN); + + if (prefixlen < 0) { + ogs_error("Invalid mask[%x:%x:%x:%x]", + ipfw_rule->ip.remote.mask[0], + ipfw_rule->ip.remote.mask[1], + ipfw_rule->ip.remote.mask[2], + ipfw_rule->ip.remote.mask[3]); + return NULL; + } else if (prefixlen == 0) { + p = ogs_slprintf(p, last, " any"); + } else if (prefixlen > 0 && prefixlen < IPV4_BITLEN) { + p = ogs_slprintf(p, last, " %s/%d", buf, prefixlen); + } else if (prefixlen == IPV4_BITLEN) { + p = ogs_slprintf(p, last, " %s", buf); + } else { + ogs_fatal("Invalid prefixlen[%d]", prefixlen); + ogs_assert_if_reached(); + } + + } else if (ipfw_rule->ipv6_remote) { + sa.ogs_sa_family = AF_INET6; + memcpy(&sa.sin6.sin6_addr, + ipfw_rule->ip.remote.addr, sizeof(struct in6_addr)); + + OGS_ADDR(&sa, buf); + prefixlen = contigmask( + (uint8_t *)ipfw_rule->ip.remote.mask, IPV6_BITLEN); + + if (prefixlen < 0) { + ogs_error("Invalid mask[%x:%x:%x:%x]", + ipfw_rule->ip.remote.mask[0], + ipfw_rule->ip.remote.mask[1], + ipfw_rule->ip.remote.mask[2], + ipfw_rule->ip.remote.mask[3]); + return NULL; + } else if (prefixlen == 0) { + p = ogs_slprintf(p, last, " any"); + } else if (prefixlen > 0 && prefixlen < IPV6_BITLEN) { + p = ogs_slprintf(p, last, " %s/%d", buf, prefixlen); + } else if (prefixlen == IPV6_BITLEN) { + p = ogs_slprintf(p, last, " %s", buf); + } else { + ogs_fatal("Invalid prefixlen[%d]", prefixlen); + ogs_assert_if_reached(); + } + } else + p = ogs_slprintf(p, last, " any"); + + if (ipfw_rule->port.remote.low == ipfw_rule->port.remote.high) { + if (ipfw_rule->port.remote.low == 0) { + /* Nothing */ + } else { + p = ogs_slprintf(p, last, " %d", ipfw_rule->port.remote.low); + } + } else { + p = ogs_slprintf(p, last, " %d-%d", + ipfw_rule->port.remote.low, ipfw_rule->port.remote.high); + } + + return ogs_strdup(flow_description); +} diff --git a/lib/ipfw/ogs-ipfw.h b/lib/ipfw/ogs-ipfw.h index ce75b783b..145d9cca7 100644 --- a/lib/ipfw/ogs-ipfw.h +++ b/lib/ipfw/ogs-ipfw.h @@ -24,6 +24,8 @@ extern "C" { #endif +#include "ogs-core.h" + typedef struct ogs_ipfw_rule_s { uint8_t proto; @@ -59,7 +61,8 @@ typedef struct ogs_ipfw_rule_s { uint32_t sdf_filter_id; } ogs_ipfw_rule_t; -int ogs_ipfw_compile_rule(ogs_ipfw_rule_t *ipfw_rule, char *description); +int ogs_ipfw_compile_rule(ogs_ipfw_rule_t *ipfw_rule, char *flow_description); +char *ogs_ipfw_encode_flow_description(ogs_ipfw_rule_t *ipfw_rule); #ifdef __cplusplus } diff --git a/lib/meson.build b/lib/meson.build index a34e913b8..98e0c4a93 100644 --- a/lib/meson.build +++ b/lib/meson.build @@ -17,8 +17,8 @@ libinc = include_directories('.') -subdir('ipfw') subdir('core') +subdir('ipfw') subdir('crypt') subdir('sctp') subdir('dbi') diff --git a/lib/pfcp/build.c b/lib/pfcp/build.c new file mode 100644 index 000000000..b4c3740ea --- /dev/null +++ b/lib/pfcp/build.c @@ -0,0 +1,660 @@ +/* + * 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 "ogs-pfcp.h" + +ogs_pkbuf_t *ogs_pfcp_build_heartbeat_request(uint8_t type) +{ + ogs_pfcp_message_t pfcp_message; + ogs_pfcp_heartbeat_request_t *req = NULL; + + ogs_debug("Heartbeat Request"); + + req = &pfcp_message.pfcp_heartbeat_request; + memset(&pfcp_message, 0, sizeof(ogs_pfcp_message_t)); + + req->recovery_time_stamp.presence = 1; + req->recovery_time_stamp.u32 = ogs_pfcp_self()->pfcp_started; + + pfcp_message.h.type = type; + return ogs_pfcp_build_msg(&pfcp_message); +} + +ogs_pkbuf_t *ogs_pfcp_build_heartbeat_response(uint8_t type) +{ + ogs_pfcp_message_t pfcp_message; + ogs_pfcp_heartbeat_response_t *rsp = NULL; + + ogs_debug("Heartbeat Response"); + + rsp = &pfcp_message.pfcp_heartbeat_response; + memset(&pfcp_message, 0, sizeof(ogs_pfcp_message_t)); + + rsp->recovery_time_stamp.presence = 1; + rsp->recovery_time_stamp.u32 = ogs_pfcp_self()->pfcp_started; + + pfcp_message.h.type = type; + return ogs_pfcp_build_msg(&pfcp_message); +} + +ogs_pkbuf_t *ogs_pfcp_cp_build_association_setup_request(uint8_t type) +{ + ogs_pfcp_message_t pfcp_message; + ogs_pfcp_association_setup_request_t *req = NULL; + + ogs_pfcp_node_id_t node_id; + int node_id_len = 0; + + ogs_debug("Association Setup Request"); + + req = &pfcp_message.pfcp_association_setup_request; + memset(&pfcp_message, 0, sizeof(ogs_pfcp_message_t)); + + ogs_pfcp_sockaddr_to_node_id( + ogs_pfcp_self()->pfcp_addr, ogs_pfcp_self()->pfcp_addr6, + ogs_config()->parameter.prefer_ipv4, + &node_id, &node_id_len); + req->node_id.presence = 1; + req->node_id.data = &node_id; + req->node_id.len = node_id_len; + + req->recovery_time_stamp.presence = 1; + req->recovery_time_stamp.u32 = ogs_pfcp_self()->pfcp_started; + + req->cp_function_features.presence = 1; + req->cp_function_features.u8 = ogs_pfcp_self()->cp_function_features.octet5; + + pfcp_message.h.type = type; + return ogs_pfcp_build_msg(&pfcp_message); +} + +ogs_pkbuf_t *ogs_pfcp_cp_build_association_setup_response(uint8_t type, + uint8_t cause) +{ + ogs_pfcp_message_t pfcp_message; + ogs_pfcp_association_setup_response_t *rsp = NULL; + + ogs_pfcp_node_id_t node_id; + int node_id_len = 0; + + ogs_debug("Association Setup Response"); + + rsp = &pfcp_message.pfcp_association_setup_response; + memset(&pfcp_message, 0, sizeof(ogs_pfcp_message_t)); + + ogs_pfcp_sockaddr_to_node_id( + ogs_pfcp_self()->pfcp_addr, ogs_pfcp_self()->pfcp_addr6, + ogs_config()->parameter.prefer_ipv4, + &node_id, &node_id_len); + rsp->node_id.presence = 1; + rsp->node_id.data = &node_id; + rsp->node_id.len = node_id_len; + + rsp->cause.presence = 1; + rsp->cause.u8 = cause; + + rsp->recovery_time_stamp.presence = 1; + rsp->recovery_time_stamp.u32 = ogs_pfcp_self()->pfcp_started; + + rsp->cp_function_features.presence = 1; + rsp->cp_function_features.u8 = ogs_pfcp_self()->cp_function_features.octet5; + + pfcp_message.h.type = type; + return ogs_pfcp_build_msg(&pfcp_message); +} + +ogs_pkbuf_t *ogs_pfcp_up_build_association_setup_request(uint8_t type) +{ + ogs_pfcp_message_t pfcp_message; + ogs_pfcp_association_setup_request_t *req = NULL; + + ogs_pfcp_node_id_t node_id; + int node_id_len = 0; + + ogs_pfcp_gtpu_resource_t *resource = NULL; + char infobuf[OGS_MAX_NUM_OF_GTPU_RESOURCE] + [OGS_PFCP_MAX_USER_PLANE_IP_RESOURCE_INFO_LEN]; + int i = 0; + + ogs_debug("Association Setup Request"); + + req = &pfcp_message.pfcp_association_setup_request; + memset(&pfcp_message, 0, sizeof(ogs_pfcp_message_t)); + + ogs_pfcp_sockaddr_to_node_id( + ogs_pfcp_self()->pfcp_addr, ogs_pfcp_self()->pfcp_addr6, + ogs_config()->parameter.prefer_ipv4, + &node_id, &node_id_len); + req->node_id.presence = 1; + req->node_id.data = &node_id; + req->node_id.len = node_id_len; + + req->recovery_time_stamp.presence = 1; + req->recovery_time_stamp.u32 = ogs_pfcp_self()->pfcp_started; + + ogs_assert(ogs_pfcp_self()->up_function_features_len); + req->up_function_features.presence = 1; + req->up_function_features.data = &ogs_pfcp_self()->up_function_features; + req->up_function_features.len = ogs_pfcp_self()->up_function_features_len; + + i = 0; + ogs_list_for_each(&ogs_pfcp_self()->gtpu_resource_list, resource) { + ogs_assert(i < OGS_MAX_NUM_OF_GTPU_RESOURCE); + ogs_pfcp_tlv_user_plane_ip_resource_information_t *message = + &req->user_plane_ip_resource_information[i]; + ogs_assert(message); + + message->presence = 1; + ogs_pfcp_build_user_plane_ip_resource_info( + message, &resource->info, infobuf[i], + OGS_PFCP_MAX_USER_PLANE_IP_RESOURCE_INFO_LEN); + i++; + } + + pfcp_message.h.type = type; + return ogs_pfcp_build_msg(&pfcp_message); +} + +ogs_pkbuf_t *ogs_pfcp_up_build_association_setup_response(uint8_t type, + uint8_t cause) +{ + ogs_pfcp_message_t pfcp_message; + ogs_pfcp_association_setup_response_t *rsp = NULL; + + ogs_pfcp_node_id_t node_id; + int node_id_len = 0; + + ogs_pfcp_gtpu_resource_t *resource = NULL; + char infobuf[OGS_MAX_NUM_OF_GTPU_RESOURCE] + [OGS_PFCP_MAX_USER_PLANE_IP_RESOURCE_INFO_LEN]; + int i = 0; + + ogs_debug("Association Setup Response"); + + rsp = &pfcp_message.pfcp_association_setup_response; + memset(&pfcp_message, 0, sizeof(ogs_pfcp_message_t)); + + ogs_pfcp_sockaddr_to_node_id( + ogs_pfcp_self()->pfcp_addr, ogs_pfcp_self()->pfcp_addr6, + ogs_config()->parameter.prefer_ipv4, + &node_id, &node_id_len); + rsp->node_id.presence = 1; + rsp->node_id.data = &node_id; + rsp->node_id.len = node_id_len; + + rsp->cause.presence = 1; + rsp->cause.u8 = cause; + + rsp->recovery_time_stamp.presence = 1; + rsp->recovery_time_stamp.u32 = ogs_pfcp_self()->pfcp_started; + + ogs_assert(ogs_pfcp_self()->up_function_features_len); + rsp->up_function_features.presence = 1; + rsp->up_function_features.data = &ogs_pfcp_self()->up_function_features; + rsp->up_function_features.len = ogs_pfcp_self()->up_function_features_len; + + i = 0; + ogs_list_for_each(&ogs_pfcp_self()->gtpu_resource_list, resource) { + ogs_assert(i < OGS_MAX_NUM_OF_GTPU_RESOURCE); + ogs_pfcp_tlv_user_plane_ip_resource_information_t *message = + &rsp->user_plane_ip_resource_information[i]; + ogs_assert(message); + + message->presence = 1; + ogs_pfcp_build_user_plane_ip_resource_info( + message, &resource->info, infobuf[i], + OGS_PFCP_MAX_USER_PLANE_IP_RESOURCE_INFO_LEN); + i++; + } + + pfcp_message.h.type = type; + return ogs_pfcp_build_msg(&pfcp_message); +} + +static struct { + ogs_pfcp_f_teid_t f_teid; + char dnn[OGS_MAX_DNN_LEN]; + char *sdf_filter[OGS_MAX_NUM_OF_RULE]; +} pdrbuf[OGS_MAX_NUM_OF_PDR]; + +void ogs_pfcp_pdrbuf_init(void) +{ + memset(pdrbuf, 0, sizeof(pdrbuf)); +} + +void ogs_pfcp_pdrbuf_clear(void) +{ + int i, j; + for (i = 0; i < OGS_MAX_NUM_OF_PDR; i++) { + for (j = 0; j < OGS_MAX_NUM_OF_RULE; j++) { + if (pdrbuf[i].sdf_filter[j]) + ogs_free(pdrbuf[i].sdf_filter[j]); + } + } +} + +void ogs_pfcp_build_create_pdr( + ogs_pfcp_tlv_create_pdr_t *message, int i, ogs_pfcp_pdr_t *pdr) +{ + ogs_pfcp_far_t *far = NULL; + ogs_pfcp_sess_t *pfcp_sess = NULL; + ogs_pfcp_sdf_filter_t pfcp_sdf_filter[OGS_MAX_NUM_OF_RULE]; + int j = 0; + int len = 0; + + ogs_assert(message); + + ogs_assert(pdr); + pfcp_sess = pdr->sess; + ogs_assert(pfcp_sess); + + far = pdr->far; + ogs_assert(far); + + message->presence = 1; + message->pdr_id.presence = 1; + message->pdr_id.u16 = pdr->id; + + if (pdr->precedence) { /* No precedence in Sxa */ + message->precedence.presence = 1; + message->precedence.u32 = pdr->precedence; + } + + message->pdi.presence = 1; + message->pdi.source_interface.presence = 1; + message->pdi.source_interface.u8 = pdr->src_if; + + if (pdr->apn) { + message->pdi.network_instance.presence = 1; + message->pdi.network_instance.len = ogs_fqdn_build( + pdrbuf[i].dnn, pdr->apn, strlen(pdr->apn)); + message->pdi.network_instance.data = pdrbuf[i].dnn; + } + + memset(pfcp_sdf_filter, 0, sizeof(pfcp_sdf_filter)); + for (j = 0; j < pdr->num_of_flow; j++) { + pfcp_sdf_filter[j].fd = 1; + pfcp_sdf_filter[j].flow_description_len = + strlen(pdr->flow_description[j]); + pfcp_sdf_filter[j].flow_description = pdr->flow_description[j]; + len = sizeof(ogs_pfcp_sdf_filter_t) + + pfcp_sdf_filter[j].flow_description_len; + + message->pdi.sdf_filter[j].presence = 1; + pdrbuf[i].sdf_filter[j] = ogs_calloc(1, len); + ogs_pfcp_build_sdf_filter(&message->pdi.sdf_filter[j], + &pfcp_sdf_filter[j], pdrbuf[i].sdf_filter[j], len); + } + + if (pdr->ue_ip_addr_len) { + message->pdi.ue_ip_address.presence = 1; + message->pdi.ue_ip_address.data = &pdr->ue_ip_addr; + message->pdi.ue_ip_address.len = pdr->ue_ip_addr_len; + } + + if (pdr->f_teid_len) { + memcpy(&pdrbuf[i].f_teid, &pdr->f_teid, pdr->f_teid_len); + pdrbuf[i].f_teid.teid = htobe32(pdr->f_teid.teid); + + message->pdi.local_f_teid.presence = 1; + message->pdi.local_f_teid.data = &pdrbuf[i].f_teid; + message->pdi.local_f_teid.len = pdr->f_teid_len; + } + + if (pdr->qfi) { + message->pdi.qfi.presence = 1; + message->pdi.qfi.u8 = pdr->qfi; + } + + if (pdr->outer_header_removal_len) { + message->outer_header_removal.presence = 1; + message->outer_header_removal.data = &pdr->outer_header_removal; + message->outer_header_removal.len = pdr->outer_header_removal_len; + } + + if (pdr->far) { + message->far_id.presence = 1; + message->far_id.u32 = pdr->far->id; + } + + if (pdr->urr) { + message->urr_id.presence = 1; + message->urr_id.u32 = pdr->urr->id; + } + + if (pdr->qer) { + message->qer_id.presence = 1; + message->qer_id.u32 = pdr->qer->id; + } +} + +void ogs_pfcp_build_update_pdr( + ogs_pfcp_tlv_update_pdr_t *message, int i, ogs_pfcp_pdr_t *pdr) +{ + ogs_pfcp_sdf_filter_t pfcp_sdf_filter[OGS_MAX_NUM_OF_RULE]; + int j = 0; + int len = 0; + + ogs_assert(message); + ogs_assert(pdr); + + message->presence = 1; + message->pdr_id.presence = 1; + message->pdr_id.u16 = pdr->id; + + message->pdi.presence = 1; + message->pdi.source_interface.presence = 1; + message->pdi.source_interface.u8 = pdr->src_if; + + if (pdr->apn) { + message->pdi.network_instance.presence = 1; + message->pdi.network_instance.len = ogs_fqdn_build( + pdrbuf[i].dnn, pdr->apn, strlen(pdr->apn)); + message->pdi.network_instance.data = pdrbuf[i].dnn; + } + + memset(pfcp_sdf_filter, 0, sizeof(pfcp_sdf_filter)); + for (j = 0; j < pdr->num_of_flow; j++) { + pfcp_sdf_filter[j].fd = 1; + pfcp_sdf_filter[j].flow_description_len = + strlen(pdr->flow_description[j]); + pfcp_sdf_filter[j].flow_description = pdr->flow_description[j]; + len = sizeof(ogs_pfcp_sdf_filter_t) + + pfcp_sdf_filter[j].flow_description_len; + + message->pdi.sdf_filter[j].presence = 1; + pdrbuf[i].sdf_filter[j] = ogs_calloc(1, len); + ogs_pfcp_build_sdf_filter(&message->pdi.sdf_filter[j], + &pfcp_sdf_filter[j], pdrbuf[i].sdf_filter[j], len); + } + + if (pdr->ue_ip_addr_len) { + message->pdi.ue_ip_address.presence = 1; + message->pdi.ue_ip_address.data = &pdr->ue_ip_addr; + message->pdi.ue_ip_address.len = pdr->ue_ip_addr_len; + } + + if (pdr->f_teid_len) { + memcpy(&pdrbuf[i].f_teid, &pdr->f_teid, pdr->f_teid_len); + pdrbuf[i].f_teid.teid = htobe32(pdr->f_teid.teid); + + message->pdi.local_f_teid.presence = 1; + message->pdi.local_f_teid.data = &pdrbuf[i].f_teid; + message->pdi.local_f_teid.len = pdr->f_teid_len; + } + + if (pdr->qfi) { + message->pdi.qfi.presence = 1; + message->pdi.qfi.u8 = pdr->qfi; + } +} + +static struct { + ogs_pfcp_outer_header_creation_t outer_header_creation; +} farbuf[OGS_MAX_NUM_OF_FAR]; + +void ogs_pfcp_build_create_far( + ogs_pfcp_tlv_create_far_t *message, int i, ogs_pfcp_far_t *far) +{ + ogs_pfcp_sess_t *pfcp_sess = NULL; + + ogs_assert(message); + ogs_assert(far); + pfcp_sess = far->sess; + ogs_assert(pfcp_sess); + + message->presence = 1; + message->far_id.presence = 1; + message->far_id.u32 = far->id; + + message->apply_action.presence = 1; + message->apply_action.u8 = far->apply_action; + + message->forwarding_parameters.presence = 1; + message->forwarding_parameters.destination_interface.presence = 1; + message->forwarding_parameters.destination_interface.u8 = + far->dst_if; + + if (far->outer_header_creation_len) { + memcpy(&farbuf[i].outer_header_creation, + &far->outer_header_creation, far->outer_header_creation_len); + farbuf[i].outer_header_creation.teid = + htobe32(far->outer_header_creation.teid); + + message->forwarding_parameters.outer_header_creation.presence = 1; + message->forwarding_parameters.outer_header_creation.data = + &farbuf[i].outer_header_creation; + message->forwarding_parameters.outer_header_creation.len = + far->outer_header_creation_len; + } +} + +void ogs_pfcp_build_update_far_deactivate( + ogs_pfcp_tlv_update_far_t *message, int i, ogs_pfcp_far_t *far) +{ + ogs_assert(message); + ogs_assert(far); + + message->presence = 1; + message->far_id.presence = 1; + message->far_id.u32 = far->id; + + far->apply_action = + OGS_PFCP_APPLY_ACTION_BUFF | OGS_PFCP_APPLY_ACTION_NOCP; + message->apply_action.presence = 1; + message->apply_action.u8 = far->apply_action; +} + +void ogs_pfcp_build_update_far_activate( + ogs_pfcp_tlv_update_far_t *message, int i, ogs_pfcp_far_t *far) +{ + ogs_assert(message); + ogs_assert(far); + + message->presence = 1; + message->far_id.presence = 1; + message->far_id.u32 = far->id; + + if (far->apply_action != OGS_PFCP_APPLY_ACTION_FORW) { + far->apply_action = OGS_PFCP_APPLY_ACTION_FORW; + + message->apply_action.presence = 1; + message->apply_action.u8 = far->apply_action; + } + + if (far->outer_header_creation_len || far->smreq_flags.value) { + message->update_forwarding_parameters.presence = 1; + + if (far->outer_header_creation_len) { + memcpy(&farbuf[i].outer_header_creation, + &far->outer_header_creation, far->outer_header_creation_len); + farbuf[i].outer_header_creation.teid = + htobe32(far->outer_header_creation.teid); + + message->update_forwarding_parameters. + outer_header_creation.presence = 1; + message->update_forwarding_parameters. + outer_header_creation.data = &farbuf[i].outer_header_creation; + message->update_forwarding_parameters. + outer_header_creation.len = far->outer_header_creation_len; + + } + + if (far->smreq_flags.value) { + message->update_forwarding_parameters.pfcpsmreq_flags.presence = 1; + message->update_forwarding_parameters.pfcpsmreq_flags.u8 = + far->smreq_flags.value; + } + } +} + +static struct { + char mbr[OGS_PFCP_BITRATE_LEN]; + char gbr[OGS_PFCP_BITRATE_LEN]; +} create_qer_buf[OGS_MAX_NUM_OF_QER], update_qer_buf[OGS_MAX_NUM_OF_QER]; + +void ogs_pfcp_build_create_qer( + ogs_pfcp_tlv_create_qer_t *message, int i, ogs_pfcp_qer_t *qer) +{ + ogs_assert(message); + ogs_assert(qer); + + message->presence = 1; + message->qer_id.presence = 1; + message->qer_id.u32 = qer->id; + + message->gate_status.presence = 1; + message->gate_status.u8 = qer->gate_status.value; + + if (qer->mbr.uplink || qer->mbr.downlink) { + message->maximum_bitrate.presence = 1; + ogs_pfcp_build_bitrate( + &message->maximum_bitrate, + &qer->mbr, create_qer_buf[i].mbr, OGS_PFCP_BITRATE_LEN); + } + if (qer->gbr.uplink || qer->gbr.downlink) { + message->guaranteed_bitrate.presence = 1; + ogs_pfcp_build_bitrate( + &message->guaranteed_bitrate, + &qer->gbr, create_qer_buf[i].gbr, OGS_PFCP_BITRATE_LEN); + } + + if (qer->qfi) { + message->qos_flow_identifier.presence = 1; + message->qos_flow_identifier.u8 = qer->qfi; + } +} + +void ogs_pfcp_build_update_qer( + ogs_pfcp_tlv_update_qer_t *message, int i, ogs_pfcp_qer_t *qer) +{ + ogs_assert(message); + ogs_assert(qer); + + message->presence = 1; + message->qer_id.presence = 1; + message->qer_id.u32 = qer->id; + + if (qer->mbr.uplink || qer->mbr.downlink) { + message->maximum_bitrate.presence = 1; + ogs_pfcp_build_bitrate( + &message->maximum_bitrate, + &qer->mbr, update_qer_buf[i].mbr, OGS_PFCP_BITRATE_LEN); + } + if (qer->gbr.uplink || qer->gbr.downlink) { + message->guaranteed_bitrate.presence = 1; + ogs_pfcp_build_bitrate( + &message->guaranteed_bitrate, + &qer->gbr, update_qer_buf[i].gbr, OGS_PFCP_BITRATE_LEN); + } +} + +void ogs_pfcp_build_create_urr( + ogs_pfcp_tlv_create_urr_t *message, int i, ogs_pfcp_urr_t *urr) +{ + ogs_assert(message); + ogs_assert(urr); + + message->presence = 1; + message->urr_id.presence = 1; + message->urr_id.u32 = urr->id; +} + +ogs_pkbuf_t *ogs_pfcp_build_session_report_request( + uint8_t type, ogs_pfcp_user_plane_report_t *report) +{ + ogs_pfcp_message_t pfcp_message; + ogs_pfcp_session_report_request_t *req = NULL; + + ogs_assert(report); + + ogs_debug("PFCP session report request"); + + req = &pfcp_message.pfcp_session_report_request; + memset(&pfcp_message, 0, sizeof(ogs_pfcp_message_t)); + + req->report_type.presence = 1; + req->report_type.u8 = report->type.value; + + if (report->downlink_data.pdr_id) { + ogs_pfcp_downlink_data_service_information_t info; + int info_len = 0; + + req->downlink_data_report.presence = 1; + req->downlink_data_report.pdr_id.presence = 1; + req->downlink_data_report.pdr_id.u16 = report->downlink_data.pdr_id; + + memset(&info, 0, sizeof(info)); + if (report->downlink_data.qfi && + report->downlink_data.paging_policy_indication_value) { + + info_len = 3; + + info.qfii = 1; + info.qfi = report->downlink_data.qfi; + info.ppi = 1; + info.paging_policy_indication_value = + report->downlink_data.paging_policy_indication_value; + + } else if (report->downlink_data.qfi) { + + info_len = 2; + + info.qfii = 1; + info.qfi = report->downlink_data.qfi; + } else if (report->downlink_data.paging_policy_indication_value) { + + info_len = 2; + + info.ppi = 1; + info.paging_policy_indication_value = + report->downlink_data.paging_policy_indication_value; + } + + if (info_len) { + req->downlink_data_report. + downlink_data_service_information.presence = 1; + req->downlink_data_report. + downlink_data_service_information.data = &info; + req->downlink_data_report. + downlink_data_service_information.len = info_len; + } + } + + pfcp_message.h.type = type; + return ogs_pfcp_build_msg(&pfcp_message); +} + +ogs_pkbuf_t *ogs_pfcp_build_session_report_response( + uint8_t type, uint8_t cause) +{ + ogs_pfcp_message_t pfcp_message; + ogs_pfcp_session_report_response_t *rsp = NULL; + + ogs_debug("PFCP session report response"); + + rsp = &pfcp_message.pfcp_session_report_response; + memset(&pfcp_message, 0, sizeof(ogs_pfcp_message_t)); + + rsp->cause.presence = 1; + rsp->cause.u8 = cause; + + pfcp_message.h.type = type; + return ogs_pfcp_build_msg(&pfcp_message); +} diff --git a/lib/pfcp/build.h b/lib/pfcp/build.h new file mode 100644 index 000000000..84980fe43 --- /dev/null +++ b/lib/pfcp/build.h @@ -0,0 +1,70 @@ +/* + * 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 OGS_PFCP_BUILD_H +#define OGS_PFCP_BUILD_H + +#ifdef __cplusplus +extern "C" { +#endif + +ogs_pkbuf_t *ogs_pfcp_build_heartbeat_request(uint8_t type); +ogs_pkbuf_t *ogs_pfcp_build_heartbeat_response(uint8_t type); + +ogs_pkbuf_t *ogs_pfcp_cp_build_association_setup_request(uint8_t type); +ogs_pkbuf_t *ogs_pfcp_cp_build_association_setup_response(uint8_t type, + uint8_t cause); + +ogs_pkbuf_t *ogs_pfcp_up_build_association_setup_request(uint8_t type); +ogs_pkbuf_t *ogs_pfcp_up_build_association_setup_response(uint8_t type, + uint8_t cause); + +void ogs_pfcp_pdrbuf_init(void); +void ogs_pfcp_pdrbuf_clear(void); + +void ogs_pfcp_build_create_pdr( + ogs_pfcp_tlv_create_pdr_t *message, int i, ogs_pfcp_pdr_t *pdr); +void ogs_pfcp_build_update_pdr( + ogs_pfcp_tlv_update_pdr_t *message, int i, ogs_pfcp_pdr_t *pdr); + +void ogs_pfcp_build_create_far( + ogs_pfcp_tlv_create_far_t *message, int i, ogs_pfcp_far_t *far); +void ogs_pfcp_build_update_far_deactivate( + ogs_pfcp_tlv_update_far_t *message, int i, ogs_pfcp_far_t *far); +void ogs_pfcp_build_update_far_activate( + ogs_pfcp_tlv_update_far_t *message, int i, ogs_pfcp_far_t *far); + +void ogs_pfcp_build_create_qer( + ogs_pfcp_tlv_create_qer_t *message, int i, ogs_pfcp_qer_t *qer); +void ogs_pfcp_build_update_qer( + ogs_pfcp_tlv_update_qer_t *message, int i, ogs_pfcp_qer_t *qer); + +void ogs_pfcp_build_create_urr( + ogs_pfcp_tlv_create_urr_t *message, int i, ogs_pfcp_urr_t *urr); + +ogs_pkbuf_t *ogs_pfcp_build_session_report_request( + uint8_t type, ogs_pfcp_user_plane_report_t *report); +ogs_pkbuf_t *ogs_pfcp_build_session_report_response( + uint8_t type, uint8_t cause); + +#ifdef __cplusplus +} +#endif + +#endif /* OGS_PFCP_BUILD_H */ diff --git a/lib/pfcp/context.c b/lib/pfcp/context.c index 8cbf0340e..71b52a5ef 100644 --- a/lib/pfcp/context.c +++ b/lib/pfcp/context.c @@ -34,6 +34,7 @@ static OGS_POOL(ogs_pfcp_bar_pool, ogs_pfcp_bar_t); static OGS_POOL(ogs_pfcp_dev_pool, ogs_pfcp_dev_t); static OGS_POOL(ogs_pfcp_subnet_pool, ogs_pfcp_subnet_t); +static OGS_POOL(ogs_pfcp_rule_pool, ogs_pfcp_rule_t); static int context_initialized = 0; @@ -66,7 +67,8 @@ void ogs_pfcp_context_init(int num_of_gtpu_resource) ogs_pool_init(&ogs_pfcp_node_pool, ogs_config()->pool.pfcp); ogs_pool_init(&ogs_pfcp_gtpu_resource_pool, num_of_gtpu_resource); - ogs_list_init(&self.n4_list); + ogs_list_init(&self.peer_list); + ogs_list_init(&self.gtpu_resource_list); ogs_pool_init(&ogs_pfcp_sess_pool, ogs_config()->pool.sess); @@ -80,6 +82,8 @@ void ogs_pfcp_context_init(int num_of_gtpu_resource) ogs_config()->pool.sess * OGS_MAX_NUM_OF_QER); ogs_pool_init(&ogs_pfcp_bar_pool, ogs_config()->pool.sess * OGS_MAX_NUM_OF_BAR); + ogs_pool_init(&ogs_pfcp_rule_pool, + ogs_config()->pool.sess * OGS_MAX_NUM_OF_RULE); ogs_list_init(&self.dev_list); ogs_pool_init(&ogs_pfcp_dev_pool, OGS_MAX_NUM_OF_DEV); @@ -103,6 +107,7 @@ void ogs_pfcp_context_final(void) ogs_pool_final(&ogs_pfcp_dev_pool); ogs_pool_final(&ogs_pfcp_subnet_pool); + ogs_pool_final(&ogs_pfcp_rule_pool); ogs_pool_final(&ogs_pfcp_sess_pool); ogs_pool_final(&ogs_pfcp_pdr_pool); @@ -111,7 +116,8 @@ void ogs_pfcp_context_final(void) ogs_pool_final(&ogs_pfcp_qer_pool); ogs_pool_final(&ogs_pfcp_bar_pool); - ogs_pfcp_node_remove_all(&ogs_pfcp_self()->n4_list); + ogs_pfcp_node_remove_all(&self.peer_list); + ogs_pfcp_gtpu_resource_remove_all(&self.gtpu_resource_list); ogs_pool_final(&ogs_pfcp_node_pool); ogs_pool_final(&ogs_pfcp_gtpu_resource_pool); @@ -139,11 +145,6 @@ static int ogs_pfcp_context_validation(const char *local) ogs_error("No %s.pfcp: in '%s'", local, ogs_config()->file); return OGS_ERROR; } - if (ogs_list_first(&self.subnet_list) == NULL) { - ogs_error("No %s.pdn: in '%s'", local, ogs_config()->file); - return OGS_ERROR; - } - return OGS_OK; } @@ -621,9 +622,11 @@ int ogs_pfcp_context_parse_config(const char *local, const char *remote) ogs_config()->parameter.no_ipv6, ogs_config()->parameter.prefer_ipv4); + if (addr == NULL) continue; + node = ogs_pfcp_node_new(addr); ogs_assert(node); - ogs_list_add(&self.n4_list, node); + ogs_list_add(&self.peer_list, node); node->num_of_tac = num_of_tac; if (num_of_tac != 0) @@ -876,19 +879,13 @@ static uint64_t pdr_hash_keygen(uint32_t teid, uint8_t qfi) void ogs_pfcp_pdr_hash_set(ogs_pfcp_pdr_t *pdr) { - ogs_pfcp_qer_t *qer = NULL; - uint8_t qfi = 0; - ogs_assert(pdr); - qer = pdr->qer; - if (qer && qer->qfi) qfi = qer->qfi; - if (pdr->hashkey) ogs_hash_set(ogs_pfcp_self()->pdr_hash, &pdr->hashkey, sizeof(pdr->hashkey), NULL); - pdr->hashkey = pdr_hash_keygen(pdr->f_teid.teid, qfi); + pdr->hashkey = pdr_hash_keygen(pdr->f_teid.teid, pdr->qfi); ogs_hash_set(ogs_pfcp_self()->pdr_hash, &pdr->hashkey, sizeof(pdr->hashkey), pdr); } @@ -961,9 +958,13 @@ void ogs_pfcp_pdr_remove(ogs_pfcp_pdr_t *pdr) ogs_list_remove(&pdr->sess->pdr_list, pdr); + ogs_pfcp_rule_remove_all(pdr); + if (pdr->hashkey) ogs_hash_set(ogs_pfcp_self()->pdr_hash, &pdr->hashkey, sizeof(pdr->hashkey), NULL); + if (pdr->dnn) + ogs_free(pdr->dnn); ogs_pool_free(&ogs_pfcp_pdr_pool, pdr); } @@ -1218,6 +1219,44 @@ void ogs_pfcp_bar_delete(ogs_pfcp_bar_t *bar) ogs_pool_free(&ogs_pfcp_bar_pool, bar); } +ogs_pfcp_rule_t *ogs_pfcp_rule_add(ogs_pfcp_pdr_t *pdr) +{ + ogs_pfcp_rule_t *rule = NULL; + + ogs_assert(pdr); + + ogs_pool_alloc(&ogs_pfcp_rule_pool, &rule); + ogs_assert(rule); + memset(rule, 0, sizeof *rule); + + rule->pdr = pdr; + ogs_list_add(&pdr->rule_list, rule); + + return rule; +} + +void ogs_pfcp_rule_remove(ogs_pfcp_rule_t *rule) +{ + ogs_pfcp_pdr_t *pdr = NULL; + + ogs_assert(rule); + pdr = rule->pdr; + ogs_assert(pdr); + + ogs_list_remove(&pdr->rule_list, rule); + ogs_pool_free(&ogs_pfcp_rule_pool, rule); +} + +void ogs_pfcp_rule_remove_all(ogs_pfcp_pdr_t *pdr) +{ + ogs_pfcp_rule_t *rule = NULL, *next_rule = NULL; + + ogs_assert(pdr); + + ogs_list_for_each_safe(&pdr->rule_list, next_rule, rule) + ogs_pfcp_rule_remove(rule); +} + int ogs_pfcp_ue_pool_generate(void) { int i, rv; diff --git a/lib/pfcp/context.h b/lib/pfcp/context.h index 129f23ab1..d5015f25d 100644 --- a/lib/pfcp/context.h +++ b/lib/pfcp/context.h @@ -46,7 +46,15 @@ typedef struct ogs_pfcp_context_s { uint32_t pfcp_started; /* UTC time when the PFCP entity started */ - ogs_list_t n4_list; /* PFCP Node List */ + /* CP Function Features */ + ogs_pfcp_cp_function_features_t cp_function_features; + /* UP Function Features */ + ogs_pfcp_up_function_features_t up_function_features; + int up_function_features_len; + + ogs_list_t gtpu_resource_list; /* UP IP Resource List */ + + ogs_list_t peer_list; /* PFCP Node List */ ogs_pfcp_node_t *node; /* Iterator for Peer round-robin */ ogs_list_t dev_list; /* Tun Device List */ @@ -121,12 +129,20 @@ typedef struct ogs_pfcp_pdr_s { ogs_pfcp_precedence_t precedence; ogs_pfcp_interface_t src_if; + union { + char *apn; + char *dnn; + }; + ogs_pfcp_ue_ip_addr_t ue_ip_addr; int ue_ip_addr_len; ogs_pfcp_f_teid_t f_teid; int f_teid_len; ogs_pfcp_outer_header_removal_t outer_header_removal; + int outer_header_removal_len; + + uint8_t qfi; ogs_pfcp_far_t *far; ogs_pfcp_urr_t *urr; @@ -135,6 +151,8 @@ typedef struct ogs_pfcp_pdr_s { int num_of_flow; char *flow_description[OGS_MAX_NUM_OF_RULE]; + ogs_list_t rule_list; /* Rule List */ + /* Related Context */ ogs_pfcp_sess_t *sess; } ogs_pfcp_pdr_t; @@ -148,6 +166,8 @@ typedef struct ogs_pfcp_far_s { ogs_pfcp_outer_header_creation_t outer_header_creation; int outer_header_creation_len; + ogs_pfcp_smreq_flags_t smreq_flags; + #define MAX_NUM_OF_PACKET_BUFFER 512 uint32_t num_of_buffered_packet; ogs_pkbuf_t* buffered_packet[MAX_NUM_OF_PACKET_BUFFER]; @@ -225,6 +245,15 @@ typedef struct ogs_pfcp_subnet_s { ogs_pfcp_dev_t *dev; /* Related Context */ } ogs_pfcp_subnet_t; +typedef struct ogs_pfcp_rule_s { + ogs_lnode_t lnode; + + ogs_ipfw_rule_t ipfw; + + /* Related Context */ + ogs_pfcp_pdr_t *pdr; +} ogs_pfcp_rule_t; + void ogs_pfcp_context_init(int num_of_gtpu_resource); void ogs_pfcp_context_final(void); ogs_pfcp_context_t *ogs_pfcp_self(void); @@ -301,6 +330,10 @@ void ogs_pfcp_qer_remove_all(ogs_pfcp_sess_t *sess); ogs_pfcp_bar_t *ogs_pfcp_bar_new(ogs_pfcp_sess_t *sess); void ogs_pfcp_bar_delete(ogs_pfcp_bar_t *bar); +ogs_pfcp_rule_t *ogs_pfcp_rule_add(ogs_pfcp_pdr_t *pdr); +void ogs_pfcp_rule_remove(ogs_pfcp_rule_t *rule); +void ogs_pfcp_rule_remove_all(ogs_pfcp_pdr_t *pdr); + int ogs_pfcp_ue_pool_generate(void); ogs_pfcp_ue_ip_t *ogs_pfcp_ue_ip_alloc( int family, const char *apn, uint8_t *addr); diff --git a/lib/pfcp/handler.c b/lib/pfcp/handler.c new file mode 100644 index 000000000..6936c534d --- /dev/null +++ b/lib/pfcp/handler.c @@ -0,0 +1,718 @@ +/* + * 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 "ogs-pfcp.h" + +void ogs_pfcp_handle_heartbeat_request( + ogs_pfcp_node_t *node, ogs_pfcp_xact_t *xact, + ogs_pfcp_heartbeat_request_t *req) +{ + ogs_assert(xact); + ogs_pfcp_send_heartbeat_response(xact); +} + +void ogs_pfcp_handle_heartbeat_response( + ogs_pfcp_node_t *node, ogs_pfcp_xact_t *xact, + ogs_pfcp_heartbeat_response_t *rsp) +{ + ogs_assert(xact); + ogs_pfcp_xact_commit(xact); + + ogs_timer_start(node->t_no_heartbeat, + ogs_config()->time.message.pfcp.no_heartbeat_duration); +} + +void ogs_pfcp_cp_handle_association_setup_request( + ogs_pfcp_node_t *node, ogs_pfcp_xact_t *xact, + ogs_pfcp_association_setup_request_t *req) +{ + int i; + + ogs_assert(xact); + ogs_assert(node); + ogs_assert(req); + + ogs_pfcp_cp_send_association_setup_response( + xact, OGS_PFCP_CAUSE_REQUEST_ACCEPTED); + + ogs_pfcp_gtpu_resource_remove_all(&node->gtpu_resource_list); + + for (i = 0; i < OGS_MAX_NUM_OF_GTPU_RESOURCE; i++) { + ogs_pfcp_tlv_user_plane_ip_resource_information_t *message = + &req->user_plane_ip_resource_information[i]; + ogs_pfcp_user_plane_ip_resource_info_t info; + + if (message->presence == 0) + break; + + ogs_pfcp_parse_user_plane_ip_resource_info(&info, message); + ogs_pfcp_gtpu_resource_add(&node->gtpu_resource_list, &info); + } +} + +void ogs_pfcp_cp_handle_association_setup_response( + ogs_pfcp_node_t *node, ogs_pfcp_xact_t *xact, + ogs_pfcp_association_setup_response_t *rsp) +{ + int i; + + ogs_assert(xact); + ogs_pfcp_xact_commit(xact); + + ogs_assert(node); + ogs_assert(rsp); + + ogs_pfcp_gtpu_resource_remove_all(&node->gtpu_resource_list); + + for (i = 0; i < OGS_MAX_NUM_OF_GTPU_RESOURCE; i++) { + ogs_pfcp_tlv_user_plane_ip_resource_information_t *message = + &rsp->user_plane_ip_resource_information[i]; + ogs_pfcp_user_plane_ip_resource_info_t info; + + if (message->presence == 0) + break; + + ogs_pfcp_parse_user_plane_ip_resource_info(&info, message); + ogs_pfcp_gtpu_resource_add(&node->gtpu_resource_list, &info); + } +} + +void ogs_pfcp_up_handle_association_setup_request( + ogs_pfcp_node_t *node, ogs_pfcp_xact_t *xact, + ogs_pfcp_association_setup_request_t *req) +{ + ogs_assert(xact); + ogs_pfcp_up_send_association_setup_response( + xact, OGS_PFCP_CAUSE_REQUEST_ACCEPTED); +} + +void ogs_pfcp_up_handle_association_setup_response( + ogs_pfcp_node_t *node, ogs_pfcp_xact_t *xact, + ogs_pfcp_association_setup_response_t *rsp) +{ + ogs_assert(xact); + ogs_pfcp_xact_commit(xact); +} + +void ogs_pfcp_up_handle_pdr( + ogs_pfcp_pdr_t *pdr, ogs_pkbuf_t *recvbuf, + ogs_pfcp_user_plane_report_t *report) +{ + ogs_pfcp_far_t *far = NULL; + ogs_pkbuf_t *sendbuf = NULL; + bool buffering; + + ogs_assert(recvbuf); + ogs_assert(pdr); + ogs_assert(report); + + far = pdr->far; + ogs_assert(far); + + memset(report, 0, sizeof(*report)); + + sendbuf = ogs_pkbuf_copy(recvbuf); + ogs_assert(sendbuf); + + buffering = false; + + if (!far->gnode) { + buffering = true; + + } else { + if (far->apply_action & OGS_PFCP_APPLY_ACTION_FORW) { + + /* Forward packet */ + ogs_pfcp_send_g_pdu(pdr, sendbuf); + + } else if (far->apply_action & OGS_PFCP_APPLY_ACTION_BUFF) { + + buffering = true; + + } else { + ogs_error("Not implemented"); + ogs_pkbuf_free(sendbuf); + } + } + + if (buffering == true) { + + if (far->num_of_buffered_packet == 0) { + /* Only the first time a packet is buffered, + * it reports downlink notifications. */ + report->type.downlink_data_report = 1; + } + + if (far->num_of_buffered_packet < MAX_NUM_OF_PACKET_BUFFER) { + far->buffered_packet[far->num_of_buffered_packet++] = sendbuf; + } + } +} + +ogs_pfcp_pdr_t *ogs_pfcp_handle_create_pdr(ogs_pfcp_sess_t *sess, + ogs_pfcp_tlv_create_pdr_t *message, + uint8_t *cause_value, uint8_t *offending_ie_value) +{ + ogs_pfcp_pdr_t *pdr = NULL; + ogs_pfcp_far_t *far = NULL; + ogs_pfcp_qer_t *qer = NULL; + int i, len; + int rv; + + ogs_assert(sess); + ogs_assert(message); + + if (message->presence == 0) + return NULL; + + if (message->pdr_id.presence == 0) { + ogs_error("No PDR-ID"); + *cause_value = OGS_PFCP_CAUSE_MANDATORY_IE_MISSING; + *offending_ie_value = OGS_PFCP_PDR_ID_TYPE; + return NULL; + } + + pdr = ogs_pfcp_pdr_find_or_add(sess, message->pdr_id.u16); + ogs_assert(pdr); + + if (message->precedence.presence) { + ogs_pfcp_pdr_reorder_by_precedence(pdr, message->precedence.u32); + pdr->precedence = message->precedence.u32; + } + + if (message->pdi.presence == 0) { + ogs_error("No PDI in PDR"); + *cause_value = OGS_PFCP_CAUSE_MANDATORY_IE_MISSING; + *offending_ie_value = OGS_PFCP_PDI_TYPE; + return NULL; + } + + if (message->pdi.source_interface.presence == 0) { + ogs_error("No Source Interface in PDI"); + *cause_value = OGS_PFCP_CAUSE_MANDATORY_IE_MISSING; + *offending_ie_value = OGS_PFCP_SOURCE_INTERFACE_TYPE; + return NULL; + } + + pdr->src_if = message->pdi.source_interface.u8; + + ogs_pfcp_rule_remove_all(pdr); + + for (i = 0; i < OGS_MAX_NUM_OF_RULE; i++) { + ogs_pfcp_sdf_filter_t sdf_filter_in_message; + if (message->pdi.sdf_filter[i].presence == 0) + break; + + len = ogs_pfcp_parse_sdf_filter( + &sdf_filter_in_message, &message->pdi.sdf_filter[i]); + ogs_assert(message->pdi.sdf_filter[i].len == len); + if (sdf_filter_in_message.fd) { + ogs_pfcp_rule_t *rule = NULL; + char *flow_description = NULL; + + flow_description = ogs_malloc( + sdf_filter_in_message.flow_description_len+1); + ogs_cpystrn(flow_description, + sdf_filter_in_message.flow_description, + sdf_filter_in_message.flow_description_len+1); + + rule = ogs_pfcp_rule_add(pdr); + ogs_assert(rule); + rv = ogs_ipfw_compile_rule(&rule->ipfw, flow_description); + ogs_assert(rv == OGS_OK); + + ogs_free(flow_description); + } + } + + if (message->pdi.network_instance.presence) { + char dnn[OGS_MAX_DNN_LEN]; + + ogs_fqdn_parse(dnn, + message->pdi.network_instance.data, + message->pdi.network_instance.len); + + if (pdr->dnn) + ogs_free(pdr->dnn); + pdr->dnn = ogs_strdup(dnn); + } + + if (message->pdi.local_f_teid.presence) { + pdr->f_teid_len = message->pdi.local_f_teid.len; + memcpy(&pdr->f_teid, message->pdi.local_f_teid.data, pdr->f_teid_len); + pdr->f_teid.teid = be32toh(pdr->f_teid.teid); + } + + if (message->pdi.qfi.presence) { + pdr->qfi = message->pdi.qfi.u8; + } + + if (message->outer_header_removal.presence) { + pdr->outer_header_removal_len = message->outer_header_removal.len; + memcpy(&pdr->outer_header_removal, message->outer_header_removal.data, + pdr->outer_header_removal_len); + } + + if (message->far_id.presence) { + far = ogs_pfcp_far_find_or_add(sess, message->far_id.u32); + ogs_assert(far); + ogs_pfcp_pdr_associate_far(pdr, far); + } + + if (message->qer_id.presence) { + qer = ogs_pfcp_qer_find_or_add(sess, message->qer_id.u32); + ogs_assert(qer); + ogs_pfcp_pdr_associate_qer(pdr, qer); + } + + if (message->far_id.presence) { + far = ogs_pfcp_far_find_or_add(sess, message->far_id.u32); + ogs_assert(far); + ogs_pfcp_pdr_associate_far(pdr, far); + } + + if (message->qer_id.presence) { + qer = ogs_pfcp_qer_find_or_add(sess, message->qer_id.u32); + ogs_assert(qer); + ogs_pfcp_pdr_associate_qer(pdr, qer); + } + + return pdr; +} + +ogs_pfcp_pdr_t *ogs_pfcp_handle_update_pdr(ogs_pfcp_sess_t *sess, + ogs_pfcp_tlv_update_pdr_t *message, + uint8_t *cause_value, uint8_t *offending_ie_value) +{ + ogs_pfcp_pdr_t *pdr = NULL; + int i, len; + int rv; + + ogs_assert(message); + ogs_assert(sess); + + if (message->presence == 0) + return NULL; + + if (message->pdr_id.presence == 0) { + ogs_error("No PDR-ID"); + *cause_value = OGS_PFCP_CAUSE_MANDATORY_IE_MISSING; + *offending_ie_value = OGS_PFCP_PDR_ID_TYPE; + return NULL; + } + + pdr = ogs_pfcp_pdr_find(sess, message->pdr_id.u16); + if (!pdr) { + ogs_error("Cannot find PDR-ID[%d] in PDR", message->pdr_id.u16); + *cause_value = OGS_PFCP_CAUSE_MANDATORY_IE_INCORRECT; + *offending_ie_value = OGS_PFCP_PDR_ID_TYPE; + return NULL; + } + + if (message->pdi.presence) { + if (message->pdi.source_interface.presence == 0) { + ogs_error("No Source Interface in PDI"); + *cause_value = OGS_PFCP_CAUSE_MANDATORY_IE_MISSING; + *offending_ie_value = OGS_PFCP_SOURCE_INTERFACE_TYPE; + return NULL; + } + + pdr->src_if = message->pdi.source_interface.u8; + + ogs_pfcp_rule_remove_all(pdr); + + for (i = 0; i < OGS_MAX_NUM_OF_RULE; i++) { + ogs_pfcp_sdf_filter_t sdf_filter_in_message; + + if (message->pdi.sdf_filter[i].presence == 0) + break; + + len = ogs_pfcp_parse_sdf_filter( + &sdf_filter_in_message, &message->pdi.sdf_filter[i]); + ogs_assert(message->pdi.sdf_filter[i].len == len); + if (sdf_filter_in_message.fd) { + ogs_pfcp_rule_t *rule = NULL; + char *flow_description = NULL; + + flow_description = ogs_malloc( + sdf_filter_in_message.flow_description_len+1); + ogs_cpystrn(flow_description, + sdf_filter_in_message.flow_description, + sdf_filter_in_message.flow_description_len+1); + + rule = ogs_pfcp_rule_add(pdr); + ogs_assert(rule); + rv = ogs_ipfw_compile_rule(&rule->ipfw, flow_description); + ogs_assert(rv == OGS_OK); + + ogs_free(flow_description); + } + + } + + if (message->pdi.network_instance.presence) { + char dnn[OGS_MAX_DNN_LEN]; + + ogs_fqdn_parse(dnn, + message->pdi.network_instance.data, + message->pdi.network_instance.len); + + if (pdr->dnn) + ogs_free(pdr->dnn); + pdr->dnn = ogs_strdup(dnn); + } + + if (message->pdi.local_f_teid.presence) { + pdr->f_teid_len = message->pdi.local_f_teid.len; + memcpy(&pdr->f_teid, + message->pdi.local_f_teid.data, pdr->f_teid_len); + pdr->f_teid.teid = be32toh(pdr->f_teid.teid); + } + + if (message->pdi.qfi.presence) { + pdr->qfi = message->pdi.qfi.u8; + } + } + + return pdr; +} + +bool ogs_pfcp_handle_remove_pdr(ogs_pfcp_sess_t *sess, + ogs_pfcp_tlv_remove_pdr_t *message, + uint8_t *cause_value, uint8_t *offending_ie_value) +{ + ogs_pfcp_pdr_t *pdr = NULL; + + ogs_assert(sess); + ogs_assert(message); + + if (message->presence == 0) + return false; + + if (message->pdr_id.presence == 0) { + ogs_error("No PDR-ID"); + *cause_value = OGS_PFCP_CAUSE_MANDATORY_IE_MISSING; + *offending_ie_value = OGS_PFCP_PDR_ID_TYPE; + return false; + } + + pdr = ogs_pfcp_pdr_find(sess, message->pdr_id.u16); + if (!pdr) { + ogs_error("Unknown PDR-ID[%d]", message->pdr_id.u16); + *cause_value = OGS_PFCP_CAUSE_SESSION_CONTEXT_NOT_FOUND; + return false; + } + + ogs_pfcp_pdr_remove(pdr); + + return true; +} + +ogs_pfcp_far_t *ogs_pfcp_handle_create_far(ogs_pfcp_sess_t *sess, + ogs_pfcp_tlv_create_far_t *message, + uint8_t *cause_value, uint8_t *offending_ie_value) +{ + ogs_pfcp_far_t *far = NULL; + + ogs_assert(message); + ogs_assert(sess); + + if (message->presence == 0) + return NULL; + + if (message->far_id.presence == 0) { + ogs_error("No FAR-ID"); + *cause_value = OGS_PFCP_CAUSE_MANDATORY_IE_MISSING; + *offending_ie_value = OGS_PFCP_FAR_ID_TYPE; + return NULL; + } + + far = ogs_pfcp_far_find(sess, message->far_id.u32); + if (!far) { + ogs_error("Cannot find FAR-ID[%d] in PDR", message->far_id.u32); + *cause_value = OGS_PFCP_CAUSE_MANDATORY_IE_INCORRECT; + *offending_ie_value = OGS_PFCP_FAR_ID_TYPE; + return NULL; + } + + if (message->apply_action.presence == 0) { + ogs_error("No Apply Action"); + *cause_value = OGS_PFCP_CAUSE_MANDATORY_IE_MISSING; + *offending_ie_value = OGS_PFCP_APPLY_ACTION_TYPE; + return NULL; + } + if (message->forwarding_parameters. + destination_interface.presence == 0) { + return far; + } + + far->apply_action = message->apply_action.u8; + far->dst_if = message->forwarding_parameters.destination_interface.u8; + + if (message->forwarding_parameters.outer_header_creation.presence) { + ogs_pfcp_tlv_outer_header_creation_t *outer_header_creation = + &message->forwarding_parameters.outer_header_creation; + + ogs_assert(outer_header_creation->data); + ogs_assert(outer_header_creation->len); + + memcpy(&far->outer_header_creation, + outer_header_creation->data, outer_header_creation->len); + far->outer_header_creation.teid = + be32toh(far->outer_header_creation.teid); + } + + return far; +} + +ogs_pfcp_far_t *ogs_pfcp_handle_update_far_flags(ogs_pfcp_sess_t *sess, + ogs_pfcp_tlv_update_far_t *message, + uint8_t *cause_value, uint8_t *offending_ie_value) +{ + ogs_pfcp_far_t *far = NULL; + + ogs_assert(message); + ogs_assert(sess); + + if (message->presence == 0) + return NULL; + + if (message->far_id.presence == 0) { + ogs_error("No FAR-ID"); + *cause_value = OGS_PFCP_CAUSE_MANDATORY_IE_MISSING; + *offending_ie_value = OGS_PFCP_FAR_ID_TYPE; + return NULL; + } + + far = ogs_pfcp_far_find(sess, message->far_id.u32); + if (!far) { + ogs_error("Cannot find FAR-ID[%d] in PDR", message->far_id.u32); + *cause_value = OGS_PFCP_CAUSE_MANDATORY_IE_INCORRECT; + *offending_ie_value = OGS_PFCP_FAR_ID_TYPE; + return NULL; + } + + if (message->update_forwarding_parameters.presence) { + + if (message->update_forwarding_parameters.pfcpsmreq_flags.presence) { + far->smreq_flags.value = + message->update_forwarding_parameters.pfcpsmreq_flags.u8; + } + } + + return far; +} + +ogs_pfcp_far_t *ogs_pfcp_handle_update_far(ogs_pfcp_sess_t *sess, + ogs_pfcp_tlv_update_far_t *message, + uint8_t *cause_value, uint8_t *offending_ie_value) +{ + ogs_pfcp_far_t *far = NULL; + + ogs_assert(message); + ogs_assert(sess); + + if (message->presence == 0) + return NULL; + + if (message->far_id.presence == 0) { + ogs_error("No FAR-ID"); + *cause_value = OGS_PFCP_CAUSE_MANDATORY_IE_MISSING; + *offending_ie_value = OGS_PFCP_FAR_ID_TYPE; + return NULL; + } + + far = ogs_pfcp_far_find(sess, message->far_id.u32); + if (!far) { + ogs_error("Cannot find FAR-ID[%d] in PDR", message->far_id.u32); + *cause_value = OGS_PFCP_CAUSE_MANDATORY_IE_INCORRECT; + *offending_ie_value = OGS_PFCP_FAR_ID_TYPE; + return NULL; + } + + if (message->apply_action.presence) + far->apply_action = message->apply_action.u8; + + if (message->update_forwarding_parameters.destination_interface.presence) { + far->dst_if = message->update_forwarding_parameters. + destination_interface.u8; + } + + if (message->update_forwarding_parameters.presence) { + if (message->update_forwarding_parameters. + outer_header_creation.presence) { + ogs_pfcp_tlv_outer_header_creation_t *outer_header_creation = + &message->update_forwarding_parameters.outer_header_creation; + + ogs_assert(outer_header_creation->data); + ogs_assert(outer_header_creation->len); + + memcpy(&far->outer_header_creation, + outer_header_creation->data, outer_header_creation->len); + far->outer_header_creation.teid = + be32toh(far->outer_header_creation.teid); + } + } + + return far; +} + +bool ogs_pfcp_handle_remove_far(ogs_pfcp_sess_t *sess, + ogs_pfcp_tlv_remove_far_t *message, + uint8_t *cause_value, uint8_t *offending_ie_value) +{ + ogs_pfcp_far_t *far = NULL; + + ogs_assert(sess); + ogs_assert(message); + + if (message->presence == 0) + return false; + + if (message->far_id.presence == 0) { + ogs_error("No FAR-ID"); + *cause_value = OGS_PFCP_CAUSE_MANDATORY_IE_MISSING; + *offending_ie_value = OGS_PFCP_FAR_ID_TYPE; + return false; + } + + far = ogs_pfcp_far_find(sess, message->far_id.u32); + if (!far) { + ogs_error("Unknown FAR-ID[%d]", message->far_id.u32); + *cause_value = OGS_PFCP_CAUSE_SESSION_CONTEXT_NOT_FOUND; + return false; + } + + ogs_pfcp_far_remove(far); + + return true; +} + +ogs_pfcp_qer_t *ogs_pfcp_handle_create_qer(ogs_pfcp_sess_t *sess, + ogs_pfcp_tlv_create_qer_t *message, + uint8_t *cause_value, uint8_t *offending_ie_value) +{ + ogs_pfcp_qer_t *qer = NULL; + + ogs_assert(message); + ogs_assert(sess); + + if (message->presence == 0) + return NULL; + + if (message->qer_id.presence == 0) { + ogs_error("No QER-ID"); + *cause_value = OGS_PFCP_CAUSE_MANDATORY_IE_MISSING; + *offending_ie_value = OGS_PFCP_FAR_ID_TYPE; + return NULL; + } + + qer = ogs_pfcp_qer_find(sess, message->qer_id.u32); + if (!qer) { + ogs_error("Cannot find QER-ID[%d] in PDR", message->qer_id.u32); + *cause_value = OGS_PFCP_CAUSE_MANDATORY_IE_INCORRECT; + *offending_ie_value = OGS_PFCP_FAR_ID_TYPE; + return NULL; + } + + if (message->gate_status.presence == 0) { + ogs_error("No Gate Status"); + *cause_value = OGS_PFCP_CAUSE_MANDATORY_IE_MISSING; + *offending_ie_value = OGS_PFCP_APPLY_ACTION_TYPE; + return NULL; + } + + qer->gate_status.value = message->gate_status.u8; + + if (message->maximum_bitrate.presence) + ogs_pfcp_parse_bitrate(&qer->mbr, &message->maximum_bitrate); + if (message->guaranteed_bitrate.presence) + ogs_pfcp_parse_bitrate(&qer->gbr, &message->guaranteed_bitrate); + + if (message->qos_flow_identifier.presence) + qer->qfi = message->qos_flow_identifier.u8; + + return qer; +} + +ogs_pfcp_qer_t *ogs_pfcp_handle_update_qer(ogs_pfcp_sess_t *sess, + ogs_pfcp_tlv_update_qer_t *message, + uint8_t *cause_value, uint8_t *offending_ie_value) +{ + ogs_pfcp_qer_t *qer = NULL; + + ogs_assert(message); + ogs_assert(sess); + + if (message->presence == 0) + return NULL; + + if (message->qer_id.presence == 0) { + ogs_error("No QER-ID"); + *cause_value = OGS_PFCP_CAUSE_MANDATORY_IE_MISSING; + *offending_ie_value = OGS_PFCP_FAR_ID_TYPE; + return NULL; + } + + qer = ogs_pfcp_qer_find(sess, message->qer_id.u32); + if (!qer) { + ogs_error("Cannot find QER-ID[%d] in PDR", message->qer_id.u32); + *cause_value = OGS_PFCP_CAUSE_MANDATORY_IE_INCORRECT; + *offending_ie_value = OGS_PFCP_FAR_ID_TYPE; + return NULL; + } + + if (message->maximum_bitrate.presence) + ogs_pfcp_parse_bitrate(&qer->mbr, &message->maximum_bitrate); + if (message->guaranteed_bitrate.presence) + ogs_pfcp_parse_bitrate(&qer->gbr, &message->guaranteed_bitrate); + + return qer; +} + +bool ogs_pfcp_handle_remove_qer(ogs_pfcp_sess_t *sess, + ogs_pfcp_tlv_remove_qer_t *message, + uint8_t *cause_value, uint8_t *offending_ie_value) +{ + ogs_pfcp_qer_t *qer = NULL; + + ogs_assert(sess); + ogs_assert(message); + + if (message->presence == 0) + return false; + + if (message->qer_id.presence == 0) { + ogs_error("No QER-ID"); + *cause_value = OGS_PFCP_CAUSE_MANDATORY_IE_MISSING; + *offending_ie_value = OGS_PFCP_QER_ID_TYPE; + return false; + } + + qer = ogs_pfcp_qer_find(sess, message->qer_id.u32); + if (!qer) { + ogs_error("Unknown QER-ID[%d]", message->qer_id.u32); + *cause_value = OGS_PFCP_CAUSE_SESSION_CONTEXT_NOT_FOUND; + return false; + } + + ogs_pfcp_qer_remove(qer); + + return true; +} diff --git a/lib/pfcp/handler.h b/lib/pfcp/handler.h new file mode 100644 index 000000000..e5c16b474 --- /dev/null +++ b/lib/pfcp/handler.h @@ -0,0 +1,89 @@ +/* + * 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 OGS_PFCP_HANDLER_H +#define OGS_PFCP_HANDLER_H + +#ifdef __cplusplus +extern "C" { +#endif + +void ogs_pfcp_handle_heartbeat_request( + ogs_pfcp_node_t *node, ogs_pfcp_xact_t *xact, + ogs_pfcp_heartbeat_request_t *req); +void ogs_pfcp_handle_heartbeat_response( + ogs_pfcp_node_t *node, ogs_pfcp_xact_t *xact, + ogs_pfcp_heartbeat_response_t *req); + +void ogs_pfcp_cp_handle_association_setup_request( + ogs_pfcp_node_t *node, ogs_pfcp_xact_t *xact, + ogs_pfcp_association_setup_request_t *req); +void ogs_pfcp_cp_handle_association_setup_response( + ogs_pfcp_node_t *node, ogs_pfcp_xact_t *xact, + ogs_pfcp_association_setup_response_t *req); + +void ogs_pfcp_up_handle_association_setup_request( + ogs_pfcp_node_t *node, ogs_pfcp_xact_t *xact, + ogs_pfcp_association_setup_request_t *req); +void ogs_pfcp_up_handle_association_setup_response( + ogs_pfcp_node_t *node, ogs_pfcp_xact_t *xact, + ogs_pfcp_association_setup_response_t *req); + +void ogs_pfcp_up_handle_pdr( + ogs_pfcp_pdr_t *pdr, ogs_pkbuf_t *recvbuf, + ogs_pfcp_user_plane_report_t *report); + +ogs_pfcp_pdr_t *ogs_pfcp_handle_create_pdr(ogs_pfcp_sess_t *sess, + ogs_pfcp_tlv_create_pdr_t *message, + uint8_t *cause_value, uint8_t *offending_ie_value); +ogs_pfcp_pdr_t *ogs_pfcp_handle_update_pdr(ogs_pfcp_sess_t *sess, + ogs_pfcp_tlv_update_pdr_t *message, + uint8_t *cause_value, uint8_t *offending_ie_value); +bool ogs_pfcp_handle_remove_pdr(ogs_pfcp_sess_t *sess, + ogs_pfcp_tlv_remove_pdr_t *message, + uint8_t *cause_value, uint8_t *offending_ie_value); + +ogs_pfcp_far_t *ogs_pfcp_handle_create_far(ogs_pfcp_sess_t *sess, + ogs_pfcp_tlv_create_far_t *message, + uint8_t *cause_value, uint8_t *offending_ie_value); +ogs_pfcp_far_t *ogs_pfcp_handle_update_far_flags(ogs_pfcp_sess_t *sess, + ogs_pfcp_tlv_update_far_t *message, + uint8_t *cause_value, uint8_t *offending_ie_value); +ogs_pfcp_far_t *ogs_pfcp_handle_update_far(ogs_pfcp_sess_t *sess, + ogs_pfcp_tlv_update_far_t *message, + uint8_t *cause_value, uint8_t *offending_ie_value); +bool ogs_pfcp_handle_remove_far(ogs_pfcp_sess_t *sess, + ogs_pfcp_tlv_remove_far_t *message, + uint8_t *cause_value, uint8_t *offending_ie_value); + +ogs_pfcp_qer_t *ogs_pfcp_handle_create_qer(ogs_pfcp_sess_t *sess, + ogs_pfcp_tlv_create_qer_t *message, + uint8_t *cause_value, uint8_t *offending_ie_value); +ogs_pfcp_qer_t *ogs_pfcp_handle_update_qer(ogs_pfcp_sess_t *sess, + ogs_pfcp_tlv_update_qer_t *message, + uint8_t *cause_value, uint8_t *offending_ie_value); +bool ogs_pfcp_handle_remove_qer(ogs_pfcp_sess_t *sess, + ogs_pfcp_tlv_remove_qer_t *message, + uint8_t *cause_value, uint8_t *offending_ie_value); + +#ifdef __cplusplus +} +#endif + +#endif /* OGS_PFCP_HANDLER_H */ diff --git a/lib/pfcp/meson.build b/lib/pfcp/meson.build index 660650727..9c7aa03af 100644 --- a/lib/pfcp/meson.build +++ b/lib/pfcp/meson.build @@ -21,7 +21,8 @@ libpfcp_sources = files(''' message.h types.h conv.h - n4-build.h + build.h + handler.h path.h xact.h context.h @@ -29,7 +30,8 @@ libpfcp_sources = files(''' message.c types.c conv.c - n4-build.c + build.c + handler.c path.c xact.c context.c @@ -42,10 +44,10 @@ libpfcp = library('ogspfcp', version : libogslib_version, c_args : '-DOGS_PFCP_COMPILATION', include_directories : [libpfcp_inc, libinc], - dependencies : [libcore_dep, libapp_dep], + dependencies : [libipfw_dep, libcore_dep, libapp_dep, libgtp_dep], install : true) libpfcp_dep = declare_dependency( link_with : libpfcp, include_directories : [libpfcp_inc, libinc], - dependencies : [libcore_dep, libapp_dep]) + dependencies : [libipfw_dep, libcore_dep, libapp_dep, libgtp_dep]) diff --git a/lib/pfcp/message.c b/lib/pfcp/message.c index fab2adab0..f60fe2e6c 100644 --- a/lib/pfcp/message.c +++ b/lib/pfcp/message.c @@ -20,7 +20,7 @@ /******************************************************************************* * This file had been created by pfcp-tlv.py script v0.1.0 * Please do not modify this file but regenerate it via script. - * Created on: 2020-07-23 13:41:55.927459 by acetcom + * Created on: 2020-08-12 20:18:49.875450 by acetcom * from 29244-g10.docx ******************************************************************************/ @@ -248,10 +248,10 @@ ogs_tlv_desc_t ogs_pfcp_tlv_desc_redirect_information = ogs_tlv_desc_t ogs_pfcp_tlv_desc_report_type = { - OGS_TLV_VAR_STR, + OGS_TLV_UINT8, "Report Type", OGS_PFCP_REPORT_TYPE_TYPE, - 0, + 1, 0, sizeof(ogs_pfcp_tlv_report_type_t), { NULL } @@ -292,10 +292,10 @@ ogs_tlv_desc_t ogs_pfcp_tlv_desc_destination_interface = ogs_tlv_desc_t ogs_pfcp_tlv_desc_up_function_features = { - OGS_TLV_UINT16, + OGS_TLV_VAR_STR, "UP Function Features", OGS_PFCP_UP_FUNCTION_FEATURES_TYPE, - 2, + 0, 0, sizeof(ogs_pfcp_tlv_up_function_features_t), { NULL } @@ -358,10 +358,10 @@ ogs_tlv_desc_t ogs_pfcp_tlv_desc_dl_buffering_suggested_packet_count = ogs_tlv_desc_t ogs_pfcp_tlv_desc_pfcpsmreq_flags = { - OGS_TLV_VAR_STR, + OGS_TLV_UINT8, "PFCPSMReq-Flags", OGS_PFCP_PFCPSMREQ_FLAGS_TYPE, - 0, + 1, 0, sizeof(ogs_pfcp_tlv_pfcpsmreq_flags_t), { NULL } @@ -369,10 +369,10 @@ ogs_tlv_desc_t ogs_pfcp_tlv_desc_pfcpsmreq_flags = ogs_tlv_desc_t ogs_pfcp_tlv_desc_pfcpsrrsp_flags = { - OGS_TLV_VAR_STR, + OGS_TLV_UINT8, "PFCPSRRsp-Flags", OGS_PFCP_PFCPSRRSP_FLAGS_TYPE, - 0, + 1, 0, sizeof(ogs_pfcp_tlv_pfcpsrrsp_flags_t), { NULL } @@ -1359,10 +1359,10 @@ ogs_tlv_desc_t ogs_pfcp_tlv_desc__interface_type = ogs_tlv_desc_t ogs_pfcp_tlv_desc_pfcpsrreq_flags = { - OGS_TLV_VAR_STR, + OGS_TLV_UINT8, "PFCPSRReq-Flags", OGS_PFCP_PFCPSRREQ_FLAGS_TYPE, - 0, + 1, 0, sizeof(ogs_pfcp_tlv_pfcpsrreq_flags_t), { NULL } @@ -1370,10 +1370,10 @@ ogs_tlv_desc_t ogs_pfcp_tlv_desc_pfcpsrreq_flags = ogs_tlv_desc_t ogs_pfcp_tlv_desc_pfcpaureq_flags = { - OGS_TLV_VAR_STR, + OGS_TLV_UINT8, "PFCPAUReq-Flags", OGS_PFCP_PFCPAUREQ_FLAGS_TYPE, - 0, + 1, 0, sizeof(ogs_pfcp_tlv_pfcpaureq_flags_t), { NULL } @@ -1527,7 +1527,7 @@ ogs_tlv_desc_t ogs_pfcp_tlv_desc_ethernet_packet_filter = &ogs_pfcp_tlv_desc_c_tag, &ogs_pfcp_tlv_desc_s_tag, &ogs_pfcp_tlv_desc_sdf_filter, - &ogs_tlv_desc_more4, + &ogs_tlv_desc_more8, NULL, } }; @@ -1547,7 +1547,7 @@ ogs_tlv_desc_t ogs_pfcp_tlv_desc_pdi = &ogs_pfcp_tlv_desc_ue_ip_address, &ogs_pfcp_tlv_desc_traffic_endpoint_id, &ogs_pfcp_tlv_desc_sdf_filter, - &ogs_tlv_desc_more4, + &ogs_tlv_desc_more8, &ogs_pfcp_tlv_desc_application_id, &ogs_pfcp_tlv_desc_ethernet_pdu_session_information, &ogs_pfcp_tlv_desc_ethernet_packet_filter, @@ -2573,9 +2573,9 @@ ogs_tlv_desc_t ogs_pfcp_msg_desc_pfcp_session_establishment_request = &ogs_pfcp_tlv_desc_node_id, &ogs_pfcp_tlv_desc_f_seid, &ogs_pfcp_tlv_desc_create_pdr, - &ogs_tlv_desc_more4, + &ogs_tlv_desc_more8, &ogs_pfcp_tlv_desc_create_far, - &ogs_tlv_desc_more4, + &ogs_tlv_desc_more8, &ogs_pfcp_tlv_desc_create_urr, &ogs_tlv_desc_more2, &ogs_pfcp_tlv_desc_create_qer, @@ -2606,7 +2606,7 @@ ogs_tlv_desc_t ogs_pfcp_msg_desc_pfcp_session_establishment_response = &ogs_pfcp_tlv_desc_offending_ie, &ogs_pfcp_tlv_desc_f_seid, &ogs_pfcp_tlv_desc_created_pdr, - &ogs_tlv_desc_more4, + &ogs_tlv_desc_more8, &ogs_pfcp_tlv_desc_load_control_information, &ogs_pfcp_tlv_desc_overload_control_information, &ogs_pfcp_tlv_desc_fq_csid, @@ -2623,9 +2623,9 @@ ogs_tlv_desc_t ogs_pfcp_msg_desc_pfcp_session_modification_request = 0, 0, 0, 0, { &ogs_pfcp_tlv_desc_f_seid, &ogs_pfcp_tlv_desc_remove_pdr, - &ogs_tlv_desc_more4, + &ogs_tlv_desc_more8, &ogs_pfcp_tlv_desc_remove_far, - &ogs_tlv_desc_more4, + &ogs_tlv_desc_more8, &ogs_pfcp_tlv_desc_remove_urr, &ogs_tlv_desc_more2, &ogs_pfcp_tlv_desc_remove_qer, @@ -2633,9 +2633,9 @@ ogs_tlv_desc_t ogs_pfcp_msg_desc_pfcp_session_modification_request = &ogs_pfcp_tlv_desc_remove_bar, &ogs_pfcp_tlv_desc_remove_traffic_endpoint, &ogs_pfcp_tlv_desc_create_pdr, - &ogs_tlv_desc_more4, + &ogs_tlv_desc_more8, &ogs_pfcp_tlv_desc_create_far, - &ogs_tlv_desc_more4, + &ogs_tlv_desc_more8, &ogs_pfcp_tlv_desc_create_urr, &ogs_tlv_desc_more2, &ogs_pfcp_tlv_desc_create_qer, @@ -2643,9 +2643,9 @@ ogs_tlv_desc_t ogs_pfcp_msg_desc_pfcp_session_modification_request = &ogs_pfcp_tlv_desc_create_bar, &ogs_pfcp_tlv_desc_create_traffic_endpoint, &ogs_pfcp_tlv_desc_update_pdr, - &ogs_tlv_desc_more4, + &ogs_tlv_desc_more8, &ogs_pfcp_tlv_desc_update_far, - &ogs_tlv_desc_more4, + &ogs_tlv_desc_more8, &ogs_pfcp_tlv_desc_update_urr, &ogs_tlv_desc_more2, &ogs_pfcp_tlv_desc_update_qer, @@ -2677,7 +2677,7 @@ ogs_tlv_desc_t ogs_pfcp_msg_desc_pfcp_session_modification_response = &ogs_pfcp_tlv_desc_cause, &ogs_pfcp_tlv_desc_offending_ie, &ogs_pfcp_tlv_desc_created_pdr, - &ogs_tlv_desc_more4, + &ogs_tlv_desc_more8, &ogs_pfcp_tlv_desc_load_control_information, &ogs_pfcp_tlv_desc_overload_control_information, &ogs_pfcp_tlv_desc_usage_report_session_modification_response, diff --git a/lib/pfcp/message.h b/lib/pfcp/message.h index a4a2c5ae1..b470c47ff 100644 --- a/lib/pfcp/message.h +++ b/lib/pfcp/message.h @@ -20,7 +20,7 @@ /******************************************************************************* * This file had been created by pfcp-tlv.py script v0.1.0 * Please do not modify this file but regenerate it via script. - * Created on: 2020-07-23 13:41:55.918372 by acetcom + * Created on: 2020-08-12 20:18:49.864313 by acetcom * from 29244-g10.docx ******************************************************************************/ @@ -501,18 +501,18 @@ typedef ogs_tlv_octet_t ogs_pfcp_tlv_subsequent_time_threshold_t; typedef ogs_tlv_octet_t ogs_pfcp_tlv_inactivity_detection_time_t; typedef ogs_tlv_uint8_t ogs_pfcp_tlv_reporting_triggers_t; typedef ogs_tlv_octet_t ogs_pfcp_tlv_redirect_information_t; -typedef ogs_tlv_octet_t ogs_pfcp_tlv_report_type_t; +typedef ogs_tlv_uint8_t ogs_pfcp_tlv_report_type_t; typedef ogs_tlv_uint16_t ogs_pfcp_tlv_offending_ie_t; typedef ogs_tlv_octet_t ogs_pfcp_tlv_forwarding_policy_t; typedef ogs_tlv_uint8_t ogs_pfcp_tlv_destination_interface_t; -typedef ogs_tlv_uint16_t ogs_pfcp_tlv_up_function_features_t; +typedef ogs_tlv_octet_t ogs_pfcp_tlv_up_function_features_t; typedef ogs_tlv_uint8_t ogs_pfcp_tlv_apply_action_t; typedef ogs_tlv_octet_t ogs_pfcp_tlv_downlink_data_service_information_t; typedef ogs_tlv_octet_t ogs_pfcp_tlv_downlink_data_notification_delay_t; typedef ogs_tlv_octet_t ogs_pfcp_tlv_dl_buffering_duration_t; typedef ogs_tlv_octet_t ogs_pfcp_tlv_dl_buffering_suggested_packet_count_t; -typedef ogs_tlv_octet_t ogs_pfcp_tlv_pfcpsmreq_flags_t; -typedef ogs_tlv_octet_t ogs_pfcp_tlv_pfcpsrrsp_flags_t; +typedef ogs_tlv_uint8_t ogs_pfcp_tlv_pfcpsmreq_flags_t; +typedef ogs_tlv_uint8_t ogs_pfcp_tlv_pfcpsrrsp_flags_t; typedef ogs_tlv_octet_t ogs_pfcp_tlv_sequence_number_t; typedef ogs_tlv_octet_t ogs_pfcp_tlv_metric_t; typedef ogs_tlv_octet_t ogs_pfcp_tlv_timer_t; @@ -602,8 +602,8 @@ typedef ogs_tlv_uint32_t ogs_pfcp_tlv_averaging_window_t; typedef ogs_tlv_uint8_t ogs_pfcp_tlv_paging_policy_indicator_t; typedef ogs_tlv_octet_t ogs_pfcp_tlv_apn_dnn_t; typedef ogs_tlv_octet_t ogs_pfcp_tlv__interface_type_t; -typedef ogs_tlv_octet_t ogs_pfcp_tlv_pfcpsrreq_flags_t; -typedef ogs_tlv_octet_t ogs_pfcp_tlv_pfcpaureq_flags_t; +typedef ogs_tlv_uint8_t ogs_pfcp_tlv_pfcpsrreq_flags_t; +typedef ogs_tlv_uint8_t ogs_pfcp_tlv_pfcpaureq_flags_t; typedef ogs_tlv_octet_t ogs_pfcp_tlv_activation_time_t; typedef ogs_tlv_octet_t ogs_pfcp_tlv_deactivation_time_t; typedef ogs_tlv_octet_t ogs_pfcp_tlv_mar_id_t; @@ -626,7 +626,7 @@ typedef struct ogs_pfcp_tlv_ethernet_packet_filter_s { ogs_pfcp_tlv_ethertype_t ethertype; ogs_pfcp_tlv_c_tag_t c_tag; ogs_pfcp_tlv_s_tag_t s_tag; - ogs_pfcp_tlv_sdf_filter_t sdf_filter[4]; + ogs_pfcp_tlv_sdf_filter_t sdf_filter[8]; } ogs_pfcp_tlv_ethernet_packet_filter_t; typedef struct ogs_pfcp_tlv_pdi_s { @@ -636,7 +636,7 @@ typedef struct ogs_pfcp_tlv_pdi_s { ogs_pfcp_tlv_network_instance_t network_instance; ogs_pfcp_tlv_ue_ip_address_t ue_ip_address; ogs_pfcp_tlv_traffic_endpoint_id_t traffic_endpoint_id; - ogs_pfcp_tlv_sdf_filter_t sdf_filter[4]; + ogs_pfcp_tlv_sdf_filter_t sdf_filter[8]; ogs_pfcp_tlv_application_id_t application_id; ogs_pfcp_tlv_ethernet_pdu_session_information_t ethernet_pdu_session_information; ogs_pfcp_tlv_ethernet_packet_filter_t ethernet_packet_filter; @@ -1182,8 +1182,8 @@ typedef struct ogs_pfcp_session_set_deletion_response_s { typedef struct ogs_pfcp_session_establishment_request_s { ogs_pfcp_tlv_node_id_t node_id; ogs_pfcp_tlv_f_seid_t cp_f_seid; - ogs_pfcp_tlv_create_pdr_t create_pdr[4]; - ogs_pfcp_tlv_create_far_t create_far[4]; + ogs_pfcp_tlv_create_pdr_t create_pdr[8]; + ogs_pfcp_tlv_create_far_t create_far[8]; ogs_pfcp_tlv_create_urr_t create_urr[2]; ogs_pfcp_tlv_create_qer_t create_qer[2]; ogs_pfcp_tlv_create_bar_t create_bar; @@ -1206,7 +1206,7 @@ typedef struct ogs_pfcp_session_establishment_response_s { ogs_pfcp_tlv_cause_t cause; ogs_pfcp_tlv_offending_ie_t offending_ie; ogs_pfcp_tlv_f_seid_t up_f_seid; - ogs_pfcp_tlv_created_pdr_t created_pdr[4]; + ogs_pfcp_tlv_created_pdr_t created_pdr[8]; ogs_pfcp_tlv_load_control_information_t load_control_information; ogs_pfcp_tlv_overload_control_information_t overload_control_information; ogs_pfcp_tlv_fq_csid_t sgw_u_fq_csid; @@ -1217,20 +1217,20 @@ typedef struct ogs_pfcp_session_establishment_response_s { typedef struct ogs_pfcp_session_modification_request_s { ogs_pfcp_tlv_f_seid_t cp_f_seid; - ogs_pfcp_tlv_remove_pdr_t remove_pdr[4]; - ogs_pfcp_tlv_remove_far_t remove_far[4]; + ogs_pfcp_tlv_remove_pdr_t remove_pdr[8]; + ogs_pfcp_tlv_remove_far_t remove_far[8]; ogs_pfcp_tlv_remove_urr_t remove_urr[2]; ogs_pfcp_tlv_remove_qer_t remove_qer[2]; ogs_pfcp_tlv_remove_bar_t remove_bar; ogs_pfcp_tlv_remove_traffic_endpoint_t remove_traffic_endpoint; - ogs_pfcp_tlv_create_pdr_t create_pdr[4]; - ogs_pfcp_tlv_create_far_t create_far[4]; + ogs_pfcp_tlv_create_pdr_t create_pdr[8]; + ogs_pfcp_tlv_create_far_t create_far[8]; ogs_pfcp_tlv_create_urr_t create_urr[2]; ogs_pfcp_tlv_create_qer_t create_qer[2]; ogs_pfcp_tlv_create_bar_t create_bar; ogs_pfcp_tlv_create_traffic_endpoint_t create_traffic_endpoint; - ogs_pfcp_tlv_update_pdr_t update_pdr[4]; - ogs_pfcp_tlv_update_far_t update_far[4]; + ogs_pfcp_tlv_update_pdr_t update_pdr[8]; + ogs_pfcp_tlv_update_far_t update_far[8]; ogs_pfcp_tlv_update_urr_t update_urr[2]; ogs_pfcp_tlv_update_qer_t update_qer[2]; ogs_pfcp_tlv_update_bar_session_modification_request_t update_bar; @@ -1254,7 +1254,7 @@ typedef struct ogs_pfcp_session_modification_request_s { typedef struct ogs_pfcp_session_modification_response_s { ogs_pfcp_tlv_cause_t cause; ogs_pfcp_tlv_offending_ie_t offending_ie; - ogs_pfcp_tlv_created_pdr_t created_pdr[4]; + ogs_pfcp_tlv_created_pdr_t created_pdr[8]; ogs_pfcp_tlv_load_control_information_t load_control_information; ogs_pfcp_tlv_overload_control_information_t overload_control_information; ogs_pfcp_tlv_usage_report_session_modification_response_t usage_report; diff --git a/lib/pfcp/n4-build.c b/lib/pfcp/n4-build.c deleted file mode 100644 index 3578ace88..000000000 --- a/lib/pfcp/n4-build.c +++ /dev/null @@ -1,54 +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 "ogs-pfcp.h" - -ogs_pkbuf_t *ogs_pfcp_n4_build_heartbeat_request(uint8_t type) -{ - ogs_pfcp_message_t pfcp_message; - ogs_pfcp_heartbeat_request_t *req = NULL; - - ogs_debug("[PFCP] Heartbeat Request"); - - req = &pfcp_message.pfcp_heartbeat_request; - memset(&pfcp_message, 0, sizeof(ogs_pfcp_message_t)); - - req->recovery_time_stamp.presence = 1; - req->recovery_time_stamp.u32 = ogs_pfcp_self()->pfcp_started; - - pfcp_message.h.type = type; - return ogs_pfcp_build_msg(&pfcp_message); -} - -ogs_pkbuf_t *ogs_pfcp_n4_build_heartbeat_response(uint8_t type) -{ - ogs_pfcp_message_t pfcp_message; - ogs_pfcp_heartbeat_response_t *rsp = NULL; - - ogs_debug("[PFCP] Heartbeat Response"); - - rsp = &pfcp_message.pfcp_heartbeat_response; - memset(&pfcp_message, 0, sizeof(ogs_pfcp_message_t)); - - rsp->recovery_time_stamp.presence = 1; - rsp->recovery_time_stamp.u32 = ogs_pfcp_self()->pfcp_started; - - pfcp_message.h.type = type; - return ogs_pfcp_build_msg(&pfcp_message); -} diff --git a/lib/pfcp/ogs-pfcp.h b/lib/pfcp/ogs-pfcp.h index 6183dff69..7f3a12acb 100644 --- a/lib/pfcp/ogs-pfcp.h +++ b/lib/pfcp/ogs-pfcp.h @@ -21,11 +21,14 @@ #define OGS_PFCP_H #include "ogs-core.h" +#include "ipfw/ogs-ipfw.h" +#include "ogs-app.h" +#include "ogs-gtp.h" #define OGS_PFCP_UDP_PORT 8805 -#define OGS_MAX_NUM_OF_PDR 4 -#define OGS_MAX_NUM_OF_FAR 4 +#define OGS_MAX_NUM_OF_PDR 8 +#define OGS_MAX_NUM_OF_FAR 8 #define OGS_MAX_NUM_OF_URR 2 #define OGS_MAX_NUM_OF_QER 2 #define OGS_MAX_NUM_OF_BAR 1 @@ -37,9 +40,10 @@ #include "pfcp/types.h" #include "pfcp/conv.h" #include "pfcp/context.h" -#include "pfcp/n4-build.h" +#include "pfcp/build.h" #include "pfcp/path.h" #include "pfcp/xact.h" +#include "pfcp/handler.h" #ifdef __cplusplus extern "C" { diff --git a/lib/pfcp/path.c b/lib/pfcp/path.c index 020fb034a..4bc9d1584 100644 --- a/lib/pfcp/path.c +++ b/lib/pfcp/path.c @@ -118,10 +118,34 @@ int ogs_pfcp_sendto(ogs_pfcp_node_t *node, ogs_pkbuf_t *pkbuf) return OGS_OK; } +void ogs_pfcp_send_heartbeat_request(ogs_pfcp_node_t *node, + void (*cb)(ogs_pfcp_xact_t *xact, void *data)) +{ + int rv; + ogs_pkbuf_t *pkbuf = NULL; + ogs_pfcp_header_t h; + ogs_pfcp_xact_t *xact = NULL; + + ogs_assert(node); + + memset(&h, 0, sizeof(ogs_pfcp_header_t)); + h.type = OGS_PFCP_HEARTBEAT_REQUEST_TYPE; + h.seid = 0; + + pkbuf = ogs_pfcp_build_heartbeat_request(h.type); + ogs_expect_or_return(pkbuf); + + xact = ogs_pfcp_xact_local_create(node, &h, pkbuf, cb, node); + ogs_expect_or_return(xact); + + rv = ogs_pfcp_xact_commit(xact); + ogs_expect(rv == OGS_OK); +} + void ogs_pfcp_send_heartbeat_response(ogs_pfcp_xact_t *xact) { int rv; - ogs_pkbuf_t *n4buf = NULL; + ogs_pkbuf_t *pkbuf = NULL; ogs_pfcp_header_t h; ogs_assert(xact); @@ -130,16 +154,282 @@ void ogs_pfcp_send_heartbeat_response(ogs_pfcp_xact_t *xact) h.type = OGS_PFCP_HEARTBEAT_RESPONSE_TYPE; h.seid = 0; - n4buf = ogs_pfcp_n4_build_heartbeat_response(h.type); - ogs_expect_or_return(n4buf); + pkbuf = ogs_pfcp_build_heartbeat_response(h.type); + ogs_expect_or_return(pkbuf); - rv = ogs_pfcp_xact_update_tx(xact, &h, n4buf); + rv = ogs_pfcp_xact_update_tx(xact, &h, pkbuf); ogs_expect_or_return(rv == OGS_OK); rv = ogs_pfcp_xact_commit(xact); ogs_expect(rv == OGS_OK); } +void ogs_pfcp_cp_send_association_setup_request(ogs_pfcp_node_t *node, + void (*cb)(ogs_pfcp_xact_t *xact, void *data)) +{ + int rv; + ogs_pkbuf_t *pkbuf = NULL; + ogs_pfcp_header_t h; + ogs_pfcp_xact_t *xact = NULL; + + ogs_assert(node); + + memset(&h, 0, sizeof(ogs_pfcp_header_t)); + h.type = OGS_PFCP_ASSOCIATION_SETUP_REQUEST_TYPE; + h.seid = 0; + + pkbuf = ogs_pfcp_cp_build_association_setup_request(h.type); + ogs_expect_or_return(pkbuf); + + xact = ogs_pfcp_xact_local_create(node, &h, pkbuf, cb, node); + ogs_expect_or_return(xact); + + rv = ogs_pfcp_xact_commit(xact); + ogs_expect(rv == OGS_OK); +} + +void ogs_pfcp_cp_send_association_setup_response(ogs_pfcp_xact_t *xact, + uint8_t cause) +{ + int rv; + ogs_pkbuf_t *pkbuf = NULL; + ogs_pfcp_header_t h; + + ogs_assert(xact); + + memset(&h, 0, sizeof(ogs_pfcp_header_t)); + h.type = OGS_PFCP_ASSOCIATION_SETUP_RESPONSE_TYPE; + h.seid = 0; + + pkbuf = ogs_pfcp_cp_build_association_setup_response(h.type, cause); + ogs_expect_or_return(pkbuf); + + rv = ogs_pfcp_xact_update_tx(xact, &h, pkbuf); + ogs_expect_or_return(rv == OGS_OK); + + rv = ogs_pfcp_xact_commit(xact); + ogs_expect(rv == OGS_OK); +} + +void ogs_pfcp_up_send_association_setup_request(ogs_pfcp_node_t *node, + void (*cb)(ogs_pfcp_xact_t *xact, void *data)) +{ + int rv; + ogs_pkbuf_t *pkbuf = NULL; + ogs_pfcp_header_t h; + ogs_pfcp_xact_t *xact = NULL; + + ogs_assert(node); + + memset(&h, 0, sizeof(ogs_pfcp_header_t)); + h.type = OGS_PFCP_ASSOCIATION_SETUP_REQUEST_TYPE; + h.seid = 0; + + pkbuf = ogs_pfcp_up_build_association_setup_request(h.type); + ogs_expect_or_return(pkbuf); + + xact = ogs_pfcp_xact_local_create(node, &h, pkbuf, cb, node); + ogs_expect_or_return(xact); + + rv = ogs_pfcp_xact_commit(xact); + ogs_expect(rv == OGS_OK); +} + +void ogs_pfcp_up_send_association_setup_response(ogs_pfcp_xact_t *xact, + uint8_t cause) +{ + int rv; + ogs_pkbuf_t *pkbuf = NULL; + ogs_pfcp_header_t h; + + ogs_assert(xact); + + memset(&h, 0, sizeof(ogs_pfcp_header_t)); + h.type = OGS_PFCP_ASSOCIATION_SETUP_RESPONSE_TYPE; + h.seid = 0; + + pkbuf = ogs_pfcp_up_build_association_setup_response(h.type, cause); + ogs_expect_or_return(pkbuf); + + rv = ogs_pfcp_xact_update_tx(xact, &h, pkbuf); + ogs_expect_or_return(rv == OGS_OK); + + rv = ogs_pfcp_xact_commit(xact); + ogs_expect(rv == OGS_OK); +} + +void ogs_pfcp_send_g_pdu(ogs_pfcp_pdr_t *pdr, ogs_pkbuf_t *sendbuf) +{ + char buf[OGS_ADDRSTRLEN]; + int rv; + ogs_gtp_header_t *gtp_h = NULL; + ogs_gtp_extension_header_t *ext_h = NULL; + ogs_gtp_node_t *gnode = NULL; + + ogs_pfcp_far_t *far = NULL; + ogs_pfcp_qer_t *qer = NULL; + + ogs_assert(pdr); + + far = pdr->far; + if (!far) { + ogs_error("No FAR"); + return; + } + + gnode = far->gnode; + ogs_assert(gnode); + ogs_assert(gnode->sock); + ogs_assert(sendbuf); + + qer = pdr->qer; + + /* Add GTP-U header */ + if (qer && qer->qfi) { + ogs_assert(ogs_pkbuf_push(sendbuf, OGS_GTPV1U_5GC_HEADER_LEN)); + gtp_h = (ogs_gtp_header_t *)sendbuf->data; + /* Bits 8 7 6 5 4 3 2 1 + * +--+--+--+--+--+--+--+--+ + * |version |PT| 1| E| S|PN| + * +--+--+--+--+--+--+--+--+ + * 0 0 1 1 0 1 0 0 + */ + gtp_h->flags = 0x34; + gtp_h->type = OGS_GTPU_MSGTYPE_GPDU; + gtp_h->length = htobe16(sendbuf->len - OGS_GTPV1U_HEADER_LEN); + gtp_h->teid = htobe32(far->outer_header_creation.teid); + + ext_h = (ogs_gtp_extension_header_t *)( + sendbuf->data + OGS_GTPV1U_HEADER_LEN); + ext_h->type = OGS_GTP_EXTENSION_HEADER_TYPE_PDU_SESSION_CONTAINER; + ext_h->len = 1; + ext_h->pdu_type = + OGS_GTP_EXTENSION_HEADER_PDU_TYPE_DL_PDU_SESSION_INFORMATION; + ext_h->qos_flow_identifier = qer->qfi; + ext_h->next_type = + OGS_GTP_EXTENSION_HEADER_TYPE_NO_MORE_EXTENSION_HEADERS; + } else { + ogs_assert(ogs_pkbuf_push(sendbuf, OGS_GTPV1U_HEADER_LEN)); + gtp_h = (ogs_gtp_header_t *)sendbuf->data; + /* Bits 8 7 6 5 4 3 2 1 + * +--+--+--+--+--+--+--+--+ + * |version |PT| 1| E| S|PN| + * +--+--+--+--+--+--+--+--+ + * 0 0 1 1 0 0 0 0 + */ + gtp_h->flags = 0x30; + gtp_h->type = OGS_GTPU_MSGTYPE_GPDU; + gtp_h->length = htobe16(sendbuf->len - OGS_GTPV1U_HEADER_LEN); + gtp_h->teid = htobe32(far->outer_header_creation.teid); + } + + /* Send G-PDU */ + ogs_debug("SEND G-PDU to Peer[%s] : TEID[0x%x]", + OGS_ADDR(&gnode->addr, buf), far->outer_header_creation.teid); + rv = ogs_gtp_sendto(gnode, sendbuf); + if (rv != OGS_OK) + ogs_error("ogs_gtp_sendto() failed"); + + ogs_pkbuf_free(sendbuf); +} + +void ogs_pfcp_send_end_marker(ogs_pfcp_pdr_t *pdr) +{ + char buf[OGS_ADDRSTRLEN]; + int rv; + ogs_pkbuf_t *sendbuf = NULL; + + ogs_gtp_header_t *gtp_h = NULL; + ogs_gtp_extension_header_t *ext_h = NULL; + ogs_gtp_node_t *gnode = NULL; + + ogs_pfcp_far_t *far = NULL; + ogs_pfcp_qer_t *qer = NULL; + + ogs_assert(pdr); + far = pdr->far; + ogs_assert(far); + + gnode = far->gnode; + if (!gnode) { + ogs_error("No GTP Node Setup"); + return; + } + if (!gnode->sock) { + ogs_error("No GTP Socket Setup"); + return; + } + + sendbuf = ogs_pkbuf_alloc(NULL, + 100 /* enough for END_MARKER; use smaller buffer */); + ogs_pkbuf_put(sendbuf, 100); + memset(sendbuf->data, 0, sendbuf->len); + + gtp_h = (ogs_gtp_header_t *)sendbuf->data; + + qer = pdr->qer; + + /* Add GTP-U header */ + if (qer && qer->qfi) { + /* Bits 8 7 6 5 4 3 2 1 + * +--+--+--+--+--+--+--+--+ + * |version |PT| 1| E| S|PN| + * +--+--+--+--+--+--+--+--+ + * 0 0 1 1 0 1 0 0 + */ + gtp_h->flags = 0x34; + gtp_h->type = OGS_GTPU_MSGTYPE_END_MARKER; + gtp_h->teid = htobe32(far->outer_header_creation.teid); + + ext_h = (ogs_gtp_extension_header_t *)( + sendbuf->data + OGS_GTPV1U_HEADER_LEN); + ext_h->type = OGS_GTP_EXTENSION_HEADER_TYPE_PDU_SESSION_CONTAINER; + ext_h->len = 1; + ext_h->pdu_type = + OGS_GTP_EXTENSION_HEADER_PDU_TYPE_DL_PDU_SESSION_INFORMATION; + ext_h->qos_flow_identifier = qer->qfi; + ext_h->next_type = + OGS_GTP_EXTENSION_HEADER_TYPE_NO_MORE_EXTENSION_HEADERS; + } else { + /* Bits 8 7 6 5 4 3 2 1 + * +--+--+--+--+--+--+--+--+ + * |version |PT| 1| E| S|PN| + * +--+--+--+--+--+--+--+--+ + * 0 0 1 1 0 0 0 0 + */ + gtp_h->flags = 0x30; + gtp_h->type = OGS_GTPU_MSGTYPE_END_MARKER; + gtp_h->teid = htobe32(far->outer_header_creation.teid); + } + + /* Send End Marker */ + ogs_debug("SEND End Marker to Peer[%s] : TEID[0x%x]", + OGS_ADDR(&gnode->addr, buf), far->outer_header_creation.teid); + rv = ogs_gtp_sendto(gnode, sendbuf); + if (rv != OGS_OK) + ogs_error("ogs_gtp_sendto() failed"); + + ogs_pkbuf_free(sendbuf); +} + +void ogs_pfcp_send_buffered_packet(ogs_pfcp_pdr_t *pdr) +{ + ogs_pfcp_far_t *far = NULL; + int i; + + ogs_assert(pdr); + far = pdr->far; + + if (far && far->gnode) { + if (far->apply_action & OGS_PFCP_APPLY_ACTION_FORW) { + for (i = 0; i < far->num_of_buffered_packet; i++) { + ogs_pfcp_send_g_pdu(pdr, far->buffered_packet[i]); + } + far->num_of_buffered_packet = 0; + } + } +} + void ogs_pfcp_send_error_message( ogs_pfcp_xact_t *xact, uint64_t seid, uint8_t type, uint8_t cause_value, uint16_t offending_ie_value) diff --git a/lib/pfcp/path.h b/lib/pfcp/path.h index dddc0abc1..754e430bc 100644 --- a/lib/pfcp/path.h +++ b/lib/pfcp/path.h @@ -39,8 +39,25 @@ int ogs_pfcp_sendto(ogs_pfcp_node_t *node, ogs_pkbuf_t *pkbuf); ogs_pkbuf_t *ogs_pfcp_handle_echo_req(ogs_pkbuf_t *pkt); +void ogs_pfcp_send_heartbeat_request(ogs_pfcp_node_t *node, + void (*cb)(ogs_pfcp_xact_t *xact, void *data)); void ogs_pfcp_send_heartbeat_response(ogs_pfcp_xact_t *xact); +void ogs_pfcp_cp_send_association_setup_request(ogs_pfcp_node_t *node, + void (*cb)(ogs_pfcp_xact_t *xact, void *data)); +void ogs_pfcp_cp_send_association_setup_response(ogs_pfcp_xact_t *xact, + uint8_t cause); + +void ogs_pfcp_up_send_association_setup_request(ogs_pfcp_node_t *node, + void (*cb)(ogs_pfcp_xact_t *xact, void *data)); +void ogs_pfcp_up_send_association_setup_response(ogs_pfcp_xact_t *xact, + uint8_t cause); + +void ogs_pfcp_send_g_pdu(ogs_pfcp_pdr_t *pdr, ogs_pkbuf_t *sendbuf); +void ogs_pfcp_send_end_marker(ogs_pfcp_pdr_t *pdr); + +void ogs_pfcp_send_buffered_packet(ogs_pfcp_pdr_t *pdr); + void ogs_pfcp_send_error_message( ogs_pfcp_xact_t *xact, uint64_t seid, uint8_t type, uint8_t cause_value, uint16_t offending_ie_value); diff --git a/lib/pfcp/support/cache/tlv-group-list.py b/lib/pfcp/support/cache/tlv-group-list.py index 8f6777f8d..6003cdba3 100644 --- a/lib/pfcp/support/cache/tlv-group-list.py +++ b/lib/pfcp/support/cache/tlv-group-list.py @@ -28,8 +28,8 @@ ies.append({ "ie_type" : "F-TEID", "ie_value" : "Local F-TEID", "presence" : "O" ies.append({ "ie_type" : "Network Instance", "ie_value" : "Network Instance", "presence" : "O", "tlv_more" : "0", "comment" : "This IE shall not be present if Traffic Endpoint ID is present. It shall be present if the CP function requests the UP function to allocate a UE IP address/prefix and the Traffic Endpoint ID is not present.If present, this IE shall identify the Network instance to match for the incoming packet. See NOTE 1, NOTE2."}) ies.append({ "ie_type" : "UE IP Address", "ie_value" : "UE IP address", "presence" : "O", "tlv_more" : "0", "comment" : "This IE shall not be present if Traffic Endpoint ID is present.If present, this IE shall identify the source or destination IP address to match for the incoming packet. (NOTE 5)The CP function shall set the CHOOSE (CH) bit to 1 if the UP function supports the allocation of UE IP address/ prefix and the CP function requests the UP function to assign a UE IP address/prefix to the PDR."}) ies.append({ "ie_type" : "Traffic Endpoint ID", "ie_value" : "Traffic Endpoint ID", "presence" : "C", "tlv_more" : "0", "comment" : "This IE may be present if the UP function has indicated the support of PDI optimization.If present, this IE shall uniquely identify the Traffic Endpoint for that PFCP session.Several IEs with the same IE type may be present to provision several Traffic Endpoints with different Traffic Endpoint IDs, from which the UPF may receive packets pertaining to the same service data flow, which is subject for the same FAR, QER and URR, if the UPF has indicated it supports MTE feature as specified in clause 8.2.25. See NOTE 6."}) -type_list["SDF Filter"]["max_tlv_more"] = "3" -ies.append({ "ie_type" : "SDF Filter", "ie_value" : "SDF Filter", "presence" : "O", "tlv_more" : "3", "comment" : "If present, this IE shall identify the SDF filter to match for the incoming packet. Several IEs with the same IE type may be present to provision a list of SDF Filters. The full set of applicable SDF filters, if any, shall be provided during the creation or the modification of the PDI.See NOTE 3."}) +type_list["SDF Filter"]["max_tlv_more"] = "7" +ies.append({ "ie_type" : "SDF Filter", "ie_value" : "SDF Filter", "presence" : "O", "tlv_more" : "7", "comment" : "If present, this IE shall identify the SDF filter to match for the incoming packet. Several IEs with the same IE type may be present to provision a list of SDF Filters. The full set of applicable SDF filters, if any, shall be provided during the creation or the modification of the PDI.See NOTE 3."}) ies.append({ "ie_type" : "Application ID", "ie_value" : "Application ID", "presence" : "O", "tlv_more" : "0", "comment" : "If present, this IE shall identify the Application ID to match for the incoming packet. "}) ies.append({ "ie_type" : "Ethernet PDU Session Information", "ie_value" : "Ethernet PDU Session Information", "presence" : "O", "tlv_more" : "0", "comment" : "This IE may be present to identify all the (DL) Ethernet packets matching an Ethernet PDU session (see clause 5.13.1)."}) ies.append({ "ie_type" : "Ethernet Packet Filter", "ie_value" : "Ethernet Packet Filter", "presence" : "O", "tlv_more" : "0", "comment" : "If present, this IE shall identify the Ethernet PDU to match for the incoming packet.Several IEs with the same IE type may be present to represent a list of Ethernet Packet Filters.The full set of applicable Ethernet Packet filters, if any, shall be provided during the creation or the modification of the PDI."}) @@ -46,7 +46,7 @@ ies.append({ "ie_type" : "MAC address", "ie_value" : "MAC address", "presence" : ies.append({ "ie_type" : "Ethertype", "ie_value" : "Ethertype", "presence" : "O", "tlv_more" : "0", "comment" : "If present, this IE shall identify the Ethertype."}) ies.append({ "ie_type" : "C-TAG", "ie_value" : "C-TAG", "presence" : "O", "tlv_more" : "0", "comment" : "If present, this IE shall identify the Customer-VLAN tag."}) ies.append({ "ie_type" : "S-TAG", "ie_value" : "S-TAG", "presence" : "O", "tlv_more" : "0", "comment" : "If present, this IE shall identify the Service-VLAN tag."}) -ies.append({ "ie_type" : "SDF Filter", "ie_value" : "SDF Filter", "presence" : "O", "tlv_more" : "3", "comment" : "If packet filtering is required, for Ethernet frames with Ethertype indicating IPv4 or IPv6 payload, this IE shall describe the IP Packet Filter Set.Several IEs with the same IE type may be present to represent a list of SDF filters."}) +ies.append({ "ie_type" : "SDF Filter", "ie_value" : "SDF Filter", "presence" : "O", "tlv_more" : "7", "comment" : "If packet filtering is required, for Ethernet frames with Ethertype indicating IPv4 or IPv6 payload, this IE shall describe the IP Packet Filter Set.Several IEs with the same IE type may be present to represent a list of SDF filters."}) group_list["Ethernet Packet Filter"] = { "index" : "232", "type" : "132", "ies" : ies } ies = [] ies.append({ "ie_type" : "FAR ID", "ie_value" : "FAR ID", "presence" : "M", "tlv_more" : "0", "comment" : "This IE shall uniquely identify the FAR among all the FARs configured for that PFCP session."}) diff --git a/lib/pfcp/support/cache/tlv-msg-50.py b/lib/pfcp/support/cache/tlv-msg-50.py index f172e9eab..a7d780461 100644 --- a/lib/pfcp/support/cache/tlv-msg-50.py +++ b/lib/pfcp/support/cache/tlv-msg-50.py @@ -1,8 +1,8 @@ ies = [] ies.append({ "ie_type" : "Node ID", "ie_value" : "Node ID", "presence" : "M", "tlv_more" : "0", "comment" : "This IE shall contain the unique identifier of the sending Node."}) ies.append({ "ie_type" : "F-SEID", "ie_value" : "CP F-SEID", "presence" : "M", "tlv_more" : "0", "comment" : "This IE shall contain the unique identifier allocated by the CP function identifying the session."}) -ies.append({ "ie_type" : "Create PDR", "ie_value" : "Create PDR", "presence" : "M", "tlv_more" : "3", "comment" : "This IE shall be present for at least one PDR to be associated to the PFCP session.Several IEs with the same IE type may be present to represent multiple PDRs.See Table 7.5.2.2-1."}) -ies.append({ "ie_type" : "Create FAR", "ie_value" : "Create FAR", "presence" : "M", "tlv_more" : "3", "comment" : "This IE shall be present for at least one FAR to be associated to the PFCP session.Several IEs with the same IE type may be present to represent multiple FARs.See Table 7.5.2.3-1."}) +ies.append({ "ie_type" : "Create PDR", "ie_value" : "Create PDR", "presence" : "M", "tlv_more" : "7", "comment" : "This IE shall be present for at least one PDR to be associated to the PFCP session.Several IEs with the same IE type may be present to represent multiple PDRs.See Table 7.5.2.2-1."}) +ies.append({ "ie_type" : "Create FAR", "ie_value" : "Create FAR", "presence" : "M", "tlv_more" : "7", "comment" : "This IE shall be present for at least one FAR to be associated to the PFCP session.Several IEs with the same IE type may be present to represent multiple FARs.See Table 7.5.2.3-1."}) ies.append({ "ie_type" : "Create URR", "ie_value" : "Create URR", "presence" : "C", "tlv_more" : "1", "comment" : "This IE shall be present if a measurement action shall be applied to packets matching one or more PDR(s) of this PFCP session.Several IEs within the same IE type may be present to represent multiple URRs.See Table 7.5.2.4-1."}) ies.append({ "ie_type" : "Create QER", "ie_value" : "Create QER", "presence" : "C", "tlv_more" : "1", "comment" : "This IE shall be present if a QoS enforcement or QoS marking action shall be applied to packets matching one or more PDR(s) of this PFCP session.Several IEs within the same IE type may be present to represent multiple QERs.See Table 7.5.2.5-1."}) ies.append({ "ie_type" : "Create BAR", "ie_value" : "Create BAR", "presence" : "O", "tlv_more" : "0", "comment" : "When present, this IE shall contain the buffering instructions to be applied by the UP function to any FAR of this PFCP session set with the Apply Action requesting the packets to be buffered and with a BAR ID IE referring to this BAR. See table 7.5.2.6-1."}) diff --git a/lib/pfcp/support/cache/tlv-msg-51.py b/lib/pfcp/support/cache/tlv-msg-51.py index e4b9ed312..4fcd23da2 100644 --- a/lib/pfcp/support/cache/tlv-msg-51.py +++ b/lib/pfcp/support/cache/tlv-msg-51.py @@ -3,8 +3,8 @@ ies.append({ "ie_type" : "Node ID", "ie_value" : "Node ID", "presence" : "M", "t ies.append({ "ie_type" : "Cause", "ie_value" : "Cause", "presence" : "M", "tlv_more" : "0", "comment" : "This IE shall indicate the acceptance or the rejection of the corresponding request message."}) ies.append({ "ie_type" : "Offending IE", "ie_value" : "Offending IE", "presence" : "C", "tlv_more" : "0", "comment" : "This IE shall be included if the rejection is due to a conditional or mandatory IE missing or faulty."}) ies.append({ "ie_type" : "F-SEID", "ie_value" : "UP F-SEID", "presence" : "C", "tlv_more" : "0", "comment" : "This IE shall be present if the cause is set to Request accepted (success). When present, it shall contain the unique identifier allocated by the UP function identifing the session."}) -type_list["Created PDR"]["max_tlv_more"] = "3" -ies.append({ "ie_type" : "Created PDR", "ie_value" : "Created PDR", "presence" : "C", "tlv_more" : "3", "comment" : "This IE shall be present if the cause is set to success and the UP function was requested to allocate a local F-TEID or a UE IP address/prefix for the PDR.When present, this IE shall contain the PDR information associated to the PFCP session. There may be several instances of this IE.See table 7.5.3.2-1."}) +type_list["Created PDR"]["max_tlv_more"] = "7" +ies.append({ "ie_type" : "Created PDR", "ie_value" : "Created PDR", "presence" : "C", "tlv_more" : "7", "comment" : "This IE shall be present if the cause is set to success and the UP function was requested to allocate a local F-TEID or a UE IP address/prefix for the PDR.When present, this IE shall contain the PDR information associated to the PFCP session. There may be several instances of this IE.See table 7.5.3.2-1."}) ies.append({ "ie_type" : "Load Control Information", "ie_value" : "Load Control Information", "presence" : "O", "tlv_more" : "0", "comment" : "The UP function may include this IE if it supports the load control feature and the feature is activated in the network.See Table 7.5.3.3-1."}) ies.append({ "ie_type" : "Overload Control Information", "ie_value" : "Overload Control Information", "presence" : "O", "tlv_more" : "0", "comment" : "During an overload condition, the UP function may include this IE if it supports the overload control feature and the feature is activated in the network.See Table 7.5.3.4-1."}) ies.append({ "ie_type" : "FQ-CSID", "ie_value" : "SGW-U FQ-CSID", "presence" : "C", "tlv_more" : "0", "comment" : "This IE shall be included according to the requirements in clause 23 of 3GPPTS23.007[24]."}) diff --git a/lib/pfcp/support/cache/tlv-msg-52.py b/lib/pfcp/support/cache/tlv-msg-52.py index 1670fbbd3..efb4f1622 100644 --- a/lib/pfcp/support/cache/tlv-msg-52.py +++ b/lib/pfcp/support/cache/tlv-msg-52.py @@ -1,29 +1,29 @@ ies = [] ies.append({ "ie_type" : "F-SEID", "ie_value" : "CP F-SEID", "presence" : "C", "tlv_more" : "0", "comment" : "This IE shall be present if the CP function decides to change its F-SEID for the PFCP session. The UP function shall use the new CP F-SEID for subsequent PFCP Session related messages for this PFCP Session. See Note 2."}) -type_list["Remove PDR"]["max_tlv_more"] = "3" -ies.append({ "ie_type" : "Remove PDR", "ie_value" : "Remove PDR", "presence" : "C", "tlv_more" : "3", "comment" : "When present, this IE shall contain the PDR Rule which is requested to be removed. See Table 7.5.4-6-1.Several IEs within the same IE type may be present to represent a list of PDRs to remove."}) -type_list["Remove FAR"]["max_tlv_more"] = "3" -ies.append({ "ie_type" : "Remove FAR", "ie_value" : "Remove FAR", "presence" : "C", "tlv_more" : "3", "comment" : "When present, this IE shall contain the FAR Rule which is requested to be removed. See Table 7.5.4-7-1.Several IEs within the same IE type may be present to represent a list of FARs to remove."}) +type_list["Remove PDR"]["max_tlv_more"] = "7" +ies.append({ "ie_type" : "Remove PDR", "ie_value" : "Remove PDR", "presence" : "C", "tlv_more" : "7", "comment" : "When present, this IE shall contain the PDR Rule which is requested to be removed. See Table 7.5.4-6-1.Several IEs within the same IE type may be present to represent a list of PDRs to remove."}) +type_list["Remove FAR"]["max_tlv_more"] = "7" +ies.append({ "ie_type" : "Remove FAR", "ie_value" : "Remove FAR", "presence" : "C", "tlv_more" : "7", "comment" : "When present, this IE shall contain the FAR Rule which is requested to be removed. See Table 7.5.4-7-1.Several IEs within the same IE type may be present to represent a list of FARs to remove."}) type_list["Remove URR"]["max_tlv_more"] = "1" ies.append({ "ie_type" : "Remove URR", "ie_value" : "Remove URR", "presence" : "C", "tlv_more" : "1", "comment" : "When present, this shall contain the URR Rule which is requested to be removed. See Table 7.5.4-8-1.Several IEs within the same IE type may be present to represent a list of URRs to remove."}) type_list["Remove QER"]["max_tlv_more"] = "1" ies.append({ "ie_type" : "Remove QER", "ie_value" : "Remove QER", "presence" : "C", "tlv_more" : "1", "comment" : "When present, this IE shall contain the QER Rule which is requested to be removed. See Table 7.5.4-9-1.Several IEs within the same IE type may be present to represent a list of QERs to remove."}) ies.append({ "ie_type" : "Remove BAR", "ie_value" : "Remove BAR", "presence" : "C", "tlv_more" : "0", "comment" : "When present, this IE shall contain the BAR Rule which is requested to be removed. See Table 7.5.4.12-1."}) ies.append({ "ie_type" : "Remove Traffic Endpoint", "ie_value" : "Remove Traffic Endpoint", "presence" : "C", "tlv_more" : "0", "comment" : "When present, this IE shall contain the Traffic Endpoint ID identifying the traffic endpoint to be removed, if the UP function has indicated support of PDI optimization.All the PDRs that refer to the removed Traffic Endpoint shall be deleted.See Table 7.5.4.14-1."}) -type_list["Create PDR"]["max_tlv_more"] = "3" -ies.append({ "ie_type" : "Create PDR", "ie_value" : "Create PDR", "presence" : "C", "tlv_more" : "3", "comment" : "This IE shall be present if the CP function requests the UP function to create a new PDR.See Table 7.5.2.2-1.Several IEs within the same IE type may be present to represent a list of PDRs to create."}) -type_list["Create FAR"]["max_tlv_more"] = "3" -ies.append({ "ie_type" : "Create FAR", "ie_value" : "Create FAR", "presence" : "C", "tlv_more" : "3", "comment" : "This IE shall be present if the CP function requests the UP function to create a new FAR.See Table 7.5.2.3-1.Several IEs within the same IE type may be present to represent a list of FARs to create."}) +type_list["Create PDR"]["max_tlv_more"] = "7" +ies.append({ "ie_type" : "Create PDR", "ie_value" : "Create PDR", "presence" : "C", "tlv_more" : "7", "comment" : "This IE shall be present if the CP function requests the UP function to create a new PDR.See Table 7.5.2.2-1.Several IEs within the same IE type may be present to represent a list of PDRs to create."}) +type_list["Create FAR"]["max_tlv_more"] = "7" +ies.append({ "ie_type" : "Create FAR", "ie_value" : "Create FAR", "presence" : "C", "tlv_more" : "7", "comment" : "This IE shall be present if the CP function requests the UP function to create a new FAR.See Table 7.5.2.3-1.Several IEs within the same IE type may be present to represent a list of FARs to create."}) type_list["Create URR"]["max_tlv_more"] = "1" ies.append({ "ie_type" : "Create URR", "ie_value" : "Create URR", "presence" : "C", "tlv_more" : "1", "comment" : "This IE shall be present if the CP function requests the UP function to create a new URR. See Table 7.5.2.4-1.Several IEs within the same IE type may be present to represent a list of URRs to create."}) type_list["Create QER"]["max_tlv_more"] = "1" ies.append({ "ie_type" : "Create QER", "ie_value" : "Create QER", "presence" : "C", "tlv_more" : "1", "comment" : "This IE shall be present if the CP function requests the UP function to create a new QER. See Table 7.5.2.5-1.Several IEs within the same IE type may be present to represent a list of QERs to create."}) ies.append({ "ie_type" : "Create BAR", "ie_value" : "Create BAR", "presence" : "C", "tlv_more" : "0", "comment" : "This IE shall be present if the CP function requests the UP function to create a new BAR.See Table 7.5.2.6-1."}) ies.append({ "ie_type" : "Create Traffic Endpoint", "ie_value" : "Create Traffic Endpoint", "presence" : "C", "tlv_more" : "0", "comment" : "When present this IE shall contain the information associated with the Traffic Endpoint to be created, if the UP function has indicated support of PDI optimization. See Table 7.5.2.7-1."}) -type_list["Update PDR"]["max_tlv_more"] = "3" -ies.append({ "ie_type" : "Update PDR", "ie_value" : "Update PDR", "presence" : "C", "tlv_more" : "3", "comment" : "This IE shall be present if a PDR previously created for the PFCP session need to be modified. See Table 7.5.4.2-1.Several IEs within the same IE type may be present to represent a list of PDRs to update."}) -type_list["Update FAR"]["max_tlv_more"] = "3" -ies.append({ "ie_type" : "Update FAR", "ie_value" : "Update FAR", "presence" : "C", "tlv_more" : "3", "comment" : "This IE shall be present if a FAR previously created for the PFCP session need to be modified. See Table 7.5.4.3-1. Several IEs within the same IE type may be present to represent a list of FARs to update."}) +type_list["Update PDR"]["max_tlv_more"] = "7" +ies.append({ "ie_type" : "Update PDR", "ie_value" : "Update PDR", "presence" : "C", "tlv_more" : "7", "comment" : "This IE shall be present if a PDR previously created for the PFCP session need to be modified. See Table 7.5.4.2-1.Several IEs within the same IE type may be present to represent a list of PDRs to update."}) +type_list["Update FAR"]["max_tlv_more"] = "7" +ies.append({ "ie_type" : "Update FAR", "ie_value" : "Update FAR", "presence" : "C", "tlv_more" : "7", "comment" : "This IE shall be present if a FAR previously created for the PFCP session need to be modified. See Table 7.5.4.3-1. Several IEs within the same IE type may be present to represent a list of FARs to update."}) type_list["Update URR"]["max_tlv_more"] = "1" ies.append({ "ie_type" : "Update URR", "ie_value" : "Update URR", "presence" : "C", "tlv_more" : "1", "comment" : "This IE shall be present if URR(s) previously created for the PFCP session need to be modified.Several IEs within the same IE type may be present to represent a list of modified URRs. Previously URRs that are not modified shall not be included. See Table 7.5.4.4-1."}) type_list["Update QER"]["max_tlv_more"] = "1" diff --git a/lib/pfcp/support/cache/tlv-msg-53.py b/lib/pfcp/support/cache/tlv-msg-53.py index 08405ebc9..cf02d2bcd 100644 --- a/lib/pfcp/support/cache/tlv-msg-53.py +++ b/lib/pfcp/support/cache/tlv-msg-53.py @@ -1,7 +1,7 @@ ies = [] ies.append({ "ie_type" : "Cause", "ie_value" : "Cause", "presence" : "M", "tlv_more" : "0", "comment" : "This IE shall indicate the acceptance or the rejection of the corresponding request message."}) ies.append({ "ie_type" : "Offending IE", "ie_value" : "Offending IE", "presence" : "C", "tlv_more" : "0", "comment" : "This IE shall be included if the rejection is due to a conditional or mandatory IE missing or faulty."}) -ies.append({ "ie_type" : "Created PDR", "ie_value" : "Created PDR", "presence" : "C", "tlv_more" : "3", "comment" : "This IE shall be present if the cause is set to success, new PDR(s) were requested to be created and the UP function was requested to allocate the local F-TEID for the PDR(s).When present, this IE shall contain the PDR information associated to the PFCP session.See Table 7.5.3.2-1."}) +ies.append({ "ie_type" : "Created PDR", "ie_value" : "Created PDR", "presence" : "C", "tlv_more" : "7", "comment" : "This IE shall be present if the cause is set to success, new PDR(s) were requested to be created and the UP function was requested to allocate the local F-TEID for the PDR(s).When present, this IE shall contain the PDR information associated to the PFCP session.See Table 7.5.3.2-1."}) ies.append({ "ie_type" : "Load Control Information", "ie_value" : "Load Control Information", "presence" : "O", "tlv_more" : "0", "comment" : "The UP function may include this IE if it supports the load control feature and the feature is activated in the network.See Table 7.5.3.3-1."}) ies.append({ "ie_type" : "Overload Control Information", "ie_value" : "Overload Control Information", "presence" : "O", "tlv_more" : "0", "comment" : "During an overload condition, the UP function may include this IE if it supports the overload control feature and the feature is activated in the network."}) ies.append({ "ie_type" : "Usage Report Session Modification Response", "ie_value" : "Usage Report", "presence" : "C", "tlv_more" : "0", "comment" : "This IE shall be present if: - the Query URR IE was present or the QAURR flag was set to 1 in the PFCP Session Modification Request, - traffic usage measurements for that URR are available at the UP function, and - the UP function decides to return some or all of the requested usage reports in the PFCP Session Modification Response.This IE shall be also present if: - a URR or the last PDR associated to a URR has been removed, - non-null traffic usage measurements for that URR are available in the UP function, and - the UP function decides to return some or all of the related usage reports in the PFCP Session Modification Response (see clause 5.2.2.3.1).Several IEs within the same IE type may be present to represent a list of Usage Reports."}) diff --git a/lib/pfcp/support/pfcp-tlv.py b/lib/pfcp/support/pfcp-tlv.py index e88083c71..362d34d33 100644 --- a/lib/pfcp/support/pfcp-tlv.py +++ b/lib/pfcp/support/pfcp-tlv.py @@ -143,9 +143,9 @@ def get_cells(cells): tlv_more = "0" # PFCP has no tlv_more if ie_type == 'Create PDR' or ie_type == 'Created PDR' or ie_type == 'Update PDR' or ie_type == "Remove PDR": - tlv_more = "3" + tlv_more = "7" if ie_type == 'Create FAR' or ie_type == 'Update FAR' or ie_type == "Remove FAR": - tlv_more = "3" + tlv_more = "7" if ie_type == 'Create URR' or ie_type == 'Update URR' or ie_type == "Remove URR": tlv_more = "1" if ie_type == 'Create QER' or ie_type == 'Update QER' or ie_type == "Remove QER": @@ -153,7 +153,7 @@ def get_cells(cells): if ie_type == 'User Plane IP Resource Information': tlv_more = "3" if ie_type == 'SDF Filter': - tlv_more = "3" + tlv_more = "7" if int(tlv_more) > int(type_list[ie_type]["max_tlv_more"]): type_list[ie_type]["max_tlv_more"] = tlv_more @@ -408,10 +408,12 @@ type_list["Gate Status"]["size"] = 1 # Type 25 type_list["QER Correlation ID"]["size"] = 4 # Type 28 type_list["Precedence"]["size"] = 4 # Type 29 type_list["Reporting Triggers"]["size"] = 1 # Type 37 +type_list["Report Type"]["size"] = 1 # Type 39 type_list["Offending IE"]["size"] = 2 # Type 40 type_list["Destination Interface"]["size"] = 1 # Type 42 -type_list["UP Function Features"]["size"] = 2 # Type 43 type_list["Apply Action"]["size"] = 1 # Type 44 +type_list["PFCPSMReq-Flags"]["size"] = 1 # Type 49 +type_list["PFCPSRRsp-Flags"]["size"] = 1 # Type 50 type_list["PDR ID"]["size"] = 2 # Type 56 type_list["Measurement Method"]["size"] = 1 # Type 62 type_list["URR ID"]["size"] = 4 # Type 81 @@ -425,6 +427,8 @@ type_list["RQI"]["size"] = 1 # Type 123 type_list["QFI"]["size"] = 1 # Type 124 type_list["Averaging Window"]["size"] = 4 # Type 157 type_list["Paging Policy Indicator"]["size"] = 1 # Type 158 +type_list["PFCPSRReq-Flags"]["size"] = 1 # Type 161 +type_list["PFCPAUReq-Flags"]["size"] = 1 # Type 162 f = open(outdir + 'message.h', 'w') output_header_to_file(f) diff --git a/lib/pfcp/types.h b/lib/pfcp/types.h index 993ebc452..c1f832d15 100644 --- a/lib/pfcp/types.h +++ b/lib/pfcp/types.h @@ -96,6 +96,82 @@ typedef uint32_t ogs_pfcp_precedence_t; #define OGS_PFCP_INTERFACE_UNKNOWN 0xff typedef uint8_t ogs_pfcp_interface_t; +/* 8.2.25 UP Function Features */ +typedef struct ogs_pfcp_up_function_features_s { + union { + struct { +ED8(uint8_t treu:1;, + uint8_t heeu:1;, + uint8_t pfdm:1;, + uint8_t ftup:1;, + uint8_t trst:1;, + uint8_t dldb:1;, + uint8_t ddnd:1;, + uint8_t bucp:1;) + }; + uint8_t octet5; + }; + union { + struct { +ED8(uint8_t epfar:1;, + uint8_t pfde:1;, + uint8_t frrt:1;, + uint8_t trace:1;, + uint8_t quoac:1;, + uint8_t udbc:1;, + uint8_t pdiu:1;, + uint8_t empu:1;) + }; + uint8_t octet6; + }; + union { + struct { +ED8(uint8_t gcom:1;, + uint8_t bundl:1;, + uint8_t mte:1;, + uint8_t mnop:1;, + uint8_t sset:1;, + uint8_t ueip:1;, + uint8_t adpdp:1;, + uint8_t dpdra:1;) + }; + uint8_t octet7; + }; + union { + struct { +ED8(uint8_t mptcp:1;, + uint8_t tscu:1;, + uint8_t ip6pl:1;, + uint8_t iptv:1;, + uint8_t norp:1;, + uint8_t vtime:1;, + uint8_t rttl:1;, + uint8_t mpas:1;) + }; + uint8_t octet8; + }; + union { + struct { +ED8(uint8_t rds:1;, + uint8_t ddds:1;, + uint8_t ethar:1;, + uint8_t ciot:1;, + uint8_t mt_edt:1;, + uint8_t gpqm:1;, + uint8_t qfqm:1;, + uint8_t atsss_ll:1;) + }; + uint8_t octet9; + }; + union { + struct { +ED2(uint8_t reserved:7;, + uint8_t rttwp:1;) + }; + uint8_t octet10; + }; +} __attribute__ ((packed)) ogs_pfcp_up_function_features_t; + /* * 8.2.26 Apply Action * @@ -123,6 +199,23 @@ typedef uint8_t ogs_pfcp_interface_t; #define OGS_PFCP_APPLY_ACTION_DUPL 16 typedef uint8_t ogs_pfcp_apply_action_t; +/* 8.2.58 CP Function Features */ +typedef struct ogs_pfcp_cp_function_features_s { + union { + struct { +ED8(uint8_t reserved:1;, + uint8_t apdr:1;, + uint8_t mpas:1;, + uint8_t bundl:1;, + uint8_t sset:1;, + uint8_t epfar:1;, + uint8_t ovrl:1;, + uint8_t load:1;) + }; + uint8_t octet5; + }; +} __attribute__ ((packed)) ogs_pfcp_cp_function_features_t; + /* * 8.2.64 Outer Header Remaval * @@ -403,6 +496,14 @@ ED6(uint8_t spare:1;, uint8_t flags; }; + /* + * OGS_PFCP-GTPU-TEID = INDEX | TEID_RANGE + * INDEX = OGS_PFCP-GTPU-TEID & ~TEID_RANGE + */ +#define OGS_PFCP_GTPU_TEID_TO_INDEX(__tEID, __iND, __rANGE) \ + (__tEID & ~(__rANGE << (32 - __iND))) +#define OGS_PFCP_GTPU_INDEX_TO_TEID(__iNDEX, __iND, __rANGE) \ + (__iNDEX | (__rANGE << (32 - __iND))) uint8_t teid_range; uint32_t addr; uint8_t addr6[OGS_IPV6_LEN]; @@ -574,9 +675,96 @@ ED3(uint8_t spare:4;, }; } __attribute__ ((packed)) ogs_pfcp_gate_status_t; +/* + * 8.2.21 Report Type + * + * Octet 5 shall be encoded as follows: + * + * - Bit 1 – DLDR (Downlink Data Report): when set to 1, + * this indicates Downlink Data Report + * - Bit 2 – USAR (Usage Report): when set to 1, this indicates a Usage Report + * - Bit 3 – ERIR (Error Indication Report): when set to 1, + * this indicates an Error Indication Report. + * - Bit 4 – UPIR (User Plane Inactivity Report): when set to 1, + * this indicates a User Plane Inactivity Report. + * - Bit 5 to 8 – Spare, for future use and set to 0. + * + * At least one bit shall be set to 1. Several bits may be set to 1. + */ +typedef struct ogs_pfcp_report_type_s { + union { + struct { +ED5(uint8_t spare:4;, + uint8_t user_plane_inactivity_report:1;, + uint8_t error_indication_report:1;, + uint8_t usage_report:1;, + uint8_t downlink_data_report:1;) + }; + uint8_t value; + }; +} __attribute__ ((packed)) ogs_pfcp_report_type_t; + +typedef struct ogs_pfcp_downlink_data_service_information_s { + struct { +ED3(uint8_t spare:6;, + uint8_t qfii:1;, + uint8_t ppi:1;) + }; + union { + uint8_t paging_policy_indication_value; + uint8_t qfi; + struct { + uint8_t paging_policy_indication_value; + uint8_t qfi; + } both; + }; +} __attribute__ ((packed)) ogs_pfcp_downlink_data_service_information_t; + +/* + * 8.2.31 PFCPSMReq-Flags + * + * The following bits within Octet 5 shall indicate: + * - Bit 1 – DROBU (Drop Buffered Packets): if this bit is set to 1, + * it indicates that the UP function shall drop all the packets currently + * buffered for the PFCP session, if any, prior to further applying + * the action specified in the Apply Action value of the FARs. + * - Bit 2 – SNDEM (Send End Marker Packets): if this bit is set to 1, + * it indicates that the UP function shall construct and send End Marker + * packets towards the old F-TEID of the downstream node when switching + * to the new F- TEID. + * - Bit 3 – QAURR (Query All URRs): if this bit is set to 1, it indicates + * that the UP function shall return immediate usage report(s) + * for all the URRs previously provisioned for this PFCP session. + * - Bit 4 to 8 – Spare, for future use, shall be set to 0 by the sender and + * discarded by the receiver. + */ +typedef struct ogs_pfcp_smreq_flags_s { + union { + struct { +ED4(uint8_t spare:5;, + uint8_t query_all_urrs:1;, + uint8_t send_end_marker_packets:1;, + uint8_t drop_buffered_packets:1;) + }; + uint8_t value; + }; +} __attribute__ ((packed)) ogs_pfcp_smreq_flags_t; + +typedef struct ogs_pfcp_user_plane_report_s { + ogs_pfcp_report_type_t type; + struct { + uint8_t pdr_id; + uint8_t paging_policy_indication_value; + uint8_t qfi; + } downlink_data; + struct { + ogs_pfcp_f_teid_t remote_f_teid; + int remote_f_teid_len; + } error_indication; +} ogs_pfcp_user_plane_report_t; + #ifdef __cplusplus } #endif #endif /* OGS_PFCP_TYPES_H */ - diff --git a/lib/pfcp/xact.c b/lib/pfcp/xact.c index 2a204196f..7855e3737 100644 --- a/lib/pfcp/xact.c +++ b/lib/pfcp/xact.c @@ -692,6 +692,7 @@ static ogs_pfcp_xact_stage_t ogs_pfcp_xact_get_stage(uint8_t type, uint32_t xid) case OGS_PFCP_SESSION_ESTABLISHMENT_REQUEST_TYPE: case OGS_PFCP_SESSION_MODIFICATION_REQUEST_TYPE: case OGS_PFCP_SESSION_DELETION_REQUEST_TYPE: + case OGS_PFCP_SESSION_REPORT_REQUEST_TYPE: stage = PFCP_XACT_INITIAL_STAGE; break; case OGS_PFCP_HEARTBEAT_RESPONSE_TYPE: @@ -702,6 +703,7 @@ static ogs_pfcp_xact_stage_t ogs_pfcp_xact_get_stage(uint8_t type, uint32_t xid) case OGS_PFCP_SESSION_ESTABLISHMENT_RESPONSE_TYPE: case OGS_PFCP_SESSION_MODIFICATION_RESPONSE_TYPE: case OGS_PFCP_SESSION_DELETION_RESPONSE_TYPE: + case OGS_PFCP_SESSION_REPORT_RESPONSE_TYPE: stage = PFCP_XACT_FINAL_STAGE; break; diff --git a/lib/pfcp/xact.h b/lib/pfcp/xact.h index a12fd802c..e560f7ffb 100644 --- a/lib/pfcp/xact.h +++ b/lib/pfcp/xact.h @@ -62,22 +62,31 @@ typedef struct ogs_pfcp_xact_s { uint8_t holding_rcount; void *assoc_xact; /**< Associated GTP transaction */ + ogs_pkbuf_t *gtpbuf; /**< GTP packet buffer */ + void *assoc_session; /**< Associated SBI session */ -#define OGS_PFCP_5GC_MODIFY_CREATE ((uint64_t)1<<0) -#define OGS_PFCP_5GC_MODIFY_TFT_UPDATE ((uint64_t)1<<1) -#define OGS_PFCP_5GC_MODIFY_QOS_UPDATE ((uint64_t)1<<2) -#define OGS_PFCP_5GC_MODIFY_REMOVE ((uint64_t)1<<3) -#define OGS_PFCP_5GC_MODIFY_ACTIVATE ((uint64_t)1<<4) -#define OGS_PFCP_5GC_MODIFY_DEACTIVATE ((uint64_t)1<<5) - int modify_flags; + bool epc; /**< EPC or 5GC */ -#define OGS_PFCP_5GC_DELETE_TRIGGER_UE_REQUESTED 1 -#define OGS_PFCP_5GC_DELETE_TRIGGER_PCF_INITIATED 2 -#define OGS_PFCP_5GC_DELETE_TRIGGER_RAN_INITIATED 3 -#define OGS_PFCP_5GC_DELETE_TRIGGER_SMF_INITIATED 4 -#define OGS_PFCP_5GC_DELETE_TRIGGER_AMF_RELEASE_SM_CONTEXT 5 -#define OGS_PFCP_5GC_DELETE_TRIGGER_AMF_UPDATE_SM_CONTEXT 6 +#define OGS_PFCP_MODIFY_SESSION ((uint64_t)1<<0) +#define OGS_PFCP_MODIFY_DL_ONLY ((uint64_t)1<<1) +#define OGS_PFCP_MODIFY_UL_ONLY ((uint64_t)1<<2) +#define OGS_PFCP_MODIFY_INDIRECT ((uint64_t)1<<3) +#define OGS_PFCP_MODIFY_CREATE ((uint64_t)1<<4) +#define OGS_PFCP_MODIFY_REMOVE ((uint64_t)1<<5) +#define OGS_PFCP_MODIFY_TFT_UPDATE ((uint64_t)1<<6) +#define OGS_PFCP_MODIFY_QOS_UPDATE ((uint64_t)1<<7) +#define OGS_PFCP_MODIFY_ACTIVATE ((uint64_t)1<<8) +#define OGS_PFCP_MODIFY_DEACTIVATE ((uint64_t)1<<9) +#define OGS_PFCP_MODIFY_END_MARKER ((uint64_t)1<<10) + uint64_t modify_flags; + +#define OGS_PFCP_DELETE_TRIGGER_UE_REQUESTED 1 +#define OGS_PFCP_DELETE_TRIGGER_PCF_INITIATED 2 +#define OGS_PFCP_DELETE_TRIGGER_RAN_INITIATED 3 +#define OGS_PFCP_DELETE_TRIGGER_SMF_INITIATED 4 +#define OGS_PFCP_DELETE_TRIGGER_AMF_RELEASE_SM_CONTEXT 5 +#define OGS_PFCP_DELETE_TRIGGER_AMF_UPDATE_SM_CONTEXT 6 int delete_trigger; } ogs_pfcp_xact_t; diff --git a/lib/sbi/context.c b/lib/sbi/context.c index 1e9e77b2e..3af349e15 100644 --- a/lib/sbi/context.c +++ b/lib/sbi/context.c @@ -403,6 +403,9 @@ int ogs_sbi_context_parse_config(const char *local, const char *remote) ogs_config()->parameter.no_ipv4, ogs_config()->parameter.no_ipv6, ogs_config()->parameter.prefer_ipv4); + + if (addr == NULL) continue; + client = ogs_sbi_client_add(addr); ogs_assert(client); diff --git a/src/meson.build b/src/meson.build index dcd5a48ca..e226eab3e 100644 --- a/src/meson.build +++ b/src/meson.build @@ -32,6 +32,8 @@ configure_file(output : 'version.h', configuration : version_conf) subdir('mme') subdir('hss') subdir('sgw') +subdir('sgwc') +subdir('sgwu') subdir('pgw') subdir('pcrf') diff --git a/src/mme/mme-context.c b/src/mme/mme-context.c index a0de6bdad..a8bd3705f 100644 --- a/src/mme/mme-context.c +++ b/src/mme/mme-context.c @@ -1365,6 +1365,8 @@ int mme_context_parse_config() ogs_config()->parameter.no_ipv6, ogs_config()->parameter.prefer_ipv4); + if (addr == NULL) continue; + vlr = mme_vlr_add(addr); ogs_assert(vlr); @@ -1390,7 +1392,7 @@ int mme_context_parse_config() } else ogs_warn("unknown key `%s`", mme_key); } - } else if (!strcmp(root_key, "sgw")) { + } else if (!strcmp(root_key, "sgw") || !strcmp(root_key, "sgwc")) { ogs_yaml_iter_t sgw_iter; ogs_yaml_iter_recurse(&root_iter, &sgw_iter); while (ogs_yaml_iter_next(&sgw_iter)) { @@ -1533,6 +1535,8 @@ int mme_context_parse_config() ogs_config()->parameter.no_ipv6, ogs_config()->parameter.prefer_ipv4); + if (addr == NULL) continue; + sgw = mme_sgw_add(addr); ogs_assert(sgw); @@ -1639,6 +1643,8 @@ int mme_context_parse_config() ogs_config()->parameter.no_ipv6, ogs_config()->parameter.prefer_ipv4); + if (addr == NULL) continue; + pgw = mme_pgw_add(addr); ogs_assert(pgw); diff --git a/src/sgw/sgw-context.h b/src/sgw/sgw-context.h index 91d2c15c0..4f70171ab 100644 --- a/src/sgw/sgw-context.h +++ b/src/sgw/sgw-context.h @@ -106,7 +106,7 @@ typedef struct sgw_sess_s { */ #define SGW_S5C_TEID_TO_INDEX(__iNDEX) (__iNDEX & ~0x80000000) #define SGW_S5C_INDEX_TO_TEID(__iNDEX) (__iNDEX | 0x80000000) - uint32_t sgw_s5c_teid; /* SGW-S5C-TEID is derived from INDEX */ + uint32_t sgw_s5c_teid; /* SGW-S5C-TEID is derived from INDEX */ uint32_t pgw_s5c_teid; /* PGW-S5C-TEID is received from PGW */ /* APN Configuration */ diff --git a/src/sgw/sgw-s11-handler.c b/src/sgw/sgw-s11-handler.c index 2ea4dcbb8..6ffe1c4c6 100644 --- a/src/sgw/sgw-s11-handler.c +++ b/src/sgw/sgw-s11-handler.c @@ -574,7 +574,8 @@ void sgw_s11_handle_create_bearer_response(ogs_gtp_xact_t *s11_xact, ogs_assert(req->user_location_information.len == decoded); memcpy(&bearer->tai.plmn_id, &uli.tai.plmn_id, sizeof(uli.tai.plmn_id)); bearer->tai.tac = uli.tai.tac; - memcpy(&bearer->e_cgi.plmn_id, &uli.e_cgi.plmn_id, sizeof(uli.e_cgi.plmn_id)); + memcpy(&bearer->e_cgi.plmn_id, + &uli.e_cgi.plmn_id, sizeof(uli.e_cgi.plmn_id)); bearer->e_cgi.cell_id = uli.e_cgi.cell_id; /* Reset UE state */ @@ -1284,4 +1285,3 @@ void sgw_s11_handle_bearer_resource_command(ogs_gtp_xact_t *s11_xact, rv = ogs_gtp_xact_commit(s5c_xact); ogs_expect(rv == OGS_OK); } - diff --git a/src/sgwc/app.c b/src/sgwc/app.c new file mode 100644 index 000000000..d49326e02 --- /dev/null +++ b/src/sgwc/app.c @@ -0,0 +1,40 @@ +/* + * 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 "ogs-app.h" + +int app_initialize(const char *const argv[]) +{ + int rv; + + rv = sgwc_initialize(); + if (rv != OGS_OK) { + ogs_error("Failed to intialize SGW-C"); + return rv; + } + ogs_info("SGW-C initialize...done"); + + return OGS_OK; +} + +void app_terminate(void) +{ + sgwc_terminate(); + ogs_info("SGW-C terminate...done"); +} diff --git a/src/sgwc/context.c b/src/sgwc/context.c new file mode 100644 index 000000000..9621b522e --- /dev/null +++ b/src/sgwc/context.c @@ -0,0 +1,832 @@ +/* + * 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 + +#include "context.h" + +static sgwc_context_t self; + +int __sgwc_log_domain; + +static OGS_POOL(sgwc_ue_pool, sgwc_ue_t); +static OGS_POOL(sgwc_sess_pool, sgwc_sess_t); +static OGS_POOL(sgwc_bearer_pool, sgwc_bearer_t); +static OGS_POOL(sgwc_tunnel_pool, sgwc_tunnel_t); + +static int context_initialized = 0; + +void sgwc_context_init(void) +{ + ogs_assert(context_initialized == 0); + + memset(&self, 0, sizeof(sgwc_context_t)); + + ogs_log_install_domain(&__ogs_gtp_domain, "gtp", ogs_core()->log.level); + ogs_log_install_domain(&__sgwc_log_domain, "sgwc", ogs_core()->log.level); + + ogs_list_init(&self.gtpc_list); + ogs_list_init(&self.gtpc_list6); + + ogs_gtp_node_init(512); + ogs_list_init(&self.mme_s11_list); + ogs_list_init(&self.pgw_s5c_list); + ogs_list_init(&self.enb_s1u_list); + ogs_list_init(&self.pgw_s5u_list); + + ogs_pool_init(&sgwc_ue_pool, ogs_config()->pool.ue); + ogs_pool_init(&sgwc_sess_pool, ogs_config()->pool.sess); + ogs_pool_init(&sgwc_bearer_pool, ogs_config()->pool.bearer); + ogs_pool_init(&sgwc_tunnel_pool, ogs_config()->pool.tunnel); + + self.imsi_ue_hash = ogs_hash_make(); + + ogs_list_init(&self.sgw_ue_list); + + context_initialized = 1; +} + +void sgwc_context_final(void) +{ + ogs_assert(context_initialized == 1); + + sgwc_ue_remove_all(); + + ogs_assert(self.imsi_ue_hash); + ogs_hash_destroy(self.imsi_ue_hash); + + ogs_pool_final(&sgwc_tunnel_pool); + ogs_pool_final(&sgwc_bearer_pool); + ogs_pool_final(&sgwc_sess_pool); + ogs_pool_final(&sgwc_ue_pool); + + ogs_gtp_node_remove_all(&self.mme_s11_list); + ogs_gtp_node_remove_all(&self.pgw_s5c_list); + ogs_gtp_node_remove_all(&self.enb_s1u_list); + ogs_gtp_node_remove_all(&self.pgw_s5u_list); + ogs_gtp_node_final(); + + context_initialized = 0; +} + +sgwc_context_t *sgwc_self(void) +{ + return &self; +} + +static int sgwc_context_prepare(void) +{ + self.gtpc_port = OGS_GTPV2_C_UDP_PORT; + + return OGS_OK; +} + +static int sgwc_context_validation(void) +{ + if (ogs_list_empty(&self.gtpc_list) && + ogs_list_empty(&self.gtpc_list6)) { + ogs_error("No sgwc.gtpc in '%s'", ogs_config()->file); + return OGS_ERROR; + } + return OGS_OK; +} + +int sgwc_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 = sgwc_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, "sgwc")) { + ogs_yaml_iter_t sgwc_iter; + ogs_yaml_iter_recurse(&root_iter, &sgwc_iter); + while (ogs_yaml_iter_next(&sgwc_iter)) { + const char *sgwc_key = ogs_yaml_iter_key(&sgwc_iter); + ogs_assert(sgwc_key); + if (!strcmp(sgwc_key, "gtpc")) { + ogs_yaml_iter_t gtpc_array, gtpc_iter; + ogs_yaml_iter_recurse(&sgwc_iter, >pc_array); + do { + int family = AF_UNSPEC; + int i, num = 0; + const char *hostname[OGS_MAX_NUM_OF_HOSTNAME]; + uint16_t port = self.gtpc_port; + const char *dev = NULL; + ogs_sockaddr_t *addr = NULL; + + if (ogs_yaml_iter_type(>pc_array) == + YAML_MAPPING_NODE) { + memcpy(>pc_iter, >pc_array, + sizeof(ogs_yaml_iter_t)); + } else if (ogs_yaml_iter_type(>pc_array) == + YAML_SEQUENCE_NODE) { + if (!ogs_yaml_iter_next(>pc_array)) + break; + ogs_yaml_iter_recurse(>pc_array, >pc_iter); + } else if (ogs_yaml_iter_type(>pc_array) == + YAML_SCALAR_NODE) { + break; + } else + ogs_assert_if_reached(); + + while (ogs_yaml_iter_next(>pc_iter)) { + const char *gtpc_key = + ogs_yaml_iter_key(>pc_iter); + ogs_assert(gtpc_key); + if (!strcmp(gtpc_key, "family")) { + const char *v = ogs_yaml_iter_value(>pc_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(gtpc_key, "addr") || + !strcmp(gtpc_key, "name")) { + ogs_yaml_iter_t hostname_iter; + ogs_yaml_iter_recurse(>pc_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(gtpc_key, "port")) { + const char *v = ogs_yaml_iter_value(>pc_iter); + if (v) port = atoi(v); + } else if (!strcmp(gtpc_key, "dev")) { + dev = ogs_yaml_iter_value(>pc_iter); + } else + ogs_warn("unknown key `%s`", gtpc_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.gtpc_list, AF_INET, addr); + if (ogs_config()->parameter.no_ipv6 == 0) + ogs_socknode_add( + &self.gtpc_list6, AF_INET6, addr); + ogs_freeaddrinfo(addr); + } + + if (dev) { + rv = ogs_socknode_probe( + ogs_config()->parameter.no_ipv4 ? + NULL : &self.gtpc_list, + ogs_config()->parameter.no_ipv6 ? + NULL : &self.gtpc_list6, + dev, port); + ogs_assert(rv == OGS_OK); + } + + } while (ogs_yaml_iter_type(>pc_array) == + YAML_SEQUENCE_NODE); + + if (ogs_list_empty(&self.gtpc_list) && + ogs_list_empty(&self.gtpc_list6)) { + rv = ogs_socknode_probe( + ogs_config()->parameter.no_ipv4 ? + NULL : &self.gtpc_list, + ogs_config()->parameter.no_ipv6 ? + NULL : &self.gtpc_list6, + NULL, self.gtpc_port); + ogs_assert(rv == OGS_OK); + } + } else if (!strcmp(sgwc_key, "pfcp")) { + /* handle config in pfcp library */ + } else + ogs_warn("unknown key `%s`", sgwc_key); + } + } + } + + rv = sgwc_context_validation(); + if (rv != OGS_OK) return rv; + + return OGS_OK; +} + +sgwc_ue_t *sgwc_ue_add_by_message(ogs_gtp_message_t *message) +{ + sgwc_ue_t *sgwc_ue = NULL; + ogs_gtp_create_session_request_t *req = &message->create_session_request; + + ogs_assert(message); + + req = &message->create_session_request; + if (req->imsi.presence == 0) { + ogs_error("No IMSI"); + return NULL; + } + + ogs_trace("sgwc_ue_add_by_message() - IMSI "); + ogs_log_hexdump(OGS_LOG_TRACE, req->imsi.data, req->imsi.len); + + /* + * 7.2.1 in 3GPP TS 29.274 Release 15 + * + * If the new Create Session Request received by the SGW collides with + * an existing active PDN connection context (the existing PDN connection + * context is identified with the tuple [IMSI, EPS Bearer ID], where IMSI + * shall be replaced by TAC and SNR part of ME Identity for emergency + * attached UE without UICC or authenticated IMSI), this Create Session + * Request shall be treated as a request for a new session. Before creating + * the new session, the SGW should delete: + * + * - the existing PDN connection context locally, if the Create Session + * Request is received with the TEID set to zero in the header, or + * if it is received with a TEID not set to zero in the header and + * it collides with the default bearer of an existing PDN connection + * context; + * - the existing dedicated bearer context locally, if the Create Session + * Request collides with an existing dedicated bearer context and + * the message is received with a TEID not set to zero in the header. + */ + sgwc_ue = sgwc_ue_find_by_imsi(req->imsi.data, req->imsi.len); + if (sgwc_ue) + sgwc_ue_remove(sgwc_ue); + sgwc_ue = sgwc_ue_add(req->imsi.data, req->imsi.len); + ogs_assert(sgwc_ue); + + return sgwc_ue; +} + +sgwc_ue_t *sgwc_ue_add(uint8_t *imsi, int imsi_len) +{ + sgwc_ue_t *sgwc_ue = NULL; + + ogs_assert(imsi); + ogs_assert(imsi_len); + + ogs_pool_alloc(&sgwc_ue_pool, &sgwc_ue); + ogs_assert(sgwc_ue); + memset(sgwc_ue, 0, sizeof *sgwc_ue); + + sgwc_ue->sgw_s11_teid = ogs_pool_index(&sgwc_ue_pool, sgwc_ue); + ogs_assert(sgwc_ue->sgw_s11_teid > 0 && + sgwc_ue->sgw_s11_teid <= ogs_config()->pool.ue); + + /* Set IMSI */ + sgwc_ue->imsi_len = imsi_len; + memcpy(sgwc_ue->imsi, imsi, sgwc_ue->imsi_len); + ogs_buffer_to_bcd(sgwc_ue->imsi, sgwc_ue->imsi_len, sgwc_ue->imsi_bcd); + + ogs_list_init(&sgwc_ue->sess_list); + + ogs_hash_set(self.imsi_ue_hash, sgwc_ue->imsi, sgwc_ue->imsi_len, sgwc_ue); + + ogs_list_add(&self.sgw_ue_list, sgwc_ue); + + return sgwc_ue; +} + +int sgwc_ue_remove(sgwc_ue_t *sgwc_ue) +{ + ogs_assert(sgwc_ue); + + ogs_list_remove(&self.sgw_ue_list, sgwc_ue); + + ogs_hash_set(self.imsi_ue_hash, sgwc_ue->imsi, sgwc_ue->imsi_len, NULL); + + sgwc_sess_remove_all(sgwc_ue); + + ogs_pool_free(&sgwc_ue_pool, sgwc_ue); + + return OGS_OK; +} + +void sgwc_ue_remove_all(void) +{ + sgwc_ue_t *sgwc_ue = NULL, *next = NULL;; + + ogs_list_for_each_safe(&self.sgw_ue_list, next, sgwc_ue) + sgwc_ue_remove(sgwc_ue); +} + +sgwc_ue_t *sgwc_ue_find_by_imsi_bcd(char *imsi_bcd) +{ + uint8_t imsi[OGS_MAX_IMSI_LEN]; + int imsi_len = 0; + + ogs_assert(imsi_bcd); + + ogs_bcd_to_buffer(imsi_bcd, imsi, &imsi_len); + + return sgwc_ue_find_by_imsi(imsi, imsi_len); +} + +sgwc_ue_t *sgwc_ue_find_by_imsi(uint8_t *imsi, int imsi_len) +{ + ogs_assert(imsi && imsi_len); + + return (sgwc_ue_t *)ogs_hash_get(self.imsi_ue_hash, imsi, imsi_len); +} + +sgwc_ue_t *sgwc_ue_find_by_teid(uint32_t teid) +{ + return ogs_pool_find(&sgwc_ue_pool, teid); +} + +sgwc_sess_t *sgwc_sess_add(sgwc_ue_t *sgwc_ue, char *apn) +{ + sgwc_sess_t *sess = NULL; + + ogs_assert(sgwc_ue); + + ogs_pool_alloc(&sgwc_sess_pool, &sess); + if (!sess) { + ogs_error("Maximum number of session[%d] reached", + ogs_config()->pool.sess); + return NULL; + } + memset(sess, 0, sizeof *sess); + + sess->index = ogs_pool_index(&sgwc_sess_pool, sess); + ogs_assert(sess->index > 0 && sess->index <= ogs_config()->pool.sess); + + /* Set TEID & SEID */ + sess->sgw_s5c_teid = sess->index; + sess->sgwc_sxa_seid = sess->index; + + /* Indirect tunnel ID generator */ + sess->indirect_pdr_id = OGS_MAX_NUM_OF_PDR; + sess->indirect_far_id = OGS_MAX_NUM_OF_FAR; + + /* Set APN */ + ogs_cpystrn(sess->pdn.apn, apn, OGS_MAX_APN_LEN+1); + + sess->sgwc_ue = sgwc_ue; + + ogs_list_add(&sgwc_ue->sess_list, sess); + + return sess; +} + +static bool compare_ue_info(ogs_pfcp_node_t *node, sgwc_sess_t *sess) +{ + sgwc_ue_t *sgwc_ue = NULL; + int i; + + ogs_assert(node); + ogs_assert(sess); + sgwc_ue = sess->sgwc_ue; + ogs_assert(sgwc_ue); + + for (i = 0; i < node->num_of_tac; i++) + if (node->tac[i] == sgwc_ue->e_tai.tac) return true; + + for (i = 0; i < node->num_of_e_cell_id; i++) + if (node->e_cell_id[i] == sgwc_ue->e_cgi.cell_id) return true; + + for (i = 0; i < node->num_of_apn; i++) + if (strcmp(node->apn[i], sess->pdn.apn) == 0) return true; + + return false; +} + +static ogs_pfcp_node_t *selected_sgwu_node( + ogs_pfcp_node_t *current, sgwc_sess_t *sess) +{ + ogs_pfcp_node_t *next, *node; + + ogs_assert(current); + ogs_assert(sess); + + next = ogs_list_next(current); + for (node = next; node; node = ogs_list_next(node)) { + if (OGS_FSM_CHECK(&node->sm, sgwc_pfcp_state_associated) && + compare_ue_info(node, sess) == true) return node; + } + + for (node = ogs_list_first(&ogs_pfcp_self()->peer_list); + node != next; node = ogs_list_next(node)) { + if (OGS_FSM_CHECK(&node->sm, sgwc_pfcp_state_associated) && + compare_ue_info(node, sess) == true) return node; + } + + return next ? next : ogs_list_first(&ogs_pfcp_self()->peer_list); +} + +void sgwc_sess_select_sgwu(sgwc_sess_t *sess) +{ + char buf[OGS_ADDRSTRLEN]; + + ogs_assert(sess); + + /* + * When used for the first time, if last node is set, + * the search is performed from the first SGW-U in a round-robin manner. + */ + if (ogs_pfcp_self()->node == NULL) + ogs_pfcp_self()->node = ogs_list_last(&ogs_pfcp_self()->peer_list); + + /* setup GTP session with selected SGW-U */ + ogs_pfcp_self()->node = selected_sgwu_node(ogs_pfcp_self()->node, sess); + ogs_assert(ogs_pfcp_self()->node); + OGS_SETUP_PFCP_NODE(sess, ogs_pfcp_self()->node); + ogs_debug("UE using SGW-U on IP[%s]", + OGS_ADDR(&ogs_pfcp_self()->node->addr, buf)); + + /* iterate to next SGW-U in list for next UE attach */ + ogs_pfcp_self()->node = ogs_list_next(ogs_pfcp_self()->node); +} + +int sgwc_sess_remove(sgwc_sess_t *sess) +{ + sgwc_ue_t *sgwc_ue = NULL; + + ogs_assert(sess); + sgwc_ue = sess->sgwc_ue; + ogs_assert(sgwc_ue); + + ogs_list_remove(&sgwc_ue->sess_list, sess); + + sgwc_bearer_remove_all(sess); + + ogs_pool_free(&sgwc_sess_pool, sess); + + return OGS_OK; +} + +void sgwc_sess_remove_all(sgwc_ue_t *sgwc_ue) +{ + sgwc_sess_t *sess = NULL, *next_sess = NULL; + + ogs_assert(sgwc_ue); + ogs_list_for_each_safe(&sgwc_ue->sess_list, next_sess, sess) + sgwc_sess_remove(sess); +} + +sgwc_sess_t *sgwc_sess_find(uint32_t index) +{ + ogs_assert(index); + return ogs_pool_find(&sgwc_sess_pool, index); +} + +sgwc_sess_t* sgwc_sess_find_by_teid(uint32_t teid) +{ + return ogs_pool_find(&sgwc_sess_pool, teid); +} + +sgwc_sess_t *sgwc_sess_find_by_seid(uint64_t seid) +{ + return sgwc_sess_find(seid); +} + +sgwc_sess_t* sgwc_sess_find_by_apn(sgwc_ue_t *sgwc_ue, char *apn) +{ + sgwc_sess_t *sess = NULL; + + ogs_assert(sgwc_ue); + ogs_assert(apn); + + ogs_list_for_each(&sgwc_ue->sess_list, sess) { + if (!strcmp(sess->pdn.apn, apn)) + return sess; + } + + return NULL; +} + +sgwc_sess_t *sgwc_sess_find_by_ebi(sgwc_ue_t *sgwc_ue, uint8_t ebi) +{ + sgwc_bearer_t *bearer = NULL; + ogs_assert(sgwc_ue); + + bearer = sgwc_bearer_find_by_ue_ebi(sgwc_ue, ebi); + if (bearer) + return bearer->sess; + + return NULL; +} + +sgwc_bearer_t *sgwc_bearer_add(sgwc_sess_t *sess) +{ + sgwc_bearer_t *bearer = NULL; + sgwc_tunnel_t *tunnel = NULL; + sgwc_ue_t *sgwc_ue = NULL; + + ogs_assert(sess); + sgwc_ue = sess->sgwc_ue; + ogs_assert(sgwc_ue); + + ogs_pool_alloc(&sgwc_bearer_pool, &bearer); + ogs_assert(bearer); + memset(bearer, 0, sizeof *bearer); + + bearer->sgwc_ue = sgwc_ue; + bearer->sess = sess; + + /* Downlink */ + tunnel = sgwc_tunnel_add(bearer, OGS_GTP_F_TEID_S5_S8_SGW_GTP_U); + ogs_assert(tunnel); + + /* Uplink */ + tunnel = sgwc_tunnel_add(bearer, OGS_GTP_F_TEID_S1_U_SGW_GTP_U); + ogs_assert(tunnel); + + ogs_list_add(&sess->bearer_list, bearer); + + return bearer; +} + +int sgwc_bearer_remove(sgwc_bearer_t *bearer) +{ + int i; + + ogs_assert(bearer); + ogs_assert(bearer->sess); + + ogs_list_remove(&bearer->sess->bearer_list, bearer); + + sgwc_tunnel_remove_all(bearer); + + ogs_pfcp_sess_clear(&bearer->pfcp); + + /* Free the buffered packets */ + for (i = 0; i < bearer->num_buffered_pkt; i++) + ogs_pkbuf_free(bearer->buffered_pkts[i]); + + ogs_pool_free(&sgwc_bearer_pool, bearer); + + return OGS_OK; +} + +void sgwc_bearer_remove_all(sgwc_sess_t *sess) +{ + sgwc_bearer_t *bearer = NULL, *next_bearer = NULL; + + ogs_assert(sess); + ogs_list_for_each_safe(&sess->bearer_list, next_bearer, bearer) + sgwc_bearer_remove(bearer); +} + +sgwc_bearer_t *sgwc_bearer_find_by_sgwc_s5u_teid(uint32_t sgwc_s5u_teid) +{ + return ogs_pool_find(&sgwc_bearer_pool, sgwc_s5u_teid); +} + +sgwc_bearer_t *sgwc_bearer_find_by_sess_ebi(sgwc_sess_t *sess, uint8_t ebi) +{ + sgwc_bearer_t *bearer = NULL; + + ogs_assert(sess); + ogs_list_for_each(&sess->bearer_list, bearer) + if (ebi == bearer->ebi) return bearer; + + return NULL; +} + +sgwc_bearer_t *sgwc_bearer_find_by_ue_ebi(sgwc_ue_t *sgwc_ue, uint8_t ebi) +{ + sgwc_sess_t *sess = NULL; + sgwc_bearer_t *bearer = NULL; + + ogs_assert(sgwc_ue); + ogs_list_for_each(&sgwc_ue->sess_list, sess) { + ogs_list_for_each(&sess->bearer_list, bearer) { + if (ebi == bearer->ebi) return bearer; + } + } + + return NULL; +} + +sgwc_bearer_t *sgwc_default_bearer_in_sess(sgwc_sess_t *sess) +{ + ogs_assert(sess); + return ogs_list_first(&sess->bearer_list); +} + +sgwc_tunnel_t *sgwc_tunnel_add( + sgwc_bearer_t *bearer, uint8_t interface_type) +{ + sgwc_sess_t *sess = NULL; + sgwc_tunnel_t *tunnel = NULL; + ogs_pfcp_gtpu_resource_t *resource = NULL; + + ogs_pfcp_pdr_t *pdr = NULL; + ogs_pfcp_far_t *far = NULL; + + uint8_t src_if, dst_if; + + ogs_assert(bearer); + sess = bearer->sess; + ogs_assert(sess); + + switch (interface_type) { + /* Downlink */ + case OGS_GTP_F_TEID_S5_S8_SGW_GTP_U: + src_if = OGS_PFCP_INTERFACE_CORE; + dst_if = OGS_PFCP_INTERFACE_ACCESS; + break; + + /* Uplink */ + case OGS_GTP_F_TEID_S1_U_SGW_GTP_U: + src_if = OGS_PFCP_INTERFACE_ACCESS; + dst_if = OGS_PFCP_INTERFACE_CORE; + break; + + /* Indirect */ + case OGS_GTP_F_TEID_SGW_GTP_U_FOR_DL_DATA_FORWARDING: + case OGS_GTP_F_TEID_SGW_GTP_U_FOR_UL_DATA_FORWARDING: + src_if = OGS_PFCP_INTERFACE_ACCESS; + dst_if = OGS_PFCP_INTERFACE_ACCESS; + break; + default: + ogs_fatal("Invalid interface type = %d", interface_type); + } + + ogs_pool_alloc(&sgwc_tunnel_pool, &tunnel); + ogs_assert(tunnel); + memset(tunnel, 0, sizeof *tunnel); + + tunnel->interface_type = interface_type; + tunnel->index = ogs_pool_index(&sgwc_tunnel_pool, tunnel); + ogs_assert(tunnel->index > 0 && tunnel->index <= ogs_config()->pool.tunnel); + + pdr = ogs_pfcp_pdr_add(&bearer->pfcp); + ogs_assert(pdr); + pdr->id = OGS_NEXT_ID(sess->pdr_id, 1, OGS_MAX_NUM_OF_PDR+1); + pdr->src_if = src_if; + + if (strlen(sess->pdn.apn)) + pdr->apn = ogs_strdup(sess->pdn.apn); + + pdr->outer_header_removal_len = 1; + if (sess->pdn.pdn_type == OGS_GTP_PDN_TYPE_IPV4) { + pdr->outer_header_removal.description = + OGS_PFCP_OUTER_HEADER_REMOVAL_GTPU_UDP_IPV4; + } else if (sess->pdn.pdn_type == OGS_GTP_PDN_TYPE_IPV6) { + pdr->outer_header_removal.description = + OGS_PFCP_OUTER_HEADER_REMOVAL_GTPU_UDP_IPV6; + } else if (sess->pdn.pdn_type == OGS_GTP_PDN_TYPE_IPV4V6) { + pdr->outer_header_removal.description = + OGS_PFCP_OUTER_HEADER_REMOVAL_GTPU_UDP_IP; + } else + ogs_assert_if_reached(); + + far = ogs_pfcp_far_add(&bearer->pfcp); + ogs_assert(far); + far->id = OGS_NEXT_ID(sess->far_id, 1, OGS_MAX_NUM_OF_FAR+1); + far->dst_if = dst_if; + ogs_pfcp_pdr_associate_far(pdr, far); + + if (interface_type == OGS_GTP_F_TEID_SGW_GTP_U_FOR_DL_DATA_FORWARDING || + interface_type == OGS_GTP_F_TEID_SGW_GTP_U_FOR_UL_DATA_FORWARDING) { + + /* + * If it is an indirect tunnel, + * it will use a different ID space + * to avoid collisions with normal tunnels. + */ + pdr->id = OGS_NEXT_ID(sess->indirect_pdr_id, + OGS_MAX_NUM_OF_PDR, OGS_MAX_NUM_OF_PDR*2); + far->id = OGS_NEXT_ID(sess->indirect_far_id, + OGS_MAX_NUM_OF_FAR, OGS_MAX_NUM_OF_FAR*2); + } + + ogs_assert(sess->pfcp_node); + resource = ogs_pfcp_gtpu_resource_find( + &sess->pfcp_node->gtpu_resource_list, + sess->pdn.apn, OGS_PFCP_INTERFACE_ACCESS); + if (resource) { + ogs_pfcp_user_plane_ip_resource_info_to_sockaddr(&resource->info, + &tunnel->local_addr, &tunnel->local_addr6); + ogs_assert(tunnel->local_addr || tunnel->local_addr6); + if (resource->info.teidri) + tunnel->local_teid = OGS_PFCP_GTPU_INDEX_TO_TEID( + tunnel->index, resource->info.teidri, + resource->info.teid_range); + else + tunnel->local_teid = tunnel->index; + } else { + if (sess->pfcp_node->addr.ogs_sa_family == AF_INET) + ogs_copyaddrinfo(&tunnel->local_addr, &sess->pfcp_node->addr); + else if (sess->pfcp_node->addr.ogs_sa_family == AF_INET6) + ogs_copyaddrinfo(&tunnel->local_addr6, &sess->pfcp_node->addr); + else + ogs_assert_if_reached(); + ogs_assert(tunnel->local_addr || tunnel->local_addr6); + + tunnel->local_teid = tunnel->index; + } + + ogs_pfcp_sockaddr_to_f_teid(tunnel->local_addr, tunnel->local_addr6, + &pdr->f_teid, &pdr->f_teid_len); + pdr->f_teid.teid = tunnel->local_teid; + + tunnel->pdr = pdr; + tunnel->far = far; + + tunnel->bearer = bearer; + + ogs_list_add(&bearer->tunnel_list, tunnel); + + return tunnel; +} + +int sgwc_tunnel_remove(sgwc_tunnel_t *tunnel) +{ + ogs_assert(tunnel); + ogs_assert(tunnel->bearer); + + ogs_list_remove(&tunnel->bearer->tunnel_list, tunnel); + + if (tunnel->local_addr) + ogs_freeaddrinfo(tunnel->local_addr); + if (tunnel->local_addr6) + ogs_freeaddrinfo(tunnel->local_addr6); + + ogs_pool_free(&sgwc_tunnel_pool, tunnel); + + return OGS_OK; +} + +void sgwc_tunnel_remove_all(sgwc_bearer_t *bearer) +{ + sgwc_tunnel_t *tunnel = NULL, *next_tunnel = NULL; + + ogs_assert(bearer); + ogs_list_for_each_safe(&bearer->tunnel_list, next_tunnel, tunnel) + sgwc_tunnel_remove(tunnel); +} + +sgwc_tunnel_t *sgwc_tunnel_find_by_teid(uint32_t teid) +{ + return ogs_pool_find(&sgwc_tunnel_pool, teid); +} + +sgwc_tunnel_t *sgwc_tunnel_find_by_interface_type( + sgwc_bearer_t *bearer, uint8_t interface_type) +{ + sgwc_tunnel_t *tunnel = NULL; + + ogs_assert(bearer); + + ogs_list_for_each(&bearer->tunnel_list, tunnel) + if (tunnel->interface_type == interface_type) return tunnel; + + return NULL; +} + +sgwc_tunnel_t *sgwc_dl_tunnel_in_bearer(sgwc_bearer_t *bearer) +{ + ogs_assert(bearer); + return sgwc_tunnel_find_by_interface_type(bearer, + OGS_GTP_F_TEID_S5_S8_SGW_GTP_U); +} +sgwc_tunnel_t *sgwc_ul_tunnel_in_bearer(sgwc_bearer_t *bearer) +{ + ogs_assert(bearer); + return sgwc_tunnel_find_by_interface_type(bearer, + OGS_GTP_F_TEID_S1_U_SGW_GTP_U); +} diff --git a/src/sgwc/context.h b/src/sgwc/context.h new file mode 100644 index 000000000..4ea46de32 --- /dev/null +++ b/src/sgwc/context.h @@ -0,0 +1,225 @@ +/* + * 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 SGWC_CONTEXT_H +#define SGWC_CONTEXT_H + +#include "ogs-app.h" +#include "ogs-gtp.h" +#include "ogs-pfcp.h" + +#include "timer.h" +#include "sgwc-sm.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern int __sgwc_log_domain; + +#undef OGS_LOG_DOMAIN +#define OGS_LOG_DOMAIN __sgwc_log_domain + +typedef struct sgwc_tunnel_s sgwc_tunnel_t; + +typedef struct sgwc_context_s { + uint32_t gtpc_port; /* Default GTPC port */ + + ogs_list_t gtpc_list; /* SGW GTPC IPv4 Server List */ + ogs_list_t gtpc_list6; /* SGW GTPC IPv6 Server List */ + ogs_sock_t *gtpc_sock; /* SGW GTPC IPv4 Socket */ + ogs_sock_t *gtpc_sock6; /* SGW GTPC IPv6 Socket */ + ogs_sockaddr_t *gtpc_addr; /* SGW GTPC IPv4 Address */ + ogs_sockaddr_t *gtpc_addr6; /* SGW GTPC IPv6 Address */ + + ogs_queue_t *queue; /* Queue for processing SGW control */ + ogs_timer_mgr_t *timer_mgr; /* Timer Manager */ + ogs_pollset_t *pollset; /* Poll Set for I/O Multiplexing */ + + ogs_list_t mme_s11_list; /* MME GTPC Node List */ + ogs_list_t pgw_s5c_list; /* PGW GTPC Node List */ + ogs_list_t enb_s1u_list; /* eNB GTPU Node List */ + ogs_list_t pgw_s5u_list; /* PGW GTPU Node List */ + + ogs_hash_t *imsi_ue_hash; /* hash table (IMSI : SGW_UE) */ + + ogs_list_t sgw_ue_list; /* SGW_UE List */ +} sgwc_context_t; + +typedef struct sgwc_ue_s { + ogs_lnode_t lnode; + + uint32_t sgw_s11_teid; /* SGW-S11-TEID is derived from INDEX */ + uint32_t mme_s11_teid; /* MME-S11-TEID is received from MME */ + + /* UE identity */ + uint8_t imsi[OGS_MAX_IMSI_LEN]; + int imsi_len; + char imsi_bcd[OGS_MAX_IMSI_BCD_LEN+1]; + + /* User-Location-Info */ + ogs_eps_tai_t e_tai; + ogs_e_cgi_t e_cgi; + + ogs_list_t sess_list; + + ogs_gtp_node_t *gnode; +} sgwc_ue_t; + +typedef struct sgwc_sess_s { + ogs_lnode_t lnode; /* A node of list_t */ + uint32_t index; /**< An index of this node */ + + uint32_t sgw_s5c_teid; /* SGW-S5C-TEID is derived from INDEX */ + uint32_t pgw_s5c_teid; /* PGW-S5C-TEID is received from PGW */ + + uint64_t sgwc_sxa_seid; /* SGW-C SEID is dervied from INDEX */ + uint64_t sgwu_sxa_seid; /* SGW-U SEID is received from Peer */ + + /* + * PFCP modification request is set to FALSE + * PFCP modifitation response is set to TRUE + * + * For example, when SGW-C is received Release Access Bearers Request, + * it is used to check if all sessions are deactivated. + */ + struct { + bool release_access_bearers; + bool create_indirect_tunnel; + bool delete_indirect_tunnel; + } state; + + ogs_pfcp_pdr_id_t pdr_id; /* ID Generator(1~OGS_MAX_NUM_OF_PDR) */ + ogs_pfcp_far_id_t far_id; /* ID Generator(1~OGS_MAX_NUM_OF_FAR) */ + ogs_pfcp_urr_id_t urr_id; /* ID Generator(1~OGS_MAX_NUM_OF_URR) */ + ogs_pfcp_qer_id_t qer_id; /* ID Generator(1~OGS_MAX_NUM_OF_URR) */ + ogs_pfcp_bar_id_t bar_id; /* ID Generator(1~OGS_MAX_NUM_OF_BAR) */ + + /* ID Generator(OGS_MAX_NUM_OF_PDR~OGS_MAX_NUM_OF_PDR*2) */ + ogs_pfcp_pdr_id_t indirect_pdr_id; + /* ID Generator(OGS_MAX_NUM_OF_FAR~OGS_MAX_NUM_OF_FAR*2) */ + ogs_pfcp_far_id_t indirect_far_id; + + /* APN Configuration */ + ogs_pdn_t pdn; + + ogs_list_t bearer_list; + + /* Related Context */ + ogs_gtp_node_t *gnode; + ogs_pfcp_node_t *pfcp_node; + + sgwc_ue_t *sgwc_ue; +} sgwc_sess_t; + +#define SGWC_BEARER(pfcp_sess) ogs_container_of(pfcp_sess, sgwc_bearer_t, pfcp) +typedef struct sgwc_bearer_s { + ogs_lnode_t lnode; + + ogs_pfcp_sess_t pfcp; /* PFCP session context */ + + uint8_t ebi; + + /* Pkts which will be buffered in case of UE-IDLE */ + uint32_t num_buffered_pkt; + +#define MAX_NUM_OF_PACKET_BUFFER 512 + ogs_pkbuf_t* buffered_pkts[MAX_NUM_OF_PACKET_BUFFER]; + + ogs_list_t tunnel_list; + sgwc_sess_t *sess; + sgwc_ue_t *sgwc_ue; +} sgwc_bearer_t; + +typedef struct sgwc_tunnel_s { + ogs_lnode_t lnode; + uint32_t index; /**< An index of this node */ + + uint8_t interface_type; + + ogs_pfcp_pdr_t *pdr; + ogs_pfcp_far_t *far; + + uint32_t local_teid; + ogs_sockaddr_t *local_addr; + ogs_sockaddr_t *local_addr6; + + uint32_t remote_teid; + ogs_ip_t remote_ip; + + /* Related Context */ + sgwc_bearer_t *bearer; + ogs_gtp_node_t *gnode; +} sgwc_tunnel_t; + +void sgwc_context_init(void); +void sgwc_context_final(void); +sgwc_context_t *sgwc_self(void); + +int sgwc_context_parse_config(void); + +sgwc_ue_t *sgwc_ue_add_by_message(ogs_gtp_message_t *message); +sgwc_ue_t *sgwc_ue_find_by_imsi(uint8_t *imsi, int imsi_len); +sgwc_ue_t *sgwc_ue_find_by_imsi_bcd(char *imsi_bcd); +sgwc_ue_t *sgwc_ue_find_by_teid(uint32_t teid); + +sgwc_ue_t *sgwc_ue_add(uint8_t *imsi, int imsi_len); +int sgwc_ue_remove(sgwc_ue_t *sgwc_ue); +void sgwc_ue_remove_all(void); + +sgwc_sess_t *sgwc_sess_add(sgwc_ue_t *sgwc_ue, char *apn); + +void sgwc_sess_select_sgwu(sgwc_sess_t *sess); + +int sgwc_sess_remove(sgwc_sess_t *sess); +void sgwc_sess_remove_all(sgwc_ue_t *sgwc_ue); + +sgwc_sess_t *sgwc_sess_find(uint32_t index); +sgwc_sess_t *sgwc_sess_find_by_teid(uint32_t teid); +sgwc_sess_t *sgwc_sess_find_by_seid(uint64_t seid); + +sgwc_sess_t *sgwc_sess_find_by_apn(sgwc_ue_t *sgwc_ue, char *apn); +sgwc_sess_t *sgwc_sess_find_by_ebi(sgwc_ue_t *sgwc_ue, uint8_t ebi); + +sgwc_bearer_t *sgwc_bearer_add(sgwc_sess_t *sess); +int sgwc_bearer_remove(sgwc_bearer_t *bearer); +void sgwc_bearer_remove_all(sgwc_sess_t *sess); +sgwc_bearer_t *sgwc_bearer_find_by_sgwc_s5u_teid( + uint32_t sgwc_s5u_teid); +sgwc_bearer_t *sgwc_bearer_find_by_sess_ebi( + sgwc_sess_t *sess, uint8_t ebi); +sgwc_bearer_t *sgwc_bearer_find_by_ue_ebi( + sgwc_ue_t *sgwc_ue, uint8_t ebi); +sgwc_bearer_t *sgwc_default_bearer_in_sess(sgwc_sess_t *sess); + +sgwc_tunnel_t *sgwc_tunnel_add( + sgwc_bearer_t *bearer, uint8_t interface_type); +int sgwc_tunnel_remove(sgwc_tunnel_t *tunnel); +void sgwc_tunnel_remove_all(sgwc_bearer_t *bearer); +sgwc_tunnel_t *sgwc_tunnel_find_by_teid(uint32_t teid); +sgwc_tunnel_t *sgwc_tunnel_find_by_interface_type( + sgwc_bearer_t *bearer, uint8_t interface_type); +sgwc_tunnel_t *sgwc_dl_tunnel_in_bearer(sgwc_bearer_t *bearer); +sgwc_tunnel_t *sgwc_ul_tunnel_in_bearer(sgwc_bearer_t *bearer); + +#ifdef __cplusplus +} +#endif + +#endif /* SGWC_CONTEXT_H */ diff --git a/src/sgwc/event.c b/src/sgwc/event.c new file mode 100644 index 000000000..09aac5d35 --- /dev/null +++ b/src/sgwc/event.c @@ -0,0 +1,103 @@ +/* + * 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 "event.h" +#include "context.h" + +static OGS_POOL(pool, sgwc_event_t); + +#define EVENT_POOL 32 /* FIXME : 32 */ +void sgwc_event_init(void) +{ + ogs_pool_init(&pool, EVENT_POOL); + + sgwc_self()->queue = ogs_queue_create(EVENT_POOL); + ogs_assert(sgwc_self()->queue); + sgwc_self()->timer_mgr = ogs_timer_mgr_create(); + ogs_assert(sgwc_self()->timer_mgr); + sgwc_self()->pollset = ogs_pollset_create(); + ogs_assert(sgwc_self()->pollset); +} + +void sgwc_event_term(void) +{ + ogs_queue_term(sgwc_self()->queue); + ogs_pollset_notify(sgwc_self()->pollset); +} + +void sgwc_event_final(void) +{ + if (sgwc_self()->pollset) + ogs_pollset_destroy(sgwc_self()->pollset); + if (sgwc_self()->timer_mgr) + ogs_timer_mgr_destroy(sgwc_self()->timer_mgr); + if (sgwc_self()->queue) + ogs_queue_destroy(sgwc_self()->queue); + + ogs_pool_final(&pool); +} + +sgwc_event_t *sgwc_event_new(sgwc_event_e id) +{ + sgwc_event_t *e = NULL; + + ogs_pool_alloc(&pool, &e); + ogs_assert(e); + memset(e, 0, sizeof(*e)); + + e->id = id; + + return e; +} + +void sgwc_event_free(sgwc_event_t *e) +{ + ogs_assert(e); + ogs_pool_free(&pool, e); +} + +const char *sgwc_event_get_name(sgwc_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 SGWC_EVT_S11_MESSAGE: + return "SGWC_EVT_S11_MESSAGE"; + case SGWC_EVT_S5C_MESSAGE: + return "SGWC_EVT_S5C_MESSAGE"; + + case SGWC_EVT_SXA_MESSAGE: + return "SGWC_EVT_SXA_MESSAGE"; + case SGWC_EVT_SXA_TIMER: + return "SGWC_EVT_SXA_TIMER"; + case SGWC_EVT_SXA_NO_HEARTBEAT: + return "SGWC_EVT_SXA_NO_HEARTBEAT"; + + default: + break; + } + + return "UNKNOWN_EVENT"; +} diff --git a/src/sgwc/event.h b/src/sgwc/event.h new file mode 100644 index 000000000..270ef7d1b --- /dev/null +++ b/src/sgwc/event.h @@ -0,0 +1,78 @@ +/* + * 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 SGWC_EVENT_H +#define SGWC_EVENT_H + +#include "ogs-core.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct ogs_gtp_node_s ogs_gtp_node_t; +typedef struct ogs_gtp_message_s ogs_gtp_message_t; +typedef struct ogs_pfcp_node_s ogs_pfcp_node_t; +typedef struct ogs_pfcp_xact_s ogs_pfcp_xact_t; +typedef struct ogs_pfcp_message_s ogs_pfcp_message_t; +typedef struct sgwc_bearer_s sgwc_bearer_t; + +typedef enum { + SGWC_EVT_BASE = OGS_FSM_USER_SIG, + + SGWC_EVT_S11_MESSAGE, + SGWC_EVT_S5C_MESSAGE, + + SGWC_EVT_SXA_MESSAGE, + SGWC_EVT_SXA_TIMER, + SGWC_EVT_SXA_NO_HEARTBEAT, + + SGWC_EVT_TOP, + +} sgwc_event_e; + +typedef struct sgwc_event_s { + int id; + ogs_pkbuf_t *pkbuf; + int timer_id; + + ogs_gtp_node_t *gnode; + ogs_gtp_message_t *gtp_message; + + ogs_pfcp_node_t *pfcp_node; + ogs_pfcp_xact_t *pfcp_xact; + ogs_pfcp_message_t *pfcp_message; + + sgwc_bearer_t *bearer; +} sgwc_event_t; + +void sgwc_event_init(void); +void sgwc_event_term(void); +void sgwc_event_final(void); + +sgwc_event_t *sgwc_event_new(sgwc_event_e id); +void sgwc_event_free(sgwc_event_t *e); + +const char *sgwc_event_get_name(sgwc_event_t *e); + +#ifdef __cplusplus +} +#endif + +#endif /* SGWC_EVENT_H */ diff --git a/src/sgwc/gtp-path.c b/src/sgwc/gtp-path.c new file mode 100644 index 000000000..6acb07816 --- /dev/null +++ b/src/sgwc/gtp-path.c @@ -0,0 +1,191 @@ +/* + * 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 "gtp-path.h" + +static ogs_pkbuf_pool_t *packet_pool = NULL; + +static void _gtpv2_c_recv_cb(short when, ogs_socket_t fd, void *data) +{ + sgwc_event_t *e = NULL; + int rv; + ssize_t size; + ogs_pkbuf_t *pkbuf = NULL; + ogs_sockaddr_t from; + ogs_gtp_node_t *gnode = NULL; + + ogs_assert(fd != INVALID_SOCKET); + + pkbuf = ogs_pkbuf_alloc(NULL, OGS_MAX_SDU_LEN); + ogs_pkbuf_put(pkbuf, OGS_MAX_SDU_LEN); + + size = ogs_recvfrom(fd, pkbuf->data, pkbuf->len, 0, &from); + if (size <= 0) { + ogs_log_message(OGS_LOG_ERROR, ogs_socket_errno, + "ogs_recvfrom() failed"); + ogs_pkbuf_free(pkbuf); + return; + } + + ogs_pkbuf_trim(pkbuf, size); + + /* + * 5.5.2 in spec 29.274 + * + * If a peer's TEID is not available, the TEID field still shall be + * present in the header and its value shall be set to "0" in the + * following messages: + * + * - Create Session Request message on S2a/S2b/S5/S8 + * + * - Create Session Request message on S4/S11, if for a given UE, + * the SGSN/MME has not yet obtained the Control TEID of the SGW. + * + * - If a node receives a message and the TEID-C in the GTPv2 header of + * the received message is not known, it shall respond with + * "Context not found" Cause in the corresponding response message + * to the sender, the TEID used in the GTPv2-C header in the response + * message shall be then set to zero. + * + * - If a node receives a request message containing protocol error, + * e.g. Mandatory IE missing, which requires the receiver to reject + * the message as specified in clause 7.7, it shall reject + * the request message. For the response message, the node should + * look up the remote peer's TEID and accordingly set the GTPv2-C + * header TEID and the message cause code. As an implementation + * option, the node may not look up the remote peer's TEID and + * set the GTPv2-C header TEID to zero in the response message. + * However in this case, the cause code shall not be set to + * "Context not found". + */ + gnode = ogs_gtp_node_find_by_addr(&sgwc_self()->pgw_s5c_list, &from); + if (gnode) { + e = sgwc_event_new(SGWC_EVT_S5C_MESSAGE); + ogs_assert(e); + e->gnode = gnode; + } else { + e = sgwc_event_new(SGWC_EVT_S11_MESSAGE); + gnode = ogs_gtp_node_find_by_addr(&sgwc_self()->mme_s11_list, &from); + if (!gnode) { + gnode = ogs_gtp_node_add_by_addr(&sgwc_self()->mme_s11_list, &from); + ogs_assert(gnode); + gnode->sock = data; + } + ogs_assert(e); + e->gnode = gnode; + } + + e->pkbuf = pkbuf; + + rv = ogs_queue_push(sgwc_self()->queue, e); + if (rv != OGS_OK) { + ogs_error("ogs_queue_push() failed:%d", (int)rv); + ogs_pkbuf_free(e->pkbuf); + sgwc_event_free(e); + } +} + +int sgwc_gtp_open(void) +{ + ogs_socknode_t *node = NULL; + ogs_sock_t *sock = NULL; + + ogs_pkbuf_config_t config; + memset(&config, 0, sizeof config); + + config.cluster_8192_pool = ogs_config()->pool.packet; + + packet_pool = ogs_pkbuf_pool_create(&config); + + ogs_list_for_each(&sgwc_self()->gtpc_list, node) { + sock = ogs_gtp_server(node); + ogs_assert(sock); + + node->poll = ogs_pollset_add(sgwc_self()->pollset, + OGS_POLLIN, sock->fd, _gtpv2_c_recv_cb, sock); + } + ogs_list_for_each(&sgwc_self()->gtpc_list6, node) { + sock = ogs_gtp_server(node); + ogs_assert(sock); + + node->poll = ogs_pollset_add(sgwc_self()->pollset, + OGS_POLLIN, sock->fd, _gtpv2_c_recv_cb, sock); + } + + sgwc_self()->gtpc_sock = ogs_socknode_sock_first(&sgwc_self()->gtpc_list); + if (sgwc_self()->gtpc_sock) + sgwc_self()->gtpc_addr = &sgwc_self()->gtpc_sock->local_addr; + + sgwc_self()->gtpc_sock6 = ogs_socknode_sock_first(&sgwc_self()->gtpc_list6); + if (sgwc_self()->gtpc_sock6) + sgwc_self()->gtpc_addr6 = &sgwc_self()->gtpc_sock6->local_addr; + + ogs_assert(sgwc_self()->gtpc_addr || sgwc_self()->gtpc_addr6); + + return OGS_OK; +} + +void sgwc_gtp_close(void) +{ + ogs_socknode_remove_all(&sgwc_self()->gtpc_list); + ogs_socknode_remove_all(&sgwc_self()->gtpc_list6); + + ogs_pkbuf_pool_destroy(packet_pool); +} + +void sgwc_gtp_send_downlink_data_notification( + sgwc_bearer_t *bearer, ogs_pfcp_xact_t *pfcp_xact) +{ + int rv; + + sgwc_ue_t *sgwc_ue = NULL; + sgwc_sess_t *sess = NULL; + + ogs_gtp_xact_t *gtp_xact = NULL; + + ogs_pkbuf_t *pkbuf = NULL; + ogs_gtp_header_t h; + + ogs_assert(bearer); + ogs_assert(pfcp_xact); + + sess = bearer->sess; + ogs_assert(sess); + sgwc_ue = bearer->sgwc_ue; + ogs_assert(sgwc_ue); + + ogs_debug("Downlink Data Notification"); + ogs_debug(" MME_S11_TEID[%d] SGW_S11_TEID[%d]", + sgwc_ue->mme_s11_teid, sgwc_ue->sgw_s11_teid); + + memset(&h, 0, sizeof(ogs_gtp_header_t)); + h.type = OGS_GTP_DOWNLINK_DATA_NOTIFICATION_TYPE; + h.teid = sgwc_ue->mme_s11_teid; + + pkbuf = sgwc_s11_build_downlink_data_notification(bearer); + ogs_expect_or_return(pkbuf); + + gtp_xact = ogs_gtp_xact_local_create( + sgwc_ue->gnode, &h, pkbuf, NULL, sess); + ogs_expect_or_return(gtp_xact); + gtp_xact->pfcp_xact = pfcp_xact; + + rv = ogs_gtp_xact_commit(gtp_xact); + ogs_expect(rv == OGS_OK); +} diff --git a/src/sgwc/gtp-path.h b/src/sgwc/gtp-path.h new file mode 100644 index 000000000..b9c5cf483 --- /dev/null +++ b/src/sgwc/gtp-path.h @@ -0,0 +1,39 @@ +/* + * 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 SGWC_GTP_PATH_H +#define SGWC_GTP_PATH_H + +#include "s11-build.h" + +#ifdef __cplusplus +extern "C" { +#endif + +int sgwc_gtp_open(void); +void sgwc_gtp_close(void); + +void sgwc_gtp_send_downlink_data_notification( + sgwc_bearer_t *bearer, ogs_pfcp_xact_t *pfcp_xact); + +#ifdef __cplusplus +} +#endif + +#endif /* SGWC_GTP_PATH_H */ diff --git a/src/sgwc/init.c b/src/sgwc/init.c new file mode 100644 index 000000000..45277fe91 --- /dev/null +++ b/src/sgwc/init.c @@ -0,0 +1,122 @@ +/* + * 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 "context.h" + +static ogs_thread_t *thread; +static void sgwc_main(void *data); + +static int initialized = 0; + +int sgwc_initialize() +{ + int rv; + + ogs_pfcp_context_init(ogs_config()->max.upf * OGS_MAX_NUM_OF_GTPU_RESOURCE); + sgwc_context_init(); + sgwc_event_init(); + + rv = ogs_gtp_xact_init(sgwc_self()->timer_mgr, 512); + if (rv != OGS_OK) return rv; + + rv = ogs_pfcp_xact_init(sgwc_self()->timer_mgr, 512); + if (rv != OGS_OK) return rv; + + rv = ogs_pfcp_context_parse_config("sgwc", "sgwu"); + if (rv != OGS_OK) return rv; + + rv = sgwc_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(sgwc_main, NULL); + if (!thread) return OGS_ERROR; + + initialized = 1; + + return OGS_OK; +} + +void sgwc_terminate(void) +{ + if (!initialized) return; + + sgwc_event_term(); + + ogs_thread_destroy(thread); + + sgwc_context_final(); + ogs_pfcp_context_final(); + + ogs_pfcp_xact_final(); + ogs_gtp_xact_final(); + + sgwc_event_final(); +} + +static void sgwc_main(void *data) +{ + ogs_fsm_t sgwc_sm; + int rv; + + ogs_fsm_create(&sgwc_sm, sgwc_state_initial, sgwc_state_final); + ogs_fsm_init(&sgwc_sm, 0); + + for ( ;; ) { + ogs_pollset_poll(sgwc_self()->pollset, + ogs_timer_mgr_next(sgwc_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(sgwc_self()->timer_mgr); + + for ( ;; ) { + sgwc_event_t *e = NULL; + + rv = ogs_queue_trypop(sgwc_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(&sgwc_sm, e); + sgwc_event_free(e); + } + } +done: + + ogs_fsm_fini(&sgwc_sm, 0); + ogs_fsm_delete(&sgwc_sm); +} diff --git a/src/sgwc/meson.build b/src/sgwc/meson.build new file mode 100644 index 000000000..d0942a73b --- /dev/null +++ b/src/sgwc/meson.build @@ -0,0 +1,70 @@ +# 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 . + +libsgwc_sources = files(''' + event.h + timer.h + context.h + gtp-path.h + s11-build.h + s11-handler.h + s5c-handler.h + sxa-build.h + sxa-handler.h + pfcp-path.h + sgwc-sm.h + + init.c + event.c + timer.c + context.c + gtp-path.c + s11-build.c + s11-handler.c + s5c-handler.c + sxa-build.c + sxa-handler.c + pfcp-path.c + pfcp-sm.c + sgwc-sm.c +'''.split()) + +libsgwc = static_library('sgwc', + sources : libsgwc_sources, + dependencies : [libapp_dep, + libgtp_dep, + libpfcp_dep], + install : false) + +libsgwc_dep = declare_dependency( + link_with : libsgwc, + dependencies : [libapp_dep, + libgtp_dep, + libpfcp_dep]) + +sgwc_sources = files(''' + app.c + ../main.c +'''.split()) + +executable('open5gs-sgwcd', + sources : sgwc_sources, + c_args : '-DDEFAULT_CONFIG_FILENAME="@0@/sgwc.yaml"'.format(open5gs_sysconfdir), + include_directories : srcinc, + dependencies : libsgwc_dep, + install_rpath : libdir, + install : true) diff --git a/src/sgwc/pfcp-path.c b/src/sgwc/pfcp-path.c new file mode 100644 index 000000000..6cbbfa8de --- /dev/null +++ b/src/sgwc/pfcp-path.c @@ -0,0 +1,339 @@ +/* + * 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 "pfcp-path.h" +#if 0 +#include "n4-build.h" +#endif + +static void pfcp_node_fsm_init(ogs_pfcp_node_t *node, bool try_to_assoicate) +{ + sgwc_event_t e; + + ogs_assert(node); + + memset(&e, 0, sizeof(e)); + e.pfcp_node = node; + + if (try_to_assoicate == true) { + node->t_association = ogs_timer_add(sgwc_self()->timer_mgr, + sgwc_timer_pfcp_association, node); + ogs_assert(node->t_association); + } + + ogs_fsm_create(&node->sm, sgwc_pfcp_state_initial, sgwc_pfcp_state_final); + ogs_fsm_init(&node->sm, &e); +} + +static void pfcp_node_fsm_fini(ogs_pfcp_node_t *node) +{ + sgwc_event_t e; + + ogs_assert(node); + + memset(&e, 0, sizeof(e)); + e.pfcp_node = node; + + ogs_fsm_fini(&node->sm, &e); + ogs_fsm_delete(&node->sm); + + if (node->t_association) + ogs_timer_delete(node->t_association); +} + +static void pfcp_recv_cb(short when, ogs_socket_t fd, void *data) +{ + int rv; + + ssize_t size; + sgwc_event_t *e = NULL; + ogs_pkbuf_t *pkbuf = NULL; + ogs_sockaddr_t from; + ogs_pfcp_node_t *node = NULL; + ogs_pfcp_header_t *h = NULL; + + ogs_assert(fd != INVALID_SOCKET); + + pkbuf = ogs_pkbuf_alloc(NULL, OGS_MAX_SDU_LEN); + ogs_pkbuf_put(pkbuf, OGS_MAX_SDU_LEN); + + size = ogs_recvfrom(fd, pkbuf->data, pkbuf->len, 0, &from); + if (size <= 0) { + ogs_log_message(OGS_LOG_ERROR, ogs_socket_errno, + "ogs_recvfrom() failed"); + ogs_pkbuf_free(pkbuf); + return; + } + + ogs_pkbuf_trim(pkbuf, size); + + h = (ogs_pfcp_header_t *)pkbuf->data; + if (h->version > OGS_PFCP_VERSION) { + ogs_pfcp_header_t rsp; + + ogs_error("Not supported version[%d]", h->version); + + memset(&rsp, 0, sizeof rsp); + rsp.flags = (OGS_PFCP_VERSION << 5); + rsp.type = OGS_PFCP_VERSION_NOT_SUPPORTED_RESPONSE_TYPE; + rsp.length = htobe16(4); + rsp.sqn_only = h->sqn_only; + ogs_sendto(fd, &rsp, 8, 0, &from); + ogs_pkbuf_free(pkbuf); + + return; + } + + e = sgwc_event_new(SGWC_EVT_SXA_MESSAGE); + ogs_assert(e); + + node = ogs_pfcp_node_find(&ogs_pfcp_self()->peer_list, &from); + if (!node) { + node = ogs_pfcp_node_add(&ogs_pfcp_self()->peer_list, &from); + ogs_assert(node); + + node->sock = data; + pfcp_node_fsm_init(node, false); + } + e->pfcp_node = node; + e->pkbuf = pkbuf; + + rv = ogs_queue_push(sgwc_self()->queue, e); + if (rv != OGS_OK) { + ogs_warn("ogs_queue_push() failed:%d", (int)rv); + ogs_pkbuf_free(e->pkbuf); + sgwc_event_free(e); + } +} + +int sgwc_pfcp_open(void) +{ + ogs_socknode_t *node = NULL; + ogs_sock_t *sock = NULL; + ogs_pfcp_node_t *pfcp_node = NULL; + + /* PFCP Server */ + ogs_list_for_each(&ogs_pfcp_self()->pfcp_list, node) { + sock = ogs_pfcp_server(node); + ogs_assert(sock); + + node->poll = ogs_pollset_add(sgwc_self()->pollset, + OGS_POLLIN, sock->fd, pfcp_recv_cb, sock); + } + ogs_list_for_each(&ogs_pfcp_self()->pfcp_list6, node) { + sock = ogs_pfcp_server(node); + ogs_assert(sock); + + node->poll = ogs_pollset_add(sgwc_self()->pollset, + OGS_POLLIN, sock->fd, pfcp_recv_cb, sock); + } + + ogs_pfcp_self()->pfcp_sock = + ogs_socknode_sock_first(&ogs_pfcp_self()->pfcp_list); + if (ogs_pfcp_self()->pfcp_sock) + ogs_pfcp_self()->pfcp_addr = &ogs_pfcp_self()->pfcp_sock->local_addr; + + ogs_pfcp_self()->pfcp_sock6 = + ogs_socknode_sock_first(&ogs_pfcp_self()->pfcp_list6); + if (ogs_pfcp_self()->pfcp_sock6) + ogs_pfcp_self()->pfcp_addr6 = &ogs_pfcp_self()->pfcp_sock6->local_addr; + + ogs_assert(ogs_pfcp_self()->pfcp_addr || ogs_pfcp_self()->pfcp_addr6); + + ogs_list_for_each(&ogs_pfcp_self()->peer_list, pfcp_node) + pfcp_node_fsm_init(pfcp_node, true); + + return OGS_OK; +} + +void sgwc_pfcp_close(void) +{ + ogs_pfcp_node_t *pfcp_node = NULL; + + ogs_list_for_each(&ogs_pfcp_self()->peer_list, pfcp_node) + pfcp_node_fsm_fini(pfcp_node); + + ogs_socknode_remove_all(&ogs_pfcp_self()->pfcp_list); + ogs_socknode_remove_all(&ogs_pfcp_self()->pfcp_list6); +} + +static void sess_timeout(ogs_pfcp_xact_t *xact, void *data) +{ + uint8_t type; + + ogs_assert(xact); + type = xact->seq[0].type; + + switch (type) { + case OGS_PFCP_SESSION_ESTABLISHMENT_REQUEST_TYPE: + ogs_error("No PFCP session establishment response"); + break; + case OGS_PFCP_SESSION_MODIFICATION_REQUEST_TYPE: + ogs_error("No PFCP session modification response"); + break; + case OGS_PFCP_SESSION_DELETION_REQUEST_TYPE: + ogs_error("No PFCP session deletion response"); + break; + default: + ogs_error("Not implemented [type:%d]", type); + break; + } +} + +void sgwc_pfcp_send_session_establishment_request( + sgwc_sess_t *sess, ogs_gtp_xact_t *gtp_xact, ogs_pkbuf_t *gtpbuf) +{ + int rv; + ogs_pkbuf_t *sxabuf = NULL; + ogs_pfcp_header_t h; + ogs_pfcp_xact_t *xact = NULL; + + ogs_assert(sess); + + memset(&h, 0, sizeof(ogs_pfcp_header_t)); + h.type = OGS_PFCP_SESSION_ESTABLISHMENT_REQUEST_TYPE; + h.seid = sess->sgwu_sxa_seid; + + sxabuf = sgwc_sxa_build_session_establishment_request(h.type, sess); + ogs_expect_or_return(sxabuf); + + xact = ogs_pfcp_xact_local_create( + sess->pfcp_node, &h, sxabuf, sess_timeout, sess); + ogs_expect_or_return(xact); + xact->assoc_xact = gtp_xact; + if (gtpbuf) + xact->gtpbuf = ogs_pkbuf_copy(gtpbuf); + + rv = ogs_pfcp_xact_commit(xact); + ogs_expect(rv == OGS_OK); +} + +void sgwc_pfcp_send_sess_modification_request( + sgwc_sess_t *sess, ogs_gtp_xact_t *gtp_xact, + ogs_pkbuf_t *gtpbuf, uint64_t flags) +{ + int rv; + ogs_pkbuf_t *sxabuf = NULL; + ogs_pfcp_header_t h; + ogs_pfcp_xact_t *xact = NULL; + + ogs_assert(sess); + + memset(&h, 0, sizeof(ogs_pfcp_header_t)); + h.type = OGS_PFCP_SESSION_MODIFICATION_REQUEST_TYPE; + h.seid = sess->sgwu_sxa_seid; + + sxabuf = sgwc_sxa_build_sess_modification_request(h.type, sess, flags); + ogs_expect_or_return(sxabuf); + + xact = ogs_pfcp_xact_local_create( + sess->pfcp_node, &h, sxabuf, sess_timeout, sess); + ogs_expect_or_return(xact); + xact->assoc_xact = gtp_xact; + xact->modify_flags = flags | OGS_PFCP_MODIFY_SESSION; + if (gtpbuf) + xact->gtpbuf = ogs_pkbuf_copy(gtpbuf); + + rv = ogs_pfcp_xact_commit(xact); + ogs_expect(rv == OGS_OK); +} + +void sgwc_pfcp_send_bearer_modification_request( + sgwc_bearer_t *bearer, ogs_gtp_xact_t *gtp_xact, + ogs_pkbuf_t *gtpbuf, uint64_t flags) +{ + int rv; + ogs_pkbuf_t *sxabuf = NULL; + ogs_pfcp_header_t h; + ogs_pfcp_xact_t *xact = NULL; + sgwc_sess_t *sess = NULL; + + ogs_assert(bearer); + sess = bearer->sess; + ogs_assert(sess); + + memset(&h, 0, sizeof(ogs_pfcp_header_t)); + h.type = OGS_PFCP_SESSION_MODIFICATION_REQUEST_TYPE; + h.seid = sess->sgwu_sxa_seid; + + sxabuf = sgwc_sxa_build_bearer_modification_request(h.type, bearer, flags); + ogs_expect_or_return(sxabuf); + + xact = ogs_pfcp_xact_local_create( + sess->pfcp_node, &h, sxabuf, sess_timeout, bearer); + ogs_expect_or_return(xact); + xact->assoc_xact = gtp_xact; + xact->modify_flags = flags; + if (gtpbuf) + xact->gtpbuf = ogs_pkbuf_copy(gtpbuf); + + rv = ogs_pfcp_xact_commit(xact); + ogs_expect(rv == OGS_OK); +} + +void sgwc_pfcp_send_session_deletion_request( + sgwc_sess_t *sess, ogs_gtp_xact_t *gtp_xact, ogs_pkbuf_t *gtpbuf) +{ + int rv; + ogs_pkbuf_t *sxabuf = NULL; + ogs_pfcp_header_t h; + ogs_pfcp_xact_t *xact = NULL; + + ogs_assert(sess); + + memset(&h, 0, sizeof(ogs_pfcp_header_t)); + h.type = OGS_PFCP_SESSION_DELETION_REQUEST_TYPE; + h.seid = sess->sgwu_sxa_seid; + + sxabuf = sgwc_sxa_build_session_deletion_request(h.type, sess); + ogs_expect_or_return(sxabuf); + + xact = ogs_pfcp_xact_local_create( + sess->pfcp_node, &h, sxabuf, sess_timeout, sess); + ogs_expect_or_return(xact); + xact->assoc_xact = gtp_xact; + if (gtpbuf) + xact->gtpbuf = ogs_pkbuf_copy(gtpbuf); + + rv = ogs_pfcp_xact_commit(xact); + ogs_expect(rv == OGS_OK); +} + +void sgwc_pfcp_send_session_report_response( + ogs_pfcp_xact_t *xact, sgwc_sess_t *sess, uint8_t cause) +{ + int rv; + ogs_pkbuf_t *sxabuf = NULL; + ogs_pfcp_header_t h; + + ogs_assert(xact); + + memset(&h, 0, sizeof(ogs_pfcp_header_t)); + h.type = OGS_PFCP_SESSION_REPORT_RESPONSE_TYPE; + h.seid = sess->sgwu_sxa_seid; + + sxabuf = ogs_pfcp_build_session_report_response(h.type, cause); + ogs_expect_or_return(sxabuf); + + rv = ogs_pfcp_xact_update_tx(xact, &h, sxabuf); + ogs_expect_or_return(rv == OGS_OK); + + rv = ogs_pfcp_xact_commit(xact); + ogs_expect(rv == OGS_OK); +} diff --git a/src/sgwc/pfcp-path.h b/src/sgwc/pfcp-path.h new file mode 100644 index 000000000..9293fbbdc --- /dev/null +++ b/src/sgwc/pfcp-path.h @@ -0,0 +1,50 @@ +/* + * 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 SGWC_PFCP_PATH_H +#define SGWC_PFCP_PATH_H + +#include "sxa-build.h" + +#ifdef __cplusplus +extern "C" { +#endif + +int sgwc_pfcp_open(void); +void sgwc_pfcp_close(void); + +void sgwc_pfcp_send_session_establishment_request( + sgwc_sess_t *sess, ogs_gtp_xact_t *gtp_xact, ogs_pkbuf_t *gtpbuf); +void sgwc_pfcp_send_sess_modification_request( + sgwc_sess_t *sess, ogs_gtp_xact_t *gtp_xact, + ogs_pkbuf_t *gtpbuf, uint64_t flags); +void sgwc_pfcp_send_bearer_modification_request( + sgwc_bearer_t *bearer, ogs_gtp_xact_t *gtp_xact, + ogs_pkbuf_t *gtpbuf, uint64_t flags); +void sgwc_pfcp_send_session_deletion_request( + sgwc_sess_t *sess, ogs_gtp_xact_t *gtp_xact, ogs_pkbuf_t *gtpbuf); + +void sgwc_pfcp_send_session_report_response( + ogs_pfcp_xact_t *xact, sgwc_sess_t *sess, uint8_t cause); + +#ifdef __cplusplus +} +#endif + +#endif /* SGWC_PFCP_PATH_H */ diff --git a/src/sgwc/pfcp-sm.c b/src/sgwc/pfcp-sm.c new file mode 100644 index 000000000..c7de73255 --- /dev/null +++ b/src/sgwc/pfcp-sm.c @@ -0,0 +1,330 @@ +/* + * 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 "pfcp-path.h" +#include "sxa-handler.h" + +static void node_timeout(ogs_pfcp_xact_t *xact, void *data); + +void sgwc_pfcp_state_initial(ogs_fsm_t *s, sgwc_event_t *e) +{ + int rv; + ogs_pfcp_node_t *node = NULL; + + ogs_assert(s); + ogs_assert(e); + + sgwc_sm_debug(e); + + node = e->pfcp_node; + ogs_assert(node); + + rv = ogs_pfcp_connect( + ogs_pfcp_self()->pfcp_sock, ogs_pfcp_self()->pfcp_sock6, node); + ogs_assert(rv == OGS_OK); + + node->t_no_heartbeat = ogs_timer_add(sgwc_self()->timer_mgr, + sgwc_timer_pfcp_no_heartbeat, node); + ogs_assert(node->t_no_heartbeat); + + OGS_FSM_TRAN(s, &sgwc_pfcp_state_will_associate); +} + +void sgwc_pfcp_state_final(ogs_fsm_t *s, sgwc_event_t *e) +{ + ogs_pfcp_node_t *node = NULL; + ogs_assert(s); + ogs_assert(e); + + sgwc_sm_debug(e); + + node = e->pfcp_node; + ogs_assert(node); + + ogs_timer_delete(node->t_no_heartbeat); +} + +void sgwc_pfcp_state_will_associate(ogs_fsm_t *s, sgwc_event_t *e) +{ + char buf[OGS_ADDRSTRLEN]; + + ogs_pfcp_node_t *node = NULL; + ogs_pfcp_xact_t *xact = NULL; + ogs_pfcp_message_t *message = NULL; + + ogs_sockaddr_t *addr = NULL; + + ogs_assert(s); + ogs_assert(e); + + sgwc_sm_debug(e); + + node = e->pfcp_node; + ogs_assert(node); + addr = node->sa_list; + ogs_assert(addr); + + switch (e->id) { + case OGS_FSM_ENTRY_SIG: + if (node->t_association) { + ogs_timer_start(node->t_association, + ogs_config()->time.message.pfcp.association_interval); + + ogs_pfcp_cp_send_association_setup_request(node, node_timeout); + } + break; + + case OGS_FSM_EXIT_SIG: + if (node->t_association) { + ogs_timer_stop(node->t_association); + } + break; + + case SGWC_EVT_SXA_TIMER: + switch(e->timer_id) { + case SGWC_TIMER_PFCP_ASSOCIATION: + node = e->pfcp_node; + ogs_assert(node); + + ogs_warn("Retry to association with peer [%s]:%d failed", + OGS_ADDR(addr, buf), OGS_PORT(addr)); + + ogs_assert(node->t_association); + ogs_timer_start(node->t_association, + ogs_config()->time.message.pfcp.association_interval); + + ogs_pfcp_cp_send_association_setup_request(node, node_timeout); + break; + default: + ogs_error("Unknown timer[%s:%d]", + sgwc_timer_get_name(e->timer_id), e->timer_id); + break; + } + break; + case SGWC_EVT_SXA_MESSAGE: + message = e->pfcp_message; + ogs_assert(message); + xact = e->pfcp_xact; + ogs_assert(xact); + + switch (message->h.type) { + case OGS_PFCP_ASSOCIATION_SETUP_REQUEST_TYPE: + ogs_pfcp_cp_handle_association_setup_request(node, xact, + &message->pfcp_association_setup_request); + OGS_FSM_TRAN(s, sgwc_pfcp_state_associated); + break; + case OGS_PFCP_ASSOCIATION_SETUP_RESPONSE_TYPE: + ogs_pfcp_cp_handle_association_setup_response(node, xact, + &message->pfcp_association_setup_response); + OGS_FSM_TRAN(s, sgwc_pfcp_state_associated); + break; + default: + ogs_error("cannot handle PFCP message type[%d]", + message->h.type); + break; + } + break; + default: + ogs_error("Unknown event %s", sgwc_event_get_name(e)); + break; + } +} + +void sgwc_pfcp_state_associated(ogs_fsm_t *s, sgwc_event_t *e) +{ + char buf[OGS_ADDRSTRLEN]; + + ogs_pfcp_node_t *node = NULL; + ogs_pfcp_xact_t *xact = NULL; + ogs_pfcp_message_t *message = NULL; + + ogs_sockaddr_t *addr = NULL; + sgwc_sess_t *sess = NULL; + + ogs_assert(s); + ogs_assert(e); + + sgwc_sm_debug(e); + + node = e->pfcp_node; + ogs_assert(node); + addr = node->sa_list; + ogs_assert(addr); + + switch (e->id) { + case OGS_FSM_ENTRY_SIG: + ogs_info("PFCP associated"); + ogs_timer_start(node->t_no_heartbeat, + ogs_config()->time.message.pfcp.no_heartbeat_duration); + break; + case OGS_FSM_EXIT_SIG: + ogs_info("PFCP de-associated"); + ogs_timer_stop(node->t_no_heartbeat); + break; + case SGWC_EVT_SXA_MESSAGE: + message = e->pfcp_message; + ogs_assert(message); + xact = e->pfcp_xact; + ogs_assert(xact); + + if (message->h.seid_presence && message->h.seid != 0) + sess = sgwc_sess_find_by_seid(message->h.seid); + + switch (message->h.type) { + case OGS_PFCP_HEARTBEAT_REQUEST_TYPE: + ogs_pfcp_handle_heartbeat_request(node, xact, + &message->pfcp_heartbeat_request); + break; + case OGS_PFCP_HEARTBEAT_RESPONSE_TYPE: + ogs_pfcp_handle_heartbeat_response(node, xact, + &message->pfcp_heartbeat_response); + break; + case OGS_PFCP_ASSOCIATION_SETUP_REQUEST_TYPE: + ogs_warn("PFCP[REQ] has already been associated"); + ogs_pfcp_cp_handle_association_setup_request(node, xact, + &message->pfcp_association_setup_request); + break; + case OGS_PFCP_ASSOCIATION_SETUP_RESPONSE_TYPE: + ogs_warn("PFCP[RSP] has already been associated"); + ogs_pfcp_cp_handle_association_setup_response(node, xact, + &message->pfcp_association_setup_response); + break; + case OGS_PFCP_SESSION_ESTABLISHMENT_RESPONSE_TYPE: + if (!message->h.seid_presence) { + ogs_error("No SEID"); + break; + } + + sgwc_sxa_handle_session_establishment_response( + sess, xact, e->gtp_message, + &message->pfcp_session_establishment_response); + break; + + case OGS_PFCP_SESSION_MODIFICATION_RESPONSE_TYPE: + if (!message->h.seid_presence) { + ogs_error("No SEID"); + break; + } + + sgwc_sxa_handle_session_modification_response( + sess, xact, e->gtp_message, + &message->pfcp_session_modification_response); + break; + + case OGS_PFCP_SESSION_DELETION_RESPONSE_TYPE: + if (!message->h.seid_presence) { + ogs_error("No SEID"); + break; + } + + sgwc_sxa_handle_session_deletion_response( + sess, xact, e->gtp_message, + &message->pfcp_session_deletion_response); + break; + + case OGS_PFCP_SESSION_REPORT_REQUEST_TYPE: + if (!message->h.seid_presence) { + ogs_error("No SEID"); + break; + } + + sgwc_sxa_handle_session_report_request( + sess, xact, &message->pfcp_session_report_request); + break; + + default: + ogs_error("Not implemented PFCP message type[%d]", + message->h.type); + break; + } + + break; + case SGWC_EVT_SXA_TIMER: + switch(e->timer_id) { + case SGWC_TIMER_PFCP_NO_HEARTBEAT: + node = e->pfcp_node; + ogs_assert(node); + + ogs_pfcp_send_heartbeat_request(node, node_timeout); + break; + default: + ogs_error("Unknown timer[%s:%d]", + sgwc_timer_get_name(e->timer_id), e->timer_id); + break; + } + break; + case SGWC_EVT_SXA_NO_HEARTBEAT: + ogs_warn("No Heartbeat from UPF [%s]:%d", + OGS_ADDR(addr, buf), OGS_PORT(addr)); + OGS_FSM_TRAN(s, sgwc_pfcp_state_will_associate); + break; + default: + ogs_error("Unknown event %s", sgwc_event_get_name(e)); + break; + } +} + +void sgwc_pfcp_state_exception(ogs_fsm_t *s, sgwc_event_t *e) +{ + ogs_assert(s); + ogs_assert(e); + + sgwc_sm_debug(e); + + switch (e->id) { + case OGS_FSM_ENTRY_SIG: + break; + case OGS_FSM_EXIT_SIG: + break; + default: + ogs_error("Unknown event %s", sgwc_event_get_name(e)); + break; + } +} + +static void node_timeout(ogs_pfcp_xact_t *xact, void *data) +{ + int rv; + + sgwc_event_t *e = NULL; + uint8_t type; + + ogs_assert(xact); + type = xact->seq[0].type; + + switch (type) { + case OGS_PFCP_HEARTBEAT_REQUEST_TYPE: + ogs_assert(data); + + e = sgwc_event_new(SGWC_EVT_SXA_NO_HEARTBEAT); + e->pfcp_node = data; + + rv = ogs_queue_push(sgwc_self()->queue, e); + if (rv != OGS_OK) { + ogs_warn("ogs_queue_push() failed:%d", (int)rv); + sgwc_event_free(e); + } + break; + case OGS_PFCP_ASSOCIATION_SETUP_REQUEST_TYPE: + break; + default: + ogs_error("Not implemented [type:%d]", type); + break; + } +} diff --git a/src/sgwc/s11-build.c b/src/sgwc/s11-build.c new file mode 100644 index 000000000..063dd8727 --- /dev/null +++ b/src/sgwc/s11-build.c @@ -0,0 +1,46 @@ +/* + * 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 "s11-build.h" + +ogs_pkbuf_t *sgwc_s11_build_downlink_data_notification(sgwc_bearer_t *bearer) +{ + ogs_gtp_message_t message; + ogs_gtp_downlink_data_notification_t *noti = NULL; + + /* FIXME : ARP should be retrieved from ? */ + uint8_t arp = 0x61; + + ogs_assert(bearer); + + /* Build downlink notification message */ + noti = &message.downlink_data_notification; + memset(&message, 0, sizeof(ogs_gtp_message_t)); + + noti->eps_bearer_id.presence = 1; + noti->eps_bearer_id.u8 = bearer->ebi; + + /* FIXME : ARP should be retrieved from ? */ + noti->allocation_retention_priority.presence = 1; + noti->allocation_retention_priority.data = &arp; + noti->allocation_retention_priority.len = sizeof(arp); + + message.h.type = OGS_GTP_DOWNLINK_DATA_NOTIFICATION_TYPE; + return ogs_gtp_build_msg(&message); +} diff --git a/lib/pfcp/n4-build.h b/src/sgwc/s11-build.h similarity index 79% rename from lib/pfcp/n4-build.h rename to src/sgwc/s11-build.h index 20f4c5426..7fc7f86ca 100644 --- a/lib/pfcp/n4-build.h +++ b/src/sgwc/s11-build.h @@ -17,18 +17,19 @@ * along with this program. If not, see . */ -#ifndef OGS_PFCP_N4_BUILD_H -#define OGS_PFCP_N4_BUILD_H +#ifndef SGWC_S11_BUILD_H +#define SGWC_S11_BUILD_H + +#include "context.h" #ifdef __cplusplus extern "C" { #endif -ogs_pkbuf_t *ogs_pfcp_n4_build_heartbeat_request(uint8_t type); -ogs_pkbuf_t *ogs_pfcp_n4_build_heartbeat_response(uint8_t type); +ogs_pkbuf_t *sgwc_s11_build_downlink_data_notification(sgwc_bearer_t *bearer); #ifdef __cplusplus } #endif -#endif /* OGS_PFCP_N4_BUILD_H */ +#endif /* SGWC_S11_BUILD_H */ diff --git a/src/sgwc/s11-handler.c b/src/sgwc/s11-handler.c new file mode 100644 index 000000000..2269a697e --- /dev/null +++ b/src/sgwc/s11-handler.c @@ -0,0 +1,1090 @@ +/* + * 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 "gtp-path.h" +#include "pfcp-path.h" + +#include "s11-handler.h" + +static void timeout(ogs_gtp_xact_t *xact, void *data) +{ + sgwc_sess_t *sess = data; + sgwc_ue_t *sgwc_ue = NULL; + uint8_t type = 0; + + ogs_assert(xact); + ogs_assert(sess); + sgwc_ue = sess->sgwc_ue; + ogs_assert(sgwc_ue); + + type = xact->seq[0].type; + + ogs_error("GTP Timeout : IMSI[%s] Message-Type[%d]", + sgwc_ue->imsi_bcd, type); +} + +static uint8_t pfcp_cause_from_gtp(uint8_t gtp_cause) +{ + switch (gtp_cause) { + case OGS_GTP_CAUSE_REQUEST_ACCEPTED: + return OGS_PFCP_CAUSE_REQUEST_ACCEPTED; + case OGS_GTP_CAUSE_REQUEST_REJECTED_REASON_NOT_SPECIFIED: + return OGS_PFCP_CAUSE_REQUEST_REJECTED; + case OGS_GTP_CAUSE_CONTEXT_NOT_FOUND: + return OGS_PFCP_CAUSE_SESSION_CONTEXT_NOT_FOUND; + case OGS_GTP_CAUSE_MANDATORY_IE_MISSING: + return OGS_PFCP_CAUSE_MANDATORY_IE_MISSING; + case OGS_GTP_CAUSE_CONDITIONAL_IE_MISSING: + return OGS_PFCP_CAUSE_CONDITIONAL_IE_MISSING; + case OGS_GTP_CAUSE_INVALID_LENGTH: + return OGS_PFCP_CAUSE_INVALID_LENGTH; + case OGS_GTP_CAUSE_MANDATORY_IE_INCORRECT: + return OGS_PFCP_CAUSE_MANDATORY_IE_INCORRECT; + case OGS_GTP_CAUSE_INVALID_MESSAGE_FORMAT: + return OGS_PFCP_CAUSE_INVALID_FORWARDING_POLICY; + case OGS_GTP_CAUSE_REMOTE_PEER_NOT_RESPONDING: + return OGS_PFCP_CAUSE_NO_ESTABLISHED_PFCP_ASSOCIATION; + case OGS_GTP_CAUSE_SEMANTIC_ERROR_IN_THE_TFT_OPERATION: + return OGS_PFCP_CAUSE_RULE_CREATION_MODIFICATION_FAILURE; + case OGS_GTP_CAUSE_GTP_C_ENTITY_CONGESTION: + return OGS_PFCP_CAUSE_PFCP_ENTITY_IN_CONGESTION; + case OGS_GTP_CAUSE_NO_RESOURCES_AVAILABLE: + return OGS_PFCP_CAUSE_NO_RESOURCES_AVAILABLE; + case OGS_GTP_CAUSE_SERVICE_NOT_SUPPORTED: + return OGS_PFCP_CAUSE_SERVICE_NOT_SUPPORTED; + case OGS_GTP_CAUSE_SYSTEM_FAILURE: + return OGS_PFCP_CAUSE_SYSTEM_FAILURE; + default: + return OGS_PFCP_CAUSE_SYSTEM_FAILURE; + } + + return OGS_PFCP_CAUSE_SYSTEM_FAILURE; +} + +void sgwc_s11_handle_create_session_request( + sgwc_ue_t *sgwc_ue, ogs_gtp_xact_t *s11_xact, + ogs_pkbuf_t *gtpbuf, ogs_gtp_message_t *message) +{ + uint8_t cause_value = 0; + + sgwc_sess_t *sess = NULL; + sgwc_bearer_t *bearer = NULL; + + ogs_gtp_create_session_request_t *req = NULL; + + uint16_t decoded; + ogs_gtp_f_teid_t *mme_s11_teid = NULL; + ogs_gtp_uli_t uli; + char apn[OGS_MAX_APN_LEN]; + + ogs_assert(s11_xact); + ogs_assert(gtpbuf); + ogs_assert(message); + req = &message->create_session_request; + ogs_assert(req); + + ogs_debug("Create Session Request"); + + cause_value = OGS_GTP_CAUSE_REQUEST_ACCEPTED; + + if (!sgwc_ue) { + ogs_warn("No Context"); + cause_value = OGS_GTP_CAUSE_CONTEXT_NOT_FOUND; + } + + if (req->imsi.presence == 0) { + ogs_error("No IMSI"); + cause_value = OGS_GTP_CAUSE_MANDATORY_IE_MISSING; + } + if (req->bearer_contexts_to_be_created.presence == 0) { + ogs_error("No Bearer"); + cause_value = OGS_GTP_CAUSE_MANDATORY_IE_MISSING; + } + if (req->bearer_contexts_to_be_created.eps_bearer_id.presence == 0) { + ogs_error("No EPS Bearer ID"); + cause_value = OGS_GTP_CAUSE_MANDATORY_IE_MISSING; + } + if (req->access_point_name.presence == 0) { + ogs_error("No APN"); + cause_value = OGS_GTP_CAUSE_MANDATORY_IE_MISSING; + } + if (req->sender_f_teid_for_control_plane.presence == 0) { + ogs_error("No Sender F-TEID"); + cause_value = OGS_GTP_CAUSE_MANDATORY_IE_MISSING; + } + if (req->pgw_s5_s8_address_for_control_plane_or_pmip.presence == 0) { + ogs_error("No PGW IP"); + cause_value = OGS_GTP_CAUSE_MANDATORY_IE_MISSING; + } + if (req->user_location_information.presence == 0) { + ogs_error("No User Location Inforamtion"); + cause_value = OGS_GTP_CAUSE_MANDATORY_IE_MISSING; + } + if (req->pdn_type.presence == 0) { + ogs_error("No PDN Type"); + cause_value = OGS_GTP_CAUSE_MANDATORY_IE_MISSING; + } + + if (cause_value != OGS_GTP_CAUSE_REQUEST_ACCEPTED) { + ogs_gtp_send_error_message( + s11_xact, sgwc_ue ? sgwc_ue->mme_s11_teid : 0, + OGS_GTP_CREATE_SESSION_RESPONSE_TYPE, cause_value); + return; + } + + /* Add Session */ + ogs_fqdn_parse(apn, + req->access_point_name.data, req->access_point_name.len); + sess = sgwc_sess_find_by_ebi(sgwc_ue, + req->bearer_contexts_to_be_created.eps_bearer_id.u8); + if (sess) { + ogs_warn("OLD Session Release [IMSI:%s,APN:%s]", + sgwc_ue->imsi_bcd, sess->pdn.apn); + sgwc_sess_remove(sess); + } + sess = sgwc_sess_add(sgwc_ue, apn); + ogs_assert(sess); + + /* Set User Location Information */ + decoded = ogs_gtp_parse_uli(&uli, &req->user_location_information); + ogs_assert(req->user_location_information.len == decoded); + memcpy(&sgwc_ue->e_tai.plmn_id, &uli.tai.plmn_id, sizeof(uli.tai.plmn_id)); + sgwc_ue->e_tai.tac = uli.tai.tac; + memcpy(&sgwc_ue->e_cgi.plmn_id, + &uli.e_cgi.plmn_id, sizeof(uli.e_cgi.plmn_id)); + sgwc_ue->e_cgi.cell_id = uli.e_cgi.cell_id; + + /* Select SGW-U based on UE Location Information */ + sgwc_sess_select_sgwu(sess); + + /* Check if selected SGW-U is associated with SGW-C */ + ogs_assert(sess->pfcp_node); + if (!OGS_FSM_CHECK(&sess->pfcp_node->sm, sgwc_pfcp_state_associated)) { + ogs_gtp_send_error_message( + s11_xact, sgwc_ue ? sgwc_ue->mme_s11_teid : 0, + OGS_GTP_CREATE_SESSION_RESPONSE_TYPE, + OGS_GTP_CAUSE_REMOTE_PEER_NOT_RESPONDING); + return; + } + + /* Set PDN Type */ + sess->pdn.pdn_type = req->pdn_type.u8; + sess->pdn.paa.pdn_type = req->pdn_type.u8; + + /* Remove all previous bearer */ + sgwc_bearer_remove_all(sess); + + /* Setup Default Bearer */ + bearer = sgwc_bearer_add(sess); + ogs_assert(bearer); + + /* Set Bearer EBI */ + bearer->ebi = req->bearer_contexts_to_be_created.eps_bearer_id.u8; + + /* Receive Control Plane(DL) : MME-S11 */ + mme_s11_teid = req->sender_f_teid_for_control_plane.data; + ogs_assert(mme_s11_teid); + sgwc_ue->mme_s11_teid = be32toh(mme_s11_teid->teid); + + ogs_debug(" MME_S11_TEID[%d] SGW_S11_TEID[%d]", + sgwc_ue->mme_s11_teid, sgwc_ue->sgw_s11_teid); + + sgwc_pfcp_send_session_establishment_request(sess, s11_xact, gtpbuf); +} + +void sgwc_s11_handle_modify_bearer_request( + sgwc_ue_t *sgwc_ue, ogs_gtp_xact_t *s11_xact, + ogs_pkbuf_t *gtpbuf, ogs_gtp_message_t *message) +{ + int rv; + uint16_t decoded; + uint64_t flags = 0; + + sgwc_bearer_t *bearer = NULL; + sgwc_tunnel_t *dl_tunnel = NULL; + ogs_pfcp_far_t *far = NULL; + ogs_ip_t remote_ip; + ogs_ip_t zero_ip; + + ogs_gtp_modify_bearer_request_t *req = NULL; + + ogs_gtp_cause_t cause; + ogs_gtp_uli_t uli; + ogs_gtp_f_teid_t *enb_s1u_teid = NULL; + + ogs_assert(s11_xact); + ogs_assert(message); + req = &message->modify_bearer_request; + ogs_assert(req); + + ogs_debug("Modify Bearer Request"); + + memset(&cause, 0, sizeof(cause)); + cause.value = OGS_GTP_CAUSE_REQUEST_ACCEPTED; + + if (req->bearer_contexts_to_be_modified.presence == 0) { + ogs_error("No Bearer"); + cause.value = OGS_GTP_CAUSE_MANDATORY_IE_MISSING; + } + if (req->bearer_contexts_to_be_modified.eps_bearer_id.presence == 0) { + ogs_error("No EPS Bearer ID"); + cause.value = OGS_GTP_CAUSE_MANDATORY_IE_MISSING; + } + + if (sgwc_ue && cause.value == OGS_GTP_CAUSE_REQUEST_ACCEPTED) { + bearer = sgwc_bearer_find_by_ue_ebi(sgwc_ue, + req->bearer_contexts_to_be_modified.eps_bearer_id.u8); + ogs_assert(bearer); + } + + if (!bearer) { + ogs_warn("No Context"); + cause.value = OGS_GTP_CAUSE_CONTEXT_NOT_FOUND; + } + + if (req->bearer_contexts_to_be_modified.s1_u_enodeb_f_teid.presence == 0) { + ogs_error("No eNB TEID"); + cause.value = OGS_GTP_CAUSE_MANDATORY_IE_MISSING; + } + + if (cause.value != OGS_GTP_CAUSE_REQUEST_ACCEPTED) { + ogs_gtp_send_error_message( + s11_xact, sgwc_ue ? sgwc_ue->mme_s11_teid : 0, + OGS_GTP_MODIFY_BEARER_RESPONSE_TYPE, cause.value); + return; + } + + dl_tunnel = sgwc_dl_tunnel_in_bearer(bearer); + ogs_assert(dl_tunnel); + + /* Data Plane(DL) : eNB-S1U */ + enb_s1u_teid = req->bearer_contexts_to_be_modified.s1_u_enodeb_f_teid.data; + dl_tunnel->remote_teid = be32toh(enb_s1u_teid->teid); + + rv = ogs_gtp_f_teid_to_ip(enb_s1u_teid, &remote_ip); + if (rv != OGS_OK) { + ogs_gtp_send_error_message( + s11_xact, sgwc_ue ? sgwc_ue->mme_s11_teid : 0, + OGS_GTP_MODIFY_BEARER_RESPONSE_TYPE, + OGS_GTP_CAUSE_MANDATORY_IE_MISSING); + return; + } + + if (req->user_location_information.presence == 1) { + /* if GTP Node changes, End Marker is sent out or not */ + decoded = ogs_gtp_parse_uli( + &uli, &req->user_location_information); + ogs_assert(req->user_location_information.len == decoded); + memcpy(&sgwc_ue->e_tai.plmn_id, &uli.tai.plmn_id, + sizeof(uli.tai.plmn_id)); + sgwc_ue->e_tai.tac = uli.tai.tac; + memcpy(&sgwc_ue->e_cgi.plmn_id, &uli.e_cgi.plmn_id, + sizeof(uli.e_cgi.plmn_id)); + sgwc_ue->e_cgi.cell_id = uli.e_cgi.cell_id; + ogs_debug(" TAI[PLMN_ID:%06x,TAC:%d]", + ogs_plmn_id_hexdump(&sgwc_ue->e_tai.plmn_id), + sgwc_ue->e_tai.tac); + ogs_debug(" E_CGI[PLMN_ID:%06x,CELL_ID:%d]", + ogs_plmn_id_hexdump(&sgwc_ue->e_cgi.plmn_id), + sgwc_ue->e_cgi.cell_id); + } + + flags = (OGS_PFCP_MODIFY_DL_ONLY|OGS_PFCP_MODIFY_ACTIVATE); + + memset(&zero_ip, 0, sizeof(ogs_ip_t)); + + if (memcmp(&dl_tunnel->remote_ip, &zero_ip, sizeof(ogs_ip_t)) != 0 && + memcmp(&dl_tunnel->remote_ip, &remote_ip, sizeof(ogs_ip_t)) != 0) { + + /* eNB IP is changed during handover */ + flags |= OGS_PFCP_MODIFY_END_MARKER; + } + + memcpy(&dl_tunnel->remote_ip, &remote_ip, sizeof(ogs_ip_t)); + + far = dl_tunnel->far; + ogs_assert(far); + + ogs_pfcp_ip_to_outer_header_creation(&dl_tunnel->remote_ip, + &far->outer_header_creation, &far->outer_header_creation_len); + far->outer_header_creation.teid = dl_tunnel->remote_teid; + + ogs_debug(" MME_S11_TEID[%d] SGW_S11_TEID[%d]", + sgwc_ue->mme_s11_teid, sgwc_ue->sgw_s11_teid); + ogs_debug(" ENB_S1U_TEID[%d] SGW_S1U_TEID[%d]", + dl_tunnel->remote_teid, dl_tunnel->local_teid); + + sgwc_pfcp_send_bearer_modification_request( + bearer, s11_xact, gtpbuf, flags); +} + +void sgwc_s11_handle_delete_session_request( + sgwc_ue_t *sgwc_ue, ogs_gtp_xact_t *s11_xact, + ogs_pkbuf_t *gtpbuf, ogs_gtp_message_t *message) +{ + int rv; + uint8_t cause_value = 0; + sgwc_sess_t *sess = NULL; + ogs_gtp_xact_t *s5c_xact = NULL; + ogs_gtp_delete_session_request_t *req = NULL; + + ogs_assert(s11_xact); + ogs_assert(gtpbuf); + ogs_assert(message); + req = &message->delete_session_request; + ogs_assert(req); + + ogs_debug("Delete Session Request"); + + cause_value = OGS_GTP_CAUSE_REQUEST_ACCEPTED; + + if (!sgwc_ue) { + ogs_warn("No Context"); + cause_value = OGS_GTP_CAUSE_CONTEXT_NOT_FOUND; + } + + if (req->linked_eps_bearer_id.presence == 0) { + ogs_error("No EPS Bearer ID"); + cause_value = OGS_GTP_CAUSE_MANDATORY_IE_MISSING; + } + + if (cause_value != OGS_GTP_CAUSE_REQUEST_ACCEPTED) { + ogs_gtp_send_error_message( + s11_xact, sgwc_ue ? sgwc_ue->mme_s11_teid : 0, + OGS_GTP_DELETE_SESSION_RESPONSE_TYPE, cause_value); + return; + } + + ogs_assert(sgwc_ue); + sess = sgwc_sess_find_by_ebi(sgwc_ue, req->linked_eps_bearer_id.u8); + if (!sess) { + ogs_error("No Context [IMSI:%s, EBI:%d]", + sgwc_ue->imsi_bcd, req->linked_eps_bearer_id.u8); + ogs_gtp_send_error_message( + s11_xact, sgwc_ue ? sgwc_ue->mme_s11_teid : 0, + OGS_GTP_DELETE_SESSION_RESPONSE_TYPE, + OGS_GTP_CAUSE_CONTEXT_NOT_FOUND); + return; + } + + ogs_assert(sess); + ogs_debug(" MME_S11_TEID[%d] SGW_S11_TEID[%d]", + sgwc_ue->mme_s11_teid, sgwc_ue->sgw_s11_teid); + ogs_debug(" SGW_S5C_TEID[0x%x] PGW_S5C_TEID[0x%x]", + sess->sgw_s5c_teid, sess->pgw_s5c_teid); + + message->h.type = OGS_GTP_DELETE_SESSION_REQUEST_TYPE; + message->h.teid = sess->pgw_s5c_teid; + + gtpbuf = ogs_gtp_build_msg(message); + ogs_expect_or_return(gtpbuf); + + s5c_xact = ogs_gtp_xact_local_create( + sess->gnode, &message->h, gtpbuf, timeout, sess); + ogs_expect_or_return(s5c_xact); + + ogs_gtp_xact_associate(s11_xact, s5c_xact); + + rv = ogs_gtp_xact_commit(s5c_xact); + ogs_expect(rv == OGS_OK); +} + +void sgwc_s11_handle_create_bearer_response( + sgwc_ue_t *sgwc_ue, ogs_gtp_xact_t *s11_xact, + ogs_pkbuf_t *gtpbuf, ogs_gtp_message_t *message) +{ + int rv; + uint8_t cause_value; + uint16_t decoded; + + sgwc_sess_t *sess = NULL; + sgwc_bearer_t *bearer = NULL; + sgwc_tunnel_t *dl_tunnel = NULL, *ul_tunnel = NULL; + ogs_pfcp_far_t *far = NULL; + + ogs_gtp_xact_t *s5c_xact = NULL; + + ogs_gtp_create_bearer_response_t *rsp = NULL; + ogs_gtp_f_teid_t *sgw_s1u_teid = NULL, *enb_s1u_teid = NULL; + ogs_gtp_uli_t uli; + + ogs_assert(s11_xact); + s5c_xact = s11_xact->assoc_xact; + ogs_assert(s5c_xact); + ogs_assert(message); + rsp = &message->create_bearer_response; + ogs_assert(rsp); + + ogs_debug("Create Bearer Response"); + + if (!sgwc_ue) { + sgwc_sess_t *sess = NULL; + + ogs_warn("No Context in TEID"); + sess = s11_xact->data; + ogs_assert(sess); + sgwc_ue = sess->sgwc_ue; + ogs_assert(sgwc_ue); + } + + rv = ogs_gtp_xact_commit(s11_xact); + ogs_expect(rv == OGS_OK); + + if (rsp->cause.presence) { + ogs_gtp_cause_t *cause = rsp->cause.data; + ogs_assert(cause); + + cause_value = cause->value; + if (cause_value == OGS_GTP_CAUSE_REQUEST_ACCEPTED) { + if (rsp->bearer_contexts.cause.presence) { + cause = rsp->bearer_contexts.cause.data; + ogs_assert(cause); + + cause_value = cause->value; + } else { + ogs_error("No Cause"); + cause_value = OGS_GTP_CAUSE_MANDATORY_IE_MISSING; + } + } + } else { + ogs_error("No Cause"); + cause_value = OGS_GTP_CAUSE_MANDATORY_IE_MISSING; + } + + if (rsp->bearer_contexts.presence == 0) { + ogs_error("No Bearer"); + cause_value = OGS_GTP_CAUSE_MANDATORY_IE_MISSING; + } + if (rsp->bearer_contexts.eps_bearer_id.presence == 0) { + ogs_error("No EPS Bearer ID"); + cause_value = OGS_GTP_CAUSE_MANDATORY_IE_MISSING; + } + if (rsp->bearer_contexts.s1_u_enodeb_f_teid.presence == 0) { + ogs_error("No eNB TEID"); + cause_value = OGS_GTP_CAUSE_MANDATORY_IE_MISSING; + } + if (rsp->bearer_contexts.s4_u_sgsn_f_teid.presence == 0) { + ogs_error("No SGW TEID"); + cause_value = OGS_GTP_CAUSE_MANDATORY_IE_MISSING; + } + if (rsp->user_location_information.presence == 0) { + ogs_error("No User Location Inforamtion"); + cause_value = OGS_GTP_CAUSE_MANDATORY_IE_MISSING; + } + + if (cause_value != OGS_GTP_CAUSE_REQUEST_ACCEPTED) { + ogs_gtp_send_error_message(s5c_xact, sess ? sess->pgw_s5c_teid : 0, + OGS_GTP_CREATE_BEARER_RESPONSE_TYPE, cause_value); + return; + } + + /* Correlate with SGW-S1U-TEID */ + sgw_s1u_teid = rsp->bearer_contexts.s4_u_sgsn_f_teid.data; + ogs_assert(sgw_s1u_teid); + + /* Find the Tunnel by SGW-S1U-TEID */ + ul_tunnel = sgwc_tunnel_find_by_teid(be32toh(sgw_s1u_teid->teid)); + ogs_assert(ul_tunnel); + bearer = ul_tunnel->bearer; + ogs_assert(bearer); + dl_tunnel = sgwc_dl_tunnel_in_bearer(bearer); + ogs_assert(dl_tunnel); + sess = bearer->sess; + ogs_assert(sess); + + /* Set EBI */ + bearer->ebi = rsp->bearer_contexts.eps_bearer_id.u8; + + /* Data Plane(DL) : eNB-S1U */ + enb_s1u_teid = rsp->bearer_contexts.s1_u_enodeb_f_teid.data; + dl_tunnel->remote_teid = be32toh(enb_s1u_teid->teid); + + ogs_debug(" ENB_S1U_TEID[%d] SGW_S1U_TEID[%d]", + dl_tunnel->remote_teid, dl_tunnel->local_teid); + + rv = ogs_gtp_f_teid_to_ip(enb_s1u_teid, &dl_tunnel->remote_ip); + if (rv != OGS_OK) { + ogs_gtp_send_error_message(s5c_xact, sess ? sess->pgw_s5c_teid : 0, + OGS_GTP_CREATE_BEARER_RESPONSE_TYPE, + OGS_GTP_CAUSE_MANDATORY_IE_MISSING); + return; + } + + far = dl_tunnel->far; + ogs_assert(far); + + ogs_pfcp_ip_to_outer_header_creation(&dl_tunnel->remote_ip, + &far->outer_header_creation, &far->outer_header_creation_len); + far->outer_header_creation.teid = dl_tunnel->remote_teid; + + decoded = ogs_gtp_parse_uli(&uli, &rsp->user_location_information); + ogs_assert(rsp->user_location_information.len == decoded); + memcpy(&sgwc_ue->e_tai.plmn_id, &uli.tai.plmn_id, sizeof(uli.tai.plmn_id)); + sgwc_ue->e_tai.tac = uli.tai.tac; + memcpy(&sgwc_ue->e_cgi.plmn_id, + &uli.e_cgi.plmn_id, sizeof(uli.e_cgi.plmn_id)); + sgwc_ue->e_cgi.cell_id = uli.e_cgi.cell_id; + + ogs_debug(" TAI[PLMN_ID:%06x,TAC:%d]", + ogs_plmn_id_hexdump(&sgwc_ue->e_tai.plmn_id), + sgwc_ue->e_tai.tac); + ogs_debug(" E_CGI[PLMN_ID:%06x,CELL_ID:%d]", + ogs_plmn_id_hexdump(&sgwc_ue->e_cgi.plmn_id), + sgwc_ue->e_cgi.cell_id); + + sgwc_pfcp_send_bearer_modification_request( + bearer, s5c_xact, gtpbuf, + OGS_PFCP_MODIFY_DL_ONLY|OGS_PFCP_MODIFY_CREATE); +} + +void sgwc_s11_handle_update_bearer_response( + sgwc_ue_t *sgwc_ue, ogs_gtp_xact_t *s11_xact, + ogs_pkbuf_t *gtpbuf, ogs_gtp_message_t *message) +{ + int rv; + uint8_t cause_value; + ogs_pkbuf_t *pkbuf = NULL; + ogs_gtp_xact_t *s5c_xact = NULL; + sgwc_sess_t *sess = NULL; + sgwc_bearer_t *bearer = NULL; + ogs_gtp_update_bearer_response_t *req = NULL; + + ogs_assert(s11_xact); + s5c_xact = s11_xact->assoc_xact; + ogs_assert(s5c_xact); + ogs_assert(message); + req = &message->update_bearer_response; + ogs_assert(req); + + ogs_debug("Update Bearer Response"); + if (!sgwc_ue) { + sgwc_sess_t *sess = NULL; + + ogs_warn("No Context in TEID"); + sess = s11_xact->data; + ogs_assert(sess); + sgwc_ue = sess->sgwc_ue; + ogs_assert(sgwc_ue); + } + + rv = ogs_gtp_xact_commit(s11_xact); + ogs_expect(rv == OGS_OK); + + if (req->cause.presence) { + ogs_gtp_cause_t *cause = req->cause.data; + ogs_assert(cause); + + cause_value = cause->value; + if (cause_value == OGS_GTP_CAUSE_REQUEST_ACCEPTED) { + if (req->bearer_contexts.cause.presence) { + cause = req->bearer_contexts.cause.data; + ogs_assert(cause); + + cause_value = cause->value; + } else { + ogs_error("No Cause"); + cause_value = OGS_GTP_CAUSE_MANDATORY_IE_MISSING; + } + } + } else { + ogs_error("No Cause"); + cause_value = OGS_GTP_CAUSE_MANDATORY_IE_MISSING; + } + + if (req->bearer_contexts.presence == 0) { + ogs_error("No Bearer"); + cause_value = OGS_GTP_CAUSE_MANDATORY_IE_MISSING; + } + if (req->bearer_contexts.eps_bearer_id.presence == 0) { + ogs_error("No EPS Bearer ID"); + cause_value = OGS_GTP_CAUSE_MANDATORY_IE_MISSING; + } + + if (sgwc_ue && cause_value == OGS_GTP_CAUSE_REQUEST_ACCEPTED) { + bearer = sgwc_bearer_find_by_ue_ebi( + sgwc_ue, req->bearer_contexts.eps_bearer_id.u8); + } + + if (!bearer) { + ogs_error("No Context"); + cause_value = OGS_GTP_CAUSE_CONTEXT_NOT_FOUND; + } + + if (cause_value != OGS_GTP_CAUSE_REQUEST_ACCEPTED) { + ogs_gtp_send_error_message(s5c_xact, sess ? sess->pgw_s5c_teid : 0, + OGS_GTP_UPDATE_BEARER_RESPONSE_TYPE, cause_value); + return; + } + + ogs_assert(bearer); + sess = bearer->sess; + ogs_assert(sess); + + ogs_debug(" MME_S11_TEID[%d] SGW_S11_TEID[%d]", + sgwc_ue->mme_s11_teid, sgwc_ue->sgw_s11_teid); + ogs_debug(" SGW_S5C_TEID[0x%x] PGW_S5C_TEID[0x%x]", + sess->sgw_s5c_teid, sess->pgw_s5c_teid); + + message->h.type = OGS_GTP_UPDATE_BEARER_RESPONSE_TYPE; + message->h.teid = sess->pgw_s5c_teid; + + pkbuf = ogs_gtp_build_msg(message); + ogs_expect_or_return(pkbuf); + + rv = ogs_gtp_xact_update_tx(s5c_xact, &message->h, pkbuf); + ogs_expect_or_return(rv == OGS_OK); + + rv = ogs_gtp_xact_commit(s5c_xact); + ogs_expect(rv == OGS_OK); +} + +void sgwc_s11_handle_delete_bearer_response( + sgwc_ue_t *sgwc_ue, ogs_gtp_xact_t *s11_xact, + ogs_pkbuf_t *gtpbuf, ogs_gtp_message_t *message) +{ + int rv; + uint8_t cause_value; + ogs_gtp_xact_t *s5c_xact = NULL; + + sgwc_sess_t *sess = NULL; + sgwc_bearer_t *bearer = NULL; + ogs_gtp_delete_bearer_response_t *req = NULL; + + ogs_assert(s11_xact); + s5c_xact = s11_xact->assoc_xact; + ogs_assert(s5c_xact); + ogs_assert(message); + req = &message->delete_bearer_response; + ogs_assert(req); + + ogs_debug("Delete Bearer Response"); + + if (!sgwc_ue) { + sgwc_sess_t *sess = NULL; + + ogs_warn("No Context in TEID"); + sess = s11_xact->data; + ogs_assert(sess); + sgwc_ue = sess->sgwc_ue; + ogs_assert(sgwc_ue); + } + + rv = ogs_gtp_xact_commit(s11_xact); + ogs_expect(rv == OGS_OK); + + if (req->cause.presence) { + ogs_gtp_cause_t *cause = req->cause.data; + ogs_assert(cause); + + cause_value = cause->value; + if (cause_value == OGS_GTP_CAUSE_REQUEST_ACCEPTED) { + if (req->bearer_contexts.cause.presence) { + cause = req->bearer_contexts.cause.data; + ogs_assert(cause); + + cause_value = cause->value; + } else { + ogs_error("No Cause"); + cause_value = OGS_GTP_CAUSE_MANDATORY_IE_MISSING; + } + } + } else { + ogs_error("No Cause"); + cause_value = OGS_GTP_CAUSE_MANDATORY_IE_MISSING; + } + + if (req->bearer_contexts.presence == 0) { + ogs_error("No Bearer"); + cause_value = OGS_GTP_CAUSE_MANDATORY_IE_MISSING; + } + if (req->bearer_contexts.eps_bearer_id.presence == 0) { + ogs_error("No EPS Bearer ID"); + cause_value = OGS_GTP_CAUSE_MANDATORY_IE_MISSING; + } + + if (sgwc_ue && cause_value == OGS_GTP_CAUSE_REQUEST_ACCEPTED) { + bearer = sgwc_bearer_find_by_ue_ebi( + sgwc_ue, req->bearer_contexts.eps_bearer_id.u8); + ogs_assert(bearer); + } + + if (!bearer) { + ogs_warn("No Context"); + cause_value = OGS_GTP_CAUSE_CONTEXT_NOT_FOUND; + } + + if (cause_value != OGS_GTP_CAUSE_REQUEST_ACCEPTED) { + ogs_gtp_send_error_message(s5c_xact, sess ? sess->pgw_s5c_teid : 0, + OGS_GTP_DELETE_BEARER_RESPONSE_TYPE, cause_value); + return; + } + + sess = bearer->sess; + ogs_assert(sess); + + ogs_debug(" MME_S11_TEID[%d] SGW_S11_TEID[%d]", + sgwc_ue->mme_s11_teid, sgwc_ue->sgw_s11_teid); + ogs_debug(" SGW_S5C_TEID[0x%x] PGW_S5C_TEID[0x%x]", + sess->sgw_s5c_teid, sess->pgw_s5c_teid); + + sgwc_pfcp_send_bearer_modification_request( + bearer, s5c_xact, gtpbuf, OGS_PFCP_MODIFY_REMOVE); +} + +void sgwc_s11_handle_release_access_bearers_request( + sgwc_ue_t *sgwc_ue, ogs_gtp_xact_t *s11_xact, + ogs_pkbuf_t *gtpbuf, ogs_gtp_message_t *message) +{ + sgwc_sess_t *sess = NULL; + + ogs_gtp_release_access_bearers_request_t *req = NULL; + ogs_gtp_cause_t cause; + + ogs_assert(s11_xact); + ogs_assert(message); + req = &message->release_access_bearers_request; + ogs_assert(req); + + ogs_debug("Release Access Bearers Request"); + + memset(&cause, 0, sizeof(cause)); + cause.value = OGS_GTP_CAUSE_REQUEST_ACCEPTED; + + if (!sgwc_ue) { + ogs_warn("No Context"); + cause.value = OGS_GTP_CAUSE_CONTEXT_NOT_FOUND; + } + + if (cause.value != OGS_GTP_CAUSE_REQUEST_ACCEPTED) { + ogs_gtp_send_error_message( + s11_xact, sgwc_ue ? sgwc_ue->mme_s11_teid : 0, + OGS_GTP_RELEASE_ACCESS_BEARERS_RESPONSE_TYPE, cause.value); + return; + } + + ogs_assert(sgwc_ue); + + ogs_debug(" MME_S11_TEID[%d] SGW_S11_TEID[%d]", + sgwc_ue->mme_s11_teid, sgwc_ue->sgw_s11_teid); + + ogs_list_for_each(&sgwc_ue->sess_list, sess) { + + sess->state.release_access_bearers = false; + sgwc_pfcp_send_sess_modification_request( + sess, s11_xact, gtpbuf, + OGS_PFCP_MODIFY_DL_ONLY|OGS_PFCP_MODIFY_DEACTIVATE); + } +} + +void sgwc_s11_handle_downlink_data_notification_ack( + sgwc_ue_t *sgwc_ue, ogs_gtp_xact_t *s11_xact, + ogs_pkbuf_t *gtpbuf, ogs_gtp_message_t *message) +{ + int rv; + uint8_t cause_value; + + sgwc_sess_t *sess = NULL; + ogs_pfcp_xact_t *pfcp_xact; + + ogs_gtp_downlink_data_notification_acknowledge_t *ack = NULL; + + ogs_assert(s11_xact); + ogs_assert(message); + ack = &message->downlink_data_notification_acknowledge; + ogs_assert(ack); + + pfcp_xact = s11_xact->pfcp_xact; + ogs_assert(pfcp_xact); + sess = s11_xact->data; + ogs_assert(sess); + sgwc_ue = sess->sgwc_ue; + ogs_assert(sgwc_ue); + + rv = ogs_gtp_xact_commit(s11_xact); + ogs_expect(rv == OGS_OK); + + if (ack->cause.presence) { + ogs_gtp_cause_t *cause = ack->cause.data; + ogs_assert(cause); + + cause_value = cause->value; + } else { + ogs_error("No Cause"); + cause_value = OGS_GTP_CAUSE_MANDATORY_IE_MISSING; + } + + if (cause_value != OGS_GTP_CAUSE_REQUEST_ACCEPTED) { + ogs_pfcp_send_error_message(pfcp_xact, sess ? sess->sgwu_sxa_seid : 0, + OGS_PFCP_SESSION_REPORT_RESPONSE_TYPE, + pfcp_cause_from_gtp(cause_value), 0); + return; + } + + ogs_debug("Downlink Data Notification Acknowledge"); + ogs_debug(" MME_S11_TEID[%d] SGW_S11_TEID[%d]", + sgwc_ue->mme_s11_teid, sgwc_ue->sgw_s11_teid); + + sgwc_pfcp_send_session_report_response( + pfcp_xact, sess, pfcp_cause_from_gtp(cause_value)); +} + +void sgwc_s11_handle_create_indirect_data_forwarding_tunnel_request( + sgwc_ue_t *sgwc_ue, ogs_gtp_xact_t *s11_xact, + ogs_pkbuf_t *gtpbuf, ogs_gtp_message_t *message) +{ + int rv, i; + + sgwc_sess_t *sess = NULL; + sgwc_bearer_t *bearer = NULL; + sgwc_tunnel_t *tunnel = NULL; + ogs_pfcp_far_t *far = NULL; + + ogs_gtp_create_indirect_data_forwarding_tunnel_request_t *req = NULL; + ogs_gtp_cause_t cause; + ogs_gtp_f_teid_t *req_teid = NULL; + + ogs_assert(s11_xact); + ogs_assert(message); + req = &message->create_indirect_data_forwarding_tunnel_request; + ogs_assert(req); + + ogs_debug("Create Indirect Data Forwarding Tunnel Request"); + + memset(&cause, 0, sizeof(cause)); + cause.value = OGS_GTP_CAUSE_REQUEST_ACCEPTED; + + if (!sgwc_ue) { + ogs_warn("No Context"); + cause.value = OGS_GTP_CAUSE_CONTEXT_NOT_FOUND; + } + + if (cause.value != OGS_GTP_CAUSE_REQUEST_ACCEPTED) { + ogs_gtp_send_error_message( + s11_xact, sgwc_ue ? sgwc_ue->mme_s11_teid : 0, + OGS_GTP_CREATE_INDIRECT_DATA_FORWARDING_TUNNEL_RESPONSE_TYPE, + cause.value); + return; + } + + ogs_debug(" MME_S11_TEID[%d] SGW_S11_TEID[%d]", + sgwc_ue->mme_s11_teid, sgwc_ue->sgw_s11_teid); + + for (i = 0; req->bearer_contexts[i].presence; i++) { + if (req->bearer_contexts[i].eps_bearer_id.presence == 0) { + ogs_error("No EBI"); + ogs_gtp_send_error_message( + s11_xact, sgwc_ue ? sgwc_ue->mme_s11_teid : 0, + OGS_GTP_CREATE_INDIRECT_DATA_FORWARDING_TUNNEL_RESPONSE_TYPE, + OGS_GTP_CAUSE_MANDATORY_IE_MISSING); + return; + } + + bearer = sgwc_bearer_find_by_ue_ebi(sgwc_ue, + req->bearer_contexts[i].eps_bearer_id.u8); + ogs_assert(bearer); + + if (req->bearer_contexts[i].s1_u_enodeb_f_teid.presence) { + req_teid = req->bearer_contexts[i].s1_u_enodeb_f_teid.data; + ogs_assert(req_teid); + + tunnel = sgwc_tunnel_add(bearer, + OGS_GTP_F_TEID_SGW_GTP_U_FOR_DL_DATA_FORWARDING); + ogs_assert(tunnel); + + tunnel->remote_teid = be32toh(req_teid->teid); + + rv = ogs_gtp_f_teid_to_ip(req_teid, &tunnel->remote_ip); + if (rv != OGS_OK) { + ogs_gtp_send_error_message(s11_xact, + sgwc_ue ? sgwc_ue->mme_s11_teid : 0, + OGS_GTP_CREATE_INDIRECT_DATA_FORWARDING_TUNNEL_RESPONSE_TYPE, + OGS_GTP_CAUSE_MANDATORY_IE_MISSING); + return; + } + + far = tunnel->far; + ogs_assert(far); + + ogs_pfcp_ip_to_outer_header_creation(&tunnel->remote_ip, + &far->outer_header_creation, &far->outer_header_creation_len); + far->outer_header_creation.teid = tunnel->remote_teid; + + ogs_debug(" SGW_DL_TEID[%d] ENB_DL_TEID[%d]", + tunnel->local_teid, tunnel->remote_teid); + } + + if (req->bearer_contexts[i].s12_rnc_f_teid.presence) { + req_teid = req->bearer_contexts[i].s12_rnc_f_teid.data; + ogs_assert(req_teid); + + tunnel = sgwc_tunnel_add(bearer, + OGS_GTP_F_TEID_SGW_GTP_U_FOR_UL_DATA_FORWARDING); + ogs_assert(tunnel); + + tunnel->remote_teid = be32toh(req_teid->teid); + + rv = ogs_gtp_f_teid_to_ip(req_teid, &tunnel->remote_ip); + if (rv != OGS_OK) { + ogs_gtp_send_error_message(s11_xact, + sgwc_ue ? sgwc_ue->mme_s11_teid : 0, + OGS_GTP_CREATE_INDIRECT_DATA_FORWARDING_TUNNEL_RESPONSE_TYPE, + OGS_GTP_CAUSE_MANDATORY_IE_MISSING); + return; + } + + far = tunnel->far; + ogs_assert(far); + + ogs_pfcp_ip_to_outer_header_creation(&tunnel->remote_ip, + &far->outer_header_creation, &far->outer_header_creation_len); + far->outer_header_creation.teid = tunnel->remote_teid; + + ogs_debug(" SGW_UL_TEID[%d] ENB_UL_TEID[%d]", + tunnel->local_teid, tunnel->remote_teid); + } + } + + ogs_list_for_each(&sgwc_ue->sess_list, sess) { + + sess->state.create_indirect_tunnel = false; + sgwc_pfcp_send_sess_modification_request( + sess, s11_xact, gtpbuf, + OGS_PFCP_MODIFY_INDIRECT|OGS_PFCP_MODIFY_CREATE); + } +} + +void sgwc_s11_handle_delete_indirect_data_forwarding_tunnel_request( + sgwc_ue_t *sgwc_ue, ogs_gtp_xact_t *s11_xact, + ogs_pkbuf_t *gtpbuf, ogs_gtp_message_t *recv_message) +{ + sgwc_sess_t *sess = NULL; + + ogs_gtp_cause_t cause; + + ogs_assert(s11_xact); + + ogs_debug("Delete Indirect Data Forwarding Tunnel Request"); + + memset(&cause, 0, sizeof(cause)); + cause.value = OGS_GTP_CAUSE_REQUEST_ACCEPTED; + + if (!sgwc_ue) { + ogs_warn("No Context"); + cause.value = OGS_GTP_CAUSE_CONTEXT_NOT_FOUND; + } + + if (cause.value != OGS_GTP_CAUSE_REQUEST_ACCEPTED) { + ogs_gtp_send_error_message( + s11_xact, sgwc_ue ? sgwc_ue->mme_s11_teid : 0, + OGS_GTP_DELETE_INDIRECT_DATA_FORWARDING_TUNNEL_RESPONSE_TYPE, + cause.value); + return; + } + + ogs_assert(sgwc_ue); + + ogs_debug(" MME_S11_TEID[%d] SGW_S11_TEID[%d]", + sgwc_ue->mme_s11_teid, sgwc_ue->sgw_s11_teid); + + ogs_list_for_each(&sgwc_ue->sess_list, sess) { + + sess->state.delete_indirect_tunnel = false; + sgwc_pfcp_send_sess_modification_request( + sess, s11_xact, gtpbuf, + OGS_PFCP_MODIFY_SESSION| + OGS_PFCP_MODIFY_INDIRECT| + OGS_PFCP_MODIFY_REMOVE); + } +} + +void sgwc_s11_handle_bearer_resource_command( + sgwc_ue_t *sgwc_ue, ogs_gtp_xact_t *s11_xact, + ogs_pkbuf_t *gtpbuf, ogs_gtp_message_t *message) +{ + int rv; + ogs_pkbuf_t *pkbuf = NULL; + ogs_gtp_bearer_resource_command_t *cmd = NULL; + + uint8_t cause_value = 0; + ogs_gtp_xact_t *s5c_xact = NULL; + + sgwc_sess_t *sess = NULL; + + ogs_assert(s11_xact); + ogs_assert(message); + + ogs_debug("Bearer Resource Command"); + + cmd = &message->bearer_resource_command; + cause_value = OGS_GTP_CAUSE_REQUEST_ACCEPTED; + + if (!sgwc_ue) { + ogs_warn("No Context"); + cause_value = OGS_GTP_CAUSE_CONTEXT_NOT_FOUND; + } + + if (cmd->linked_eps_bearer_id.presence == 0) { + ogs_error("No Linked EPS Bearer ID"); + cause_value = OGS_GTP_CAUSE_MANDATORY_IE_MISSING; + } else { + sess = sgwc_sess_find_by_ebi(sgwc_ue, cmd->linked_eps_bearer_id.u8); + if (!sess) { + ogs_error("No Context for Linked EPS Bearer ID[%d]", + cmd->linked_eps_bearer_id.u8); + cause_value = OGS_GTP_CAUSE_CONTEXT_NOT_FOUND; + } + } + + if (cmd->procedure_transaction_id.presence == 0) { + ogs_error("No PTI"); + cause_value = OGS_GTP_CAUSE_MANDATORY_IE_MISSING; + } + if (cmd->traffic_aggregate_description.presence == 0) { + ogs_error("No Traffic aggregate description(TAD)"); + cause_value = OGS_GTP_CAUSE_MANDATORY_IE_MISSING; + } + + if (cause_value != OGS_GTP_CAUSE_REQUEST_ACCEPTED) { + ogs_gtp_send_error_message( + s11_xact, sgwc_ue ? sgwc_ue->mme_s11_teid : 0, + OGS_GTP_BEARER_RESOURCE_FAILURE_INDICATION_TYPE, cause_value); + return; + } + + ogs_assert(sess); + ogs_debug(" MME_S11_TEID[%d] SGW_S11_TEID[%d]", + sgwc_ue->mme_s11_teid, sgwc_ue->sgw_s11_teid); + ogs_debug(" SGW_S5C_TEID[0x%x] PGW_S5C_TEID[0x%x]", + sess->sgw_s5c_teid, sess->pgw_s5c_teid); + + message->h.type = OGS_GTP_BEARER_RESOURCE_COMMAND_TYPE; + message->h.teid = sess->pgw_s5c_teid; + + pkbuf = ogs_gtp_build_msg(message); + ogs_expect_or_return(pkbuf); + + s5c_xact = ogs_gtp_xact_local_create( + sess->gnode, &message->h, pkbuf, timeout, sess); + ogs_expect_or_return(s5c_xact); + + ogs_gtp_xact_associate(s11_xact, s5c_xact); + + rv = ogs_gtp_xact_commit(s5c_xact); + ogs_expect(rv == OGS_OK); +} diff --git a/src/sgwc/s11-handler.h b/src/sgwc/s11-handler.h new file mode 100644 index 000000000..141dd1efe --- /dev/null +++ b/src/sgwc/s11-handler.h @@ -0,0 +1,70 @@ +/* + * 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 SGWC_S11_HANDLER_H +#define SGWC_S11_HANDLER_H + +#include "context.h" + +#ifdef __cplusplus +extern "C" { +#endif + +void sgwc_s11_handle_create_session_request( + sgwc_ue_t *sgwc_ue, ogs_gtp_xact_t *s11_xact, + ogs_pkbuf_t *gtpbuf, ogs_gtp_message_t *message); +void sgwc_s11_handle_modify_bearer_request( + sgwc_ue_t *sgwc_ue, ogs_gtp_xact_t *s11_xact, + ogs_pkbuf_t *gtpbuf, ogs_gtp_message_t *message); +void sgwc_s11_handle_delete_session_request( + sgwc_ue_t *sgwc_ue, ogs_gtp_xact_t *s11_xact, + ogs_pkbuf_t *gtpbuf, ogs_gtp_message_t *message); +void sgwc_s11_handle_create_bearer_response( + sgwc_ue_t *sgwc_ue, ogs_gtp_xact_t *s11_xact, + ogs_pkbuf_t *gtpbuf, ogs_gtp_message_t *message); +void sgwc_s11_handle_update_bearer_response( + sgwc_ue_t *sgwc_ue, ogs_gtp_xact_t *s11_xact, + ogs_pkbuf_t *gtpbuf, ogs_gtp_message_t *message); +void sgwc_s11_handle_delete_bearer_response( + sgwc_ue_t *sgwc_ue, ogs_gtp_xact_t *s11_xact, + ogs_pkbuf_t *gtpbuf, ogs_gtp_message_t *message); + +void sgwc_s11_handle_release_access_bearers_request( + sgwc_ue_t *sgwc_ue, ogs_gtp_xact_t *s11_xact, + ogs_pkbuf_t *gtpbuf, ogs_gtp_message_t *message); +void sgwc_s11_handle_downlink_data_notification_ack( + sgwc_ue_t *sgwc_ue, ogs_gtp_xact_t *s11_xact, + ogs_pkbuf_t *gtpbuf, ogs_gtp_message_t *message); + +void sgwc_s11_handle_create_indirect_data_forwarding_tunnel_request( + sgwc_ue_t *sgwc_ue, ogs_gtp_xact_t *s11_xact, + ogs_pkbuf_t *gtpbuf, ogs_gtp_message_t *message); +void sgwc_s11_handle_delete_indirect_data_forwarding_tunnel_request( + sgwc_ue_t *sgwc_ue, ogs_gtp_xact_t *s11_xact, + ogs_pkbuf_t *gtpbuf, ogs_gtp_message_t *message); + +void sgwc_s11_handle_bearer_resource_command( + sgwc_ue_t *sgwc_ue, ogs_gtp_xact_t *s11_xact, + ogs_pkbuf_t *gtpbuf, ogs_gtp_message_t *message); + +#ifdef __cplusplus +} +#endif + +#endif /* SGWC_S11_HANDLER_H */ diff --git a/src/sgwc/s5c-handler.c b/src/sgwc/s5c-handler.c new file mode 100644 index 000000000..811277250 --- /dev/null +++ b/src/sgwc/s5c-handler.c @@ -0,0 +1,523 @@ +/* + * 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 "gtp-path.h" +#include "pfcp-path.h" + +#include "s5c-handler.h" + +static void timeout(ogs_gtp_xact_t *xact, void *data) +{ + sgwc_sess_t *sess = data; + sgwc_ue_t *sgwc_ue = NULL; + uint8_t type = 0; + + ogs_assert(xact); + ogs_assert(sess); + sgwc_ue = sess->sgwc_ue; + ogs_assert(sgwc_ue); + + type = xact->seq[0].type; + + ogs_error("GTP Timeout : IMSI[%s] Message-Type[%d]", + sgwc_ue->imsi_bcd, type); +} + +void sgwc_s5c_handle_create_session_response( + sgwc_sess_t *sess, ogs_gtp_xact_t *s5c_xact, + ogs_pkbuf_t *gtpbuf, ogs_gtp_message_t *message) +{ + int rv; + uint8_t cause_value; + + sgwc_ue_t *sgwc_ue = NULL; + sgwc_bearer_t *bearer = NULL; + sgwc_tunnel_t *ul_tunnel = NULL; + ogs_pfcp_far_t *far = NULL; + + ogs_gtp_f_teid_t *pgw_s5c_teid = NULL; + ogs_gtp_f_teid_t *pgw_s5u_teid = NULL; + + ogs_gtp_create_session_response_t *rsp = NULL; + ogs_gtp_xact_t *s11_xact = NULL; + + ogs_assert(s5c_xact); + s11_xact = s5c_xact->assoc_xact; + ogs_assert(s11_xact); + ogs_assert(gtpbuf); + ogs_assert(message); + rsp = &message->create_session_response; + ogs_assert(rsp); + + ogs_debug("Create Session Response"); + + if (!sess) { + ogs_warn("No Context in TEID"); + sess = s5c_xact->data; + ogs_assert(sess); + } + + rv = ogs_gtp_xact_commit(s5c_xact); + ogs_expect(rv == OGS_OK); + + if (rsp->cause.presence) { + ogs_gtp_cause_t *cause = rsp->cause.data; + ogs_assert(cause); + + cause_value = cause->value; + if (cause_value == OGS_GTP_CAUSE_REQUEST_ACCEPTED) { + if (rsp->bearer_contexts_created.cause.presence) { + cause = rsp->bearer_contexts_created.cause.data; + ogs_assert(cause); + + cause_value = cause->value; + } else { + ogs_error("No Cause"); + cause_value = OGS_GTP_CAUSE_MANDATORY_IE_MISSING; + } + } else { + /* Deliver PGW cause value to the MME */ + ogs_warn("Cause[%d] : No Accepted", cause_value); + ogs_gtp_send_error_message( + s11_xact, sgwc_ue ? sgwc_ue->mme_s11_teid : 0, + OGS_GTP_CREATE_SESSION_RESPONSE_TYPE, + cause_value); + return; + } + } else { + ogs_error("No Cause"); + cause_value = OGS_GTP_CAUSE_MANDATORY_IE_MISSING; + } + + if (cause_value != OGS_GTP_CAUSE_REQUEST_ACCEPTED) + ogs_warn("Cause[%d] : No Accepted", cause_value); + + if (rsp->bearer_contexts_created.cause.presence == 0) { + ogs_error("No EPS Bearer Cause"); + cause_value = OGS_GTP_CAUSE_MANDATORY_IE_MISSING; + } + if (rsp->bearer_contexts_created.presence == 0) { + ogs_error("No Bearer"); + cause_value = OGS_GTP_CAUSE_MANDATORY_IE_MISSING; + } + if (rsp->bearer_contexts_created.eps_bearer_id.presence == 0) { + ogs_error("No EPS Bearer ID"); + cause_value = OGS_GTP_CAUSE_MANDATORY_IE_MISSING; + } + + if (cause_value == OGS_GTP_CAUSE_REQUEST_ACCEPTED) { + bearer = sgwc_bearer_find_by_sess_ebi(sess, + rsp->bearer_contexts_created.eps_bearer_id.u8); + ogs_assert(bearer); + ul_tunnel = sgwc_ul_tunnel_in_bearer(bearer); + ogs_assert(ul_tunnel); + + sgwc_ue = sess->sgwc_ue; + ogs_assert(sgwc_ue); + } + if (!bearer) { + ogs_warn("No Context"); + cause_value = OGS_GTP_CAUSE_CONTEXT_NOT_FOUND; + } + + if (rsp->pgw_s5_s8__s2a_s2b_f_teid_for_pmip_based_interface_or_for_gtp_based_control_plane_interface. + presence == 0) { + ogs_error("No GTP TEID"); + cause_value = OGS_GTP_CAUSE_MANDATORY_IE_MISSING; + } + if (rsp->bearer_contexts_created.s5_s8_u_sgw_f_teid.presence == 0) { + ogs_error("No GTP TEID"); + cause_value = OGS_GTP_CAUSE_MANDATORY_IE_MISSING; + } + + if (cause_value != OGS_GTP_CAUSE_REQUEST_ACCEPTED) { + ogs_gtp_send_error_message( + s11_xact, sgwc_ue ? sgwc_ue->mme_s11_teid : 0, + OGS_GTP_CREATE_SESSION_RESPONSE_TYPE, cause_value); + return; + } + + /* Receive Data Plane(UL) : PGW-S5U */ + pgw_s5u_teid = rsp->bearer_contexts_created.s5_s8_u_sgw_f_teid.data; + ogs_assert(pgw_s5u_teid); + ul_tunnel->remote_teid = be32toh(pgw_s5u_teid->teid); + + rv = ogs_gtp_f_teid_to_ip(pgw_s5u_teid, &ul_tunnel->remote_ip); + if (rv != OGS_OK) { + ogs_gtp_send_error_message( + s11_xact, sgwc_ue ? sgwc_ue->mme_s11_teid : 0, + OGS_GTP_CREATE_SESSION_RESPONSE_TYPE, + OGS_GTP_CAUSE_MANDATORY_IE_MISSING); + return; + } + + far = ul_tunnel->far; + ogs_assert(far); + + ogs_pfcp_ip_to_outer_header_creation(&ul_tunnel->remote_ip, + &far->outer_header_creation, &far->outer_header_creation_len); + far->outer_header_creation.teid = ul_tunnel->remote_teid; + + /* Receive Control Plane(UL) : PGW-S5C */ + pgw_s5c_teid = rsp->pgw_s5_s8__s2a_s2b_f_teid_for_pmip_based_interface_or_for_gtp_based_control_plane_interface.data; + ogs_assert(pgw_s5c_teid); + sess->pgw_s5c_teid = be32toh(pgw_s5c_teid->teid); + + ogs_debug(" MME_S11_TEID[%d] SGW_S11_TEID[%d]", + sgwc_ue->mme_s11_teid, sgwc_ue->sgw_s11_teid); + ogs_debug(" SGW_S5C_TEID[0x%x] PGW_S5C_TEID[0x%x]", + sess->sgw_s5c_teid, sess->pgw_s5c_teid); + ogs_debug(" SGW_S5U_TEID[%d] PGW_S5U_TEID[%d]", + ul_tunnel->local_teid, ul_tunnel->remote_teid); + + sgwc_pfcp_send_bearer_modification_request( + bearer, s11_xact, gtpbuf, + OGS_PFCP_MODIFY_UL_ONLY|OGS_PFCP_MODIFY_ACTIVATE); +} + +void sgwc_s5c_handle_delete_session_response( + sgwc_sess_t *sess, ogs_gtp_xact_t *s5c_xact, + ogs_pkbuf_t *gtpbuf, ogs_gtp_message_t *message) +{ + int rv; + uint8_t cause_value; + + sgwc_ue_t *sgwc_ue = NULL; + + ogs_gtp_xact_t *s11_xact = NULL; + ogs_gtp_delete_session_response_t *rsp = NULL; + + ogs_assert(s5c_xact); + s11_xact = s5c_xact->assoc_xact; + ogs_assert(s11_xact); + ogs_assert(message); + rsp = &message->delete_session_response; + ogs_assert(rsp); + + ogs_debug("Delete Session Response"); + + if (!sess) { + ogs_warn("No Context in TEID"); + sess = s5c_xact->data; + ogs_assert(sess); + } + + rv = ogs_gtp_xact_commit(s5c_xact); + ogs_expect(rv == OGS_OK); + + if (rsp->cause.presence) { + ogs_gtp_cause_t *cause = rsp->cause.data; + ogs_assert(cause); + + cause_value = cause->value; + } else { + ogs_error("No Cause"); + cause_value = OGS_GTP_CAUSE_MANDATORY_IE_MISSING; + } + + if (cause_value != OGS_GTP_CAUSE_REQUEST_ACCEPTED) { + ogs_gtp_send_error_message( + s11_xact, sgwc_ue ? sgwc_ue->mme_s11_teid : 0, + OGS_GTP_DELETE_SESSION_RESPONSE_TYPE, cause_value); + return; + } + + sgwc_ue = sess->sgwc_ue; + ogs_assert(sgwc_ue); + + /* Remove a pgw session */ + ogs_debug(" MME_S11_TEID[%d] SGW_S11_TEID[%d]", + sgwc_ue->mme_s11_teid, sgwc_ue->sgw_s11_teid); + ogs_debug(" SGW_S5C_TEID[0x%x] PGW_S5C_TEID[0x%x]", + sess->sgw_s5c_teid, sess->pgw_s5c_teid); + + sgwc_pfcp_send_session_deletion_request(sess, s11_xact, gtpbuf); +} + +void sgwc_s5c_handle_create_bearer_request( + sgwc_sess_t *sess, ogs_gtp_xact_t *s5c_xact, + ogs_pkbuf_t *gtpbuf, ogs_gtp_message_t *message) +{ + int rv; + uint8_t cause_value = 0; + + sgwc_ue_t *sgwc_ue = NULL; + sgwc_bearer_t *bearer = NULL; + sgwc_tunnel_t *ul_tunnel = NULL; + ogs_pfcp_far_t *far = NULL; + + ogs_gtp_create_bearer_request_t *req = NULL; + ogs_gtp_f_teid_t *pgw_s5u_teid = NULL; + + ogs_assert(s5c_xact); + ogs_assert(message); + req = &message->create_bearer_request; + ogs_assert(req); + + ogs_debug("Create Bearer Request"); + + cause_value = OGS_GTP_CAUSE_REQUEST_ACCEPTED; + + if (!sess) { + ogs_warn("No Context"); + cause_value = OGS_GTP_CAUSE_CONTEXT_NOT_FOUND; + } + + if (req->linked_eps_bearer_id.presence == 0) { + ogs_error("No Linked EBI"); + cause_value = OGS_GTP_CAUSE_MANDATORY_IE_MISSING; + } + if (req->bearer_contexts.presence == 0) { + ogs_error("No Bearer"); + cause_value = OGS_GTP_CAUSE_MANDATORY_IE_MISSING; + } + if (req->bearer_contexts.eps_bearer_id.presence == 0) { + ogs_error("No EPS Bearer ID"); + cause_value = OGS_GTP_CAUSE_MANDATORY_IE_MISSING; + } + if (req->bearer_contexts.s5_s8_u_sgw_f_teid.presence == 0) { + ogs_error("No GTP TEID"); + cause_value = OGS_GTP_CAUSE_MANDATORY_IE_MISSING; + } + + if (cause_value != OGS_GTP_CAUSE_REQUEST_ACCEPTED) { + ogs_gtp_send_error_message(s5c_xact, sess ? sess->pgw_s5c_teid : 0, + OGS_GTP_CREATE_BEARER_RESPONSE_TYPE, cause_value); + return; + } + + sgwc_ue = sess->sgwc_ue; + ogs_assert(sgwc_ue); + + bearer = sgwc_bearer_add(sess); + ogs_assert(bearer); + ul_tunnel = sgwc_ul_tunnel_in_bearer(bearer); + ogs_assert(ul_tunnel); + + /* Receive Data Plane(UL) : PGW-S5U */ + pgw_s5u_teid = req->bearer_contexts.s5_s8_u_sgw_f_teid.data; + ogs_assert(pgw_s5u_teid); + ul_tunnel->remote_teid = be32toh(pgw_s5u_teid->teid); + + ogs_debug(" MME_S11_TEID[%d] SGW_S11_TEID[%d]", + sgwc_ue->mme_s11_teid, sgwc_ue->sgw_s11_teid); + ogs_debug(" SGW_S5C_TEID[0x%x] PGW_S5C_TEID[0x%x]", + sess->sgw_s5c_teid, sess->pgw_s5c_teid); + ogs_debug(" SGW_S5U_TEID[%d] PGW_S5U_TEID[%d]", + ul_tunnel->local_teid, ul_tunnel->remote_teid); + + rv = ogs_gtp_f_teid_to_ip(pgw_s5u_teid, &ul_tunnel->remote_ip); + if (rv != OGS_OK) { + ogs_gtp_send_error_message(s5c_xact, sess ? sess->pgw_s5c_teid : 0, + OGS_GTP_CREATE_BEARER_RESPONSE_TYPE, + OGS_GTP_CAUSE_MANDATORY_IE_MISSING); + return; + } + + far = ul_tunnel->far; + ogs_assert(far); + + ogs_pfcp_ip_to_outer_header_creation(&ul_tunnel->remote_ip, + &far->outer_header_creation, &far->outer_header_creation_len); + far->outer_header_creation.teid = ul_tunnel->remote_teid; + + sgwc_pfcp_send_bearer_modification_request( + bearer, s5c_xact, gtpbuf, + OGS_PFCP_MODIFY_UL_ONLY|OGS_PFCP_MODIFY_CREATE); +} + +void sgwc_s5c_handle_update_bearer_request( + sgwc_sess_t *sess, ogs_gtp_xact_t *s5c_xact, + ogs_pkbuf_t *gtpbuf, ogs_gtp_message_t *message) +{ + int rv; + uint8_t cause_value = 0; + ogs_gtp_xact_t *s11_xact = NULL; + ogs_gtp_update_bearer_request_t *req = NULL; + ogs_pkbuf_t *pkbuf = NULL; + sgwc_ue_t *sgwc_ue = NULL; + + ogs_assert(s5c_xact); + ogs_assert(message); + req = &message->update_bearer_request; + ogs_assert(req); + + ogs_debug("Update Bearer Request"); + + cause_value = OGS_GTP_CAUSE_REQUEST_ACCEPTED; + + if (!sess) { + ogs_warn("No Context"); + cause_value = OGS_GTP_CAUSE_CONTEXT_NOT_FOUND; + } + + if (req->bearer_contexts.presence == 0) { + ogs_error("No Bearer"); + cause_value = OGS_GTP_CAUSE_MANDATORY_IE_MISSING; + } + if (req->bearer_contexts.eps_bearer_id.presence == 0) { + ogs_error("No EPS Bearer ID"); + cause_value = OGS_GTP_CAUSE_MANDATORY_IE_MISSING; + } + + if (cause_value != OGS_GTP_CAUSE_REQUEST_ACCEPTED) { + ogs_gtp_send_error_message(s5c_xact, sess ? sess->pgw_s5c_teid : 0, + OGS_GTP_UPDATE_BEARER_RESPONSE_TYPE, cause_value); + return; + } + + sgwc_ue = sess->sgwc_ue; + ogs_assert(sgwc_ue); + + ogs_debug(" MME_S11_TEID[%d] SGW_S11_TEID[%d]", + sgwc_ue->mme_s11_teid, sgwc_ue->sgw_s11_teid); + ogs_debug(" SGW_S5C_TEID[0x%x] PGW_S5C_TEID[0x%x]", + sess->sgw_s5c_teid, sess->pgw_s5c_teid); + + message->h.type = OGS_GTP_UPDATE_BEARER_REQUEST_TYPE; + message->h.teid = sgwc_ue->mme_s11_teid; + + pkbuf = ogs_gtp_build_msg(message); + ogs_expect_or_return(pkbuf); + + s11_xact = s5c_xact->assoc_xact; + if (!s11_xact) { + s11_xact = ogs_gtp_xact_local_create( + sgwc_ue->gnode, &message->h, pkbuf, timeout, sess); + ogs_expect_or_return(s11_xact); + + ogs_gtp_xact_associate(s5c_xact, s11_xact); + } else { + rv = ogs_gtp_xact_update_tx(s11_xact, &message->h, pkbuf); + ogs_expect_or_return(rv == OGS_OK); + } + + rv = ogs_gtp_xact_commit(s11_xact); + ogs_expect(rv == OGS_OK); + + ogs_debug("Update Bearer Request : SGW <-- PGW"); +} + +void sgwc_s5c_handle_delete_bearer_request( + sgwc_sess_t *sess, ogs_gtp_xact_t *s5c_xact, + ogs_pkbuf_t *gtpbuf, ogs_gtp_message_t *message) +{ + int rv; + uint8_t cause_value = 0; + ogs_gtp_xact_t *s11_xact = NULL; + ogs_gtp_delete_bearer_request_t *req = NULL; + ogs_pkbuf_t *pkbuf = NULL; + sgwc_ue_t *sgwc_ue = NULL; + + ogs_assert(s5c_xact); + ogs_assert(message); + req = &message->delete_bearer_request; + ogs_assert(req); + + ogs_debug("Delete Bearer Request"); + + cause_value = OGS_GTP_CAUSE_REQUEST_ACCEPTED; + + if (!sess) { + ogs_warn("No Context"); + cause_value = OGS_GTP_CAUSE_CONTEXT_NOT_FOUND; + } + + if (req->linked_eps_bearer_id.presence == 0 && + req->eps_bearer_ids.presence == 0) { + ogs_error("No Linked EBI or EPS Bearer ID"); + cause_value = OGS_GTP_CAUSE_CONTEXT_NOT_FOUND; + } + + if (cause_value != OGS_GTP_CAUSE_REQUEST_ACCEPTED) { + ogs_gtp_send_error_message(s5c_xact, sess ? sess->pgw_s5c_teid : 0, + OGS_GTP_DELETE_BEARER_RESPONSE_TYPE, cause_value); + return; + } + + sgwc_ue = sess->sgwc_ue; + ogs_assert(sgwc_ue); + + ogs_debug(" MME_S11_TEID[%d] SGW_S11_TEID[%d]", + sgwc_ue->mme_s11_teid, sgwc_ue->sgw_s11_teid); + ogs_debug(" SGW_S5C_TEID[0x%x] PGW_S5C_TEID[0x%x]", + sess->sgw_s5c_teid, sess->pgw_s5c_teid); + + message->h.type = OGS_GTP_DELETE_BEARER_REQUEST_TYPE; + message->h.teid = sgwc_ue->mme_s11_teid; + + pkbuf = ogs_gtp_build_msg(message); + ogs_expect_or_return(pkbuf); + + s11_xact = s5c_xact->assoc_xact; + if (!s11_xact) { + s11_xact = ogs_gtp_xact_local_create( + sgwc_ue->gnode, &message->h, pkbuf, timeout, sess); + ogs_expect_or_return(s11_xact); + + ogs_gtp_xact_associate(s5c_xact, s11_xact); + } else { + rv = ogs_gtp_xact_update_tx(s11_xact, &message->h, pkbuf); + ogs_expect_or_return(rv == OGS_OK); + } + + rv = ogs_gtp_xact_commit(s11_xact); + ogs_expect(rv == OGS_OK); +} + +void sgwc_s5c_handle_bearer_resource_failure_indication( + sgwc_sess_t *sess, ogs_gtp_xact_t *s5c_xact, + ogs_pkbuf_t *gtpbuf, ogs_gtp_message_t *message) +{ + uint8_t cause_value = 0; + ogs_gtp_xact_t *s11_xact = NULL; + ogs_gtp_bearer_resource_failure_indication_t *ind = NULL; + + sgwc_ue_t *sgwc_ue = NULL; + + ogs_assert(s5c_xact); + s11_xact = s5c_xact->assoc_xact; + ogs_assert(s11_xact); + ogs_assert(message); + ind = &message->bearer_resource_failure_indication; + ogs_assert(ind); + + ogs_debug("Bearer Resource Failure Indication"); + + if (!sess) { + ogs_warn("No Context"); + cause_value = OGS_GTP_CAUSE_CONTEXT_NOT_FOUND; + } + + if (ind->cause.presence) { + ogs_gtp_cause_t *cause = ind->cause.data; + ogs_assert(cause); + + cause_value = cause->value; + } else { + ogs_error("No Cause"); + cause_value = OGS_GTP_CAUSE_MANDATORY_IE_MISSING; + } + + sgwc_ue = sess->sgwc_ue; + ogs_assert(sgwc_ue); + + ogs_gtp_send_error_message(s11_xact, sgwc_ue ? sgwc_ue->mme_s11_teid : 0, + OGS_GTP_BEARER_RESOURCE_FAILURE_INDICATION_TYPE, cause_value); +} diff --git a/src/sgwc/s5c-handler.h b/src/sgwc/s5c-handler.h new file mode 100644 index 000000000..b1238ee71 --- /dev/null +++ b/src/sgwc/s5c-handler.h @@ -0,0 +1,52 @@ +/* + * 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 SGWC_S5C_HANDLER_H +#define SGWC_S5C_HANDLER_H + +#include "context.h" + +#ifdef __cplusplus +extern "C" { +#endif + +void sgwc_s5c_handle_create_session_response( + sgwc_sess_t *sess, ogs_gtp_xact_t *s5c_xact, + ogs_pkbuf_t *gtpbuf, ogs_gtp_message_t *message); +void sgwc_s5c_handle_delete_session_response( + sgwc_sess_t *sess, ogs_gtp_xact_t *s5c_xact, + ogs_pkbuf_t *gtpbuf, ogs_gtp_message_t *message); +void sgwc_s5c_handle_create_bearer_request( + sgwc_sess_t *sess, ogs_gtp_xact_t *s5c_xact, + ogs_pkbuf_t *gtpbuf, ogs_gtp_message_t *message); +void sgwc_s5c_handle_update_bearer_request( + sgwc_sess_t *sess, ogs_gtp_xact_t *s5c_xact, + ogs_pkbuf_t *gtpbuf, ogs_gtp_message_t *message); +void sgwc_s5c_handle_delete_bearer_request( + sgwc_sess_t *sess, ogs_gtp_xact_t *s5c_xact, + ogs_pkbuf_t *gtpbuf, ogs_gtp_message_t *message); +void sgwc_s5c_handle_bearer_resource_failure_indication( + sgwc_sess_t *sess, ogs_gtp_xact_t *s5c_xact, + ogs_pkbuf_t *gtpbuf, ogs_gtp_message_t *message); + +#ifdef __cplusplus +} +#endif + +#endif /* SGWC_S5C_HANDLER_H */ diff --git a/src/sgwc/sgwc-sm.c b/src/sgwc/sgwc-sm.c new file mode 100644 index 000000000..adabfbb52 --- /dev/null +++ b/src/sgwc/sgwc-sm.c @@ -0,0 +1,313 @@ +/* + * 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 "s11-handler.h" +#include "s5c-handler.h" + +#include "gtp-path.h" +#include "pfcp-path.h" + +static void sgwc_handle_echo_request( + ogs_gtp_xact_t *xact, ogs_gtp_echo_request_t *req) +{ + ogs_assert(xact); + ogs_assert(req); + + ogs_debug("[SGW] Receiving Echo Request"); + /* FIXME : Before implementing recovery counter correctly, + * I'll re-use the recovery value in request message */ + ogs_gtp_send_echo_response(xact, req->recovery.u8, 0); +} + +static void sgwc_handle_echo_response( + ogs_gtp_xact_t *s11_xact, ogs_gtp_echo_response_t *rsp) +{ + /* Not Implemented */ +} + + +void sgwc_state_initial(ogs_fsm_t *s, sgwc_event_t *e) +{ + sgwc_sm_debug(e); + + ogs_assert(s); + + OGS_FSM_TRAN(s, &sgwc_state_operational); +} + +void sgwc_state_final(ogs_fsm_t *s, sgwc_event_t *e) +{ + sgwc_sm_debug(e); + + ogs_assert(s); +} + +void sgwc_state_operational(ogs_fsm_t *s, sgwc_event_t *e) +{ + int rv; + + ogs_pkbuf_t *recvbuf = NULL; + sgwc_ue_t *sgwc_ue = NULL; + sgwc_sess_t *sess = NULL; + + ogs_gtp_xact_t *gtp_xact = NULL; + ogs_gtp_message_t gtp_message; + ogs_gtp_node_t *gnode = NULL; + + ogs_pfcp_node_t *pfcp_node = NULL; + ogs_pfcp_xact_t *pfcp_xact = NULL; + ogs_pfcp_message_t pfcp_message; + + sgwc_sm_debug(e); + + ogs_assert(s); + + switch (e->id) { + case OGS_FSM_ENTRY_SIG: + rv = sgwc_gtp_open(); + if (rv != OGS_OK) { + ogs_error("Can't establish SGW path"); + break; + } + rv = sgwc_pfcp_open(); + if (rv != OGS_OK) { + ogs_fatal("Can't establish N4-PFCP path"); + break; + } + break; + + case OGS_FSM_EXIT_SIG: + sgwc_gtp_close(); + sgwc_pfcp_close(); + break; + + case SGWC_EVT_SXA_MESSAGE: + ogs_assert(e); + recvbuf = e->pkbuf; + ogs_assert(recvbuf); + pfcp_node = e->pfcp_node; + ogs_assert(pfcp_node); + ogs_assert(OGS_FSM_STATE(&pfcp_node->sm)); + + if (ogs_pfcp_parse_msg(&pfcp_message, recvbuf) != OGS_OK) { + ogs_error("ogs_pfcp_parse_msg() failed"); + ogs_pkbuf_free(recvbuf); + break; + } + + rv = ogs_pfcp_xact_receive(pfcp_node, &pfcp_message.h, &pfcp_xact); + if (rv != OGS_OK) { + ogs_pkbuf_free(recvbuf); + break; + } + + e->pfcp_message = &pfcp_message; + e->pfcp_xact = pfcp_xact; + + e->gtp_message = NULL; + if (pfcp_xact->gtpbuf) { + rv = ogs_gtp_parse_msg(>p_message, pfcp_xact->gtpbuf); + + ogs_pkbuf_free(pfcp_xact->gtpbuf); + e->gtp_message = >p_message; + } + + ogs_fsm_dispatch(&pfcp_node->sm, e); + if (OGS_FSM_CHECK(&pfcp_node->sm, sgwc_pfcp_state_exception)) { + ogs_error("PFCP state machine exception"); + } + + ogs_pkbuf_free(recvbuf); + break; + + case SGWC_EVT_SXA_TIMER: + case SGWC_EVT_SXA_NO_HEARTBEAT: + ogs_assert(e); + pfcp_node = e->pfcp_node; + ogs_assert(pfcp_node); + ogs_assert(OGS_FSM_STATE(&pfcp_node->sm)); + + ogs_fsm_dispatch(&pfcp_node->sm, e); + break; + + case SGWC_EVT_S11_MESSAGE: + ogs_assert(e); + recvbuf = e->pkbuf; + ogs_assert(recvbuf); + + if (ogs_gtp_parse_msg(>p_message, recvbuf) != OGS_OK) { + ogs_error("ogs_gtp_parse_msg() failed"); + ogs_pkbuf_free(recvbuf); + break; + } + + if (gtp_message.h.teid_presence && gtp_message.h.teid != 0) { + /* Cause is not "Context not found" */ + sgwc_ue = sgwc_ue_find_by_teid(gtp_message.h.teid); + } + + if (sgwc_ue) { + gnode = sgwc_ue->gnode; + ogs_assert(gnode); + } else { + gnode = e->gnode; + ogs_assert(gnode); + } + + rv = ogs_gtp_xact_receive(gnode, >p_message.h, >p_xact); + if (rv != OGS_OK) { + ogs_pkbuf_free(recvbuf); + break; + } + + switch(gtp_message.h.type) { + case OGS_GTP_ECHO_REQUEST_TYPE: + sgwc_handle_echo_request(gtp_xact, >p_message.echo_request); + break; + case OGS_GTP_ECHO_RESPONSE_TYPE: + sgwc_handle_echo_response(gtp_xact, >p_message.echo_response); + break; + case OGS_GTP_CREATE_SESSION_REQUEST_TYPE: + if (gtp_message.h.teid == 0) { + ogs_expect(!sgwc_ue); + sgwc_ue = sgwc_ue_add_by_message(>p_message); + if (sgwc_ue) + OGS_SETUP_GTP_NODE(sgwc_ue, gnode); + } + sgwc_s11_handle_create_session_request( + sgwc_ue, gtp_xact, recvbuf, >p_message); + break; + case OGS_GTP_MODIFY_BEARER_REQUEST_TYPE: + sgwc_s11_handle_modify_bearer_request( + sgwc_ue, gtp_xact, recvbuf, >p_message); + break; + case OGS_GTP_DELETE_SESSION_REQUEST_TYPE: + sgwc_s11_handle_delete_session_request( + sgwc_ue, gtp_xact, recvbuf, >p_message); + break; + case OGS_GTP_CREATE_BEARER_RESPONSE_TYPE: + sgwc_s11_handle_create_bearer_response( + sgwc_ue, gtp_xact, recvbuf, >p_message); + break; + case OGS_GTP_UPDATE_BEARER_RESPONSE_TYPE: + sgwc_s11_handle_update_bearer_response( + sgwc_ue, gtp_xact, recvbuf, >p_message); + break; + case OGS_GTP_DELETE_BEARER_RESPONSE_TYPE: + sgwc_s11_handle_delete_bearer_response( + sgwc_ue, gtp_xact, recvbuf, >p_message); + break; + case OGS_GTP_RELEASE_ACCESS_BEARERS_REQUEST_TYPE: + sgwc_s11_handle_release_access_bearers_request( + sgwc_ue, gtp_xact, recvbuf, >p_message); + break; + case OGS_GTP_DOWNLINK_DATA_NOTIFICATION_ACKNOWLEDGE_TYPE: + sgwc_s11_handle_downlink_data_notification_ack( + sgwc_ue, gtp_xact, recvbuf, >p_message); + break; + case OGS_GTP_CREATE_INDIRECT_DATA_FORWARDING_TUNNEL_REQUEST_TYPE: + sgwc_s11_handle_create_indirect_data_forwarding_tunnel_request( + sgwc_ue, gtp_xact, recvbuf, >p_message); + break; + case OGS_GTP_DELETE_INDIRECT_DATA_FORWARDING_TUNNEL_REQUEST_TYPE: + sgwc_s11_handle_delete_indirect_data_forwarding_tunnel_request( + sgwc_ue, gtp_xact, recvbuf, >p_message); + break; + case OGS_GTP_BEARER_RESOURCE_COMMAND_TYPE: + sgwc_s11_handle_bearer_resource_command( + sgwc_ue, gtp_xact, recvbuf, >p_message); + break; + default: + ogs_warn("Not implemented(type:%d)", gtp_message.h.type); + break; + } + ogs_pkbuf_free(recvbuf); + break; + + case SGWC_EVT_S5C_MESSAGE: + ogs_assert(e); + recvbuf = e->pkbuf; + ogs_assert(recvbuf); + + if (ogs_gtp_parse_msg(>p_message, recvbuf) != OGS_OK) { + ogs_error("ogs_gtp_parse_msg() failed"); + ogs_pkbuf_free(recvbuf); + break; + } + + if (gtp_message.h.teid_presence && gtp_message.h.teid != 0) { + sess = sgwc_sess_find_by_teid(gtp_message.h.teid); + } + + if (sess) { + gnode = sess->gnode; + ogs_assert(gnode); + } else { + gnode = e->gnode; + ogs_assert(gnode); + } + + rv = ogs_gtp_xact_receive(gnode, >p_message.h, >p_xact); + if (rv != OGS_OK) { + ogs_pkbuf_free(recvbuf); + break; + } + + switch(gtp_message.h.type) { + case OGS_GTP_ECHO_REQUEST_TYPE: + sgwc_handle_echo_request(gtp_xact, >p_message.echo_request); + break; + case OGS_GTP_ECHO_RESPONSE_TYPE: + sgwc_handle_echo_response(gtp_xact, >p_message.echo_response); + break; + case OGS_GTP_CREATE_SESSION_RESPONSE_TYPE: + sgwc_s5c_handle_create_session_response( + sess, gtp_xact, recvbuf, >p_message); + break; + case OGS_GTP_DELETE_SESSION_RESPONSE_TYPE: + sgwc_s5c_handle_delete_session_response( + sess, gtp_xact, recvbuf, >p_message); + break; + case OGS_GTP_CREATE_BEARER_REQUEST_TYPE: + sgwc_s5c_handle_create_bearer_request( + sess, gtp_xact, recvbuf, >p_message); + break; + case OGS_GTP_UPDATE_BEARER_REQUEST_TYPE: + sgwc_s5c_handle_update_bearer_request( + sess, gtp_xact, recvbuf, >p_message); + break; + case OGS_GTP_DELETE_BEARER_REQUEST_TYPE: + sgwc_s5c_handle_delete_bearer_request( + sess, gtp_xact, recvbuf, >p_message); + break; + case OGS_GTP_BEARER_RESOURCE_FAILURE_INDICATION_TYPE: + sgwc_s5c_handle_bearer_resource_failure_indication( + sess, gtp_xact, recvbuf, >p_message); + break; + default: + ogs_warn("Not implmeneted(type:%d)", gtp_message.h.type); + break; + } + ogs_pkbuf_free(recvbuf); + break; + default: + ogs_error("No handler for event %s", sgwc_event_get_name(e)); + break; + } +} diff --git a/src/sgwc/sgwc-sm.h b/src/sgwc/sgwc-sm.h new file mode 100644 index 000000000..10238b96e --- /dev/null +++ b/src/sgwc/sgwc-sm.h @@ -0,0 +1,47 @@ +/* + * 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 SGWC_SM_H +#define SGWC_SM_H + +#include "event.h" + +#ifdef __cplusplus +extern "C" { +#endif + +void sgwc_state_initial(ogs_fsm_t *s, sgwc_event_t *e); +void sgwc_state_final(ogs_fsm_t *s, sgwc_event_t *e); +void sgwc_state_operational(ogs_fsm_t *s, sgwc_event_t *e); +void sgwc_state_exception(ogs_fsm_t *s, sgwc_event_t *e); + +void sgwc_pfcp_state_initial(ogs_fsm_t *s, sgwc_event_t *e); +void sgwc_pfcp_state_final(ogs_fsm_t *s, sgwc_event_t *e); +void sgwc_pfcp_state_will_associate(ogs_fsm_t *s, sgwc_event_t *e); +void sgwc_pfcp_state_associated(ogs_fsm_t *s, sgwc_event_t *e); +void sgwc_pfcp_state_exception(ogs_fsm_t *s, sgwc_event_t *e); + +#define sgwc_sm_debug(__pe) \ + ogs_debug("%s(): %s\n", __func__, sgwc_event_get_name(__pe)) + +#ifdef __cplusplus +} +#endif + +#endif /* !SGWC_SM_H */ diff --git a/src/sgwc/sxa-build.c b/src/sgwc/sxa-build.c new file mode 100644 index 000000000..c14a76fb6 --- /dev/null +++ b/src/sgwc/sxa-build.c @@ -0,0 +1,370 @@ +/* + * 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 "sxa-build.h" + +ogs_pkbuf_t *sgwc_sxa_build_session_establishment_request( + uint8_t type, sgwc_sess_t *sess) +{ + ogs_pfcp_message_t pfcp_message; + ogs_pfcp_session_establishment_request_t *req = NULL; + ogs_pkbuf_t *pkbuf = NULL; + + sgwc_bearer_t *bearer = NULL; + + ogs_pfcp_pdr_t *pdr = NULL; + ogs_pfcp_far_t *far = NULL; + ogs_pfcp_urr_t *urr = NULL; + ogs_pfcp_qer_t *qer = NULL; + int i; + + ogs_pfcp_node_id_t node_id; + ogs_pfcp_f_seid_t f_seid; + int len; + + ogs_debug("Session Establishment Request"); + ogs_assert(sess); + + req = &pfcp_message.pfcp_session_establishment_request; + memset(&pfcp_message, 0, sizeof(ogs_pfcp_message_t)); + + /* Node ID */ + ogs_pfcp_sockaddr_to_node_id( + ogs_pfcp_self()->pfcp_addr, ogs_pfcp_self()->pfcp_addr6, + ogs_config()->parameter.prefer_ipv4, + &node_id, &len); + req->node_id.presence = 1; + req->node_id.data = &node_id; + req->node_id.len = len; + + /* F-SEID */ + ogs_pfcp_sockaddr_to_f_seid( + ogs_pfcp_self()->pfcp_addr, ogs_pfcp_self()->pfcp_addr6, + &f_seid, &len); + f_seid.seid = htobe64(sess->sgwc_sxa_seid); + req->cp_f_seid.presence = 1; + req->cp_f_seid.data = &f_seid; + req->cp_f_seid.len = len; + + ogs_pfcp_pdrbuf_init(); + + /* Create PDR */ + i = 0; + ogs_list_for_each(&sess->bearer_list, bearer) { + ogs_list_for_each(&bearer->pfcp.pdr_list, pdr) { + ogs_pfcp_build_create_pdr(&req->create_pdr[i], i, pdr); + i++; + } + } + + /* Create FAR */ + i = 0; + ogs_list_for_each(&sess->bearer_list, bearer) { + ogs_list_for_each(&bearer->pfcp.far_list, far) { + ogs_pfcp_build_create_far(&req->create_far[i], i, far); + i++; + } + } + + /* Create URR */ + i = 0; + ogs_list_for_each(&sess->bearer_list, bearer) { + ogs_list_for_each(&bearer->pfcp.urr_list, urr) { + ogs_pfcp_build_create_urr(&req->create_urr[i], i, urr); + i++; + } + } + + /* Create QER */ + i = 0; + ogs_list_for_each(&sess->bearer_list, bearer) { + ogs_list_for_each(&bearer->pfcp.qer_list, qer) { + ogs_pfcp_build_create_qer(&req->create_qer[i], i, qer); + i++; + } + } + + /* PDN Type */ + req->pdn_type.presence = 1; + req->pdn_type.u8 = sess->pdn.paa.pdn_type; + + pfcp_message.h.type = type; + pkbuf = ogs_pfcp_build_msg(&pfcp_message); + + ogs_pfcp_pdrbuf_clear(); + + return pkbuf; +} + +ogs_pkbuf_t *sgwc_sxa_build_sess_modification_request( + uint8_t type, sgwc_sess_t *sess, uint64_t modify_flags) +{ + ogs_pfcp_message_t pfcp_message; + ogs_pfcp_session_modification_request_t *req = NULL; + ogs_pfcp_pdr_t *pdr = NULL; + ogs_pfcp_far_t *far = NULL; + ogs_pkbuf_t *pkbuf = NULL; + sgwc_bearer_t *bearer = NULL; + sgwc_tunnel_t *tunnel = NULL; + + int num_of_remove_pdr = 0; + int num_of_remove_far = 0; + int num_of_create_pdr = 0; + int num_of_create_far = 0; + int num_of_update_far = 0; + + ogs_debug("Session Modification Request"); + ogs_assert(sess); + ogs_assert(modify_flags); + + req = &pfcp_message.pfcp_session_modification_request; + memset(&pfcp_message, 0, sizeof(ogs_pfcp_message_t)); + + ogs_list_for_each(&sess->bearer_list, bearer) { + ogs_list_for_each(&bearer->tunnel_list, tunnel) { + if (((modify_flags & + (OGS_PFCP_MODIFY_DL_ONLY| + OGS_PFCP_MODIFY_UL_ONLY| + OGS_PFCP_MODIFY_INDIRECT)) == 0) || + + ((modify_flags & OGS_PFCP_MODIFY_DL_ONLY) && + (tunnel->interface_type == OGS_GTP_F_TEID_S5_S8_SGW_GTP_U)) || + + ((modify_flags & OGS_PFCP_MODIFY_UL_ONLY) && + (tunnel->interface_type == OGS_GTP_F_TEID_S1_U_SGW_GTP_U)) || + + (((modify_flags & OGS_PFCP_MODIFY_INDIRECT) && + ((tunnel->interface_type == + OGS_GTP_F_TEID_SGW_GTP_U_FOR_DL_DATA_FORWARDING) || + (tunnel->interface_type == + OGS_GTP_F_TEID_SGW_GTP_U_FOR_UL_DATA_FORWARDING))))) { + + if (modify_flags & OGS_PFCP_MODIFY_REMOVE) { + pdr = tunnel->pdr; + if (pdr) { + ogs_pfcp_tlv_remove_pdr_t *message = + &req->remove_pdr[num_of_remove_pdr]; + + message->presence = 1; + message->pdr_id.presence = 1; + message->pdr_id.u16 = pdr->id; + + num_of_remove_pdr++; + } else + ogs_assert_if_reached(); + + far = tunnel->far; + if (far) { + ogs_pfcp_tlv_remove_far_t *message = + &req->remove_far[num_of_remove_far]; + + message->presence = 1; + message->far_id.presence = 1; + message->far_id.u32 = far->id; + + num_of_remove_far++; + } else + ogs_assert_if_reached(); + + } else { + if (modify_flags & OGS_PFCP_MODIFY_CREATE) { + pdr = tunnel->pdr; + if (pdr) { + ogs_pfcp_build_create_pdr( + &req->create_pdr[num_of_create_pdr], + num_of_create_pdr, pdr); + + num_of_create_pdr++; + } else + ogs_assert_if_reached(); + + far = tunnel->far; + if (far) { + ogs_pfcp_build_create_far( + &req->create_far[num_of_create_far], + num_of_create_far, far); + + num_of_create_far++; + } else + ogs_assert_if_reached(); + } + if (modify_flags & OGS_PFCP_MODIFY_DEACTIVATE) { + far = tunnel->far; + if (far) { + ogs_pfcp_build_update_far_deactivate( + &req->update_far[num_of_update_far], + num_of_update_far, far); + + num_of_update_far++; + } else + ogs_assert_if_reached(); + } + } + } + + } + } + + pfcp_message.h.type = type; + pkbuf = ogs_pfcp_build_msg(&pfcp_message); + + return pkbuf; +} + +ogs_pkbuf_t *sgwc_sxa_build_bearer_modification_request( + uint8_t type, sgwc_bearer_t *bearer, uint64_t modify_flags) +{ + ogs_pfcp_message_t pfcp_message; + ogs_pfcp_session_modification_request_t *req = NULL; + sgwc_tunnel_t *tunnel = NULL; + ogs_pfcp_pdr_t *pdr = NULL; + ogs_pfcp_far_t *far = NULL; + ogs_pkbuf_t *pkbuf = NULL; + int num_of_remove_pdr = 0; + int num_of_remove_far = 0; + int num_of_create_pdr = 0; + int num_of_create_far = 0; + int num_of_update_far = 0; + + sgwc_sess_t *sess = NULL; + + ogs_debug("Session Modification Request"); + ogs_assert(bearer); + sess = bearer->sess; + ogs_assert(sess); + ogs_assert(modify_flags); + + req = &pfcp_message.pfcp_session_modification_request; + memset(&pfcp_message, 0, sizeof(ogs_pfcp_message_t)); + + if (modify_flags & OGS_PFCP_MODIFY_CREATE) { + ogs_pfcp_pdrbuf_init(); + } + + ogs_list_for_each(&bearer->tunnel_list, tunnel) { + if (((modify_flags & + (OGS_PFCP_MODIFY_DL_ONLY| + OGS_PFCP_MODIFY_UL_ONLY| + OGS_PFCP_MODIFY_INDIRECT)) == 0) || + + ((modify_flags & OGS_PFCP_MODIFY_DL_ONLY) && + (tunnel->interface_type == OGS_GTP_F_TEID_S5_S8_SGW_GTP_U)) || + + ((modify_flags & OGS_PFCP_MODIFY_UL_ONLY) && + (tunnel->interface_type == OGS_GTP_F_TEID_S1_U_SGW_GTP_U)) || + + (((modify_flags & OGS_PFCP_MODIFY_INDIRECT) && + ((tunnel->interface_type == + OGS_GTP_F_TEID_SGW_GTP_U_FOR_DL_DATA_FORWARDING) || + (tunnel->interface_type == + OGS_GTP_F_TEID_SGW_GTP_U_FOR_UL_DATA_FORWARDING))))) { + + if (modify_flags & OGS_PFCP_MODIFY_REMOVE) { + pdr = tunnel->pdr; + if (pdr) { + ogs_pfcp_tlv_remove_pdr_t *message = + &req->remove_pdr[num_of_remove_pdr]; + + message->presence = 1; + message->pdr_id.presence = 1; + message->pdr_id.u16 = pdr->id; + + num_of_remove_pdr++; + } else + ogs_assert_if_reached(); + + far = tunnel->far; + if (far) { + ogs_pfcp_tlv_remove_far_t *message = + &req->remove_far[num_of_remove_far]; + + message->presence = 1; + message->far_id.presence = 1; + message->far_id.u32 = far->id; + + num_of_remove_far++; + } else + ogs_assert_if_reached(); + + } else { + if (modify_flags & OGS_PFCP_MODIFY_CREATE) { + pdr = tunnel->pdr; + if (pdr) { + ogs_pfcp_build_create_pdr( + &req->create_pdr[num_of_create_pdr], + num_of_create_pdr, pdr); + + num_of_create_pdr++; + } else + ogs_assert_if_reached(); + + far = tunnel->far; + if (far) { + ogs_pfcp_build_create_far( + &req->create_far[num_of_create_far], + num_of_create_far, far); + + num_of_create_far++; + } else + ogs_assert_if_reached(); + } + + if (modify_flags & OGS_PFCP_MODIFY_ACTIVATE) { + far = tunnel->far; + if (far) { + if (modify_flags & OGS_PFCP_MODIFY_END_MARKER) { + far->smreq_flags.send_end_marker_packets = 1; + } + + ogs_pfcp_build_update_far_activate( + &req->update_far[num_of_update_far], + num_of_update_far, far); + + num_of_update_far++; + + /* Clear all FAR flags */ + tunnel->far->smreq_flags.value = 0; + } else + ogs_assert_if_reached(); + } + } + } + } + + pfcp_message.h.type = type; + pkbuf = ogs_pfcp_build_msg(&pfcp_message); + + if (modify_flags & OGS_PFCP_MODIFY_CREATE) { + ogs_pfcp_pdrbuf_clear(); + } + + return pkbuf; +} + +ogs_pkbuf_t *sgwc_sxa_build_session_deletion_request( + uint8_t type, sgwc_sess_t *sess) +{ + ogs_pfcp_message_t pfcp_message; + + ogs_debug("Session Deletion Request"); + ogs_assert(sess); + + pfcp_message.h.type = type; + return ogs_pfcp_build_msg(&pfcp_message); +} diff --git a/src/sgwc/sxa-build.h b/src/sgwc/sxa-build.h new file mode 100644 index 000000000..7acf28aa4 --- /dev/null +++ b/src/sgwc/sxa-build.h @@ -0,0 +1,42 @@ +/* + * 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 SGWC_SXA_BUILD_H +#define SGWC_SXA_BUILD_H + +#include "context.h" + +#ifdef __cplusplus +extern "C" { +#endif + +ogs_pkbuf_t *sgwc_sxa_build_session_establishment_request( + uint8_t type, sgwc_sess_t *sess); +ogs_pkbuf_t *sgwc_sxa_build_sess_modification_request( + uint8_t type, sgwc_sess_t *sess, uint64_t modify_flags); +ogs_pkbuf_t *sgwc_sxa_build_bearer_modification_request( + uint8_t type, sgwc_bearer_t *bearer, uint64_t modify_flags); +ogs_pkbuf_t *sgwc_sxa_build_session_deletion_request( + uint8_t type, sgwc_sess_t *sess); + +#ifdef __cplusplus +} +#endif + +#endif /* SGWC_SXA_BUILD_H */ diff --git a/src/sgwc/sxa-handler.c b/src/sgwc/sxa-handler.c new file mode 100644 index 000000000..e6f3a3c1f --- /dev/null +++ b/src/sgwc/sxa-handler.c @@ -0,0 +1,912 @@ +/* + * 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 "pfcp-path.h" +#include "gtp-path.h" +#include "sxa-handler.h" + +static uint8_t gtp_cause_from_pfcp(uint8_t pfcp_cause) +{ + switch (pfcp_cause) { + case OGS_PFCP_CAUSE_REQUEST_ACCEPTED: + return OGS_GTP_CAUSE_REQUEST_ACCEPTED; + case OGS_PFCP_CAUSE_REQUEST_REJECTED: + return OGS_GTP_CAUSE_REQUEST_REJECTED_REASON_NOT_SPECIFIED; + case OGS_PFCP_CAUSE_SESSION_CONTEXT_NOT_FOUND: + return OGS_GTP_CAUSE_CONTEXT_NOT_FOUND; + case OGS_PFCP_CAUSE_MANDATORY_IE_MISSING: + return OGS_GTP_CAUSE_MANDATORY_IE_MISSING; + case OGS_PFCP_CAUSE_CONDITIONAL_IE_MISSING: + return OGS_GTP_CAUSE_CONDITIONAL_IE_MISSING; + case OGS_PFCP_CAUSE_INVALID_LENGTH: + return OGS_GTP_CAUSE_INVALID_LENGTH; + case OGS_PFCP_CAUSE_MANDATORY_IE_INCORRECT: + return OGS_GTP_CAUSE_MANDATORY_IE_INCORRECT; + case OGS_PFCP_CAUSE_INVALID_FORWARDING_POLICY: + case OGS_PFCP_CAUSE_INVALID_F_TEID_ALLOCATION_OPTION: + return OGS_GTP_CAUSE_INVALID_MESSAGE_FORMAT; + case OGS_PFCP_CAUSE_NO_ESTABLISHED_PFCP_ASSOCIATION: + return OGS_GTP_CAUSE_REMOTE_PEER_NOT_RESPONDING; + case OGS_PFCP_CAUSE_RULE_CREATION_MODIFICATION_FAILURE: + return OGS_GTP_CAUSE_SEMANTIC_ERROR_IN_THE_TFT_OPERATION; + case OGS_PFCP_CAUSE_PFCP_ENTITY_IN_CONGESTION: + return OGS_GTP_CAUSE_GTP_C_ENTITY_CONGESTION; + case OGS_PFCP_CAUSE_NO_RESOURCES_AVAILABLE: + return OGS_GTP_CAUSE_NO_RESOURCES_AVAILABLE; + case OGS_PFCP_CAUSE_SERVICE_NOT_SUPPORTED: + return OGS_GTP_CAUSE_SERVICE_NOT_SUPPORTED; + case OGS_PFCP_CAUSE_SYSTEM_FAILURE: + return OGS_GTP_CAUSE_SYSTEM_FAILURE; + default: + return OGS_GTP_CAUSE_SYSTEM_FAILURE; + } + + return OGS_GTP_CAUSE_SYSTEM_FAILURE; +} + +static void timeout(ogs_gtp_xact_t *xact, void *data) +{ + sgwc_sess_t *sess = data; + sgwc_ue_t *sgwc_ue = NULL; + uint8_t type = 0; + + ogs_assert(xact); + ogs_assert(sess); + sgwc_ue = sess->sgwc_ue; + ogs_assert(sgwc_ue); + + type = xact->seq[0].type; + + ogs_error("GTP Timeout : IMSI[%s] Message-Type[%d]", + sgwc_ue->imsi_bcd, type); +} + +void sgwc_sxa_handle_session_establishment_response( + sgwc_sess_t *sess, ogs_pfcp_xact_t *pfcp_xact, + ogs_gtp_message_t *gtp_message, + ogs_pfcp_session_establishment_response_t *pfcp_rsp) +{ + int rv, len = 0; + uint8_t cause_value = 0; + ogs_pfcp_f_seid_t *up_f_seid = NULL; + + ogs_gtp_f_teid_t sgw_s5c_teid, sgw_s5u_teid; + ogs_gtp_f_teid_t *pgw_s5c_teid = NULL; + + ogs_gtp_xact_t *s11_xact = NULL, *s5c_xact = NULL; + ogs_gtp_node_t *pgw = NULL; + + sgwc_ue_t *sgwc_ue = NULL; + sgwc_bearer_t *bearer = NULL; + sgwc_tunnel_t *dl_tunnel = NULL; + + ogs_gtp_create_session_request_t *gtp_req = NULL; + ogs_pkbuf_t *pkbuf = NULL; + + ogs_assert(pfcp_xact); + ogs_assert(pfcp_rsp); + ogs_assert(gtp_message); + + gtp_req = >p_message->create_session_request; + ogs_assert(gtp_req); + + s11_xact = pfcp_xact->assoc_xact; + ogs_assert(s11_xact); + + ogs_pfcp_xact_commit(pfcp_xact); + + cause_value = OGS_GTP_CAUSE_REQUEST_ACCEPTED; + + if (!sess) { + ogs_warn("No Context"); + cause_value = OGS_GTP_CAUSE_CONTEXT_NOT_FOUND; + } + + if (pfcp_rsp->up_f_seid.presence == 0) { + ogs_error("No UP F-SEID"); + cause_value = OGS_GTP_CAUSE_MANDATORY_IE_MISSING; + } + + if (pfcp_rsp->cause.presence) { + if (pfcp_rsp->cause.u8 != OGS_PFCP_CAUSE_REQUEST_ACCEPTED) { + ogs_warn("PFCP Cause [%d] : Not Accepted", pfcp_rsp->cause.u8); + cause_value = gtp_cause_from_pfcp(pfcp_rsp->cause.u8); + } + } else { + ogs_error("No Cause"); + cause_value = OGS_GTP_CAUSE_MANDATORY_IE_MISSING; + } + + if (cause_value != OGS_GTP_CAUSE_REQUEST_ACCEPTED) { + if (sess) sgwc_ue = sess->sgwc_ue; + ogs_gtp_send_error_message( + s11_xact, sgwc_ue ? sgwc_ue->mme_s11_teid : 0, + OGS_GTP_CREATE_SESSION_RESPONSE_TYPE, cause_value); + return; + } + + ogs_assert(sess); + + bearer = sgwc_default_bearer_in_sess(sess); + ogs_assert(bearer); + dl_tunnel = sgwc_dl_tunnel_in_bearer(bearer); + ogs_assert(dl_tunnel); + + /* UP F-SEID */ + up_f_seid = pfcp_rsp->up_f_seid.data; + ogs_assert(up_f_seid); + sess->sgwu_sxa_seid = be64toh(up_f_seid->seid); + + /* Send Control Plane(DL) : SGW-S5C */ + memset(&sgw_s5c_teid, 0, sizeof(ogs_gtp_f_teid_t)); + sgw_s5c_teid.interface_type = OGS_GTP_F_TEID_S5_S8_SGW_GTP_C; + sgw_s5c_teid.teid = htobe32(sess->sgw_s5c_teid); + rv = ogs_gtp_sockaddr_to_f_teid( + sgwc_self()->gtpc_addr, sgwc_self()->gtpc_addr6, &sgw_s5c_teid, &len); + ogs_assert(rv == OGS_OK); + gtp_req->sender_f_teid_for_control_plane.presence = 1; + gtp_req->sender_f_teid_for_control_plane.data = &sgw_s5c_teid; + gtp_req->sender_f_teid_for_control_plane.len = len; + + ogs_debug(" SGW_S5C_TEID[0x%x] PGW_S5C_TEID[0x%x]", + sess->sgw_s5c_teid, sess->pgw_s5c_teid); + ogs_debug(" SGW_S5U_TEID[%d] PGW_S5U_TEID[%d]", + dl_tunnel->local_teid, dl_tunnel->remote_teid); + + pgw_s5c_teid = gtp_req->pgw_s5_s8_address_for_control_plane_or_pmip.data; + ogs_assert(pgw_s5c_teid); + + pgw = ogs_gtp_node_find_by_f_teid(&sgwc_self()->pgw_s5c_list, pgw_s5c_teid); + if (!pgw) { + pgw = ogs_gtp_node_add_by_f_teid( + &sgwc_self()->pgw_s5c_list, pgw_s5c_teid, sgwc_self()->gtpc_port, + ogs_config()->parameter.no_ipv4, + ogs_config()->parameter.no_ipv6, + ogs_config()->parameter.prefer_ipv4); + ogs_assert(pgw); + + rv = ogs_gtp_connect( + sgwc_self()->gtpc_sock, sgwc_self()->gtpc_sock6, pgw); + ogs_assert(rv == OGS_OK); + } + /* Setup GTP Node */ + OGS_SETUP_GTP_NODE(sess, pgw); + + /* Remove PGW-S5C */ + gtp_req->pgw_s5_s8_address_for_control_plane_or_pmip.presence = 0; + + /* Data Plane(DL) : SGW-S5U */ + memset(&sgw_s5u_teid, 0, sizeof(ogs_gtp_f_teid_t)); + sgw_s5u_teid.teid = htobe32(dl_tunnel->local_teid); + sgw_s5u_teid.interface_type = dl_tunnel->interface_type; + rv = ogs_gtp_sockaddr_to_f_teid( + dl_tunnel->local_addr, dl_tunnel->local_addr6, &sgw_s5u_teid, &len); + ogs_assert(rv == OGS_OK); + gtp_req->bearer_contexts_to_be_created.s5_s8_u_sgw_f_teid.presence = 1; + gtp_req->bearer_contexts_to_be_created.s5_s8_u_sgw_f_teid.data = + &sgw_s5u_teid; + gtp_req->bearer_contexts_to_be_created.s5_s8_u_sgw_f_teid.len = len; + + gtp_message->h.type = OGS_GTP_CREATE_SESSION_REQUEST_TYPE; + gtp_message->h.teid = sess->pgw_s5c_teid; + + pkbuf = ogs_gtp_build_msg(gtp_message); + ogs_expect_or_return(pkbuf); + + s5c_xact = ogs_gtp_xact_local_create( + sess->gnode, >p_message->h, pkbuf, timeout, sess); + ogs_expect_or_return(s5c_xact); + + ogs_gtp_xact_associate(s11_xact, s5c_xact); + + rv = ogs_gtp_xact_commit(s5c_xact); + ogs_expect(rv == OGS_OK); +} + +void sgwc_sxa_handle_session_modification_response( + sgwc_sess_t *sess, ogs_pfcp_xact_t *pfcp_xact, + ogs_gtp_message_t *recv_message, + ogs_pfcp_session_modification_response_t *pfcp_rsp) +{ + int i, rv, len = 0; + uint8_t cause_value = 0; + uint64_t flags; + + ogs_gtp_xact_t *s11_xact = NULL; + ogs_gtp_xact_t *s5c_xact = NULL; + + ogs_gtp_message_t send_message; + + sgwc_bearer_t *bearer = NULL; + sgwc_tunnel_t *dl_tunnel = NULL, *ul_tunnel = NULL; + sgwc_ue_t *sgwc_ue = NULL; + + ogs_pkbuf_t *pkbuf = NULL; + + ogs_gtp_cause_t cause; + + ogs_assert(pfcp_xact); + ogs_assert(pfcp_rsp); + ogs_assert(recv_message); + + flags = pfcp_xact->modify_flags; + ogs_assert(flags); + + cause_value = OGS_GTP_CAUSE_REQUEST_ACCEPTED; + + if (!sess) { + ogs_warn("No Context"); + cause_value = OGS_GTP_CAUSE_CONTEXT_NOT_FOUND; + } + + if (pfcp_rsp->cause.presence) { + if (pfcp_rsp->cause.u8 != OGS_PFCP_CAUSE_REQUEST_ACCEPTED) { + ogs_warn("PFCP Cause [%d] : Not Accepted", pfcp_rsp->cause.u8); + cause_value = gtp_cause_from_pfcp(pfcp_rsp->cause.u8); + } + } else { + ogs_error("No Cause"); + cause_value = OGS_GTP_CAUSE_MANDATORY_IE_MISSING; + } + + if (cause_value != OGS_GTP_CAUSE_REQUEST_ACCEPTED) { + if (sess) sgwc_ue = sess->sgwc_ue; + if (flags & OGS_PFCP_MODIFY_CREATE) { + s5c_xact = pfcp_xact->assoc_xact; + ogs_assert(s5c_xact); + + ogs_gtp_send_error_message( + s5c_xact, sess ? sess->pgw_s5c_teid : 0, + OGS_GTP_CREATE_BEARER_RESPONSE_TYPE, cause_value); + + } else if (flags & OGS_PFCP_MODIFY_REMOVE) { + s5c_xact = pfcp_xact->assoc_xact; + ogs_assert(s5c_xact); + + ogs_gtp_send_error_message( + s5c_xact, sess ? sess->pgw_s5c_teid : 0, + OGS_GTP_DELETE_BEARER_RESPONSE_TYPE, cause_value); + + } else if (flags & OGS_PFCP_MODIFY_ACTIVATE) { + if (flags & OGS_PFCP_MODIFY_UL_ONLY) { + s11_xact = pfcp_xact->assoc_xact; + ogs_assert(s11_xact); + + ogs_gtp_send_error_message( + s11_xact, sgwc_ue ? sgwc_ue->mme_s11_teid : 0, + OGS_GTP_CREATE_SESSION_RESPONSE_TYPE, cause_value); + + } else if (flags & OGS_PFCP_MODIFY_DL_ONLY) { + s11_xact = pfcp_xact->assoc_xact; + ogs_assert(s11_xact); + + ogs_gtp_send_error_message( + s11_xact, sgwc_ue ? sgwc_ue->mme_s11_teid : 0, + OGS_GTP_MODIFY_BEARER_RESPONSE_TYPE, cause_value); + } else { + ogs_fatal("Invalid modify_flags[0x%llx]", (long long)flags); + ogs_assert_if_reached(); + } + } else if (flags & OGS_PFCP_MODIFY_DEACTIVATE) { + s11_xact = pfcp_xact->assoc_xact; + ogs_assert(s11_xact); + + ogs_gtp_send_error_message( + s11_xact, sgwc_ue ? sgwc_ue->mme_s11_teid : 0, + OGS_GTP_RELEASE_ACCESS_BEARERS_RESPONSE_TYPE, cause_value); + } + + return; + } + + if (flags & OGS_PFCP_MODIFY_SESSION) { + sess = pfcp_xact->data; + ogs_assert(sess); + sgwc_ue = sess->sgwc_ue; + ogs_assert(sgwc_ue); + + } else { + bearer = pfcp_xact->data; + ogs_assert(bearer); + sgwc_ue = bearer->sgwc_ue; + ogs_assert(sgwc_ue); + + dl_tunnel = sgwc_dl_tunnel_in_bearer(bearer); + ogs_assert(dl_tunnel); + ul_tunnel = sgwc_ul_tunnel_in_bearer(bearer); + ogs_assert(ul_tunnel); + } + + ogs_pfcp_xact_commit(pfcp_xact); + + if (flags & OGS_PFCP_MODIFY_CREATE) { + if (flags & OGS_PFCP_MODIFY_UL_ONLY) { + ogs_gtp_create_bearer_request_t *gtp_req = NULL; + ogs_gtp_f_teid_t sgw_s1u_teid; + + s5c_xact = pfcp_xact->assoc_xact; + ogs_assert(s5c_xact); + + gtp_req = &recv_message->create_bearer_request; + ogs_assert(gtp_req); + + /* Remove S5U-F-TEID */ + gtp_req->bearer_contexts.s5_s8_u_sgw_f_teid.presence = 0; + + /* Send Data Plane(UL) : SGW-S1U */ + memset(&sgw_s1u_teid, 0, sizeof(ogs_gtp_f_teid_t)); + sgw_s1u_teid.interface_type = ul_tunnel->interface_type; + sgw_s1u_teid.teid = htobe32(ul_tunnel->local_teid); + rv = ogs_gtp_sockaddr_to_f_teid( + ul_tunnel->local_addr, ul_tunnel->local_addr6, + &sgw_s1u_teid, &len); + ogs_expect(rv == OGS_OK); + gtp_req->bearer_contexts.s1_u_enodeb_f_teid.presence = 1; + gtp_req->bearer_contexts.s1_u_enodeb_f_teid.data = &sgw_s1u_teid; + gtp_req->bearer_contexts.s1_u_enodeb_f_teid.len = len; + + recv_message->h.type = OGS_GTP_CREATE_BEARER_REQUEST_TYPE; + recv_message->h.teid = sgwc_ue->mme_s11_teid; + + pkbuf = ogs_gtp_build_msg(recv_message); + ogs_expect_or_return(pkbuf); + + s11_xact = ogs_gtp_xact_local_create( + sgwc_ue->gnode, &recv_message->h, pkbuf, timeout, sess); + ogs_expect_or_return(s11_xact); + + ogs_gtp_xact_associate(s5c_xact, s11_xact); + + rv = ogs_gtp_xact_commit(s11_xact); + ogs_expect(rv == OGS_OK); + + } else if (flags & OGS_PFCP_MODIFY_DL_ONLY) { + ogs_gtp_create_bearer_response_t *gtp_rsp = NULL; + ogs_gtp_f_teid_t sgw_s5u_teid, pgw_s5u_teid; + + s5c_xact = pfcp_xact->assoc_xact; + ogs_assert(s5c_xact); + + gtp_rsp = &recv_message->create_bearer_response; + ogs_assert(gtp_rsp); + + /* Remove SGW-S1U-TEID */ + gtp_rsp->bearer_contexts.s4_u_sgsn_f_teid.presence = 0; + + /* Remove S1U-F-TEID */ + gtp_rsp->bearer_contexts.s1_u_enodeb_f_teid.presence = 0; + + /* Data Plane(DL) : SGW-S5U */ + memset(&sgw_s5u_teid, 0, sizeof(ogs_gtp_f_teid_t)); + sgw_s5u_teid.interface_type = OGS_GTP_F_TEID_S5_S8_SGW_GTP_U; + sgw_s5u_teid.teid = htobe32(dl_tunnel->local_teid); + rv = ogs_gtp_sockaddr_to_f_teid( + dl_tunnel->local_addr, dl_tunnel->local_addr6, + &sgw_s5u_teid, &len); + ogs_expect(rv == OGS_OK); + gtp_rsp->bearer_contexts.s5_s8_u_sgw_f_teid.presence = 1; + gtp_rsp->bearer_contexts.s5_s8_u_sgw_f_teid.data = &sgw_s5u_teid; + gtp_rsp->bearer_contexts.s5_s8_u_sgw_f_teid.len = len; + + /* Data Plane(UL) : PGW-S5U */ + pgw_s5u_teid.interface_type = OGS_GTP_F_TEID_S5_S8_PGW_GTP_U; + pgw_s5u_teid.teid = htobe32(ul_tunnel->remote_teid); + rv = ogs_gtp_ip_to_f_teid( + &ul_tunnel->remote_ip, &pgw_s5u_teid, &len); + gtp_rsp->bearer_contexts.s5_s8_u_pgw_f_teid.presence = 1; + gtp_rsp->bearer_contexts.s5_s8_u_pgw_f_teid.data = &pgw_s5u_teid; + gtp_rsp->bearer_contexts.s5_s8_u_pgw_f_teid.len = len; + + recv_message->h.type = OGS_GTP_CREATE_BEARER_RESPONSE_TYPE; + recv_message->h.teid = sess->pgw_s5c_teid; + + pkbuf = ogs_gtp_build_msg(recv_message); + ogs_expect_or_return(pkbuf); + + rv = ogs_gtp_xact_update_tx(s5c_xact, &recv_message->h, pkbuf); + ogs_expect_or_return(rv == OGS_OK); + + rv = ogs_gtp_xact_commit(s5c_xact); + ogs_expect(rv == OGS_OK); + + } else if (flags & OGS_PFCP_MODIFY_INDIRECT) { + bool create_indirect_tunnel_is_done; + + s11_xact = pfcp_xact->assoc_xact; + ogs_assert(s11_xact); + + sess->state.create_indirect_tunnel = true; + + create_indirect_tunnel_is_done = true; + ogs_list_for_each(&sgwc_ue->sess_list, sess) { + if (sess->state.create_indirect_tunnel == false) + create_indirect_tunnel_is_done = false; + } + + if (create_indirect_tunnel_is_done == true) { + sgwc_tunnel_t *tunnel = NULL; + + ogs_gtp_create_indirect_data_forwarding_tunnel_request_t + *gtp_req = NULL; + ogs_gtp_create_indirect_data_forwarding_tunnel_response_t + *gtp_rsp = NULL; + + ogs_gtp_f_teid_t rsp_dl_teid[OGS_GTP_MAX_INDIRECT_TUNNEL]; + ogs_gtp_f_teid_t rsp_ul_teid[OGS_GTP_MAX_INDIRECT_TUNNEL]; + + gtp_req = &recv_message-> + create_indirect_data_forwarding_tunnel_request; + ogs_assert(gtp_req); + gtp_rsp = &send_message. + create_indirect_data_forwarding_tunnel_response; + ogs_assert(gtp_rsp); + + memset(&send_message, 0, sizeof(ogs_gtp_message_t)); + + memset(&cause, 0, sizeof(cause)); + cause.value = OGS_GTP_CAUSE_REQUEST_ACCEPTED; + + gtp_rsp->cause.presence = 1; + gtp_rsp->cause.data = &cause; + gtp_rsp->cause.len = sizeof(cause); + + for (i = 0; gtp_req->bearer_contexts[i].presence; i++) { + ogs_assert(gtp_req-> + bearer_contexts[i].eps_bearer_id.presence); + bearer = sgwc_bearer_find_by_ue_ebi(sgwc_ue, + gtp_req->bearer_contexts[i].eps_bearer_id.u8); + ogs_assert(bearer); + + ogs_list_for_each(&bearer->tunnel_list, tunnel) { + if (tunnel->interface_type == + OGS_GTP_F_TEID_SGW_GTP_U_FOR_DL_DATA_FORWARDING) { + + memset(&rsp_dl_teid[i], + 0, sizeof(ogs_gtp_f_teid_t)); + rsp_dl_teid[i].interface_type = + tunnel->interface_type; + rsp_dl_teid[i].teid = htobe32(tunnel->local_teid); + rv = ogs_gtp_sockaddr_to_f_teid( + tunnel->local_addr, tunnel->local_addr6, + &rsp_dl_teid[i], &len); + ogs_expect(rv == OGS_OK); + gtp_rsp->bearer_contexts[i]. + s4_u_sgsn_f_teid.presence = 1; + gtp_rsp->bearer_contexts[i]. + s4_u_sgsn_f_teid.data = &rsp_dl_teid[i]; + gtp_rsp->bearer_contexts[i]. + s4_u_sgsn_f_teid.len = len; + + } else if (tunnel->interface_type == + OGS_GTP_F_TEID_SGW_GTP_U_FOR_UL_DATA_FORWARDING) { + + memset(&rsp_ul_teid[i], + 0, sizeof(ogs_gtp_f_teid_t)); + rsp_ul_teid[i].teid = htobe32(tunnel->local_teid); + rsp_ul_teid[i].interface_type = + tunnel->interface_type; + rv = ogs_gtp_sockaddr_to_f_teid( + tunnel->local_addr, tunnel->local_addr6, + &rsp_ul_teid[i], &len); + ogs_expect(rv == OGS_OK); + gtp_rsp->bearer_contexts[i]. + s2b_u_epdg_f_teid_5.presence = 1; + gtp_rsp->bearer_contexts[i]. + s2b_u_epdg_f_teid_5.data = &rsp_ul_teid[i]; + gtp_rsp->bearer_contexts[i]. + s2b_u_epdg_f_teid_5.len = len; + + } + + } + + if (gtp_rsp->bearer_contexts[i]. + s4_u_sgsn_f_teid.presence || + gtp_rsp->bearer_contexts[i]. + s2b_u_epdg_f_teid_5.presence) { + + gtp_rsp->bearer_contexts[i].presence = 1; + gtp_rsp->bearer_contexts[i].eps_bearer_id.presence = 1; + gtp_rsp->bearer_contexts[i].eps_bearer_id.u8 = + bearer->ebi; + + gtp_rsp->bearer_contexts[i].cause.presence = 1; + gtp_rsp->bearer_contexts[i].cause.data = &cause; + gtp_rsp->bearer_contexts[i].cause.len = sizeof(cause); + } + } + + send_message.h.type = + OGS_GTP_CREATE_INDIRECT_DATA_FORWARDING_TUNNEL_RESPONSE_TYPE; + send_message.h.teid = sgwc_ue->mme_s11_teid; + + pkbuf = ogs_gtp_build_msg(&send_message); + ogs_expect_or_return(pkbuf); + + rv = ogs_gtp_xact_update_tx(s11_xact, &send_message.h, pkbuf); + ogs_expect_or_return(rv == OGS_OK); + + rv = ogs_gtp_xact_commit(s11_xact); + ogs_expect(rv == OGS_OK); + } + } else { + ogs_fatal("Invalid modify_flags[0x%llx]", (long long)flags); + ogs_assert_if_reached(); + } + + } else if (flags & OGS_PFCP_MODIFY_REMOVE) { + if (flags & OGS_PFCP_MODIFY_INDIRECT) { + bool delete_indirect_tunnel_is_done; + + s11_xact = pfcp_xact->assoc_xact; + ogs_assert(s11_xact); + + sess->state.delete_indirect_tunnel = true; + + delete_indirect_tunnel_is_done = true; + ogs_list_for_each(&sgwc_ue->sess_list, sess) { + if (sess->state.delete_indirect_tunnel == false) + delete_indirect_tunnel_is_done = false; + } + + if (delete_indirect_tunnel_is_done == true) { + sgwc_tunnel_t *tunnel = NULL, *next_tunnel = NULL; + ogs_gtp_delete_indirect_data_forwarding_tunnel_response_t + *gtp_rsp = NULL; + + ogs_list_for_each(&sgwc_ue->sess_list, sess) { + ogs_list_for_each(&sess->bearer_list, bearer) { + ogs_list_for_each_safe(&bearer->tunnel_list, + next_tunnel, tunnel) { + if (tunnel->interface_type == + OGS_GTP_F_TEID_SGW_GTP_U_FOR_DL_DATA_FORWARDING || + tunnel->interface_type == + OGS_GTP_F_TEID_SGW_GTP_U_FOR_UL_DATA_FORWARDING) { + sgwc_tunnel_remove(tunnel); + } + } + } + } + + gtp_rsp = &send_message. + delete_indirect_data_forwarding_tunnel_response; + ogs_assert(gtp_rsp); + + memset(&send_message, 0, sizeof(ogs_gtp_message_t)); + + memset(&cause, 0, sizeof(cause)); + cause.value = OGS_GTP_CAUSE_REQUEST_ACCEPTED; + + gtp_rsp->cause.presence = 1; + gtp_rsp->cause.data = &cause; + gtp_rsp->cause.len = sizeof(cause); + + send_message.h.type = + OGS_GTP_DELETE_INDIRECT_DATA_FORWARDING_TUNNEL_RESPONSE_TYPE; + send_message.h.teid = sgwc_ue->mme_s11_teid; + + pkbuf = ogs_gtp_build_msg(&send_message); + ogs_expect_or_return(pkbuf); + + rv = ogs_gtp_xact_update_tx(s11_xact, &send_message.h, pkbuf); + ogs_expect_or_return(rv == OGS_OK); + + rv = ogs_gtp_xact_commit(s11_xact); + ogs_expect(rv == OGS_OK); + } + + } else { + s5c_xact = pfcp_xact->assoc_xact; + ogs_assert(s5c_xact); + + recv_message->h.type = OGS_GTP_DELETE_BEARER_RESPONSE_TYPE; + recv_message->h.teid = sess->pgw_s5c_teid; + + pkbuf = ogs_gtp_build_msg(recv_message); + ogs_expect_or_return(pkbuf); + + rv = ogs_gtp_xact_update_tx(s5c_xact, &recv_message->h, pkbuf); + ogs_expect_or_return(rv == OGS_OK); + + rv = ogs_gtp_xact_commit(s5c_xact); + ogs_expect(rv == OGS_OK); + + sgwc_bearer_remove(bearer); + } + + } else if (flags & OGS_PFCP_MODIFY_ACTIVATE) { + s11_xact = pfcp_xact->assoc_xact; + ogs_assert(s11_xact); + + if (flags & OGS_PFCP_MODIFY_UL_ONLY) { + ogs_gtp_create_session_response_t *gtp_rsp = NULL; + ogs_gtp_f_teid_t sgw_s11_teid; + ogs_gtp_f_teid_t sgw_s1u_teid; + + gtp_rsp = &recv_message->create_session_response; + ogs_assert(gtp_rsp); + + /* Send Control Plane(UL) : SGW-S11 */ + memset(&sgw_s11_teid, 0, sizeof(ogs_gtp_f_teid_t)); + sgw_s11_teid.interface_type = OGS_GTP_F_TEID_S11_S4_SGW_GTP_C; + sgw_s11_teid.teid = htobe32(sgwc_ue->sgw_s11_teid); + rv = ogs_gtp_sockaddr_to_f_teid( + sgwc_self()->gtpc_addr, sgwc_self()->gtpc_addr6, + &sgw_s11_teid, &len); + ogs_expect(rv == OGS_OK); + gtp_rsp->sender_f_teid_for_control_plane.presence = 1; + gtp_rsp->sender_f_teid_for_control_plane.data = &sgw_s11_teid; + gtp_rsp->sender_f_teid_for_control_plane.len = len; + + /* Send Data Plane(UL) : SGW-S1U */ + memset(&sgw_s1u_teid, 0, sizeof(ogs_gtp_f_teid_t)); + sgw_s1u_teid.interface_type = ul_tunnel->interface_type; + sgw_s1u_teid.teid = htobe32(ul_tunnel->local_teid); + rv = ogs_gtp_sockaddr_to_f_teid( + ul_tunnel->local_addr, ul_tunnel->local_addr6, + &sgw_s1u_teid, &len); + ogs_expect(rv == OGS_OK); + gtp_rsp->bearer_contexts_created.s1_u_enodeb_f_teid.presence = 1; + gtp_rsp->bearer_contexts_created.s1_u_enodeb_f_teid.data = + &sgw_s1u_teid; + gtp_rsp->bearer_contexts_created.s1_u_enodeb_f_teid.len = len; + + recv_message->h.type = OGS_GTP_CREATE_SESSION_RESPONSE_TYPE; + recv_message->h.teid = sgwc_ue->mme_s11_teid; + + pkbuf = ogs_gtp_build_msg(recv_message); + ogs_expect_or_return(pkbuf); + + rv = ogs_gtp_xact_update_tx(s11_xact, &recv_message->h, pkbuf); + ogs_expect_or_return(rv == OGS_OK); + + rv = ogs_gtp_xact_commit(s11_xact); + ogs_expect(rv == OGS_OK); + + } else if (flags & OGS_PFCP_MODIFY_DL_ONLY) { + ogs_gtp_modify_bearer_request_t *gtp_req = NULL; + ogs_gtp_modify_bearer_response_t *gtp_rsp = NULL; + + gtp_req = &recv_message->modify_bearer_request; + ogs_assert(gtp_req); + + gtp_rsp = &send_message.modify_bearer_response; + ogs_assert(gtp_rsp); + + memset(&send_message, 0, sizeof(ogs_gtp_message_t)); + + memset(&cause, 0, sizeof(cause)); + cause.value = OGS_GTP_CAUSE_REQUEST_ACCEPTED; + + gtp_rsp->cause.presence = 1; + gtp_rsp->cause.data = &cause; + gtp_rsp->cause.len = sizeof(cause); + + /* Copy Bearer-Contexts-Modified from Modify-Bearer-Request + * + * TS 29.274 Table 7.2.7-2 + * NOTE 1: The SGW shall not change its F-TEID for a given interface + * during the Handover, Service Request, E-UTRAN Initial Attach, + * UE Requested PDN connectivity and PDP Context Activation procedures. + * The SGW F-TEID shall be same for S1-U, S4-U and S12. During Handover + * and Service Request the target eNodeB/RNC/SGSN may use a different + * IP type than the one used by the source eNodeB/RNC/SGSN. + * In order to support such a scenario, the SGW F-TEID should contain + * both an IPv4 address and an IPv6 address + * (see also subclause 8.22 "F-TEID"). + */ + gtp_rsp->bearer_contexts_modified.presence = 1; + gtp_rsp->bearer_contexts_modified.eps_bearer_id.presence = 1; + gtp_rsp->bearer_contexts_modified.eps_bearer_id.u8 = + gtp_req->bearer_contexts_to_be_modified.eps_bearer_id.u8; + gtp_rsp->bearer_contexts_modified.s1_u_enodeb_f_teid.presence = 1; + gtp_rsp->bearer_contexts_modified.s1_u_enodeb_f_teid.data = + gtp_req->bearer_contexts_to_be_modified.s1_u_enodeb_f_teid.data; + gtp_rsp->bearer_contexts_modified.s1_u_enodeb_f_teid.len = + gtp_req->bearer_contexts_to_be_modified.s1_u_enodeb_f_teid.len; + + gtp_rsp->bearer_contexts_modified.cause.presence = 1; + gtp_rsp->bearer_contexts_modified.cause.len = sizeof(cause); + gtp_rsp->bearer_contexts_modified.cause.data = &cause; + + send_message.h.type = OGS_GTP_MODIFY_BEARER_RESPONSE_TYPE; + send_message.h.teid = sgwc_ue->mme_s11_teid; + + pkbuf = ogs_gtp_build_msg(&send_message); + ogs_expect_or_return(pkbuf); + + rv = ogs_gtp_xact_update_tx(s11_xact, &send_message.h, pkbuf); + ogs_expect_or_return(rv == OGS_OK); + + rv = ogs_gtp_xact_commit(s11_xact); + ogs_expect(rv == OGS_OK); + + } else { + ogs_fatal("Invalid modify_flags[0x%llx]", (long long)flags); + ogs_assert_if_reached(); + } + } else if (flags & OGS_PFCP_MODIFY_DEACTIVATE) { + bool release_access_bearers_is_done; + + s11_xact = pfcp_xact->assoc_xact; + ogs_assert(s11_xact); + + sess->state.release_access_bearers = true; + + release_access_bearers_is_done = true; + ogs_list_for_each(&sgwc_ue->sess_list, sess) { + if (sess->state.release_access_bearers == false) + release_access_bearers_is_done = false; + } + + if (release_access_bearers_is_done == true) { + ogs_gtp_release_access_bearers_response_t *gtp_rsp = NULL; + + gtp_rsp = &send_message.release_access_bearers_response; + ogs_assert(gtp_rsp); + + memset(&send_message, 0, sizeof(ogs_gtp_message_t)); + + memset(&cause, 0, sizeof(cause)); + cause.value = OGS_GTP_CAUSE_REQUEST_ACCEPTED; + + gtp_rsp->cause.presence = 1; + gtp_rsp->cause.data = &cause; + gtp_rsp->cause.len = sizeof(cause); + + send_message.h.type = OGS_GTP_RELEASE_ACCESS_BEARERS_RESPONSE_TYPE; + send_message.h.teid = sgwc_ue->mme_s11_teid; + + pkbuf = ogs_gtp_build_msg(&send_message); + ogs_expect_or_return(pkbuf); + + rv = ogs_gtp_xact_update_tx(s11_xact, &send_message.h, pkbuf); + ogs_expect_or_return(rv == OGS_OK); + + rv = ogs_gtp_xact_commit(s11_xact); + ogs_expect(rv == OGS_OK); + } + } else { + ogs_fatal("Invalid modify_flags[0x%llx]", (long long)flags); + ogs_assert_if_reached(); + } +} + +void sgwc_sxa_handle_session_deletion_response( + sgwc_sess_t *sess, ogs_pfcp_xact_t *pfcp_xact, + ogs_gtp_message_t *gtp_message, + ogs_pfcp_session_deletion_response_t *pfcp_rsp) +{ + int rv; + uint8_t cause_value = 0; + + sgwc_ue_t *sgwc_ue = NULL; + + ogs_gtp_xact_t *s11_xact = NULL; + ogs_pkbuf_t *pkbuf = NULL; + + ogs_assert(pfcp_xact); + ogs_assert(pfcp_rsp); + + s11_xact = pfcp_xact->assoc_xact; + ogs_assert(s11_xact); + + ogs_pfcp_xact_commit(pfcp_xact); + + cause_value = OGS_GTP_CAUSE_REQUEST_ACCEPTED; + + if (!sess) { + ogs_warn("No Context"); + cause_value = OGS_GTP_CAUSE_CONTEXT_NOT_FOUND; + } + + if (pfcp_rsp->cause.presence) { + if (pfcp_rsp->cause.u8 != OGS_PFCP_CAUSE_REQUEST_ACCEPTED) { + ogs_warn("PFCP Cause[%d] : Not Accepted", pfcp_rsp->cause.u8); + cause_value = gtp_cause_from_pfcp(pfcp_rsp->cause.u8); + } + } else { + ogs_error("No Cause"); + cause_value = OGS_GTP_CAUSE_MANDATORY_IE_MISSING; + } + + if (cause_value != OGS_GTP_CAUSE_REQUEST_ACCEPTED) { + if (sess) sgwc_ue = sess->sgwc_ue; + ogs_gtp_send_error_message( + s11_xact, sgwc_ue ? sgwc_ue->mme_s11_teid : 0, + OGS_GTP_DELETE_SESSION_RESPONSE_TYPE, cause_value); + return; + } + + ogs_assert(sess); + sgwc_ue = sess->sgwc_ue; + ogs_assert(sgwc_ue); + + gtp_message->h.type = OGS_GTP_DELETE_SESSION_RESPONSE_TYPE; + gtp_message->h.teid = sgwc_ue->mme_s11_teid; + + pkbuf = ogs_gtp_build_msg(gtp_message); + ogs_expect_or_return(pkbuf); + + rv = ogs_gtp_xact_update_tx(s11_xact, >p_message->h, pkbuf); + ogs_expect_or_return(rv == OGS_OK); + + rv = ogs_gtp_xact_commit(s11_xact); + ogs_expect(rv == OGS_OK); + + sgwc_sess_remove(sess); +} + +void sgwc_sxa_handle_session_report_request( + sgwc_sess_t *sess, ogs_pfcp_xact_t *pfcp_xact, + ogs_pfcp_session_report_request_t *pfcp_req) +{ + sgwc_bearer_t *bearer = NULL; + + ogs_pfcp_report_type_t report_type; + uint8_t cause_value = 0; + uint16_t pdr_id = 0; + + ogs_assert(pfcp_xact); + ogs_assert(pfcp_req); + + cause_value = OGS_GTP_CAUSE_REQUEST_ACCEPTED; + + if (!sess) { + ogs_warn("No Context"); + cause_value = OGS_PFCP_CAUSE_SESSION_CONTEXT_NOT_FOUND; + } + + if (pfcp_req->report_type.presence == 0) { + ogs_error("No Report Type"); + cause_value = OGS_GTP_CAUSE_MANDATORY_IE_MISSING; + } + + if (cause_value != OGS_GTP_CAUSE_REQUEST_ACCEPTED) { + ogs_pfcp_send_error_message(pfcp_xact, 0, + OGS_PFCP_SESSION_REPORT_RESPONSE_TYPE, + cause_value, 0); + return; + } + + ogs_assert(sess); + + report_type.value = pfcp_req->report_type.u8; + if (report_type.downlink_data_report) { + if (pfcp_req->downlink_data_report.presence && + pfcp_req->downlink_data_report.pdr_id.presence) { + + pdr_id = pfcp_req->downlink_data_report.pdr_id.u16; + + ogs_list_for_each(&sess->bearer_list, bearer) { + if (ogs_pfcp_pdr_find(&bearer->pfcp, pdr_id)) break; + } + } + } + + if (!bearer) { + ogs_error("No Bearer"); + ogs_pfcp_send_error_message(pfcp_xact, 0, + OGS_PFCP_SESSION_REPORT_RESPONSE_TYPE, + OGS_PFCP_CAUSE_SESSION_CONTEXT_NOT_FOUND, 0); + return; + } + + sgwc_gtp_send_downlink_data_notification(bearer, pfcp_xact); +} diff --git a/src/sgwc/sxa-handler.h b/src/sgwc/sxa-handler.h new file mode 100644 index 000000000..d9dd1b2e4 --- /dev/null +++ b/src/sgwc/sxa-handler.h @@ -0,0 +1,49 @@ +/* + * 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 SGWC_SXA_HANDLER_H +#define SGWC_SXA_HANDLER_H + +#include "context.h" + +#ifdef __cplusplus +extern "C" { +#endif + +void sgwc_sxa_handle_session_establishment_response( + sgwc_sess_t *sess, ogs_pfcp_xact_t *pfcp_xact, + ogs_gtp_message_t *gtp_message, + ogs_pfcp_session_establishment_response_t *pfcp_rsp); +void sgwc_sxa_handle_session_modification_response( + sgwc_sess_t *sess, ogs_pfcp_xact_t *pfcp_xact, + ogs_gtp_message_t *gtp_message, + ogs_pfcp_session_modification_response_t *pfcp_rsp); +void sgwc_sxa_handle_session_deletion_response( + sgwc_sess_t *sess, ogs_pfcp_xact_t *pfcp_xact, + ogs_gtp_message_t *gtp_message, + ogs_pfcp_session_deletion_response_t *pfcp_rsp); +void sgwc_sxa_handle_session_report_request( + sgwc_sess_t *sess, ogs_pfcp_xact_t *pfcp_xact, + ogs_pfcp_session_report_request_t *pfcp_req); + +#ifdef __cplusplus +} +#endif + +#endif /* SGWC_SXA_HANDLER_H */ diff --git a/src/sgwc/timer.c b/src/sgwc/timer.c new file mode 100644 index 000000000..bb4f4f30b --- /dev/null +++ b/src/sgwc/timer.c @@ -0,0 +1,74 @@ +/* + * 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 "timer.h" +#include "event.h" +#include "context.h" + +const char *sgwc_timer_get_name(sgwc_timer_e id) +{ + switch (id) { + case SGWC_TIMER_PFCP_ASSOCIATION: + return "SGWC_TIMER_PFCP_ASSOCIATION"; + case SGWC_TIMER_PFCP_NO_HEARTBEAT: + return "SGWC_TIMER_PFCP_NO_HEARTBEAT"; + default: + break; + } + + return "UNKNOWN_TIMER"; +} + +static void timer_send_event(int timer_id, void *data) +{ + int rv; + sgwc_event_t *e = NULL; + ogs_assert(data); + + switch (timer_id) { + case SGWC_TIMER_PFCP_ASSOCIATION: + case SGWC_TIMER_PFCP_NO_HEARTBEAT: + e = sgwc_event_new(SGWC_EVT_SXA_TIMER); + ogs_assert(e); + e->timer_id = timer_id; + e->pfcp_node = data; + break; + default: + ogs_fatal("Unknown timer id[%d]", timer_id); + ogs_assert_if_reached(); + break; + } + + rv = ogs_queue_push(sgwc_self()->queue, e); + if (rv != OGS_OK) { + ogs_warn("ogs_queue_push() failed [%d] in %s", + (int)rv, sgwc_timer_get_name(e->timer_id)); + sgwc_event_free(e); + } +} + +void sgwc_timer_pfcp_association(void *data) +{ + timer_send_event(SGWC_TIMER_PFCP_ASSOCIATION, data); +} + +void sgwc_timer_pfcp_no_heartbeat(void *data) +{ + timer_send_event(SGWC_TIMER_PFCP_NO_HEARTBEAT, data); +} diff --git a/src/sgwc/timer.h b/src/sgwc/timer.h new file mode 100644 index 000000000..c3f818645 --- /dev/null +++ b/src/sgwc/timer.h @@ -0,0 +1,49 @@ +/* + * 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 SGWC_TIMER_H +#define SGWC_TIMER_H + +#include "ogs-core.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* forward declaration */ +typedef enum { + SGWC_TIMER_BASE = 0, + + SGWC_TIMER_PFCP_ASSOCIATION, + SGWC_TIMER_PFCP_NO_HEARTBEAT, + + MAX_NUM_OF_SGWC_TIMER, + +} sgwc_timer_e; + +const char *sgwc_timer_get_name(sgwc_timer_e id); + +void sgwc_timer_pfcp_association(void *data); +void sgwc_timer_pfcp_no_heartbeat(void *data); + +#ifdef __cplusplus +} +#endif + +#endif /* SGWC_TIMER_H */ diff --git a/src/sgwu/app.c b/src/sgwu/app.c new file mode 100644 index 000000000..fd1f95274 --- /dev/null +++ b/src/sgwu/app.c @@ -0,0 +1,40 @@ +/* + * 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 "ogs-app.h" + +int app_initialize(const char *const argv[]) +{ + int rv; + + rv = sgwu_initialize(); + if (rv != OGS_OK) { + ogs_error("Failed to intialize SGW-U"); + return rv; + } + ogs_info("SGW-U initialize...done"); + + return OGS_OK; +} + +void app_terminate(void) +{ + sgwu_terminate(); + ogs_info("SGW-U terminate...done"); +} diff --git a/src/sgwu/context.c b/src/sgwu/context.c new file mode 100644 index 000000000..0d9a7dffa --- /dev/null +++ b/src/sgwu/context.c @@ -0,0 +1,503 @@ +/* + * 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 "context.h" + +static sgwu_context_t self; + +int __sgwu_log_domain; + +static OGS_POOL(sgwu_sess_pool, sgwu_sess_t); + +static int context_initialized = 0; + +void sgwu_context_init(void) +{ + ogs_assert(context_initialized == 0); + + /* Initialize SGWU context */ + memset(&self, 0, sizeof(sgwu_context_t)); + + ogs_log_install_domain(&__ogs_gtp_domain, "gtp", ogs_core()->log.level); + ogs_log_install_domain(&__sgwu_log_domain, "sgwu", ogs_core()->log.level); + + /* Setup UP Function Features */ + ogs_pfcp_self()->up_function_features.empu = 1; + ogs_pfcp_self()->up_function_features_len = 2; + + ogs_gtp_node_init(512); + + ogs_list_init(&self.sess_list); + ogs_list_init(&self.gtpu_list); + ogs_list_init(&self.peer_list); + + ogs_pool_init(&sgwu_sess_pool, ogs_config()->pool.sess); + + self.sess_hash = ogs_hash_make(); + + context_initialized = 1; +} + +void sgwu_context_final(void) +{ + ogs_assert(context_initialized == 1); + + sgwu_sess_remove_all(); + + ogs_assert(self.sess_hash); + ogs_hash_destroy(self.sess_hash); + + ogs_pool_final(&sgwu_sess_pool); + + ogs_gtp_node_remove_all(&self.peer_list); + + ogs_gtp_node_final(); + + context_initialized = 0; +} + +sgwu_context_t *sgwu_self(void) +{ + return &self; +} + +static int sgwu_context_prepare(void) +{ + self.gtpu_port = OGS_GTPV1_U_UDP_PORT; + + return OGS_OK; +} + +static int sgwu_context_validation(void) +{ + if (ogs_list_first(&self.gtpu_list) == NULL) { + ogs_error("No sgwu.gtpu in '%s'", ogs_config()->file); + return OGS_ERROR; + } + return OGS_OK; +} + +int sgwu_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 = sgwu_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, "sgwu")) { + ogs_yaml_iter_t sgwu_iter; + ogs_yaml_iter_recurse(&root_iter, &sgwu_iter); + while (ogs_yaml_iter_next(&sgwu_iter)) { + const char *sgwu_key = ogs_yaml_iter_key(&sgwu_iter); + ogs_assert(sgwu_key); + if (!strcmp(sgwu_key, "gtpu")) { + ogs_list_t list, list6; + ogs_socknode_t *node = NULL, *node6 = NULL; + ogs_socknode_t *iter = NULL, *next_iter = NULL; + + ogs_yaml_iter_t gtpu_array, gtpu_iter; + ogs_yaml_iter_recurse(&sgwu_iter, >pu_array); + + do { + int family = AF_UNSPEC; + int i, num = 0; + const char *hostname[OGS_MAX_NUM_OF_HOSTNAME]; + uint16_t port = self.gtpu_port; + const char *dev = NULL; + ogs_sockaddr_t *addr = NULL; + const char *teid_range_indication = NULL; + const char *teid_range = NULL; + const char *network_instance = NULL; + const char *source_interface = NULL; + + if (ogs_yaml_iter_type(>pu_array) == + YAML_MAPPING_NODE) { + memcpy(>pu_iter, >pu_array, + sizeof(ogs_yaml_iter_t)); + } else if (ogs_yaml_iter_type(>pu_array) == + YAML_SEQUENCE_NODE) { + if (!ogs_yaml_iter_next(>pu_array)) + break; + ogs_yaml_iter_recurse(>pu_array, >pu_iter); + } else if (ogs_yaml_iter_type(>pu_array) == + YAML_SCALAR_NODE) { + break; + } else + ogs_assert_if_reached(); + + while (ogs_yaml_iter_next(>pu_iter)) { + const char *gtpu_key = + ogs_yaml_iter_key(>pu_iter); + ogs_assert(gtpu_key); + + if (ogs_list_count( + &ogs_pfcp_self()->gtpu_resource_list) >= + OGS_MAX_NUM_OF_GTPU_RESOURCE) { + ogs_warn("[Overflow]: Number of User Plane " + "IP Resource <= %d", + OGS_MAX_NUM_OF_GTPU_RESOURCE); + break; + } + + if (!strcmp(gtpu_key, "family")) { + const char *v = ogs_yaml_iter_value(>pu_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(gtpu_key, "addr") || + !strcmp(gtpu_key, "name")) { + ogs_yaml_iter_t hostname_iter; + ogs_yaml_iter_recurse( + >pu_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(gtpu_key, "port")) { + const char *v = ogs_yaml_iter_value(>pu_iter); + if (v) port = atoi(v); + } else if (!strcmp(gtpu_key, "dev")) { + dev = ogs_yaml_iter_value(>pu_iter); + } else if (!strcmp(gtpu_key, + "teid_range_indication")) { + teid_range_indication = + ogs_yaml_iter_value(>pu_iter); + } else if (!strcmp(gtpu_key, + "teid_range")) { + teid_range = ogs_yaml_iter_value(>pu_iter); + } else if (!strcmp(gtpu_key, + "network_instance")) { + network_instance = + ogs_yaml_iter_value(>pu_iter); + } else if (!strcmp(gtpu_key, + "source_interface")) { + source_interface = + ogs_yaml_iter_value(>pu_iter); + } else + ogs_warn("unknown key `%s`", gtpu_key); + } + + addr = NULL; + for (i = 0; i < num; i++) { + rv = ogs_addaddrinfo(&addr, + family, hostname[i], port, 0); + ogs_assert(rv == OGS_OK); + } + + ogs_list_init(&list); + ogs_list_init(&list6); + + if (addr) { + if (ogs_config()->parameter.no_ipv4 == 0) + ogs_socknode_add(&list, AF_INET, addr); + if (ogs_config()->parameter.no_ipv6 == 0) + ogs_socknode_add(&list6, AF_INET6, addr); + ogs_freeaddrinfo(addr); + } + + if (dev) { + rv = ogs_socknode_probe( + ogs_config()->parameter.no_ipv4 ? NULL : &list, + ogs_config()->parameter.no_ipv6 ? NULL : &list6, + dev, port); + ogs_assert(rv == OGS_OK); + } + + /* Find first IPv4/IPv6 address in the list. + * + * In the following configuration, + * 127.0.0.4, 127.0.0.5 and cafe::1 are ignored + * on PFCP Assocation Response message's + * user plane IP resource information. + * + * gtpu: + * - addr: + * - 127.0.0.3 + * - ::1 + * - 127.0.0.4 + * - 127.0.0.5 + * - cafe::1 + * + * To include all user plane IP resource information, + * configure as below: + * + * gtpu: + * - addr: + * - 127.0.0.3 + * - ::1 + * - addr: 127.0.0.4 + * - addr + * - 127.0.0.5 + * - cafe::1 + */ + node = ogs_list_first(&list); + node6 = ogs_list_first(&list6); + if (node || node6) { + ogs_pfcp_user_plane_ip_resource_info_t info; + + memset(&info, 0, sizeof(info)); + ogs_pfcp_sockaddr_to_user_plane_ip_resource_info( + node ? node->addr : NULL, + node6 ? node6->addr : NULL, + &info); + + if (teid_range_indication) { + info.teidri = atoi(teid_range_indication); + if (teid_range) { + info.teid_range = atoi(teid_range); + } + } + if (network_instance) { + info.assoni = 1; + ogs_cpystrn(info.network_instance, + network_instance, OGS_MAX_APN_LEN+1); + } + if (source_interface) { + info.assosi = 1; + info.source_interface = atoi(source_interface); + } + + ogs_pfcp_gtpu_resource_add( + &ogs_pfcp_self()->gtpu_resource_list, &info); + } + + ogs_list_for_each_safe(&list, next_iter, iter) + ogs_list_add(&self.gtpu_list, iter); + ogs_list_for_each_safe(&list6, next_iter, iter) + ogs_list_add(&self.gtpu_list, iter); + + } while (ogs_yaml_iter_type(>pu_array) == + YAML_SEQUENCE_NODE); + + if (ogs_list_first(&self.gtpu_list) == NULL) { + ogs_list_init(&list); + ogs_list_init(&list6); + + rv = ogs_socknode_probe( + ogs_config()->parameter.no_ipv4 ? NULL : &list, + ogs_config()->parameter.no_ipv6 ? NULL : &list6, + NULL, self.gtpu_port); + ogs_assert(rv == OGS_OK); + + /* + * The first tuple IPv4/IPv6 are added + * in User Plane IP resource information. + * + * TEID Range, Network Instance, Source Interface + * cannot be configured in automatic IP detection. + */ + node = ogs_list_first(&list); + node6 = ogs_list_first(&list6); + if (node || node6) { + ogs_pfcp_user_plane_ip_resource_info_t info; + + memset(&info, 0, sizeof(info)); + ogs_pfcp_sockaddr_to_user_plane_ip_resource_info( + node ? node->addr : NULL, + node6 ? node6->addr : NULL, + &info); + + ogs_pfcp_gtpu_resource_add( + &ogs_pfcp_self()->gtpu_resource_list, &info); + } + + ogs_list_for_each_safe(&list, next_iter, iter) + ogs_list_add(&self.gtpu_list, iter); + ogs_list_for_each_safe(&list6, next_iter, iter) + ogs_list_add(&self.gtpu_list, iter); + } + } else if (!strcmp(sgwu_key, "pdn")) { + /* handle config in pfcp library */ + } + } + } + } + + rv = sgwu_context_validation(); + if (rv != OGS_OK) return rv; + + return OGS_OK; +} + +sgwu_sess_t *sgwu_sess_add(ogs_pfcp_f_seid_t *cp_f_seid, + const char *apn, uint8_t pdn_type, ogs_pfcp_pdr_id_t default_pdr_id) +{ + sgwu_sess_t *sess = NULL; + + ogs_assert(cp_f_seid); + ogs_assert(apn); + + ogs_pool_alloc(&sgwu_sess_pool, &sess); + ogs_assert(sess); + memset(sess, 0, sizeof *sess); + + sess->index = ogs_pool_index(&sgwu_sess_pool, sess); + ogs_assert(sess->index > 0 && sess->index <= ogs_config()->pool.sess); + + sess->sgwu_sxa_seid = sess->index; + sess->sgwc_sxa_seid = cp_f_seid->seid; + ogs_hash_set(self.sess_hash, &sess->sgwc_sxa_seid, + sizeof(sess->sgwc_sxa_seid), sess); + + /* Set APN */ + ogs_cpystrn(sess->pdn.apn, apn, OGS_MAX_APN_LEN+1); + + ogs_info("UE F-SEID[CP:0x%lx,UP:0x%lx] APN[%s] PDN-Type[%d]", + (long)sess->sgwu_sxa_seid, (long)sess->sgwc_sxa_seid, apn, pdn_type); + + ogs_list_add(&self.sess_list, sess); + + ogs_info("Added a session. Number of active sessions is now %d", + ogs_list_count(&self.sess_list)); + + return sess; +} + +int sgwu_sess_remove(sgwu_sess_t *sess) +{ + ogs_assert(sess); + + ogs_list_remove(&self.sess_list, sess); + ogs_pfcp_sess_clear(&sess->pfcp); + + ogs_hash_set(self.sess_hash, &sess->sgwc_sxa_seid, + sizeof(sess->sgwc_sxa_seid), NULL); + + ogs_pool_free(&sgwu_sess_pool, sess); + + ogs_info("Removed a session. Number of active sessions is now %d", + ogs_list_count(&self.sess_list)); + + return OGS_OK; +} + +void sgwu_sess_remove_all(void) +{ + sgwu_sess_t *sess = NULL, *next = NULL;; + + ogs_list_for_each_safe(&self.sess_list, next, sess) { + sgwu_sess_remove(sess); + } +} + +sgwu_sess_t *sgwu_sess_find(uint32_t index) +{ + ogs_assert(index); + return ogs_pool_find(&sgwu_sess_pool, index); +} + +sgwu_sess_t *sgwu_sess_find_by_cp_seid(uint64_t seid) +{ + return (sgwu_sess_t *)ogs_hash_get(self.sess_hash, &seid, sizeof(seid)); +} + +sgwu_sess_t *sgwu_sess_find_by_up_seid(uint64_t seid) +{ + return sgwu_sess_find(seid); +} + +sgwu_sess_t *sgwu_sess_add_by_message(ogs_pfcp_message_t *message) +{ + sgwu_sess_t *sess = NULL; + + ogs_pfcp_f_seid_t *f_seid = NULL; + char apn[OGS_MAX_APN_LEN]; + ogs_pfcp_pdr_id_t default_pdr_id = 0; + + ogs_pfcp_session_establishment_request_t *req = + &message->pfcp_session_establishment_request;; + int i; + + f_seid = req->cp_f_seid.data; + if (req->cp_f_seid.presence == 0 || f_seid == NULL) { + ogs_error("No CP F-SEID"); + return NULL; + } + f_seid->seid = be64toh(f_seid->seid); + + if (req->pdn_type.presence == 0) { + ogs_error("No PDN Type"); + return NULL; + } + + /* Find APN + * - PDR ID is existed + * - APN(Network Instance) is existed + */ + memset(apn, 0, sizeof(apn)); + for (i = 0; i < OGS_MAX_NUM_OF_PDR; i++) { + ogs_pfcp_tlv_create_pdr_t *message = &req->create_pdr[i]; + ogs_assert(message); + if (message->presence == 0) + continue; + if (message->pdr_id.presence == 0) + continue; + if (message->pdi.presence == 0) + continue; + if (message->pdi.network_instance.presence == 0) + continue; + + ogs_fqdn_parse(apn, + message->pdi.network_instance.data, + message->pdi.network_instance.len); + break; + } + + if (strlen(apn) == 0) { + ogs_error("No APN in PDR"); + return NULL; + } + + sess = sgwu_sess_find_by_cp_seid(f_seid->seid); + if (!sess) { + sess = sgwu_sess_add( + f_seid, apn, req->pdn_type.u8, default_pdr_id); + if (!sess) return NULL; + } + ogs_assert(sess); + + return sess; +} diff --git a/src/sgwu/context.h b/src/sgwu/context.h new file mode 100644 index 000000000..5b78f825f --- /dev/null +++ b/src/sgwu/context.h @@ -0,0 +1,91 @@ +/* + * 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 SGWU_CONTEXT_H +#define SGWU_CONTEXT_H + +#include "ogs-gtp.h" +#include "ogs-pfcp.h" +#include "ogs-app.h" + +#include "timer.h" +#include "sgwu-sm.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern int __sgwu_log_domain; + +#undef OGS_LOG_DOMAIN +#define OGS_LOG_DOMAIN __sgwu_log_domain + +typedef struct sgwu_context_s { + uint32_t gtpu_port; /* Default: SGWU GTP-U local port */ + + ogs_list_t gtpu_list; /* SGWU GTPU Server List */ + ogs_sock_t *gtpu_sock; /* SGWU GTPU IPv4 Socket */ + ogs_sock_t *gtpu_sock6; /* SGWU GTPU IPv6 Socket */ + + ogs_queue_t *queue; /* Queue for processing SGWU control */ + ogs_timer_mgr_t *timer_mgr; /* Timer Manager */ + ogs_pollset_t *pollset; /* Poll Set for I/O Multiplexing */ + + ogs_list_t peer_list; /* gNB/SMF Node List */ + ogs_hash_t *sess_hash; /* hash table (F-SEID) */ + ogs_list_t sess_list; +} sgwu_context_t; + +#define SGWU_SESS(pfcp_sess) ogs_container_of(pfcp_sess, sgwu_sess_t, pfcp) +typedef struct sgwu_sess_s { + ogs_lnode_t lnode; + uint32_t index; /**< An index of this node */ + + ogs_pfcp_sess_t pfcp; + + uint64_t sgwu_sxa_seid; /* SGW-U SEID is dervied from INDEX */ + uint64_t sgwc_sxa_seid; /* SGW-C SEID is received from Peer */ + + /* APN Configuration */ + ogs_pdn_t pdn; + + ogs_pfcp_node_t *pfcp_node; +} sgwu_sess_t; + +void sgwu_context_init(void); +void sgwu_context_final(void); +sgwu_context_t *sgwu_self(void); + +int sgwu_context_parse_config(void); + +sgwu_sess_t *sgwu_sess_add_by_message(ogs_pfcp_message_t *message); + +sgwu_sess_t *sgwu_sess_add(ogs_pfcp_f_seid_t *f_seid, + const char *apn, uint8_t pdn_type, ogs_pfcp_pdr_id_t default_pdr_id); +int sgwu_sess_remove(sgwu_sess_t *sess); +void sgwu_sess_remove_all(void); +sgwu_sess_t *sgwu_sess_find(uint32_t index); +sgwu_sess_t *sgwu_sess_find_by_cp_seid(uint64_t seid); +sgwu_sess_t *sgwu_sess_find_by_up_seid(uint64_t seid); + +#ifdef __cplusplus +} +#endif + +#endif /* SGWU_CONTEXT_H */ diff --git a/src/sgwu/event.c b/src/sgwu/event.c new file mode 100644 index 000000000..f27e21552 --- /dev/null +++ b/src/sgwu/event.c @@ -0,0 +1,98 @@ +/* + * 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 "event.h" +#include "context.h" + +static OGS_POOL(pool, sgwu_event_t); + +#define EVENT_POOL 32 /* FIXME : 32 */ +void sgwu_event_init(void) +{ + ogs_pool_init(&pool, EVENT_POOL); + + sgwu_self()->queue = ogs_queue_create(EVENT_POOL); + ogs_assert(sgwu_self()->queue); + sgwu_self()->timer_mgr = ogs_timer_mgr_create(); + ogs_assert(sgwu_self()->timer_mgr); + sgwu_self()->pollset = ogs_pollset_create(); + ogs_assert(sgwu_self()->pollset); +} + +void sgwu_event_term(void) +{ + ogs_queue_term(sgwu_self()->queue); + ogs_pollset_notify(sgwu_self()->pollset); +} + +void sgwu_event_final(void) +{ + if (sgwu_self()->pollset) + ogs_pollset_destroy(sgwu_self()->pollset); + if (sgwu_self()->timer_mgr) + ogs_timer_mgr_destroy(sgwu_self()->timer_mgr); + if (sgwu_self()->queue) + ogs_queue_destroy(sgwu_self()->queue); + + ogs_pool_final(&pool); +} + +sgwu_event_t *sgwu_event_new(sgwu_event_e id) +{ + sgwu_event_t *e = NULL; + + ogs_pool_alloc(&pool, &e); + ogs_assert(e); + memset(e, 0, sizeof(*e)); + + e->id = id; + + return e; +} + +void sgwu_event_free(sgwu_event_t *e) +{ + ogs_assert(e); + ogs_pool_free(&pool, e); +} + +const char *sgwu_event_get_name(sgwu_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 SGWU_EVT_SXA_MESSAGE: + return "SGWU_EVT_SXA_MESSAGE"; + case SGWU_EVT_SXA_TIMER: + return "SGWU_EVT_SXA_TIMER"; + case SGWU_EVT_SXA_NO_HEARTBEAT: + return "SGWU_EVT_SXA_NO_HEARTBEAT"; + + default: + break; + } + + return "UNKNOWN_EVENT"; +} diff --git a/src/sgwu/event.h b/src/sgwu/event.h new file mode 100644 index 000000000..b83dce2a5 --- /dev/null +++ b/src/sgwu/event.h @@ -0,0 +1,75 @@ +/* + * 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 SGWU_EVENT_H +#define SGWU_EVENT_H + +#include "ogs-core.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct ogs_gtp_node_s ogs_gtp_node_t; +typedef struct ogs_pfcp_node_s ogs_pfcp_node_t; +typedef struct ogs_pfcp_xact_s ogs_pfcp_xact_t; +typedef struct ogs_pfcp_message_s ogs_pfcp_message_t; +typedef struct sgwu_bearer_s sgwu_bearer_t; + +typedef enum { + SGWU_EVT_BASE = OGS_FSM_USER_SIG, + + SGWU_EVT_LO_DLDATA_NOTI, + + SGWU_EVT_SXA_MESSAGE, + SGWU_EVT_SXA_TIMER, + SGWU_EVT_SXA_NO_HEARTBEAT, + + SGWU_EVT_TOP, + +} sgwu_event_e; + +typedef struct sgwu_event_s { + int id; + ogs_pkbuf_t *pkbuf; + int timer_id; + + ogs_gtp_node_t *gnode; + + ogs_pfcp_node_t *pfcp_node; + ogs_pfcp_xact_t *pfcp_xact; + ogs_pfcp_message_t *pfcp_message; + + sgwu_bearer_t *bearer; +} sgwu_event_t; + +void sgwu_event_init(void); +void sgwu_event_term(void); +void sgwu_event_final(void); + +sgwu_event_t *sgwu_event_new(sgwu_event_e id); +void sgwu_event_free(sgwu_event_t *e); + +const char *sgwu_event_get_name(sgwu_event_t *e); + +#ifdef __cplusplus +} +#endif + +#endif /* SGWU_EVENT_H */ diff --git a/src/sgwu/gtp-path.c b/src/sgwu/gtp-path.c new file mode 100644 index 000000000..dbef1d628 --- /dev/null +++ b/src/sgwu/gtp-path.c @@ -0,0 +1,208 @@ +/* + * 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 "gtp-path.h" +#include "pfcp-path.h" + +#define SGWU_GTP_HANDLED 1 + +static ogs_pkbuf_pool_t *packet_pool = NULL; + +static void _gtpv1_u_recv_cb(short when, ogs_socket_t fd, void *data) +{ + int len; + ssize_t size; + char buf[OGS_ADDRSTRLEN]; + + ogs_pkbuf_t *pkbuf = NULL; + ogs_sockaddr_t from; + + ogs_gtp_header_t *gtp_h = NULL; + struct ip *ip_h = NULL; + + uint32_t teid; + uint8_t qfi; + ogs_pfcp_pdr_t *pdr = NULL; + ogs_pfcp_user_plane_report_t report; + + ogs_assert(fd != INVALID_SOCKET); + + pkbuf = ogs_pkbuf_alloc(packet_pool, OGS_MAX_SDU_LEN); + ogs_pkbuf_put(pkbuf, OGS_MAX_SDU_LEN); + + size = ogs_recvfrom(fd, pkbuf->data, pkbuf->len, 0, &from); + if (size <= 0) { + ogs_log_message(OGS_LOG_ERROR, ogs_socket_errno, + "ogs_recv() failed"); + goto cleanup; + } + + ogs_pkbuf_trim(pkbuf, size); + + ogs_assert(pkbuf); + ogs_assert(pkbuf->len); + + gtp_h = (ogs_gtp_header_t *)pkbuf->data; + if (gtp_h->version != OGS_GTP_VERSION_1) { + ogs_error("[DROP] Invalid GTPU version [%d]", gtp_h->version); + ogs_log_hexdump(OGS_LOG_ERROR, pkbuf->data, pkbuf->len); + goto cleanup; + } + + if (gtp_h->type == OGS_GTPU_MSGTYPE_ECHO_REQ) { + ogs_pkbuf_t *echo_rsp; + + ogs_debug("[RECV] Echo Request from [%s]", OGS_ADDR(&from, buf)); + echo_rsp = ogs_gtp_handle_echo_req(pkbuf); + if (echo_rsp) { + ssize_t sent; + + /* Echo reply */ + ogs_debug("[SEND] Echo Response to [%s]", OGS_ADDR(&from, buf)); + + sent = ogs_sendto(fd, echo_rsp->data, echo_rsp->len, 0, &from); + if (sent < 0 || sent != echo_rsp->len) { + ogs_log_message(OGS_LOG_ERROR, ogs_socket_errno, + "ogs_sendto() failed"); + } + ogs_pkbuf_free(echo_rsp); + } + goto cleanup; + } + + teid = be32toh(gtp_h->teid); + + if (gtp_h->type == OGS_GTPU_MSGTYPE_END_MARKER) { + ogs_debug("[RECV] End Marker from [%s] : TEID[0x%x]", + OGS_ADDR(&from, buf), teid); + goto cleanup; + } + + if (gtp_h->type == OGS_GTPU_MSGTYPE_ERR_IND) { + ogs_error("[RECV] Error Indication from [%s]", OGS_ADDR(&from, buf)); + goto cleanup; + } + + if (gtp_h->type != OGS_GTPU_MSGTYPE_GPDU) { + ogs_error("[DROP] Invalid GTPU Type [%d]", gtp_h->type); + ogs_log_hexdump(OGS_LOG_ERROR, pkbuf->data, pkbuf->len); + goto cleanup; + } + + ogs_debug("[RECV] GPU-U from [%s] : TEID[0x%x]", + OGS_ADDR(&from, buf), teid); + + qfi = 0; + if (gtp_h->flags & OGS_GTPU_FLAGS_E) { + /* + * TS29.281 + * 5.2.1 General format of the GTP-U Extension Header + * Figure 5.2.1-3: Definition of Extension Header Type + * + * Note 4 : For a GTP-PDU with several Extension Headers, the PDU + * Session Container should be the first Extension Header + */ + ogs_gtp_extension_header_t *extension_header = + (ogs_gtp_extension_header_t *)(pkbuf->data + OGS_GTPV1U_HEADER_LEN); + ogs_assert(extension_header); + if (extension_header->type == + OGS_GTP_EXTENSION_HEADER_TYPE_PDU_SESSION_CONTAINER) { + if (extension_header->pdu_type == + OGS_GTP_EXTENSION_HEADER_PDU_TYPE_UL_PDU_SESSION_INFORMATION) { + ogs_debug(" QFI [0x%x]", + extension_header->qos_flow_identifier); + qfi = extension_header->qos_flow_identifier; + } + } + } + + /* Remove GTP header and send packets to peer NF */ + len = ogs_gtpu_header_len(pkbuf); + if (len < 0) { + ogs_error("[DROP] Cannot decode GTPU packet"); + ogs_log_hexdump(OGS_LOG_ERROR, pkbuf->data, pkbuf->len); + goto cleanup; + } + ogs_assert(ogs_pkbuf_pull(pkbuf, len)); + + ip_h = (struct ip *)pkbuf->data; + ogs_assert(ip_h); + + pdr = ogs_pfcp_pdr_find_by_teid_and_qfi(teid, qfi); + if (!pdr) { + ogs_warn("[DROP] Cannot find PDR : TEID[0x%x] QFI[%d]", + teid, qfi); + goto cleanup; + } + + ogs_pfcp_up_handle_pdr(pdr, pkbuf, &report); + + if (report.type.downlink_data_report) { + sgwu_sess_t *sess = NULL; + + ogs_assert(pdr->sess); + sess = SGWU_SESS(pdr->sess); + ogs_assert(sess); + + report.downlink_data.pdr_id = pdr->id; + report.downlink_data.qfi = qfi; /* for 5GC */ + + sgwu_pfcp_send_session_report_request(sess, &report); + } + +cleanup: + ogs_pkbuf_free(pkbuf); +} + +int sgwu_gtp_open(void) +{ + ogs_socknode_t *node = NULL; + ogs_sock_t *sock = NULL; + + ogs_pkbuf_config_t config; + memset(&config, 0, sizeof config); + + config.cluster_8192_pool = ogs_config()->pool.packet; + + packet_pool = ogs_pkbuf_pool_create(&config); + + ogs_list_for_each(&sgwu_self()->gtpu_list, node) { + sock = ogs_gtp_server(node); + ogs_assert(sock); + + if (sock->family == AF_INET) + sgwu_self()->gtpu_sock = sock; + else if (sock->family == AF_INET6) + sgwu_self()->gtpu_sock6 = sock; + + node->poll = ogs_pollset_add(sgwu_self()->pollset, + OGS_POLLIN, sock->fd, _gtpv1_u_recv_cb, sock); + } + + ogs_assert(sgwu_self()->gtpu_sock || sgwu_self()->gtpu_sock6); + + return OGS_OK; +} + +void sgwu_gtp_close(void) +{ + ogs_socknode_remove_all(&sgwu_self()->gtpu_list); + + ogs_pkbuf_pool_destroy(packet_pool); +} diff --git a/src/sgwu/gtp-path.h b/src/sgwu/gtp-path.h new file mode 100644 index 000000000..78dde1c3c --- /dev/null +++ b/src/sgwu/gtp-path.h @@ -0,0 +1,40 @@ +/* + * 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 SGWU_GTP_PATH_H +#define SGWU_GTP_PATH_H + +#include "context.h" + +#ifdef __cplusplus +extern "C" { +#endif + +int sgwu_gtp_open(void); +void sgwu_gtp_close(void); + +#if 0 +void sgwu_gtp_send_end_marker(sgwu_tunnel_t *s1u_tunnel); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* SGWU_GTP_PATH_H */ diff --git a/src/sgwu/init.c b/src/sgwu/init.c new file mode 100644 index 000000000..66dba081e --- /dev/null +++ b/src/sgwu/init.c @@ -0,0 +1,118 @@ +/* + * 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 "context.h" + +static ogs_thread_t *thread; +static void sgwu_main(void *data); + +static int initialized = 0; + +int sgwu_initialize() +{ + int rv; + + ogs_pfcp_context_init(OGS_MAX_NUM_OF_GTPU_RESOURCE); + sgwu_context_init(); + sgwu_event_init(); + + rv = ogs_pfcp_xact_init(sgwu_self()->timer_mgr, 512); + if (rv != OGS_OK) return rv; + + rv = ogs_pfcp_context_parse_config("sgwu", "sgwc"); + if (rv != OGS_OK) return rv; + + rv = sgwu_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(sgwu_main, NULL); + if (!thread) return OGS_ERROR; + + initialized = 1; + + return OGS_OK; +} + +void sgwu_terminate(void) +{ + if (!initialized) return; + + sgwu_event_term(); + + ogs_thread_destroy(thread); + + sgwu_context_final(); + ogs_pfcp_context_final(); + + ogs_pfcp_xact_final(); + + sgwu_event_final(); +} + +static void sgwu_main(void *data) +{ + ogs_fsm_t sgwu_sm; + int rv; + + ogs_fsm_create(&sgwu_sm, sgwu_state_initial, sgwu_state_final); + ogs_fsm_init(&sgwu_sm, 0); + + for ( ;; ) { + ogs_pollset_poll(sgwu_self()->pollset, + ogs_timer_mgr_next(sgwu_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(sgwu_self()->timer_mgr); + + for ( ;; ) { + sgwu_event_t *e = NULL; + + rv = ogs_queue_trypop(sgwu_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(&sgwu_sm, e); + sgwu_event_free(e); + } + } +done: + + ogs_fsm_fini(&sgwu_sm, 0); + ogs_fsm_delete(&sgwu_sm); +} diff --git a/src/sgwu/meson.build b/src/sgwu/meson.build new file mode 100644 index 000000000..270c101e3 --- /dev/null +++ b/src/sgwu/meson.build @@ -0,0 +1,64 @@ +# 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 . + +libsgwu_sources = files(''' + timer.h + event.h + context.h + gtp-path.h + sxa-build.h + sxa-handler.h + pfcp-path.h + sgwu-sm.h + + init.c + timer.c + event.c + context.c + gtp-path.c + sxa-build.c + sxa-handler.c + pfcp-path.c + pfcp-sm.c + sgwu-sm.c +'''.split()) + +libsgwu = static_library('sgwu', + sources : libsgwu_sources, + dependencies : [libapp_dep, + libgtp_dep, + libpfcp_dep], + install : false) + +libsgwu_dep = declare_dependency( + link_with : libsgwu, + dependencies : [libapp_dep, + libgtp_dep, + libpfcp_dep]) + +sgwu_sources = files(''' + app.c + ../main.c +'''.split()) + +executable('open5gs-sgwud', + sources : sgwu_sources, + c_args : '-DDEFAULT_CONFIG_FILENAME="@0@/sgwu.yaml"'.format(open5gs_sysconfdir), + include_directories : srcinc, + dependencies : libsgwu_dep, + install_rpath : libdir, + install : true) diff --git a/src/sgwu/pfcp-path.c b/src/sgwu/pfcp-path.c new file mode 100644 index 000000000..a4b816979 --- /dev/null +++ b/src/sgwu/pfcp-path.c @@ -0,0 +1,288 @@ +/* + * 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 "pfcp-path.h" + +static void pfcp_node_fsm_init(ogs_pfcp_node_t *node, bool try_to_assoicate) +{ + sgwu_event_t e; + + ogs_assert(node); + + memset(&e, 0, sizeof(e)); + e.pfcp_node = node; + + if (try_to_assoicate == true) { + node->t_association = ogs_timer_add(sgwu_self()->timer_mgr, + sgwu_timer_association, node); + ogs_assert(node->t_association); + } + + ogs_fsm_create(&node->sm, sgwu_pfcp_state_initial, sgwu_pfcp_state_final); + ogs_fsm_init(&node->sm, &e); +} + +static void pfcp_node_fsm_fini(ogs_pfcp_node_t *node) +{ + sgwu_event_t e; + + ogs_assert(node); + + memset(&e, 0, sizeof(e)); + e.pfcp_node = node; + + ogs_fsm_fini(&node->sm, &e); + ogs_fsm_delete(&node->sm); + + if (node->t_association) + ogs_timer_delete(node->t_association); +} + +static void pfcp_recv_cb(short when, ogs_socket_t fd, void *data) +{ + int rv; + + ssize_t size; + sgwu_event_t *e = NULL; + ogs_pkbuf_t *pkbuf = NULL; + ogs_sockaddr_t from; + ogs_pfcp_node_t *node = NULL; + ogs_pfcp_header_t *h = NULL; + + ogs_assert(fd != INVALID_SOCKET); + + pkbuf = ogs_pkbuf_alloc(NULL, OGS_MAX_SDU_LEN); + ogs_pkbuf_put(pkbuf, OGS_MAX_SDU_LEN); + + size = ogs_recvfrom(fd, pkbuf->data, pkbuf->len, 0, &from); + if (size <= 0) { + ogs_log_message(OGS_LOG_ERROR, ogs_socket_errno, + "ogs_recvfrom() failed"); + ogs_pkbuf_free(pkbuf); + return; + } + + ogs_pkbuf_trim(pkbuf, size); + + h = (ogs_pfcp_header_t *)pkbuf->data; + if (h->version > OGS_PFCP_VERSION) { + ogs_pfcp_header_t rsp; + + ogs_error("Not supported version[%d]", h->version); + + memset(&rsp, 0, sizeof rsp); + rsp.flags = (OGS_PFCP_VERSION << 5); + rsp.type = OGS_PFCP_VERSION_NOT_SUPPORTED_RESPONSE_TYPE; + rsp.length = htobe16(4); + rsp.sqn_only = h->sqn_only; + ogs_sendto(fd, &rsp, 8, 0, &from); + ogs_pkbuf_free(pkbuf); + + return; + } + + e = sgwu_event_new(SGWU_EVT_SXA_MESSAGE); + ogs_assert(e); + + node = ogs_pfcp_node_find(&ogs_pfcp_self()->peer_list, &from); + if (!node) { + node = ogs_pfcp_node_add(&ogs_pfcp_self()->peer_list, &from); + ogs_assert(node); + + node->sock = data; + pfcp_node_fsm_init(node, false); + } + e->pfcp_node = node; + e->pkbuf = pkbuf; + + rv = ogs_queue_push(sgwu_self()->queue, e); + if (rv != OGS_OK) { + ogs_warn("ogs_queue_push() failed:%d", (int)rv); + ogs_pkbuf_free(e->pkbuf); + sgwu_event_free(e); + } +} + +int sgwu_pfcp_open(void) +{ + ogs_socknode_t *node = NULL; + ogs_sock_t *sock = NULL; + ogs_pfcp_node_t *pfcp_node = NULL; + + /* PFCP Server */ + ogs_list_for_each(&ogs_pfcp_self()->pfcp_list, node) { + sock = ogs_pfcp_server(node); + ogs_assert(sock); + + node->poll = ogs_pollset_add(sgwu_self()->pollset, + OGS_POLLIN, sock->fd, pfcp_recv_cb, sock); + } + ogs_list_for_each(&ogs_pfcp_self()->pfcp_list6, node) { + sock = ogs_pfcp_server(node); + ogs_assert(sock); + + node->poll = ogs_pollset_add(sgwu_self()->pollset, + OGS_POLLIN, sock->fd, pfcp_recv_cb, sock); + } + + ogs_pfcp_self()->pfcp_sock = + ogs_socknode_sock_first(&ogs_pfcp_self()->pfcp_list); + if (ogs_pfcp_self()->pfcp_sock) + ogs_pfcp_self()->pfcp_addr = &ogs_pfcp_self()->pfcp_sock->local_addr; + + ogs_pfcp_self()->pfcp_sock6 = + ogs_socknode_sock_first(&ogs_pfcp_self()->pfcp_list6); + if (ogs_pfcp_self()->pfcp_sock6) + ogs_pfcp_self()->pfcp_addr6 = &ogs_pfcp_self()->pfcp_sock6->local_addr; + + ogs_assert(ogs_pfcp_self()->pfcp_addr || ogs_pfcp_self()->pfcp_addr6); + + ogs_list_for_each(&ogs_pfcp_self()->peer_list, pfcp_node) + pfcp_node_fsm_init(pfcp_node, true); + + return OGS_OK; +} + +void sgwu_pfcp_close(void) +{ + ogs_pfcp_node_t *pfcp_node = NULL; + + ogs_list_for_each(&ogs_pfcp_self()->peer_list, pfcp_node) + pfcp_node_fsm_fini(pfcp_node); + + ogs_socknode_remove_all(&ogs_pfcp_self()->pfcp_list); + ogs_socknode_remove_all(&ogs_pfcp_self()->pfcp_list6); +} + +void sgwu_pfcp_send_session_establishment_response( + ogs_pfcp_xact_t *xact, sgwu_sess_t *sess, + ogs_pfcp_pdr_t *created_pdr[], int num_of_created_pdr) +{ + int rv; + ogs_pkbuf_t *sxabuf = NULL; + ogs_pfcp_header_t h; + + ogs_assert(xact); + + memset(&h, 0, sizeof(ogs_pfcp_header_t)); + h.type = OGS_PFCP_SESSION_ESTABLISHMENT_RESPONSE_TYPE; + h.seid = sess->sgwc_sxa_seid; + + sxabuf = sgwu_sxa_build_session_establishment_response( + h.type, sess, created_pdr, num_of_created_pdr); + ogs_expect_or_return(sxabuf); + + rv = ogs_pfcp_xact_update_tx(xact, &h, sxabuf); + ogs_expect_or_return(rv == OGS_OK); + + rv = ogs_pfcp_xact_commit(xact); + ogs_expect(rv == OGS_OK); +} + +void sgwu_pfcp_send_session_modification_response( + ogs_pfcp_xact_t *xact, sgwu_sess_t *sess, + ogs_pfcp_pdr_t *created_pdr[], int num_of_created_pdr) +{ + int rv; + ogs_pkbuf_t *sxabuf = NULL; + ogs_pfcp_header_t h; + + ogs_assert(xact); + ogs_assert(created_pdr); + + memset(&h, 0, sizeof(ogs_pfcp_header_t)); + h.type = OGS_PFCP_SESSION_MODIFICATION_RESPONSE_TYPE; + h.seid = sess->sgwc_sxa_seid; + + sxabuf = sgwu_sxa_build_session_modification_response( + h.type, sess, created_pdr, num_of_created_pdr); + ogs_expect_or_return(sxabuf); + + rv = ogs_pfcp_xact_update_tx(xact, &h, sxabuf); + ogs_expect_or_return(rv == OGS_OK); + + rv = ogs_pfcp_xact_commit(xact); + ogs_expect(rv == OGS_OK); +} + +void sgwu_pfcp_send_session_deletion_response(ogs_pfcp_xact_t *xact, + sgwu_sess_t *sess) +{ + int rv; + ogs_pkbuf_t *sxabuf = NULL; + ogs_pfcp_header_t h; + + ogs_assert(xact); + + memset(&h, 0, sizeof(ogs_pfcp_header_t)); + h.type = OGS_PFCP_SESSION_DELETION_RESPONSE_TYPE; + h.seid = sess->sgwc_sxa_seid; + + sxabuf = sgwu_sxa_build_session_deletion_response(h.type, sess); + ogs_expect_or_return(sxabuf); + + rv = ogs_pfcp_xact_update_tx(xact, &h, sxabuf); + ogs_expect_or_return(rv == OGS_OK); + + rv = ogs_pfcp_xact_commit(xact); + ogs_expect(rv == OGS_OK); +} + +static void sess_timeout(ogs_pfcp_xact_t *xact, void *data) +{ + uint8_t type; + + ogs_assert(xact); + type = xact->seq[0].type; + + switch (type) { + case OGS_PFCP_SESSION_REPORT_REQUEST_TYPE: + ogs_error("No PFCP session report response"); + break; + default: + ogs_error("Not implemented [type:%d]", type); + break; + } +} + +void sgwu_pfcp_send_session_report_request( + sgwu_sess_t *sess, ogs_pfcp_user_plane_report_t *report) +{ + int rv; + ogs_pkbuf_t *sxabuf = NULL; + ogs_pfcp_header_t h; + ogs_pfcp_xact_t *xact = NULL; + + ogs_assert(sess); + ogs_assert(report); + + memset(&h, 0, sizeof(ogs_pfcp_header_t)); + h.type = OGS_PFCP_SESSION_REPORT_REQUEST_TYPE; + h.seid = sess->sgwc_sxa_seid; + + sxabuf = ogs_pfcp_build_session_report_request(h.type, report); + ogs_expect_or_return(sxabuf); + + xact = ogs_pfcp_xact_local_create( + sess->pfcp_node, &h, sxabuf, sess_timeout, sess); + ogs_expect_or_return(xact); + + rv = ogs_pfcp_xact_commit(xact); + ogs_expect(rv == OGS_OK); +} diff --git a/src/sgwu/pfcp-path.h b/src/sgwu/pfcp-path.h new file mode 100644 index 000000000..9a47cd93e --- /dev/null +++ b/src/sgwu/pfcp-path.h @@ -0,0 +1,48 @@ +/* + * 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 SGWU_PFCP_PATH_H +#define SGWU_PFCP_PATH_H + +#include "sxa-build.h" + +#ifdef __cplusplus +extern "C" { +#endif + +int sgwu_pfcp_open(void); +void sgwu_pfcp_close(void); + +void sgwu_pfcp_send_session_establishment_response( + ogs_pfcp_xact_t *xact, sgwu_sess_t *sess, + ogs_pfcp_pdr_t *created_pdr[], int num_of_created_pdr); +void sgwu_pfcp_send_session_modification_response( + ogs_pfcp_xact_t *xact, sgwu_sess_t *sess, + ogs_pfcp_pdr_t *created_pdr[], int num_of_created_pdr); +void sgwu_pfcp_send_session_deletion_response(ogs_pfcp_xact_t *xact, + sgwu_sess_t *sess); + +void sgwu_pfcp_send_session_report_request( + sgwu_sess_t *sess, ogs_pfcp_user_plane_report_t *report); + +#ifdef __cplusplus +} +#endif + +#endif /* SGWU_PFCP_PATH_H */ diff --git a/src/sgwu/pfcp-sm.c b/src/sgwu/pfcp-sm.c new file mode 100644 index 000000000..d45a216c5 --- /dev/null +++ b/src/sgwu/pfcp-sm.c @@ -0,0 +1,305 @@ +/* + * 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 "pfcp-path.h" +#include "sxa-handler.h" + +static void node_timeout(ogs_pfcp_xact_t *xact, void *data); + +void sgwu_pfcp_state_initial(ogs_fsm_t *s, sgwu_event_t *e) +{ + int rv; + ogs_pfcp_node_t *node = NULL; + + ogs_assert(s); + ogs_assert(e); + + sgwu_sm_debug(e); + + node = e->pfcp_node; + ogs_assert(node); + + rv = ogs_pfcp_connect( + ogs_pfcp_self()->pfcp_sock, ogs_pfcp_self()->pfcp_sock6, node); + ogs_assert(rv == OGS_OK); + + node->t_no_heartbeat = ogs_timer_add(sgwu_self()->timer_mgr, + sgwu_timer_no_heartbeat, node); + ogs_assert(node->t_no_heartbeat); + + OGS_FSM_TRAN(s, &sgwu_pfcp_state_will_associate); +} + +void sgwu_pfcp_state_final(ogs_fsm_t *s, sgwu_event_t *e) +{ + ogs_pfcp_node_t *node = NULL; + ogs_assert(s); + ogs_assert(e); + + sgwu_sm_debug(e); + + node = e->pfcp_node; + ogs_assert(node); + + ogs_timer_delete(node->t_no_heartbeat); +} + +void sgwu_pfcp_state_will_associate(ogs_fsm_t *s, sgwu_event_t *e) +{ + char buf[OGS_ADDRSTRLEN]; + + ogs_pfcp_node_t *node = NULL; + ogs_pfcp_xact_t *xact = NULL; + ogs_pfcp_message_t *message = NULL; + ogs_sockaddr_t *addr = NULL; + ogs_assert(s); + ogs_assert(e); + + sgwu_sm_debug(e); + + node = e->pfcp_node; + ogs_assert(node); + + switch (e->id) { + case OGS_FSM_ENTRY_SIG: + if (node->t_association) { + ogs_timer_start(node->t_association, + ogs_config()->time.message.pfcp.association_interval); + + ogs_pfcp_up_send_association_setup_request(node, node_timeout); + } + break; + + case OGS_FSM_EXIT_SIG: + if (node->t_association) { + ogs_timer_stop(node->t_association); + } + break; + + case SGWU_EVT_SXA_TIMER: + switch(e->timer_id) { + case SGWU_TIMER_ASSOCIATION: + addr = node->sa_list; + ogs_assert(addr); + + ogs_warn("Retry to association with peer [%s]:%d failed", + OGS_ADDR(addr, buf), OGS_PORT(addr)); + + ogs_assert(node->t_association); + ogs_timer_start(node->t_association, + ogs_config()->time.message.pfcp.association_interval); + + ogs_pfcp_up_send_association_setup_request(node, node_timeout); + break; + default: + ogs_error("Unknown timer[%s:%d]", + sgwu_timer_get_name(e->timer_id), e->timer_id); + break; + } + break; + case SGWU_EVT_SXA_MESSAGE: + message = e->pfcp_message; + ogs_assert(message); + xact = e->pfcp_xact; + ogs_assert(xact); + + switch (message->h.type) { + case OGS_PFCP_ASSOCIATION_SETUP_REQUEST_TYPE: + ogs_pfcp_up_handle_association_setup_request(node, xact, + &message->pfcp_association_setup_request); + OGS_FSM_TRAN(s, sgwu_pfcp_state_associated); + break; + case OGS_PFCP_ASSOCIATION_SETUP_RESPONSE_TYPE: + ogs_pfcp_up_handle_association_setup_response(node, xact, + &message->pfcp_association_setup_response); + OGS_FSM_TRAN(s, sgwu_pfcp_state_associated); + break; + default: + ogs_error("cannot handle PFCP message type[%d]", + message->h.type); + break; + } + break; + default: + ogs_error("Unknown event %s", sgwu_event_get_name(e)); + break; + } +} + +void sgwu_pfcp_state_associated(ogs_fsm_t *s, sgwu_event_t *e) +{ + char buf[OGS_ADDRSTRLEN]; + + ogs_pfcp_node_t *node = NULL; + ogs_pfcp_xact_t *xact = NULL; + ogs_pfcp_message_t *message = NULL; + + ogs_sockaddr_t *addr = NULL; + sgwu_sess_t *sess = NULL; + + ogs_assert(s); + ogs_assert(e); + + sgwu_sm_debug(e); + + node = e->pfcp_node; + ogs_assert(node); + addr = node->sa_list; + ogs_assert(addr); + + switch (e->id) { + case OGS_FSM_ENTRY_SIG: + ogs_info("PFCP associated"); + ogs_timer_start(node->t_no_heartbeat, + ogs_config()->time.message.pfcp.no_heartbeat_duration); + break; + case OGS_FSM_EXIT_SIG: + ogs_info("PFCP de-associated"); + ogs_timer_stop(node->t_no_heartbeat); + break; + case SGWU_EVT_SXA_MESSAGE: + message = e->pfcp_message; + ogs_assert(message); + xact = e->pfcp_xact; + ogs_assert(xact); + + if (message->h.seid_presence && message->h.seid != 0) + sess = sgwu_sess_find_by_up_seid(message->h.seid); + + switch (message->h.type) { + case OGS_PFCP_HEARTBEAT_REQUEST_TYPE: + ogs_pfcp_handle_heartbeat_request(node, xact, + &message->pfcp_heartbeat_request); + break; + case OGS_PFCP_HEARTBEAT_RESPONSE_TYPE: + ogs_pfcp_handle_heartbeat_response(node, xact, + &message->pfcp_heartbeat_response); + break; + case OGS_PFCP_ASSOCIATION_SETUP_REQUEST_TYPE: + ogs_warn("PFCP[REQ] has already been associated"); + ogs_pfcp_up_handle_association_setup_request(node, xact, + &message->pfcp_association_setup_request); + break; + case OGS_PFCP_ASSOCIATION_SETUP_RESPONSE_TYPE: + ogs_warn("PFCP[RSP] has already been associated"); + ogs_pfcp_up_handle_association_setup_response(node, xact, + &message->pfcp_association_setup_response); + break; + case OGS_PFCP_SESSION_ESTABLISHMENT_REQUEST_TYPE: + if (message->h.seid_presence && message->h.seid == 0) { + ogs_expect(!sess); + sess = sgwu_sess_add_by_message(message); + if (sess) + OGS_SETUP_PFCP_NODE(sess, node); + } + sgwu_sxa_handle_session_establishment_request( + sess, xact, &message->pfcp_session_establishment_request); + break; + case OGS_PFCP_SESSION_MODIFICATION_REQUEST_TYPE: + sgwu_sxa_handle_session_modification_request( + sess, xact, &message->pfcp_session_modification_request); + break; + case OGS_PFCP_SESSION_DELETION_REQUEST_TYPE: + sgwu_sxa_handle_session_deletion_request( + sess, xact, &message->pfcp_session_deletion_request); + break; + case OGS_PFCP_SESSION_REPORT_RESPONSE_TYPE: + sgwu_sxa_handle_session_report_response( + sess, xact, &message->pfcp_session_report_response); + break; + default: + ogs_error("Not implemented PFCP message type[%d]", + message->h.type); + break; + } + + break; + case SGWU_EVT_SXA_TIMER: + switch(e->timer_id) { + case SGWU_TIMER_NO_HEARTBEAT: + node = e->pfcp_node; + ogs_assert(node); + + ogs_pfcp_send_heartbeat_request(node, node_timeout); + break; + default: + ogs_error("Unknown timer[%s:%d]", + sgwu_timer_get_name(e->timer_id), e->timer_id); + break; + } + break; + case SGWU_EVT_SXA_NO_HEARTBEAT: + ogs_warn("No Heartbeat from SGWU [%s]:%d", + OGS_ADDR(addr, buf), OGS_PORT(addr)); + OGS_FSM_TRAN(s, sgwu_pfcp_state_will_associate); + break; + default: + ogs_error("Unknown event %s", sgwu_event_get_name(e)); + break; + } +} + +void sgwu_pfcp_state_exception(ogs_fsm_t *s, sgwu_event_t *e) +{ + ogs_assert(s); + ogs_assert(e); + + sgwu_sm_debug(e); + + switch (e->id) { + case OGS_FSM_ENTRY_SIG: + break; + case OGS_FSM_EXIT_SIG: + break; + default: + ogs_error("Unknown event %s", sgwu_event_get_name(e)); + break; + } +} + +static void node_timeout(ogs_pfcp_xact_t *xact, void *data) +{ + int rv; + + sgwu_event_t *e = NULL; + uint8_t type; + + ogs_assert(xact); + type = xact->seq[0].type; + + switch (type) { + case OGS_PFCP_HEARTBEAT_REQUEST_TYPE: + ogs_assert(data); + + e = sgwu_event_new(SGWU_EVT_SXA_NO_HEARTBEAT); + e->pfcp_node = data; + + rv = ogs_queue_push(sgwu_self()->queue, e); + if (rv != OGS_OK) { + ogs_warn("ogs_queue_push() failed:%d", (int)rv); + sgwu_event_free(e); + } + break; + case OGS_PFCP_ASSOCIATION_SETUP_REQUEST_TYPE: + break; + default: + ogs_error("Not implemented [type:%d]", type); + break; + } +} diff --git a/src/sgwu/sgwu-sm.c b/src/sgwu/sgwu-sm.c new file mode 100644 index 000000000..5b18605e2 --- /dev/null +++ b/src/sgwu/sgwu-sm.c @@ -0,0 +1,121 @@ +/* + * 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 "gtp-path.h" +#include "pfcp-path.h" + +void sgwu_state_initial(ogs_fsm_t *s, sgwu_event_t *e) +{ + sgwu_sm_debug(e); + + ogs_assert(s); + + OGS_FSM_TRAN(s, &sgwu_state_operational); +} + +void sgwu_state_final(ogs_fsm_t *s, sgwu_event_t *e) +{ + sgwu_sm_debug(e); + + ogs_assert(s); +} + +void sgwu_state_operational(ogs_fsm_t *s, sgwu_event_t *e) +{ + int rv; + ogs_pkbuf_t *recvbuf = NULL; + + ogs_pfcp_message_t pfcp_message; + ogs_pfcp_node_t *node = NULL; + ogs_pfcp_xact_t *xact = NULL; + + sgwu_bearer_t *bearer = NULL; + + sgwu_sm_debug(e); + + ogs_assert(s); + + switch (e->id) { + case OGS_FSM_ENTRY_SIG: + rv = sgwu_pfcp_open(); + if (rv != OGS_OK) { + ogs_fatal("Can't establish N4-PFCP path"); + } + rv = sgwu_gtp_open(); + if (rv != OGS_OK) { + ogs_error("Can't establish SGW path"); + break; + } + break; + case OGS_FSM_EXIT_SIG: + sgwu_pfcp_close(); + sgwu_gtp_close(); + break; + case SGWU_EVT_LO_DLDATA_NOTI: + ogs_assert(e); + + bearer = e->bearer; + ogs_assert(bearer); + +#if 0 + sgwu_s11_handle_lo_dldata_notification(bearer); +#endif + break; + case SGWU_EVT_SXA_MESSAGE: + ogs_assert(e); + recvbuf = e->pkbuf; + ogs_assert(recvbuf); + node = e->pfcp_node; + ogs_assert(node); + + if (ogs_pfcp_parse_msg(&pfcp_message, recvbuf) != OGS_OK) { + ogs_error("ogs_pfcp_parse_msg() failed"); + ogs_pkbuf_free(recvbuf); + break; + } + + rv = ogs_pfcp_xact_receive(node, &pfcp_message.h, &xact); + if (rv != OGS_OK) { + ogs_pkbuf_free(recvbuf); + break; + } + + e->pfcp_message = &pfcp_message; + e->pfcp_xact = xact; + ogs_fsm_dispatch(&node->sm, e); + if (OGS_FSM_CHECK(&node->sm, sgwu_pfcp_state_exception)) { + ogs_error("PFCP state machine exception"); + break; + } + + ogs_pkbuf_free(recvbuf); + break; + case SGWU_EVT_SXA_TIMER: + case SGWU_EVT_SXA_NO_HEARTBEAT: + node = e->pfcp_node; + ogs_assert(node); + ogs_assert(OGS_FSM_STATE(&node->sm)); + + ogs_fsm_dispatch(&node->sm, e); + break; + default: + ogs_error("No handler for event %s", sgwu_event_get_name(e)); + break; + } +} diff --git a/src/sgwu/sgwu-sm.h b/src/sgwu/sgwu-sm.h new file mode 100644 index 000000000..a3c6abbd9 --- /dev/null +++ b/src/sgwu/sgwu-sm.h @@ -0,0 +1,47 @@ +/* + * 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 SGWU_SM_H +#define SGWU_SM_H + +#include "event.h" + +#ifdef __cplusplus +extern "C" { +#endif + +void sgwu_state_initial(ogs_fsm_t *s, sgwu_event_t *e); +void sgwu_state_final(ogs_fsm_t *s, sgwu_event_t *e); +void sgwu_state_operational(ogs_fsm_t *s, sgwu_event_t *e); +void sgwu_state_exception(ogs_fsm_t *s, sgwu_event_t *e); + +void sgwu_pfcp_state_initial(ogs_fsm_t *s, sgwu_event_t *e); +void sgwu_pfcp_state_final(ogs_fsm_t *s, sgwu_event_t *e); +void sgwu_pfcp_state_will_associate(ogs_fsm_t *s, sgwu_event_t *e); +void sgwu_pfcp_state_associated(ogs_fsm_t *s, sgwu_event_t *e); +void sgwu_pfcp_state_exception(ogs_fsm_t *s, sgwu_event_t *e); + +#define sgwu_sm_debug(__pe) \ + ogs_debug("%s(): %s\n", __func__, sgwu_event_get_name(__pe)) + +#ifdef __cplusplus +} +#endif + +#endif /* !SGWU_SM_H */ diff --git a/src/sgwu/sxa-build.c b/src/sgwu/sxa-build.c new file mode 100644 index 000000000..79a5ef174 --- /dev/null +++ b/src/sgwu/sxa-build.c @@ -0,0 +1,123 @@ +/* + * 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 "sxa-build.h" + +ogs_pkbuf_t *sgwu_sxa_build_session_establishment_response(uint8_t type, + sgwu_sess_t *sess, ogs_pfcp_pdr_t *created_pdr[], int num_of_created_pdr) +{ + ogs_pfcp_message_t pfcp_message; + ogs_pfcp_session_establishment_response_t *rsp = NULL; + + int i = 0; + + ogs_pfcp_node_id_t node_id; + ogs_pfcp_f_seid_t f_seid; + int len = 0; + + ogs_debug("Session Establishment Response"); + + rsp = &pfcp_message.pfcp_session_establishment_response; + memset(&pfcp_message, 0, sizeof(ogs_pfcp_message_t)); + + /* Node ID */ + ogs_pfcp_sockaddr_to_node_id( + ogs_pfcp_self()->pfcp_addr, ogs_pfcp_self()->pfcp_addr6, + ogs_config()->parameter.prefer_ipv4, + &node_id, &len); + rsp->node_id.presence = 1; + rsp->node_id.data = &node_id; + rsp->node_id.len = len; + + /* Cause */ + rsp->cause.presence = 1; + rsp->cause.u8 = OGS_PFCP_CAUSE_REQUEST_ACCEPTED; + + /* F-SEID */ + ogs_pfcp_sockaddr_to_f_seid( + ogs_pfcp_self()->pfcp_addr, ogs_pfcp_self()->pfcp_addr6, + &f_seid, &len); + f_seid.seid = htobe64(sess->sgwu_sxa_seid); + rsp->up_f_seid.presence = 1; + rsp->up_f_seid.data = &f_seid; + rsp->up_f_seid.len = len; + + /* Created PDR */ + for (i = 0; i < num_of_created_pdr; i++) { + ogs_pfcp_tlv_created_pdr_t *message = &rsp->created_pdr[i]; + ogs_assert(message); + + message->presence = 1; + message->pdr_id.presence = 1; + message->pdr_id.u16 = created_pdr[i]->id; + } + + pfcp_message.h.type = type; + return ogs_pfcp_build_msg(&pfcp_message); +} + +ogs_pkbuf_t *sgwu_sxa_build_session_modification_response(uint8_t type, + sgwu_sess_t *sess, ogs_pfcp_pdr_t *created_pdr[], int num_of_created_pdr) +{ + ogs_pfcp_message_t pfcp_message; + ogs_pfcp_session_modification_response_t *rsp = NULL; + + int i = 0; + + ogs_debug("Session Modification Response"); + + rsp = &pfcp_message.pfcp_session_modification_response; + memset(&pfcp_message, 0, sizeof(ogs_pfcp_message_t)); + + /* Cause */ + rsp->cause.presence = 1; + rsp->cause.u8 = OGS_PFCP_CAUSE_REQUEST_ACCEPTED; + + /* Created PDR */ + for (i = 0; i < num_of_created_pdr; i++) { + ogs_pfcp_tlv_created_pdr_t *message = &rsp->created_pdr[i]; + ogs_assert(message); + + message->presence = 1; + message->pdr_id.presence = 1; + message->pdr_id.u16 = created_pdr[i]->id; + } + + pfcp_message.h.type = type; + return ogs_pfcp_build_msg(&pfcp_message); +} + +ogs_pkbuf_t *sgwu_sxa_build_session_deletion_response(uint8_t type, + sgwu_sess_t *sess) +{ + ogs_pfcp_message_t pfcp_message; + ogs_pfcp_session_deletion_response_t *rsp = NULL; + + ogs_debug("Session Deletion Response"); + + rsp = &pfcp_message.pfcp_session_deletion_response; + memset(&pfcp_message, 0, sizeof(ogs_pfcp_message_t)); + + /* Cause */ + rsp->cause.presence = 1; + rsp->cause.u8 = OGS_PFCP_CAUSE_REQUEST_ACCEPTED; + + pfcp_message.h.type = type; + return ogs_pfcp_build_msg(&pfcp_message); +} diff --git a/src/sgwu/sxa-build.h b/src/sgwu/sxa-build.h new file mode 100644 index 000000000..6d3069309 --- /dev/null +++ b/src/sgwu/sxa-build.h @@ -0,0 +1,40 @@ +/* + * 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 SGWU_SXA_BUILD_H +#define SGWU_SXA_BUILD_H + +#include "context.h" + +#ifdef __cplusplus +extern "C" { +#endif + +ogs_pkbuf_t *sgwu_sxa_build_session_establishment_response(uint8_t type, + sgwu_sess_t *sess, ogs_pfcp_pdr_t *created_pdr[], int num_of_created_pdr); +ogs_pkbuf_t *sgwu_sxa_build_session_modification_response(uint8_t type, + sgwu_sess_t *sess, ogs_pfcp_pdr_t *created_pdr[], int num_of_created_pdr); +ogs_pkbuf_t *sgwu_sxa_build_session_deletion_response(uint8_t type, + sgwu_sess_t *sess); + +#ifdef __cplusplus +} +#endif + +#endif /* SGWU_SXA_BUILD_H */ diff --git a/src/sgwu/sxa-handler.c b/src/sgwu/sxa-handler.c new file mode 100644 index 000000000..a418d13ce --- /dev/null +++ b/src/sgwu/sxa-handler.c @@ -0,0 +1,337 @@ +/* + * 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 "pfcp-path.h" +#include "gtp-path.h" +#include "sxa-handler.h" + +static void setup_gtp_node(ogs_pfcp_far_t *far) +{ + int rv; + ogs_ip_t ip; + ogs_gtp_node_t *gnode = NULL; + + ogs_assert(far); + + ogs_pfcp_outer_header_creation_to_ip(&far->outer_header_creation, &ip); + + /* No Outer Header Creation */ + if (ip.len == 0) return; + + gnode = ogs_gtp_node_find_by_ip(&sgwu_self()->peer_list, &ip); + if (!gnode) { + gnode = ogs_gtp_node_add_by_ip( + &sgwu_self()->peer_list, &ip, sgwu_self()->gtpu_port, + ogs_config()->parameter.no_ipv4, + ogs_config()->parameter.no_ipv6, + ogs_config()->parameter.prefer_ipv4); + ogs_assert(gnode); + + rv = ogs_gtp_connect( + sgwu_self()->gtpu_sock, sgwu_self()->gtpu_sock6, gnode); + ogs_assert(rv == OGS_OK); + } + OGS_SETUP_GTP_NODE(far, gnode); +} + +void sgwu_sxa_handle_session_establishment_request( + sgwu_sess_t *sess, ogs_pfcp_xact_t *xact, + ogs_pfcp_session_establishment_request_t *req) +{ + ogs_pfcp_pdr_t *pdr = NULL; + ogs_pfcp_far_t *far = NULL; + ogs_pfcp_pdr_t *created_pdr[OGS_MAX_NUM_OF_PDR]; + int num_of_created_pdr = 0; + uint8_t cause_value = 0; + uint8_t offending_ie_value = 0; + int i; + + ogs_assert(xact); + ogs_assert(req); + + ogs_debug("Session Establishment Request"); + + cause_value = OGS_PFCP_CAUSE_REQUEST_ACCEPTED; + + if (!sess) { + ogs_error("No Context"); + ogs_pfcp_send_error_message(xact, 0, + OGS_PFCP_SESSION_ESTABLISHMENT_RESPONSE_TYPE, + OGS_PFCP_CAUSE_SESSION_CONTEXT_NOT_FOUND, 0); + return; + } + + for (i = 0; i < OGS_MAX_NUM_OF_PDR; i++) { + created_pdr[i] = ogs_pfcp_handle_create_pdr(&sess->pfcp, + &req->create_pdr[i], &cause_value, &offending_ie_value); + if (created_pdr[i] == NULL) + break; + } + num_of_created_pdr = i; + if (cause_value != OGS_PFCP_CAUSE_REQUEST_ACCEPTED) + goto cleanup; + + for (i = 0; i < OGS_MAX_NUM_OF_FAR; i++) { + if (ogs_pfcp_handle_create_far(&sess->pfcp, &req->create_far[i], + &cause_value, &offending_ie_value) == NULL) + break; + } + if (cause_value != OGS_PFCP_CAUSE_REQUEST_ACCEPTED) + goto cleanup; + + for (i = 0; i < OGS_MAX_NUM_OF_QER; i++) { + if (ogs_pfcp_handle_create_qer(&sess->pfcp, &req->create_qer[i], + &cause_value, &offending_ie_value) == NULL) + break; + } + if (cause_value != OGS_PFCP_CAUSE_REQUEST_ACCEPTED) + goto cleanup; + + /* Setup GTP Node */ + ogs_list_for_each(&sess->pfcp.far_list, far) + setup_gtp_node(far); + + /* Setup TEID Hash */ + for (i = 0; i < num_of_created_pdr; i++) { + pdr = created_pdr[i]; + ogs_assert(pdr); + + if (pdr->f_teid_len) + ogs_pfcp_pdr_hash_set(pdr); + } + + /* Send Buffered Packet to gNB */ + ogs_list_for_each(&sess->pfcp.pdr_list, pdr) { + if (pdr->src_if == OGS_PFCP_INTERFACE_CORE) { /* Downlink */ + ogs_pfcp_send_buffered_packet(pdr); + } + } + + sgwu_pfcp_send_session_establishment_response( + xact, sess, created_pdr, num_of_created_pdr); + return; + +cleanup: + ogs_pfcp_sess_clear(&sess->pfcp); + ogs_pfcp_send_error_message(xact, sess ? sess->sgwu_sxa_seid : 0, + OGS_PFCP_SESSION_ESTABLISHMENT_RESPONSE_TYPE, + cause_value, offending_ie_value); +} + +void sgwu_sxa_handle_session_modification_request( + sgwu_sess_t *sess, ogs_pfcp_xact_t *xact, + ogs_pfcp_session_modification_request_t *req) +{ + ogs_pfcp_pdr_t *pdr = NULL; + ogs_pfcp_far_t *far = NULL; + ogs_pfcp_pdr_t *created_pdr[OGS_MAX_NUM_OF_PDR]; + int num_of_created_pdr = 0; + uint8_t cause_value = 0; + uint8_t offending_ie_value = 0; + int i; + + ogs_assert(xact); + ogs_assert(req); + + ogs_debug("Session Modification Request"); + + cause_value = OGS_PFCP_CAUSE_REQUEST_ACCEPTED; + + if (!sess) { + ogs_error("No Context"); + ogs_pfcp_send_error_message(xact, 0, + OGS_PFCP_SESSION_MODIFICATION_RESPONSE_TYPE, + OGS_PFCP_CAUSE_SESSION_CONTEXT_NOT_FOUND, 0); + return; + } + + for (i = 0; i < OGS_MAX_NUM_OF_PDR; i++) { + created_pdr[i] = ogs_pfcp_handle_create_pdr(&sess->pfcp, + &req->create_pdr[i], &cause_value, &offending_ie_value); + if (created_pdr[i] == NULL) + break; + } + num_of_created_pdr = i; + if (cause_value != OGS_PFCP_CAUSE_REQUEST_ACCEPTED) + goto cleanup; + + for (i = 0; i < OGS_MAX_NUM_OF_PDR; i++) { + if (ogs_pfcp_handle_remove_pdr(&sess->pfcp, &req->remove_pdr[i], + &cause_value, &offending_ie_value) == false) + break; + } + if (cause_value != OGS_PFCP_CAUSE_REQUEST_ACCEPTED) + goto cleanup; + + for (i = 0; i < OGS_MAX_NUM_OF_FAR; i++) { + if (ogs_pfcp_handle_create_far(&sess->pfcp, &req->create_far[i], + &cause_value, &offending_ie_value) == NULL) + break; + } + if (cause_value != OGS_PFCP_CAUSE_REQUEST_ACCEPTED) + goto cleanup; + + for (i = 0; i < OGS_MAX_NUM_OF_FAR; i++) { + if (ogs_pfcp_handle_update_far_flags(&sess->pfcp, &req->update_far[i], + &cause_value, &offending_ie_value) == NULL) + break; + } + if (cause_value != OGS_PFCP_CAUSE_REQUEST_ACCEPTED) + goto cleanup; + + /* Send End Marker to gNB */ + ogs_list_for_each(&sess->pfcp.pdr_list, pdr) { + far = pdr->far; + if (far && far->smreq_flags.send_end_marker_packets) + ogs_pfcp_send_end_marker(pdr); + } + /* Clear PFCPSMReq-Flags */ + ogs_list_for_each(&sess->pfcp.far_list, far) + far->smreq_flags.value = 0; + + for (i = 0; i < OGS_MAX_NUM_OF_FAR; i++) { + if (ogs_pfcp_handle_update_far(&sess->pfcp, &req->update_far[i], + &cause_value, &offending_ie_value) == NULL) + break; + } + if (cause_value != OGS_PFCP_CAUSE_REQUEST_ACCEPTED) + goto cleanup; + + for (i = 0; i < OGS_MAX_NUM_OF_FAR; i++) { + if (ogs_pfcp_handle_remove_far(&sess->pfcp, &req->remove_far[i], + &cause_value, &offending_ie_value) == false) + break; + } + if (cause_value != OGS_PFCP_CAUSE_REQUEST_ACCEPTED) + goto cleanup; + + for (i = 0; i < OGS_MAX_NUM_OF_QER; i++) { + if (ogs_pfcp_handle_create_qer(&sess->pfcp, &req->create_qer[i], + &cause_value, &offending_ie_value) == NULL) + break; + } + if (cause_value != OGS_PFCP_CAUSE_REQUEST_ACCEPTED) + goto cleanup; + + for (i = 0; i < OGS_MAX_NUM_OF_QER; i++) { + if (ogs_pfcp_handle_update_qer(&sess->pfcp, &req->update_qer[i], + &cause_value, &offending_ie_value) == NULL) + break; + } + if (cause_value != OGS_PFCP_CAUSE_REQUEST_ACCEPTED) + goto cleanup; + + for (i = 0; i < OGS_MAX_NUM_OF_QER; i++) { + if (ogs_pfcp_handle_remove_qer(&sess->pfcp, &req->remove_qer[i], + &cause_value, &offending_ie_value) == false) + break; + } + if (cause_value != OGS_PFCP_CAUSE_REQUEST_ACCEPTED) + goto cleanup; + + /* Setup GTP Node */ + ogs_list_for_each(&sess->pfcp.far_list, far) + setup_gtp_node(far); + + /* Setup TEID Hash */ + for (i = 0; i < num_of_created_pdr; i++) { + pdr = created_pdr[i]; + ogs_assert(pdr); + + if (pdr->f_teid_len) + ogs_pfcp_pdr_hash_set(pdr); + } + + /* Send Buffered Packet to gNB */ + ogs_list_for_each(&sess->pfcp.pdr_list, pdr) { + if (pdr->src_if == OGS_PFCP_INTERFACE_CORE) { /* Downlink */ + ogs_pfcp_send_buffered_packet(pdr); + } + } + + sgwu_pfcp_send_session_modification_response( + xact, sess, created_pdr, num_of_created_pdr); + return; + +cleanup: + ogs_pfcp_sess_clear(&sess->pfcp); + ogs_pfcp_send_error_message(xact, sess ? sess->sgwu_sxa_seid : 0, + OGS_PFCP_SESSION_MODIFICATION_RESPONSE_TYPE, + cause_value, offending_ie_value); +} + +void sgwu_sxa_handle_session_deletion_request( + sgwu_sess_t *sess, ogs_pfcp_xact_t *xact, + ogs_pfcp_session_deletion_request_t *req) +{ + ogs_assert(xact); + ogs_assert(req); + + ogs_debug("Session Deletion Request"); + + if (!sess) { + ogs_error("No Context"); + ogs_pfcp_send_error_message(xact, 0, + OGS_PFCP_SESSION_DELETION_RESPONSE_TYPE, + OGS_PFCP_CAUSE_SESSION_CONTEXT_NOT_FOUND, 0); + return; + } + + ogs_assert(sess); + + sgwu_pfcp_send_session_deletion_response(xact, sess); + + sgwu_sess_remove(sess); +} + +void sgwu_sxa_handle_session_report_response( + sgwu_sess_t *sess, ogs_pfcp_xact_t *xact, + ogs_pfcp_session_report_response_t *rsp) +{ + uint8_t cause_value = 0; + + ogs_assert(xact); + ogs_assert(rsp); + + ogs_pfcp_xact_commit(xact); + + ogs_debug("Session report resopnse"); + + cause_value = OGS_PFCP_CAUSE_REQUEST_ACCEPTED; + + if (!sess) { + ogs_warn("No Context"); + cause_value = OGS_PFCP_CAUSE_SESSION_CONTEXT_NOT_FOUND; + } + + if (rsp->cause.presence) { + if (rsp->cause.u8 != OGS_PFCP_CAUSE_REQUEST_ACCEPTED) { + ogs_error("PFCP Cause[%d] : Not Accepted", rsp->cause.u8); + cause_value = rsp->cause.u8; + } + } else { + ogs_error("No Cause"); + cause_value = OGS_PFCP_CAUSE_MANDATORY_IE_MISSING; + } + + if (cause_value != OGS_PFCP_CAUSE_REQUEST_ACCEPTED) { + ogs_error("Cause request not accepted[%d]", cause_value); + return; + } +} diff --git a/src/sgwu/sxa-handler.h b/src/sgwu/sxa-handler.h new file mode 100644 index 000000000..5ceec47eb --- /dev/null +++ b/src/sgwu/sxa-handler.h @@ -0,0 +1,47 @@ +/* + * 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 SGWU_SXA_HANDLER_H +#define SGWU_SXA_HANDLER_H + +#include "context.h" + +#ifdef __cplusplus +extern "C" { +#endif + +void sgwu_sxa_handle_session_establishment_request( + sgwu_sess_t *sess, ogs_pfcp_xact_t *xact, + ogs_pfcp_session_establishment_request_t *req); +void sgwu_sxa_handle_session_modification_request( + sgwu_sess_t *sess, ogs_pfcp_xact_t *xact, + ogs_pfcp_session_modification_request_t *req); +void sgwu_sxa_handle_session_deletion_request( + sgwu_sess_t *sess, ogs_pfcp_xact_t *xact, + ogs_pfcp_session_deletion_request_t *req); + +void sgwu_sxa_handle_session_report_response( + sgwu_sess_t *sess, ogs_pfcp_xact_t *xact, + ogs_pfcp_session_report_response_t *rsp); + +#ifdef __cplusplus +} +#endif + +#endif /* SGWU_SXA_HANDLER_H */ diff --git a/src/sgwu/timer.c b/src/sgwu/timer.c new file mode 100644 index 000000000..31b8436ce --- /dev/null +++ b/src/sgwu/timer.c @@ -0,0 +1,61 @@ +/* + * 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 "context.h" + +const char *sgwu_timer_get_name(sgwu_timer_e id) +{ + switch (id) { + case SGWU_TIMER_ASSOCIATION: + return "SGWU_TIMER_ASSOCIATION"; + case SGWU_TIMER_NO_HEARTBEAT: + return "SGWU_TIMER_NO_HEARTBEAT"; + default: + break; + } + + return "UNKNOWN_TIMER"; +} + +static void timer_send_event(int timer_id, void *data) +{ + int rv; + sgwu_event_t *e = NULL; + ogs_assert(data); + + e = sgwu_event_new(SGWU_EVT_SXA_TIMER); + e->timer_id = timer_id; + e->pfcp_node = data; + + rv = ogs_queue_push(sgwu_self()->queue, e); + if (rv != OGS_OK) { + ogs_warn("ogs_queue_push() failed:%d", (int)rv); + sgwu_event_free(e); + } +} + +void sgwu_timer_association(void *data) +{ + timer_send_event(SGWU_TIMER_ASSOCIATION, data); +} + +void sgwu_timer_no_heartbeat(void *data) +{ + timer_send_event(SGWU_TIMER_NO_HEARTBEAT, data); +} diff --git a/src/sgwu/timer.h b/src/sgwu/timer.h new file mode 100644 index 000000000..fc0627a19 --- /dev/null +++ b/src/sgwu/timer.h @@ -0,0 +1,49 @@ +/* + * 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 SGWU_TIMER_H +#define SGWU_TIMER_H + +#include "ogs-core.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* forward declaration */ +typedef enum { + SGWU_TIMER_BASE = 0, + + SGWU_TIMER_ASSOCIATION, + SGWU_TIMER_NO_HEARTBEAT, + + MAX_NUM_OF_SGWU_TIMER, + +} sgwu_timer_e; + +const char *sgwu_timer_get_name(sgwu_timer_e id); + +void sgwu_timer_association(void *data); +void sgwu_timer_no_heartbeat(void *data); + +#ifdef __cplusplus +} +#endif + +#endif /* SGWU_TIMER_H */ diff --git a/src/smf/bearer-binding.c b/src/smf/bearer-binding.c index 8986866a0..8bcc59e30 100644 --- a/src/smf/bearer-binding.c +++ b/src/smf/bearer-binding.c @@ -55,76 +55,83 @@ static void encode_traffic_flow_template( tft->pf[i].precedence = i+1; j = 0, len = 0; - if (pf->rule.proto) { + if (pf->ipfw_rule.proto) { tft->pf[i].component[j].type = GTP_PACKET_FILTER_PROTOCOL_IDENTIFIER_NEXT_HEADER_TYPE; - tft->pf[i].component[j].proto = pf->rule.proto; + tft->pf[i].component[j].proto = pf->ipfw_rule.proto; j++; len += 2; } - if (pf->rule.ipv4_local) { + if (pf->ipfw_rule.ipv4_local) { tft->pf[i].component[j].type = GTP_PACKET_FILTER_IPV4_LOCAL_ADDRESS_TYPE; - tft->pf[i].component[j].ipv4.addr = pf->rule.ip.local.addr[0]; - tft->pf[i].component[j].ipv4.mask = pf->rule.ip.local.mask[0]; + tft->pf[i].component[j].ipv4.addr = pf->ipfw_rule.ip.local.addr[0]; + tft->pf[i].component[j].ipv4.mask = pf->ipfw_rule.ip.local.mask[0]; j++; len += 9; } - if (pf->rule.ipv4_remote) { + if (pf->ipfw_rule.ipv4_remote) { tft->pf[i].component[j].type = GTP_PACKET_FILTER_IPV4_REMOTE_ADDRESS_TYPE; - tft->pf[i].component[j].ipv4.addr = pf->rule.ip.remote.addr[0]; - tft->pf[i].component[j].ipv4.mask = pf->rule.ip.remote.mask[0]; + tft->pf[i].component[j].ipv4.addr = pf->ipfw_rule.ip.remote.addr[0]; + tft->pf[i].component[j].ipv4.mask = pf->ipfw_rule.ip.remote.mask[0]; j++; len += 9; } - if (pf->rule.ipv6_local) { + if (pf->ipfw_rule.ipv6_local) { tft->pf[i].component[j].type = GTP_PACKET_FILTER_IPV6_LOCAL_ADDRESS_PREFIX_LENGTH_TYPE; - memcpy(tft->pf[i].component[j].ipv6.addr, pf->rule.ip.local.addr, - sizeof pf->rule.ip.local.addr); + memcpy(tft->pf[i].component[j].ipv6.addr, + pf->ipfw_rule.ip.local.addr, + sizeof pf->ipfw_rule.ip.local.addr); tft->pf[i].component[j].ipv6.prefixlen = - contigmask((uint8_t *)pf->rule.ip.local.mask, 128); + contigmask((uint8_t *)pf->ipfw_rule.ip.local.mask, 128); j++; len += 18; } - if (pf->rule.ipv6_remote) { + if (pf->ipfw_rule.ipv6_remote) { tft->pf[i].component[j].type = GTP_PACKET_FILTER_IPV6_REMOTE_ADDRESS_PREFIX_LENGTH_TYPE; - memcpy(tft->pf[i].component[j].ipv6.addr, pf->rule.ip.remote.addr, - sizeof pf->rule.ip.remote.addr); + memcpy(tft->pf[i].component[j].ipv6.addr, + pf->ipfw_rule.ip.remote.addr, + sizeof pf->ipfw_rule.ip.remote.addr); tft->pf[i].component[j].ipv6.prefixlen = - contigmask((uint8_t *)pf->rule.ip.remote.mask, 128); + contigmask((uint8_t *)pf->ipfw_rule.ip.remote.mask, 128); j++; len += 18; } - if (pf->rule.port.local.low) { - if (pf->rule.port.local.low == pf->rule.port.local.high) + if (pf->ipfw_rule.port.local.low) { + if (pf->ipfw_rule.port.local.low == pf->ipfw_rule.port.local.high) { tft->pf[i].component[j].type = GTP_PACKET_FILTER_SINGLE_LOCAL_PORT_TYPE; - tft->pf[i].component[j].port.low = pf->rule.port.local.low; + tft->pf[i].component[j].port.low = pf->ipfw_rule.port.local.low; j++; len += 3; } else { tft->pf[i].component[j].type = GTP_PACKET_FILTER_LOCAL_PORT_RANGE_TYPE; - tft->pf[i].component[j].port.low = pf->rule.port.local.low; - tft->pf[i].component[j].port.high = pf->rule.port.local.high; + tft->pf[i].component[j].port.low = pf->ipfw_rule.port.local.low; + tft->pf[i].component[j].port.high = + pf->ipfw_rule.port.local.high; j++; len += 5; } } - if (pf->rule.port.remote.low) { - if (pf->rule.port.remote.low == pf->rule.port.remote.high) { + if (pf->ipfw_rule.port.remote.low) { + if (pf->ipfw_rule.port.remote.low == + pf->ipfw_rule.port.remote.high) { tft->pf[i].component[j].type = GTP_PACKET_FILTER_SINGLE_REMOTE_PORT_TYPE; - tft->pf[i].component[j].port.low = pf->rule.port.remote.low; + tft->pf[i].component[j].port.low = + pf->ipfw_rule.port.remote.low; j++; len += 3; } else { tft->pf[i].component[j].type = GTP_PACKET_FILTER_REMOTE_PORT_RANGE_TYPE; - tft->pf[i].component[j].port.low = pf->rule.port.remote.low; - tft->pf[i].component[j].port.high = pf->rule.port.remote.high; + tft->pf[i].component[j].port.low = + pf->ipfw_rule.port.remote.low; + tft->pf[i].component[j].port.high = + pf->ipfw_rule.port.remote.high; j++; len += 5; } } @@ -191,6 +198,7 @@ void smf_bearer_binding(smf_sess_t *sess) memcpy(&bearer->qos, &pcc_rule->qos, sizeof(ogs_qos_t)); bearer_created = 1; + } else { ogs_assert(strcmp(bearer->name, pcc_rule->name) == 0); @@ -223,31 +231,26 @@ void smf_bearer_binding(smf_sess_t *sess) } } + ogs_list_for_each(&bearer->pfcp.pdr_list, pdr) + pdr->num_of_flow = 0; + for (j = 0; j < pcc_rule->num_of_flow; j++) { ogs_flow_t *flow = &pcc_rule->flow[j]; - ogs_ipfw_rule_t rule; smf_pf_t *pf = NULL; - char *tmp = NULL; ogs_expect_or_return(flow); ogs_expect_or_return(flow->description); - /* Find First Downlink/Uplink PDR in the Bearer */ ogs_list_for_each(&bearer->pfcp.pdr_list, pdr) { - ogs_pfcp_far_t *far = NULL; - far = pdr->far; - ogs_assert(far); if (flow->direction == OGS_FLOW_DOWNLINK_ONLY) { - if (pdr->src_if == OGS_PFCP_INTERFACE_CORE && - far->dst_if == OGS_PFCP_INTERFACE_ACCESS) { + if (pdr->src_if == OGS_PFCP_INTERFACE_CORE) { pdr->flow_description[pdr->num_of_flow++] = flow->description; break; } } else if (flow->direction == OGS_FLOW_UPLINK_ONLY) { - if (pdr->src_if == OGS_PFCP_INTERFACE_ACCESS && - far->dst_if == OGS_PFCP_INTERFACE_CORE) { + if (pdr->src_if == OGS_PFCP_INTERFACE_ACCESS) { pdr->flow_description[pdr->num_of_flow++] = flow->description; break; @@ -257,19 +260,21 @@ void smf_bearer_binding(smf_sess_t *sess) flow->direction); break; } - } - tmp = ogs_strdup(flow->description); - rv = ogs_ipfw_compile_rule(&rule, tmp); - ogs_free(tmp); - ogs_expect_or_return(rv == OGS_OK); - pf = smf_pf_add(bearer, pcc_rule->precedence); - ogs_expect_or_return(pf); + ogs_assert(pf); - memcpy(&pf->rule, &rule, sizeof(ogs_ipfw_rule_t)); pf->direction = flow->direction; + pf->flow_description = ogs_strdup(flow->description); + rv = ogs_ipfw_compile_rule( + &pf->ipfw_rule, pf->flow_description); + if (rv != OGS_OK) { + ogs_error("Invalid Flow-Description[%s]", + pf->flow_description); + smf_pf_remove(pf); + break; + } } memset(&tft, 0, sizeof tft); @@ -278,10 +283,6 @@ void smf_bearer_binding(smf_sess_t *sess) memset(&h, 0, sizeof(ogs_gtp_header_t)); if (bearer_created == 1) { - memset(&bearer->pfcp_epc_modify, - 0, sizeof(bearer->pfcp_epc_modify)); - bearer->pfcp_epc_modify.create = true; - h.type = OGS_GTP_CREATE_BEARER_REQUEST_TYPE; h.teid = sess->sgw_s5c_teid; @@ -292,14 +293,12 @@ void smf_bearer_binding(smf_sess_t *sess) pkbuf = smf_s5c_build_create_bearer_request( h.type, bearer, pcc_rule->num_of_flow ? &tft : NULL); ogs_expect_or_return(pkbuf); - } else { - memset(&bearer->pfcp_epc_modify, - 0, sizeof(bearer->pfcp_epc_modify)); - if (pcc_rule->num_of_flow) - bearer->pfcp_epc_modify.tft_update = true; - if (qos_presence) - bearer->pfcp_epc_modify.qos_update = true; + xact = ogs_gtp_xact_local_create( + sess->gnode, &h, pkbuf, timeout, sess); + ogs_expect_or_return(xact); + + } else { h.type = OGS_GTP_UPDATE_BEARER_REQUEST_TYPE; h.teid = sess->sgw_s5c_teid; @@ -308,14 +307,20 @@ void smf_bearer_binding(smf_sess_t *sess) OGS_NAS_PROCEDURE_TRANSACTION_IDENTITY_UNASSIGNED, pcc_rule->num_of_flow ? &tft : NULL, qos_presence); ogs_expect_or_return(pkbuf); - } - xact = ogs_gtp_xact_local_create( - sess->gnode, &h, pkbuf, timeout, sess); - ogs_expect_or_return(xact); + xact = ogs_gtp_xact_local_create( + sess->gnode, &h, pkbuf, timeout, sess); + ogs_expect_or_return(xact); + + if (pcc_rule->num_of_flow) + xact->update_flags |= OGS_GTP_MODIFY_TFT_UPDATE; + if (qos_presence) + xact->update_flags |= OGS_GTP_MODIFY_QOS_UPDATE; + } rv = ogs_gtp_xact_commit(xact); ogs_expect(rv == OGS_OK); + } else if (pcc_rule->type == OGS_PCC_RULE_TYPE_REMOVE) { bearer = smf_bearer_find_by_name(sess, pcc_rule->name); ogs_assert(bearer); @@ -327,10 +332,6 @@ void smf_bearer_binding(smf_sess_t *sess) return; } - memset(&bearer->pfcp_epc_modify, 0, - sizeof(bearer->pfcp_epc_modify)); - bearer->pfcp_epc_modify.remove = true; - memset(&h, 0, sizeof(ogs_gtp_header_t)); h.type = OGS_GTP_DELETE_BEARER_REQUEST_TYPE; h.teid = sess->sgw_s5c_teid; diff --git a/src/smf/context.c b/src/smf/context.c index 0bf30dfbf..ac23dcf1b 100644 --- a/src/smf/context.c +++ b/src/smf/context.c @@ -135,6 +135,10 @@ static int smf_context_validation(void) ogs_error("No smf.dns in '%s'", ogs_config()->file); return OGS_ERROR; } + if (ogs_list_first(&ogs_pfcp_self()->subnet_list) == NULL) { + ogs_error("No smf.pdn: in '%s'", ogs_config()->file); + return OGS_ERROR; + } return OGS_OK; } @@ -635,13 +639,13 @@ static ogs_pfcp_node_t *selected_upf_node( compare_ue_info(node, sess) == true) return node; } - for (node = ogs_list_first(&ogs_pfcp_self()->n4_list); + for (node = ogs_list_first(&ogs_pfcp_self()->peer_list); node != next; node = ogs_list_next(node)) { if (OGS_FSM_CHECK(&node->sm, smf_pfcp_state_associated) && compare_ue_info(node, sess) == true) return node; } - return next ? next : ogs_list_first(&ogs_pfcp_self()->n4_list); + return next ? next : ogs_list_first(&ogs_pfcp_self()->peer_list); } void smf_sess_select_upf(smf_sess_t *sess) @@ -652,10 +656,10 @@ void smf_sess_select_upf(smf_sess_t *sess) /* * When used for the first time, if last node is set, - * the search is performed from the first SGW in a round-robin manner. + * the search is performed from the first UPF in a round-robin manner. */ if (ogs_pfcp_self()->node == NULL) - ogs_pfcp_self()->node = ogs_list_last(&ogs_pfcp_self()->n4_list); + ogs_pfcp_self()->node = ogs_list_last(&ogs_pfcp_self()->peer_list); /* setup GTP session with selected UPF */ ogs_pfcp_self()->node = selected_upf_node(ogs_pfcp_self()->node, sess); @@ -680,7 +684,7 @@ smf_sess_t *smf_sess_add_by_apn(smf_ue_t *smf_ue, char *apn) ogs_pool_alloc(&smf_sess_pool, &sess); if (!sess) { ogs_error("Maximum number of session[%d] reached", - ogs_config()->pool.sess); + ogs_config()->pool.sess); return NULL; } memset(sess, 0, sizeof *sess); @@ -690,7 +694,7 @@ smf_sess_t *smf_sess_add_by_apn(smf_ue_t *smf_ue, char *apn) /* Set TEID & SEID */ sess->smf_n4_teid = sess->index; - sess->smf_n4_seid = SMF_INDEX_TO_SEID(sess->index); + sess->smf_n4_seid = sess->index; /* Set APN */ ogs_cpystrn(sess->pdn.apn, apn, OGS_MAX_APN_LEN+1); @@ -1011,7 +1015,7 @@ smf_sess_t *smf_sess_find_by_teid(uint32_t teid) smf_sess_t *smf_sess_find_by_seid(uint64_t seid) { - return smf_sess_find(SMF_SEID_TO_INDEX(seid)); + return smf_sess_find(seid); } smf_sess_t *smf_sess_find_by_apn(smf_ue_t *smf_ue, char *apn) @@ -1100,11 +1104,30 @@ smf_bearer_t *smf_qos_flow_add(smf_sess_t *sess) dl_pdr->id = OGS_NEXT_ID(sess->pdr_id, 1, OGS_MAX_NUM_OF_PDR+1); dl_pdr->src_if = OGS_PFCP_INTERFACE_CORE; + if (strlen(sess->pdn.apn)) + dl_pdr->apn = ogs_strdup(sess->pdn.apn); + ul_pdr = ogs_pfcp_pdr_add(&qos_flow->pfcp); ogs_assert(ul_pdr); ul_pdr->id = OGS_NEXT_ID(sess->pdr_id, 1, OGS_MAX_NUM_OF_PDR+1); ul_pdr->src_if = OGS_PFCP_INTERFACE_ACCESS; + if (strlen(sess->pdn.apn)) + ul_pdr->apn = ogs_strdup(sess->pdn.apn); + + ul_pdr->outer_header_removal_len = 1; + if (sess->pdn.pdn_type == OGS_GTP_PDN_TYPE_IPV4) { + ul_pdr->outer_header_removal.description = + OGS_PFCP_OUTER_HEADER_REMOVAL_GTPU_UDP_IPV4; + } else if (sess->pdn.pdn_type == OGS_GTP_PDN_TYPE_IPV6) { + ul_pdr->outer_header_removal.description = + OGS_PFCP_OUTER_HEADER_REMOVAL_GTPU_UDP_IPV6; + } else if (sess->pdn.pdn_type == OGS_GTP_PDN_TYPE_IPV4V6) { + ul_pdr->outer_header_removal.description = + OGS_PFCP_OUTER_HEADER_REMOVAL_GTPU_UDP_IP; + } else + ogs_assert_if_reached(); + dl_far = ogs_pfcp_far_add(&qos_flow->pfcp); ogs_assert(dl_far); dl_far->id = OGS_NEXT_ID(sess->far_id, 1, OGS_MAX_NUM_OF_FAR+1); @@ -1125,7 +1148,8 @@ smf_bearer_t *smf_qos_flow_add(smf_sess_t *sess) /* Allocate QFI */ qer->qfi = OGS_NEXT_ID(sess->qos_flow_identifier, 1, OGS_MAX_QOS_FLOW_ID+1); - qos_flow->qfi = qer->qfi; + + qos_flow->qfi = ul_pdr->qfi = qer->qfi; qos_flow->sess = sess; @@ -1174,11 +1198,30 @@ smf_bearer_t *smf_bearer_add(smf_sess_t *sess) dl_pdr->id = OGS_NEXT_ID(sess->pdr_id, 1, OGS_MAX_NUM_OF_PDR+1); dl_pdr->src_if = OGS_PFCP_INTERFACE_CORE; + if (strlen(sess->pdn.apn)) + dl_pdr->apn = ogs_strdup(sess->pdn.apn); + ul_pdr = ogs_pfcp_pdr_add(&bearer->pfcp); ogs_assert(ul_pdr); ul_pdr->id = OGS_NEXT_ID(sess->pdr_id, 1, OGS_MAX_NUM_OF_PDR+1); ul_pdr->src_if = OGS_PFCP_INTERFACE_ACCESS; + if (strlen(sess->pdn.apn)) + ul_pdr->apn = ogs_strdup(sess->pdn.apn); + + ul_pdr->outer_header_removal_len = 1; + if (sess->pdn.pdn_type == OGS_GTP_PDN_TYPE_IPV4) { + ul_pdr->outer_header_removal.description = + OGS_PFCP_OUTER_HEADER_REMOVAL_GTPU_UDP_IPV4; + } else if (sess->pdn.pdn_type == OGS_GTP_PDN_TYPE_IPV6) { + ul_pdr->outer_header_removal.description = + OGS_PFCP_OUTER_HEADER_REMOVAL_GTPU_UDP_IPV6; + } else if (sess->pdn.pdn_type == OGS_GTP_PDN_TYPE_IPV4V6) { + ul_pdr->outer_header_removal.description = + OGS_PFCP_OUTER_HEADER_REMOVAL_GTPU_UDP_IP; + } else + ogs_assert_if_reached(); + dl_far = ogs_pfcp_far_add(&bearer->pfcp); ogs_assert(dl_far); dl_far->id = OGS_NEXT_ID(sess->far_id, 1, OGS_MAX_NUM_OF_FAR+1); @@ -1200,7 +1243,7 @@ smf_bearer_t *smf_bearer_add(smf_sess_t *sess) &bearer->upf_s5u_addr, &bearer->upf_s5u_addr6); ogs_assert(bearer->upf_s5u_addr || bearer->upf_s5u_addr6); if (resource->info.teidri) - bearer->upf_s5u_teid = UPF_GTPU_INDEX_TO_TEID( + bearer->upf_s5u_teid = OGS_PFCP_GTPU_INDEX_TO_TEID( bearer->index, resource->info.teidri, resource->info.teid_range); else @@ -1265,9 +1308,9 @@ smf_bearer_t *smf_bearer_find(uint32_t index) return ogs_pool_find(&smf_bearer_pool, index); } -smf_bearer_t *smf_bearer_find_by_smf_s5u_teid(uint32_t smf_s5u_teid) +smf_bearer_t *smf_bearer_find_by_upf_s5u_teid(uint32_t upf_s5u_teid) { - return smf_bearer_find(smf_s5u_teid); + return smf_bearer_find(upf_s5u_teid); } smf_bearer_t *smf_bearer_find_by_ebi(smf_sess_t *sess, uint8_t ebi) @@ -1397,6 +1440,9 @@ int smf_pf_remove(smf_pf_t *pf) ogs_assert(pf->bearer); ogs_list_remove(&pf->bearer->pf_list, pf); + if (pf->flow_description) + ogs_free(pf->flow_description); + ogs_pool_free(&smf_pf_pool, pf); return OGS_OK; diff --git a/src/smf/context.h b/src/smf/context.h index 6ddefbbb9..67f27f348 100644 --- a/src/smf/context.h +++ b/src/smf/context.h @@ -63,8 +63,6 @@ typedef struct smf_context_s { ogs_sockaddr_t *gtpc_addr; /* SMF GTPC IPv4 Address */ ogs_sockaddr_t *gtpc_addr6; /* SMF GTPC IPv6 Address */ - uint8_t function_features; /* CP Function Features */ - ogs_queue_t *queue; /* Queue for processing SMF control */ ogs_timer_mgr_t *timer_mgr; /* Timer Manager */ ogs_pollset_t *pollset; /* Poll Set for I/O Multiplexing */ @@ -145,20 +143,9 @@ typedef struct smf_sess_s { uint32_t smf_n4_teid; /* SMF-N4-TEID is derived from INDEX */ uint32_t sgw_s5c_teid; /* SGW-S5C-TEID is received from SGW */ -#define SMF_SEID_TO_INDEX(__iNDEX) (__iNDEX & ~0x8000000000000000) -#define SMF_INDEX_TO_SEID(__iNDEX) (__iNDEX | 0x8000000000000000) -#define SMF_EPC_SEID(__sEID) (__sEID & 0x8000000000000000) uint64_t smf_n4_seid; /* SMF SEID is dervied from INDEX */ uint64_t upf_n4_seid; /* UPF SEID is received from Peer */ - /* - * UPF-GTPU-TEID = INDEX | TEID_RANGE - * INDEX = UPF-GTPU-TEID & ~TEID_RANGE - */ -#define UPF_GTPU_TEID_TO_INDEX(__tEID, __iND, __rANGE) \ - (__tEID & ~(__rANGE << (32 - __iND))) -#define UPF_GTPU_INDEX_TO_TEID(__iNDEX, __iND, __rANGE) \ - (__iNDEX | (__rANGE << (32 - __iND))) uint32_t upf_n3_teid; /* UPF-N3 TEID */ ogs_sockaddr_t *upf_n3_addr; /* UPF-N3 IPv4 */ ogs_sockaddr_t *upf_n3_addr6; /* UPF-N3 IPv6 */ @@ -269,13 +256,6 @@ typedef struct smf_bearer_s { /* Packet Filter List */ ogs_list_t pf_list; - struct { - bool create; - bool tft_update; - bool qos_update; - bool remove; - } pfcp_epc_modify; - smf_sess_t *sess; } smf_bearer_t; @@ -285,7 +265,9 @@ typedef struct smf_pf_s { ED3(uint8_t spare:2;, uint8_t direction:2;, uint8_t identifier:4;) - ogs_ipfw_rule_t rule; + + ogs_ipfw_rule_t ipfw_rule; + char *flow_description; smf_bearer_t *bearer; } smf_pf_t; @@ -334,7 +316,7 @@ smf_bearer_t *smf_bearer_add(smf_sess_t *sess); int smf_bearer_remove(smf_bearer_t *bearer); void smf_bearer_remove_all(smf_sess_t *sess); smf_bearer_t *smf_bearer_find(uint32_t index); -smf_bearer_t *smf_bearer_find_by_smf_s5u_teid(uint32_t smf_s5u_teid); +smf_bearer_t *smf_bearer_find_by_upf_s5u_teid(uint32_t upf_s5u_teid); smf_bearer_t *smf_bearer_find_by_ebi(smf_sess_t *sess, uint8_t ebi); smf_bearer_t *smf_bearer_find_by_name(smf_sess_t *sess, char *name); smf_bearer_t *smf_bearer_find_by_qci_arp(smf_sess_t *sess, diff --git a/src/smf/gsm-sm.c b/src/smf/gsm-sm.c index 7f247a1e5..98b14af06 100644 --- a/src/smf/gsm-sm.c +++ b/src/smf/gsm-sm.c @@ -184,7 +184,7 @@ void smf_gsm_state_operational(ogs_fsm_t *s, smf_event_t *e) case OGS_NAS_5GS_PDU_SESSION_RELEASE_REQUEST: smf_5gc_pfcp_send_session_deletion_request( - sess, session, OGS_PFCP_5GC_DELETE_TRIGGER_UE_REQUESTED); + sess, session, OGS_PFCP_DELETE_TRIGGER_UE_REQUESTED); break; case OGS_NAS_5GS_PDU_SESSION_RELEASE_COMPLETE: diff --git a/src/smf/n4-build.c b/src/smf/n4-build.c index f664a3e22..048f5631b 100644 --- a/src/smf/n4-build.c +++ b/src/smf/n4-build.c @@ -20,366 +20,6 @@ #include "context.h" #include "n4-build.h" -ogs_pkbuf_t *smf_n4_build_association_setup_request(uint8_t type) -{ - ogs_pfcp_message_t pfcp_message; - ogs_pfcp_association_setup_request_t *req = NULL; - - ogs_pfcp_node_id_t node_id; - int node_id_len = 0; - - ogs_debug("Association Setup Request"); - - req = &pfcp_message.pfcp_association_setup_request; - memset(&pfcp_message, 0, sizeof(ogs_pfcp_message_t)); - - ogs_pfcp_sockaddr_to_node_id( - ogs_pfcp_self()->pfcp_addr, ogs_pfcp_self()->pfcp_addr6, - ogs_config()->parameter.prefer_ipv4, - &node_id, &node_id_len); - req->node_id.presence = 1; - req->node_id.data = &node_id; - req->node_id.len = node_id_len; - - req->recovery_time_stamp.presence = 1; - req->recovery_time_stamp.u32 = ogs_pfcp_self()->pfcp_started; - - req->cp_function_features.presence = 1; - req->cp_function_features.u8 = smf_self()->function_features; - - pfcp_message.h.type = type; - return ogs_pfcp_build_msg(&pfcp_message); -} - -ogs_pkbuf_t *smf_n4_build_association_setup_response(uint8_t type, - uint8_t cause) -{ - ogs_pfcp_message_t pfcp_message; - ogs_pfcp_association_setup_response_t *rsp = NULL; - - ogs_pfcp_node_id_t node_id; - int node_id_len = 0; - - ogs_debug("Association Setup Response"); - - rsp = &pfcp_message.pfcp_association_setup_response; - memset(&pfcp_message, 0, sizeof(ogs_pfcp_message_t)); - - ogs_pfcp_sockaddr_to_node_id( - ogs_pfcp_self()->pfcp_addr, ogs_pfcp_self()->pfcp_addr6, - ogs_config()->parameter.prefer_ipv4, - &node_id, &node_id_len); - rsp->node_id.presence = 1; - rsp->node_id.data = &node_id; - rsp->node_id.len = node_id_len; - - rsp->cause.presence = 1; - rsp->cause.u8 = cause; - - rsp->recovery_time_stamp.presence = 1; - rsp->recovery_time_stamp.u32 = ogs_pfcp_self()->pfcp_started; - - rsp->cp_function_features.presence = 1; - rsp->cp_function_features.u8 = smf_self()->function_features; - - pfcp_message.h.type = type; - return ogs_pfcp_build_msg(&pfcp_message); -} - -static struct { - ogs_pfcp_outer_header_removal_t outer_header_removal; - ogs_pfcp_f_teid_t f_teid; - char dnn[OGS_MAX_DNN_LEN]; - char *sdf_filter[OGS_MAX_NUM_OF_RULE]; -} pdrbuf[OGS_MAX_NUM_OF_PDR]; - -static void pdrbuf_init(void) -{ - memset(pdrbuf, 0, sizeof(pdrbuf)); -} -static void pdrbuf_clear(void) -{ - int i, j; - for (i = 0; i < OGS_MAX_NUM_OF_PDR; i++) { - for (j = 0; j < OGS_MAX_NUM_OF_RULE; j++) { - if (pdrbuf[i].sdf_filter[j]) - ogs_free(pdrbuf[i].sdf_filter[j]); - } - } -} - -static void build_create_pdr( - ogs_pfcp_tlv_create_pdr_t *message, int i, ogs_pfcp_pdr_t *pdr) -{ - ogs_pfcp_far_t *far = NULL; - ogs_pfcp_sess_t *pfcp_sess = NULL; - smf_sess_t *sess = NULL; - smf_bearer_t *bearer = NULL; - int j = 0; - int len = 0; - - ogs_assert(message); - - ogs_assert(pdr); - pfcp_sess = pdr->sess; - ogs_assert(pfcp_sess); - bearer = SMF_BEARER(pfcp_sess); - ogs_assert(bearer); - sess = bearer->sess; - ogs_assert(sess); - - far = pdr->far; - ogs_assert(far); - - message->presence = 1; - message->pdr_id.presence = 1; - message->pdr_id.u16 = pdr->id; - - message->precedence.presence = 1; - message->precedence.u32 = pdr->precedence; - - message->pdi.presence = 1; - message->pdi.source_interface.presence = 1; - message->pdi.source_interface.u8 = pdr->src_if; - - message->pdi.network_instance.presence = 1; - message->pdi.network_instance.len = ogs_fqdn_build( - pdrbuf[i].dnn, sess->pdn.dnn, strlen(sess->pdn.dnn)); - message->pdi.network_instance.data = pdrbuf[i].dnn; - - for (j = 0; j < pdr->num_of_flow; j++) { - ogs_pfcp_sdf_filter_t pfcp_sdf_filter[OGS_MAX_NUM_OF_RULE]; - pfcp_sdf_filter[j].fd = 1; - pfcp_sdf_filter[j].flow_description_len = - strlen(pdr->flow_description[j]); - pfcp_sdf_filter[j].flow_description = pdr->flow_description[j]; - len = sizeof(ogs_pfcp_sdf_filter_t) + - pfcp_sdf_filter[j].flow_description_len; - - message->pdi.sdf_filter[j].presence = 1; - pdrbuf[i].sdf_filter[j] = ogs_calloc(1, len); - ogs_pfcp_build_sdf_filter(&message->pdi.sdf_filter[j], - &pfcp_sdf_filter[j], pdrbuf[i].sdf_filter[j], len); - } - - if (pdr->src_if == OGS_PFCP_INTERFACE_CORE) { /* Downlink */ - if (pdr->ue_ip_addr_len) { - message->pdi.ue_ip_address.presence = 1; - message->pdi.ue_ip_address.data = &pdr->ue_ip_addr; - message->pdi.ue_ip_address.len = pdr->ue_ip_addr_len; - } - - } else if (pdr->src_if == OGS_PFCP_INTERFACE_ACCESS) { /* Uplink */ - if (pdr->f_teid_len) { - memcpy(&pdrbuf[i].f_teid, &pdr->f_teid, pdr->f_teid_len); - pdrbuf[i].f_teid.teid = htobe32(pdr->f_teid.teid); - - message->pdi.local_f_teid.presence = 1; - message->pdi.local_f_teid.data = &pdrbuf[i].f_teid; - message->pdi.local_f_teid.len = pdr->f_teid_len; - } - - if (sess->pdn.paa.pdn_type == OGS_GTP_PDN_TYPE_IPV4) { - pdrbuf[i].outer_header_removal.description = - OGS_PFCP_OUTER_HEADER_REMOVAL_GTPU_UDP_IPV4; - } else if (sess->pdn.paa.pdn_type == OGS_GTP_PDN_TYPE_IPV6) { - pdrbuf[i].outer_header_removal.description = - OGS_PFCP_OUTER_HEADER_REMOVAL_GTPU_UDP_IPV6; - } else if (sess->pdn.paa.pdn_type == OGS_GTP_PDN_TYPE_IPV4V6) { - pdrbuf[i].outer_header_removal.description = - OGS_PFCP_OUTER_HEADER_REMOVAL_GTPU_UDP_IP; - } else - ogs_assert_if_reached(); - - message->outer_header_removal.presence = 1; - message->outer_header_removal.data = - &pdrbuf[i].outer_header_removal.description; - message->outer_header_removal.len = 1; - } - - if (pdr->far) { - message->far_id.presence = 1; - message->far_id.u32 = pdr->far->id; - } - - if (pdr->urr) { - message->urr_id.presence = 1; - message->urr_id.u32 = pdr->urr->id; - } - - if (pdr->qer) { - message->qer_id.presence = 1; - message->qer_id.u32 = pdr->qer->id; - } -} - -static struct { - ogs_pfcp_outer_header_creation_t outer_header_creation; -} farbuf[OGS_MAX_NUM_OF_FAR]; - -static void build_create_far( - ogs_pfcp_tlv_create_far_t *message, int i, ogs_pfcp_far_t *far) -{ - ogs_pfcp_sess_t *pfcp_sess = NULL; - - ogs_assert(message); - ogs_assert(far); - pfcp_sess = far->sess; - ogs_assert(pfcp_sess); - - message->presence = 1; - message->far_id.presence = 1; - message->far_id.u32 = far->id; - - message->apply_action.presence = 1; - message->apply_action.u8 = far->apply_action; - - message->forwarding_parameters.presence = 1; - message->forwarding_parameters.destination_interface.presence = 1; - message->forwarding_parameters.destination_interface.u8 = - far->dst_if; - - if (far->dst_if == OGS_PFCP_INTERFACE_ACCESS) { /* Downlink */ - if (far->outer_header_creation_len) { - memcpy(&farbuf[i].outer_header_creation, - &far->outer_header_creation, far->outer_header_creation_len); - farbuf[i].outer_header_creation.teid = - htobe32(far->outer_header_creation.teid); - - message->forwarding_parameters.outer_header_creation.presence = 1; - message->forwarding_parameters.outer_header_creation.data = - &farbuf[i].outer_header_creation; - message->forwarding_parameters.outer_header_creation.len = - far->outer_header_creation_len; - } - } -} - -static void build_create_urr( - ogs_pfcp_tlv_create_urr_t *message, int i, ogs_pfcp_urr_t *urr) -{ - ogs_assert(message); - ogs_assert(urr); - - message->presence = 1; - message->urr_id.presence = 1; - message->urr_id.u32 = urr->id; -} - -static struct { - char mbr[OGS_PFCP_BITRATE_LEN]; - char gbr[OGS_PFCP_BITRATE_LEN]; -} create_qer_buf[OGS_MAX_NUM_OF_QER], update_qer_buf[OGS_MAX_NUM_OF_QER]; - -static void build_create_qer( - ogs_pfcp_tlv_create_qer_t *message, int i, ogs_pfcp_qer_t *qer) -{ - ogs_assert(message); - ogs_assert(qer); - - message->presence = 1; - message->qer_id.presence = 1; - message->qer_id.u32 = qer->id; - - message->gate_status.presence = 1; - message->gate_status.u8 = qer->gate_status.value; - - if (qer->mbr.uplink || qer->mbr.downlink) { - message->maximum_bitrate.presence = 1; - ogs_pfcp_build_bitrate( - &message->maximum_bitrate, - &qer->mbr, create_qer_buf[i].mbr, OGS_PFCP_BITRATE_LEN); - } - if (qer->gbr.uplink || qer->gbr.downlink) { - message->guaranteed_bitrate.presence = 1; - ogs_pfcp_build_bitrate( - &message->guaranteed_bitrate, - &qer->gbr, create_qer_buf[i].gbr, OGS_PFCP_BITRATE_LEN); - } - - if (qer->qfi) { - message->qos_flow_identifier.presence = 1; - message->qos_flow_identifier.u8 = qer->qfi; - } -} - -static void build_update_qer( - ogs_pfcp_tlv_update_qer_t *message, int i, ogs_pfcp_qer_t *qer) -{ - ogs_assert(message); - ogs_assert(qer); - - message->presence = 1; - message->qer_id.presence = 1; - message->qer_id.u32 = qer->id; - - if (qer->mbr.uplink || qer->mbr.downlink) { - message->maximum_bitrate.presence = 1; - ogs_pfcp_build_bitrate( - &message->maximum_bitrate, - &qer->mbr, update_qer_buf[i].mbr, OGS_PFCP_BITRATE_LEN); - } - if (qer->gbr.uplink || qer->gbr.downlink) { - message->guaranteed_bitrate.presence = 1; - ogs_pfcp_build_bitrate( - &message->guaranteed_bitrate, - &qer->gbr, update_qer_buf[i].gbr, OGS_PFCP_BITRATE_LEN); - } -} - -static void build_update_dl_far_deactivate( - ogs_pfcp_tlv_update_far_t *message, int i, ogs_pfcp_far_t *far) -{ - ogs_assert(message); - ogs_assert(far); - - if (far->dst_if == OGS_PFCP_INTERFACE_ACCESS) { - message->presence = 1; - message->far_id.presence = 1; - message->far_id.u32 = far->id; - - far->apply_action = - OGS_PFCP_APPLY_ACTION_BUFF | OGS_PFCP_APPLY_ACTION_NOCP; - message->apply_action.presence = 1; - message->apply_action.u8 = far->apply_action; - } -} - -static void build_update_dl_far_activate( - ogs_pfcp_tlv_update_far_t *message, int i, ogs_pfcp_far_t *far) -{ - ogs_assert(message); - ogs_assert(far); - - if (far->dst_if == OGS_PFCP_INTERFACE_ACCESS) { - ogs_assert(far->outer_header_creation_len); - - message->presence = 1; - message->far_id.presence = 1; - message->far_id.u32 = far->id; - - if (far->apply_action != OGS_PFCP_APPLY_ACTION_FORW) { - far->apply_action = OGS_PFCP_APPLY_ACTION_FORW; - - message->apply_action.presence = 1; - message->apply_action.u8 = far->apply_action; - } - - memcpy(&farbuf[i].outer_header_creation, - &far->outer_header_creation, far->outer_header_creation_len); - farbuf[i].outer_header_creation.teid = - htobe32(far->outer_header_creation.teid); - - message->update_forwarding_parameters.presence = 1; - message->update_forwarding_parameters. - outer_header_creation.presence = 1; - message->update_forwarding_parameters. - outer_header_creation.data = &farbuf[i].outer_header_creation; - message->update_forwarding_parameters. - outer_header_creation.len = far->outer_header_creation_len; - } -} - ogs_pkbuf_t *smf_n4_build_session_establishment_request( uint8_t type, smf_sess_t *sess) { @@ -423,13 +63,13 @@ ogs_pkbuf_t *smf_n4_build_session_establishment_request( req->cp_f_seid.data = &f_seid; req->cp_f_seid.len = len; - pdrbuf_init(); + ogs_pfcp_pdrbuf_init(); /* Create PDR */ i = 0; ogs_list_for_each(&sess->bearer_list, bearer) { ogs_list_for_each(&bearer->pfcp.pdr_list, pdr) { - build_create_pdr(&req->create_pdr[i], i, pdr); + ogs_pfcp_build_create_pdr(&req->create_pdr[i], i, pdr); i++; } } @@ -438,7 +78,7 @@ ogs_pkbuf_t *smf_n4_build_session_establishment_request( i = 0; ogs_list_for_each(&sess->bearer_list, bearer) { ogs_list_for_each(&bearer->pfcp.far_list, far) { - build_create_far(&req->create_far[i], i, far); + ogs_pfcp_build_create_far(&req->create_far[i], i, far); i++; } } @@ -447,7 +87,7 @@ ogs_pkbuf_t *smf_n4_build_session_establishment_request( i = 0; ogs_list_for_each(&sess->bearer_list, bearer) { ogs_list_for_each(&bearer->pfcp.urr_list, urr) { - build_create_urr(&req->create_urr[i], i, urr); + ogs_pfcp_build_create_urr(&req->create_urr[i], i, urr); i++; } } @@ -456,7 +96,7 @@ ogs_pkbuf_t *smf_n4_build_session_establishment_request( i = 0; ogs_list_for_each(&sess->bearer_list, bearer) { ogs_list_for_each(&bearer->pfcp.qer_list, qer) { - build_create_qer(&req->create_qer[i], i, qer); + ogs_pfcp_build_create_qer(&req->create_qer[i], i, qer); i++; } } @@ -468,47 +108,12 @@ ogs_pkbuf_t *smf_n4_build_session_establishment_request( pfcp_message.h.type = type; pkbuf = ogs_pfcp_build_msg(&pfcp_message); - pdrbuf_clear(); + ogs_pfcp_pdrbuf_clear(); return pkbuf; } -ogs_pkbuf_t *smf_5gc_n4_build_session_modification_request( - uint8_t type, smf_sess_t *sess, uint64_t modify_flags) -{ - int i; - - ogs_pfcp_message_t pfcp_message; - ogs_pfcp_session_modification_request_t *req = NULL; - ogs_pfcp_far_t *far = NULL; - ogs_pkbuf_t *pkbuf = NULL; - smf_bearer_t *bearer = NULL; - - ogs_debug("Session Modification Request"); - ogs_assert(sess); - ogs_assert(modify_flags); - - req = &pfcp_message.pfcp_session_modification_request; - memset(&pfcp_message, 0, sizeof(ogs_pfcp_message_t)); - - if (modify_flags & OGS_PFCP_5GC_MODIFY_DEACTIVATE) { - /* Update FAR - Only DL */ - i = 0; - ogs_list_for_each(&sess->bearer_list, bearer) { - ogs_list_for_each(&bearer->pfcp.far_list, far) { - build_update_dl_far_deactivate(&req->update_far[i], i, far); - i++; - } - } - } - - pfcp_message.h.type = type; - pkbuf = ogs_pfcp_build_msg(&pfcp_message); - - return pkbuf; -} - -ogs_pkbuf_t *smf_5gc_n4_build_qos_flow_modification_request( +ogs_pkbuf_t *smf_n4_build_qos_flow_modification_request( uint8_t type, smf_bearer_t *qos_flow, uint64_t modify_flags) { ogs_pfcp_message_t pfcp_message; @@ -530,7 +135,7 @@ ogs_pkbuf_t *smf_5gc_n4_build_qos_flow_modification_request( req = &pfcp_message.pfcp_session_modification_request; memset(&pfcp_message, 0, sizeof(ogs_pfcp_message_t)); - if (modify_flags & OGS_PFCP_5GC_MODIFY_REMOVE) { + if (modify_flags & OGS_PFCP_MODIFY_REMOVE) { /* Remove PDR */ i = 0; ogs_list_for_each(&qos_flow->pfcp.pdr_list, pdr) { @@ -564,43 +169,64 @@ ogs_pkbuf_t *smf_5gc_n4_build_qos_flow_modification_request( i++; } } else { - if (modify_flags & OGS_PFCP_5GC_MODIFY_CREATE) { - pdrbuf_init(); + if (modify_flags & OGS_PFCP_MODIFY_CREATE) { + ogs_pfcp_pdrbuf_init(); /* Create PDR */ i = 0; ogs_list_for_each(&qos_flow->pfcp.pdr_list, pdr) { - build_create_pdr(&req->create_pdr[i], i, pdr); + ogs_pfcp_build_create_pdr(&req->create_pdr[i], i, pdr); i++; } /* Create FAR */ i = 0; ogs_list_for_each(&qos_flow->pfcp.far_list, far) { - build_create_far(&req->create_far[i], i, far); + ogs_pfcp_build_create_far(&req->create_far[i], i, far); i++; } /* Create QER */ i = 0; ogs_list_for_each(&qos_flow->pfcp.qer_list, qer) { - build_create_qer(&req->create_qer[i], i, qer); + ogs_pfcp_build_create_qer(&req->create_qer[i], i, qer); i++; } } - if (modify_flags & OGS_PFCP_5GC_MODIFY_QOS_UPDATE) { - /* Update QER */ + if (modify_flags & OGS_PFCP_MODIFY_TFT_UPDATE) { + ogs_pfcp_pdrbuf_init(); + + /* Update PDR */ i = 0; - ogs_list_for_each(&qos_flow->pfcp.qer_list, qer) { - build_update_qer(&req->update_qer[i], i, qer); + ogs_list_for_each(&qos_flow->pfcp.pdr_list, pdr) { + ogs_pfcp_build_update_pdr(&req->update_pdr[i], i, pdr); i++; } } - if (modify_flags & OGS_PFCP_5GC_MODIFY_ACTIVATE) { + if (modify_flags & OGS_PFCP_MODIFY_ACTIVATE) { /* Update FAR - Only DL */ i = 0; ogs_list_for_each(&qos_flow->pfcp.far_list, far) { - build_update_dl_far_activate(&req->update_far[i], i, far); + if (far->dst_if == OGS_PFCP_INTERFACE_ACCESS) { + + if (modify_flags & OGS_PFCP_MODIFY_END_MARKER) { + far->smreq_flags.send_end_marker_packets = 1; + } + + ogs_pfcp_build_update_far_activate( + &req->update_far[i], i, far); + i++; + + /* Clear all FAR flags */ + far->smreq_flags.value = 0; + } + } + } + if (modify_flags & OGS_PFCP_MODIFY_QOS_UPDATE) { + /* Update QER */ + i = 0; + ogs_list_for_each(&qos_flow->pfcp.qer_list, qer) { + ogs_pfcp_build_update_qer(&req->update_qer[i], i, qer); i++; } } @@ -609,8 +235,8 @@ ogs_pkbuf_t *smf_5gc_n4_build_qos_flow_modification_request( pfcp_message.h.type = type; pkbuf = ogs_pfcp_build_msg(&pfcp_message); - if (modify_flags & OGS_PFCP_5GC_MODIFY_CREATE) { - pdrbuf_clear(); + if (modify_flags & (OGS_PFCP_MODIFY_CREATE|OGS_PFCP_MODIFY_TFT_UPDATE)) { + ogs_pfcp_pdrbuf_clear(); } return pkbuf; @@ -628,98 +254,31 @@ ogs_pkbuf_t *smf_n4_build_session_deletion_request( return ogs_pfcp_build_msg(&pfcp_message); } -ogs_pkbuf_t *smf_epc_n4_build_session_modification_request( - uint8_t type, smf_bearer_t *bearer) +ogs_pkbuf_t *smf_5gc_n4_build_session_modification_request( + uint8_t type, smf_sess_t *sess, uint64_t modify_flags) { - ogs_pfcp_message_t pfcp_message; - ogs_pfcp_session_modification_request_t *req = NULL; - ogs_pfcp_pdr_t *pdr = NULL; - ogs_pfcp_far_t *far = NULL; - ogs_pfcp_qer_t *qer = NULL; - ogs_pkbuf_t *pkbuf = NULL; int i; - smf_sess_t *sess = NULL; + ogs_pfcp_message_t pfcp_message; + ogs_pfcp_session_modification_request_t *req = NULL; + ogs_pfcp_far_t *far = NULL; + ogs_pkbuf_t *pkbuf = NULL; + smf_bearer_t *bearer = NULL; ogs_debug("Session Modification Request"); - ogs_assert(bearer); - sess = bearer->sess; ogs_assert(sess); + ogs_assert(modify_flags & OGS_PFCP_MODIFY_DEACTIVATE); req = &pfcp_message.pfcp_session_modification_request; memset(&pfcp_message, 0, sizeof(ogs_pfcp_message_t)); - if (!bearer->pfcp_epc_modify.create && - !bearer->pfcp_epc_modify.tft_update && - !bearer->pfcp_epc_modify.qos_update && - !bearer->pfcp_epc_modify.remove) { - ogs_error("No Session Modification"); - return NULL; - } - - if (bearer->pfcp_epc_modify.remove) { - /* Remove PDR */ - i = 0; - ogs_list_for_each(&bearer->pfcp.pdr_list, pdr) { - ogs_pfcp_tlv_remove_pdr_t *message = &req->remove_pdr[i]; - - message->presence = 1; - message->pdr_id.presence = 1; - message->pdr_id.u16 = pdr->id; - i++; - } - - /* Remove FAR */ - i = 0; + i = 0; + ogs_list_for_each(&sess->bearer_list, bearer) { ogs_list_for_each(&bearer->pfcp.far_list, far) { - ogs_pfcp_tlv_remove_far_t *message = &req->remove_far[i]; - - message->presence = 1; - message->far_id.presence = 1; - message->far_id.u32 = far->id; - i++; - } - - /* Remove QER */ - i = 0; - ogs_list_for_each(&bearer->pfcp.qer_list, qer) { - ogs_pfcp_tlv_remove_qer_t *message = &req->remove_qer[i]; - - message->presence = 1; - message->qer_id.presence = 1; - message->qer_id.u32 = qer->id; - i++; - } - } else { - if (bearer->pfcp_epc_modify.create) { - pdrbuf_init(); - - /* Create PDR */ - i = 0; - ogs_list_for_each(&bearer->pfcp.pdr_list, pdr) { - build_create_pdr(&req->create_pdr[i], i, pdr); - i++; - } - - /* Create FAR */ - i = 0; - ogs_list_for_each(&bearer->pfcp.far_list, far) { - build_create_far(&req->create_far[i], i, far); - i++; - } - - /* Create QER */ - i = 0; - ogs_list_for_each(&bearer->pfcp.qer_list, qer) { - build_create_qer(&req->create_qer[i], i, qer); - i++; - } - } - if (bearer->pfcp_epc_modify.qos_update) { - /* Update QER */ - i = 0; - ogs_list_for_each(&bearer->pfcp.qer_list, qer) { - build_update_qer(&req->update_qer[i], i, qer); + /* Update FAR - Only DL */ + if (far->dst_if == OGS_PFCP_INTERFACE_ACCESS) { + ogs_pfcp_build_update_far_deactivate( + &req->update_far[i], i, far); i++; } } @@ -728,9 +287,5 @@ ogs_pkbuf_t *smf_epc_n4_build_session_modification_request( pfcp_message.h.type = type; pkbuf = ogs_pfcp_build_msg(&pfcp_message); - if (bearer->pfcp_epc_modify.create) { - pdrbuf_clear(); - } - return pkbuf; } diff --git a/src/smf/n4-build.h b/src/smf/n4-build.h index f807c6d36..8f36c0973 100644 --- a/src/smf/n4-build.h +++ b/src/smf/n4-build.h @@ -26,23 +26,15 @@ extern "C" { #endif -ogs_pkbuf_t *smf_n4_build_association_setup_request(uint8_t type); -ogs_pkbuf_t *smf_n4_build_association_setup_response(uint8_t type, - uint8_t cause); - ogs_pkbuf_t *smf_n4_build_session_establishment_request( uint8_t type, smf_sess_t *sess); +ogs_pkbuf_t *smf_n4_build_qos_flow_modification_request( + uint8_t type, smf_bearer_t *qos_flow, uint64_t modify_flags); +ogs_pkbuf_t *smf_n4_build_session_deletion_request( + uint8_t type, smf_sess_t *sess); ogs_pkbuf_t *smf_5gc_n4_build_session_modification_request( uint8_t type, smf_sess_t *sess, uint64_t modify_flags); -ogs_pkbuf_t *smf_5gc_n4_build_qos_flow_modification_request( - uint8_t type, smf_bearer_t *qos_flow, uint64_t modify_flags); - -ogs_pkbuf_t *smf_n4_build_session_deletion_request( - uint8_t type, smf_sess_t *sess); - -ogs_pkbuf_t *smf_epc_n4_build_session_modification_request( - uint8_t type, smf_bearer_t *bearer); #ifdef __cplusplus } diff --git a/src/smf/n4-handler.c b/src/smf/n4-handler.c index ab45c6a7b..94cdb38cb 100644 --- a/src/smf/n4-handler.c +++ b/src/smf/n4-handler.c @@ -95,80 +95,6 @@ static int sbi_status_from_pfcp(uint8_t pfcp_cause) return OGS_SBI_HTTP_STATUS_INTERNAL_SERVER_ERROR; } -void smf_n4_handle_association_setup_request( - ogs_pfcp_node_t *node, ogs_pfcp_xact_t *xact, - ogs_pfcp_association_setup_request_t *req) -{ - int i; - - ogs_assert(xact); - ogs_assert(node); - ogs_assert(req); - - smf_pfcp_send_association_setup_response( - xact, OGS_PFCP_CAUSE_REQUEST_ACCEPTED); - - ogs_pfcp_gtpu_resource_remove_all(&node->gtpu_resource_list); - - for (i = 0; i < OGS_MAX_NUM_OF_GTPU_RESOURCE; i++) { - ogs_pfcp_tlv_user_plane_ip_resource_information_t *message = - &req->user_plane_ip_resource_information[i]; - ogs_pfcp_user_plane_ip_resource_info_t info; - - if (message->presence == 0) - break; - - ogs_pfcp_parse_user_plane_ip_resource_info(&info, message); - ogs_pfcp_gtpu_resource_add(&node->gtpu_resource_list, &info); - } -} - -void smf_n4_handle_association_setup_response( - ogs_pfcp_node_t *node, ogs_pfcp_xact_t *xact, - ogs_pfcp_association_setup_response_t *rsp) -{ - int i; - - ogs_assert(xact); - ogs_pfcp_xact_commit(xact); - - ogs_assert(node); - ogs_assert(rsp); - - ogs_pfcp_gtpu_resource_remove_all(&node->gtpu_resource_list); - - for (i = 0; i < OGS_MAX_NUM_OF_GTPU_RESOURCE; i++) { - ogs_pfcp_tlv_user_plane_ip_resource_information_t *message = - &rsp->user_plane_ip_resource_information[i]; - ogs_pfcp_user_plane_ip_resource_info_t info; - - if (message->presence == 0) - break; - - ogs_pfcp_parse_user_plane_ip_resource_info(&info, message); - ogs_pfcp_gtpu_resource_add(&node->gtpu_resource_list, &info); - } -} - -void smf_n4_handle_heartbeat_request( - ogs_pfcp_node_t *node, ogs_pfcp_xact_t *xact, - ogs_pfcp_heartbeat_request_t *req) -{ - ogs_assert(xact); - ogs_pfcp_send_heartbeat_response(xact); -} - -void smf_n4_handle_heartbeat_response( - ogs_pfcp_node_t *node, ogs_pfcp_xact_t *xact, - ogs_pfcp_heartbeat_response_t *rsp) -{ - ogs_assert(xact); - ogs_pfcp_xact_commit(xact); - - ogs_timer_start(node->t_no_heartbeat, - ogs_config()->time.message.pfcp.no_heartbeat_duration); -} - void smf_5gc_n4_handle_session_establishment_response( smf_sess_t *sess, ogs_pfcp_xact_t *xact, ogs_pfcp_session_establishment_response_t *rsp) @@ -266,11 +192,11 @@ void smf_5gc_n4_handle_session_modification_response( ogs_assert(sess); - if (flags & OGS_PFCP_5GC_MODIFY_ACTIVATE) { + if (flags & OGS_PFCP_MODIFY_ACTIVATE) { /* ACTIVATED Is NOT Inlcuded in RESPONSE */ smf_sbi_send_sm_context_updated_data(sess, session, 0); - } else if (flags & OGS_PFCP_5GC_MODIFY_DEACTIVATE) { + } else if (flags & OGS_PFCP_MODIFY_DEACTIVATE) { /* Only ACTIVING & DEACTIVATED is Included */ smf_sbi_send_sm_context_updated_data( sess, session, OpenAPI_up_cnx_state_DEACTIVATED); @@ -326,7 +252,7 @@ void smf_5gc_n4_handle_session_deletion_response( ogs_assert(sess); - if (trigger == OGS_PFCP_5GC_DELETE_TRIGGER_UE_REQUESTED) { + if (trigger == OGS_PFCP_DELETE_TRIGGER_UE_REQUESTED) { smf_sbi_send_sm_context_updated_data_in_session_deletion(sess, session); @@ -404,16 +330,19 @@ void smf_epc_n4_handle_session_modification_response( ogs_pfcp_session_modification_response_t *rsp) { smf_bearer_t *bearer = NULL; + uint64_t flags = 0; ogs_assert(xact); ogs_assert(rsp); bearer = xact->data; ogs_assert(bearer); + flags = xact->modify_flags; + ogs_assert(flags); ogs_pfcp_xact_commit(xact); - if (bearer->pfcp_epc_modify.remove) + if (flags & OGS_PFCP_MODIFY_REMOVE) smf_bearer_remove(bearer); } diff --git a/src/smf/n4-handler.h b/src/smf/n4-handler.h index cfcbab68e..0696ac3f0 100644 --- a/src/smf/n4-handler.h +++ b/src/smf/n4-handler.h @@ -17,8 +17,8 @@ * along with this program. If not, see . */ -#ifndef UPF_N4_HANDLER_H -#define UPF_N4_HANDLER_H +#ifndef SMF_N4_HANDLER_H +#define SMF_N4_HANDLER_H #include "ogs-gtp.h" @@ -26,19 +26,6 @@ extern "C" { #endif -void smf_n4_handle_association_setup_request( - ogs_pfcp_node_t *node, ogs_pfcp_xact_t *xact, - ogs_pfcp_association_setup_request_t *req); -void smf_n4_handle_association_setup_response( - ogs_pfcp_node_t *node, ogs_pfcp_xact_t *xact, - ogs_pfcp_association_setup_response_t *req); -void smf_n4_handle_heartbeat_request( - ogs_pfcp_node_t *node, ogs_pfcp_xact_t *xact, - ogs_pfcp_heartbeat_request_t *req); -void smf_n4_handle_heartbeat_response( - ogs_pfcp_node_t *node, ogs_pfcp_xact_t *xact, - ogs_pfcp_heartbeat_response_t *req); - void smf_5gc_n4_handle_session_establishment_response( smf_sess_t *sess, ogs_pfcp_xact_t *xact, ogs_pfcp_session_establishment_response_t *rsp); @@ -63,4 +50,4 @@ void smf_epc_n4_handle_session_deletion_response( } #endif -#endif /* UPF_N4_HANDLER_H */ +#endif /* SMF_N4_HANDLER_H */ diff --git a/src/smf/ngap-handler.c b/src/smf/ngap-handler.c index 24ffccad4..074f298ed 100644 --- a/src/smf/ngap-handler.c +++ b/src/smf/ngap-handler.c @@ -140,7 +140,7 @@ int ngap_handle_pdu_session_resource_setup_response_transfer( if (far_update) { smf_5gc_pfcp_send_qos_flow_modification_request( - qos_flow, session, OGS_PFCP_5GC_MODIFY_ACTIVATE); + qos_flow, session, OGS_PFCP_MODIFY_ACTIVATE); } else { /* ACTIVATED Is NOT Inlcuded in RESPONSE */ smf_sbi_send_sm_context_updated_data(sess, session, 0); diff --git a/src/smf/nsmf-handler.c b/src/smf/nsmf-handler.c index e5a80c171..7c004a5ec 100644 --- a/src/smf/nsmf-handler.c +++ b/src/smf/nsmf-handler.c @@ -349,7 +349,7 @@ bool smf_nsmf_handle_update_sm_context( * Handle DEACTIVATED ********************************************************/ smf_5gc_pfcp_send_session_modification_request( - sess, session, OGS_PFCP_5GC_MODIFY_DEACTIVATE); + sess, session, OGS_PFCP_MODIFY_DEACTIVATE); } else if (SmContextUpdateData->up_cnx_state == OpenAPI_up_cnx_state_ACTIVATING) { @@ -483,7 +483,7 @@ bool smf_nsmf_handle_release_sm_context( } smf_5gc_pfcp_send_session_deletion_request(sess, session, - OGS_PFCP_5GC_DELETE_TRIGGER_AMF_RELEASE_SM_CONTEXT); + OGS_PFCP_DELETE_TRIGGER_AMF_RELEASE_SM_CONTEXT); return true; } diff --git a/src/smf/nudm-handler.c b/src/smf/nudm-handler.c index 1a1b064ab..74c8c2a84 100644 --- a/src/smf/nudm-handler.c +++ b/src/smf/nudm-handler.c @@ -243,7 +243,7 @@ bool smf_nudm_sdm_handle_get(smf_sess_t *sess, ogs_sbi_session_t *session, &sess->upf_n3_addr, &sess->upf_n3_addr6); ogs_assert(sess->upf_n3_addr || sess->upf_n3_addr6); if (resource->info.teidri) - sess->upf_n3_teid = UPF_GTPU_INDEX_TO_TEID( + sess->upf_n3_teid = OGS_PFCP_GTPU_INDEX_TO_TEID( sess->index, resource->info.teidri, resource->info.teid_range); else diff --git a/src/smf/pfcp-path.c b/src/smf/pfcp-path.c index 14eb555cb..75027dce6 100644 --- a/src/smf/pfcp-path.c +++ b/src/smf/pfcp-path.c @@ -102,9 +102,9 @@ static void pfcp_recv_cb(short when, ogs_socket_t fd, void *data) e = smf_event_new(SMF_EVT_N4_MESSAGE); ogs_assert(e); - node = ogs_pfcp_node_find(&ogs_pfcp_self()->n4_list, &from); + node = ogs_pfcp_node_find(&ogs_pfcp_self()->peer_list, &from); if (!node) { - node = ogs_pfcp_node_add(&ogs_pfcp_self()->n4_list, &from); + node = ogs_pfcp_node_add(&ogs_pfcp_self()->peer_list, &from); ogs_assert(node); node->sock = data; @@ -155,7 +155,7 @@ int smf_pfcp_open(void) ogs_assert(ogs_pfcp_self()->pfcp_addr || ogs_pfcp_self()->pfcp_addr6); - ogs_list_for_each(&ogs_pfcp_self()->n4_list, pfcp_node) + ogs_list_for_each(&ogs_pfcp_self()->peer_list, pfcp_node) pfcp_node_fsm_init(pfcp_node, true); return OGS_OK; @@ -165,44 +165,13 @@ void smf_pfcp_close(void) { ogs_pfcp_node_t *pfcp_node = NULL; - ogs_list_for_each(&ogs_pfcp_self()->n4_list, pfcp_node) + ogs_list_for_each(&ogs_pfcp_self()->peer_list, pfcp_node) pfcp_node_fsm_fini(pfcp_node); ogs_socknode_remove_all(&ogs_pfcp_self()->pfcp_list); ogs_socknode_remove_all(&ogs_pfcp_self()->pfcp_list6); } -static void timeout(ogs_pfcp_xact_t *xact, void *data) -{ - int rv; - - smf_event_t *e = NULL; - uint8_t type; - - ogs_assert(xact); - type = xact->seq[0].type; - - switch (type) { - case OGS_PFCP_HEARTBEAT_REQUEST_TYPE: - ogs_assert(data); - - e = smf_event_new(SMF_EVT_N4_NO_HEARTBEAT); - e->pfcp_node = data; - - rv = ogs_queue_push(smf_self()->queue, e); - if (rv != OGS_OK) { - ogs_warn("ogs_queue_push() failed:%d", (int)rv); - smf_event_free(e); - } - break; - case OGS_PFCP_ASSOCIATION_SETUP_REQUEST_TYPE: - break; - default: - ogs_error("Not implemented [type:%d]", type); - break; - } -} - static void sess_5gc_timeout(ogs_pfcp_xact_t *xact, void *data) { smf_ue_t *smf_ue = NULL; @@ -269,75 +238,6 @@ static void sess_epc_timeout(ogs_pfcp_xact_t *xact, void *data) } } -void smf_pfcp_send_association_setup_request(ogs_pfcp_node_t *node) -{ - int rv; - ogs_pkbuf_t *n4buf = NULL; - ogs_pfcp_header_t h; - ogs_pfcp_xact_t *xact = NULL; - - ogs_assert(node); - - memset(&h, 0, sizeof(ogs_pfcp_header_t)); - h.type = OGS_PFCP_ASSOCIATION_SETUP_REQUEST_TYPE; - h.seid = 0; - - n4buf = smf_n4_build_association_setup_request(h.type); - ogs_expect_or_return(n4buf); - - xact = ogs_pfcp_xact_local_create(node, &h, n4buf, timeout, node); - ogs_expect_or_return(xact); - - rv = ogs_pfcp_xact_commit(xact); - ogs_expect(rv == OGS_OK); -} - -void smf_pfcp_send_association_setup_response(ogs_pfcp_xact_t *xact, - uint8_t cause) -{ - int rv; - ogs_pkbuf_t *n4buf = NULL; - ogs_pfcp_header_t h; - - ogs_assert(xact); - - memset(&h, 0, sizeof(ogs_pfcp_header_t)); - h.type = OGS_PFCP_ASSOCIATION_SETUP_RESPONSE_TYPE; - h.seid = 0; - - n4buf = smf_n4_build_association_setup_response(h.type, cause); - ogs_expect_or_return(n4buf); - - rv = ogs_pfcp_xact_update_tx(xact, &h, n4buf); - ogs_expect_or_return(rv == OGS_OK); - - rv = ogs_pfcp_xact_commit(xact); - ogs_expect(rv == OGS_OK); -} - -void smf_pfcp_send_heartbeat_request(ogs_pfcp_node_t *node) -{ - int rv; - ogs_pkbuf_t *n4buf = NULL; - ogs_pfcp_header_t h; - ogs_pfcp_xact_t *xact = NULL; - - ogs_assert(node); - - memset(&h, 0, sizeof(ogs_pfcp_header_t)); - h.type = OGS_PFCP_HEARTBEAT_REQUEST_TYPE; - h.seid = 0; - - n4buf = ogs_pfcp_n4_build_heartbeat_request(h.type); - ogs_expect_or_return(n4buf); - - xact = ogs_pfcp_xact_local_create(node, &h, n4buf, timeout, node); - ogs_expect_or_return(xact); - - rv = ogs_pfcp_xact_commit(xact); - ogs_expect(rv == OGS_OK); -} - void smf_5gc_pfcp_send_session_establishment_request( smf_sess_t *sess, ogs_sbi_session_t *session) { @@ -386,6 +286,39 @@ void smf_5gc_pfcp_send_session_modification_request( xact = ogs_pfcp_xact_local_create( sess->pfcp_node, &h, n4buf, sess_5gc_timeout, sess); ogs_expect_or_return(xact); + xact->assoc_session = session; + xact->modify_flags = flags | OGS_PFCP_MODIFY_SESSION; + + rv = ogs_pfcp_xact_commit(xact); + ogs_expect(rv == OGS_OK); +} + +void smf_5gc_pfcp_send_qos_flow_modification_request(smf_bearer_t *qos_flow, + ogs_sbi_session_t *session, uint64_t flags) +{ + int rv; + ogs_pkbuf_t *n4buf = NULL; + ogs_pfcp_header_t h; + ogs_pfcp_xact_t *xact = NULL; + smf_sess_t *sess = NULL; + + ogs_assert(qos_flow); + sess = qos_flow->sess; + ogs_assert(sess); + + ogs_assert(session); + + memset(&h, 0, sizeof(ogs_pfcp_header_t)); + h.type = OGS_PFCP_SESSION_MODIFICATION_REQUEST_TYPE; + h.seid = sess->upf_n4_seid; + + n4buf = smf_n4_build_qos_flow_modification_request(h.type, qos_flow, flags); + ogs_expect_or_return(n4buf); + + xact = ogs_pfcp_xact_local_create( + sess->pfcp_node, &h, n4buf, sess_5gc_timeout, qos_flow); + ogs_expect_or_return(xact); + xact->assoc_session = session; xact->modify_flags = flags; @@ -442,13 +375,16 @@ void smf_epc_pfcp_send_session_establishment_request( xact = ogs_pfcp_xact_local_create( sess->pfcp_node, &h, n4buf, sess_epc_timeout, sess); ogs_expect_or_return(xact); + + xact->epc = true; /* EPC PFCP transaction */ xact->assoc_xact = gtp_xact; rv = ogs_pfcp_xact_commit(xact); ogs_expect(rv == OGS_OK); } -void smf_epc_pfcp_send_session_modification_request(smf_bearer_t *bearer) +void smf_epc_pfcp_send_bearer_modification_request( + smf_bearer_t *bearer, uint64_t flags) { int rv; ogs_pkbuf_t *n4buf = NULL; @@ -464,13 +400,16 @@ void smf_epc_pfcp_send_session_modification_request(smf_bearer_t *bearer) h.type = OGS_PFCP_SESSION_MODIFICATION_REQUEST_TYPE; h.seid = sess->upf_n4_seid; - n4buf = smf_epc_n4_build_session_modification_request(h.type, bearer); + n4buf = smf_n4_build_qos_flow_modification_request(h.type, bearer, flags); ogs_expect_or_return(n4buf); xact = ogs_pfcp_xact_local_create( sess->pfcp_node, &h, n4buf, sess_epc_timeout, bearer); ogs_expect_or_return(xact); + xact->epc = true; /* EPC PFCP transaction */ + xact->modify_flags = flags; + rv = ogs_pfcp_xact_commit(xact); ogs_expect(rv == OGS_OK); } @@ -495,42 +434,10 @@ void smf_epc_pfcp_send_session_deletion_request( xact = ogs_pfcp_xact_local_create( sess->pfcp_node, &h, n4buf, sess_epc_timeout, sess); ogs_expect_or_return(xact); + + xact->epc = true; /* EPC PFCP transaction */ xact->assoc_xact = gtp_xact; rv = ogs_pfcp_xact_commit(xact); ogs_expect(rv == OGS_OK); } - -void smf_5gc_pfcp_send_qos_flow_modification_request(smf_bearer_t *qos_flow, - ogs_sbi_session_t *session, uint64_t flags) -{ - int rv; - ogs_pkbuf_t *n4buf = NULL; - ogs_pfcp_header_t h; - ogs_pfcp_xact_t *xact = NULL; - smf_sess_t *sess = NULL; - - ogs_assert(qos_flow); - sess = qos_flow->sess; - ogs_assert(sess); - - ogs_assert(session); - - memset(&h, 0, sizeof(ogs_pfcp_header_t)); - h.type = OGS_PFCP_SESSION_MODIFICATION_REQUEST_TYPE; - h.seid = sess->upf_n4_seid; - - n4buf = smf_5gc_n4_build_qos_flow_modification_request( - h.type, qos_flow, flags); - ogs_expect_or_return(n4buf); - - xact = ogs_pfcp_xact_local_create( - sess->pfcp_node, &h, n4buf, sess_5gc_timeout, qos_flow); - ogs_expect_or_return(xact); - - xact->assoc_session = session; - xact->modify_flags = flags; - - rv = ogs_pfcp_xact_commit(xact); - ogs_expect(rv == OGS_OK); -} diff --git a/src/smf/pfcp-path.h b/src/smf/pfcp-path.h index 452513b3a..51bfc1a96 100644 --- a/src/smf/pfcp-path.h +++ b/src/smf/pfcp-path.h @@ -29,27 +29,22 @@ extern "C" { int smf_pfcp_open(void); void smf_pfcp_close(void); -void smf_pfcp_send_association_setup_request(ogs_pfcp_node_t *node); -void smf_pfcp_send_association_setup_response(ogs_pfcp_xact_t *xact, - uint8_t cause); -void smf_pfcp_send_heartbeat_request(ogs_pfcp_node_t *node); - void smf_5gc_pfcp_send_session_establishment_request( smf_sess_t *sess, ogs_sbi_session_t *session); void smf_5gc_pfcp_send_session_modification_request( smf_sess_t *sess, ogs_sbi_session_t *session, uint64_t flags); +void smf_5gc_pfcp_send_qos_flow_modification_request(smf_bearer_t *qos_flow, + ogs_sbi_session_t *session, uint64_t flags); void smf_5gc_pfcp_send_session_deletion_request( smf_sess_t *sess, ogs_sbi_session_t *session, int trigger); void smf_epc_pfcp_send_session_establishment_request( smf_sess_t *sess, void *gtp_xact); -void smf_epc_pfcp_send_session_modification_request(smf_bearer_t *bearer); +void smf_epc_pfcp_send_bearer_modification_request( + smf_bearer_t *bearer, uint64_t flags); void smf_epc_pfcp_send_session_deletion_request( smf_sess_t *sess, void *gtp_xact); -void smf_5gc_pfcp_send_qos_flow_modification_request(smf_bearer_t *qos_flow, - ogs_sbi_session_t *session, uint64_t flags); - #ifdef __cplusplus } #endif diff --git a/src/smf/pfcp-sm.c b/src/smf/pfcp-sm.c index d9a0dfdec..1500be637 100644 --- a/src/smf/pfcp-sm.c +++ b/src/smf/pfcp-sm.c @@ -17,12 +17,13 @@ * along with this program. If not, see . */ -#include "context.h" -#include "timer.h" - +#include "sbi-path.h" #include "pfcp-path.h" + #include "n4-handler.h" +static void node_timeout(ogs_pfcp_xact_t *xact, void *data); + void smf_pfcp_state_initial(ogs_fsm_t *s, smf_event_t *e) { int rv; @@ -87,7 +88,7 @@ void smf_pfcp_state_will_associate(ogs_fsm_t *s, smf_event_t *e) ogs_timer_start(node->t_association, ogs_config()->time.message.pfcp.association_interval); - smf_pfcp_send_association_setup_request(node); + ogs_pfcp_cp_send_association_setup_request(node, node_timeout); } break; @@ -110,7 +111,7 @@ void smf_pfcp_state_will_associate(ogs_fsm_t *s, smf_event_t *e) ogs_timer_start(node->t_association, ogs_config()->time.message.pfcp.association_interval); - smf_pfcp_send_association_setup_request(node); + ogs_pfcp_cp_send_association_setup_request(node, node_timeout); break; default: ogs_error("Unknown timer[%s:%d]", @@ -126,12 +127,12 @@ void smf_pfcp_state_will_associate(ogs_fsm_t *s, smf_event_t *e) switch (message->h.type) { case OGS_PFCP_ASSOCIATION_SETUP_REQUEST_TYPE: - smf_n4_handle_association_setup_request(node, xact, + ogs_pfcp_cp_handle_association_setup_request(node, xact, &message->pfcp_association_setup_request); OGS_FSM_TRAN(s, smf_pfcp_state_associated); break; case OGS_PFCP_ASSOCIATION_SETUP_RESPONSE_TYPE: - smf_n4_handle_association_setup_response(node, xact, + ogs_pfcp_cp_handle_association_setup_response(node, xact, &message->pfcp_association_setup_response); OGS_FSM_TRAN(s, smf_pfcp_state_associated); break; @@ -189,21 +190,21 @@ void smf_pfcp_state_associated(ogs_fsm_t *s, smf_event_t *e) switch (message->h.type) { case OGS_PFCP_HEARTBEAT_REQUEST_TYPE: - smf_n4_handle_heartbeat_request(node, xact, + ogs_pfcp_handle_heartbeat_request(node, xact, &message->pfcp_heartbeat_request); break; case OGS_PFCP_HEARTBEAT_RESPONSE_TYPE: - smf_n4_handle_heartbeat_response(node, xact, + ogs_pfcp_handle_heartbeat_response(node, xact, &message->pfcp_heartbeat_response); break; case OGS_PFCP_ASSOCIATION_SETUP_REQUEST_TYPE: ogs_warn("PFCP[REQ] has already been associated"); - smf_n4_handle_association_setup_request(node, xact, + ogs_pfcp_cp_handle_association_setup_request(node, xact, &message->pfcp_association_setup_request); break; case OGS_PFCP_ASSOCIATION_SETUP_RESPONSE_TYPE: ogs_warn("PFCP[RSP] has already been associated"); - smf_n4_handle_association_setup_response(node, xact, + ogs_pfcp_cp_handle_association_setup_response(node, xact, &message->pfcp_association_setup_response); break; case OGS_PFCP_SESSION_ESTABLISHMENT_RESPONSE_TYPE: @@ -212,7 +213,7 @@ void smf_pfcp_state_associated(ogs_fsm_t *s, smf_event_t *e) break; } - if (SMF_EPC_SEID(message->h.seid)) + if (xact->epc) smf_epc_n4_handle_session_establishment_response( sess, xact, &message->pfcp_session_establishment_response); else @@ -226,7 +227,7 @@ void smf_pfcp_state_associated(ogs_fsm_t *s, smf_event_t *e) break; } - if (SMF_EPC_SEID(message->h.seid)) + if (xact->epc) smf_epc_n4_handle_session_modification_response( sess, xact, &message->pfcp_session_modification_response); else @@ -240,7 +241,7 @@ void smf_pfcp_state_associated(ogs_fsm_t *s, smf_event_t *e) break; } - if (SMF_EPC_SEID(message->h.seid)) + if (xact->epc) smf_epc_n4_handle_session_deletion_response( sess, xact, &message->pfcp_session_deletion_response); else @@ -261,7 +262,7 @@ void smf_pfcp_state_associated(ogs_fsm_t *s, smf_event_t *e) node = e->pfcp_node; ogs_assert(node); - smf_pfcp_send_heartbeat_request(node); + ogs_pfcp_send_heartbeat_request(node, node_timeout); break; default: ogs_error("Unknown timer[%s:%d]", @@ -297,3 +298,34 @@ void smf_pfcp_state_exception(ogs_fsm_t *s, smf_event_t *e) break; } } + +static void node_timeout(ogs_pfcp_xact_t *xact, void *data) +{ + int rv; + + smf_event_t *e = NULL; + uint8_t type; + + ogs_assert(xact); + type = xact->seq[0].type; + + switch (type) { + case OGS_PFCP_HEARTBEAT_REQUEST_TYPE: + ogs_assert(data); + + e = smf_event_new(SMF_EVT_N4_NO_HEARTBEAT); + e->pfcp_node = data; + + rv = ogs_queue_push(smf_self()->queue, e); + if (rv != OGS_OK) { + ogs_warn("ogs_queue_push() failed:%d", (int)rv); + smf_event_free(e); + } + break; + case OGS_PFCP_ASSOCIATION_SETUP_REQUEST_TYPE: + break; + default: + ogs_error("Not implemented [type:%d]", type); + break; + } +} diff --git a/src/smf/s5c-build.c b/src/smf/s5c-build.c index 6c4ad59e7..5df42e002 100644 --- a/src/smf/s5c-build.c +++ b/src/smf/s5c-build.c @@ -234,7 +234,7 @@ ogs_pkbuf_t *smf_s5c_build_create_bearer_request( req->bearer_contexts.eps_bearer_id.presence = 1; req->bearer_contexts.eps_bearer_id.u8 = bearer->ebi; - /* Data Plane(UL) : SMF_S5U */ + /* Data Plane(UL) : UPF-S5U */ memset(&upf_s5u_teid, 0, sizeof(ogs_gtp_f_teid_t)); upf_s5u_teid.interface_type = OGS_GTP_F_TEID_S5_S8_PGW_GTP_U; upf_s5u_teid.teid = htobe32(bearer->upf_s5u_teid); diff --git a/src/smf/s5c-handler.c b/src/smf/s5c-handler.c index fa2e2cc08..602f326e2 100644 --- a/src/smf/s5c-handler.c +++ b/src/smf/s5c-handler.c @@ -55,7 +55,6 @@ void smf_s5c_handle_create_session_request( int rv; uint8_t cause_value = 0; - char apn[OGS_MAX_APN_LEN]; ogs_gtp_uli_t uli; smf_ue_t *smf_ue = NULL; @@ -155,8 +154,8 @@ void smf_s5c_handle_create_session_request( smf_sess_set_ue_ip(sess); - ogs_info("UE IMSI:[%s] APN:[%s] IPv4:[%s] IPv6:[%s]", - smf_ue->imsi_bcd, apn, + ogs_info("UE IMSI:[%s] IPv4:[%s] IPv6:[%s]", + smf_ue->imsi_bcd, sess->ipv4 ? OGS_INET_NTOP(&sess->ipv4->addr, buf1) : "", sess->ipv6 ? OGS_INET6_NTOP(&sess->ipv6->addr, buf2) : ""); @@ -274,7 +273,7 @@ void smf_s5c_handle_create_bearer_response( ogs_gtp_create_bearer_response_t *rsp) { int rv; - ogs_gtp_f_teid_t *sgw_s5u_teid, *smf_s5u_teid; + ogs_gtp_f_teid_t *sgw_s5u_teid, *upf_s5u_teid; smf_bearer_t *bearer = NULL; ogs_pfcp_far_t *far = NULL; @@ -321,12 +320,12 @@ void smf_s5c_handle_create_bearer_response( return; } - /* Correlate with SMF-S5U-TEID */ - smf_s5u_teid = rsp->bearer_contexts.s5_s8_u_pgw_f_teid.data; - ogs_assert(smf_s5u_teid); + /* Correlate with UPF-S5U-TEID */ + upf_s5u_teid = rsp->bearer_contexts.s5_s8_u_pgw_f_teid.data; + ogs_assert(upf_s5u_teid); - /* Find the Bearer by SMF-S5U-TEID */ - bearer = smf_bearer_find_by_smf_s5u_teid(be32toh(smf_s5u_teid->teid)); + /* Find the Bearer by UPF-S5U-TEID */ + bearer = smf_bearer_find_by_upf_s5u_teid(be32toh(upf_s5u_teid->teid)); ogs_assert(bearer); /* Set EBI */ @@ -374,7 +373,8 @@ void smf_s5c_handle_create_bearer_response( qer->gbr.downlink = bearer->qos.gbr.downlink; } - smf_epc_pfcp_send_session_modification_request(bearer); + smf_epc_pfcp_send_bearer_modification_request( + bearer, OGS_PFCP_MODIFY_CREATE); } void smf_s5c_handle_update_bearer_response( @@ -382,10 +382,14 @@ void smf_s5c_handle_update_bearer_response( ogs_gtp_update_bearer_response_t *rsp) { int rv; + uint64_t gtp_flags = 0; + uint64_t pfcp_flags = 0; smf_bearer_t *bearer = NULL; ogs_assert(xact); ogs_assert(rsp); + gtp_flags = xact->update_flags; + ogs_assert(gtp_flags); ogs_debug("[SMF] Update Bearer Response"); @@ -426,7 +430,40 @@ void smf_s5c_handle_update_bearer_response( ogs_debug("[SMF] Update Bearer Response : SGW[0x%x] --> SMF[0x%x]", sess->sgw_s5c_teid, sess->smf_n4_teid); - if (bearer->pfcp_epc_modify.qos_update) { + if (gtp_flags & OGS_GTP_MODIFY_TFT_UPDATE) { + smf_pf_t *pf = NULL; + ogs_pfcp_pdr_t *pdr = NULL; + + ogs_list_for_each(&bearer->pfcp.pdr_list, pdr) + pdr->num_of_flow = 0; + + ogs_list_for_each(&bearer->pf_list, pf) { + ogs_list_for_each(&bearer->pfcp.pdr_list, pdr) { + if (pf->direction == OGS_FLOW_DOWNLINK_ONLY) { + if (pdr->src_if == OGS_PFCP_INTERFACE_CORE) { + pdr->flow_description[pdr->num_of_flow++] = + pf->flow_description; + break; + } + + } else if (pf->direction == OGS_FLOW_UPLINK_ONLY) { + if (pdr->src_if == OGS_PFCP_INTERFACE_ACCESS) { + pdr->flow_description[pdr->num_of_flow++] = + pf->flow_description; + break; + } + } else { + ogs_error("Flow Bidirectional is not supported[%d]", + pf->direction); + break; + } + } + } + + pfcp_flags |= OGS_PFCP_MODIFY_TFT_UPDATE; + } + + if (gtp_flags & OGS_GTP_MODIFY_QOS_UPDATE) { ogs_pfcp_qer_t *qer = NULL; /* Only 1 QER is used per bearer */ @@ -438,19 +475,12 @@ void smf_s5c_handle_update_bearer_response( qer->gbr.downlink = bearer->qos.gbr.downlink; } + + pfcp_flags |= OGS_PFCP_MODIFY_QOS_UPDATE; } - if (bearer->pfcp_epc_modify.tft_update) - ogs_warn("Not Implemented"); - -#if 0 /* FIXME */ - if (bearer->pfcp_epc_modify.qos_update || - bearer->pfcp_epc_modify.tft_update) { -#else - if (bearer->pfcp_epc_modify.qos_update) { -#endif - smf_epc_pfcp_send_session_modification_request(bearer); - } + if (pfcp_flags) + smf_epc_pfcp_send_bearer_modification_request(bearer, pfcp_flags); } void smf_s5c_handle_delete_bearer_response( @@ -502,77 +532,79 @@ void smf_s5c_handle_delete_bearer_response( ogs_debug("[SMF] Delete Bearer Response : SGW[0x%x] --> SMF[0x%x]", sess->sgw_s5c_teid, sess->smf_n4_teid); - smf_epc_pfcp_send_session_modification_request(bearer); + smf_epc_pfcp_send_bearer_modification_request( + bearer, OGS_PFCP_MODIFY_REMOVE); } static int reconfigure_packet_filter(smf_pf_t *pf, ogs_gtp_tft_t *tft, int i) { int j; + memset(&pf->ipfw_rule, 0, sizeof(ogs_ipfw_rule_t)); for (j = 0; j < tft->pf[i].num_of_component; j++) { switch(tft->pf[i].component[j].type) { case GTP_PACKET_FILTER_PROTOCOL_IDENTIFIER_NEXT_HEADER_TYPE: - pf->rule.proto = tft->pf[i].component[j].proto; + pf->ipfw_rule.proto = tft->pf[i].component[j].proto; break; case GTP_PACKET_FILTER_IPV4_REMOTE_ADDRESS_TYPE: - pf->rule.ipv4_remote = 1; - pf->rule.ip.remote.addr[0] = tft->pf[i].component[j].ipv4.addr; - pf->rule.ip.remote.mask[0] = tft->pf[i].component[j].ipv4.mask; + pf->ipfw_rule.ipv4_remote = 1; + pf->ipfw_rule.ip.remote.addr[0] = tft->pf[i].component[j].ipv4.addr; + pf->ipfw_rule.ip.remote.mask[0] = tft->pf[i].component[j].ipv4.mask; break; case GTP_PACKET_FILTER_IPV4_LOCAL_ADDRESS_TYPE: - pf->rule.ipv4_local = 1; - pf->rule.ip.local.addr[0] = tft->pf[i].component[j].ipv4.addr; - pf->rule.ip.local.mask[0] = tft->pf[i].component[j].ipv4.mask; + pf->ipfw_rule.ipv4_local = 1; + pf->ipfw_rule.ip.local.addr[0] = tft->pf[i].component[j].ipv4.addr; + pf->ipfw_rule.ip.local.mask[0] = tft->pf[i].component[j].ipv4.mask; break; case GTP_PACKET_FILTER_IPV6_REMOTE_ADDRESS_TYPE: - pf->rule.ipv6_remote = 1; - memcpy(pf->rule.ip.remote.addr, + pf->ipfw_rule.ipv6_remote = 1; + memcpy(pf->ipfw_rule.ip.remote.addr, tft->pf[i].component[j].ipv6_mask.addr, - sizeof(pf->rule.ip.remote.addr)); - memcpy(pf->rule.ip.remote.mask, + sizeof(pf->ipfw_rule.ip.remote.addr)); + memcpy(pf->ipfw_rule.ip.remote.mask, tft->pf[i].component[j].ipv6_mask.mask, - sizeof(pf->rule.ip.remote.mask)); + sizeof(pf->ipfw_rule.ip.remote.mask)); break; case GTP_PACKET_FILTER_IPV6_REMOTE_ADDRESS_PREFIX_LENGTH_TYPE: - pf->rule.ipv6_remote = 1; - memcpy(pf->rule.ip.remote.addr, + pf->ipfw_rule.ipv6_remote = 1; + memcpy(pf->ipfw_rule.ip.remote.addr, tft->pf[i].component[j].ipv6_mask.addr, - sizeof(pf->rule.ip.remote.addr)); - n2mask((struct in6_addr *)pf->rule.ip.remote.mask, + sizeof(pf->ipfw_rule.ip.remote.addr)); + n2mask((struct in6_addr *)pf->ipfw_rule.ip.remote.mask, tft->pf[i].component[j].ipv6.prefixlen); break; case GTP_PACKET_FILTER_IPV6_LOCAL_ADDRESS_TYPE: - pf->rule.ipv6_local = 1; - memcpy(pf->rule.ip.local.addr, + pf->ipfw_rule.ipv6_local = 1; + memcpy(pf->ipfw_rule.ip.local.addr, tft->pf[i].component[j].ipv6_mask.addr, - sizeof(pf->rule.ip.local.addr)); - memcpy(pf->rule.ip.local.mask, + sizeof(pf->ipfw_rule.ip.local.addr)); + memcpy(pf->ipfw_rule.ip.local.mask, tft->pf[i].component[j].ipv6_mask.mask, - sizeof(pf->rule.ip.local.mask)); + sizeof(pf->ipfw_rule.ip.local.mask)); break; case GTP_PACKET_FILTER_IPV6_LOCAL_ADDRESS_PREFIX_LENGTH_TYPE: - pf->rule.ipv6_local = 1; - memcpy(pf->rule.ip.local.addr, + pf->ipfw_rule.ipv6_local = 1; + memcpy(pf->ipfw_rule.ip.local.addr, tft->pf[i].component[j].ipv6_mask.addr, - sizeof(pf->rule.ip.local.addr)); - n2mask((struct in6_addr *)pf->rule.ip.local.mask, + sizeof(pf->ipfw_rule.ip.local.addr)); + n2mask((struct in6_addr *)pf->ipfw_rule.ip.local.mask, tft->pf[i].component[j].ipv6.prefixlen); break; case GTP_PACKET_FILTER_SINGLE_LOCAL_PORT_TYPE: - pf->rule.port.local.low = pf->rule.port.local.high = + pf->ipfw_rule.port.local.low = pf->ipfw_rule.port.local.high = tft->pf[i].component[j].port.low; break; case GTP_PACKET_FILTER_SINGLE_REMOTE_PORT_TYPE: - pf->rule.port.remote.low = pf->rule.port.remote.high = + pf->ipfw_rule.port.remote.low = pf->ipfw_rule.port.remote.high = tft->pf[i].component[j].port.low; break; case GTP_PACKET_FILTER_LOCAL_PORT_RANGE_TYPE: - pf->rule.port.local.low = tft->pf[i].component[j].port.low; - pf->rule.port.local.high = tft->pf[i].component[j].port.high; + pf->ipfw_rule.port.local.low = tft->pf[i].component[j].port.low; + pf->ipfw_rule.port.local.high = tft->pf[i].component[j].port.high; break; case GTP_PACKET_FILTER_REMOTE_PORT_RANGE_TYPE: - pf->rule.port.remote.low = tft->pf[i].component[j].port.low; - pf->rule.port.remote.high = tft->pf[i].component[j].port.high; + pf->ipfw_rule.port.remote.low = tft->pf[i].component[j].port.low; + pf->ipfw_rule.port.remote.high = tft->pf[i].component[j].port.high; break; default: ogs_error("Unknown Packet Filter Type(%d)", @@ -665,6 +697,12 @@ void smf_s5c_handle_bearer_resource_command( OGS_GTP_CAUSE_SEMANTIC_ERROR_IN_THE_TAD_OPERATION); return; } + + if (pf->flow_description) + ogs_free(pf->flow_description); + pf->flow_description = + ogs_ipfw_encode_flow_description(&pf->ipfw_rule); + pf->direction = tft.pf[i].direction; } tft_update = 1; @@ -689,6 +727,12 @@ void smf_s5c_handle_bearer_resource_command( return; } + if (pf->flow_description) + ogs_free(pf->flow_description); + pf->flow_description = + ogs_ipfw_encode_flow_description(&pf->ipfw_rule); + pf->direction = tft.pf[i].direction; + tft_update = 1; } } else if (tft.code == @@ -732,31 +776,30 @@ void smf_s5c_handle_bearer_resource_command( h.teid = sess->sgw_s5c_teid; if (tft_delete) { - memset(&bearer->pfcp_epc_modify, 0, sizeof(bearer->pfcp_epc_modify)); - bearer->pfcp_epc_modify.remove = true; - h.type = OGS_GTP_DELETE_BEARER_REQUEST_TYPE; pkbuf = smf_s5c_build_delete_bearer_request( h.type, bearer, cmd->procedure_transaction_id.u8); ogs_expect_or_return(pkbuf); - } else { - memset(&bearer->pfcp_epc_modify, 0, sizeof(bearer->pfcp_epc_modify)); - if (tft_update) - bearer->pfcp_epc_modify.tft_update = true; - if (qos_update) - bearer->pfcp_epc_modify.qos_update = true; + rv = ogs_gtp_xact_update_tx(xact, &h, pkbuf); + ogs_expect_or_return(rv == OGS_OK); + + } else { h.type = OGS_GTP_UPDATE_BEARER_REQUEST_TYPE; pkbuf = smf_s5c_build_update_bearer_request( h.type, bearer, cmd->procedure_transaction_id.u8, tft_update ? &tft : NULL, qos_update); ogs_expect_or_return(pkbuf); + + rv = ogs_gtp_xact_update_tx(xact, &h, pkbuf); + ogs_expect_or_return(rv == OGS_OK); + + if (tft_update) + xact->update_flags |= OGS_GTP_MODIFY_TFT_UPDATE; + if (qos_update) + xact->update_flags |= OGS_GTP_MODIFY_QOS_UPDATE; } - - rv = ogs_gtp_xact_update_tx(xact, &h, pkbuf); - ogs_expect_or_return(rv == OGS_OK); - rv = ogs_gtp_xact_commit(xact); ogs_expect(rv == OGS_OK); } diff --git a/src/upf/context.c b/src/upf/context.c index 912bc60ed..615008054 100644 --- a/src/upf/context.c +++ b/src/upf/context.c @@ -24,7 +24,6 @@ static upf_context_t self; int __upf_log_domain; static OGS_POOL(upf_sess_pool, upf_sess_t); -static OGS_POOL(upf_sdf_filter_pool, upf_sdf_filter_t); static int context_initialized = 0; @@ -38,18 +37,17 @@ void upf_context_init(void) ogs_log_install_domain(&__ogs_gtp_domain, "gtp", ogs_core()->log.level); ogs_log_install_domain(&__upf_log_domain, "upf", ogs_core()->log.level); + /* Setup UP Function Features */ + ogs_pfcp_self()->up_function_features.empu = 1; + ogs_pfcp_self()->up_function_features_len = 2; + ogs_gtp_node_init(512); ogs_list_init(&self.sess_list); - ogs_list_init(&self.gtpu_list); - ogs_list_init(&self.gtpu_resource_list); - - ogs_list_init(&self.gnb_n3_list); + ogs_list_init(&self.peer_list); ogs_pool_init(&upf_sess_pool, ogs_config()->pool.sess); - ogs_pool_init(&upf_sdf_filter_pool, - ogs_config()->pool.sess * OGS_MAX_NUM_OF_RULE); self.sess_hash = ogs_hash_make(); self.ipv4_hash = ogs_hash_make(); @@ -72,12 +70,10 @@ void upf_context_final(void) ogs_hash_destroy(self.ipv6_hash); ogs_pool_final(&upf_sess_pool); - ogs_pool_final(&upf_sdf_filter_pool); - ogs_gtp_node_remove_all(&self.gnb_n3_list); + ogs_gtp_node_remove_all(&self.peer_list); ogs_gtp_node_final(); - ogs_pfcp_gtpu_resource_remove_all(&self.gtpu_resource_list); context_initialized = 0; } @@ -100,6 +96,10 @@ static int upf_context_validation(void) ogs_error("No upf.gtpu in '%s'", ogs_config()->file); return OGS_ERROR; } + if (ogs_list_first(&ogs_pfcp_self()->subnet_list) == NULL) { + ogs_error("No upf.pdn: in '%s'", ogs_config()->file); + return OGS_ERROR; + } return OGS_OK; } @@ -166,7 +166,7 @@ int upf_context_parse_config(void) ogs_assert(gtpu_key); if (ogs_list_count( - &self.gtpu_resource_list) >= + &ogs_pfcp_self()->gtpu_resource_list) >= OGS_MAX_NUM_OF_GTPU_RESOURCE) { ogs_warn("[Overflow]: Number of User Plane " "IP Resource <= %d", @@ -311,7 +311,7 @@ int upf_context_parse_config(void) } ogs_pfcp_gtpu_resource_add( - &self.gtpu_resource_list, &info); + &ogs_pfcp_self()->gtpu_resource_list, &info); } ogs_list_for_each_safe(&list, next_iter, iter) @@ -319,7 +319,7 @@ int upf_context_parse_config(void) ogs_list_for_each_safe(&list6, next_iter, iter) ogs_list_add(&self.gtpu_list, iter); - } while (ogs_yaml_iter_type(>pu_array) == + } while (ogs_yaml_iter_type(>pu_array) == YAML_SEQUENCE_NODE); if (ogs_list_first(&self.gtpu_list) == NULL) { @@ -351,7 +351,7 @@ int upf_context_parse_config(void) &info); ogs_pfcp_gtpu_resource_add( - &self.gtpu_resource_list, &info); + &ogs_pfcp_self()->gtpu_resource_list, &info); } ogs_list_for_each_safe(&list, next_iter, iter) @@ -453,7 +453,7 @@ upf_sess_t *upf_sess_add(ogs_pfcp_f_seid_t *cp_f_seid, sess->pfcp.default_pdr->id); ogs_list_add(&self.sess_list, sess); - + ogs_info("Added a session. Number of active sessions is now %d", ogs_list_count(&self.sess_list)); @@ -470,7 +470,6 @@ int upf_sess_remove(upf_sess_t *sess) ogs_list_remove(&self.sess_list, sess); ogs_pfcp_sess_clear(&sess->pfcp); - upf_sdf_filter_remove_all(sess); ogs_hash_set(self.sess_hash, &sess->smf_n4_seid, sizeof(sess->smf_n4_seid), NULL); @@ -619,53 +618,3 @@ upf_sess_t *upf_sess_add_by_message(ogs_pfcp_message_t *message) return sess; } - -upf_sdf_filter_t *upf_sdf_filter_add(ogs_pfcp_pdr_t *pdr) -{ - upf_sdf_filter_t *sdf_filter = NULL; - ogs_pfcp_sess_t *pfcp = NULL; - upf_sess_t *sess = NULL; - - ogs_assert(pdr); - pfcp = pdr->sess; - ogs_assert(pfcp); - sess = UPF_SESS(pfcp); - ogs_assert(sess); - - ogs_pool_alloc(&upf_sdf_filter_pool, &sdf_filter); - ogs_assert(sdf_filter); - memset(sdf_filter, 0, sizeof *sdf_filter); - - sdf_filter->pdr = pdr; - ogs_list_add(&sess->sdf_filter_list, sdf_filter); - - return sdf_filter; -} - -void upf_sdf_filter_remove(upf_sdf_filter_t *sdf_filter) -{ - ogs_pfcp_pdr_t *pdr = NULL; - ogs_pfcp_sess_t *pfcp = NULL; - upf_sess_t *sess = NULL; - - ogs_assert(sdf_filter); - pdr = sdf_filter->pdr; - ogs_assert(pdr); - pfcp = pdr->sess; - ogs_assert(pfcp); - sess = UPF_SESS(pfcp); - ogs_assert(sess); - - ogs_list_remove(&sess->sdf_filter_list, sdf_filter); - ogs_pool_free(&upf_sdf_filter_pool, sdf_filter); -} - -void upf_sdf_filter_remove_all(upf_sess_t *sess) -{ - upf_sdf_filter_t *sdf_filter = NULL, *next_sdf_filter = NULL; - - ogs_assert(sess); - - ogs_list_for_each_safe(&sess->sdf_filter_list, next_sdf_filter, sdf_filter) - upf_sdf_filter_remove(sdf_filter); -} diff --git a/src/upf/context.h b/src/upf/context.h index 80b3f198a..af2e874ad 100644 --- a/src/upf/context.h +++ b/src/upf/context.h @@ -51,15 +51,12 @@ typedef struct upf_context_s { ogs_list_t gtpu_list; /* UPF GTPU Server List */ ogs_sock_t *gtpu_sock; /* UPF GTPU IPv4 Socket */ ogs_sock_t *gtpu_sock6; /* UPF GTPU IPv6 Socket */ - ogs_list_t gtpu_resource_list; /* UP IP Resource List */ - uint16_t function_features; /* UP Function Features */ 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 */ - ogs_list_t gnb_n3_list; /* gNB N3 Node List */ - ogs_list_t ip_pool_list; + ogs_list_t peer_list; /* gNB N3 Node List */ ogs_hash_t *sess_hash; /* hash table (F-SEID) */ ogs_hash_t *ipv4_hash; /* hash table (IPv4 Address) */ @@ -74,7 +71,6 @@ typedef struct upf_sess_s { uint32_t index; /**< An index of this node */ ogs_pfcp_sess_t pfcp; - ogs_list_t sdf_filter_list; /* SDF Filter List */ uint64_t upf_n4_seid; /* UPF SEID is dervied from INDEX */ uint64_t smf_n4_seid; /* SMF SEID is received from Peer */ @@ -88,15 +84,6 @@ typedef struct upf_sess_s { ogs_pfcp_node_t *pfcp_node; } upf_sess_t; -typedef struct upf_sdf_filter_s { - ogs_lnode_t lnode; - - ogs_ipfw_rule_t rule; - - /* Related Context */ - ogs_pfcp_pdr_t *pdr; -} upf_sdf_filter_t; - void upf_context_init(void); void upf_context_final(void); upf_context_t *upf_self(void); @@ -116,10 +103,6 @@ upf_sess_t *upf_sess_find_by_up_seid(uint64_t seid); upf_sess_t *upf_sess_find_by_ipv4(uint32_t addr); upf_sess_t *upf_sess_find_by_ipv6(uint32_t *addr6); -upf_sdf_filter_t *upf_sdf_filter_add(ogs_pfcp_pdr_t *pdr); -void upf_sdf_filter_remove(upf_sdf_filter_t *sdf_filter); -void upf_sdf_filter_remove_all(upf_sess_t *sess); - #ifdef __cplusplus } #endif diff --git a/src/upf/event.h b/src/upf/event.h index ba12d1ed6..9914c608c 100644 --- a/src/upf/event.h +++ b/src/upf/event.h @@ -30,7 +30,6 @@ typedef struct ogs_pfcp_node_s ogs_pfcp_node_t; typedef struct ogs_pfcp_xact_s ogs_pfcp_xact_t; typedef struct ogs_pfcp_message_s ogs_pfcp_message_t; typedef struct upf_sess_s upf_sess_t; -typedef struct upf_upf_s upf_upf_t; typedef enum { UPF_EVT_BASE = OGS_FSM_USER_SIG, diff --git a/src/upf/gtp-path.c b/src/upf/gtp-path.c index 4048b3062..e2f7b1926 100644 --- a/src/upf/gtp-path.c +++ b/src/upf/gtp-path.c @@ -41,10 +41,8 @@ #define UPF_GTP_HANDLED 1 -static void upf_gtp_send_to_gnb(ogs_pfcp_pdr_t *pdr, ogs_pkbuf_t *sendbuf); -static int upf_gtp_handle_multicast(ogs_pkbuf_t *recvbuf); +static void upf_gtp_handle_multicast(ogs_pkbuf_t *recvbuf); static int upf_gtp_handle_slaac(upf_sess_t *sess, ogs_pkbuf_t *recvbuf); -static int upf_gtp_handle_pdr(ogs_pfcp_pdr_t *pdr, ogs_pkbuf_t *recvbuf); static int upf_gtp_send_router_advertisement( upf_sess_t *sess, uint8_t *ip6_dst); @@ -53,6 +51,7 @@ static void _gtpv1_tun_recv_cb(short when, ogs_socket_t fd, void *data) ogs_pkbuf_t *recvbuf = NULL; int n; ogs_pfcp_pdr_t *pdr = NULL; + ogs_pfcp_user_plane_report_t report; recvbuf = ogs_pkbuf_alloc(NULL, OGS_MAX_SDU_LEN); ogs_pkbuf_reserve(recvbuf, OGS_GTPV1U_5GC_HEADER_LEN); @@ -71,7 +70,7 @@ static void _gtpv1_tun_recv_cb(short when, ogs_socket_t fd, void *data) pdr = upf_pdr_find_by_packet(recvbuf); if (pdr) { /* Unicast */ - upf_gtp_handle_pdr(pdr, recvbuf); + ogs_pfcp_up_handle_pdr(pdr, recvbuf, &report); } else { if (ogs_config()->parameter.multicast) { upf_gtp_handle_multicast(recvbuf); @@ -330,148 +329,11 @@ void upf_gtp_close(void) } } -void upf_gtp_send_buffered_packet(ogs_pfcp_pdr_t *pdr) -{ - ogs_pfcp_far_t *far = NULL; - int i; - - ogs_assert(pdr); - far = pdr->far; - - if (far && far->gnode) { - if (far->apply_action & OGS_PFCP_APPLY_ACTION_FORW) { - for (i = 0; i < far->num_of_buffered_packet; i++) { - upf_gtp_send_to_gnb(pdr, far->buffered_packet[i]); - } - far->num_of_buffered_packet = 0; - } - } -} - -static void upf_gtp_send_to_gnb(ogs_pfcp_pdr_t *pdr, ogs_pkbuf_t *sendbuf) -{ - char buf[OGS_ADDRSTRLEN]; - int rv; - ogs_gtp_header_t *gtp_h = NULL; - ogs_gtp_extension_header_t *ext_h = NULL; - ogs_gtp_node_t *gnode = NULL; - - ogs_pfcp_far_t *far = NULL; - ogs_pfcp_qer_t *qer = NULL; - - ogs_assert(pdr); - - far = pdr->far; - if (!far) { - ogs_error("No FAR"); - return; - } - - if (far->dst_if != OGS_PFCP_INTERFACE_ACCESS) { - ogs_error("FAR is NOT Downlink"); - return; - } - - gnode = far->gnode; - ogs_assert(gnode); - ogs_assert(gnode->sock); - ogs_assert(sendbuf); - - qer = pdr->qer; - - /* Add GTP-U header */ - if (qer && qer->qfi) { - ogs_assert(ogs_pkbuf_push(sendbuf, OGS_GTPV1U_5GC_HEADER_LEN)); - gtp_h = (ogs_gtp_header_t *)sendbuf->data; - /* Bits 8 7 6 5 4 3 2 1 - * +--+--+--+--+--+--+--+--+ - * |version |PT| 1| E| S|PN| - * +--+--+--+--+--+--+--+--+ - * 0 0 1 1 0 1 0 0 - */ - gtp_h->flags = 0x34; - gtp_h->type = OGS_GTPU_MSGTYPE_GPDU; - gtp_h->length = htobe16(sendbuf->len - OGS_GTPV1U_HEADER_LEN); - gtp_h->teid = htobe32(far->outer_header_creation.teid); - - ext_h = (ogs_gtp_extension_header_t *)( - sendbuf->data + OGS_GTPV1U_HEADER_LEN); - ext_h->type = OGS_GTP_EXTENSION_HEADER_TYPE_PDU_SESSION_CONTAINER; - ext_h->len = 1; - ext_h->pdu_type = - OGS_GTP_EXTENSION_HEADER_PDU_TYPE_DL_PDU_SESSION_INFORMATION; - ext_h->qos_flow_identifier = qer->qfi; - ext_h->next_type = - OGS_GTP_EXTENSION_HEADER_TYPE_NO_MORE_EXTENSION_HEADERS; - } else { - ogs_assert(ogs_pkbuf_push(sendbuf, OGS_GTPV1U_HEADER_LEN)); - gtp_h = (ogs_gtp_header_t *)sendbuf->data; - /* Bits 8 7 6 5 4 3 2 1 - * +--+--+--+--+--+--+--+--+ - * |version |PT| 1| E| S|PN| - * +--+--+--+--+--+--+--+--+ - * 0 0 1 1 0 0 0 0 - */ - gtp_h->flags = 0x30; - gtp_h->type = OGS_GTPU_MSGTYPE_GPDU; - gtp_h->length = htobe16(sendbuf->len - OGS_GTPV1U_HEADER_LEN); - gtp_h->teid = htobe32(far->outer_header_creation.teid); - } - - /* Send to gNB */ - ogs_debug("SEND GPU-U to gNB[%s] : TEID[0x%x]", - OGS_ADDR(&gnode->addr, buf), far->outer_header_creation.teid); - rv = ogs_gtp_sendto(gnode, sendbuf); - if (rv != OGS_OK) - ogs_error("ogs_gtp_sendto() failed"); - - ogs_pkbuf_free(sendbuf); -} - -static int upf_gtp_handle_pdr(ogs_pfcp_pdr_t *pdr, ogs_pkbuf_t *recvbuf) -{ - ogs_pfcp_far_t *far = NULL; - ogs_pkbuf_t *sendbuf = NULL; - - ogs_assert(recvbuf); - ogs_assert(pdr); - - if (pdr->src_if != OGS_PFCP_INTERFACE_CORE) { - ogs_error("PDR is NOT Downlink"); - return OGS_ERROR; - } - - far = pdr->far; - ogs_assert(far); - - sendbuf = ogs_pkbuf_copy(recvbuf); - ogs_assert(sendbuf); - if (!far->gnode) { - /* Default apply action : buffering */ - if (far->num_of_buffered_packet < MAX_NUM_OF_PACKET_BUFFER) { - far->buffered_packet[far->num_of_buffered_packet++] = sendbuf; - return UPF_GTP_HANDLED; - } - } else { - if (far->apply_action & OGS_PFCP_APPLY_ACTION_FORW) { - upf_gtp_send_to_gnb(pdr, sendbuf); - } else if (far->apply_action & OGS_PFCP_APPLY_ACTION_BUFF) { - if (far->num_of_buffered_packet < MAX_NUM_OF_PACKET_BUFFER) { - far->buffered_packet[far->num_of_buffered_packet++] = sendbuf; - return UPF_GTP_HANDLED; - } - } - return UPF_GTP_HANDLED; - } - - ogs_pkbuf_free(sendbuf); - return OGS_OK; -} - -static int upf_gtp_handle_multicast(ogs_pkbuf_t *recvbuf) +static void upf_gtp_handle_multicast(ogs_pkbuf_t *recvbuf) { struct ip *ip_h = NULL; struct ip6_hdr *ip6_h = NULL; + ogs_pfcp_user_plane_report_t report; ip_h = (struct ip *)recvbuf->data; if (ip_h->ip_v == 6) { @@ -495,15 +357,13 @@ static int upf_gtp_handle_multicast(ogs_pkbuf_t *recvbuf) pdr = ogs_pfcp_sess_default_pdr(&sess->pfcp); ogs_assert(pdr); - upf_gtp_handle_pdr(pdr, recvbuf); - return UPF_GTP_HANDLED; + ogs_pfcp_up_handle_pdr(pdr, recvbuf, &report); + return; } } } } - - return OGS_OK; } static int upf_gtp_handle_slaac(upf_sess_t *sess, ogs_pkbuf_t *recvbuf) @@ -540,6 +400,7 @@ static int upf_gtp_send_router_advertisement( { int rv; ogs_pkbuf_t *pkbuf = NULL; + ogs_pfcp_user_plane_report_t report; ogs_pfcp_pdr_t *pdr = NULL; ogs_pfcp_far_t *far = NULL; @@ -622,7 +483,7 @@ static int upf_gtp_send_router_advertisement( memcpy(ip6_h->ip6_src.s6_addr, src_ipsub.sub, sizeof src_ipsub.sub); memcpy(ip6_h->ip6_dst.s6_addr, ip6_dst, OGS_IPV6_LEN); - upf_gtp_handle_pdr(pdr, pkbuf); + ogs_pfcp_up_handle_pdr(pdr, pkbuf, &report); ogs_debug(" Router Advertisement"); diff --git a/src/upf/gtp-path.h b/src/upf/gtp-path.h index 36de03294..45e35b2a9 100644 --- a/src/upf/gtp-path.h +++ b/src/upf/gtp-path.h @@ -30,8 +30,6 @@ extern "C" { int upf_gtp_open(void); void upf_gtp_close(void); -void upf_gtp_send_buffered_packet(ogs_pfcp_pdr_t *pdr); - #ifdef __cplusplus } #endif diff --git a/src/upf/n4-build.c b/src/upf/n4-build.c index cc9de5133..04e8b8412 100644 --- a/src/upf/n4-build.c +++ b/src/upf/n4-build.c @@ -20,110 +20,6 @@ #include "context.h" #include "n4-build.h" -ogs_pkbuf_t *upf_n4_build_association_setup_request(uint8_t type) -{ - ogs_pfcp_message_t pfcp_message; - ogs_pfcp_association_setup_request_t *req = NULL; - - ogs_pfcp_node_id_t node_id; - int node_id_len = 0; - - ogs_pfcp_gtpu_resource_t *resource = NULL; - char infobuf[OGS_MAX_NUM_OF_GTPU_RESOURCE] - [OGS_PFCP_MAX_USER_PLANE_IP_RESOURCE_INFO_LEN]; - int i = 0; - - ogs_debug("Association Setup Request"); - - req = &pfcp_message.pfcp_association_setup_request; - memset(&pfcp_message, 0, sizeof(ogs_pfcp_message_t)); - - ogs_pfcp_sockaddr_to_node_id( - ogs_pfcp_self()->pfcp_addr, ogs_pfcp_self()->pfcp_addr6, - ogs_config()->parameter.prefer_ipv4, - &node_id, &node_id_len); - req->node_id.presence = 1; - req->node_id.data = &node_id; - req->node_id.len = node_id_len; - - req->recovery_time_stamp.presence = 1; - req->recovery_time_stamp.u32 = ogs_pfcp_self()->pfcp_started; - - req->up_function_features.presence = 1; - req->up_function_features.u16 = htobe16(upf_self()->function_features); - - i = 0; - ogs_list_for_each(&upf_self()->gtpu_resource_list, resource) { - ogs_assert(i < OGS_MAX_NUM_OF_GTPU_RESOURCE); - ogs_pfcp_tlv_user_plane_ip_resource_information_t *message = - &req->user_plane_ip_resource_information[i]; - ogs_assert(message); - - message->presence = 1; - ogs_pfcp_build_user_plane_ip_resource_info( - message, &resource->info, infobuf[i], - OGS_PFCP_MAX_USER_PLANE_IP_RESOURCE_INFO_LEN); - i++; - } - - pfcp_message.h.type = type; - return ogs_pfcp_build_msg(&pfcp_message); -} - -ogs_pkbuf_t *upf_n4_build_association_setup_response(uint8_t type, - uint8_t cause) -{ - ogs_pfcp_message_t pfcp_message; - ogs_pfcp_association_setup_response_t *rsp = NULL; - - ogs_pfcp_node_id_t node_id; - int node_id_len = 0; - - ogs_pfcp_gtpu_resource_t *resource = NULL; - char infobuf[OGS_MAX_NUM_OF_GTPU_RESOURCE] - [OGS_PFCP_MAX_USER_PLANE_IP_RESOURCE_INFO_LEN]; - int i = 0; - - ogs_debug("Association Setup Response"); - - rsp = &pfcp_message.pfcp_association_setup_response; - memset(&pfcp_message, 0, sizeof(ogs_pfcp_message_t)); - - ogs_pfcp_sockaddr_to_node_id( - ogs_pfcp_self()->pfcp_addr, ogs_pfcp_self()->pfcp_addr6, - ogs_config()->parameter.prefer_ipv4, - &node_id, &node_id_len); - rsp->node_id.presence = 1; - rsp->node_id.data = &node_id; - rsp->node_id.len = node_id_len; - - rsp->cause.presence = 1; - rsp->cause.u8 = cause; - - rsp->recovery_time_stamp.presence = 1; - rsp->recovery_time_stamp.u32 = ogs_pfcp_self()->pfcp_started; - - rsp->up_function_features.presence = 1; - rsp->up_function_features.u16 = htobe16(upf_self()->function_features); - - i = 0; - ogs_list_for_each(&upf_self()->gtpu_resource_list, resource) { - ogs_assert(i < OGS_MAX_NUM_OF_GTPU_RESOURCE); - ogs_pfcp_tlv_user_plane_ip_resource_information_t *message = - &rsp->user_plane_ip_resource_information[i]; - ogs_assert(message); - - message->presence = 1; - ogs_pfcp_build_user_plane_ip_resource_info( - message, &resource->info, infobuf[i], - OGS_PFCP_MAX_USER_PLANE_IP_RESOURCE_INFO_LEN); - i++; - } - - pfcp_message.h.type = type; - return ogs_pfcp_build_msg(&pfcp_message); -} - ogs_pkbuf_t *upf_n4_build_session_establishment_response(uint8_t type, upf_sess_t *sess, ogs_pfcp_pdr_t *created_pdr[], int num_of_created_pdr) { diff --git a/src/upf/n4-build.h b/src/upf/n4-build.h index ec6325baa..77ff27d18 100644 --- a/src/upf/n4-build.h +++ b/src/upf/n4-build.h @@ -26,10 +26,6 @@ extern "C" { #endif -ogs_pkbuf_t *upf_n4_build_association_setup_request(uint8_t type); -ogs_pkbuf_t *upf_n4_build_association_setup_response(uint8_t type, - uint8_t cause); - ogs_pkbuf_t *upf_n4_build_session_establishment_response(uint8_t type, upf_sess_t *sess, ogs_pfcp_pdr_t *created_pdr[], int num_of_created_pdr); ogs_pkbuf_t *upf_n4_build_session_modification_response(uint8_t type, diff --git a/src/upf/n4-handler.c b/src/upf/n4-handler.c index 7068f9be1..9e869737e 100644 --- a/src/upf/n4-handler.c +++ b/src/upf/n4-handler.c @@ -22,64 +22,23 @@ #include "gtp-path.h" #include "n4-handler.h" -void upf_n4_handle_association_setup_request( - ogs_pfcp_node_t *node, ogs_pfcp_xact_t *xact, - ogs_pfcp_association_setup_request_t *req) -{ - ogs_assert(xact); - upf_pfcp_send_association_setup_response( - xact, OGS_PFCP_CAUSE_REQUEST_ACCEPTED); -} - -void upf_n4_handle_association_setup_response( - ogs_pfcp_node_t *node, ogs_pfcp_xact_t *xact, - ogs_pfcp_association_setup_response_t *rsp) -{ - ogs_assert(xact); - ogs_pfcp_xact_commit(xact); -} - -void upf_n4_handle_heartbeat_request( - ogs_pfcp_node_t *node, ogs_pfcp_xact_t *xact, - ogs_pfcp_heartbeat_request_t *req) -{ - ogs_assert(xact); - ogs_pfcp_send_heartbeat_response(xact); -} - -void upf_n4_handle_heartbeat_response( - ogs_pfcp_node_t *node, ogs_pfcp_xact_t *xact, - ogs_pfcp_heartbeat_response_t *rsp) -{ - ogs_assert(xact); - ogs_pfcp_xact_commit(xact); - - ogs_timer_start(node->t_no_heartbeat, - ogs_config()->time.message.pfcp.no_heartbeat_duration); -} - -static void setup_gtp_node(ogs_pfcp_far_t *far, - ogs_pfcp_tlv_outer_header_creation_t *outer_header_creation) +static void setup_gtp_node(ogs_pfcp_far_t *far) { int rv; ogs_ip_t ip; ogs_gtp_node_t *gnode = NULL; ogs_assert(far); - ogs_assert(outer_header_creation); - ogs_assert(outer_header_creation->presence); - memcpy(&far->outer_header_creation, - outer_header_creation->data, outer_header_creation->len); - far->outer_header_creation.teid = be32toh(far->outer_header_creation.teid); + ogs_pfcp_outer_header_creation_to_ip(&far->outer_header_creation, &ip); - rv = ogs_pfcp_outer_header_creation_to_ip(&far->outer_header_creation, &ip); - ogs_assert(rv == OGS_OK); + /* No Outer Header Creation */ + if (ip.len == 0) return; - gnode = ogs_gtp_node_find_by_ip(&upf_self()->gnb_n3_list, &ip); + gnode = ogs_gtp_node_find_by_ip(&upf_self()->peer_list, &ip); if (!gnode) { gnode = ogs_gtp_node_add_by_ip( - &upf_self()->gnb_n3_list, &ip, upf_self()->gtpu_port, + &upf_self()->peer_list, &ip, upf_self()->gtpu_port, ogs_config()->parameter.no_ipv4, ogs_config()->parameter.no_ipv6, ogs_config()->parameter.prefer_ipv4); @@ -92,433 +51,12 @@ static void setup_gtp_node(ogs_pfcp_far_t *far, OGS_SETUP_GTP_NODE(far, gnode); } -static ogs_pfcp_pdr_t *handle_create_pdr(ogs_pfcp_sess_t *sess, - ogs_pfcp_tlv_create_pdr_t *message, - uint8_t *cause_value, uint8_t *offending_ie_value) -{ - ogs_pfcp_pdr_t *pdr = NULL; - ogs_pfcp_far_t *far = NULL; - ogs_pfcp_qer_t *qer = NULL; - int i, len; - int rv; - - ogs_assert(sess); - ogs_assert(message); - - if (message->presence == 0) - return NULL; - - if (message->pdr_id.presence == 0) { - ogs_warn("No PDR-ID"); - *cause_value = OGS_PFCP_CAUSE_MANDATORY_IE_MISSING; - *offending_ie_value = OGS_PFCP_PDR_ID_TYPE; - return NULL; - } - - if (message->precedence.presence == 0) { - ogs_warn("No Presence in PDR"); - *cause_value = OGS_PFCP_CAUSE_MANDATORY_IE_MISSING; - *offending_ie_value = OGS_PFCP_PRECEDENCE_TYPE; - return NULL; - } - - pdr = ogs_pfcp_pdr_find_or_add(sess, message->pdr_id.u16); - ogs_assert(pdr); - ogs_pfcp_pdr_reorder_by_precedence(pdr, message->precedence.u32); - - if (message->pdi.presence == 0) { - ogs_warn("No PDI in PDR"); - *cause_value = OGS_PFCP_CAUSE_MANDATORY_IE_MISSING; - *offending_ie_value = OGS_PFCP_PDI_TYPE; - return NULL; - } - - if (message->pdi.source_interface.presence == 0) { - ogs_warn("No Source Interface in PDI"); - *cause_value = OGS_PFCP_CAUSE_MANDATORY_IE_MISSING; - *offending_ie_value = OGS_PFCP_SOURCE_INTERFACE_TYPE; - return NULL; - } - - pdr->precedence = message->precedence.u32; - pdr->src_if = message->pdi.source_interface.u8; - - for (i = 0; i < OGS_MAX_NUM_OF_RULE; i++) { - ogs_pfcp_sdf_filter_t sdf_filter_in_message; - if (message->pdi.sdf_filter[i].presence == 0) - break; - - len = ogs_pfcp_parse_sdf_filter( - &sdf_filter_in_message, &message->pdi.sdf_filter[i]); - ogs_assert(message->pdi.sdf_filter[i].len == len); - if (sdf_filter_in_message.fd) { - upf_sdf_filter_t *sdf_filter = NULL; - char *flow_description = NULL; - - flow_description = ogs_malloc( - sdf_filter_in_message.flow_description_len+1); - ogs_cpystrn(flow_description, - sdf_filter_in_message.flow_description, - sdf_filter_in_message.flow_description_len+1); - - sdf_filter = upf_sdf_filter_add(pdr); - ogs_assert(sdf_filter); - rv = ogs_ipfw_compile_rule(&sdf_filter->rule, flow_description); - ogs_assert(rv == OGS_OK); - sdf_filter->pdr = pdr; - - ogs_free(flow_description); - } - } - - /* APN(Network Instance) and UE IP Address - * has already been processed in upf_sess_add() */ - - if (pdr->src_if == OGS_PFCP_INTERFACE_CORE) { /* Downlink */ - - /* Nothing */ - - } else if (pdr->src_if == OGS_PFCP_INTERFACE_ACCESS) { /* Uplink */ - if (message->pdi.local_f_teid.presence == 0) { - ogs_warn("No F-TEID in PDI"); - *cause_value = OGS_PFCP_CAUSE_MANDATORY_IE_MISSING; - *offending_ie_value = OGS_PFCP_F_TEID_TYPE; - return NULL; - } - - if (message->outer_header_removal.presence == 0) { - ogs_warn("No Outer Header Removal in PDI"); - *cause_value = OGS_PFCP_CAUSE_MANDATORY_IE_MISSING; - *offending_ie_value = OGS_PFCP_OUTER_HEADER_REMOVAL_TYPE; - return NULL; - } - - pdr->f_teid_len = message->pdi.local_f_teid.len; - memcpy(&pdr->f_teid, message->pdi.local_f_teid.data, pdr->f_teid_len); - pdr->f_teid.teid = be32toh(pdr->f_teid.teid); - - memcpy(&pdr->outer_header_removal, - message->outer_header_removal.data, - message->outer_header_removal.len); - } else { - ogs_error("Invalid Source Interface[%d] in PDR", pdr->src_if); - *cause_value = OGS_PFCP_CAUSE_MANDATORY_IE_INCORRECT; - *offending_ie_value = OGS_PFCP_SOURCE_INTERFACE_TYPE; - return NULL; - } - - if (message->far_id.presence) { - far = ogs_pfcp_far_find_or_add(sess, message->far_id.u32); - ogs_assert(far); - ogs_pfcp_pdr_associate_far(pdr, far); - } - - if (message->qer_id.presence) { - qer = ogs_pfcp_qer_find_or_add(sess, message->qer_id.u32); - ogs_assert(qer); - ogs_pfcp_pdr_associate_qer(pdr, qer); - } - - return pdr; -} - -static bool handle_remove_pdr(ogs_pfcp_sess_t *sess, - ogs_pfcp_tlv_remove_pdr_t *message, - uint8_t *cause_value, uint8_t *offending_ie_value) -{ - ogs_pfcp_pdr_t *pdr = NULL; - - ogs_assert(sess); - ogs_assert(message); - - if (message->presence == 0) - return false; - - if (message->pdr_id.presence == 0) { - ogs_warn("No PDR-ID"); - *cause_value = OGS_PFCP_CAUSE_MANDATORY_IE_MISSING; - *offending_ie_value = OGS_PFCP_PDR_ID_TYPE; - return false; - } - - pdr = ogs_pfcp_pdr_find(sess, message->pdr_id.u16); - if (!pdr) { - ogs_error("Unknown PDR-ID[%d]", message->pdr_id.u16); - *cause_value = OGS_PFCP_CAUSE_SESSION_CONTEXT_NOT_FOUND; - return false; - } - - ogs_pfcp_pdr_remove(pdr); - - return true; -} - -static ogs_pfcp_far_t *handle_create_far(ogs_pfcp_sess_t *sess, - ogs_pfcp_tlv_create_far_t *message, - uint8_t *cause_value, uint8_t *offending_ie_value) -{ - ogs_pfcp_far_t *far = NULL; - - ogs_assert(message); - ogs_assert(sess); - - if (message->presence == 0) - return NULL; - - if (message->far_id.presence == 0) { - ogs_warn("No FAR-ID"); - *cause_value = OGS_PFCP_CAUSE_MANDATORY_IE_MISSING; - *offending_ie_value = OGS_PFCP_FAR_ID_TYPE; - return NULL; - } - - far = ogs_pfcp_far_find(sess, message->far_id.u32); - if (!far) { - ogs_error("Cannot find FAR-ID[%d] in PDR", message->far_id.u32); - *cause_value = OGS_PFCP_CAUSE_MANDATORY_IE_INCORRECT; - *offending_ie_value = OGS_PFCP_FAR_ID_TYPE; - return NULL; - } - - if (message->apply_action.presence == 0) { - ogs_warn("No Apply Action"); - *cause_value = OGS_PFCP_CAUSE_MANDATORY_IE_MISSING; - *offending_ie_value = OGS_PFCP_APPLY_ACTION_TYPE; - return NULL; - } - if (message->forwarding_parameters. - destination_interface.presence == 0) { - return far; - } - - far->apply_action = message->apply_action.u8; - far->dst_if = message->forwarding_parameters.destination_interface.u8; - - if (far->dst_if == OGS_PFCP_INTERFACE_ACCESS) { /* Downlink */ - if (message->forwarding_parameters.outer_header_creation.presence) { - setup_gtp_node(far, - &message->forwarding_parameters.outer_header_creation); - } - - } else if (far->dst_if == OGS_PFCP_INTERFACE_CORE) { /* Uplink */ - - /* Nothing */ - - } else { - ogs_error("Invalid Destination Interface[%d] in FAR", far->dst_if); - *cause_value = OGS_PFCP_CAUSE_MANDATORY_IE_INCORRECT; - *offending_ie_value = OGS_PFCP_DESTINATION_INTERFACE_TYPE; - return NULL; - } - - return far; -} - -static ogs_pfcp_far_t *handle_update_far(ogs_pfcp_sess_t *sess, - ogs_pfcp_tlv_update_far_t *message, - uint8_t *cause_value, uint8_t *offending_ie_value) -{ - ogs_pfcp_far_t *far = NULL; - - ogs_assert(message); - ogs_assert(sess); - - if (message->presence == 0) - return NULL; - - if (message->far_id.presence == 0) { - ogs_warn("No FAR-ID"); - *cause_value = OGS_PFCP_CAUSE_MANDATORY_IE_MISSING; - *offending_ie_value = OGS_PFCP_FAR_ID_TYPE; - return NULL; - } - - far = ogs_pfcp_far_find(sess, message->far_id.u32); - if (!far) { - ogs_error("Cannot find FAR-ID[%d] in PDR", message->far_id.u32); - *cause_value = OGS_PFCP_CAUSE_MANDATORY_IE_INCORRECT; - *offending_ie_value = OGS_PFCP_FAR_ID_TYPE; - return NULL; - } - - if (message->apply_action.presence) - far->apply_action = message->apply_action.u8; - - if (message->update_forwarding_parameters. - destination_interface.presence == 0) - far->dst_if = message->update_forwarding_parameters. - destination_interface.u8; - - if (far->dst_if == OGS_PFCP_INTERFACE_ACCESS) { /* Downlink */ - if (message->update_forwarding_parameters. - outer_header_creation.presence) { - setup_gtp_node(far, - &message->update_forwarding_parameters.outer_header_creation); - } - - } else if (far->dst_if == OGS_PFCP_INTERFACE_CORE) { /* Uplink */ - - /* Nothing */ - - } else { - ogs_error("Invalid Destination Interface[%d] in FAR", far->dst_if); - *cause_value = OGS_PFCP_CAUSE_MANDATORY_IE_INCORRECT; - *offending_ie_value = OGS_PFCP_DESTINATION_INTERFACE_TYPE; - return NULL; - } - - return far; -} - -static bool handle_remove_far(ogs_pfcp_sess_t *sess, - ogs_pfcp_tlv_remove_far_t *message, - uint8_t *cause_value, uint8_t *offending_ie_value) -{ - ogs_pfcp_far_t *far = NULL; - - ogs_assert(sess); - ogs_assert(message); - - if (message->presence == 0) - return false; - - if (message->far_id.presence == 0) { - ogs_warn("No FAR-ID"); - *cause_value = OGS_PFCP_CAUSE_MANDATORY_IE_MISSING; - *offending_ie_value = OGS_PFCP_FAR_ID_TYPE; - return false; - } - - far = ogs_pfcp_far_find(sess, message->far_id.u32); - if (!far) { - ogs_error("Unknown FAR-ID[%d]", message->far_id.u32); - *cause_value = OGS_PFCP_CAUSE_SESSION_CONTEXT_NOT_FOUND; - return false; - } - - ogs_pfcp_far_remove(far); - - return true; -} - -static ogs_pfcp_qer_t *handle_create_qer(ogs_pfcp_sess_t *sess, - ogs_pfcp_tlv_create_qer_t *message, - uint8_t *cause_value, uint8_t *offending_ie_value) -{ - ogs_pfcp_qer_t *qer = NULL; - - ogs_assert(message); - ogs_assert(sess); - - if (message->presence == 0) - return NULL; - - if (message->qer_id.presence == 0) { - ogs_warn("No QER-ID"); - *cause_value = OGS_PFCP_CAUSE_MANDATORY_IE_MISSING; - *offending_ie_value = OGS_PFCP_FAR_ID_TYPE; - return NULL; - } - - qer = ogs_pfcp_qer_find(sess, message->qer_id.u32); - if (!qer) { - ogs_error("Cannot find QER-ID[%d] in PDR", message->qer_id.u32); - *cause_value = OGS_PFCP_CAUSE_MANDATORY_IE_INCORRECT; - *offending_ie_value = OGS_PFCP_FAR_ID_TYPE; - return NULL; - } - - if (message->gate_status.presence == 0) { - ogs_warn("No Gate Status"); - *cause_value = OGS_PFCP_CAUSE_MANDATORY_IE_MISSING; - *offending_ie_value = OGS_PFCP_APPLY_ACTION_TYPE; - return NULL; - } - - qer->gate_status.value = message->gate_status.u8; - - if (message->maximum_bitrate.presence) - ogs_pfcp_parse_bitrate(&qer->mbr, &message->maximum_bitrate); - if (message->guaranteed_bitrate.presence) - ogs_pfcp_parse_bitrate(&qer->gbr, &message->guaranteed_bitrate); - - if (message->qos_flow_identifier.presence) - qer->qfi = message->qos_flow_identifier.u8; - - return qer; -} - -static ogs_pfcp_qer_t *handle_update_qer(ogs_pfcp_sess_t *sess, - ogs_pfcp_tlv_update_qer_t *message, - uint8_t *cause_value, uint8_t *offending_ie_value) -{ - ogs_pfcp_qer_t *qer = NULL; - - ogs_assert(message); - ogs_assert(sess); - - if (message->presence == 0) - return NULL; - - if (message->qer_id.presence == 0) { - ogs_warn("No QER-ID"); - *cause_value = OGS_PFCP_CAUSE_MANDATORY_IE_MISSING; - *offending_ie_value = OGS_PFCP_FAR_ID_TYPE; - return NULL; - } - - qer = ogs_pfcp_qer_find(sess, message->qer_id.u32); - if (!qer) { - ogs_error("Cannot find QER-ID[%d] in PDR", message->qer_id.u32); - *cause_value = OGS_PFCP_CAUSE_MANDATORY_IE_INCORRECT; - *offending_ie_value = OGS_PFCP_FAR_ID_TYPE; - return NULL; - } - - if (message->maximum_bitrate.presence) - ogs_pfcp_parse_bitrate(&qer->mbr, &message->maximum_bitrate); - if (message->guaranteed_bitrate.presence) - ogs_pfcp_parse_bitrate(&qer->gbr, &message->guaranteed_bitrate); - - return qer; -} - -static bool handle_remove_qer(ogs_pfcp_sess_t *sess, - ogs_pfcp_tlv_remove_qer_t *message, - uint8_t *cause_value, uint8_t *offending_ie_value) -{ - ogs_pfcp_qer_t *qer = NULL; - - ogs_assert(sess); - ogs_assert(message); - - if (message->presence == 0) - return false; - - if (message->qer_id.presence == 0) { - ogs_warn("No QER-ID"); - *cause_value = OGS_PFCP_CAUSE_MANDATORY_IE_MISSING; - *offending_ie_value = OGS_PFCP_QER_ID_TYPE; - return false; - } - - qer = ogs_pfcp_qer_find(sess, message->qer_id.u32); - if (!qer) { - ogs_error("Unknown QER-ID[%d]", message->qer_id.u32); - *cause_value = OGS_PFCP_CAUSE_SESSION_CONTEXT_NOT_FOUND; - return false; - } - - ogs_pfcp_qer_remove(qer); - - return true; -} - void upf_n4_handle_session_establishment_request( upf_sess_t *sess, ogs_pfcp_xact_t *xact, ogs_pfcp_session_establishment_request_t *req) { ogs_pfcp_pdr_t *pdr = NULL; + ogs_pfcp_far_t *far = NULL; ogs_pfcp_pdr_t *created_pdr[OGS_MAX_NUM_OF_PDR]; int num_of_created_pdr = 0; uint8_t cause_value = 0; @@ -533,7 +71,7 @@ void upf_n4_handle_session_establishment_request( cause_value = OGS_PFCP_CAUSE_REQUEST_ACCEPTED; if (!sess) { - ogs_warn("No Context"); + ogs_error("No Context"); ogs_pfcp_send_error_message(xact, 0, OGS_PFCP_SESSION_ESTABLISHMENT_RESPONSE_TYPE, OGS_PFCP_CAUSE_SESSION_CONTEXT_NOT_FOUND, 0); @@ -541,7 +79,7 @@ void upf_n4_handle_session_establishment_request( } for (i = 0; i < OGS_MAX_NUM_OF_PDR; i++) { - created_pdr[i] = handle_create_pdr(&sess->pfcp, + created_pdr[i] = ogs_pfcp_handle_create_pdr(&sess->pfcp, &req->create_pdr[i], &cause_value, &offending_ie_value); if (created_pdr[i] == NULL) break; @@ -551,7 +89,7 @@ void upf_n4_handle_session_establishment_request( goto cleanup; for (i = 0; i < OGS_MAX_NUM_OF_FAR; i++) { - if (handle_create_far(&sess->pfcp, &req->create_far[i], + if (ogs_pfcp_handle_create_far(&sess->pfcp, &req->create_far[i], &cause_value, &offending_ie_value) == NULL) break; } @@ -559,13 +97,17 @@ void upf_n4_handle_session_establishment_request( goto cleanup; for (i = 0; i < OGS_MAX_NUM_OF_QER; i++) { - if (handle_create_qer(&sess->pfcp, &req->create_qer[i], + if (ogs_pfcp_handle_create_qer(&sess->pfcp, &req->create_qer[i], &cause_value, &offending_ie_value) == NULL) break; } if (cause_value != OGS_PFCP_CAUSE_REQUEST_ACCEPTED) goto cleanup; + /* Setup GTP Node */ + ogs_list_for_each(&sess->pfcp.far_list, far) + setup_gtp_node(far); + /* Setup UPF-N3-TEID & QFI Hash */ for (i = 0; i < num_of_created_pdr; i++) { pdr = created_pdr[i]; @@ -580,7 +122,7 @@ void upf_n4_handle_session_establishment_request( /* Send Buffered Packet to gNB/SGW */ ogs_list_for_each(&sess->pfcp.pdr_list, pdr) { if (pdr->src_if == OGS_PFCP_INTERFACE_CORE) { /* Downlink */ - upf_gtp_send_buffered_packet(pdr); + ogs_pfcp_send_buffered_packet(pdr); } } @@ -600,6 +142,7 @@ void upf_n4_handle_session_modification_request( ogs_pfcp_session_modification_request_t *req) { ogs_pfcp_pdr_t *pdr = NULL; + ogs_pfcp_far_t *far = NULL; ogs_pfcp_pdr_t *created_pdr[OGS_MAX_NUM_OF_PDR]; int num_of_created_pdr = 0; uint8_t cause_value = 0; @@ -614,7 +157,7 @@ void upf_n4_handle_session_modification_request( cause_value = OGS_PFCP_CAUSE_REQUEST_ACCEPTED; if (!sess) { - ogs_warn("No Context"); + ogs_error("No Context"); ogs_pfcp_send_error_message(xact, 0, OGS_PFCP_SESSION_MODIFICATION_RESPONSE_TYPE, OGS_PFCP_CAUSE_SESSION_CONTEXT_NOT_FOUND, 0); @@ -622,7 +165,7 @@ void upf_n4_handle_session_modification_request( } for (i = 0; i < OGS_MAX_NUM_OF_PDR; i++) { - created_pdr[i] = handle_create_pdr(&sess->pfcp, + created_pdr[i] = ogs_pfcp_handle_create_pdr(&sess->pfcp, &req->create_pdr[i], &cause_value, &offending_ie_value); if (created_pdr[i] == NULL) break; @@ -632,7 +175,15 @@ void upf_n4_handle_session_modification_request( goto cleanup; for (i = 0; i < OGS_MAX_NUM_OF_PDR; i++) { - if (handle_remove_pdr(&sess->pfcp, &req->remove_pdr[i], + if (ogs_pfcp_handle_update_pdr(&sess->pfcp, &req->update_pdr[i], + &cause_value, &offending_ie_value) == NULL) + break; + } + if (cause_value != OGS_PFCP_CAUSE_REQUEST_ACCEPTED) + goto cleanup; + + for (i = 0; i < OGS_MAX_NUM_OF_PDR; i++) { + if (ogs_pfcp_handle_remove_pdr(&sess->pfcp, &req->remove_pdr[i], &cause_value, &offending_ie_value) == false) break; } @@ -640,7 +191,7 @@ void upf_n4_handle_session_modification_request( goto cleanup; for (i = 0; i < OGS_MAX_NUM_OF_FAR; i++) { - if (handle_create_far(&sess->pfcp, &req->create_far[i], + if (ogs_pfcp_handle_create_far(&sess->pfcp, &req->create_far[i], &cause_value, &offending_ie_value) == NULL) break; } @@ -648,7 +199,25 @@ void upf_n4_handle_session_modification_request( goto cleanup; for (i = 0; i < OGS_MAX_NUM_OF_FAR; i++) { - if (handle_update_far(&sess->pfcp, &req->update_far[i], + if (ogs_pfcp_handle_update_far_flags(&sess->pfcp, &req->update_far[i], + &cause_value, &offending_ie_value) == NULL) + break; + } + if (cause_value != OGS_PFCP_CAUSE_REQUEST_ACCEPTED) + goto cleanup; + + /* Send End Marker to gNB */ + ogs_list_for_each(&sess->pfcp.pdr_list, pdr) { + far = pdr->far; + if (far && far->smreq_flags.send_end_marker_packets) + ogs_pfcp_send_end_marker(pdr); + } + /* Clear PFCPSMReq-Flags */ + ogs_list_for_each(&sess->pfcp.far_list, far) + far->smreq_flags.value = 0; + + for (i = 0; i < OGS_MAX_NUM_OF_FAR; i++) { + if (ogs_pfcp_handle_update_far(&sess->pfcp, &req->update_far[i], &cause_value, &offending_ie_value) == NULL) break; } @@ -656,7 +225,7 @@ void upf_n4_handle_session_modification_request( goto cleanup; for (i = 0; i < OGS_MAX_NUM_OF_FAR; i++) { - if (handle_remove_far(&sess->pfcp, &req->remove_far[i], + if (ogs_pfcp_handle_remove_far(&sess->pfcp, &req->remove_far[i], &cause_value, &offending_ie_value) == false) break; } @@ -664,7 +233,7 @@ void upf_n4_handle_session_modification_request( goto cleanup; for (i = 0; i < OGS_MAX_NUM_OF_QER; i++) { - if (handle_create_qer(&sess->pfcp, &req->create_qer[i], + if (ogs_pfcp_handle_create_qer(&sess->pfcp, &req->create_qer[i], &cause_value, &offending_ie_value) == NULL) break; } @@ -672,7 +241,7 @@ void upf_n4_handle_session_modification_request( goto cleanup; for (i = 0; i < OGS_MAX_NUM_OF_QER; i++) { - if (handle_update_qer(&sess->pfcp, &req->update_qer[i], + if (ogs_pfcp_handle_update_qer(&sess->pfcp, &req->update_qer[i], &cause_value, &offending_ie_value) == NULL) break; } @@ -680,13 +249,17 @@ void upf_n4_handle_session_modification_request( goto cleanup; for (i = 0; i < OGS_MAX_NUM_OF_QER; i++) { - if (handle_remove_qer(&sess->pfcp, &req->remove_qer[i], + if (ogs_pfcp_handle_remove_qer(&sess->pfcp, &req->remove_qer[i], &cause_value, &offending_ie_value) == false) break; } if (cause_value != OGS_PFCP_CAUSE_REQUEST_ACCEPTED) goto cleanup; + /* Setup GTP Node */ + ogs_list_for_each(&sess->pfcp.far_list, far) + setup_gtp_node(far); + /* Setup UPF-N3-TEID & QFI Hash */ for (i = 0; i < num_of_created_pdr; i++) { pdr = created_pdr[i]; @@ -701,7 +274,7 @@ void upf_n4_handle_session_modification_request( /* Send Buffered Packet to gNB/SGW */ ogs_list_for_each(&sess->pfcp.pdr_list, pdr) { if (pdr->src_if == OGS_PFCP_INTERFACE_CORE) { /* Downlink */ - upf_gtp_send_buffered_packet(pdr); + ogs_pfcp_send_buffered_packet(pdr); } } @@ -726,7 +299,7 @@ void upf_n4_handle_session_deletion_request( ogs_debug("Session Deletion Request"); if (!sess) { - ogs_warn("No Context"); + ogs_error("No Context"); ogs_pfcp_send_error_message(xact, 0, OGS_PFCP_SESSION_DELETION_RESPONSE_TYPE, OGS_PFCP_CAUSE_SESSION_CONTEXT_NOT_FOUND, 0); diff --git a/src/upf/n4-handler.h b/src/upf/n4-handler.h index 5dd46264e..7508cb548 100644 --- a/src/upf/n4-handler.h +++ b/src/upf/n4-handler.h @@ -26,19 +26,6 @@ extern "C" { #endif -void upf_n4_handle_association_setup_request( - ogs_pfcp_node_t *node, ogs_pfcp_xact_t *xact, - ogs_pfcp_association_setup_request_t *req); -void upf_n4_handle_association_setup_response( - ogs_pfcp_node_t *node, ogs_pfcp_xact_t *xact, - ogs_pfcp_association_setup_response_t *req); -void upf_n4_handle_heartbeat_request( - ogs_pfcp_node_t *node, ogs_pfcp_xact_t *xact, - ogs_pfcp_heartbeat_request_t *req); -void upf_n4_handle_heartbeat_response( - ogs_pfcp_node_t *node, ogs_pfcp_xact_t *xact, - ogs_pfcp_heartbeat_response_t *req); - void upf_n4_handle_session_establishment_request( upf_sess_t *sess, ogs_pfcp_xact_t *xact, ogs_pfcp_session_establishment_request_t *req); diff --git a/src/upf/pfcp-path.c b/src/upf/pfcp-path.c index 0b910d9de..d72f560bc 100644 --- a/src/upf/pfcp-path.c +++ b/src/upf/pfcp-path.c @@ -103,9 +103,9 @@ static void pfcp_recv_cb(short when, ogs_socket_t fd, void *data) e = upf_event_new(UPF_EVT_N4_MESSAGE); ogs_assert(e); - node = ogs_pfcp_node_find(&ogs_pfcp_self()->n4_list, &from); + node = ogs_pfcp_node_find(&ogs_pfcp_self()->peer_list, &from); if (!node) { - node = ogs_pfcp_node_add(&ogs_pfcp_self()->n4_list, &from); + node = ogs_pfcp_node_add(&ogs_pfcp_self()->peer_list, &from); ogs_assert(node); node->sock = data; @@ -156,7 +156,7 @@ int upf_pfcp_open(void) ogs_assert(ogs_pfcp_self()->pfcp_addr || ogs_pfcp_self()->pfcp_addr6); - ogs_list_for_each(&ogs_pfcp_self()->n4_list, pfcp_node) + ogs_list_for_each(&ogs_pfcp_self()->peer_list, pfcp_node) pfcp_node_fsm_init(pfcp_node, true); return OGS_OK; @@ -166,110 +166,13 @@ void upf_pfcp_close(void) { ogs_pfcp_node_t *pfcp_node = NULL; - ogs_list_for_each(&ogs_pfcp_self()->n4_list, pfcp_node) + ogs_list_for_each(&ogs_pfcp_self()->peer_list, pfcp_node) pfcp_node_fsm_fini(pfcp_node); ogs_socknode_remove_all(&ogs_pfcp_self()->pfcp_list); ogs_socknode_remove_all(&ogs_pfcp_self()->pfcp_list6); } -static void timeout(ogs_pfcp_xact_t *xact, void *data) -{ - int rv; - - upf_event_t *e = NULL; - uint8_t type; - - ogs_assert(xact); - type = xact->seq[0].type; - - switch (type) { - case OGS_PFCP_HEARTBEAT_REQUEST_TYPE: - ogs_assert(data); - - e = upf_event_new(UPF_EVT_N4_NO_HEARTBEAT); - e->pfcp_node = data; - - rv = ogs_queue_push(upf_self()->queue, e); - if (rv != OGS_OK) { - ogs_warn("ogs_queue_push() failed:%d", (int)rv); - upf_event_free(e); - } - break; - default: - break; - } -} - -void upf_pfcp_send_association_setup_request(ogs_pfcp_node_t *node) -{ - int rv; - ogs_pkbuf_t *n4buf = NULL; - ogs_pfcp_header_t h; - ogs_pfcp_xact_t *xact = NULL; - - ogs_assert(node); - - memset(&h, 0, sizeof(ogs_pfcp_header_t)); - h.type = OGS_PFCP_ASSOCIATION_SETUP_REQUEST_TYPE; - h.seid = 0; - - n4buf = upf_n4_build_association_setup_request(h.type); - ogs_expect_or_return(n4buf); - - xact = ogs_pfcp_xact_local_create(node, &h, n4buf, timeout, node); - ogs_expect_or_return(xact); - - rv = ogs_pfcp_xact_commit(xact); - ogs_expect(rv == OGS_OK); -} - -void upf_pfcp_send_association_setup_response(ogs_pfcp_xact_t *xact, - uint8_t cause) -{ - int rv; - ogs_pkbuf_t *n4buf = NULL; - ogs_pfcp_header_t h; - - ogs_assert(xact); - - memset(&h, 0, sizeof(ogs_pfcp_header_t)); - h.type = OGS_PFCP_ASSOCIATION_SETUP_RESPONSE_TYPE; - h.seid = 0; - - n4buf = upf_n4_build_association_setup_response(h.type, cause); - ogs_expect_or_return(n4buf); - - rv = ogs_pfcp_xact_update_tx(xact, &h, n4buf); - ogs_expect_or_return(rv == OGS_OK); - - rv = ogs_pfcp_xact_commit(xact); - ogs_expect(rv == OGS_OK); -} - -void upf_pfcp_send_heartbeat_request(ogs_pfcp_node_t *node) -{ - int rv; - ogs_pkbuf_t *n4buf = NULL; - ogs_pfcp_header_t h; - ogs_pfcp_xact_t *xact = NULL; - - ogs_assert(node); - - memset(&h, 0, sizeof(ogs_pfcp_header_t)); - h.type = OGS_PFCP_HEARTBEAT_REQUEST_TYPE; - h.seid = 0; - - n4buf = ogs_pfcp_n4_build_heartbeat_request(h.type); - ogs_expect_or_return(n4buf); - - xact = ogs_pfcp_xact_local_create(node, &h, n4buf, timeout, node); - ogs_expect_or_return(xact); - - rv = ogs_pfcp_xact_commit(xact); - ogs_expect(rv == OGS_OK); -} - void upf_pfcp_send_session_establishment_response( ogs_pfcp_xact_t *xact, upf_sess_t *sess, ogs_pfcp_pdr_t *created_pdr[], int num_of_created_pdr) diff --git a/src/upf/pfcp-path.h b/src/upf/pfcp-path.h index 8a8016471..06dadce85 100644 --- a/src/upf/pfcp-path.h +++ b/src/upf/pfcp-path.h @@ -29,11 +29,6 @@ extern "C" { int upf_pfcp_open(void); void upf_pfcp_close(void); -void upf_pfcp_send_association_setup_request(ogs_pfcp_node_t *node); -void upf_pfcp_send_association_setup_response(ogs_pfcp_xact_t *xact, - uint8_t cause); -void upf_pfcp_send_heartbeat_request(ogs_pfcp_node_t *node); - void upf_pfcp_send_session_establishment_response( ogs_pfcp_xact_t *xact, upf_sess_t *sess, ogs_pfcp_pdr_t *created_pdr[], int num_of_created_pdr); diff --git a/src/upf/pfcp-sm.c b/src/upf/pfcp-sm.c index 90cadab9c..b737a0be9 100644 --- a/src/upf/pfcp-sm.c +++ b/src/upf/pfcp-sm.c @@ -25,6 +25,8 @@ #include "pfcp-path.h" #include "n4-handler.h" +static void node_timeout(ogs_pfcp_xact_t *xact, void *data); + void upf_pfcp_state_initial(ogs_fsm_t *s, upf_event_t *e) { int rv; @@ -85,7 +87,7 @@ void upf_pfcp_state_will_associate(ogs_fsm_t *s, upf_event_t *e) ogs_timer_start(node->t_association, ogs_config()->time.message.pfcp.association_interval); - upf_pfcp_send_association_setup_request(node); + ogs_pfcp_up_send_association_setup_request(node, node_timeout); } break; @@ -108,7 +110,7 @@ void upf_pfcp_state_will_associate(ogs_fsm_t *s, upf_event_t *e) ogs_timer_start(node->t_association, ogs_config()->time.message.pfcp.association_interval); - upf_pfcp_send_association_setup_request(node); + ogs_pfcp_up_send_association_setup_request(node, node_timeout); break; default: ogs_error("Unknown timer[%s:%d]", @@ -124,12 +126,12 @@ void upf_pfcp_state_will_associate(ogs_fsm_t *s, upf_event_t *e) switch (message->h.type) { case OGS_PFCP_ASSOCIATION_SETUP_REQUEST_TYPE: - upf_n4_handle_association_setup_request(node, xact, + ogs_pfcp_up_handle_association_setup_request(node, xact, &message->pfcp_association_setup_request); OGS_FSM_TRAN(s, upf_pfcp_state_associated); break; case OGS_PFCP_ASSOCIATION_SETUP_RESPONSE_TYPE: - upf_n4_handle_association_setup_response(node, xact, + ogs_pfcp_up_handle_association_setup_response(node, xact, &message->pfcp_association_setup_response); OGS_FSM_TRAN(s, upf_pfcp_state_associated); break; @@ -187,21 +189,21 @@ void upf_pfcp_state_associated(ogs_fsm_t *s, upf_event_t *e) switch (message->h.type) { case OGS_PFCP_HEARTBEAT_REQUEST_TYPE: - upf_n4_handle_heartbeat_request(node, xact, + ogs_pfcp_handle_heartbeat_request(node, xact, &message->pfcp_heartbeat_request); break; case OGS_PFCP_HEARTBEAT_RESPONSE_TYPE: - upf_n4_handle_heartbeat_response(node, xact, + ogs_pfcp_handle_heartbeat_response(node, xact, &message->pfcp_heartbeat_response); break; case OGS_PFCP_ASSOCIATION_SETUP_REQUEST_TYPE: ogs_warn("PFCP[REQ] has already been associated"); - upf_n4_handle_association_setup_request(node, xact, + ogs_pfcp_up_handle_association_setup_request(node, xact, &message->pfcp_association_setup_request); break; case OGS_PFCP_ASSOCIATION_SETUP_RESPONSE_TYPE: ogs_warn("PFCP[RSP] has already been associated"); - upf_n4_handle_association_setup_response(node, xact, + ogs_pfcp_up_handle_association_setup_response(node, xact, &message->pfcp_association_setup_response); break; case OGS_PFCP_SESSION_ESTABLISHMENT_REQUEST_TYPE: @@ -235,7 +237,7 @@ void upf_pfcp_state_associated(ogs_fsm_t *s, upf_event_t *e) node = e->pfcp_node; ogs_assert(node); - upf_pfcp_send_heartbeat_request(node); + ogs_pfcp_send_heartbeat_request(node, node_timeout); break; default: ogs_error("Unknown timer[%s:%d]", @@ -271,3 +273,34 @@ void upf_pfcp_state_exception(ogs_fsm_t *s, upf_event_t *e) break; } } + +static void node_timeout(ogs_pfcp_xact_t *xact, void *data) +{ + int rv; + + upf_event_t *e = NULL; + uint8_t type; + + ogs_assert(xact); + type = xact->seq[0].type; + + switch (type) { + case OGS_PFCP_HEARTBEAT_REQUEST_TYPE: + ogs_assert(data); + + e = upf_event_new(UPF_EVT_N4_NO_HEARTBEAT); + e->pfcp_node = data; + + rv = ogs_queue_push(upf_self()->queue, e); + if (rv != OGS_OK) { + ogs_warn("ogs_queue_push() failed:%d", (int)rv); + upf_event_free(e); + } + break; + case OGS_PFCP_ASSOCIATION_SETUP_REQUEST_TYPE: + break; + default: + ogs_error("Not implemented [type:%d]", type); + break; + } +} diff --git a/src/upf/rule-match.c b/src/upf/rule-match.c index 335ae451c..8600cfde3 100644 --- a/src/upf/rule-match.c +++ b/src/upf/rule-match.c @@ -154,9 +154,9 @@ ogs_pfcp_pdr_t *upf_pdr_find_by_packet(ogs_pkbuf_t *pkt) if (sess) { ogs_pfcp_pdr_t *default_pdr = NULL; - upf_sdf_filter_t *sdf_filter = NULL; ogs_pfcp_pdr_t *pdr = NULL; ogs_pfcp_far_t *far = NULL; + ogs_pfcp_rule_t *rule = NULL; if (ip_h && sess->ipv4) ogs_debug("PAA IPv4:%s", OGS_INET_NTOP(&sess->ipv4->addr, buf)); @@ -170,141 +170,149 @@ ogs_pfcp_pdr_t *upf_pdr_find_by_packet(ogs_pkbuf_t *pkt) /* Found */ ogs_debug("Found Session : Default PDR-ID[%d]", default_pdr->id); - ogs_list_for_each(&sess->sdf_filter_list, sdf_filter) { - int k; - uint32_t src_mask[4]; - uint32_t dst_mask[4]; - ogs_ipfw_rule_t *rule = NULL; + ogs_list_for_each(&sess->pfcp.pdr_list, pdr) { + ogs_list_for_each(&pdr->rule_list, rule) { + int k; + uint32_t src_mask[4]; + uint32_t dst_mask[4]; + ogs_ipfw_rule_t *ipfw = NULL; - pdr = sdf_filter->pdr; - ogs_assert(pdr); - far = pdr->far; - ogs_assert(far); - rule = &sdf_filter->rule; - ogs_assert(rule); + pdr = rule->pdr; + ogs_assert(pdr); + far = pdr->far; + ogs_assert(far); + ipfw = &rule->ipfw; + ogs_assert(ipfw); - /* Skip if PDR is default */ - if (pdr->id == default_pdr->id) - continue; + /* Skip if PDR is default */ + if (pdr->id == default_pdr->id) + continue; - /* Check if PDR is Downlink */ - if (pdr->src_if != OGS_PFCP_INTERFACE_CORE) - continue; + /* Check if PDR is Downlink */ + if (pdr->src_if != OGS_PFCP_INTERFACE_CORE) + continue; - /* Check if FAR is Downlink */ - if (far->dst_if != OGS_PFCP_INTERFACE_ACCESS) - continue; + /* Check if FAR is Downlink */ + if (far->dst_if != OGS_PFCP_INTERFACE_ACCESS) + continue; - /* Check if Create Bearer Response is received */ - if (far->outer_header_creation.teid == 0) - continue; + /* Check if Outer header creation */ + if (far->outer_header_creation.teid == 0) + continue; - ogs_debug("PROTO:%d SRC:%d-%d DST:%d-%d", - rule->proto, - rule->port.local.low, - rule->port.local.high, - rule->port.remote.low, - rule->port.remote.high); - ogs_debug("SRC:%08x %08x %08x %08x/%08x %08x %08x %08x", - be32toh(rule->ip.local.addr[0]), - be32toh(rule->ip.local.addr[1]), - be32toh(rule->ip.local.addr[2]), - be32toh(rule->ip.local.addr[3]), - be32toh(rule->ip.local.mask[0]), - be32toh(rule->ip.local.mask[1]), - be32toh(rule->ip.local.mask[2]), - be32toh(rule->ip.local.mask[3])); - ogs_debug("DST:%08x %08x %08x %08x/%08x %08x %08x %08x", - be32toh(rule->ip.remote.addr[0]), - be32toh(rule->ip.remote.addr[1]), - be32toh(rule->ip.remote.addr[2]), - be32toh(rule->ip.remote.addr[3]), - be32toh(rule->ip.remote.mask[0]), - be32toh(rule->ip.remote.mask[1]), - be32toh(rule->ip.remote.mask[2]), - be32toh(rule->ip.remote.mask[3])); + ogs_debug("PROTO:%d SRC:%d-%d DST:%d-%d", + ipfw->proto, + ipfw->port.local.low, + ipfw->port.local.high, + ipfw->port.remote.low, + ipfw->port.remote.high); + ogs_debug("SRC:%08x %08x %08x %08x/%08x %08x %08x %08x", + be32toh(ipfw->ip.local.addr[0]), + be32toh(ipfw->ip.local.addr[1]), + be32toh(ipfw->ip.local.addr[2]), + be32toh(ipfw->ip.local.addr[3]), + be32toh(ipfw->ip.local.mask[0]), + be32toh(ipfw->ip.local.mask[1]), + be32toh(ipfw->ip.local.mask[2]), + be32toh(ipfw->ip.local.mask[3])); + ogs_debug("DST:%08x %08x %08x %08x/%08x %08x %08x %08x", + be32toh(ipfw->ip.remote.addr[0]), + be32toh(ipfw->ip.remote.addr[1]), + be32toh(ipfw->ip.remote.addr[2]), + be32toh(ipfw->ip.remote.addr[3]), + be32toh(ipfw->ip.remote.mask[0]), + be32toh(ipfw->ip.remote.mask[1]), + be32toh(ipfw->ip.remote.mask[2]), + be32toh(ipfw->ip.remote.mask[3])); - for (k = 0; k < 4; k++) { - src_mask[k] = src_addr[k] & rule->ip.local.mask[k]; - dst_mask[k] = dst_addr[k] & rule->ip.remote.mask[k]; - } - - if (memcmp(src_mask, rule->ip.local.addr, addr_len) == 0 && - memcmp(dst_mask, rule->ip.remote.addr, addr_len) == 0) { - /* Protocol match */ - if (rule->proto == 0) { /* IP */ - /* No need to match port */ - break; + for (k = 0; k < 4; k++) { + src_mask[k] = src_addr[k] & ipfw->ip.local.mask[k]; + dst_mask[k] = dst_addr[k] & ipfw->ip.remote.mask[k]; } - if (rule->proto == proto) { - if (rule->proto == IPPROTO_TCP) { - struct tcphdr *tcph = - (struct tcphdr *)((char *)pkt->data + ip_hlen); - - /* Source port */ - if (rule->port.local.low && - be16toh(tcph->th_sport) < rule->port.local.low) { - continue; - } - - if (rule->port.local.high && - be16toh(tcph->th_sport) > rule->port.local.high) { - continue; - } - - /* Dst Port*/ - if (rule->port.remote.low && - be16toh(tcph->th_dport) < rule->port.remote.low) { - continue; - } - - if (rule->port.remote.high && - be16toh(tcph->th_dport) > - rule->port.remote.high) { - continue; - } - - /* Matched */ - break; - } else if (rule->proto == IPPROTO_UDP) { - struct udphdr *udph = - (struct udphdr *)((char *)pkt->data + ip_hlen); - - /* Source port */ - if (rule->port.local.low && - be16toh(udph->uh_sport) < rule->port.local.low) { - continue; - } - - if (rule->port.local.high && - be16toh(udph->uh_sport) > rule->port.local.high) { - continue; - } - - /* Dst Port*/ - if (rule->port.remote.low && - be16toh(udph->uh_dport) < rule->port.remote.low) { - continue; - } - - if (rule->port.remote.high && - be16toh(udph->uh_dport) > - rule->port.remote.high) { - continue; - } - - /* Matched */ - break; - } else { + if (memcmp(src_mask, ipfw->ip.local.addr, addr_len) == 0 && + memcmp(dst_mask, ipfw->ip.remote.addr, addr_len) == 0) { + /* Protocol match */ + if (ipfw->proto == 0) { /* IP */ /* No need to match port */ break; } + + if (ipfw->proto == proto) { + if (ipfw->proto == IPPROTO_TCP) { + struct tcphdr *tcph = + (struct tcphdr *)((char *)pkt->data + ip_hlen); + + /* Source port */ + if (ipfw->port.local.low && + be16toh(tcph->th_sport) < + ipfw->port.local.low) { + continue; + } + + if (ipfw->port.local.high && + be16toh(tcph->th_sport) > + ipfw->port.local.high) { + continue; + } + + /* Dst Port*/ + if (ipfw->port.remote.low && + be16toh(tcph->th_dport) < + ipfw->port.remote.low) { + continue; + } + + if (ipfw->port.remote.high && + be16toh(tcph->th_dport) > + ipfw->port.remote.high) { + continue; + } + + /* Matched */ + break; + } else if (ipfw->proto == IPPROTO_UDP) { + struct udphdr *udph = + (struct udphdr *)((char *)pkt->data + ip_hlen); + + /* Source port */ + if (ipfw->port.local.low && + be16toh(udph->uh_sport) < + ipfw->port.local.low) { + continue; + } + + if (ipfw->port.local.high && + be16toh(udph->uh_sport) > + ipfw->port.local.high) { + continue; + } + + /* Dst Port*/ + if (ipfw->port.remote.low && + be16toh(udph->uh_dport) < + ipfw->port.remote.low) { + continue; + } + + if (ipfw->port.remote.high && + be16toh(udph->uh_dport) > + ipfw->port.remote.high) { + continue; + } + + /* Matched */ + break; + } else { + /* No need to match port */ + break; + } + } } } } - if (sdf_filter) { + if (rule) { ogs_debug("Found Dedicated PDR : PDR ID[%d]", pdr->id); return pdr; } diff --git a/tests/23504/abts-main.c b/tests/23504/abts-main.c index e025c2f05..43ba11f8a 100644 --- a/tests/23504/abts-main.c +++ b/tests/23504/abts-main.c @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -#include "test-epc.h" +#include "test-app.h" abts_suite *test_r16(abts_suite *suite); @@ -35,7 +35,7 @@ static void terminate(void) test_child_terminate(); app_terminate(); - test_epc_final(); + test_app_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_epc_init(); + test_app_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_epc_run(argc, argv, "23504.yaml", initialize); + test_5gc_run(argc, argv, "23504.yaml", initialize); for (i = 0; alltests[i].func; i++) suite = alltests[i].func(suite); diff --git a/tests/23504/meson.build b/tests/23504/meson.build index 3be51d9db..9b0189616 100644 --- a/tests/23504/meson.build +++ b/tests/23504/meson.build @@ -15,12 +15,12 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -test5gc_23504_sources = files(''' +testapp_23504_sources = files(''' abts-main.c r16-test.c '''.split()) -test5gc_23504_exe = executable('23504', - sources : test5gc_23504_sources, +testapp_23504_exe = executable('23504', + sources : testapp_23504_sources, c_args : testunit_core_cc_flags, - dependencies : libtestepc_dep) + dependencies : libtestapp_dep) diff --git a/tests/app/app-init.c b/tests/app/app-init.c new file mode 100644 index 000000000..acf09025a --- /dev/null +++ b/tests/app/app-init.c @@ -0,0 +1,99 @@ +/* + * 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" + +static ogs_thread_t *nrf_thread = NULL; +static ogs_thread_t *pcrf_thread = NULL; +static ogs_thread_t *upf_thread = NULL; +static ogs_thread_t *smf_thread = NULL; +static ogs_thread_t *sgwc_thread = NULL; +static ogs_thread_t *sgwu_thread = NULL; +static ogs_thread_t *hss_thread = NULL; +static ogs_thread_t *mme_thread = NULL; + +int app_initialize(const char *const argv[]) +{ + 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_pcrf == 0) + pcrf_thread = test_child_create("pcrf", argv_out); + if (ogs_config()->parameter.no_hss == 0) + hss_thread = test_child_create("hss", argv_out); + if (ogs_config()->parameter.no_upf == 0) + upf_thread = test_child_create("upf", argv_out); + if (ogs_config()->parameter.no_sgwu == 0) + sgwu_thread = test_child_create("sgwu", argv_out); + if (ogs_config()->parameter.no_smf == 0) + smf_thread = test_child_create("smf", argv_out); + if (ogs_config()->parameter.no_sgwc == 0) + sgwc_thread = test_child_create("sgwc", argv_out); + if (ogs_config()->parameter.no_mme == 0) + mme_thread = test_child_create("mme", argv_out); + + return OGS_OK;; +} + +void app_terminate(void) +{ + if (mme_thread) ogs_thread_destroy(mme_thread); + if (sgwc_thread) ogs_thread_destroy(sgwc_thread); + if (smf_thread) ogs_thread_destroy(smf_thread); + if (sgwu_thread) ogs_thread_destroy(sgwu_thread); + if (upf_thread) ogs_thread_destroy(upf_thread); + if (hss_thread) ogs_thread_destroy(hss_thread); + if (pcrf_thread) ogs_thread_destroy(pcrf_thread); + if (nrf_thread) ogs_thread_destroy(nrf_thread); +} + +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_sctp_init(ogs_config()->usrsctp.udp_port); + ogs_assert(ogs_dbi_init(ogs_config()->db_uri) == OGS_OK); +} + +void test_app_final(void) +{ + ogs_dbi_final(); + ogs_sctp_final(); +} diff --git a/tests/app/epc-init.c b/tests/app/epc-init.c index fb1123caa..06757dfd6 100644 --- a/tests/app/epc-init.c +++ b/tests/app/epc-init.c @@ -19,9 +19,12 @@ #include "test-epc.h" +static ogs_thread_t *nrf_thread = NULL; 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 *upf_thread = NULL; +static ogs_thread_t *smf_thread = NULL; +static ogs_thread_t *sgwc_thread = NULL; +static ogs_thread_t *sgwu_thread = NULL; static ogs_thread_t *hss_thread = NULL; int app_initialize(const char *const argv[]) @@ -46,14 +49,20 @@ int app_initialize(const char *const argv[]) argv_out[i] = NULL; } + if (ogs_config()->parameter.no_nrf == 0) + nrf_thread = test_child_create("nrf", argv_out); if (ogs_config()->parameter.no_pcrf == 0) pcrf_thread = test_child_create("pcrf", argv_out); - if (ogs_config()->parameter.no_pgw == 0) - pgw_thread = test_child_create("pgw", argv_out); - if (ogs_config()->parameter.no_sgw == 0) - sgw_thread = test_child_create("sgw", argv_out); if (ogs_config()->parameter.no_hss == 0) hss_thread = test_child_create("hss", argv_out); + if (ogs_config()->parameter.no_upf == 0) + upf_thread = test_child_create("upf", argv_out); + if (ogs_config()->parameter.no_sgwu == 0) + sgwu_thread = test_child_create("sgwu", argv_out); + if (ogs_config()->parameter.no_smf == 0) + smf_thread = test_child_create("smf", argv_out); + if (ogs_config()->parameter.no_sgwc == 0) + sgwc_thread = test_child_create("sgwc", argv_out); ogs_sctp_init(ogs_config()->usrsctp.udp_port); @@ -71,10 +80,13 @@ void app_terminate(void) ogs_sctp_final(); ogs_info("MME terminate...done"); + if (sgwc_thread) ogs_thread_destroy(sgwc_thread); + if (smf_thread) ogs_thread_destroy(smf_thread); + if (sgwu_thread) ogs_thread_destroy(sgwu_thread); + if (upf_thread) ogs_thread_destroy(upf_thread); if (hss_thread) ogs_thread_destroy(hss_thread); - if (sgw_thread) ogs_thread_destroy(sgw_thread); - if (pgw_thread) ogs_thread_destroy(pgw_thread); if (pcrf_thread) ogs_thread_destroy(pcrf_thread); + if (nrf_thread) ogs_thread_destroy(nrf_thread); } void test_epc_init(void) diff --git a/tests/app/meson.build b/tests/app/meson.build index f56279dc2..47b9267db 100644 --- a/tests/app/meson.build +++ b/tests/app/meson.build @@ -86,3 +86,43 @@ executable('5gc', c_args : libtest5gc_cc_args, include_directories : srcinc, dependencies : libtest5gc_dep) + +libtestapp_cc_args = '-DDEFAULT_CONFIG_FILENAME="@0@/configs/app.yaml"'.format(meson.build_root()) + +libtestapp_sources = files(''' + test-packet.c + app-init.c +'''.split()) + +libtestapp = static_library('testapp', + sources : [libtestapp_sources], + c_args : libtestapp_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, srcinc], + dependencies : [libtestcommon_dep, + libmme_dep, + libhss_dep, + libsgw_dep, + libpgw_dep, + libpcrf_dep]) + +testapp_sources = files(''' + ../../src/main.c +'''.split()) + +executable('app', + sources : [testapp_sources], + c_args : libtestapp_cc_args, + include_directories : srcinc, + dependencies : libtestapp_dep) diff --git a/tests/app/test-app.h b/tests/app/test-app.h new file mode 100644 index 000000000..3f0c235dd --- /dev/null +++ b/tests/app/test-app.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_APP_H +#define TEST_APP_H + +#include "test-packet.h" + +#include "mme/mme-context.h" + +#ifdef __cplusplus +extern "C" { +#endif + +void test_app_init(void); +void test_app_final(void); + +#ifdef __cplusplus +} +#endif + +#endif /* TEST_APP_H */ diff --git a/tests/common/application.c b/tests/common/application.c index 02f8fa331..f4d0113bb 100644 --- a/tests/common/application.c +++ b/tests/common/application.c @@ -78,10 +78,12 @@ void test_epc_run(int argc, const char *const argv[], test_app_run(argc, argv, name, init); +#if 0 /* We will not use this */ while(1) { if (connected_count == 1) break; ogs_msleep(50); } +#endif ogs_msleep(500); /* Wait for listening all sockets */ } diff --git a/tests/common/context.h b/tests/common/context.h index 3f6979b83..c5c1af99e 100644 --- a/tests/common/context.h +++ b/tests/common/context.h @@ -28,6 +28,15 @@ extern "C" { #endif +#define TEST_MME_IPV4 "127.0.0.1" +#define TEST_ENB_IPV4 "127.0.0.5" +#define TEST_AMF_IPV4 TEST_MME_IPV4 +#define TEST_GNB_IPV4 TEST_ENB_IPV4 +#define TEST_UPF_IPV4 "127.0.0.4" +#define TEST_SGWU_IPV4 "127.0.0.7" + +#define TEST_PING_IPV4 "10.45.0.1" + #define MAX_NUM_OF_SERVED_GUAMI 8 typedef struct test_context_s { diff --git a/tests/common/gtpu.c b/tests/common/gtpu.c index b7c826e19..755a4c694 100644 --- a/tests/common/gtpu.c +++ b/tests/common/gtpu.c @@ -86,6 +86,33 @@ int testgnb_gtpu_send(ogs_socknode_t *node, ogs_pkbuf_t *sendbuf) return OGS_OK; } +int testgnb_gtpu_sendto( + ogs_socknode_t *node, ogs_pkbuf_t *sendbuf, const char *ipstr) +{ + int rv; + + ogs_sockaddr_t *addr; + int port = OGS_GTPV1_U_UDP_PORT; + ssize_t sent; + + ogs_assert(node); + ogs_assert(node->sock); + ogs_assert(ipstr); + + rv = ogs_getaddrinfo(&addr, AF_UNSPEC, ipstr, port, 0); + ogs_assert(rv == OGS_OK); + + sent = ogs_sendto(node->sock->fd, sendbuf->data, sendbuf->len, 0, addr); + + ogs_pkbuf_free(sendbuf); + ogs_freeaddrinfo(addr); + + if (sent < 0 || sent != sendbuf->len) + return OGS_ERROR; + + return OGS_OK; +} + void test_gtpu_close(ogs_socknode_t *node) { ogs_socknode_free(node); @@ -129,30 +156,44 @@ int test_gtpu_build_ping(ogs_pkbuf_t **sendbuf, memset(pkbuf->data, 0, pkbuf->len); gtp_h = (ogs_gtp_header_t *)pkbuf->data; - gtp_h->flags = 0x34; + if (sess->qfi) + gtp_h->flags = 0x34; + else + gtp_h->flags = 0x30; gtp_h->type = OGS_GTPU_MSGTYPE_GPDU; gtp_h->teid = htobe32(sess->upf_n3_teid); - ext_h = (ogs_gtp_extension_header_t *)(pkbuf->data + OGS_GTPV1U_HEADER_LEN); - ext_h->type = OGS_GTP_EXTENSION_HEADER_TYPE_PDU_SESSION_CONTAINER; - ext_h->len = 1; - ext_h->pdu_type = - OGS_GTP_EXTENSION_HEADER_PDU_TYPE_UL_PDU_SESSION_INFORMATION; - ext_h->qos_flow_identifier = 1; - ext_h->next_type = OGS_GTP_EXTENSION_HEADER_TYPE_NO_MORE_EXTENSION_HEADERS; + if (sess->qfi) { + ext_h = (ogs_gtp_extension_header_t *) + (pkbuf->data + OGS_GTPV1U_HEADER_LEN); + ext_h->type = OGS_GTP_EXTENSION_HEADER_TYPE_PDU_SESSION_CONTAINER; + ext_h->len = 1; + ext_h->pdu_type = + OGS_GTP_EXTENSION_HEADER_PDU_TYPE_UL_PDU_SESSION_INFORMATION; + ext_h->qos_flow_identifier = sess->qfi; + ext_h->next_type = + OGS_GTP_EXTENSION_HEADER_TYPE_NO_MORE_EXTENSION_HEADERS; + } if (dst_ipsub.family == AF_INET) { struct ip *ip_h = NULL; struct icmp *icmp_h = NULL; - gtp_h->length = htobe16( - sizeof *ip_h + ICMP_MINLEN + - OGS_GTPV1U_EXTENSION_HEADER_LEN + ext_h->len * 4); + if (sess->qfi) { + gtp_h->length = htobe16( + sizeof *ip_h + ICMP_MINLEN + + OGS_GTPV1U_EXTENSION_HEADER_LEN + ext_h->len * 4); - ip_h = (struct ip *)(pkbuf->data + - OGS_GTPV1U_HEADER_LEN + - OGS_GTPV1U_EXTENSION_HEADER_LEN + ext_h->len * 4); - icmp_h = (struct icmp *)((uint8_t *)ip_h + sizeof *ip_h); + ip_h = (struct ip *)(pkbuf->data + + OGS_GTPV1U_HEADER_LEN + + OGS_GTPV1U_EXTENSION_HEADER_LEN + ext_h->len * 4); + icmp_h = (struct icmp *)((uint8_t *)ip_h + sizeof *ip_h); + } else { + gtp_h->length = htobe16(sizeof *ip_h + ICMP_MINLEN); + + ip_h = (struct ip *)(pkbuf->data + OGS_GTPV1U_HEADER_LEN); + icmp_h = (struct icmp *)((uint8_t *)ip_h + sizeof *ip_h); + } ip_h->ip_v = 4; ip_h->ip_hl = 5; diff --git a/tests/common/gtpu.h b/tests/common/gtpu.h index 1dff983ee..15785d880 100644 --- a/tests/common/gtpu.h +++ b/tests/common/gtpu.h @@ -31,6 +31,8 @@ extern "C" { ogs_socknode_t *test_gtpu_server(const char *ipstr, int port); ogs_pkbuf_t *test_gtpu_read(ogs_socknode_t *node); int testgnb_gtpu_send(ogs_socknode_t *node, ogs_pkbuf_t *sendbuf); +int testgnb_gtpu_sendto( + ogs_socknode_t *node, ogs_pkbuf_t *sendbuf, const char *ipstr); void test_gtpu_close(ogs_socknode_t *node); int test_gtpu_build_ping(ogs_pkbuf_t **sendbuf, diff --git a/tests/csfb/abts-main.c b/tests/csfb/abts-main.c index a107635ca..c584ab062 100644 --- a/tests/csfb/abts-main.c +++ b/tests/csfb/abts-main.c @@ -40,9 +40,12 @@ const struct testlist { {NULL}, }; +static ogs_thread_t *nrf_thread = NULL; 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 *upf_thread = NULL; +static ogs_thread_t *smf_thread = NULL; +static ogs_thread_t *sgwc_thread = NULL; +static ogs_thread_t *sgwu_thread = NULL; static ogs_thread_t *hss_thread = NULL; ogs_socknode_t *sgsap = NULL; @@ -61,10 +64,13 @@ static void terminate(void) test_epc_final(); ogs_info("MME terminate...done"); + if (sgwc_thread) ogs_thread_destroy(sgwc_thread); + if (smf_thread) ogs_thread_destroy(smf_thread); + if (sgwu_thread) ogs_thread_destroy(sgwu_thread); + if (upf_thread) ogs_thread_destroy(upf_thread); if (hss_thread) ogs_thread_destroy(hss_thread); - if (sgw_thread) ogs_thread_destroy(sgw_thread); - if (pgw_thread) ogs_thread_destroy(pgw_thread); if (pcrf_thread) ogs_thread_destroy(pcrf_thread); + if (nrf_thread) ogs_thread_destroy(nrf_thread); ogs_app_terminate(); } @@ -76,10 +82,20 @@ static void initialize(const char *const argv[]) rv = ogs_app_initialize(NULL, argv); ogs_assert(rv == OGS_OK); - pcrf_thread = test_child_create("pcrf", argv); - pgw_thread = test_child_create("pgw", argv); - sgw_thread = test_child_create("sgw", argv); - hss_thread = test_child_create("hss", argv); + if (ogs_config()->parameter.no_nrf == 0) + nrf_thread = test_child_create("nrf", argv); + if (ogs_config()->parameter.no_pcrf == 0) + pcrf_thread = test_child_create("pcrf", argv); + if (ogs_config()->parameter.no_hss == 0) + hss_thread = test_child_create("hss", argv); + if (ogs_config()->parameter.no_upf == 0) + upf_thread = test_child_create("upf", argv); + if (ogs_config()->parameter.no_sgwu == 0) + sgwu_thread = test_child_create("sgwu", argv); + if (ogs_config()->parameter.no_smf == 0) + smf_thread = test_child_create("smf", argv); + if (ogs_config()->parameter.no_sgwc == 0) + sgwc_thread = test_child_create("sgwc", argv); test_epc_init(); ogs_sctp_init(ogs_config()->usrsctp.udp_port); diff --git a/tests/csfb/mo-idle-test.c b/tests/csfb/mo-idle-test.c index 83dd3174e..583820ae4 100644 --- a/tests/csfb/mo-idle-test.c +++ b/tests/csfb/mo-idle-test.c @@ -52,11 +52,11 @@ static void test1_func(abts_case *tc, void *data) "036f72670a010104 0509f1070926"; const char *_initial_context_setup_request = "00090080e9000007 0000000200010008 000200010042000a 183d090000603d09" - "0000001800808e00 0034008088450009 230f807f00000200 00000179270104e1" - "7e02074202490620 09f1070007004652 34c101090908696e 7465726e65740501" + "0000001800808e00 0034008088450009 230f807f00000700 000002792758bf47" + "8002074202490620 09f1070007004652 34c101090908696e 7465726e65740501" "0a2d00025e06fefe e2e2030327278080 2110020000108106 0808080883060808" - "0404000d04080808 08000d0408080404 0010020578500bf6 09f107000201c100" - "c3ba1309f1070926 2305f49ee88e6459 4964020108006b00 051c000e00000049" + "0404000d04080808 08000d0408080404 0010020578500bf6 09f107000201ea00" + "8e461309f1070926 2305f49ee88e6459 4964020108006b00 051c000e00000049" "0020f9f4f80b206c 33ae286c6daff4c2 53585174c3a0a12a 661967f5e1ba0a68" "6c8c00c040083572 200924ffff14"; const char *_emm_information = diff --git a/tests/cups/abts-main.c b/tests/cups/abts-main.c index ab15b50f6..39478368a 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-epc.h" +#include "test-app.h" #include "pcscf-fd-path.h" @@ -30,33 +30,16 @@ const struct testlist { {NULL}, }; -static ogs_thread_t *nrf_thread = NULL; -static ogs_thread_t *pcrf_thread = NULL; -static ogs_thread_t *upf_thread = NULL; -static ogs_thread_t *smf_thread = NULL; -static ogs_thread_t *sgw_thread = NULL; -static ogs_thread_t *hss_thread = NULL; -static ogs_thread_t *mme_thread = NULL; - static void terminate(void) { ogs_msleep(50); test_child_terminate(); - - if (mme_thread) ogs_thread_destroy(mme_thread); - if (hss_thread) ogs_thread_destroy(hss_thread); - if (sgw_thread) ogs_thread_destroy(sgw_thread); - if (smf_thread) ogs_thread_destroy(smf_thread); - if (upf_thread) ogs_thread_destroy(upf_thread); - if (pcrf_thread) ogs_thread_destroy(pcrf_thread); - if (nrf_thread) ogs_thread_destroy(nrf_thread); + app_terminate(); pcscf_fd_final(); - ogs_sctp_final(); - - test_epc_final(); + test_app_final(); ogs_app_terminate(); } @@ -64,21 +47,12 @@ static void initialize(const char *const argv[]) { int rv; - test_no_mme_self = true; - rv = ogs_app_initialize(NULL, argv); ogs_assert(rv == OGS_OK); + test_app_init(); - nrf_thread = test_child_create("nrf", argv); - pcrf_thread = test_child_create("pcrf", argv); - upf_thread = test_child_create("upf", argv); - smf_thread = test_child_create("smf", argv); - sgw_thread = test_child_create("sgw", argv); - hss_thread = test_child_create("hss", argv); - mme_thread = test_child_create("mme", argv); - - test_epc_init(); - ogs_sctp_init(ogs_config()->usrsctp.udp_port); + rv = app_initialize(argv); + ogs_assert(rv == OGS_OK); rv = pcscf_fd_init(); ogs_assert(rv == OGS_OK); @@ -90,7 +64,8 @@ int main(int argc, const char *const argv[]) abts_suite *suite = NULL; atexit(terminate); - test_epc_run(argc, argv, "cups.yaml", initialize); + test_5gc_run(argc, argv, "cups.yaml", initialize); + ogs_msleep(1000); 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 c72e2afc4..4ca6f769a 100644 --- a/tests/cups/cups-test.c +++ b/tests/cups/cups-test.c @@ -20,8 +20,7 @@ #include "test-epc.h" #include "pcscf-fd-path.h" -#define TEST1_PING 1 -#define TEST3_PING 0 +#define TEST3_ONLY_PING 0 static void cups_test1(abts_case *tc, void *data) { @@ -35,6 +34,9 @@ static void cups_test1(abts_case *tc, void *data) int msgindex = 0; uint8_t *rx_sid = NULL; + test_ue_t test_ue; + test_sess_t test_sess; + mongoc_collection_t *collection = NULL; bson_t *doc = NULL; int64_t count = 0; @@ -97,12 +99,27 @@ static void cups_test1(abts_case *tc, void *data) "\"__v\" : 0" "}"; + /* Setup Test UE & Session Context */ + memset(&test_ue, 0, sizeof(test_ue)); + memset(&test_sess, 0, sizeof(test_sess)); + test_sess.test_ue = &test_ue; + test_ue.sess = &test_sess; + + test_ue.imsi = (char *)"001010123456819"; + + test_sess.gnb_n3_ip.ipv4 = true; + test_sess.gnb_n3_ip.addr = inet_addr(TEST_GNB_IPV4); + test_sess.gnb_n3_teid = 0; + + test_sess.upf_n3_ip.ipv4 = true; + test_sess.upf_n3_ip.addr = inet_addr(TEST_SGWU_IPV4); + /* eNB connects to MME */ - s1ap = testenb_s1ap_client("127.0.0.1"); + s1ap = testenb_s1ap_client(TEST_MME_IPV4); ABTS_PTR_NOTNULL(tc, s1ap); /* eNB connects to SGW */ - gtpu = testenb_gtpu_server("127.0.0.5"); + gtpu = testenb_gtpu_server(TEST_ENB_IPV4); ABTS_PTR_NOTNULL(tc, gtpu); /* Send S1-Setup Reqeust */ @@ -120,9 +137,20 @@ static void cups_test1(abts_case *tc, void *data) ogs_s1ap_free(&message); ogs_pkbuf_free(recvbuf); + /********** Insert Subscriber in Database */ collection = mongoc_client_get_collection( ogs_mongoc()->client, ogs_mongoc()->name, "subscribers"); ABTS_PTR_NOTNULL(tc, collection); + doc = BCON_NEW("imsi", BCON_UTF8(test_ue.imsi)); + ABTS_PTR_NOTNULL(tc, doc); + + count = mongoc_collection_count ( + collection, MONGOC_QUERY_NONE, doc, 0, 0, NULL, &error); + if (count) { + ABTS_TRUE(tc, mongoc_collection_remove(collection, + MONGOC_REMOVE_SINGLE_REMOVE, doc, NULL, &error)) + } + bson_destroy(doc); doc = bson_new_from_json((const uint8_t *)json, -1, &error);; ABTS_PTR_NOTNULL(tc, doc); @@ -130,7 +158,7 @@ static void cups_test1(abts_case *tc, void *data) MONGOC_INSERT_NONE, doc, NULL, &error)); bson_destroy(doc); - doc = BCON_NEW("imsi", BCON_UTF8("001010123456819")); + doc = BCON_NEW("imsi", BCON_UTF8(test_ue.imsi)); ABTS_PTR_NOTNULL(tc, doc); do { count = mongoc_collection_count ( @@ -193,8 +221,8 @@ static void cups_test1(abts_case *tc, void *data) ABTS_INT_EQUAL(tc, OGS_OK, rv); /* Send Initial Context Setup Response */ - rv = tests1ap_build_initial_context_setup_response(&sendbuf, - 1, 1, 5, 1, "127.0.0.5"); + rv = tests1ap_build_initial_context_setup_response( + &sendbuf, 1, 1, 5, 1, TEST_ENB_IPV4); ABTS_INT_EQUAL(tc, OGS_OK, rv); rv = testenb_s1ap_send(s1ap, sendbuf); ABTS_INT_EQUAL(tc, OGS_OK, rv); @@ -210,18 +238,19 @@ static void cups_test1(abts_case *tc, void *data) ABTS_PTR_NOTNULL(tc, recvbuf); ogs_pkbuf_free(recvbuf); -#if TEST1_PING /* Send GTP-U ICMP Packet */ - rv = testgtpu_build_ping(&sendbuf, 1, "10.45.0.2", "10.45.0.1"); + test_sess.upf_n3_teid = 2; + test_sess.ue_ip.addr = inet_addr("10.45.0.2"); + + rv = test_gtpu_build_ping(&sendbuf, &test_sess, TEST_PING_IPV4); ABTS_INT_EQUAL(tc, OGS_OK, rv); - rv = testenb_gtpu_send(gtpu, sendbuf); + rv = testgnb_gtpu_sendto(gtpu, sendbuf, TEST_SGWU_IPV4); ABTS_INT_EQUAL(tc, OGS_OK, rv); /* Receive GTP-U ICMP Packet */ recvbuf = testenb_gtpu_read(gtpu); ABTS_PTR_NOTNULL(tc, recvbuf); ogs_pkbuf_free(recvbuf); -#endif /* Send PDN Connectivity Request */ rv = tests1ap_build_pdn_connectivity_request(&sendbuf, msgindex); @@ -236,7 +265,8 @@ static void cups_test1(abts_case *tc, void *data) ogs_pkbuf_free(recvbuf); /* Send E-RAB Setup Response */ - rv = tests1ap_build_e_rab_setup_response(&sendbuf, 1, 1, 6, 2, "127.0.0.5"); + rv = tests1ap_build_e_rab_setup_response( + &sendbuf, 1, 1, 6, 2, TEST_ENB_IPV4); ABTS_INT_EQUAL(tc, OGS_OK, rv); rv = testenb_s1ap_send(s1ap, sendbuf); ABTS_INT_EQUAL(tc, OGS_OK, rv); @@ -248,19 +278,18 @@ static void cups_test1(abts_case *tc, void *data) ABTS_INT_EQUAL(tc, OGS_OK, rv); /* Send GTP-U ICMP Packet */ - ogs_msleep(50); + test_sess.upf_n3_teid = 4; + test_sess.ue_ip.addr = inet_addr("10.45.0.3"); -#if TEST1_PING - rv = testgtpu_build_ping(&sendbuf, 3, "10.45.0.3", "10.45.0.1"); + rv = test_gtpu_build_ping(&sendbuf, &test_sess, TEST_PING_IPV4); ABTS_INT_EQUAL(tc, OGS_OK, rv); - rv = testenb_gtpu_send(gtpu, sendbuf); + rv = testgnb_gtpu_sendto(gtpu, sendbuf, TEST_SGWU_IPV4); ABTS_INT_EQUAL(tc, OGS_OK, rv); /* Receive GTP-U ICMP Packet */ recvbuf = testenb_gtpu_read(gtpu); ABTS_PTR_NOTNULL(tc, recvbuf); ogs_pkbuf_free(recvbuf); -#endif /* Send AA-Request */ ogs_msleep(300); @@ -273,7 +302,8 @@ static void cups_test1(abts_case *tc, void *data) ogs_pkbuf_free(recvbuf); /* Send E-RAB Setup Response */ - rv = tests1ap_build_e_rab_setup_response(&sendbuf, 1, 1, 7, 3, "127.0.0.5"); + rv = tests1ap_build_e_rab_setup_response(&sendbuf, + 1, 1, 7, 3, TEST_ENB_IPV4); ABTS_INT_EQUAL(tc, OGS_OK, rv); rv = testenb_s1ap_send(s1ap, sendbuf); ABTS_INT_EQUAL(tc, OGS_OK, rv); @@ -433,7 +463,7 @@ static void cups_test1(abts_case *tc, void *data) ogs_msleep(300); /********** Remove Subscriber in Database */ - doc = BCON_NEW("imsi", BCON_UTF8("001010123456819")); + doc = BCON_NEW("imsi", BCON_UTF8(test_ue.imsi)); ABTS_PTR_NOTNULL(tc, doc); ABTS_TRUE(tc, mongoc_collection_remove(collection, MONGOC_REMOVE_SINGLE_REMOVE, doc, NULL, &error)) @@ -459,6 +489,9 @@ static void cups_test2(abts_case *tc, void *data) int msgindex = 0; uint8_t *rx_sid = NULL; + test_ue_t test_ue; + test_sess_t test_sess; + mongoc_collection_t *collection = NULL; bson_t *doc = NULL; int64_t count = 0; @@ -534,8 +567,23 @@ static void cups_test2(abts_case *tc, void *data) "\"__v\" : 0" "}"; + /* Setup Test UE & Session Context */ + memset(&test_ue, 0, sizeof(test_ue)); + memset(&test_sess, 0, sizeof(test_sess)); + test_sess.test_ue = &test_ue; + test_ue.sess = &test_sess; + + test_ue.imsi = (char *)"001010123456819"; + + test_sess.gnb_n3_ip.ipv4 = true; + test_sess.gnb_n3_ip.addr = inet_addr(TEST_GNB_IPV4); + test_sess.gnb_n3_teid = 0; + + test_sess.upf_n3_ip.ipv4 = true; + test_sess.upf_n3_ip.addr = inet_addr(TEST_SGWU_IPV4); + /* eNB connects to MME */ - s1ap = testenb_s1ap_client("127.0.0.1"); + s1ap = testenb_s1ap_client(TEST_MME_IPV4); ABTS_PTR_NOTNULL(tc, s1ap); /* Send S1-Setup Reqeust */ @@ -553,9 +601,20 @@ static void cups_test2(abts_case *tc, void *data) ogs_s1ap_free(&message); ogs_pkbuf_free(recvbuf); + /********** Insert Subscriber in Database */ collection = mongoc_client_get_collection( ogs_mongoc()->client, ogs_mongoc()->name, "subscribers"); ABTS_PTR_NOTNULL(tc, collection); + doc = BCON_NEW("imsi", BCON_UTF8(test_ue.imsi)); + ABTS_PTR_NOTNULL(tc, doc); + + count = mongoc_collection_count ( + collection, MONGOC_QUERY_NONE, doc, 0, 0, NULL, &error); + if (count) { + ABTS_TRUE(tc, mongoc_collection_remove(collection, + MONGOC_REMOVE_SINGLE_REMOVE, doc, NULL, &error)) + } + bson_destroy(doc); doc = bson_new_from_json((const uint8_t *)json, -1, &error);; ABTS_PTR_NOTNULL(tc, doc); @@ -563,7 +622,7 @@ static void cups_test2(abts_case *tc, void *data) MONGOC_INSERT_NONE, doc, NULL, &error)); bson_destroy(doc); - doc = BCON_NEW("imsi", BCON_UTF8("001010123456819")); + doc = BCON_NEW("imsi", BCON_UTF8(test_ue.imsi)); ABTS_PTR_NOTNULL(tc, doc); do { count = mongoc_collection_count ( @@ -626,8 +685,8 @@ static void cups_test2(abts_case *tc, void *data) ABTS_INT_EQUAL(tc, OGS_OK, rv); /* Send Initial Context Setup Response */ - rv = tests1ap_build_initial_context_setup_response(&sendbuf, - 1, 1, 5, 1, "127.0.0.5"); + rv = tests1ap_build_initial_context_setup_response( + &sendbuf, 1, 1, 5, 1, TEST_ENB_IPV4); ABTS_INT_EQUAL(tc, OGS_OK, rv); rv = testenb_s1ap_send(s1ap, sendbuf); ABTS_INT_EQUAL(tc, OGS_OK, rv); @@ -656,7 +715,8 @@ static void cups_test2(abts_case *tc, void *data) ogs_pkbuf_free(recvbuf); /* Send E-RAB Setup Response */ - rv = tests1ap_build_e_rab_setup_response(&sendbuf, 1, 1, 6, 2, "127.0.0.5"); + rv = tests1ap_build_e_rab_setup_response( + &sendbuf, 1, 1, 6, 2, TEST_ENB_IPV4); ABTS_INT_EQUAL(tc, OGS_OK, rv); rv = testenb_s1ap_send(s1ap, sendbuf); ABTS_INT_EQUAL(tc, OGS_OK, rv); @@ -680,7 +740,8 @@ static void cups_test2(abts_case *tc, void *data) ABTS_INT_EQUAL(tc, OGS_OK, rv); /* Send E-RAB Setup Response */ - rv = tests1ap_build_e_rab_setup_response(&sendbuf, 1, 1, 7, 3, "127.0.0.5"); + rv = tests1ap_build_e_rab_setup_response( + &sendbuf, 1, 1, 7, 3, TEST_ENB_IPV4); ABTS_INT_EQUAL(tc, OGS_OK, rv); rv = testenb_s1ap_send(s1ap, sendbuf); ABTS_INT_EQUAL(tc, OGS_OK, rv); @@ -728,7 +789,7 @@ static void cups_test2(abts_case *tc, void *data) ogs_msleep(300); /********** Remove Subscriber in Database */ - doc = BCON_NEW("imsi", BCON_UTF8("001010123456819")); + doc = BCON_NEW("imsi", BCON_UTF8(test_ue.imsi)); ABTS_PTR_NOTNULL(tc, doc); ABTS_TRUE(tc, mongoc_collection_remove(collection, MONGOC_REMOVE_SINGLE_REMOVE, doc, NULL, &error)) @@ -751,6 +812,9 @@ static void cups_test3(abts_case *tc, void *data) int i; int msgindex = 0; + test_ue_t test_ue; + test_sess_t test_sess; + mongoc_collection_t *collection = NULL; bson_t *doc = NULL; int64_t count = 0; @@ -826,12 +890,27 @@ static void cups_test3(abts_case *tc, void *data) "\"__v\" : 0" "}"; + /* Setup Test UE & Session Context */ + memset(&test_ue, 0, sizeof(test_ue)); + memset(&test_sess, 0, sizeof(test_sess)); + test_sess.test_ue = &test_ue; + test_ue.sess = &test_sess; + + test_ue.imsi = (char *)"001010123456819"; + + test_sess.gnb_n3_ip.ipv4 = true; + test_sess.gnb_n3_ip.addr = inet_addr(TEST_GNB_IPV4); + test_sess.gnb_n3_teid = 0; + + test_sess.upf_n3_ip.ipv4 = true; + test_sess.upf_n3_ip.addr = inet_addr(TEST_SGWU_IPV4); + /* eNB connects to MME */ - s1ap = testenb_s1ap_client("127.0.0.1"); + s1ap = testenb_s1ap_client(TEST_MME_IPV4); ABTS_PTR_NOTNULL(tc, s1ap); /* eNB connects to SGW */ - gtpu = testenb_gtpu_server("127.0.0.5"); + gtpu = testenb_gtpu_server(TEST_ENB_IPV4); ABTS_PTR_NOTNULL(tc, gtpu); /* Send S1-Setup Reqeust */ @@ -849,9 +928,20 @@ static void cups_test3(abts_case *tc, void *data) ogs_s1ap_free(&message); ogs_pkbuf_free(recvbuf); + /********** Insert Subscriber in Database */ collection = mongoc_client_get_collection( ogs_mongoc()->client, ogs_mongoc()->name, "subscribers"); ABTS_PTR_NOTNULL(tc, collection); + doc = BCON_NEW("imsi", BCON_UTF8(test_ue.imsi)); + ABTS_PTR_NOTNULL(tc, doc); + + count = mongoc_collection_count ( + collection, MONGOC_QUERY_NONE, doc, 0, 0, NULL, &error); + if (count) { + ABTS_TRUE(tc, mongoc_collection_remove(collection, + MONGOC_REMOVE_SINGLE_REMOVE, doc, NULL, &error)) + } + bson_destroy(doc); doc = bson_new_from_json((const uint8_t *)json, -1, &error);; ABTS_PTR_NOTNULL(tc, doc); @@ -859,7 +949,7 @@ static void cups_test3(abts_case *tc, void *data) MONGOC_INSERT_NONE, doc, NULL, &error)); bson_destroy(doc); - doc = BCON_NEW("imsi", BCON_UTF8("001010123456819")); + doc = BCON_NEW("imsi", BCON_UTF8(test_ue.imsi)); ABTS_PTR_NOTNULL(tc, doc); do { count = mongoc_collection_count ( @@ -939,18 +1029,24 @@ static void cups_test3(abts_case *tc, void *data) ABTS_PTR_NOTNULL(tc, recvbuf); ogs_pkbuf_free(recvbuf); -#if TEST3_PING /* Send GTP-U ICMP Packet */ - rv = testgtpu_build_ping(&sendbuf, 1, "10.45.0.2", "10.45.0.1"); +#if TEST3_ONLY_PING + test_sess.upf_n3_teid = 2; + test_sess.ue_ip.addr = inet_addr("10.45.0.2"); +#else + test_sess.upf_n3_teid = 0xe; + test_sess.ue_ip.addr = inet_addr("10.45.0.6"); +#endif + + rv = test_gtpu_build_ping(&sendbuf, &test_sess, TEST_PING_IPV4); ABTS_INT_EQUAL(tc, OGS_OK, rv); - rv = testenb_gtpu_send(gtpu, sendbuf); + rv = testgnb_gtpu_sendto(gtpu, sendbuf, TEST_SGWU_IPV4); ABTS_INT_EQUAL(tc, OGS_OK, rv); /* Receive GTP-U ICMP Packet */ recvbuf = testenb_gtpu_read(gtpu); ABTS_PTR_NOTNULL(tc, recvbuf); ogs_pkbuf_free(recvbuf); -#endif /* Send PDN Connectivity Request */ rv = tests1ap_build_pdn_connectivity_request(&sendbuf, msgindex); @@ -965,7 +1061,8 @@ static void cups_test3(abts_case *tc, void *data) ogs_pkbuf_free(recvbuf); /* Send E-RAB Setup Response */ - rv = tests1ap_build_e_rab_setup_response(&sendbuf, 1, 1, 6, 2, "127.0.0.5"); + rv = tests1ap_build_e_rab_setup_response( + &sendbuf, 1, 1, 6, 2, TEST_ENB_IPV4); ABTS_INT_EQUAL(tc, OGS_OK, rv); rv = testenb_s1ap_send(s1ap, sendbuf); ABTS_INT_EQUAL(tc, OGS_OK, rv); @@ -989,7 +1086,8 @@ static void cups_test3(abts_case *tc, void *data) ABTS_INT_EQUAL(tc, OGS_OK, rv); /* Send E-RAB Setup Response */ - rv = tests1ap_build_e_rab_setup_response(&sendbuf, 1, 1, 7, 3, "127.0.0.5"); + rv = tests1ap_build_e_rab_setup_response( + &sendbuf, 1, 1, 7, 3, TEST_ENB_IPV4); ABTS_INT_EQUAL(tc, OGS_OK, rv); rv = testenb_s1ap_send(s1ap, sendbuf); ABTS_INT_EQUAL(tc, OGS_OK, rv); @@ -997,17 +1095,26 @@ static void cups_test3(abts_case *tc, void *data) /* Send GTP-U ICMP Packet */ ogs_msleep(50); -#if TEST3_PING - rv = testgtpu_build_ping(&sendbuf, 3, "10.45.0.3", "10.45.0.1"); + /* Send GTP-U ICMP Packet */ +#if TEST3_ONLY_PING + test_sess.upf_n3_teid = 4; + test_sess.ue_ip.addr = inet_addr("10.45.0.3"); +#else + test_sess.upf_n3_teid = 0x12; + test_sess.ue_ip.addr = inet_addr("10.45.0.7"); +#endif + + rv = test_gtpu_build_ping(&sendbuf, &test_sess, TEST_PING_IPV4); ABTS_INT_EQUAL(tc, OGS_OK, rv); - rv = testenb_gtpu_send(gtpu, sendbuf); + rv = testgnb_gtpu_sendto(gtpu, sendbuf, TEST_SGWU_IPV4); ABTS_INT_EQUAL(tc, OGS_OK, rv); /* Receive GTP-U ICMP Packet */ recvbuf = testenb_gtpu_read(gtpu); ABTS_PTR_NOTNULL(tc, recvbuf); ogs_pkbuf_free(recvbuf); -#endif + + ogs_msleep(300); /* Send PDN disconnectivity request */ rv = tests1ap_build_pdn_disconnectivity_request(&sendbuf, msgindex); @@ -1058,7 +1165,7 @@ static void cups_test3(abts_case *tc, void *data) ogs_msleep(300); /********** Remove Subscriber in Database */ - doc = BCON_NEW("imsi", BCON_UTF8("001010123456819")); + doc = BCON_NEW("imsi", BCON_UTF8(test_ue.imsi)); ABTS_PTR_NOTNULL(tc, doc); ABTS_TRUE(tc, mongoc_collection_remove(collection, MONGOC_REMOVE_SINGLE_REMOVE, doc, NULL, &error)) @@ -1083,6 +1190,9 @@ static void cups_test4(abts_case *tc, void *data) int i; int msgindex = 0; + test_ue_t test_ue; + test_sess_t test_sess; + mongoc_collection_t *collection = NULL; bson_t *doc = NULL; int64_t count = 0; @@ -1158,8 +1268,23 @@ static void cups_test4(abts_case *tc, void *data) "\"__v\" : 0" "}"; + /* Setup Test UE & Session Context */ + memset(&test_ue, 0, sizeof(test_ue)); + memset(&test_sess, 0, sizeof(test_sess)); + test_sess.test_ue = &test_ue; + test_ue.sess = &test_sess; + + test_ue.imsi = (char *)"001010123456819"; + + test_sess.gnb_n3_ip.ipv4 = true; + test_sess.gnb_n3_ip.addr = inet_addr(TEST_GNB_IPV4); + test_sess.gnb_n3_teid = 0; + + test_sess.upf_n3_ip.ipv4 = true; + test_sess.upf_n3_ip.addr = inet_addr(TEST_SGWU_IPV4); + /* eNB connects to MME */ - s1ap = testenb_s1ap_client("127.0.0.1"); + s1ap = testenb_s1ap_client(TEST_MME_IPV4); ABTS_PTR_NOTNULL(tc, s1ap); /* Send S1-Setup Reqeust */ @@ -1177,9 +1302,20 @@ static void cups_test4(abts_case *tc, void *data) ogs_s1ap_free(&message); ogs_pkbuf_free(recvbuf); + /********** Insert Subscriber in Database */ collection = mongoc_client_get_collection( ogs_mongoc()->client, ogs_mongoc()->name, "subscribers"); ABTS_PTR_NOTNULL(tc, collection); + doc = BCON_NEW("imsi", BCON_UTF8(test_ue.imsi)); + ABTS_PTR_NOTNULL(tc, doc); + + count = mongoc_collection_count ( + collection, MONGOC_QUERY_NONE, doc, 0, 0, NULL, &error); + if (count) { + ABTS_TRUE(tc, mongoc_collection_remove(collection, + MONGOC_REMOVE_SINGLE_REMOVE, doc, NULL, &error)) + } + bson_destroy(doc); doc = bson_new_from_json((const uint8_t *)json, -1, &error);; ABTS_PTR_NOTNULL(tc, doc); @@ -1187,7 +1323,7 @@ static void cups_test4(abts_case *tc, void *data) MONGOC_INSERT_NONE, doc, NULL, &error)); bson_destroy(doc); - doc = BCON_NEW("imsi", BCON_UTF8("001010123456819")); + doc = BCON_NEW("imsi", BCON_UTF8(test_ue.imsi)); ABTS_PTR_NOTNULL(tc, doc); do { count = mongoc_collection_count ( @@ -1251,7 +1387,7 @@ static void cups_test4(abts_case *tc, void *data) /* Send Initial Context Setup Response */ rv = tests1ap_build_initial_context_setup_response( - &sendbuf, 16777373, 1, 5, 1, "127.0.0.5"); + &sendbuf, 16777373, 1, 5, 1, TEST_ENB_IPV4); ABTS_INT_EQUAL(tc, OGS_OK, rv); rv = testenb_s1ap_send(s1ap, sendbuf); ABTS_INT_EQUAL(tc, OGS_OK, rv); @@ -1275,7 +1411,7 @@ static void cups_test4(abts_case *tc, void *data) /* Send E-RAB Setup Response */ rv = tests1ap_build_e_rab_setup_response( - &sendbuf, 33554492, 1, 6, 2, "127.0.0.5"); + &sendbuf, 33554492, 1, 6, 2, TEST_ENB_IPV4); ABTS_INT_EQUAL(tc, OGS_OK, rv); rv = testenb_s1ap_send(s1ap, sendbuf); ABTS_INT_EQUAL(tc, OGS_OK, rv); @@ -1289,7 +1425,7 @@ static void cups_test4(abts_case *tc, void *data) ogs_msleep(300); /********** Remove Subscriber in Database */ - doc = BCON_NEW("imsi", BCON_UTF8("001010123456819")); + doc = BCON_NEW("imsi", BCON_UTF8(test_ue.imsi)); ABTS_PTR_NOTNULL(tc, doc); ABTS_TRUE(tc, mongoc_collection_remove(collection, MONGOC_REMOVE_SINGLE_REMOVE, doc, NULL, &error)) diff --git a/tests/cups/meson.build b/tests/cups/meson.build index 2f7702dd6..e9da736c9 100644 --- a/tests/cups/meson.build +++ b/tests/cups/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 . -test5gc_cups_sources = files(''' +testapp_cups_sources = files(''' pcscf-fd-path.h pcscf-fd-path.c @@ -23,10 +23,10 @@ test5gc_cups_sources = files(''' cups-test.c '''.split()) -test5gc_cups_exe = executable('cups', - sources : test5gc_cups_sources, +testapp_cups_exe = executable('cups', + sources : testapp_cups_sources, c_args : [testunit_core_cc_flags, '-DFD_EXT_DIR="@0@"'.format(freediameter_extensions_builddir)], - dependencies : libtestepc_dep) + dependencies : libtestapp_dep) -test('cups', test5gc_cups_exe, is_parallel : false, suite: '5gc') +test('cups', testapp_cups_exe, is_parallel : false, suite: 'app') diff --git a/tests/epc/attach-test.c b/tests/epc/attach-test.c index 23f0343a7..ef28662ec 100644 --- a/tests/epc/attach-test.c +++ b/tests/epc/attach-test.c @@ -747,6 +747,7 @@ static void attach_test3(abts_case *tc, void *data) { int rv; ogs_socknode_t *s1ap; + ogs_socknode_t *gtpu; ogs_pkbuf_t *sendbuf; ogs_pkbuf_t *recvbuf; ogs_s1ap_message_t message; @@ -834,6 +835,10 @@ static void attach_test3(abts_case *tc, void *data) s1ap = testenb_s1ap_client("127.0.0.1"); ABTS_PTR_NOTNULL(tc, s1ap); + /* eNB connects to SGW */ + gtpu = testenb_gtpu_server("127.0.0.5"); + ABTS_PTR_NOTNULL(tc, gtpu); + /* Send S1-Setup Reqeust */ rv = tests1ap_build_setup_req( &sendbuf, S1AP_ENB_ID_PR_macroENB_ID, 0x54f64, 12345, 1, 1, 2); @@ -1060,6 +1065,9 @@ static void attach_test3(abts_case *tc, void *data) /* eNB disonncect from MME */ testenb_s1ap_close(s1ap); + + /* eNB disonncect from SGW */ + testenb_gtpu_close(gtpu); } /************************************************************** diff --git a/tests/epc/crash-test.c b/tests/epc/crash-test.c index ecbd74633..942f52390 100644 --- a/tests/epc/crash-test.c +++ b/tests/epc/crash-test.c @@ -258,6 +258,8 @@ static void crash_test1(abts_case *tc, void *data) rv = testenb_s1ap_send(s1ap, sendbuf); ABTS_INT_EQUAL(tc, OGS_OK, rv); + ogs_msleep(300); + doc = BCON_NEW("imsi", BCON_UTF8("311980000000725")); ABTS_PTR_NOTNULL(tc, doc); ABTS_TRUE(tc, mongoc_collection_remove(collection, @@ -567,6 +569,8 @@ static void crash_test2(abts_case *tc, void *data) rv = testenb_s1ap_send(s1ap, sendbuf); ABTS_INT_EQUAL(tc, OGS_OK, rv); + ogs_msleep(300); + doc = BCON_NEW("imsi", BCON_UTF8("311980000000725")); ABTS_PTR_NOTNULL(tc, doc); ABTS_TRUE(tc, mongoc_collection_remove(collection, diff --git a/tests/mnc3/abts-main.c b/tests/mnc3/abts-main.c index f0fca248f..aa45c2019 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-epc.h" +#include "test-app.h" abts_suite *test_mnc3(abts_suite *suite); @@ -35,7 +35,7 @@ static void terminate(void) test_child_terminate(); app_terminate(); - test_epc_final(); + test_app_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_epc_init(); + test_app_init(); rv = app_initialize(argv); ogs_assert(rv == OGS_OK); @@ -57,7 +57,8 @@ int main(int argc, const char *const argv[]) abts_suite *suite = NULL; atexit(terminate); - test_epc_run(argc, argv, "mnc3.yaml", initialize); + test_5gc_run(argc, argv, "mnc3.yaml", initialize); + ogs_msleep(1000); 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 d19f76a38..d2b25dc9e 100644 --- a/tests/mnc3/meson.build +++ b/tests/mnc3/meson.build @@ -15,14 +15,14 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -testepc_mnc3_sources = files(''' +testapp_mnc3_sources = files(''' abts-main.c mnc3-test.c '''.split()) -testepc_mnc3_exe = executable('mnc3', - sources : testepc_mnc3_sources, +testapp_mnc3_exe = executable('mnc3', + sources : testapp_mnc3_sources, c_args : testunit_core_cc_flags, - dependencies : libtestepc_dep) + dependencies : libtestapp_dep) -test('mnc3', testepc_mnc3_exe, is_parallel : false, suite: 'epc') +test('mnc3', testapp_mnc3_exe, is_parallel : false, suite: 'app') diff --git a/tests/mnc3/mnc3-test.c b/tests/mnc3/mnc3-test.c index 02709d793..49c0abb0b 100644 --- a/tests/mnc3/mnc3-test.c +++ b/tests/mnc3/mnc3-test.c @@ -35,25 +35,33 @@ static void test1_func(abts_case *tc, void *data) uint8_t tmp[OGS_MAX_SDU_LEN]; const char *_authentication_request = - "000b403b00000300 000005c001a00102 000800020018001a 002524075200906d" - "231ff57ef278c719 1d170303deb610d0 7c4defa47480001f 2b5350926bdb3a"; + "000b403800000300 0000020001000800 020018001a002524 075200906d231ff5" + "7ef278c7191d1703 03deb610d07c4def a47480001f2b5350 926bdb3a"; const char *_security_mode_command = - "000b402f00000300 000005c001a00102 000800020018001a 00191837b4fe6e27" - "00075d010002e0e0 c14f08d14b7a88d1 d1d02e"; + "000b402c00000300 0000020001000800 020018001a001918 37b4fe6e2700075d" + "010002e0e0c14f08 d14b7a88d1d1d02e"; const char *_esm_information_request = - "000b402000000300 000005c001a00102 000800020018001a 000a0927846a01a8" - "010201d9"; + "000b401d00000300 0000020001000800 020018001a000a09 27846a01a8010201" + "d9"; const char *_initial_context_setup_request = - "00090080c2000006 00000005c001a001 0200080002001800 42000a183d090000" - "603d090000001800 71000034006c4500 093d0f807f000002 000000015d279cc9" - "988102074201490c 0313401000320033 0034003500315201 c101090c07737461" - "72656e7403636f6d 05010a2d00025e06 fefee2e20303270f 80000d0408080808" - "000d040808040450 0bf6134010801e64 ee00502459496402 0108006b00051800" - "0c00000049002046 c789cba93e9b9775 8335c097e6c386c8 72e4b82434a48037" - "c30601590edd8e"; + "00090080bf000006 0000000200010008 000200180042000a 183d090000603d09" + "0000001800710000 34006c4500093d0f 807f000007000000 025d2722ab599e02" + "074201490c031340 1000320033003400 3500315201c10109 0c0773746172656e" + "7403636f6d05010a 2d00025e06fefee2 e20303270f80000d 0408080808000d04" + "08080404500bf613 4010801e64e400ae b859496402010800 6b000518000c0000" + "0049002046c789cb a93e9b97758335c0 97e6c386c872e4b8 2434a48037c30601" + "590edd8e"; const char *_emm_information = - "000b403b00000300 000005c001a00102 000800020018001a 0025242729f8b0bb" - "030761430f10004f 00700065006e0035 0047005347914032 80113463490100"; + "000b403800000300 0000020001000800 020018001a002524 27e211a94a030761" + "430f10004f007000 65006e0035004700 5347028040325204 69490100"; + + test_ue_t test_ue; + test_sess_t test_sess; + + const char *_k_string = "465b5ce8b199b49faa5f0a2ee238a6bc"; + uint8_t k[OGS_KEY_LEN]; + const char *_opc_string = "e8ed289deba952e4283b54e88e6183ca"; + uint8_t opc[OGS_KEY_LEN]; mongoc_collection_t *collection = NULL; bson_t *doc = NULL; @@ -107,12 +115,27 @@ static void test1_func(abts_case *tc, void *data) "\"__v\" : 0 " "}"; + /* Setup Test UE & Session Context */ + memset(&test_ue, 0, sizeof(test_ue)); + memset(&test_sess, 0, sizeof(test_sess)); + test_sess.test_ue = &test_ue; + test_ue.sess = &test_sess; + + test_ue.imsi = (char *)"310014987654004"; + + test_sess.gnb_n3_ip.ipv4 = true; + test_sess.gnb_n3_ip.addr = inet_addr(TEST_GNB_IPV4); + test_sess.gnb_n3_teid = 0; + + test_sess.upf_n3_ip.ipv4 = true; + test_sess.upf_n3_ip.addr = inet_addr(TEST_SGWU_IPV4); + /* eNB connects to MME */ - s1ap = testenb_s1ap_client("127.0.0.1"); + s1ap = testenb_s1ap_client(TEST_MME_IPV4); ABTS_PTR_NOTNULL(tc, s1ap); /* eNB connects to SGW */ - gtpu = testenb_gtpu_server("127.0.0.5"); + gtpu = testenb_gtpu_server(TEST_ENB_IPV4); ABTS_PTR_NOTNULL(tc, gtpu); /* Send S1-Setup Reqeust */ @@ -130,18 +153,28 @@ static void test1_func(abts_case *tc, void *data) ogs_s1ap_free(&message); ogs_pkbuf_free(recvbuf); + /********** Insert Subscriber in Database */ collection = mongoc_client_get_collection( ogs_mongoc()->client, ogs_mongoc()->name, "subscribers"); ABTS_PTR_NOTNULL(tc, collection); + doc = BCON_NEW("imsi", BCON_UTF8(test_ue.imsi)); + ABTS_PTR_NOTNULL(tc, doc); + + count = mongoc_collection_count ( + collection, MONGOC_QUERY_NONE, doc, 0, 0, NULL, &error); + if (count) { + ABTS_TRUE(tc, mongoc_collection_remove(collection, + MONGOC_REMOVE_SINGLE_REMOVE, doc, NULL, &error)) + } + bson_destroy(doc); - /********** 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, + ABTS_TRUE(tc, mongoc_collection_insert(collection, MONGOC_INSERT_NONE, doc, NULL, &error)); bson_destroy(doc); - doc = BCON_NEW("imsi", BCON_UTF8("310014987654004")); + doc = BCON_NEW("imsi", BCON_UTF8(test_ue.imsi)); ABTS_PTR_NOTNULL(tc, doc); do { count = mongoc_collection_count ( @@ -149,7 +182,7 @@ static void test1_func(abts_case *tc, void *data) } while (count == 0); bson_destroy(doc); - mme_self()->mme_ue_s1ap_id = 27263233; + /* Send Attach Request */ rv = tests1ap_build_initial_ue_msg(&sendbuf, msgindex); ABTS_INT_EQUAL(tc, OGS_OK, rv); rv = testenb_s1ap_send(s1ap, sendbuf); @@ -187,7 +220,8 @@ static void test1_func(abts_case *tc, void *data) recvbuf = testenb_s1ap_read(s1ap); ABTS_PTR_NOTNULL(tc, recvbuf); ABTS_TRUE(tc, memcmp(recvbuf->data, - OGS_HEX(_esm_information_request, strlen(_security_mode_command), tmp), + OGS_HEX(_esm_information_request, + strlen(_esm_information_request), tmp), recvbuf->len) == 0); ogs_pkbuf_free(recvbuf); @@ -204,9 +238,9 @@ static void test1_func(abts_case *tc, void *data) 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); + ABTS_TRUE(tc, memcmp(recvbuf->data, tmp, 59) == 0); + ABTS_TRUE(tc, memcmp(recvbuf->data+63, tmp+63, 78) == 0); + ABTS_TRUE(tc, memcmp(recvbuf->data+145, tmp+145, 50) == 0); ogs_pkbuf_free(recvbuf); /* Send UE Capability Info Indication */ @@ -215,11 +249,9 @@ static void test1_func(abts_case *tc, void *data) rv = testenb_s1ap_send(s1ap, sendbuf); ABTS_INT_EQUAL(tc, OGS_OK, rv); - ogs_msleep(50); - /* Send Initial Context Setup Response */ rv = tests1ap_build_initial_context_setup_response(&sendbuf, - 27263233, 24, 5, 1, "127.0.0.5"); + 1, 24, 5, 1, TEST_ENB_IPV4); ABTS_INT_EQUAL(tc, OGS_OK, rv); rv = testenb_s1ap_send(s1ap, sendbuf); ABTS_INT_EQUAL(tc, OGS_OK, rv); @@ -230,20 +262,21 @@ static void test1_func(abts_case *tc, void *data) rv = testenb_s1ap_send(s1ap, sendbuf); ABTS_INT_EQUAL(tc, OGS_OK, rv); - ogs_msleep(50); - /* Receive EMM information */ recvbuf = testenb_s1ap_read(s1ap); 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); + ABTS_TRUE(tc, memcmp(recvbuf->data, tmp, 25) == 0); + ABTS_TRUE(tc, memcmp(recvbuf->data+29, tmp+29, 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"); + test_sess.upf_n3_teid = 2; + test_sess.ue_ip.addr = inet_addr("10.45.0.2"); + + rv = test_gtpu_build_ping(&sendbuf, &test_sess, TEST_PING_IPV4); ABTS_INT_EQUAL(tc, OGS_OK, rv); - rv = testenb_gtpu_send(gtpu, sendbuf); + rv = testgnb_gtpu_sendto(gtpu, sendbuf, TEST_SGWU_IPV4); ABTS_INT_EQUAL(tc, OGS_OK, rv); /* Receive GTP-U ICMP Packet */ @@ -252,10 +285,10 @@ static void test1_func(abts_case *tc, void *data) ogs_pkbuf_free(recvbuf); /********** Remove Subscriber in Database */ - doc = BCON_NEW("imsi", BCON_UTF8("310014987654004")); + doc = BCON_NEW("imsi", BCON_UTF8(test_ue.imsi)); ABTS_PTR_NOTNULL(tc, doc); - ABTS_TRUE(tc, mongoc_collection_remove(collection, - MONGOC_REMOVE_SINGLE_REMOVE, doc, NULL, &error)) + ABTS_TRUE(tc, mongoc_collection_remove(collection, + MONGOC_REMOVE_SINGLE_REMOVE, doc, NULL, &error)) bson_destroy(doc); mongoc_collection_destroy(collection); diff --git a/tests/volte/abts-main.c b/tests/volte/abts-main.c index 26ddad6ac..a12b60952 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-epc.h" +#include "test-app.h" #include "pcscf-fd-path.h" @@ -30,29 +30,16 @@ const struct testlist { {NULL}, }; -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_thread_t *mme_thread = NULL; - static void terminate(void) { ogs_msleep(50); test_child_terminate(); - - if (mme_thread) ogs_thread_destroy(mme_thread); - if (hss_thread) ogs_thread_destroy(hss_thread); - if (sgw_thread) ogs_thread_destroy(sgw_thread); - if (pgw_thread) ogs_thread_destroy(pgw_thread); - if (pcrf_thread) ogs_thread_destroy(pcrf_thread); + app_terminate(); pcscf_fd_final(); - ogs_sctp_final(); - - test_epc_final(); + test_app_final(); ogs_app_terminate(); } @@ -60,19 +47,12 @@ static void initialize(const char *const argv[]) { int rv; - test_no_mme_self = true; - rv = ogs_app_initialize(NULL, argv); ogs_assert(rv == OGS_OK); + test_app_init(); - pcrf_thread = test_child_create("pcrf", argv); - pgw_thread = test_child_create("pgw", argv); - sgw_thread = test_child_create("sgw", argv); - hss_thread = test_child_create("hss", argv); - mme_thread = test_child_create("mme", argv); - - test_epc_init(); - ogs_sctp_init(ogs_config()->usrsctp.udp_port); + rv = app_initialize(argv); + ogs_assert(rv == OGS_OK); rv = pcscf_fd_init(); ogs_assert(rv == OGS_OK); @@ -84,7 +64,8 @@ int main(int argc, const char *const argv[]) abts_suite *suite = NULL; atexit(terminate); - test_epc_run(argc, argv, "volte.yaml", initialize); + test_5gc_run(argc, argv, "volte.yaml", initialize); + ogs_msleep(1000); 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 029fb162d..7eb2a2557 100644 --- a/tests/volte/meson.build +++ b/tests/volte/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 . -testepc_volte_sources = files(''' +testapp_volte_sources = files(''' pcscf-fd-path.h pcscf-fd-path.c @@ -23,10 +23,10 @@ testepc_volte_sources = files(''' volte-test.c '''.split()) -testepc_volte_exe = executable('volte', - sources : testepc_volte_sources, +testapp_volte_exe = executable('volte', + sources : testapp_volte_sources, c_args : [testunit_core_cc_flags, '-DFD_EXT_DIR="@0@"'.format(freediameter_extensions_builddir)], - dependencies : libtestepc_dep) + dependencies : libtestapp_dep) -test('volte', testepc_volte_exe, is_parallel : false, suite: 'epc') +test('volte', testapp_volte_exe, is_parallel : false, suite: 'app') diff --git a/tests/volte/volte-test.c b/tests/volte/volte-test.c index ae34b558e..dfc7b0a08 100644 --- a/tests/volte/volte-test.c +++ b/tests/volte/volte-test.c @@ -31,6 +31,9 @@ static void volte_test1(abts_case *tc, void *data) int msgindex = 0; uint8_t *rx_sid = NULL; + test_ue_t test_ue; + test_sess_t test_sess; + mongoc_collection_t *collection = NULL; bson_t *doc = NULL; int64_t count = 0; @@ -93,8 +96,23 @@ static void volte_test1(abts_case *tc, void *data) "\"__v\" : 0" "}"; + /* Setup Test UE & Session Context */ + memset(&test_ue, 0, sizeof(test_ue)); + memset(&test_sess, 0, sizeof(test_sess)); + test_sess.test_ue = &test_ue; + test_ue.sess = &test_sess; + + test_ue.imsi = (char *)"001010123456819"; + + test_sess.gnb_n3_ip.ipv4 = true; + test_sess.gnb_n3_ip.addr = inet_addr(TEST_GNB_IPV4); + test_sess.gnb_n3_teid = 0; + + test_sess.upf_n3_ip.ipv4 = true; + test_sess.upf_n3_ip.addr = inet_addr(TEST_SGWU_IPV4); + /* eNB connects to MME */ - s1ap = testenb_s1ap_client("127.0.0.1"); + s1ap = testenb_s1ap_client(TEST_MME_IPV4); ABTS_PTR_NOTNULL(tc, s1ap); /* Send S1-Setup Reqeust */ @@ -112,17 +130,28 @@ static void volte_test1(abts_case *tc, void *data) ogs_s1ap_free(&message); ogs_pkbuf_free(recvbuf); + /********** Insert Subscriber in Database */ collection = mongoc_client_get_collection( ogs_mongoc()->client, ogs_mongoc()->name, "subscribers"); ABTS_PTR_NOTNULL(tc, collection); + doc = BCON_NEW("imsi", BCON_UTF8(test_ue.imsi)); + ABTS_PTR_NOTNULL(tc, doc); + + count = mongoc_collection_count ( + collection, MONGOC_QUERY_NONE, doc, 0, 0, NULL, &error); + if (count) { + ABTS_TRUE(tc, mongoc_collection_remove(collection, + MONGOC_REMOVE_SINGLE_REMOVE, doc, NULL, &error)) + } + bson_destroy(doc); doc = bson_new_from_json((const uint8_t *)json, -1, &error);; ABTS_PTR_NOTNULL(tc, doc); - ABTS_TRUE(tc, mongoc_collection_insert(collection, + ABTS_TRUE(tc, mongoc_collection_insert(collection, MONGOC_INSERT_NONE, doc, NULL, &error)); bson_destroy(doc); - doc = BCON_NEW("imsi", BCON_UTF8("001010123456819")); + doc = BCON_NEW("imsi", BCON_UTF8(test_ue.imsi)); ABTS_PTR_NOTNULL(tc, doc); do { count = mongoc_collection_count ( @@ -130,6 +159,7 @@ static void volte_test1(abts_case *tc, void *data) } while (count == 0); bson_destroy(doc); + /*********************************************************************** * Attach Request : Known IMSI, Integrity Protected, No Security Context * Send Initial-UE Message + Attach Request + PDN Connectivity */ @@ -185,8 +215,8 @@ static void volte_test1(abts_case *tc, void *data) ABTS_INT_EQUAL(tc, OGS_OK, rv); /* Send Initial Context Setup Response */ - rv = tests1ap_build_initial_context_setup_response(&sendbuf, - 1, 1, 5, 1, "127.0.0.5"); + rv = tests1ap_build_initial_context_setup_response( + &sendbuf, 1, 1, 5, 1, TEST_ENB_IPV4); ABTS_INT_EQUAL(tc, OGS_OK, rv); rv = testenb_s1ap_send(s1ap, sendbuf); ABTS_INT_EQUAL(tc, OGS_OK, rv); @@ -215,7 +245,8 @@ static void volte_test1(abts_case *tc, void *data) ogs_pkbuf_free(recvbuf); /* Send E-RAB Setup Response */ - rv = tests1ap_build_e_rab_setup_response(&sendbuf, 1, 1, 6, 2, "127.0.0.5"); + rv = tests1ap_build_e_rab_setup_response( + &sendbuf, 1, 1, 6, 2, TEST_ENB_IPV4); ABTS_INT_EQUAL(tc, OGS_OK, rv); rv = testenb_s1ap_send(s1ap, sendbuf); ABTS_INT_EQUAL(tc, OGS_OK, rv); @@ -420,10 +451,10 @@ static void volte_test1(abts_case *tc, void *data) ogs_msleep(300); /********** Remove Subscriber in Database */ - doc = BCON_NEW("imsi", BCON_UTF8("001010123456819")); + doc = BCON_NEW("imsi", BCON_UTF8(test_ue.imsi)); ABTS_PTR_NOTNULL(tc, doc); - ABTS_TRUE(tc, mongoc_collection_remove(collection, - MONGOC_REMOVE_SINGLE_REMOVE, doc, NULL, &error)) + ABTS_TRUE(tc, mongoc_collection_remove(collection, + MONGOC_REMOVE_SINGLE_REMOVE, doc, NULL, &error)) bson_destroy(doc); mongoc_collection_destroy(collection); @@ -443,6 +474,9 @@ static void volte_test2(abts_case *tc, void *data) int msgindex = 0; uint8_t *rx_sid = NULL; + test_ue_t test_ue; + test_sess_t test_sess; + mongoc_collection_t *collection = NULL; bson_t *doc = NULL; int64_t count = 0; @@ -518,8 +552,23 @@ static void volte_test2(abts_case *tc, void *data) "\"__v\" : 0" "}"; + /* Setup Test UE & Session Context */ + memset(&test_ue, 0, sizeof(test_ue)); + memset(&test_sess, 0, sizeof(test_sess)); + test_sess.test_ue = &test_ue; + test_ue.sess = &test_sess; + + test_ue.imsi = (char *)"001010123456819"; + + test_sess.gnb_n3_ip.ipv4 = true; + test_sess.gnb_n3_ip.addr = inet_addr(TEST_GNB_IPV4); + test_sess.gnb_n3_teid = 0; + + test_sess.upf_n3_ip.ipv4 = true; + test_sess.upf_n3_ip.addr = inet_addr(TEST_SGWU_IPV4); + /* eNB connects to MME */ - s1ap = testenb_s1ap_client("127.0.0.1"); + s1ap = testenb_s1ap_client(TEST_MME_IPV4); ABTS_PTR_NOTNULL(tc, s1ap); /* Send S1-Setup Reqeust */ @@ -537,17 +586,28 @@ static void volte_test2(abts_case *tc, void *data) ogs_s1ap_free(&message); ogs_pkbuf_free(recvbuf); + /********** Insert Subscriber in Database */ collection = mongoc_client_get_collection( ogs_mongoc()->client, ogs_mongoc()->name, "subscribers"); ABTS_PTR_NOTNULL(tc, collection); + doc = BCON_NEW("imsi", BCON_UTF8(test_ue.imsi)); + ABTS_PTR_NOTNULL(tc, doc); + + count = mongoc_collection_count ( + collection, MONGOC_QUERY_NONE, doc, 0, 0, NULL, &error); + if (count) { + ABTS_TRUE(tc, mongoc_collection_remove(collection, + MONGOC_REMOVE_SINGLE_REMOVE, doc, NULL, &error)) + } + bson_destroy(doc); doc = bson_new_from_json((const uint8_t *)json, -1, &error);; ABTS_PTR_NOTNULL(tc, doc); - ABTS_TRUE(tc, mongoc_collection_insert(collection, + ABTS_TRUE(tc, mongoc_collection_insert(collection, MONGOC_INSERT_NONE, doc, NULL, &error)); bson_destroy(doc); - doc = BCON_NEW("imsi", BCON_UTF8("001010123456819")); + doc = BCON_NEW("imsi", BCON_UTF8(test_ue.imsi)); ABTS_PTR_NOTNULL(tc, doc); do { count = mongoc_collection_count ( @@ -610,8 +670,8 @@ static void volte_test2(abts_case *tc, void *data) ABTS_INT_EQUAL(tc, OGS_OK, rv); /* Send Initial Context Setup Response */ - rv = tests1ap_build_initial_context_setup_response(&sendbuf, - 1, 1, 5, 1, "127.0.0.5"); + rv = tests1ap_build_initial_context_setup_response( + &sendbuf, 1, 1, 5, 1, TEST_ENB_IPV4); ABTS_INT_EQUAL(tc, OGS_OK, rv); rv = testenb_s1ap_send(s1ap, sendbuf); ABTS_INT_EQUAL(tc, OGS_OK, rv); @@ -640,7 +700,8 @@ static void volte_test2(abts_case *tc, void *data) ogs_pkbuf_free(recvbuf); /* Send E-RAB Setup Response */ - rv = tests1ap_build_e_rab_setup_response(&sendbuf, 1, 1, 6, 2, "127.0.0.5"); + rv = tests1ap_build_e_rab_setup_response( + &sendbuf, 1, 1, 6, 2, TEST_ENB_IPV4); ABTS_INT_EQUAL(tc, OGS_OK, rv); rv = testenb_s1ap_send(s1ap, sendbuf); ABTS_INT_EQUAL(tc, OGS_OK, rv); @@ -664,7 +725,8 @@ static void volte_test2(abts_case *tc, void *data) ABTS_INT_EQUAL(tc, OGS_OK, rv); /* Send E-RAB Setup Response */ - rv = tests1ap_build_e_rab_setup_response(&sendbuf, 1, 1, 7, 3, "127.0.0.5"); + rv = tests1ap_build_e_rab_setup_response( + &sendbuf, 1, 1, 7, 3, TEST_ENB_IPV4); ABTS_INT_EQUAL(tc, OGS_OK, rv); rv = testenb_s1ap_send(s1ap, sendbuf); ABTS_INT_EQUAL(tc, OGS_OK, rv); @@ -712,10 +774,10 @@ static void volte_test2(abts_case *tc, void *data) ogs_msleep(300); /********** Remove Subscriber in Database */ - doc = BCON_NEW("imsi", BCON_UTF8("001010123456819")); + doc = BCON_NEW("imsi", BCON_UTF8(test_ue.imsi)); ABTS_PTR_NOTNULL(tc, doc); - ABTS_TRUE(tc, mongoc_collection_remove(collection, - MONGOC_REMOVE_SINGLE_REMOVE, doc, NULL, &error)) + ABTS_TRUE(tc, mongoc_collection_remove(collection, + MONGOC_REMOVE_SINGLE_REMOVE, doc, NULL, &error)) bson_destroy(doc); mongoc_collection_destroy(collection);