From 4dd0821ed6b726d033f108703957e4349adb9a02 Mon Sep 17 00:00:00 2001 From: Manuel Requena Date: Sat, 12 Jan 2019 17:51:17 -0800 Subject: [PATCH] lte: EPC upgrade The LTE/EPC model has been enhanced with new features: * SGW, PGW and MME are full nodes. * There are P2P links between core network nodes. * New S5 interface between SGW and PGW nodes based on GTPv2-C protocol. * Allow simulations with multiple SGWs and PGWs. LTE eNB RRC is extended to support: * S1 signalling with the core network is initiated after the RRC connection establishment procedure is finished. * New ATTACH_REQUEST state to wait for finalization of the S1 signalling with the core network. * New InitialContextSetupRequest primitive of the S1 SAP that is received by the eNB RRC when the S1 signalling from the core network is finished. Squashed commit of the following from mrequena:epc-upgrade: lte: Clarify that e2e is IPv4/IPv6 and core network is IPv4-only lte: Remove duplicated example from wscript lte: Add new features of the EPC upgrade to CHANGES.html lte: Remove duplicated example lte: (fixes #3027) S1 signalling is done before RRC connection establishment is finished lte: Add new ATTACH_REQUEST state in eNB RRC lte: cast a uint8_t when logging lte: Fix doxygen comments and doc typos lte: Add documentation for EPC split lte: Use protocolNumber in new EpcPgwApplication lte: Add doxygen doc for EpcSgwApplication class lte: Add doxygen doc for EpcPgwApplication class lte: Add doxygen doc for EpcMmeApplication class lte: PreSerialize/PreDesearialize are only used in the derived messages lte: Command line parameters take precedence over parameters in config file lte: Rename number of node pairs variable lte: Add IPv6 support to new PgwApplication lte: Change to new pgw node lte: Number of nodes has changed after EPC split lte: InitialUeMessage cannot be called if simulation is not started lte: Change some names of addresses and clean debug code lte: New GetPacketFilters method lte: Add new files to build system lte: Don't need to build the GtpcHeader by hand, just the message lte: GTPC messages derived from GtpcHeader lte: Add new PGW Application lte: Add new SGW Application lte: Add new MME Application lte: Add new GTP-C protocol header lte: Split SGW/PGW/MME in different nodes lte: Add new example --- CHANGES.html | 15 + RELEASE_NOTES | 1 + doc/models/Makefile | 11 +- src/lte/doc/Makefile | 6 +- src/lte/doc/source/figures/helpers.seqdiag | 48 +- src/lte/doc/source/figures/nas-attach.seqdiag | 39 +- src/lte/doc/source/lte-design.rst | 247 ++-- src/lte/doc/source/lte-references.rst | 2 +- src/lte/examples/lena-simple-epc.cc | 63 +- src/lte/helper/point-to-point-epc-helper.cc | 242 +++- src/lte/helper/point-to-point-epc-helper.h | 110 +- src/lte/model/epc-enb-application.cc | 19 +- src/lte/model/epc-enb-s1-sap.h | 31 +- src/lte/model/epc-gtpc-header.cc | 1287 +++++++++++++++++ src/lte/model/epc-gtpc-header.h | 475 ++++++ src/lte/model/epc-mme-application.cc | 367 +++++ src/lte/model/epc-mme-application.h | 240 +++ src/lte/model/epc-pgw-application.cc | 507 +++++++ src/lte/model/epc-pgw-application.h | 343 +++++ src/lte/model/epc-sgw-application.cc | 480 ++++++ src/lte/model/epc-sgw-application.h | 277 ++++ src/lte/model/epc-tft.cc | 6 + src/lte/model/epc-tft.h | 2 + src/lte/model/fdtbfq-ff-mac-scheduler.cc | 4 +- src/lte/model/lte-enb-rrc.cc | 46 +- src/lte/model/lte-enb-rrc.h | 13 + src/lte/model/pss-ff-mac-scheduler.cc | 2 +- src/lte/test/epc-test-s1u-downlink.cc | 8 +- src/lte/test/epc-test-s1u-uplink.cc | 9 +- src/lte/test/lte-test-entities.cc | 6 + src/lte/test/lte-test-entities.h | 5 + src/lte/test/lte-test-ue-measurements.cc | 4 +- src/lte/wscript | 8 + 33 files changed, 4652 insertions(+), 271 deletions(-) create mode 100644 src/lte/model/epc-gtpc-header.cc create mode 100644 src/lte/model/epc-gtpc-header.h create mode 100644 src/lte/model/epc-mme-application.cc create mode 100644 src/lte/model/epc-mme-application.h create mode 100644 src/lte/model/epc-pgw-application.cc create mode 100644 src/lte/model/epc-pgw-application.h create mode 100644 src/lte/model/epc-sgw-application.cc create mode 100644 src/lte/model/epc-sgw-application.h diff --git a/CHANGES.html b/CHANGES.html index 35824a2fc..50000293c 100644 --- a/CHANGES.html +++ b/CHANGES.html @@ -83,6 +83,21 @@ us a note on ns-developers mailing list.

Changed behavior:


diff --git a/RELEASE_NOTES b/RELEASE_NOTES index bfd5857af..8dd8915bd 100644 --- a/RELEASE_NOTES +++ b/RELEASE_NOTES @@ -32,6 +32,7 @@ Bugs fixed - Bug 2992 - lte: Send method of the LteUeNetDevice doesn't use protocolNumber parameter - Bug 2997 - lte: EpcTft::PacketFilter::Matches does not use ipv6 address to match an IP packet - Bug 2860 - mobility: Set Z coordinate for position-allocation classes +- Bug 3027 - lte: S1 signalling is done before RRC connection establishment is finished Known issues ------------ diff --git a/doc/models/Makefile b/doc/models/Makefile index d95dbef8c..4c9dd791b 100644 --- a/doc/models/Makefile +++ b/doc/models/Makefile @@ -130,9 +130,12 @@ SOURCEFIGS = \ $(SRC)/wimax/doc/WimaxArchitecture.dia \ $(SRC)/lte/doc/source/figures/epc-ctrl-arch.dia \ $(SRC)/lte/doc/source/figures/epc-data-flow-dl.dia \ + $(SRC)/lte/doc/source/figures/epc-data-flow-dl-with-split.dia \ $(SRC)/lte/doc/source/figures/epc-data-flow-ul.dia \ + $(SRC)/lte/doc/source/figures/epc-data-flow-ul-with-split.dia \ $(SRC)/lte/doc/source/figures/epc-profiling-scenario.dia \ $(SRC)/lte/doc/source/figures/epc-topology.dia \ + $(SRC)/lte/doc/source/figures/epc-topology-with-split.dia \ $(SRC)/lte/doc/source/figures/epc-topology-x2-enhanced.dia \ $(SRC)/lte/doc/source/figures/eutran-profiling-scenario.dia \ $(SRC)/lte/doc/source/figures/ff-example.dia \ @@ -144,7 +147,8 @@ SOURCEFIGS = \ $(SRC)/lte/doc/source/figures/lte-enb-phy.dia \ $(SRC)/lte/doc/source/figures/lte-ue-phy.dia \ $(SRC)/lte/doc/source/figures/lte-epc-x2-handover-seq-diagram.dia \ - $(SRC)/lte/doc/source/figures/lte-epc-e2e-data-protocol-stack.dia \ + $(SRC)/lte/doc/source/figures/lte-epc-e2e-data-protocol-stack-with-split.dia \ + $(SRC)/lte/doc/source/figures/lte-epc-e2e-control-protocol-stack-with-split.dia \ $(SRC)/lte/doc/source/figures/lte-interference-test-scenario.dia \ $(SRC)/lte/doc/source/figures/lte-subframe-structure.dia \ $(SRC)/lte/doc/source/figures/lte-epc-x2-interface.dia \ @@ -319,9 +323,12 @@ IMAGES_EPS = \ $(FIGURES)/WimaxArchitecture.eps \ $(FIGURES)/epc-ctrl-arch.eps \ $(FIGURES)/epc-data-flow-dl.eps \ + $(FIGURES)/epc-data-flow-dl-with-split.eps \ $(FIGURES)/epc-data-flow-ul.eps \ + $(FIGURES)/epc-data-flow-ul-with-split.eps \ $(FIGURES)/epc-profiling-scenario.eps \ $(FIGURES)/epc-topology.eps \ + $(FIGURES)/epc-topology-with-split.eps \ $(FIGURES)/epc-topology-x2-enhanced.eps \ $(FIGURES)/eutran-profiling-scenario.eps \ $(FIGURES)/ff-example.eps \ @@ -343,6 +350,8 @@ IMAGES_EPS = \ $(FIGURES)/lte-enb-phy.eps \ $(FIGURES)/lte-ue-phy.eps \ $(FIGURES)/lte-epc-e2e-data-protocol-stack.eps \ + $(FIGURES)/lte-epc-e2e-data-protocol-stack-with-split.eps \ + $(FIGURES)/lte-epc-e2e-control-protocol-stack-with-split.eps \ $(FIGURES)/lte-interference-test-scenario.eps \ $(FIGURES)/lte-subframe-structure.eps \ $(FIGURES)/lte-epc-x2-interface.eps \ diff --git a/src/lte/doc/Makefile b/src/lte/doc/Makefile index 0e4db44ce..eb303204d 100644 --- a/src/lte/doc/Makefile +++ b/src/lte/doc/Makefile @@ -13,9 +13,12 @@ FIGURES = $(SOURCE)/figures IMAGES_DIA = \ $(FIGURES)/epc-ctrl-arch.dia \ $(FIGURES)/epc-data-flow-dl.dia \ + $(FIGURES)/epc-data-flow-dl-with-split.dia \ $(FIGURES)/epc-data-flow-ul.dia \ + $(FIGURES)/epc-data-flow-ul-with-split.dia \ $(FIGURES)/epc-profiling-scenario.dia \ $(FIGURES)/epc-topology.dia \ + $(FIGURES)/epc-topology-with-split.dia \ $(FIGURES)/epc-topology-x2-enhanced.dia \ $(FIGURES)/eutran-profiling-scenario.dia \ $(FIGURES)/ff-example.dia \ @@ -27,7 +30,8 @@ IMAGES_DIA = \ $(FIGURES)/lte-enb-phy.dia \ $(FIGURES)/lte-ue-phy.dia \ $(FIGURES)/lte-epc-x2-handover-seq-diagram.dia \ - $(FIGURES)/lte-epc-e2e-data-protocol-stack.dia \ + $(FIGURES)/lte-epc-e2e-data-protocol-stack-with-split.dia \ + $(FIGURES)/lte-epc-e2e-control-protocol-stack-with-split.dia \ $(FIGURES)/lte-interference-test-scenario.dia \ $(FIGURES)/lte-subframe-structure.dia \ $(FIGURES)/lte-epc-x2-interface.dia \ diff --git a/src/lte/doc/source/figures/helpers.seqdiag b/src/lte/doc/source/figures/helpers.seqdiag index f7db1c60d..4a2ae22e7 100644 --- a/src/lte/doc/source/figures/helpers.seqdiag +++ b/src/lte/doc/source/figures/helpers.seqdiag @@ -1,24 +1,34 @@ - - diagram { SimProgram; LteHelper; EpcHelper; -SimProgram ->> LteHelper [label="create"] -SimProgram ->> EpcHelper [label="create"] -EpcHelper ->> EpcHelper [label="create MME and SGW/PGW"] -SimProgram ->> LteHelper [label="InstallEnbDevice"] -LteHelper ->> LteHelper [label="install protocol stack on eNB"] -LteHelper ->> EpcHelper [label="AddEnb"] -EpcHelper ->> EpcHelper [label="setup S1-U, S1-AP and S11"] -SimProgram ->> LteHelper [label="InstallUeDevice"] -LteHelper ->> LteHelper [label="install protocol stack on UE"] -SimProgram ->> LteHelper [label="Attach (UE, eNB)"] -LteHelper ->> LteHelper [label="tell UE NAS to start connection"] -LteHelper ->> EpcHelper [label="ActivateEpsBearer (default)"] -EpcHelper ->> EpcHelper [label="tell MME to activate bearer when UE connects"] -SimProgram ->> LteHelper [label="ActivateDedicatedEpsBearer"] -LteHelper ->> EpcHelper [label="ActivateEpsBearer"] -EpcHelper ->> EpcHelper [label="tell MME to activate bearer when UE connects"] +SimProgram => LteHelper [label="create"] +SimProgram => EpcHelper [label="create"] { + EpcHelper -> EpcHelper [label="create MME, SGW and PGW"] +} -} \ No newline at end of file +SimProgram => LteHelper [label="InstallEnbDevice"] { + LteHelper -> LteHelper [label="install protocol stack on eNB"] + LteHelper => EpcHelper [label="AddEnb"] { + EpcHelper -> EpcHelper [label="setup S1-U, S1-AP, S5 and S11"] + } +} + +SimProgram => LteHelper [label="InstallUeDevice"] { + LteHelper -> LteHelper [label="install protocol stack on UE"] +} + +SimProgram => LteHelper [label="Attach (UE, eNB)"] { + LteHelper -> LteHelper [label="tell UE NAS to start connection"] + LteHelper => EpcHelper [label="ActivateEpsBearer (default)"] { + EpcHelper -> EpcHelper [label="tell MME to activate bearer when UE connects"] + } +} + +SimProgram => LteHelper [label="ActivateDedicatedEpsBearer"] { + LteHelper => EpcHelper [label="ActivateEpsBearer"] { + EpcHelper -> EpcHelper [label="tell MME to activate bearer when UE connects"] + } +} + +} diff --git a/src/lte/doc/source/figures/nas-attach.seqdiag b/src/lte/doc/source/figures/nas-attach.seqdiag index e3df38269..1e414d9ac 100644 --- a/src/lte/doc/source/figures/nas-attach.seqdiag +++ b/src/lte/doc/source/figures/nas-attach.seqdiag @@ -1,24 +1,25 @@ diagram { - EpcUeNas; LteUeRrc; LteEnbRrc; EpcEnbApplication; EpcSgwPgwApplication; EpcMme; + EpcUeNas; LteUeRrc; LteEnbRrc; EpcEnbApplication; EpcMmeApplication; EpcSgwApplication; EpcPgwApplication; - EpcUeNas ->> LteUeRrc [label="ForceCampedOnEnb (CellId)"]; - EpcUeNas ->> LteUeRrc [label="Connect"] - LteUeRrc ->> LteEnbRrc [label="RRC Connection Request"] - LteEnbRrc ->> EpcEnbApplication [label="initial UE message"] - EpcEnbApplication ->> EpcMme [label="S1-AP INITIAL UE MESSAGE"] - EpcMme ->> EpcMme [label="store IMSI->eNB UE id (RNTI) mapping"] - EpcMme ->> EpcSgwPgwApplication [label="S11 CREATE SESSION"] - EpcSgwPgwApplication ->> EpcSgwPgwApplication [label="setup S1-U bearers"] - EpcMme <<- EpcSgwPgwApplication [label="S11 CREATE SESSION RESPONSE"] - EpcEnbApplication <<- EpcMme [label="S1-AP INITIAL CONTEXT SETUP (bearers to be created)"] - EpcEnbApplication ->> EpcMme [label="S1-AP INITIAL CONTEXT SETUP RESPONSE"] - EpcEnbApplication ->> EpcEnbApplication [label="setup S1-U bearers"] - LteEnbRrc <<- EpcEnbApplication [label="DataRadioBearerSetupRequest"] - LteEnbRrc ->> LteEnbRrc [label="setup data radio bearers"] - LteUeRrc <<- LteEnbRrc [label="RRC Connection Reconfiguration"] - LteUeRrc ->> LteUeRrc [label="setup data radio bearers"] - LteUeRrc ->> LteEnbRrc [label="RRC Connection Reconfiguration Completed"] + EpcUeNas ->> LteUeRrc [label="ForceCampedOnEnb (CellId)"] + EpcUeNas ->> LteUeRrc [label="Connect"] + LteUeRrc ->> LteEnbRrc [label="RRC Connection Request"] + LteEnbRrc ->> EpcEnbApplication [label="initial UE message"] + EpcEnbApplication ->> EpcMmeApplication [label="S1-AP INITIAL UE MESSAGE"] + EpcMmeApplication ->> EpcMmeApplication [label="store IMSI->\n eNB UE id (RNTI)\n mapping"] + EpcMmeApplication ->> EpcSgwApplication [label="S11 CREATE SESSION"] + EpcSgwApplication ->> EpcSgwApplication [label="setup S1-U\nbearers"] + EpcSgwApplication ->> EpcPgwApplication [label="S5 CREATE SESSION"] + EpcPgwApplication ->> EpcPgwApplication [label="setup S5 bearers"] + EpcSgwApplication <<- EpcPgwApplication [label="S5 CREATE SESSION\nRESPONSE"] + EpcMmeApplication <<- EpcSgwApplication [label="S11 CREATE SESSION\nRESPONSE"] + EpcEnbApplication <<- EpcMmeApplication [label="S1-AP INITIAL CONTEXT\n SETUP (bearers to be created)"] + EpcEnbApplication ->> EpcEnbApplication [label="setup S1-U\n bearers"] + LteEnbRrc <<- EpcEnbApplication [label="DataRadioBearerSetupRequest"] + LteEnbRrc ->> LteEnbRrc [label="setup data\n radio bearers"] + LteUeRrc <<- LteEnbRrc [label="RRC Connection\n Reconfiguration"] + LteUeRrc ->> LteUeRrc [label="setup data\n radio bearers"] + LteUeRrc ->> LteEnbRrc [label="RRC Connection\n Reconfiguration Completed"] } - diff --git a/src/lte/doc/source/lte-design.rst b/src/lte/doc/source/lte-design.rst index 27183b980..945e8bdba 100644 --- a/src/lte/doc/source/lte-design.rst +++ b/src/lte/doc/source/lte-design.rst @@ -18,7 +18,7 @@ the figure :ref:`fig-epc-topology`. There are two main components: stack (RRC, PDCP, RLC, MAC, PHY). These entities reside entirely within the UE and the eNB nodes. - * the EPC Model. This models includes core network + * the EPC Model. This model includes core network interfaces, protocols and entities. These entities and protocols reside within the SGW, PGW and MME nodes, and partially within the eNB nodes. @@ -26,7 +26,7 @@ the figure :ref:`fig-epc-topology`. There are two main components: .. _fig-epc-topology: -.. figure:: figures/epc-topology.* +.. figure:: figures/epc-topology-with-split.* :align: center Overview of the LTE-EPC simulation model @@ -115,16 +115,22 @@ The main objective of the EPC model is to provides means for the simulation of end-to-end IP connectivity over the LTE model. To this aim, it supports for the interconnection of multiple UEs to the Internet, via a radio access -network of multiple eNBs connected to a single SGW/PGW node, as shown +network of multiple eNBs connected to the core network, as shown in Figure :ref:`fig-epc-topology`. The following design choices have been made for the EPC model: #. The Packet Data Network (PDN) type supported is both IPv4 and IPv6. - #. The SGW and PGW functional entities are implemented within a single - node, which is hence referred to as the SGW/PGW node. - #. The scenarios with inter-SGW mobility are not of interests. Hence, a - single SGW/PGW node will be present in all simulations scenarios + In other words, the end-to-end connections between the UEs and the remote + hosts can be IPv4 and IPv6. However, the networks between the core network + elements (MME, SGWs and PGWs) are IPv4-only. + #. The SGW and PGW functional entities are implemented in different + nodes, which are hence referred to as the SGW node and PGW node, + respectively. + #. The MME functional entities is implemented as a network node, + which is hence referred to as the MME node. + #. The scenarios with inter-SGW mobility are not of interest. But + several SGW nodes may be present in simulations scenarios. #. A requirement for the EPC model is that it can be used to simulate the end-to-end performance of realistic applications. Hence, it should be possible to use with the EPC model any regular ns-3 application @@ -133,19 +139,20 @@ The following design choices have been made for the EPC model: with the presence of multiple eNBs, some of which might be equipped with a backhaul connection with limited capabilities. In order to simulate such scenarios, the user data plane - protocols being used between the eNBs and the SGW/PGW should be + protocols being used between the eNBs and the SGW should be modeled accurately. #. It should be possible for a single UE to use different applications with different QoS profiles. Hence, multiple EPS bearers should be supported for each UE. This includes the necessary classification of TCP/UDP traffic over IP done at the UE in the uplink and at the PGW in the downlink. - #. The focus of the EPC model is mainly on the EPC data plane. The - accurate modeling of the EPC control plane is, - for the time being, not a requirement; hence, the necessary control plane - interactions can be modeled in a simplified way by leveraging on direct - interaction among the different simulation objects via the - provided helper objects. + #. The initial focus of the EPC model is mainly on the EPC data plane. + The accurate modeling of the EPC control plane is, + for the time being, not a requirement; however, the necessary control + plane interactions among the different network nodes of the core network + are realized by implementing control protocols/messages among them. + Direct interaction among the different simulation objects via the + provided helper objects should be avoided as much as possible. #. The focus of the EPC model is on simulations of active users in ECM connected mode. Hence, all the functionality that is only relevant for ECM idle mode (in particular, tracking area update and paging) @@ -255,54 +262,44 @@ EPC Model EPC data plane -------------- -In Figure :ref:`fig-lte-epc-e2e-data-protocol-stack`, we represent the +In Figure :ref:`fig-lte-epc-e2e-data-protocol-stack-with-split`, we represent the end-to-end LTE-EPC data plane protocol stack as it is modeled in the -simulator. From the figure, it is evident that the -biggest simplification introduced in the data plane model -is the inclusion of the SGW and PGW functionality within a single -SGW/PGW node, which removes the need for the S5 or S8 interfaces -specified by 3GPP. On the other hand, for both the S1-U protocol stack and -the LTE radio protocol stack all the protocol layers specified by 3GPP -are present. +simulator. The figure shows all nodes in the data path, i.e. UE, eNB, +SGW, PGW and a remote host in the Internet. All protocol stacks +(S5 protocol stack, S1-U protocol stack and the LTE radio protocol stack) +specified by 3GPP are present. -.. _fig-lte-epc-e2e-data-protocol-stack: +.. _fig-lte-epc-e2e-data-protocol-stack-with-split: -.. figure:: figures/lte-epc-e2e-data-protocol-stack.* +.. figure:: figures/lte-epc-e2e-data-protocol-stack-with-split.* :align: center LTE-EPC data plane protocol stack - EPC control plane ----------------- The architecture of the implementation of the control plane model is -shown in figure :ref:`fig-epc-ctrl-arch`. The control interfaces that are -modeled explicitly are the S1-AP, the X2-AP and the S11 interfaces. +shown in figure :ref:`fig-lte-epc-e2e-control-protocol-stack-with-split`. +The control interfaces that are modeled explicitly are the S1-MME, the S11, and the S5 +interfaces. The X2 interface is also modeled explicitly and it is described in more +detail in section :ref:`sec-x2` -We note that the S1-AP and the S11 interfaces are modeled in a simplified -fashion, by using just one pair of interface classes to model the -interaction between entities that reside on different nodes (the eNB -and the MME for the S1-AP interface, and the MME and the SGW for the -S11 interface). In practice, this means that the primitives of these -interfaces are mapped to a direct function call between the two -objects. On the other hand, the X2-AP interface is being modeled using -protocol data units sent over an X2 link (modeled as a point-to-point -link); for this reason, the X2-AP interface model is more realistic. +The S1-MME, the S11 and the S5 interfaces are modeled using procotol data units sent +over its respective links. These interfaces use the SCTP protocol as transport protocol +but currently, the SCTP protocol is not modeled in the ns-3 simulator, so the +UDP protocol is used instead of the SCTP protocol. +.. _fig-lte-epc-e2e-control-protocol-stack-with-split: - -.. _fig-epc-ctrl-arch: - -.. figure:: figures/epc-ctrl-arch.* +.. figure:: figures/lte-epc-e2e-control-protocol-stack-with-split.* :align: center - EPC control model - + LTE-EPC control plane protocol stack @@ -3036,6 +3033,7 @@ The following RRC SAP have been implemented: \clearpage +.. _sec-nas: --- NAS @@ -3044,9 +3042,9 @@ NAS The focus of the LTE-EPC model is on the NAS Active state, which corresponds to EMM Registered, ECM connected, and RRC connected. Because of this, the following simplifications are made: - - EMM and ECM are not modeled explicitly; instead, the NAS entity at the UE will interact directly with the MME to perform actions that are equivalent (with gross simplifications) to taking the UE to the states EMM Connected and ECM Connected; +- EMM and ECM are not modeled explicitly; instead, the NAS entity at the UE will interact directly with the MME to perform actions that are equivalent (with gross simplifications) to taking the UE to the states EMM Connected and ECM Connected; - - the NAS also takes care of multiplexing uplink data packets coming from the upper layers into the appropriate EPS bearer by using the Traffic Flow Template classifier (TftClassifier). +- the NAS also takes care of multiplexing uplink data packets coming from the upper layers into the appropriate EPS bearer by using the Traffic Flow Template classifier (TftClassifier). - the NAS does not support PLMN and CSG selection @@ -3080,88 +3078,108 @@ procedure. ------------------ -S1 ------------------ +---------------- +S1, S5 and S11 +---------------- -S1-U -+++++++++ +S1-U and S5 (user plane) ++++++++++++++++++++++++++ -The S1-U interface is modeled in a realistic way by encapsulating +The S1-U and S5 interfaces are modeled in a realistic way by encapsulating data packets over GTP/UDP/IP, as done in real LTE-EPC systems. The corresponding protocol stack is shown in Figure -:ref:`fig-lte-epc-e2e-data-protocol-stack`. As shown in the figure, +:ref:`fig-lte-epc-e2e-data-protocol-stack-with-split`. As shown in the figure, there are two different layers of IP networking. The first one is the end-to-end layer, which provides end-to-end -connectivity to the users; this layers involves the UEs, the PGW and +connectivity to the users; this layer involves the UEs, the PGW and the remote host (including eventual internet routers and hosts in -between), but does not involve the eNB. In this version of LTE, the EPC +between), but does not involve the eNB and the SGW. In this version of LTE, the EPC supports both IPv4 and IPv6 type users. The 3GPP unique 64 bit IPv6 prefix allocation process for each UE and PGW is followed here. Each EPC is assigned -an unique 16 bit IPv4 and a 48 bit IPv6 network address from the pool of +a unique 16 bit IPv4 and a 48 bit IPv6 network address from the pool of 7.0.0.0/8 and 7777:f00d::/32 respectively. In the end-to-end IP connection between UE and PGW, all addresses are configured using these prefixes. The PGW's address is used by all UEs as the gateway to reach the internet. The second layer of IP networking is the EPC local area network. This -involves all eNB nodes and the SGW/PGW node. This network is +involves all eNB nodes, SGW nodes and PGW nodes. This network is implemented as a set of point-to-point links which connect each eNB -with the SGW/PGW node; thus, the SGW/PGW has a set of point-to-point -devices, each providing connectivity to a different eNB. By default, a -10.x.y.z/30 subnet is assigned to each point-to-point link (a /30 -subnet is the smallest subnet that allows for two distinct host -addresses). +with its corresponding SGW node and a point-to-point link which connect +each SGW node with its corresponding PGW node; +thus, each SGW has a set of point-to-point devices, each providing +connectivity to a different eNB. By default, a 10.x.y.z/30 subnet +is assigned to each point-to-point link (a /30 subnet is the smallest +subnet that allows for two distinct host addresses). As specified by 3GPP, the end-to-end IP communications is tunneled over the local EPC IP network using GTP/UDP/IP. In the following, we explain how this tunneling is implemented in the EPC model. The explanation is done by discussing the -end-to-end flow of data packets. +end-to-end flow of data packets. -.. _fig-epc-data-flow-dl: +.. _fig-epc-data-flow-dl-with-split: -.. figure:: figures/epc-data-flow-dl.* +.. figure:: figures/epc-data-flow-dl-with-split.* :align: center Data flow in the downlink between the internet and the UE To begin with, we consider the case of the downlink, which is depicted -in Figure :ref:`fig-epc-data-flow-dl`. +in Figure :ref:`fig-epc-data-flow-dl-with-split`. Downlink IPv4/IPv6 packets are generated from a generic remote host, and addressed to one of the UE device. Internet routing will take care of -forwarding the packet to the generic NetDevice of the SGW/PGW node +forwarding the packet to the generic NetDevice of the PGW node which is connected to the internet (this is the Gi interface according -to 3GPP terminology). The SGW/PGW has a VirtualNetDevice which is +to 3GPP terminology). The PGW has a VirtualNetDevice which is assigned the base IPv4 address of the EPC network; hence, static routing rules will cause the incoming packet from the internet to be routed through this VirtualNetDevice. In case of IPv6 address as destination, a manual route towards the VirtualNetDevice is inserted in the routing table, containing the 48 bit IPv6 prefix from which all the IPv6 addresses of the UEs and PGW are configured. Such device starts the GTP/UDP/IP tunneling procedure, -by forwarding the packet to a dedicated application in the SGW/PGW node which -is called EpcSgwPgwApplication. This application does the following operations: +by forwarding the packet to a dedicated application in the PGW node which +is called EpcPgwApplication. This application does the following operations: - #. it determines the eNB node to which the UE is attached, by looking - at the IP destination address (which is the address of the UE); + #. it determines the SGW node to which it must route the traffic + for this UE, by looking at the IP destination address + (which is the address of the UE); #. it classifies the packet using Traffic Flow Templates (TFTs) to identify to which EPS Bearer it belongs. EPS bearers have a - one-to-one mapping to S1-U Bearers, so this operation returns the + one-to-one mapping to S5 Bearers, so this operation returns the GTP-U Tunnel Endpoint Identifier (TEID) to which the packet belongs; #. it adds the corresponding GTP-U protocol header to the packet; - #. finally, it sends the packet over an UDP socket to the S1-U + #. finally, it sends the packet over a UDP socket to the S5 + point-to-point NetDevice, addressed to the appropriate SGW. + +As a consequence, the end-to-end IP packet with newly added IP, UDP +and GTP headers is sent through one of the S5 links to the SGW, where +it is received and delivered locally (as the destination address of +the outermost IP header matches the SGW IP address). The local delivery +process will forward the packet, via an UDP socket, to a dedicated +application called EpcSgwApplication. This application then performs +the following operations: + + #. it determines the eNB node to which the UE is attached, by looking + at the S5 TEID; + #. it maps the S5 TEID to get the S1 TEID. EPS bearers have a + one-to-one mapping to S1-U Bearers, so this operation returns the + S1 GTP-U Tunnel Endpoint Identifier (TEID) to which the packet + belongs; + #. it adds a new GTP-U protocol header to the packet; + #. finally, it sends the packet over a UDP socket to the S1-U point-to-point NetDevice, addressed to the eNB to which the UE is attached. -As a consequence, the end-to-end IP packet with newly added IP, UDP +Finally, the end-to-end IP packet with newly added IP, UDP and GTP headers is sent through one of the S1 links to the eNB, where it is received and delivered locally (as the destination address of -the outmost IP header matches the eNB IP address). The local delivery +the outermost IP header matches the eNB IP address). The local delivery process will forward the packet, via an UDP socket, to a dedicated application called EpcEnbApplication. This application then performs the following operations: - #. it removes the GTP header and retrieves the TEID which is + #. it removes the GTP header and retrieves the S1 TEID which is contained in it; #. leveraging on the one-to-one mapping between S1-U bearers and Radio Bearers (which is a 3GPP requirement), it determines the @@ -3185,15 +3203,15 @@ the UE, which is the end point of the downlink communication. -.. _fig-epc-data-flow-ul: +.. _fig-epc-data-flow-ul-with-split: -.. figure:: figures/epc-data-flow-ul.* +.. figure:: figures/epc-data-flow-ul-with-split.* :align: center Data flow in the uplink between the UE and the internet -The case of the uplink is depicted in Figure :ref:`fig-epc-data-flow-ul`. +The case of the uplink is depicted in Figure :ref:`fig-epc-data-flow-ul-with-split`. Uplink IP packets are generated by a generic application inside the UE, and forwarded by the local TCP/IP stack to the LteUeNetDevice of the UE. The LteUeNetDevice then performs the following operations: @@ -3221,27 +3239,45 @@ following operations: Bearers; #. it adds a GTP-U header on the packet, including the TEID determined previously; - #. it sends the packet to the SGW/PGW node via the UDP socket + #. it sends the packet to the SGW node via the UDP socket connected to the S1-U point-to-point net device. At this point, the packet contains the S1-U IP, UDP and GTP headers in addition to the original end-to-end IP header. When the packet is received by the corresponding S1-U point-to-point NetDevice of the -SGW/PGW node, it is delivered locally (as the destination address of +SGW node, it is delivered locally (as the destination address of the outmost IP header matches the address of the point-to-point net device). The local delivery process will forward the packet to the -EpcSgwPgwApplication via the corresponding UDP socket. The -EpcSgwPgwApplication then removes the GTP header and forwards the +EpcSgwApplication via the corresponding UDP socket. The +EpcSgwApplication then perfoms the following operations: + + #. it removes the GTP header and retrieves the S1-U TEID; + #. it maps the S1-U TEID to get the S5 TEID to which the packet + belongs; + #. it determines the PGW to which it must send the packet from + the TEID mapping; + #. it add a new GTP-U protocol header to the packet; + #. finally, it sends the packet over a UDP socket to the S5 + point-to-point NetDevice, addressed to the corresponding PGW. + +At this point, the packet contains the S5 IP, UDP and GTP headers in +addition to the original end-to-end IP header. When the packet is +received by the corresponding S5 point-to-point NetDevice of the +PGW node, it is delivered locally (as the destination address of +the outmost IP header matches the address of the point-to-point net +device). The local delivery process will forward the packet to the +EpcPgwApplication via the corresponding UDP socket. The +EpcPgwApplication then removes the GTP header and forwards the packet to the VirtualNetDevice. At this point, the outmost header of the packet is the end-to-end IP header. Hence, if the destination address within this header is a remote host on the internet, the packet is sent to the internet via the corresponding NetDevice of the -SGW/PGW. In the event that the packet is addressed to another UE, the -IP stack of the SGW/PGW will redirect the packet again to the -VirtualNetDevice, and the packet will go through the dowlink delivery +PGW. In the event that the packet is addressed to another UE, the +IP stack of the PGW will redirect the packet again to the +VirtualNetDevice, and the packet will go through the downlink delivery process in order to reach its destination UE. -Note that the EPS Bearer QoS is not enforced on the S1-U +Note that the EPS Bearer QoS is not enforced on the S1-U and S5 links, it is assumed that the overprovisioning of the link bandwidth is sufficient to meet the QoS requirements of all bearers. @@ -3250,11 +3286,9 @@ S1AP +++++ The S1-AP interface provides control plane interaction between the eNB -and the MME. In the simulator, this interface is modeled in an ideal -fashion, with direct interaction between the eNB and the MME objects, -without actually implementing the encoding of S1AP messages and -information elements specified in [TS36413]_ and without actually -transmitting any PDU on any link. +and the MME. In the simulator, this interface is modeled in a realistic +fashion transmitting the encoded S1AP messages and information elements +specified in [TS36413]_ on the S1-MME link. The S1-AP primitives that are modeled are: @@ -3264,6 +3298,37 @@ The S1-AP primitives that are modeled are: * PATH SWITCH REQUEST * PATH SWITCH REQUEST ACKNOWLEDGE + +S5 and S11 ++++++++++++ + +The S5 interface provides control plane interaction between the SGW +and the PGW. The S11 interface provides control plane interaction between +the SGw and the MME. Both interfaces use the GPRS Tunneling Protocol (GTPv2-C) +to tunnel signalling messages [TS29274]_ and use UDP as transport protocol. +In the simulator, these interfaces and protocol are modeled in a realistic +fashion transmitting the encoded GTP-C messages. + +The GTPv2-C primitives that are modeled are: + + * CREATE SESSION REQUEST + * CREATE SESSION RESPONSE + * MODIFY BEARER REQUEST + * MODIFY BEARER RESPONSE + * DELETE SESSION REQUEST + * DELETE SESSION RESPONSE + * DELETE BEARER COMMAND + * DELETE BEARER REQUEST + * DELETE BEARER RESPONSE + +Of these primitives, the first two are used upon initial UE attachment for the establishment +of the S1-U and S5 bearers. Section :ref:`sec-nas` shows the implementation of the attach +procedure. The other primitives are used during the handover to switch the S1-U bearers from +the source eNB to the target eNB as a consequence of the reception by the MME of a +PATH SWITCH REQUEST S1-AP message. + + + .. only:: latex .. raw:: latex diff --git a/src/lte/doc/source/lte-references.rst b/src/lte/doc/source/lte-references.rst index 6c55a90fc..c3709bc82 100644 --- a/src/lte/doc/source/lte-references.rst +++ b/src/lte/doc/source/lte-references.rst @@ -6,7 +6,7 @@ References .. [TS25814] 3GPP TS 25.814 "Physical layer aspect for evolved Universal Terrestrial Radio Access" -.. [TS29274] 3GPP TS 29.274 "Tunnelling Protocol for Control plane (GTPv2-C)" +.. [TS29274] 3GPP TS 29.274 "GPRS Tunnelling Protocol for Control plane (GTPv2-C)" .. [TS36101] 3GPP TS 36.101 "E-UTRA User Equipment (UE) radio transmission and reception" diff --git a/src/lte/examples/lena-simple-epc.cc b/src/lte/examples/lena-simple-epc.cc index dcd0cdb59..f21e06193 100644 --- a/src/lte/examples/lena-simple-epc.cc +++ b/src/lte/examples/lena-simple-epc.cc @@ -15,36 +15,33 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * - * Author: Jaume Nin + * Authors: Jaume Nin + * Manuel Requena */ -#include "ns3/lte-helper.h" -#include "ns3/epc-helper.h" #include "ns3/core-module.h" -#include "ns3/network-module.h" -#include "ns3/ipv4-global-routing-helper.h" +#include "ns3/point-to-point-module.h" #include "ns3/internet-module.h" -#include "ns3/mobility-module.h" -#include "ns3/lte-module.h" #include "ns3/applications-module.h" -#include "ns3/point-to-point-helper.h" -#include "ns3/config-store.h" +#include "ns3/mobility-module.h" +#include "ns3/config-store-module.h" +#include "ns3/lte-module.h" //#include "ns3/gtk-config-store.h" using namespace ns3; /** - * Sample simulation script for LTE+EPC. It instantiates several eNodeB, - * attaches one UE per eNodeB starts a flow for each UE to and from a remote host. - * It also starts yet another flow between each UE pair. + * Sample simulation script for LTE+EPC. It instantiates several eNodeBs, + * attaches one UE per eNodeB starts a flow for each UE to and from a remote host. + * It also starts another flow between each UE pair. */ -NS_LOG_COMPONENT_DEFINE ("EpcFirstExample"); +NS_LOG_COMPONENT_DEFINE ("LenaSimpleEpc"); int main (int argc, char *argv[]) { - uint16_t numberOfNodes = 2; + uint16_t numNodePairs = 2; Time simTime = MilliSeconds (1100); double distance = 60.0; Time interPacketInterval = MilliSeconds (100); @@ -55,7 +52,7 @@ main (int argc, char *argv[]) // Command line arguments CommandLine cmd; - cmd.AddValue ("numberOfNodes", "Number of eNodeBs + UE pairs", numberOfNodes); + cmd.AddValue ("numNodePairs", "Number of eNodeBs + UE pairs", numNodePairs); cmd.AddValue ("simTime", "Total duration of the simulation", simTime); cmd.AddValue ("distance", "Distance between eNBs [m]", distance); cmd.AddValue ("interPacketInterval", "Inter packet interval", interPacketInterval); @@ -63,6 +60,12 @@ main (int argc, char *argv[]) cmd.AddValue ("disableDl", "Disable downlink data flows", disableDl); cmd.AddValue ("disableUl", "Disable uplink data flows", disableUl); cmd.AddValue ("disablePl", "Disable data flows between peer UEs", disablePl); + cmd.Parse (argc, argv); + + ConfigStore inputConfig; + inputConfig.ConfigureDefaults (); + + // parse again so you can override default values from the command line cmd.Parse(argc, argv); if (useCa) @@ -73,15 +76,9 @@ main (int argc, char *argv[]) } Ptr lteHelper = CreateObject (); - Ptr epcHelper = CreateObject (); + Ptr epcHelper = CreateObject (); lteHelper->SetEpcHelper (epcHelper); - ConfigStore inputConfig; - inputConfig.ConfigureDefaults(); - - // parse again so you can override default values from the command line - cmd.Parse(argc, argv); - Ptr pgw = epcHelper->GetPgwNode (); // Create a single RemoteHost @@ -109,14 +106,14 @@ main (int argc, char *argv[]) NodeContainer ueNodes; NodeContainer enbNodes; - enbNodes.Create(numberOfNodes); - ueNodes.Create(numberOfNodes); + enbNodes.Create (numNodePairs); + ueNodes.Create (numNodePairs); // Install Mobility Model Ptr positionAlloc = CreateObject (); - for (uint16_t i = 0; i < numberOfNodes; i++) + for (uint16_t i = 0; i < numNodePairs; i++) { - positionAlloc->Add (Vector(distance * i, 0, 0)); + positionAlloc->Add (Vector (distance * i, 0, 0)); } MobilityHelper mobility; mobility.SetMobilityModel("ns3::ConstantPositionMobilityModel"); @@ -142,7 +139,7 @@ main (int argc, char *argv[]) } // Attach one UE per eNodeB - for (uint16_t i = 0; i < numberOfNodes; i++) + for (uint16_t i = 0; i < numNodePairs; i++) { lteHelper->Attach (ueLteDevs.Get(i), enbLteDevs.Get(i)); // side effect: the default EPS bearer will be activated @@ -160,7 +157,7 @@ main (int argc, char *argv[]) if (!disableDl) { PacketSinkHelper dlPacketSinkHelper ("ns3::UdpSocketFactory", InetSocketAddress (Ipv4Address::GetAny (), dlPort)); - serverApps.Add (dlPacketSinkHelper.Install (ueNodes.Get(u))); + serverApps.Add (dlPacketSinkHelper.Install (ueNodes.Get (u))); UdpClientHelper dlClient (ueIpIface.GetAddress (u), dlPort); dlClient.SetAttribute ("Interval", TimeValue (interPacketInterval)); @@ -180,21 +177,21 @@ main (int argc, char *argv[]) clientApps.Add (ulClient.Install (ueNodes.Get(u))); } - if (!disablePl && numberOfNodes > 1) + if (!disablePl && numNodePairs > 1) { ++otherPort; PacketSinkHelper packetSinkHelper ("ns3::UdpSocketFactory", InetSocketAddress (Ipv4Address::GetAny (), otherPort)); - serverApps.Add (packetSinkHelper.Install (ueNodes.Get(u))); + serverApps.Add (packetSinkHelper.Install (ueNodes.Get (u))); UdpClientHelper client (ueIpIface.GetAddress (u), otherPort); client.SetAttribute ("Interval", TimeValue (interPacketInterval)); client.SetAttribute ("MaxPackets", UintegerValue (1000000)); - clientApps.Add (client.Install (ueNodes.Get ((u + 1) % numberOfNodes))); + clientApps.Add (client.Install (ueNodes.Get ((u + 1) % numNodePairs))); } } - serverApps.Start (MilliSeconds (10)); - clientApps.Start (MilliSeconds (40)); + serverApps.Start (MilliSeconds (500)); + clientApps.Start (MilliSeconds (500)); lteHelper->EnableTraces (); // Uncomment to enable PCAP tracing //p2ph.EnablePcapAll("lena-simple-epc"); diff --git a/src/lte/helper/point-to-point-epc-helper.cc b/src/lte/helper/point-to-point-epc-helper.cc index 8fd0cb156..380e7036b 100644 --- a/src/lte/helper/point-to-point-epc-helper.cc +++ b/src/lte/helper/point-to-point-epc-helper.cc @@ -1,6 +1,6 @@ /* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ /* - * Copyright (c) 2011-2013 Centre Tecnologic de Telecomunicacions de Catalunya (CTTC) + * Copyright (c) 2011-2018 Centre Tecnologic de Telecomunicacions de Catalunya (CTTC) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -33,14 +33,16 @@ #include #include #include -#include #include "ns3/ipv6-static-routing.h" #include "ns3/ipv6-static-routing-helper.h" +#include +#include +#include + #include #include #include #include -#include #include #include #include @@ -54,18 +56,29 @@ NS_OBJECT_ENSURE_REGISTERED (PointToPointEpcHelper); PointToPointEpcHelper::PointToPointEpcHelper () - : m_gtpuUdpPort (2152) // fixed by the standard + : m_gtpuUdpPort (2152), // fixed by the standard + m_s11LinkDataRate (DataRate ("10Gb/s")), + m_s11LinkDelay (Seconds (0)), + m_s11LinkMtu (3000), + m_gtpcUdpPort (2123), // fixed by the standard + m_s5LinkDataRate (DataRate ("10Gb/s")), + m_s5LinkDelay (Seconds (0)), + m_s5LinkMtu (3000) { NS_LOG_FUNCTION (this); // To access the attribute value within the constructor ObjectBase::ConstructSelf (AttributeConstructionList ()); - // since we use point-to-point links for all S1-U links, + int retval; + // since we use point-to-point links for all links, // we use a /30 subnet which can hold exactly two addresses // (remember that net broadcast and null address are not valid) m_s1uIpv4AddressHelper.SetBase ("10.0.0.0", "255.255.255.252"); + m_s1apIpv4AddressHelper.SetBase ("11.0.0.0", "255.255.255.252"); m_x2Ipv4AddressHelper.SetBase ("12.0.0.0", "255.255.255.252"); + m_s11Ipv4AddressHelper.SetBase ("13.0.0.0", "255.255.255.252"); + m_s5Ipv4AddressHelper.SetBase ("14.0.0.0", "255.255.255.252"); // we use a /8 net for all UEs m_uePgwAddressHelper.SetBase ("7.0.0.0", "255.0.0.0"); @@ -73,24 +86,23 @@ PointToPointEpcHelper::PointToPointEpcHelper () // we use a /64 IPv6 net all UEs m_uePgwAddressHelper6.SetBase ("7777:f00d::", Ipv6Prefix (64)); - // create SgwPgwNode - m_sgwPgw = CreateObject (); + // Create PGW, SGW and MME nodes + m_pgw = CreateObject (); + m_sgw = CreateObject (); + m_mme = CreateObject (); InternetStackHelper internet; - internet.Install (m_sgwPgw); + internet.Install (m_pgw); + internet.Install (m_sgw); + internet.Install (m_mme); // The Tun device resides in different 64 bit subnet. // We must create an unique route to tun device for all the packets destined // to all 64 bit IPv6 prefixes of UEs, based by the unique 48 bit network prefix of this EPC network Ipv6StaticRoutingHelper ipv6RoutingHelper; - Ptr pgwStaticRouting = ipv6RoutingHelper.GetStaticRouting (m_sgwPgw->GetObject ()); + Ptr pgwStaticRouting = ipv6RoutingHelper.GetStaticRouting (m_pgw->GetObject ()); pgwStaticRouting->AddNetworkRouteTo ("7777:f00d::", Ipv6Prefix (64), Ipv6Address ("::"), 1, 0); - - // create S1-U socket - Ptr sgwPgwS1uSocket = Socket::CreateSocket (m_sgwPgw, TypeId::LookupByName ("ns3::UdpSocketFactory")); - int retval = sgwPgwS1uSocket->Bind (InetSocketAddress (Ipv4Address::GetAny (), m_gtpuUdpPort)); - NS_ASSERT (retval == 0); - // create TUN device implementing tunneling of user data over GTP-U/UDP/IP + // create TUN device implementing tunneling of user data over GTP-U/UDP/IP in the PGW m_tunDevice = CreateObject (); // allow jumbo packets @@ -99,10 +111,9 @@ PointToPointEpcHelper::PointToPointEpcHelper () // yes we need this m_tunDevice->SetAddress (Mac48Address::Allocate ()); - m_sgwPgw->AddDevice (m_tunDevice); + m_pgw->AddDevice (m_tunDevice); NetDeviceContainer tunDeviceContainer; tunDeviceContainer.Add (m_tunDevice); - // the TUN device is on the same subnet as the UEs, so when a packet // addressed to an UE arrives at the intenet to the WAN interface of // the PGW it will be forwarded to the TUN device. @@ -119,18 +130,106 @@ PointToPointEpcHelper::PointToPointEpcHelper () tunDeviceIpv6IfContainer.SetForwarding (0,true); tunDeviceIpv6IfContainer.SetDefaultRouteInAllNodes (0); - // create EpcSgwPgwApplication - m_sgwPgwApp = CreateObject (m_tunDevice, sgwPgwS1uSocket); - m_sgwPgw->AddApplication (m_sgwPgwApp); - - // connect SgwPgwApplication and virtual net device for tunneling - m_tunDevice->SetSendCallback (MakeCallback (&EpcSgwPgwApplication::RecvFromTunDevice, m_sgwPgwApp)); + // Create S5 link between PGW and SGW + NodeContainer pgwSgwNodes; + pgwSgwNodes.Add (m_pgw); + pgwSgwNodes.Add (m_sgw); + PointToPointHelper p2ph; + p2ph.SetDeviceAttribute ("DataRate", DataRateValue (m_s5LinkDataRate)); + p2ph.SetDeviceAttribute ("Mtu", UintegerValue (m_s5LinkMtu)); + p2ph.SetChannelAttribute ("Delay", TimeValue (m_s5LinkDelay)); + NetDeviceContainer pgwSgwDevices = p2ph.Install (m_pgw, m_sgw); + NS_LOG_LOGIC ("IPv4 ifaces of the PGW after installing p2p dev: " << m_pgw->GetObject ()->GetNInterfaces ()); + NS_LOG_LOGIC ("IPv4 ifaces of the SGW after installing p2p dev: " << m_sgw->GetObject ()->GetNInterfaces ()); + Ptr pgwDev = pgwSgwDevices.Get (0); + Ptr sgwDev = pgwSgwDevices.Get (1); + m_s5Ipv4AddressHelper.NewNetwork (); + Ipv4InterfaceContainer pgwSgwIpIfaces = m_s5Ipv4AddressHelper.Assign (pgwSgwDevices); + NS_LOG_LOGIC ("IPv4 ifaces of the PGW after assigning Ipv4 addr to S5 dev: " << m_pgw->GetObject ()->GetNInterfaces ()); + NS_LOG_LOGIC ("IPv4 ifaces of the SGW after assigning Ipv4 addr to S5 dev: " << m_sgw->GetObject ()->GetNInterfaces ()); + + Ipv4Address pgwS5Address = pgwSgwIpIfaces.GetAddress (0); + Ipv4Address sgwS5Address = pgwSgwIpIfaces.GetAddress (1); + + // Create S5-U socket in the PGW + Ptr pgwS5uSocket = Socket::CreateSocket (m_pgw, TypeId::LookupByName ("ns3::UdpSocketFactory")); + retval = pgwS5uSocket->Bind (InetSocketAddress (pgwS5Address, m_gtpuUdpPort)); + NS_ASSERT (retval == 0); + + // Create S5-C socket in the PGW + Ptr pgwS5cSocket = Socket::CreateSocket (m_pgw, TypeId::LookupByName ("ns3::UdpSocketFactory")); + retval = pgwS5cSocket->Bind (InetSocketAddress (pgwS5Address, m_gtpcUdpPort)); + NS_ASSERT (retval == 0); + + // Create EpcPgwApplication + m_pgwApp = CreateObject (m_tunDevice, pgwS5Address, pgwS5uSocket, pgwS5cSocket); + m_pgw->AddApplication (m_pgwApp); + + // Connect EpcPgwApplication and virtual net device for tunneling + m_tunDevice->SetSendCallback (MakeCallback (&EpcPgwApplication::RecvFromTunDevice, m_pgwApp)); - // Create MME and connect with SGW via S11 interface - m_mme = CreateObject (); - m_mme->SetS11SapSgw (m_sgwPgwApp->GetS11SapSgw ()); - m_sgwPgwApp->SetS11SapMme (m_mme->GetS11SapMme ()); + // Create S5-U socket in the SGW + Ptr sgwS5uSocket = Socket::CreateSocket (m_sgw, TypeId::LookupByName ("ns3::UdpSocketFactory")); + retval = sgwS5uSocket->Bind (InetSocketAddress (sgwS5Address, m_gtpuUdpPort)); + NS_ASSERT (retval == 0); + + // Create S5-C socket in the SGW + Ptr sgwS5cSocket = Socket::CreateSocket (m_sgw, TypeId::LookupByName ("ns3::UdpSocketFactory")); + retval = sgwS5cSocket->Bind (InetSocketAddress (sgwS5Address, m_gtpcUdpPort)); + NS_ASSERT (retval == 0); + + // Create S1-U socket in the SGW + Ptr sgwS1uSocket = Socket::CreateSocket (m_sgw, TypeId::LookupByName ("ns3::UdpSocketFactory")); + retval = sgwS1uSocket->Bind (InetSocketAddress (Ipv4Address::GetAny (), m_gtpuUdpPort)); + NS_ASSERT (retval == 0); + + // Create EpcSgwApplication + m_sgwApp = CreateObject (sgwS1uSocket, sgwS5Address, sgwS5uSocket, sgwS5cSocket); + m_sgw->AddApplication (m_sgwApp); + m_sgwApp->AddPgw (pgwS5Address); + m_pgwApp->AddSgw (sgwS5Address); + + + // Create S11 link between MME and SGW + NodeContainer mmeSgwNodes; + mmeSgwNodes.Add (m_mme); + mmeSgwNodes.Add (m_sgw); + PointToPointHelper s11P2ph; + s11P2ph.SetDeviceAttribute ("DataRate", DataRateValue (m_s11LinkDataRate)); + s11P2ph.SetDeviceAttribute ("Mtu", UintegerValue (m_s11LinkMtu)); + s11P2ph.SetChannelAttribute ("Delay", TimeValue (m_s11LinkDelay)); + NetDeviceContainer mmeSgwDevices = s11P2ph.Install (m_mme, m_sgw); + NS_LOG_LOGIC ("MME's IPv4 ifaces after installing p2p dev: " << m_mme->GetObject ()->GetNInterfaces ()); + NS_LOG_LOGIC ("SGW's IPv4 ifaces after installing p2p dev: " << m_sgw->GetObject ()->GetNInterfaces ()); + Ptr mmeDev = mmeSgwDevices.Get (0); + Ptr sgwS11Dev = mmeSgwDevices.Get (1); + m_s11Ipv4AddressHelper.NewNetwork (); + Ipv4InterfaceContainer mmeSgwIpIfaces = m_s11Ipv4AddressHelper.Assign (mmeSgwDevices); + NS_LOG_LOGIC ("MME's IPv4 ifaces after assigning Ipv4 addr to S11 dev: " << m_mme->GetObject ()->GetNInterfaces ()); + NS_LOG_LOGIC ("SGW's IPv4 ifaces after assigning Ipv4 addr to S11 dev: " << m_sgw->GetObject ()->GetNInterfaces ()); + + Ipv4Address mmeS11Address = mmeSgwIpIfaces.GetAddress (0); + Ipv4Address sgwS11Address = mmeSgwIpIfaces.GetAddress (1); + + // Create S11 socket in the MME + Ptr mmeS11Socket = Socket::CreateSocket (m_mme, TypeId::LookupByName ("ns3::UdpSocketFactory")); + retval = mmeS11Socket->Bind (InetSocketAddress (mmeS11Address, m_gtpcUdpPort)); + NS_ASSERT (retval == 0); + + // Create S11 socket in the SGW + Ptr sgwS11Socket = Socket::CreateSocket (m_sgw, TypeId::LookupByName ("ns3::UdpSocketFactory")); + retval = sgwS11Socket->Bind (InetSocketAddress (sgwS11Address, m_gtpcUdpPort)); + NS_ASSERT (retval == 0); + + + // Create MME Application and connect with SGW via S11 interface + m_mmeApp = CreateObject (); + m_mme->AddApplication (m_mmeApp); +// // m_mmeApp->SetS11SapSgw (m_sgwApp->GetS11SapSgw ()); +// // m_sgwApp->SetS11SapMme (m_mmeApp->GetS11SapMme ()); + m_mmeApp->AddSgw (sgwS11Address, mmeS11Address, mmeS11Socket); + m_sgwApp->AddMme (mmeS11Address, sgwS11Socket); } PointToPointEpcHelper::~PointToPointEpcHelper () @@ -141,6 +240,7 @@ PointToPointEpcHelper::~PointToPointEpcHelper () TypeId PointToPointEpcHelper::GetTypeId (void) { + NS_LOG_FUNCTION_NOARGS (); static TypeId tid = TypeId ("ns3::PointToPointEpcHelper") .SetParent () .SetGroupName("Lte") @@ -160,6 +260,36 @@ PointToPointEpcHelper::GetTypeId (void) UintegerValue (2000), MakeUintegerAccessor (&PointToPointEpcHelper::m_s1uLinkMtu), MakeUintegerChecker ()) + .AddAttribute ("S5LinkDataRate", + "The data rate to be used for the next S5 link to be created", + DataRateValue (DataRate ("10Gb/s")), + MakeDataRateAccessor (&PointToPointEpcHelper::m_s5LinkDataRate), + MakeDataRateChecker ()) + .AddAttribute ("S5LinkDelay", + "The delay to be used for the next S5 link to be created", + TimeValue (Seconds (0)), + MakeTimeAccessor (&PointToPointEpcHelper::m_s5LinkDelay), + MakeTimeChecker ()) + .AddAttribute ("S5LinkMtu", + "The MTU of the next S5 link to be created", + UintegerValue (2000), + MakeUintegerAccessor (&PointToPointEpcHelper::m_s5LinkMtu), + MakeUintegerChecker ()) + .AddAttribute ("S11LinkDataRate", + "The data rate to be used for the next S11 link to be created", + DataRateValue (DataRate ("10Gb/s")), + MakeDataRateAccessor (&PointToPointEpcHelper::m_s11LinkDataRate), + MakeDataRateChecker ()) + .AddAttribute ("S11LinkDelay", + "The delay to be used for the next S11 link to be created", + TimeValue (Seconds (0)), + MakeTimeAccessor (&PointToPointEpcHelper::m_s11LinkDelay), + MakeTimeChecker ()) + .AddAttribute ("S11LinkMtu", + "The MTU of the next S11 link to be created.", + UintegerValue (2000), + MakeUintegerAccessor (&PointToPointEpcHelper::m_s11LinkMtu), + MakeUintegerChecker ()) .AddAttribute ("X2LinkDataRate", "The data rate to be used for the next X2 link to be created", DataRateValue (DataRate ("10Gb/s")), @@ -211,8 +341,12 @@ PointToPointEpcHelper::DoDispose () NS_LOG_FUNCTION (this); m_tunDevice->SetSendCallback (MakeNullCallback, const Address&, const Address&, uint16_t> ()); m_tunDevice = 0; - m_sgwPgwApp = 0; - m_sgwPgw->Dispose (); + m_sgwApp = 0; + m_sgw->Dispose (); + m_pgwApp = 0; + m_pgw->Dispose (); + m_mmeApp = 0; + m_mme->Dispose (); } @@ -231,14 +365,14 @@ PointToPointEpcHelper::AddEnb (Ptr enb, Ptr lteEnbNetDevice, ui // create a point to point link between the new eNB and the SGW with // the corresponding new NetDevices on each side NodeContainer enbSgwNodes; - enbSgwNodes.Add (m_sgwPgw); + enbSgwNodes.Add (m_sgw); enbSgwNodes.Add (enb); PointToPointHelper p2ph; p2ph.SetDeviceAttribute ("DataRate", DataRateValue (m_s1uLinkDataRate)); p2ph.SetDeviceAttribute ("Mtu", UintegerValue (m_s1uLinkMtu)); p2ph.SetChannelAttribute ("Delay", TimeValue (m_s1uLinkDelay)); - NetDeviceContainer enbSgwDevices = p2ph.Install (enb, m_sgwPgw); - NS_LOG_LOGIC ("number of Ipv4 ifaces of the eNB after installing p2p dev: " << enb->GetObject ()->GetNInterfaces ()); + NetDeviceContainer enbSgwDevices = p2ph.Install (enb, m_sgw); + NS_LOG_LOGIC ("Ipv4 ifaces of the eNB after installing p2p dev: " << enb->GetObject ()->GetNInterfaces ()); Ptr enbDev = enbSgwDevices.Get (0); Ptr sgwDev = enbSgwDevices.Get (1); @@ -251,21 +385,24 @@ PointToPointEpcHelper::AddEnb (Ptr enb, Ptr lteEnbNetDevice, ui Ipv4InterfaceContainer enbSgwIpIfaces = m_s1uIpv4AddressHelper.Assign (enbSgwDevices); NS_LOG_LOGIC ("number of Ipv4 ifaces of the eNB after assigning Ipv4 addr to S1 dev: " << enb->GetObject ()->GetNInterfaces ()); - Ipv4Address enbAddress = enbSgwIpIfaces.GetAddress (0); - Ipv4Address sgwAddress = enbSgwIpIfaces.GetAddress (1); + Ipv4Address enbS1uAddress = enbSgwIpIfaces.GetAddress (0); + Ipv4Address sgwS1uAddress = enbSgwIpIfaces.GetAddress (1); // create S1-U socket for the ENB Ptr enbS1uSocket = Socket::CreateSocket (enb, TypeId::LookupByName ("ns3::UdpSocketFactory")); - int retval = enbS1uSocket->Bind (InetSocketAddress (enbAddress, m_gtpuUdpPort)); + int retval = enbS1uSocket->Bind (InetSocketAddress (enbS1uAddress, m_gtpuUdpPort)); NS_ASSERT (retval == 0); + // give PacketSocket powers to the eNB + //PacketSocketHelper packetSocket; + //packetSocket.Install (enb); // create LTE socket for the ENB Ptr enbLteSocket = Socket::CreateSocket (enb, TypeId::LookupByName ("ns3::PacketSocketFactory")); PacketSocketAddress enbLteSocketBindAddress; enbLteSocketBindAddress.SetSingleDevice (lteEnbNetDevice->GetIfIndex ()); enbLteSocketBindAddress.SetProtocol (Ipv4L3Protocol::PROT_NUMBER); retval = enbLteSocket->Bind (enbLteSocketBindAddress); - NS_ASSERT (retval == 0); + NS_ASSERT (retval == 0); PacketSocketAddress enbLteSocketConnectAddress; enbLteSocketConnectAddress.SetPhysicalAddress (Mac48Address::GetBroadcast ()); enbLteSocketConnectAddress.SetSingleDevice (lteEnbNetDevice->GetIfIndex ()); @@ -287,22 +424,21 @@ PointToPointEpcHelper::AddEnb (Ptr enb, Ptr lteEnbNetDevice, ui retval = enbLteSocket6->Connect (enbLteSocketConnectAddress6); NS_ASSERT (retval == 0); - NS_LOG_INFO ("create EpcEnbApplication"); - Ptr enbApp = CreateObject (enbLteSocket, enbLteSocket6, enbS1uSocket, enbAddress, sgwAddress, cellId); + NS_LOG_INFO ("Create EpcEnbApplication"); + Ptr enbApp = CreateObject (enbLteSocket, enbLteSocket6, enbS1uSocket, enbS1uAddress, sgwS1uAddress, cellId); enb->AddApplication (enbApp); NS_ASSERT (enb->GetNApplications () == 1); NS_ASSERT_MSG (enb->GetApplication (0)->GetObject () != 0, "cannot retrieve EpcEnbApplication"); NS_LOG_LOGIC ("enb: " << enb << ", enb->GetApplication (0): " << enb->GetApplication (0)); - NS_LOG_INFO ("Create EpcX2 entity"); Ptr x2 = CreateObject (); enb->AggregateObject (x2); - NS_LOG_INFO ("connect S1-AP interface"); - m_mme->AddEnb (cellId, enbAddress, enbApp->GetS1apSapEnb ()); - m_sgwPgwApp->AddEnb (cellId, enbAddress, sgwAddress); - enbApp->SetS1apSapMme (m_mme->GetS1apSapMme ()); + NS_LOG_INFO ("Connect S1-AP interface"); + m_mmeApp->AddEnb (cellId, enbS1uAddress, enbApp->GetS1apSapEnb ()); + m_sgwApp->AddEnb (cellId, enbS1uAddress, sgwS1uAddress); + enbApp->SetS1apSapMme (m_mmeApp->GetS1apSapMme ()); } @@ -361,12 +497,10 @@ PointToPointEpcHelper::AddX2Interface (Ptr enb1, Ptr enb2) void PointToPointEpcHelper::AddUe (Ptr ueDevice, uint64_t imsi) { - NS_LOG_FUNCTION (this << imsi << ueDevice ); - - m_mme->AddUe (imsi); - m_sgwPgwApp->AddUe (imsi); - + NS_LOG_FUNCTION (this << imsi << ueDevice); + m_mmeApp->AddUe (imsi); + m_pgwApp->AddUe (imsi); } uint8_t @@ -389,7 +523,7 @@ PointToPointEpcHelper::ActivateEpsBearer (Ptr ueDevice, uint64_t imsi { Ipv4Address ueAddr = ueIpv4->GetAddress (interface, 0).GetLocal (); NS_LOG_LOGIC (" UE IPv4 address: " << ueAddr); - m_sgwPgwApp->SetUeAddress (imsi, ueAddr); + m_pgwApp->SetUeAddress (imsi, ueAddr); } } if (ueIpv6) @@ -399,10 +533,10 @@ PointToPointEpcHelper::ActivateEpsBearer (Ptr ueDevice, uint64_t imsi { Ipv6Address ueAddr6 = ueIpv6->GetAddress (interface6, 1).GetAddress (); NS_LOG_LOGIC (" UE IPv6 address: " << ueAddr6); - m_sgwPgwApp->SetUeAddress6 (imsi, ueAddr6); + m_pgwApp->SetUeAddress6 (imsi, ueAddr6); } } - uint8_t bearerId = m_mme->AddBearer (imsi, tft, bearer); + uint8_t bearerId = m_mmeApp->AddBearer (imsi, tft, bearer); Ptr ueLteDevice = ueDevice->GetObject (); if (ueLteDevice) { @@ -414,7 +548,7 @@ PointToPointEpcHelper::ActivateEpsBearer (Ptr ueDevice, uint64_t imsi Ptr PointToPointEpcHelper::GetPgwNode () { - return m_sgwPgw; + return m_pgw; } Ipv4InterfaceContainer @@ -440,14 +574,14 @@ Ipv4Address PointToPointEpcHelper::GetUeDefaultGatewayAddress () { // return the address of the tun device - return m_sgwPgw->GetObject ()->GetAddress (1, 0).GetLocal (); + return m_pgw->GetObject ()->GetAddress (1, 0).GetLocal (); } Ipv6Address PointToPointEpcHelper::GetUeDefaultGatewayAddress6 () { // return the address of the tun device - return m_sgwPgw->GetObject ()->GetAddress (1, 1).GetAddress (); + return m_pgw->GetObject ()->GetAddress (1, 1).GetAddress (); } } // namespace ns3 diff --git a/src/lte/helper/point-to-point-epc-helper.h b/src/lte/helper/point-to-point-epc-helper.h index c6da63591..980d8bc78 100644 --- a/src/lte/helper/point-to-point-epc-helper.h +++ b/src/lte/helper/point-to-point-epc-helper.h @@ -1,6 +1,6 @@ /* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ /* - * Copyright (c) 2011-2013 Centre Tecnologic de Telecomunicacions de Catalunya (CTTC) + * Copyright (c) 2011-2018 Centre Tecnologic de Telecomunicacions de Catalunya (CTTC) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -36,31 +36,31 @@ namespace ns3 { class Node; class NetDevice; class VirtualNetDevice; -class EpcSgwPgwApplication; +class EpcSgwApplication; +class EpcPgwApplication; +class EpcMmeApplication; class EpcX2; -class EpcMme; /** * \ingroup lte * \brief Create an EPC network with PointToPoint links * - * This Helper will create an EPC network topology comprising of a - * single node that implements both the SGW and PGW functionality, and - * an MME node. The S1-U, X2-U and X2-C interfaces are realized over + * This Helper will create an EPC network topology comprising of + * three nodes: SGW, PGW and MME. + * The S1-U, X2-U, X2-C, S5 and S11 interfaces are realized over * PointToPoint links. */ class PointToPointEpcHelper : public EpcHelper { public: - - /** + /** * Constructor */ PointToPointEpcHelper (); - /** + /** * Destructor - */ + */ virtual ~PointToPointEpcHelper (); // inherited from Object @@ -96,31 +96,46 @@ private: Ipv6AddressHelper m_uePgwAddressHelper6; /** - * SGW-PGW network element + * SGW network element */ - Ptr m_sgwPgw; + Ptr m_sgw; /** - * SGW-PGW application + * PGW network element */ - Ptr m_sgwPgwApp; + Ptr m_pgw; + + /** + * MME network element + */ + Ptr m_mme; + + /** + * SGW application + */ + Ptr m_sgwApp; + + /** + * PGW application + */ + Ptr m_pgwApp; + + /** + * MME application + */ + Ptr m_mmeApp; /** * TUN device implementing tunneling of user data over GTP-U/UDP/IP */ Ptr m_tunDevice; - /** - * MME network element - */ - Ptr m_mme; - /** * S1-U interfaces */ - /** - * helper to assign addresses to S1-U NetDevices + /** + * Helper to assign addresses to S1-U NetDevices */ Ipv4AddressHelper m_s1uIpv4AddressHelper; @@ -146,6 +161,59 @@ private: * UDP port where the GTP-U Socket is bound, fixed by the standard as 2152 */ uint16_t m_gtpuUdpPort; + /** + * Helper to assign addresses to S1-MME NetDevices + */ + Ipv4AddressHelper m_s1apIpv4AddressHelper; + + /** + * Helper to assign addresses to S11 NetDevices + */ + Ipv4AddressHelper m_s11Ipv4AddressHelper; + + /** + * The data rate to be used for the next S11 link to be created + */ + DataRate m_s11LinkDataRate; + + /** + * The delay to be used for the next S11 link to be created + */ + Time m_s11LinkDelay; + + /** + * The MTU of the next S11 link to be created + */ + uint16_t m_s11LinkMtu; + + /** + * UDP port where the GTPv2-C Socket is bound, fixed by the standard as 2123 + */ + uint16_t m_gtpcUdpPort; + + /** + * S5 interfaces + */ + + /** + * Helper to assign addresses to S5 NetDevices + */ + Ipv4AddressHelper m_s5Ipv4AddressHelper; + + /** + * The data rate to be used for the next S5 link to be created + */ + DataRate m_s5LinkDataRate; + + /** + * The delay to be used for the next S5 link to be created + */ + Time m_s5LinkDelay; + + /** + * The MTU of the next S5 link to be created + */ + uint16_t m_s5LinkMtu; /** * Map storing for each IMSI the corresponding eNB NetDevice diff --git a/src/lte/model/epc-enb-application.cc b/src/lte/model/epc-enb-application.cc index dcc5dc517..e78fa0e01 100644 --- a/src/lte/model/epc-enb-application.cc +++ b/src/lte/model/epc-enb-application.cc @@ -206,18 +206,17 @@ void EpcEnbApplication::DoInitialContextSetupRequest (uint64_t mmeUeS1Id, uint16_t enbUeS1Id, std::list erabToBeSetupList) { NS_LOG_FUNCTION (this); - + + uint64_t imsi = mmeUeS1Id; + std::map::iterator imsiIt = m_imsiRntiMap.find (imsi); + NS_ASSERT_MSG (imsiIt != m_imsiRntiMap.end (), "unknown IMSI"); + uint16_t rnti = imsiIt->second; + for (std::list::iterator erabIt = erabToBeSetupList.begin (); erabIt != erabToBeSetupList.end (); ++erabIt) { // request the RRC to setup a radio bearer - - uint64_t imsi = mmeUeS1Id; - std::map::iterator imsiIt = m_imsiRntiMap.find (imsi); - NS_ASSERT_MSG (imsiIt != m_imsiRntiMap.end (), "unknown IMSI"); - uint16_t rnti = imsiIt->second; - struct EpcEnbS1SapUser::DataRadioBearerSetupRequestParameters params; params.rnti = rnti; params.bearer = erabIt->erabLevelQosParameters; @@ -229,8 +228,12 @@ EpcEnbApplication::DoInitialContextSetupRequest (uint64_t mmeUeS1Id, uint16_t en // side effect: create entries if not exist m_rbidTeidMap[rnti][erabIt->erabId] = params.gtpTeid; m_teidRbidMap[params.gtpTeid] = rbid; - } + + // Send Initial Context Setup Request to RRC + struct EpcEnbS1SapUser::InitialContextSetupRequestParameters params; + params.rnti = rnti; + m_s1SapUser->InitialContextSetupRequest (params); } void diff --git a/src/lte/model/epc-enb-s1-sap.h b/src/lte/model/epc-enb-s1-sap.h index 6b474826d..1ac4aa697 100644 --- a/src/lte/model/epc-enb-s1-sap.h +++ b/src/lte/model/epc-enb-s1-sap.h @@ -22,7 +22,6 @@ #define EPC_ENB_S1_SAP_H #include -#include #include #include @@ -105,17 +104,30 @@ class EpcEnbS1SapUser { public: virtual ~EpcEnbS1SapUser (); - + + /** + * Parameters passed to InitialContextSetupRequest () + */ + struct InitialContextSetupRequestParameters + { + uint16_t rnti; /**< the RNTI identifying the UE */ + }; + + /** + * Initial context setup request + * + * \param params + */ + virtual void InitialContextSetupRequest (InitialContextSetupRequestParameters params) = 0; + /** * Parameters passed to DataRadioBearerSetupRequest () - * */ struct DataRadioBearerSetupRequestParameters { uint16_t rnti; /**< the RNTI identifying the UE for which the - DataRadioBearer is to be created */ - EpsBearer bearer; /**< the characteristics of the bearer to be set - up */ + DataRadioBearer is to be created */ + EpsBearer bearer; /**< the characteristics of the bearer to be setup */ uint8_t bearerId; /**< the EPS Bearer Identifier */ uint32_t gtpTeid; /**< S1-bearer GTP tunnel endpoint identifier, see 36.423 9.2.1 */ Ipv4Address transportLayerAddress; /**< IP Address of the SGW, see 36.423 9.2.1 */ @@ -228,6 +240,7 @@ public: MemberEpcEnbS1SapUser (C* owner); // inherited from EpcEnbS1SapUser + virtual void InitialContextSetupRequest (InitialContextSetupRequestParameters params); virtual void DataRadioBearerSetupRequest (DataRadioBearerSetupRequestParameters params); virtual void PathSwitchRequestAcknowledge (PathSwitchRequestAcknowledgeParameters params); @@ -247,6 +260,12 @@ MemberEpcEnbS1SapUser::MemberEpcEnbS1SapUser () { } +template +void MemberEpcEnbS1SapUser::InitialContextSetupRequest (InitialContextSetupRequestParameters params) +{ + m_owner->DoInitialContextSetupRequest (params); +} + template void MemberEpcEnbS1SapUser::DataRadioBearerSetupRequest (DataRadioBearerSetupRequestParameters params) { diff --git a/src/lte/model/epc-gtpc-header.cc b/src/lte/model/epc-gtpc-header.cc new file mode 100644 index 000000000..e699cd9fb --- /dev/null +++ b/src/lte/model/epc-gtpc-header.cc @@ -0,0 +1,1287 @@ +/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2018 Centre Tecnologic de Telecomunicacions de Catalunya (CTTC) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Manuel Requena + */ + +#include "ns3/epc-gtpc-header.h" +#include "ns3/log.h" + +namespace ns3 { + +NS_LOG_COMPONENT_DEFINE ("GtpcHeader"); + +NS_OBJECT_ENSURE_REGISTERED (GtpcHeader); + +TypeId +GtpcHeader::GetTypeId (void) +{ + static TypeId tid = TypeId ("ns3::GtpcHeader") + .SetParent
() + .SetGroupName ("Lte") + .AddConstructor (); + return tid; +} + +GtpcHeader::GtpcHeader () + : m_teidFlag (false), + m_messageType (0), + m_messageLength (4), + m_teid (0), + m_sequenceNumber (0) +{ +} + +GtpcHeader::~GtpcHeader () +{ +} + +TypeId +GtpcHeader::GetInstanceTypeId (void) const +{ + return GetTypeId (); +} + +uint32_t +GtpcHeader::GetSerializedSize (void) const +{ + return m_teidFlag ? 12 : 8; +} + +void +GtpcHeader::Serialize (Buffer::Iterator start) const +{ + NS_FATAL_ERROR ("Serialize GTP-C header is forbidden"); +} + +void +GtpcHeader::PreSerialize (Buffer::Iterator &i) const +{ + i.WriteU8 ((2 << 5) | (1 << 3)); + i.WriteU8 (m_messageType); + i.WriteHtonU16 (m_messageLength); + i.WriteHtonU32 (m_teid); + i.WriteU8 ((m_sequenceNumber & 0x00ff0000) >> 16); + i.WriteU8 ((m_sequenceNumber & 0x0000ff00) >> 8); + i.WriteU8 (m_sequenceNumber & 0x000000ff); + i.WriteU8 (0); +} + +uint32_t +GtpcHeader::Deserialize (Buffer::Iterator start) +{ + return PreDeserialize (start); +} + +uint32_t +GtpcHeader::PreDeserialize (Buffer::Iterator &i) +{ + uint8_t firstByte = i.ReadU8 (); + uint8_t version = (firstByte >> 5) & 0x07; + if (version != 2) + { + NS_FATAL_ERROR ("GTP-C version not supported"); + return 0; + } + + m_teidFlag = ((firstByte >> 3) & 0x01) == 1; + if (!m_teidFlag) + { + NS_FATAL_ERROR ("TEID is missing"); + return 0; + } + + m_messageType = i.ReadU8 (); + m_messageLength = i.ReadNtohU16 (); + if (m_teidFlag) + { + m_teid = i.ReadNtohU32 (); + } + m_sequenceNumber = i.ReadU8 () << 16 | i.ReadU8 () << 8 | i.ReadU8 (); + i.ReadU8(); + + return GtpcHeader::GetSerializedSize (); +} + +void +GtpcHeader::Print (std::ostream &os) const +{ + os << " messageType " << (uint32_t) m_messageType << " messageLength " << m_messageLength; + os << " TEID " << m_teid << " sequenceNumber " << m_sequenceNumber; +} + +uint32_t +GtpcHeader::GetMessageSize (void) const +{ + return 0; +} + +uint8_t +GtpcHeader::GetMessageType () const +{ + return m_messageType; +} + +uint16_t +GtpcHeader::GetMessageLength () const +{ + return m_messageLength; +} + +uint32_t +GtpcHeader::GetTeid () const +{ + return m_teid; +} + +uint32_t +GtpcHeader::GetSequenceNumber () const +{ + return m_sequenceNumber; +} + +void +GtpcHeader::SetMessageType (uint8_t messageType) +{ + m_messageType = messageType; +} + +void +GtpcHeader::SetMessageLength (uint16_t messageLength) +{ + m_messageLength = messageLength; +} + +void +GtpcHeader::SetTeid (uint32_t teid) +{ + m_teidFlag = true; + m_teid = teid; + m_messageLength = m_teidFlag ? 8 : 4; +} + +void +GtpcHeader::SetSequenceNumber (uint32_t sequenceNumber) +{ + m_sequenceNumber = sequenceNumber; +} + +void +GtpcHeader::SetIesLength (uint16_t iesLength) +{ + m_messageLength = iesLength; + m_messageLength += (m_teidFlag) ? 8 : 4; +} + +void +GtpcHeader::ComputeMessageLength (void) +{ + SetIesLength (GetMessageSize ()); +} +///////////////////////////////////////////////////////////////////// + +void +GtpcIes::SerializeImsi (Buffer::Iterator &i, uint64_t imsi) const +{ + i.WriteU8 (1); // IE Type = IMSI + i.WriteHtonU16 (8); // Length + i.WriteU8 (0); // Spare + Instance + i.WriteHtonU64 (imsi); +} + +uint32_t +GtpcIes::DeserializeImsi (Buffer::Iterator &i, uint64_t &imsi) +{ + uint8_t type = i.ReadU8 (); + NS_ASSERT_MSG (type == 1, "Wrong IMSI IE type = " << (uint16_t) type); + uint16_t length = i.ReadNtohU16 (); + NS_ASSERT_MSG (length == 8, "Wrong IMSI IE length"); + uint8_t instance = i.ReadU8 () & 0x0f; + NS_ASSERT_MSG (instance == 0, "Wrong IMSI IE instance"); + imsi = i.ReadNtohU64 (); + + return serializedSizeImsi; +} + +void +GtpcIes::SerializeCause (Buffer::Iterator &i, Cause_t cause) const +{ + i.WriteU8 (2); // IE Type = Cause + i.WriteHtonU16 (2); // Length + i.WriteU8 (0); // Spare + Instance + i.WriteU8 (cause); // Cause value + i.WriteU8 (0); // Spare + CS +} + +uint32_t +GtpcIes::DeserializeCause (Buffer::Iterator &i, Cause_t &cause) +{ + uint8_t type = i.ReadU8 (); + NS_ASSERT_MSG (type == 2, "Wrong Cause IE type = " << (uint16_t) type); + uint16_t length = i.ReadNtohU16 (); + NS_ASSERT_MSG (length == 2, "Wrong Cause IE length"); + uint8_t instance = i.ReadU8 () & 0x0f; + NS_ASSERT_MSG (instance == 0, "Wrong Cause IE instance"); + cause = Cause_t (i.ReadU8 ()); + i.ReadU8 (); + + return serializedSizeCause; +} + +void +GtpcIes::SerializeEbi (Buffer::Iterator &i, uint8_t epsBearerId) const +{ + i.WriteU8 (73); // IE Type = EPS Bearer ID (EBI) + i.WriteHtonU16 (1); // Length + i.WriteU8 (0); // Spare + Instance + i.WriteU8 (epsBearerId & 0x0f); +} + +uint32_t +GtpcIes::DeserializeEbi (Buffer::Iterator &i, uint8_t &epsBearerId) +{ + uint8_t type = i.ReadU8 (); + NS_ASSERT_MSG (type == 73, "Wrong EBI IE type = " << (uint16_t) type); + uint16_t length = i.ReadNtohU16 (); + NS_ASSERT_MSG (length == 1, "Wrong EBI IE length"); + uint8_t instance = i.ReadU8 (); + NS_ASSERT_MSG (instance == 0, "Wrong EBI IE instance"); + epsBearerId = i.ReadU8 () & 0x0f; + + return serializedSizeEbi; +} + +void +GtpcIes::WriteHtonU40 (Buffer::Iterator &i, uint64_t data) const +{ + i.WriteU8 ((data >> 32) & 0xff); + i.WriteU8 ((data >> 24) & 0xff); + i.WriteU8 ((data >> 16) & 0xff); + i.WriteU8 ((data >> 8) & 0xff); + i.WriteU8 ((data >> 0) & 0xff); +} + +uint64_t +GtpcIes::ReadNtohU40 (Buffer::Iterator &i) +{ + uint64_t retval = 0; + retval |= i.ReadU8 (); + retval <<= 8; + retval |= i.ReadU8 (); + retval <<= 8; + retval |= i.ReadU8 (); + retval <<= 8; + retval |= i.ReadU8 (); + retval <<= 8; + retval |= i.ReadU8 (); + return retval; +} + +void +GtpcIes::SerializeBearerQos (Buffer::Iterator &i, EpsBearer bearerQos) const +{ + i.WriteU8 (80); // IE Type = Bearer QoS + i.WriteHtonU16 (22); // Length + i.WriteU8 (0); // Spare + Instance + i.WriteU8 (0); // MRE TODO: bearerQos.arp + i.WriteU8 (bearerQos.qci); + WriteHtonU40 (i, bearerQos.gbrQosInfo.mbrUl); + WriteHtonU40 (i, bearerQos.gbrQosInfo.mbrDl); + WriteHtonU40 (i, bearerQos.gbrQosInfo.gbrUl); + WriteHtonU40 (i, bearerQos.gbrQosInfo.gbrDl); +} + +uint32_t +GtpcIes::DeserializeBearerQos (Buffer::Iterator &i, EpsBearer &bearerQos) +{ + uint8_t type = i.ReadU8 (); + NS_ASSERT_MSG (type == 80, "Wrong Bearer QoS IE type = " << (uint16_t) type); + uint16_t length = i.ReadNtohU16 (); + NS_ASSERT_MSG (length == 22, "Wrong Bearer QoS IE length"); + uint8_t instance = i.ReadU8 (); + NS_ASSERT_MSG (instance == 0, "Wrong Bearer QoS IE instance"); + i.ReadU8 (); + bearerQos.qci = EpsBearer::Qci (i.ReadU8 ()); + bearerQos.gbrQosInfo.mbrUl = ReadNtohU40 (i); + bearerQos.gbrQosInfo.mbrDl = ReadNtohU40 (i); + bearerQos.gbrQosInfo.gbrUl = ReadNtohU40 (i); + bearerQos.gbrQosInfo.gbrDl = ReadNtohU40 (i); + return serializedSizeBearerQos; +} + +void +GtpcIes::SerializeBearerTft (Buffer::Iterator &i, std::list packetFilters) const +{ + i.WriteU8 (84); // IE Type = EPS Bearer Level Fraffic Flow Template (Bearer TFT) + i.WriteHtonU16 (1 + packetFilters.size () * serializedSizePacketFilter); + i.WriteU8 (0); // Spare + Instance + i.WriteU8 (0x20 + (packetFilters.size () & 0x0f)); // Create new TFT + Number of packet filters + + for (auto &pf : packetFilters) + { + i.WriteU8 ((pf.direction << 4) & 0x30); + i.WriteU8 (pf.precedence); + i.WriteU8 (serializedSizePacketFilter - 3); // Length of Packet filter contents + + i.WriteU8 (0x10); // IPv4 remote address type + i.WriteHtonU32 (pf.remoteAddress.Get ()); + i.WriteHtonU32 (pf.remoteMask.Get ()); + i.WriteU8 (0x11); // IPv4 local address type + i.WriteHtonU32 (pf.localAddress.Get ()); + i.WriteHtonU32 (pf.localMask.Get ()); + i.WriteU8 (0x41); // Local port range type + i.WriteHtonU16 (pf.localPortStart); + i.WriteHtonU16 (pf.localPortEnd); + i.WriteU8 (0x51); // Remote port range type + i.WriteHtonU16 (pf.remotePortStart); + i.WriteHtonU16 (pf.remotePortEnd); + i.WriteU8 (0x70); // Type of service + i.WriteU8 (pf.typeOfService); + i.WriteU8 (pf.typeOfServiceMask); + } +} + +uint32_t +GtpcIes::DeserializeBearerTft (Buffer::Iterator &i, Ptr epcTft) +{ + uint8_t type = i.ReadU8 (); + NS_ASSERT_MSG (type == 84, "Wrong Bearer TFT IE type = " << (uint16_t) type); + i.ReadNtohU16 (); + i.ReadU8 (); + uint8_t numberOfPacketFilters = i.ReadU8 () & 0x0f; + + for (uint8_t pf = 0; pf < numberOfPacketFilters; ++pf) + { + EpcTft::PacketFilter packetFilter; + packetFilter.direction = EpcTft::Direction ((i.ReadU8 () & 0x30) >> 4); + packetFilter.precedence = i.ReadU8 (); + i.ReadU8 (); // Length of Packet filter contents + i.ReadU8 (); + packetFilter.remoteAddress = Ipv4Address (i.ReadNtohU32 ()); + packetFilter.remoteMask = Ipv4Mask (i.ReadNtohU32 ()); + i.ReadU8 (); + packetFilter.localAddress = Ipv4Address (i.ReadNtohU32 ()); + packetFilter.localMask = Ipv4Mask (i.ReadNtohU32 ()); + i.ReadU8 (); + packetFilter.localPortStart = i.ReadNtohU16 (); + packetFilter.localPortEnd = i.ReadNtohU16 (); + i.ReadU8 (); + packetFilter.remotePortStart = i.ReadNtohU16 (); + packetFilter.remotePortEnd = i.ReadNtohU16 (); + i.ReadU8 (); + packetFilter.typeOfService = i.ReadU8 (); + packetFilter.typeOfServiceMask = i.ReadU8 (); + epcTft->Add (packetFilter); + } + + return GetSerializedSizeBearerTft (epcTft->GetPacketFilters ()); +} + +inline uint32_t +GtpcIes::GetSerializedSizeBearerTft (std::list packetFilters) const +{ + return (5 + packetFilters.size () * serializedSizePacketFilter); +} + +void +GtpcIes::SerializeUliEcgi (Buffer::Iterator &i, uint32_t uliEcgi) const +{ + i.WriteU8 (86); // IE Type = ULI (ECGI) + i.WriteHtonU16 (8); // Length + i.WriteU8 (0); // Spare + Instance + i.WriteU8 (0x10); // ECGI flag + i.WriteU8 (0); // Dummy MCC and MNC + i.WriteU8 (0); // Dummy MCC and MNC + i.WriteU8 (0); // Dummy MCC and MNC + i.WriteHtonU32 (uliEcgi); +} + +uint32_t +GtpcIes::DeserializeUliEcgi (Buffer::Iterator &i, uint32_t &uliEcgi) +{ + uint8_t type = i.ReadU8 (); + NS_ASSERT_MSG (type == 86, "Wrong ULI ECGI IE type = " << (uint16_t) type); + uint16_t length = i.ReadNtohU16 (); + NS_ASSERT_MSG (length == 8, "Wrong ULI ECGI IE length"); + uint8_t instance = i.ReadU8 () & 0x0f; + NS_ASSERT_MSG (instance == 0, "Wrong ULI ECGI IE instance"); + i.Next (4); + uliEcgi = i.ReadNtohU32 () & 0x0fffffff; + + return serializedSizeUliEcgi; +} + +void +GtpcIes::SerializeFteid (Buffer::Iterator &i, GtpcHeader::Fteid_t fteid) const +{ + i.WriteU8 (87); // IE Type = Fully Qualified TEID (F-TEID) + i.WriteHtonU16 (9); // Length + i.WriteU8 (0); // Spare + Instance + i.WriteU8 (0x80 | ((uint8_t) fteid.interfaceType & 0x1f)); // IP version flag + Iface type + i.WriteHtonU32 (fteid.teid); // TEID + i.WriteHtonU32 (fteid.addr.Get ()); // IPv4 address +} + +uint32_t +GtpcIes::DeserializeFteid (Buffer::Iterator &i, GtpcHeader::Fteid_t &fteid) +{ + uint8_t type = i.ReadU8 (); + NS_ASSERT_MSG (type == 87, "Wrong FTEID IE type = " << (uint16_t) type); + uint16_t length = i.ReadNtohU16 (); + NS_ASSERT_MSG (length == 9, "Wrong FTEID IE length"); + uint8_t instance = i.ReadU8 () & 0x0f; + NS_ASSERT_MSG (instance == 0, "Wrong FTEID IE instance"); + uint8_t flags = i.ReadU8 (); // IP version flag + Iface type + fteid.interfaceType = GtpcHeader::InterfaceType_t (flags & 0x1f); + fteid.teid = i.ReadNtohU32 (); // TEID + fteid.addr.Set (i.ReadNtohU32 ()); // IPv4 address + + return serializedSizeFteid; +} + +void +GtpcIes::SerializeBearerContextHeader (Buffer::Iterator &i, uint16_t length) const +{ + i.WriteU8 (93); // IE Type = Bearer Context + i.WriteU16 (length); + i.WriteU8 (0); // Spare + Instance +} + +uint32_t +GtpcIes::DeserializeBearerContextHeader (Buffer::Iterator &i, uint16_t &length) +{ + uint8_t type = i.ReadU8 (); + NS_ASSERT_MSG (type == 93, "Wrong Bearer Context IE type = " << (uint16_t) type); + length = i.ReadNtohU16 (); + uint8_t instance = i.ReadU8 () & 0x0f; + NS_ASSERT_MSG (instance == 0, "Wrong Bearer Context IE instance"); + + return serializedSizeBearerContextHeader; +} + +///////////////////////////////////////////////////////////////////// + +TypeId +GtpcCreateSessionRequestMessage::GetTypeId (void) +{ + static TypeId tid = TypeId ("ns3::GtpcCreateSessionRequestMessage") + .SetParent
() + .SetGroupName ("Lte") + .AddConstructor (); + return tid; +} + +GtpcCreateSessionRequestMessage::GtpcCreateSessionRequestMessage () +{ + SetMessageType (GtpcHeader::CreateSessionRequest); + SetSequenceNumber (0); + m_imsi = 0; + m_uliEcgi = 0; +} + +GtpcCreateSessionRequestMessage::~GtpcCreateSessionRequestMessage () +{ +} + +TypeId +GtpcCreateSessionRequestMessage::GetInstanceTypeId (void) const +{ + return GetTypeId (); +} + +uint32_t +GtpcCreateSessionRequestMessage::GetMessageSize (void) const +{ + uint32_t serializedSize = serializedSizeImsi + serializedSizeUliEcgi + serializedSizeFteid; + for (auto &bc : m_bearerContextsToBeCreated) + { + serializedSize += serializedSizeBearerContextHeader + serializedSizeEbi + + GetSerializedSizeBearerTft (bc.tft->GetPacketFilters ()) + + serializedSizeFteid + serializedSizeBearerQos; + } + + return serializedSize; +} + +uint32_t +GtpcCreateSessionRequestMessage::GetSerializedSize (void) const +{ + return GtpcHeader::GetSerializedSize () + GetMessageSize (); +} + +void +GtpcCreateSessionRequestMessage::Serialize (Buffer::Iterator start) const +{ + Buffer::Iterator i = start; + + GtpcHeader::PreSerialize (i); + SerializeImsi (i, m_imsi); + SerializeUliEcgi (i, m_uliEcgi); + SerializeFteid (i, m_senderCpFteid); + + for (auto &bc : m_bearerContextsToBeCreated) + { + std::list packetFilters = bc.tft->GetPacketFilters (); + + SerializeBearerContextHeader (i, serializedSizeEbi + GetSerializedSizeBearerTft (packetFilters) + serializedSizeFteid + serializedSizeBearerQos); + + SerializeEbi (i, bc.epsBearerId); + SerializeBearerTft (i, packetFilters); + SerializeFteid (i, bc.sgwS5uFteid); + SerializeBearerQos (i, bc.bearerLevelQos); + } +} + +uint32_t +GtpcCreateSessionRequestMessage::Deserialize (Buffer::Iterator start) +{ + Buffer::Iterator i = start; + GtpcHeader::PreDeserialize (i); + + DeserializeImsi (i, m_imsi); + DeserializeUliEcgi (i, m_uliEcgi); + DeserializeFteid (i, m_senderCpFteid); + + m_bearerContextsToBeCreated.clear (); + while (i.GetRemainingSize () > 0) + { + uint16_t length; + DeserializeBearerContextHeader (i, length); + + BearerContextToBeCreated bearerContext; + DeserializeEbi (i, bearerContext.epsBearerId); + + Ptr epcTft = Create (); + DeserializeBearerTft (i, epcTft); + bearerContext.tft = epcTft; + + DeserializeFteid (i, bearerContext.sgwS5uFteid); + DeserializeBearerQos (i, bearerContext.bearerLevelQos); + + m_bearerContextsToBeCreated.push_back (bearerContext); + } + + return GetSerializedSize (); +} + +void +GtpcCreateSessionRequestMessage::Print (std::ostream &os) const +{ + os << " imsi " << m_imsi << " uliEcgi " << m_uliEcgi; +} + +uint64_t +GtpcCreateSessionRequestMessage::GetImsi () const +{ + return m_imsi; +} + +void +GtpcCreateSessionRequestMessage::SetImsi (uint64_t imsi) +{ + m_imsi = imsi; +} + +uint32_t +GtpcCreateSessionRequestMessage::GetUliEcgi () const +{ + return m_uliEcgi; +} + +void +GtpcCreateSessionRequestMessage::SetUliEcgi (uint32_t uliEcgi) +{ + m_uliEcgi = uliEcgi; +} + +GtpcHeader::Fteid_t +GtpcCreateSessionRequestMessage::GetSenderCpFteid () const +{ + return m_senderCpFteid; +} + +void +GtpcCreateSessionRequestMessage::SetSenderCpFteid (GtpcHeader::Fteid_t fteid) +{ + m_senderCpFteid = fteid; +} + +std::list +GtpcCreateSessionRequestMessage::GetBearerContextsToBeCreated () const +{ + return m_bearerContextsToBeCreated; +} + +void +GtpcCreateSessionRequestMessage::SetBearerContextsToBeCreated (std::list bearerContexts) +{ + m_bearerContextsToBeCreated = bearerContexts; +} + +///////////////////////////////////////////////////////////////////// + +TypeId +GtpcCreateSessionResponseMessage::GetTypeId (void) +{ + static TypeId tid = TypeId ("ns3::GtpcCreateSessionResponseMessage") + .SetParent
() + .SetGroupName ("Lte") + .AddConstructor (); + return tid; +} + +GtpcCreateSessionResponseMessage::GtpcCreateSessionResponseMessage () +{ + SetMessageType (GtpcHeader::CreateSessionResponse); + SetSequenceNumber (0); + m_cause = Cause_t::RESERVED; +} + +GtpcCreateSessionResponseMessage::~GtpcCreateSessionResponseMessage () +{ +} + +TypeId +GtpcCreateSessionResponseMessage::GetInstanceTypeId (void) const +{ + return GetTypeId (); +} + +uint32_t +GtpcCreateSessionResponseMessage::GetMessageSize (void) const +{ + uint32_t serializedSize = serializedSizeCause + serializedSizeFteid; + for (auto &bc : m_bearerContextsCreated) + { + serializedSize += serializedSizeBearerContextHeader + serializedSizeEbi + + GetSerializedSizeBearerTft (bc.tft->GetPacketFilters ()) + + serializedSizeFteid + serializedSizeBearerQos; + } + + return serializedSize; +} + +uint32_t +GtpcCreateSessionResponseMessage::GetSerializedSize (void) const +{ + return GtpcHeader::GetSerializedSize () + GetMessageSize (); +} + +void +GtpcCreateSessionResponseMessage::Serialize (Buffer::Iterator start) const +{ + Buffer::Iterator i = start; + + GtpcHeader::PreSerialize (i); + SerializeCause (i, m_cause); + SerializeFteid (i, m_senderCpFteid); + + for (auto &bc : m_bearerContextsCreated) + { + std::list packetFilters = bc.tft->GetPacketFilters (); + + SerializeBearerContextHeader (i, serializedSizeEbi + GetSerializedSizeBearerTft (packetFilters) + serializedSizeFteid + serializedSizeBearerQos); + + SerializeEbi (i, bc.epsBearerId); + SerializeBearerTft (i, packetFilters); + SerializeFteid (i, bc.fteid); + SerializeBearerQos (i, bc.bearerLevelQos); + } +} + +uint32_t +GtpcCreateSessionResponseMessage::Deserialize (Buffer::Iterator start) +{ + Buffer::Iterator i = start; + GtpcHeader::PreDeserialize (i); + + DeserializeCause (i, m_cause); + DeserializeFteid (i, m_senderCpFteid); + + m_bearerContextsCreated.clear (); + while (i.GetRemainingSize () > 0) + { + BearerContextCreated bearerContext; + uint16_t length; + + DeserializeBearerContextHeader (i, length); + DeserializeEbi (i, bearerContext.epsBearerId); + + Ptr epcTft = Create (); + DeserializeBearerTft (i, epcTft); + bearerContext.tft = epcTft; + + DeserializeFteid (i, bearerContext.fteid); + DeserializeBearerQos (i, bearerContext.bearerLevelQos); + + m_bearerContextsCreated.push_back (bearerContext); + } + + return GetSerializedSize (); +} + +void +GtpcCreateSessionResponseMessage::Print (std::ostream &os) const +{ + os << " cause " << m_cause << " FTEID " << m_senderCpFteid.addr << "," << m_senderCpFteid.teid ; +} + +GtpcCreateSessionResponseMessage::Cause_t +GtpcCreateSessionResponseMessage::GetCause () const +{ + return m_cause; +} + +void +GtpcCreateSessionResponseMessage::SetCause (GtpcCreateSessionResponseMessage::Cause_t cause) +{ + m_cause = cause; +} + +GtpcHeader::Fteid_t +GtpcCreateSessionResponseMessage::GetSenderCpFteid () const +{ + return m_senderCpFteid; +} + +void +GtpcCreateSessionResponseMessage::SetSenderCpFteid (GtpcHeader::Fteid_t fteid) +{ + m_senderCpFteid = fteid; +} + +std::list +GtpcCreateSessionResponseMessage::GetBearerContextsCreated () const +{ + return m_bearerContextsCreated; +} + +void +GtpcCreateSessionResponseMessage::SetBearerContextsCreated (std::list bearerContexts) +{ + m_bearerContextsCreated = bearerContexts; +} + +///////////////////////////////////////////////////////////////////// + +TypeId +GtpcModifyBearerRequestMessage::GetTypeId (void) +{ + static TypeId tid = TypeId ("ns3::GtpcModifyBearerRequestMessage") + .SetParent
() + .SetGroupName ("Lte") + .AddConstructor (); + return tid; +} + +GtpcModifyBearerRequestMessage::GtpcModifyBearerRequestMessage () +{ + SetMessageType (GtpcHeader::ModifyBearerRequest); + SetSequenceNumber (0); + m_imsi = 0; + m_uliEcgi = 0; +} + +GtpcModifyBearerRequestMessage::~GtpcModifyBearerRequestMessage () +{ +} + +TypeId +GtpcModifyBearerRequestMessage::GetInstanceTypeId (void) const +{ + return GetTypeId (); +} + +uint32_t +GtpcModifyBearerRequestMessage::GetMessageSize (void) const +{ + uint32_t serializedSize = serializedSizeImsi + serializedSizeUliEcgi + + m_bearerContextsToBeModified.size () + * (serializedSizeBearerContextHeader + + serializedSizeEbi + serializedSizeFteid); + return serializedSize; +} + +uint32_t +GtpcModifyBearerRequestMessage::GetSerializedSize (void) const +{ + return GtpcHeader::GetSerializedSize () + GetMessageSize (); +} + +void +GtpcModifyBearerRequestMessage::Serialize (Buffer::Iterator start) const +{ + Buffer::Iterator i = start; + + GtpcHeader::PreSerialize (i); + SerializeImsi (i, m_imsi); + SerializeUliEcgi (i, m_uliEcgi); + + for (auto &bc : m_bearerContextsToBeModified) + { + SerializeBearerContextHeader (i, serializedSizeEbi + serializedSizeFteid); + + SerializeEbi (i, bc.epsBearerId); + SerializeFteid (i, bc.fteid); + } +} + +uint32_t +GtpcModifyBearerRequestMessage::Deserialize (Buffer::Iterator start) +{ + Buffer::Iterator i = start; + GtpcHeader::PreDeserialize (i); + + DeserializeImsi (i, m_imsi); + DeserializeUliEcgi (i, m_uliEcgi); + + while (i.GetRemainingSize () > 0) + { + BearerContextToBeModified bearerContext; + uint16_t length; + + DeserializeBearerContextHeader (i, length); + + DeserializeEbi (i, bearerContext.epsBearerId); + DeserializeFteid (i, bearerContext.fteid); + + m_bearerContextsToBeModified.push_back (bearerContext); + } + + return GetSerializedSize (); +} + +void +GtpcModifyBearerRequestMessage::Print (std::ostream &os) const +{ + os << " imsi " << m_imsi << " uliEcgi " << m_uliEcgi; +} + +uint8_t +GtpcModifyBearerRequestMessage::GetImsi () const +{ + return m_imsi; +} + +void +GtpcModifyBearerRequestMessage::SetImsi (uint8_t imsi) +{ + m_imsi = imsi; +} + +uint32_t +GtpcModifyBearerRequestMessage::GetUliEcgi () const +{ + return m_uliEcgi; +} + +void +GtpcModifyBearerRequestMessage::SetUliEcgi (uint32_t uliEcgi) +{ + m_uliEcgi = uliEcgi; +} + +std::list +GtpcModifyBearerRequestMessage::GetBearerContextsToBeModified () const +{ + return m_bearerContextsToBeModified; +} + +void +GtpcModifyBearerRequestMessage::SetBearerContextsToBeModified (std::list bearerContexts) +{ + m_bearerContextsToBeModified = bearerContexts; +} + +///////////////////////////////////////////////////////////////////// + +TypeId +GtpcModifyBearerResponseMessage::GetTypeId (void) +{ + static TypeId tid = TypeId ("ns3::GtpcModifyBearerResponseMessage") + .SetParent
() + .SetGroupName ("Lte") + .AddConstructor (); + return tid; +} + +GtpcModifyBearerResponseMessage::GtpcModifyBearerResponseMessage () +{ + SetMessageType (GtpcHeader::ModifyBearerResponse); + SetSequenceNumber (0); + m_cause = Cause_t::RESERVED; +} + +GtpcModifyBearerResponseMessage::~GtpcModifyBearerResponseMessage () +{ +} + +TypeId +GtpcModifyBearerResponseMessage::GetInstanceTypeId (void) const +{ + return GetTypeId (); +} + +uint32_t +GtpcModifyBearerResponseMessage::GetMessageSize (void) const +{ + return serializedSizeCause; +} + +uint32_t +GtpcModifyBearerResponseMessage::GetSerializedSize (void) const +{ + return GtpcHeader::GetSerializedSize () + GetMessageSize (); +} + +void +GtpcModifyBearerResponseMessage::Serialize (Buffer::Iterator start) const +{ + Buffer::Iterator i = start; + + GtpcHeader::PreSerialize (i); + SerializeCause (i, m_cause); +} + +uint32_t +GtpcModifyBearerResponseMessage::Deserialize (Buffer::Iterator start) +{ + Buffer::Iterator i = start; + GtpcHeader::PreDeserialize (i); + + DeserializeCause (i, m_cause); + + return GetSerializedSize (); +} + +void +GtpcModifyBearerResponseMessage::Print (std::ostream &os) const +{ + os << " cause " << (uint16_t)m_cause; +} + +GtpcModifyBearerResponseMessage::Cause_t +GtpcModifyBearerResponseMessage::GetCause () const +{ + return m_cause; +} + +void +GtpcModifyBearerResponseMessage::SetCause (GtpcModifyBearerResponseMessage::Cause_t cause) +{ + m_cause = cause; +} + +///////////////////////////////////////////////////////////////////// + +TypeId +GtpcDeleteBearerCommandMessage::GetTypeId (void) +{ + static TypeId tid = TypeId ("ns3::GtpcDeleteBearerCommandMessage") + .SetParent
() + .SetGroupName ("Lte") + .AddConstructor (); + return tid; +} + +GtpcDeleteBearerCommandMessage::GtpcDeleteBearerCommandMessage () +{ + SetMessageType (GtpcHeader::DeleteBearerCommand); + SetSequenceNumber (0); +} + +GtpcDeleteBearerCommandMessage::~GtpcDeleteBearerCommandMessage () +{ +} + +TypeId +GtpcDeleteBearerCommandMessage::GetInstanceTypeId (void) const +{ + return GetTypeId (); +} + +uint32_t +GtpcDeleteBearerCommandMessage::GetMessageSize (void) const +{ + uint32_t serializedSize = m_bearerContexts.size () + * (serializedSizeBearerContextHeader + serializedSizeEbi); + return serializedSize; +} + +uint32_t +GtpcDeleteBearerCommandMessage::GetSerializedSize (void) const +{ + return GtpcHeader::GetSerializedSize () + GetMessageSize (); +} + +void +GtpcDeleteBearerCommandMessage::Serialize (Buffer::Iterator start) const +{ + Buffer::Iterator i = start; + + GtpcHeader::PreSerialize (i); + for (auto &bearerContext : m_bearerContexts) + { + SerializeBearerContextHeader (i, serializedSizeEbi); + + SerializeEbi (i, bearerContext.m_epsBearerId); + } +} + +uint32_t +GtpcDeleteBearerCommandMessage::Deserialize (Buffer::Iterator start) +{ + Buffer::Iterator i = start; + GtpcHeader::PreDeserialize (i); + + while (i.GetRemainingSize () > 0) + { + uint16_t length; + DeserializeBearerContextHeader (i, length); + + BearerContext bearerContext; + DeserializeEbi (i, bearerContext.m_epsBearerId); + m_bearerContexts.push_back (bearerContext); + } + + return GetSerializedSize (); +} + +void +GtpcDeleteBearerCommandMessage::Print (std::ostream &os) const +{ + os << " bearerContexts ["; + for (auto &bearerContext : m_bearerContexts) + { + os << (uint16_t)bearerContext.m_epsBearerId << " "; + } + os << "]"; +} + +std::list +GtpcDeleteBearerCommandMessage::GetBearerContexts () const +{ + return m_bearerContexts; +} + +void +GtpcDeleteBearerCommandMessage::SetBearerContexts (std::list bearerContexts) +{ + m_bearerContexts = bearerContexts; +} + +///////////////////////////////////////////////////////////////////// + +TypeId +GtpcDeleteBearerRequestMessage::GetTypeId (void) +{ + static TypeId tid = TypeId ("ns3::GtpcDeleteBearerRequestMessage") + .SetParent
() + .SetGroupName ("Lte") + .AddConstructor (); + return tid; +} + +GtpcDeleteBearerRequestMessage::GtpcDeleteBearerRequestMessage () +{ + SetMessageType (GtpcHeader::DeleteBearerRequest); + SetSequenceNumber (0); +} + +GtpcDeleteBearerRequestMessage::~GtpcDeleteBearerRequestMessage () +{ +} + +TypeId +GtpcDeleteBearerRequestMessage::GetInstanceTypeId (void) const +{ + return GetTypeId (); +} + +uint32_t +GtpcDeleteBearerRequestMessage::GetMessageSize (void) const +{ + uint32_t serializedSize = m_epsBearerIds.size () * serializedSizeEbi; + return serializedSize; +} + +uint32_t +GtpcDeleteBearerRequestMessage::GetSerializedSize (void) const +{ + return GtpcHeader::GetSerializedSize () + GetMessageSize (); +} + +void +GtpcDeleteBearerRequestMessage::Serialize (Buffer::Iterator start) const +{ + Buffer::Iterator i = start; + + GtpcHeader::PreSerialize (i); + for (auto &epsBearerId : m_epsBearerIds) + { + SerializeEbi (i, epsBearerId); + } +} + +uint32_t +GtpcDeleteBearerRequestMessage::Deserialize (Buffer::Iterator start) +{ + Buffer::Iterator i = start; + GtpcHeader::PreDeserialize (i); + + while (i.GetRemainingSize () > 0) + { + uint8_t epsBearerId; + DeserializeEbi (i, epsBearerId); + m_epsBearerIds.push_back (epsBearerId); + } + + return GetSerializedSize (); +} + +void +GtpcDeleteBearerRequestMessage::Print (std::ostream &os) const +{ + os << " epsBearerIds ["; + for (auto &epsBearerId : m_epsBearerIds) + { + os << (uint16_t)epsBearerId << " "; + } + os << "]"; +} + +std::list +GtpcDeleteBearerRequestMessage::GetEpsBearerIds () const +{ + return m_epsBearerIds; +} + +void +GtpcDeleteBearerRequestMessage::SetEpsBearerIds (std::list epsBearerId) +{ + m_epsBearerIds = epsBearerId; +} + +///////////////////////////////////////////////////////////////////// + +TypeId +GtpcDeleteBearerResponseMessage::GetTypeId (void) +{ + static TypeId tid = TypeId ("ns3::GtpcDeleteBearerResponseMessage") + .SetParent
() + .SetGroupName ("Lte") + .AddConstructor (); + return tid; +} + +GtpcDeleteBearerResponseMessage::GtpcDeleteBearerResponseMessage () +{ + SetMessageType (GtpcHeader::DeleteBearerResponse); + SetSequenceNumber (0); +} + +GtpcDeleteBearerResponseMessage::~GtpcDeleteBearerResponseMessage () +{ +} + +TypeId +GtpcDeleteBearerResponseMessage::GetInstanceTypeId (void) const +{ + return GetTypeId (); +} + +uint32_t +GtpcDeleteBearerResponseMessage::GetMessageSize (void) const +{ + uint32_t serializedSize = serializedSizeCause + m_epsBearerIds.size () * serializedSizeEbi; + return serializedSize; +} + +uint32_t +GtpcDeleteBearerResponseMessage::GetSerializedSize (void) const +{ + return GtpcHeader::GetSerializedSize () + GetMessageSize (); +} + +void +GtpcDeleteBearerResponseMessage::Serialize (Buffer::Iterator start) const +{ + Buffer::Iterator i = start; + + GtpcHeader::PreSerialize (i); + SerializeCause (i, m_cause); + + for (auto &epsBearerId : m_epsBearerIds) + { + SerializeEbi (i, epsBearerId); + } +} + +uint32_t +GtpcDeleteBearerResponseMessage::Deserialize (Buffer::Iterator start) +{ + Buffer::Iterator i = start; + GtpcHeader::PreDeserialize (i); + + DeserializeCause (i, m_cause); + + while (i.GetRemainingSize () > 0) + { + uint8_t epsBearerId; + DeserializeEbi (i, epsBearerId); + m_epsBearerIds.push_back (epsBearerId); + } + + return GetSerializedSize (); +} + +void +GtpcDeleteBearerResponseMessage::Print (std::ostream &os) const +{ + os << " cause " << (uint16_t)m_cause << " epsBearerIds ["; + for (auto &epsBearerId : m_epsBearerIds) + { + os << (uint16_t)epsBearerId << " "; + } + os << "]"; +} + +GtpcDeleteBearerResponseMessage::Cause_t +GtpcDeleteBearerResponseMessage::GetCause () const +{ + return m_cause; +} + +void +GtpcDeleteBearerResponseMessage::SetCause (GtpcDeleteBearerResponseMessage::Cause_t cause) +{ + m_cause = cause; +} + +std::list +GtpcDeleteBearerResponseMessage::GetEpsBearerIds () const +{ + return m_epsBearerIds; +} + +void +GtpcDeleteBearerResponseMessage::SetEpsBearerIds (std::list epsBearerId) +{ + m_epsBearerIds = epsBearerId; +} + +} // namespace ns3 diff --git a/src/lte/model/epc-gtpc-header.h b/src/lte/model/epc-gtpc-header.h new file mode 100644 index 000000000..e1cacf5cb --- /dev/null +++ b/src/lte/model/epc-gtpc-header.h @@ -0,0 +1,475 @@ +/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2018 Centre Tecnologic de Telecomunicacions de Catalunya (CTTC) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Manuel Requena + */ + +#ifndef EPC_GTPC_HEADER_H +#define EPC_GTPC_HEADER_H + +#include "ns3/header.h" +#include "ns3/epc-tft.h" +#include "ns3/eps-bearer.h" + +namespace ns3 { + +/** + * \ingroup lte + * + * \brief Header of the GTPv2-C protocol + * + * Implementation of the GPRS Tunnelling Protocol for Control Plane (GTPv2-C) header + * according to the 3GPP TS 29.274 document + */ +class GtpcHeader : public Header +{ +public: + GtpcHeader (); + virtual ~GtpcHeader (); + /** + * \brief Get the type ID. + * \return the object TypeId + */ + static TypeId GetTypeId (void); + virtual TypeId GetInstanceTypeId (void) const; + virtual uint32_t GetSerializedSize (void) const; + virtual void Serialize (Buffer::Iterator start) const; + virtual uint32_t Deserialize (Buffer::Iterator start); + virtual void Print (std::ostream &os) const; + + virtual uint32_t GetMessageSize (void) const; + + /** + * Get message type + * \returns the message type + */ + uint8_t GetMessageType () const; + /** + * Get message length + * \returns the message length + */ + uint16_t GetMessageLength () const; + /** + * Get TEID + * \returns the TEID + */ + uint32_t GetTeid () const; + /** + * Get sequence number + * \returns the sequence number + */ + uint32_t GetSequenceNumber () const; + + /** + * Set message type + * \param messageType the message type + */ + void SetMessageType (uint8_t messageType); + /** + * Set message length + * \param messageLength the message length + */ + void SetMessageLength (uint16_t messageLength); + /** + * Set TEID + * \param teid the TEID + */ + void SetTeid (uint32_t teid); + /** + * Set sequence number + * \param sequenceNumber the sequence number + */ + void SetSequenceNumber (uint32_t sequenceNumber); + /** + * Set IEs length. It is used to compute the message length + * \param iesLength the IEs length + */ + void SetIesLength (uint16_t iesLength); + + void ComputeMessageLength (void); + + /// Interface Type enumeration + enum InterfaceType_t + { + S1U_ENB_GTPU = 0, + S5_SGW_GTPU = 4, + S5_PGW_GTPU = 5, + S5_SGW_GTPC = 6, + S5_PGW_GTPC = 7, + S11_MME_GTPC = 10, + }; + + /// FTEID structure + struct Fteid_t + { + InterfaceType_t interfaceType; + Ipv4Address addr; + uint32_t teid; + }; + + /// Message Type enumeration + enum MessageType_t + { + Reserved = 0, + CreateSessionRequest = 32, + CreateSessionResponse = 33, + ModifyBearerRequest = 34, + ModifyBearerResponse = 35, + DeleteSessionRequest = 36, + DeleteSessionResponse = 37, + DeleteBearerCommand = 66, + DeleteBearerRequest = 99, + DeleteBearerResponse = 100, + }; + + +private: + /** + * Version of the GTPv2-C protocol. + * The version number shall be set to 2. + */ + uint8_t m_version; + /** + * TEID flag. + * This flag indicates if TEID field is present or not + */ + bool m_teidFlag; + /** + * Message type field. + * It can be one of the values of MessageType_t + */ + uint8_t m_messageType; + /** + * Message length field. + * This field indicates the length of the message in octets excluding + * the mandatory part of the GTP-C header (the first 4 octets) + */ + uint16_t m_messageLength; + /** + * Tunnel Endpoint Identifier (TEID) field + */ + uint32_t m_teid; + /** + * GTP Sequence number field + */ + uint32_t m_sequenceNumber; + + +protected: + /** + * Serialize the GTP-C header in the GTP-C messages + * \param i the buffer iterator + */ + void PreSerialize (Buffer::Iterator &i) const; + /** + * Deserialize the GTP-C header in the GTP-C messages + * \param i the buffer iterator + */ + uint32_t PreDeserialize (Buffer::Iterator &i); + +}; + + +class GtpcIes +{ +public: + enum Cause_t + { + RESERVED = 0, + REQUEST_ACCEPTED = 16, + }; + + const uint32_t serializedSizeImsi = 12; + const uint32_t serializedSizeCause = 6; + const uint32_t serializedSizeEbi = 5; + const uint32_t serializedSizeBearerQos = 26; + const uint32_t serializedSizePacketFilter = 3 + 9 + 9 + 5 + 5 + 3; + uint32_t GetSerializedSizeBearerTft (std::list packetFilters) const; + const uint32_t serializedSizeUliEcgi = 12; + const uint32_t serializedSizeFteid = 13; + const uint32_t serializedSizeBearerContextHeader = 4; + + + void SerializeImsi (Buffer::Iterator &i, uint64_t imsi) const; + uint32_t DeserializeImsi (Buffer::Iterator &i, uint64_t &imsi); + + void SerializeCause (Buffer::Iterator &i, Cause_t cause) const; + uint32_t DeserializeCause (Buffer::Iterator &i, Cause_t &cause); + + void SerializeEbi (Buffer::Iterator &i, uint8_t epsBearerId) const; + uint32_t DeserializeEbi (Buffer::Iterator &i, uint8_t &epsBearerId); + + void WriteHtonU40 (Buffer::Iterator &i, uint64_t data) const; + uint64_t ReadNtohU40 (Buffer::Iterator &i); + + void SerializeBearerQos (Buffer::Iterator &i, EpsBearer bearerQos) const; + uint32_t DeserializeBearerQos (Buffer::Iterator &i, EpsBearer &bearerQos); + + void SerializeBearerTft (Buffer::Iterator &i, std::list packetFilters) const; + uint32_t DeserializeBearerTft (Buffer::Iterator &i, Ptr epcTft); + + void SerializeUliEcgi (Buffer::Iterator &i, uint32_t uliEcgi) const; + uint32_t DeserializeUliEcgi (Buffer::Iterator &i, uint32_t &uliEcgi); + + void SerializeFteid (Buffer::Iterator &i, GtpcHeader::Fteid_t fteid) const; + uint32_t DeserializeFteid (Buffer::Iterator &i, GtpcHeader::Fteid_t &fteid); + + void SerializeBearerContextHeader (Buffer::Iterator &i, uint16_t length) const; + uint32_t DeserializeBearerContextHeader (Buffer::Iterator &i, uint16_t &length); +}; + + +class GtpcCreateSessionRequestMessage : public GtpcHeader, public GtpcIes +{ +public: + GtpcCreateSessionRequestMessage (); + virtual ~GtpcCreateSessionRequestMessage (); + /** + * \brief Get the type ID. + * \return the object TypeId + */ + static TypeId GetTypeId (void); + virtual TypeId GetInstanceTypeId (void) const; + virtual uint32_t GetSerializedSize (void) const; + virtual void Serialize (Buffer::Iterator start) const; + virtual uint32_t Deserialize (Buffer::Iterator start); + virtual void Print (std::ostream &os) const; + virtual uint32_t GetMessageSize (void) const; + + uint64_t GetImsi () const; + void SetImsi (uint64_t imsi); + + uint32_t GetUliEcgi () const; + void SetUliEcgi (uint32_t uliEcgi); + + GtpcHeader::Fteid_t GetSenderCpFteid () const; + void SetSenderCpFteid (GtpcHeader::Fteid_t fteid); + + struct BearerContextToBeCreated + { + GtpcHeader::Fteid_t sgwS5uFteid; ///< FTEID + uint8_t epsBearerId; ///< EPS bearer ID + Ptr tft; ///< traffic flow template + EpsBearer bearerLevelQos; ///< bearer QOS level + }; + + std::list GetBearerContextsToBeCreated () const; + void SetBearerContextsToBeCreated (std::list bearerContexts); + +private: + uint64_t m_imsi; + uint32_t m_uliEcgi; + GtpcHeader::Fteid_t m_senderCpFteid; + + std::list m_bearerContextsToBeCreated; +}; + + +class GtpcCreateSessionResponseMessage : public GtpcHeader, public GtpcIes +{ +public: + GtpcCreateSessionResponseMessage (); + virtual ~GtpcCreateSessionResponseMessage (); + /** + * \brief Get the type ID. + * \return the object TypeId + */ + static TypeId GetTypeId (void); + virtual TypeId GetInstanceTypeId (void) const; + virtual uint32_t GetSerializedSize (void) const; + virtual void Serialize (Buffer::Iterator start) const; + virtual uint32_t Deserialize (Buffer::Iterator start); + virtual void Print (std::ostream &os) const; + virtual uint32_t GetMessageSize (void) const; + + Cause_t GetCause () const; + void SetCause (Cause_t cause); + + GtpcHeader::Fteid_t GetSenderCpFteid () const; + void SetSenderCpFteid (GtpcHeader::Fteid_t fteid); + + struct BearerContextCreated + { + uint8_t epsBearerId; ///< EPS bearer ID + uint8_t cause; ///< Cause + Ptr tft; ///< Bearer traffic flow template + GtpcHeader::Fteid_t fteid; ///< FTEID + EpsBearer bearerLevelQos; ///< Bearer QOS level + }; + + std::list GetBearerContextsCreated () const; + void SetBearerContextsCreated (std::list bearerContexts); + +private: + Cause_t m_cause; + GtpcHeader::Fteid_t m_senderCpFteid; + + std::list m_bearerContextsCreated; +}; + + +class GtpcModifyBearerRequestMessage : public GtpcHeader, public GtpcIes +{ +public: + GtpcModifyBearerRequestMessage (); + virtual ~GtpcModifyBearerRequestMessage (); + /** + * \brief Get the type ID. + * \return the object TypeId + */ + static TypeId GetTypeId (void); + virtual TypeId GetInstanceTypeId (void) const; + virtual uint32_t GetSerializedSize (void) const; + virtual void Serialize (Buffer::Iterator start) const; + virtual uint32_t Deserialize (Buffer::Iterator start); + virtual void Print (std::ostream &os) const; + virtual uint32_t GetMessageSize (void) const; + + uint8_t GetImsi () const; + void SetImsi (uint8_t imsi); + + uint32_t GetUliEcgi () const; + void SetUliEcgi (uint32_t uliEcgi); + + struct BearerContextToBeModified + { + uint8_t epsBearerId; ///< EPS bearer ID + GtpcHeader::Fteid_t fteid; ///< FTEID + }; + + std::list GetBearerContextsToBeModified () const; + void SetBearerContextsToBeModified (std::list bearerContexts); + +private: + uint64_t m_imsi; + uint32_t m_uliEcgi; + + std::list m_bearerContextsToBeModified; +}; + + +class GtpcModifyBearerResponseMessage : public GtpcHeader, public GtpcIes +{ +public: + GtpcModifyBearerResponseMessage (); + virtual ~GtpcModifyBearerResponseMessage (); + /** + * \brief Get the type ID. + * \return the object TypeId + */ + static TypeId GetTypeId (void); + virtual TypeId GetInstanceTypeId (void) const; + virtual uint32_t GetSerializedSize (void) const; + virtual void Serialize (Buffer::Iterator start) const; + virtual uint32_t Deserialize (Buffer::Iterator start); + virtual void Print (std::ostream &os) const; + virtual uint32_t GetMessageSize (void) const; + + Cause_t GetCause () const; + void SetCause (Cause_t cause); + +private: + Cause_t m_cause; +}; + + +class GtpcDeleteBearerCommandMessage : public GtpcHeader, public GtpcIes +{ +public: + GtpcDeleteBearerCommandMessage (); + virtual ~GtpcDeleteBearerCommandMessage (); + /** + * \brief Get the type ID. + * \return the object TypeId + */ + static TypeId GetTypeId (void); + virtual TypeId GetInstanceTypeId (void) const; + virtual uint32_t GetSerializedSize (void) const; + virtual void Serialize (Buffer::Iterator start) const; + virtual uint32_t Deserialize (Buffer::Iterator start); + virtual void Print (std::ostream &os) const; + virtual uint32_t GetMessageSize (void) const; + + struct BearerContext + { + uint8_t m_epsBearerId; ///< EPS bearer ID + }; + + std::list GetBearerContexts () const; + void SetBearerContexts (std::list bearerContexts); + +private: + std::list m_bearerContexts; +}; + + +class GtpcDeleteBearerRequestMessage : public GtpcHeader, public GtpcIes +{ +public: + GtpcDeleteBearerRequestMessage (); + virtual ~GtpcDeleteBearerRequestMessage (); + /** + * \brief Get the type ID. + * \return the object TypeId + */ + static TypeId GetTypeId (void); + virtual TypeId GetInstanceTypeId (void) const; + virtual uint32_t GetSerializedSize (void) const; + virtual void Serialize (Buffer::Iterator start) const; + virtual uint32_t Deserialize (Buffer::Iterator start); + virtual void Print (std::ostream &os) const; + virtual uint32_t GetMessageSize (void) const; + + std::list GetEpsBearerIds () const; + void SetEpsBearerIds (std::list epsBearerIds); + +private: + std::list m_epsBearerIds; +}; + + +class GtpcDeleteBearerResponseMessage : public GtpcHeader, public GtpcIes +{ +public: + GtpcDeleteBearerResponseMessage (); + virtual ~GtpcDeleteBearerResponseMessage (); + /** + * \brief Get the type ID. + * \return the object TypeId + */ + static TypeId GetTypeId (void); + virtual TypeId GetInstanceTypeId (void) const; + virtual uint32_t GetSerializedSize (void) const; + virtual void Serialize (Buffer::Iterator start) const; + virtual uint32_t Deserialize (Buffer::Iterator start); + virtual void Print (std::ostream &os) const; + virtual uint32_t GetMessageSize (void) const; + + Cause_t GetCause () const; + void SetCause (Cause_t cause); + + std::list GetEpsBearerIds () const; + void SetEpsBearerIds (std::list epsBearerIds); + +private: + Cause_t m_cause; + std::list m_epsBearerIds; +}; + +} // namespace ns3 + +#endif // EPC_GTPC_HEADER_H diff --git a/src/lte/model/epc-mme-application.cc b/src/lte/model/epc-mme-application.cc new file mode 100644 index 000000000..d205d3e62 --- /dev/null +++ b/src/lte/model/epc-mme-application.cc @@ -0,0 +1,367 @@ +/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2017-2018 Centre Tecnologic de Telecomunicacions de Catalunya (CTTC) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Manuel Requena + */ + +#include "ns3/log.h" +#include "ns3/epc-gtpc-header.h" +#include "ns3/epc-mme-application.h" + +namespace ns3 { + +NS_LOG_COMPONENT_DEFINE ("EpcMmeApplication"); + +NS_OBJECT_ENSURE_REGISTERED (EpcMmeApplication); + +EpcMmeApplication::EpcMmeApplication () + : m_gtpcUdpPort (2123) //fixed by the standard +{ + NS_LOG_FUNCTION (this); + m_s1apSapMme = new MemberEpcS1apSapMme (this); +} +EpcMmeApplication::~EpcMmeApplication () +{ + NS_LOG_FUNCTION (this); +} + +void +EpcMmeApplication::DoDispose () +{ + NS_LOG_FUNCTION (this); + delete m_s1apSapMme; +} + +TypeId +EpcMmeApplication::GetTypeId (void) +{ + static TypeId tid = TypeId ("ns3::EpcMmeApplication") + .SetParent () + .SetGroupName ("Lte") + .AddConstructor (); + return tid; +} + +EpcS1apSapMme* +EpcMmeApplication::GetS1apSapMme () +{ + return m_s1apSapMme; +} + +void +EpcMmeApplication::AddSgw (Ipv4Address sgwS11Addr, Ipv4Address mmeS11Addr, Ptr mmeS11Socket) +{ + NS_LOG_FUNCTION (this << sgwS11Addr << mmeS11Addr << mmeS11Socket); + m_sgwS11Addr = sgwS11Addr; + m_mmeS11Addr = mmeS11Addr; + m_s11Socket = mmeS11Socket; + m_s11Socket->SetRecvCallback (MakeCallback (&EpcMmeApplication::RecvFromS11Socket, this)); +} + +void +EpcMmeApplication::AddEnb (uint16_t gci, Ipv4Address enbS1uAddr, EpcS1apSapEnb* enbS1apSap) +{ + NS_LOG_FUNCTION (this << gci << enbS1uAddr << enbS1apSap); + Ptr enbInfo = Create (); + enbInfo->gci = gci; + enbInfo->s1uAddr = enbS1uAddr; + enbInfo->s1apSapEnb = enbS1apSap; + m_enbInfoMap[gci] = enbInfo; +} + +void +EpcMmeApplication::AddUe (uint64_t imsi) +{ + NS_LOG_FUNCTION (this << imsi); + Ptr ueInfo = Create (); + ueInfo->imsi = imsi; + ueInfo->mmeUeS1Id = imsi; + ueInfo->bearerCounter = 0; + m_ueInfoMap[imsi] = ueInfo; +} + +uint8_t +EpcMmeApplication::AddBearer (uint64_t imsi, Ptr tft, EpsBearer bearer) +{ + NS_LOG_FUNCTION (this << imsi); + std::map >::iterator it = m_ueInfoMap.find (imsi); + NS_ASSERT_MSG (it != m_ueInfoMap.end (), "could not find any UE with IMSI " << imsi); + NS_ASSERT_MSG (it->second->bearerCounter < 11, "too many bearers already! " << it->second->bearerCounter); + BearerInfo bearerInfo; + bearerInfo.bearerId = ++(it->second->bearerCounter); + bearerInfo.tft = tft; + bearerInfo.bearer = bearer; + it->second->bearersToBeActivated.push_back (bearerInfo); + return bearerInfo.bearerId; +} + + +// S1-AP SAP MME forwarded methods + +void +EpcMmeApplication::DoInitialUeMessage (uint64_t mmeUeS1Id, uint16_t enbUeS1Id, uint64_t imsi, uint16_t gci) +{ + NS_LOG_FUNCTION (this << mmeUeS1Id << enbUeS1Id << imsi << gci); + std::map >::iterator it = m_ueInfoMap.find (imsi); + NS_ASSERT_MSG (it != m_ueInfoMap.end (), "could not find any UE with IMSI " << imsi); + it->second->cellId = gci; + + GtpcCreateSessionRequestMessage msg; + msg.SetImsi (imsi); + msg.SetUliEcgi (gci); + + GtpcHeader::Fteid_t mmeS11Fteid; + mmeS11Fteid.interfaceType = GtpcHeader::S11_MME_GTPC; + mmeS11Fteid.teid = imsi; + mmeS11Fteid.addr = m_mmeS11Addr; + msg.SetSenderCpFteid (mmeS11Fteid); // S11 MME GTP-C F-TEID + + std::list bearerContexts; + for (std::list::iterator bit = it->second->bearersToBeActivated.begin (); + bit != it->second->bearersToBeActivated.end (); + ++bit) + { + GtpcCreateSessionRequestMessage::BearerContextToBeCreated bearerContext; + bearerContext.epsBearerId = bit->bearerId; + bearerContext.tft = bit->tft; + bearerContext.bearerLevelQos = bit->bearer; + bearerContexts.push_back (bearerContext); + } + NS_LOG_DEBUG ("BearerContextToBeCreated size = " << bearerContexts.size ()); + msg.SetBearerContextsToBeCreated (bearerContexts); + + msg.SetTeid (0); + msg.ComputeMessageLength (); + + Ptr packet = Create (); + packet->AddHeader (msg); + NS_LOG_DEBUG ("Send CreateSessionRequest to SGW " << m_sgwS11Addr); + m_s11Socket->SendTo (packet, 0, InetSocketAddress (m_sgwS11Addr, m_gtpcUdpPort)); +} + +void +EpcMmeApplication::DoInitialContextSetupResponse (uint64_t mmeUeS1Id, uint16_t enbUeS1Id, std::list erabSetupList) +{ + NS_LOG_FUNCTION (this << mmeUeS1Id << enbUeS1Id); + NS_FATAL_ERROR ("unimplemented"); +} + +void +EpcMmeApplication::DoPathSwitchRequest (uint64_t enbUeS1Id, uint64_t mmeUeS1Id, uint16_t gci, std::list erabToBeSwitchedInDownlinkList) +{ + NS_LOG_FUNCTION (this << mmeUeS1Id << enbUeS1Id << gci); + uint64_t imsi = mmeUeS1Id; + std::map >::iterator it = m_ueInfoMap.find (imsi); + NS_ASSERT_MSG (it != m_ueInfoMap.end (), "could not find any UE with IMSI " << imsi); + NS_LOG_INFO ("IMSI " << imsi << " old eNB: " << it->second->cellId << ", new eNB: " << gci); + it->second->cellId = gci; + it->second->enbUeS1Id = enbUeS1Id; + + GtpcModifyBearerRequestMessage msg; + msg.SetImsi (imsi); + msg.SetUliEcgi (gci); + + std::list bearerContexts; + for (auto &erab : erabToBeSwitchedInDownlinkList) + { + NS_LOG_DEBUG ("erabId " << erab.erabId << + " eNB " << erab.enbTransportLayerAddress << + " TEID " << erab.enbTeid); + + GtpcModifyBearerRequestMessage::BearerContextToBeModified bearerContext; + bearerContext.epsBearerId = erab.erabId; + bearerContext.fteid.interfaceType = GtpcHeader::S1U_ENB_GTPU; + bearerContext.fteid.addr = erab.enbTransportLayerAddress; + bearerContext.fteid.teid = erab.enbTeid; + bearerContexts.push_back (bearerContext); + } + msg.SetBearerContextsToBeModified (bearerContexts); + msg.SetTeid (imsi); + msg.ComputeMessageLength (); + + Ptr packet = Create (); + packet->AddHeader (msg); + NS_LOG_DEBUG ("Send ModifyBearerRequest to SGW " << m_sgwS11Addr); + m_s11Socket->SendTo (packet, 0, InetSocketAddress (m_sgwS11Addr, m_gtpcUdpPort)); +} + +void +EpcMmeApplication::DoErabReleaseIndication (uint64_t mmeUeS1Id, uint16_t enbUeS1Id, std::list erabToBeReleaseIndication) +{ + NS_LOG_FUNCTION (this << mmeUeS1Id << enbUeS1Id); + uint64_t imsi = mmeUeS1Id; + std::map >::iterator it = m_ueInfoMap.find (imsi); + NS_ASSERT_MSG (it != m_ueInfoMap.end (), "could not find any UE with IMSI " << imsi); + + GtpcDeleteBearerCommandMessage msg; + std::list bearerContexts; + for (auto &erab : erabToBeReleaseIndication) + { + NS_LOG_DEBUG ("erabId " << (uint16_t)erab.erabId); + GtpcDeleteBearerCommandMessage::BearerContext bearerContext; + bearerContext.m_epsBearerId = erab.erabId; + bearerContexts.push_back (bearerContext); + } + msg.SetBearerContexts (bearerContexts); + msg.SetTeid (imsi); + msg.ComputeMessageLength (); + + Ptr packet = Create (); + packet->AddHeader (msg); + NS_LOG_DEBUG ("Send DeleteBearerCommand to SGW " << m_sgwS11Addr); + m_s11Socket->SendTo (packet, 0, InetSocketAddress (m_sgwS11Addr, m_gtpcUdpPort)); +} + +void +EpcMmeApplication::RemoveBearer (Ptr ueInfo, uint8_t epsBearerId) +{ + NS_LOG_FUNCTION (this << epsBearerId); + std::list::iterator bit = ueInfo->bearersToBeActivated.begin (); + while (bit != ueInfo->bearersToBeActivated.end ()) + { + if (bit->bearerId == epsBearerId) + { + ueInfo->bearersToBeActivated.erase (bit); + break; + } + ++bit; + } +} + + +void +EpcMmeApplication::RecvFromS11Socket (Ptr socket) +{ + NS_LOG_FUNCTION (this << socket); + NS_ASSERT (socket == m_s11Socket); + Ptr packet = socket->Recv (); + GtpcHeader header; + packet->PeekHeader (header); + uint16_t msgType = header.GetMessageType (); + + switch (msgType) + { + case GtpcHeader::CreateSessionResponse: + DoRecvCreateSessionResponse (header, packet); + break; + + case GtpcHeader::ModifyBearerResponse: + DoRecvModifyBearerResponse (header, packet); + break; + + case GtpcHeader::DeleteBearerRequest: + DoRecvDeleteBearerRequest (header, packet); + break; + + default: + NS_FATAL_ERROR ("GTP-C message not supported"); + break; + } +} + +void +EpcMmeApplication::DoRecvCreateSessionResponse (GtpcHeader &header, Ptr packet) +{ + NS_LOG_FUNCTION (this << header); + uint64_t imsi = header.GetTeid (); + NS_LOG_DEBUG ("TEID/IMSI " << imsi); + std::map >::iterator it = m_ueInfoMap.find (imsi); + NS_ASSERT_MSG (it != m_ueInfoMap.end (), "could not find any UE with IMSI " << imsi); + uint16_t cellId = it->second->cellId; + uint16_t enbUeS1Id = it->second->enbUeS1Id; + uint64_t mmeUeS1Id = it->second->mmeUeS1Id; + NS_LOG_DEBUG ("cellId " << cellId << " mmeUeS1Id " << mmeUeS1Id << " enbUeS1Id " << enbUeS1Id); + std::map >::iterator jt = m_enbInfoMap.find (cellId); + NS_ASSERT_MSG (jt != m_enbInfoMap.end (), "could not find any eNB with CellId " << cellId); + + GtpcCreateSessionResponseMessage msg; + packet->RemoveHeader (msg); + + std::list erabToBeSetupList; + std::list bearerContexts = + msg.GetBearerContextsCreated (); + NS_LOG_DEBUG ("BearerContextsCreated size = " << bearerContexts.size ()); + for (auto &bearerContext : bearerContexts) + { + EpcS1apSapEnb::ErabToBeSetupItem erab; + erab.erabId = bearerContext.epsBearerId; + erab.erabLevelQosParameters = bearerContext.bearerLevelQos; + erab.transportLayerAddress = bearerContext.fteid.addr; // SGW S1U address + erab.sgwTeid = bearerContext.fteid.teid; + NS_LOG_DEBUG ("SGW " << erab.transportLayerAddress << " TEID " << erab.sgwTeid); + erabToBeSetupList.push_back (erab); + } + + NS_LOG_DEBUG ("Send InitialContextSetupRequest to eNB " << jt->second->s1apSapEnb); + jt->second->s1apSapEnb->InitialContextSetupRequest (mmeUeS1Id, enbUeS1Id, erabToBeSetupList); +} + +void +EpcMmeApplication::DoRecvModifyBearerResponse (GtpcHeader &header, Ptr packet) +{ + NS_LOG_FUNCTION (this << header); + GtpcModifyBearerResponseMessage msg; + packet->RemoveHeader (msg); + NS_ASSERT (msg.GetCause () == GtpcModifyBearerResponseMessage::REQUEST_ACCEPTED); + + uint64_t imsi = header.GetTeid (); + NS_LOG_DEBUG ("TEID/IMSI " << imsi); + std::map >::iterator it = m_ueInfoMap.find (imsi); + NS_ASSERT_MSG (it != m_ueInfoMap.end (), "could not find any UE with IMSI " << imsi); + uint16_t cellId = it->second->cellId; + uint16_t enbUeS1Id = it->second->enbUeS1Id; + uint64_t mmeUeS1Id = it->second->mmeUeS1Id; + NS_LOG_DEBUG ("cellId " << cellId << " mmeUeS1Id " << mmeUeS1Id << " enbUeS1Id " << enbUeS1Id); + std::list erabToBeSwitchedInUplinkList; // unused for now + std::map >::iterator jt = m_enbInfoMap.find (cellId); + NS_ASSERT_MSG (jt != m_enbInfoMap.end (), "could not find any eNB with CellId " << cellId); + + NS_LOG_DEBUG ("Send PathSwitchRequestAcknowledge to eNB " << jt->second->s1apSapEnb); + jt->second->s1apSapEnb->PathSwitchRequestAcknowledge (enbUeS1Id, mmeUeS1Id, cellId, erabToBeSwitchedInUplinkList); +} + +void +EpcMmeApplication::DoRecvDeleteBearerRequest (GtpcHeader &header, Ptr packet) +{ + NS_LOG_FUNCTION (this << header); + uint64_t imsi = header.GetTeid (); + NS_LOG_DEBUG ("TEID/IMSI " << imsi); + std::map >::iterator it = m_ueInfoMap.find (imsi); + NS_ASSERT_MSG (it != m_ueInfoMap.end (), "could not find any UE with IMSI " << imsi); + + GtpcDeleteBearerRequestMessage msg; + packet->RemoveHeader (msg); + + GtpcDeleteBearerResponseMessage msgOut; + + std::list epsBearerIds; + for (auto &ebid : msg.GetEpsBearerIds ()) + { + epsBearerIds.push_back (ebid); + RemoveBearer (it->second, ebid); //schedules function to erase, context of de-activated bearer + } + msgOut.SetEpsBearerIds (epsBearerIds); + msgOut.SetTeid (imsi); + msgOut.ComputeMessageLength (); + + Ptr packetOut = Create (); + packetOut->AddHeader (msgOut); + NS_LOG_DEBUG ("Send DeleteBearerResponse to SGW " << m_sgwS11Addr); + m_s11Socket->SendTo (packetOut, 0, InetSocketAddress (m_sgwS11Addr, m_gtpcUdpPort)); +} + +} // namespace ns3 diff --git a/src/lte/model/epc-mme-application.h b/src/lte/model/epc-mme-application.h new file mode 100644 index 000000000..77204de04 --- /dev/null +++ b/src/lte/model/epc-mme-application.h @@ -0,0 +1,240 @@ +/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2017-2018 Centre Tecnologic de Telecomunicacions de Catalunya (CTTC) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Manuel Requena + */ + +#ifndef EPC_MME_APPLICATION_H +#define EPC_MME_APPLICATION_H + +#include "ns3/application.h" +#include "ns3/socket.h" +#include "ns3/epc-s1ap-sap.h" + +namespace ns3 { + +/** + * \ingroup lte + * + * This application implements the Mobility Management Entity (MME) according to + * the 3GPP TS 23.401 document. + * + * This Application implements the MME side of the S1-MME interface between + * the MME node and the eNB nodes and the MME side of the S11 interface between + * the MME node and the SGW node. It supports the following functions and messages: + * + * - Bearer management functions including dedicated bearer establishment + * - NAS signalling + * - Tunnel Management messages + * + * Others functions enumerated in section 4.4.2 of 3GPP TS 23.401 are not supported. + */ +class EpcMmeApplication : public Application +{ + /// allow MemberEpcS1apSapMme class friend access + friend class MemberEpcS1apSapMme; + +public: + /** + * \brief Get the type ID. + * \return the object TypeId + */ + static TypeId GetTypeId (void); + virtual void DoDispose (); + + /** Constructor */ + EpcMmeApplication (); + + /** Destructor */ + virtual ~EpcMmeApplication (); + + /** + * + * \return the MME side of the S1-AP SAP + */ + EpcS1apSapMme* GetS1apSapMme (); + + + /** + * Add a new SGW to the MME + * + * \param sgwS11Addr IPv4 address of the SGW S11 interface + * \param mmeS11Addr IPv4 address of the MME S11 interface + * \param mmeS11Socket socket of the MME S11 interface + */ + void AddSgw (Ipv4Address sgwS11Addr, Ipv4Address mmeS11Addr, Ptr mmeS11Socket); + + /** + * Add a new eNB to the MME + * + * \param ecgi E-UTRAN Cell Global ID, the unique identifier of the eNodeB + * \param enbS1UAddr IPv4 address of the eNB for S1-U communications + * \param enbS1apSap the eNB side of the S1-AP SAP + */ + void AddEnb (uint16_t ecgi, Ipv4Address enbS1UAddr, EpcS1apSapEnb* enbS1apSap); + + /** + * Add a new UE to the MME. This is the equivalent of storing the UE + * credentials before the UE is ever turned on. + * + * \param imsi the unique identifier of the UE + */ + void AddUe (uint64_t imsi); + + /** + * Add an EPS bearer to the list of bearers to be activated for this UE. + * The bearer will be activated when the UE enters the ECM + * connected state. + * + * \param imsi UE identifier + * \param tft traffic flow template of the bearer + * \param bearer QoS characteristics of the bearer + * \returns bearer ID + */ + uint8_t AddBearer (uint64_t imsi, Ptr tft, EpsBearer bearer); + + +private: + // S1-AP SAP MME forwarded methods + + /** + * Process the S1 Initial UE Message received from an eNB + * \param mmeUeS1Id the MME UE S1 ID + * \param enbUeS1Id the ENB UE S1 ID + * \param imsi the IMSI + * \param ecgi the ECGI + */ + void DoInitialUeMessage (uint64_t mmeUeS1Id, uint16_t enbUeS1Id, uint64_t imsi, uint16_t ecgi); + + /** + * Process the S1 Initial Context Setup Response received from an eNB + * \param mmeUeS1Id the MME UE S1 ID + * \param enbUeS1Id the ENB UE S1 ID + * \param erabSetupList the ERAB setup list + */ + void DoInitialContextSetupResponse (uint64_t mmeUeS1Id, uint16_t enbUeS1Id, std::list erabSetupList); + + /** + * Process the S1 Path Switch Request received from an eNB + * \param mmeUeS1Id the MME UE S1 ID + * \param enbUeS1Id the ENB UE S1 ID + * \param cgi the CGI + * \param erabToBeSwitchedInDownlinkList the ERAB to be switched in downlink list + */ + void DoPathSwitchRequest (uint64_t enbUeS1Id, uint64_t mmeUeS1Id, uint16_t cgi, std::list erabToBeSwitchedInDownlinkList); + + /** + * Process ERAB Release Indication received from an eNB + * \param mmeUeS1Id the MME UE S1 ID + * \param enbUeS1Id the ENB UE S1 ID + * \param erabToBeReleaseIndication the ERAB to be release indication list + */ + void DoErabReleaseIndication (uint64_t mmeUeS1Id, uint16_t enbUeS1Id, std::list erabToBeReleaseIndication); + + + // Methods to read/process GTP-C messages of the S11 interface + + /** + * Reads the S11 messages from a socket + * \param socket the socket + */ + void RecvFromS11Socket (Ptr socket); + + /** + * Process GTP-C Create Session Response message + * \param header the GTP-C header + * \param packet the packet containing the message + */ + void DoRecvCreateSessionResponse (GtpcHeader &header, Ptr packet); + + /** + * Process GTP-C Modify Bearer Response message + * \param header the GTP-C header + * \param packet the packet containing the message + */ + void DoRecvModifyBearerResponse (GtpcHeader &header, Ptr packet); + + /** + * Process GTP-C Delete Bearer Request message + * \param header the GTP-C header + * \param packet the packet containing the message + */ + void DoRecvDeleteBearerRequest (GtpcHeader &header, Ptr packet); + + + /** + * Hold info on an EPS bearer to be activated + */ + struct BearerInfo + { + Ptr tft; ///< traffic flow template + EpsBearer bearer; ///< bearer QOS characteristics + uint8_t bearerId; ///< bearer ID + }; + + /** + * Hold info on a UE + */ + struct UeInfo : public SimpleRefCount + { + uint64_t imsi; ///< UE identifier + uint64_t mmeUeS1Id; ///< mmeUeS1Id + uint16_t enbUeS1Id; ///< enbUeS1Id + uint16_t cellId; ///< cell ID + uint16_t bearerCounter; ///< bearer counter + std::list bearersToBeActivated; ///< list of bearers to be activated + }; + + /** + * UeInfo stored by IMSI + */ + std::map > m_ueInfoMap; + + /** + * \brief This Function erases all contexts of bearer from MME side + * \param ueInfo UE information pointer + * \param epsBearerId Bearer Id which need to be removed corresponding to UE + */ + void RemoveBearer (Ptr ueInfo, uint8_t epsBearerId); + + /** + * Hold info on an ENB + */ + struct EnbInfo : public SimpleRefCount + { + uint16_t gci; ///< GCI + Ipv4Address s1uAddr; ///< IP address of the S1-U interface + EpcS1apSapEnb* s1apSapEnb; ///< EpcS1apSapEnb + }; + + /** + * EnbInfo stored by EGCI + */ + std::map > m_enbInfoMap; + + + EpcS1apSapMme* m_s1apSapMme; ///< EpcS1apSapMme + + Ptr m_s11Socket; ///< Socket to send/receive messages in the S11 interface + Ipv4Address m_mmeS11Addr; ///< IPv4 address of the MME S11 interface + Ipv4Address m_sgwS11Addr; ///< IPv4 address of the SGW S11 interface + uint16_t m_gtpcUdpPort; ///< UDP port for GTP-C protocol. Fixed by the standard to port 2123 +}; + +} // namespace ns3 + +#endif // EPC_MME_APPLICATION_H diff --git a/src/lte/model/epc-pgw-application.cc b/src/lte/model/epc-pgw-application.cc new file mode 100644 index 000000000..a90c2d010 --- /dev/null +++ b/src/lte/model/epc-pgw-application.cc @@ -0,0 +1,507 @@ +/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2018 Centre Tecnologic de Telecomunicacions de Catalunya (CTTC) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Manuel Requena + * (based on epc-sgw-pgw-application.cc) + */ + +#include "ns3/log.h" +#include "ns3/abort.h" +#include "ns3/mac48-address.h" +#include "ns3/ipv4.h" +#include "ns3/ipv4-l3-protocol.h" +#include "ns3/ipv6.h" +#include "ns3/ipv6-header.h" +#include "ns3/ipv6-l3-protocol.h" +#include "ns3/inet-socket-address.h" +#include "ns3/epc-gtpu-header.h" +#include "ns3/epc-pgw-application.h" + +namespace ns3 { + +NS_LOG_COMPONENT_DEFINE ("EpcPgwApplication"); + + +///////////////////////// +// UeInfo +///////////////////////// + +EpcPgwApplication::UeInfo::UeInfo () +{ + NS_LOG_FUNCTION (this); +} + +void +EpcPgwApplication::UeInfo::AddBearer (uint8_t bearerId, uint32_t teid, Ptr tft) +{ + NS_LOG_FUNCTION (this << (uint16_t) bearerId << teid << tft); + m_teidByBearerIdMap[bearerId] = teid; + return m_tftClassifier.Add (tft, teid); +} + +void +EpcPgwApplication::UeInfo::RemoveBearer (uint8_t bearerId) +{ + NS_LOG_FUNCTION (this << (uint16_t) bearerId); + m_teidByBearerIdMap.erase (bearerId); +} + +uint32_t +EpcPgwApplication::UeInfo::Classify (Ptr p, uint16_t protocolNumber) +{ + NS_LOG_FUNCTION (this << p); + // we hardcode DOWNLINK direction since the PGW is expected to + // classify only downlink packets (uplink packets will go to the + // internet without any classification). + return m_tftClassifier.Classify (p, EpcTft::DOWNLINK, protocolNumber); +} + +Ipv4Address +EpcPgwApplication::UeInfo::GetSgwAddr () +{ + return m_sgwAddr; +} + +void +EpcPgwApplication::UeInfo::SetSgwAddr (Ipv4Address sgwAddr) +{ + m_sgwAddr = sgwAddr; +} + +Ipv4Address +EpcPgwApplication::UeInfo::GetUeAddr () +{ + return m_ueAddr; +} + +void +EpcPgwApplication::UeInfo::SetUeAddr (Ipv4Address ueAddr) +{ + m_ueAddr = ueAddr; +} + +Ipv6Address +EpcPgwApplication::UeInfo::GetUeAddr6 () +{ + return m_ueAddr6; +} + +void +EpcPgwApplication::UeInfo::SetUeAddr6 (Ipv6Address ueAddr) +{ + m_ueAddr6 = ueAddr; +} + +///////////////////////// +// EpcPgwApplication +///////////////////////// + +TypeId +EpcPgwApplication::GetTypeId (void) +{ + static TypeId tid = TypeId ("ns3::EpcPgwApplication") + .SetParent () + .SetGroupName ("Lte") + .AddTraceSource ("RxFromTun", + "Receive data packets from internet in Tunnel NetDevice", + MakeTraceSourceAccessor (&EpcPgwApplication::m_rxTunPktTrace), + "ns3::EpcPgwApplication::RxTracedCallback") + .AddTraceSource ("RxFromS1u", + "Receive data packets from S5 Socket", + MakeTraceSourceAccessor (&EpcPgwApplication::m_rxS5PktTrace), + "ns3::EpcPgwApplication::RxTracedCallback") + ; + return tid; +} + +void +EpcPgwApplication::DoDispose () +{ + NS_LOG_FUNCTION (this); + m_s5uSocket->SetRecvCallback (MakeNullCallback > ()); + m_s5uSocket = 0; + m_s5cSocket->SetRecvCallback (MakeNullCallback > ()); + m_s5cSocket = 0; +} + +EpcPgwApplication::EpcPgwApplication (const Ptr tunDevice, Ipv4Address s5Addr, + const Ptr s5uSocket, const Ptr s5cSocket) + : m_pgwS5Addr (s5Addr), + m_s5uSocket (s5uSocket), + m_s5cSocket (s5cSocket), + m_tunDevice (tunDevice), + m_gtpuUdpPort (2152), // fixed by the standard + m_gtpcUdpPort (2123) // fixed by the standard +{ + NS_LOG_FUNCTION (this << tunDevice << s5Addr << s5uSocket << s5cSocket); + m_s5uSocket->SetRecvCallback (MakeCallback (&EpcPgwApplication::RecvFromS5uSocket, this)); + m_s5cSocket->SetRecvCallback (MakeCallback (&EpcPgwApplication::RecvFromS5cSocket, this)); +} + +EpcPgwApplication::~EpcPgwApplication () +{ + NS_LOG_FUNCTION (this); +} + +bool +EpcPgwApplication::RecvFromTunDevice (Ptr packet, const Address& source, const Address& dest, uint16_t protocolNumber) +{ + NS_LOG_FUNCTION (this << source << dest << protocolNumber << packet << packet->GetSize ()); + m_rxTunPktTrace (packet->Copy ()); + + // get IP address of UE + if (protocolNumber == Ipv4L3Protocol::PROT_NUMBER) + { + Ipv4Header ipv4Header; + packet->PeekHeader (ipv4Header); + Ipv4Address ueAddr = ipv4Header.GetDestination (); + NS_LOG_LOGIC ("packet addressed to UE " << ueAddr); + + // find corresponding UeInfo address + std::map >::iterator it = m_ueInfoByAddrMap.find (ueAddr); + if (it == m_ueInfoByAddrMap.end ()) + { + NS_LOG_WARN ("unknown UE address " << ueAddr); + } + else + { + Ipv4Address sgwAddr = it->second->GetSgwAddr (); + uint32_t teid = it->second->Classify (packet, protocolNumber); + if (teid == 0) + { + NS_LOG_WARN ("no matching bearer for this packet"); + } + else + { + SendToS5uSocket (packet, sgwAddr, teid); + } + } + } + else if (protocolNumber == Ipv6L3Protocol::PROT_NUMBER) + { + Ipv6Header ipv6Header; + packet->PeekHeader (ipv6Header); + Ipv6Address ueAddr = ipv6Header.GetDestinationAddress (); + NS_LOG_LOGIC ("packet addressed to UE " << ueAddr); + + // find corresponding UeInfo address + std::map >::iterator it = m_ueInfoByAddrMap6.find (ueAddr); + if (it == m_ueInfoByAddrMap6.end ()) + { + NS_LOG_WARN ("unknown UE address " << ueAddr); + } + else + { + Ipv4Address sgwAddr = it->second->GetSgwAddr (); + uint32_t teid = it->second->Classify (packet, protocolNumber); + if (teid == 0) + { + NS_LOG_WARN ("no matching bearer for this packet"); + } + else + { + SendToS5uSocket (packet, sgwAddr, teid); + } + } + } + else + { + NS_ABORT_MSG ("Unknown IP type"); + } + + // there is no reason why we should notify the TUN + // VirtualNetDevice that he failed to send the packet: if we receive + // any bogus packet, it will just be silently discarded. + const bool succeeded = true; + return succeeded; +} + +void +EpcPgwApplication::RecvFromS5uSocket (Ptr socket) +{ + NS_LOG_FUNCTION (this << socket); + NS_ASSERT (socket == m_s5uSocket); + Ptr packet = socket->Recv (); + m_rxS5PktTrace (packet->Copy ()); + + GtpuHeader gtpu; + packet->RemoveHeader (gtpu); + uint32_t teid = gtpu.GetTeid (); + + SendToTunDevice (packet, teid); +} + +void +EpcPgwApplication::RecvFromS5cSocket (Ptr socket) +{ + NS_LOG_FUNCTION (this << socket); + NS_ASSERT (socket == m_s5cSocket); + Ptr packet = socket->Recv (); + GtpcHeader header; + packet->PeekHeader (header); + uint16_t msgType = header.GetMessageType (); + + switch (msgType) + { + case GtpcHeader::CreateSessionRequest: + DoRecvCreateSessionRequest (packet); + break; + + case GtpcHeader::ModifyBearerRequest: + DoRecvModifyBearerRequest (packet); + break; + + case GtpcHeader::DeleteBearerCommand: + DoRecvDeleteBearerCommand (packet); + break; + + case GtpcHeader::DeleteBearerResponse: + DoRecvDeleteBearerResponse (packet); + break; + + default: + NS_FATAL_ERROR ("GTP-C message not supported"); + break; + } +} + +void +EpcPgwApplication::DoRecvCreateSessionRequest (Ptr packet) +{ + NS_LOG_FUNCTION (this); + + GtpcCreateSessionRequestMessage msg; + packet->RemoveHeader (msg); + uint64_t imsi = msg.GetImsi (); + uint16_t cellId = msg.GetUliEcgi (); + NS_LOG_DEBUG ("cellId " << cellId << " imsi " << (uint16_t)imsi); + + std::map >::iterator ueit = m_ueInfoByImsiMap.find (imsi); + NS_ASSERT_MSG (ueit != m_ueInfoByImsiMap.end (), "unknown IMSI " << imsi); + ueit->second->SetSgwAddr (m_sgwS5Addr); + + GtpcHeader::Fteid_t sgwS5cFteid = msg.GetSenderCpFteid (); + NS_ASSERT_MSG (sgwS5cFteid.interfaceType == GtpcHeader::S5_SGW_GTPC, + "Wrong interface type"); + + GtpcCreateSessionResponseMessage msgOut; + msgOut.SetTeid (sgwS5cFteid.teid); + msgOut.SetCause (GtpcCreateSessionResponseMessage::REQUEST_ACCEPTED); + + GtpcHeader::Fteid_t pgwS5cFteid; + pgwS5cFteid.interfaceType = GtpcHeader::S5_PGW_GTPC; + pgwS5cFteid.teid = sgwS5cFteid.teid; + pgwS5cFteid.addr = m_pgwS5Addr; + msgOut.SetSenderCpFteid (pgwS5cFteid); + + std::list bearerContexts = + msg.GetBearerContextsToBeCreated (); + NS_LOG_DEBUG ("BearerContextsToBeCreated size = " << bearerContexts.size ()); + + std::list bearerContextsCreated; + for (auto &bearerContext : bearerContexts) + { + uint32_t teid = bearerContext.sgwS5uFteid.teid; + NS_LOG_DEBUG ("bearerId " << (uint16_t)bearerContext.epsBearerId << + " SGW " << bearerContext.sgwS5uFteid.addr << " TEID " << teid); + + ueit->second->AddBearer (bearerContext.epsBearerId, teid, bearerContext.tft); + + GtpcCreateSessionResponseMessage::BearerContextCreated bearerContextOut; + bearerContextOut.fteid.interfaceType = GtpcHeader::S5_PGW_GTPU; + bearerContextOut.fteid.teid = teid; + bearerContextOut.fteid.addr = m_pgwS5Addr; + bearerContextOut.epsBearerId = bearerContext.epsBearerId; + bearerContextOut.bearerLevelQos = bearerContext.bearerLevelQos; + bearerContextOut.tft = bearerContext.tft; + bearerContextsCreated.push_back (bearerContextOut); + } + + NS_LOG_DEBUG ("BearerContextsCreated size = " << bearerContextsCreated.size ()); + msgOut.SetBearerContextsCreated (bearerContextsCreated); + msgOut.SetTeid (sgwS5cFteid.teid); + msgOut.ComputeMessageLength (); + + Ptr packetOut = Create (); + packetOut->AddHeader (msgOut); + NS_LOG_DEBUG ("Send CreateSessionResponse to SGW " << sgwS5cFteid.addr); + m_s5cSocket->SendTo (packetOut, 0, InetSocketAddress (sgwS5cFteid.addr, m_gtpcUdpPort)); +} + +void +EpcPgwApplication::DoRecvModifyBearerRequest (Ptr packet) +{ + NS_LOG_FUNCTION (this); + + GtpcModifyBearerRequestMessage msg; + packet->RemoveHeader (msg); + uint8_t imsi = msg.GetImsi (); + uint16_t cellId = msg.GetUliEcgi (); + NS_LOG_DEBUG ("cellId " << cellId << "IMSI " << (uint16_t)imsi); + + std::map >::iterator ueit = m_ueInfoByImsiMap.find (imsi); + NS_ASSERT_MSG (ueit != m_ueInfoByImsiMap.end (), "unknown IMSI " << imsi); + ueit->second->SetSgwAddr (m_sgwS5Addr); + + std::list bearerContexts = + msg.GetBearerContextsToBeModified (); + NS_LOG_DEBUG ("BearerContextsToBeModified size = " << bearerContexts.size ()); + + for (auto &bearerContext : bearerContexts) + { + Ipv4Address sgwAddr = bearerContext.fteid.addr; + uint32_t teid = bearerContext.fteid.teid; + NS_LOG_DEBUG ("bearerId " << (uint16_t)bearerContext.epsBearerId << + " SGW " << sgwAddr << " TEID " << teid); + } + + GtpcModifyBearerResponseMessage msgOut; + msgOut.SetCause (GtpcIes::REQUEST_ACCEPTED); + msgOut.SetTeid (imsi); + msgOut.ComputeMessageLength (); + + Ptr packetOut = Create (); + packetOut->AddHeader (msgOut); + NS_LOG_DEBUG ("Send ModifyBearerResponse to SGW " << m_sgwS5Addr); + m_s5cSocket->SendTo (packetOut, 0, InetSocketAddress (m_sgwS5Addr, m_gtpcUdpPort)); +} + +void +EpcPgwApplication::DoRecvDeleteBearerCommand (Ptr packet) +{ + NS_LOG_FUNCTION (this); + + GtpcDeleteBearerCommandMessage msg; + packet->RemoveHeader (msg); + + std::list epsBearerIds; + for (auto &bearerContext : msg.GetBearerContexts ()) + { + NS_LOG_DEBUG ("ebid " << bearerContext.m_epsBearerId); + epsBearerIds.push_back (bearerContext.m_epsBearerId); + } + + GtpcDeleteBearerRequestMessage msgOut; + msgOut.SetEpsBearerIds (epsBearerIds); + msgOut.SetTeid (msg.GetTeid ()); + msgOut.ComputeMessageLength (); + + Ptr packetOut = Create (); + packetOut->AddHeader (msgOut); + NS_LOG_DEBUG ("Send DeleteBearerRequest to SGW " << m_sgwS5Addr); + m_s5cSocket->SendTo (packetOut, 0, InetSocketAddress (m_sgwS5Addr, m_gtpcUdpPort)); +} + +void +EpcPgwApplication::DoRecvDeleteBearerResponse (Ptr packet) +{ + NS_LOG_FUNCTION (this); + + GtpcDeleteBearerResponseMessage msg; + packet->RemoveHeader (msg); + + uint64_t imsi = msg.GetTeid (); + std::map >::iterator ueit = m_ueInfoByImsiMap.find (imsi); + NS_ASSERT_MSG (ueit != m_ueInfoByImsiMap.end (), "unknown IMSI " << imsi); + + for (auto &epsBearerId : msg.GetEpsBearerIds ()) + { + // Remove de-activated bearer contexts from PGW side + ueit->second->RemoveBearer (epsBearerId); + } +} + + +void +EpcPgwApplication::SendToTunDevice (Ptr packet, uint32_t teid) +{ + NS_LOG_FUNCTION (this << packet << teid); + NS_LOG_LOGIC ("packet size: " << packet->GetSize () << " bytes"); + + uint8_t ipType; + packet->CopyData (&ipType, 1); + ipType = (ipType>>4) & 0x0f; + + uint16_t protocol = 0; + if (ipType == 0x04) + { + protocol = 0x0800; + } + else if (ipType == 0x06) + { + protocol = 0x86DD; + } + else + { + NS_ABORT_MSG ("Unknown IP type"); + } + + m_tunDevice->Receive (packet, protocol, m_tunDevice->GetAddress (), m_tunDevice->GetAddress (), NetDevice::PACKET_HOST); +} + +void +EpcPgwApplication::SendToS5uSocket (Ptr packet, Ipv4Address sgwAddr, uint32_t teid) +{ + NS_LOG_FUNCTION (this << packet << sgwAddr << teid); + + GtpuHeader gtpu; + gtpu.SetTeid (teid); + // From 3GPP TS 29.281 v10.0.0 Section 5.1 + // Length of the payload + the non obligatory GTP-U header + gtpu.SetLength (packet->GetSize () + gtpu.GetSerializedSize () - 8); + packet->AddHeader (gtpu); + uint32_t flags = 0; + m_s5uSocket->SendTo (packet, flags, InetSocketAddress (sgwAddr, m_gtpuUdpPort)); +} + + +void +EpcPgwApplication::AddSgw (Ipv4Address sgwS5Addr) +{ + NS_LOG_FUNCTION (this << sgwS5Addr); + m_sgwS5Addr = sgwS5Addr; +} + +void +EpcPgwApplication::AddUe (uint64_t imsi) +{ + NS_LOG_FUNCTION (this << imsi); + Ptr ueInfo = Create (); + m_ueInfoByImsiMap[imsi] = ueInfo; +} + +void +EpcPgwApplication::SetUeAddress (uint64_t imsi, Ipv4Address ueAddr) +{ + NS_LOG_FUNCTION (this << imsi << ueAddr); + std::map >::iterator ueit = m_ueInfoByImsiMap.find (imsi); + NS_ASSERT_MSG (ueit != m_ueInfoByImsiMap.end (), "unknown IMSI" << imsi); + ueit->second->SetUeAddr (ueAddr); + m_ueInfoByAddrMap[ueAddr] = ueit->second; +} + +void +EpcPgwApplication::SetUeAddress6 (uint64_t imsi, Ipv6Address ueAddr) +{ + NS_LOG_FUNCTION (this << imsi << ueAddr); + std::map >::iterator ueit = m_ueInfoByImsiMap.find (imsi); + NS_ASSERT_MSG (ueit != m_ueInfoByImsiMap.end (), "unknown IMSI " << imsi); + m_ueInfoByAddrMap6[ueAddr] = ueit->second; + ueit->second->SetUeAddr6 (ueAddr); +} + +} // namespace ns3 diff --git a/src/lte/model/epc-pgw-application.h b/src/lte/model/epc-pgw-application.h new file mode 100644 index 000000000..0317acd14 --- /dev/null +++ b/src/lte/model/epc-pgw-application.h @@ -0,0 +1,343 @@ +/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2018 Centre Tecnologic de Telecomunicacions de Catalunya (CTTC) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Manuel Requena + * (based on epc-sgw-pgw-application.h) + */ + +#ifndef EPC_PGW_APPLICATION_H +#define EPC_PGW_APPLICATION_H + +#include "ns3/virtual-net-device.h" +#include "ns3/socket.h" +#include "ns3/application.h" +#include "ns3/epc-tft-classifier.h" +#include "ns3/epc-gtpc-header.h" + +namespace ns3 { + +/** + * \ingroup lte + * + * This application implements the Packet Data Network (PDN) Gateway Entity (PGW) + * according to the 3GPP TS 23.401 document. + * + * This Application implements the PGW side of the S5 interface between + * the PGW node and the SGW nodes and the PGW side of the SGi interface between + * the PGW node and the internet hosts. It supports the following functions and messages: + * + * - S5 connectivity (i.e. GTPv2-C signalling and GTP-U data plane) + * - Bearer management functions including dedicated bearer establishment + * - Per-user based packet filtering + * - UL and DL bearer binding + * - Tunnel Management messages + * + * Others functions enumerated in section 4.4.3.3 of 3GPP TS 23.401 are not supported. + */ +class EpcPgwApplication : public Application +{ +public: + + /** + * \brief Get the type ID. + * \return the object TypeId + */ + static TypeId GetTypeId (void); + virtual void DoDispose (); + + /** + * Constructor that binds the tap device to the callback methods. + * + * \param tunDevice TUN VirtualNetDevice used to tunnel IP packets from + * the SGi interface of the PGW in the internet + * over GTP-U/UDP/IP on the S5 interface + * \param s5Addr IP address of the PGW S5 interface + * \param s5uSocket socket used to send GTP-U packets to the peer SGW + * \param s5cSocket socket used to send GTP-C packets to the peer SGW + */ + EpcPgwApplication (const Ptr tunDevice, Ipv4Address s5Addr, + const Ptr s5uSocket, const Ptr s5cSocket); + + /** Destructor */ + virtual ~EpcPgwApplication (void); + + /** + * Method to be assigned to the callback of the SGi TUN VirtualNetDevice. + * It is called when the PGW receives a data packet from the + * internet (including IP headers) that is to be sent to the UE via + * its associated SGW and eNB, tunneling IP over GTP-U/UDP/IP. + * + * \param packet + * \param source + * \param dest + * \param protocolNumber + * \return true always + */ + bool RecvFromTunDevice (Ptr packet, const Address& source, const Address& dest, uint16_t protocolNumber); + + /** + * Method to be assigned to the receiver callback of the S5-U socket. + * It is called when the PGW receives a data packet from the SGW + * that is to be forwarded to the internet. + * + * \param socket pointer to the S5-U socket + */ + void RecvFromS5uSocket (Ptr socket); + + /** + * Method to be assigned to the receiver callback of the S5-C socket. + * It is called when the PGW receives a control packet from the SGW. + * + * \param socket pointer to the S5-C socket + */ + void RecvFromS5cSocket (Ptr socket); + + /** + * Send a data packet to the internet via the SGi interface of the PGW + * + * \param packet packet to be sent + * \param teid the Tunnel Enpoint Identifier + */ + void SendToTunDevice (Ptr packet, uint32_t teid); + + /** + * Send a data packet to the SGW via the S5-U interface + * + * \param packet packet to be sent + * \param sgwS5uAddress the address of the SGW + * \param teid the Tunnel Enpoint IDentifier + */ + void SendToS5uSocket (Ptr packet, Ipv4Address sgwS5uAddress, uint32_t teid); + + + /** + * Let the PGW be aware of a new SGW + * + * \param sgwS5Addr the address of the SGW S5 interface + */ + void AddSgw (Ipv4Address sgwS5Addr); + + /** + * Let the PGW be aware of a new UE + * + * \param imsi the unique identifier of the UE + */ + void AddUe (uint64_t imsi); + + /** + * Set the address of a previously added UE + * + * \param imsi the unique identifier of the UE + * \param ueAddr the IPv4 address of the UE + */ + void SetUeAddress (uint64_t imsi, Ipv4Address ueAddr); + + /** + * set the address of a previously added UE + * + * \param imsi the unique identifier of the UE + * \param ueAddr the IPv6 address of the UE + */ + void SetUeAddress6 (uint64_t imsi, Ipv6Address ueAddr); + + /** + * TracedCallback signature for data Packet reception event. + * + * \param [in] packet The data packet sent from the internet. + */ + typedef void (* RxTracedCallback) + (Ptr packet); + +private: + + /** + * Process Create Session Request message + * \param packet GTPv2-C Create Session Request message + */ + void DoRecvCreateSessionRequest (Ptr packet); + + /** + * Process Modify Bearer Request message + * \param packet GTPv2-C Modify Bearer Request message + */ + void DoRecvModifyBearerRequest (Ptr packet); + + /** + * Process Delete Bearer Command message + * \param packet GTPv2-C Delete Bearer Command message + */ + void DoRecvDeleteBearerCommand (Ptr packet); + + /** + * Process Delete Bearer Response message + * \param packet GTPv2-C Delete Bearer Response message + */ + void DoRecvDeleteBearerResponse (Ptr packet); + + + /** + * store info for each UE connected to this PGW + */ + class UeInfo : public SimpleRefCount + { + public: + UeInfo (); + + /** + * Add a bearer for this UE on PGW side + * + * \param bearerId the ID of the EPS Bearer to be activated + * \param teid the TEID of the new bearer + * \param tft the Traffic Flow Template of the new bearer to be added + */ + void AddBearer (uint8_t bearerId, uint32_t teid, Ptr tft); + + /** + * Delete context of bearer for this UE on PGW side + * + * \param bearerId the ID of the EPS Bearer whose contexts is to be removed + */ + void RemoveBearer (uint8_t bearerId); + + /** + * Classify the packet according to TFTs of this UE + * + * \param p the IPv4 or IPv6 packet from the internet to be classified + * \param protocolNumber identifies the type of packet. + * Only IPv4 and IPv6 packets are allowed. + * + * \return the corresponding bearer ID > 0 identifying the bearer + * among all the bearers of this UE; returns 0 if no bearers + * matches with the previously declared TFTs + */ + uint32_t Classify (Ptr p, uint16_t protocolNumber); + + /** + * Get the address of the SGW to which the UE is connected + * + * \return the address of the SGW + */ + Ipv4Address GetSgwAddr (); + + /** + * Set the address of the eNB to which the UE is connected + * + * \param addr the address of the SGW + */ + void SetSgwAddr (Ipv4Address addr); + + /** + * Get the IPv4 address of the UE + * + * \return the IPv4 address of the UE + */ + Ipv4Address GetUeAddr (); + + /** + * Set the IPv4 address of the UE + * + * \param addr the IPv4 address of the UE + */ + void SetUeAddr (Ipv4Address addr); + + /** + * Get the IPv6 address of the UE + * + * \return the IPv6 address of the UE + */ + Ipv6Address GetUeAddr6 (); + + /** + * Set the IPv6 address of the UE + * + * \param addr the IPv6 address of the UE + */ + void SetUeAddr6 (Ipv6Address addr); + + private: + Ipv4Address m_ueAddr; ///< UE IPv4 address + Ipv6Address m_ueAddr6; ///< UE IPv6 address + Ipv4Address m_sgwAddr; ///< SGW IPv4 address + EpcTftClassifier m_tftClassifier; ///< TFT classifier + std::map m_teidByBearerIdMap; ///< TEID By bearer ID Map + }; + + /** + * PGW address of the S5 interface + */ + Ipv4Address m_pgwS5Addr; + + /** + * UDP socket to send/receive GTP-U packets to/from the S5 interface + */ + Ptr m_s5uSocket; + + /** + * UDP socket to send/receive GTPv2-C packets to/from the S5 interface + */ + Ptr m_s5cSocket; + + /** + * TUN VirtualNetDevice used for tunneling/detunneling IP packets + * from/to the internet over GTP-U/UDP/IP on the S5 interface + */ + Ptr m_tunDevice; + + /** + * UeInfo stored by UE IPv4 address + */ + std::map > m_ueInfoByAddrMap; + + /** + * UeInfo stored by UE IPv6 address + */ + std::map > m_ueInfoByAddrMap6; + + /** + * UeInfo stored by IMSI + */ + std::map > m_ueInfoByImsiMap; + + /** + * UDP port to be used for GTP-U + */ + uint16_t m_gtpuUdpPort; + + /** + * UDP port to be used for GTPv2-C + */ + uint16_t m_gtpcUdpPort; + + /** + * SGW address of the S5 interface + */ + Ipv4Address m_sgwS5Addr; + + /** + * \brief Callback to trace received data packets at Tun NetDevice from internet. + */ + TracedCallback > m_rxTunPktTrace; + + /** + * \brief Callback to trace received data packets from S5 socket. + */ + TracedCallback > m_rxS5PktTrace; +}; + +} // namespace ns3 + +#endif // EPC_PGW_APPLICATION_H diff --git a/src/lte/model/epc-sgw-application.cc b/src/lte/model/epc-sgw-application.cc new file mode 100644 index 000000000..503a9f50e --- /dev/null +++ b/src/lte/model/epc-sgw-application.cc @@ -0,0 +1,480 @@ +/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2017-2018 Centre Tecnologic de Telecomunicacions de Catalunya (CTTC) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Manuel Requena + */ + +#include "ns3/log.h" +#include "ns3/epc-gtpu-header.h" +#include "ns3/epc-sgw-application.h" + +namespace ns3 { + +NS_LOG_COMPONENT_DEFINE ("EpcSgwApplication"); + +NS_OBJECT_ENSURE_REGISTERED (EpcSgwApplication); + +EpcSgwApplication::EpcSgwApplication (const Ptr s1uSocket, Ipv4Address s5Addr, + const Ptr s5uSocket, const Ptr s5cSocket) + : m_s5Addr (s5Addr), + m_s5uSocket (s5uSocket), + m_s5cSocket (s5cSocket), + m_s1uSocket (s1uSocket), + m_gtpuUdpPort (2152), // fixed by the standard + m_gtpcUdpPort (2123), // fixed by the standard + m_teidCount (0) +{ + NS_LOG_FUNCTION (this << s1uSocket << s5Addr << s5uSocket << s5cSocket); + m_s1uSocket->SetRecvCallback (MakeCallback (&EpcSgwApplication::RecvFromS1uSocket, this)); + m_s5uSocket->SetRecvCallback (MakeCallback (&EpcSgwApplication::RecvFromS5uSocket, this)); + m_s5cSocket->SetRecvCallback (MakeCallback (&EpcSgwApplication::RecvFromS5cSocket, this)); +} + +EpcSgwApplication::~EpcSgwApplication () +{ + NS_LOG_FUNCTION (this); +} + +void +EpcSgwApplication::DoDispose () +{ + NS_LOG_FUNCTION (this); + m_s1uSocket->SetRecvCallback (MakeNullCallback > ()); + m_s1uSocket = 0; + m_s5uSocket->SetRecvCallback (MakeNullCallback > ()); + m_s5uSocket = 0; + m_s5cSocket->SetRecvCallback (MakeNullCallback > ()); + m_s5cSocket = 0; +} + +TypeId +EpcSgwApplication::GetTypeId (void) +{ + static TypeId tid = TypeId ("ns3::EpcSgwApplication") + .SetParent () + .SetGroupName("Lte"); + return tid; +} + + +void +EpcSgwApplication::AddMme (Ipv4Address mmeS11Addr, Ptr s11Socket) +{ + NS_LOG_FUNCTION (this << mmeS11Addr << s11Socket); + m_mmeS11Addr = mmeS11Addr; + m_s11Socket = s11Socket; + m_s11Socket->SetRecvCallback (MakeCallback (&EpcSgwApplication::RecvFromS11Socket, this)); +} + +void +EpcSgwApplication::AddPgw (Ipv4Address pgwAddr) +{ + NS_LOG_FUNCTION (this << pgwAddr); + m_pgwAddr = pgwAddr; +} + +void +EpcSgwApplication::AddEnb (uint16_t cellId, Ipv4Address enbAddr, Ipv4Address sgwAddr) +{ + NS_LOG_FUNCTION (this << cellId << enbAddr << sgwAddr); + EnbInfo enbInfo; + enbInfo.enbAddr = enbAddr; + enbInfo.sgwAddr = sgwAddr; + m_enbInfoByCellId[cellId] = enbInfo; +} + + +void +EpcSgwApplication::RecvFromS11Socket (Ptr socket) +{ + NS_LOG_FUNCTION (this << socket); + NS_ASSERT (socket == m_s11Socket); + Ptr packet = socket->Recv (); + GtpcHeader header; + packet->PeekHeader (header); + uint16_t msgType = header.GetMessageType (); + + switch (msgType) + { + case GtpcHeader::CreateSessionRequest: + DoRecvCreateSessionRequest (packet); + break; + + case GtpcHeader::ModifyBearerRequest: + DoRecvModifyBearerRequest (packet); + break; + + case GtpcHeader::DeleteBearerCommand: + DoRecvDeleteBearerCommand (packet); + break; + + case GtpcHeader::DeleteBearerResponse: + DoRecvDeleteBearerResponse (packet); + break; + + default: + NS_FATAL_ERROR ("GTP-C message not supported"); + break; + } +} + + +void +EpcSgwApplication::RecvFromS5uSocket (Ptr socket) +{ + NS_LOG_FUNCTION (this << socket); + NS_ASSERT (socket == m_s5uSocket); + Ptr packet = socket->Recv (); + GtpuHeader gtpu; + packet->RemoveHeader (gtpu); + uint32_t teid = gtpu.GetTeid (); + + Ipv4Address enbAddr = m_enbByTeidMap[teid]; + NS_LOG_DEBUG ("eNB " << enbAddr << " TEID " << teid); + SendToS1uSocket (packet, enbAddr, teid); +} + +void +EpcSgwApplication::RecvFromS5cSocket (Ptr socket) +{ + NS_LOG_FUNCTION (this << socket); + NS_ASSERT (socket == m_s5cSocket); + Ptr packet = socket->Recv (); + GtpcHeader header; + packet->PeekHeader (header); + uint16_t msgType = header.GetMessageType (); + + switch (msgType) + { + case GtpcHeader::CreateSessionResponse: + DoRecvCreateSessionResponse (packet); + break; + + case GtpcHeader::ModifyBearerResponse: + DoRecvModifyBearerResponse (packet); + break; + + case GtpcHeader::DeleteBearerRequest: + DoRecvDeleteBearerRequest (packet); + break; + + default: + NS_FATAL_ERROR ("GTP-C message not supported"); + break; + } +} + +void +EpcSgwApplication::RecvFromS1uSocket (Ptr socket) +{ + NS_LOG_FUNCTION (this << socket); + NS_ASSERT (socket == m_s1uSocket); + Ptr packet = socket->Recv (); + GtpuHeader gtpu; + packet->RemoveHeader (gtpu); + uint32_t teid = gtpu.GetTeid (); + + SendToS5uSocket (packet, m_pgwAddr, teid); +} + +void +EpcSgwApplication::SendToS1uSocket (Ptr packet, Ipv4Address enbAddr, uint32_t teid) +{ + NS_LOG_FUNCTION (this << packet << enbAddr << teid); + + GtpuHeader gtpu; + gtpu.SetTeid (teid); + // From 3GPP TS 29.281 v10.0.0 Section 5.1 + // Length of the payload + the non obligatory GTP-U header + gtpu.SetLength (packet->GetSize () + gtpu.GetSerializedSize () - 8); + packet->AddHeader (gtpu); + m_s1uSocket->SendTo (packet, 0, InetSocketAddress (enbAddr, m_gtpuUdpPort)); +} + +void +EpcSgwApplication::SendToS5uSocket (Ptr packet, Ipv4Address pgwAddr, uint32_t teid) +{ + NS_LOG_FUNCTION (this << packet << pgwAddr << teid); + + GtpuHeader gtpu; + gtpu.SetTeid (teid); + // From 3GPP TS 29.281 v10.0.0 Section 5.1 + // Length of the payload + the non obligatory GTP-U header + gtpu.SetLength (packet->GetSize () + gtpu.GetSerializedSize () - 8); + packet->AddHeader (gtpu); + m_s5uSocket->SendTo (packet, 0, InetSocketAddress (pgwAddr, m_gtpuUdpPort)); +} + + +/////////////////////////////////// +// Process messages from the MME +/////////////////////////////////// + +void +EpcSgwApplication::DoRecvCreateSessionRequest (Ptr packet) +{ + NS_LOG_FUNCTION (this); + + GtpcCreateSessionRequestMessage msg; + packet->RemoveHeader (msg); + uint8_t imsi = msg.GetImsi (); + uint16_t cellId = msg.GetUliEcgi (); + NS_LOG_DEBUG ("IMSI " << (uint16_t)imsi << " cellId " << cellId); + + std::map::iterator enbit = m_enbInfoByCellId.find (cellId); + NS_ASSERT_MSG (enbit != m_enbInfoByCellId.end (), "unknown CellId " << cellId); + Ipv4Address enbAddr = enbit->second.enbAddr; + NS_LOG_DEBUG ("eNB " << enbAddr); + + GtpcHeader::Fteid_t mmeS11Fteid = msg.GetSenderCpFteid (); + NS_ASSERT_MSG (mmeS11Fteid.interfaceType == GtpcHeader::S11_MME_GTPC, + "wrong interface type"); + + GtpcCreateSessionRequestMessage msgOut; + msgOut.SetImsi (imsi); + msgOut.SetUliEcgi (cellId); + + GtpcHeader::Fteid_t sgwS5cFteid; + sgwS5cFteid.interfaceType = GtpcHeader::S5_SGW_GTPC; + sgwS5cFteid.teid = imsi; + m_mmeS11FteidBySgwS5cTeid[sgwS5cFteid.teid] = mmeS11Fteid; + sgwS5cFteid.addr = m_s5Addr; + msgOut.SetSenderCpFteid (sgwS5cFteid); // S5 SGW GTP-C TEID + + std::list bearerContexts = + msg.GetBearerContextsToBeCreated (); + NS_LOG_DEBUG ("BearerContextToBeCreated size = " << bearerContexts.size ()); + std::list bearerContextsOut; + for (auto &bearerContext : bearerContexts) + { + // simple sanity check. If you ever need more than 4M teids + // throughout your simulation, you'll need to implement a smarter teid + // management algorithm. + NS_ABORT_IF (m_teidCount == 0xFFFFFFFF); + uint32_t teid = ++m_teidCount; + + NS_LOG_DEBUG (" TEID " << teid); + m_enbByTeidMap[teid] = enbAddr; + + GtpcCreateSessionRequestMessage::BearerContextToBeCreated bearerContextOut; + bearerContextOut.sgwS5uFteid.interfaceType = GtpcHeader::S5_SGW_GTPU; + bearerContextOut.sgwS5uFteid.teid = teid; // S5U SGW FTEID + bearerContextOut.sgwS5uFteid.addr = enbit->second.sgwAddr; + bearerContextOut.epsBearerId = bearerContext.epsBearerId; + bearerContextOut.bearerLevelQos = bearerContext.bearerLevelQos; + bearerContextOut.tft = bearerContext.tft; + bearerContextsOut.push_back (bearerContextOut); + } + + msgOut.SetBearerContextsToBeCreated (bearerContextsOut); + + msgOut.SetTeid (0); + msgOut.ComputeMessageLength (); + + Ptr packetOut = Create (); + packetOut->AddHeader (msgOut); + NS_LOG_DEBUG ("Send CreateSessionRequest to PGW " << m_pgwAddr); + m_s5cSocket->SendTo (packetOut, 0, InetSocketAddress (m_pgwAddr, m_gtpcUdpPort)); +} + +void +EpcSgwApplication::DoRecvModifyBearerRequest (Ptr packet) +{ + NS_LOG_FUNCTION (this); + + GtpcModifyBearerRequestMessage msg; + packet->RemoveHeader (msg); + uint8_t imsi = msg.GetImsi (); + uint16_t cellId = msg.GetUliEcgi (); + NS_LOG_DEBUG ("IMSI " << (uint16_t)imsi << " cellId " << cellId); + + std::map::iterator enbit = m_enbInfoByCellId.find (cellId); + NS_ASSERT_MSG (enbit != m_enbInfoByCellId.end (), "unknown CellId " << cellId); + Ipv4Address enbAddr = enbit->second.enbAddr; + NS_LOG_DEBUG ("eNB " << enbAddr); + + GtpcModifyBearerRequestMessage msgOut; + msgOut.SetImsi (imsi); + msgOut.SetUliEcgi (cellId); + + std::list bearerContextsOut; + std::list bearerContexts + = msg.GetBearerContextsToBeModified (); + NS_LOG_DEBUG ("BearerContextsToBeModified size = " << bearerContexts.size ()); + for (auto &bearerContext : bearerContexts) + { + NS_ASSERT_MSG (bearerContext.fteid.interfaceType == GtpcHeader::S1U_ENB_GTPU, + "Wrong FTEID in ModifyBearerRequest msg"); + uint32_t teid = bearerContext.fteid.teid; + Ipv4Address enbAddr = bearerContext.fteid.addr; + NS_LOG_DEBUG ("bearerId " << (uint16_t)bearerContext.epsBearerId << + " TEID " << teid); + std::map::iterator addrit = m_enbByTeidMap.find (teid); + NS_ASSERT_MSG (addrit != m_enbByTeidMap.end (), "unknown TEID " << teid); + addrit->second = enbAddr; + GtpcModifyBearerRequestMessage::BearerContextToBeModified bearerContextOut; + bearerContextOut.epsBearerId = bearerContext.epsBearerId; + bearerContextOut.fteid.interfaceType = GtpcHeader::S5_SGW_GTPU; + bearerContextOut.fteid.addr = m_s5Addr; + bearerContextOut.fteid.teid = bearerContext.fteid.teid; + + bearerContextsOut.push_back (bearerContextOut); + } + + msgOut.SetTeid (imsi); + msgOut.ComputeMessageLength (); + + Ptr packetOut = Create (); + packetOut->AddHeader (msgOut); + NS_LOG_DEBUG ("Send ModifyBearerRequest to PGW " << m_pgwAddr); + m_s5cSocket->SendTo (packetOut, 0, InetSocketAddress (m_pgwAddr, m_gtpcUdpPort)); +} + +void +EpcSgwApplication::DoRecvDeleteBearerCommand (Ptr packet) +{ + NS_LOG_FUNCTION (this); + + GtpcDeleteBearerCommandMessage msg; + packet->RemoveHeader (msg); + + std::list bearerContextsOut; + for (auto &bearerContext : msg.GetBearerContexts ()) + { + NS_LOG_DEBUG ("ebid " << bearerContext.m_epsBearerId); + GtpcDeleteBearerCommandMessage::BearerContext bearerContextOut; + bearerContextOut.m_epsBearerId = bearerContext.m_epsBearerId; + bearerContextsOut.push_back (bearerContextOut); + } + + GtpcDeleteBearerCommandMessage msgOut; + msgOut.SetBearerContexts (bearerContextsOut); + msgOut.SetTeid (msg.GetTeid ()); + msgOut.ComputeMessageLength (); + + Ptr packetOut = Create (); + packetOut->AddHeader (msgOut); + NS_LOG_DEBUG ("Send DeleteBearerCommand to PGW " << m_pgwAddr); + m_s11Socket->SendTo (packetOut, 0, InetSocketAddress (m_pgwAddr, m_gtpcUdpPort)); +} + +void +EpcSgwApplication::DoRecvDeleteBearerResponse (Ptr packet) +{ + NS_LOG_FUNCTION (this); + + GtpcDeleteBearerResponseMessage msg; + packet->RemoveHeader (msg); + GtpcDeleteBearerResponseMessage msgOut; + msgOut.SetEpsBearerIds (msg.GetEpsBearerIds ()); + msgOut.SetTeid (msg.GetTeid ()); + msgOut.ComputeMessageLength (); + + Ptr packetOut = Create (); + packetOut->AddHeader (msgOut); + NS_LOG_DEBUG ("Send DeleteBearerResponse to PGW " << m_pgwAddr); + m_s5cSocket->SendTo (packetOut, 0, InetSocketAddress (m_pgwAddr, m_gtpcUdpPort)); +} + + +//////////////////////////////////////////// +// Process messages received from the PGW +//////////////////////////////////////////// + +void +EpcSgwApplication::DoRecvCreateSessionResponse (Ptr packet) +{ + NS_LOG_FUNCTION (this); + + GtpcCreateSessionResponseMessage msg; + packet->RemoveHeader (msg); + + GtpcHeader::Fteid_t pgwS5cFteid = msg.GetSenderCpFteid (); + NS_ASSERT_MSG (pgwS5cFteid.interfaceType == GtpcHeader::S5_PGW_GTPC, + "wrong interface type"); + + GtpcCreateSessionResponseMessage msgOut; + msgOut.SetCause (GtpcCreateSessionResponseMessage::REQUEST_ACCEPTED); + + uint32_t teid = msg.GetTeid (); + GtpcHeader::Fteid_t mmeS11Fteid = m_mmeS11FteidBySgwS5cTeid[teid]; + + std::list bearerContexts = + msg.GetBearerContextsCreated (); + NS_LOG_DEBUG ("BearerContextsCreated size = " << bearerContexts.size ()); + std::list bearerContextsOut; + for (auto &bearerContext : bearerContexts) + { + GtpcCreateSessionResponseMessage::BearerContextCreated bearerContextOut; + bearerContextOut.fteid.interfaceType = GtpcHeader::S5_SGW_GTPU; + bearerContextOut.fteid.teid = bearerContext.fteid.teid; + bearerContextOut.fteid.addr = m_s5Addr; + bearerContextOut.epsBearerId = bearerContext.epsBearerId; + bearerContextOut.bearerLevelQos = bearerContext.bearerLevelQos; + bearerContextOut.tft = bearerContext.tft; + bearerContextsOut.push_back (bearerContext); + } + msgOut.SetBearerContextsCreated (bearerContextsOut); + + msgOut.SetTeid (mmeS11Fteid.teid); + msgOut.ComputeMessageLength (); + + Ptr packetOut = Create (); + packetOut->AddHeader (msgOut); + NS_LOG_DEBUG ("Send CreateSessionResponse to MME " << mmeS11Fteid.addr); + m_s11Socket->SendTo (packetOut, 0, InetSocketAddress (mmeS11Fteid.addr, m_gtpcUdpPort)); +} + +void +EpcSgwApplication::DoRecvModifyBearerResponse (Ptr packet) +{ + NS_LOG_FUNCTION (this); + + GtpcModifyBearerResponseMessage msg; + packet->RemoveHeader (msg); + + GtpcModifyBearerResponseMessage msgOut; + msgOut.SetCause (GtpcIes::REQUEST_ACCEPTED); + msgOut.SetTeid (msg.GetTeid ()); + msgOut.ComputeMessageLength (); + + Ptr packetOut = Create (); + packetOut->AddHeader (msgOut); + NS_LOG_DEBUG ("Send ModifyBearerResponse to MME " << m_mmeS11Addr); + m_s11Socket->SendTo (packetOut, 0, InetSocketAddress (m_mmeS11Addr, m_gtpcUdpPort)); +} + +void +EpcSgwApplication::DoRecvDeleteBearerRequest (Ptr packet) +{ + NS_LOG_FUNCTION (this); + + GtpcDeleteBearerRequestMessage msg; + packet->RemoveHeader (msg); + + GtpcDeleteBearerRequestMessage msgOut; + msgOut.SetEpsBearerIds (msg.GetEpsBearerIds ()); + msgOut.SetTeid (msg.GetTeid ()); + msgOut.ComputeMessageLength (); + + Ptr packetOut = Create (); + packetOut->AddHeader (msgOut); + NS_LOG_DEBUG ("Send DeleteBearerRequest to MME " << m_mmeS11Addr); + m_s11Socket->SendTo (packetOut, 0, InetSocketAddress (m_mmeS11Addr, m_gtpcUdpPort)); +} + +} // namespace ns3 diff --git a/src/lte/model/epc-sgw-application.h b/src/lte/model/epc-sgw-application.h new file mode 100644 index 000000000..7905e07b9 --- /dev/null +++ b/src/lte/model/epc-sgw-application.h @@ -0,0 +1,277 @@ +/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2017-2018 Centre Tecnologic de Telecomunicacions de Catalunya (CTTC) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Manuel Requena + */ + +#ifndef EPC_SGW_APPLICATION_H +#define EPC_SGW_APPLICATION_H + +#include "ns3/application.h" +#include "ns3/address.h" +#include "ns3/socket.h" +#include "ns3/epc-gtpc-header.h" + +namespace ns3 { + +/** + * \ingroup lte + * + * This application implements the Serving Gateway Entity (SGW) + * according to the 3GPP TS 23.401 document. + * + * This Application implements the SGW side of the S5 interface between + * the SGW node and the PGW node and the SGW side of the S11 interface between + * the SGW node and the MME node hosts. It supports the following functions and messages: + * + * - S5 connectivity (i.e. GTPv2-C signalling and GTP-U data plane) + * - Bearer management functions including dedicated bearer establishment + * - UL and DL bearer binding + * - Tunnel Management messages + * + * Others functions enumerated in section 4.4.3.2 of 3GPP TS 23.401 are not supported. + */ +class EpcSgwApplication : public Application +{ +public: + + /** + * \brief Get the type ID. + * \return the object TypeId + */ + static TypeId GetTypeId (void); + virtual void DoDispose (); + + /** + * Constructor that binds callback methods of sockets. + * + * \param s1uSocket socket used to send/receive GTP-U packets to/from the eNBs + * \param s5Addr IPv4 address of the S5 interface + * \param s5uSocket socket used to send/receive GTP-U packets to/from the PGW + * \param s5cSocket socket used to send/receive GTP-C packets to/from the PGW + */ + EpcSgwApplication (const Ptr s1uSocket, Ipv4Address s5Addr, + const Ptr s5uSocket, const Ptr s5cSocket); + + /** Destructor */ + virtual ~EpcSgwApplication (void); + + + /** + * Let the SGW be aware of an MME + * + * \param mmeS11Addr the address of the MME + * \param s11Socket the socket to send/receive messages from the MME + */ + void AddMme (Ipv4Address mmeS11Addr, Ptr s11Socket); + + /** + * Let the SGW be aware of a PGW + * + * \param pgwAddr the address of the PGW + */ + void AddPgw (Ipv4Address pgwAddr); + + /** + * Let the SGW be aware of a new eNB + * + * \param cellId the cell identifier + * \param enbAddr the address of the eNB + * \param sgwAddr the address of the SGW + */ + void AddEnb (uint16_t cellId, Ipv4Address enbAddr, Ipv4Address sgwAddr); + + +private: + /** + * Method to be assigned to the recv callback of the S11 socket. + * It is called when the SGW receives a control packet from the MME. + * + * \param socket pointer to the S11 socket + */ + void RecvFromS11Socket (Ptr socket); + + /** + * Method to be assigned to the recv callback of the S5-U socket. + * It is called when the SGW receives a data packet from the PGW + * that is to be forwarded to an eNB. + * + * \param socket pointer to the S5-U socket + */ + void RecvFromS5uSocket (Ptr socket); + + /** + * Method to be assigned to the recv callback of the S5-C socket. + * It is called when the SGW receives a control packet from the PGW. + * + * \param socket pointer to the S5-C socket + */ + void RecvFromS5cSocket (Ptr socket); + + /** + * Method to be assigned to the recv callback of the S1-U socket. + * It is called when the SGW receives a data packet from the eNB + * that is to be forwarded to the PGW. + * + * \param socket pointer to the S1-U socket + */ + void RecvFromS1uSocket (Ptr socket); + + /** + * Send a data packet to the PGW via the S5 interface + * + * \param packet packet to be sent + * \param pgwAddr the address of the PGW + * \param teid the Tunnel Enpoint Identifier + */ + void SendToS5uSocket (Ptr packet, Ipv4Address pgwAddr, uint32_t teid); + + /** + * Send a data packet to an eNB via the S1-U interface + * + * \param packet packet to be sent + * \param enbS1uAddress the address of the eNB + * \param teid the Tunnel Enpoint IDentifier + */ + void SendToS1uSocket (Ptr packet, Ipv4Address enbS1uAddress, uint32_t teid); + + + // Process messages received from the MME + + /** + * Process GTP-C Create Session Request message + * \param packet the packet containing the message + */ + void DoRecvCreateSessionRequest (Ptr packet); + + /** + * Process GTP-C Modify Bearer Request message + * \param packet the packet containing the message + */ + void DoRecvModifyBearerRequest (Ptr packet); + + /** + * Process GTP-C Delete Bearer Command message + * \param packet the packet containing the message + */ + void DoRecvDeleteBearerCommand (Ptr packet); + + /** + * Process GTP-C Delete Bearer Response message + * \param packet the packet containing the message + */ + void DoRecvDeleteBearerResponse (Ptr packet); + + + // Process messages received from the PGW + + /** + * Process GTP-C Create Session Response message + * \param packet the packet containing the message + */ + void DoRecvCreateSessionResponse (Ptr packet); + + /** + * Process GTP-C Modify Bearer Response message + * \param packet the packet containing the message + */ + void DoRecvModifyBearerResponse (Ptr packet); + + /** + * Process GTP-C Delete Bearer Request message + * \param packet the packet containing the message + */ + void DoRecvDeleteBearerRequest (Ptr packet); + + + /** + * SGW address in the S5 interface + */ + Ipv4Address m_s5Addr; + + /** + * MME address in the S11 interface + */ + Ipv4Address m_mmeS11Addr; + + /** + * UDP socket to send/receive control messages to/from the S11 interface + */ + Ptr m_s11Socket; + + /** + * PGW address in the S5 interface + */ + Ipv4Address m_pgwAddr; + + /** + * UDP socket to send/receive GTP-U packets to/from the S5 interface + */ + Ptr m_s5uSocket; + + /** + * UDP socket to send/receive GTP-C packets to/from the S5 interface + */ + Ptr m_s5cSocket; + + /** + * UDP socket to send/receive GTP-U packets to/from the S1-U interface + */ + Ptr m_s1uSocket; + + /** + * UDP port to be used for GTP-U + */ + uint16_t m_gtpuUdpPort; + + /** + * UDP port to be used for GTP-C + */ + uint16_t m_gtpcUdpPort; + + /** + * TEID count + */ + uint32_t m_teidCount; + + + /// EnbInfo structure + struct EnbInfo + { + Ipv4Address enbAddr; ///< eNB address + Ipv4Address sgwAddr; ///< SGW address + }; + + /** + * Map for eNB info by cell ID + */ + std::map m_enbInfoByCellId; + + /** + * Map for eNB address by TEID + */ + std::map m_enbByTeidMap; + + /** + * MME S11 FTEID by SGW S5C TEID + */ + std::map m_mmeS11FteidBySgwS5cTeid; +}; + +} // namespace ns3 + +#endif // EPC_SGW_APPLICATION_H diff --git a/src/lte/model/epc-tft.cc b/src/lte/model/epc-tft.cc index 1d0503e68..fc40b17f8 100644 --- a/src/lte/model/epc-tft.cc +++ b/src/lte/model/epc-tft.cc @@ -295,5 +295,11 @@ EpcTft::Matches (Direction direction, return false; } +std::list +EpcTft::GetPacketFilters () const +{ + NS_LOG_FUNCTION (this); + return m_filters; +}; } // namespace ns3 diff --git a/src/lte/model/epc-tft.h b/src/lte/model/epc-tft.h index 5a342688b..d8f0c9017 100644 --- a/src/lte/model/epc-tft.h +++ b/src/lte/model/epc-tft.h @@ -195,6 +195,8 @@ public: uint8_t typeOfService); + std::list GetPacketFilters () const; + private: std::list m_filters; ///< packet filter list diff --git a/src/lte/model/fdtbfq-ff-mac-scheduler.cc b/src/lte/model/fdtbfq-ff-mac-scheduler.cc index 77d364b27..6543678de 100644 --- a/src/lte/model/fdtbfq-ff-mac-scheduler.cc +++ b/src/lte/model/fdtbfq-ff-mac-scheduler.cc @@ -636,7 +636,7 @@ FdTbfqFfMacScheduler::DoSchedDlTriggerReq (const struct FfMacSchedSapProvider::S newRar.m_grant.m_tpc = 0; newRar.m_grant.m_cqiRequest = false; newRar.m_grant.m_ulDelay = false; - NS_LOG_INFO (this << " UL grant allocated to RNTI " << (*itRach).m_rnti << " rbStart " << rbStart << " rbLen " << rbLen << " MCS " << m_ulGrantMcs << " tbSize " << newRar.m_grant.m_tbSize); + NS_LOG_INFO (this << " UL grant allocated to RNTI " << (*itRach).m_rnti << " rbStart " << rbStart << " rbLen " << rbLen << " MCS " << (uint16_t) m_ulGrantMcs << " tbSize " << newRar.m_grant.m_tbSize); for (uint16_t i = rbStart; i < rbStart + rbLen; i++) { m_rachAllocationMap.at (i) = (*itRach).m_rnti; @@ -1385,7 +1385,7 @@ FdTbfqFfMacScheduler::DoSchedDlTriggerReq (const struct FfMacSchedSapProvider::S newDci.m_mcs.push_back (m_amc->GetMcsFromCqi (worstCqi.at (j))); int tbSize = (m_amc->GetDlTbSizeFromMcs (newDci.m_mcs.at (j), RgbPerRnti * rbgSize) / 8); // (size of TB in bytes according to table 7.1.7.2.1-1 of 36.213) newDci.m_tbsSize.push_back (tbSize); - NS_LOG_INFO (this << " Layer " << (uint16_t)j << " MCS selected" << m_amc->GetMcsFromCqi (worstCqi.at (j))); + NS_LOG_INFO (this << " Layer " << (uint16_t)j << " MCS selected" << (uint16_t) m_amc->GetMcsFromCqi (worstCqi.at (j))); bytesTxed += tbSize; } diff --git a/src/lte/model/lte-enb-rrc.cc b/src/lte/model/lte-enb-rrc.cc index d5029986e..83939d0ad 100644 --- a/src/lte/model/lte-enb-rrc.cc +++ b/src/lte/model/lte-enb-rrc.cc @@ -114,6 +114,7 @@ static const std::string g_ueManagerStateName[UeManager::NUM_STATES] = "INITIAL_RANDOM_ACCESS", "CONNECTION_SETUP", "CONNECTION_REJECTED", + "ATTACH_REQUEST", "CONNECTED_NORMALLY", "CONNECTION_RECONFIGURATION", "CONNECTION_REESTABLISHMENT", @@ -362,6 +363,21 @@ UeManager::SetImsi (uint64_t imsi) m_imsi = imsi; } +void +UeManager::InitialContextSetupRequest () +{ + NS_LOG_FUNCTION (this << m_rnti); + + if (m_state == ATTACH_REQUEST) + { + SwitchToState (CONNECTED_NORMALLY); + } + else + { + NS_FATAL_ERROR ("method unexpected in state " << ToString (m_state)); + } +} + void UeManager::SetupDataRadioBearer (EpsBearer bearer, uint8_t bearerId, uint32_t gtpTeid, Ipv4Address transportLayerAddress) { @@ -554,6 +570,7 @@ UeManager::ScheduleRrcConnectionReconfiguration () { case INITIAL_RANDOM_ACCESS: case CONNECTION_SETUP: + case ATTACH_REQUEST: case CONNECTION_RECONFIGURATION: case CONNECTION_REESTABLISHMENT: case HANDOVER_PREPARATION: @@ -878,10 +895,6 @@ UeManager::RecvRrcConnectionRequest (LteRrcSap::RrcConnectionRequest msg) if (m_rrc->m_admitRrcConnectionRequest == true) { m_imsi = msg.ueIdentity; - if (m_rrc->m_s1SapProvider != 0) - { - m_rrc->m_s1SapProvider->InitialUeMessage (m_imsi, m_rnti); - } // send RRC CONNECTION SETUP to UE LteRrcSap::RrcConnectionSetup msg2; @@ -931,12 +944,16 @@ UeManager::RecvRrcConnectionSetupCompleted (LteRrcSap::RrcConnectionSetupComplet m_pendingRrcConnectionReconfiguration = true; // Force Reconfiguration m_pendingStartDataRadioBearers = true; } + + if (m_rrc->m_s1SapProvider != 0) + { + m_rrc->m_s1SapProvider->InitialUeMessage (m_imsi, m_rnti); + SwitchToState (ATTACH_REQUEST); + } else { - m_pendingStartDataRadioBearers = false; - StartDataRadioBearers (); + SwitchToState (CONNECTED_NORMALLY); } - SwitchToState (CONNECTED_NORMALLY); m_rrc->m_connectionEstablishedTrace (m_imsi, m_rrc->ComponentCarrierToCellId (m_componentCarrierId), m_rnti); break; @@ -1291,6 +1308,7 @@ UeManager::BuildRrcConnectionReconfiguration () LteRrcSap::RadioResourceConfigDedicated UeManager::BuildRadioResourceConfigDedicated () { + NS_LOG_FUNCTION (this); LteRrcSap::RadioResourceConfigDedicated rrcd; if (m_srb1 != 0) @@ -1322,6 +1340,7 @@ UeManager::BuildRadioResourceConfigDedicated () uint8_t UeManager::GetNewRrcTransactionIdentifier () { + NS_LOG_FUNCTION (this); ++m_lastRrcTransactionIdentifier; m_lastRrcTransactionIdentifier %= 4; return m_lastRrcTransactionIdentifier; @@ -1385,6 +1404,9 @@ UeManager::SwitchToState (State newState) case CONNECTION_SETUP: break; + case ATTACH_REQUEST: + break; + case CONNECTED_NORMALLY: { if (m_pendingRrcConnectionReconfiguration == true) @@ -2272,9 +2294,18 @@ LteEnbRrc::DoRecvMeasurementReport (uint16_t rnti, LteRrcSap::MeasurementReport GetUeManager (rnti)->RecvMeasurementReport (msg); } +void +LteEnbRrc::DoInitialContextSetupRequest (EpcEnbS1SapUser::InitialContextSetupRequestParameters msg) +{ + NS_LOG_FUNCTION (this); + Ptr ueManager = GetUeManager (msg.rnti); + ueManager->InitialContextSetupRequest (); +} + void LteEnbRrc::DoDataRadioBearerSetupRequest (EpcEnbS1SapUser::DataRadioBearerSetupRequestParameters request) { + NS_LOG_FUNCTION (this); Ptr ueManager = GetUeManager (request.rnti); ueManager->SetupDataRadioBearer (request.bearer, request.bearerId, request.gtpTeid, request.transportLayerAddress); } @@ -2282,6 +2313,7 @@ LteEnbRrc::DoDataRadioBearerSetupRequest (EpcEnbS1SapUser::DataRadioBearerSetupR void LteEnbRrc::DoPathSwitchRequestAcknowledge (EpcEnbS1SapUser::PathSwitchRequestAcknowledgeParameters params) { + NS_LOG_FUNCTION (this); Ptr ueManager = GetUeManager (params.rnti); ueManager->SendUeContextRelease (); } diff --git a/src/lte/model/lte-enb-rrc.h b/src/lte/model/lte-enb-rrc.h index ab0b46abc..fee531f52 100644 --- a/src/lte/model/lte-enb-rrc.h +++ b/src/lte/model/lte-enb-rrc.h @@ -86,6 +86,7 @@ public: INITIAL_RANDOM_ACCESS = 0, CONNECTION_SETUP, CONNECTION_REJECTED, + ATTACH_REQUEST, CONNECTED_NORMALLY, CONNECTION_RECONFIGURATION, CONNECTION_REESTABLISHMENT, @@ -139,6 +140,12 @@ public: */ void SetImsi (uint64_t imsi); + /** + * Process Initial context setup request message from the MME. + * It triggers RRC connection reconfiguration. + */ + void InitialContextSetupRequest (); + /** * Setup a new data radio bearer, including both the configuration * within the eNB and the necessary RRC signaling with the UE @@ -1086,6 +1093,12 @@ private: // S1 SAP methods + /** + * Initial context setup request function + * + * \param params EpcEnbS1SapUser::InitialContextSetupRequestParameters + */ + void DoInitialContextSetupRequest (EpcEnbS1SapUser::InitialContextSetupRequestParameters params); /** * Data radio beaerer setup request function * diff --git a/src/lte/model/pss-ff-mac-scheduler.cc b/src/lte/model/pss-ff-mac-scheduler.cc index bffb2582c..e49aa16fd 100644 --- a/src/lte/model/pss-ff-mac-scheduler.cc +++ b/src/lte/model/pss-ff-mac-scheduler.cc @@ -620,7 +620,7 @@ PssFfMacScheduler::DoSchedDlTriggerReq (const struct FfMacSchedSapProvider::Sche newRar.m_grant.m_tpc = 0; newRar.m_grant.m_cqiRequest = false; newRar.m_grant.m_ulDelay = false; - NS_LOG_INFO (this << " UL grant allocated to RNTI " << (*itRach).m_rnti << " rbStart " << rbStart << " rbLen " << rbLen << " MCS " << m_ulGrantMcs << " tbSize " << newRar.m_grant.m_tbSize); + NS_LOG_INFO (this << " UL grant allocated to RNTI " << (*itRach).m_rnti << " rbStart " << rbStart << " rbLen " << rbLen << " MCS " << (uint16_t)m_ulGrantMcs << " tbSize " << newRar.m_grant.m_tbSize); for (uint16_t i = rbStart; i < rbStart + rbLen; i++) { m_rachAllocationMap.at (i) = (*itRach).m_rnti; diff --git a/src/lte/test/epc-test-s1u-downlink.cc b/src/lte/test/epc-test-s1u-downlink.cc index d0c2287bf..5bb2b3465 100644 --- a/src/lte/test/epc-test-s1u-downlink.cc +++ b/src/lte/test/epc-test-s1u-downlink.cc @@ -160,6 +160,7 @@ EpcS1uDlTestCase::DoRun () NodeContainer enbs; uint16_t cellIdCounter = 0; + uint64_t imsiCounter = 0; for (std::vector::iterator enbit = m_enbDlTestData.begin (); enbit < m_enbDlTestData.end (); @@ -194,6 +195,7 @@ EpcS1uDlTestCase::DoRun () Ptr enbApp = enb->GetApplication (0)->GetObject (); NS_ASSERT_MSG (enbApp != 0, "cannot retrieve EpcEnbApplication"); Ptr rrc = CreateObject (); + enb->AggregateObject (rrc); rrc->SetS1SapProvider (enbApp->GetS1SapProvider ()); enbApp->SetS1SapUser (rrc->GetS1SapUser ()); @@ -231,10 +233,12 @@ EpcS1uDlTestCase::DoRun () apps.Stop (Seconds (10.0)); enbit->ues[u].clientApp = apps.Get (0); - uint64_t imsi = u+1; + uint64_t imsi = ++imsiCounter; epcHelper->AddUe (ueLteDevice, imsi); epcHelper->ActivateEpsBearer (ueLteDevice, imsi, EpcTft::Default (), EpsBearer (EpsBearer::NGBR_VIDEO_TCP_DEFAULT)); - enbApp->GetS1SapProvider ()->InitialUeMessage (imsi, (uint16_t) imsi); + Simulator::Schedule (MilliSeconds (10), + &EpcEnbS1SapProvider::InitialUeMessage, + enbApp->GetS1SapProvider (), imsi, (uint16_t) imsi); } } diff --git a/src/lte/test/epc-test-s1u-uplink.cc b/src/lte/test/epc-test-s1u-uplink.cc index a59862efa..f04fcbbed 100644 --- a/src/lte/test/epc-test-s1u-uplink.cc +++ b/src/lte/test/epc-test-s1u-uplink.cc @@ -378,6 +378,7 @@ EpcS1uUlTestCase::DoRun () NodeContainer enbs; uint16_t cellIdCounter = 0; + uint64_t imsiCounter = 0; for (std::vector::iterator enbit = m_enbUlTestData.begin (); enbit < m_enbUlTestData.end (); @@ -412,6 +413,7 @@ EpcS1uUlTestCase::DoRun () Ptr enbApp = enb->GetApplication (0)->GetObject (); NS_ASSERT_MSG (enbApp != 0, "cannot retrieve EpcEnbApplication"); Ptr rrc = CreateObject (); + enb->AggregateObject (rrc); rrc->SetS1SapProvider (enbApp->GetS1SapProvider ()); enbApp->SetS1SapUser (rrc->GetS1SapUser ()); @@ -475,11 +477,12 @@ EpcS1uUlTestCase::DoRun () clientApp.Stop (Seconds (10.0)); enbit->ues[u].clientApp = client; - uint64_t imsi = u+1; + uint64_t imsi = ++imsiCounter; epcHelper->AddUe (ueLteDevice, imsi); epcHelper->ActivateEpsBearer (ueLteDevice, imsi, EpcTft::Default (), EpsBearer (EpsBearer::NGBR_VIDEO_TCP_DEFAULT)); - enbApp->GetS1SapProvider ()->InitialUeMessage (imsi, (uint16_t) imsi); - + Simulator::Schedule (MilliSeconds (10), + &EpcEnbS1SapProvider::InitialUeMessage, + enbApp->GetS1SapProvider (), imsi, enbit->ues[u].rnti); // need this since all sinks are installed in the same node ++udpSinkPort; } diff --git a/src/lte/test/lte-test-entities.cc b/src/lte/test/lte-test-entities.cc index 5082d6260..c2849e2aa 100644 --- a/src/lte/test/lte-test-entities.cc +++ b/src/lte/test/lte-test-entities.cc @@ -725,6 +725,12 @@ EpcTestRrc::GetS1SapUser () return m_s1SapUser; } +void +EpcTestRrc::DoInitialContextSetupRequest (EpcEnbS1SapUser::InitialContextSetupRequestParameters request) +{ + +} + void EpcTestRrc::DoDataRadioBearerSetupRequest (EpcEnbS1SapUser::DataRadioBearerSetupRequestParameters request) { diff --git a/src/lte/test/lte-test-entities.h b/src/lte/test/lte-test-entities.h index 7adb7aafb..6a26e32c8 100644 --- a/src/lte/test/lte-test-entities.h +++ b/src/lte/test/lte-test-entities.h @@ -446,6 +446,11 @@ public: private: // S1 SAP methods + /** + * Initial context setup request + * \param params EpcEnbS1SapUser::InitialContextSetupRequestParameters + */ + void DoInitialContextSetupRequest (EpcEnbS1SapUser::InitialContextSetupRequestParameters params); /** * Data radio bearer setup request * \param params EpcEnbS1SapUser::DataRadioBearerSetupRequestParameters diff --git a/src/lte/test/lte-test-ue-measurements.cc b/src/lte/test/lte-test-ue-measurements.cc index 918db5cda..02ce2d30a 100644 --- a/src/lte/test/lte-test-ue-measurements.cc +++ b/src/lte/test/lte-test-ue-measurements.cc @@ -1801,12 +1801,12 @@ LteUeMeasurementsHandoverTestCase::DoRun () lteHelper->AddX2Interface (enbNodes); // Connect to trace sources in source eNodeB - Config::Connect ("/NodeList/1/DeviceList/0/LteEnbRrc/RecvMeasurementReport", + Config::Connect ("/NodeList/3/DeviceList/0/LteEnbRrc/RecvMeasurementReport", MakeCallback (&LteUeMeasurementsHandoverTestCase::RecvMeasurementReportCallback, this)); // Connect to trace sources in target eNodeB - Config::Connect ("/NodeList/2/DeviceList/0/LteEnbRrc/RecvMeasurementReport", + Config::Connect ("/NodeList/4/DeviceList/0/LteEnbRrc/RecvMeasurementReport", MakeCallback (&LteUeMeasurementsHandoverTestCase::RecvMeasurementReportCallback, this)); diff --git a/src/lte/wscript b/src/lte/wscript index c3f3e229b..dcf85dbfc 100644 --- a/src/lte/wscript +++ b/src/lte/wscript @@ -84,9 +84,13 @@ def build(bld): 'model/pss-ff-mac-scheduler.cc', 'model/cqa-ff-mac-scheduler.cc', 'model/epc-gtpu-header.cc', + 'model/epc-gtpc-header.cc', 'model/trace-fading-loss-model.cc', 'model/epc-enb-application.cc', 'model/epc-sgw-pgw-application.cc', + 'model/epc-sgw-application.cc', + 'model/epc-pgw-application.cc', + 'model/epc-mme-application.cc', 'model/epc-x2-sap.cc', 'model/epc-x2-header.cc', 'model/epc-x2.cc', @@ -276,8 +280,12 @@ def build(bld): 'model/cqa-ff-mac-scheduler.h', 'model/trace-fading-loss-model.h', 'model/epc-gtpu-header.h', + 'model/epc-gtpc-header.h', 'model/epc-enb-application.h', 'model/epc-sgw-pgw-application.h', + 'model/epc-sgw-application.h', + 'model/epc-pgw-application.h', + 'model/epc-mme-application.h', 'model/lte-vendor-specific-parameters.h', 'model/epc-x2-sap.h', 'model/epc-x2-header.h',