Split SGW into SGW-C/SGW-U

This commit is contained in:
Sukchan Lee 2020-08-12 20:31:22 -04:00
parent 9f24b7f295
commit 19b9360687
155 changed files with 13939 additions and 2783 deletions

View File

@ -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

145
configs/app.yaml.in Normal file
View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -47,6 +47,7 @@ example_conf = '''
volte.yaml
srslte.yaml
cups.yaml
app.yaml
'''.split()
foreach file : example_conf

View File

@ -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

View File

@ -23,6 +23,8 @@ open5gs_conf = '''
mme.yaml
hss.yaml
sgw.yaml
sgwc.yaml
sgwu.yaml
pgw.yaml
pcrf.yaml

View File

@ -226,7 +226,7 @@ mme:
mme_name: open5gs-mme0
#
# sgw:
# sgwc:
#
# <GTP-C Client>
#
@ -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:
#
# <GTP-C Client>
#
# 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

View File

@ -26,21 +26,21 @@ logger:
#
# <GTP-C Server>
#
# 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@
#
# <GTP-U Server>
#
# 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:

View File

@ -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:
#
# <GTP-C Server>
#
# 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@
#
# <PFCP Server>
#
# 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:
#
# <PFCP Client>>
#
# o PFCP Client(127.0.0.7:8805)
#
# pfcp:
# addr: 127.0.0.7
#
# <UPF_SELECTION_MODE - EPC only>
#
# 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:

View File

@ -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:
#
# <GTP-U Server>
#
# 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
#
# <PFCP Server>
#
# 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:
#
# <PFCP Client>>
#
# 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:

View File

@ -77,8 +77,6 @@ upf:
# addr: 127.0.0.3
#
smf:
pfcp:
- addr: 127.0.0.3
#
# parameter:

View File

@ -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

View File

@ -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
#
# <P-CSCF>
#
# 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

View File

@ -0,0 +1,407 @@
---
title: Moving 4G EPC to the CUPS
head_inline: "<style> .blue { color: blue; } </style>"
---
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)
```

View File

@ -7,6 +7,7 @@ head_inline: "<style> ul { padding-bottom: 1em; } </style>"
- 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)

Binary file not shown.

View File

@ -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);

View File

@ -339,6 +339,15 @@ int ogs_config_parse()
if (!strcmp(parameter_key, "no_hss")) {
self.parameter.no_hss =
ogs_yaml_iter_bool(&parameter_iter);
} else if (!strcmp(parameter_key, "no_mme")) {
self.parameter.no_mme =
ogs_yaml_iter_bool(&parameter_iter);
} else if (!strcmp(parameter_key, "no_sgwu")) {
self.parameter.no_sgwu =
ogs_yaml_iter_bool(&parameter_iter);
} else if (!strcmp(parameter_key, "no_sgwc")) {
self.parameter.no_sgwc =
ogs_yaml_iter_bool(&parameter_iter);
} else if (!strcmp(parameter_key, "no_sgw")) {
self.parameter.no_sgw =
ogs_yaml_iter_bool(&parameter_iter);

View File

@ -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;

View File

@ -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;
}

View File

@ -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
******************************************************************************/

View File

@ -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 ..

View File

@ -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;
}

View File

@ -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);

View File

@ -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)

View File

@ -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);
}

View File

@ -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
}

View File

@ -17,8 +17,8 @@
libinc = include_directories('.')
subdir('ipfw')
subdir('core')
subdir('ipfw')
subdir('crypt')
subdir('sctp')
subdir('dbi')

660
lib/pfcp/build.c Normal file
View File

@ -0,0 +1,660 @@
/*
* Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
*
* 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 <https://www.gnu.org/licenses/>.
*/
#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);
}

70
lib/pfcp/build.h Normal file
View File

@ -0,0 +1,70 @@
/*
* Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
*
* 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 <https://www.gnu.org/licenses/>.
*/
#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 */

View File

@ -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;

View File

@ -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);

718
lib/pfcp/handler.c Normal file
View File

@ -0,0 +1,718 @@
/*
* Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
*
* 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 <https://www.gnu.org/licenses/>.
*/
#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;
}

89
lib/pfcp/handler.h Normal file
View File

@ -0,0 +1,89 @@
/*
* Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
*
* 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 <https://www.gnu.org/licenses/>.
*/
#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 */

View File

@ -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])

View File

@ -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,

View File

@ -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;

View File

@ -1,54 +0,0 @@
/*
* Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
*
* 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 <https://www.gnu.org/licenses/>.
*/
#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);
}

View File

@ -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" {

View File

@ -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)

View File

@ -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);

View File

@ -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."})

View File

@ -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."})

View File

@ -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]."})

View File

@ -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"

View File

@ -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."})

View File

@ -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)

View File

@ -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 */

View File

@ -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;

View File

@ -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;

View File

@ -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);

View File

@ -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')

View File

@ -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);

View File

@ -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 */

View File

@ -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);
}

40
src/sgwc/app.c Normal file
View File

@ -0,0 +1,40 @@
/*
* Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
*
* 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 <https://www.gnu.org/licenses/>.
*/
#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");
}

832
src/sgwc/context.c Normal file
View File

@ -0,0 +1,832 @@
/*
* Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
*
* 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 <https://www.gnu.org/licenses/>.
*/
#include <yaml.h>
#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, &gtpc_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(&gtpc_array) ==
YAML_MAPPING_NODE) {
memcpy(&gtpc_iter, &gtpc_array,
sizeof(ogs_yaml_iter_t));
} else if (ogs_yaml_iter_type(&gtpc_array) ==
YAML_SEQUENCE_NODE) {
if (!ogs_yaml_iter_next(&gtpc_array))
break;
ogs_yaml_iter_recurse(&gtpc_array, &gtpc_iter);
} else if (ogs_yaml_iter_type(&gtpc_array) ==
YAML_SCALAR_NODE) {
break;
} else
ogs_assert_if_reached();
while (ogs_yaml_iter_next(&gtpc_iter)) {
const char *gtpc_key =
ogs_yaml_iter_key(&gtpc_iter);
ogs_assert(gtpc_key);
if (!strcmp(gtpc_key, "family")) {
const char *v = ogs_yaml_iter_value(&gtpc_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(&gtpc_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(&gtpc_iter);
if (v) port = atoi(v);
} else if (!strcmp(gtpc_key, "dev")) {
dev = ogs_yaml_iter_value(&gtpc_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(&gtpc_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);
}

225
src/sgwc/context.h Normal file
View File

@ -0,0 +1,225 @@
/*
* Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
*
* 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 <https://www.gnu.org/licenses/>.
*/
#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 */

103
src/sgwc/event.c Normal file
View File

@ -0,0 +1,103 @@
/*
* Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
*
* 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 <https://www.gnu.org/licenses/>.
*/
#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";
}

78
src/sgwc/event.h Normal file
View File

@ -0,0 +1,78 @@
/*
* Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
*
* 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 <https://www.gnu.org/licenses/>.
*/
#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 */

191
src/sgwc/gtp-path.c Normal file
View File

@ -0,0 +1,191 @@
/*
* Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
*
* 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 <https://www.gnu.org/licenses/>.
*/
#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);
}

39
src/sgwc/gtp-path.h Normal file
View File

@ -0,0 +1,39 @@
/*
* Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
*
* 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 <https://www.gnu.org/licenses/>.
*/
#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 */

122
src/sgwc/init.c Normal file
View File

@ -0,0 +1,122 @@
/*
* Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
*
* 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 <https://www.gnu.org/licenses/>.
*/
#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);
}

70
src/sgwc/meson.build Normal file
View File

@ -0,0 +1,70 @@
# Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
# 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 <https://www.gnu.org/licenses/>.
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)

339
src/sgwc/pfcp-path.c Normal file
View File

@ -0,0 +1,339 @@
/*
* Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
*
* 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 <https://www.gnu.org/licenses/>.
*/
#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);
}

50
src/sgwc/pfcp-path.h Normal file
View File

@ -0,0 +1,50 @@
/*
* Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
*
* 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 <https://www.gnu.org/licenses/>.
*/
#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 */

330
src/sgwc/pfcp-sm.c Normal file
View File

@ -0,0 +1,330 @@
/*
* Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
*
* 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 <https://www.gnu.org/licenses/>.
*/
#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;
}
}

46
src/sgwc/s11-build.c Normal file
View File

@ -0,0 +1,46 @@
/*
* Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
*
* 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 <https://www.gnu.org/licenses/>.
*/
#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);
}

View File

@ -17,18 +17,19 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#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 */

1090
src/sgwc/s11-handler.c Normal file

File diff suppressed because it is too large Load Diff

70
src/sgwc/s11-handler.h Normal file
View File

@ -0,0 +1,70 @@
/*
* Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
*
* 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 <https://www.gnu.org/licenses/>.
*/
#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 */

523
src/sgwc/s5c-handler.c Normal file
View File

@ -0,0 +1,523 @@
/*
* Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
*
* 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 <https://www.gnu.org/licenses/>.
*/
#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);
}

52
src/sgwc/s5c-handler.h Normal file
View File

@ -0,0 +1,52 @@
/*
* Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
*
* 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 <https://www.gnu.org/licenses/>.
*/
#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 */

313
src/sgwc/sgwc-sm.c Normal file
View File

@ -0,0 +1,313 @@
/*
* Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
*
* 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 <https://www.gnu.org/licenses/>.
*/
#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(&gtp_message, pfcp_xact->gtpbuf);
ogs_pkbuf_free(pfcp_xact->gtpbuf);
e->gtp_message = &gtp_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(&gtp_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, &gtp_message.h, &gtp_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, &gtp_message.echo_request);
break;
case OGS_GTP_ECHO_RESPONSE_TYPE:
sgwc_handle_echo_response(gtp_xact, &gtp_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(&gtp_message);
if (sgwc_ue)
OGS_SETUP_GTP_NODE(sgwc_ue, gnode);
}
sgwc_s11_handle_create_session_request(
sgwc_ue, gtp_xact, recvbuf, &gtp_message);
break;
case OGS_GTP_MODIFY_BEARER_REQUEST_TYPE:
sgwc_s11_handle_modify_bearer_request(
sgwc_ue, gtp_xact, recvbuf, &gtp_message);
break;
case OGS_GTP_DELETE_SESSION_REQUEST_TYPE:
sgwc_s11_handle_delete_session_request(
sgwc_ue, gtp_xact, recvbuf, &gtp_message);
break;
case OGS_GTP_CREATE_BEARER_RESPONSE_TYPE:
sgwc_s11_handle_create_bearer_response(
sgwc_ue, gtp_xact, recvbuf, &gtp_message);
break;
case OGS_GTP_UPDATE_BEARER_RESPONSE_TYPE:
sgwc_s11_handle_update_bearer_response(
sgwc_ue, gtp_xact, recvbuf, &gtp_message);
break;
case OGS_GTP_DELETE_BEARER_RESPONSE_TYPE:
sgwc_s11_handle_delete_bearer_response(
sgwc_ue, gtp_xact, recvbuf, &gtp_message);
break;
case OGS_GTP_RELEASE_ACCESS_BEARERS_REQUEST_TYPE:
sgwc_s11_handle_release_access_bearers_request(
sgwc_ue, gtp_xact, recvbuf, &gtp_message);
break;
case OGS_GTP_DOWNLINK_DATA_NOTIFICATION_ACKNOWLEDGE_TYPE:
sgwc_s11_handle_downlink_data_notification_ack(
sgwc_ue, gtp_xact, recvbuf, &gtp_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, &gtp_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, &gtp_message);
break;
case OGS_GTP_BEARER_RESOURCE_COMMAND_TYPE:
sgwc_s11_handle_bearer_resource_command(
sgwc_ue, gtp_xact, recvbuf, &gtp_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(&gtp_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, &gtp_message.h, &gtp_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, &gtp_message.echo_request);
break;
case OGS_GTP_ECHO_RESPONSE_TYPE:
sgwc_handle_echo_response(gtp_xact, &gtp_message.echo_response);
break;
case OGS_GTP_CREATE_SESSION_RESPONSE_TYPE:
sgwc_s5c_handle_create_session_response(
sess, gtp_xact, recvbuf, &gtp_message);
break;
case OGS_GTP_DELETE_SESSION_RESPONSE_TYPE:
sgwc_s5c_handle_delete_session_response(
sess, gtp_xact, recvbuf, &gtp_message);
break;
case OGS_GTP_CREATE_BEARER_REQUEST_TYPE:
sgwc_s5c_handle_create_bearer_request(
sess, gtp_xact, recvbuf, &gtp_message);
break;
case OGS_GTP_UPDATE_BEARER_REQUEST_TYPE:
sgwc_s5c_handle_update_bearer_request(
sess, gtp_xact, recvbuf, &gtp_message);
break;
case OGS_GTP_DELETE_BEARER_REQUEST_TYPE:
sgwc_s5c_handle_delete_bearer_request(
sess, gtp_xact, recvbuf, &gtp_message);
break;
case OGS_GTP_BEARER_RESOURCE_FAILURE_INDICATION_TYPE:
sgwc_s5c_handle_bearer_resource_failure_indication(
sess, gtp_xact, recvbuf, &gtp_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;
}
}

47
src/sgwc/sgwc-sm.h Normal file
View File

@ -0,0 +1,47 @@
/*
* Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
*
* 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 <https://www.gnu.org/licenses/>.
*/
#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 */

370
src/sgwc/sxa-build.c Normal file
View File

@ -0,0 +1,370 @@
/*
* Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
*
* 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 <https://www.gnu.org/licenses/>.
*/
#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);
}

42
src/sgwc/sxa-build.h Normal file
View File

@ -0,0 +1,42 @@
/*
* Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
*
* 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 <https://www.gnu.org/licenses/>.
*/
#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 */

912
src/sgwc/sxa-handler.c Normal file
View File

@ -0,0 +1,912 @@
/*
* Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
*
* 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 <https://www.gnu.org/licenses/>.
*/
#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 = &gtp_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, &gtp_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, &gtp_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);
}

49
src/sgwc/sxa-handler.h Normal file
View File

@ -0,0 +1,49 @@
/*
* Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
*
* 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 <https://www.gnu.org/licenses/>.
*/
#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 */

74
src/sgwc/timer.c Normal file
View File

@ -0,0 +1,74 @@
/*
* Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
*
* 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 <https://www.gnu.org/licenses/>.
*/
#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);
}

49
src/sgwc/timer.h Normal file
View File

@ -0,0 +1,49 @@
/*
* Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
*
* 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 <https://www.gnu.org/licenses/>.
*/
#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 */

40
src/sgwu/app.c Normal file
View File

@ -0,0 +1,40 @@
/*
* Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
*
* 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 <https://www.gnu.org/licenses/>.
*/
#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");
}

503
src/sgwu/context.c Normal file
View File

@ -0,0 +1,503 @@
/*
* Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
*
* 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 <https://www.gnu.org/licenses/>.
*/
#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, &gtpu_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(&gtpu_array) ==
YAML_MAPPING_NODE) {
memcpy(&gtpu_iter, &gtpu_array,
sizeof(ogs_yaml_iter_t));
} else if (ogs_yaml_iter_type(&gtpu_array) ==
YAML_SEQUENCE_NODE) {
if (!ogs_yaml_iter_next(&gtpu_array))
break;
ogs_yaml_iter_recurse(&gtpu_array, &gtpu_iter);
} else if (ogs_yaml_iter_type(&gtpu_array) ==
YAML_SCALAR_NODE) {
break;
} else
ogs_assert_if_reached();
while (ogs_yaml_iter_next(&gtpu_iter)) {
const char *gtpu_key =
ogs_yaml_iter_key(&gtpu_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(&gtpu_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(
&gtpu_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(&gtpu_iter);
if (v) port = atoi(v);
} else if (!strcmp(gtpu_key, "dev")) {
dev = ogs_yaml_iter_value(&gtpu_iter);
} else if (!strcmp(gtpu_key,
"teid_range_indication")) {
teid_range_indication =
ogs_yaml_iter_value(&gtpu_iter);
} else if (!strcmp(gtpu_key,
"teid_range")) {
teid_range = ogs_yaml_iter_value(&gtpu_iter);
} else if (!strcmp(gtpu_key,
"network_instance")) {
network_instance =
ogs_yaml_iter_value(&gtpu_iter);
} else if (!strcmp(gtpu_key,
"source_interface")) {
source_interface =
ogs_yaml_iter_value(&gtpu_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(&gtpu_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;
}

91
src/sgwu/context.h Normal file
View File

@ -0,0 +1,91 @@
/*
* Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
*
* 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 <https://www.gnu.org/licenses/>.
*/
#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 */

98
src/sgwu/event.c Normal file
View File

@ -0,0 +1,98 @@
/*
* Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
*
* 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 <https://www.gnu.org/licenses/>.
*/
#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";
}

75
src/sgwu/event.h Normal file
View File

@ -0,0 +1,75 @@
/*
* Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
*
* 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 <https://www.gnu.org/licenses/>.
*/
#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 */

208
src/sgwu/gtp-path.c Normal file
View File

@ -0,0 +1,208 @@
/*
* Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
*
* 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 <https://www.gnu.org/licenses/>.
*/
#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);
}

40
src/sgwu/gtp-path.h Normal file
View File

@ -0,0 +1,40 @@
/*
* Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
*
* 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 <https://www.gnu.org/licenses/>.
*/
#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 */

118
src/sgwu/init.c Normal file
View File

@ -0,0 +1,118 @@
/*
* Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
*
* 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 <https://www.gnu.org/licenses/>.
*/
#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);
}

64
src/sgwu/meson.build Normal file
View File

@ -0,0 +1,64 @@
# Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
# 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 <https://www.gnu.org/licenses/>.
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)

288
src/sgwu/pfcp-path.c Normal file
View File

@ -0,0 +1,288 @@
/*
* Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
*
* 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 <https://www.gnu.org/licenses/>.
*/
#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);
}

48
src/sgwu/pfcp-path.h Normal file
View File

@ -0,0 +1,48 @@
/*
* Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
*
* 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 <https://www.gnu.org/licenses/>.
*/
#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 */

305
src/sgwu/pfcp-sm.c Normal file
View File

@ -0,0 +1,305 @@
/*
* Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
*
* 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 <https://www.gnu.org/licenses/>.
*/
#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;
}
}

121
src/sgwu/sgwu-sm.c Normal file
View File

@ -0,0 +1,121 @@
/*
* Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
*
* 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 <https://www.gnu.org/licenses/>.
*/
#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;
}
}

47
src/sgwu/sgwu-sm.h Normal file
View File

@ -0,0 +1,47 @@
/*
* Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
*
* 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 <https://www.gnu.org/licenses/>.
*/
#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 */

123
src/sgwu/sxa-build.c Normal file
View File

@ -0,0 +1,123 @@
/*
* Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
*
* 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 <https://www.gnu.org/licenses/>.
*/
#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);
}

40
src/sgwu/sxa-build.h Normal file
View File

@ -0,0 +1,40 @@
/*
* Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
*
* 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 <https://www.gnu.org/licenses/>.
*/
#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 */

337
src/sgwu/sxa-handler.c Normal file
View File

@ -0,0 +1,337 @@
/*
* Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
*
* 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 <https://www.gnu.org/licenses/>.
*/
#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;
}
}

Some files were not shown because too many files have changed in this diff Show More