diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 00000000..e69de29b diff --git a/404.html b/404.html new file mode 100644 index 00000000..5a77a805 --- /dev/null +++ b/404.html @@ -0,0 +1,604 @@ + + + +
+ + + + + + + + + + + + + +Note
+Author: Jimmy Chang
+Date: 2023/7/5
This technique leverages namespace to run UERANSIM, an opensource 5G-UE and RAN(gNodeB) simulator, and connect to free5GC.
+UERANSIM follows the 3GPP specification for developing and can support multiple 5G core (5GC) including free5GC.
Why are we using namespace? Well, you can follow ULCL and free5GC compose to set up the environment with VM and docker, but there are limitations for hardware’s capability. With network namespace, you can have different and separate network instances of network
+interfaces and routing tables that operate independently.
So, what is network namespace? Network namespace makes a copy of network stack with its own routing table, firewall and devices. A named network namespace is an object at /var/run/netns/
. The file descriptor resulting from opening /var/run/netns/
refers to the specified network namespace. Holding that file descriptor open
+keeps the network namespace alive.
And how to make both namespaces communicating? A virtual Ethernet device (veth) pair provides the abstraction that can be used to create tunnels between network namespaces, and can be used to create bridge to a physical network device in another namespace. Veth pair also be used as standalone network devices.
+When the namespace freed, veth device which attatch to would be destroyed.
The environment is as follow. Suppose you have already installed as well as set up free5GC and UERANSIM properly.
+Note
+Namespace free5GC represents host network namespace. And enp0s5 is an ethernet interface connectting to external.
+Each devices as follow
+| Device | IP |
+| ------------- | ------------- |
+| veth0 | 10.200.200.1 |
+| veth1 | 10.200.200.2 |
+| br-veth0 | none |
+| br-veth1 | none |
+| enp0s5 | 10.211.55.23 |
+
+
+UE information in UERANSIM as follow. Already
+| IMSI | DNN |
+| ---------------- | ------------- |
+| 208930000000003 | internet |
+
Replace ngapIpList IP from 127.0.0.18
to 10.200.200.2
:
info:
+ version: 1.0.9
+ description: AMF initial local configuration
+
+configuration:
+ amfName: AMF # the name of this AMF
+ ngapIpList: # the IP list of N2 interfaces on this AMF
+ - 10.200.200.2 # 127.0.0.18
+ ngapPort: 38412 # the SCTP port listened by NGAP
+ sbi: # Service-based interface information
+ scheme: http # the protocol for sbi (http or https)
+ registerIPv4: 127.0.0.18 # IP used to register to NRF
+ bindingIPv4: 127.0.0.18 # IP used to bind the service
+ port: 8000 # port used to bind the service
+ tls: # the local path of TLS key
+ pem: cert/amf.pem # AMF TLS Certificate
+ key: cert/amf.key # AMF TLS Private key
+ serviceNameList: # the SBI services provided by this AMF, refer to TS 29.518
+ - namf-comm # Namf_Communication service
+ - namf-evts # Namf_EventExposure service
+ - namf-mt # Namf_MT service
+ - namf-loc # Namf_Location service
+ - namf-oam # OAM service
+ servedGuamiList: # Guami (Globally Unique AMF ID) list supported by this AMF
+ # <GUAMI> = <MCC><MNC><AMF ID>
+ - plmnId: # Public Land Mobile Network ID, <PLMN ID> = <MCC><MNC>
+ mcc: 208 # Mobile Country Code (3 digits string, digit: 0~9)
+ mnc: 93 # Mobile Network Code (2 or 3 digits string, digit: 0~9)
+ amfId: cafe00 # AMF identifier (3 bytes hex string, range: 000000~FFFFFF)
+ supportTaiList: # the TAI (Tracking Area Identifier) list supported by this AMF
+ - plmnId: # Public Land Mobile Network ID, <PLMN ID> = <MCC><MNC>
+ mcc: 208 # Mobile Country Code (3 digits string, digit: 0~9)
+ mnc: 93 # Mobile Network Code (2 or 3 digits string, digit: 0~9)
+ tac: 000001 # Tracking Area Code (3 bytes hex string, range: 000000~FFFFFF)
+ plmnSupportList: # the PLMNs (Public land mobile network) list supported by this AMF
+ - plmnId: # Public Land Mobile Network ID, <PLMN ID> = <MCC><MNC>
+ mcc: 208 # Mobile Country Code (3 digits string, digit: 0~9)
+ mnc: 93 # Mobile Network Code (2 or 3 digits string, digit: 0~9)
+ snssaiList: # the S-NSSAI (Single Network Slice Selection Assistance Information) list supported by this AMF
+ - sst: 1 # Slice/Service Type (uinteger, range: 0~255)
+ sd: 010203 # Slice Differentiator (3 bytes hex string, range: 000000~FFFFFF)
+ - sst: 1 # Slice/Service Type (uinteger, range: 0~255)
+ sd: 112233 # Slice Differentiator (3 bytes hex string, range: 000000~FFFFFF)
+ supportDnnList: # the DNN (Data Network Name) list supported by this AMF
+ - internet
+ nrfUri: http://127.0.0.10:8000 # a valid URI of NRF
+ security: # NAS security parameters
+ integrityOrder: # the priority of integrity algorithms
+ - NIA2
+ # - NIA0
+ cipheringOrder: # the priority of ciphering algorithms
+ - NEA0
+ # - NEA2
+ networkName: # the name of this core network
+ full: free5GC
+ short: free
+ ngapIE: # Optional NGAP IEs
+ mobilityRestrictionList: # Mobility Restriction List IE, refer to TS 38.413
+ enable: true # append this IE in related message or not
+ maskedIMEISV: # Masked IMEISV IE, refer to TS 38.413
+ enable: true # append this IE in related message or not
+ redirectionVoiceFallback: # Redirection Voice Fallback IE, refer to TS 38.413
+ enable: false # append this IE in related message or not
+ nasIE: # Optional NAS IEs
+ networkFeatureSupport5GS: # 5gs Network Feature Support IE, refer to TS 24.501
+ enable: true # append this IE in Registration accept or not
+ length: 1 # IE content length (uinteger, range: 1~3)
+ imsVoPS: 0 # IMS voice over PS session indicator (uinteger, range: 0~1)
+ emc: 0 # Emergency service support indicator for 3GPP access (uinteger, range: 0~3)
+ emf: 0 # Emergency service fallback indicator for 3GPP access (uinteger, range: 0~3)
+ iwkN26: 0 # Interworking without N26 interface indicator (uinteger, range: 0~1)
+ mpsi: 0 # MPS indicator (uinteger, range: 0~1)
+ emcN3: 0 # Emergency service support indicator for Non-3GPP access (uinteger, range: 0~1)
+ mcsi: 0 # MCS indicator (uinteger, range: 0~1)
+ t3502Value: 720 # timer value (seconds) at UE side
+ t3512Value: 3600 # timer value (seconds) at UE side
+ non3gppDeregTimerValue: 3240 # timer value (seconds) at UE side
+ # retransmission timer for paging message
+ t3513:
+ enable: true # true or false
+ expireTime: 6s # default is 6 seconds
+ maxRetryTimes: 4 # the max number of retransmission
+ # retransmission timer for NAS Deregistration Request message
+ t3522:
+ enable: true # true or false
+ expireTime: 6s # default is 6 seconds
+ maxRetryTimes: 4 # the max number of retransmission
+ # retransmission timer for NAS Registration Accept message
+ t3550:
+ enable: true # true or false
+ expireTime: 6s # default is 6 seconds
+ maxRetryTimes: 4 # the max number of retransmission
+ # retransmission timer for NAS Authentication Request/Security Mode Command message
+ t3560:
+ enable: true # true or false
+ expireTime: 6s # default is 6 seconds
+ maxRetryTimes: 4 # the max number of retransmission
+ # retransmission timer for NAS Notification message
+ t3565:
+ enable: true # true or false
+ expireTime: 6s # default is 6 seconds
+ maxRetryTimes: 4 # the max number of retransmission
+ # retransmission timer for NAS Identity Request message
+ t3570:
+ enable: true # true or false
+ expireTime: 6s # default is 6 seconds
+ maxRetryTimes: 4 # the max number of retransmission
+ locality: area1 # Name of the location where a set of AMF, SMF, PCF and UPFs are located
+ sctp: # set the sctp server setting <optinal>, once this field is set, please also add maxInputStream, maxOsStream, maxAttempts, maxInitTimeOut
+ numOstreams: 3 # the maximum out streams of each sctp connection
+ maxInstreams: 5 # the maximum in streams of each sctp connection
+ maxAttempts: 2 # the maximum attempts of each sctp connection
+ maxInitTimeout: 2 # the maximum init timeout of each sctp connection
+ defaultUECtxReq: false # the default value of UE Context Request to decide when triggering Initial Context Setup procedure
+
+logger: # log output setting
+ enable: true # true or false
+ level: info # how detailed to output, value: trace, debug, info, warn, error, fatal, panic
+ reportCaller: false # enable the caller report or not, value: true or false
+
Replace userplaneInformation / upNodes / UPF / interfaces / endpoints from 127.0.0.8
to 10.200.200.2
:
info:
+ version: 1.0.7
+ description: SMF initial local configuration
+
+configuration:
+ smfName: SMF # the name of this SMF
+ sbi: # Service-based interface information
+ scheme: http # the protocol for sbi (http or https)
+ registerIPv4: 127.0.0.2 # IP used to register to NRF
+ bindingIPv4: 127.0.0.2 # IP used to bind the service
+ port: 8000 # Port used to bind the service
+ tls: # the local path of TLS key
+ key: cert/smf.key # SMF TLS Certificate
+ pem: cert/smf.pem # SMF TLS Private key
+ serviceNameList: # the SBI services provided by this SMF, refer to TS 29.502
+ - nsmf-pdusession # Nsmf_PDUSession service
+ - nsmf-event-exposure # Nsmf_EventExposure service
+ - nsmf-oam # OAM service
+ snssaiInfos: # the S-NSSAI (Single Network Slice Selection Assistance Information) list supported by this AMF
+ - sNssai: # S-NSSAI (Single Network Slice Selection Assistance Information)
+ sst: 1 # Slice/Service Type (uinteger, range: 0~255)
+ sd: 010203 # Slice Differentiator (3 bytes hex string, range: 000000~FFFFFF)
+ dnnInfos: # DNN information list
+ - dnn: internet # Data Network Name
+ dns: # the IP address of DNS
+ ipv4: 8.8.8.8
+ ipv6: 2001:4860:4860::8888
+ - sNssai: # S-NSSAI (Single Network Slice Selection Assistance Information)
+ sst: 1 # Slice/Service Type (uinteger, range: 0~255)
+ sd: 112233 # Slice Differentiator (3 bytes hex string, range: 000000~FFFFFF)
+ dnnInfos: # DNN information list
+ - dnn: internet # Data Network Name
+ dns: # the IP address of DNS
+ ipv4: 8.8.8.8
+ ipv6: 2001:4860:4860::8888
+ plmnList: # the list of PLMN IDs that this SMF belongs to (optional, remove this key when unnecessary)
+ - mcc: 208 # Mobile Country Code (3 digits string, digit: 0~9)
+ mnc: 93 # Mobile Network Code (2 or 3 digits string, digit: 0~9)
+ locality: area1 # Name of the location where a set of AMF, SMF, PCF and UPFs are located
+ pfcp: # the IP address of N4 interface on this SMF (PFCP)
+ # addr config is deprecated in smf config v1.0.3, please use the following config
+ nodeID: 127.0.0.1 # the Node ID of this SMF
+ listenAddr: 127.0.0.1 # the IP/FQDN of N4 interface on this SMF (PFCP)
+ externalAddr: 127.0.0.1 # the IP/FQDN of N4 interface on this SMF (PFCP)
+ userplaneInformation: # list of userplane information
+ upNodes: # information of userplane node (AN or UPF)
+ gNB1: # the name of the node
+ type: AN # the type of the node (AN or UPF)
+ UPF: # the name of the node
+ type: UPF # the type of the node (AN or UPF)
+ nodeID: 127.0.0.8 # the Node ID of this UPF
+ addr: 127.0.0.8 # the IP/FQDN of N4 interface on this UPF (PFCP)
+ sNssaiUpfInfos: # S-NSSAI information list for this UPF
+ - sNssai: # S-NSSAI (Single Network Slice Selection Assistance Information)
+ sst: 1 # Slice/Service Type (uinteger, range: 0~255)
+ sd: 010203 # Slice Differentiator (3 bytes hex string, range: 000000~FFFFFF)
+ dnnUpfInfoList: # DNN information list for this S-NSSAI
+ - dnn: internet
+ pools:
+ - cidr: 10.60.0.0/16
+ staticPools:
+ - cidr: 10.60.100.0/24
+ - sNssai: # S-NSSAI (Single Network Slice Selection Assistance Information)
+ sst: 1 # Slice/Service Type (uinteger, range: 0~255)
+ sd: 112233 # Slice Differentiator (3 bytes hex string, range: 000000~FFFFFF)
+ dnnUpfInfoList: # DNN information list for this S-NSSAI
+ - dnn: internet
+ pools:
+ - cidr: 10.61.0.0/16
+ staticPools:
+ - cidr: 10.61.100.0/24
+ interfaces: # Interface list for this UPF
+ - interfaceType: N3 # the type of the interface (N3 or N9)
+ endpoints: # the IP address of this N3/N9 interface on this UPF
+ - 10.200.200.2 # 127.0.0.8
+ networkInstances: # Data Network Name (DNN)
+ - internet
+ links: # the topology graph of userplane, A and B represent the two nodes of each link
+ - A: gNB1
+ B: UPF
+ # retransmission timer for pdu session modification command
+ t3591:
+ enable: true # true or false
+ expireTime: 16s # default is 6 seconds
+ maxRetryTimes: 3 # the max number of retransmission
+ # retransmission timer for pdu session release command
+ t3592:
+ enable: true # true or false
+ expireTime: 16s # default is 6 seconds
+ maxRetryTimes: 3 # the max number of retransmission
+ nrfUri: http://127.0.0.10:8000 # a valid URI of NRF
+ #urrPeriod: 10 # default usage report period in seconds
+ #urrThreshold: 1000 # default usage report threshold in bytes
+
+logger: # log output setting
+ enable: true # true or false
+ level: info # how detailed to output, value: trace, debug, info, warn, error, fatal, panic
+ reportCaller: false # enable the caller report or not, value: true or false
+
Replace gtpu from 127.0.0.8
to 10.200.200.2
:
version: 1.0.3
+description: UPF initial local configuration
+
+# The listen IP and nodeID of the N4 interface on this UPF (Can't set to 0.0.0.0)
+pfcp:
+ addr: 127.0.0.8 # IP addr for listening
+ nodeID: 127.0.0.8 # External IP or FQDN can be reached
+ retransTimeout: 1s # retransmission timeout
+ maxRetrans: 3 # the max number of retransmission
+
+gtpu:
+ forwarder: gtp5g
+ # The IP list of the N3/N9 interfaces on this UPF
+ # If there are multiple connection, set addr to 0.0.0.0 or list all the addresses
+ ifList:
+ - addr: 10.200.200.2 # 127.0.0.8
+ type: N3
+ # name: upf.5gc.nctu.me
+ # ifname: gtpif
+ # mtu: 1400
+
+# The DNN list supported by UPF
+dnnList:
+ - dnn: internet # Data Network Name
+ cidr: 10.60.0.0/24 # Classless Inter-Domain Routing for assigned IPv4 pool of UE
+ # natifname: eth0
+
+logger: # log output setting
+ enable: true # true or false
+ level: info # how detailed to output, value: trace, debug, info, warn, error, fatal, panic
+ reportCaller: false # enable the caller report or not, value: true or false
+
UERANSIM/config/free5gc-gnb.yaml
+Replace ngapIp from 127.0.0.1
to 10.200.200.1
Replace gtpIp from 127.0.0.1
to 10.200.200.1
Replace amfConfigs / address from 127.0.0.1
to 10.200.200.2
mcc: '208' # Mobile Country Code value
+mnc: '93' # Mobile Network Code value (2 or 3 digits)
+
+nci: '0x000000010' # NR Cell Identity (36-bit)
+idLength: 32 # NR gNB ID length in bits [22...32]
+tac: 1 # Tracking Area Code
+
+linkIp: 127.0.0.1 # gNB's local IP address for Radio Link Simulation (Usually same with local IP)
+ngapIp: 10.200.200.1 # 127.0.0.1 # gNB's local IP address for N2 Interface (Usually same with local IP)
+gtpIp: 10.200.200.1 # 127.0.0.1 # gNB's local IP address for N3 Interface (Usually same with local IP)
+
+# List of AMF address information
+amfConfigs:
+ - address: 10.200.200.2 # 127.0.0.1
+ port: 38412
+
+# List of supported S-NSSAIs by this gNB
+slices:
+ - sst: 0x1
+ sd: 0x010203
+
+# Indicates whether or not SCTP stream number errors should be ignored.
+ignoreStreamIds: true
+
# IMSI number of the UE. IMSI = [MCC|MNC|MSISDN] (In total 15 or 16 digits)
+supi: 'imsi-208930000000003'
+# Mobile Country Code value
+mcc: '208'
+# Mobile Network Code value (2 or 3 digits)
+mnc: '93'
+
+# Permanent subscription key
+key: '8baf473f2f8fd09487cccbd7097c6862'
+# Operator code (OP or OPC) of the UE
+op: '8e27b6af0e692e750f32667a3b14605d'
+# This value specifies the OP type and it can be either 'OP' or 'OPC'
+opType: 'OP'
+# Authentication Management Field (AMF) value
+amf: '8000'
+# IMEI number of the device. It is used if no SUPI is provided
+imei: '356938035643803'
+# IMEISV number of the device. It is used if no SUPI and IMEI is provided
+imeiSv: '4370816125816151'
+
+# List of gNB IP addresses for Radio Link Simulation
+gnbSearchList:
+ - 127.0.0.1
+
+# Initial PDU sessions to be established
+sessions:
+ - type: 'IPv4'
+ apn: 'internet'
+ slice:
+ sst: 0x01
+ sd: 0x010203
+
+# List of requested S-NSSAIs by this UE
+slices:
+ - sst: 0x01
+ sd: 0x010203
+
+# Supported encryption and integrity algorithms by this UE
+integrity:
+ IA1: true
+ IA2: true
+ IA3: true
+ciphering:
+ EA1: true
+ EA2: true
+ EA3: true
+
First, create a namespace:
+Note
+Assume that you are either running as root, or it behoves you to prepend sudo
to commands as necessary.
ip netns add ueransim
+
ip link add free5gc-br type bridge
+
ip link add veth0 type veth peer name br-veth0
+ip link add veth1 type veth peer name br-veth1
+
root@free5gc:~# ip a
+1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
+ link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
+ inet 127.0.0.1/8 scope host lo
+ valid_lft forever preferred_lft forever
+ inet6 ::1/128 scope host
+ valid_lft forever preferred_lft forever
+2: enp0s5: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
+ link/ether 00:1c:42:b1:ba:f4 brd ff:ff:ff:ff:ff:ff
+ inet 10.211.55.23/24 brd 10.211.55.255 scope global dynamic enp0s5
+ valid_lft 1714sec preferred_lft 1714sec
+ inet6 fdb2:2c26:f4e4:0:21c:42ff:feb1:baf4/64 scope global dynamic mngtmpaddr noprefixroute
+ valid_lft 2591750sec preferred_lft 604550sec
+ inet6 fe80::21c:42ff:feb1:baf4/64 scope link
+ valid_lft forever preferred_lft forever
+3: enp0s6: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
+ link/ether 00:1c:42:f1:11:c6 brd ff:ff:ff:ff:ff:ff
+ inet 10.37.129.20/24 brd 10.37.129.255 scope global enp0s6
+ valid_lft forever preferred_lft forever
+ inet6 fdb2:2c26:f4e4:1:21c:42ff:fef1:11c6/64 scope global dynamic mngtmpaddr noprefixroute
+ valid_lft 2591750sec preferred_lft 604550sec
+ inet6 fe80::21c:42ff:fef1:11c6/64 scope link
+ valid_lft forever preferred_lft forever
+4: free5gc-br: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000
+ link/ether 4e:f6:d7:9c:50:de brd ff:ff:ff:ff:ff:ff
+5: br-veth0@veth0: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN group default qlen 1000
+ link/ether c2:31:0c:5f:45:81 brd ff:ff:ff:ff:ff:ff
+6: veth0@br-veth0: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN group default qlen 1000
+ link/ether 4a:0f:1e:80:9b:be brd ff:ff:ff:ff:ff:ff
+7: br-veth1@veth1: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN group default qlen 1000
+ link/ether 56:99:b0:82:78:0d brd ff:ff:ff:ff:ff:ff
+8: veth1@br-veth1: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN group default qlen 1000
+ link/ether 12:5a:56:00:5b:be brd ff:ff:ff:ff:ff:ff
+
Next, assign interface to namespace:
+
ip link set dev veth0 netns ueransim
+
ip netns exec ueransim ip a add 10.200.200.1/24 dev veth0
+
ip netns exec ueransim ip link set lo up
+ip netns exec ueransim ip link set veth0 up
+
ip a
:root@free5gc:~# ip netns exec ueransim ip a
+1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
+ link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
+ inet 127.0.0.1/8 scope host lo
+ valid_lft forever preferred_lft forever
+ inet6 ::1/128 scope host
+ valid_lft forever preferred_lft forever
+6: veth0@if5: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state LOWERLAYERDOWN group default qlen 1000
+ link/ether 4a:0f:1e:80:9b:be brd ff:ff:ff:ff:ff:ff link-netnsid 0
+ inet 10.200.200.1/24 scope global veth0
+ valid_lft forever preferred_lft forever
+
ip a add 10.200.200.2/24 dev veth1
+ip link set veth1 up
+
ip link set dev br-veth0 master free5gc-br
+ip link set dev br-veth1 master free5gc-br
+ip link set br-veth0 up
+ip link set br-veth1 up
+ip link set free5gc-br up
+
bridge link
to check:root@free5gc:~# bridge link
+5: br-veth0@if6: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 master free5gc-br state forwarding priority 32 cost 2
+7: br-veth1@veth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 master free5gc-br state forwarding priority 32 cost 2
+
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
+ link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
+ inet 127.0.0.1/8 scope host lo
+ valid_lft forever preferred_lft forever
+ inet6 ::1/128 scope host
+ valid_lft forever preferred_lft forever
+2: enp0s5: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
+ link/ether 00:1c:42:b1:ba:f4 brd ff:ff:ff:ff:ff:ff
+ inet 10.211.55.23/24 brd 10.211.55.255 scope global dynamic enp0s5
+ valid_lft 1000sec preferred_lft 1000sec
+ inet6 fdb2:2c26:f4e4:0:21c:42ff:feb1:baf4/64 scope global dynamic mngtmpaddr noprefixroute
+ valid_lft 2591870sec preferred_lft 604670sec
+ inet6 fe80::21c:42ff:feb1:baf4/64 scope link
+ valid_lft forever preferred_lft forever
+3: enp0s6: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
+ link/ether 00:1c:42:f1:11:c6 brd ff:ff:ff:ff:ff:ff
+ inet 10.37.129.20/24 brd 10.37.129.255 scope global enp0s6
+ valid_lft forever preferred_lft forever
+ inet6 fdb2:2c26:f4e4:1:21c:42ff:fef1:11c6/64 scope global dynamic mngtmpaddr noprefixroute
+ valid_lft 2591870sec preferred_lft 604670sec
+ inet6 fe80::21c:42ff:fef1:11c6/64 scope link
+ valid_lft forever preferred_lft forever
+4: free5gc-br: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
+ link/ether 56:99:b0:82:78:0d brd ff:ff:ff:ff:ff:ff
+ inet6 fe80::5499:b0ff:fe82:780d/64 scope link
+ valid_lft forever preferred_lft forever
+5: br-veth0@if6: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master free5gc-br state UP group default qlen 1000
+ link/ether c2:31:0c:5f:45:81 brd ff:ff:ff:ff:ff:ff link-netns ueransim
+ inet6 fe80::c031:cff:fe5f:4581/64 scope link
+ valid_lft forever preferred_lft forever
+7: br-veth1@veth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master free5gc-br state UP group default qlen 1000
+ link/ether 56:99:b0:82:78:0d brd ff:ff:ff:ff:ff:ff
+ inet6 fe80::5499:b0ff:fe82:780d/64 scope link
+ valid_lft forever preferred_lft forever
+8: veth1@br-veth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
+ link/ether 12:5a:56:00:5b:be brd ff:ff:ff:ff:ff:ff
+ inet 10.200.200.2/24 scope global veth1
+ valid_lft forever preferred_lft forever
+ inet6 fe80::105a:56ff:fe00:5bbe/64 scope link
+ valid_lft forever preferred_lft forever
+
Note
+You can perform ip netns exec ueransim /bin/bash --rcfile <(echo "PS1=\"ueransim> \"")
to enter namespace and modify shell prefix.
root@free5gc:~# ip netns exec ueransim /bin/bash --rcfile <(echo "PS1=\"ueransim> \"")
+ueransim> ping -c2 10.200.200.2
+PING 10.200.200.2 (10.200.200.2) 56(84) bytes of data.
+64 bytes from 10.200.200.2: icmp_seq=1 ttl=64 time=0.089 ms
+64 bytes from 10.200.200.2: icmp_seq=2 ttl=64 time=0.226 ms
+
+--- 10.200.200.2 ping statistics ---
+2 packets transmitted, 2 received, 0% packet loss, time 1020ms
+rtt min/avg/max/mdev = 0.089/0.157/0.226/0.068 ms
+
ueransim> ip route add default via 10.200.200.2
+ueransim> netstat -rn
+Kernel IP routing table
+Destination Gateway Genmask Flags MSS Window irtt Iface
+0.0.0.0 10.200.200.2 0.0.0.0 UG 0 0 0 veth0
+10.200.200.0 0.0.0.0 255.255.255.0 U 0 0 0 veth0
+
ueransim> ping -c2 8.8.8.8
+PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
+
+--- 8.8.8.8 ping statistics ---
+2 packets transmitted, 0 received, 100% packet loss, time 1028ms
+
root@free5gc:~# iptables -t nat -A POSTROUTING -o enp0s5 -j MASQUERADE
+root@free5gc:~# sysctl -w net.ipv4.ip_forward=1
+root@free5gc:~# sudo iptables -I FORWARD 1 -j ACCEPT
+
ueransim> ping -c2 8.8.8.8
+PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
+64 bytes from 8.8.8.8: icmp_seq=1 ttl=127 time=13.9 ms
+64 bytes from 8.8.8.8: icmp_seq=2 ttl=127 time=28.0 ms
+
+--- 8.8.8.8 ping statistics ---
+2 packets transmitted, 2 received, 0% packet loss, time 1002ms
+rtt min/avg/max/mdev = 13.866/20.939/28.012/7.073 ms
+
After free5GC execute run.sh
, it's time for UERANSIM:
In terminal 1:
+
ueransim> build/nr-gnb -c config/free5gc-gnb.yaml
+UERANSIM v3.1.0
+[2023-07-05 19:58:26.368] [sctp] [info] Trying to establish SCTP connection... (10.200.200.2:38412)
+[2023-07-05 19:58:26.373] [sctp] [info] SCTP connection established (10.200.200.2:38412)
+[2023-07-05 19:58:26.374] [sctp] [debug] SCTP association setup ascId[3]
+[2023-07-05 19:58:26.375] [ngap] [debug] Sending NG Setup Request
+[2023-07-05 19:58:26.380] [ngap] [debug] NG Setup Response received
+[2023-07-05 19:58:26.380] [ngap] [info] NG Setup procedure is successful
+[2023-07-05 19:58:35.804] [mr] [info] New UE connected to gNB. Total number of UEs is now: 1
+[2023-07-05 19:58:35.806] [rrc] [debug] Sending RRC Setup for UE[3]
+[2023-07-05 19:58:35.807] [ngap] [debug] Initial NAS message received from UE 3
+[2023-07-05 19:58:35.869] [ngap] [debug] Initial Context Setup Request received
+[2023-07-05 19:58:36.108] [ngap] [info] PDU session resource is established for UE[3] count[1]
+
ueransim> sudo build/nr-ue -c config/free5gc-ue.yaml
+UERANSIM v3.1.0
+[2023-07-05 19:58:35.803] [nas] [debug] NAS layer started
+[2023-07-05 19:58:35.803] [rrc] [debug] RRC layer started
+[2023-07-05 19:58:35.804] [nas] [info] UE switches to state: MM-DEREGISTERED/PLMN-SEARCH
+[2023-07-05 19:58:35.804] [nas] [info] UE connected to gNB
+[2023-07-05 19:58:35.804] [nas] [info] UE switches to state: MM-DEREGISTERED/NORMAL-SERVICE
+[2023-07-05 19:58:35.804] [nas] [info] UE switches to state: MM-REGISTERED-INITIATED/NA
+[2023-07-05 19:58:35.805] [rrc] [debug] Sending RRC Setup Request
+[2023-07-05 19:58:35.806] [rrc] [info] RRC connection established
+[2023-07-05 19:58:35.806] [nas] [info] UE switches to state: CM-CONNECTED
+[2023-07-05 19:58:35.838] [nas] [debug] Received rand[61262F32A617D0BAD716603B1CBDA477] autn[44778026F4238000FC14B59D68855328]
+[2023-07-05 19:58:35.838] [nas] [debug] Calculated res[47759045F5ACEA59] ck[1C559301F29EF49572F5D150B3B99288] ik[D223317F752F233CE4C7AA253644D882] ak[528433D1FBE6] mac_a[FC14B59D68855328]
+[2023-07-05 19:58:35.838] [nas] [debug] Used snn[5G:mnc093.mcc208.3gppnetwork.org] sqn[16F3B3F70FC5]
+[2023-07-05 19:58:35.838] [nas] [debug] Derived kSeaf[7FC8B7FB1B141B6579B9C0FAEB9CCF1312FE9F9634868E234756DE49FD67C5F1] kAusf[FA0402A892E6046D52F4DECACA40B2A75B698FCEAD5EB320139FC69B77BD4C46] kAmf[3D4AD68E153B9642ACBECC67AD399015F7CB578F9DF4C88A35EED99C72C9B95B]
+[2023-07-05 19:58:35.843] [nas] [debug] Derived kNasEnc[1F829EB2BA238DD0226C3484E6A79D1F] kNasInt[251C0412B1BAD88A9DD0008F32D6F216]
+[2023-07-05 19:58:35.843] [nas] [debug] Selected integrity[2] ciphering[0]
+[2023-07-05 19:58:35.869] [nas] [debug] T3512 started with int[3600]
+[2023-07-05 19:58:35.869] [nas] [info] UE switches to state: MM-REGISTERED/NORMAL-SERVICE
+[2023-07-05 19:58:35.869] [nas] [info] Initial Registration is successful
+[2023-07-05 19:58:35.869] [nas] [info] Initial PDU sessions are establishing [1#]
+[2023-07-05 19:58:35.869] [nas] [debug] Sending PDU session establishment request
+[2023-07-05 19:58:36.108] [nas] [info] PDU Session establishment is successful PSI[1]
+[2023-07-05 19:58:36.113] [app] [info] Connection setup for PDU session[1] is successful, TUN interface[uesimtun0, 10.60.0.1] is up.
+
ueransim> ip a
+1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
+ link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
+ inet 127.0.0.1/8 scope host lo
+ valid_lft forever preferred_lft forever
+ inet6 ::1/128 scope host
+ valid_lft forever preferred_lft forever
+2: uesimtun0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UNKNOWN group default qlen 500
+ link/none
+ inet 10.60.0.1/32 scope global uesimtun0
+ valid_lft forever preferred_lft forever
+ inet6 fe80::b5ef:5b4:e3f6:af64/64 scope link stable-privacy
+ valid_lft forever preferred_lft forever
+6: veth0@if5: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
+ link/ether 4a:0f:1e:80:9b:be brd ff:ff:ff:ff:ff:ff link-netnsid 0
+ inet 10.200.200.1/24 scope global veth0
+ valid_lft forever preferred_lft forever
+ inet6 fe80::480f:1eff:fe80:9bbe/64 scope link
+ valid_lft forever preferred_lft forever
+ueransim> ping -c2 -I uesimtun0 8.8.8.8
+PING 8.8.8.8 (8.8.8.8) from 10.60.0.1 uesimtun0: 56(84) bytes of data.
+64 bytes from 8.8.8.8: icmp_seq=1 ttl=127 time=19.5 ms
+64 bytes from 8.8.8.8: icmp_seq=2 ttl=127 time=33.2 ms
+
+--- 8.8.8.8 ping statistics ---
+2 packets transmitted, 2 received, 0% packet loss, time 1006ms
+rtt min/avg/max/mdev = 19.478/26.348/33.219/6.870 ms
+
ueransim> ping -c2 -I uesimtun0 google.com
+PING google.com (172.217.160.110) from 10.60.0.1 uesimtun0: 56(84) bytes of data.
+64 bytes from tsa03s06-in-f14.1e100.net (172.217.160.110): icmp_seq=1 ttl=127 time=17.3 ms
+64 bytes from tsa03s06-in-f14.1e100.net (172.217.160.110): icmp_seq=2 ttl=127 time=29.5 ms
+
+--- google.com ping statistics ---
+2 packets transmitted, 2 received, 0% packet loss, time 1005ms
+rtt min/avg/max/mdev = 17.295/23.385/29.476/6.090 ms
+
Same as before, you should create another namespace for UERANSIM, called it ueransim2:
+
root@free5gc:~# ip netns ls
+ueransim2 (id: 1)
+ueransim (id: 0)
+
ip link add veth2 type veth peer name br-veth2
+ip link set dev veth2 netns ueransim2
+ip link set br-veth2 master free5gc-br
+ip link set br-veth2 up
+ip netns exec ueransim2 ip a add 10.200.200.3/24 dev veth2
+ip netns exec ueransim2 ip link set lo up
+ip netns exec ueransim2 ip link set veth2 up
+ip netns exec ueransim2 ip route add default via 10.200.200.2
+
Copy UERANSIM/config/free5gc-gnb.yaml and UERANSIM/config/free5gc-ue.yaml to free5gc-gnb2.yaml and free5gc-ue2.yaml, modify:
+free5gc-gnb2.yaml
+127.0.0.1
to 10.200.200.3
127.0.0.1
to 10.200.200.3
...
+ngapIp: 10.200.200.3 # 127.0.0.1 # gNB's local IP address for N2 Interface (Usually same with local IP)
+gtpIp: 10.200.200.3 # 127.0.0.1 # gNB's local IP address for N3 Interface (Usually same with local IP)
+
+# List of AMF address information
+amfConfigs:
+ - address: 10.200.200.2 # 127.0.0.1
+ port: 38412
+...
+
supi
change to imsi-208930000000004
...
+# IMSI number of the UE. IMSI = [MCC|MNC|MSISDN] (In total 15 or 16 digits)
+supi: 'imsi-208930000000004'
+...
+
Note
+Should register ue to webconsole first.
+The result:
+
ueransim> ip a
+1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
+ link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
+ inet 127.0.0.1/8 scope host lo
+ valid_lft forever preferred_lft forever
+ inet6 ::1/128 scope host
+ valid_lft forever preferred_lft forever
+6: veth0@if5: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
+ link/ether 4a:0f:1e:80:9b:be brd ff:ff:ff:ff:ff:ff link-netnsid 0
+ inet 10.200.200.1/24 scope global veth0
+ valid_lft forever preferred_lft forever
+ inet6 fe80::480f:1eff:fe80:9bbe/64 scope link
+ valid_lft forever preferred_lft forever
+7: uesimtun0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UNKNOWN group default qlen 500
+ link/none
+ inet 10.60.0.1/32 scope global uesimtun0
+ valid_lft forever preferred_lft forever
+ inet6 fe80::f6d7:dd81:fe7f:496a/64 scope link stable-privacy
+ valid_lft forever preferred_lft forever
+ueransim> ping -c2 -I uesimtun0 google.com
+PING google.com (172.217.160.110) from 10.60.0.1 uesimtun0: 56(84) bytes of data.
+64 bytes from tsa03s06-in-f14.1e100.net (172.217.160.110): icmp_seq=1 ttl=127 time=17.2 ms
+64 bytes from tsa03s06-in-f14.1e100.net (172.217.160.110): icmp_seq=2 ttl=127 time=28.5 ms
+
+--- google.com ping statistics ---
+2 packets transmitted, 2 received, 0% packet loss, time 1003ms
+rtt min/avg/max/mdev = 17.200/22.863/28.527/5.663 ms
+
ueransim2> ip a
+1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
+ link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
+ inet 127.0.0.1/8 scope host lo
+ valid_lft forever preferred_lft forever
+ inet6 ::1/128 scope host
+ valid_lft forever preferred_lft forever
+5: uesimtun0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UNKNOWN group default qlen 500
+ link/none
+ inet 10.60.0.2/32 scope global uesimtun0
+ valid_lft forever preferred_lft forever
+ inet6 fe80::16a4:523a:a86:bf83/64 scope link stable-privacy
+ valid_lft forever preferred_lft forever
+12: veth2@if11: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
+ link/ether fa:12:bb:9c:fa:40 brd ff:ff:ff:ff:ff:ff link-netnsid 0
+ inet 10.200.200.3/24 scope global veth2
+ valid_lft forever preferred_lft forever
+ inet6 fe80::f812:bbff:fe9c:fa40/64 scope link
+ valid_lft forever preferred_lft forever
+ueransim2> ping -c2 -I uesimtun0 google.com
+PING google.com (172.217.160.110) from 10.60.0.2 uesimtun0: 56(84) bytes of data.
+64 bytes from tsa03s06-in-f14.1e100.net (172.217.160.110): icmp_seq=1 ttl=127 time=18.9 ms
+64 bytes from tsa03s06-in-f14.1e100.net (172.217.160.110): icmp_seq=2 ttl=127 time=15.8 ms
+
+--- google.com ping statistics ---
+2 packets transmitted, 2 received, 0% packet loss, time 1002ms
+rtt min/avg/max/mdev = 15.786/17.353/18.921/1.567 ms
+
Hi, my name is Jimmy Chang. The current research topic is 5G LAN with a focus on the 5G Data Plane. Any questions or errors in the article are welcome for correction. Please feel free to send an email to provide feedback.
+Note
+Author: Wilson
+Date: 2023/8/2
Regarding the theme this time, I will briefly introduce OAuth. OAuth 2.0 defines four types of authorization flows. I choose the Client Credentials Flow to explain because the authentication mechanism in NRF is closely related to the Client Credentials Flow.
+Next, I will explain how to apply the concept of the Client Credentials Flow to NRF and introduce Nnrf_AccessToken Service
, because Nnrf_AccessToken Service
is closely related to the Client Credentials Flow.
Finally, I will make a simple experiment of the authentication mechanism in NRF and share the environment settings and methods of operation.
+Before explaining the authentication mechanism in NRF, I will introduce OAuth. Regarding the OAuth flow, we can log in to the account through the platform before accessing an application. After logging in, we agree that an application can limitedly obtain the information of the user on the platform. The application can be LinkedIn, YouTube, etc. The platform can be Google, Facebook, etc.
+The full English name of OAuth is Open standard Authorization. OAuth is an open standard, and it's used to deal with authorization-related behaviors. OAuth 2.0 defines four types of authorization flows. The four types of the authorization flows are:
+This article explains the entire authorization of the Client Credentials Flow only, because the authentication mechanism in NRF adopts the Client Credentials.
+++If you're interested in how authorization mechanism works, please refer to this article for more details.
+
+
Figure 1. Client Credentials Flow
Referring to the Figure 1, the Client Credentials Flow is mainly composed of:
+The entire authorization of the Client Credentials Flow can be devided into 3 steps:
+In addition, the Client and the Authorization Server have their own Scope list. The Scope list records a series of the actions. The Client or the Authorization Server is permitted to do the actions for obtaining the user’s name, deleting posts, etc.
+Below I will explain how to apply the Client Credentials Flow to NRF after talking about the Client Credentials Flow.
+The Figure 2 and the Figure 3 originate from the Figure 13.4.1.1-1 and the Figure 13.4.1.1-2 of the TS 33.501.
+
+
Figure 2. NF Service Consumer Obtaining Access Token before NF Service Access
The entire flow in Figure 2 is the same as Step 1 and Step 2 in the Figure 1. The role of the Client is played by the NF Service Consumer, and the role of the Authorization Server is played by the NRF.
+First, the NF Service Consumer registers with NRF. Then the NF Service Consumer sends the Nnrf_AccessToken_Get Request
to NRF. The Nnrf_AccessToken_Get Request
includes:
The NF Type can be AMF, SMF, etc. , and the NF Service Name can be namf-comm
, nsmf-pdusession
, etc.
NRF verifies the information provided by the NF Service Consumer after it receives the Nnrf_AccessToken_Get Request
. NRF generates an Access Token and uses the NRF private key to sign on the Access Token after the verification is successful.
Finally, NRF returns the Nnrf_AccessToken_Get Response
to the NF Service Consumer. The NF Service Consumer stores the Access Token within the validity period after it gets the Access Token. The services provided by the NF Service Producer are in the Expected NF Service Name. The NF Service Consumer doesn’t need to verify again when it wants to use the services provided by the NF Service Producer.
+
Figure 3. NF Service Consumer Requesting Service Access with an Access Token
The entire flow in Figure 3 is the same as Step 3 in the Figure 1. The role of the Client is played by the NF Service Consumer, and the role of the Resource Server is played by the NF Service Producer.
+First, the NF Service Consumer sends the NF Service Request
to the NF Service Producer with the Access Token. Simply put, the NF Service Consumer wants to consume the service provided by the NF Service Producer.
The NF Service Producer uses the NRF public key to verify the signed Access Token after it receives the NF Service Request
. If the verification is successful, the NF Service Producer will send the NF Service Response
to the NF Service Consumer.
Now I will talk about the Nnrf_AccessToken Service
after explaining how to apply the Client Credentials Flow to NRF.
+
Figure 4. Access Token Request
The Figure 4 originates from the Figure 5.4.2.2.1-1 of the TS 29.510.
+First, the NF Service Consumer sends the POST /oauth2/token
to NRF, and the data is stored in the AccessTokenReq
. The attribute name, the data type, and the formulation rule of the AccessTokenReq
are shown in the Table 1. The Table 1 originates from the Table 6.3.5.2.2-1 of the TS 29.510.
+
Table 1. Definition of Type AccessTokenReq
Definition of type AccessTokenReq
:
grant_type
: The value must be set to the client_credentials, and it is checked in the Snippet 1.nfInstanceId
: The value stores the ID of the NF Service Consumer.targetNfInstanceId
: The value stores the ID of the NF Service Producer.nfType
: The value stores the network function name of the NF Service Consumer. The network function name can be the AMF, SMF, etc. targetNfType
: The value stores the network function name of the NF Service Producer.scope
: It stores the services. The services can be the namf-comm
, nsmf-pdusession
, etc. When the NF Service Consumer requests the services. The services will be provided by the NF Service Producer.requesterPlmn
: It is mainly used in the roaming.targetPlmn
: It is mainly used in the roaming.if reqGrantType != "client_credentials" {
+ return &models.AccessTokenErr{
+ Error: "unsupported_grant_type",
+ }
+}
+
NRF sends AccessTokenRsp
to the NF Service Consumer in the Step 2a of the Figure 4. The attribute name, the data type, and the formulation rule of the AccessTokenRsp
are shown in the Table 2. The Table 2 originates from the Table 6.3.5.2.3-1 of the TS 29.510.
+
Table 2. Definition of Type AccessTokenRsp
The AccessTokenRsp
contains four attribute names. The four attribute names are:
access_token
: It stores all the attribute names and values of the AccessTokenClaims in the Table 3. The Table 3 originates form the Table 6.3.5.2.4-1 of the TS 29.510. token_type
: It must be set to the Bearer and can be seen in the Snippet 2.expires_in
: It stores information related to the expiration date.scope
: The NF Service Consumer and the NF Service Producer have their own scope list. The scope in the AccessTokenRsp
has a series of these services, and the NF Service Producer is permitted to consume these services.
+
Table 3. Definition of Type AccessTokenClaims
Definition of Type AccessTokenClaims
:
iss
: It is called issuer, and the content usually stores the ID of NRF.sub
: It is called subject, and the content stores the ID of the NF Service Consumer.aud
: It is called audience, and the content stores the ID of the NF Service Producer.scope
: The scope in the AccessTokenClaims
has a series of these services, and the NF Service Consumer is authorized by the NF Service Producer and permitted to consume these services.exp
: It stores information related to the validity period.func AccessTokenProcedure(request models.AccessTokenReq) (
+ *models.AccessTokenRsp, *models.AccessTokenErr,
+) {
+ logger.AccTokenLog.Infoln("In AccessTokenProcedure")
+
+ var expiration int32 = 1000
+ scope := request.Scope
+ tokenType := "Bearer"
+ now := int32(time.Now().Unix())
+
+ errResponse := AccessTokenScopeCheck(request)
+ if errResponse != nil {
+ return nil, errResponse
+ }
+
+ // Create AccessToken
+ nrfCtx := nrf_context.GetSelf()
+ accessTokenClaims := models.AccessTokenClaims{
+ Iss: nrfCtx.Nrf_NfInstanceID, // NF instance id of the NRF
+ Sub: request.NfInstanceId, // nfInstanceId of service consumer
+ Aud: request.TargetNfInstanceId, // nfInstanceId of service producer
+ Scope: request.Scope, // TODO: the name of the NF services for which the
+ Exp: now + expiration, // access_token is authorized for use
+ StandardClaims: jwt.StandardClaims{},
+ }
+ accessTokenClaims.IssuedAt = int64(now)
+
+ // Use NRF private key to sign AccessToken
+ token := jwt.NewWithClaims(jwt.GetSigningMethod("RS512"), accessTokenClaims)
+ accessToken, err := token.SignedString(nrfCtx.NrfPrivKey)
+ if err != nil {
+ logger.AccTokenLog.Warnln("Signed string error: ", err)
+ return nil, &models.AccessTokenErr{
+ Error: "invalid_request",
+ }
+ }
+
+ response := &models.AccessTokenRsp{
+ AccessToken: accessToken,
+ TokenType: tokenType,
+ ExpiresIn: expiration,
+ Scope: scope,
+ }
+ return response, nil
+}
+
The Snippet 2 is the AccessTokenProcedure()
function. The function is executed in NRF.
The function mainly processes:
+AccessTokenReq
sent by the NF Service Customer.AccessTokenScopeCheck()
function. The AccessTokenScopeCheck()
function checks whether the content of the attribute name in the AccessTokenReq
complies with the requirements of the TS 29.510. If not, the AccessTokenProcedure()
function immediately returns the AccessTokenErr
to the NF Service Customer.AccessTokenRsp
. The AccessTokenRsp
is sent back to the NF Service Customer. The Iss
in the AccessToken obtains its own ID in NRF. The Sub
and Aud
are obtained from the NfInstancedId
and the TargetNfInstanceId
in the AccessTokenReq
respectively. The Scope
is obtained from the scope
in the AccessTokenReq
. The expiration is set to the 1000 in the Snippet 2. Therefore, the value of the exp
is the current time + 1000.AccessTokenErr
to the NF Service Customer.AccessTokenRsp
. The value of the TokenType
is set to the Bearer by the function. The function sets the ExprieIn
and the Scope
in the Snippet 2.Finally, I make a simple experiment about the Access Token and share the environment setting and method of operation with you.
+The Table 4 is my environment setting. I provide the Table 4 for you. You can refer it.
+
+
Table 4. Environment
Remove the part of the tls
and add the content of the cert
, rootcert
and oauth
under sbi
in the nrfcfg.yaml
before implementing about the Access Token.
info:
+ version: 1.0.2
+ description: NRF initial local configuration
+
+configuration:
+ MongoDBName: free5gc # database name in MongoDB
+ MongoDBUrl: mongodb://127.0.0.1:27017 # a valid URL of the mongodb
+ sbi: # Service-based interface information
+ scheme: http # the protocol for sbi (http or https)
+ registerIPv4: 127.0.0.10 # IP used to serve NFs or register to another NRF
+ bindingIPv4: 127.0.0.10 # IP used to bind the service
+ port: 8000 # port used to bind the service
+ cert:
+ pem: cert/nrf.pem
+ key: cert/nrf.key
+ rootcert:
+ pem: cert/nrf.pem
+ key: cert/nrf.key
+ oauth: true
+ DefaultPlmnId:
+ mcc: 208 # Mobile Country Code (3 digits string, digit: 0~9)
+ mnc: 93 # Mobile Network Code (2 or 3 digits string, digit: 0~9)
+ serviceNameList: # the SBI services provided by this NRF, refer to TS 29.510
+ - nnrf-nfm # Nnrf_NFManagement service
+ - nnrf-disc # Nnrf_NFDiscovery service
+
+logger: # log output setting
+ enable: true # true or false
+ level: info # how detailed to output, value: trace, debug, info, warn, error, fatal, panic
+ reportCaller: false # enable the caller report or not, value: true or false
+
Find the http://127.0.0.10:8000/nnrf-nfm/v1/nf-instances/8f7891b4-b127-4f59-9ec2-b5e6aade5531
in the NRF log, and you will get the 8f7891b4-b127-4f59-9ec2-b5e6aade5531
. The 8f7891b4-b127-4f59-9ec2-b5e6aade5531
is the nfInstanceID
.
2023-08-02T20:07:43.300826205Z [INFO][NRF][NFM] Handle NFRegisterRequest
+2023-08-02T20:07:43.308259291Z [INFO][NRF][NFM] urilist create
+2023-08-02T20:07:43.311674255Z [INFO][NRF][NFM] Create NF Profile
+2023-08-02T20:07:43.318192771Z [INFO][NRF][NFM] Location header: http://127.0.0.10:8000/nnrf-nfm/v1/nf-instances/8f7891b4-b127-4f59-9ec2-b5e6aade5531
+2023-08-02T20:07:43.325073275Z [INFO][NRF][GIN] | 201 | 127.0.0.1 | PUT | /nnrf-nfm/v1/nf-instances/8f7891b4-b127-4f59-9ec2-b5e6aade5531 |
+
Execute $curl -X GET {apiRoot}/nnrf-nfm/v1/nf-instances/{nfInstanceID}
, and you will obtain the detail information about the nfInstanceID
. You can see the nfType
of the nfInstanceID
is NSSF, and the information about the nfInstanceID
is used when you implement the Access Token.
ubuntu@free5GC:~/free5gc/NFs/nrf$ curl -X GET http://127.0.0.10:8000/nnrf-nfm/v1/nf-instances/8f7891b4-b127-4f59-9ec2-b5e6aade5531
+{"ipv4Addresses":["127.0.0.31"],"nfInstanceId":"8f7891b4-b127-4f59-9ec2-b5e6aade5531","nfServices":[{"apiPrefix":"http://127.0.0.31:8000","ipEndPoints":[{"ipv4Address":"127.0.0.31","port":8000,"transport":"TCP"}],"nfServiceStatus":"REGISTERED","scheme":"http","serviceInstanceId":"0","serviceName":"nnssf-nsselection","versions":[{"apiFullVersion":"1.0.2","apiVersionInUri":"v1"}]},{"apiPrefix":"http://127.0.0.31:8000","ipEndPoints":[{"ipv4Address":"127.0.0.31","port":8000,"transport":"TCP"}],"nfServiceStatus":"REGISTERED","scheme":"http","serviceInstanceId":"1","serviceName":"nnssf-nssaiavailability","versions":[{"apiFullVersion":"1.0.2","apiVersionInUri":"v1"}]}],"nfStatus":"REGISTERED","nfType":"NSSF","plmnList":[{"mcc":"208","mnc":"93"}]}
+
Then you execute this command, see below.
+
$curl -X POST -H "Content-Type: application/json" -d '{"nfInstanceId": {nfInstanceID}, "grant_type": "client_credentials", "nfType": {nfType}, "targetNfType": "UDR", "scope": "nudr-dr"}' {apiRoot}/oauth2/token
+
AccessTokenRsp
.
+ubuntu@free5GC:~/free5gc/NFs/nrf$ curl -X POST -H "Content-Type: application/json" -d '{"nfInstanceId": "8f7891b4-b127-4f59-9ec2-b5e6aade5531", "grant_type": "client_credentials", "nfType": "NSSF", "targetNfType": "UDR", "scope": "nudr-dr"}' http://127.0.0.10:8000/oauth2/token
+"eyJhY2Nlc3NfdG9rZW4iOiJleUpoYkdjaU9pSlNVelV4TWlJc0luUjVjQ0k2SWtwWFZDSjkuZXlKcGMzTWlPaUlpTENKemRXSWlPaUk0WmpjNE9URmlOQzFpTVRJM0xUUm1OVGt0T1dWak1pMWlOV1UyWVdGa1pUVTFNekVpTENKaGRXUWlPaUlpTENKelkyOXdaU0k2SW01MVpISXRaSElpTENKbGVIQWlPakUyT1RFd01EZzBPRGdzSW1saGRDSTZNVFk1TVRBd056UTRPSDAuY3VHSkkwTndfV280S2lQbS1fZEZVdnVTQWM1WVEwMmRKYk5PTUhmMV9IOHdIZ2JKWFhUam9xU1Y2OTNYSmFKemkweGIxdC1DMW14TWhVZkZjbXpNMC1Nd2oxTXZYaWhyTTktdDFRUFItSFcxQlBlN0tHMUxBV3d5MEJfcXpIalltRlR6eGhONVlyNkpURDhBbkMxaFJFeEh4WHBjV1NqbV9vZnV0NVhfUFRFRkZtaHZrbmtVbU8waWFrTmdRWElRVTc1NnlvZ29ZTlFDRnJvSmRWamJMdnpFdkJLYTVFN0hQeXc3RkRDRHpTZU5WT2t2WTlobU11eldYZ3dOVmRIT3c1c2lNbmppbTlmTVZ0RTFxS1hjWDlScXlUdXlsWjM2ZlJ1QjdVZ2hkLU15Q19xd2VJRE41ZFdYOWZqdnA3VUNZZ01mVHhSLUI2M3d5OWFjQ183eThRIiwidG9rZW5fdHlwZSI6IkJlYXJlciIsImV4cGlyZXNfaW4iOjEwMDAsInNjb3BlIjoibnVkci1kciJ9"
+
NSSF sends the AccessTokenReq
to NRF after you execute the above command. In the AccessTokenReq
, the nfInstanceId
is set to 8f7891b4-b127-4f59-9ec2-b5e6aade5531
. The grant_type
is set to the client_credentials. The nfType
is set to NSSF. The targetNfType
is set to UDR. The scope
is set to nudr-dr
. The information is shown in the NRF log. The value of the targetNfInstanceId
, requesterPlmn
, and targetPlmn
is empty because they are not set.
2023-08-02T20:18:08.127557565Z [INFO][NRF][Token] In AccessTokenProcedure
+2023-08-02T20:18:08.127586736Z [INFO][NRF][Token] Access Token Request
+2023-08-02T20:18:08.127611885Z [INFO][NRF][Token] Grant Type: client_credentials
+2023-08-02T20:18:08.127637480Z [INFO][NRF][Token] NF Instance ID: 8f7891b4-b127-4f59-9ec2-b5e6aade5531
+2023-08-02T20:18:08.127664415Z [INFO][NRF][Token] Target NF Instance ID:
+2023-08-02T20:18:08.127689792Z [INFO][NRF][Token] NF Type: NSSF
+2023-08-02T20:18:08.127712916Z [INFO][NRF][Token] Target NF Type: UDR
+2023-08-02T20:18:08.127734827Z [INFO][NRF][Token] Scope: nudr-dr
+2023-08-02T20:18:08.127758317Z [INFO][NRF][Token] Requester PLMN: <nil>
+2023-08-02T20:18:08.127781052Z [INFO][NRF][Token] Target PLMN: <nil>
+
Next, you can see the Access Token in the NRF log. The value of the Sub
is 8f7891b4-b127-4f59-9ec2-b5e6aade5531
. The Sub
represents NSSF, and NSSF belongs to the NF Service Customer. The value of the Scope
is the nudr-dr
. The value of the Exp
is 1691008488.
2023-08-02T20:18:08.134096785Z [INFO][NRF][Token] Access Token Claims
+2023-08-02T20:18:08.138100978Z [INFO][NRF][Token] Iss:
+2023-08-02T20:18:08.138185972Z [INFO][NRF][Token] Sub: 8f7891b4-b127-4f59-9ec2-b5e6aade5531
+2023-08-02T20:18:08.138228925Z [INFO][NRF][Token] Aud:
+2023-08-02T20:18:08.138264519Z [INFO][NRF][Token] Scope: nudr-dr
+2023-08-02T20:18:08.138298628Z [INFO][NRF][Token] Exp: 1691008488
+
Next, you can see the AccessTokenRsp
. You can see that the Access Token has become the long symbols. The value of the Token Type
is set to the Bearer. The value of the ExpiresIn
is set to 1000. The value of the Scope
is set to nudr-dr
.
2023-08-02T20:18:08.149587382Z [INFO][NRF][Token] Access Token Response
+2023-08-02T20:18:08.150006665Z [INFO][NRF][Token] Access Token: eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiIiLCJzdWIiOiI4Zjc4OTFiNC1iMTI3LTRmNTktOWVjMi1iNWU2YWFkZTU1MzEiLCJhdWQiOiIiLCJzY29wZSI6Im51ZHItZHIiLCJleHAiOjE2OTEwMDg0ODgsImlhdCI6MTY5MTAwNzQ4OH0.cuGJI0Nw_Wo4KiPm-_dFUvuSAc5YQ02dJbNOMHf1_H8wHgbJXXTjoqSV693XJaJzi0xb1t-C1mxMhUfFcmzM0-Mwj1MvXihrM9-t1QPR-HW1BPe7KG1LAWwy0B_qzHjYmFTzxhN5Yr6JTD8AnC1hRExHxXpcWSjm_ofut5X_PTEFFmhvknkUmO0iakNgQXIQU756yogoYNQCFroJdVjbLvzEvBKa5E7HPyw7FDCDzSeNVOkvY9hmMuzWXgwNVdHOw5siMnjim9fMVtE1qKXcX9RqyTuylZ36fRuB7Ughd-MyC_qweIDN5dWX9fjvp7UCYgMfTxR-B63wy9acC_7y8Q
+2023-08-02T20:18:08.150094277Z [INFO][NRF][Token] Token Type: Bearer
+2023-08-02T20:18:08.150133189Z [INFO][NRF][Token] Expires In: 1000
+2023-08-02T20:18:08.150167371Z [INFO][NRF][Token] Scope: nudr-dr
+
Finally, you can see 200. 200 means that AUSF sends the AccessTokenReq
to NRF. NRF successfully sends to AUSF after verification.
2023-08-02T20:18:08.150302345Z [INFO][NRF][GIN] | 200 | 127.0.0.1 | POST | /oauth2/token |
+
Hi, my name is Wilson. I am a master’s student. My main area of research is network slicing. In the future, I will introduce more information about 5G. Hope you enjoy it.
+ + + + + + +This article is intended for individuals who possess an interest in free5gc/webconsole and hold concerns regarding security matters. It aims to provide a concise introduction to the webconsole, followed by an exposition of a significant security concern along with our corresponding solution. Within webconsole v1.2.0, aligning with the most recent iteration of free5gc v3.3.0, certain vulnerabilities have been identified that could potentially lead to the exposure of subscriber data. It is my responsibility to address and rectify these vulnerabilities, enhancing the webconsole's resilience against cyber attacks.
+The Webconsole serves as a web-based tool designed to manage User Equipment (UE) subscription data. It plays a crucial role in aiding the free5GC Core Network manager by facilitating the configuration of UEs and providing the ability to monitor the status of activated UEs.
+Prior to building webconsole, install nodejs and yarn package first:
+sudo apt remove cmdtest
+sudo apt remove yarn
+curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
+echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
+sudo apt-get update
+sudo apt-get install -y nodejs yarn
+
To run free5GC webconsole server. The following steps are to be considered.
+git clone https://github.com/free5gc/webconsole.git
+cd frontend
+yarn install
+yarn build
+rm -rf ../public
+cp -R build ../public
+cd ..
+go run server.go
+
Default account and password is admin/free5gc
Creation/deletion/editing the subscriber's data:
+ +A Subscriber data contains these informations:
+- PLMN ID
+- SUPI (UE ID)
+- AKA parameters
+- S-NSSAI Configurations
+ - Sst/Sd
+ - DNN
+ - Name
+ - AMBR
+ - Flow Rules
+ - IP Filter
+ - Precedence
+ - 5QI
+ - GBR
+ - MBR
The Webconsole also allows for the creation, deletion, and editing of tenants. A tenant functions as an access control group, delineating specific permissions and boundaries. In this setup, if you do not possess admin privileges, you are unable to access subscriber data generated by other tenants, ensuring data privacy and security.
+ +Furthermore, the capability to incorporate users within a tenant is available. To illustrate, by selecting the brian1 tenant and clicking on the New User option, it becomes possible to introduce a new user. As an example, a user with the email address aaabbb@gmail.com can be added through this process.
+ +The data within MongoDB can be accessed and reviewed using the MongoDBCompass tool.
+ +The vulnerability was discovered by INCIBE, and they promptly notified the free5GC team via email.
+
The corresponding issue related to this vulnerability is also documented in the free5gc repository. Despite the typical deployment of the webconsole within LAN or Docker environments, it's essential to exercise caution regarding users who operate this service on a public IP or within an insecure network environment.
+In a nutshell, an attacker can gain unauthorized access to the database by merely setting the token to the term 'admin'.
+
$ curl '<webconsole's IP>:5000/api/subscriber' -H 'User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:103.0) Gecko/20100101 Firefox/103.0' -H 'Accept: application/json' -H 'Accept-Language: en-US,en;q=0.5' -H 'Accept-Encoding: gzip, deflate' -H 'Referer: http://<webconsole's IP>:5000/' -H 'Connection: keep-alive' -H 'X-Requested-With: XMLHttpRequest' -H 'Token: admin' -H 'Pragma: no-cache' -H 'Cache-Control: no-cache'
+
Subsequently, they can directly retrieve subscriber information from the server's MongoDB.
+
[{"plmnID":"20893","ueId":"imsi-208930000000003"}]
+
Undoubtedly, this vulnerability is of significant concern since the intended safeguard, allowing access solely to admin, has been compromised, thereby enabling easy access for anyone.
+In webconsole/frontend/src/util/AuthHelper.js
+- In scenarios where the default username and password (admin/free5gc) are employed, the ApiHelper.login()
function remains untouched. This practice might expedite agile development, but it comes at the cost of compromising security.
In webconsole/frontend/WebUI/api_webui.go
+- In situations where a webconsole client configures the tokenStr
as 'admin', the backend process will omit the execution of ParseJWT()
.
The Webconsole relies on JSON Web Token (JWT) as its authentication mechanism, a specification outlined in RFC 7519.
+++JSON Web Token (JWT) is a compact, URL-safe means of representing claims to be transferred between two parties. The claims in a JWT are encoded as a JSON object that is used as the payload of a JSON Web Signature (JWS) structure or as the plaintext of a JSON Web Encryption (JWE) structure, enabling the claims to be digitally signed or integrity protected with a Message Authentication Code (MAC) and/or encrypted.
+
When a client employs a web browser to initiate a login via the HTTP(s) protocol, the Web server is expected to furnish the client with a JWT token in response. Subsequently, the client employs this JWT token to interact with resources by sending requests through the RESTful API (such as GET, POST, PUT, etc.).
+A JSON Web Token (JWT) consists of three distinct parts. In the context of the webconsole backend, the following fields are relevant:
+- Header: This section contains information about the type of token and the signing algorithm used. It often includes the "alg" (algorithm) and "typ" (type) fields.
+- Payload: The payload holds the actual claims or data that are being conveyed by the token. For the webconsole backend, specific fields within this section could include:
+ - Claim (JSON object)
+ - sub
: identifies the principal that is the subject of the JWT.
+ - iat
: identifies the time at which the JWT was issued.
+ - exp
: identifies the expiration time onor after which the JWT MUST NOT be accepted for processing.
+ - email
+ - tenantId
+ - ...(you can design the attribute yourself)
+- Signature: This component is created by combining the encoded header and payload with a secret key (or a public/private key pair) to ensure the token's integrity and authenticity. The signature allows the recipient to verify that the token hasn't been tampered with.
++The JWT Claims Set represents a JSON object whose members are the claims conveyed by the JWT. The Claim Names within a JWT Claims Set MUST be unique; JWT parsers MUST either reject JWTs with duplicate Claim Names or use a JSON parser that returns only the lexically last duplicate member name
+
The image depicted below illustrates the process of using jwt.io to both encode and decode JWT tokens. These tokens are segmented into distinct sections denoted by the red, purple and blue divisions, separated by periods dots.
+ +Given that the Payload can be decoded using the algorithm specified in the Header, it's essential to refrain from including sensitive details like passwords or credit card numbers within it. Instead, the Payload typically holds claims and application-specific metadata.
+To maintain the security of the process, the server retains a confidential key used to validate the signature. In situations where a client endeavors to access a resource using a JWT token that possesses an incorrect Verify Signature, an error response will be generated. This stringent signature verification mechanism ensures that the authenticity and integrity of both the token and its enclosed data are upheld.
+In webconsole/frontend/WebUI/api_webui.go
, we can find the implementation of JWT.
+- JWT()
is for encoding:
+```go=394
+func JWT(email, userId, tenantId string) string {
+ token := jwt.New(jwt.SigningMethodHS256)
claims := token.Claims.(jwt.MapClaims)
+claims["sub"] = userId
+claims["iat"] = time.Now()
+claims["exp"] = time.Now().Add(time.Hour * 24).Unix()
+claims["email"] = email
+claims["tenantId"] = tenantId
+
+if jwtKey == "" {
+ return ""
+}
+
+tokenString, err := token.SignedString([]byte(jwtKey))
+if err != nil {
+ logger.ProcLog.Errorf("JWT err: %+v", err)
+ return ""
+}
+
+return tokenString
+
+}
+- `ParseJWT()` is for decoding:
go=491
+func ParseJWT(tokenStr string) (jwt.MapClaims, error) {
+ token, err := jwt.Parse(tokenStr, func(token jwt.Token) (interface{}, error) {
+ return []byte(jwtKey), nil
+ })
+ if err != nil {
+ return nil, errors.Wrap(err, "ParseJWT error")
+ }
+
+ claims, _ := token.Claims.(jwt.MapClaims)
+
+ return claims, nil
+}
+- The function `CheckAuth()` serves the purpose of determining whether a user possesses the authorization to access a particular resource.
go=505
+func CheckAuth(c gin.Context) bool {
+ tokenStr := c.GetHeader("Token")
+ if tokenStr == "admin" {
+ return true
+ } else {
+ return false
+ }
+}
+``
+
+:::
+- The secret key utilized for signature verification is obtained through
os.Getenv("SIGNINGKEY"). However, there's a possibility that
SIGNINGKEYmight not be exported as an environment variable, leading to a potential return of an empty value. Under such circumstances, an implication arises: an admin in Webconsole A could potentially gain access to subscriber data within Webconsole B.
+- Within the
CheckAuth()` function, if the client sets the JWT token to 'admin', the function will evaluate to true, effectively allowing the check to be passed.
+:::
+
Initially, I have revised the design of the CheckAuth()
function to ensure the mandatory execution of ParseJWT()
.
+go=
+func CheckAuth(c *gin.Context) bool {
+ tokenStr := c.GetHeader("Token")
+ claims, err := ParseJWT(tokenStr)
+
+ if err == nil && claims["email"] == "admin" {
+ return true
+ } else {
+ return false
+ }
+}
+Furthermore, I've implemented a second change where, considering that the webconsole v1.2.0 doesn't inherently establish a tenant named 'admin' or a user named 'admin', I propose a more effective approach. During the initialization of the webconsole backend, it is recommended to generate an 'admin' tenant and user. This means that executing go run server.go
within the webconsole/
directory should consistently generate an admin user, thereby fulfilling the initial login requirement.
Certainly, in the backend/WebUI/api_webui.go
file, I propose the addition of a function named SetAdmin()
. To streamline the process and maintain consistency with the rest of the free5GC project, it is recommended to leverage the mongoapi
module established within the free5gc/util
repository. Given the project's heavy reliance on MongoDB, employing mongoapi
over frequent calls to mongo-driver
is essential to ensure efficiency and coherence.
```go=
+func SetAdmin() {
+ err := mongoapi.RestfulAPIDeleteOne("tenantData", bson.M{"tenantName": "admin"})
+ if err != nil {
+ logger.InitLog.Errorf("RestfulAPIDeleteOne err: %+v", err)
+ }
+ err = mongoapi.RestfulAPIDeleteOne("userData", bson.M{"email": "admin"})
+ if err != nil {
+ logger.InitLog.Errorf("RestfulAPIDeleteOne err: %+v", err)
+ }
// Create Admin tenant
+logger.InitLog.Infoln("Create tenant: admin")
+
+adminTenantData := bson.M{
+ "tenantId": uuid.Must(uuid.NewRandom()).String(),
+ "tenantName": "admin",
+}
+
+_, err = mongoapi.RestfulAPIPutOne("tenantData", bson.M{"tenantName": "admin"}, adminTenantData)
+if err != nil {
+ logger.InitLog.Errorf("RestfulAPIPutOne err: %+v", err)
+}
+
+AmdinTenant, err := mongoapi.RestfulAPIGetOne("tenantData", bson.M{"tenantName": "admin"})
+if err != nil {
+ logger.InitLog.Errorf("RestfulAPIGetOne err: %+v", err)
+}
+
+// Create Admin user
+logger.InitLog.Infoln("Create user: admin")
+
+hash, err := bcrypt.GenerateFromPassword([]byte("free5gc"), 12)
+if err != nil {
+ logger.InitLog.Errorf("GenerateFromPassword err: %+v", err)
+}
+
+adminUserData := bson.M{
+ "userId": uuid.Must(uuid.NewRandom()).String(),
+ "tenantId": AmdinTenant["tenantId"],
+ "email": "admin",
+ "encryptedPassword": string(hash),
+}
+
+_, err = mongoapi.RestfulAPIPutOne("userData", bson.M{"email": "admin"}, adminUserData)
+if err != nil {
+ logger.InitLog.Errorf("RestfulAPIPutOne err: %+v", err)
+}
+
+}
+```
+
Certainly, within the backend/WebUI/api_webui.go
file, I recommend introducing a string variable named jwtKey
to serve as the private key for JWT Verify Signature. Although the length of jwtKey is specified as 256 bytes, it's worth noting that the distinction between 256 bytes and 256 bits is inconsequential in this context. The jwt module will adeptly transform the key to a 256-bit form. For further insights, you can refer to issue 28.
+
+go=
+var jwtKey = "" // for generating JWT
+
+/* ... */
+
+func InitJwtKey() error {
+ randomBytes := make([]byte, 256)
+ _, err := rand.Read(randomBytes)
+ if err != nil {
+ return errors.Wrap(err, "Init JWT key error")
+ } else {
+ jwtKey = string(randomBytes)
+ }
+ return nil
+}
In backend/webui_service/webui_init.go
:
```go=
+func (a WebuiApp) Start(tlsKeyLogPath string) {
+ / ... /
+ WebUI.SetAdmin()
+ if err := WebUI.InitJwtKey(); err != nil {
+ logger.InitLog.Errorln(err)
+ return
+ }
+ / ... */
+}
+
### Frontend Login
+
+Certainly, in the `frontend/src/util/AuthHelper.js` file, it is advised to remove the section of code that could be considered a "cheating snippet." To ensure a robust authentication process, all users should be required to successfully pass through the `ApiHelper.login` function and receive a response code of 200. This approach ensures a consistent and legitimate authentication mechanism.
+
+```javascript
+static async login(username, password) {
+ let response = await ApiHelper.login({username: username, password: password});
+
+ if (response !== undefined && response.status === 200) {
+ var user = null
+ if (username == "admin") {
+ user = new User(username, "System Administrator", response.data.access_token);
+ } else {
+ user = new User(username, "User", response.data.access_token);
+ }
+ LocalStorageHelper.setUserInfo(user);
+ store.dispatch(authActions.setUser(user));
+ return true;
+ } else {
+ return false;
+ }
+ }
+
In this endeavor, we've successfully addressed the CSRF vulnerability issue, as highlighted in issue #387 and acknowledged by INCIBE. Furthermore, I've introduced the concept of JWT tokens in this article, detailing their implementation and the corresponding adjustments made within the webconsole. You can locate the detailed implementation in the merged PR #44 of the webconsole repository. I'd like to extend my gratitude to the contributors kishiguro and LaumiH for their significant role in refactoring the webconsole. As a result of their efforts, the webconsole UI has been notably enhanced. Our upcoming focus involves the integration of the charging function, which we are actively pursuing.
+free5gc/webconsole
+merged PR #44
+free5gc issue #387
+free5gc issue #28
+JSON Token
+RFC 7519
+MongoDBCompass
Hello everyone,
+I'm Brian Chen (陳煜盛), and I've been immersed in the realm of 5G Core Network technologies. Over the course of seven months, I've had the privilege of serving as an intern at Saviah. In this role, my responsibilities encompass a spectrum of tasks including maintenance, development, and rigorous testing of the free5GC project.
+Should any inquiries, questions, or bug reports regarding free5GC arise, I encourage you to reach out by creating an issue in the free5gc repository or by participating in discussions on the forum. I'm here to assist and collaborate with the community as we navigate the intricacies of this project.
+ + + + + + + +Note
+Author: Elisa Lee
+Date: 2023/8/16
In the initial section of the article, I will provide an introduction to Kubernetes. Moving on to the subsequent part, I will delve into the utilization of Kubernetes for facilitating the deployment of free5GC. Lastly, in the final segment of the article, I will elaborate on the effective utilization of Kubernetes for monitoring services.
+Do you know why Kubernetes is called k8s?
+It's due to a shorthand notation that uses the first letter "k," followed by the number "8," and ending with the last letter "s" to represent the full name.
+Kubernetes,stands as an open-source container orchestration platform that bestows organizations with the capacity to adeptly govern, deploy, and expand containerized applications. Initially conceived by Google and presently overseen by the Cloud Native Computing Foundation (CNCF), Kubernetes has ascended to become a pivotal technological underpinning within the realm of contemporary cloud-native computing.
For those who find themselves unacquainted with the intricacies of Kubernetes, let us embark on an exploration of its architectural framework.
+
+I understand that the image presented above might appear intricate at first glance. However, there's no need for concern. Allow me to guide you through each component step by step.
A "Pod" stands as the most diminutive executable entity within the Kubernetes ecosystem. It possesses the capability to encompass either an individual container or a collective assembly of containers. The subsequent enumeration outlines several salient distinctions that set it apart from the act of directly launching a standalone container.
+Notably, a Pod possesses its dedicated network interface, affording all enclosed containers the ability to intercommunicate seamlessly by interfacing with the "localhost." Furthermore, connectivity to other Pods is conveniently established through direct usage of their respective IP addresses within the Kubernetes environment.
+The Pod's inherent duplicability and capacity for effortless restarts, even from the point of its most recent execution, distinguish it. Additionally, the versatility of including a functioning container within its initial state further characterizes its nature.
+Collectively, these attributes contribute to the refinement and streamlined nature of networking within the Kubernetes ecosystem. Such qualities not only facilitate smoother scalability but also offer enhanced capabilities for restarting services with utmost ease.
+Consider a "node" as a tangible computing entity akin to a physical machine. Analogous to our personal machines, these nodes possess the capacity to concurrently execute multiple tasks, akin to the pods referenced earlier. The orchestration of nodes is overseen by a pivotal component known as the Kubernetes control plane, which, in an automated fashion, allocates pods across the available nodes. Within each node, a minimum of two services operate in tandem.
+Kubelet: This crucial service undertakes the responsibility of facilitating seamless communication between the Kubernetes control plane and the individual node. It serves as the intermediary that relays instructions and status updates, ensuring synchronization and cooperation.
+Container Runtime: Operating in tandem with Kubelet, the container runtime undertakes pivotal functions. These encompass retrieving container images from registries, the unpacking of containers, and the actual execution of applications. A prime example of such a container runtime is Docker, renowned for its role in enabling containerization.
+In essence, this intricate interplay of nodes, services, and orchestration elements underscores the dynamism and efficiency inherent to the Kubernetes ecosystem. Through these interlocking mechanisms, the platform optimizes resource utilization, ensures effective communication, and enables the seamless execution of applications across a distributed infrastructure.
+We've now explored all the scalable components and the task runner. Undoubtedly, to orchestrate and oversee everything, a central command hub is necessary – this is referred to as the master node. Although direct intervention within these nodes isn't typically required to ensure the seamless operation of the entire system, it's still beneficial for you to grasp a basic understanding of its functionality.
+It determines which interface among all nodes can be externally accessed. Any subsequent commands you execute will be channeled through this service to the designated node or pod. Furthermore, essential cluster information can also be obtained from this service.
+Similar to an airport's control tower, its function is akin to orchestrating the deployment of pods on specific nodes based on the rules you've established and the data obtained from the API server. The effectiveness of these rules is pivotal, as they often determine the system's overall efficiency.
+When the need arises to enact concrete modifications on a pod, such as terminating or pausing its operation, a fundamental prerequisite is pinpointing the pod's process location and establishing the means to interact with it. This is precisely the role fulfilled by the controller manager. Additionally, this manager oversees vital components, including accounts, services, and more.
+For a simplified understanding, we can view this as essentially a comprehensive backup of the entire cluster.
+In Kubernetes, a "service" is an abstraction that enables communication between different sets of pods, usually to provide a stable network endpoint for accessing a specific group of pods. Pods in Kubernetes are ephemeral and can be created, terminated, or scaled dynamically, which makes their IP addresses and lifecycles unpredictable. Services provide a way to decouple the frontend of an application from the backend pods, making it easier for other components or users to access the application without having to know the exact locations or IP addresses of the pods.
+A service can be defined in Kubernetes using a YAML or JSON configuration file, and it is associated with a set of pods based on a label selector. The service acts as a load balancer, distributing incoming network traffic among the pods that match the specified selector. This distribution ensures that even if pods are scaled up or down, the service remains available and reachable.
+There are different types of services in Kubernetes:
+ClusterIP: This is the default service type, and it exposes the service on a cluster-internal IP address. It is accessible only within the cluster.
+NodePort: This type exposes the service on each node's IP address at a static port. It allows external access to the service using the node's IP and the specified static port.
+LoadBalancer: This type automatically provisions a cloud provider load balancer to expose the service externally. It works in environments that support external load balancers.
+ExternalName: This type provides an alias for an external service by returning a CNAME record with the configured DNS name.
+Services are a fundamental concept in Kubernetes and play a crucial role in enabling communication and load balancing between pods and external clients. They provide a stable and abstracted network endpoint that allows applications to scale and be more resilient without disrupting access from users or other components.
+In the upcoming section, I will delve into the process of deploying free5GC on Kubernetes.
+Now I'm going to introduce how to implement free5GC on Kubernetes with helm
+sudo apt update -y
+sudo apt upgrade -y
+
"apt-transport-https" is a crucial package that equips your system with the essential tools and libraries required to seamlessly integrate the HTTPS protocol. This integration ensures secure and encrypted communication when connecting to package repositories while utilizing the Advanced Package Tool (APT) for effective package management.
+
sudo apt install -y curl wget apt-transport-https
+
"gtp5g" refers to a customized Linux kernel module specifically designed to handle packets by PFCP (Packet Forwarding Control Protocol) Information Elements (IEs) such as PDR (Packet Detection Rule) and FAR (Forwarding Action Rule). For comprehensive insights, you can delve into the 3GPP specifications TS 29.281 and TS 29.244.
+To employ the UPF (User Plane Function) component effectively, it's imperative to operate on either the 5.0.0-23-generic or 5.4.x version of the Linux kernel. This ensures optimal compatibility and seamless integration with the necessary functionalities.
+
sudo apt install gcc
+sudo apt install make
+git clone -b v0.8.1 https://github.com/free5gc/gtp5g.git
+cd gtp5g
+make
+sudo make install
+
"docker" is a platform that enables developers to build, package, and distribute applications as containers. Containers are lightweight, portable, and self-sufficient units that encapsulate everything an application needs to run, including the code, runtime, system tools, system libraries, and settings. Docker provides a consistent environment across different development and deployment stages, from local development to testing and production.
+
for pkg in docker.io docker-doc docker-compose podman-docker containerd runc; do sudo apt-get remove $pkg; done
+sudo apt-get update
+sudo apt-get install ca-certificates curl gnupg
+sudo install -m 0755 -d /etc/apt/keyrings
+curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
+sudo chmod a+r /etc/apt/keyrings/docker.gpg
+echo \
+ "deb [arch="$(dpkg --print-architecture)" signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
+ "$(. /etc/os-release && echo "$VERSION_CODENAME")" stable" | \
+ sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
+ sudo apt-get update
+ sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
+
"minikube" is an open-source tool that enables developers to set up and run a single-node Kubernetes cluster locally on their own computer. It's particularly useful for learning, development, and testing purposes. Minikube provides an easy way to experience Kubernetes without needing access to a full-scale cluster, making it a great tool for getting familiar with Kubernetes concepts and features.
+
wget https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64
+sudo cp minikube-linux-amd64 /usr/local/bin/minikube
+sudo chmod +x /usr/local/bin/minikube
+
"kubectl" is the command-line tool used to interact with and manage Kubernetes clusters. It is an essential component for working with Kubernetes, allowing users to perform various tasks and operations on Kubernetes clusters directly from the terminal.
+
curl -LO https://storage.googleapis.com/kubernetes-release/release/`curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt`/bin/linux/amd64/kubectl
+chmod +x kubectl
+sudo mv kubectl /usr/local/bin/
+
"helm" is a package manager for Kubernetes that simplifies the deployment and management of applications and services on a Kubernetes cluster. It allows you to define, install, and upgrade complex applications using pre-configured templates called "charts." These charts encapsulate all the necessary resources, configurations, and dependencies required to run an application.
+
curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3
+chmod 700 get_helm.sh
+./get_helm.sh
+helm list -A
+
"multus-cni" is a project that provides a Kubernetes network plugin, specifically a "Container Network Interface" (CNI) plugin, which enables the attachment of multiple network interfaces to pods in a Kubernetes cluster.
+
git clone https://github.com/k8snetworkplumbingwg/multus-cni.git
+
Now, I will proceed to introduce a selection of kubectl command that can be employed during the deployment of free5GC.
+"kubectl get pods" retrieves a list of running pods in the current namespace along with their names, statuses, and other relevant information.
+
kubectl get pods
+
kubectl describe pod [pod-name]
+
kubectl logs [pod-name]
+
-it
flag enables interactive terminal access.kubectl exec -it [pod-name] -- [command]
+
kubectl apply -f [yaml-file]
+
kubectl delete [resource-type] [resource-name]
+
kubectl expose deployment [deployment-name] --type=LoadBalancer --port=[port]
+
"kubectl get services" lists all services running in the current namespace along with their details, including ClusterIP, external IP (if applicable), and ports.
+
kubectl get services
+
kubectl get nodes
+
kubectl describe node [node-name]
+
kubectl get namespaces
+
kubectl create namespace [namespace-name]
+
kubectl port-forward [pod-name] [local-port]:[remote-port]
+
Use flannel as cni plugin to start minikue. Flannel is a popular "Container Network Interface" (CNI) plugin used for networking in Kubernetes and other container orchestration platforms. It provides a simple and lightweight network fabric designed to facilitate communication between containers and pods in a distributed environment, such as a Kubernetes cluster.
+
sudo usermod -aG docker $USER && newgrp docker
+minikube start --driver=docker --cpus=4 --memory=8g --disk-size=20g --cni=flannel
+## verify minikube installation
+minikube status
+
cd multus-cni
+cat ./deployments/multus-daemonset.yml | kubectl apply -f -
+
If you have only one interface on each Kubernetes node and its name is toto
. Then you have to set these parameters to toto
:
+global.n2network.masterIf
+global.n3network.masterIf
+global.n4network.masterIf
+global.n6network.masterIf
+global.n9network.masterIf
kubectl create ns free5gc
+git clone https://github.com/Orange-OpenSource/towards5gs-helm.git
+cd towards5gs-helm/charts/
+helm -n free5gc install free5gc-v1 ./free5gc/
+helm -n free5gc install ueransim-v1 ./ueransim/
+watch kubectl get pods -n free5gc
+
free5GC offers a user-friendly web tool called WebConsole, designed to facilitate the creation and management of User Equipment (UE) registrations. This tool serves as a valuable resource for multiple 5G network functions (NFs), streamlining the process of handling UE registrations and associated tasks.
+
kubectl port-forward --namespace free5gc svc/webui-service 5000:5000
+
admin
and password free5gc
.ssh -L localhost:5000:localhost:5000 ubuntu@[VM ip]
+
For monitoring Kubernetes, I utilized Prometheus and Grafana. The installation of Prometheus and Grafana services is facilitated through the Helm chart provided by the prometheus-community.
+
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
+helm repo update
+kubectl create namespace prometheus
+helm install prometheus prometheus-community/kube-prometheus-stack -n prometheus
+watch kubectl get pods -n prometheus
+
kubectl port-forward -n prometheus svc/prometheus-grafana 8080:80
+
ssh -L localhost:8080:localhost:8080 ubuntu@[VM ip]
+
https://free5gc.org/
+https://medium.com/rahasak/deploying-5g-core-network-with-free5gc-kubernets-and-helm-charts-29741cea3922
+https://github.com/Orange-OpenSource/towards5gs-helm
+https://github.com/k8snetworkplumbingwg/multus-cni
+Hello, I am Elisa Lee. My ongoing research revolves around VoNR (Voice over New Radio). I encourage any inquiries or identification of errors within the article, as they are welcomed for correction. Your feedback is invaluable, so please don't hesitate to reach out via email to share your insights.
+ + + + + + +Note
+Author: Ya-shih Tseng
+Date: 2023/7/12
This blog focuses on the role of the 5G system in 3GPP Release 16 TSN (Time-Sensitive Networking).
+Traditional Ethernet technology can only achieve "best-effort" communication and cannot meet the high reliability and low latency requirements of industrial manufacturing applications.
+Therefore, in the context of industrial automation, there is a need to upgrade the traditional "best-effort" Ethernet to provide "deterministic" services.
Time Sensitive Networking (TSN) brings determinism and real-time communication to standard Ethernet through mechanisms and protocols defined by the IEEE 802.1 standard, which is used by Audio Video Bridging (AVB) and TSN. It offers reliable message delivery, minimized jitter, and guaranteed delivery through central management, time scheduling, and other key features. The introduction of TSN technology holds great potential and benefits for real-time applications in industrial control, automation, and other fields.
+
+ ++
There are a lot of standards that TSN task group has completed or ongoing projects. Here are some base standards.
+Standard | +Title | +
---|---|
IEEE 1588 V2 | +Precision Clock Synchronization Protocol for Networked Measurement and Control Systems | +
IEEE 802.1Q-2022 | +Bridges and Bridged Networks | +
IEEE 802.1AB-2016 | +Station and Media Access Control Connectivity Discovery (specifies the Link Layer Discovery Protocol (LLDP)) | +
IEEE 802.1AS-2020 | +Timing and Synchronization for Time-Sensitive Applications | +
IEEE 802.1AX-2020 | +Link Aggregation | +
IEEE 802.1CB-2017 | +Frame Replication and Elimination for Reliability | +
IEEE 802.1CS-2020 | +Link-local Registration Protocol | +
With the increasing demands for wireless control in applications such as industrial automation, remote surgical operations, smart grid distribution automation, transportation safety, autonomous driving, and more, there is a growing need to meet the low-latency requirements of these applications while achieving management, scheduling, and traffic planning. Time synchronization becomes a critical aspect. The following will explain how the interaction between TSN and 5G systems enables time synchronization.
+To achieve time synchronization between TSN and 5G systems, TSN utilizes the time synchronization method defined in IEEE 802.1AS, which is the generalized Precision Time Protocol (gPTP). gPTP supports time synchronization for Time-aware end stations and Time-aware Bridges in Layer 2. In the 3GPP TS23.501 release 16 specification, the 5G system plays the role of a "Time-aware system" as defined in IEEE 802.1AS and is designated as a Logical bridge, connecting TSN system end stations.
+Note
+gPTP is an extended version of PTP (Precision Time Protocol) that primarily expands support for second-layer network devices.
+How can we synchronize the time of two end stations into the same time domain?
+First, the time synchronization architecture includes Master clocks and Slave clocks. The Master regularly sends sync messages to allow the Slave to obtain the Master's time. The Slave, in turn, periodically sends peer delay requests to exchange messages with the Master, obtaining the delay time between the two devices for time correction. Additionally, the resident time, which is the message propagation delay introduced by bridges, should also be taken into account. By considering all these factors, the time synchronization of both sides can be achieved within the TSN time domain.
+
+ ++
Check the link for more detail about how PTP works.
+By now, I believe you have gained an understanding of the time synchronization mechanism in TSN. Let's briefly explain how the 5G system supports TSN as a logical TSN bridge.
+The 3GPP has defined new functionalities such as NW-TT, DS-TT, and TSN-AF, as well as TSN control nodes like CUC and CNC. Please check TS 23.501 Release 16 for more details.
+
++System architecture of 5G support TSN
+
To archive the intergration, 5G system should support ingress port and egress port pair via an Ethernet Type PDU session between the corresponding UE and UPF. As mentioned above, gPTP supports layer 2 (Ethernet) only.
+In the 5G system, DS-TT (Device-side TSN translator) and NW-TT (Network-side TSN translator) serve as TSN translators. DS-TT is responsible for connecting TSN Slave endpoints with the UE, while NW-TT connects TSN Master endpoints with the UPF.
+When the sync message generated by the Master clock reaches the bridge, NW-TT captures its Ingress Timestamp and measures the delay between NW-TT and the Master clock. These timestamps are then embedded within the sync message and transmitted to the UE. Once the UE receives the sync message, DS-TT calculates the resident time by subtracting the Ingress Timestamp provided in the sync message, from the Egress Timestamp which represents the time of sync message reception. The resident time is added to the delay time mentioned in the sync message to determine the corrected time. Through the assistance of the TSN translators, the Slave endpoint receives the message and obtains information about time deviation and other relevant data for further adjustment.
+Note
+DS-TT and NW-TT enable the 5G system to function as a virtual bridge. The bridge is also called "Transparent clock" which is definded in IEEE 1588 and required in IEEE 802.1AS. You can say that Master and Slaver don't know the exist of the 5G TSN bridge, since it's logical transparent.
+"Transparent clocks are used to route timing messages within a network. Used when: Ethernet timing must pass through switches." - different type of clocks
+With TSN-AF, CNC can manage the 5G system functioning as a logical bridge and achieve the integration of the 5G TSN bridge with the TSN network in collaboration with NW-TT and DS-TT. Additionally, TSN-AF gathers information and capability lists of the 5G TSN Bridge and transmits them to CNC.
+To meet the requirements of application services and control TSN, there are two key functions utilized in the TSN system.
+CNC (Centralized Network Controller), as the central controller in the TSN system, receives the information from CUC (Centralized User Configuration) and performs scheduling and planning tasks. It calculates the optimal transmission schedule for the TSN traffic based on factors such as bandwidth requirements, latency constraints, and network conditions. Once the transmission schedule is computed and confirmed, CNC proceeds to deploy the necessary network resource configuration on the TSN switches. This ensures that the TSN network operates efficiently and effectively in delivering the required QoS (Quality of Service) for the application services.
Hi, This is Ya-shih Tseng. I am currently researching the implementation of 5G TSN (Time-Sensitive Networking) as part of my master's studies. In the future, I will introduce more information about TSN. Hope you enjoy it.
+ + + + + + +Note
+Author: 張哲睿
+Date: 2023/7/19
In this article, I will introduce UDM and its three services that will be used in the general UE registration procedure (Nudm_UECM service, Nudm_SubscriberDataManagement Service, and Nudm_UEAuthentication service) to let everyone understand UDM more clearly.
+Unified Data Management is responsible for managing information related to UE. When other NFs need to use the UE subscription information, they will obtain it from UDM through the SBI of UDM.
+This service is used by AUSF to retrieve authentication-related information and, after authentication, confirm the result.
+ ++ ++
In Authentication, AUSF uses the GET operation to retrieve authentication information for the UE. The request contains the UE’s identity (supi or suci) and the serving network name. The serving network name is used in the derivation of the anchor key, which is used by subsensual authentication. UE’s identity will be contained in the URI, and the serving network name will be contained in the request body.
+Upon reception of the Nudm_UEAuthentication_Get Request, the UDM shall de-conceal SUCI to gain SUPI if SUCI is received. At this time, UDM will query the authentication subscription data from UDR. Then, UDM shall select the authentication method based on SUPI, and if required (e.g., 5G-AKA), UDM will calculate the authentication vector and pass it to AUSF.
+logger.UeauLog.Traceln("In GenerateAuthDataProcedure")
+
+response = &models.AuthenticationInfoResult{}
+rand.Seed(time.Now().UnixNano())
+supi, err := suci.ToSupi(supiOrSuci, udm_context.Getself().SuciProfiles)
+if err != nil {
+ problemDetails = &models.ProblemDetails{
+ Status: http.StatusForbidden,
+ Cause: authenticationRejected,
+ Detail: err.Error(),
+ }
+
+ logger.UeauLog.Errorln("suciToSupi error: ", err.Error())
+ return nil, problemDetails
+}
+
+logger.UeauLog.Tracef("supi conversion => [%s]", supi)
+
+client, err := createUDMClientToUDR(supi)
+if err != nil {
+ return nil, openapi.ProblemDetailsSystemFailure(err.Error())
+}
+authSubs, res, err := client.AuthenticationDataDocumentApi.QueryAuthSubsData(context.Background(), supi, nil)
+
+//in the udm/internal/sbi/producer/generate_auth_data.go, GenerateAuthDataProcedure function.
+
From the code, we can see UDM first de-conceal SUCI (line 5), then use QueryAuthSubsData to get authSub from UDR. After that, UDM uses this information to create the authentication vector.
+Then we record the packet sent in the registration process and find the packet according to the URI specified by the specification. We can find the packet corresponding to this service.
+ +Open the response packet, and we can see the response body matches the AuthenticationInfoResult data type.
+ + ++ ++
After AUSF authenticates the UE, it will confirm the result with UDM. These details will be used in linking authentication confirmation to the Nudm_UECM_Registration procedure from AMF.
+func communicateWithUDM(ue *context.AmfUe, accessType models.AccessType) error {
+ ue.GmmLog.Debugln("communicateWithUDM")
+ amfSelf := context.GetSelf()
+
+ // UDM selection described in TS 23.501 6.3.8
+ // TODO: consider udm group id, Routing ID part of SUCI, GPSI or External Group ID (e.g., by the NEF)
+ param := Nnrf_NFDiscovery.SearchNFInstancesParamOpts{
+ Supi: optional.NewString(ue.Supi),
+ }
+ resp, err := consumer.SendSearchNFInstances(amfSelf.NrfUri, models.NfType_UDM, models.NfType_AMF, ¶m)
+ if err != nil {
+ return errors.Errorf("AMF can not select an UDM by NRF: SendSearchNFInstances failed")
+ }
+
+ var uecmUri, sdmUri string
+ for _, nfProfile := range resp.NfInstances {
+ ue.UdmId = nfProfile.NfInstanceId
+ uecmUri = util.SearchNFServiceUri(nfProfile, models.ServiceName_NUDM_UECM, models.NfServiceStatus_REGISTERED)
+ sdmUri = util.SearchNFServiceUri(nfProfile, models.ServiceName_NUDM_SDM, models.NfServiceStatus_REGISTERED)
+ if uecmUri != "" && sdmUri != "" {
+ break
+ }
+ }
+ ue.NudmUECMUri = uecmUri
+ ue.NudmSDMUri = sdmUri
+ if ue.NudmUECMUri == "" || ue.NudmSDMUri == "" {
+ return errors.Errorf("AMF can not select an UDM by NRF: SearchNFServiceUri failed")
+ }
+
+ problemDetails, err := consumer.UeCmRegistration(ue, accessType, true)
+ if problemDetails != nil {
+ return errors.Errorf(problemDetails.Cause)
+ } else if err != nil {
+ return errors.Wrap(err, "UECM_Registration Error")
+ }
+
+ // TS 23.502 4.2.2.2.1 14a-c.
+ // "After a successful response is received, the AMF subscribes to be notified
+ // using Nudm_SDM_Subscribe when the data requested is modified"
+ problemDetails, err = consumer.SDMGetAmData(ue)
+ if problemDetails != nil {
+ return errors.Errorf(problemDetails.Cause)
+ } else if err != nil {
+ return errors.Wrap(err, "SDM_Get AmData Error")
+ }
+
+ problemDetails, err = consumer.SDMGetSmfSelectData(ue)
+ if problemDetails != nil {
+ return errors.Errorf(problemDetails.Cause)
+ } else if err != nil {
+ return errors.Wrap(err, "SDM_Get SmfSelectData Error")
+ }
+
+ problemDetails, err = consumer.SDMGetUeContextInSmfData(ue)
+ if problemDetails != nil {
+ return errors.Errorf(problemDetails.Cause)
+ } else if err != nil {
+ return errors.Wrap(err, "SDM_Get UeContextInSmfData Error")
+ }
+
+ problemDetails, err = consumer.SDMSubscribe(ue)
+ if problemDetails != nil {
+ return errors.Errorf(problemDetails.Cause)
+ } else if err != nil {
+ return errors.Wrap(err, "SDM Subscribe Error")
+ }
+ ue.ContextValid = true
+ return nil
+}
+
+
+//in the amf/internal/gmm/handler.go.
+
Next, let's take a look at this function. It is called in HandleInitialRegistration, which handles UE's initial registration. UeCmRegistration will use the Nudm_UECM (UECM) service to store related UE Context Management information in UDM. In lines 40, 47, and 54, AMF uses the Nudm_SubscriberDataManagement (SDM) Service to get some subscribe data.
+In the UeCmRegistration function, AMF registers as UE's serving NF on UDM and stores related UE Context Management information in UDM. Looking at the packet, you can see that the request body contains amfInstanceId
and guami
, representing the amf identity, and ratType
, representing the radio access technology type used by UE.
// TS 29.503 5.3.2.2.2
+func RegistrationAmf3gppAccessProcedure(registerRequest models.Amf3GppAccessRegistration, ueID string) (
+ header http.Header, response *models.Amf3GppAccessRegistration, problemDetails *models.ProblemDetails,
+) {
+ // TODO: EPS interworking with N26 is not supported yet in this stage
+ var oldAmf3GppAccessRegContext *models.Amf3GppAccessRegistration
+ if udm_context.Getself().UdmAmf3gppRegContextExists(ueID) {
+ ue, _ := udm_context.Getself().UdmUeFindBySupi(ueID)
+ oldAmf3GppAccessRegContext = ue.Amf3GppAccessRegistration
+ }
+
+ udm_context.Getself().CreateAmf3gppRegContext(ueID, registerRequest)
+
+ clientAPI, err := createUDMClientToUDR(ueID)
+ if err != nil {
+ return nil, nil, openapi.ProblemDetailsSystemFailure(err.Error())
+ }
+
+ var createAmfContext3gppParamOpts Nudr_DataRepository.CreateAmfContext3gppParamOpts
+ optInterface := optional.NewInterface(registerRequest)
+ createAmfContext3gppParamOpts.Amf3GppAccessRegistration = optInterface
+ resp, err := clientAPI.AMF3GPPAccessRegistrationDocumentApi.CreateAmfContext3gpp(context.Background(),
+ ueID, &createAmfContext3gppParamOpts)
+ if err != nil {
+ logger.UecmLog.Errorln("CreateAmfContext3gpp error : ", err)
+ problemDetails = &models.ProblemDetails{
+ Status: int32(resp.StatusCode),
+ Cause: err.(openapi.GenericOpenAPIError).Model().(models.ProblemDetails).Cause,
+ Detail: err.Error(),
+ }
+ return nil, nil, problemDetails
+ }
+ defer func() {
+ if rspCloseErr := resp.Body.Close(); rspCloseErr != nil {
+ logger.UecmLog.Errorf("CreateAmfContext3gpp response body cannot close: %+v", rspCloseErr)
+ }
+ }()
+
+ // TS 23.502 4.2.2.2.2 14d: UDM initiate a Nudm_UECM_DeregistrationNotification to the old AMF
+ // corresponding to the same (e.g. 3GPP) access, if one exists
+ if oldAmf3GppAccessRegContext != nil {
+ deregistData := models.DeregistrationData{
+ DeregReason: models.DeregistrationReason_SUBSCRIPTION_WITHDRAWN,
+ AccessType: models.AccessType__3_GPP_ACCESS,
+ }
+ callback.SendOnDeregistrationNotification(ueID, oldAmf3GppAccessRegContext.DeregCallbackUri,
+ deregistData) // Deregistration Notify Triggered
+
+ return nil, nil, nil
+ } else {
+ header = make(http.Header)
+ udmUe, _ := udm_context.Getself().UdmUeFindBySupi(ueID)
+ header.Set("Location", udmUe.GetLocationURI(udm_context.LocationUriAmf3GppAccessRegistration))
+ return header, ®isterRequest, nil
+ }
+}
+
+//in the udm/internal/sbi/producer/ue_context_management.go
+
In the RegistrationAmf3gppAccessProcedure function, UDM first checks whether the context has been established for that UE; if UDM has such a context, it initiates a Nudm_UECM_DeregistrationNotification to the old AMF later. UDM used the received information to create context and stored it in UDR.
+The SDM service is used to retrieve the UE's individual subscription data relevant to the consumer's NF from the UDM. In the SDMGetAmData function, AMF gets subscription data used in registration and mobility management. In the response packet, AMF got gpsis
, subscribedUeAmbr
, and nssai
.
The GPSI (Generic Public Subscription Identifier) is used to address a 3GPP subscription in data networks outside the realms of a 3GPP system. It contains either an External ID or an MSISDN (Mobile Subscriber ISDN Number).The subscribedUeAmbr
is The Maximum Aggregated uplink and downlink MBRs (max. bit rate) to be shared across all Non-GBR (non-guaranteed Bit Rate) QoS Flows according to the subscription of the user.
In the SDMGetSmfSelectData function, AMF gets subscribed S-NSSAIs (Single Network Slice Selection Assistance Information) and Data Network Names for these S-NSSAIs. AMF will use this information to select an SMF that manages the PDU Session.
+func HandleInitialRegistration(ue *context.AmfUe, anType models.AccessType) error {
+ ue.GmmLog.Infoln("Handle InitialRegistration")
+
+ amfSelf := context.GetSelf()
+
+ // update Kgnb/Kn3iwf
+ ue.UpdateSecurityContext(anType)
+
+ // Registration with AMF re-allocation (TS 23.502 4.2.2.2.3)
+ if len(ue.SubscribedNssai) == 0 {
+ getSubscribedNssai(ue)
+ }
+
+ if err := handleRequestedNssai(ue, anType); err != nil {
+ return err
+ }
+
+//in the amf/internal/gmm/handler.go.
+
In the initialization of HandleInitialRegistration, AMF sends a request to the UDM to receive the UE's NSSAI (Network Slice Selection Assistance Information). After receiving subscribed NSSAI, AMF will compare it to UE's requested NSSAI. If there is a S-NSSAI that has not been subscribed before, AMF will request NSSF for Allowed NSSAI.
+func handleRequestedNssai(ue *context.AmfUe, anType models.AccessType) error {
+ amfSelf := context.GetSelf()
+
+ if ue.RegistrationRequest.RequestedNSSAI != nil {
+ requestedNssai, err := nasConvert.RequestedNssaiToModels(ue.RegistrationRequest.RequestedNSSAI)
+ if err != nil {
+ return fmt.Errorf("Decode failed at RequestedNSSAI[%s]", err)
+ }
+
+ needSliceSelection := false
+ for _, requestedSnssai := range requestedNssai {
+ ue.GmmLog.Infof("RequestedNssai - ServingSnssai: %+v, HomeSnssai: %+v",
+ requestedSnssai.ServingSnssai, requestedSnssai.HomeSnssai)
+ if ue.InSubscribedNssai(*requestedSnssai.ServingSnssai) {
+ allowedSnssai := models.AllowedSnssai{
+ AllowedSnssai: &models.Snssai{
+ Sst: requestedSnssai.ServingSnssai.Sst,
+ Sd: requestedSnssai.ServingSnssai.Sd,
+ },
+ MappedHomeSnssai: requestedSnssai.HomeSnssai,
+ }
+ if !ue.InAllowedNssai(*allowedSnssai.AllowedSnssai, anType) {
+ ue.AllowedNssai[anType] = append(ue.AllowedNssai[anType], allowedSnssai)
+ }
+ } else {
+ needSliceSelection = true
+ break
+ }
+ }
+
+ if needSliceSelection {
+ if ue.NssfUri == "" {
+ for {
+ err := consumer.SearchNssfNSSelectionInstance(ue, amfSelf.NrfUri, models.NfType_NSSF, models.NfType_AMF, nil)
+ if err != nil {
+ ue.GmmLog.Errorf("AMF can not select an NSSF Instance by NRF[Error: %+v]", err)
+ time.Sleep(2 * time.Second)
+ } else {
+ break
+ }
+ }
+ }
+
+ // Step 4
+ problemDetails, err := consumer.NSSelectionGetForRegistration(ue, requestedNssai)
+ if problemDetails != nil {
+ ue.GmmLog.Errorf("NSSelection Get Failed Problem[%+v]", problemDetails)
+ gmm_message.SendRegistrationReject(ue.RanUe[anType], nasMessage.Cause5GMMProtocolErrorUnspecified, "")
+ return fmt.Errorf("Handle Requested Nssai of UE failed")
+ } else if err != nil {
+ ue.GmmLog.Errorf("NSSelection Get Error[%+v]", err)
+ gmm_message.SendRegistrationReject(ue.RanUe[anType], nasMessage.Cause5GMMProtocolErrorUnspecified, "")
+ return fmt.Errorf("Handle Requested Nssai of UE failed")
+ }
+
+//in the amf/internal/gmm/handler.go.
+
if param.SliceInfoRequestForRegistration.RequestedNssai != nil &&
+ len(param.SliceInfoRequestForRegistration.RequestedNssai) != 0 {
+ // Requested NSSAI is provided
+ // Verify which S-NSSAI(s) in the Requested NSSAI are permitted based on comparing the Subscribed S-NSSAI(s)
+ if param.Tai != nil &&
+ !util.CheckSupportedNssaiInPlmn(param.SliceInfoRequestForRegistration.RequestedNssai, *param.Tai.PlmnId) {
+ // Return ProblemDetails indicating S-NSSAI is not supported
+ // TODO: Based on TS 23.501 V15.2.0, if the Requested NSSAI includes an S-NSSAI that is not valid in the
+ // Serving PLMN, the NSSF may derive the Configured NSSAI for Serving PLMN
+ *problemDetails = models.ProblemDetails{
+ Title: util.UNSUPPORTED_RESOURCE,
+ Status: http.StatusForbidden,
+ Detail: "S-NSSAI in Requested NSSAI is not supported in PLMN",
+ Cause: "SNSSAI_NOT_SUPPORTED",
+ }
+
+ status = http.StatusForbidden
+ return status
+ }
+
+ // Check if any Requested S-NSSAIs is present in Subscribed S-NSSAIs
+ checkIfRequestAllowed := false
+
+ for _, requestedSnssai := range param.SliceInfoRequestForRegistration.RequestedNssai {
+ if param.Tai != nil && !util.CheckSupportedSnssaiInTa(requestedSnssai, *param.Tai) {
+ // Requested S-NSSAI does not supported in UE's current TA
+ // Add it to Rejected NSSAI in TA
+ authorizedNetworkSliceInfo.RejectedNssaiInTa = append(
+ authorizedNetworkSliceInfo.RejectedNssaiInTa,
+ requestedSnssai)
+ continue
+ }
+
+ var mappingOfRequestedSnssai models.Snssai
+ // TODO: Compared with Restricted S-NSSAI list in configuration under roaming scenario
+ if param.HomePlmnId != nil && !util.CheckStandardSnssai(requestedSnssai) {
+ // Standard S-NSSAIs are supported to be commonly decided by all roaming partners
+ // Only non-standard S-NSSAIs are required to find mappings
+ targetMapping, found := util.FindMappingWithServingSnssai(requestedSnssai,
+ param.SliceInfoRequestForRegistration.MappingOfNssai)
+
+ if !found {
+ // No mapping of Requested S-NSSAI to HPLMN S-NSSAI is provided by UE
+ // TODO: Search for local configuration if there is no provided mapping from UE, and update UE's
+ // Configured NSSAI
+ checkInvalidRequestedNssai = true
+ authorizedNetworkSliceInfo.RejectedNssaiInPlmn = append(
+ authorizedNetworkSliceInfo.RejectedNssaiInPlmn,
+ requestedSnssai)
+ continue
+ } else {
+ // TODO: Check if mappings of S-NSSAIs are correct
+ // If not, update UE's Configured NSSAI
+ mappingOfRequestedSnssai = *targetMapping.HomeSnssai
+ }
+ } else {
+ mappingOfRequestedSnssai = requestedSnssai
+ }
+
+ hitSubscription := false
+ for _, subscribedSnssai := range param.SliceInfoRequestForRegistration.SubscribedNssai {
+ if mappingOfRequestedSnssai == *subscribedSnssai.SubscribedSnssai {
+ // Requested S-NSSAI matches one of Subscribed S-NSSAI
+ // Add it to Allowed NSSAI list
+ hitSubscription = true
+
+ var allowedSnssaiElement models.AllowedSnssai
+ allowedSnssaiElement.AllowedSnssai = new(models.Snssai)
+ *allowedSnssaiElement.AllowedSnssai = requestedSnssai
+ nsiInformationList := util.GetNsiInformationListFromConfig(requestedSnssai)
+ if nsiInformationList != nil {
+ // TODO: `NsiInformationList` should be slice in `AllowedSnssai` instead of pointer of slice
+ allowedSnssaiElement.NsiInformationList = append(
+ allowedSnssaiElement.NsiInformationList,
+ nsiInformationList...)
+ }
+ if param.HomePlmnId != nil && !util.CheckStandardSnssai(requestedSnssai) {
+ allowedSnssaiElement.MappedHomeSnssai = new(models.Snssai)
+ *allowedSnssaiElement.MappedHomeSnssai = *subscribedSnssai.SubscribedSnssai
+ }
+
+ // Default Access Type is set to 3GPP Access if no TAI is provided
+ // TODO: Depend on operator implementation, it may also return S-NSSAIs in all valid Access Type if
+ // UE's Access Type could not be identified
+ var accessType models.AccessType = models.AccessType__3_GPP_ACCESS
+ if param.Tai != nil {
+ accessType = util.GetAccessTypeFromConfig(*param.Tai)
+ }
+
+ util.AddAllowedSnssai(allowedSnssaiElement, accessType, authorizedNetworkSliceInfo)
+
+ checkIfRequestAllowed = true
+ break
+ }
+ }
+
+ if !hitSubscription {
+ // Requested S-NSSAI does not match any Subscribed S-NSSAI
+ // Add it to Rejected NSSAI in PLMN
+ checkInvalidRequestedNssai = true
+ authorizedNetworkSliceInfo.RejectedNssaiInPlmn = append(
+ authorizedNetworkSliceInfo.RejectedNssaiInPlmn,
+ requestedSnssai)
+ }
+ }
+
+ if !checkIfRequestAllowed {
+ // No S-NSSAI from Requested NSSAI is present in Subscribed S-NSSAIs
+ // Subscribed S-NSSAIs marked as default are used
+ useDefaultSubscribedSnssai(param, authorizedNetworkSliceInfo)
+ }
+} else {
+ // No Requested NSSAI is provided
+ // Subscribed S-NSSAIs marked as default are used
+ checkInvalidRequestedNssai = true
+ useDefaultSubscribedSnssai(param, authorizedNetworkSliceInfo)
+}
+
+//in the nssf/internal/sbi/producer/nsselection_for_registration.go, nsselectionForRegistration funcion.
+
If NSSF needs to select S-NSSAI, it first finds the mapping of requested NSSAI to configured NSSAI for the HPLMN and converts requested S-NSSAI to S-NSSAI in configured NSSAI for the HPLMN. Then compare these S-NSSAIs with Subscribed S-NSSAIs; if NSSF find one match, set it as AllowedSnssai
. If NSSF can't find such a mapping or no S-NSSAI in the mapping matches subscribed S-NSSAIs, it will use default subscribed S-NSSAIs.
Hello! My name is 張哲睿, and my current research topic is ATSSS (Access Traffic Steering, Switching and Splitting), I will continue to write articles related to 5G networks in the future. If you find any mistakes in my articles or have any topics you want to know about, please contact me.
+Note
+Author: Yu-Sheng Liu
+Date: 2023/8/9
In this article, we begin by introducing the concept of fuzz testing and its significance in software testing. Subsequently, we present Go Fuzzing as an illustrative example to demonstrate how to implement fuzz testing in Go. Lastly, we showcase a practical case, CVE-2022-43677, to exemplify how we conduct fuzz testing on the free5GC system.
+Fuzz testing, commonly known as fuzzing, is an automated software testing technique used to uncover vulnerabilities, defects, and unexpected behavior in computer systems, applications, and networks.
+The primary objective of fuzzing is to identify security flaws, crashes, or abnormal program behavior caused by invalid or unexpected inputs.
Fuzz testing involves subjecting the target software or system to a large number of inputs, including random or malformed data, to see how it handles them.
+The idea is to explore edge cases and input combinations that may not have been adequately tested during traditional software testing.
+Here's how the fuzzing process typically works:
Test Input Generation:
+Test Execution:
+Monitoring and Analysis:
+Feedback and Iteration:
+Black Box Fuzzing:
+White Box Fuzzing:
+Grey Box Fuzzing:
+Bug and Vulnerability Discovery:
+Automation and Efficiency:
+Diverse Test Inputs:
+Early Vulnerability Detection:
+Fuzz testing, or fuzzing, is a powerful and essential technique in the realm of software security testing.
+By providing a diverse set of inputs and exploring uncharted code paths, fuzz testing uncovers vulnerabilities and defects that might otherwise remain hidden.
Next, we will use Go fuzzing as an example to introduce how to develop a fuzzing in Go.
+Go officially supports fuzzing starting from Go 1.18, and its official figure provides a brief and clear summary of the fuzzing function components.
+
Similar to Go's unit test functions, the fuzzing function in Go must follow the naming convention FuzzXxx
and take an argument of type *testing.F
. This argument has two main functions, Add
and Fuzz
.
Add
Function:
Add
function to add your own test data to the seed corpus for fuzz testing. The seed corpus is the initial set of inputs that go-fuzz
will use to start the fuzzing process.Fuzz
Function:
Fuzz
function will be the target function that you want to test using fuzzing. It must have *testing.T
as its first argument, similar to regular unit tests.Fuzz
function supports variadic arguments with the following basic data types:string
, []byte
int
, int8
, int16
, int32/rune
, int64
uint
, uint8/byte
, uint16
, uint32
, uint64
float32
, float64
bool
These data types represent the different kinds of input data that can be passed to the Fuzz
function during the fuzzing process. The fuzzer will generate and mutate inputs of these types to explore different code paths and uncover bugs or unexpected behavior in the target function.
In summary, when writing fuzzing functions in Go, remember to use the FuzzXxx
naming pattern, accept *testing.F
as an argument, utilize the Add
function to customize the seed corpus, and use the Fuzz
function with supported basic data types to perform fuzz testing on your target functions.
You can use the command to execute the fuzz testing:
+
go test -fuzz=<regex> -fuzztime=<duration or times>
+
+# Execute the fuzz testing until it crashs or finding some errors
+go test -fuzz=Fuzz
+
+# Execute the fuzz testing ten iterations
+go test -fuzz=Fuzz -fuzztime=10x
+
+# Execute the fuzz testing twenty seconds
+go test -fuzz=Fuzz -fuzztime=20s
+
We have developed a very simple function called Division
that accepts two arguments, dividend
and divisor
, and then returns two results: quotient
and remainder
.
func Division(dividend, divisor int32) (
+ quotient, remainder int32,
+) {
+ quotient = dividend / divisor
+ remainder = dividend % divisor
+
+ return
+}
+
In the FuzzDivision
function, we utilize the data generated by the Go fuzzer to test our Division
function.
func FuzzDivision(f *testing.F) {
+ f.Fuzz(func(t *testing.T,
+ n1, n2 int32,
+ ) {
+ q, r := Division(n1, n2)
+
+ require.Equal(t, n1, n2*q+r)
+ })
+}
+
We expected to see:
+
n1 / n2 = q ... r
+n1 = n2 * q + r
+
There should not be any problems with this implementation.
+Then we can use the following command to start the fuzz testing.
+go test -fuzz=^FuzzDivision$
+
The fuzz testing reports the error "integer divide by zero".
+ +As a normal user, we understand that the divisor cannot be zero. However, the input data may not always be as expected. This is precisely why we use fuzz testing—to help us find edge cases and uncover unexpected behavior.
+Go stores the data that caused the fuzz testing to fail. You can check them using the following command.
+ * Note: The file name, 29bf8459dc5d452f64d41eb8a253f6a672939b146b07fcced0b17e99729e9b91
, may not be the same.
cat testdata/fuzz/FuzzDivision/29bf8459dc5d452f64d41eb8a253f6a672939b146b07fcced0b17e99729e9b91
+
The content of the file is as follows:
+
go test fuzz v1
+int32(-7)
+rune('\x00')
+
The first line indicates the encoding version, and the subsequent lines represent the argument values that triggered the error during the fuzz testing.
+Now we can modify our Division
function to check the divisor
if it is zero.
var ErrorDivideByZero = fmt.Errorf("integer divide by zero")
+
+func Division(dividend, divisor int32) (
+ quotient, remainder int32, err error,
+) {
+ if divisor == 0 {
+ err = ErrorDivideByZero
+ return
+ }
+
+ quotient = dividend / divisor
+ remainder = dividend % divisor
+
+ return
+}
+
Similarly, the FuzzDivision
fuzzing function now checks for the presence of the ErrorDivideByZero
error.
func FuzzDivision(f *testing.F) {
+ f.Add(int32(67), int32(3))
+
+ f.Fuzz(func(t *testing.T,
+ n1, n2 int32,
+ ) {
+ if q, r, err := Division(n1, n2); err != ErrorDivideByZero {
+ require.Equal(t, n1, n2*q+r)
+ }
+ })
+}
+
Now, we can use the following command to re-test the failing case.
+go test -run=FuzzDivision/29bf8459dc5d452f64d41eb8a253f6a672939b146b07fcced0b17e99729e9b91
+
We have used a simple example to describe how to develop a fuzzing function in Go and how to leverage the Go command-line tool to execute fuzz testing.
+Next, we will examine a real case, CVE-2022-43677, and demonstrate the process of developing a fuzzing function to identify edge cases.
+Accroding to the descriptoin:
+++In free5GC 3.2.1, a malformed NGAP message can crash the AMF and NGAP decoders via an index-out-of-range panic in aper.GetBitString.
+
In response to this vulnerability, we have developed a fuzzing function to test the NGAP decoder.
+The function utilizes two approaches: modifying the NGAP message's content under a valid template or adjusting its format by changing the Information Elements (IEs) with variable lengths.
// Put the code under the free5gc/test
+func FuzzNgapDecode(f *testing.F) {
+ f.Fuzz(func(t *testing.T,
+ modifyWhat uint8,
+ changeIe0, changeIe1, changeIe2, changeIe3, changeIe4 bool,
+ valueIe0A uint32,
+ valueIe2ACellId uint64, valueIe2ATac uint32,
+ valueIe3A uint64,
+ valueIe4A uint64,
+ valueIePlmn uint32,
+ ) {
+ var idx, n int
+ var sendMsg []byte
+ var registrationRequest []byte
+ var bs []byte
+ var err error
+ var ngapPdu ngapType.NGAPPDU
+ var mobileIdentity5GS nasType.MobileIdentity5GS
+ var ue *test.RanUeContext
+
+ // New UE
+ ue = test.NewRanUeContext("imsi-2089300007487", 1, security.AlgCiphering128NEA0, security.AlgIntegrity128NIA2,
+ models.AccessType__3_GPP_ACCESS)
+ ue.AmfUeNgapId = 1
+ ue.AuthenticationSubs = test.GetAuthSubscription(TestGenAuthData.MilenageTestSet19.K,
+ TestGenAuthData.MilenageTestSet19.OPC,
+ TestGenAuthData.MilenageTestSet19.OP)
+
+ mobileIdentity5GS = nasType.MobileIdentity5GS{
+ Len: 12, // suci
+ Buffer: []uint8{0x01, 0x02, 0xf8, 0x39, 0xf0, 0xff, 0x00, 0x00, 0x00, 0x00, 0x47, 0x78},
+ }
+
+ if modifyWhat%2 == DoModifyContent {
+ if changeIe0 {
+ // RAN UE NGAP ID
+ ue.RanUeNgapId = int64(valueIe0A)
+ }
+
+ registrationRequest = nasTestpacket.GetRegistrationRequest(
+ nasMessage.RegistrationType5GSInitialRegistration, mobileIdentity5GS, nil, ue.GetUESecurityCapability(), nil, nil, nil)
+ ngapPdu = ngapTestpacket.BuildInitialUEMessage(ue.RanUeNgapId, registrationRequest, "")
+
+ if changeIe2 {
+ // User Location Information
+ for _, ie := range ngapPdu.InitiatingMessage.Value.InitialUEMessage.ProtocolIEs.List {
+ if ie.Id.Value == ngapType.ProtocolIEIDUserLocationInformation {
+ bs = make([]byte, 4)
+ valueIePlmn &= uint32(PlmnMask)
+ binary.LittleEndian.PutUint32(bs, valueIePlmn)
+
+ NgRan := ie.Value.UserLocationInformation.UserLocationInformationNR
+ NgRan.NRCGI.PLMNIdentity.Value = bs[:PlmnByteLen]
+ NgRan.TAI.PLMNIdentity.Value = bs[:PlmnByteLen]
+
+ bs = make([]byte, 8)
+ valueIe2ACellId &= uint64(CellIdMask)
+ binary.LittleEndian.PutUint64(bs, valueIe2ACellId)
+ NgRan.NRCGI.NRCellIdentity.Value.Bytes = bs[:CellIdByteLen]
+
+ bs = make([]byte, 4)
+ valueIe2ATac &= uint32(TacMask)
+ binary.LittleEndian.PutUint32(bs, valueIe2ATac)
+ NgRan.TAI.TAC.Value = bs[:TacByteLen]
+ }
+ }
+ }
+ if changeIe3 {
+ // RRC Establishment Cause
+ for _, ie := range ngapPdu.InitiatingMessage.Value.InitialUEMessage.ProtocolIEs.List {
+ if ie.Id.Value == ngapType.ProtocolIEIDRRCEstablishmentCause {
+ ie.Value.RRCEstablishmentCause.Value = aper.Enumerated(valueIe3A)
+ }
+ }
+ }
+ if changeIe4 {
+ // UE Context Request
+ for _, ie := range ngapPdu.InitiatingMessage.Value.InitialUEMessage.ProtocolIEs.List {
+ if ie.Id.Value == ngapType.ProtocolIEIDUEContextRequest {
+ ie.Value.UEContextRequest.Value = aper.Enumerated(valueIe4A)
+ }
+ }
+ }
+ sendMsg, err = ngap.Encoder(ngapPdu)
+ } else if modifyWhat%2 == DoModifyFormat {
+ registrationRequest = nasTestpacket.GetRegistrationRequest(
+ nasMessage.RegistrationType5GSInitialRegistration, mobileIdentity5GS, nil, ue.GetUESecurityCapability(), nil, nil, nil)
+
+ if changeIe1 {
+ registrationRequest[3] += 1
+ registrationRequest = append(registrationRequest, registrationRequest[len(registrationRequest)-1])
+ } else {
+ registrationRequest[3] -= 1
+ registrationRequest = registrationRequest[:len(registrationRequest)-1]
+ }
+
+ ngapPdu = ngapTestpacket.BuildInitialUEMessage(ue.RanUeNgapId, registrationRequest, "")
+ sendMsg, err = ngap.Encoder(ngapPdu)
+ require.Nil(t, err, "Error: %v", err)
+ require.Equal(t, int(sendMsg[3]), len(sendMsg[4:]), "%v", sendMsg)
+
+ idx = bytes.Index(sendMsg, []byte("\x00\x70\x40"))
+ assert.NotEqual(t, idx, -1, "Can not find UE context Request")
+ if idx != -1 {
+ if valueIe4A%8 == 0 || valueIe4A%8 == 1 {
+ n = 2
+ } else {
+ n = int(valueIe4A % 8)
+ }
+ sendMsg[idx+3] = uint8(n)
+ sendMsg = sendMsg[:idx+4]
+ bs = make([]byte, 8)
+ binary.LittleEndian.PutUint64(bs, valueIe4A)
+
+ for i := 0; i < n; i++ {
+ sendMsg = append(sendMsg, bs[i])
+ }
+
+ sendMsg[3] += uint8(n - 1) // total length
+ }
+ }
+ require.Equal(t, int(sendMsg[3]), len(sendMsg[4:]), "%v", sendMsg)
+
+ _, err = ngap.Decoder(sendMsg)
+ })
+}
+
We can use the following command to execute the fuzz testing.
+go test -fuzz=^FuzzNgapDecode$ -run=^FuzzNgapDecode$
+
The test resulted in a crash, which confirms the presence of the vulnerability as described in CVE-2022-43677.
+ +The bug was found in the package aper at version v1.0.4. Fortunately, the latest version of the package has already fixed this issue. To verify the fix, we can update the aper package to the latest commit using the following commands:
+# Update package aper to the latest commit
+go get github.com/free5gc/aper@main
+
After updating the aper package, we can test it again with the fuzzing function:
+go test -fuzz=^FuzzNgapDecode$ -run=^FuzzNgapDecode$
+
# Alternatively, re-testing the failing case
+go test -run=FuzzNgapDecode/87af855bbc381c8d510af5ce897fcdd7f9154574e61c0413223f7e31769c2767
+
Fuzz testing is a powerful technique for improving the security and reliability of software systems. By subjecting programs to a wide range of inputs, fuzzing can uncover vulnerabilities and defects that might not be found through traditional testing methods. It automates the testing process, making it efficient and scalable for large codebases.
+In the context of Go programming, Go fuzzing is well-supported and integrates seamlessly with the standard testing framework. Developers can create fuzzing functions to target specific functions and uncover potential issues using random or mutated inputs.
+To demonstrate the effectiveness of fuzz testing, we presented a real case, CVE-2022-43677, which affected free5GC version 3.2.1. By developing a fuzzing function for the NGAP decoder, we were able to identify a vulnerability that caused a crash.
+In conclusion, fuzz testing is a critical practice in software development, enabling developers to proactively discover and resolve bugs and vulnerabilities. It empowers them to deliver more secure and robust software systems, providing users with a higher level of confidence in the applications they use. By incorporating fuzz testing as part of the software development lifecycle, developers can significantly enhance the quality and security of their software products.
+I'm Yu-Sheng Liu, a master's student at National Yang Ming Chiao Tung University. My research topic focuses on improving the performance of the 5G core network, such as reducing the latency of message propagation in SBI.
+If you have any questions, please don't hesitate to contact me!
Note
+Author: Daniel Hsieh
+Date: 2023/7/26
Network slicing allows for the creation of multiple logical, isolated, and independent virtual networks that can coexist within a shared physical infrastructure. Each network slice provides dedicated and customized network resources to meet the specific requirements of different services
+The main elements of a network slice include:
Virtualized Network Functions (VNFs): Each network slice can include a set of virtualized network functions that provide specific network capabilities and services. These VNFs can include functions like routing, switching, firewalling, load balancing, or any other network service required by the slice.
+Isolation and Resource Allocation: Network slicing ensures the isolation of resources between slices, preventing interference and conflicts. It allows for the allocation of dedicated and optimized resources such as bandwidth, processing power, and storage to each slice based on its specific needs.
+Orchestration and Management: Network slice orchestration involves the creation, provisioning, and management of network slices. It involves configuring the appropriate VNFs, assigning resources, and establishing connectivity between the different components of a slice.
++ ++
Take Figure 1 as an example. The first slice is designed for mobile devices such as smartphones. Such slice requires a huge diversity of VNFs, and virtual links with high speed and low latency to support the broadband service of smartphones. In 5G network, Those slices are referred to as eMBB (enhanced mobile broadband) slices.
+The second slice is designed for autonomous driving. In such scenario, extremely low latency and high reliability are paramount to ensure the vehicles' operability, smoothness and safety. To achieve low latency, some of the NFs should be deployed close to the access node,i.e. on edge cloud. To achieve high reliability, a NF should have multiple instances on available physical resources to make the slice more fault tolerant. Such slice is referred to as URLLC (Ultra-Reliable Low-Latency Communications) slice.
+The third slice is designed for massive IoT. IoT devices are expected to not move and send very small amount of data intermittently. Due to the nature of such devices, functions that handle mobiltiy and always-on connections are not needed. Such slices are referred to as mIoT (massive IoT) slices.
+In this article, we utilize MANO network function virtualization (NFV) architecture to deploy virtual network function (VNF). It plays the role of creating, deploying, and managing VNFs. MANO consists of three main functional components: NFV Orchestrator (NFVO), Virtualized Infrastructure Manager (VIM), and Virtual Network Function Manager (VNFM).
+ ++ ++
NFVO manages the underlying resource by coordinating VIM and VNFM. It handles tasks such as receiving requests, service instantiation, scaling, termination, and monitoring.
+VNFM manages the lifecycle of VNF instances. It interacts with the VIM to instantiate, configure, monitor, and terminate VNF instances.
+VIM is responsible for managing the underlying virtualized infrastructure that hosts the VNFs. It abstracts the physical resources, such as compute, storage, and networking, and provides a unified view to the NFVO. The VIM handles tasks like resource allocation, performance monitoring, fault management, and virtualization management.
+For VIM, we use OpenStack, an open-source software that provides IaaS, to utilize the physical resources. For VNFM and NFVO, we use Tacker, a service component of OpenStack, to manage VNFs.
+OpenStack is an open-source cloud computing platform that provides a set of software tools for building and managing customized clouds. OpenStack offers a infrastructure-as-a-service (IaaS) solution, enabling organizations to create and manage virtualized resources in a cloud environment. It is designed to be modular and consists of various components that work together to deliver a comprehensive cloud computing platform. Some of the key components include:
+Nova: Nova is the computing component of OpenStack and serves as the main compute engine. It manages the creation, scheduling, and management of virtual machines (VMs) and provides APIs for controlling and interacting with the compute resources.
+Cinder: Cinder is the block storage component of OpenStack. It provides persistent storage for virtual machines. With Cinder, users can create and manage volumes that can be attached to instances, allowing for flexible and scalable storage options.
+Neutron: Neutron is the networking component of OpenStack. It provides a networking-as-a-service (NaaS) solution, allowing users to define and manage network resources. Neutron supports virtual LANs, software-defined networking (SDN), and network function virtualization (NFV), etc.
+Keystone: Keystone is the identity service component of OpenStack. It provides authentication and authorization services, enabling users to securely access and manage resources within the cloud. Keystone supports multiple authentication mechanisms, including username/password, token-based, and external identity providers.
+Horizon: Horizon is the web-based dashboard for OpenStack. It provides a user-friendly interface for managing and monitoring the cloud infrastructure. With Horizon, users can perform various tasks, such as launching instances, managing storage resources, and configuring networking options.
++ ++
OpenStack is highly flexible and customizable, allowing organizations to tailor the cloud infrastructure to their specific needs. It supports multiple hypervisors, including KVM, VMware, and Hyper-V.
+To enable NFV, we need another service component of OpenStack called Tacker. Tacker is designed to simplify the deployment and lifecycle management of VNFs and network service functions (NSFs) in a cloud infrastructure. It leverages OpenStack's existing components, such as Nova, Neutron, and Heat, to provide a comprehensive solution for network service orchestration.
+Tacker provides several key features and functionalities:
Service Templates: Tacker uses service templates to define the composition and behavior of network services. These templates describe the VNFs and NSFs involved, their interconnections, resource requirements, etc. Service templates are written using the TOSCA (Topology and Orchestration Specification for Cloud Applications) standard.
+Lifecycle Management: Tacker automates the entire lifecycle of network services, including provisioning, scaling, healing, and termination. It leverages Heat, OpenStack's orchestration service, to manage the underlying infrastructure resources required by the services and handle dynamic scaling of VNFs based on traffic demands.
+VNF Manager: Tacker includes a VNF Manager component responsible for managing the lifecycle of VNFs. It interacts with OpenStack's compute and networking services, to instantiate and manage VNF instances.
+Multi-VIM Support: Tacker supports multiple virtual infrastructure managers to accommodate different cloud platforms and environments. It can interact with OpenStack, VMware vSphere and Kubernetes and so on, enabling operators to deploy network services across heterogeneous infrastructure environments.
++ ++
In our implementation, we install OpenStack and Tacker on two different virtual machines for resource utilization reasons, but in fact, they can be installed on the same virtual machine.
+we need to install OpenStack on a virtual machine. Specific details and corresponding compatibility can be found on OpenStack official website. Using devstack scripts for installation enables operators to customize the environment based on their needs, such as extra plugins (softwares that extends the functionality of OpenStack environment) and overcommit (allows deploying NFs that require more resource than existing physical resourcce) functionality. Upon completion, a web UI enabled by Horizon can be used to access and operate on your own personalized OpenStack cloud.
+Install Tacker on another virtual machine, which requires four OpenStack service components, Keystone, Mistral, Barbican and Horizon. Once the installation is completed, we can register our OpenStack VIM on Tacker using openstack vim register
command.
Create two instances that will be used as images (one for control plane VNFs, one for UPF) for the VNFs that we will create. Then, ssh
into those instances to set up the configurations for the VNFs, such as, installing required packages (go language, mongodb, libtool, etc.) and git clone
free5GC source code. Once all the configurations are done, use OpenStack dashboard to take snapshots of these instances, which will be used as the images for VNFs.
Import all the VNF descriptors (VNFD) of the VNFs we need by using openstack vnf descriptor create
command. VNFDs should be written in accordance with TOSCA format. TOSCA format allows you to define the virtual links (a virtual network VNFs will be running in) and virtual deployment unit (operation unit of a VNF).
+ Below is an example of UPF VNFD:
+
tosca_definitions_version: tosca_simple_profile_for_nfv_1_0_0
+description: description
+node_types:
+ tosca.nodes.nfv.VNF11:
+ requirements:
+ - virtualLink1:
+ type: tosca.nodes.nfv.VL
+ required: true
+metadata:
+ template_name: free5GCSetup
+topology_template:
+ substitution_mappings:
+ node_type: tosca.nodes.nfv.VNF11
+ node_templates:
+ VDU1:
+ type: tosca.nodes.nfv.VDU.Tacker
+ properties:
+ name: free5gc-upf1-VNF
+ image: stage3-up
+ flavor: free5gc
+ availability_zone: nova
+ mgmt_driver: noop
+ key_name: free5gc
+ user_data_format: RAW
+ user_data: |
+ #!/bin/sh
+ cd /home/ubuntu/free5gc/src/upf/build
+ cat > config/upfcfg.yaml <<- EOM
+ info:
+ version: 1.0.0
+ description: UPF configuration
+
+ configuration:
+ # debugLevel: panic|fatal|error|warn|info|debug|trace
+ debugLevel: info
+
+ pfcp:
+ - addr: 192.168.2.111
+
+ gtpu:
+ - addr: 192.168.2.111
+ # [optional] gtpu.name
+ # - name: upf.5gc.nctu.me
+ # [optional] gtpu.ifname
+ # - ifname: gtpif
+
+ apn_list:
+ - apn: internet
+ cidr: 60.60.0.0/24
+ # [optional] apn_list[*].natifname
+ # natifname: eth0
+ EOM
+ #sudo ./bin/free5gc-upfd -f config/upfcfg.yaml
+
+ CP1:
+ type: tosca.nodes.nfv.CP.Tacker
+ properties:
+ ip_address: 192.168.2.111
+ management: true
+ requirements:
+ - virtualLink:
+ node: VL1
+ - virtualBinding:
+ node: VDU1
+ VL1:
+ type: tosca.nodes.nfv.VL
+ properties:
+ network_name: 5GC
+ vendor: Tacker
+ FIP1:
+ type: tosca.nodes.network.FloatingIP
+ properties:
+ floating_network: public
+ floating_ip_address: 172.24.4.111
+ requirements:
+ - link:
+ node: CP1
+
openstack ns descriptor create
command. The NSD should also be written in accordance with TOSCA format. Once all the VNFDs and NSD are all successfully imported, we can use openstack ns create
to deploy the network slice. The VNFs specified in the NSD will also be instantiated along with the network slice. Their instances can be viewed on OpenStack dashboard enabled by Horizon or just use openstack vnf list
to check the status of the VNFs.tosca_definitions_version: tosca_simple_profile_for_nfv_1_0_0
+description: Import Common Slice VNFDs (already on-boarded)
+imports:
+ - mongo
+ - nrf
+ - amf
+ - smf
+ - udr
+ - pcf
+ - udm
+ - nssf
+ - ausf
+topology_template:
+ node_templates:
+ VNF0:
+ type: tosca.nodes.nfv.VNF0
+ VNF1:
+ type: tosca.nodes.nfv.VNF1
+ VNF2:
+ type: tosca.nodes.nfv.VNF2
+ VNF3:
+ type: tosca.nodes.nfv.VNF3
+ VNF4:
+ type: tosca.nodes.nfv.VNF4
+ VNF5:
+ type: tosca.nodes.nfv.VNF5
+ VNF6:
+ type: tosca.nodes.nfv.VNF6
+ VNF7:
+ type: tosca.nodes.nfv.VNF7
+ VNF8:
+ type: tosca.nodes.nfv.VNF8
+
ssh
into the VNF instances to make the necessary configuration for each VNF and start the free5GC VNF.There are many other ways to set up a network slice. For example, we can deploy VNFs of the same network slice on different VIMs, or we can deploy all the network slices on the same VIM, as long as it is specified in the VNFDs.
+Hi, my name is Daniel Hsieh. I am a CS major graduate student. My research field is network slicing. If there are any questions about the article, please feel free to contact.
+https://www.acecloudhosting.com/blog/openstack-the-catalyst-of-the-public-cloud-market/
+https://telcocloudbridge.com/blog/a-beginners-guide-to-nfv-management-orchestration-mano/
+https://wiki.openstack.org/wiki/Tacker
+B. Chatras, U. S. Tsang Kwong and N. Bihannic, "NFV enabling network slicing for 5G," 2017 20th Conference on Innovations in Clouds, Internet and Networks (ICIN), Paris, France, 2017, pp. 219-225, doi: 10.1109/ICIN.2017.7899415.
+In this demo, we will
+Search virtualbox download
, or visit virtualbox.org to download and install VirtualBox (currently 6.1.18) for your operation system.
+
Once installed VirtualBox, launch and see if you have something like this:
+
Search ubuntu server download
on the web and download the latest Ubuntu Server LTS, or visit ubuntu.com, choose Manual Installation Option to download the .iso
file (currently 20.04.2 LTS)
+
You should have downloaded a .iso image
file with name like ubuntu-20.04.1-live-server-amd64.iso
, probably in your download directory.
+
Launch VirtualBox and create your first Ubuntu VM using the downloaded .iso image file. We use Ubuntu Server instead of Ubuntu Desktop because we only need a basic server machine without too many unnecessary functionalities. The resulting overhead to your host machine is smaller, and the VM starts up faster too.
+Tips
+ubuntu-server
, or ubuntu-20.04
.Refer to the videos Creating VM, Setting up VM.
+Some notes about installing Ubuntu:
+Refer to videos Install Ubuntu 1, Install Ubuntu 2.
+Reboot after Ubuntu installation complete; wait a little bit for some initialization steps complete. Then log in with your username and password.
+
First try the ifconfig
command:
+
ubuntu@ubuntu:~$ ifconfig
+Command 'ifconfig' not found, but can be installed with:
+sudo apt install net-tools
+ubuntu@ubuntu:~$
+
If some messages like above show, it means ifconfig
has not been installed yet. (ifconfig
is no longer installed by defaults in newer Ubuntu, and is replaced by more versatile ip command, but we will use it here for simplicity).
Follow its suggestion and install ifconfig
:
+
ubuntu@ubuntu:~$ sudo apt install net-tools
+
Run ifconfig
again to check the network interfaces:
+
Your display may look different, but take notes about the IP address of the Host-only interface card. The example above shows 192.168.56.101
. You can SSH from your host machine into this Ubuntu VM using the IP later. (Another IP address, 10.0.2.15
is the IP address of the NAT interface card, the apps in your host machine cannot access it).
Finally check if the VM has internet access:
+
ubuntu@ubuntu:~$ ping google.com
+
Refer to the first part of the video Ping, SSH, and Upgrade.
+Launch your favorite SSH client from the host machine.
+Some operation systems (Mac, Ubuntu, some Windows) have pre-installed SSH clients. If you are using Windows, you can also download third-party SSH clients. For example, search “windows ssh download” on the web.
The benefit of using SSH is that you can easily copy and paste commands from your machine to Ubuntu VM for execution, and vice versa. You can also create multiple SSH connections with the Ubuntu VM for control and monitoring at the same time.
+Below shows some examples on a Mac host machine. Suppose the Host-only network IP is 192.168.56.101
, and tue username is ubuntu:
+
ssh 192.168.56.101 -l ubuntu
+
Tips
+If somehow SSH shows some warning messages telling you the machine has potential security risk, you may have to remove an entry in the file <your home directory>/.ssh/known_hosts
related the the IP address.
If you log in successfully, you will enter a command line interface:
+
Repeat the basic commands such as ping
, ifconfig
to see if the VM is working properly. If so, we can access the Ubuntu VM “remotely” from now on.
+
Let also update and upgrade the Ubuntu VM right now to make sure it is up-to-date with proper security updates.
+
sudo apt update
+sudo apt upgrade
+
In this demo we will exercise:
+Tips
+Refer to video Clone VM and Change IP.
+Launch VirtualBox, and make sure the Ubuntu VM (ubuntu) we created before can boot up, then:
+sudo apt update
and sudo apt upgrade
(or you can do it again)sudo shutdown -P now
, orsudo shutdown -r now
First let’s clone a new VM:
+free5gc
.After the new VM is created:
+ping
and ifconfig
again to make sure it has internet access, and also make note of the IP address of the Host-only network interface.192.168.56.101
, and the interface name is enp0s8
.The cloned free5gc VM still has host name ubuntu
(or the name you gave it in the original VM). Let’s rename the VM to free5gc
. You can do this by editing the file /etc/hostname
(using vi
or nano
):
+
sudo nano /etc/hostname
+# or
+sudo vi /etc/hostname
+
free5gc
。If you are using nano ,you can press Ctrl-O
to save the file, then Ctrl-X
to exit.
+Let’s also change the file /etc/hosts
by replacing the ubuntu inside into free5gc
:
+
sudo nano /etc/hosts
+
New content of the file /etc/hosts
looks like this:
+
127.0.0.1 localhost
+127.0.1.1 free5gc
+...
+
The changes will take effect after next reboot.
+The Host-only network interface, by default, gets its IP address through DHCP. The cloned free5gc VM seems to have trouble obtaining new IP address. We can change the host-only interface to use static IP address instead, which can save a lot of trouble later.
+Here let’s fix the static IP address as 192.168.56.101
:
+
$ cd /etc/netplan
+$ ls
+00-installer-config.yaml
+$ cat 00-installer-config.yaml
+
00-installer-config.yaml
looks like:# This is the network config written by 'subiquity'
+network:
+ ethernets:
+ enp0s3:
+ dhcp4: true
+ enp0s8:
+ dhcp4: true
+ version: 2
+
ifconfig
we know that enp0s8
is the name of the Host-only network interface. We can edit the file:sudo nano 00-installer-config.yaml
+
# This is the network config written by 'subiquity'
+network:
+ ethernets:
+ enp0s3:
+ dhcp4: true
+ enp0s8:
+ dhcp4: no
+ addresses: [192.168.56.101/24]
+ version: 2
+
sudo netplan try
+
sudo netplan apply
+
ifconfig
to see if the network setting has been changed correctly:enp0s3: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
+ inet 10.0.2.15 netmask 255.255.255.0 broadcast 10.0.2.255
+ inet6 fe80::a00:27ff:fec4:254f prefixlen 64 scopeid 0x20<link>
+ ether 08:00:27:c4:25:4f txqueuelen 1000 (Ethernet)
+ RX packets 2 bytes 1180 (1.1 KB)
+ RX errors 0 dropped 0 overruns 0 frame 0
+ TX packets 18 bytes 1894 (1.8 KB)
+ TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
+
+enp0s8: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
+ inet 192.168.56.101 netmask 255.255.255.0 broadcast 192.168.56.255
+ inet6 fe80::a00:27ff:fe7e:ada6 prefixlen 64 scopeid 0x20<link>
+ ether 08:00:27:7e:ad:a6 txqueuelen 1000 (Ethernet)
+ RX packets 8420 bytes 531867 (531.8 KB)
+ RX errors 0 dropped 0 overruns 0 frame 0
+ TX packets 10887 bytes 823487 (823.4 KB)
+ TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
+
+lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
+ inet 127.0.0.1 netmask 255.0.0.0
+ inet6 ::1 prefixlen 128 scopeid 0x10<host>
+ loop txqueuelen 1000 (Local Loopback)
+ RX packets 6621 bytes 596035 (596.0 KB)
+ RX errors 0 dropped 0 overruns 0 frame 0
+ TX packets 6621 bytes 596035 (596.0 KB)
+ TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
+
$ route -n
+Kernel IP routing table
+Destination Gateway Genmask Flags Metric Ref Use Iface
+0.0.0.0 10.0.2.2 0.0.0.0 UG 100 0 0 enp0s3
+10.0.2.0 0.0.0.0 255.255.255.0 U 0 0 0 enp0s3
+10.0.2.2 0.0.0.0 255.255.255.255 UH 100 0 0 enp0s3
+192.168.56.0 0.0.0.0 255.255.255.0 U 0 0 0 enp0s8
+
For the display above, we learn that the Host-only network 192.168.56.0/24
does not have internet access by itself (even though we can access it using SSH from the host machine). Internet access is through the NAT network 10.0.2.0/24
, with the gateway being 10.0.2.2
(provided by VirtualBox).
+Now we can SSH into free5gc VM using 192.168.56.101
:
+
ssh 192.168.56.101 -l ubuntu
+
Linux Kernel Version
+5.0.0-23-generic
or 5.4.x
version of the Linux kernel. free5gc uses the gtp5g kernel module, which has been tested and compiled against that kernel versions only. If you installed Ubuntu 20.04, the version looks like 5.4.x. To determine the version of the Linux kernel you are using: $ uname -r
+ 5.4.0-65-generic
+
You will not be able to run most of the tests in Test section unless you deploy a UPF.
+Golang Version
+ go version
+
# this assumes your current version of Go is in the default location
+ sudo rm -rf /usr/local/go
+ wget https://dl.google.com/go/go1.17.8.linux-amd64.tar.gz
+ sudo tar -C /usr/local -zxvf go1.17.8.linux-amd64.tar.gz
+
wget https://dl.google.com/go/go1.17.8.linux-amd64.tar.gz
+ sudo tar -C /usr/local -zxvf go1.17.8.linux-amd64.tar.gz
+ mkdir -p ~/go/{bin,pkg,src}
+ # The following assume that your shell is bash
+ echo 'export GOPATH=$HOME/go' >> ~/.bashrc
+ echo 'export GOROOT=/usr/local/go' >> ~/.bashrc
+ echo 'export PATH=$PATH:$GOPATH/bin:$GOROOT/bin' >> ~/.bashrc
+ echo 'export GO111MODULE=auto' >> ~/.bashrc
+ source ~/.bashrc
+
golang
are available at the official golang site.Control-plane Supporting Packages
+sudo apt -y update
+sudo apt -y install mongodb wget git
+sudo systemctl start mongodb
+
WARNING: MongoDB 5.0+ requires a CPU with AVX support. Or downgrade your MongoDB to 4.4
+see https://www.mongodb.com/community/forums/t/mongodb-5-0-cpu-intel-g4650-compatibility/116610/2
+see also docker-library/mongo#485 (comment)
+User-plane Supporting Packages
+sudo apt -y update
+sudo apt -y install git gcc g++ cmake autoconf libtool pkg-config libmnl-dev libyaml-dev
+
sudo sysctl -w net.ipv4.ip_forward=1
+sudo iptables -t nat -A POSTROUTING -o <dn_interface> -j MASQUERADE
+sudo iptables -A FORWARD -p tcp -m tcp --tcp-flags SYN,RST SYN -j TCPMSS --set-mss 1400
+sudo systemctl stop ufw
+
Clone the free5GC repository
+ cd ~
+ git clone --recursive -b v3.3.0 -j `nproc` https://github.com/free5gc/free5gc.git
+ cd free5gc
+
cd ~/free5gc
+ git checkout main
+ git submodule sync
+ git submodule update --init --jobs `nproc`
+ git submodule foreach git checkout main
+ git submodule foreach git pull --jobs `nproc`
+
Compile network function services in free5gc
cd ~/free5gc
+ make amf
+
cd ~/free5gc
+ make
+
5.0.0-23-generic
or 5.4.x
. To verify your version:uname -r
+
git
and build itgit clone -b v0.8.1 https://github.com/free5gc/gtp5g.git
+cd gtp5g
+make
+sudo make install
+
Build the UPF (you may skip this step if you built all network functions above):
+to build using make:
+cd ~/free5gc
+make upf
+
run.sh
is free5gc/config/upfcfg.yaml
.sudo apt remove cmdtest
+sudo apt remove yarn
+curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
+echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
+curl -sL https://deb.nodesource.com/setup_12.x | sudo -E bash -
+sudo apt-get update
+sudo apt-get install -y nodejs yarn
+
Build WebConsole
+to build using make:
+cd ~/free5gc
+make webconsole
+
cd ~/free5gc/webconsole/frontend
+yarn install
+yarn build
+rm -rf ../public
+cp -R build ../public
+cd ..
+go build -o bin/webconsole server.go
+
Note: 2GB or more of OS memory is recommended. WebConsole may be failed to build if memory is less then 1GB.
+ + + + + + +Start a Wireshark capture on any core-connected interface, applying the filter 'pfcp||icmp||gtp'
.
In order to run the tests, first do this:
+cd ~/free5gc
+make upf
+chmod +x ./test.sh
+
The tests are all run from within ~/free5gc
.
a. TestRegistration
+./test.sh TestRegistration
+
b. TestGUTIRegistration
+./test.sh TestGUTIRegistration
+
c. TestServiceRequest
+./test.sh TestServiceRequest
+
d. TestXnHandover
+./test.sh TestXnHandover
+
e. TestDeregistration
+./test.sh TestDeregistration
+
f. TestPDUSessionReleaseRequest
+./test.sh TestPDUSessionReleaseRequest
+
g. TestPaging
+./test.sh TestPaging
+
h. TestN2Handover
+./test.sh TestN2Handover
+
i. TestNon3GPP
+./test.sh TestNon3GPP
+
j. TestReSynchronization
+./test.sh TestReSynchronization
+
k. TestULCL
+./test_ulcl.sh TestRequestTwoPDUSessions
+
In this demo we will practice:
+Repeat the steps of cloning free5gc
VM from the base VM, create a new VM for the UERANSIM simulator:
ueransim
, and create new MAC addresses for all network cards.ueransim
.192.168.56.102
.192.168.56.101
from the ueransim VM, and also ping 192.168.56.102
from the free5gc VM.Search “ueransim” on the web, and get the web site.
+On the web site, review what the UERANSIM open-source project is about, then browse into the installation page.
To download UERANSIM:
+
cd ~
+git clone https://github.com/aligungr/UERANSIM
+cd UERANSIM
+git checkout 3a96298
+
Update and upgrade ueransim VM first:
+
sudo apt update
+sudo apt upgrade
+
Install required tools:
+
sudo apt install make
+sudo apt install g++
+sudo apt install libsctp-dev lksctp-tools
+sudo apt install iproute2
+sudo snap install cmake --classic
+
Build UERANSIM:
+
cd ~/UERANSIM
+make
+
free5GC provides a simple web tool WebConsole to help creating and managing UE registrations to be used by various 5G network functions (NF). To build WebConsole we need Node.js and Yarn.
+First SSH into free5gc (192.168.56.101
),and remove obsolete tools that may exists:
+
sudo apt remove cmdtest
+sudo apt remove yarn
+
Then install Node.js
and Yarn
:
+
curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
+echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
+sudo apt-get update
+sudo apt-get install -y nodejs yarn
+
To build WebConsole:
+
cd ~/free5gc
+make webconsole
+
First start up the WebConsole server:
+
cd ~/free5gc/webconsole
+go run server.go
+
The screen shows the port number :5000
at the end. Open your web browser from your host machine, and enter the URL http://192.168.56.101:5000
admin
and password free5gc
.Subscribers
and create a new data:Ctrl-C
on the terminal to quit WebConsole.In free5gc VM, we need to edit three files:
+~/free5gc/config/amfcfg.yaml
~/free5gc/config/smfcfg.yaml
~/free5gc/config/upfcfg.yaml
First SSH into free5gc VM, and change ~/free5gc/config/amfcfg.yaml
:
+
cd ~/free5gc
+nano config/amfcfg.yaml
+
Replace ngapIpList IP from 127.0.0.1
to 192.168.56.101
, namely from:
+
...
+ ngapIpList: # the IP list of N2 interfaces on this AMF
+ - 127.0.0.1
+
...
+ ngapIpList: # the IP list of N2 interfaces on this AMF
+ - 192.168.56.101 # 127.0.0.1
+
Next edit ~/free5gc/config/smfcfg.yaml
:
+
nano config/smfcfg.yaml
+
inside userplane_information / up_nodes / UPF / interfaces / endpoints
, change the IP from 127.0.0.8
to 192.168.56.101
, namely from:...
+ interfaces: # Interface list for this UPF
+ - interfaceType: N3 # the type of the interface (N3 or N9)
+ endpoints: # the IP address of this N3/N9 interface on this UPF
+ - 127.0.0.8
+
...
+ interfaces: # Interface list for this UPF
+ - interfaceType: N3 # the type of the interface (N3 or N9)
+ endpoints: # the IP address of this N3/N9 interface on this UPF
+ - 192.168.56.101 # 127.0.0.8
+
~/free5gc/config/upfcfg.yaml
,and chage gtpu IP from 127.0.0.8
into 192.168.56.101
, namely from:...
+ gtpu:
+ forwarder: gtp5g
+ # The IP list of the N3/N9 interfaces on this UPF
+ # If there are multiple connection, set addr to 0.0.0.0 or list all the addresses
+ ifList:
+ - addr: 127.0.0.8
+ type: N3
+
...
+ gtpu:
+ forwarder: gtp5g
+ # The IP list of the N3/N9 interfaces on this UPF
+ # If there are multiple connection, set addr to 0.0.0.0 or list all the addresses
+ ifList:
+ - addr: 192.168.56.101 # 127.0.0.8
+ type: N3
+
In the ueransim VM, there are two files related to free5GC:
+~/UERANSIM/config/free5gc-gnb.yaml
~/UERANSIM/config/free5gc-ue.yaml
The second file is for UE, which we don’t have to change if the data inside is consistent with the (default) registration data we set using WebConsole previously.
+First SSH into ueransim, and edit the file ~/UERANSIM/config/free5gc-gnb.yaml
, and change the ngapIp IP, as well as the gtpIp IP, from 127.0.0.1
to 192.168.56.102
,and also change the IP in amfConfigs into 192.168.56.101
, that is, from:
+
...
+ ngapIp: 127.0.0.1 # gNB's local IP address for N2 Interface (Usually same with local IP)
+ gtpIp: 127.0.0.1 # gNB's local IP address for N3 Interface (Usually same with local IP)
+
+ # List of AMF address information
+ amfConfigs:
+ - address: 127.0.0.1
+
...
+ ngapIp: 192.168.56.102 # 127.0.0.1 # gNB's local IP address for N2 Interface (Usually same with local IP)
+ gtpIp: 192.168.56.102 # 127.0.0.1 # gNB's local IP address for N3 Interface (Usually same with local IP)
+
+ # List of AMF address information
+ amfConfigs:
+ - address: 192.168.56.101 # 127.0.0.1
+
~/UERANSIM/config/free5gc-ue.yaml
,and see if the settings is consistent with those in free5GC (via WebConsole), for example:# IMSI number of the UE. IMSI = [MCC|MNC|MSISDN] (In total 15 or 16 digits)
+supi: 'imsi-208930000000003'
+# Mobile Country Code value
+mcc: '208'
+# Mobile Network Code value (2 or 3 digits)
+mnc: '93'
+
+# Permanent subscription key
+key: '8baf473f2f8fd09487cccbd7097c6862'
+# Operator code (OP or OPC) of the UE
+op: '8e27b6af0e692e750f32667a3b14605d'
+# This value specifies the OP type and it can be either 'OP' or 'OPC'
+opType: 'OP'
+
+...
+
+# Initial PDU sessions to be established
+sessions:
+ - type: 'IPv4'
+ apn: 'internet'
+ slice:
+ sst: 0x01
+ sd: 0x010203
+
+# List of requested S-NSSAIs by this UE
+slices:
+ - sst: 0x01
+ sd: 0x010203
+
+...
+
SSH into free5gc. If you have rebooted free5gc, remember to do:
+
sudo sysctl -w net.ipv4.ip_forward=1
+sudo iptables -t nat -A POSTROUTING -o enp0s3 -j MASQUERADE
+sudo systemctl stop ufw
+
In addition, execute the following command:
+
sudo iptables -I FORWARD 1 -j ACCEPT
+
Also, make sure you have make proper changes to the free5GC configuration files, then run ./run.sh
:
+
cd ~/free5gc
+./run.sh
+
At this time free5GC has been started.
+Next, prepare three additional SSH terminals from your host machine (if you know how to use tmux
, you can use just one).
In terminal 1: SSH into ueransim, make sure UERANSIM is built, and configuration files have been changed correctly, then execute nr-gnb
:
+
cd ~/UERANSIM
+build/nr-gnb -c config/free5gc-gnb.yaml
+
In terminal 2, SSH into ueransim, and execute nr-ue
with admin right:
+
cd ~/UERANSIM
+sudo build/nr-ue -c config/free5gc-ue.yaml # for multiple-UEs, use -n and -t for number and delay
+
In terminal 3, SSH into ueransim, and ping 192.168.56.101
to see free5gc is alive. Then, use ifconfig to see if the tunnel uesimtun0
has been created (by nr-ue):
+
$ ifconfig
+
+enp0s3: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
+ inet 10.0.2.15 netmask 255.255.255.0 broadcast 10.0.2.255
+ inet6 fe80::a00:27ff:fe65:1472 prefixlen 64 scopeid 0x20<link>
+ ether 08:00:27:65:14:72 txqueuelen 1000 (Ethernet)
+ RX packets 80 bytes 32423 (32.4 KB)
+ RX errors 0 dropped 0 overruns 0 frame 0
+ TX packets 90 bytes 12860 (12.8 KB)
+ TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
+
+enp0s8: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
+ inet 192.168.56.102 netmask 255.255.255.0 broadcast 192.168.56.255
+ inet6 fe80::a00:27ff:fe5e:be64 prefixlen 64 scopeid 0x20<link>
+ ether 08:00:27:5e:be:64 txqueuelen 1000 (Ethernet)
+ RX packets 1515 bytes 130490 (130.4 KB)
+ RX errors 0 dropped 0 overruns 0 frame 0
+ TX packets 1010 bytes 206670 (206.6 KB)
+ TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
+
+lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
+ inet 127.0.0.1 netmask 255.0.0.0
+ inet6 ::1 prefixlen 128 scopeid 0x10<host>
+ loop txqueuelen 1000 (Local Loopback)
+ RX packets 3445 bytes 174416 (174.4 KB)
+ RX errors 0 dropped 0 overruns 0 frame 0
+ TX packets 3445 bytes 174416 (174.4 KB)
+ TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
+
+uesimtun0: flags=4305<UP,POINTOPOINT,RUNNING,NOARP,MULTICAST> mtu 1500
+ inet 60.60.0.1 netmask 255.255.255.255 destination 60.60.0.1
+ inet6 fe80::2034:d00:a76:84b7 prefixlen 64 scopeid 0x20<link>
+ unspec 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00 txqueuelen 500 (UNSPEC)
+ RX packets 3 bytes 252 (252.0 B)
+ RX errors 0 dropped 0 overruns 0 frame 0
+ TX packets 13 bytes 732 (732.0 B)
+ TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
+
Now use ping
:
+
ping -I uesimtun0 google.com
+
ping
gets replies, then free5GC is running properly. Congratulations!
+
+
+
+
+
+
+ In this demo we will use free5GC together with UERANSIM to exercise on some simple network applications:
+ping
+ tcpdump
wget
and curl
First start up free5GC and ueransim VMs. This requires one SSH terminal for free5gc, and two for ueransim.
+Open another SSH terminal and log in into ueransim:
+
ssh 192.168.56.102 -l ubuntu
+
ifconfig
to check if uesimtun0
tunnel has been created, and use ping to check if we can ping
through it:$ ping google.com
+PING google.com (172.217.27.142) 56(84) bytes of data.
+64 bytes from tsa03s02-in-f14.1e100.net (172.217.27.142): icmp_seq=1 ttl=63 time=3.98 ms
+64 bytes from tsa03s02-in-f14.1e100.net (172.217.27.142): icmp_seq=2 ttl=63 time=3.87 ms
+64 bytes from tsa03s02-in-f14.1e100.net (172.217.27.142): icmp_seq=3 ttl=63 time=4.06 ms
+^C
+--- google.com ping statistics ---
+3 packets transmitted, 3 received, 0% packet loss, time 2003ms
+rtt min/avg/max/mdev = 3.872/3.970/4.060/0.076 ms
+
$ ping -I uesimtun0 google.com
+PING google.com (172.217.27.142) from 60.60.0.1 uesimtun0: 56(84) bytes of data.
+64 bytes from tsa03s02-in-f14.1e100.net (172.217.27.142): icmp_seq=1 ttl=61 time=5.85 ms
+64 bytes from tsa03s02-in-f14.1e100.net (172.217.27.142): icmp_seq=2 ttl=61 time=4.87 ms
+64 bytes from tsa03s02-in-f14.1e100.net (172.217.27.142): icmp_seq=3 ttl=61 time=4.76 ms
+^C
+--- google.com ping statistics ---
+3 packets transmitted, 3 received, 0% packet loss, time 2004ms
+rtt min/avg/max/mdev = 4.760/5.160/5.847/0.487 ms
+
Also use route -n
to observe if current routing table shows some routing rules regarding the two network interfaces enp0s3
and enp0s8
:
+
$ route -n
+Kernel IP routing table
+Destination Gateway Genmask Flags Metric Ref Use Iface
+0.0.0.0 10.0.2.2 0.0.0.0 UG 100 0 0 enp0s3
+10.0.2.0 0.0.0.0 255.255.255.0 U 0 0 0 enp0s3
+10.0.2.2 0.0.0.0 255.255.255.255 UH 100 0 0 enp0s3
+192.168.56.0 0.0.0.0 255.255.255.0 U 0 0 0 enp0s8
+
The network 10.0.2.0/24
and its enp0s3
interface are related to VirtualBox NAT network card. We can bring down this interface:
+
$ sudo ifconfig enp0s3 down
+$ route -n
+Kernel IP routing table
+Destination Gateway Genmask Flags Metric Ref Use Iface
+192.168.56.0 0.0.0.0 255.255.255.0 U 0 0 0 enp0s8
+
192.168.56.0/24
left. Run ping
again:$ ping 8.8.8.8
+ping: connect: Network is unreachable
+
And see that it can not ping through, but runing:
+
$ ping -I uesimtun0 8.8.8.8
+PING 8.8.8.8 (8.8.8.8) from 60.60.0.1 uesimtun0: 56(84) bytes of data.
+64 bytes from 8.8.8.8: icmp_seq=1 ttl=61 time=7.17 ms
+64 bytes from 8.8.8.8: icmp_seq=2 ttl=61 time=5.41 ms
+64 bytes from 8.8.8.8: icmp_seq=3 ttl=61 time=5.15 ms
+^C
+--- 8.8.8.8 ping statistics ---
+3 packets transmitted, 3 received, 0% packet loss, time 2005ms
+rtt min/avg/max/mdev = 5.150/5.907/7.165/0.895 ms
+
shows some responses, since we ask ping
to go through the free5GC core network. To make ping 8.8.8.8
in addition to ping -I uesimtun0 8.8.8.8
work, we can set the uesimtun0
interface (IP 60.60.0.1
) as the new default gateway:
+
$ sudo ip r add default dev uesimtun0
+$ route -n
+Kernel IP routing table
+Destination Gateway Genmask Flags Metric Ref Use Iface
+0.0.0.0 0.0.0.0 0.0.0.0 U 0 0 0 uesimtun0
+192.168.56.0 0.0.0.0 255.255.255.0 U 0 0 0 enp0s8
+
192.168.56.0/24
network will go to uesimtun0
, and ping 8.8.8.8
works this time:$ ping 8.8.8.8
+PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
+64 bytes from 8.8.8.8: icmp_seq=1 ttl=61 time=5.02 ms
+64 bytes from 8.8.8.8: icmp_seq=2 ttl=61 time=6.31 ms
+64 bytes from 8.8.8.8: icmp_seq=3 ttl=61 time=5.41 ms
+^C
+--- 8.8.8.8 ping statistics ---
+3 packets transmitted, 3 received, 0% packet loss, time 2004ms
+rtt min/avg/max/mdev = 5.017/5.581/6.312/0.541 ms
+...
+
Note that normally we are using ueransim to simulate “terminal” UE device, not as a network device or proxy, therefore the above two routing rules suffice.
+Now if we still want to run:
+
$ ping google.com
+ping: google.com: Temporary failure in name resolution
+
we will get unresolved domain name. To solve this, we can modify the file /etc/resolv.conf
:
+
sudo nano /etc/resolv.conf
+
and change the nameserver IP to 8.8.8.8
:
+
nameserver 8.8.8.8
+
After the change, we can see ping
getting responses:
+
$ ping google.com
+PING google.com (216.58.200.46) 56(84) bytes of data.
+64 bytes from tsa01s08-in-f46.1e100.net (216.58.200.46): icmp_seq=1 ttl=61 time=5.19 ms
+64 bytes from tsa01s08-in-f46.1e100.net (216.58.200.46): icmp_seq=2 ttl=61 time=50.4 ms
+64 bytes from tsa01s08-in-f46.1e100.net (216.58.200.46): icmp_seq=3 ttl=61 time=5.66 ms
+^C
+--- google.com ping statistics ---
+3 packets transmitted, 3 received, 0% packet loss, time 2004ms
+rtt min/avg/max/mdev = 5.191/20.423/50.414/21.207 ms
+
We can also examine the network traffic happening underneath in the scenario above. First we open another SSH terminal into ueransim, and run the following command:
+
$ sudo tcpdump -n -i any host 60.60.0.1 or 192.168.56.101
+tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
+listening on any, link-type LINUX_SLL (Linux cooked v1), capture size 262144 bytes
+
then run ping 8.8.8.8
again, wait for a couple seconds, then Ctrl-C
to exit. We see the data packets actually going in and out uesimtun0
.
+
$ sudo tcpdump -n -i any host 60.60.0.1 or 192.168.56.101
+tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
+listening on any, link-type LINUX_SLL (Linux cooked v1), capture size 262144 bytes
+10:24:56.138729 IP 192.168.56.101.38412 > 192.168.56.102.38740: sctp (1) [HB REQ]
+10:24:56.138783 IP 192.168.56.102.38740 > 192.168.56.101.38412: sctp (1) [HB ACK]
+10:24:58.456532 IP 60.60.0.1 > 8.8.8.8: ICMP echo request, id 33, seq 1, length 64
+10:24:58.457416 IP 192.168.56.102.2152 > 192.168.56.101.2152: UDP, length 100
+10:24:58.462136 IP 192.168.56.101.2152 > 192.168.56.102.2152: UDP, length 92
+10:24:58.462324 IP 8.8.8.8 > 60.60.0.1: ICMP echo reply, id 33, seq 1, length 64
+10:24:59.458823 IP 60.60.0.1 > 8.8.8.8: ICMP echo request, id 33, seq 2, length 64
+10:24:59.459031 IP 192.168.56.102.2152 > 192.168.56.101.2152: UDP, length 100
+10:24:59.464214 IP 192.168.56.101.2152 > 192.168.56.102.2152: UDP, length 92
+10:24:59.464396 IP 8.8.8.8 > 60.60.0.1: ICMP echo reply, id 33, seq 2, length 64
+10:25:00.461293 IP 60.60.0.1 > 8.8.8.8: ICMP echo request, id 33, seq 3, length 64
+10:25:00.462178 IP 192.168.56.102.2152 > 192.168.56.101.2152: UDP, length 100
+10:25:00.474941 IP 192.168.56.101.2152 > 192.168.56.102.2152: UDP, length 92
+10:25:00.475561 IP 8.8.8.8 > 60.60.0.1: ICMP echo reply, id 33, seq 3, length 64
+10:25:01.463946 IP 60.60.0.1 > 8.8.8.8: ICMP echo request, id 33, seq 4, length 64
+10:25:01.464523 IP 192.168.56.102.2152 > 192.168.56.101.2152: UDP, length 100
+10:25:01.469297 IP 192.168.56.101.2152 > 192.168.56.102.2152: UDP, length 92
+10:25:01.470314 IP 8.8.8.8 > 60.60.0.1: ICMP echo reply, id 33, seq 4, length 64
+
Simply look for any web page for file download on the web. For example, if we choose Golang web site as an example, we may find the URL:
+
https://golang.org/dl/go1.15.8.darwin-amd64.pkg
+
wget https://golang.org/dl/go1.15.8.darwin-amd64.pkg
+
ssh bbsu@ptt.cc
)You can actually use SSH in the ueransim VM to access remote site. For example, you can SSH to a well-known terminal-based BBS site in Taiwan:
+
ssh bbsu@ppt.cc
+
You can also use Youtube as an example app. To achieve this goal, you can install a desktop VM with graphical UI, such as Ubuntu Desktop, and follow the same procedure to install and start up UERANSIM, then access Youtube through uesimtun0
and free5GC.
To reduce resource consumption on your host machine, you may install Lubuntu (at https://lubuntu.me), a more light-weight Ubuntu desktop distro instead. But since viewing free5GC YouTube Channel requires quite sime CPU consumption, you may have to set at least 2 CPUs and 2048 MB memory for the VM.
+Refer to videos Access Youtube on Lubuntu (1, 2, 3, 4 and 5).
+ + + + + + +cd webconsole
+go run server.go
+
URL: http://localhost:5000
+Username: admin
+Password: free5gc
+
Note: You can add the subscribers here too
+Please refer to free5gmano
+Please refer to free5GC/IPTV
+The below commands may be helpful for development purposes.
+ls /dev/mqueue/
rm /dev/mqueue/*
cd ./src/upf/lib/libgtp5gnl/tools
./gtp5g-tunnel list pdr
./gtp5g-tunnel list far
cd ./src/upf/lib/libgtp5gnl/tools
sudo ./gtp5g-link del {Dev-Name}
uname -r
5.0.0-23-generic
for examplesudo apt search 'linux-image-5.0.0-23-generic'
+sudo apt install 'linux-image-5.0.0-23-generic'
+sudo apt install 'linux-headers-5.0.0-23-generic'
+
sudo update-initramfs -u -k all
+sudo update-grub
+
5.0.0-23-generic
sudo reboot
+
sudo apt remove 'linux-image-5.0.0-23-generic'
+sudo apt remove 'linux-headers-5.0.0-23-generic'
+
Install packages:
+
sudo apt-get install pcscd pcsc-tools libccid python-dev swig python-setuptools python-pip libpcsclite-dev
+sudo pip install pycrypto
+
Download PySIM
+
git clone git://git.osmocom.org/pysim.git
+
Change to pyscard folder and install
+
cd <pyscard-path>
+sudo /usr/bin/python setup.py build_ext install
+
Verify your reader is ready
+sudo pcsc_scan
+
Check whether your reader can read the SIM card
+
cd <pysim-path>
+./pySim-read.py –p 0
+
Program your SIM card information
+
./pySim-prog.py -p 0 -x 208 -y 93 -t sysmoUSIM-SJS1 -i 208930000000003 --op=8e27b6af0e692e750f32667a3b14605d -k 8baf473f2f8fd09487cccbd7097c6862 -s 8988211000000088313 -a 23605945
+
You can get your SIM card from sysmocom. You also need a card reader to write your SIM card. You can get a card reader from here or use other similar devices.
+ + + + + + +There are registerIP and bindingIP design on every NF's sbi interface.
+ +This is due to some orchestration, such as Kubernets or OpenStack, has the design of service IP mapping.
+ +Use Kubernets as an example. K8S has the service type that enable users to define the service IP outside the pod. But the service IP may be different from the IP assigned inside the pod. Therefore, if we register the binding IP inside the pod to NRF, NRF cannot know which service IP outside the pod has attached. As the result, we need to separate registerIP from bindingIP in this scenario.
+If you are not sure what IP you should set, just configure it as the same IP address.
+We provide a sample config to connect to outer ran under /sample/ran_attach_config/
. The architecture is as following.
As the result, user's RAN IP must set to 192.168.0.0/24 subnet or let the routing route to this subnet.
+Notice: If user wants to use the setting, aware to set 192.168.0.1 to your host as well.
+smfcfg.yaml
uerouting.yaml
For more detail of SMF config, please refer to here.
+ + + + + + +free5gc has been tested against the following environment:
+The listed kernel version is required for the UPF element.
+Minimum Hardware
+Recommended Hardware
+This guide assumes that you will run all 5GC elements on a single machine.
+ + + + + + +If another version of free5GC was ran before, you have to delete MongoDB.
+$ mongo --eval "db.dropDatabase()" free5gc
+
+$ cd ~/free5gc/webconsole
+$ ./bin/webconsole
+
Enter
+
Username: admin
+Password: free5gc
+
There are some issues for subscriber modification.
+If you want to modify the existed subscriber, please Delete
it first and New
again for now.
This document explains the detail of SMF config.
+Also provide some examples about conversion between config file and real userplane topology
ULCL limitation:
+The branching UPF now can't connect to the Internet.
+It only serves as a Intranet in the UPF topology.
+(Please refers to the topology of example 2)
Field | +meaning | +
---|---|
scheme | +The protocol for SBI | +
registerIPv4 | +IP used to register to NRF | +
bindingIPv4 | +IP used to bind the service | +
port | +SMF bind the SBI service to this port | +
Field | +meaning | +
---|---|
addr | +The IP address of N4 interface on the SMF (PFCP) | +
Field | +meaning | +
---|---|
userplane_information | +Includes topology and information of RAN and UPFs which are controlled by this SMF | +
up_nodes | +The node in the user plane topology. Includes gNodeB, I-UPF and A-UPF | +
links | +The edge in the user plane topology | +
type | +Indicate it is RAN or specific kind of UPF | +
node_id | +The PFCP IPv4 address for UPF | +
Note:
+up_resource_ip serves as default user plane IP for the UPF.
+In this version, UPF will determine its user plane IP by itself.
+So setting up_resource_ip in SMF config won't affect real config in user plane.
To understand whole PDU session config, we must take a step forward to understand the AMF config.
+Field | +meaning | +
---|---|
NGAPIPList | +The IP list of N2 interfaces on the AMF | +
SBI | +Same meaning with SMF/SBI. | +
ERROR: [SCTP] Failed to connect given AMF N3IWF=NGAP
This error occured when N3IWF was started before AMF finishing initialization. This error usually appears when you run the TestNon3GPP in the first time.
+Rerun the test should be fine. If it still not be solved, larger the sleeping time in line 110 of test.sh
.
TestNon3GPP will modify the config/amfcfg.conf
. So, if you had killed the TestNon3GPP test before it finished, you might need to copy config/amfcfg.conf.bak
back to config/amfcfg.conf
to let other tests pass.
cp config/amfcfg.conf.bak config/amfcfg.conf
If you meet any problems about https or mogodb, it maybe couse our new version from v3.0.1 to v3.0.2 has change http to H2C verion. Try the command below.
+mongo --eval "db.NfProfile.drop()" free5gc
MQCreate() Error creating message queue: Too many open files UPF=Util
(UPF)Remove POSIX message queues
+ls /dev/mqueue/
+rm /dev/mqueue/*
+
cd lib/libgtp5gnl/tools
+sudo ./gtp5g-link del {Dev-Name}
+
UPF Cli Run Error: open Gtp5g: open link: create: file exists
sudo ip link del upfgtp
+
Run Network Function
+Check has XXFsslkey.log
+Edit >> Preference >> Protocols >> SSL (TLS)
+ +Add keylog
+ +Filter http2
+ +The similar reason as NEA0 NAS message. Althrough H2C is clear text, wirshark still considers these packets as the normal TCP packets and does not decode them by HTTP2.
+To see the details of H2C packets, do the following configuration.
+Analyze → Decode As…
+ +click Add button to add the decode rules
+ +Decode the packets from the TCP ports listened by each NF as HTTP2 packets.
+Some 5G UE and gNodeB hardware have been tested with free5GC by partners or community members:
+5G UE (Support 5G SA):
+gNodeB:
+Reports of tested hardware not listed above on Github issue or free5GC forum are welcome.
+PS: if you don't have any hardware available, we suggest to use UERANSIM to simulate.
+(Refer to Advanced environment setup section)
+ + + + + + +Here are the features on the roadmap. These items are planned to be supported in the near future:
+For people who are not familiar with virtual machines and Linux installation, here are some example demonstrations:
+For Container deployment:
+The free5GC is an open-source project for 5th generation (5G) mobile core networks. The ultimate goal of this project is to implement the 5G core network (5GC) defined in 3GPP Release 15 (R15) and beyond.
+Currently, the major contributors are from National Yang Ming Chiao Tung University (NYCU). Please refer to our roadmap for the features of each release.
+Note
+Thank you very much for your interest in free5GC. The license of free5GC follows Apache 2.0. That is, anyone can use free5GC for commercial purposes for free.
+free5GC is a nonprofit organization dedicated to developing innovative and next-generation features for open-source code of the 5G Core (5GC) Network under Apache 2.0 license. Your generous support and sponsorship will sustain our technology development and the operation of the community. Your company/organization logo will be displayed on the free5GC website and listed as the sponsorship you participate. Here are the sponsorship tiers we offer.
+Your generosity is appreciated. You can now support free5GC by using your credit cards. Please visit https://fund.nycu.edu.tw/plans/HYnZMprdAGx for donation.
+Tips
+The free5GC is an open-source project for 5th generation (5G) mobile core networks. The ultimate goal of this project is to implement the 5G core network (5GC) defined in 3GPP Release 15 (R15) and beyond.
Currently, the major contributors are from National Yang Ming Chiao Tung University (NYCU). Please refer to our roadmap for the features of each release.
Note
Thank you very much for your interest in free5GC. The license of free5GC follows Apache 2.0. That is, anyone can use free5GC for commercial purposes for free.
"},{"location":"#sponsors","title":"Sponsors","text":""},{"location":"#platinum","title":"Platinum","text":""},{"location":"#gold","title":"Gold","text":""},{"location":"#silver","title":"Silver","text":""},{"location":"#hardware-sponsors","title":"Hardware Sponsors","text":""},{"location":"publication/","title":"Relavent Publication","text":""},{"location":"publication/#publications-using-free5gc","title":"Publications using free5GC","text":""},{"location":"publication/#from-nycu","title":"From NYCU","text":"Akraino Blueprints: Integrated Cloud Native Private Wireless, The Linux Foundation, October 11, 2021
SD Core Techinar July 7 2021, Open Networking Foundation, July 13, 2021
Aarna Networks MWC 2021 Demo, Aarna Networks Channel, June 27, 2021
OpenStack Tacker Demo, Open Infrastructure Foundation, April 26, 2021
OpenNess Tungsten Fabric free5GC demo, Aarna Networks Channel, February 16, 2021
5G Core on Diamanti, Diamanti, Inc., February 3, 2021
free5GC (5G Core) Orchestration on Kubernetes with Tungsten Fabric CNI and Testing, Aarna Networks Channel, December 2, 2020
IoT LoRa (sensors and gateway in hardware), RAN in hardware (SDR) and software, and the free5GC, LABORA Research Group, July 3, 2020
UE and eNodeB in Hardware (conventional cell phone + SDR) and free5GC: a pratical approach in 5G, LABORA Research Group, July 3, 2020
OpenAirInterface and free5GC: a pratical approach in 5G networks, LABORA Research Group, June 29, 2020
Note
Author: Jimmy Chang Date: 2023/7/5
"},{"location":"blog/1-free5gc-with-namespace/#overview","title":"Overview","text":"This technique leverages namespace to run UERANSIM, an opensource 5G-UE and RAN(gNodeB) simulator, and connect to free5GC. UERANSIM follows the 3GPP specification for developing and can support multiple 5G core (5GC) including free5GC.
Why are we using namespace? Well, you can follow ULCL and free5GC compose to set up the environment with VM and docker, but there are limitations for hardware\u2019s capability. With network namespace, you can have different and separate network instances of network interfaces and routing tables that operate independently.
So, what is network namespace? Network namespace makes a copy of network stack with its own routing table, firewall and devices. A named network namespace is an object at /var/run/netns/
. The file descriptor resulting from opening /var/run/netns/
refers to the specified network namespace. Holding that file descriptor open keeps the network namespace alive.
And how to make both namespaces communicating? A virtual Ethernet device (veth) pair provides the abstraction that can be used to create tunnels between network namespaces, and can be used to create bridge to a physical network device in another namespace. Veth pair also be used as standalone network devices. When the namespace freed, veth device which attatch to would be destroyed.
The environment is as follow. Suppose you have already installed as well as set up free5GC and UERANSIM properly.
Note
Namespace free5GC represents host network namespace. And enp0s5 is an ethernet interface connectting to external.
Each devices as follow\n| Device | IP |\n| ------------- | ------------- |\n| veth0 | 10.200.200.1 |\n| veth1 | 10.200.200.2 |\n| br-veth0 | none |\n| br-veth1 | none |\n| enp0s5 | 10.211.55.23 |\n\n\nUE information in UERANSIM as follow. Already \n| IMSI | DNN |\n| ---------------- | ------------- |\n| 208930000000003 | internet |\n
"},{"location":"blog/1-free5gc-with-namespace/#configuration-file-of-free5gc-and-ueransim","title":"Configuration file of free5GC and UERANSIM","text":""},{"location":"blog/1-free5gc-with-namespace/#free5gc","title":"free5GC","text":"Replace ngapIpList IP from 127.0.0.18
to 10.200.200.2
:
info:\nversion: 1.0.9\ndescription: AMF initial local configuration\n\nconfiguration:\namfName: AMF # the name of this AMF\nngapIpList: # the IP list of N2 interfaces on this AMF\n- 10.200.200.2 # 127.0.0.18\nngapPort: 38412 # the SCTP port listened by NGAP\nsbi: # Service-based interface information\nscheme: http # the protocol for sbi (http or https)\nregisterIPv4: 127.0.0.18 # IP used to register to NRF\nbindingIPv4: 127.0.0.18 # IP used to bind the service\nport: 8000 # port used to bind the service\ntls: # the local path of TLS key\npem: cert/amf.pem # AMF TLS Certificate\nkey: cert/amf.key # AMF TLS Private key\nserviceNameList: # the SBI services provided by this AMF, refer to TS 29.518\n- namf-comm # Namf_Communication service\n- namf-evts # Namf_EventExposure service\n- namf-mt # Namf_MT service\n- namf-loc # Namf_Location service\n- namf-oam # OAM service\nservedGuamiList: # Guami (Globally Unique AMF ID) list supported by this AMF\n# <GUAMI> = <MCC><MNC><AMF ID>\n- plmnId: # Public Land Mobile Network ID, <PLMN ID> = <MCC><MNC>\nmcc: 208 # Mobile Country Code (3 digits string, digit: 0~9)\nmnc: 93 # Mobile Network Code (2 or 3 digits string, digit: 0~9)\namfId: cafe00 # AMF identifier (3 bytes hex string, range: 000000~FFFFFF)\nsupportTaiList: # the TAI (Tracking Area Identifier) list supported by this AMF\n- plmnId: # Public Land Mobile Network ID, <PLMN ID> = <MCC><MNC>\nmcc: 208 # Mobile Country Code (3 digits string, digit: 0~9)\nmnc: 93 # Mobile Network Code (2 or 3 digits string, digit: 0~9)\ntac: 000001 # Tracking Area Code (3 bytes hex string, range: 000000~FFFFFF)\nplmnSupportList: # the PLMNs (Public land mobile network) list supported by this AMF\n- plmnId: # Public Land Mobile Network ID, <PLMN ID> = <MCC><MNC>\nmcc: 208 # Mobile Country Code (3 digits string, digit: 0~9)\nmnc: 93 # Mobile Network Code (2 or 3 digits string, digit: 0~9)\nsnssaiList: # the S-NSSAI (Single Network Slice Selection Assistance Information) list supported by this AMF\n- sst: 1 # Slice/Service Type (uinteger, range: 0~255)\nsd: 010203 # Slice Differentiator (3 bytes hex string, range: 000000~FFFFFF)\n- sst: 1 # Slice/Service Type (uinteger, range: 0~255)\nsd: 112233 # Slice Differentiator (3 bytes hex string, range: 000000~FFFFFF)\nsupportDnnList: # the DNN (Data Network Name) list supported by this AMF\n- internet\nnrfUri: http://127.0.0.10:8000 # a valid URI of NRF\nsecurity: # NAS security parameters\nintegrityOrder: # the priority of integrity algorithms\n- NIA2\n# - NIA0\ncipheringOrder: # the priority of ciphering algorithms\n- NEA0\n# - NEA2\nnetworkName: # the name of this core network\nfull: free5GC\nshort: free\nngapIE: # Optional NGAP IEs\nmobilityRestrictionList: # Mobility Restriction List IE, refer to TS 38.413\nenable: true # append this IE in related message or not\nmaskedIMEISV: # Masked IMEISV IE, refer to TS 38.413\nenable: true # append this IE in related message or not\nredirectionVoiceFallback: # Redirection Voice Fallback IE, refer to TS 38.413\nenable: false # append this IE in related message or not\nnasIE: # Optional NAS IEs\nnetworkFeatureSupport5GS: # 5gs Network Feature Support IE, refer to TS 24.501\nenable: true # append this IE in Registration accept or not\nlength: 1 # IE content length (uinteger, range: 1~3)\nimsVoPS: 0 # IMS voice over PS session indicator (uinteger, range: 0~1)\nemc: 0 # Emergency service support indicator for 3GPP access (uinteger, range: 0~3)\nemf: 0 # Emergency service fallback indicator for 3GPP access (uinteger, range: 0~3)\niwkN26: 0 # Interworking without N26 interface indicator (uinteger, range: 0~1)\nmpsi: 0 # MPS indicator (uinteger, range: 0~1)\nemcN3: 0 # Emergency service support indicator for Non-3GPP access (uinteger, range: 0~1)\nmcsi: 0 # MCS indicator (uinteger, range: 0~1)\nt3502Value: 720 # timer value (seconds) at UE side\nt3512Value: 3600 # timer value (seconds) at UE side\nnon3gppDeregTimerValue: 3240 # timer value (seconds) at UE side\n# retransmission timer for paging message\nt3513:\nenable: true # true or false\nexpireTime: 6s # default is 6 seconds\nmaxRetryTimes: 4 # the max number of retransmission\n# retransmission timer for NAS Deregistration Request message\nt3522:\nenable: true # true or false\nexpireTime: 6s # default is 6 seconds\nmaxRetryTimes: 4 # the max number of retransmission\n# retransmission timer for NAS Registration Accept message\nt3550:\nenable: true # true or false\nexpireTime: 6s # default is 6 seconds\nmaxRetryTimes: 4 # the max number of retransmission\n# retransmission timer for NAS Authentication Request/Security Mode Command message\nt3560:\nenable: true # true or false\nexpireTime: 6s # default is 6 seconds\nmaxRetryTimes: 4 # the max number of retransmission\n# retransmission timer for NAS Notification message\nt3565:\nenable: true # true or false\nexpireTime: 6s # default is 6 seconds\nmaxRetryTimes: 4 # the max number of retransmission\n# retransmission timer for NAS Identity Request message\nt3570:\nenable: true # true or false\nexpireTime: 6s # default is 6 seconds\nmaxRetryTimes: 4 # the max number of retransmission\nlocality: area1 # Name of the location where a set of AMF, SMF, PCF and UPFs are located\nsctp: # set the sctp server setting <optinal>, once this field is set, please also add maxInputStream, maxOsStream, maxAttempts, maxInitTimeOut\nnumOstreams: 3 # the maximum out streams of each sctp connection\nmaxInstreams: 5 # the maximum in streams of each sctp connection\nmaxAttempts: 2 # the maximum attempts of each sctp connection\nmaxInitTimeout: 2 # the maximum init timeout of each sctp connection\ndefaultUECtxReq: false # the default value of UE Context Request to decide when triggering Initial Context Setup procedure\n\nlogger: # log output setting\nenable: true # true or false\nlevel: info # how detailed to output, value: trace, debug, info, warn, error, fatal, panic\nreportCaller: false # enable the caller report or not, value: true or false\n
- free5gc/config/smfcfg.yaml Replace userplaneInformation / upNodes / UPF / interfaces / endpoints from 127.0.0.8
to 10.200.200.2
:
info:\nversion: 1.0.7\ndescription: SMF initial local configuration\n\nconfiguration:\nsmfName: SMF # the name of this SMF\nsbi: # Service-based interface information\nscheme: http # the protocol for sbi (http or https)\nregisterIPv4: 127.0.0.2 # IP used to register to NRF\nbindingIPv4: 127.0.0.2 # IP used to bind the service\nport: 8000 # Port used to bind the service\ntls: # the local path of TLS key\nkey: cert/smf.key # SMF TLS Certificate\npem: cert/smf.pem # SMF TLS Private key\nserviceNameList: # the SBI services provided by this SMF, refer to TS 29.502\n- nsmf-pdusession # Nsmf_PDUSession service\n- nsmf-event-exposure # Nsmf_EventExposure service\n- nsmf-oam # OAM service\nsnssaiInfos: # the S-NSSAI (Single Network Slice Selection Assistance Information) list supported by this AMF\n- sNssai: # S-NSSAI (Single Network Slice Selection Assistance Information)\nsst: 1 # Slice/Service Type (uinteger, range: 0~255)\nsd: 010203 # Slice Differentiator (3 bytes hex string, range: 000000~FFFFFF)\ndnnInfos: # DNN information list\n- dnn: internet # Data Network Name\ndns: # the IP address of DNS\nipv4: 8.8.8.8\nipv6: 2001:4860:4860::8888\n- sNssai: # S-NSSAI (Single Network Slice Selection Assistance Information)\nsst: 1 # Slice/Service Type (uinteger, range: 0~255)\nsd: 112233 # Slice Differentiator (3 bytes hex string, range: 000000~FFFFFF)\ndnnInfos: # DNN information list\n- dnn: internet # Data Network Name\ndns: # the IP address of DNS\nipv4: 8.8.8.8\nipv6: 2001:4860:4860::8888\nplmnList: # the list of PLMN IDs that this SMF belongs to (optional, remove this key when unnecessary)\n- mcc: 208 # Mobile Country Code (3 digits string, digit: 0~9)\nmnc: 93 # Mobile Network Code (2 or 3 digits string, digit: 0~9)\nlocality: area1 # Name of the location where a set of AMF, SMF, PCF and UPFs are located\npfcp: # the IP address of N4 interface on this SMF (PFCP)\n# addr config is deprecated in smf config v1.0.3, please use the following config\nnodeID: 127.0.0.1 # the Node ID of this SMF\nlistenAddr: 127.0.0.1 # the IP/FQDN of N4 interface on this SMF (PFCP)\nexternalAddr: 127.0.0.1 # the IP/FQDN of N4 interface on this SMF (PFCP)\nuserplaneInformation: # list of userplane information\nupNodes: # information of userplane node (AN or UPF)\ngNB1: # the name of the node\ntype: AN # the type of the node (AN or UPF)\nUPF: # the name of the node\ntype: UPF # the type of the node (AN or UPF)\nnodeID: 127.0.0.8 # the Node ID of this UPF\naddr: 127.0.0.8 # the IP/FQDN of N4 interface on this UPF (PFCP)\nsNssaiUpfInfos: # S-NSSAI information list for this UPF\n- sNssai: # S-NSSAI (Single Network Slice Selection Assistance Information)\nsst: 1 # Slice/Service Type (uinteger, range: 0~255)\nsd: 010203 # Slice Differentiator (3 bytes hex string, range: 000000~FFFFFF)\ndnnUpfInfoList: # DNN information list for this S-NSSAI\n- dnn: internet\npools:\n- cidr: 10.60.0.0/16\nstaticPools:\n- cidr: 10.60.100.0/24\n- sNssai: # S-NSSAI (Single Network Slice Selection Assistance Information)\nsst: 1 # Slice/Service Type (uinteger, range: 0~255)\nsd: 112233 # Slice Differentiator (3 bytes hex string, range: 000000~FFFFFF)\ndnnUpfInfoList: # DNN information list for this S-NSSAI\n- dnn: internet\npools:\n- cidr: 10.61.0.0/16\nstaticPools:\n- cidr: 10.61.100.0/24\ninterfaces: # Interface list for this UPF\n- interfaceType: N3 # the type of the interface (N3 or N9)\nendpoints: # the IP address of this N3/N9 interface on this UPF\n- 10.200.200.2 # 127.0.0.8\nnetworkInstances: # Data Network Name (DNN)\n- internet\nlinks: # the topology graph of userplane, A and B represent the two nodes of each link\n- A: gNB1\nB: UPF\n# retransmission timer for pdu session modification command\nt3591:\nenable: true # true or false\nexpireTime: 16s # default is 6 seconds\nmaxRetryTimes: 3 # the max number of retransmission\n# retransmission timer for pdu session release command\nt3592:\nenable: true # true or false\nexpireTime: 16s # default is 6 seconds\nmaxRetryTimes: 3 # the max number of retransmission\nnrfUri: http://127.0.0.10:8000 # a valid URI of NRF\n#urrPeriod: 10 # default usage report period in seconds\n#urrThreshold: 1000 # default usage report threshold in bytes\n\nlogger: # log output setting\nenable: true # true or false\nlevel: info # how detailed to output, value: trace, debug, info, warn, error, fatal, panic\nreportCaller: false # enable the caller report or not, value: true or false\n
- free5gc/config/upfcfg.yaml Replace gtpu from 127.0.0.8
to 10.200.200.2
:
version: 1.0.3\ndescription: UPF initial local configuration\n\n# The listen IP and nodeID of the N4 interface on this UPF (Can't set to 0.0.0.0)\npfcp:\naddr: 127.0.0.8 # IP addr for listening\nnodeID: 127.0.0.8 # External IP or FQDN can be reached\nretransTimeout: 1s # retransmission timeout\nmaxRetrans: 3 # the max number of retransmission\n\ngtpu:\nforwarder: gtp5g\n# The IP list of the N3/N9 interfaces on this UPF\n# If there are multiple connection, set addr to 0.0.0.0 or list all the addresses\nifList:\n- addr: 10.200.200.2 # 127.0.0.8\ntype: N3\n# name: upf.5gc.nctu.me\n# ifname: gtpif\n# mtu: 1400\n\n# The DNN list supported by UPF\ndnnList:\n- dnn: internet # Data Network Name\ncidr: 10.60.0.0/24 # Classless Inter-Domain Routing for assigned IPv4 pool of UE\n# natifname: eth0\n\nlogger: # log output setting\nenable: true # true or false\nlevel: info # how detailed to output, value: trace, debug, info, warn, error, fatal, panic\nreportCaller: false # enable the caller report or not, value: true or false\n
"},{"location":"blog/1-free5gc-with-namespace/#ueransim","title":"UERANSIM","text":"UERANSIM/config/free5gc-gnb.yaml
Replace ngapIp from 127.0.0.1
to 10.200.200.1
Replace gtpIp from 127.0.0.1
to 10.200.200.1
Replace amfConfigs / address from 127.0.0.1
to 10.200.200.2
mcc: '208' # Mobile Country Code value\nmnc: '93' # Mobile Network Code value (2 or 3 digits)\n\nnci: '0x000000010' # NR Cell Identity (36-bit)\nidLength: 32 # NR gNB ID length in bits [22...32]\ntac: 1 # Tracking Area Code\n\nlinkIp: 127.0.0.1 # gNB's local IP address for Radio Link Simulation (Usually same with local IP)\nngapIp: 10.200.200.1 # 127.0.0.1 # gNB's local IP address for N2 Interface (Usually same with local IP)\ngtpIp: 10.200.200.1 # 127.0.0.1 # gNB's local IP address for N3 Interface (Usually same with local IP)\n\n# List of AMF address information\namfConfigs:\n- address: 10.200.200.2 # 127.0.0.1\nport: 38412\n\n# List of supported S-NSSAIs by this gNB\nslices:\n- sst: 0x1\nsd: 0x010203\n\n# Indicates whether or not SCTP stream number errors should be ignored.\nignoreStreamIds: true\n
- UERANSIM/config/free5gc-ue.yaml # IMSI number of the UE. IMSI = [MCC|MNC|MSISDN] (In total 15 or 16 digits)\nsupi: 'imsi-208930000000003'\n# Mobile Country Code value\nmcc: '208'\n# Mobile Network Code value (2 or 3 digits)\nmnc: '93'\n\n# Permanent subscription key\nkey: '8baf473f2f8fd09487cccbd7097c6862'\n# Operator code (OP or OPC) of the UE\nop: '8e27b6af0e692e750f32667a3b14605d'\n# This value specifies the OP type and it can be either 'OP' or 'OPC'\nopType: 'OP'\n# Authentication Management Field (AMF) value\namf: '8000'\n# IMEI number of the device. It is used if no SUPI is provided\nimei: '356938035643803'\n# IMEISV number of the device. It is used if no SUPI and IMEI is provided\nimeiSv: '4370816125816151'\n\n# List of gNB IP addresses for Radio Link Simulation\ngnbSearchList:\n- 127.0.0.1\n\n# Initial PDU sessions to be established\nsessions:\n- type: 'IPv4'\napn: 'internet'\nslice:\nsst: 0x01\nsd: 0x010203\n\n# List of requested S-NSSAIs by this UE\nslices:\n- sst: 0x01\nsd: 0x010203\n\n# Supported encryption and integrity algorithms by this UE\nintegrity:\nIA1: true\nIA2: true\nIA3: true\nciphering:\nEA1: true\nEA2: true\nEA3: true\n
"},{"location":"blog/1-free5gc-with-namespace/#environment-set-up-of-free5gc-and-ueransim","title":"Environment set up of free5GC and UERANSIM","text":"First, create a namespace:
Note
Assume that you are either running as root, or it behoves you to prepend sudo
to commands as necessary.
ip netns add ueransim\n
Next, add the bridge: ip link add free5gc-br type bridge\n
Add two pairs of veth: ip link add veth0 type veth peer name br-veth0\nip link add veth1 type veth peer name br-veth1\n
Now, it could be like: root@free5gc:~# ip a\n1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000\n link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00\n inet 127.0.0.1/8 scope host lo\n valid_lft forever preferred_lft forever\n inet6 ::1/128 scope host\n valid_lft forever preferred_lft forever\n2: enp0s5: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000\n link/ether 00:1c:42:b1:ba:f4 brd ff:ff:ff:ff:ff:ff\n inet 10.211.55.23/24 brd 10.211.55.255 scope global dynamic enp0s5\n valid_lft 1714sec preferred_lft 1714sec\n inet6 fdb2:2c26:f4e4:0:21c:42ff:feb1:baf4/64 scope global dynamic mngtmpaddr noprefixroute\n valid_lft 2591750sec preferred_lft 604550sec\n inet6 fe80::21c:42ff:feb1:baf4/64 scope link\n valid_lft forever preferred_lft forever\n3: enp0s6: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000\n link/ether 00:1c:42:f1:11:c6 brd ff:ff:ff:ff:ff:ff\n inet 10.37.129.20/24 brd 10.37.129.255 scope global enp0s6\n valid_lft forever preferred_lft forever\n inet6 fdb2:2c26:f4e4:1:21c:42ff:fef1:11c6/64 scope global dynamic mngtmpaddr noprefixroute\n valid_lft 2591750sec preferred_lft 604550sec\n inet6 fe80::21c:42ff:fef1:11c6/64 scope link\n valid_lft forever preferred_lft forever\n4: free5gc-br: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000\n link/ether 4e:f6:d7:9c:50:de brd ff:ff:ff:ff:ff:ff\n5: br-veth0@veth0: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN group default qlen 1000\n link/ether c2:31:0c:5f:45:81 brd ff:ff:ff:ff:ff:ff\n6: veth0@br-veth0: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN group default qlen 1000\n link/ether 4a:0f:1e:80:9b:be brd ff:ff:ff:ff:ff:ff\n7: br-veth1@veth1: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN group default qlen 1000\n link/ether 56:99:b0:82:78:0d brd ff:ff:ff:ff:ff:ff\n8: veth1@br-veth1: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN group default qlen 1000\n link/ether 12:5a:56:00:5b:be brd ff:ff:ff:ff:ff:ff\n
Next, assign interface to namespace:
ip link set dev veth0 netns ueransim\n
Set ip address: ip netns exec ueransim ip a add 10.200.200.1/24 dev veth0\n
Enable both interface. Don't forget lo: ip netns exec ueransim ip link set lo up\nip netns exec ueransim ip link set veth0 up\n
Check with ip a
: root@free5gc:~# ip netns exec ueransim ip a\n1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000\n link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00\n inet 127.0.0.1/8 scope host lo\n valid_lft forever preferred_lft forever\n inet6 ::1/128 scope host\n valid_lft forever preferred_lft forever\n6: veth0@if5: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state LOWERLAYERDOWN group default qlen 1000\n link/ether 4a:0f:1e:80:9b:be brd ff:ff:ff:ff:ff:ff link-netnsid 0\n inet 10.200.200.1/24 scope global veth0\n valid_lft forever preferred_lft forever\n
Set for veth1 as well: ip a add 10.200.200.2/24 dev veth1\nip link set veth1 up\n
Let two interfaces attatch to bridge: ip link set dev br-veth0 master free5gc-br\nip link set dev br-veth1 master free5gc-br\nip link set br-veth0 up\nip link set br-veth1 up\nip link set free5gc-br up\n
Using bridge link
to check: root@free5gc:~# bridge link\n5: br-veth0@if6: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 master free5gc-br state forwarding priority 32 cost 2\n7: br-veth1@veth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 master free5gc-br state forwarding priority 32 cost 2\n
Now it looks like: 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000\n link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00\n inet 127.0.0.1/8 scope host lo\n valid_lft forever preferred_lft forever\n inet6 ::1/128 scope host\n valid_lft forever preferred_lft forever\n2: enp0s5: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000\n link/ether 00:1c:42:b1:ba:f4 brd ff:ff:ff:ff:ff:ff\n inet 10.211.55.23/24 brd 10.211.55.255 scope global dynamic enp0s5\n valid_lft 1000sec preferred_lft 1000sec\n inet6 fdb2:2c26:f4e4:0:21c:42ff:feb1:baf4/64 scope global dynamic mngtmpaddr noprefixroute\n valid_lft 2591870sec preferred_lft 604670sec\n inet6 fe80::21c:42ff:feb1:baf4/64 scope link\n valid_lft forever preferred_lft forever\n3: enp0s6: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000\n link/ether 00:1c:42:f1:11:c6 brd ff:ff:ff:ff:ff:ff\n inet 10.37.129.20/24 brd 10.37.129.255 scope global enp0s6\n valid_lft forever preferred_lft forever\n inet6 fdb2:2c26:f4e4:1:21c:42ff:fef1:11c6/64 scope global dynamic mngtmpaddr noprefixroute\n valid_lft 2591870sec preferred_lft 604670sec\n inet6 fe80::21c:42ff:fef1:11c6/64 scope link\n valid_lft forever preferred_lft forever\n4: free5gc-br: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000\n link/ether 56:99:b0:82:78:0d brd ff:ff:ff:ff:ff:ff\n inet6 fe80::5499:b0ff:fe82:780d/64 scope link\n valid_lft forever preferred_lft forever\n5: br-veth0@if6: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master free5gc-br state UP group default qlen 1000\n link/ether c2:31:0c:5f:45:81 brd ff:ff:ff:ff:ff:ff link-netns ueransim\n inet6 fe80::c031:cff:fe5f:4581/64 scope link\n valid_lft forever preferred_lft forever\n7: br-veth1@veth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master free5gc-br state UP group default qlen 1000\n link/ether 56:99:b0:82:78:0d brd ff:ff:ff:ff:ff:ff\n inet6 fe80::5499:b0ff:fe82:780d/64 scope link\n valid_lft forever preferred_lft forever\n8: veth1@br-veth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000\n link/ether 12:5a:56:00:5b:be brd ff:ff:ff:ff:ff:ff\n inet 10.200.200.2/24 scope global veth1\n valid_lft forever preferred_lft forever\n inet6 fe80::105a:56ff:fe00:5bbe/64 scope link\n valid_lft forever preferred_lft forever\n
Let's test it: Note
You can perform ip netns exec ueransim /bin/bash --rcfile <(echo \"PS1=\\\"ueransim> \\\"\")
to enter namespace and modify shell prefix.
root@free5gc:~# ip netns exec ueransim /bin/bash --rcfile <(echo \"PS1=\\\"ueransim> \\\"\")\nueransim> ping -c2 10.200.200.2\nPING 10.200.200.2 (10.200.200.2) 56(84) bytes of data.\n64 bytes from 10.200.200.2: icmp_seq=1 ttl=64 time=0.089 ms\n64 bytes from 10.200.200.2: icmp_seq=2 ttl=64 time=0.226 ms\n\n--- 10.200.200.2 ping statistics ---\n2 packets transmitted, 2 received, 0% packet loss, time 1020ms\nrtt min/avg/max/mdev = 0.089/0.157/0.226/0.068 ms\n
Insert default routing rule: ueransim> ip route add default via 10.200.200.2\nueransim> netstat -rn\nKernel IP routing table\nDestination Gateway Genmask Flags MSS Window irtt Iface\n0.0.0.0 10.200.200.2 0.0.0.0 UG 0 0 0 veth0\n10.200.200.0 0.0.0.0 255.255.255.0 U 0 0 0 veth0\n
Try to ping 8.8.8.8: ueransim> ping -c2 8.8.8.8\nPING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.\n\n--- 8.8.8.8 ping statistics ---\n2 packets transmitted, 0 received, 100% packet loss, time 1028ms\n
It is because the main host must translate the source addresses. Besides, the main host need to forward packet: root@free5gc:~# iptables -t nat -A POSTROUTING -o enp0s5 -j MASQUERADE\nroot@free5gc:~# sysctl -w net.ipv4.ip_forward=1\nroot@free5gc:~# sudo iptables -I FORWARD 1 -j ACCEPT\n
And then: ueransim> ping -c2 8.8.8.8\nPING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.\n64 bytes from 8.8.8.8: icmp_seq=1 ttl=127 time=13.9 ms\n64 bytes from 8.8.8.8: icmp_seq=2 ttl=127 time=28.0 ms\n\n--- 8.8.8.8 ping statistics ---\n2 packets transmitted, 2 received, 0% packet loss, time 1002ms\nrtt min/avg/max/mdev = 13.866/20.939/28.012/7.073 ms\n
After free5GC execute run.sh
, it's time for UERANSIM:
In terminal 1:
ueransim> build/nr-gnb -c config/free5gc-gnb.yaml\nUERANSIM v3.1.0\n[2023-07-05 19:58:26.368] [sctp] [info] Trying to establish SCTP connection... (10.200.200.2:38412)\n[2023-07-05 19:58:26.373] [sctp] [info] SCTP connection established (10.200.200.2:38412)\n[2023-07-05 19:58:26.374] [sctp] [debug] SCTP association setup ascId[3]\n[2023-07-05 19:58:26.375] [ngap] [debug] Sending NG Setup Request\n[2023-07-05 19:58:26.380] [ngap] [debug] NG Setup Response received\n[2023-07-05 19:58:26.380] [ngap] [info] NG Setup procedure is successful\n[2023-07-05 19:58:35.804] [mr] [info] New UE connected to gNB. Total number of UEs is now: 1\n[2023-07-05 19:58:35.806] [rrc] [debug] Sending RRC Setup for UE[3]\n[2023-07-05 19:58:35.807] [ngap] [debug] Initial NAS message received from UE 3\n[2023-07-05 19:58:35.869] [ngap] [debug] Initial Context Setup Request received\n[2023-07-05 19:58:36.108] [ngap] [info] PDU session resource is established for UE[3] count[1]\n
In terminal 2: ueransim> sudo build/nr-ue -c config/free5gc-ue.yaml\nUERANSIM v3.1.0\n[2023-07-05 19:58:35.803] [nas] [debug] NAS layer started\n[2023-07-05 19:58:35.803] [rrc] [debug] RRC layer started\n[2023-07-05 19:58:35.804] [nas] [info] UE switches to state: MM-DEREGISTERED/PLMN-SEARCH\n[2023-07-05 19:58:35.804] [nas] [info] UE connected to gNB\n[2023-07-05 19:58:35.804] [nas] [info] UE switches to state: MM-DEREGISTERED/NORMAL-SERVICE\n[2023-07-05 19:58:35.804] [nas] [info] UE switches to state: MM-REGISTERED-INITIATED/NA\n[2023-07-05 19:58:35.805] [rrc] [debug] Sending RRC Setup Request\n[2023-07-05 19:58:35.806] [rrc] [info] RRC connection established\n[2023-07-05 19:58:35.806] [nas] [info] UE switches to state: CM-CONNECTED\n[2023-07-05 19:58:35.838] [nas] [debug] Received rand[61262F32A617D0BAD716603B1CBDA477] autn[44778026F4238000FC14B59D68855328]\n[2023-07-05 19:58:35.838] [nas] [debug] Calculated res[47759045F5ACEA59] ck[1C559301F29EF49572F5D150B3B99288] ik[D223317F752F233CE4C7AA253644D882] ak[528433D1FBE6] mac_a[FC14B59D68855328]\n[2023-07-05 19:58:35.838] [nas] [debug] Used snn[5G:mnc093.mcc208.3gppnetwork.org] sqn[16F3B3F70FC5]\n[2023-07-05 19:58:35.838] [nas] [debug] Derived kSeaf[7FC8B7FB1B141B6579B9C0FAEB9CCF1312FE9F9634868E234756DE49FD67C5F1] kAusf[FA0402A892E6046D52F4DECACA40B2A75B698FCEAD5EB320139FC69B77BD4C46] kAmf[3D4AD68E153B9642ACBECC67AD399015F7CB578F9DF4C88A35EED99C72C9B95B]\n[2023-07-05 19:58:35.843] [nas] [debug] Derived kNasEnc[1F829EB2BA238DD0226C3484E6A79D1F] kNasInt[251C0412B1BAD88A9DD0008F32D6F216]\n[2023-07-05 19:58:35.843] [nas] [debug] Selected integrity[2] ciphering[0]\n[2023-07-05 19:58:35.869] [nas] [debug] T3512 started with int[3600]\n[2023-07-05 19:58:35.869] [nas] [info] UE switches to state: MM-REGISTERED/NORMAL-SERVICE\n[2023-07-05 19:58:35.869] [nas] [info] Initial Registration is successful\n[2023-07-05 19:58:35.869] [nas] [info] Initial PDU sessions are establishing [1#]\n[2023-07-05 19:58:35.869] [nas] [debug] Sending PDU session establishment request\n[2023-07-05 19:58:36.108] [nas] [info] PDU Session establishment is successful PSI[1]\n[2023-07-05 19:58:36.113] [app] [info] Connection setup for PDU session[1] is successful, TUN interface[uesimtun0, 10.60.0.1] is up.\n
In terminal 3: ueransim> ip a\n1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000\n link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00\n inet 127.0.0.1/8 scope host lo\n valid_lft forever preferred_lft forever\n inet6 ::1/128 scope host\n valid_lft forever preferred_lft forever\n2: uesimtun0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UNKNOWN group default qlen 500\n link/none\n inet 10.60.0.1/32 scope global uesimtun0\n valid_lft forever preferred_lft forever\n inet6 fe80::b5ef:5b4:e3f6:af64/64 scope link stable-privacy\n valid_lft forever preferred_lft forever\n6: veth0@if5: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000\n link/ether 4a:0f:1e:80:9b:be brd ff:ff:ff:ff:ff:ff link-netnsid 0\n inet 10.200.200.1/24 scope global veth0\n valid_lft forever preferred_lft forever\n inet6 fe80::480f:1eff:fe80:9bbe/64 scope link\n valid_lft forever preferred_lft forever\nueransim> ping -c2 -I uesimtun0 8.8.8.8\nPING 8.8.8.8 (8.8.8.8) from 10.60.0.1 uesimtun0: 56(84) bytes of data.\n64 bytes from 8.8.8.8: icmp_seq=1 ttl=127 time=19.5 ms\n64 bytes from 8.8.8.8: icmp_seq=2 ttl=127 time=33.2 ms\n\n--- 8.8.8.8 ping statistics ---\n2 packets transmitted, 2 received, 0% packet loss, time 1006ms\nrtt min/avg/max/mdev = 19.478/26.348/33.219/6.870 ms\n
Also ping to google.com: ueransim> ping -c2 -I uesimtun0 google.com\nPING google.com (172.217.160.110) from 10.60.0.1 uesimtun0: 56(84) bytes of data.\n64 bytes from tsa03s06-in-f14.1e100.net (172.217.160.110): icmp_seq=1 ttl=127 time=17.3 ms\n64 bytes from tsa03s06-in-f14.1e100.net (172.217.160.110): icmp_seq=2 ttl=127 time=29.5 ms\n\n--- google.com ping statistics ---\n2 packets transmitted, 2 received, 0% packet loss, time 1005ms\nrtt min/avg/max/mdev = 17.295/23.385/29.476/6.090 ms\n
"},{"location":"blog/1-free5gc-with-namespace/#what-if-two-ueransims-with-two-namespaces","title":"What if two UERANSIMs with two namespaces?","text":"Same as before, you should create another namespace for UERANSIM, called it ueransim2:
root@free5gc:~# ip netns ls\nueransim2 (id: 1)\nueransim (id: 0)\n
And then: ip link add veth2 type veth peer name br-veth2\nip link set dev veth2 netns ueransim2\nip link set br-veth2 master free5gc-br\nip link set br-veth2 up\nip netns exec ueransim2 ip a add 10.200.200.3/24 dev veth2\nip netns exec ueransim2 ip link set lo up\nip netns exec ueransim2 ip link set veth2 up\nip netns exec ueransim2 ip route add default via 10.200.200.2\n
Copy UERANSIM/config/free5gc-gnb.yaml and UERANSIM/config/free5gc-ue.yaml to free5gc-gnb2.yaml and free5gc-ue2.yaml, modify:
free5gc-gnb2.yaml
127.0.0.1
to 10.200.200.3
127.0.0.1
to 10.200.200.3
...\nngapIp: 10.200.200.3 # 127.0.0.1 # gNB's local IP address for N2 Interface (Usually same with local IP)\ngtpIp: 10.200.200.3 # 127.0.0.1 # gNB's local IP address for N3 Interface (Usually same with local IP)\n\n# List of AMF address information\namfConfigs:\n- address: 10.200.200.2 # 127.0.0.1\nport: 38412\n...\n
free5gc-ue2.yaml supi
change to imsi-208930000000004
...\n# IMSI number of the UE. IMSI = [MCC|MNC|MSISDN] (In total 15 or 16 digits)\nsupi: 'imsi-208930000000004'\n...\n
Note
Should register ue to webconsole first.
The result:
ueransim> ip a\n1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000\n link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00\n inet 127.0.0.1/8 scope host lo\n valid_lft forever preferred_lft forever\n inet6 ::1/128 scope host\n valid_lft forever preferred_lft forever\n6: veth0@if5: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000\n link/ether 4a:0f:1e:80:9b:be brd ff:ff:ff:ff:ff:ff link-netnsid 0\n inet 10.200.200.1/24 scope global veth0\n valid_lft forever preferred_lft forever\n inet6 fe80::480f:1eff:fe80:9bbe/64 scope link\n valid_lft forever preferred_lft forever\n7: uesimtun0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UNKNOWN group default qlen 500\n link/none\n inet 10.60.0.1/32 scope global uesimtun0\n valid_lft forever preferred_lft forever\n inet6 fe80::f6d7:dd81:fe7f:496a/64 scope link stable-privacy\n valid_lft forever preferred_lft forever\nueransim> ping -c2 -I uesimtun0 google.com\nPING google.com (172.217.160.110) from 10.60.0.1 uesimtun0: 56(84) bytes of data.\n64 bytes from tsa03s06-in-f14.1e100.net (172.217.160.110): icmp_seq=1 ttl=127 time=17.2 ms\n64 bytes from tsa03s06-in-f14.1e100.net (172.217.160.110): icmp_seq=2 ttl=127 time=28.5 ms\n\n--- google.com ping statistics ---\n2 packets transmitted, 2 received, 0% packet loss, time 1003ms\nrtt min/avg/max/mdev = 17.200/22.863/28.527/5.663 ms\n
ueransim2> ip a\n1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000\n link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00\n inet 127.0.0.1/8 scope host lo\n valid_lft forever preferred_lft forever\n inet6 ::1/128 scope host \n valid_lft forever preferred_lft forever\n5: uesimtun0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UNKNOWN group default qlen 500\n link/none \n inet 10.60.0.2/32 scope global uesimtun0\n valid_lft forever preferred_lft forever\n inet6 fe80::16a4:523a:a86:bf83/64 scope link stable-privacy \n valid_lft forever preferred_lft forever\n12: veth2@if11: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000\n link/ether fa:12:bb:9c:fa:40 brd ff:ff:ff:ff:ff:ff link-netnsid 0\n inet 10.200.200.3/24 scope global veth2\n valid_lft forever preferred_lft forever\n inet6 fe80::f812:bbff:fe9c:fa40/64 scope link \n valid_lft forever preferred_lft forever\nueransim2> ping -c2 -I uesimtun0 google.com\nPING google.com (172.217.160.110) from 10.60.0.2 uesimtun0: 56(84) bytes of data.\n64 bytes from tsa03s06-in-f14.1e100.net (172.217.160.110): icmp_seq=1 ttl=127 time=18.9 ms\n64 bytes from tsa03s06-in-f14.1e100.net (172.217.160.110): icmp_seq=2 ttl=127 time=15.8 ms\n\n--- google.com ping statistics ---\n2 packets transmitted, 2 received, 0% packet loss, time 1002ms\nrtt min/avg/max/mdev = 15.786/17.353/18.921/1.567 ms\n
"},{"location":"blog/1-free5gc-with-namespace/#about","title":"About","text":"Hi, my name is Jimmy Chang. The current research topic is 5G LAN with a focus on the 5G Data Plane. Any questions or errors in the article are welcome for correction. Please feel free to send an email to provide feedback.
Note
Author: Wilson Date: 2023/8/2
"},{"location":"blog/20230802/#abstract","title":"Abstract","text":"Regarding the theme this time, I will briefly introduce OAuth. OAuth 2.0 defines four types of authorization flows. I choose the Client Credentials Flow to explain because the authentication mechanism in NRF is closely related to the Client Credentials Flow.
Next, I will explain how to apply the concept of the Client Credentials Flow to NRF and introduce Nnrf_AccessToken Service
, because Nnrf_AccessToken Service
is closely related to the Client Credentials Flow.
Finally, I will make a simple experiment of the authentication mechanism in NRF and share the environment settings and methods of operation.
"},{"location":"blog/20230802/#oauth","title":"OAuth","text":"Before explaining the authentication mechanism in NRF, I will introduce OAuth. Regarding the OAuth flow, we can log in to the account through the platform before accessing an application. After logging in, we agree that an application can limitedly obtain the information of the user on the platform. The application can be LinkedIn, YouTube, etc. The platform can be Google, Facebook, etc.
The full English name of OAuth is Open standard Authorization. OAuth is an open standard, and it's used to deal with authorization-related behaviors. OAuth 2.0 defines four types of authorization flows. The four types of the authorization flows are:
This article explains the entire authorization of the Client Credentials Flow only, because the authentication mechanism in NRF adopts the Client Credentials.
If you're interested in how authorization mechanism works, please refer to this article for more details.
"},{"location":"blog/20230802/#client-credentials-flow","title":"Client Credentials Flow","text":"Figure 1. Client Credentials Flow
Referring to the Figure 1, the Client Credentials Flow is mainly composed of:
The entire authorization of the Client Credentials Flow can be devided into 3 steps:
In addition, the Client and the Authorization Server have their own Scope list. The Scope list records a series of the actions. The Client or the Authorization Server is permitted to do the actions for obtaining the user\u2019s name, deleting posts, etc.
Below I will explain how to apply the Client Credentials Flow to NRF after talking about the Client Credentials Flow.
"},{"location":"blog/20230802/#client-credentials-flow-in-nrf","title":"Client Credentials Flow in NRF","text":"The Figure 2 and the Figure 3 originate from the Figure 13.4.1.1-1 and the Figure 13.4.1.1-2 of the TS 33.501.
Figure 2. NF Service Consumer Obtaining Access Token before NF Service Access
The entire flow in Figure 2 is the same as Step 1 and Step 2 in the Figure 1. The role of the Client is played by the NF Service Consumer, and the role of the Authorization Server is played by the NRF.
First, the NF Service Consumer registers with NRF. Then the NF Service Consumer sends the Nnrf_AccessToken_Get Request
to NRF. The Nnrf_AccessToken_Get Request
includes:
The NF Type can be AMF, SMF, etc. , and the NF Service Name can be namf-comm
, nsmf-pdusession
, etc.
NRF verifies the information provided by the NF Service Consumer after it receives the Nnrf_AccessToken_Get Request
. NRF generates an Access Token and uses the NRF private key to sign on the Access Token after the verification is successful.
Finally, NRF returns the Nnrf_AccessToken_Get Response
to the NF Service Consumer. The NF Service Consumer stores the Access Token within the validity period after it gets the Access Token. The services provided by the NF Service Producer are in the Expected NF Service Name. The NF Service Consumer doesn\u2019t need to verify again when it wants to use the services provided by the NF Service Producer.
Figure 3. NF Service Consumer Requesting Service Access with an Access Token
The entire flow in Figure 3 is the same as Step 3 in the Figure 1. The role of the Client is played by the NF Service Consumer, and the role of the Resource Server is played by the NF Service Producer.
First, the NF Service Consumer sends the NF Service Request
to the NF Service Producer with the Access Token. Simply put, the NF Service Consumer wants to consume the service provided by the NF Service Producer.
The NF Service Producer uses the NRF public key to verify the signed Access Token after it receives the NF Service Request
. If the verification is successful, the NF Service Producer will send the NF Service Response
to the NF Service Consumer.
Now I will talk about the Nnrf_AccessToken Service
after explaining how to apply the Client Credentials Flow to NRF.
Figure 4. Access Token Request
The Figure 4 originates from the Figure 5.4.2.2.1-1 of the TS 29.510.
First, the NF Service Consumer sends the POST /oauth2/token
to NRF, and the data is stored in the AccessTokenReq
. The attribute name, the data type, and the formulation rule of the AccessTokenReq
are shown in the Table 1. The Table 1 originates from the Table 6.3.5.2.2-1 of the TS 29.510.
Table 1. Definition of Type AccessTokenReq
Definition of type AccessTokenReq
:
grant_type
: The value must be set to the client_credentials, and it is checked in the Snippet 1.nfInstanceId
: The value stores the ID of the NF Service Consumer.targetNfInstanceId
: The value stores the ID of the NF Service Producer.nfType
: The value stores the network function name of the NF Service Consumer. The network function name can be the AMF, SMF, etc. targetNfType
: The value stores the network function name of the NF Service Producer.scope
: It stores the services. The services can be the namf-comm
, nsmf-pdusession
, etc. When the NF Service Consumer requests the services. The services will be provided by the NF Service Producer.requesterPlmn
: It is mainly used in the roaming.targetPlmn
: It is mainly used in the roaming.if reqGrantType != \"client_credentials\" {\nreturn &models.AccessTokenErr{\nError: \"unsupported_grant_type\",\n}\n}\n
Snippet 1. Grant Type Value Checking NRF sends AccessTokenRsp
to the NF Service Consumer in the Step 2a of the Figure 4. The attribute name, the data type, and the formulation rule of the AccessTokenRsp
are shown in the Table 2. The Table 2 originates from the Table 6.3.5.2.3-1 of the TS 29.510.
Table 2. Definition of Type AccessTokenRsp
The AccessTokenRsp
contains four attribute names. The four attribute names are:
access_token
: It stores all the attribute names and values of the AccessTokenClaims in the Table 3. The Table 3 originates form the Table 6.3.5.2.4-1 of the TS 29.510. token_type
: It must be set to the Bearer and can be seen in the Snippet 2.expires_in
: It stores information related to the expiration date.scope
: The NF Service Consumer and the NF Service Producer have their own scope list. The scope in the AccessTokenRsp
has a series of these services, and the NF Service Producer is permitted to consume these services.Table 3. Definition of Type AccessTokenClaims
Definition of Type AccessTokenClaims
:
iss
: It is called issuer, and the content usually stores the ID of NRF.sub
: It is called subject, and the content stores the ID of the NF Service Consumer.aud
: It is called audience, and the content stores the ID of the NF Service Producer.scope
: The scope in the AccessTokenClaims
has a series of these services, and the NF Service Consumer is authorized by the NF Service Producer and permitted to consume these services.exp
: It stores information related to the validity period.func AccessTokenProcedure(request models.AccessTokenReq) (\n*models.AccessTokenRsp, *models.AccessTokenErr,\n) {\nlogger.AccTokenLog.Infoln(\"In AccessTokenProcedure\")\n\nvar expiration int32 = 1000\nscope := request.Scope\ntokenType := \"Bearer\"\nnow := int32(time.Now().Unix())\n\nerrResponse := AccessTokenScopeCheck(request)\nif errResponse != nil {\nreturn nil, errResponse\n}\n\n// Create AccessToken\nnrfCtx := nrf_context.GetSelf()\naccessTokenClaims := models.AccessTokenClaims{\nIss: nrfCtx.Nrf_NfInstanceID, // NF instance id of the NRF\nSub: request.NfInstanceId, // nfInstanceId of service consumer\nAud: request.TargetNfInstanceId, // nfInstanceId of service producer\nScope: request.Scope, // TODO: the name of the NF services for which the\nExp: now + expiration, // access_token is authorized for use\nStandardClaims: jwt.StandardClaims{},\n}\naccessTokenClaims.IssuedAt = int64(now)\n\n// Use NRF private key to sign AccessToken\ntoken := jwt.NewWithClaims(jwt.GetSigningMethod(\"RS512\"), accessTokenClaims)\naccessToken, err := token.SignedString(nrfCtx.NrfPrivKey)\nif err != nil {\nlogger.AccTokenLog.Warnln(\"Signed string error: \", err)\nreturn nil, &models.AccessTokenErr{\nError: \"invalid_request\",\n}\n}\n\nresponse := &models.AccessTokenRsp{\nAccessToken: accessToken,\nTokenType: tokenType,\nExpiresIn: expiration,\nScope: scope,\n}\nreturn response, nil\n}\n
Snippet 2. AccessTokenProcedure Function The Snippet 2 is the AccessTokenProcedure()
function. The function is executed in NRF.
The function mainly processes:
AccessTokenReq
sent by the NF Service Customer.AccessTokenScopeCheck()
function. The AccessTokenScopeCheck()
function checks whether the content of the attribute name in the AccessTokenReq
complies with the requirements of the TS 29.510. If not, the AccessTokenProcedure()
function immediately returns the AccessTokenErr
to the NF Service Customer.AccessTokenRsp
. The AccessTokenRsp
is sent back to the NF Service Customer. The Iss
in the AccessToken obtains its own ID in NRF. The Sub
and Aud
are obtained from the NfInstancedId
and the TargetNfInstanceId
in the AccessTokenReq
respectively. The Scope
is obtained from the scope
in the AccessTokenReq
. The expiration is set to the 1000 in the Snippet 2. Therefore, the value of the exp
is the current time + 1000.AccessTokenErr
to the NF Service Customer.AccessTokenRsp
. The value of the TokenType
is set to the Bearer by the function. The function sets the ExprieIn
and the Scope
in the Snippet 2.Finally, I make a simple experiment about the Access Token and share the environment setting and method of operation with you.
"},{"location":"blog/20230802/#experiment","title":"Experiment","text":"The Table 4 is my environment setting. I provide the Table 4 for you. You can refer it.
Table 4. Environment
Remove the part of the tls
and add the content of the cert
, rootcert
and oauth
under sbi
in the nrfcfg.yaml
before implementing about the Access Token.
info:\nversion: 1.0.2\ndescription: NRF initial local configuration\n\nconfiguration:\nMongoDBName: free5gc # database name in MongoDB\nMongoDBUrl: mongodb://127.0.0.1:27017 # a valid URL of the mongodb\nsbi: # Service-based interface information\nscheme: http # the protocol for sbi (http or https)\nregisterIPv4: 127.0.0.10 # IP used to serve NFs or register to another NRF\nbindingIPv4: 127.0.0.10 # IP used to bind the service\nport: 8000 # port used to bind the service\ncert:\npem: cert/nrf.pem\nkey: cert/nrf.key\nrootcert:\npem: cert/nrf.pem\nkey: cert/nrf.key\noauth: true\nDefaultPlmnId:\nmcc: 208 # Mobile Country Code (3 digits string, digit: 0~9)\nmnc: 93 # Mobile Network Code (2 or 3 digits string, digit: 0~9)\nserviceNameList: # the SBI services provided by this NRF, refer to TS 29.510\n- nnrf-nfm # Nnrf_NFManagement service\n- nnrf-disc # Nnrf_NFDiscovery service\n\nlogger: # log output setting\nenable: true # true or false\nlevel: info # how detailed to output, value: trace, debug, info, warn, error, fatal, panic\nreportCaller: false # enable the caller report or not, value: true or false\n
nrfcfg.yaml Find the http://127.0.0.10:8000/nnrf-nfm/v1/nf-instances/8f7891b4-b127-4f59-9ec2-b5e6aade5531
in the NRF log, and you will get the 8f7891b4-b127-4f59-9ec2-b5e6aade5531
. The 8f7891b4-b127-4f59-9ec2-b5e6aade5531
is the nfInstanceID
.
2023-08-02T20:07:43.300826205Z [INFO][NRF][NFM] Handle NFRegisterRequest\n2023-08-02T20:07:43.308259291Z [INFO][NRF][NFM] urilist create\n2023-08-02T20:07:43.311674255Z [INFO][NRF][NFM] Create NF Profile\n2023-08-02T20:07:43.318192771Z [INFO][NRF][NFM] Location header: http://127.0.0.10:8000/nnrf-nfm/v1/nf-instances/8f7891b4-b127-4f59-9ec2-b5e6aade5531\n2023-08-02T20:07:43.325073275Z [INFO][NRF][GIN] | 201 | 127.0.0.1 | PUT | /nnrf-nfm/v1/nf-instances/8f7891b4-b127-4f59-9ec2-b5e6aade5531 |\n
Execute $curl -X GET {apiRoot}/nnrf-nfm/v1/nf-instances/{nfInstanceID}
, and you will obtain the detail information about the nfInstanceID
. You can see the nfType
of the nfInstanceID
is NSSF, and the information about the nfInstanceID
is used when you implement the Access Token.
ubuntu@free5GC:~/free5gc/NFs/nrf$ curl -X GET http://127.0.0.10:8000/nnrf-nfm/v1/nf-instances/8f7891b4-b127-4f59-9ec2-b5e6aade5531\n{\"ipv4Addresses\":[\"127.0.0.31\"],\"nfInstanceId\":\"8f7891b4-b127-4f59-9ec2-b5e6aade5531\",\"nfServices\":[{\"apiPrefix\":\"http://127.0.0.31:8000\",\"ipEndPoints\":[{\"ipv4Address\":\"127.0.0.31\",\"port\":8000,\"transport\":\"TCP\"}],\"nfServiceStatus\":\"REGISTERED\",\"scheme\":\"http\",\"serviceInstanceId\":\"0\",\"serviceName\":\"nnssf-nsselection\",\"versions\":[{\"apiFullVersion\":\"1.0.2\",\"apiVersionInUri\":\"v1\"}]},{\"apiPrefix\":\"http://127.0.0.31:8000\",\"ipEndPoints\":[{\"ipv4Address\":\"127.0.0.31\",\"port\":8000,\"transport\":\"TCP\"}],\"nfServiceStatus\":\"REGISTERED\",\"scheme\":\"http\",\"serviceInstanceId\":\"1\",\"serviceName\":\"nnssf-nssaiavailability\",\"versions\":[{\"apiFullVersion\":\"1.0.2\",\"apiVersionInUri\":\"v1\"}]}],\"nfStatus\":\"REGISTERED\",\"nfType\":\"NSSF\",\"plmnList\":[{\"mcc\":\"208\",\"mnc\":\"93\"}]}\n
Then you execute this command, see below.
$curl -X POST -H \"Content-Type: application/json\" -d '{\"nfInstanceId\": {nfInstanceID}, \"grant_type\": \"client_credentials\", \"nfType\": {nfType}, \"targetNfType\": \"UDR\", \"scope\": \"nudr-dr\"}' {apiRoot}/oauth2/token\n
You will get the long symbols. The long symbols is that the Access Token is encrypted by the private key of NRF and stored in the AccessTokenRsp
. ubuntu@free5GC:~/free5gc/NFs/nrf$ curl -X POST -H \"Content-Type: application/json\" -d '{\"nfInstanceId\": \"8f7891b4-b127-4f59-9ec2-b5e6aade5531\", \"grant_type\": \"client_credentials\", \"nfType\": \"NSSF\", \"targetNfType\": \"UDR\", \"scope\": \"nudr-dr\"}' http://127.0.0.10:8000/oauth2/token\n\"eyJhY2Nlc3NfdG9rZW4iOiJleUpoYkdjaU9pSlNVelV4TWlJc0luUjVjQ0k2SWtwWFZDSjkuZXlKcGMzTWlPaUlpTENKemRXSWlPaUk0WmpjNE9URmlOQzFpTVRJM0xUUm1OVGt0T1dWak1pMWlOV1UyWVdGa1pUVTFNekVpTENKaGRXUWlPaUlpTENKelkyOXdaU0k2SW01MVpISXRaSElpTENKbGVIQWlPakUyT1RFd01EZzBPRGdzSW1saGRDSTZNVFk1TVRBd056UTRPSDAuY3VHSkkwTndfV280S2lQbS1fZEZVdnVTQWM1WVEwMmRKYk5PTUhmMV9IOHdIZ2JKWFhUam9xU1Y2OTNYSmFKemkweGIxdC1DMW14TWhVZkZjbXpNMC1Nd2oxTXZYaWhyTTktdDFRUFItSFcxQlBlN0tHMUxBV3d5MEJfcXpIalltRlR6eGhONVlyNkpURDhBbkMxaFJFeEh4WHBjV1NqbV9vZnV0NVhfUFRFRkZtaHZrbmtVbU8waWFrTmdRWElRVTc1NnlvZ29ZTlFDRnJvSmRWamJMdnpFdkJLYTVFN0hQeXc3RkRDRHpTZU5WT2t2WTlobU11eldYZ3dOVmRIT3c1c2lNbmppbTlmTVZ0RTFxS1hjWDlScXlUdXlsWjM2ZlJ1QjdVZ2hkLU15Q19xd2VJRE41ZFdYOWZqdnA3VUNZZ01mVHhSLUI2M3d5OWFjQ183eThRIiwidG9rZW5fdHlwZSI6IkJlYXJlciIsImV4cGlyZXNfaW4iOjEwMDAsInNjb3BlIjoibnVkci1kciJ9\"\n
NSSF sends the AccessTokenReq
to NRF after you execute the above command. In the AccessTokenReq
, the nfInstanceId
is set to 8f7891b4-b127-4f59-9ec2-b5e6aade5531
. The grant_type
is set to the client_credentials. The nfType
is set to NSSF. The targetNfType
is set to UDR. The scope
is set to nudr-dr
. The information is shown in the NRF log. The value of the targetNfInstanceId
, requesterPlmn
, and targetPlmn
is empty because they are not set.
2023-08-02T20:18:08.127557565Z [INFO][NRF][Token] In AccessTokenProcedure\n2023-08-02T20:18:08.127586736Z [INFO][NRF][Token] Access Token Request\n2023-08-02T20:18:08.127611885Z [INFO][NRF][Token] Grant Type: client_credentials\n2023-08-02T20:18:08.127637480Z [INFO][NRF][Token] NF Instance ID: 8f7891b4-b127-4f59-9ec2-b5e6aade5531\n2023-08-02T20:18:08.127664415Z [INFO][NRF][Token] Target NF Instance ID:\n2023-08-02T20:18:08.127689792Z [INFO][NRF][Token] NF Type: NSSF\n2023-08-02T20:18:08.127712916Z [INFO][NRF][Token] Target NF Type: UDR\n2023-08-02T20:18:08.127734827Z [INFO][NRF][Token] Scope: nudr-dr\n2023-08-02T20:18:08.127758317Z [INFO][NRF][Token] Requester PLMN: <nil>\n2023-08-02T20:18:08.127781052Z [INFO][NRF][Token] Target PLMN: <nil>\n
Next, you can see the Access Token in the NRF log. The value of the Sub
is 8f7891b4-b127-4f59-9ec2-b5e6aade5531
. The Sub
represents NSSF, and NSSF belongs to the NF Service Customer. The value of the Scope
is the nudr-dr
. The value of the Exp
is 1691008488.
2023-08-02T20:18:08.134096785Z [INFO][NRF][Token] Access Token Claims\n2023-08-02T20:18:08.138100978Z [INFO][NRF][Token] Iss:\n2023-08-02T20:18:08.138185972Z [INFO][NRF][Token] Sub: 8f7891b4-b127-4f59-9ec2-b5e6aade5531\n2023-08-02T20:18:08.138228925Z [INFO][NRF][Token] Aud:\n2023-08-02T20:18:08.138264519Z [INFO][NRF][Token] Scope: nudr-dr\n2023-08-02T20:18:08.138298628Z [INFO][NRF][Token] Exp: 1691008488\n
Next, you can see the AccessTokenRsp
. You can see that the Access Token has become the long symbols. The value of the Token Type
is set to the Bearer. The value of the ExpiresIn
is set to 1000. The value of the Scope
is set to nudr-dr
.
2023-08-02T20:18:08.149587382Z [INFO][NRF][Token] Access Token Response\n2023-08-02T20:18:08.150006665Z [INFO][NRF][Token] Access Token: eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiIiLCJzdWIiOiI4Zjc4OTFiNC1iMTI3LTRmNTktOWVjMi1iNWU2YWFkZTU1MzEiLCJhdWQiOiIiLCJzY29wZSI6Im51ZHItZHIiLCJleHAiOjE2OTEwMDg0ODgsImlhdCI6MTY5MTAwNzQ4OH0.cuGJI0Nw_Wo4KiPm-_dFUvuSAc5YQ02dJbNOMHf1_H8wHgbJXXTjoqSV693XJaJzi0xb1t-C1mxMhUfFcmzM0-Mwj1MvXihrM9-t1QPR-HW1BPe7KG1LAWwy0B_qzHjYmFTzxhN5Yr6JTD8AnC1hRExHxXpcWSjm_ofut5X_PTEFFmhvknkUmO0iakNgQXIQU756yogoYNQCFroJdVjbLvzEvBKa5E7HPyw7FDCDzSeNVOkvY9hmMuzWXgwNVdHOw5siMnjim9fMVtE1qKXcX9RqyTuylZ36fRuB7Ughd-MyC_qweIDN5dWX9fjvp7UCYgMfTxR-B63wy9acC_7y8Q\n2023-08-02T20:18:08.150094277Z [INFO][NRF][Token] Token Type: Bearer\n2023-08-02T20:18:08.150133189Z [INFO][NRF][Token] Expires In: 1000\n2023-08-02T20:18:08.150167371Z [INFO][NRF][Token] Scope: nudr-dr\n
Finally, you can see 200. 200 means that AUSF sends the AccessTokenReq
to NRF. NRF successfully sends to AUSF after verification.
2023-08-02T20:18:08.150302345Z [INFO][NRF][GIN] | 200 | 127.0.0.1 | POST | /oauth2/token |\n
"},{"location":"blog/20230802/#reference","title":"Reference","text":"Hi, my name is Wilson. I am a master\u2019s student. My main area of research is network slicing. In the future, I will introduce more information about 5G. Hope you enjoy it.
"},{"location":"blog/TSN/","title":"TSN","text":""},{"location":"blog/TSN/#time-sensitive-networking-over-5g-system-introduction-rel-16","title":"Time-Sensitive Networking over 5G system - Introduction (Rel-16)","text":"Note
Author: Ya-shih Tseng Date: 2023/7/12
This blog focuses on the role of the 5G system in 3GPP Release 16 TSN (Time-Sensitive Networking).
"},{"location":"blog/TSN/#what-is-time-sensitive-network-tsn","title":"What is Time-Sensitive Network (TSN)","text":"Traditional Ethernet technology can only achieve \"best-effort\" communication and cannot meet the high reliability and low latency requirements of industrial manufacturing applications. Therefore, in the context of industrial automation, there is a need to upgrade the traditional \"best-effort\" Ethernet to provide \"deterministic\" services.
Time Sensitive Networking (TSN) brings determinism and real-time communication to standard Ethernet through mechanisms and protocols defined by the IEEE 802.1 standard, which is used by Audio Video Bridging (AVB) and TSN. It offers reliable message delivery, minimized jitter, and guaranteed delivery through central management, time scheduling, and other key features. The introduction of TSN technology holds great potential and benefits for real-time applications in industrial control, automation, and other fields.
TSN_OSI_layer
"},{"location":"blog/TSN/#tsn-standard","title":"TSN Standard","text":"There are a lot of standards that TSN task group has completed or ongoing projects. Here are some base standards.
Standard Title IEEE 1588 V2 Precision Clock Synchronization Protocol for Networked Measurement and Control Systems IEEE 802.1Q-2022 Bridges and Bridged Networks IEEE 802.1AB-2016 Station and Media Access Control Connectivity Discovery (specifies the Link Layer Discovery Protocol (LLDP)) IEEE 802.1AS-2020 Timing and Synchronization for Time-Sensitive Applications IEEE 802.1AX-2020 Link Aggregation IEEE 802.1CB-2017 Frame Replication and Elimination for Reliability IEEE 802.1CS-2020 Link-local Registration Protocol"},{"location":"blog/TSN/#the-role-of-5g-system-in-tsn","title":"The role of 5G system in TSN","text":"With the increasing demands for wireless control in applications such as industrial automation, remote surgical operations, smart grid distribution automation, transportation safety, autonomous driving, and more, there is a growing need to meet the low-latency requirements of these applications while achieving management, scheduling, and traffic planning. Time synchronization becomes a critical aspect. The following will explain how the interaction between TSN and 5G systems enables time synchronization.
"},{"location":"blog/TSN/#time-synchronization","title":"Time Synchronization","text":"To achieve time synchronization between TSN and 5G systems, TSN utilizes the time synchronization method defined in IEEE 802.1AS, which is the generalized Precision Time Protocol (gPTP). gPTP supports time synchronization for Time-aware end stations and Time-aware Bridges in Layer 2. In the 3GPP TS23.501 release 16 specification, the 5G system plays the role of a \"Time-aware system\" as defined in IEEE 802.1AS and is designated as a Logical bridge, connecting TSN system end stations.
Note
gPTP is an extended version of PTP (Precision Time Protocol) that primarily expands support for second-layer network devices.
How can we synchronize the time of two end stations into the same time domain?
First, the time synchronization architecture includes Master clocks and Slave clocks. The Master regularly sends sync messages to allow the Slave to obtain the Master's time. The Slave, in turn, periodically sends peer delay requests to exchange messages with the Master, obtaining the delay time between the two devices for time correction. Additionally, the resident time, which is the message propagation delay introduced by bridges, should also be taken into account. By considering all these factors, the time synchronization of both sides can be achieved within the TSN time domain.
Time Synchronization process of gPTP
Check the link for more detail about how PTP works.
"},{"location":"blog/TSN/#intergration-of-tsn-and-5g","title":"Intergration of TSN and 5G","text":"By now, I believe you have gained an understanding of the time synchronization mechanism in TSN. Let's briefly explain how the 5G system supports TSN as a logical TSN bridge. The 3GPP has defined new functionalities such as NW-TT, DS-TT, and TSN-AF, as well as TSN control nodes like CUC and CNC. Please check TS 23.501 Release 16 for more details.
System architecture of 5G support TSN
"},{"location":"blog/TSN/#support-ethernet-type-pdu-session","title":"Support Ethernet type PDU session","text":"To archive the intergration, 5G system should support ingress port and egress port pair via an Ethernet Type PDU session between the corresponding UE and UPF. As mentioned above, gPTP supports layer 2 (Ethernet) only.
"},{"location":"blog/TSN/#ds-tt-and-nw-tt","title":"DS-TT and NW-TT","text":"In the 5G system, DS-TT (Device-side TSN translator) and NW-TT (Network-side TSN translator) serve as TSN translators. DS-TT is responsible for connecting TSN Slave endpoints with the UE, while NW-TT connects TSN Master endpoints with the UPF.
When the sync message generated by the Master clock reaches the bridge, NW-TT captures its Ingress Timestamp and measures the delay between NW-TT and the Master clock. These timestamps are then embedded within the sync message and transmitted to the UE. Once the UE receives the sync message, DS-TT calculates the resident time by subtracting the Ingress Timestamp provided in the sync message, from the Egress Timestamp which represents the time of sync message reception. The resident time is added to the delay time mentioned in the sync message to determine the corrected time. Through the assistance of the TSN translators, the Slave endpoint receives the message and obtains information about time deviation and other relevant data for further adjustment.
Note
DS-TT and NW-TT enable the 5G system to function as a virtual bridge. The bridge is also called \"Transparent clock\" which is definded in IEEE 1588 and required in IEEE 802.1AS. You can say that Master and Slaver don't know the exist of the 5G TSN bridge, since it's logical transparent.
\"Transparent clocks are used to route timing messages within a network. Used when: Ethernet timing must pass through switches.\" - different type of clocks
"},{"location":"blog/TSN/#tsn-af","title":"TSN-AF","text":"With TSN-AF, CNC can manage the 5G system functioning as a logical bridge and achieve the integration of the 5G TSN bridge with the TSN network in collaboration with NW-TT and DS-TT. Additionally, TSN-AF gathers information and capability lists of the 5G TSN Bridge and transmits them to CNC.
"},{"location":"blog/TSN/#tsn-control-nodes","title":"TSN control nodes","text":"To meet the requirements of application services and control TSN, there are two key functions utilized in the TSN system. CNC (Centralized Network Controller), as the central controller in the TSN system, receives the information from CUC (Centralized User Configuration) and performs scheduling and planning tasks. It calculates the optimal transmission schedule for the TSN traffic based on factors such as bandwidth requirements, latency constraints, and network conditions. Once the transmission schedule is computed and confirmed, CNC proceeds to deploy the necessary network resource configuration on the TSN switches. This ensures that the TSN network operates efficiently and effectively in delivering the required QoS (Quality of Service) for the application services.
"},{"location":"blog/TSN/#reference","title":"Reference","text":"Hi, This is Ya-shih Tseng. I am currently researching the implementation of 5G TSN (Time-Sensitive Networking) as part of my master's studies. In the future, I will introduce more information about TSN. Hope you enjoy it.
"},{"location":"blog/UDM_introduce/","title":"Network function UDM introduction","text":"Note
Author: \u5f35\u54f2\u777f Date: 2023/7/19
"},{"location":"blog/UDM_introduce/#overview","title":"Overview","text":"In this article, I will introduce UDM and its three services that will be used in the general UE registration procedure (Nudm_UECM service, Nudm_SubscriberDataManagement Service, and Nudm_UEAuthentication service) to let everyone understand UDM more clearly.
"},{"location":"blog/UDM_introduce/#udm","title":"UDM","text":"Unified Data Management is responsible for managing information related to UE. When other NFs need to use the UE subscription information, they will obtain it from UDM through the SBI of UDM.
"},{"location":"blog/UDM_introduce/#nudm_ueauthentication-service","title":"Nudm_UEAuthentication Service","text":"This service is used by AUSF to retrieve authentication-related information and, after authentication, confirm the result.
3GPP TS33.501 v15.2.0
In Authentication, AUSF uses the GET operation to retrieve authentication information for the UE. The request contains the UE\u2019s identity (supi or suci) and the serving network name. The serving network name is used in the derivation of the anchor key, which is used by subsensual authentication. UE\u2019s identity will be contained in the URI, and the serving network name will be contained in the request body.
Upon reception of the Nudm_UEAuthentication_Get Request, the UDM shall de-conceal SUCI to gain SUPI if SUCI is received. At this time, UDM will query the authentication subscription data from UDR. Then, UDM shall select the authentication method based on SUPI, and if required (e.g., 5G-AKA), UDM will calculate the authentication vector and pass it to AUSF.
logger.UeauLog.Traceln(\"In GenerateAuthDataProcedure\")\n\nresponse = &models.AuthenticationInfoResult{}\nrand.Seed(time.Now().UnixNano())\nsupi, err := suci.ToSupi(supiOrSuci, udm_context.Getself().SuciProfiles)\nif err != nil {\nproblemDetails = &models.ProblemDetails{\nStatus: http.StatusForbidden,\nCause: authenticationRejected,\nDetail: err.Error(),\n}\n\nlogger.UeauLog.Errorln(\"suciToSupi error: \", err.Error())\nreturn nil, problemDetails\n}\n\nlogger.UeauLog.Tracef(\"supi conversion => [%s]\", supi)\n\nclient, err := createUDMClientToUDR(supi)\nif err != nil {\nreturn nil, openapi.ProblemDetailsSystemFailure(err.Error())\n}\nauthSubs, res, err := client.AuthenticationDataDocumentApi.QueryAuthSubsData(context.Background(), supi, nil)\n\n//in the udm/internal/sbi/producer/generate_auth_data.go, GenerateAuthDataProcedure function.\n
From the code, we can see UDM first de-conceal SUCI (line 5), then use QueryAuthSubsData to get authSub from UDR. After that, UDM uses this information to create the authentication vector.
Then we record the packet sent in the registration process and find the packet according to the URI specified by the specification. We can find the packet corresponding to this service.
Open the response packet, and we can see the response body matches the AuthenticationInfoResult data type.
3GPP TS29.503 v15.2.1
After AUSF authenticates the UE, it will confirm the result with UDM. These details will be used in linking authentication confirmation to the Nudm_UECM_Registration procedure from AMF.
func communicateWithUDM(ue *context.AmfUe, accessType models.AccessType) error {\nue.GmmLog.Debugln(\"communicateWithUDM\")\namfSelf := context.GetSelf()\n\n// UDM selection described in TS 23.501 6.3.8\n// TODO: consider udm group id, Routing ID part of SUCI, GPSI or External Group ID (e.g., by the NEF)\nparam := Nnrf_NFDiscovery.SearchNFInstancesParamOpts{\nSupi: optional.NewString(ue.Supi),\n}\nresp, err := consumer.SendSearchNFInstances(amfSelf.NrfUri, models.NfType_UDM, models.NfType_AMF, ¶m)\nif err != nil {\nreturn errors.Errorf(\"AMF can not select an UDM by NRF: SendSearchNFInstances failed\")\n}\n\nvar uecmUri, sdmUri string\nfor _, nfProfile := range resp.NfInstances {\nue.UdmId = nfProfile.NfInstanceId\nuecmUri = util.SearchNFServiceUri(nfProfile, models.ServiceName_NUDM_UECM, models.NfServiceStatus_REGISTERED)\nsdmUri = util.SearchNFServiceUri(nfProfile, models.ServiceName_NUDM_SDM, models.NfServiceStatus_REGISTERED)\nif uecmUri != \"\" && sdmUri != \"\" {\nbreak\n}\n}\nue.NudmUECMUri = uecmUri\nue.NudmSDMUri = sdmUri\nif ue.NudmUECMUri == \"\" || ue.NudmSDMUri == \"\" {\nreturn errors.Errorf(\"AMF can not select an UDM by NRF: SearchNFServiceUri failed\")\n}\n\nproblemDetails, err := consumer.UeCmRegistration(ue, accessType, true)\nif problemDetails != nil {\nreturn errors.Errorf(problemDetails.Cause)\n} else if err != nil {\nreturn errors.Wrap(err, \"UECM_Registration Error\")\n}\n\n// TS 23.502 4.2.2.2.1 14a-c.\n// \"After a successful response is received, the AMF subscribes to be notified\n// using Nudm_SDM_Subscribe when the data requested is modified\"\nproblemDetails, err = consumer.SDMGetAmData(ue)\nif problemDetails != nil {\nreturn errors.Errorf(problemDetails.Cause)\n} else if err != nil {\nreturn errors.Wrap(err, \"SDM_Get AmData Error\")\n}\n\nproblemDetails, err = consumer.SDMGetSmfSelectData(ue)\nif problemDetails != nil {\nreturn errors.Errorf(problemDetails.Cause)\n} else if err != nil {\nreturn errors.Wrap(err, \"SDM_Get SmfSelectData Error\")\n}\n\nproblemDetails, err = consumer.SDMGetUeContextInSmfData(ue)\nif problemDetails != nil {\nreturn errors.Errorf(problemDetails.Cause)\n} else if err != nil {\nreturn errors.Wrap(err, \"SDM_Get UeContextInSmfData Error\")\n}\n\nproblemDetails, err = consumer.SDMSubscribe(ue)\nif problemDetails != nil {\nreturn errors.Errorf(problemDetails.Cause)\n} else if err != nil {\nreturn errors.Wrap(err, \"SDM Subscribe Error\")\n}\nue.ContextValid = true\nreturn nil\n}\n\n\n//in the amf/internal/gmm/handler.go.\n
Next, let's take a look at this function. It is called in HandleInitialRegistration, which handles UE's initial registration. UeCmRegistration will use the Nudm_UECM (UECM) service to store related UE Context Management information in UDM. In lines 40, 47, and 54, AMF uses the Nudm_SubscriberDataManagement (SDM) Service to get some subscribe data.
"},{"location":"blog/UDM_introduce/#nudm_uecontextmanagement-service","title":"Nudm_UEContextManagement Service","text":"In the UeCmRegistration function, AMF registers as UE's serving NF on UDM and stores related UE Context Management information in UDM. Looking at the packet, you can see that the request body contains amfInstanceId
and guami
, representing the amf identity, and ratType
, representing the radio access technology type used by UE.
// TS 29.503 5.3.2.2.2\nfunc RegistrationAmf3gppAccessProcedure(registerRequest models.Amf3GppAccessRegistration, ueID string) (\nheader http.Header, response *models.Amf3GppAccessRegistration, problemDetails *models.ProblemDetails,\n) {\n// TODO: EPS interworking with N26 is not supported yet in this stage\nvar oldAmf3GppAccessRegContext *models.Amf3GppAccessRegistration\nif udm_context.Getself().UdmAmf3gppRegContextExists(ueID) {\nue, _ := udm_context.Getself().UdmUeFindBySupi(ueID)\noldAmf3GppAccessRegContext = ue.Amf3GppAccessRegistration\n}\n\nudm_context.Getself().CreateAmf3gppRegContext(ueID, registerRequest)\n\nclientAPI, err := createUDMClientToUDR(ueID)\nif err != nil {\nreturn nil, nil, openapi.ProblemDetailsSystemFailure(err.Error())\n}\n\nvar createAmfContext3gppParamOpts Nudr_DataRepository.CreateAmfContext3gppParamOpts\noptInterface := optional.NewInterface(registerRequest)\ncreateAmfContext3gppParamOpts.Amf3GppAccessRegistration = optInterface\nresp, err := clientAPI.AMF3GPPAccessRegistrationDocumentApi.CreateAmfContext3gpp(context.Background(),\nueID, &createAmfContext3gppParamOpts)\nif err != nil {\nlogger.UecmLog.Errorln(\"CreateAmfContext3gpp error : \", err)\nproblemDetails = &models.ProblemDetails{\nStatus: int32(resp.StatusCode),\nCause: err.(openapi.GenericOpenAPIError).Model().(models.ProblemDetails).Cause,\nDetail: err.Error(),\n}\nreturn nil, nil, problemDetails\n}\ndefer func() {\nif rspCloseErr := resp.Body.Close(); rspCloseErr != nil {\nlogger.UecmLog.Errorf(\"CreateAmfContext3gpp response body cannot close: %+v\", rspCloseErr)\n}\n}()\n\n// TS 23.502 4.2.2.2.2 14d: UDM initiate a Nudm_UECM_DeregistrationNotification to the old AMF\n// corresponding to the same (e.g. 3GPP) access, if one exists\nif oldAmf3GppAccessRegContext != nil {\nderegistData := models.DeregistrationData{\nDeregReason: models.DeregistrationReason_SUBSCRIPTION_WITHDRAWN,\nAccessType: models.AccessType__3_GPP_ACCESS,\n}\ncallback.SendOnDeregistrationNotification(ueID, oldAmf3GppAccessRegContext.DeregCallbackUri,\nderegistData) // Deregistration Notify Triggered\n\nreturn nil, nil, nil\n} else {\nheader = make(http.Header)\nudmUe, _ := udm_context.Getself().UdmUeFindBySupi(ueID)\nheader.Set(\"Location\", udmUe.GetLocationURI(udm_context.LocationUriAmf3GppAccessRegistration))\nreturn header, ®isterRequest, nil\n}\n}\n\n//in the udm/internal/sbi/producer/ue_context_management.go\n
In the RegistrationAmf3gppAccessProcedure function, UDM first checks whether the context has been established for that UE; if UDM has such a context, it initiates a Nudm_UECM_DeregistrationNotification to the old AMF later. UDM used the received information to create context and stored it in UDR.
"},{"location":"blog/UDM_introduce/#nudm_subscriberdatamanagement-sdm-service","title":"Nudm_SubscriberDataManagement (SDM) Service","text":"The SDM service is used to retrieve the UE's individual subscription data relevant to the consumer's NF from the UDM. In the SDMGetAmData function, AMF gets subscription data used in registration and mobility management. In the response packet, AMF got gpsis
, subscribedUeAmbr
, and nssai
.
The GPSI (Generic Public Subscription Identifier) is used to address a 3GPP subscription in data networks outside the realms of a 3GPP system. It contains either an External ID or an MSISDN \uff08Mobile Subscriber ISDN Number\uff09.The subscribedUeAmbr
is The Maximum Aggregated uplink and downlink MBRs (max. bit rate) to be shared across all Non-GBR (non-guaranteed Bit Rate) QoS Flows according to the subscription of the user.
In the SDMGetSmfSelectData function, AMF gets subscribed S-NSSAIs (Single Network Slice Selection Assistance Information) and Data Network Names for these S-NSSAIs. AMF will use this information to select an SMF that manages the PDU Session.
func HandleInitialRegistration(ue *context.AmfUe, anType models.AccessType) error {\nue.GmmLog.Infoln(\"Handle InitialRegistration\")\n\namfSelf := context.GetSelf()\n\n// update Kgnb/Kn3iwf\nue.UpdateSecurityContext(anType)\n\n// Registration with AMF re-allocation (TS 23.502 4.2.2.2.3)\nif len(ue.SubscribedNssai) == 0 {\ngetSubscribedNssai(ue)\n}\n\nif err := handleRequestedNssai(ue, anType); err != nil {\nreturn err\n}\n\n//in the amf/internal/gmm/handler.go.\n
In the initialization of HandleInitialRegistration, AMF sends a request to the UDM to receive the UE's NSSAI (Network Slice Selection Assistance Information). After receiving subscribed NSSAI, AMF will compare it to UE's requested NSSAI. If there is a S-NSSAI that has not been subscribed before, AMF will request NSSF for Allowed NSSAI.
func handleRequestedNssai(ue *context.AmfUe, anType models.AccessType) error {\namfSelf := context.GetSelf()\n\nif ue.RegistrationRequest.RequestedNSSAI != nil {\nrequestedNssai, err := nasConvert.RequestedNssaiToModels(ue.RegistrationRequest.RequestedNSSAI)\nif err != nil {\nreturn fmt.Errorf(\"Decode failed at RequestedNSSAI[%s]\", err)\n}\n\nneedSliceSelection := false\nfor _, requestedSnssai := range requestedNssai {\nue.GmmLog.Infof(\"RequestedNssai - ServingSnssai: %+v, HomeSnssai: %+v\",\nrequestedSnssai.ServingSnssai, requestedSnssai.HomeSnssai)\nif ue.InSubscribedNssai(*requestedSnssai.ServingSnssai) {\nallowedSnssai := models.AllowedSnssai{\nAllowedSnssai: &models.Snssai{\nSst: requestedSnssai.ServingSnssai.Sst,\nSd: requestedSnssai.ServingSnssai.Sd,\n},\nMappedHomeSnssai: requestedSnssai.HomeSnssai,\n}\nif !ue.InAllowedNssai(*allowedSnssai.AllowedSnssai, anType) {\nue.AllowedNssai[anType] = append(ue.AllowedNssai[anType], allowedSnssai)\n}\n} else {\nneedSliceSelection = true\nbreak\n}\n}\n\nif needSliceSelection {\nif ue.NssfUri == \"\" {\nfor {\nerr := consumer.SearchNssfNSSelectionInstance(ue, amfSelf.NrfUri, models.NfType_NSSF, models.NfType_AMF, nil)\nif err != nil {\nue.GmmLog.Errorf(\"AMF can not select an NSSF Instance by NRF[Error: %+v]\", err)\ntime.Sleep(2 * time.Second)\n} else {\nbreak\n}\n}\n}\n\n// Step 4\nproblemDetails, err := consumer.NSSelectionGetForRegistration(ue, requestedNssai)\nif problemDetails != nil {\nue.GmmLog.Errorf(\"NSSelection Get Failed Problem[%+v]\", problemDetails)\ngmm_message.SendRegistrationReject(ue.RanUe[anType], nasMessage.Cause5GMMProtocolErrorUnspecified, \"\")\nreturn fmt.Errorf(\"Handle Requested Nssai of UE failed\")\n} else if err != nil {\nue.GmmLog.Errorf(\"NSSelection Get Error[%+v]\", err)\ngmm_message.SendRegistrationReject(ue.RanUe[anType], nasMessage.Cause5GMMProtocolErrorUnspecified, \"\")\nreturn fmt.Errorf(\"Handle Requested Nssai of UE failed\")\n}\n\n//in the amf/internal/gmm/handler.go.\n
if param.SliceInfoRequestForRegistration.RequestedNssai != nil &&\nlen(param.SliceInfoRequestForRegistration.RequestedNssai) != 0 {\n// Requested NSSAI is provided\n// Verify which S-NSSAI(s) in the Requested NSSAI are permitted based on comparing the Subscribed S-NSSAI(s)\nif param.Tai != nil &&\n!util.CheckSupportedNssaiInPlmn(param.SliceInfoRequestForRegistration.RequestedNssai, *param.Tai.PlmnId) {\n// Return ProblemDetails indicating S-NSSAI is not supported\n// TODO: Based on TS 23.501 V15.2.0, if the Requested NSSAI includes an S-NSSAI that is not valid in the\n// Serving PLMN, the NSSF may derive the Configured NSSAI for Serving PLMN\n*problemDetails = models.ProblemDetails{\nTitle: util.UNSUPPORTED_RESOURCE,\nStatus: http.StatusForbidden,\nDetail: \"S-NSSAI in Requested NSSAI is not supported in PLMN\",\nCause: \"SNSSAI_NOT_SUPPORTED\",\n}\n\nstatus = http.StatusForbidden\nreturn status\n}\n\n// Check if any Requested S-NSSAIs is present in Subscribed S-NSSAIs\ncheckIfRequestAllowed := false\n\nfor _, requestedSnssai := range param.SliceInfoRequestForRegistration.RequestedNssai {\nif param.Tai != nil && !util.CheckSupportedSnssaiInTa(requestedSnssai, *param.Tai) {\n// Requested S-NSSAI does not supported in UE's current TA\n// Add it to Rejected NSSAI in TA\nauthorizedNetworkSliceInfo.RejectedNssaiInTa = append(\nauthorizedNetworkSliceInfo.RejectedNssaiInTa,\nrequestedSnssai)\ncontinue\n}\n\nvar mappingOfRequestedSnssai models.Snssai\n// TODO: Compared with Restricted S-NSSAI list in configuration under roaming scenario\nif param.HomePlmnId != nil && !util.CheckStandardSnssai(requestedSnssai) {\n// Standard S-NSSAIs are supported to be commonly decided by all roaming partners\n// Only non-standard S-NSSAIs are required to find mappings\ntargetMapping, found := util.FindMappingWithServingSnssai(requestedSnssai,\nparam.SliceInfoRequestForRegistration.MappingOfNssai)\n\nif !found {\n// No mapping of Requested S-NSSAI to HPLMN S-NSSAI is provided by UE\n// TODO: Search for local configuration if there is no provided mapping from UE, and update UE's\n// Configured NSSAI\ncheckInvalidRequestedNssai = true\nauthorizedNetworkSliceInfo.RejectedNssaiInPlmn = append(\nauthorizedNetworkSliceInfo.RejectedNssaiInPlmn,\nrequestedSnssai)\ncontinue\n} else {\n// TODO: Check if mappings of S-NSSAIs are correct\n// If not, update UE's Configured NSSAI\nmappingOfRequestedSnssai = *targetMapping.HomeSnssai\n}\n} else {\nmappingOfRequestedSnssai = requestedSnssai\n}\n\nhitSubscription := false\nfor _, subscribedSnssai := range param.SliceInfoRequestForRegistration.SubscribedNssai {\nif mappingOfRequestedSnssai == *subscribedSnssai.SubscribedSnssai {\n// Requested S-NSSAI matches one of Subscribed S-NSSAI\n// Add it to Allowed NSSAI list\nhitSubscription = true\n\nvar allowedSnssaiElement models.AllowedSnssai\nallowedSnssaiElement.AllowedSnssai = new(models.Snssai)\n*allowedSnssaiElement.AllowedSnssai = requestedSnssai\nnsiInformationList := util.GetNsiInformationListFromConfig(requestedSnssai)\nif nsiInformationList != nil {\n// TODO: `NsiInformationList` should be slice in `AllowedSnssai` instead of pointer of slice\nallowedSnssaiElement.NsiInformationList = append(\nallowedSnssaiElement.NsiInformationList,\nnsiInformationList...)\n}\nif param.HomePlmnId != nil && !util.CheckStandardSnssai(requestedSnssai) {\nallowedSnssaiElement.MappedHomeSnssai = new(models.Snssai)\n*allowedSnssaiElement.MappedHomeSnssai = *subscribedSnssai.SubscribedSnssai\n}\n\n// Default Access Type is set to 3GPP Access if no TAI is provided\n// TODO: Depend on operator implementation, it may also return S-NSSAIs in all valid Access Type if\n// UE's Access Type could not be identified\nvar accessType models.AccessType = models.AccessType__3_GPP_ACCESS\nif param.Tai != nil {\naccessType = util.GetAccessTypeFromConfig(*param.Tai)\n}\n\nutil.AddAllowedSnssai(allowedSnssaiElement, accessType, authorizedNetworkSliceInfo)\n\ncheckIfRequestAllowed = true\nbreak\n}\n}\n\nif !hitSubscription {\n// Requested S-NSSAI does not match any Subscribed S-NSSAI\n// Add it to Rejected NSSAI in PLMN\ncheckInvalidRequestedNssai = true\nauthorizedNetworkSliceInfo.RejectedNssaiInPlmn = append(\nauthorizedNetworkSliceInfo.RejectedNssaiInPlmn,\nrequestedSnssai)\n}\n}\n\nif !checkIfRequestAllowed {\n// No S-NSSAI from Requested NSSAI is present in Subscribed S-NSSAIs\n// Subscribed S-NSSAIs marked as default are used\nuseDefaultSubscribedSnssai(param, authorizedNetworkSliceInfo)\n}\n} else {\n// No Requested NSSAI is provided\n// Subscribed S-NSSAIs marked as default are used\ncheckInvalidRequestedNssai = true\nuseDefaultSubscribedSnssai(param, authorizedNetworkSliceInfo)\n}\n\n//in the nssf/internal/sbi/producer/nsselection_for_registration.go, nsselectionForRegistration funcion.\n
If NSSF needs to select S-NSSAI, it first finds the mapping of requested NSSAI to configured NSSAI for the HPLMN and converts requested S-NSSAI to S-NSSAI in configured NSSAI for the HPLMN. Then compare these S-NSSAIs with Subscribed S-NSSAIs; if NSSF find one match, set it as AllowedSnssai
. If NSSF can't find such a mapping or no S-NSSAI in the mapping matches subscribed S-NSSAIs, it will use default subscribed S-NSSAIs.
Hello! My name is \u5f35\u54f2\u777f, and my current research topic is ATSSS (Access Traffic Steering, Switching and Splitting), I will continue to write articles related to 5G networks in the future. If you find any mistakes in my articles or have any topics you want to know about, please contact me.
Note
Author: Daniel Hsieh Date: 2023/7/26
"},{"location":"blog/network_slice/#whats-network-slicing","title":"What's Network Slicing","text":"Network slicing allows for the creation of multiple logical, isolated, and independent virtual networks that can coexist within a shared physical infrastructure. Each network slice provides dedicated and customized network resources to meet the specific requirements of different services The main elements of a network slice include:
Virtualized Network Functions (VNFs): Each network slice can include a set of virtualized network functions that provide specific network capabilities and services. These VNFs can include functions like routing, switching, firewalling, load balancing, or any other network service required by the slice.
Isolation and Resource Allocation: Network slicing ensures the isolation of resources between slices, preventing interference and conflicts. It allows for the allocation of dedicated and optimized resources such as bandwidth, processing power, and storage to each slice based on its specific needs.
Orchestration and Management: Network slice orchestration involves the creation, provisioning, and management of network slices. It involves configuring the appropriate VNFs, assigning resources, and establishing connectivity between the different components of a slice.
NFV Enabling Network Slicing for 5G
Take Figure 1 as an example. The first slice is designed for mobile devices such as smartphones. Such slice requires a huge diversity of VNFs, and virtual links with high speed and low latency to support the broadband service of smartphones. In 5G network, Those slices are referred to as eMBB (enhanced mobile broadband) slices.
The second slice is designed for autonomous driving. In such scenario, extremely low latency and high reliability are paramount to ensure the vehicles' operability, smoothness and safety. To achieve low latency, some of the NFs should be deployed close to the access node,i.e. on edge cloud. To achieve high reliability, a NF should have multiple instances on available physical resources to make the slice more fault tolerant. Such slice is referred to as URLLC (Ultra-Reliable Low-Latency Communications) slice.
The third slice is designed for massive IoT. IoT devices are expected to not move and send very small amount of data intermittently. Due to the nature of such devices, functions that handle mobiltiy and always-on connections are not needed. Such slices are referred to as mIoT (massive IoT) slices.
"},{"location":"blog/network_slice/#mano-architecture","title":"MANO Architecture","text":"In this article, we utilize MANO network function virtualization (NFV) architecture to deploy virtual network function (VNF). It plays the role of creating, deploying, and managing VNFs. MANO consists of three main functional components: NFV Orchestrator (NFVO), Virtualized Infrastructure Manager (VIM), and Virtual Network Function Manager (VNFM).
NFV MANO Architecture
NFVO manages the underlying resource by coordinating VIM and VNFM. It handles tasks such as receiving requests, service instantiation, scaling, termination, and monitoring.
VNFM manages the lifecycle of VNF instances. It interacts with the VIM to instantiate, configure, monitor, and terminate VNF instances.
VIM is responsible for managing the underlying virtualized infrastructure that hosts the VNFs. It abstracts the physical resources, such as compute, storage, and networking, and provides a unified view to the NFVO. The VIM handles tasks like resource allocation, performance monitoring, fault management, and virtualization management.
For VIM, we use OpenStack, an open-source software that provides IaaS, to utilize the physical resources. For VNFM and NFVO, we use Tacker, a service component of OpenStack, to manage VNFs.
"},{"location":"blog/network_slice/#openstack","title":"OpenStack","text":"OpenStack is an open-source cloud computing platform that provides a set of software tools for building and managing customized clouds. OpenStack offers a infrastructure-as-a-service (IaaS) solution, enabling organizations to create and manage virtualized resources in a cloud environment. It is designed to be modular and consists of various components that work together to deliver a comprehensive cloud computing platform. Some of the key components include:
Nova: Nova is the computing component of OpenStack and serves as the main compute engine. It manages the creation, scheduling, and management of virtual machines (VMs) and provides APIs for controlling and interacting with the compute resources.
Cinder: Cinder is the block storage component of OpenStack. It provides persistent storage for virtual machines. With Cinder, users can create and manage volumes that can be attached to instances, allowing for flexible and scalable storage options.
Neutron: Neutron is the networking component of OpenStack. It provides a networking-as-a-service (NaaS) solution, allowing users to define and manage network resources. Neutron supports virtual LANs, software-defined networking (SDN), and network function virtualization (NFV), etc.
Keystone: Keystone is the identity service component of OpenStack. It provides authentication and authorization services, enabling users to securely access and manage resources within the cloud. Keystone supports multiple authentication mechanisms, including username/password, token-based, and external identity providers.
Horizon: Horizon is the web-based dashboard for OpenStack. It provides a user-friendly interface for managing and monitoring the cloud infrastructure. With Horizon, users can perform various tasks, such as launching instances, managing storage resources, and configuring networking options.
OpenStack Architecture
OpenStack is highly flexible and customizable, allowing organizations to tailor the cloud infrastructure to their specific needs. It supports multiple hypervisors, including KVM, VMware, and Hyper-V.
"},{"location":"blog/network_slice/#tacker","title":"Tacker","text":"To enable NFV, we need another service component of OpenStack called Tacker. Tacker is designed to simplify the deployment and lifecycle management of VNFs and network service functions (NSFs) in a cloud infrastructure. It leverages OpenStack's existing components, such as Nova, Neutron, and Heat, to provide a comprehensive solution for network service orchestration. Tacker provides several key features and functionalities:
Service Templates: Tacker uses service templates to define the composition and behavior of network services. These templates describe the VNFs and NSFs involved, their interconnections, resource requirements, etc. Service templates are written using the TOSCA (Topology and Orchestration Specification for Cloud Applications) standard.
Lifecycle Management: Tacker automates the entire lifecycle of network services, including provisioning, scaling, healing, and termination. It leverages Heat, OpenStack's orchestration service, to manage the underlying infrastructure resources required by the services and handle dynamic scaling of VNFs based on traffic demands.
VNF Manager: Tacker includes a VNF Manager component responsible for managing the lifecycle of VNFs. It interacts with OpenStack's compute and networking services, to instantiate and manage VNF instances.
Multi-VIM Support: Tacker supports multiple virtual infrastructure managers to accommodate different cloud platforms and environments. It can interact with OpenStack, VMware vSphere and Kubernetes and so on, enabling operators to deploy network services across heterogeneous infrastructure environments.
Tacker Architecture
"},{"location":"blog/network_slice/#deploy-a-free5gc-network-slice","title":"Deploy a free5GC Network Slice","text":"In our implementation, we install OpenStack and Tacker on two different virtual machines for resource utilization reasons, but in fact, they can be installed on the same virtual machine.
we need to install OpenStack on a virtual machine. Specific details and corresponding compatibility can be found on OpenStack official website. Using devstack scripts for installation enables operators to customize the environment based on their needs, such as extra plugins (softwares that extends the functionality of OpenStack environment) and overcommit (allows deploying NFs that require more resource than existing physical resourcce) functionality. Upon completion, a web UI enabled by Horizon can be used to access and operate on your own personalized OpenStack cloud.
Install Tacker on another virtual machine, which requires four OpenStack service components, Keystone, Mistral, Barbican and Horizon. Once the installation is completed, we can register our OpenStack VIM on Tacker using openstack vim register
command.
Create two instances that will be used as images (one for control plane VNFs, one for UPF) for the VNFs that we will create. Then, ssh
into those instances to set up the configurations for the VNFs, such as, installing required packages (go language, mongodb, libtool, etc.) and git clone
free5GC source code. Once all the configurations are done, use OpenStack dashboard to take snapshots of these instances, which will be used as the images for VNFs.
Import all the VNF descriptors (VNFD) of the VNFs we need by using openstack vnf descriptor create
command. VNFDs should be written in accordance with TOSCA format. TOSCA format allows you to define the virtual links (a virtual network VNFs will be running in) and virtual deployment unit (operation unit of a VNF). Below is an example of UPF VNFD:
tosca_definitions_version: tosca_simple_profile_for_nfv_1_0_0\ndescription: description\nnode_types:\ntosca.nodes.nfv.VNF11:\nrequirements:\n- virtualLink1:\ntype: tosca.nodes.nfv.VL\nrequired: true\nmetadata:\ntemplate_name: free5GCSetup\ntopology_template:\nsubstitution_mappings:\nnode_type: tosca.nodes.nfv.VNF11\nnode_templates:\nVDU1:\ntype: tosca.nodes.nfv.VDU.Tacker\nproperties:\nname: free5gc-upf1-VNF\nimage: stage3-up\nflavor: free5gc\navailability_zone: nova\nmgmt_driver: noop\nkey_name: free5gc\nuser_data_format: RAW\nuser_data: |\n#!/bin/sh\ncd /home/ubuntu/free5gc/src/upf/build \ncat > config/upfcfg.yaml <<- EOM\ninfo:\nversion: 1.0.0\ndescription: UPF configuration\n\nconfiguration:\n# debugLevel: panic|fatal|error|warn|info|debug|trace\ndebugLevel: info\n\npfcp:\n- addr: 192.168.2.111\n\ngtpu:\n- addr: 192.168.2.111\n# [optional] gtpu.name\n# - name: upf.5gc.nctu.me\n# [optional] gtpu.ifname\n# - ifname: gtpif\n\napn_list:\n- apn: internet\ncidr: 60.60.0.0/24\n# [optional] apn_list[*].natifname\n# natifname: eth0\nEOM\n#sudo ./bin/free5gc-upfd -f config/upfcfg.yaml\n\nCP1:\ntype: tosca.nodes.nfv.CP.Tacker\nproperties:\nip_address: 192.168.2.111\nmanagement: true\nrequirements:\n- virtualLink:\nnode: VL1\n- virtualBinding:\nnode: VDU1\nVL1:\ntype: tosca.nodes.nfv.VL\nproperties:\nnetwork_name: 5GC\nvendor: Tacker\nFIP1:\ntype: tosca.nodes.network.FloatingIP\nproperties:\nfloating_network: public\nfloating_ip_address: 172.24.4.111\nrequirements:\n- link:\nnode: CP1\n
openstack ns descriptor create
command. The NSD should also be written in accordance with TOSCA format. Once all the VNFDs and NSD are all successfully imported, we can use openstack ns create
to deploy the network slice. The VNFs specified in the NSD will also be instantiated along with the network slice. Their instances can be viewed on OpenStack dashboard enabled by Horizon or just use openstack vnf list
to check the status of the VNFs. Below is an example of NSD tosca_definitions_version: tosca_simple_profile_for_nfv_1_0_0\ndescription: Import Common Slice VNFDs (already on-boarded)\nimports:\n- mongo\n- nrf\n- amf\n- smf\n- udr\n- pcf\n- udm\n- nssf\n- ausf\ntopology_template:\nnode_templates:\nVNF0:\ntype: tosca.nodes.nfv.VNF0\nVNF1:\ntype: tosca.nodes.nfv.VNF1\nVNF2:\ntype: tosca.nodes.nfv.VNF2\nVNF3:\ntype: tosca.nodes.nfv.VNF3\nVNF4:\ntype: tosca.nodes.nfv.VNF4\nVNF5:\ntype: tosca.nodes.nfv.VNF5\nVNF6:\ntype: tosca.nodes.nfv.VNF6\nVNF7:\ntype: tosca.nodes.nfv.VNF7\nVNF8:\ntype: tosca.nodes.nfv.VNF8\n
ssh
into the VNF instances to make the necessary configuration for each VNF and start the free5GC VNF.There are many other ways to set up a network slice. For example, we can deploy VNFs of the same network slice on different VIMs, or we can deploy all the network slices on the same VIM, as long as it is specified in the VNFDs.
"},{"location":"blog/network_slice/#about","title":"About","text":"Hi, my name is Daniel Hsieh. I am a CS major graduate student. My research field is network slicing. If there are any questions about the article, please feel free to contact.
https://www.acecloudhosting.com/blog/openstack-the-catalyst-of-the-public-cloud-market/
https://telcocloudbridge.com/blog/a-beginners-guide-to-nfv-management-orchestration-mano/
https://wiki.openstack.org/wiki/Tacker
B. Chatras, U. S. Tsang Kwong and N. Bihannic, \"NFV enabling network slicing for 5G,\" 2017 20th Conference on Innovations in Clouds, Internet and Networks (ICIN), Paris, France, 2017, pp. 219-225, doi: 10.1109/ICIN.2017.7899415.
This article is intended for individuals who possess an interest in free5gc/webconsole and hold concerns regarding security matters. It aims to provide a concise introduction to the webconsole, followed by an exposition of a significant security concern along with our corresponding solution. Within webconsole v1.2.0, aligning with the most recent iteration of free5gc v3.3.0, certain vulnerabilities have been identified that could potentially lead to the exposure of subscriber data. It is my responsibility to address and rectify these vulnerabilities, enhancing the webconsole's resilience against cyber attacks.
"},{"location":"blog/CSRF/20230823/#webconsole-overview","title":"Webconsole Overview","text":"The Webconsole serves as a web-based tool designed to manage User Equipment (UE) subscription data. It plays a crucial role in aiding the free5GC Core Network manager by facilitating the configuration of UEs and providing the ability to monitor the status of activated UEs.
"},{"location":"blog/CSRF/20230823/#environment","title":"Environment","text":"Prior to building webconsole, install nodejs and yarn package first:
sudo apt remove cmdtest\nsudo apt remove yarn\ncurl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -\necho \"deb https://dl.yarnpkg.com/debian/ stable main\" | sudo tee /etc/apt/sources.list.d/yarn.list\nsudo apt-get update\nsudo apt-get install -y nodejs yarn\n
To run free5GC webconsole server. The following steps are to be considered.
git clone https://github.com/free5gc/webconsole.git\ncd frontend\nyarn install\nyarn build\nrm -rf ../public\ncp -R build ../public\ncd ..\ngo run server.go\n
Default account and password is admin/free5gc
Creation/deletion/editing the subscriber's data:
A Subscriber data contains these informations: - PLMN ID - SUPI (UE ID) - AKA parameters - S-NSSAI Configurations - Sst/Sd - DNN - Name - AMBR - Flow Rules - IP Filter - Precedence - 5QI - GBR - MBR
"},{"location":"blog/CSRF/20230823/#tenant-and-user","title":"TENANT AND USER","text":"The Webconsole also allows for the creation, deletion, and editing of tenants. A tenant functions as an access control group, delineating specific permissions and boundaries. In this setup, if you do not possess admin privileges, you are unable to access subscriber data generated by other tenants, ensuring data privacy and security.
Furthermore, the capability to incorporate users within a tenant is available. To illustrate, by selecting the brian1 tenant and clicking on the New User option, it becomes possible to introduce a new user. As an example, a user with the email address aaabbb@gmail.com can be added through this process.
The data within MongoDB can be accessed and reviewed using the MongoDBCompass tool.
"},{"location":"blog/CSRF/20230823/#csrf-cross-site-request-forgery-vulnerability","title":"CSRF (Cross-Site Request Forgery) Vulnerability","text":"The vulnerability was discovered by INCIBE, and they promptly notified the free5GC team via email.
The corresponding issue related to this vulnerability is also documented in the free5gc repository. Despite the typical deployment of the webconsole within LAN or Docker environments, it's essential to exercise caution regarding users who operate this service on a public IP or within an insecure network environment.
In a nutshell, an attacker can gain unauthorized access to the database by merely setting the token to the term 'admin'.
$ curl '<webconsole's IP>:5000/api/subscriber' -H 'User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:103.0) Gecko/20100101 Firefox/103.0' -H 'Accept: application/json' -H 'Accept-Language: en-US,en;q=0.5' -H 'Accept-Encoding: gzip, deflate' -H 'Referer: http://<webconsole's IP>:5000/' -H 'Connection: keep-alive' -H 'X-Requested-With: XMLHttpRequest' -H 'Token: admin' -H 'Pragma: no-cache' -H 'Cache-Control: no-cache'\n
Subsequently, they can directly retrieve subscriber information from the server's MongoDB.
[{\"plmnID\":\"20893\",\"ueId\":\"imsi-208930000000003\"}]\n
Undoubtedly, this vulnerability is of significant concern since the intended safeguard, allowing access solely to admin, has been compromised, thereby enabling easy access for anyone.
"},{"location":"blog/CSRF/20230823/#trace-code","title":"Trace Code","text":""},{"location":"blog/CSRF/20230823/#frontend","title":"Frontend","text":"In webconsole/frontend/src/util/AuthHelper.js
- In scenarios where the default username and password (admin/free5gc) are employed, the ApiHelper.login()
function remains untouched. This practice might expedite agile development, but it comes at the cost of compromising security.
In webconsole/frontend/WebUI/api_webui.go
- In situations where a webconsole client configures the tokenStr
as 'admin', the backend process will omit the execution of ParseJWT()
.
The Webconsole relies on JSON Web Token (JWT) as its authentication mechanism, a specification outlined in RFC 7519.
JSON Web Token (JWT) is a compact, URL-safe means of representing claims to be transferred between two parties. The claims in a JWT are encoded as a JSON object that is used as the payload of a JSON Web Signature (JWS) structure or as the plaintext of a JSON Web Encryption (JWE) structure, enabling the claims to be digitally signed or integrity protected with a Message Authentication Code (MAC) and/or encrypted.
When a client employs a web browser to initiate a login via the HTTP(s) protocol, the Web server is expected to furnish the client with a JWT token in response. Subsequently, the client employs this JWT token to interact with resources by sending requests through the RESTful API (such as GET, POST, PUT, etc.).
A JSON Web Token (JWT) consists of three distinct parts. In the context of the webconsole backend, the following fields are relevant: - Header: This section contains information about the type of token and the signing algorithm used. It often includes the \"alg\" (algorithm) and \"typ\" (type) fields. - Payload: The payload holds the actual claims or data that are being conveyed by the token. For the webconsole backend, specific fields within this section could include: - Claim (JSON object) - sub
: identifies the principal that is the subject of the JWT. - iat
: identifies the time at which the JWT was issued. - exp
: identifies the expiration time onor after which the JWT MUST NOT be accepted for processing. - email
- tenantId
- ...(you can design the attribute yourself) - Signature: This component is created by combining the encoded header and payload with a secret key (or a public/private key pair) to ensure the token's integrity and authenticity. The signature allows the recipient to verify that the token hasn't been tampered with.
The JWT Claims Set represents a JSON object whose members are the claims conveyed by the JWT. The Claim Names within a JWT Claims Set MUST be unique; JWT parsers MUST either reject JWTs with duplicate Claim Names or use a JSON parser that returns only the lexically last duplicate member name
The image depicted below illustrates the process of using jwt.io to both encode and decode JWT tokens. These tokens are segmented into distinct sections denoted by the red, purple and blue divisions, separated by periods dots.
Given that the Payload can be decoded using the algorithm specified in the Header, it's essential to refrain from including sensitive details like passwords or credit card numbers within it. Instead, the Payload typically holds claims and application-specific metadata.
To maintain the security of the process, the server retains a confidential key used to validate the signature. In situations where a client endeavors to access a resource using a JWT token that possesses an incorrect Verify Signature, an error response will be generated. This stringent signature verification mechanism ensures that the authenticity and integrity of both the token and its enclosed data are upheld.
"},{"location":"blog/CSRF/20230823/#trace-code-cont","title":"Trace Code (Cont.)","text":"In webconsole/frontend/WebUI/api_webui.go
, we can find the implementation of JWT. - JWT()
is for encoding: ```go=394 func JWT(email, userId, tenantId string) string { token := jwt.New(jwt.SigningMethodHS256)
claims := token.Claims.(jwt.MapClaims)\nclaims[\"sub\"] = userId\nclaims[\"iat\"] = time.Now()\nclaims[\"exp\"] = time.Now().Add(time.Hour * 24).Unix()\nclaims[\"email\"] = email\nclaims[\"tenantId\"] = tenantId\n\nif jwtKey == \"\" {\n return \"\"\n}\n\ntokenString, err := token.SignedString([]byte(jwtKey))\nif err != nil {\n logger.ProcLog.Errorf(\"JWT err: %+v\", err)\n return \"\"\n}\n\nreturn tokenString\n
} - `ParseJWT()` is for decoding:
go=491 func ParseJWT(tokenStr string) (jwt.MapClaims, error) { token, err := jwt.Parse(tokenStr, func(token jwt.Token) (interface{}, error) { return []byte(jwtKey), nil }) if err != nil { return nil, errors.Wrap(err, \"ParseJWT error\") } claims, _ := token.Claims.(jwt.MapClaims) return claims, nil } - The function `CheckAuth()` serves the purpose of determining whether a user possesses the authorization to access a particular resource.
go=505 func CheckAuth(c gin.Context) bool { tokenStr := c.GetHeader(\"Token\") if tokenStr == \"admin\" { return true } else { return false } } `` ::: - The secret key utilized for signature verification is obtained through
os.Getenv(\"SIGNINGKEY\"). However, there's a possibility that
SIGNINGKEYmight not be exported as an environment variable, leading to a potential return of an empty value. Under such circumstances, an implication arises: an admin in Webconsole A could potentially gain access to subscriber data within Webconsole B. - Within the
CheckAuth()` function, if the client sets the JWT token to 'admin', the function will evaluate to true, effectively allowing the check to be passed. :::
Initially, I have revised the design of the CheckAuth()
function to ensure the mandatory execution of ParseJWT()
. go= func CheckAuth(c *gin.Context) bool { tokenStr := c.GetHeader(\"Token\") claims, err := ParseJWT(tokenStr) if err == nil && claims[\"email\"] == \"admin\" { return true } else { return false } }
Furthermore, I've implemented a second change where, considering that the webconsole v1.2.0 doesn't inherently establish a tenant named 'admin' or a user named 'admin', I propose a more effective approach. During the initialization of the webconsole backend, it is recommended to generate an 'admin' tenant and user. This means that executing go run server.go
within the webconsole/
directory should consistently generate an admin user, thereby fulfilling the initial login requirement.
Certainly, in the backend/WebUI/api_webui.go
file, I propose the addition of a function named SetAdmin()
. To streamline the process and maintain consistency with the rest of the free5GC project, it is recommended to leverage the mongoapi
module established within the free5gc/util
repository. Given the project's heavy reliance on MongoDB, employing mongoapi
over frequent calls to mongo-driver
is essential to ensure efficiency and coherence.
```go= func SetAdmin() { err := mongoapi.RestfulAPIDeleteOne(\"tenantData\", bson.M{\"tenantName\": \"admin\"}) if err != nil { logger.InitLog.Errorf(\"RestfulAPIDeleteOne err: %+v\", err) } err = mongoapi.RestfulAPIDeleteOne(\"userData\", bson.M{\"email\": \"admin\"}) if err != nil { logger.InitLog.Errorf(\"RestfulAPIDeleteOne err: %+v\", err) }
// Create Admin tenant\nlogger.InitLog.Infoln(\"Create tenant: admin\")\n\nadminTenantData := bson.M{\n \"tenantId\": uuid.Must(uuid.NewRandom()).String(),\n \"tenantName\": \"admin\",\n}\n\n_, err = mongoapi.RestfulAPIPutOne(\"tenantData\", bson.M{\"tenantName\": \"admin\"}, adminTenantData)\nif err != nil {\n logger.InitLog.Errorf(\"RestfulAPIPutOne err: %+v\", err)\n}\n\nAmdinTenant, err := mongoapi.RestfulAPIGetOne(\"tenantData\", bson.M{\"tenantName\": \"admin\"})\nif err != nil {\n logger.InitLog.Errorf(\"RestfulAPIGetOne err: %+v\", err)\n}\n\n// Create Admin user\nlogger.InitLog.Infoln(\"Create user: admin\")\n\nhash, err := bcrypt.GenerateFromPassword([]byte(\"free5gc\"), 12)\nif err != nil {\n logger.InitLog.Errorf(\"GenerateFromPassword err: %+v\", err)\n}\n\nadminUserData := bson.M{\n \"userId\": uuid.Must(uuid.NewRandom()).String(),\n \"tenantId\": AmdinTenant[\"tenantId\"],\n \"email\": \"admin\",\n \"encryptedPassword\": string(hash),\n}\n\n_, err = mongoapi.RestfulAPIPutOne(\"userData\", bson.M{\"email\": \"admin\"}, adminUserData)\nif err != nil {\n logger.InitLog.Errorf(\"RestfulAPIPutOne err: %+v\", err)\n}\n
} ```
"},{"location":"blog/CSRF/20230823/#jwt-verify-signature","title":"JWT Verify Signature","text":"Certainly, within the backend/WebUI/api_webui.go
file, I recommend introducing a string variable named jwtKey
to serve as the private key for JWT Verify Signature. Although the length of jwtKey is specified as 256 bytes, it's worth noting that the distinction between 256 bytes and 256 bits is inconsequential in this context. The jwt module will adeptly transform the key to a 256-bit form. For further insights, you can refer to issue 28. go= var jwtKey = \"\" // for generating JWT /* ... */ func InitJwtKey() error { randomBytes := make([]byte, 256) _, err := rand.Read(randomBytes) if err != nil { return errors.Wrap(err, \"Init JWT key error\") } else { jwtKey = string(randomBytes) } return nil }
In backend/webui_service/webui_init.go
:
```go= func (a WebuiApp) Start(tlsKeyLogPath string) { / ... / WebUI.SetAdmin() if err := WebUI.InitJwtKey(); err != nil { logger.InitLog.Errorln(err) return } / ... */ }
### Frontend Login\n\nCertainly, in the `frontend/src/util/AuthHelper.js` file, it is advised to remove the section of code that could be considered a \"cheating snippet.\" To ensure a robust authentication process, all users should be required to successfully pass through the `ApiHelper.login` function and receive a response code of 200. This approach ensures a consistent and legitimate authentication mechanism.\n\n```javascript\nstatic async login(username, password) {\n let response = await ApiHelper.login({username: username, password: password});\n\n if (response !== undefined && response.status === 200) {\n var user = null\n if (username == \"admin\") {\n user = new User(username, \"System Administrator\", response.data.access_token);\n } else {\n user = new User(username, \"User\", response.data.access_token);\n }\n LocalStorageHelper.setUserInfo(user);\n store.dispatch(authActions.setUser(user));\n return true;\n } else {\n return false;\n }\n }\n
"},{"location":"blog/CSRF/20230823/#conclusion","title":"Conclusion","text":"In this endeavor, we've successfully addressed the CSRF vulnerability issue, as highlighted in issue #387 and acknowledged by INCIBE. Furthermore, I've introduced the concept of JWT tokens in this article, detailing their implementation and the corresponding adjustments made within the webconsole. You can locate the detailed implementation in the merged PR #44 of the webconsole repository. I'd like to extend my gratitude to the contributors kishiguro and LaumiH for their significant role in refactoring the webconsole. As a result of their efforts, the webconsole UI has been notably enhanced. Our upcoming focus involves the integration of the charging function, which we are actively pursuing.
"},{"location":"blog/CSRF/20230823/#reference","title":"Reference","text":"free5gc/webconsole merged PR #44 free5gc issue #387 free5gc issue #28 JSON Token RFC 7519 MongoDBCompass
"},{"location":"blog/CSRF/20230823/#about","title":"About","text":"Hello everyone,
I'm Brian Chen (\u9673\u715c\u76db), and I've been immersed in the realm of 5G Core Network technologies. Over the course of seven months, I've had the privilege of serving as an intern at Saviah. In this role, my responsibilities encompass a spectrum of tasks including maintenance, development, and rigorous testing of the free5GC project.
Should any inquiries, questions, or bug reports regarding free5GC arise, I encourage you to reach out by creating an issue in the free5gc repository or by participating in discussions on the forum. I'm here to assist and collaborate with the community as we navigate the intricacies of this project.
Warm regards, Brian Chen (\u9673\u715c\u76db) - Github - LinkedIn
"},{"location":"blog/IntroduceKubernetesAndDeploymentfree5GConKubernetesWithHelm/main/","title":"Introduce Kubernetes and Deploy free5GC on Kubernetes with helm","text":"Note
Author: Elisa Lee Date: 2023/8/16
"},{"location":"blog/IntroduceKubernetesAndDeploymentfree5GConKubernetesWithHelm/main/#abstract","title":"Abstract","text":"In the initial section of the article, I will provide an introduction to Kubernetes. Moving on to the subsequent part, I will delve into the utilization of Kubernetes for facilitating the deployment of free5GC. Lastly, in the final segment of the article, I will elaborate on the effective utilization of Kubernetes for monitoring services.
"},{"location":"blog/IntroduceKubernetesAndDeploymentfree5GConKubernetesWithHelm/main/#introduce-kubernetes","title":"Introduce Kubernetes","text":"Do you know why Kubernetes is called k8s?
It's due to a shorthand notation that uses the first letter \"k,\" followed by the number \"8,\" and ending with the last letter \"s\" to represent the full name. Kubernetes,stands as an open-source container orchestration platform that bestows organizations with the capacity to adeptly govern, deploy, and expand containerized applications. Initially conceived by Google and presently overseen by the Cloud Native Computing Foundation (CNCF), Kubernetes has ascended to become a pivotal technological underpinning within the realm of contemporary cloud-native computing.
For those who find themselves unacquainted with the intricacies of Kubernetes, let us embark on an exploration of its architectural framework.
I understand that the image presented above might appear intricate at first glance. However, there's no need for concern. Allow me to guide you through each component step by step.
"},{"location":"blog/IntroduceKubernetesAndDeploymentfree5GConKubernetesWithHelm/main/#pods","title":"Pods","text":"A \"Pod\" stands as the most diminutive executable entity within the Kubernetes ecosystem. It possesses the capability to encompass either an individual container or a collective assembly of containers. The subsequent enumeration outlines several salient distinctions that set it apart from the act of directly launching a standalone container.
Notably, a Pod possesses its dedicated network interface, affording all enclosed containers the ability to intercommunicate seamlessly by interfacing with the \"localhost.\" Furthermore, connectivity to other Pods is conveniently established through direct usage of their respective IP addresses within the Kubernetes environment.
The Pod's inherent duplicability and capacity for effortless restarts, even from the point of its most recent execution, distinguish it. Additionally, the versatility of including a functioning container within its initial state further characterizes its nature.
Collectively, these attributes contribute to the refinement and streamlined nature of networking within the Kubernetes ecosystem. Such qualities not only facilitate smoother scalability but also offer enhanced capabilities for restarting services with utmost ease.
"},{"location":"blog/IntroduceKubernetesAndDeploymentfree5GConKubernetesWithHelm/main/#nodes","title":"Nodes","text":"Consider a \"node\" as a tangible computing entity akin to a physical machine. Analogous to our personal machines, these nodes possess the capacity to concurrently execute multiple tasks, akin to the pods referenced earlier. The orchestration of nodes is overseen by a pivotal component known as the Kubernetes control plane, which, in an automated fashion, allocates pods across the available nodes. Within each node, a minimum of two services operate in tandem.
Kubelet: This crucial service undertakes the responsibility of facilitating seamless communication between the Kubernetes control plane and the individual node. It serves as the intermediary that relays instructions and status updates, ensuring synchronization and cooperation.
Container Runtime: Operating in tandem with Kubelet, the container runtime undertakes pivotal functions. These encompass retrieving container images from registries, the unpacking of containers, and the actual execution of applications. A prime example of such a container runtime is Docker, renowned for its role in enabling containerization.
In essence, this intricate interplay of nodes, services, and orchestration elements underscores the dynamism and efficiency inherent to the Kubernetes ecosystem. Through these interlocking mechanisms, the platform optimizes resource utilization, ensures effective communication, and enables the seamless execution of applications across a distributed infrastructure.
"},{"location":"blog/IntroduceKubernetesAndDeploymentfree5GConKubernetesWithHelm/main/#master-nodes","title":"Master Nodes","text":"We've now explored all the scalable components and the task runner. Undoubtedly, to orchestrate and oversee everything, a central command hub is necessary \u2013 this is referred to as the master node. Although direct intervention within these nodes isn't typically required to ensure the seamless operation of the entire system, it's still beneficial for you to grasp a basic understanding of its functionality.
"},{"location":"blog/IntroduceKubernetesAndDeploymentfree5GConKubernetesWithHelm/main/#api-server","title":"API server","text":"It determines which interface among all nodes can be externally accessed. Any subsequent commands you execute will be channeled through this service to the designated node or pod. Furthermore, essential cluster information can also be obtained from this service.
"},{"location":"blog/IntroduceKubernetesAndDeploymentfree5GConKubernetesWithHelm/main/#scheduler","title":"Scheduler","text":"Similar to an airport's control tower, its function is akin to orchestrating the deployment of pods on specific nodes based on the rules you've established and the data obtained from the API server. The effectiveness of these rules is pivotal, as they often determine the system's overall efficiency.
"},{"location":"blog/IntroduceKubernetesAndDeploymentfree5GConKubernetesWithHelm/main/#controller-manager","title":"Controller Manager","text":"When the need arises to enact concrete modifications on a pod, such as terminating or pausing its operation, a fundamental prerequisite is pinpointing the pod's process location and establishing the means to interact with it. This is precisely the role fulfilled by the controller manager. Additionally, this manager oversees vital components, including accounts, services, and more.
"},{"location":"blog/IntroduceKubernetesAndDeploymentfree5GConKubernetesWithHelm/main/#etcd","title":"Etcd","text":"For a simplified understanding, we can view this as essentially a comprehensive backup of the entire cluster.
"},{"location":"blog/IntroduceKubernetesAndDeploymentfree5GConKubernetesWithHelm/main/#service","title":"Service","text":"In Kubernetes, a \"service\" is an abstraction that enables communication between different sets of pods, usually to provide a stable network endpoint for accessing a specific group of pods. Pods in Kubernetes are ephemeral and can be created, terminated, or scaled dynamically, which makes their IP addresses and lifecycles unpredictable. Services provide a way to decouple the frontend of an application from the backend pods, making it easier for other components or users to access the application without having to know the exact locations or IP addresses of the pods.
A service can be defined in Kubernetes using a YAML or JSON configuration file, and it is associated with a set of pods based on a label selector. The service acts as a load balancer, distributing incoming network traffic among the pods that match the specified selector. This distribution ensures that even if pods are scaled up or down, the service remains available and reachable.
There are different types of services in Kubernetes:
ClusterIP: This is the default service type, and it exposes the service on a cluster-internal IP address. It is accessible only within the cluster.
NodePort: This type exposes the service on each node's IP address at a static port. It allows external access to the service using the node's IP and the specified static port.
LoadBalancer: This type automatically provisions a cloud provider load balancer to expose the service externally. It works in environments that support external load balancers.
ExternalName: This type provides an alias for an external service by returning a CNAME record with the configured DNS name.
Services are a fundamental concept in Kubernetes and play a crucial role in enabling communication and load balancing between pods and external clients. They provide a stable and abstracted network endpoint that allows applications to scale and be more resilient without disrupting access from users or other components.
In the upcoming section, I will delve into the process of deploying free5GC on Kubernetes.
"},{"location":"blog/IntroduceKubernetesAndDeploymentfree5GConKubernetesWithHelm/main/#deploying-5g-core-network-with-free5gc","title":"Deploying 5G core network with free5GC","text":"Now I'm going to introduce how to implement free5GC on Kubernetes with helm
"},{"location":"blog/IntroduceKubernetesAndDeploymentfree5GConKubernetesWithHelm/main/#install-require-packages","title":"Install require packages","text":"sudo apt update -y\nsudo apt upgrade -y\n
"},{"location":"blog/IntroduceKubernetesAndDeploymentfree5GConKubernetesWithHelm/main/#install-apt-transport-https","title":"Install apt-transport-https","text":"\"apt-transport-https\" is a crucial package that equips your system with the essential tools and libraries required to seamlessly integrate the HTTPS protocol. This integration ensures secure and encrypted communication when connecting to package repositories while utilizing the Advanced Package Tool (APT) for effective package management.
sudo apt install -y curl wget apt-transport-https\n
"},{"location":"blog/IntroduceKubernetesAndDeploymentfree5GConKubernetesWithHelm/main/#install-gtp5g","title":"Install gtp5g","text":"\"gtp5g\" refers to a customized Linux kernel module specifically designed to handle packets by PFCP (Packet Forwarding Control Protocol) Information Elements (IEs) such as PDR (Packet Detection Rule) and FAR (Forwarding Action Rule). For comprehensive insights, you can delve into the 3GPP specifications TS 29.281 and TS 29.244. To employ the UPF (User Plane Function) component effectively, it's imperative to operate on either the 5.0.0-23-generic or 5.4.x version of the Linux kernel. This ensures optimal compatibility and seamless integration with the necessary functionalities.
sudo apt install gcc\nsudo apt install make\ngit clone -b v0.8.1 https://github.com/free5gc/gtp5g.git\ncd gtp5g\nmake\nsudo make install\n
"},{"location":"blog/IntroduceKubernetesAndDeploymentfree5GConKubernetesWithHelm/main/#install-docker","title":"Install docker","text":"\"docker\" is a platform that enables developers to build, package, and distribute applications as containers. Containers are lightweight, portable, and self-sufficient units that encapsulate everything an application needs to run, including the code, runtime, system tools, system libraries, and settings. Docker provides a consistent environment across different development and deployment stages, from local development to testing and production.
for pkg in docker.io docker-doc docker-compose podman-docker containerd runc; do sudo apt-get remove $pkg; done\nsudo apt-get update\nsudo apt-get install ca-certificates curl gnupg\nsudo install -m 0755 -d /etc/apt/keyrings\ncurl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg\nsudo chmod a+r /etc/apt/keyrings/docker.gpg\necho \\\n \"deb [arch=\"$(dpkg --print-architecture)\" signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \\\n \"$(. /etc/os-release && echo \"$VERSION_CODENAME\")\" stable\" | \\\n sudo tee /etc/apt/sources.list.d/docker.list > /dev/null\n sudo apt-get update\n sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin\n
"},{"location":"blog/IntroduceKubernetesAndDeploymentfree5GConKubernetesWithHelm/main/#install-minikube","title":"Install minikube","text":"\"minikube\" is an open-source tool that enables developers to set up and run a single-node Kubernetes cluster locally on their own computer. It's particularly useful for learning, development, and testing purposes. Minikube provides an easy way to experience Kubernetes without needing access to a full-scale cluster, making it a great tool for getting familiar with Kubernetes concepts and features.
wget https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64\nsudo cp minikube-linux-amd64 /usr/local/bin/minikube\nsudo chmod +x /usr/local/bin/minikube\n
"},{"location":"blog/IntroduceKubernetesAndDeploymentfree5GConKubernetesWithHelm/main/#install-kubectl","title":"Install kubectl","text":"\"kubectl\" is the command-line tool used to interact with and manage Kubernetes clusters. It is an essential component for working with Kubernetes, allowing users to perform various tasks and operations on Kubernetes clusters directly from the terminal.
curl -LO https://storage.googleapis.com/kubernetes-release/release/`curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt`/bin/linux/amd64/kubectl\nchmod +x kubectl\nsudo mv kubectl /usr/local/bin/\n
"},{"location":"blog/IntroduceKubernetesAndDeploymentfree5GConKubernetesWithHelm/main/#install-helm","title":"Install helm","text":"\"helm\" is a package manager for Kubernetes that simplifies the deployment and management of applications and services on a Kubernetes cluster. It allows you to define, install, and upgrade complex applications using pre-configured templates called \"charts.\" These charts encapsulate all the necessary resources, configurations, and dependencies required to run an application.
curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3\nchmod 700 get_helm.sh\n./get_helm.sh\nhelm list -A\n
"},{"location":"blog/IntroduceKubernetesAndDeploymentfree5GConKubernetesWithHelm/main/#install-multus-cni","title":"Install multus-cni","text":"\"multus-cni\" is a project that provides a Kubernetes network plugin, specifically a \"Container Network Interface\" (CNI) plugin, which enables the attachment of multiple network interfaces to pods in a Kubernetes cluster.
git clone https://github.com/k8snetworkplumbingwg/multus-cni.git \n
"},{"location":"blog/IntroduceKubernetesAndDeploymentfree5GConKubernetesWithHelm/main/#deploy-free5gc","title":"Deploy free5GC","text":""},{"location":"blog/IntroduceKubernetesAndDeploymentfree5GConKubernetesWithHelm/main/#useful-kubectl-command","title":"Useful kubectl command","text":"Now, I will proceed to introduce a selection of kubectl command that can be employed during the deployment of free5GC.
\"kubectl get pods\" retrieves a list of running pods in the current namespace along with their names, statuses, and other relevant information.
kubectl get pods \n
\"kubectl describe pod\" is used to get detailed information about a specific pod, including its status, events, labels, and more. kubectl describe pod [pod-name]\n
\"kubectl logs\" fetches the logs of a specific pod, helping you troubleshoot issues and monitor application output. kubectl logs [pod-name]\n
\"kubectl exec -it\" allows you to execute a command inside a running pod. The -it
flag enables interactive terminal access. kubectl exec -it [pod-name] -- [command]\n
\"kubectl apply -f \" deploys resources defined in a YAML file, such as pods, services, or deployments, to your cluster. kubectl apply -f [yaml-file]\n
\"kubectl delete\" deletes a specific resource by specifying its type and name, freeing up resources and cleaning the cluster. kubectl delete [resource-type] [resource-name]\n
\"kubectl expose deployment\" creates a new service, typically of type LoadBalancer, to expose a deployment's pods to external network traffic. kubectl expose deployment [deployment-name] --type=LoadBalancer --port=[port]\n
\"kubectl get services\" lists all services running in the current namespace along with their details, including ClusterIP, external IP (if applicable), and ports.
kubectl get services\n
\"kubectl get nodes\" retrieves information about the worker nodes in the cluster, displaying their statuses, roles, and other essential data. kubectl get nodes\n
\"kubectl describe node\" provides detailed information about a specific node, including its capacity, allocated resources, and conditions. kubectl describe node [node-name]\n
\"kubectl get namespaces\" displays all available namespaces in the cluster, which are used to isolate resources and manage multi-tenancy. kubectl get namespaces\n
\"kubectl create namespace\" creates a new namespace, allowing you to logically separate and organize resources. kubectl create namespace [namespace-name]\n
\"kubectl port-forward\" enables you to create a network tunnel between your local machine and a specific pod running within a Kubernetes cluster. This allows you to access services or applications running inside the pod as if they were running on your local machine. The command forwards traffic from a specified local port to a port on the selected pod. kubectl port-forward [pod-name] [local-port]:[remote-port]\n
"},{"location":"blog/IntroduceKubernetesAndDeploymentfree5GConKubernetesWithHelm/main/#start-minikube","title":"Start minikube","text":"Use flannel as cni plugin to start minikue. Flannel is a popular \"Container Network Interface\" (CNI) plugin used for networking in Kubernetes and other container orchestration platforms. It provides a simple and lightweight network fabric designed to facilitate communication between containers and pods in a distributed environment, such as a Kubernetes cluster.
sudo usermod -aG docker $USER && newgrp docker\nminikube start --driver=docker --cpus=4 --memory=8g --disk-size=20g --cni=flannel\n## verify minikube installation\nminikube status \n
"},{"location":"blog/IntroduceKubernetesAndDeploymentfree5GConKubernetesWithHelm/main/#enable-multus-cni-plugin","title":"Enable Multus-CNI Plugin","text":"cd multus-cni\ncat ./deployments/multus-daemonset.yml | kubectl apply -f -\n
"},{"location":"blog/IntroduceKubernetesAndDeploymentfree5GConKubernetesWithHelm/main/#install-free5gc-and-ueransim","title":"Install free5GC and UERANSIM","text":"If you have only one interface on each Kubernetes node and its name is toto
. Then you have to set these parameters to toto
: global.n2network.masterIf
global.n3network.masterIf
global.n4network.masterIf
global.n6network.masterIf
global.n9network.masterIf
kubectl create ns free5gc\ngit clone https://github.com/Orange-OpenSource/towards5gs-helm.git\ncd towards5gs-helm/charts/\nhelm -n free5gc install free5gc-v1 ./free5gc/\nhelm -n free5gc install ueransim-v1 ./ueransim/\nwatch kubectl get pods -n free5gc\n
"},{"location":"blog/IntroduceKubernetesAndDeploymentfree5GConKubernetesWithHelm/main/#start-webconsole","title":"Start WebConsole","text":"free5GC offers a user-friendly web tool called WebConsole, designed to facilitate the creation and management of User Equipment (UE) registrations. This tool serves as a valuable resource for multiple 5G network functions (NFs), streamlining the process of handling UE registrations and associated tasks.
kubectl port-forward --namespace free5gc svc/webui-service 5000:5000\n
Execute the following command in your local machine's terminal, and subsequently, you will be able to access the WebConsole via localhost:5000. You can login with username admin
and password free5gc
. ssh -L localhost:5000:localhost:5000 ubuntu@[VM ip]\n
"},{"location":"blog/IntroduceKubernetesAndDeploymentfree5GConKubernetesWithHelm/main/#service-monitoring","title":"Service Monitoring","text":""},{"location":"blog/IntroduceKubernetesAndDeploymentfree5GConKubernetesWithHelm/main/#install-prometheusgrafana-services","title":"install Prometheus/Grafana services","text":"For monitoring Kubernetes, I utilized Prometheus and Grafana. The installation of Prometheus and Grafana services is facilitated through the Helm chart provided by the prometheus-community.
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts\nhelm repo update\nkubectl create namespace prometheus\nhelm install prometheus prometheus-community/kube-prometheus-stack -n prometheus\nwatch kubectl get pods -n prometheus\n
kubectl port-forward -n prometheus svc/prometheus-grafana 8080:80\n
Execute the following command in your local machine's terminal, and subsequently, you will be able to access the WebConsole via localhost:8080. ssh -L localhost:8080:localhost:8080 ubuntu@[VM ip]\n
A variety of dashboards are available, offering different perspectives for those who are interested. Below is a snapshot of one such dashboard option. "},{"location":"blog/IntroduceKubernetesAndDeploymentfree5GConKubernetesWithHelm/main/#reference","title":"Reference","text":"https://free5gc.org/
https://medium.com/rahasak/deploying-5g-core-network-with-free5gc-kubernets-and-helm-charts-29741cea3922
https://github.com/Orange-OpenSource/towards5gs-helm
https://github.com/k8snetworkplumbingwg/multus-cni
"},{"location":"blog/IntroduceKubernetesAndDeploymentfree5GConKubernetesWithHelm/main/#about","title":"About","text":"Hello, I am Elisa Lee. My ongoing research revolves around VoNR (Voice over New Radio). I encourage any inquiries or identification of errors within the article, as they are welcomed for correction. Your feedback is invaluable, so please don't hesitate to reach out via email to share your insights.
"},{"location":"blog/fuzzing/main/","title":"Fuzz Testing in Go: Discovering Vulnerabilities and Analyzing a Real Case (CVE-2022-43677)","text":"Note
Author: Yu-Sheng Liu Date: 2023/8/9
"},{"location":"blog/fuzzing/main/#overview","title":"Overview","text":"In this article, we begin by introducing the concept of fuzz testing and its significance in software testing. Subsequently, we present Go Fuzzing as an illustrative example to demonstrate how to implement fuzz testing in Go. Lastly, we showcase a practical case, CVE-2022-43677, to exemplify how we conduct fuzz testing on the free5GC system.
"},{"location":"blog/fuzzing/main/#fuzz-testing","title":"Fuzz Testing","text":""},{"location":"blog/fuzzing/main/#what-is-fuzz-testing-fuzzing","title":"What is Fuzz Testing (Fuzzing)?","text":"Fuzz testing, commonly known as fuzzing, is an automated software testing technique used to uncover vulnerabilities, defects, and unexpected behavior in computer systems, applications, and networks. The primary objective of fuzzing is to identify security flaws, crashes, or abnormal program behavior caused by invalid or unexpected inputs.
"},{"location":"blog/fuzzing/main/#how-fuzz-testing-works","title":"How Fuzz Testing Works","text":"Fuzz testing involves subjecting the target software or system to a large number of inputs, including random or malformed data, to see how it handles them. The idea is to explore edge cases and input combinations that may not have been adequately tested during traditional software testing. Here's how the fuzzing process typically works:
Test Input Generation:
Test Execution:
Monitoring and Analysis:
Feedback and Iteration:
Black Box Fuzzing:
White Box Fuzzing:
Grey Box Fuzzing:
Bug and Vulnerability Discovery:
Automation and Efficiency:
Diverse Test Inputs:
Early Vulnerability Detection:
Fuzz testing, or fuzzing, is a powerful and essential technique in the realm of software security testing. By providing a diverse set of inputs and exploring uncharted code paths, fuzz testing uncovers vulnerabilities and defects that might otherwise remain hidden.
Next, we will use Go fuzzing as an example to introduce how to develop a fuzzing in Go.
"},{"location":"blog/fuzzing/main/#go-fuzzing","title":"Go Fuzzing","text":"Go officially supports fuzzing starting from Go 1.18, and its official figure provides a brief and clear summary of the fuzzing function components.
Similar to Go's unit test functions, the fuzzing function in Go must follow the naming convention FuzzXxx
and take an argument of type *testing.F
. This argument has two main functions, Add
and Fuzz
.
Add
Function:
Add
function to add your own test data to the seed corpus for fuzz testing. The seed corpus is the initial set of inputs that go-fuzz
will use to start the fuzzing process.Fuzz
Function:
Fuzz
function will be the target function that you want to test using fuzzing. It must have *testing.T
as its first argument, similar to regular unit tests.Fuzz
function supports variadic arguments with the following basic data types:string
, []byte
int
, int8
, int16
, int32/rune
, int64
uint
, uint8/byte
, uint16
, uint32
, uint64
float32
, float64
bool
These data types represent the different kinds of input data that can be passed to the Fuzz
function during the fuzzing process. The fuzzer will generate and mutate inputs of these types to explore different code paths and uncover bugs or unexpected behavior in the target function.
In summary, when writing fuzzing functions in Go, remember to use the FuzzXxx
naming pattern, accept *testing.F
as an argument, utilize the Add
function to customize the seed corpus, and use the Fuzz
function with supported basic data types to perform fuzz testing on your target functions.
You can use the command to execute the fuzz testing:
go test -fuzz=<regex> -fuzztime=<duration or times>\n\n# Execute the fuzz testing until it crashs or finding some errors\ngo test -fuzz=Fuzz\n\n# Execute the fuzz testing ten iterations\ngo test -fuzz=Fuzz -fuzztime=10x\n\n# Execute the fuzz testing twenty seconds\ngo test -fuzz=Fuzz -fuzztime=20s\n
"},{"location":"blog/fuzzing/main/#simple-example-division","title":"Simple Example - Division","text":"We have developed a very simple function called Division
that accepts two arguments, dividend
and divisor
, and then returns two results: quotient
and remainder
.
func Division(dividend, divisor int32) (\nquotient, remainder int32,\n) {\nquotient = dividend / divisor\nremainder = dividend % divisor\n\nreturn\n}\n
In the FuzzDivision
function, we utilize the data generated by the Go fuzzer to test our Division
function.
func FuzzDivision(f *testing.F) {\nf.Fuzz(func(t *testing.T,\nn1, n2 int32,\n) {\nq, r := Division(n1, n2)\n\nrequire.Equal(t, n1, n2*q+r)\n})\n}\n
We expected to see:
n1 / n2 = q ... r\nn1 = n2 * q + r\n
There should not be any problems with this implementation.
Then we can use the following command to start the fuzz testing.
go test -fuzz=^FuzzDivision$\n
The fuzz testing reports the error \"integer divide by zero\".
As a normal user, we understand that the divisor cannot be zero. However, the input data may not always be as expected. This is precisely why we use fuzz testing\u2014to help us find edge cases and uncover unexpected behavior.
Go stores the data that caused the fuzz testing to fail. You can check them using the following command. * Note: The file name, 29bf8459dc5d452f64d41eb8a253f6a672939b146b07fcced0b17e99729e9b91
, may not be the same.
cat testdata/fuzz/FuzzDivision/29bf8459dc5d452f64d41eb8a253f6a672939b146b07fcced0b17e99729e9b91\n
The content of the file is as follows:
go test fuzz v1\nint32(-7)\nrune('\\x00')\n
The first line indicates the encoding version, and the subsequent lines represent the argument values that triggered the error during the fuzz testing.
Now we can modify our Division
function to check the divisor
if it is zero.
var ErrorDivideByZero = fmt.Errorf(\"integer divide by zero\")\n\nfunc Division(dividend, divisor int32) (\nquotient, remainder int32, err error,\n) {\nif divisor == 0 {\nerr = ErrorDivideByZero\nreturn\n}\n\nquotient = dividend / divisor\nremainder = dividend % divisor\n\nreturn\n}\n
Similarly, the FuzzDivision
fuzzing function now checks for the presence of the ErrorDivideByZero
error.
func FuzzDivision(f *testing.F) {\nf.Add(int32(67), int32(3))\n\nf.Fuzz(func(t *testing.T,\nn1, n2 int32,\n) {\nif q, r, err := Division(n1, n2); err != ErrorDivideByZero {\nrequire.Equal(t, n1, n2*q+r)\n}\n})\n}\n
Now, we can use the following command to re-test the failing case.
go test -run=FuzzDivision/29bf8459dc5d452f64d41eb8a253f6a672939b146b07fcced0b17e99729e9b91\n
"},{"location":"blog/fuzzing/main/#conclusion-for-go-fuzzing","title":"Conclusion for Go Fuzzing","text":"We have used a simple example to describe how to develop a fuzzing function in Go and how to leverage the Go command-line tool to execute fuzz testing.
Next, we will examine a real case, CVE-2022-43677, and demonstrate the process of developing a fuzzing function to identify edge cases.
"},{"location":"blog/fuzzing/main/#cve-2022-43677","title":"CVE-2022-43677","text":"Accroding to the descriptoin:
In free5GC 3.2.1, a malformed NGAP message can crash the AMF and NGAP decoders via an index-out-of-range panic in aper.GetBitString.
In response to this vulnerability, we have developed a fuzzing function to test the NGAP decoder. The function utilizes two approaches: modifying the NGAP message's content under a valid template or adjusting its format by changing the Information Elements (IEs) with variable lengths.
// Put the code under the free5gc/test\nfunc FuzzNgapDecode(f *testing.F) {\nf.Fuzz(func(t *testing.T,\nmodifyWhat uint8,\nchangeIe0, changeIe1, changeIe2, changeIe3, changeIe4 bool,\nvalueIe0A uint32,\nvalueIe2ACellId uint64, valueIe2ATac uint32,\nvalueIe3A uint64,\nvalueIe4A uint64,\nvalueIePlmn uint32,\n) {\nvar idx, n int\nvar sendMsg []byte\nvar registrationRequest []byte\nvar bs []byte\nvar err error\nvar ngapPdu ngapType.NGAPPDU\nvar mobileIdentity5GS nasType.MobileIdentity5GS\nvar ue *test.RanUeContext\n\n// New UE\nue = test.NewRanUeContext(\"imsi-2089300007487\", 1, security.AlgCiphering128NEA0, security.AlgIntegrity128NIA2,\nmodels.AccessType__3_GPP_ACCESS)\nue.AmfUeNgapId = 1\nue.AuthenticationSubs = test.GetAuthSubscription(TestGenAuthData.MilenageTestSet19.K,\nTestGenAuthData.MilenageTestSet19.OPC,\nTestGenAuthData.MilenageTestSet19.OP)\n\nmobileIdentity5GS = nasType.MobileIdentity5GS{\nLen: 12, // suci\nBuffer: []uint8{0x01, 0x02, 0xf8, 0x39, 0xf0, 0xff, 0x00, 0x00, 0x00, 0x00, 0x47, 0x78},\n}\n\nif modifyWhat%2 == DoModifyContent {\nif changeIe0 {\n// RAN UE NGAP ID\nue.RanUeNgapId = int64(valueIe0A)\n}\n\nregistrationRequest = nasTestpacket.GetRegistrationRequest(\nnasMessage.RegistrationType5GSInitialRegistration, mobileIdentity5GS, nil, ue.GetUESecurityCapability(), nil, nil, nil)\nngapPdu = ngapTestpacket.BuildInitialUEMessage(ue.RanUeNgapId, registrationRequest, \"\")\n\nif changeIe2 {\n// User Location Information\nfor _, ie := range ngapPdu.InitiatingMessage.Value.InitialUEMessage.ProtocolIEs.List {\nif ie.Id.Value == ngapType.ProtocolIEIDUserLocationInformation {\nbs = make([]byte, 4)\nvalueIePlmn &= uint32(PlmnMask)\nbinary.LittleEndian.PutUint32(bs, valueIePlmn)\n\nNgRan := ie.Value.UserLocationInformation.UserLocationInformationNR\nNgRan.NRCGI.PLMNIdentity.Value = bs[:PlmnByteLen]\nNgRan.TAI.PLMNIdentity.Value = bs[:PlmnByteLen]\n\nbs = make([]byte, 8)\nvalueIe2ACellId &= uint64(CellIdMask)\nbinary.LittleEndian.PutUint64(bs, valueIe2ACellId)\nNgRan.NRCGI.NRCellIdentity.Value.Bytes = bs[:CellIdByteLen]\n\nbs = make([]byte, 4)\nvalueIe2ATac &= uint32(TacMask)\nbinary.LittleEndian.PutUint32(bs, valueIe2ATac)\nNgRan.TAI.TAC.Value = bs[:TacByteLen]\n}\n}\n}\nif changeIe3 {\n// RRC Establishment Cause\nfor _, ie := range ngapPdu.InitiatingMessage.Value.InitialUEMessage.ProtocolIEs.List {\nif ie.Id.Value == ngapType.ProtocolIEIDRRCEstablishmentCause {\nie.Value.RRCEstablishmentCause.Value = aper.Enumerated(valueIe3A)\n}\n}\n}\nif changeIe4 {\n// UE Context Request\nfor _, ie := range ngapPdu.InitiatingMessage.Value.InitialUEMessage.ProtocolIEs.List {\nif ie.Id.Value == ngapType.ProtocolIEIDUEContextRequest {\nie.Value.UEContextRequest.Value = aper.Enumerated(valueIe4A)\n}\n}\n}\nsendMsg, err = ngap.Encoder(ngapPdu)\n} else if modifyWhat%2 == DoModifyFormat {\nregistrationRequest = nasTestpacket.GetRegistrationRequest(\nnasMessage.RegistrationType5GSInitialRegistration, mobileIdentity5GS, nil, ue.GetUESecurityCapability(), nil, nil, nil)\n\nif changeIe1 {\nregistrationRequest[3] += 1\nregistrationRequest = append(registrationRequest, registrationRequest[len(registrationRequest)-1])\n} else {\nregistrationRequest[3] -= 1\nregistrationRequest = registrationRequest[:len(registrationRequest)-1]\n}\n\nngapPdu = ngapTestpacket.BuildInitialUEMessage(ue.RanUeNgapId, registrationRequest, \"\")\nsendMsg, err = ngap.Encoder(ngapPdu)\nrequire.Nil(t, err, \"Error: %v\", err)\nrequire.Equal(t, int(sendMsg[3]), len(sendMsg[4:]), \"%v\", sendMsg)\n\nidx = bytes.Index(sendMsg, []byte(\"\\x00\\x70\\x40\"))\nassert.NotEqual(t, idx, -1, \"Can not find UE context Request\")\nif idx != -1 {\nif valueIe4A%8 == 0 || valueIe4A%8 == 1 {\nn = 2\n} else {\nn = int(valueIe4A % 8)\n}\nsendMsg[idx+3] = uint8(n)\nsendMsg = sendMsg[:idx+4]\nbs = make([]byte, 8)\nbinary.LittleEndian.PutUint64(bs, valueIe4A)\n\nfor i := 0; i < n; i++ {\nsendMsg = append(sendMsg, bs[i])\n}\n\nsendMsg[3] += uint8(n - 1) // total length\n}\n}\nrequire.Equal(t, int(sendMsg[3]), len(sendMsg[4:]), \"%v\", sendMsg)\n\n_, err = ngap.Decoder(sendMsg)\n})\n}\n
We can use the following command to execute the fuzz testing.
go test -fuzz=^FuzzNgapDecode$ -run=^FuzzNgapDecode$\n
The test resulted in a crash, which confirms the presence of the vulnerability as described in CVE-2022-43677.
The bug was found in the package aper at version v1.0.4. Fortunately, the latest version of the package has already fixed this issue. To verify the fix, we can update the aper package to the latest commit using the following commands:
# Update package aper to the latest commit\ngo get github.com/free5gc/aper@main\n
After updating the aper package, we can test it again with the fuzzing function:
go test -fuzz=^FuzzNgapDecode$ -run=^FuzzNgapDecode$\n
# Alternatively, re-testing the failing case\ngo test -run=FuzzNgapDecode/87af855bbc381c8d510af5ce897fcdd7f9154574e61c0413223f7e31769c2767\n
"},{"location":"blog/fuzzing/main/#conclusion","title":"Conclusion","text":"Fuzz testing is a powerful technique for improving the security and reliability of software systems. By subjecting programs to a wide range of inputs, fuzzing can uncover vulnerabilities and defects that might not be found through traditional testing methods. It automates the testing process, making it efficient and scalable for large codebases.
In the context of Go programming, Go fuzzing is well-supported and integrates seamlessly with the standard testing framework. Developers can create fuzzing functions to target specific functions and uncover potential issues using random or mutated inputs.
To demonstrate the effectiveness of fuzz testing, we presented a real case, CVE-2022-43677, which affected free5GC version 3.2.1. By developing a fuzzing function for the NGAP decoder, we were able to identify a vulnerability that caused a crash.
In conclusion, fuzz testing is a critical practice in software development, enabling developers to proactively discover and resolve bugs and vulnerabilities. It empowers them to deliver more secure and robust software systems, providing users with a higher level of confidence in the applications they use. By incorporating fuzz testing as part of the software development lifecycle, developers can significantly enhance the quality and security of their software products.
"},{"location":"blog/fuzzing/main/#reference","title":"Reference","text":"I'm Yu-Sheng Liu, a master's student at National Yang Ming Chiao Tung University. My research topic focuses on improving the performance of the 5G core network, such as reducing the latency of message propagation in SBI. If you have any questions, please don't hesitate to contact me!
Here are the features on the roadmap. These items are planned to be supported in the near future:
For people who are not familiar with virtual machines and Linux installation, here are some example demonstrations:
For Container deployment:
In this demo, we will
Search virtualbox download
, or visit virtualbox.org to download and install VirtualBox (currently 6.1.18) for your operation system.
Once installed VirtualBox, launch and see if you have something like this:
"},{"location":"guide/1-vm-en/#2-download-ubuntu-server","title":"2. Download Ubuntu Server","text":"Search ubuntu server download
on the web and download the latest Ubuntu Server LTS, or visit ubuntu.com, choose Manual Installation Option to download the .iso
file (currently 20.04.2 LTS)
You should have downloaded a .iso image
file with name like ubuntu-20.04.1-live-server-amd64.iso
, probably in your download directory.
Launch VirtualBox and create your first Ubuntu VM using the downloaded .iso image file. We use Ubuntu Server instead of Ubuntu Desktop because we only need a basic server machine without too many unnecessary functionalities. The resulting overhead to your host machine is smaller, and the VM starts up faster too.
Tips
ubuntu-server
, or ubuntu-20.04
.Refer to the videos Creating VM, Setting up VM.
"},{"location":"guide/1-vm-en/#31-start-installing-ubuntu","title":"3.1 Start Installing Ubuntu","text":"Some notes about installing Ubuntu:
Refer to videos Install Ubuntu 1, Install Ubuntu 2.
"},{"location":"guide/1-vm-en/#32-log-in-into-ubuntu","title":"3.2 Log in into Ubuntu","text":"Reboot after Ubuntu installation complete; wait a little bit for some initialization steps complete. Then log in with your username and password.
First try the ifconfig
command\uff1a
ubuntu@ubuntu:~$ ifconfig\nCommand 'ifconfig' not found, but can be installed with:\nsudo apt install net-tools\nubuntu@ubuntu:~$\n
If some messages like above show, it means ifconfig
has not been installed yet. (ifconfig
is no longer installed by defaults in newer Ubuntu, and is replaced by more versatile ip command, but we will use it here for simplicity).
Follow its suggestion and install ifconfig
:
ubuntu@ubuntu:~$ sudo apt install net-tools\n
Below shows the installation result: Run ifconfig
again to check the network interfaces:
Your display may look different, but take notes about the IP address of the Host-only interface card. The example above shows 192.168.56.101
. You can SSH from your host machine into this Ubuntu VM using the IP later. (Another IP address, 10.0.2.15
is the IP address of the NAT interface card, the apps in your host machine cannot access it).
Finally check if the VM has internet access:
ubuntu@ubuntu:~$ ping google.com\n
Refer to the first part of the video Ping, SSH, and Upgrade.
"},{"location":"guide/1-vm-en/#4-connect-to-the-ubuntu-vm-using-ssh","title":"4. Connect to the Ubuntu VM using SSH","text":"Launch your favorite SSH client from the host machine. Some operation systems (Mac, Ubuntu, some Windows) have pre-installed SSH clients. If you are using Windows, you can also download third-party SSH clients. For example, search \u201cwindows ssh download\u201d on the web.
The benefit of using SSH is that you can easily copy and paste commands from your machine to Ubuntu VM for execution, and vice versa. You can also create multiple SSH connections with the Ubuntu VM for control and monitoring at the same time.
Below shows some examples on a Mac host machine. Suppose the Host-only network IP is 192.168.56.101
, and tue username is ubuntu:
ssh 192.168.56.101 -l ubuntu\n
The first time you connect to the VM, your SSH client may show some message asking you for confirmation. Enter yes: Tips
If somehow SSH shows some warning messages telling you the machine has potential security risk, you may have to remove an entry in the file <your home directory>/.ssh/known_hosts
related the the IP address.
If you log in successfully, you will enter a command line interface:
Repeat the basic commands such as ping
, ifconfig
to see if the VM is working properly. If so, we can access the Ubuntu VM \u201cremotely\u201d from now on.
"},{"location":"guide/1-vm-en/#5-update-and-upgrade-your-ubuntu","title":"5. Update and Upgrade your Ubuntu","text":"
Let also update and upgrade the Ubuntu VM right now to make sure it is up-to-date with proper security updates.
sudo apt update\nsudo apt upgrade\n
"},{"location":"guide/2-config-vm-en/","title":"2 config vm en","text":""},{"location":"guide/2-config-vm-en/#creating-a-free5gc-vm-and-setting-up-network","title":"Creating a free5GC VM and Setting up Network","text":"In this demo we will exercise:
Tips
Refer to video Clone VM and Change IP.
"},{"location":"guide/2-config-vm-en/#1-check-up-an-existing-vm-for-cloning","title":"1. Check up an existing VM for Cloning","text":"Launch VirtualBox, and make sure the Ubuntu VM (ubuntu) we created before can boot up, then:
sudo apt update
and sudo apt upgrade
(or you can do it again)sudo shutdown -P now
, orsudo shutdown -r now
First let\u2019s clone a new VM:
free5gc
.After the new VM is created:
ping
and ifconfig
again to make sure it has internet access, and also make note of the IP address of the Host-only network interface.192.168.56.101
, and the interface name is enp0s8
.The cloned free5gc VM still has host name ubuntu
(or the name you gave it in the original VM). Let\u2019s rename the VM to free5gc
. You can do this by editing the file /etc/hostname
(using vi
or nano
):
sudo nano /etc/hostname\n# or \nsudo vi /etc/hostname\n
In the file, change ubuntu into free5gc
\u3002If you are using nano \uff0cyou can press Ctrl-O
to save the file, then Ctrl-X
to exit. Let\u2019s also change the file /etc/hosts
by replacing the ubuntu inside into free5gc
:
sudo nano /etc/hosts\n
New content of the file /etc/hosts
looks like this:
127.0.0.1 localhost\n127.0.1.1 free5gc\n...\n
The changes will take effect after next reboot.
"},{"location":"guide/2-config-vm-en/#4-setting-static-ip-address","title":"4. Setting Static IP Address","text":"The Host-only network interface, by default, gets its IP address through DHCP. The cloned free5gc VM seems to have trouble obtaining new IP address. We can change the host-only interface to use static IP address instead, which can save a lot of trouble later.
Here let\u2019s fix the static IP address as 192.168.56.101
:
$ cd /etc/netplan\n$ ls\n00-installer-config.yaml\n$ cat 00-installer-config.yaml\n
The original content of the file 00-installer-config.yaml
looks like: # This is the network config written by 'subiquity'\nnetwork:\n ethernets:\n enp0s3:\n dhcp4: true\n enp0s8:\n dhcp4: true\n version: 2\n
meaning the VM has two network interfaces. Using ifconfig
we know that enp0s8
is the name of the Host-only network interface. We can edit the file: sudo nano 00-installer-config.yaml\n
and change it into: # This is the network config written by 'subiquity'\nnetwork:\n ethernets:\n enp0s3:\n dhcp4: true\n enp0s8:\n dhcp4: no\n addresses: [192.168.56.101/24]\n version: 2\n
First check if the new content is correct: sudo netplan try\n
Press enter to exit, if successful. The apply tne new interface setting: sudo netplan apply\n
Run ifconfig
to see if the network setting has been changed correctly: enp0s3: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500\n inet 10.0.2.15 netmask 255.255.255.0 broadcast 10.0.2.255\n inet6 fe80::a00:27ff:fec4:254f prefixlen 64 scopeid 0x20<link>\n ether 08:00:27:c4:25:4f txqueuelen 1000 (Ethernet)\n RX packets 2 bytes 1180 (1.1 KB)\n RX errors 0 dropped 0 overruns 0 frame 0\n TX packets 18 bytes 1894 (1.8 KB)\n TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0\n\nenp0s8: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500\n inet 192.168.56.101 netmask 255.255.255.0 broadcast 192.168.56.255\n inet6 fe80::a00:27ff:fe7e:ada6 prefixlen 64 scopeid 0x20<link>\n ether 08:00:27:7e:ad:a6 txqueuelen 1000 (Ethernet)\n RX packets 8420 bytes 531867 (531.8 KB)\n RX errors 0 dropped 0 overruns 0 frame 0\n TX packets 10887 bytes 823487 (823.4 KB)\n TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0\n\nlo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536\n inet 127.0.0.1 netmask 255.0.0.0\n inet6 ::1 prefixlen 128 scopeid 0x10<host>\n loop txqueuelen 1000 (Local Loopback)\n RX packets 6621 bytes 596035 (596.0 KB)\n RX errors 0 dropped 0 overruns 0 frame 0\n TX packets 6621 bytes 596035 (596.0 KB)\n TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0\n
We can also check the routing table, just to have a grasp of what is going on regarding the network setting: $ route -n\nKernel IP routing table\nDestination Gateway Genmask Flags Metric Ref Use Iface\n0.0.0.0 10.0.2.2 0.0.0.0 UG 100 0 0 enp0s3\n10.0.2.0 0.0.0.0 255.255.255.0 U 0 0 0 enp0s3\n10.0.2.2 0.0.0.0 255.255.255.255 UH 100 0 0 enp0s3\n192.168.56.0 0.0.0.0 255.255.255.0 U 0 0 0 enp0s8\n
For the display above, we learn that the Host-only network 192.168.56.0/24
does not have internet access by itself (even though we can access it using SSH from the host machine). Internet access is through the NAT network 10.0.2.0/24
, with the gateway being 10.0.2.2
(provided by VirtualBox). Now we can SSH into free5gc VM using 192.168.56.101
:
ssh 192.168.56.101 -l ubuntu\n
This is also how we interact with free5gc VM from now on."},{"location":"guide/3-install-free5gc/","title":"3 install free5gc","text":""},{"location":"guide/3-install-free5gc/#installation","title":"Installation","text":""},{"location":"guide/3-install-free5gc/#a-prerequisites","title":"A. Prerequisites","text":"Linux Kernel Version
5.0.0-23-generic
or 5.4.x
version of the Linux kernel. free5gc uses the gtp5g kernel module, which has been tested and compiled against that kernel versions only. If you installed Ubuntu 20.04, the version looks like 5.4.x. To determine the version of the Linux kernel you are using: $ uname -r\n 5.4.0-65-generic\n
You will not be able to run most of the tests in Test section unless you deploy a UPF.
Golang Version
go version\n
# this assumes your current version of Go is in the default location\nsudo rm -rf /usr/local/go\n wget https://dl.google.com/go/go1.17.8.linux-amd64.tar.gz\n sudo tar -C /usr/local -zxvf go1.17.8.linux-amd64.tar.gz\n
wget https://dl.google.com/go/go1.17.8.linux-amd64.tar.gz\n sudo tar -C /usr/local -zxvf go1.17.8.linux-amd64.tar.gz\n mkdir -p ~/go/{bin,pkg,src}\n# The following assume that your shell is bash\necho 'export GOPATH=$HOME/go' >> ~/.bashrc\n echo 'export GOROOT=/usr/local/go' >> ~/.bashrc\n echo 'export PATH=$PATH:$GOPATH/bin:$GOROOT/bin' >> ~/.bashrc\n echo 'export GO111MODULE=auto' >> ~/.bashrc\n source ~/.bashrc\n
golang
are available at the official golang site.Control-plane Supporting Packages
sudo apt -y update\nsudo apt -y install mongodb wget git\nsudo systemctl start mongodb\n
WARNING: MongoDB 5.0+ requires a CPU with AVX support. Or downgrade your MongoDB to 4.4
see https://www.mongodb.com/community/forums/t/mongodb-5-0-cpu-intel-g4650-compatibility/116610/2
see also docker-library/mongo#485 (comment)
User-plane Supporting Packages
sudo apt -y update\nsudo apt -y install git gcc g++ cmake autoconf libtool pkg-config libmnl-dev libyaml-dev\n
sudo sysctl -w net.ipv4.ip_forward=1\nsudo iptables -t nat -A POSTROUTING -o <dn_interface> -j MASQUERADE\nsudo iptables -A FORWARD -p tcp -m tcp --tcp-flags SYN,RST SYN -j TCPMSS --set-mss 1400\nsudo systemctl stop ufw\n
"},{"location":"guide/3-install-free5gc/#b-install-control-plane-elements","title":"B. Install Control Plane Elements","text":"Clone the free5GC repository
cd ~\n git clone --recursive -b v3.3.0 -j `nproc` https://github.com/free5gc/free5gc.git\n cd free5gc\n
cd ~/free5gc\n git checkout main\n git submodule sync\n git submodule update --init --jobs `nproc`\ngit submodule foreach git checkout main\n git submodule foreach git pull --jobs `nproc`\n
Compile network function services in free5gc
cd ~/free5gc\n make amf\n
cd ~/free5gc\n make\n
5.0.0-23-generic
or 5.4.x
. To verify your version:uname -r\n
git
and build itgit clone -b v0.8.1 https://github.com/free5gc/gtp5g.git\ncd gtp5g\nmake\nsudo make install\n
Build the UPF (you may skip this step if you built all network functions above):
to build using make:
cd ~/free5gc\nmake upf\n
run.sh
is free5gc/config/upfcfg.yaml
.sudo apt remove cmdtest\nsudo apt remove yarn\ncurl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -\necho \"deb https://dl.yarnpkg.com/debian/ stable main\" | sudo tee /etc/apt/sources.list.d/yarn.list\ncurl -sL https://deb.nodesource.com/setup_12.x | sudo -E bash -\nsudo apt-get update\nsudo apt-get install -y nodejs yarn\n
Build WebConsole
to build using make:
cd ~/free5gc\nmake webconsole\n
cd ~/free5gc/webconsole/frontend\nyarn install\nyarn build\nrm -rf ../public\ncp -R build ../public\ncd ..\ngo build -o bin/webconsole server.go\n
Note: 2GB or more of OS memory is recommended. WebConsole may be failed to build if memory is less then 1GB.
"},{"location":"guide/4-test-free5gc/","title":"4 test free5gc","text":""},{"location":"guide/4-test-free5gc/#test-free5gc","title":"Test free5GC","text":"Start a Wireshark capture on any core-connected interface, applying the filter 'pfcp||icmp||gtp'
.
In order to run the tests, first do this:
cd ~/free5gc\nmake upf\nchmod +x ./test.sh\n
The tests are all run from within ~/free5gc
.
a. TestRegistration
./test.sh TestRegistration\n
b. TestGUTIRegistration
./test.sh TestGUTIRegistration\n
c. TestServiceRequest
./test.sh TestServiceRequest\n
d. TestXnHandover
./test.sh TestXnHandover\n
e. TestDeregistration
./test.sh TestDeregistration\n
f. TestPDUSessionReleaseRequest
./test.sh TestPDUSessionReleaseRequest\n
g. TestPaging
./test.sh TestPaging\n
h. TestN2Handover
./test.sh TestN2Handover\n
i. TestNon3GPP
./test.sh TestNon3GPP\n
j. TestReSynchronization
./test.sh TestReSynchronization\n
k. TestULCL
./test_ulcl.sh TestRequestTwoPDUSessions\n
"},{"location":"guide/5-install-ueransim/","title":"5 install ueransim","text":""},{"location":"guide/5-install-ueransim/#installing-ueransim-a-ueran-simulator","title":"Installing UERANSIM - a UE/RAN Simulator","text":"In this demo we will practice:
Repeat the steps of cloning free5gc
VM from the base VM, create a new VM for the UERANSIM simulator:
ueransim
, and create new MAC addresses for all network cards.ueransim
.192.168.56.102
.192.168.56.101
from the ueransim VM, and also ping 192.168.56.102
from the free5gc VM.Search \u201cueransim\u201d on the web, and get the web site. On the web site, review what the UERANSIM open-source project is about, then browse into the installation page.
To download UERANSIM:
cd ~\ngit clone https://github.com/aligungr/UERANSIM\ncd UERANSIM\ngit checkout 3a96298\n
Update and upgrade ueransim VM first:
sudo apt update\nsudo apt upgrade\n
Install required tools:
sudo apt install make\nsudo apt install g++\nsudo apt install libsctp-dev lksctp-tools\nsudo apt install iproute2\nsudo snap install cmake --classic\n
Build UERANSIM:
cd ~/UERANSIM\nmake\n
"},{"location":"guide/5-install-ueransim/#3-install-free5gc-webconsole","title":"3. Install free5GC WebConsole","text":"free5GC provides a simple web tool WebConsole to help creating and managing UE registrations to be used by various 5G network functions (NF). To build WebConsole we need Node.js and Yarn.
First SSH into free5gc (192.168.56.101
)\uff0cand remove obsolete tools that may exists:
sudo apt remove cmdtest\nsudo apt remove yarn\n
Then install Node.js
and Yarn
:
curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -\necho \"deb https://dl.yarnpkg.com/debian/ stable main\" | sudo tee /etc/apt/sources.list.d/yarn.list\nsudo apt-get update\nsudo apt-get install -y nodejs yarn\n
To build WebConsole:
cd ~/free5gc\nmake webconsole\n
"},{"location":"guide/5-install-ueransim/#4-use-webconsole-to-add-an-ue","title":"4. Use WebConsole to Add an UE","text":"First start up the WebConsole server:
cd ~/free5gc/webconsole\ngo run server.go\n
The screen shows the port number :5000
at the end. Open your web browser from your host machine, and enter the URL http://192.168.56.101:5000
admin
and password free5gc
.Subscribers
and create a new data:Ctrl-C
on the terminal to quit WebConsole.In free5gc VM, we need to edit three files:
~/free5gc/config/amfcfg.yaml
~/free5gc/config/smfcfg.yaml
~/free5gc/config/upfcfg.yaml
First SSH into free5gc VM, and change ~/free5gc/config/amfcfg.yaml
:
cd ~/free5gc\nnano config/amfcfg.yaml\n
Replace ngapIpList IP from 127.0.0.1
to 192.168.56.101
, namely from:
...\n ngapIpList: # the IP list of N2 interfaces on this AMF\n - 127.0.0.1\n
into: ...\n ngapIpList: # the IP list of N2 interfaces on this AMF\n - 192.168.56.101 # 127.0.0.1\n
Next edit ~/free5gc/config/smfcfg.yaml
:
nano config/smfcfg.yaml\n
and in the entry inside userplane_information / up_nodes / UPF / interfaces / endpoints
, change the IP from 127.0.0.8
to 192.168.56.101
, namely from: ...\n interfaces: # Interface list for this UPF\n - interfaceType: N3 # the type of the interface (N3 or N9)\n endpoints: # the IP address of this N3/N9 interface on this UPF\n - 127.0.0.8\n
into: ...\n interfaces: # Interface list for this UPF\n - interfaceType: N3 # the type of the interface (N3 or N9)\n endpoints: # the IP address of this N3/N9 interface on this UPF\n - 192.168.56.101 # 127.0.0.8\n
Finally, edit ~/free5gc/config/upfcfg.yaml
\uff0cand chage gtpu IP from 127.0.0.8
into 192.168.56.101
, namely from: ...\n gtpu:\n forwarder: gtp5g\n # The IP list of the N3/N9 interfaces on this UPF\n # If there are multiple connection, set addr to 0.0.0.0 or list all the addresses\n ifList:\n - addr: 127.0.0.8\n type: N3\n
into: ...\n gtpu:\n forwarder: gtp5g\n # The IP list of the N3/N9 interfaces on this UPF\n # If there are multiple connection, set addr to 0.0.0.0 or list all the addresses\n ifList:\n - addr: 192.168.56.101 # 127.0.0.8\n type: N3\n
"},{"location":"guide/5-install-ueransim/#6-setting-ueransim","title":"6. Setting UERANSIM","text":"In the ueransim VM, there are two files related to free5GC\uff1a
~/UERANSIM/config/free5gc-gnb.yaml
~/UERANSIM/config/free5gc-ue.yaml
The second file is for UE, which we don\u2019t have to change if the data inside is consistent with the (default) registration data we set using WebConsole previously.
First SSH into ueransim, and edit the file ~/UERANSIM/config/free5gc-gnb.yaml
, and change the ngapIp IP, as well as the gtpIp IP, from 127.0.0.1
to 192.168.56.102
\uff0cand also change the IP in amfConfigs into 192.168.56.101
, that is, from:
...\n ngapIp: 127.0.0.1 # gNB's local IP address for N2 Interface (Usually same with local IP)\n gtpIp: 127.0.0.1 # gNB's local IP address for N3 Interface (Usually same with local IP)\n\n # List of AMF address information\n amfConfigs:\n - address: 127.0.0.1\n
into: ...\n ngapIp: 192.168.56.102 # 127.0.0.1 # gNB's local IP address for N2 Interface (Usually same with local IP)\n gtpIp: 192.168.56.102 # 127.0.0.1 # gNB's local IP address for N3 Interface (Usually same with local IP)\n\n # List of AMF address information\n amfConfigs:\n - address: 192.168.56.101 # 127.0.0.1\n
Next we examine the file ~/UERANSIM/config/free5gc-ue.yaml
\uff0cand see if the settings is consistent with those in free5GC (via WebConsole), for example: # IMSI number of the UE. IMSI = [MCC|MNC|MSISDN] (In total 15 or 16 digits)\nsupi: 'imsi-208930000000003'\n# Mobile Country Code value\nmcc: '208'\n# Mobile Network Code value (2 or 3 digits)\nmnc: '93'\n\n# Permanent subscription key\nkey: '8baf473f2f8fd09487cccbd7097c6862'\n# Operator code (OP or OPC) of the UE\nop: '8e27b6af0e692e750f32667a3b14605d'\n# This value specifies the OP type and it can be either 'OP' or 'OPC'\nopType: 'OP'\n\n...\n\n# Initial PDU sessions to be established\nsessions:\n - type: 'IPv4'\n apn: 'internet'\n slice:\n sst: 0x01\n sd: 0x010203\n\n# List of requested S-NSSAIs by this UE\nslices:\n - sst: 0x01\n sd: 0x010203\n\n...\n
The data appear to be the same as what we set in WebConsole."},{"location":"guide/5-install-ueransim/#7-testing-ueransim-against-free5gc","title":"7. Testing UERANSIM against free5GC","text":"SSH into free5gc. If you have rebooted free5gc, remember to do:
sudo sysctl -w net.ipv4.ip_forward=1\nsudo iptables -t nat -A POSTROUTING -o enp0s3 -j MASQUERADE\nsudo systemctl stop ufw\n
In addition, execute the following command:
sudo iptables -I FORWARD 1 -j ACCEPT\n
Also, make sure you have make proper changes to the free5GC configuration files, then run ./run.sh
:
cd ~/free5gc\n./run.sh\n
At this time free5GC has been started.
Next, prepare three additional SSH terminals from your host machine (if you know how to use tmux
, you can use just one).
In terminal 1: SSH into ueransim, make sure UERANSIM is built, and configuration files have been changed correctly, then execute nr-gnb
:
cd ~/UERANSIM\nbuild/nr-gnb -c config/free5gc-gnb.yaml\n
In terminal 2, SSH into ueransim, and execute nr-ue
with admin right:
cd ~/UERANSIM\nsudo build/nr-ue -c config/free5gc-ue.yaml # for multiple-UEs, use -n and -t for number and delay\n
In terminal 3, SSH into ueransim, and ping 192.168.56.101
to see free5gc is alive. Then, use ifconfig to see if the tunnel uesimtun0
has been created (by nr-ue):
$ ifconfig\n\nenp0s3: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500\n inet 10.0.2.15 netmask 255.255.255.0 broadcast 10.0.2.255\n inet6 fe80::a00:27ff:fe65:1472 prefixlen 64 scopeid 0x20<link>\n ether 08:00:27:65:14:72 txqueuelen 1000 (Ethernet)\n RX packets 80 bytes 32423 (32.4 KB)\n RX errors 0 dropped 0 overruns 0 frame 0\n TX packets 90 bytes 12860 (12.8 KB)\n TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0\n\nenp0s8: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500\n inet 192.168.56.102 netmask 255.255.255.0 broadcast 192.168.56.255\n inet6 fe80::a00:27ff:fe5e:be64 prefixlen 64 scopeid 0x20<link>\n ether 08:00:27:5e:be:64 txqueuelen 1000 (Ethernet)\n RX packets 1515 bytes 130490 (130.4 KB)\n RX errors 0 dropped 0 overruns 0 frame 0\n TX packets 1010 bytes 206670 (206.6 KB)\n TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0\n\nlo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536\n inet 127.0.0.1 netmask 255.0.0.0\n inet6 ::1 prefixlen 128 scopeid 0x10<host>\n loop txqueuelen 1000 (Local Loopback)\n RX packets 3445 bytes 174416 (174.4 KB)\n RX errors 0 dropped 0 overruns 0 frame 0\n TX packets 3445 bytes 174416 (174.4 KB)\n TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0\n\nuesimtun0: flags=4305<UP,POINTOPOINT,RUNNING,NOARP,MULTICAST> mtu 1500\n inet 60.60.0.1 netmask 255.255.255.255 destination 60.60.0.1\n inet6 fe80::2034:d00:a76:84b7 prefixlen 64 scopeid 0x20<link>\n unspec 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00 txqueuelen 500 (UNSPEC)\n RX packets 3 bytes 252 (252.0 B)\n RX errors 0 dropped 0 overruns 0 frame 0\n TX packets 13 bytes 732 (732.0 B)\n TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0\n
Now use ping
:
ping -I uesimtun0 google.com\n
If ping
gets replies, then free5GC is running properly. Congratulations!"},{"location":"guide/6-simple-app/","title":"6 simple app","text":""},{"location":"guide/6-simple-app/#free5gc-simple-apps","title":"free5GC Simple Apps","text":"In this demo we will use free5GC together with UERANSIM to exercise on some simple network applications:
ping
+ tcpdump
wget
and curl
First start up free5GC and ueransim VMs. This requires one SSH terminal for free5gc, and two for ueransim.
Open another SSH terminal and log in into ueransim:
ssh 192.168.56.102 -l ubuntu\n
Use ifconfig
to check if uesimtun0
tunnel has been created, and use ping to check if we can ping
through it\uff1a $ ping google.com\nPING google.com (172.217.27.142) 56(84) bytes of data.\n64 bytes from tsa03s02-in-f14.1e100.net (172.217.27.142): icmp_seq=1 ttl=63 time=3.98 ms\n64 bytes from tsa03s02-in-f14.1e100.net (172.217.27.142): icmp_seq=2 ttl=63 time=3.87 ms\n64 bytes from tsa03s02-in-f14.1e100.net (172.217.27.142): icmp_seq=3 ttl=63 time=4.06 ms\n^C\n--- google.com ping statistics ---\n3 packets transmitted, 3 received, 0% packet loss, time 2003ms\nrtt min/avg/max/mdev = 3.872/3.970/4.060/0.076 ms\n
$ ping -I uesimtun0 google.com\nPING google.com (172.217.27.142) from 60.60.0.1 uesimtun0: 56(84) bytes of data.\n64 bytes from tsa03s02-in-f14.1e100.net (172.217.27.142): icmp_seq=1 ttl=61 time=5.85 ms\n64 bytes from tsa03s02-in-f14.1e100.net (172.217.27.142): icmp_seq=2 ttl=61 time=4.87 ms\n64 bytes from tsa03s02-in-f14.1e100.net (172.217.27.142): icmp_seq=3 ttl=61 time=4.76 ms\n^C\n--- google.com ping statistics ---\n3 packets transmitted, 3 received, 0% packet loss, time 2004ms\nrtt min/avg/max/mdev = 4.760/5.160/5.847/0.487 ms\n
Also use route -n
to observe if current routing table shows some routing rules regarding the two network interfaces enp0s3
and enp0s8
:
$ route -n\nKernel IP routing table\nDestination Gateway Genmask Flags Metric Ref Use Iface\n0.0.0.0 10.0.2.2 0.0.0.0 UG 100 0 0 enp0s3\n10.0.2.0 0.0.0.0 255.255.255.0 U 0 0 0 enp0s3\n10.0.2.2 0.0.0.0 255.255.255.255 UH 100 0 0 enp0s3\n192.168.56.0 0.0.0.0 255.255.255.0 U 0 0 0 enp0s8\n
The network 10.0.2.0/24
and its enp0s3
interface are related to VirtualBox NAT network card. We can bring down this interface:
$ sudo ifconfig enp0s3 down\n$ route -n\nKernel IP routing table\nDestination Gateway Genmask Flags Metric Ref Use Iface\n192.168.56.0 0.0.0.0 255.255.255.0 U 0 0 0 enp0s8\n
As shown aboe we have only Host-only network 192.168.56.0/24
left. Run ping
again: $ ping 8.8.8.8\nping: connect: Network is unreachable\n
And see that it can not ping through, but runing:
$ ping -I uesimtun0 8.8.8.8\nPING 8.8.8.8 (8.8.8.8) from 60.60.0.1 uesimtun0: 56(84) bytes of data.\n64 bytes from 8.8.8.8: icmp_seq=1 ttl=61 time=7.17 ms\n64 bytes from 8.8.8.8: icmp_seq=2 ttl=61 time=5.41 ms\n64 bytes from 8.8.8.8: icmp_seq=3 ttl=61 time=5.15 ms\n^C\n--- 8.8.8.8 ping statistics ---\n3 packets transmitted, 3 received, 0% packet loss, time 2005ms\nrtt min/avg/max/mdev = 5.150/5.907/7.165/0.895 ms\n
shows some responses, since we ask ping
to go through the free5GC core network. To make ping 8.8.8.8
in addition to ping -I uesimtun0 8.8.8.8
work, we can set the uesimtun0
interface (IP 60.60.0.1
) as the new default gateway:
$ sudo ip r add default dev uesimtun0\n$ route -n\nKernel IP routing table\nDestination Gateway Genmask Flags Metric Ref Use Iface\n0.0.0.0 0.0.0.0 0.0.0.0 U 0 0 0 uesimtun0\n192.168.56.0 0.0.0.0 255.255.255.0 U 0 0 0 enp0s8\n
Now traffic not for the 192.168.56.0/24
network will go to uesimtun0
, and ping 8.8.8.8
works this time: $ ping 8.8.8.8\nPING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.\n64 bytes from 8.8.8.8: icmp_seq=1 ttl=61 time=5.02 ms\n64 bytes from 8.8.8.8: icmp_seq=2 ttl=61 time=6.31 ms\n64 bytes from 8.8.8.8: icmp_seq=3 ttl=61 time=5.41 ms\n^C\n--- 8.8.8.8 ping statistics ---\n3 packets transmitted, 3 received, 0% packet loss, time 2004ms\nrtt min/avg/max/mdev = 5.017/5.581/6.312/0.541 ms\n...\n
Note that normally we are using ueransim to simulate \u201cterminal\u201d UE device, not as a network device or proxy, therefore the above two routing rules suffice.
Now if we still want to run:
$ ping google.com\nping: google.com: Temporary failure in name resolution\n
we will get unresolved domain name. To solve this, we can modify the file /etc/resolv.conf
:
sudo nano /etc/resolv.conf\n
and change the nameserver IP to 8.8.8.8
:
nameserver 8.8.8.8\n
After the change, we can see ping
getting responses:
$ ping google.com\nPING google.com (216.58.200.46) 56(84) bytes of data.\n64 bytes from tsa01s08-in-f46.1e100.net (216.58.200.46): icmp_seq=1 ttl=61 time=5.19 ms\n64 bytes from tsa01s08-in-f46.1e100.net (216.58.200.46): icmp_seq=2 ttl=61 time=50.4 ms\n64 bytes from tsa01s08-in-f46.1e100.net (216.58.200.46): icmp_seq=3 ttl=61 time=5.66 ms\n^C\n--- google.com ping statistics ---\n3 packets transmitted, 3 received, 0% packet loss, time 2004ms\nrtt min/avg/max/mdev = 5.191/20.423/50.414/21.207 ms\n
We can also examine the network traffic happening underneath in the scenario above. First we open another SSH terminal into ueransim, and run the following command:
$ sudo tcpdump -n -i any host 60.60.0.1 or 192.168.56.101\ntcpdump: verbose output suppressed, use -v or -vv for full protocol decode\nlistening on any, link-type LINUX_SLL (Linux cooked v1), capture size 262144 bytes\n
then run ping 8.8.8.8
again, wait for a couple seconds, then Ctrl-C
to exit. We see the data packets actually going in and out uesimtun0
.
$ sudo tcpdump -n -i any host 60.60.0.1 or 192.168.56.101\ntcpdump: verbose output suppressed, use -v or -vv for full protocol decode\nlistening on any, link-type LINUX_SLL (Linux cooked v1), capture size 262144 bytes\n10:24:56.138729 IP 192.168.56.101.38412 > 192.168.56.102.38740: sctp (1) [HB REQ]\n10:24:56.138783 IP 192.168.56.102.38740 > 192.168.56.101.38412: sctp (1) [HB ACK]\n10:24:58.456532 IP 60.60.0.1 > 8.8.8.8: ICMP echo request, id 33, seq 1, length 64\n10:24:58.457416 IP 192.168.56.102.2152 > 192.168.56.101.2152: UDP, length 100\n10:24:58.462136 IP 192.168.56.101.2152 > 192.168.56.102.2152: UDP, length 92\n10:24:58.462324 IP 8.8.8.8 > 60.60.0.1: ICMP echo reply, id 33, seq 1, length 64\n10:24:59.458823 IP 60.60.0.1 > 8.8.8.8: ICMP echo request, id 33, seq 2, length 64\n10:24:59.459031 IP 192.168.56.102.2152 > 192.168.56.101.2152: UDP, length 100\n10:24:59.464214 IP 192.168.56.101.2152 > 192.168.56.102.2152: UDP, length 92\n10:24:59.464396 IP 8.8.8.8 > 60.60.0.1: ICMP echo reply, id 33, seq 2, length 64\n10:25:00.461293 IP 60.60.0.1 > 8.8.8.8: ICMP echo request, id 33, seq 3, length 64\n10:25:00.462178 IP 192.168.56.102.2152 > 192.168.56.101.2152: UDP, length 100\n10:25:00.474941 IP 192.168.56.101.2152 > 192.168.56.102.2152: UDP, length 92\n10:25:00.475561 IP 8.8.8.8 > 60.60.0.1: ICMP echo reply, id 33, seq 3, length 64\n10:25:01.463946 IP 60.60.0.1 > 8.8.8.8: ICMP echo request, id 33, seq 4, length 64\n10:25:01.464523 IP 192.168.56.102.2152 > 192.168.56.101.2152: UDP, length 100\n10:25:01.469297 IP 192.168.56.101.2152 > 192.168.56.102.2152: UDP, length 92\n10:25:01.470314 IP 8.8.8.8 > 60.60.0.1: ICMP echo reply, id 33, seq 4, length 64\n
"},{"location":"guide/6-simple-app/#wget","title":"wget","text":"Simply look for any web page for file download on the web. For example, if we choose Golang web site as an example, we may find the URL:
https://golang.org/dl/go1.15.8.darwin-amd64.pkg\n
Using the same network settings is the previous exercise, just wget https://golang.org/dl/go1.15.8.darwin-amd64.pkg\n
And see if you can download a Golang 1.15.8 install file."},{"location":"guide/6-simple-app/#ptt-ssh-bbsupttcc","title":"ptt (ssh bbsu@ptt.cc
)","text":"You can actually use SSH in the ueransim VM to access remote site. For example, you can SSH to a well-known terminal-based BBS site in Taiwan:
ssh bbsu@ppt.cc\n
"},{"location":"guide/6-simple-app/#youtube","title":"Youtube","text":"You can also use Youtube as an example app. To achieve this goal, you can install a desktop VM with graphical UI, such as Ubuntu Desktop, and follow the same procedure to install and start up UERANSIM, then access Youtube through uesimtun0
and free5GC.
To reduce resource consumption on your host machine, you may install Lubuntu (at https://lubuntu.me), a more light-weight Ubuntu desktop distro instead. But since viewing free5GC YouTube Channel requires quite sime CPU consumption, you may have to set at least 2 CPUs and 2048 MB memory for the VM.
Refer to videos Access Youtube on Lubuntu (1, 2, 3, 4 and 5).
"},{"location":"guide/Appendix/","title":"Appendix","text":""},{"location":"guide/Appendix/#appendix","title":"Appendix","text":""},{"location":"guide/Appendix/#appendix-a-oam","title":"Appendix A: OAM","text":"cd webconsole\ngo run server.go\n
URL: http://localhost:5000\nUsername: admin\nPassword: free5gc\n
Note: You can add the subscribers here too
"},{"location":"guide/Appendix/#appendix-b-orchestrator","title":"Appendix B: Orchestrator","text":"Please refer to free5gmano
"},{"location":"guide/Appendix/#appendix-c-iptv","title":"Appendix C: IPTV","text":"Please refer to free5GC/IPTV
"},{"location":"guide/Appendix/#appendix-d-system-environment-cleaning","title":"Appendix D: System Environment Cleaning","text":"The below commands may be helpful for development purposes.
ls /dev/mqueue/
rm /dev/mqueue/*
cd ./src/upf/lib/libgtp5gnl/tools
./gtp5g-tunnel list pdr
./gtp5g-tunnel list far
cd ./src/upf/lib/libgtp5gnl/tools
sudo ./gtp5g-link del {Dev-Name}
uname -r
5.0.0-23-generic
for example sudo apt search 'linux-image-5.0.0-23-generic'\nsudo apt install 'linux-image-5.0.0-23-generic'\nsudo apt install 'linux-headers-5.0.0-23-generic'\n
sudo update-initramfs -u -k all\nsudo update-grub\n
5.0.0-23-generic
sudo reboot\n
sudo apt remove 'linux-image-5.0.0-23-generic'\nsudo apt remove 'linux-headers-5.0.0-23-generic'\n
"},{"location":"guide/Appendix/#appendix-f-program-the-sim-card","title":"Appendix F: Program the SIM Card","text":"Install packages:
sudo apt-get install pcscd pcsc-tools libccid python-dev swig python-setuptools python-pip libpcsclite-dev\nsudo pip install pycrypto\n
Download PySIM
git clone git://git.osmocom.org/pysim.git\n
Change to pyscard folder and install
cd <pyscard-path>\nsudo /usr/bin/python setup.py build_ext install\n
Verify your reader is ready
sudo pcsc_scan\n
Check whether your reader can read the SIM card
cd <pysim-path>\n./pySim-read.py \u2013p 0\n
Program your SIM card information
./pySim-prog.py -p 0 -x 208 -y 93 -t sysmoUSIM-SJS1 -i 208930000000003 --op=8e27b6af0e692e750f32667a3b14605d -k 8baf473f2f8fd09487cccbd7097c6862 -s 8988211000000088313 -a 23605945\n
You can get your SIM card from sysmocom. You also need a card reader to write your SIM card. You can get a card reader from here or use other similar devices.
"},{"location":"guide/Configuration/","title":"Configuration","text":""},{"location":"guide/Configuration/#configuration","title":"Configuration","text":""},{"location":"guide/Configuration/#sbi-configuration","title":"SBI Configuration","text":"There are registerIP and bindingIP design on every NF's sbi interface.
This is due to some orchestration, such as Kubernets or OpenStack, has the design of service IP mapping.
Use Kubernets as an example. K8S has the service type that enable users to define the service IP outside the pod. But the service IP may be different from the IP assigned inside the pod. Therefore, if we register the binding IP inside the pod to NRF, NRF cannot know which service IP outside the pod has attached. As the result, we need to separate registerIP from bindingIP in this scenario.
If you are not sure what IP you should set, just configure it as the same IP address.
"},{"location":"guide/Configuration/#sample-configuration","title":"Sample configuration","text":"We provide a sample config to connect to outer ran under /sample/ran_attach_config/
. The architecture is as following.
As the result, user's RAN IP must set to 192.168.0.0/24 subnet or let the routing route to this subnet.
Notice: If user wants to use the setting, aware to set 192.168.0.1 to your host as well.
"},{"location":"guide/Configuration/#smf-configuration","title":"SMF Configuration","text":""},{"location":"guide/Configuration/#a-configure-smf-with-s-nssai","title":"A. Configure SMF with S-NSSAI","text":"smfcfg.yaml
uerouting.yaml
For more detail of SMF config, please refer to here.
"},{"location":"guide/Environment/","title":"Environment","text":""},{"location":"guide/Environment/#recommended-environment","title":"Recommended Environment","text":"free5gc has been tested against the following environment:
The listed kernel version is required for the UPF element.
Minimum Hardware
Recommended Hardware
This guide assumes that you will run all 5GC elements on a single machine.
"},{"location":"guide/New-Subscriber-via-webconsole/","title":"New Subscriber via webconsole","text":""},{"location":"guide/New-Subscriber-via-webconsole/#new-subscriber-via-webconsole","title":"New Subscriber via webconsole","text":""},{"location":"guide/New-Subscriber-via-webconsole/#1-install-webconsole","title":"1. Install webconsole","text":""},{"location":"guide/New-Subscriber-via-webconsole/#2-optionaldelete-mongodb","title":"2. (Optional)Delete MongoDB","text":"If another version of free5GC was ran before, you have to delete MongoDB.
$ mongo --eval \"db.dropDatabase()\" free5gc\n
"},{"location":"guide/New-Subscriber-via-webconsole/#3-run-webconsole-server","title":"3. Run WebConsole server","text":"$ cd ~/free5gc/webconsole\n$ ./bin/webconsole\n
"},{"location":"guide/New-Subscriber-via-webconsole/#4-use-browser-to-connect-to-webconsole","title":"4. Use browser to connect to WebConsole","text":"Enter :5000 in URL bar.
Username: admin\nPassword: free5gc\n
"},{"location":"guide/New-Subscriber-via-webconsole/#5-add-new-subscriber","title":"5. Add new subscriber","text":"There are some issues for subscriber modification. If you want to modify the existed subscriber, please Delete
it first and New
again for now.
This document explains the detail of SMF config. Also provide some examples about conversion between config file and real userplane topology
ULCL limitation: The branching UPF now can't connect to the Internet. It only serves as a Intranet in the UPF topology. (Please refers to the topology of example 2)
"},{"location":"guide/SMF-Config/#sbi","title":"SBI","text":"Field meaning scheme The protocol for SBI registerIPv4 IP used to register to NRF bindingIPv4 IP used to bind the service port SMF bind the SBI service to this port"},{"location":"guide/SMF-Config/#pfcp","title":"PFCP","text":"Field meaning addr The IP address of N4 interface on the SMF (PFCP)"},{"location":"guide/SMF-Config/#userplane-information","title":"Userplane Information","text":"Field meaning userplane_information Includes topology and information of RAN and UPFs which are controlled by this SMF up_nodes The node in the user plane topology. Includes gNodeB, I-UPF and A-UPF links The edge in the user plane topology type Indicate it is RAN or specific kind of UPF node_id The PFCP IPv4 address for UPFNote: up_resource_ip serves as default user plane IP for the UPF. In this version, UPF will determine its user plane IP by itself. So setting up_resource_ip in SMF config won't affect real config in user plane.
"},{"location":"guide/SMF-Config/#amf-config","title":"AMF Config","text":"To understand whole PDU session config, we must take a step forward to understand the AMF config.
Field meaning NGAPIPList The IP list of N2 interfaces on the AMF SBI Same meaning with SMF/SBI."},{"location":"guide/SMF-Config/#example-1","title":"Example 1","text":""},{"location":"guide/SMF-Config/#smf-config","title":"SMF Config","text":"ERROR: [SCTP] Failed to connect given AMF N3IWF=NGAP
","text":"This error occured when N3IWF was started before AMF finishing initialization. This error usually appears when you run the TestNon3GPP in the first time.
Rerun the test should be fine. If it still not be solved, larger the sleeping time in line 110 of test.sh
.
TestNon3GPP will modify the config/amfcfg.conf
. So, if you had killed the TestNon3GPP test before it finished, you might need to copy config/amfcfg.conf.bak
back to config/amfcfg.conf
to let other tests pass.
cp config/amfcfg.conf.bak config/amfcfg.conf
If you meet any problems about https or mogodb, it maybe couse our new version from v3.0.1 to v3.0.2 has change http to H2C verion. Try the command below.
mongo --eval \"db.NfProfile.drop()\" free5gc
MQCreate() Error creating message queue: Too many open files UPF=Util
(UPF)","text":"Remove POSIX message queues
ls /dev/mqueue/\nrm /dev/mqueue/*\n
"},{"location":"guide/Trouble_Shooting/#5-remove-gtp-devices-using-tools-in-libgtp5gnl-upf","title":"5. Remove gtp devices (using tools in libgtp5gnl) (UPF)","text":"cd lib/libgtp5gnl/tools\nsudo ./gtp5g-link del {Dev-Name}\n
"},{"location":"guide/Trouble_Shooting/#6-upf-cli-run-error-open-gtp5g-open-link-create-file-exists","title":"6. UPF Cli Run Error: open Gtp5g: open link: create: file exists
","text":"sudo ip link del upfgtp\n
"},{"location":"guide/Trouble_Shooting/#7-decode-http2-packet-in-wireshark","title":"7. Decode HTTP/2 packet in Wireshark","text":"Run Network Function
Check has XXFsslkey.log
Edit >> Preference >> Protocols >> SSL (TLS)
Add keylog
Filter http2
The similar reason as NEA0 NAS message. Althrough H2C is clear text, wirshark still considers these packets as the normal TCP packets and does not decode them by HTTP2.
To see the details of H2C packets, do the following configuration.
Analyze \u2192 Decode As\u2026
click Add button to add the decode rules
Decode the packets from the TCP ports listened by each NF as HTTP2 packets.
Some 5G UE and gNodeB hardware have been tested with free5GC by partners or community members:
5G UE (Support 5G SA):
gNodeB:
Reports of tested hardware not listed above on Github issue or free5GC forum are welcome.
PS: if you don't have any hardware available, we suggest to use UERANSIM to simulate.
(Refer to Advanced environment setup section)
"},{"location":"membership/","title":"Index","text":""},{"location":"membership/#sponsorship-info","title":"Sponsorship Info","text":""},{"location":"membership/#sponsorship-tiers","title":"Sponsorship Tiers","text":"free5GC is a nonprofit organization dedicated to developing innovative and next-generation features for open-source code of the 5G Core (5GC) Network under Apache 2.0 license. Your generous support and sponsorship will sustain our technology development and the operation of the community. Your company/organization logo will be displayed on the free5GC website and listed as the sponsorship you participate. Here are the sponsorship tiers we offer.
Your generosity is appreciated. You can now support free5GC by using your credit cards. Please visit https://fund.nycu.edu.tw/plans/HYnZMprdAGx for donation.
Tips
If you encounter the usage problem on free5GC, please join our official forum forum.free5gc.org and initiate a new discussion.
Otherwise, you can raise the issue on our GitHub repository for reporting the bugs/suggestions (related to vulnerability/functionality/deployment/testing), or create the pull request for contributing to our community!
Tips
If your problem can not be solved via the platforms listed above, please send an email directly to free5GC.org@gmail.com
. Thanks.
If you encounter the usage problem on free5GC, please join our official forum forum.free5gc.org and initiate a new discussion.
+Otherwise, you can raise the issue on our GitHub repository for reporting the bugs/suggestions (related to vulnerability/functionality/deployment/testing), or create the pull request for contributing to our community!
+Tips
+If your problem can not be solved via the platforms listed above, please send an email directly to free5GC.org@gmail.com
.
+Thanks.
Akraino Blueprints: Integrated Cloud Native Private Wireless, The Linux Foundation, October 11, 2021
+SD Core Techinar July 7 2021, Open Networking Foundation, July 13, 2021
+Aarna Networks MWC 2021 Demo, Aarna Networks Channel, June 27, 2021
+OpenStack Tacker Demo, Open Infrastructure Foundation, April 26, 2021
+OpenNess Tungsten Fabric free5GC demo, Aarna Networks Channel, February 16, 2021
+5G Core on Diamanti, Diamanti, Inc., February 3, 2021
+free5GC (5G Core) Orchestration on Kubernetes with Tungsten Fabric CNI and Testing, Aarna Networks Channel, December 2, 2020
+IoT LoRa (sensors and gateway in hardware), RAN in hardware (SDR) and software, and the free5GC, LABORA Research Group, July 3, 2020
+UE and eNodeB in Hardware (conventional cell phone + SDR) and free5GC: a pratical approach in 5G, LABORA Research Group, July 3, 2020
+OpenAirInterface and free5GC: a pratical approach in 5G networks, LABORA Research Group, June 29, 2020
+