Compare commits

...

98 Commits

Author SHA1 Message Date
Pau Espin Pedrol
1fd205f0b8 ranap: Take into account RNC availability during paging
Avoid transmitting a RANAP paging message to an RNC if we already know
it's not currently available over SCCP.
Take into account that information when deciding/printing whether the
paging could be sent or not.

Take the chance to clean up the iu paging function helpers inherited
from osmo-iuh iu_client.c to better fit the data domain in osmo-sgsn
(iu_rnc).

Change-Id: I24e5446bcf4c958028577230b231960acea9e5b9
2025-09-02 18:55:03 +02:00
Pau Espin Pedrol
b062c3647e stats: Introduce stats sgsn.iu_peers.{total,active}
Change-Id: I51b5227d92027f1251dc4debbbf59737e7c1a9ba
2025-09-02 18:55:03 +02:00
Pau Espin Pedrol
d6c29beaf3 Introduce iu_rnc FSM
This FSM is similar to the already existing ran_peer_fsm in osmo-msc,
which already had better logic around SCCP and RANAP state handling.
Similarly, osmo-sgsn's struct ranap_iu_rnc maps to osmo-msc's struct ran_peer.

With this FSM we can currently track the RANAP link state towards a given
RNC peer:
* Reject (RANAP Error Indication) all UE-related messages until a RANAP
  RESET from RNC is received first.
* Tear down all subsriber connections whenever the RANAP peer sends us a
  RESET message.
* Tear down all subscriber connections whenever the SCCP link towards
  RNC becomes unavailable.
* Send a RESET towards RNC peer once the SCCP link towrdards it becomes
  available again.

This commit only implements so far the Rx path of the FSM, ie. when
receiving events/messages from a peer over SCCP and pushing them locally
up the stack (RANAP). The Tx side will be implemented in a follow-up
commit, which will allow discarding messages if the lower layers towards
a given RNC are known to be down.

Related: OS#3403
Change-Id: I18b7803500163e78ff6a684095194174b0fb6ee1
2025-09-02 18:55:03 +02:00
Pau Espin Pedrol
a24ebc7051 iu_rnc: Introduce helper API iu_rnc_discard_all_ue_ctx()
This is a preparation commit to introduce iu_rnc_fsm in follow-up patch.
The helper API will be used whenever the entire RNC is considered reset.

* Code inside handle_notice_ind() is moved to its own function
  iu_rnc_discard_all_ue_ctx().
* Dependent helper ue_ctx_link_invalidated_free() API is properly
  prefixed/renamed to ue_conn_ctx_link_invalidated_free(), where methods
  for ue_conn_ctx object are placed.
* ue_conn_ctx_find() is properly prefixed/renamed to
  sgsn_scu_iups_ue_conn_ctx_find() and made available to functions in
  other files.

Change-Id: Ie3a4732a85a69d115dd5756bfa64b296e9e5edd2
2025-09-02 18:55:02 +02:00
Pau Espin Pedrol
611b32811e Store scu_iups in iu_rnc instead of ue_ctx
All the ue_ctx belong to a given peer RNC, which holds the SCCP User to
talk to it.

Change-Id: I3969af765b7b0d1375b5e6ad8f3f2e9845f939ef
2025-09-02 18:53:17 +02:00
Pau Espin Pedrol
7673bbd7ad ranap: Create iu_rnc upon rx RESET
RANAP RESET, the first message an SGSN usually receives from an RNC,
contain the GlobalRNC-ID we can already use to register the iu_rnc
object.
This allows keeping track of peers since first message, before first
InitialUE message is received.
Once we start tracking peer RNC state within osmo-sgsn (as we already do
in other osmo-* programs), this will be needed.

Change-Id: Iaf31271feb4d88881382ed8594d0d8e20e22b194
2025-09-02 18:51:43 +02:00
Pau Espin Pedrol
b9c9271a1f ranap: Improve error handling in Rx RESET
* Verify the CN Indicator is set to PS to avoid reacting on content from
  unexpected peers (eg. IuCS). If none is set, be permissive on rx and
  assume PS.

* If GlobalRNC-ID IE is missing, reject the RESET with an Error
  Indication towards the peer to notify about the rejection.
  Same if we couldn't decode properly it.

* Introduce Tx helpers for generic SCCP UNITDATA.req and RANAP Error
  Indication helpers to simplify code and avoid duplication.

Depends: osmo-iuh.git Change-Id I58c75b3e0e0f33f48d077385ffac820a6b2be59e
Change-Id: I2a53e10c3903992a42d081e3ec300fbdb9b3c34c
2025-09-02 18:51:43 +02:00
Pau Espin Pedrol
dfcb514076 iu_rnc: Split iu_rnc_register API into 2 steps
Split lookup+allocation from RAI updating.
This will be needed once we start creating iu_rnc objects upon SCCP
rx RANAP RESET messages, which hold Global RNC-ID but no RAI
information.

Change-Id: I3761cc539c63a7ed680b04bb8136a43d397c10aa
2025-09-02 18:51:43 +02:00
Pau Espin Pedrol
009c25f968 ranap: Reject InitialUE without RAC IE
According to 3GPP TS 25.413 clauses 8.22.2 and 9.1.33,
RAC IE is mandatory in PS domain.

Change-Id: I7d6b996ddf1c5a7cde1bf06b500d3ed19c6090c6
2025-09-02 18:51:43 +02:00
Pau Espin Pedrol
202039ed02 iu_rnc: Use API osmo_sccp_addr_ri_cmp() to compare addresses
Change-Id: I04fb37f28b52da9eecb1f8ea40c38ee3eced1c29
2025-09-02 18:51:43 +02:00
Pau Espin Pedrol
ecaa198fa0 Apply uniform prefix to remaining APIs in gprs_ranap.h
Change-Id: I4f253f35013085bdebf80e94ab0d2f771e40dba6
2025-09-02 18:51:43 +02:00
Pau Espin Pedrol
eb5959f6b9 Split Iu RANAP conn release over different layers
Move and rename functions acting on mmctx to mmctx.{c,h}.
From there, call functions acting on ranap ue_ctx.

Change-Id: I4acdbc857df36b7409c8210d364758192dfdb0bb
2025-09-02 18:51:43 +02:00
Pau Espin Pedrol
c1f2de5790 Split Iu RANAP RAB PS act/deact over different layers
Properly separate logic acting on objects vs logic building and
generating RANAP msg on the wire.

Change-Id: I98788468b7e50619cfd99b3aef2311bb601fb6be
2025-09-02 18:51:43 +02:00
Pau Espin Pedrol
a0203ce7ff ranap: Clean up RANAP rx path param passing
Use the osmo-iuh ranap_cn_rx_*_decode2() APIs directly. This
allows getting rid of extra "ctx" temporary structs and another level of
cb indirection.

Take the chance to update most pointers consitfying them.

Depends: osmo-iuh.git Change-Id I667dc2ef377c1ceb5b11315458f00b282c143c81
Change-Id: Ib3c00b5105b7ba4bb1a2dad1a14bea233a03c358
2025-09-02 18:51:43 +02:00
Pau Espin Pedrol
20d032bdd2 iu_rnc: Constify function params
Change-Id: I288bc21d24aa5abd3d9bd97e796ac9f8590290bd
2025-09-02 18:51:43 +02:00
Pau Espin Pedrol
203676d047 sccp: Introduce helper func sgsn_scu_iups_tx_data_req()
This avoid code deduplication plus moving SCCP SAP logic into sccp.c.

Change-Id: Id34f86b4f5e40ddceac4066ec18ebc60529cafab
2025-09-02 18:51:43 +02:00
Pau Espin Pedrol
265bc33b05 Split most of iu_client.{c,h} into sccp.{c,h} and iu_rnc.{c,h}
* iu_rnc is easily spotted as an object class in the data domain which
  can be put into its own file, handling its specific logic and data.

* SCCP code can also be moved into own specific file, which eases
  self-containment of the layer logic and data.

* As a result, lots of RANAP Rx/Tx code in iu_client.{c,h} can also be
  moved to the already existing gprs_ranap.{c,h}.

All these changes improve a lot readibility of code and makes it a lot
easier to improve/extend the different layers/objects in future patches.

Change-Id: I4c792ae665720460b5a954b5c7cbfe5acbc37473
2025-09-02 18:51:08 +02:00
Pau Espin Pedrol
77cf72ea91 Move gsm0408_gprs_rcvmsg_iu to gprs_gmm.{c,h}
It belongs to that layer, same as existing gsm0408_gprs_rcvmsg_gb()
counterpart.

Change-Id: I09c19689fd7016b76ebeee821b551427965b7cf1
2025-08-27 14:44:34 +02:00
Pau Espin Pedrol
ad61ccd07b debug.h: Remove unused log category DIUCS
It's used nowhere, not even defined in the initialization array of
categories, because IuCS has no place in SGSN.

Change-Id: I0f7c6bedaf3b7f909c3e27175ca99967492ab7c3
2025-08-26 17:51:43 +02:00
Alexander Couzens
02fbdb59c2 Re-introduce Iu/UTRAN support
Add support for UTRAN routing areas.

Change-Id: I1b1aedd2a7c358bd388aa3d8a9f3c6a0011b4889
2025-08-21 23:08:52 +02:00
Alexander Couzens
b1444a6c11 [4/4] iu_client: start using adopted iu_client
Use internal headers.
Use renamed event names/types.

Change-Id: I7c99ad0498c6f885361562c54fcf0ebdc086616b
2025-08-21 18:40:49 +02:00
Alexander Couzens
6043666db5 [3/4] iu_client: hide symbols to prevent clashes
Change-Id: Ide2f7767b90501790a5d609222185918da66336b
2025-08-21 18:40:43 +02:00
Alexander Couzens
6534fdbf27 [2/4] iu_client: rename symbols to prevent clashes with libosmo-ranap
While moving the iu_client into osmoSGSN, it's not allowed to have
the same global symbols in the library as in osmoSGSN.

Change-Id: I2af29b38f110b80f69ef598c8e2700ff323a9d7c
2025-08-21 18:10:25 +02:00
Alexander Couzens
2d4fe639cc [1/4] Adopt iu_client from osmo-iuh
iu_client was original written to have an abstraction
for Iu. However the MSC stopped using iu_client a long
time ago because the abstraption didn't fit well.
There was never time to rewrite SGSN to direct talk RANAP
using the primitives.
Adopt iu_client for now, but with the plan to rewrite
it in a similar way as osmo-msc is doing IuCS.

Adopted from osmo-iuh:
1631c8ac5a2f ("iu_client: fix linter issues")
Related: OS#5487

Change-Id: I9c0d9667ddcb57b52eae34da942acfc1688c1418
2025-08-21 18:10:21 +02:00
Alexander Couzens
4ba6638840 routing area: prefix GERAN functions with geran
Make it easier to understand the purpose
of the functions.

Change-Id: Ic616bd8f9afe6cd3702cd4af08706b7b36987f42
2025-08-06 15:00:40 +02:00
Alexander Couzens
bae75b9a60 BSSGP: reset cells when receiving a Reset on Signal BVC
When a PCU sends a BVC Reset over BSSGP on the Signal BVC,
all cells which has been set up previously via PtP BVCs
has to be removed.

They might be introduced after the Signal BVC reset procedure
has finished.

Change-Id: I380c8f9ef90d8c7face5c380d923ab5168f5b484
2025-08-06 14:46:34 +02:00
Alexander Couzens
c156925405 tests: RA: add test for a GERAN cell to change its BVCI
Change-Id: Idcada8364bf62aae2244b92e0d7a43c120c9c0ad
2025-08-01 17:44:33 +02:00
Alexander Couzens
2422c0375b routing area: sgsn_ra_geran_page_ra: make argument rai const
The second argument mmctx can't made const because of the paging info,
which uses non-const fields.

Change-Id: I21d7e66e433f2b891c94bae91697aa46198fb20c
2025-08-01 17:44:33 +02:00
Alexander Couzens
fec2e5884a routing area: add LOGRA() logging macro
Prepend the routing area in the log message.

Change-Id: Ib287b70c34b21ce64bc864f69fcf6af5fa0698b9
2025-08-01 17:44:33 +02:00
Alexander Couzens
4b894f1a85 routing area: introduce sgsn_ra_find_or_create()
If the RA is the same, return the RA, but if the ran type is different,
return NULL instead of allocating a second RA in the other
RAT.

Change-Id: I6955a09d9d8f3ba4dc3a14d7ed5cd9798d929c61
2025-08-01 17:43:57 +02:00
Alexander Couzens
328edfd6f8 routing area: move cell.cell_id behind a geran union
To prepare for Iu support, use the SGSN RA cell for both,
a GERAN Cell and a UTRAN Service Area.

Change-Id: I0c24d98dd699748842dca271b93e58d27cf02285
2025-08-01 14:27:43 +02:00
Alexander Couzens
9eb1f7e006 use variable name rai for struct osmo_routing_area_id
Be consistent with variable naming if a single
`struct osmo_routing_area_id *` area id is used.

Change-Id: I946983d4857035e06c68af378654466b35025014
2025-08-01 14:25:48 +02:00
Alexander Couzens
f34503289e routing area: introduce ran_type on the RA
A Routing Area should be uniquely identified by the RAI.
Following this, it is forbidden to have a RAI which is valid
in both, GERAN and UTRAN.

Change-Id: I59c35f1a4912ff11574bb31e4fe424816993548c
2025-08-01 14:25:16 +02:00
Alexander Couzens
dda9c6780b ranap: split mmctx related iu events into own function
In preparation of Routing Areas for Iu, split
off mmctx related events into an own function.
All mmctx related events must contain a valid ctx or are
otherwise invalid.

Change-Id: Ie6f131d29da299f09e897240ec5c39a395ce6f1b
2025-07-17 11:42:24 +02:00
Alexander Couzens
234da0eed4 mmctx: LOGIUP: allow ue to be NULL
In certain cases, the ue can be NULL.
E.g. when receiving a new Iu event without a context.

Change-Id: Ie569fa100abe518cb63c1b918ad7748a7ea3bb99
2025-07-17 11:41:35 +02:00
Alexander Couzens
b79797d056 gprs_gmm: fix endianess of GSM48_IE_GMM_PDP_CTX_STATUS
The pdp_status is actually encoded as little endian,
but there is no tlvp_val16le yet.

Change-Id: Ib9bee2f8d0b1f89986d15cf3ce6404ad76378c8c
2025-07-14 17:39:22 +02:00
Alexander Couzens
34182d28a4 GMM: implement IMEI SV on auth/ciph response
The SGSN is requesting the IMEI SV on an auth/ciphering response.
Parse the answer of the MS/UE.

Change-Id: Ia8b428967a4ce3132a4a63ae10fe610de81c3a29
2025-02-17 23:13:43 +01:00
Alexander Couzens
901ca02e0e Assign MME GroupId/Code to remote MMEs
In preparation to route SGSN Context Req/Resp/Ack the SGSN
need to route GTP messages based on the MME GroupId and MME Code.

Change-Id: I881aeba904fb6ca815842d36e466a2459ad81913
2025-02-17 23:13:42 +01:00
Alexander Couzens
944b6dfb8d gprs_gmm_util: use a RAU specific TLV dictionary
Limit the known and allowed TLVs according to the spec.

Change-Id: I460971deeebc9977a984937c542b263d941e78df
2025-01-31 16:44:58 +01:00
Alexander Couzens
27dd6d84ec gprs_gmm_util: add parsing of GMM Attach Requests
This will be used in the following commit.

Change-Id: I4a82e0c4070da982efd6c2f15fd145393423772b
2025-01-31 16:43:43 +01:00
Alexander Couzens
028c299bb2 Add include prefix for all libgtp related includes
The libgtp includes contain all the prefix osmocom/gtp.
The old non-prefix was used in the old OpenGGSN code base.

Change-Id: I5ea50c9766f719e1eea89cbf6783fafc2093773d
2025-01-28 00:17:16 +01:00
Alexander Couzens
d3b037ccfa gprs_ms_net_cap_gea_mask(): protect against empty MS Network Capabilities
When the MS Network Capability is empty, expect to support at least
unencrypted communications.
This shouldn't be empty at all, as the minimum length of it is 2 when
the MS/UE is doing an Attach Request.
But when receiving a MS/UE SGSN Context via Gn, the field
is optional.
The MS/UE shall include it into the Routing Area Request,
but not must.

Change-Id: Ieef2e3eeaaadc90c35fff6f20d47bd36aaa4b9e6
2025-01-28 00:17:15 +01:00
Alexander Couzens
921af3d428 Refactor mmctx_is_r99 into mmctx.c
It is related to the mmctx and should be in mmctx.c instead
of the GMM message parsing or handling.

Change-Id: I198a3c9d49667c70e8995808b3fcb5ea26e8f17a
2025-01-28 00:17:15 +01:00
Alexander Couzens
0e8f055631 GMM: rework PDP context status
Use an uint16 instead of an pointer with an unknown length.
Also encode the current active PDP context (e.g. on 2G/4G
mobilty this could change). It resynchronizes the UE with the SGSN
state.

Change-Id: I12d3110c9365132f96ef7ff8a1be22a431682c81
2025-01-28 00:16:53 +01:00
Pau Espin Pedrol
df44350b4b jenkins.sh: No need to build libosmo-sigtran with doxygen
Change-Id: Iab3d1b2e1591e66702a01def6c322f4a11970ead
2024-12-10 16:52:17 +01:00
Pau Espin Pedrol
7a45b57485 sgsn_vty: Drop unneeded use of abis/ipa.h header
The header is not needed and will eventually be deprecated.

Change-Id: I48a513b8ea63dadbc6d6d571380980f55ff04a06
2024-12-10 14:37:31 +01:00
Alexander Couzens
7d94476bde GTP: CreatePDPContext: only use IMEISV IE when IMEISV is known
The IE is optional, but if it is present, it must be 11 byte long
containing a 8 byte IMEISV. If IMEI is unknown the SGSN
would add an empty IMEISV IE which is invalid.

Change-Id: I812af1e702e77214244f32ae65663c1a03b23962
2024-12-05 12:28:05 +00:00
Pau Espin Pedrol
6668b2b29a vty: Avoid accessing gsupclient object fields directly
Use new APIs recently added.

Depends: osmo-hlr.git Change-Id I401af83232022f1c141eef1f428cbe206a8aaaa2
Change-Id: Ie443ef0bd983635a5b29e434d79b93ef3f7ce370
2024-12-03 14:32:57 +01:00
Pau Espin Pedrol
b69e326e69 gmm: Fix reject small size RAU request
A RAU Request can actually be 14 bytes long:
"""
GSM A-I/F DTAP - Routing Area Update Request
    Protocol Discriminator: GPRS mobility management messages (8)
        .... 1000 = Protocol discriminator: GPRS mobility management messages (0x8)
        0000 .... = Skip Indicator: No indication of selected PLMN (0)
    DTAP GPRS Mobility Management Message Type: Routing Area Update Request (0x08)
    Update Type
        .... 0... = Follow-on request pending: False
        .... .000 = Update type: RA updating (0)
    Ciphering Key Sequence Number
        0... .... = Spare bit(s): 0
        .111 .... = key sequence: No key is available (MS to network) (7)
    Routing Area Identification - Old routing area identification - RAI: 262-42-13135-0
        Routing area identification: 262-42-13135-0
            Mobile Country Code (MCC): Germany (262)
            Mobile Network Code (MNC): Vodafone GmbH (42)
            Location Area Code (LAC): 0x334f (13135)
            Routing Area Code (RAC): 0x00 (0)
    MS Radio Access Capability
        Length: 4
        MS RA capability 1
            0001 .... = Access Technology Type: GSM E --note that GSM E covers GSM P (1)
            .... 0001  111. .... = Length in bits: 0x0f (15)
            ...0 01.. RF Power Capability, GMSK Power Class: Not specified (1)
            A5 Bits: Same values apply for parameters as in the immediately preceding Access capabilities field within this IE (0)
            .... ...1 = Controlled early Classmark Sending: Implemented
            0... .... = Pseudo Synchronisation: Not Present
            .0.. .... = Voice Group Call Service: no VGCS capability or no notifications wanted
            ..0. .... = Voice Broadcast Service: no VBS capability or no notifications wanted
            ...1 .... = Multislot capability struct: Present
                HSCSD multislot class: Bits are not available (0)
                GPRS multislot class: Bits are not available (0)
                SMS_VALUE (Switch-Measure-Switch): Bits are not available (0)
                ECSD multislot class: Bits are not available (0)
                EGPRS multislot class: Bits are not available (0)
                DTM GPRS Multi Slot Class: Bits are not available (0)
"""

Change-Id: I49210a04b16e6e2fc9d799b99c2fa415f28ddbba
2024-11-25 19:10:12 +00:00
Pau Espin Pedrol
11854f52ac jenkins.sh: libosmo-netif no longer depends on libosmo-abis
Change-Id: Id62040f41e9a5c233cc42eedb0bd5df9f62df418
Depends: libosmo-abis.git Change-Id I079dc3999de508301dd37ed03e399356a58d3cab
Depends: libosmo-netif.git Change-Id I13d6e88158f6d9ce017986283183ee9c2cc68cae
2024-11-21 14:54:24 +01:00
Pau Espin Pedrol
61d3e3a377 gsm48_gmm_ie_tlvdef: Add newer definitions from current enum gsm48_gprs_ie_mm
Depends: libosmocore.git a28ae056435b0de575441f7608101117d1beda0c
Change-Id: I38dc87bcaa23a5c4ec36ae4ab082b150dad93840
2024-11-19 19:30:01 +01:00
Pau Espin Pedrol
d4bded987b gsm48_gmm_ie_tlvdef: Add missing definitions from current enum gsm48_gprs_ie_mm
Fill in defintions for all enum fields defined as of libosmocore.git
51bbb34a16359941f17a0b80d8fe9b6d73d12307.

Change-Id: I2648fbfb0f133069470a829d80fdbe80b7e6e1ef
2024-11-19 18:59:19 +01:00
Pau Espin Pedrol
99bc975225 gsm48_gmm_ie_tlvdef: Sort based on enum gsm48_gprs_ie_mm order
Change-Id: Icb943e06b7ee7bd7f2def30c9b5ec5d24123cd94
2024-11-19 18:56:48 +01:00
Alexander Couzens
7138f8eb93 Use %d instead of %i
%d and %i has the same meaning when used in format string for printf.
The linter will warn when using %i. %i was only added to provide
symmetry between scanf() and printf().

Change-Id: Icd5f5f9b0d2ed8fd82afa406787acb4bd95358bf
2024-10-22 10:25:51 +02:00
Alexander Couzens
5b289e83cf GMM: split parsing of a RA Update Request in a separate file
gprs_gmm.c is huge. Further split the general validation
and parsing of the message into an own function.

Change-Id: I413da1b6b4b7c0c4781393acd8564661bc74ce2d
2024-10-22 10:25:51 +02:00
Alexander Couzens
8a2ed97336 move gsm48_gmm_att_tlvdef into gprs_gmm_util
The gprs_gmm.c is huge. Move message parsing into gprs_gmm_util.c.

Change-Id: I67dcdb986fd01dc093501d324b5c376246a5d30d
2024-10-22 10:25:49 +02:00
Alexander Couzens
72283a1686 Refactor diffing same GMM messages
gprs_gmm_attach_req_ies() isn't specific for the attach request.
It also did not cover the full message because of a fixed message length
when comparing both messages.

A diff of the old and new GMM message is required to figure out
if the MS/UE is retransmitting an old message or starting
a new procedure.

Change-Id: Ie698d3a6894a5796663c22c8bfd12b47acda57e6
2024-10-21 20:53:52 +02:00
Alexander Couzens
e7303adc8d Implement correct Routing Area based paging
Previous the SGSN would not support multiple BSS within the
same routing area.
Add support paging of a whole routing area.

Change-Id: I181da9f656e394ccfcb8999021a5b7e13ca0419f
2024-10-21 20:53:52 +02:00
Alexander Couzens
e8c82d9cd1 Add Routing Areas
Add a routing area layer which tracks routing area and
cells within a routing area.

Change-Id: I2474b19a7471a1dea3c863ddf8372b16180211aa
2024-10-21 20:53:52 +02:00
Pau Espin Pedrol
6213201b95 ranap: Reject ActivatePDPCtx from UE if RABAssResp parsing fails
Before this patch, if Rx path at RAB Ass Req failed, the PDP ctx in the
middle of being activated was left untouched.
Explicitly communicate the failure to both sides (UE and GGSN).

Related: OS#6508
Related: SYS#7119
Change-Id: I111cc4b17100d2e1ef756b654dc9e98415b3a8bb
2024-10-16 17:22:35 +02:00
Pau Espin Pedrol
3087322c3e ranap: Fix decoding of padded ITU X.213 IPv4 TLA in Rab Ass Resp
Simply use the existing omos-iuh library helper to decode the IP
address, which is more robust than existing adhoc code in osmo-sgsn.
The library version already supports decoding ITU X.213 IANA binary IPv4
addresses padded to 20 bytes.

Related: OS#6508
Related: SYS#7119
Change-Id: I9c48b676068574338f60b6ed7ad8b61ba748b52e
2024-10-16 17:22:09 +02:00
Oliver Smith
b12c892a42 contrib/jenkins: libosmo-sccp -> libosmo-sigtran
Change-Id: Id587ed1a8604c4881fd82784ad400c91d9125e32
2024-09-16 12:29:43 +02:00
Oliver Smith
a39a564906 debian/control: remove build-dep: libosmo-sccp-dev
This follows the removal of the libsccp.a and the pkg-config
libosmo-sccp.pc from libosmo-sccp.git

Related: libosmo-sccp I299a32a2f8f61477cf49c8126567fef2092d5407
Change-Id: I2fc827cf15b7f78173e5ee489abe2c6d12b82c2f
2024-09-10 09:54:58 +02:00
Vadim Yanitskiy
13592d2fa5 sm: pdpctx_timer_stop(): warn about timer not running
Similarly to 282de031, this commit turns errors like:

  DGPRS ERROR PDP(---/0) Stopping PDP timer 3395 but 0 is running

into warnings with a more accurate reason:

  DGPRS ERROR PDP(---/0) Stopping *inactive* PDP timer 3395

Change-Id: I64932d1cbd93667ab2e94e04691d6601435dba81
Related: 282de031 "gmm: mmctx_timer_stop(): warn about timer not running"
2024-08-30 05:21:41 +00:00
Alexander Couzens
12ed86d17a Replace gprs_ra_id with modern osmo_routing_area_id
The new modern osmo_routing_area_id is more flexible
and allow to access PLMN, LAC more convient.

Depends: Iaef54cac541913534af00f40483723e9952a6807 (libosmocore)
Change-Id: Ia41eb8f51d3836b1bc65325ff1ec6bdb16e20c7e
2024-08-20 14:08:14 +02:00
Pau Espin Pedrol
35c178e84d llc: Mark old/current tlli as all 1's when unassigning LLME
TS 44.064 section 8.3.3 (and other sections) talk about special
unassigned value of "all 1's", but I couldn't find any reference to a
"all 0's" specific value/meaning.
In practice in the code this may not be super important since those
values may not ve checked due to the FSM state, but in any case they are
initially set to all 1's, so it makes total sense to re-set them to the
same unassigned value instead of a randomly chosen all 0's value.

Change-Id: I660c8d0ef08b34f8cb74fd51b5c59e5628d687ae
2024-08-19 18:26:31 +02:00
Pau Espin Pedrol
868d818e6e Fix double free during RAU with unexpected Old RAI
If an MS which had an MMCTX at the SGSN sent RAU update with an
unexpected Old RA field, the RAU was rejected and LLME (LLC layer)
unassigned (freed), because no MMCTX was found matching the wrong old
RA.
However, an MMCTX may actually exist pointing to that LLME, and hence
when the LLME is freed, it stayed unnoticed with a dangling pointer to
the freed LLME in ctx->gb.llme.
Let's try to harder to avoid this kind of bugs which make osmo-sgsn
crash.

Once we properly split the code into separate independent layers (LLC,
MMCTX, etc.) each holding their own structs, this kind of bugs shouldn't
happen anymore.

Related: OS#6441
Change-Id: I5a4328c6e945b85dd815215724feecadba59c435
2024-08-19 18:20:53 +02:00
Vadim Yanitskiy
140017e0ed build: do not link against libosmo-abis
We use 'struct ipa_client_conn' from libosmo-abis:

  src/sgsn/sgsn_vty.c:56:#include <osmocom/abis/ipa.h>

but do not call any of its API directly, so linking against it
is not necessary.  Remove it from $LDADD, but keep in $CFLAGS.

Change-Id: I11168a7b25942d6c70566441fb0a1d26c3ae5c43
Related: OS#6535
2024-08-09 17:57:31 +07:00
Vadim Yanitskiy
c239545936 build: remove indirect libosmo-netif dependency
Neither we include its headers nor call any of its functions.
It's actually a dependency of libosmo-sccp, which is required
for '--enable-iu', so keep it in contrib/jenkins.sh.

Change-Id: Ia3395fd335d3a693c314b29b8e92968eef55da9a
Related: OS#6535
2024-08-09 17:57:22 +07:00
Alexander Couzens
9650961c4b includes: add forward declartion of external types
Change-Id: Ifbf0a045a6391694155f6627967f2d92b930d765
2024-08-04 19:52:35 +02:00
Alexander Couzens
c05dad1035 includes: sgsn.h: use full path include for gtp.h
The gtp header of ggsn have been moved to osmocom/gtp/gtp.h for a while.
Use the full path to prevent clashs with older versions

Change-Id: I764644110c0f332b18117e1e8311ddc55eafd5fa
2024-08-04 17:44:37 +02:00
Pau Espin Pedrol
7d5337fe80 Handle rx UpdatePDPCtxReq with Tunnel Direct Flags EI=1
GGSN informed us that it received an Error Indication when sending DL data to the RNC.
This probably means the RNC lost its state, aka crashed or was rebooted.
Mark the subscriber as PMM-IDLE, release Iu and point GTPU back to SGSN.
Then page the UE so it hopefully re-creates the state at the RNC.

Related: OS#6512
Related: OS#6519
Related: SYS#5435
Depends: osmo-ggsn.git Change-Id Ic80a9a928c55b6ff85be96014920bb42793cb943
Change-Id: I76d4c387730fdbfb9e7e0dd23a5afb9e762228b1
2024-08-01 11:39:00 +02:00
Pau Espin Pedrol
2cd06e28f4 Announce SGSN own TEID during UpdatePDPCtxReq after UE goes PMM-IDLE
When the UE/RNC closes the Iu conn (while keeping the PDP Context
activated), the SGSN updates the GGSN to point the GTPU tunnel to
itself. Unfortunately, only the IP address was being updated while the
TEID was kept the same (the one from the RNC).

As a result, when new MT data arrived at the GGSN, it would forward it
over GTPU to the SGSN using the incorrect RNC TEID, which the libgtp
stack at osmo-sgsn would silently drop due to not being known (because
it was >PDP_MAX=1024).

The issue can be triggered in test SGSN_Tests_Iu.TC_pmm_idle_rx_mt_data.

Related: OS#5773
Related: SYS#5435
Change-Id: I782aa43c71569922a945bd44544bb1388bf8c878
2024-07-30 21:38:41 +02:00
Pau Espin Pedrol
0bab0007c7 gtp: Set Direct Tunnel Flags DTI during UpdatePDPCtx
This is required as per TS 28.060 to tell the GGSN that the remote
Address/TEID it is receiving it's the one of the RNC.
Upon receiving a GTPU Error Indication, the GGSN knowing it's using
DirectTunnel, can then update the SGSN with UpdatePDPContextReq with
Direct Tunnel Flags DTI=1 EI=1 and then the SGSN can decide whether
reconfigure the Direct Tunnel or switch to 2-leg tunnel until the
connection with RNC/UE can be reestablished.

Depends: osmo-ggsn.git Change-Id Ia3e360a35d30858eab1e438dc2508fd756c2e22e
Related: SYS#5435
Change-Id: Iefe73eeea41df0c55db673194c9e9547504cbf0d
2024-07-30 21:38:37 +02:00
Pau Espin Pedrol
4ced617eb6 Fix DeactPDPCtxAcc when UE goes PMM ENABLED but lost its PDP context
Scenario: UE activates a PDP context, then after a while goes PMM IDLE
(Iu conn is destroyed but PDP is kept).
When UE connects through Iu again, it sends eg. RAU or ServiceRequest
with pdp_status bitmask statis the active NSAPIs.
If some NSAPI (PDP context) is enabled at SGSN but doesn't show up in
the bitmask, SGSN will destroy the PDP context with GGSN
(DeletePDPContextReq) towards GGSN prior to re-creating it.
When SGSN receives the DeletePDPContextResp, it would forward a
DeactivatePDPContextReq to the UE for a PDP context which was not known
by the UE anymore, this is wrong.

With this patch, the state of the NSAPI/PDP at the UE side is tracked,
and used to know whether when the PDP gets deleted on the GGSN side then
it needs to also be deleted on the Iu side.

Change-Id: I0ccd9228d71c29248b5f510356dbfdb09565dc30
2024-07-30 21:36:01 +02:00
Oliver Smith
f8d248e2d1 Bump version: 1.11.1.22-bea6a-dirty → 1.12.0
Change-Id: I9331fb5c69c5d00d419e859f1fa81d6e231b0318
2024-07-24 17:31:38 +02:00
Vadim Yanitskiy
bea6a0ffb5 README.md: cosmetic: fix a typo
Change-Id: Ia23fdbde29691c7af3834effee3d03ab866ca809
2024-06-05 18:34:07 +07:00
Oliver Smith
a13cc20c98 doc: set state-dir to /var/lib/osmocom/osmo-sgsn
Be consistent with osmo-ggsn, and set the state dir to
/var/lib/osmocom/osmo-sgsn. Without this patch, it defaults to the
current directory, which means in case of running with the systemd
service, /var/lib/osmocom.

Copy osmo-sgsn.cfg and osmo-sgsn-accept-all.cfg to the tests dir and do
not set the state-dir there. Usually the user that runs the VTY and CTRL
tests is not allowed to write to /var/lib/osmocom. (I've also tried
generating these on the fly, but that breaks in 'make distcheck', as I
would need to write to the source dir or would need to change
osmo-python-tests etc. to read the config file from another directory.)

Related: osmo-ggsn I5b51529b4f8bd2462e54f58a1ce2e2d7c76ff46a
Depends: osmo-python-tests Ic312d546da1c21f68a80b6a188616ef9bc84f4c6
Change-Id: I309807ff0bc125d4653222b2b4ba69ded3bbff70
2024-05-27 14:36:01 +02:00
Oliver Smith
1fd4a4c135 debian/osmo-gtphub.init: delete
Remove SysV init scripts. These are not really maintained anymore and
this makes it consistent with other Osmocom projects.

Avoids synchronizing with SysV scripts on debian:
  # systemctl enable osmo-gtphub
  Synchronizing state of osmo-gtphub.service with SysV service script with /usr/lib/systemd/systemd-sysv-install.
  Executing: /usr/lib/systemd/systemd-sysv-install enable osmo-gtphub

Change-Id: I9008944369314a4cbb345bfbf01bdb57aa7590fb
2024-05-22 13:33:52 +02:00
Oliver Smith
b3ce06058c sgsn/sgsn_vty: create state-dir
Prepare to change the state-dir in the default config in a follow-up
commit. Create the directory if it does not exist.

Change-Id: I8db4898cdaa2fcbd6bbf7c543764b9cdf828de83
2024-05-16 10:50:56 +02:00
Oliver Smith
0a6fe9727f debian/postinst: add checks, be verbose
Do not attempt to change permissions/ownership if the package gets
upgraded from a version higher than the next release.

Do not fail if the user deleted the config file.

Be verbose when changing permissions.

Related: OS#4107
Change-Id: I2b01a7625cf66fbb7d203f939ddcc1cbab43cf33
2024-05-14 15:21:06 +02:00
Oliver Smith
7ba62d3061 contrib: remove rpm spec file
Related: https://osmocom.org/news/255
Related: OS#6446
Change-Id: I9e7a3beb861faab1b6852aa5b57847c590986976
2024-05-13 08:39:35 +00:00
Vadim Yanitskiy
62b28ffe1b VTY: sync default UMTS UEA config with osmo-msc
As was reported in OS#6442, nano3g S16 is unhappy when CS and PS
domains use different UEA configuration for simultaneous RANAP
connections.  Bring osmo-sgsn in sync with osmo-msc to avoid this.

Change-Id: I4eb9451b4267fc1436ed90a55ff200cf36f16bf6
Related: OS#6442
2024-05-09 10:50:08 +02:00
Vadim Yanitskiy
cdf716cf9a VTY: move default settings to sgsn_instance_alloc()
It's cleaner to have all configuration defaults in one place.

Change-Id: I1a439dcc76272a181986b6ec9368ef16441dc098
Related: OS#6442
2024-05-09 10:48:29 +02:00
Oliver Smith
a9c39c04de .deb/.rpm: various fixes related to non-root
* Explicitly chown /var/lib/osmocom to osmocom:osmocom, instead of
  relying on systemd to do it when the service starts up. This does not
  work with the systemd versions in debian 10 and almalinux 8.
* deb: Use "useradd" instead of the interactive "adduser" perl script
  from Debian. This makes it consistent with how we do it in rpm, and
  avoids the dependency on "adduser".
* deb: Consistently use tabs through the file, instead of mixing tabs
  and spaces.
* deb: Remove support for the "dpkg-statoverride --list" logic. This
  seems to be a rather obscure feature to override permissions for
  certain files or directories, for which it does not seem to be a good
  idea to make the postinst script less maintainable. Something similar
  can be achieved by using your own Osmocom config file in a different
  path with different permissions.

Related: OS#4107
Change-Id: I406ff0d625b02991d580c8382aa4be04dba45a00
2024-05-08 06:46:32 +00:00
Vadim Yanitskiy
282de031f1 gmm: mmctx_timer_stop(): warn about timer not running
This turns errors like:

  DMM ERROR MM(262420000000038/e2ff704e) Stopping MM timer 3350 but 0 is running

into warnings with a more accurate reason:

  DMM NOTICE MM(262420000000037/e2ff704e) Stopping *inactive* MM timer 3350

Change-Id: I56ecad9d8f1049974b0896f6d0e7fc61580155ec
2024-05-01 10:44:27 +02:00
Vadim Yanitskiy
c3156193da gmm: cosmetic: fix preprocessor macro formatting
Change-Id: I77171d65db23794d8fd9872e0cc4d6f3b50dda0d
2024-05-01 10:44:27 +02:00
Alexander Couzens
cd3a8cfad6 docs: front page: use https:// instead of http://
Change-Id: If3c3b8e79f94da7a3bcc9262808da09f6a5a601c
2024-04-28 12:17:10 +00:00
Alexander Couzens
e51b3be379 docs: update year to 2024
Change-Id: I85a987eee470d2040c91289d33c5d97c3e90674d
2024-04-28 12:17:10 +00:00
Alexander Couzens
b34e0a5720 docs: replace legacy NS with new NS2 chapters
osmo-sgsn already switched to the new NS2 code. Use the correct
NS2 chapter

Change-Id: I9cc86d234e029b5192e36aeb14b0e39d1496842d
2024-04-28 12:17:10 +00:00
Oliver Smith
05363e0a32 contrib/osmo-sgsn.spec: fix build for almalinux:8
Add the missing "%if 0%{?suse_version}" around %service_del/add
commands, as these are only available on opensuse.

Fix for:
  error: line 106: Too many names: %preun  -n osmo-gtphub %service_del_preun   osmo-gtphub.service

Fixes: a07e6d9c (".deb/.rpm: add osmocom user during package install")
Change-Id: I89802f926bfccc0f7b4bb1ff64115b232b1db022
2024-04-26 09:58:51 +02:00
Max
a07e6d9c58 .deb/.rpm: add osmocom user during package install
Create osmocom user & group during package installation.
Fix the configuration dir/files permission to match.

Related: OS#4107
Tweaked-By: Oliver Smith <osmith@sysmocom.de>
Change-Id: I55ce205d4b314d01b2641c8f3d52455c051d6282
2024-04-24 11:52:50 +02:00
Harald Welte
1ede89a35a README.md: Add Forum and Issue Tracker sections
Change-Id: I69dfe4124c2a2be30c9ef04e70e3f15d10a2305c
2024-03-23 17:15:07 +01:00
Harald Welte
647304fbe5 README.md: Overhaul (more links; improved formatting)
Change-Id: I0cf9898877aab2f834c886491f4d203d55a8b4b9
2024-03-23 17:07:07 +01:00
Harald Welte
370d6e8b59 Add funding link to github mirror
see https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/displaying-a-sponsor-button-in-your-repository

Change-Id: Icf0fd068742eba60c23f0e567e6e72b42063f2c0
2024-03-23 17:03:36 +01:00
Pau Espin Pedrol
dc4c294f94 gsup: Use new libosmogsm struct osmo_gsup_pdp_info fields
This also makes sure it doesn't compile against older libosmogsm gsup
versions which would break ABI.

Change-Id: I0d03d368e73ab62ec631420769f6af91f2ff9987
Related: OS#6091
Depends: libosmocore.git Change-Id 70be3560659c58f24b8db529c4fc85da4bb0ec04
2024-01-29 11:07:03 +01:00
Vadim Yanitskiy
f2545b1b8f build: include README.md into the release tarball
Change-Id: Ia467ae8cc6eec0dc9fb36d4c159d2762e965c4f8
2024-01-26 23:37:30 +07:00
Andreas Eversberg
1f1d90f175 Use uniform log format for default config files
Related: OS#6272
Change-Id: I6b6aa5a5100cf0045dcba1b062acc9376d34b0ae
2023-12-01 12:40:54 +01:00
88 changed files with 5263 additions and 798 deletions

1
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1 @@
open_collective: osmocom

View File

@@ -19,7 +19,7 @@ SUBDIRS = \
BUILT_SOURCES = $(top_srcdir)/.version
EXTRA_DIST = \
.version \
contrib/osmo-sgsn.spec.in \
README.md \
debian \
git-version-gen \
osmoappdesc.py \

View File

@@ -1,28 +1,30 @@
osmo-sgssn - Osmocom SGSN Implementation
========================================
osmo-sgsn - Osmocom SGSN Implementation
=======================================
This repository contains a C-language implementation of a GSM Serving GPRS
Support Node (SGSN) for 2G (GSM) and 3G (UMTS). It is part of the
This repository contains a C-language implementation of a *Serving GPRS
Support Node (SGSN)* for 2.5/2.75G (GPRS/EDGE) and 3G (UMTS). It is part of the
[Osmocom](https://osmocom.org/) Open Source Mobile Communications
project.
OsmoSGSN exposes
* Gb towards PCUs (e.g. OsmoPCU): Various GbIP flavors + Gb/FR/E1
* GTP towards a GGSN (e.g. OsmoGGSN);
* IuPS over IP towards RNCs / HNBGW (e.g. osmo-hnbgw)
* The Osmocom typical telnet VTY and CTRL interfaces.
* The Osmocom typical statsd exporter.
* GSUP (custom MAP-like protocol) towards osmo-hlr
* *Gb* towards PCUs (e.g. [OsmoPCU](https://osmocom.org/projects/osmopcu/wiki/OsmoPCU)): Various GbIP flavors + Gb/FR/E1
* *GTP* towards a GGSN (e.g. [OsmoGGSN](https://osmocom.org/projects/openggsn/wiki))
* IuPS over IP towards RNCs / HNBGW (e.g. [osmo-hnbgw](https://osmocom.org/projects/osmohnbgw/wiki))
* The Osmocom typical telnet *VTY* and *CTRL* interfaces.
* The Osmocom typical *statsd* exporter.
* GSUP (custom MAP-like protocol) towards [osmo-hlr](https://osmocom.org/projects/osmo-hlr/wiki/OsmoHLR)
OsmoSGSN implements
* GPRS mobility management
* GPRS session management
Homepage
--------
You can find the OsmoSGSN issue tracker and wiki online at
<https://osmocom.org/projects/osmosgsn> and <https://osmocom.org/projects/osmosgsn/wiki>.
You can find the OsmoSGSN homepage online at <https://osmocom.org/projects/osmosgsn/wiki>.
GIT Repository
@@ -46,6 +48,14 @@ Pre-rendered PDF version of the current "master" can be found at
as well as the [VTY Reference Manual](https://ftp.osmocom.org/docs/latest/osmosgsn-vty-reference.pdf)
Forum
-----
We welcome any osmo-sgsn related discussions in the
[Cellular Network Infratructure -> 2G/3G Core Network](https://discourse.osmocom.org/c/cni/2g-3g-cn).
section of the osmocom discourse (web based Forum).
Mailing List
------------
@@ -58,13 +68,22 @@ Please observe the [Osmocom Mailing List
Rules](https://osmocom.org/projects/cellular-infrastructure/wiki/Mailing_List_Rules)
when posting.
Issue Tracker
-------------
We use the [issue tracker of the osmo-sgsn project on osmocom.org](https://osmocom.org/projects/osmosgsn/issues) for
tracking the state of bug reports and feature requests. Feel free to submit any issues you may find, or help
us out by resolving existing issues.
Contributing
------------
Our coding standards are described at
<https://osmocom.org/projects/cellular-infrastructure/wiki/Coding_standards>
We us a gerrit based patch submission/review process for managing
We use a Gerrit based patch submission/review process for managing
contributions. Please see
<https://osmocom.org/projects/cellular-infrastructure/wiki/Gerrit> for
more details

View File

@@ -1 +1,15 @@
#component what description / commit summary line
# When cleaning up this file: bump API version in corresponding Makefile.am and rename corresponding debian/lib*.install
# according to https://osmocom.org/projects/cellular-infrastructure/wiki/Make_a_new_release
# In short: https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html#Updating-version-info
# LIBVERSION=c:r:a
# If the library source code has changed at all since the last update, then increment revision: c:r + 1:a.
# If any interfaces have been added, removed, or changed since the last update: c + 1:0:a.
# If any interfaces have been added since the last public release: c:r:a + 1.
# If any interfaces have been removed or changed since the last public release: c:r:0.
#library what description / commit summary line
libgtp >1.12.0 new field dir_tun_flags in struct pdp_t
libgtp >1.12.0 gtp_set_cb_update_context_ind(), gtp_update_context_resp()
libosmocore >1.10.0 enum gsm48_gprs_ie_mm: GSM48_IE_GMM_UE_NET_CAP, GSM48_IE_GMM_VD_PREF_UE_USAGE
libosmo-gsup-client >1.8.0 osmo_gsup_client_is_connected(), osmo_gsup_client_get_rem_addr(), osmo_gsup_client_get_rem_port()
osmo-iuh >1.7.0 ranap multiple params are constified, see osmo-iuh Change-Id I667dc2ef377c1ceb5b11315458f00b282c143c81
osmo-iuh >1.7.0 ranap_new_msg_error_ind()

View File

@@ -38,23 +38,22 @@ dnl use a defined standard across all builds and don't depend on compiler defaul
CFLAGS="$CFLAGS -std=gnu11"
dnl checks for libraries
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.9.0)
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.9.0)
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.9.0)
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.9.0)
PKG_CHECK_MODULES(LIBOSMOGB, libosmogb >= 1.9.0)
PKG_CHECK_MODULES(LIBOSMOABIS, libosmoabis >= 1.5.0)
PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 1.4.0)
PKG_CHECK_MODULES(LIBOSMOGSUPCLIENT, libosmo-gsup-client >= 1.7.0)
PKG_CHECK_MODULES(LIBGTP, libgtp >= 1.11.0)
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.10.0)
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.10.0)
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.10.0)
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.10.0)
PKG_CHECK_MODULES(LIBOSMOGB, libosmogb >= 1.10.0)
PKG_CHECK_MODULES(LIBOSMOABIS, libosmoabis >= 1.6.0)
PKG_CHECK_MODULES(LIBOSMOGSUPCLIENT, libosmo-gsup-client >= 1.8.0)
PKG_CHECK_MODULES(LIBGTP, libgtp >= 1.12.0)
# Enable/disable 3G aka IuPS + IuCS support?
AC_ARG_ENABLE([iu], [AS_HELP_STRING([--enable-iu], [Build 3G support, aka IuPS and IuCS interfaces])],
[osmo_ac_iu="$enableval"],[osmo_ac_iu="no"])
if test "x$osmo_ac_iu" = "xyes" ; then
PKG_CHECK_MODULES(LIBOSMOSIGTRAN, libosmo-sigtran >= 1.8.0)
PKG_CHECK_MODULES(LIBOSMOSIGTRAN, libosmo-sigtran >= 1.9.0)
PKG_CHECK_MODULES(LIBASN1C, libasn1c >= 0.9.30)
PKG_CHECK_MODULES(LIBOSMORANAP, libosmo-ranap >= 1.5.0)
PKG_CHECK_MODULES(LIBOSMORANAP, libosmo-ranap >= 1.6.0)
AC_DEFINE(BUILD_IU, 1, [Define if we want to build IuPS and IuCS interfaces support])
fi
AM_CONDITIONAL(BUILD_IU, test "x$osmo_ac_iu" = "xyes")
@@ -244,6 +243,7 @@ AC_OUTPUT(
tests/Makefile
tests/atlocal
tests/gprs/Makefile
tests/gprs_routing_area/Makefile
tests/sgsn/Makefile
tests/gtphub/Makefile
tests/xid/Makefile
@@ -255,5 +255,4 @@ AC_OUTPUT(
doc/manuals/Makefile
contrib/Makefile
contrib/systemd/Makefile
contrib/osmo-sgsn.spec
Makefile)

View File

@@ -32,13 +32,13 @@ export PKG_CONFIG_PATH="$inst/lib/pkgconfig:$PKG_CONFIG_PATH"
export LD_LIBRARY_PATH="$inst/lib"
export PATH="$inst/bin:$PATH"
osmo-build-dep.sh libosmo-netif "" --disable-doxygen
osmo-build-dep.sh libosmo-abis
osmo-build-dep.sh libosmo-netif
osmo-build-dep.sh osmo-ggsn
osmo-build-dep.sh osmo-hlr
if [ "x$IU" = "x--enable-iu" ]; then
osmo-build-dep.sh libosmo-sccp
osmo-build-dep.sh libosmo-sigtran "" --disable-doxygen
osmo-build-dep.sh libasn1c
#osmo-build-dep.sh asn1c aper-prefix # only needed for make regen in osmo-iuh
osmo-build-dep.sh osmo-iuh

View File

@@ -1,118 +0,0 @@
#
# spec file for package osmo-sgsn
#
# Copyright (c) 2017, Martin Hauke <mardnh@gmx.de>
#
# All modifications and additions to the file contributed by third parties
# remain the property of their copyright owners, unless otherwise agreed
# upon. The license for this file, and modifications and additions to the
# file, is the same license as for the pristine package itself (unless the
# license for the pristine package is not an Open Source License, in which
# case the license is the MIT License). An "Open Source License" is a
# license that conforms to the Open Source Definition (Version 1.9)
# published by the Open Source Initiative.
## Disable LTO for now since it breaks compilation of the tests
## https://osmocom.org/issues/4116
%define _lto_cflags %{nil}
%define with_iu 1
Name: osmo-sgsn
Version: @VERSION@
Release: 0
Summary: Osmocom's SGSN for 2G and 3G packet-switched mobile networks
License: AGPL-3.0-or-later AND GPL-2.0-or-later
Group: Productivity/Telephony/Servers
URL: https://osmocom.org/projects/osmosgsn
Source: %{name}-%{version}.tar.xz
BuildRequires: autoconf
BuildRequires: automake
BuildRequires: libtool
BuildRequires: pkgconfig
%if 0%{?suse_version}
BuildRequires: systemd-rpm-macros
%endif
BuildRequires: pkgconfig(libcares)
BuildRequires: pkgconfig(libcrypto) >= 0.9.5
BuildRequires: pkgconfig(libgtp) >= 1.11.0
BuildRequires: pkgconfig(libosmo-gsup-client) >= 1.7.0
BuildRequires: pkgconfig(libosmo-netif) >= 1.4.0
BuildRequires: pkgconfig(libosmoabis) >= 1.5.0
BuildRequires: pkgconfig(libosmocore) >= 1.9.0
BuildRequires: pkgconfig(libosmoctrl) >= 1.9.0
BuildRequires: pkgconfig(libosmogb) >= 1.9.0
BuildRequires: pkgconfig(libosmogsm) >= 1.9.0
BuildRequires: pkgconfig(libosmovty) >= 1.9.0
%{?systemd_requires}
%if %{with_iu}
BuildRequires: pkgconfig(libasn1c)
BuildRequires: pkgconfig(libosmo-ranap) >= 1.5.0
BuildRequires: pkgconfig(libosmo-sigtran) >= 1.8.0
%endif
%description
OsmoSGSN is Osmocom's Serving GPRS Support Node for 2G and 3G
packet-switched mobile networks.
%package -n osmo-gtphub
Summary: Osmocom GTP Hub: Proxy for GTP traffic between multiple SGSNs and GGSNs
Group: Productivity/Telephony/Servers
%description -n osmo-gtphub
Osmocom GTP Hub: Proxy for GTP traffic between multiple SGSNs and GGSNs.
%prep
%setup -q
%build
echo "%{version}" >.tarball-version
autoreconf -fi
%configure \
%if %{with_iu}
--enable-iu \
%endif
--docdir=%{_docdir}/%{name} \
--with-systemdsystemunitdir=%{_unitdir}
make %{?_smp_mflags}
%install
%make_install
%if 0%{?suse_version}
%preun %service_del_preun %{name}.service
%postun %service_del_postun %{name}.service
%pre %service_add_pre %{name}.service
%post %service_add_post %{name}.service
%preun -n osmo-gtphub %service_del_preun osmo-gtphub.service
%postun -n osmo-gtphub %service_del_postun osmo-gtphub.service
%pre -n osmo-gtphub %service_add_pre osmo-gtphub.service
%post -n osmo-gtphub %service_add_post osmo-gtphub.service
%endif
%check
make %{?_smp_mflags} check || (find . -name testsuite.log -exec cat {} +)
%files
%doc AUTHORS README.md
%dir %{_docdir}/%{name}/examples
%dir %{_docdir}/%{name}/examples/osmo-sgsn
%exclude %{_docdir}/%{name}/examples/osmo-gtphub
%{_docdir}/%{name}/examples/osmo-sgsn/osmo-sgsn-accept-all.cfg
%{_docdir}/%{name}/examples/osmo-sgsn/osmo-sgsn.cfg
%{_docdir}/%{name}/examples/osmo-sgsn/osmo-sgsn_custom-sccp.cfg
%{_bindir}/osmo-sgsn
%dir %{_sysconfdir}/osmocom
%config(noreplace) %{_sysconfdir}/osmocom/osmo-sgsn.cfg
%{_unitdir}/%{name}.service
%files -n osmo-gtphub
%dir %{_docdir}/%{name}/examples
%dir %{_docdir}/%{name}/examples/osmo-gtphub
%{_docdir}/%{name}/examples/osmo-gtphub/osmo-gtphub-1iface.cfg
%{_docdir}/%{name}/examples/osmo-gtphub/osmo-gtphub.cfg
%{_bindir}/osmo-gtphub
%dir %{_sysconfdir}/osmocom
%config(noreplace) %{_sysconfdir}/osmocom/osmo-gtphub.cfg
%{_unitdir}/osmo-gtphub.service
%changelog

View File

@@ -5,6 +5,8 @@ Wants=network-online.target
[Service]
Type=simple
User=osmocom
Group=osmocom
ExecStart=/usr/bin/osmo-gtphub -c /etc/osmocom/osmo-gtphub.cfg
StateDirectory=osmocom
WorkingDirectory=%S/osmocom

View File

@@ -11,6 +11,8 @@ Type=simple
StateDirectory=osmocom
WorkingDirectory=%S/osmocom
Restart=always
User=osmocom
Group=osmocom
ExecStart=/usr/bin/osmo-sgsn -c /etc/osmocom/osmo-sgsn.cfg
RestartSec=2

40
debian/changelog vendored
View File

@@ -1,3 +1,43 @@
osmo-sgsn (1.12.0) unstable; urgency=medium
[ Andreas Eversberg ]
* Use uniform log format for default config files
[ Vadim Yanitskiy ]
* build: include README.md into the release tarball
* gmm: cosmetic: fix preprocessor macro formatting
* gmm: mmctx_timer_stop(): warn about timer not running
* VTY: move default settings to sgsn_instance_alloc()
* VTY: sync default UMTS UEA config with osmo-msc
* README.md: cosmetic: fix a typo
[ Pau Espin Pedrol ]
* gsup: Use new libosmogsm struct osmo_gsup_pdp_info fields
[ Harald Welte ]
* Add funding link to github mirror
* README.md: Overhaul (more links; improved formatting)
* README.md: Add Forum and Issue Tracker sections
[ Max ]
* .deb/.rpm: add osmocom user during package install
[ Oliver Smith ]
* contrib/osmo-sgsn.spec: fix build for almalinux:8
* .deb/.rpm: various fixes related to non-root
* contrib: remove rpm spec file
* debian/postinst: add checks, be verbose
* sgsn/sgsn_vty: create state-dir
* debian/osmo-gtphub.init: delete
* doc: set state-dir to /var/lib/osmocom/osmo-sgsn
[ Alexander Couzens ]
* docs: replace legacy NS with new NS2 chapters
* docs: update year to 2024
* docs: front page: use https:// instead of http://
-- Oliver Smith <osmith@sysmocom.de> Wed, 24 Jul 2024 17:31:38 +0200
osmo-sgsn (1.11.1) unstable; urgency=medium
[ Philipp Maier ]

16
debian/control vendored
View File

@@ -11,16 +11,14 @@ Build-Depends: debhelper (>= 10),
pkg-config,
libtalloc-dev,
libc-ares-dev,
libgtp-dev (>= 1.11.0),
libosmocore-dev (>= 1.9.0),
libosmo-abis-dev (>= 1.5.0),
libosmo-netif-dev (>= 1.4.0),
libosmo-gsup-client-dev (>= 1.7.0),
libgtp-dev (>= 1.12.0),
libosmocore-dev (>= 1.10.0),
libosmo-abis-dev (>= 1.6.0),
libosmo-gsup-client-dev (>= 1.8.0),
libasn1c-dev (>= 0.9.30),
libosmo-ranap-dev (>= 1.5.0),
libosmo-sigtran-dev (>= 1.8.0),
libosmo-sccp-dev (>= 1.8.0),
osmo-gsm-manuals-dev (>= 1.5.0)
libosmo-ranap-dev (>= 1.6.0),
libosmo-sigtran-dev (>= 1.9.0),
osmo-gsm-manuals-dev (>= 1.6.0)
Standards-Version: 3.9.8
Vcs-Git: https://gitea.osmocom.org/cellular-infrastructure/osmo-sgsn
Vcs-Browser: https://gitea.osmocom.org/cellular-infrastructure/osmo-sgsn

View File

@@ -1,150 +0,0 @@
#!/bin/sh
### BEGIN INIT INFO
# Provides: osmo-gtphub
# Required-Start: $network $local_fs
# Required-Stop:
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Osmocom GTP hub
# Description: Osmocom GTP hub
### END INIT INFO
# Author: Neels Hofmeyr <nhofmeyr@sysmocom.de>
# PATH should only include /usr/* if it runs after the mountnfs.sh script
PATH=/sbin:/usr/sbin:/bin:/usr/bin
NAME=osmo-gtphub # Introduce the short server's name here
DESC="Osmocom GTP hub" # Introduce a short description here
DAEMON=/usr/bin/osmo-gtphub # Introduce the server's location here
SCRIPTNAME=/etc/init.d/osmo-gtphub
# Exit if the package is not installed
[ -x $DAEMON ] || exit 0
# Read configuration variable file if it is present
[ -r /etc/default/osmo-gtphub ] && . /etc/default/osmo-gtphub
# Load the VERBOSE setting and other rcS variables
. /lib/init/vars.sh
# Define LSB log_* functions.
# Depend on lsb-base (>= 3.0-6) to ensure that this file is present.
. /lib/lsb/init-functions
DAEMON_ARGS="$DAEMON_ARGS -D -c $CONFIG_FILE"
#
# Function that starts the daemon/service
#
do_start()
{
# Return
# 0 if daemon has been started
# 1 if daemon was already running
# 2 if daemon could not be started
start-stop-daemon --start --quiet --exec $DAEMON --test > /dev/null \
|| return 1
start-stop-daemon --start --quiet --exec $DAEMON -- \
$DAEMON_ARGS \
|| return 2
# Add code here, if necessary, that waits for the process to be ready
# to handle requests from services started subsequently which depend
# on this one. As a last resort, sleep for some time.
}
#
# Function that stops the daemon/service
#
do_stop()
{
# Return
# 0 if daemon has been stopped
# 1 if daemon was already stopped
# 2 if daemon could not be stopped
# other if a failure occurred
start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --name $NAME
RETVAL="$?"
[ "$RETVAL" = 2 ] && return 2
# Wait for children to finish too if this is a daemon that forks
# and if the daemon is only ever run from this initscript.
# If the above conditions are not satisfied then add some other code
# that waits for the process to drop all resources that could be
# needed by services started subsequently. A last resort is to
# sleep for some time.
start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON
[ "$?" = 2 ] && return 2
return "$RETVAL"
}
#
# Function that sends a SIGHUP to the daemon/service
#
do_reload() {
#
# If the daemon can reload its configuration without
# restarting (for example, when it is sent a SIGHUP),
# then implement that here.
#
start-stop-daemon --stop --signal 1 --quiet $PIDFILE --name $NAME
return 0
}
case "$1" in
start)
[ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC " "$NAME"
do_start
case "$?" in
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
esac
;;
stop)
[ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME"
do_stop
case "$?" in
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
esac
;;
status)
status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $?
;;
#reload|force-reload)
#
# If do_reload() is not implemented then leave this commented out
# and leave 'force-reload' as an alias for 'restart'.
#
#log_daemon_msg "Reloading $DESC" "$NAME"
#do_reload
#log_end_msg $?
#;;
restart|force-reload)
#
# If the "reload" option is implemented then remove the
# 'force-reload' alias
#
log_daemon_msg "Restarting $DESC" "$NAME"
do_stop
case "$?" in
0|1)
do_start
case "$?" in
0) log_end_msg 0 ;;
1) log_end_msg 1 ;; # Old process is still running
*) log_end_msg 1 ;; # Failed to start
esac
;;
*)
# Failed to stop
log_end_msg 1
;;
esac
;;
*)
#echo "Usage: $SCRIPTNAME {start|stop|restart|reload|force-reload}" >&2
echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" >&2
exit 3
;;
esac
:

38
debian/postinst vendored Executable file
View File

@@ -0,0 +1,38 @@
#!/bin/sh -e
case "$1" in
configure)
# Create the osmocom group and user (if it doesn't exist yet)
if ! getent group osmocom >/dev/null; then
groupadd --system osmocom
fi
if ! getent passwd osmocom >/dev/null; then
useradd \
--system \
--gid osmocom \
--home-dir /var/lib/osmocom \
--shell /sbin/nologin \
--comment "Open Source Mobile Communications" \
osmocom
fi
# Fix permissions of previous (root-owned) install (OS#4107)
if dpkg --compare-versions "$2" le "1.12.0"; then
if [ -e /etc/osmocom/osmo-sgsn.cfg ]; then
chown -v osmocom:osmocom /etc/osmocom/osmo-sgsn.cfg
chmod -v 0660 /etc/osmocom/osmo-sgsn.cfg
fi
if [ -d /etc/osmocom ]; then
chown -v root:osmocom /etc/osmocom
chmod -v 2775 /etc/osmocom
fi
mkdir -p /var/lib/osmocom
chown -R -v osmocom:osmocom /var/lib/osmocom
fi
;;
esac
# dh_installdeb(1) will replace this with shell code automatically
# generated by other debhelper scripts.
#DEBHELPER#

View File

@@ -5,6 +5,14 @@
! For the test, try to use most config commands.
!
log stderr
logging color 1
logging print category-hex 0
logging print category 1
logging timestamp 0
logging print file basename last
logging print level 1
line vty
no login

View File

@@ -2,6 +2,14 @@
! Osmocom gtphub configuration
!
log stderr
logging color 1
logging print category-hex 0
logging print category 1
logging timestamp 0
logging print file basename last
logging print level 1
line vty
no login

View File

@@ -2,10 +2,19 @@
! Osmocom SGSN configuration
!
!
log stderr
logging color 1
logging print category-hex 0
logging print category 1
logging timestamp 0
logging print file basename last
logging print level 1
line vty
no login
!
sgsn
gtp state-dir /var/lib/osmocom/osmo-sgsn
gtp local-ip 127.0.0.1
ggsn 0 remote-ip 127.0.0.2
ggsn 0 gtp-version 1

View File

@@ -2,10 +2,19 @@
! Osmocom SGSN configuration
!
!
log stderr
logging color 1
logging print category-hex 0
logging print category 1
logging timestamp 0
logging print file basename last
logging print level 1
line vty
no login
!
sgsn
gtp state-dir /var/lib/osmocom/osmo-sgsn
gtp local-ip 127.0.0.1
ggsn 0 remote-ip 127.0.0.2
ggsn 0 gtp-version 1

View File

@@ -2,6 +2,14 @@
! Osmocom SGSN configuration
!
!
log stderr
logging color 1
logging print category-hex 0
logging print category 1
logging timestamp 0
logging print file basename last
logging print level 1
line vty
no login
!
@@ -15,6 +23,7 @@ cs7 instance 0
asp asp-clnt-OsmoSGSN-A
routing-key 3 0.23.4
sgsn
gtp state-dir /var/lib/osmocom/osmo-sgsn
gtp local-ip 127.0.0.1
ggsn 0 remote-ip 127.0.0.2
ggsn 0 gtp-version 1

View File

@@ -15,6 +15,14 @@
Conversion to asciidoc, removal of sysmoBTS specific parts.
</revremark>
</revision>
<revision>
<revnumber>3</revnumber>
<date>April 2024</date>
<authorinitials>AC</authorinitials>
<revremark>
Replace NS chapter with new NS2 chapter to match the code.
</revremark>
</revision>
</revhistory>
<authorgroup>
@@ -29,10 +37,21 @@
<jobtitle>Managing Director</jobtitle>
</affiliation>
</author>
<author>
<firstname>Alexander</firstname>
<surname>Couzens</surname>
<email>acouzens@sysmocom.de</email>
<authorinitials>AC</authorinitials>
<affiliation>
<shortaffil>sysmocom</shortaffil>
<orgname>sysmocom - s.f.m.c. GmbH</orgname>
<jobtitle>Developer</jobtitle>
</affiliation>
</author>
</authorgroup>
<copyright>
<year>2013-2016</year>
<year>2013-2024</year>
<holder>sysmocom - s.f.m.c. GmbH</holder>
</copyright>
@@ -47,8 +66,8 @@
</para>
<para>
The Asciidoc source code of this manual can be found at
<ulink url="http://git.osmocom.org/osmo-gsm-manuals/">
http://git.osmocom.org/osmo-gsm-manuals/
<ulink url="https://git.osmocom.org/osmo-gsm-manuals/">
https://git.osmocom.org/osmo-gsm-manuals/
</ulink>
</para>
</legalnotice>

View File

@@ -21,7 +21,7 @@ include::{srcdir}/chapters/configuration.adoc[]
include::./common/chapters/cs7-config.adoc[]
include::./common/chapters/gb.adoc[]
include::./common/chapters/gb-ns2.adoc[]
include::./common/chapters/control_if.adoc[]

View File

@@ -8,12 +8,14 @@ noinst_HEADERS = \
gprs_gmm.h \
gprs_gmm_fsm.h \
gprs_gmm_attach.h \
gprs_gmm_util.h \
gprs_mm_state_gb_fsm.h \
gprs_mm_state_iu_fsm.h \
gprs_ns.h \
gprs_llc.h \
gprs_llc_xid.h \
gprs_ranap.h \
gprs_routing_area.h \
gprs_sm.h \
gprs_sndcp_comp.h \
gprs_sndcp_dcomp.h \
@@ -25,8 +27,12 @@ noinst_HEADERS = \
gtp.h \
gtp_ggsn.h \
gtp_mme.h \
iu_client.h \
iu_rnc.h \
iu_rnc_fsm.h \
mmctx.h \
pdpctx.h \
sccp.h \
sgsn.h \
sgsn_rim.h \
signal.h \

View File

@@ -22,11 +22,11 @@ enum {
DRANAP,
DSUA,
DV42BIS,
DIUCS,
DSIGTRAN,
DGTP,
DOBJ,
DRIM,
DRA, /* Routing Area handling */
Debug_LastEntry,
};

View File

@@ -2,11 +2,14 @@
#include <osmocom/core/msgb.h>
struct osmo_prim_hdr;
struct sgsn_mm_ctx;
/* Called by bssgp layer when a prim is received from lower layers. */
int sgsn_bssgp_rx_prim(struct osmo_prim_hdr *oph);
/* called by the bssgp layer to send NS PDUs */
int sgsn_bssgp_dispatch_ns_unitdata_req_cb(void *ctx, struct msgb *msg);
/* page a MS in its routing area */
int sgsn_bssgp_page_ps_ra(struct sgsn_mm_ctx *mmctx);
/* page a MS in a single cell */
int sgsn_bssgp_page_ps_bvci(struct sgsn_mm_ctx *mmctx, uint16_t nsei, uint16_t bvci);

View File

@@ -2,6 +2,7 @@
#define _GPRS_GMM_H
#include <stdbool.h>
#include <stdint.h>
#include <osmocom/core/msgb.h>
#include <osmocom/gsm/gsm48.h>
@@ -9,6 +10,7 @@
struct sgsn_mm_ctx;
struct gprs_llc_llme;
struct osmo_routing_area_id;
int gsm48_tx_gmm_auth_ciph_req(struct sgsn_mm_ctx *mm,
const struct osmo_auth_vector *vec,
@@ -28,12 +30,16 @@ void gsm0408_gprs_access_denied(struct sgsn_mm_ctx *mmctx, int gmm_cause);
void gsm0408_gprs_access_cancelled(struct sgsn_mm_ctx *mmctx, int gmm_cause);
void gsm0408_gprs_authenticate(struct sgsn_mm_ctx *mmctx);
int gprs_gmm_rx_suspend(struct gprs_ra_id *raid, uint32_t tlli);
int gprs_gmm_rx_resume(struct gprs_ra_id *raid, uint32_t tlli,
int gprs_gmm_rx_suspend(struct osmo_routing_area_id *raid, uint32_t tlli);
int gprs_gmm_rx_resume(struct osmo_routing_area_id *raid, uint32_t tlli,
uint8_t suspend_ref);
int gsm0408_gprs_rcvmsg_gb(struct msgb *msg, struct gprs_llc_llme *llme,
bool drop_cipherable);
#ifdef BUILD_IU
int gsm0408_gprs_rcvmsg_iu(struct msgb *msg, struct gprs_ra_id *ra_id, uint16_t *sai);
#endif /* ifdef BUILD_IU */
/* Has to be called whenever any PDU (signaling, data, ...) has been received */
void gprs_gb_recv_pdu(struct sgsn_mm_ctx *mmctx, const struct msgb *msg);
@@ -44,7 +50,7 @@ int gsm48_tx_gmm_att_rej(struct sgsn_mm_ctx *mm,
uint8_t gmm_cause);
int gsm48_tx_gmm_att_ack(struct sgsn_mm_ctx *mm);
int gprs_gmm_attach_req_ies(struct msgb *a, struct msgb *b);
int gprs_gmm_msg_cmp(struct msgb *a, struct msgb *b);
int gsm48_gmm_authorize(struct sgsn_mm_ctx *ctx);
/* TODO: move extract_subscr_* when gsm48_gmm_authorize() got removed */

View File

@@ -0,0 +1,42 @@
#pragma once
#include <stdbool.h>
#include <stdint.h>
#include <osmocom/gsm/gsm23003.h>
#include <osmocom/gsm/gsm48.h>
#include <osmocom/gsm/tlv.h>
struct msgb;
extern const struct tlv_definition gsm48_gmm_ie_tlvdef;
/* 9.4.14 RAU Request */
struct gprs_gmm_ra_upd_req {
uint8_t skip_ind; /* 10.3.1 */
uint8_t update_type; /* 10.5.5.18 */
bool follow_up_req; /* 10.5.5.18 */
uint8_t cksq; /* 10.5.1.2 */
struct osmo_routing_area_id old_rai; /* 10.5.5.15 */
uint8_t *ms_radio_cap; /* 10.5.5.12a */
uint8_t ms_radio_cap_len;
struct tlv_parsed tlv;
};
struct gprs_gmm_att_req {
uint8_t skip_ind; /* 10.3.1 */
uint8_t attach_type; /* 10.5.5.2 */
bool follow_up_req; /* 10.5.5.2 */
uint8_t cksq; /* 10.5.1.2 */
uint16_t drx_parms; /* 10.5.5.6 */
struct osmo_mobile_identity mi; /* 10.5.1.4 */
struct osmo_routing_area_id old_rai; /* 10.5.5.15 */
uint8_t *ms_network_cap; /* 10.5.5.12 */
uint8_t ms_network_cap_len;
uint8_t *ms_radio_cap; /* 10.5.5.12a */
uint8_t ms_radio_cap_len;
struct tlv_parsed tlv;
};
int gprs_gmm_parse_ra_upd_req(const struct msgb *msg, struct gprs_gmm_ra_upd_req *rau_req);
int gprs_gmm_parse_att_req(const struct msgb *msg, struct gprs_gmm_att_req *att_req);

View File

@@ -17,6 +17,7 @@ enum mm_state_iu_fsm_events {
E_PMM_PS_CONN_RELEASE,
E_PMM_PS_CONN_ESTABLISH,
E_PMM_RA_UPDATE, /* = Serving RNC relocation */
E_PMM_RX_GGSN_GTPU_DT_EI, /* param: struct sgsn_pdp_ctx *pctx */
};
extern struct osmo_fsm mm_state_iu_fsm;

View File

@@ -1,32 +1,67 @@
#pragma once
#include "config.h"
#include <osmocom/core/msgb.h>
#ifdef BUILD_IU
#include <osmocom/ranap/ranap_ies_defs.h>
#include <osmocom/ranap/ranap_msg_factory.h>
#include <osmocom/ranap/iu_client.h>
#include <osmocom/sgsn/iu_client.h>
#include <osmocom/sgsn/sccp.h>
struct sgsn_mm_ctx;
struct sgsn_pdp_ctx;
void activate_pdp_rabs(struct sgsn_mm_ctx *ctx);
/* struct RANAP_GlobalRNC_ID with a coupled buffer where .buf points to.
* Used to easily generate a struct RANAP_GlobalRNC_ID to encode,
* see sgsn_ranap_iu_grnc_id_compose(). */
struct iu_grnc_id {
uint8_t plmn_buf[3];
struct RANAP_GlobalRNC_ID grnc_id;
};
int sgsn_ranap_iu_grnc_id_compose(struct iu_grnc_id *dst, const struct osmo_rnc_id *src);
int sgsn_ranap_iu_event(struct ranap_ue_conn_ctx *ctx, enum ranap_iu_event_type type, void *data);
int iu_rab_act_ps(uint8_t rab_id, struct sgsn_pdp_ctx *pdp);
/* free the Iu UE context */
void sgsn_ranap_iu_free(struct sgsn_mm_ctx *ctx);
int sgsn_ranap_iu_tx(struct msgb *msg, uint8_t sapi);
int sgsn_ranap_iu_tx_rab_ps_ass_req(struct ranap_ue_conn_ctx *ue_ctx,
uint8_t rab_id, uint32_t gtp_ip, uint32_t gtp_tei);
int sgsn_ranap_iu_tx_sec_mode_cmd(struct ranap_ue_conn_ctx *uectx, struct osmo_auth_vector *vec,
int send_ck, int new_key);
int sgsn_ranap_iu_tx_common_id(struct ranap_ue_conn_ctx *ue_ctx, const char *imsi);
/* send a Iu Release Command and free afterwards the UE context */
void sgsn_ranap_iu_release_free(struct sgsn_mm_ctx *ctx,
const struct RANAP_Cause *cause);
int sgsn_ranap_iu_tx_release(struct ranap_ue_conn_ctx *ctx, const struct RANAP_Cause *cause);
/* Transmit a Iu Release Command and submit event RANAP_IU_EVENT_IU_RELEASE upon
* Release Complete or timeout. Caller is responsible to free the context and
* closing the SCCP connection (sgsn_ranap_iu_free_ue) upon recieval of the event. */
void sgsn_ranap_iu_tx_release_free(struct ranap_ue_conn_ctx *ctx,
const struct RANAP_Cause *cause,
int timeout);
int gsm0408_gprs_rcvmsg_iu(struct msgb *msg, struct gprs_ra_id *ra_id, uint16_t *sai);
int sgsn_ranap_iu_tx_cl(struct sgsn_sccp_user_iups *scu_iups,
const struct osmo_sccp_addr *dst_addr,
struct msgb *msg);
int sgsn_ranap_iu_tx_error_ind(struct sgsn_sccp_user_iups *scu_iups,
const struct osmo_sccp_addr *dst_addr,
const RANAP_Cause_t *cause);
#else /* ifndef BUILD_IU */
inline static void sgsn_ranap_iu_free(void *ctx) {};
inline static void sgsn_ranap_iu_release_free(void *ctx, void *cause) {};
#endif /* BUILD_IU*/
void sgsn_ranap_iu_handle_co_initial(struct ranap_iu_rnc *iu_rnc,
uint32_t conn_id,
const ranap_message *message);
void sgsn_ranap_iu_handle_co(struct ranap_ue_conn_ctx *ue_ctx, const ranap_message *message);
/* Entry points from rx SCCP: */
int sgsn_ranap_iu_rx_cl_msg(struct sgsn_sccp_user_iups *scu_iups,
const struct osmo_scu_unitdata_param *ud_prim,
const uint8_t *data, size_t len);
int sgsn_ranap_iu_rx_co_initial_msg(struct sgsn_sccp_user_iups *scu_iups,
const struct osmo_sccp_addr *rem_sccp_addr,
uint32_t conn_id,
const uint8_t *data, size_t len);
int sgsn_ranap_iu_rx_co_msg(struct ranap_ue_conn_ctx *ue_ctx, const uint8_t *data, size_t len);
#endif /* ifdef BUILD_IU */
struct ranap_ue_conn_ctx;
/* On RANAP, Returns pointer to he associated ranap_ue_conn_ctx in msg, filled

View File

@@ -0,0 +1,140 @@
/*! \file gprs_routing_area.h */
#pragma once
#include <stdbool.h>
#include <stdint.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/utils.h>
#include <osmocom/gsm/gsm23003.h>
/* rai -> struct osmo_routing_area_id * */
#define LOGRAI(level, rai, fmt, args...) \
do {\
char __log_rai_buf[32]; \
LOGP(DRA, level, "RA(%s) " fmt, \
osmo_rai_name2_buf(__log_rai_buf, sizeof(__log_rai_buf), rai), \
## args); \
} while (0)
/* ra -> struct sgsn_ra * */
#define LOGRA(level, ra, fmt, args...) \
LOGRAI(level, (&(ra)->rai), fmt, ## args)
struct sgsn_instance;
struct sgsn_mm_ctx;
struct sgsn_ra_global {
/* list of struct sgsn_ra */
struct llist_head ra_list;
};
enum sgsn_ra_ran_type {
RA_TYPE_GERAN_Gb,
RA_TYPE_UTRAN_Iu,
};
extern const struct value_string sgsn_ra_ran_type_names[];
struct sgsn_ra {
/* Entry in sgsn_ra_global->ra_list */
struct llist_head list;
struct osmo_routing_area_id rai;
/* For GERAN: every PCU is connected to the SGSN. It allows the SGSN to know every single cell.
* For routing, the SGSN must know to which PCU a given cell is connected.
* It is possible that more than one PCU serves the same Routing Area.
*
* For UTRAN: only the RNC (HNB via HNBGW) is communicating with the SGSN.
* The SGSN doesn't know every cell, because they aren't accepted individually by the SGSN.
* The SGSN only "knows" RAI/SAI if they have been used. In the future it would be a good idea to
* allow configuring RA in the vty/config as well.
* Similar to the GERAN Cell, but iu_client doesn't notify us for every given SAI, only for RAC.
* Further the SGSN doesn't get informed about Service Area and can't relate the SAI to a given UE.
* For UTRAN only do a LAC/RAC <> RNC relation and don't have a specific cell relation.
*/
enum sgsn_ra_ran_type ran_type;
union {
struct {
/* the RNC id must be the same for a given Routing Area */
struct osmo_rnc_id rnc_id;
} utran;
} u;
/* GERAN/UTRAN: cells contains a list of sgsn_ra_cells which are alive */
struct llist_head cells_alive_list;
};
struct sgsn_ra_cell {
/* Entry in sgsn_ra->cells */
struct llist_head list;
/*! link back to the parent */
struct sgsn_ra *ra;
enum sgsn_ra_ran_type ran_type;
union {
struct {
uint16_t nsei;
uint16_t bvci;
uint16_t cell_id;
} geran;
struct {
/* the RNC id must be the same for a given Routing Area */
uint16_t sac;
} utran;
} u;
};
void sgsn_ra_init(struct sgsn_instance *inst);
struct sgsn_ra *sgsn_ra_alloc(const struct osmo_routing_area_id *rai, enum sgsn_ra_ran_type ran_type);
struct sgsn_ra *sgsn_ra_find_or_create(const struct osmo_routing_area_id *rai, enum sgsn_ra_ran_type ran_type);
struct sgsn_ra *sgsn_ra_get_ra(const struct osmo_routing_area_id *rai);
void sgsn_ra_free(struct sgsn_ra *ra);
struct sgsn_ra_cell *sgsn_ra_cell_alloc_geran(struct sgsn_ra *ra, uint16_t cell_id, uint16_t nsei, uint16_t bvci);
void sgsn_ra_cell_free(struct sgsn_ra_cell *cell);
/* GERAN */
/* Called by BSSGP layer to inform about a reset on a PtP BVCI */
int sgsn_ra_geran_bvc_cell_reset_ind(uint16_t nsei, uint16_t bvci, struct osmo_cell_global_id_ps *cgi_ps);
/* Called by BSSGP layer to inform about a reset on a Signal BVCI */
void sgsn_ra_geran_bvc_sign_reset_ind(uint16_t nsei);
/* FIXME: handle BVC BLOCK/UNBLOCK/UNAVAILABLE */
/* Called by NS-VC layer to inform about an unavailable NSEI (and all BVCI on them) */
int sgsn_ra_geran_nsei_failure_ind(uint16_t nsei);
struct sgsn_ra_cell *sgsn_ra_geran_get_cell_by_cgi_ps(const struct osmo_cell_global_id_ps *cgi_ps);
struct sgsn_ra_cell *sgsn_ra_geran_get_cell_by_lai(const struct osmo_location_area_id *lai, uint16_t cell_id);
struct sgsn_ra_cell *sgsn_ra_geran_get_cell_by_cgi(const struct osmo_cell_global_id *cgi);
struct sgsn_ra_cell *sgsn_ra_geran_get_cell_by_ra(const struct sgsn_ra *ra, uint16_t cell_id);
struct sgsn_ra_cell *sgsn_ra_geran_get_cell_by_gb(uint16_t nsei, uint16_t bvci);
/* UTRAN */
int sgsn_ra_utran_register(const struct osmo_routing_area_id *rai, const struct osmo_rnc_id *rnc_id);
struct sgsn_ra *sgsn_ra_geran_get_ra(const struct osmo_routing_area_id *rai);
/* Page the whole routing area for this mmctx */
int sgsn_ra_geran_page_ra(const struct osmo_routing_area_id *rai, struct sgsn_mm_ctx *mmctx);
struct sgsn_ra *sgsn_ra_utran_get_ra(const struct osmo_routing_area_id *rai);
/*
* return value for callbacks.
* STOP: stop calling the callback for the remaining cells, sgsn_ra_foreach_ra() returns 0
* CONT: continue to call the callback for remaining cells
* ABORT: stop calling the callback for the remaining cells, sgsn_ra_foreach_ra() returns -1
*/
#define SGSN_RA_CB_STOP 1
#define SGSN_RA_CB_CONT 0
#define SGSN_RA_CB_ERROR -1
typedef int (sgsn_ra_cb_t)(struct sgsn_ra_cell *ra_cell, void *cb_data);
int sgsn_ra_foreach_cell(struct sgsn_ra *ra, sgsn_ra_cb_t *cb, void *cb_data);
int sgsn_ra_foreach_cell2(struct osmo_routing_area_id *rai, sgsn_ra_cb_t *cb, void *cb_data);
/* Page the whole routing area for this mmctx */
int sgsn_ra_utran_page_ra(const struct osmo_routing_area_id *rai, const struct sgsn_mm_ctx *mmctx);

View File

@@ -47,7 +47,7 @@ struct gprs_sndcp_entity {
struct llist_head list;
/* FIXME: move this RA_ID up to the LLME or even higher */
struct gprs_ra_id ra_id;
struct osmo_routing_area_id rai;
/* reference to the LLC Entity below this SNDCP entity */
struct gprs_llc_lle *lle;
/* The NSAPI we shall use on top of LLC */

View File

@@ -4,7 +4,9 @@
#include <stdint.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/socket.h>
#include <osmocom/gsm/protocol/gsm_23_003.h>
#include <osmocom/gsm/protocol/gsm_04_08_gprs.h>
#include <osmocom/sgsn/apn.h>
@@ -54,7 +56,9 @@ struct sgsn_subscriber_pdp_data {
struct llist_head list;
unsigned int context_id;
uint16_t pdp_type;
enum gsm48_pdp_type_org pdp_type_org;
enum gsm48_pdp_type_nr pdp_type_nr;
struct osmo_sockaddr pdp_address[2];
char apn_str[GSM_APN_LENGTH];
uint8_t qos_subscribed[20];
size_t qos_subscribed_len;

View File

@@ -23,8 +23,7 @@ struct sgsn_pdp_ctx *sgsn_create_pdp_ctx(struct sgsn_ggsn_ctx *ggsn,
uint16_t nsapi,
struct tlv_parsed *tp);
int sgsn_gtp_data_req(struct gprs_ra_id *ra_id, int32_t tlli, uint8_t nsapi,
int sgsn_gtp_data_req(struct osmo_routing_area_id *rai, int32_t tlli, uint8_t nsapi,
struct msgb *msg, uint32_t npdu_len, uint8_t *npdu);
int sgsn_delete_pdp_ctx(struct sgsn_pdp_ctx *pctx);
void sgsn_pdp_upd_gtp_u(struct sgsn_pdp_ctx *pdp, void *addr, size_t alen);
int send_act_pdp_cont_acc(struct sgsn_pdp_ctx *pctx);

View File

@@ -5,6 +5,7 @@
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/timer.h>
#include <osmocom/gprs/protocol/gsm_24_301.h>
#include <osmocom/gsm/gsm23003.h>
struct gsn_t;
@@ -20,6 +21,9 @@ struct sgsn_mme_ctx {
char *name;
struct in_addr remote_addr;
struct osmo_gummei gummei;
bool gummei_valid;
/* is it the default route for outgoing message? are all incoming messages accepted? */
bool default_route;
};
@@ -30,6 +34,7 @@ void sgsn_mme_ctx_free(struct sgsn_mme_ctx *mme);
struct sgsn_mme_ctx *sgsn_mme_ctx_by_name(const struct sgsn_instance *sgsn, const char *name);
struct sgsn_mme_ctx *sgsn_mme_ctx_by_addr(const struct sgsn_instance *sgsn, const struct in_addr *addr);
struct sgsn_mme_ctx *sgsn_mme_ctx_by_route(const struct sgsn_instance *sgsn, const struct osmo_eutran_tai *tai);
struct sgsn_mme_ctx *sgsn_mme_ctx_by_gummei(const struct sgsn_instance *sgsn, const struct osmo_gummei *gummei);
struct sgsn_mme_ctx *sgsn_mme_ctx_by_default_route(const struct sgsn_instance *sgsn);
void sgsn_mme_ctx_route_add(struct sgsn_mme_ctx *mme, const struct osmo_eutran_tai *tai);

View File

@@ -0,0 +1,95 @@
#pragma once
#include <stdbool.h>
#include <osmocom/core/defs.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/gsm/gsm48.h>
#include <osmocom/iuh/common.h>
#include <osmocom/ranap/ranap_common.h>
#include <osmocom/ranap/vty.h>
#include <osmocom/sigtran/sccp_sap.h>
struct msgb;
struct osmo_auth_vector;
struct RANAP_RAB_SetupOrModifiedItemIEs_s;
struct ranap_iu_rnc;
struct ranap_ue_conn_ctx {
struct llist_head list; /* item in sgsn_sccp->ue_conn_ctx_list */
struct ranap_iu_rnc *rnc;
uint32_t conn_id;
int integrity_active;
struct gprs_ra_id ra_id;
enum ranap_nsap_addr_enc rab_assign_addr_enc;
bool notification; /* send notification to the upstream user */
/* if true the ue_ctx will be free on Iu release complete */
bool free_on_release;
/* Will be set when the Iu Release Command has been sent */
struct osmo_timer_list release_timeout;
};
enum ranap_iu_event_new_area_type {
RANAP_IU_NEW_LAC,
RANAP_IU_NEW_RAC,
};
struct ranap_iu_event_new_area {
const struct osmo_rnc_id *rnc_id;
enum ranap_iu_event_new_area_type cell_type;
union {
const struct osmo_location_area_id *lai;
const struct osmo_routing_area_id *rai;
} u;
};
enum ranap_iu_event_type {
RANAP_IU_EVENT_RAB_ASSIGN,
RANAP_IU_EVENT_SECURITY_MODE_COMPLETE,
RANAP_IU_EVENT_IU_RELEASE, /* An actual Iu Release message was received */
RANAP_IU_EVENT_LINK_INVALIDATED, /* A SUA link was lost or closed down */
RANAP_IU_EVENT_NEW_AREA, /* Either a new LAC/RAC has been detected */
};
extern const struct value_string iu_client_event_type_names[];
static inline const char *iu_client_event_type_str(enum ranap_iu_event_type e)
{
return get_value_string(iu_client_event_type_names, e);
}
/* Implementations of iu_recv_cb_t shall find the ranap_ue_conn_ctx in msg->dst. */
typedef int (*ranap_iu_recv_cb_t)(struct msgb *msg, struct gprs_ra_id *ra_id,
uint16_t *sai);
typedef int (*ranap_iu_event_cb_t)(struct ranap_ue_conn_ctx *ue_ctx,
enum ranap_iu_event_type type, void *data);
typedef int (*ranap_iu_rab_ass_resp_cb_t)(struct ranap_ue_conn_ctx *ue_ctx, uint8_t rab_id,
struct RANAP_RAB_SetupOrModifiedItemIEs_s *setup_ies);
int global_iu_event(struct ranap_ue_conn_ctx *ue_ctx,
enum ranap_iu_event_type type,
void *data);
int ranap_iu_init(void *ctx);
int ranap_iu_page_cs(const char *imsi, const uint32_t *tmsi, uint16_t lac)
OSMO_DEPRECATED("Use ranap_iu_page_cs2 instead");
int ranap_iu_page_ps(const char *imsi, const uint32_t *ptmsi, uint16_t lac, uint8_t rac)
OSMO_DEPRECATED("Use ranap_iu_page_ps2 instead");
int ranap_iu_page_cs2(const char *imsi, const uint32_t *tmsi, const struct osmo_location_area_id *lai);
int ranap_iu_page_ps2(const char *imsi, const uint32_t *ptmsi, const struct osmo_routing_area_id *rai);
struct ranap_ue_conn_ctx *ue_conn_ctx_alloc(struct ranap_iu_rnc *rnc, uint32_t conn_id);
void ue_conn_ctx_link_invalidated_free(struct ranap_ue_conn_ctx *ue);
/* freeing the UE will release all resources
* This will close the SCCP connection connected to the UE */
void sgsn_ranap_iu_free_ue(struct ranap_ue_conn_ctx *ue_ctx);

View File

@@ -0,0 +1,57 @@
#pragma once
#include <stdbool.h>
#include <stdint.h>
#include <osmocom/core/defs.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/fsm.h>
#include <osmocom/gsm/gsm48.h>
#include <osmocom/iuh/common.h>
#include <osmocom/sigtran/sccp_sap.h>
struct iu_lac_rac_entry {
struct llist_head entry;
struct osmo_routing_area_id rai;
};
/* A remote RNC (Radio Network Controller, like BSC but for UMTS) that has
* called us and is currently reachable at the given osmo_sccp_addr. So, when we
* know a LAC for a subscriber, we can page it at the RNC matching that LAC or
* RAC. An HNB-GW typically presents itself as if it were a single RNC, even
* though it may have several RNCs in hNodeBs connected to it. Those will then
* share the same RNC id, which they actually receive and adopt from the HNB-GW
* in the HNBAP HNB REGISTER ACCEPT message. */
struct ranap_iu_rnc {
struct llist_head entry;
struct osmo_rnc_id rnc_id;
struct sgsn_sccp_user_iups *scu_iups;
struct osmo_sccp_addr sccp_addr;
struct osmo_fsm_inst *fi;
/* A list of struct iu_lac_rac_entry */
struct llist_head lac_rac_list;
};
struct ranap_iu_rnc *iu_rnc_find_or_create(const struct osmo_rnc_id *rnc_id,
struct sgsn_sccp_user_iups *scu_iups,
const struct osmo_sccp_addr *addr);
struct ranap_iu_rnc *iu_rnc_find_by_addr(const struct osmo_sccp_addr *rnc_sccp_addr);
void iu_rnc_update_rai_seen(struct ranap_iu_rnc *rnc, const struct osmo_routing_area_id *rai);
void iu_rnc_discard_all_ue_ctx(struct ranap_iu_rnc *rnc);
int iu_rnc_tx_paging_cmd(struct ranap_iu_rnc *rnc,
const char *imsi,
const uint32_t *tmsi,
bool is_ps,
uint32_t paging_cause);
#define LOG_RNC_CAT(IU_RNC, subsys, loglevel, fmt, args ...) \
LOGPFSMSL((IU_RNC)->fi, subsys, loglevel, fmt, ## args)
#define LOG_RNC(IU_RNC, loglevel, fmt, args ...) \
LOG_RNC_CAT(IU_RNC, DRANAP, loglevel, fmt, ## args)

View File

@@ -0,0 +1,37 @@
#include <stdint.h>
#include <osmocom/core/fsm.h>
#include <osmocom/ranap/ranap_ies_defs.h>
struct ranap_iu_rnc;
enum iu_rnc_state {
IU_RNC_ST_WAIT_RX_RESET = 0,
IU_RNC_ST_WAIT_RX_RESET_ACK,
IU_RNC_ST_READY,
IU_RNC_ST_DISCARDING,
};
struct iu_rnc_ev_msg_up_co_initial_ctx {
struct ranap_iu_rnc *rnc;
uint32_t conn_id;
ranap_message message;
};
struct iu_rnc_ev_msg_up_co_ctx {
struct ranap_ue_conn_ctx *ue_ctx;
ranap_message message;
};
enum iu_rnc_event {
IU_RNC_EV_MSG_UP_CO_INITIAL, /* struct iu_rnc_ev_msg_up_co_initial_ctx* */
IU_RNC_EV_MSG_UP_CO, /* struct iu_rnc_ev_msg_up_co_ctx* */
IU_RNC_EV_RX_RESET, /* no param */
IU_RNC_EV_RX_RESET_ACK, /* no param */
IU_RNC_EV_MSG_DOWN_CL, /* struct msgb* */
IU_RNC_EV_AVAILABLE,
IU_RNC_EV_UNAVAILABLE
};
extern struct osmo_fsm iu_rnc_fsm;

View File

@@ -1,5 +1,7 @@
#pragma once
#include "config.h"
#include <stdint.h>
#include <netinet/in.h>
#include <inttypes.h>
@@ -105,7 +107,7 @@ struct sgsn_mm_ctx {
char imei[GSM23003_IMEISV_NUM_DIGITS+1];
/* Opt: Software Version Numbber / TS 23.195 */
char msisdn[GSM_EXTENSION_LENGTH];
struct gprs_ra_id ra;
struct osmo_routing_area_id ra;
struct {
uint16_t cell_id; /* Gb only */
uint32_t cell_id_age; /* Gb only */
@@ -231,7 +233,8 @@ static inline bool sgsn_mm_ctx_is_authenticated(struct sgsn_mm_ctx *ctx)
#ifdef BUILD_IU
#define LOGIUP(ue, level, fmt, args...) \
LOGP(DMM, level, "UE(0x%x){%s} " fmt, ue->conn_id, osmo_rai_name(&(ue)->ra_id), ## args)
LOGP(DMM, level, "UE(0x%x){%s} " fmt, (ue) ? (ue)->conn_id : 0xffffffff, \
(ue) ? osmo_rai_name(&(ue)->ra_id) : "", ## args)
#else
#define LOGIUP(ue, level, fmt, args...) \
LOGP(DMM, level, "UE(%p){NOTSUPPORTED} " fmt, ue, ## args)
@@ -253,18 +256,19 @@ static inline bool sgsn_mm_ctx_is_authenticated(struct sgsn_mm_ctx *ctx)
/* look-up a SGSN MM context based on TLLI + RAI */
struct sgsn_mm_ctx *sgsn_mm_ctx_by_tlli(uint32_t tlli,
const struct gprs_ra_id *raid);
const struct osmo_routing_area_id *raid);
struct sgsn_mm_ctx *sgsn_mm_ctx_by_ptmsi(uint32_t tmsi);
struct sgsn_mm_ctx *sgsn_mm_ctx_by_imsi(const char *imsi);
struct sgsn_mm_ctx *sgsn_mm_ctx_by_ue_ctx(const void *uectx);
struct sgsn_mm_ctx *sgsn_mm_ctx_by_llme(const struct gprs_llc_llme *llme);
/* look-up by matching TLLI and P-TMSI (think twice before using this) */
struct sgsn_mm_ctx *sgsn_mm_ctx_by_tlli_and_ptmsi(uint32_t tlli,
const struct gprs_ra_id *raid);
const struct osmo_routing_area_id *raid);
/* Allocate a new SGSN MM context */
struct sgsn_mm_ctx *sgsn_mm_ctx_alloc_gb(uint32_t tlli,
const struct gprs_ra_id *raid);
const struct osmo_routing_area_id *raid);
struct sgsn_mm_ctx *sgsn_mm_ctx_alloc_iu(void *uectx);
void sgsn_mm_ctx_cleanup_free(struct sgsn_mm_ctx *ctx);
@@ -281,7 +285,17 @@ struct sgsn_pdp_ctx *sgsn_pdp_ctx_by_nsapi(const struct sgsn_mm_ctx *mm,
struct sgsn_pdp_ctx *sgsn_pdp_ctx_by_tid(const struct sgsn_mm_ctx *mm,
uint8_t tid);
bool sgsn_mm_ctx_is_r99(const struct sgsn_mm_ctx *mm);
uint32_t sgsn_alloc_ptmsi(void);
/* Called on subscriber data updates */
void sgsn_update_subscriber_data(struct sgsn_mm_ctx *mmctx);
#ifdef BUILD_IU
struct RANAP_Cause;
void sgsn_mm_ctx_iu_activate_rabs(struct sgsn_mm_ctx *ctx);
void sgsn_mm_ctx_iu_ranap_release_free(struct sgsn_mm_ctx *ctx,
const struct RANAP_Cause *cause);
void sgsn_mm_ctx_iu_ranap_free(struct sgsn_mm_ctx *ctx);
#endif /* ifdef BUILD_IU */

View File

@@ -1,5 +1,7 @@
#pragma once
#include "config.h"
#include <stdint.h>
#include <netinet/in.h>
#include <inttypes.h>
@@ -68,6 +70,10 @@ struct sgsn_pdp_ctx {
//uint32_t qos_profile_neg;
uint8_t radio_prio;
//uint32_t charging_id;
bool ue_pdp_active; /* PDP Context is active for this NSAPI? */
/* Keeps original SGSN local TEID when lib->teid_own is updated with
* RNC's TEID upon use of Direct Tunnel feature: */
uint32_t sgsn_teid_own;
struct osmo_timer_list timer;
unsigned int T; /* Txxxx number */
@@ -90,5 +96,10 @@ struct sgsn_pdp_ctx *sgsn_pdp_ctx_alloc(struct sgsn_mm_ctx *mm,
void sgsn_pdp_ctx_terminate(struct sgsn_pdp_ctx *pdp);
void sgsn_pdp_ctx_free(struct sgsn_pdp_ctx *pdp);
#ifdef BUILD_IU
int sgsn_pdp_ctx_iu_rab_activate(struct sgsn_pdp_ctx *pdp, uint8_t rab_id);
int sgsn_pdp_ctx_iu_rab_deactivate(struct sgsn_pdp_ctx *pdp, uint8_t rab_id);
#endif /* ifdef BUILD_IU */
char *gprs_pdpaddr2str(uint8_t *pdpa, uint8_t len, bool return_ipv6);

View File

@@ -0,0 +1,46 @@
/* SCCP Handling */
/* (C) 2025 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#pragma once
#include <stdint.h>
#include <osmocom/sigtran/sccp_sap.h>
struct sgsn_instance;
struct ranap_ue_conn_ctx;
struct sgsn_sccp_user_iups {
struct sgsn_instance *sgsn; /* backpointer */
struct osmo_sccp_instance *sccp; /* backpointer */
struct osmo_sccp_user *scu; /* IuPS */
struct osmo_sccp_addr local_sccp_addr;
struct llist_head ue_conn_ctx_list; /* list of "struct ranap_ue_conn_ctx" */
struct llist_head ue_conn_sccp_addr_list; /* list of "struct iu_new_ctx_entry" */
};
struct sgsn_sccp_user_iups *sgsn_scu_iups_inst_alloc(struct sgsn_instance *sgsn, struct osmo_sccp_instance *sccp);
void sgsn_scu_iups_free(struct sgsn_sccp_user_iups *scu_iups);
int sgsn_scu_iups_tx_data_req(struct sgsn_sccp_user_iups *scu_iups, uint32_t conn_id, struct msgb *ranap_msg);
struct ranap_ue_conn_ctx *sgsn_scu_iups_ue_conn_ctx_find(struct sgsn_sccp_user_iups *scu_iups, uint32_t conn_id);
int sgsn_sccp_init(struct sgsn_instance *sgsn);
void sgsn_sccp_release(struct sgsn_instance *sgsn);

View File

@@ -1,9 +1,11 @@
#ifndef _SGSN_H
#define _SGSN_H
#include "config.h"
#include <osmocom/core/msgb.h>
#include <osmocom/core/select.h>
#include <osmocom/core/stat_item.h>
#include <osmocom/crypt/gprs_cipher.h>
#include <osmocom/gprs/gprs_ns2.h>
#include <osmocom/gprs/gprs_bssgp.h>
@@ -14,19 +16,21 @@
#include <osmocom/gsupclient/gsup_client.h>
#include <osmocom/sgsn/common.h>
#include "../../config.h"
#if BUILD_IU
#include <osmocom/ranap/iu_client.h>
#include <osmocom/sigtran/sccp_sap.h>
#include <osmocom/sgsn/iu_client.h>
#endif
#include <ares.h>
#include <gtp.h>
#include <osmocom/gtp/gtp.h>
struct hostent;
#define SGSN_ERROR_CAUSE_NONE (-1)
/* This rac will be used internally. RAC with 0xff will be rejected */
#define OSMO_RESERVED_RAC 0xff
enum sgsn_auth_policy {
SGSN_AUTH_POLICY_OPEN,
SGSN_AUTH_POLICY_CLOSED,
@@ -152,20 +156,51 @@ struct sgsn_instance {
ares_channel ares_channel;
struct ares_addr_node *ares_servers;
/* Routing areas */
struct sgsn_ra_global *routing_area;
struct rate_ctr_group *rate_ctrs;
struct osmo_stat_item_group *statg;
struct llist_head apn_list; /* list of struct sgsn_apn_ctx */
struct llist_head ggsn_list; /* list of struct sgsn_ggsn_ctx */
struct llist_head mme_list; /* list of struct sgsn_mme_ctx */
struct llist_head mm_list; /* list of struct sgsn_mm_ctx */
struct llist_head pdp_list; /* list of struct sgsn_pdp_ctx */
#if BUILD_IU
struct llist_head rnc_list; /* list of struct ranap_iu_rnc */
#endif /* if BUILD_IU */
struct ctrl_handle *ctrlh;
#if BUILD_IU
/* SCCP (Iu) */
struct {
struct osmo_sccp_instance *sccp;
struct sgsn_sccp_user_iups *scu_iups;
} sccp;
#endif /* if BUILD_IU */
};
extern struct osmo_tdef sgsn_T_defs[];
extern struct sgsn_instance *sgsn;
extern void *tall_sgsn_ctx;
enum {
SGSN_STAT_IU_PEERS_TOTAL,
SGSN_STAT_IU_PEERS_ACTIVE,
};
static inline void sgsn_stat_inc(unsigned int idx, int32_t value)
{
osmo_stat_item_inc(osmo_stat_item_group_get_item(sgsn->statg, idx), value);
}
static inline void sgsn_stat_dec(unsigned int idx, int32_t value)
{
osmo_stat_item_dec(osmo_stat_item_group_get_item(sgsn->statg, idx), value);
}
/*
* ctrl interface related work (sgsn_ctrl.c)
*/

View File

@@ -16,7 +16,7 @@
app_configs = {
"sgsn": ["doc/examples/osmo-sgsn/osmo-sgsn.cfg"],
"sgsn": ["tests/osmo-sgsn.cfg"],
"gtphub": ["doc/examples/osmo-gtphub/osmo-gtphub-1iface.cfg"]
}
@@ -25,7 +25,6 @@ apps = [(4245, "src/sgsn/osmo-sgsn", "OsmoSGSN", "sgsn"),
(4253, "src/gtphub/osmo-gtphub", "OsmoGTPhub", "gtphub")
]
vty_command = ["./src/sgsn/osmo-sgsn", "-c",
"doc/examples/osmo-sgsn/osmo-sgsn.cfg"]
vty_command = ["./src/sgsn/osmo-sgsn", "-c", "tests/osmo-sgsn.cfg"]
vty_app = apps[0]

View File

@@ -29,8 +29,8 @@
#include <netinet/in.h>
#include <arpa/inet.h>
#include <gtp.h>
#include <gtpie.h>
#include <osmocom/gtp/gtp.h>
#include <osmocom/gtp/gtpie.h>
#include <osmocom/gtphub/gtphub.h>
#include <osmocom/sgsn/debug.h>

View File

@@ -45,8 +45,10 @@ osmo_sgsn_SOURCES = \
gprs_gmm_attach.c \
gprs_gmm.c \
gprs_gmm_fsm.c \
gprs_gmm_util.c \
gprs_mm_state_gb_fsm.c \
gprs_ns.c \
gprs_routing_area.c \
gprs_sm.c \
gprs_sndcp.c \
gprs_sndcp_comp.c \
@@ -79,7 +81,6 @@ osmo_sgsn_LDADD = \
$(top_builddir)/src/gprs/gprs_utils.o \
$(top_builddir)/src/gprs/sgsn_ares.o \
$(OSMO_LIBS) \
$(LIBOSMOABIS_LIBS) \
$(LIBOSMOGSUPCLIENT_LIBS) \
$(LIBCARES_LIBS) \
$(LIBGTP_LIBS) \
@@ -95,6 +96,11 @@ osmo_sgsn_LDADD += \
osmo_sgsn_SOURCES += \
gprs_mm_state_iu_fsm.c \
gprs_ranap.c
gprs_ranap.c \
iu_client.c \
iu_rnc.c \
iu_rnc_fsm.c \
sccp.c \
$(NULL)
endif

View File

@@ -20,21 +20,49 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <osmocom/core/prim.h>
#include <osmocom/core/rate_ctr.h>
#include <osmocom/gprs/gprs_bssgp.h>
#include <osmocom/gprs/gprs_ns2.h>
#include <osmocom/gsm/gsm48.h>
#include <osmocom/sgsn/gprs_llc.h>
#include <osmocom/sgsn/gprs_gmm.h>
#include <osmocom/sgsn/gprs_routing_area.h>
#include <osmocom/sgsn/sgsn_rim.h>
#include <osmocom/sgsn/mmctx.h>
#include <osmocom/sgsn/debug.h>
static int bssgp_nm_bvc_reset_ind(struct osmo_bssgp_prim *bp)
{
struct osmo_cell_global_id_ps cgi_ps = {};
/* Signalling: BVCI == 0, no CGI-PS
* PtP BVCI: BVCI != 0, CGI-PS */
if (bp->bvci == 0) {
sgsn_ra_geran_bvc_sign_reset_ind(bp->nsei);
return 0;
}
if (!bp->tp)
return -EINVAL;
if (!TLVP_PRES_LEN(bp->tp, BSSGP_IE_CELL_ID, 8))
return -EINVAL;
bssgp_parse_cell_id2(&cgi_ps.rai, &cgi_ps.cell_identity, TLVP_VAL(bp->tp, BSSGP_IE_CELL_ID), 8);
return sgsn_ra_geran_bvc_cell_reset_ind(bp->nsei, bp->bvci, &cgi_ps);
}
/* call-back function for the BSSGP protocol */
int sgsn_bssgp_rx_prim(struct osmo_prim_hdr *oph)
{
struct osmo_bssgp_prim *bp;
struct osmo_routing_area_id rai = {};
bp = container_of(oph, struct osmo_bssgp_prim, oph);
switch (oph->sap) {
@@ -45,15 +73,28 @@ int sgsn_bssgp_rx_prim(struct osmo_prim_hdr *oph)
}
break;
case SAP_BSSGP_GMM:
gprs_rai_to_osmo(&rai, bp->ra_id);
switch (oph->primitive) {
case PRIM_BSSGP_GMM_SUSPEND:
return gprs_gmm_rx_suspend(bp->ra_id, bp->tlli);
return gprs_gmm_rx_suspend(&rai, bp->tlli);
case PRIM_BSSGP_GMM_RESUME:
return gprs_gmm_rx_resume(bp->ra_id, bp->tlli,
return gprs_gmm_rx_resume(&rai, bp->tlli,
bp->u.resume.suspend_ref);
}
break;
case SAP_BSSGP_NM:
switch (oph->primitive) {
case PRIM_NM_BVC_RESET:
if (oph->operation == PRIM_OP_INDICATION)
bssgp_nm_bvc_reset_ind(bp);
break;
case PRIM_NM_BVC_BLOCK:
case PRIM_NM_BVC_UNBLOCK:
case PRIM_NM_STATUS:
case PRIM_NM_LLC_DISCARDED:
break;
}
break;
case SAP_BSSGP_RIM:
return sgsn_rim_rx_from_gb(bp, oph->msg);
@@ -61,26 +102,20 @@ int sgsn_bssgp_rx_prim(struct osmo_prim_hdr *oph)
return 0;
}
int sgsn_bssgp_page_ps_ra(struct sgsn_mm_ctx *mmctx)
int sgsn_bssgp_page_ps_bvci(struct sgsn_mm_ctx *mmctx, uint16_t nsei, uint16_t bvci)
{
struct bssgp_paging_info pinfo;
int rc;
/* FIXME: page whole routing area, not only the last known cell */
/* initiate PS PAGING procedure */
memset(&pinfo, 0, sizeof(pinfo));
pinfo.mode = BSSGP_PAGING_PS;
pinfo.scope = BSSGP_PAGING_BVCI;
pinfo.bvci = mmctx->gb.bvci;
pinfo.bvci = bvci;
pinfo.imsi = mmctx->imsi;
pinfo.ptmsi = &mmctx->p_tmsi;
pinfo.drx_params = mmctx->drx_parms;
pinfo.qos[0] = 0; // FIXME
rc = bssgp_tx_paging(mmctx->gb.nsei, 0, &pinfo);
rate_ctr_inc(rate_ctr_group_get_ctr(mmctx->ctrg, GMM_CTR_PAGING_PS));
return rc;
return bssgp_tx_paging(nsei, 0, &pinfo);
}
/* called by the bssgp layer to send NS PDUs */

View File

@@ -44,6 +44,8 @@
#include <osmocom/crypt/utran_cipher.h>
#include <osmocom/gsm/protocol/gsm_04_08_gprs.h>
#include <osmocom/gtp/pdp.h>
#include <osmocom/gprs/gprs_bssgp.h>
#include <osmocom/sgsn/debug.h>
@@ -63,30 +65,10 @@
#include <osmocom/sgsn/gprs_sm.h>
#include <osmocom/sgsn/gtp.h>
#include <osmocom/sgsn/pdpctx.h>
#include <pdp.h>
#include <osmocom/sgsn/gprs_gmm_util.h>
#define PTMSI_ALLOC
static const struct tlv_definition gsm48_gmm_att_tlvdef = {
.def = {
[GSM48_IE_GMM_CIPH_CKSN] = { TLV_TYPE_SINGLE_TV, 1 },
[GSM48_IE_GMM_TIMER_READY] = { TLV_TYPE_TV, 1 },
[GSM48_IE_GMM_ALLOC_PTMSI] = { TLV_TYPE_TLV, 0 },
[GSM48_IE_GMM_PTMSI_SIG] = { TLV_TYPE_FIXED, 3 },
[GSM48_IE_GMM_AUTH_RAND] = { TLV_TYPE_FIXED, 16 },
[GSM48_IE_GMM_AUTH_SRES] = { TLV_TYPE_FIXED, 4 },
[GSM48_IE_GMM_AUTH_RES_EXT] = { TLV_TYPE_TLV, 0 },
[GSM48_IE_GMM_AUTH_FAIL_PAR] = { TLV_TYPE_TLV, 0 },
[GSM48_IE_GMM_IMEISV] = { TLV_TYPE_TLV, 0 },
[GSM48_IE_GMM_RX_NPDU_NUM_LIST] = { TLV_TYPE_TLV, 0 },
[GSM48_IE_GMM_DRX_PARAM] = { TLV_TYPE_FIXED, 2 },
[GSM48_IE_GMM_MS_NET_CAPA] = { TLV_TYPE_TLV, 0 },
[GSM48_IE_GMM_PDP_CTX_STATUS] = { TLV_TYPE_TLV, 0 },
[GSM48_IE_GMM_PS_LCS_CAPA] = { TLV_TYPE_TLV, 0 },
[GSM48_IE_GMM_GMM_MBMS_CTX_ST] = { TLV_TYPE_TLV, 0 },
},
};
/* Our implementation, should be kept in SGSN */
@@ -111,9 +93,14 @@ static void mmctx_timer_start(struct sgsn_mm_ctx *mm, unsigned int T)
static void mmctx_timer_stop(struct sgsn_mm_ctx *mm, unsigned int T)
{
if (mm->T != T)
if (!osmo_timer_pending(&mm->timer)) {
LOGMMCTXP(LOGL_NOTICE, mm, "Stopping *inactive* MM timer %u\n", T);
return;
}
if (mm->T != T) {
LOGMMCTXP(LOGL_ERROR, mm, "Stopping MM timer %u but "
"%u is running\n", T, mm->T);
}
osmo_timer_del(&mm->timer);
}
@@ -136,13 +123,13 @@ int gsm48_gmm_sendmsg(struct msgb *msg, int command,
rate_ctr_inc(rate_ctr_group_get_ctr(mm->ctrg, GMM_CTR_PKTS_SIG_OUT));
#ifdef BUILD_IU
if (mm->ran_type == MM_CTX_T_UTRAN_Iu)
return ranap_iu_tx(msg, GPRS_SAPI_GMM);
return sgsn_ranap_iu_tx(msg, GPRS_SAPI_GMM);
#endif
}
#ifdef BUILD_IU
if (MSG_IU_UE_CTX(msg))
return ranap_iu_tx(msg, GPRS_SAPI_GMM);
return sgsn_ranap_iu_tx(msg, GPRS_SAPI_GMM);
#endif
/* caller needs to provide TLLI, BVCI and NSEI */
@@ -209,6 +196,61 @@ static void mm_ctx_cleanup_free(struct sgsn_mm_ctx *ctx, const char *log_text)
sgsn_mm_ctx_cleanup_free(ctx);
}
/* 3GPP TS 24.008 § 10.5.7.1 Process PDP context status value, bit 0 corresponds to nsapi 0 */
static void process_ms_ctx_status(struct sgsn_mm_ctx *mmctx,
uint16_t pdp_status)
{
struct sgsn_pdp_ctx *pdp, *pdp2;
/* 24.008 4.7.5.1.3: If the PDP context status information element is
* included in ROUTING AREA UPDATE REQUEST message, then the network
* shall deactivate all those PDP contexts locally (without peer to
* peer signalling between the MS and the network), which are not in SM
* state PDP-INACTIVE on network side but are indicated by the MS as
* being in state PDP-INACTIVE. */
/* NSAPI 0 - 4 are spare, ignore these */
pdp_status &= 0xffe0;
llist_for_each_entry_safe(pdp, pdp2, &mmctx->pdp_list, list) {
bool active = (pdp_status & (1 << pdp->nsapi));
if (active)
continue;
LOGMMCTXP(LOGL_NOTICE, mmctx, "Dropping PDP context for NSAPI=%u "
"due to PDP CTX STATUS IE=0x%02x\n",
pdp->nsapi, pdp_status);
pdp->ue_pdp_active = false;
if (pdp->ggsn)
sgsn_delete_pdp_ctx(pdp);
else /* GTP side already detached, freeing */
sgsn_pdp_ctx_free(pdp);
}
}
/* 3GPP TS 24.008 § 10.5.7.1 Encode PDP context status value, bit 0 correspond to nsapi 0 */
uint16_t encode_ms_ctx_status(struct sgsn_mm_ctx *mmctx)
{
struct sgsn_pdp_ctx *pdp;
uint16_t pdp_status = 0;
llist_for_each_entry(pdp, &mmctx->pdp_list, list) {
if (pdp->ue_pdp_active)
pdp_status |= (1 << pdp->nsapi);
}
return pdp_status;
}
/* 3GPP TS 24.008 § 4.7.13.4/10.5.7.1 Service request procedure not accepted by the
* network. Returns true if MS has active PDP contexts in pdp_status */
bool pdp_status_has_active_nsapis(uint16_t pdp_status)
{
/* NSAPI 0 - 4 are spare and should be ignored 0 */
return (pdp_status >> 5) != 0;
}
/* Chapter 9.4.18 */
static int _tx_status(struct msgb *msg, uint8_t cause,
struct sgsn_mm_ctx *mmctx)
@@ -307,7 +349,7 @@ int gsm48_tx_gmm_att_ack(struct sgsn_mm_ctx *mm)
t = osmo_tdef_get(sgsn->cfg.T_defs, 3312, OSMO_TDEF_S, -1);
aa->ra_upd_timer = gprs_secs_to_tmr_floor(t);
aa->radio_prio = 0x44; /* lowest */
gsm48_encode_ra(&aa->ra_id, &mm->ra);
osmo_routing_area_id_encode_buf((uint8_t *) &aa->ra_id, sizeof(struct gsm48_ra_id), &mm->ra);
#if 0
/* Optional: P-TMSI signature */
@@ -437,16 +479,6 @@ int gsm48_tx_gmm_id_req(struct sgsn_mm_ctx *mm, uint8_t id_type)
return gsm48_gmm_sendmsg(msg, 1, mm, false);
}
/* determine if the MS/UE supports R99 or later */
static bool mmctx_is_r99(const struct sgsn_mm_ctx *mm)
{
if (mm->ms_network_capa.len < 1)
return false;
if (mm->ms_network_capa.buf[0] & 0x01)
return true;
return false;
}
static enum gprs_ciph_algo gprs_ms_net_select_best_gea(uint8_t net_mask, uint8_t ms_mask) {
uint8_t common_mask = net_mask & ms_mask;
uint8_t r = 0;
@@ -472,8 +504,8 @@ int gsm48_tx_gmm_auth_ciph_req(struct sgsn_mm_ctx *mm,
LOGMMCTXP(LOGL_INFO, mm, "<- GMM AUTH AND CIPHERING REQ (rand = %s,"
" mmctx_is_r99=%d, vec->auth_types=0x%x",
osmo_hexdump(vec->rand, sizeof(vec->rand)),
mmctx_is_r99(mm), vec->auth_types);
if (mmctx_is_r99(mm) && vec
sgsn_mm_ctx_is_r99(mm), vec->auth_types);
if (sgsn_mm_ctx_is_r99(mm) && vec
&& (vec->auth_types & OSMO_AUTH_TYPE_UMTS)) {
LOGPC(DMM, LOGL_INFO, ", autn = %s)\n",
osmo_hexdump(vec->autn, sizeof(vec->autn)));
@@ -516,7 +548,7 @@ int gsm48_tx_gmm_auth_ciph_req(struct sgsn_mm_ctx *mm,
* the optional AUTN IE. If a classic GSM SIM is
* inserted, it will simply ignore AUTN and just use
* RAND */
if (mmctx_is_r99(mm) &&
if (sgsn_mm_ctx_is_r99(mm) &&
(vec->auth_types & OSMO_AUTH_TYPE_UMTS)) {
msgb_tlv_put(msg, GSM48_IE_GMM_AUTN,
sizeof(vec->autn), vec->autn);
@@ -560,7 +592,7 @@ static enum osmo_sub_auth_type check_auth_resp(struct sgsn_mm_ctx *ctx,
* However, on GERAN, even if we sent a UMTS AKA Authentication Request, the MS may decide to
* instead reply with a GSM AKA SRES response. */
if (is_utran
|| (mmctx_is_r99(ctx) && (vec->auth_types & OSMO_AUTH_TYPE_UMTS)
|| (sgsn_mm_ctx_is_r99(ctx) && (vec->auth_types & OSMO_AUTH_TYPE_UMTS)
&& (res_len > sizeof(vec->sres)))) {
expect_type = OSMO_AUTH_TYPE_UMTS;
expect_str = "UMTS RES";
@@ -605,6 +637,7 @@ static int gsm48_rx_gmm_auth_ciph_resp(struct sgsn_mm_ctx *ctx,
{
struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(msg);
struct gsm48_auth_ciph_resp *acr = (struct gsm48_auth_ciph_resp *)gh->data;
struct osmo_mobile_identity mi;
struct tlv_parsed tp;
struct gsm_auth_tuple *at;
const char *res_name = "(no response)";
@@ -630,7 +663,7 @@ static int gsm48_rx_gmm_auth_ciph_resp(struct sgsn_mm_ctx *ctx,
/* Stop T3360 */
mmctx_timer_stop(ctx, 3360);
tlv_parse(&tp, &gsm48_gmm_att_tlvdef, acr->data,
tlv_parse(&tp, &gsm48_gmm_ie_tlvdef, acr->data,
(msg->data + msg->len) - acr->data, 0, 0);
if (!TLVP_PRESENT(&tp, GSM48_IE_GMM_AUTH_SRES) ||
@@ -641,6 +674,28 @@ static int gsm48_rx_gmm_auth_ciph_resp(struct sgsn_mm_ctx *ctx,
return -EINVAL;
}
/* IMEI SV parsing */
if (TLVP_LEN(&tp, GSM48_IE_GMM_IMEISV) != 9) {
LOGMMCTXP(LOGL_ERROR, ctx, "-> GMM AUTH AND CIPH RESPONSE: Invalid IMEISV\n");
return -EINVAL;
}
/* FIXME: should we allow invalid IMEI SV? Are there phones which have broken SV in hex? */
if (osmo_mobile_identity_decode(&mi,
TLVP_VAL(&tp, GSM48_IE_GMM_IMEISV),
TLVP_LEN(&tp, GSM48_IE_GMM_IMEISV),
false)) {
LOGMMCTXP(LOGL_ERROR, ctx, "-> GMM AUTH AND CIPH RESPONSE: Cannot decode IMEISV\n");
return -EINVAL;
}
if (mi.type != GSM_MI_TYPE_IMEISV) {
LOGMMCTXP(LOGL_ERROR, ctx, "-> GMM AUTH AND CIPH RESPONSE: Invalid MI type found in IMEISV %d\n", mi.type);
return -EINVAL;
}
memcpy(&ctx->imei, mi.imeisv, ARRAY_SIZE(ctx->imei));
/* Start with the good old 4-byte SRES */
memcpy(res, TLVP_VAL(&tp, GSM48_IE_GMM_AUTH_SRES), 4);
res_len = 4;
@@ -689,7 +744,7 @@ static int gsm48_rx_gmm_auth_ciph_fail(struct sgsn_mm_ctx *ctx,
LOGMMCTXP(LOGL_INFO, ctx, "-> GMM AUTH AND CIPH FAILURE (cause = %s)\n",
get_value_string(gsm48_gmm_cause_names, gmm_cause));
tlv_parse(&tp, &gsm48_gmm_att_tlvdef, gh->data+1, msg->len - 1, 0, 0);
tlv_parse(&tp, &gsm48_gmm_ie_tlvdef, gh->data+1, msg->len - 1, 0, 0);
/* Only if GMM cause is present and the AUTS is provided, we can
* start re-sync procedure */
@@ -924,7 +979,7 @@ int gsm48_gmm_authorize(struct sgsn_mm_ctx *ctx)
send_ck ? "sending" : "not sending", sgsn->cfg.uea_encryption_mask);
/* FIXME: we should send the set of allowed UEA, as in ranap_new_msg_sec_mod_cmd2(). However, this
* is not possible in the iu_client API. See OS#5487. */
rc = ranap_iu_tx_sec_mode_cmd(ctx->iu.ue_ctx, &ctx->auth_triplet.vec, send_ck, ctx->iu.new_key);
rc = sgsn_ranap_iu_tx_sec_mode_cmd(ctx->iu.ue_ctx, &ctx->auth_triplet.vec, send_ck, ctx->iu.new_key);
ctx->iu.new_key = 0;
return rc;
}
@@ -959,7 +1014,7 @@ int gsm48_gmm_authorize(struct sgsn_mm_ctx *ctx)
rc = gsm48_tx_gmm_service_ack(ctx);
if (ctx->iu.service.type != GPRS_SERVICE_T_SIGNALLING)
activate_pdp_rabs(ctx);
sgsn_mm_ctx_iu_activate_rabs(ctx);
return rc;
#endif
@@ -1169,6 +1224,10 @@ static void mmctx_handle_rat_change(struct sgsn_mm_ctx *mmctx, struct msgb *msg,
static uint8_t gprs_ms_net_cap_gea_mask(const uint8_t *ms_net_cap, uint8_t cap_len)
{
uint8_t mask = (1 << GPRS_ALGO_GEA0);
if (cap_len == 0)
return mask;
mask |= (0x80 & ms_net_cap[0]) ? (1 << GPRS_ALGO_GEA1) : 0;
if (cap_len < 2)
@@ -1190,7 +1249,7 @@ static int gsm48_rx_gmm_att_req(struct sgsn_mm_ctx *ctx, struct msgb *msg,
uint8_t msnc_len, att_type, mi_len, ms_ra_acc_cap_len;
uint16_t drx_par;
char mi_log_string[32];
struct gprs_ra_id ra_id;
struct osmo_routing_area_id rai;
uint16_t cid = 0;
enum gsm48_gmm_cause reject_cause;
struct osmo_mobile_identity mi;
@@ -1205,10 +1264,10 @@ static int gsm48_rx_gmm_att_req(struct sgsn_mm_ctx *ctx, struct msgb *msg,
if (!MSG_IU_UE_CTX(msg)) {
/* Gb mode */
cid = bssgp_parse_cell_id(&ra_id, msgb_bcid(msg));
bssgp_parse_cell_id2(&rai, &cid, msgb_bcid(msg), 8);
} else {
#ifdef BUILD_IU
ra_id = MSG_IU_UE_CTX(msg)->ra_id;
gprs_rai_to_osmo(&rai, &MSG_IU_UE_CTX(msg)->ra_id);
#else
LOGMMCTXP(LOGL_ERROR, ctx, "Cannot handle Iu Attach Request, built without Iu support\n");
return -ENOTSUP;
@@ -1271,7 +1330,7 @@ static int gsm48_rx_gmm_att_req(struct sgsn_mm_ctx *ctx, struct msgb *msg,
if (MSG_IU_UE_CTX(msg))
ctx = sgsn_mm_ctx_alloc_iu(MSG_IU_UE_CTX(msg));
else
ctx = sgsn_mm_ctx_alloc_gb(0, &ra_id);
ctx = sgsn_mm_ctx_alloc_gb(0, &rai);
if (!ctx) {
reject_cause = GMM_CAUSE_NET_FAIL;
goto rejected;
@@ -1289,7 +1348,7 @@ static int gsm48_rx_gmm_att_req(struct sgsn_mm_ctx *ctx, struct msgb *msg,
if (MSG_IU_UE_CTX(msg))
ctx = sgsn_mm_ctx_alloc_iu(MSG_IU_UE_CTX(msg));
else
ctx = sgsn_mm_ctx_alloc_gb(msgb_tlli(msg), &ra_id);
ctx = sgsn_mm_ctx_alloc_gb(msgb_tlli(msg), &rai);
if (!ctx) {
reject_cause = GMM_CAUSE_NET_FAIL;
goto rejected;
@@ -1313,7 +1372,7 @@ static int gsm48_rx_gmm_att_req(struct sgsn_mm_ctx *ctx, struct msgb *msg,
}
msgid2mmctx(ctx, msg);
/* Update MM Context with currient RA and Cell ID */
ctx->ra = ra_id;
ctx->ra = rai;
if (ctx->ran_type == MM_CTX_T_GERAN_Gb)
ctx->gb.cell_id = cid;
@@ -1391,11 +1450,11 @@ static int gsm48_rx_gmm_att_compl(struct sgsn_mm_ctx *mmctx)
/* only in case SGSN offered new P-TMSI */
LOGMMCTXP(LOGL_INFO, mmctx, "-> GMM ATTACH COMPLETE\n");
#ifdef BUILD_IU
#ifdef BUILD_IU
if (mmctx->iu.ue_ctx) {
ranap_iu_tx_release(mmctx->iu.ue_ctx, NULL);
sgsn_ranap_iu_tx_release(mmctx->iu.ue_ctx, NULL);
}
#endif
#endif
mmctx_timer_stop(mmctx, 3350);
mmctx->t3350_mode = GMM_T3350_MODE_NONE;
@@ -1424,25 +1483,18 @@ static int gsm48_rx_gmm_att_compl(struct sgsn_mm_ctx *mmctx)
return 0;
}
/* Checks if two attach request contain the IEs and IE values
/* Checks if two GMM are the same (required diffing Attach Requests/RAU Requests
* return 0 if equal
* return -1 if error
* return 1 if unequal
*
* Only do a simple memcmp for now.
*/
int gprs_gmm_attach_req_ies(struct msgb *a, struct msgb *b)
int gprs_gmm_msg_cmp(struct msgb *a, struct msgb *b)
{
struct gsm48_hdr *gh_a = (struct gsm48_hdr *) msgb_gmmh(a);
struct gsm48_hdr *gh_b = (struct gsm48_hdr *) msgb_gmmh(b);
#define GMM_ATTACH_REQ_LEN 26
if (msgb_l3len(a) != msgb_l3len(b))
return 2;
/* there is the LLC FCS behind */
if (msgb_l3len(a) < GMM_ATTACH_REQ_LEN || msgb_l3len(b) < GMM_ATTACH_REQ_LEN)
return -1;
return !!memcmp(gh_a, gh_b, GMM_ATTACH_REQ_LEN);
return memcmp(gh_a, gh_b, msgb_l3len(a));
}
/* 3GPP TS 24.008 § 4.7.4.1 / 9.4.5.2 MO Detach request */
@@ -1507,10 +1559,12 @@ static int gsm48_tx_gmm_ra_upd_ack(struct sgsn_mm_ctx *mm)
rua = (struct gsm48_ra_upd_ack *) msgb_put(msg, sizeof(*rua));
rua->force_stby = 0; /* not indicated */
rua->upd_result = 0; /* RA updated */
/* Periodic RA update timer */
t = osmo_tdef_get(sgsn->cfg.T_defs, 3312, OSMO_TDEF_S, -1);
rua->ra_upd_timer = gprs_secs_to_tmr_floor(t);
gsm48_encode_ra(&rua->ra_id, &mm->ra);
osmo_routing_area_id_encode_buf((uint8_t *)&rua->ra_id, sizeof(struct gsm48_ra_id), &mm->ra);
#if 0
/* Optional: P-TMSI signature */
@@ -1535,12 +1589,19 @@ static int gsm48_tx_gmm_ra_upd_ack(struct sgsn_mm_ctx *mm)
}
*l = rc;
#endif
/* MS identity */
/* List of Received N-PDU */
/* Optional: Negotiated READY timer value */
t = osmo_tdef_get(sgsn->cfg.T_defs, 3314, OSMO_TDEF_S, -1);
msgb_tv_put(msg, GSM48_IE_GMM_TIMER_READY, gprs_secs_to_tmr_floor(t));
/* Option: MS ID, ... */
/* GMM cause */
/* PDP Context Status */
uint16_t pdp_ctx_status = encode_ms_ctx_status(mm);
msgb_tlv_put(msg, GSM48_IE_GMM_PDP_CTX_STATUS, 2, (uint8_t *) &pdp_ctx_status);
/* MS ID, ... */
return gsm48_gmm_sendmsg(msg, 0, mm, true);
}
@@ -1565,47 +1626,6 @@ int gsm48_tx_gmm_ra_upd_rej(struct msgb *old_msg, uint8_t cause)
return gsm48_gmm_sendmsg(msg, 0, NULL, false);
}
static void process_ms_ctx_status(struct sgsn_mm_ctx *mmctx,
const uint8_t *pdp_status)
{
struct sgsn_pdp_ctx *pdp, *pdp2;
/* 24.008 4.7.5.1.3: If the PDP context status information element is
* included in ROUTING AREA UPDATE REQUEST message, then the network
* shall deactivate all those PDP contexts locally (without peer to
* peer signalling between the MS and the network), which are not in SM
* state PDP-INACTIVE on network side but are indicated by the MS as
* being in state PDP-INACTIVE. */
llist_for_each_entry_safe(pdp, pdp2, &mmctx->pdp_list, list) {
bool inactive = (pdp->nsapi < 8) ?
!(pdp_status[0] & (1 << pdp->nsapi)) :
!(pdp_status[1] & (1 << (pdp->nsapi - 8)));
if (!inactive)
continue;
LOGMMCTXP(LOGL_NOTICE, mmctx, "Dropping PDP context for NSAPI=%u "
"due to PDP CTX STATUS IE=0x%02x%02x\n",
pdp->nsapi, pdp_status[1], pdp_status[0]);
if (pdp->ggsn)
sgsn_delete_pdp_ctx(pdp);
else /* GTP side already detached, freeing */
sgsn_pdp_ctx_free(pdp);
}
}
/* 3GPP TS 24.008 § 4.7.13.4 Service request procedure not accepted by the
* network. Returns true if MS has active PDP contexts in pdp_status */
bool pdp_status_has_active_nsapis(const uint8_t *pdp_status, const size_t pdp_status_len)
{
size_t i;
for (i = 0; i < pdp_status_len; i++)
if (pdp_status[i] != 0)
return true;
return false;
}
/* Chapter 9.4.14: Routing area update request */
static int gsm48_rx_gmm_ra_upd_req(struct sgsn_mm_ctx *mmctx, struct msgb *msg,
struct gprs_llc_llme *llme)
@@ -1613,52 +1633,30 @@ static int gsm48_rx_gmm_ra_upd_req(struct sgsn_mm_ctx *mmctx, struct msgb *msg,
#ifndef PTMSI_ALLOC
struct sgsn_signal_data sig_data;
#endif
struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(msg);
uint8_t *cur = gh->data;
uint8_t ms_ra_acc_cap_len;
struct gprs_ra_id old_ra_id;
struct tlv_parsed tp;
uint8_t upd_type;
enum gsm48_gmm_cause reject_cause = GMM_CAUSE_PROTO_ERR_UNSPEC;
struct gprs_gmm_ra_upd_req req;
int rc;
rc = gprs_gmm_parse_ra_upd_req(msg, &req);
if (rc) {
reject_cause = rc;
goto rejected;
}
/* TODO: In iu mode - handle follow-on request.
* The follow-on request can be signaled in an Attach Request on IuPS.
* This means the MS/UE asks to keep the PS connection open for further requests
* after the Attach Request succeed.
* The SGSN can decide if it close the connection or not. Both are spec conform. */
/* Update Type 10.5.5.18 */
upd_type = *cur++ & 0x07;
rate_ctr_inc(rate_ctr_group_get_ctr(sgsn->rate_ctrs, CTR_GPRS_ROUTING_AREA_REQUEST));
LOGMMCTXP(LOGL_INFO, mmctx, "-> GMM RA UPDATE REQUEST type=\"%s\"\n",
get_value_string(gprs_upd_t_strs, upd_type));
get_value_string(gprs_upd_t_strs, req.update_type));
/* Old routing area identification 10.5.5.15 */
gsm48_parse_ra(&old_ra_id, cur);
cur += 6;
/* MS Radio Access Capability 10.5.5.12a */
ms_ra_acc_cap_len = *cur++;
if (ms_ra_acc_cap_len > 52) {
LOGMMCTXP(LOGL_ERROR, mmctx,
"Rejecting GMM RA Update Request: MS Radio Access Capability too long"
" (ms_ra_acc_cap_len = %u > 52)\n", ms_ra_acc_cap_len);
reject_cause = GMM_CAUSE_PROTO_ERR_UNSPEC;
goto rejected;
}
cur += ms_ra_acc_cap_len;
/* Optional: Old P-TMSI Signature, Requested READY timer, TMSI Status,
* DRX parameter, MS network capability */
tlv_parse(&tp, &gsm48_gmm_att_tlvdef, cur,
(msg->data + msg->len) - cur, 0, 0);
switch (upd_type) {
switch (req.update_type) {
case GPRS_UPD_T_RA_LA:
case GPRS_UPD_T_RA_LA_IMSI_ATT:
LOGMMCTXP(LOGL_NOTICE, mmctx, "Update type %i unsupported in Mode III, is your SI13 corrupt?\n", upd_type);
LOGMMCTXP(LOGL_NOTICE, mmctx, "Update type %d unsupported in Mode III, is your SI13 corrupt?\n", req.update_type);
reject_cause = GMM_CAUSE_PROTO_ERR_UNSPEC;
goto rejected;
case GPRS_UPD_T_RA:
@@ -1677,13 +1675,13 @@ static int gsm48_rx_gmm_ra_upd_req(struct sgsn_mm_ctx *mmctx, struct msgb *msg,
/* Look-up the MM context based on old RA-ID and TLLI */
if (!MSG_IU_UE_CTX(msg)) {
/* Gb */
mmctx = sgsn_mm_ctx_by_tlli_and_ptmsi(msgb_tlli(msg), &old_ra_id);
} else if (TLVP_PRESENT(&tp, GSM48_IE_GMM_ALLOC_PTMSI)) {
mmctx = sgsn_mm_ctx_by_tlli_and_ptmsi(msgb_tlli(msg), &req.old_rai);
} else if (TLVP_PRESENT(&req.tlv, GSM48_IE_GMM_ALLOC_PTMSI)) {
#ifdef BUILD_IU
/* In Iu mode search only for ptmsi */
struct osmo_mobile_identity mi;
if (osmo_mobile_identity_decode(&mi, TLVP_VAL(&tp, GSM48_IE_GMM_ALLOC_PTMSI),
TLVP_LEN(&tp, GSM48_IE_GMM_ALLOC_PTMSI), false)
if (osmo_mobile_identity_decode(&mi, TLVP_VAL(&req.tlv, GSM48_IE_GMM_ALLOC_PTMSI),
TLVP_LEN(&req.tlv, GSM48_IE_GMM_ALLOC_PTMSI), false)
|| mi.type != GSM_MI_TYPE_TMSI) {
LOGIUP(MSG_IU_UE_CTX(msg), LOGL_ERROR, "Cannot decode P-TMSI\n");
goto rejected;
@@ -1703,7 +1701,7 @@ static int gsm48_rx_gmm_ra_upd_req(struct sgsn_mm_ctx *mmctx, struct msgb *msg,
msgb_tlli(msg),
mmctx->p_tmsi, mmctx->p_tmsi_old,
mmctx->gb.tlli, mmctx->gb.tlli_new,
osmo_rai_name(&mmctx->ra));
osmo_rai_name2(&mmctx->ra));
/* A RAT change will trigger the common procedure
* below after handling the RAT change. Protect it
* here from being called twice */
@@ -1711,26 +1709,26 @@ static int gsm48_rx_gmm_ra_upd_req(struct sgsn_mm_ctx *mmctx, struct msgb *msg,
osmo_fsm_inst_dispatch(mmctx->gmm_fsm, E_GMM_COMMON_PROC_INIT_REQ, NULL);
}
} else if (!gprs_ra_id_equals(&mmctx->ra, &old_ra_id) ||
} else if (osmo_rai_cmp(&mmctx->ra, &req.old_rai) ||
mmctx->gmm_fsm->state == ST_GMM_DEREGISTERED)
{
/* We've received either a RAU for a MS which isn't registered
* or a RAU with an unknown RA ID. As long the SGSN doesn't support
* PS handover we treat this as invalid RAU */
struct gprs_ra_id new_ra_id;
struct osmo_routing_area_id new_ra_id = {};
char new_ra[32];
bssgp_parse_cell_id(&new_ra_id, msgb_bcid(msg));
osmo_rai_name_buf(new_ra, sizeof(new_ra), &new_ra_id);
bssgp_parse_cell_id2(&new_ra_id, NULL, msgb_bcid(msg), 8);
osmo_rai_name2_buf(new_ra, sizeof(new_ra), &new_ra_id);
if (mmctx->gmm_fsm->state == ST_GMM_DEREGISTERED)
LOGMMCTXP(LOGL_INFO, mmctx,
"Rejecting RAU - GMM state is deregistered. Old RA: %s New RA: %s\n",
osmo_rai_name(&old_ra_id), new_ra);
osmo_rai_name2(&req.old_rai), new_ra);
else
LOGMMCTXP(LOGL_INFO, mmctx,
"Rejecting RAU - Old RA doesn't match MM. Old RA: %s New RA: %s\n",
osmo_rai_name(&old_ra_id), new_ra);
osmo_rai_name2(&req.old_rai), new_ra);
reject_cause = GMM_CAUSE_IMPL_DETACHED;
goto rejected;
@@ -1742,6 +1740,21 @@ static int gsm48_rx_gmm_ra_upd_req(struct sgsn_mm_ctx *mmctx, struct msgb *msg,
* in the MS */
LOGGBP(llme, DMM, LOGL_NOTICE, "LLC XID RESET\n");
gprs_llgmm_reset_oldmsg(msg, GPRS_SAPI_GMM, llme);
/* The RAU didn't come from expected TLLI+RAI, so it's for sure bad and should be rejected.
* In any case, before unassigning (freeing) the LLME during the REJECT below, make sure
* beforehand that if there's an mmctx relating to that llme it is also freed.
* Otherwise it would be kept pointining at a dangling freed llme object.
*/
mmctx = sgsn_mm_ctx_by_llme(llme);
if (mmctx) {
char old_ra_id_name[32];
osmo_rai_name2_buf(old_ra_id_name, sizeof(old_ra_id_name), &req.old_rai);
LOGMMCTXP(LOGL_NOTICE, mmctx,
"Rx RA Update Request with unexpected TLLI=%08x Old RA=%s (expected Old RA: %s)!\n",
msgb_tlli(msg), old_ra_id_name, osmo_rai_name2(&mmctx->ra));
/* mmctx will be released (and its llme un assigned) after REJECT below. */
}
}
/* The MS has to perform GPRS attach */
/* Device is still IMSI attached for CS but initiate GPRS ATTACH,
@@ -1763,13 +1776,13 @@ static int gsm48_rx_gmm_ra_upd_req(struct sgsn_mm_ctx *mmctx, struct msgb *msg,
/* Update the MM context with the new RA-ID */
if (mmctx->ran_type == MM_CTX_T_GERAN_Gb && msgb_bcid(msg)) {
bssgp_parse_cell_id(&mmctx->ra, msgb_bcid(msg));
bssgp_parse_cell_id2(&mmctx->ra, NULL, msgb_bcid(msg), 8);
/* Update the MM context with the new (i.e. foreign) TLLI */
mmctx->gb.tlli = msgb_tlli(msg);
}
/* Update the MM context with the new DRX params */
if (TLVP_PRESENT(&tp, GSM48_IE_GMM_DRX_PARAM))
memcpy(&mmctx->drx_parms, TLVP_VAL(&tp, GSM48_IE_GMM_DRX_PARAM), sizeof(mmctx->drx_parms));
if (TLVP_PRESENT(&req.tlv, GSM48_IE_GMM_DRX_PARAM))
memcpy(&mmctx->drx_parms, TLVP_VAL(&req.tlv, GSM48_IE_GMM_DRX_PARAM), sizeof(mmctx->drx_parms));
/* FIXME: Update the MM context with the MS radio acc capabilities */
/* FIXME: Update the MM context with the MS network capabilities */
@@ -1802,8 +1815,8 @@ static int gsm48_rx_gmm_ra_upd_req(struct sgsn_mm_ctx *mmctx, struct msgb *msg,
/* Look at PDP Context Status IE and see if MS's view of
* activated/deactivated NSAPIs agrees with our view */
if (TLVP_PRESENT(&tp, GSM48_IE_GMM_PDP_CTX_STATUS)) {
const uint8_t *pdp_status = TLVP_VAL(&tp, GSM48_IE_GMM_PDP_CTX_STATUS);
if (TLVP_PRESENT(&req.tlv, GSM48_IE_GMM_PDP_CTX_STATUS)) {
uint16_t pdp_status = osmo_load16le(TLVP_VAL(&req.tlv, GSM48_IE_GMM_PDP_CTX_STATUS));
process_ms_ctx_status(mmctx, pdp_status);
}
@@ -1826,7 +1839,7 @@ rejected:
#ifdef BUILD_IU
else if (MSG_IU_UE_CTX(msg)) {
unsigned long X1001 = osmo_tdef_get(sgsn->cfg.T_defs, -1001, OSMO_TDEF_S, -1);
ranap_iu_tx_release_free(MSG_IU_UE_CTX(msg), NULL, (int) X1001);
sgsn_ranap_iu_tx_release_free(MSG_IU_UE_CTX(msg), NULL, (int) X1001);
}
#endif
@@ -1923,7 +1936,7 @@ static int gsm48_rx_gmm_service_req(struct sgsn_mm_ctx *ctx, struct msgb *msg)
LOGPC(DMM, LOGL_INFO, "\n");
/* Optional: PDP context status, MBMS context status, Uplink data status, Device properties */
tlv_parse(&tp, &gsm48_gmm_att_tlvdef, cur, (msg->data + msg->len) - cur, 0, 0);
tlv_parse(&tp, &gsm48_gmm_ie_tlvdef, cur, (msg->data + msg->len) - cur, 0, 0);
switch (mi.type) {
case GSM_MI_TYPE_IMSI:
@@ -1962,8 +1975,8 @@ static int gsm48_rx_gmm_service_req(struct sgsn_mm_ctx *ctx, struct msgb *msg)
/* Look at PDP Context Status IE and see if MS's view of
* activated/deactivated NSAPIs agrees with our view */
if (TLVP_PRESENT(&tp, GSM48_IE_GMM_PDP_CTX_STATUS)) {
const uint8_t *pdp_status = TLVP_VAL(&tp, GSM48_IE_GMM_PDP_CTX_STATUS);
const size_t pdp_status_len = TLVP_LEN(&tp, GSM48_IE_GMM_PDP_CTX_STATUS);
/* TODO: Use tlvp_val16le when available */
uint16_t pdp_status = osmo_load16le(TLVP_VAL(&tp, GSM48_IE_GMM_PDP_CTX_STATUS));
process_ms_ctx_status(ctx, pdp_status);
@@ -1972,7 +1985,7 @@ static int gsm48_rx_gmm_service_req(struct sgsn_mm_ctx *ctx, struct msgb *msg)
* Active state in pdp_status but there is no PDP contexts on
* SGSN side then Reject with the cause will force the mobile to
* reset PDP contexts */
if (llist_empty(&ctx->pdp_list) && pdp_status_has_active_nsapis(pdp_status, pdp_status_len)) {
if (llist_empty(&ctx->pdp_list) && pdp_status_has_active_nsapis(pdp_status)) {
reject_cause = GMM_CAUSE_NO_PDP_ACTIVATED;
goto rejected;
}
@@ -2271,7 +2284,7 @@ int gsm0408_gprs_force_reattach(struct sgsn_mm_ctx *mmctx)
return rc;
}
int gprs_gmm_rx_suspend(struct gprs_ra_id *raid, uint32_t tlli)
int gprs_gmm_rx_suspend(struct osmo_routing_area_id *raid, uint32_t tlli)
{
struct sgsn_mm_ctx *mmctx;
@@ -2293,7 +2306,7 @@ int gprs_gmm_rx_suspend(struct gprs_ra_id *raid, uint32_t tlli)
return 0;
}
int gprs_gmm_rx_resume(struct gprs_ra_id *raid, uint32_t tlli,
int gprs_gmm_rx_resume(struct osmo_routing_area_id *raid, uint32_t tlli,
uint8_t suspend_ref)
{
struct sgsn_mm_ctx *mmctx;
@@ -2334,10 +2347,10 @@ int gsm0408_gprs_rcvmsg_gb(struct msgb *msg, struct gprs_llc_llme *llme,
struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(msg);
uint8_t pdisc = gsm48_hdr_pdisc(gh);
struct sgsn_mm_ctx *mmctx;
struct gprs_ra_id ra_id;
struct osmo_routing_area_id ra_id = {};
int rc = -EINVAL;
bssgp_parse_cell_id(&ra_id, msgb_bcid(msg));
bssgp_parse_cell_id2(&ra_id, NULL, msgb_bcid(msg), 8);
mmctx = sgsn_mm_ctx_by_tlli(msgb_tlli(msg), &ra_id);
if (mmctx) {
rate_ctr_inc(rate_ctr_group_get_ctr(mmctx->ctrg, GMM_CTR_PKTS_SIG_IN));
@@ -2366,3 +2379,44 @@ int gsm0408_gprs_rcvmsg_gb(struct msgb *msg, struct gprs_llc_llme *llme,
return rc;
}
#ifdef BUILD_IU
/* Main entry point for incoming 04.08 GPRS messages from Iu */
int gsm0408_gprs_rcvmsg_iu(struct msgb *msg, struct gprs_ra_id *ra_id,
uint16_t *sai)
{
struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(msg);
uint8_t pdisc = gsm48_hdr_pdisc(gh);
struct sgsn_mm_ctx *mmctx;
int rc = -EINVAL;
mmctx = sgsn_mm_ctx_by_ue_ctx(MSG_IU_UE_CTX(msg));
if (mmctx) {
rate_ctr_inc(rate_ctr_group_get_ctr(mmctx->ctrg, GMM_CTR_PKTS_SIG_IN));
if (ra_id)
memcpy(&mmctx->ra, ra_id, sizeof(mmctx->ra));
}
/* MMCTX can be NULL */
switch (pdisc) {
case GSM48_PDISC_MM_GPRS:
rc = gsm0408_rcv_gmm(mmctx, msg, NULL, false);
#pragma message "set drop_cipherable arg for gsm0408_rcv_gmm() from IuPS?"
break;
case GSM48_PDISC_SM_GPRS:
rc = gsm0408_rcv_gsm(mmctx, msg, NULL);
break;
default:
LOGMMCTXP(LOGL_NOTICE, mmctx,
"Unknown GSM 04.08 discriminator 0x%02x: %s\n",
pdisc, osmo_hexdump((uint8_t *)gh, msgb_l3len(msg)));
/* FIXME: return status message */
break;
}
/* MMCTX can be invalid */
return rc;
}
#endif /* ifdef BUILD_IU */

View File

@@ -1,3 +1,5 @@
#include "config.h"
#include <osmocom/core/tdef.h>
#include <osmocom/crypt/utran_cipher.h>
@@ -7,6 +9,7 @@
#include <osmocom/sgsn/debug.h>
#include <osmocom/sgsn/gprs_gmm.h>
#include <osmocom/sgsn/mmctx.h>
#include <osmocom/sgsn/gprs_ranap.h>
#include <osmocom/sgsn/sgsn.h>
#define X(s) (1 << (s))
@@ -274,7 +277,7 @@ static void st_iu_security_cmd_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_
/* FIXME: we should send the set of allowed UEA, as in ranap_new_msg_sec_mod_cmd2(). However, this
* is not possible in the iu_client API. See OS#5487. */
ranap_iu_tx_sec_mode_cmd(ctx->iu.ue_ctx, &ctx->auth_triplet.vec, send_ck, ctx->iu.new_key);
sgsn_ranap_iu_tx_sec_mode_cmd(ctx->iu.ue_ctx, &ctx->auth_triplet.vec, send_ck, ctx->iu.new_key);
ctx->iu.new_key = 0;
#endif
}
@@ -385,7 +388,7 @@ void gmm_attach_allstate_action(struct osmo_fsm_inst *fi, uint32_t event, void *
/* 04.08 4.7.3.1.6 d) Abnormal Case
* Only do action if Req IEs differs. */
if (ctx->gmm_att_req.attach_req &&
gprs_gmm_attach_req_ies(new_attach_req, ctx->gmm_att_req.attach_req)) {
gprs_gmm_msg_cmp(new_attach_req, ctx->gmm_att_req.attach_req)) {
osmo_fsm_inst_state_chg(fi, ST_INIT, 0, 0);
st_init(fi, event, data);
}

273
src/sgsn/gprs_gmm_util.c Normal file
View File

@@ -0,0 +1,273 @@
/*! \file gprs_bssgp_util.c
* GPRS GMM protocol implementation as per 3GPP TS 24.008 */
/*
* (C) 2009-2015 by Harald Welte <laforge@gnumonks.org>
* (C) 2024 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved
*
* SPDX-License-Identifier: AGPL-3.0+
*
* Author: Alexander Couzens <lynxis@fe80.eu>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <osmocom/core/msgb.h>
#include <osmocom/gprs/gprs_msgb.h>
#include <osmocom/gsm/gsm48.h>
#include <osmocom/gsm/protocol/gsm_04_08_gprs.h>
#include <osmocom/gsm/tlv.h>
#include <osmocom/sgsn/debug.h>
#include <osmocom/sgsn/gprs_gmm_util.h>
const struct tlv_definition gsm48_gmm_ie_tlvdef = {
.def = {
[GSM48_IE_GMM_CIPH_CKSN] = { TLV_TYPE_SINGLE_TV, 1 },
[GSM48_IE_GMM_PTMSI_TYPE] = { TLV_TYPE_SINGLE_TV, 1 },
[GSM48_IE_GMM_TMSI_BASED_NRI_C] = { TLV_TYPE_TLV, 0 },
[GSM48_IE_GMM_TIMER_READY] = { TLV_TYPE_TV, 1 },
[GSM48_IE_GMM_ALLOC_PTMSI] = { TLV_TYPE_TLV, 0 },
[GSM48_IE_GMM_PTMSI_SIG] = { TLV_TYPE_FIXED, 3 },
[GSM48_IE_GMM_ADD_IDENTITY] = { TLV_TYPE_TLV, 0 },
[GSM48_IE_GMM_RAI2] = { TLV_TYPE_TLV, 0 },
[GSM48_IE_GMM_AUTH_RAND] = { TLV_TYPE_FIXED, 16 },
[GSM48_IE_GMM_AUTH_SRES] = { TLV_TYPE_FIXED, 4 },
[GSM48_IE_GMM_IMEISV] = { TLV_TYPE_TLV, 0 },
[GSM48_IE_GMM_CAUSE] = { TLV_TYPE_TV, 1 },
[GSM48_IE_GMM_RX_NPDU_NUM_LIST] = { TLV_TYPE_TLV, 0 },
[GSM48_IE_GMM_DRX_PARAM] = { TLV_TYPE_FIXED, 2 },
[GSM48_IE_GMM_AUTN] = { TLV_TYPE_TLV, 0 },
[GSM48_IE_GMM_AUTH_RES_EXT] = { TLV_TYPE_TLV, 0 },
[GSM48_IE_GMM_TIMER_T3302] = { TLV_TYPE_TLV, 0 },
[GSM48_IE_GMM_AUTH_FAIL_PAR] = { TLV_TYPE_TLV, 0 },
[GSM48_IE_GMM_MS_NET_CAPA] = { TLV_TYPE_TLV, 0 },
[GSM48_IE_GMM_PDP_CTX_STATUS] = { TLV_TYPE_TLV, 0 },
[GSM48_IE_GMM_PS_LCS_CAPA] = { TLV_TYPE_TLV, 0 },
[GSM48_IE_GMM_GMM_MBMS_CTX_ST] = { TLV_TYPE_TLV, 0 },
[GSM48_IE_GMM_TIMER_T3346] = { TLV_TYPE_TLV, 0 },
[GSM48_IE_GMM_UE_NET_CAP] = { TLV_TYPE_TLV, 0 },
[GSM48_IE_GMM_VD_PREF_UE_USAGE] = { TLV_TYPE_TLV, 0 },
[GSM48_IE_GMM_NET_FEAT_SUPPORT] = { TLV_TYPE_SINGLE_TV, 1 },
},
};
/* Minimum length of a GMM Attach Request (only L3/GMM) */
#define GSM48_GMM_ATT_REQ_MIN_LEN 25
const struct tlv_definition gsm48_gmm_att_ie_tlvdef = {
.def = {
[GSM48_IE_GMM_PTMSI_SIG] = { TLV_TYPE_FIXED, 3 },
[GSM48_IE_GMM_TIMER_READY] = { TLV_TYPE_TV, 1 },
[GSM48_IE_GMM_TMSI_STATUS] = { TLV_TYPE_SINGLE_TV, 1 },
[GSM48_IE_GMM_PS_LCS_CAPA] = { TLV_TYPE_TLV, 0 },
[GSM48_IE_GMM_MS_CLASSMARK2] = { TLV_TYPE_TLV, 3 },
[GSM48_IE_GMM_MS_CLASSMARK3] = { TLV_TYPE_TLV, 0 },
[GSM48_IE_GMM_SUPP_CODEC_LIST] = { TLV_TYPE_TLV, 3 },
[GSM48_IE_GMM_UE_NET_CAP] = { TLV_TYPE_TLV, 2 },
[GSM48_IE_GMM_ADD_IDENTITY] = { TLV_TYPE_TLV, 0 },
[GSM48_IE_GMM_RAI2] = { TLV_TYPE_TLV, 0 },
[GSM48_IE_GMM_VD_PREF_UE_USAGE] = { TLV_TYPE_TLV, 1 },
[GSM48_IE_GMM_DEVICE_PROP] = { TLV_TYPE_SINGLE_TV, 1 },
[GSM48_IE_GMM_PTMSI_TYPE] = { TLV_TYPE_SINGLE_TV, 1 },
[GSM48_IE_GMM_MS_NET_FEAT_SUP] = { TLV_TYPE_SINGLE_TV, 1 },
[GSM48_IE_GMM_ADD_UPDATE_TYPE] = { TLV_TYPE_SINGLE_TV, 1 },
[GSM48_IE_GMM_TMSI_BASED_NRI_C] = { TLV_TYPE_TLV, 4 },
[GMM48_IE_GMM_TIMER_T3324] = { TLV_TYPE_TLV, 3 },
[GSM48_IE_GMM_TIMER_T3312_EXT] = { TLV_TYPE_TLV, 3 },
[GSM48_IE_GMM_EXT_DRX_PARAMS] = { TLV_TYPE_TLV, 3 },
}
};
const struct tlv_definition gsm48_gmm_rau_ie_tlvdef = {
.def = {
[GSM48_IE_GMM_PTMSI_SIG] = { TLV_TYPE_FIXED, 3 },
[GSM48_IE_GMM_TIMER_READY] = { TLV_TYPE_TV, 1 },
[GSM48_IE_GMM_DRX_PARAM] = { TLV_TYPE_FIXED, 2 },
[GSM48_IE_GMM_TMSI_STATUS] = { TLV_TYPE_SINGLE_TV, 1 },
[GSM48_IE_GMM_ALLOC_PTMSI] = { TLV_TYPE_TLV, 5 },
[GSM48_IE_GMM_MS_NET_CAPA] = { TLV_TYPE_TLV, 2 },
[GSM48_IE_GMM_PDP_CTX_STATUS] = { TLV_TYPE_TLV, 2 },
[GSM48_IE_GMM_PS_LCS_CAPA] = { TLV_TYPE_TLV, 1 },
[GSM48_IE_GMM_GMM_MBMS_CTX_ST] = { TLV_TYPE_TLV, 0 },
[GSM48_IE_GMM_UE_NET_CAP] = { TLV_TYPE_TLV, 2 },
[GSM48_IE_GMM_ADD_IDENTITY] = { TLV_TYPE_TLV, 0 },
[GSM48_IE_GMM_RAI2] = { TLV_TYPE_TLV, 0 },
[GSM48_IE_GMM_MS_CLASSMARK2] = { TLV_TYPE_TLV, 3 },
[GSM48_IE_GMM_MS_CLASSMARK3] = { TLV_TYPE_TLV, 0 },
[GSM48_IE_GMM_SUPP_CODEC_LIST] = { TLV_TYPE_TLV, 3 },
[GSM48_IE_GMM_VD_PREF_UE_USAGE] = { TLV_TYPE_TLV, 1 },
[GSM48_IE_GMM_PTMSI_TYPE] = { TLV_TYPE_SINGLE_TV, 1 },
[GSM48_IE_GMM_DEVICE_PROP] = { TLV_TYPE_SINGLE_TV, 1 },
[GSM48_IE_GMM_MS_NET_FEAT_SUP] = { TLV_TYPE_SINGLE_TV, 1 },
[GSM48_IE_GMM_OLD_LAI] = { TLV_TYPE_TLV, 5 },
[GSM48_IE_GMM_ADD_UPDATE_TYPE] = { TLV_TYPE_SINGLE_TV, 1 },
[GSM48_IE_GMM_TMSI_BASED_NRI_C] = { TLV_TYPE_TLV, 4 },
[GMM48_IE_GMM_TIMER_T3324] = { TLV_TYPE_TLV, 3 },
[GSM48_IE_GMM_TIMER_T3312_EXT] = { TLV_TYPE_TLV, 3 },
[GSM48_IE_GMM_EXT_DRX_PARAMS] = { TLV_TYPE_TLV, 3 },
}
};
/*! Parse 24.008 9.4.1 Attach Request
* \param[in] msg l3 pointers must point to gmm.
* \param[out] rau_req parsed RA update request
* \returns 0 on success or GMM cause
*/
int gprs_gmm_parse_att_req(const struct msgb *msg, struct gprs_gmm_att_req *att_req)
{
uint8_t *cur, len;
size_t mandatory_fields_len;
struct gsm48_hdr *gh;
int ret;
OSMO_ASSERT(msg);
OSMO_ASSERT(att_req);
memset(att_req, 0, sizeof(struct gprs_gmm_att_req));
/* all mandatory fields */
if (msgb_l3len(msg) < GSM48_GMM_ATT_REQ_MIN_LEN)
return GMM_CAUSE_PROTO_ERR_UNSPEC;
gh = (struct gsm48_hdr *) msgb_gmmh(msg);
cur = gh->data;
att_req->skip_ind = gh->proto_discr >> 4;
/* LV: MS network cap 10.5.5.12 */
len = *cur++;
if (msgb_l3len(msg) < (len + 21 + (cur - msgb_gmmh(msg))))
return GMM_CAUSE_PROTO_ERR_UNSPEC;
/* MS network cap can't be empty */
if (len == 0)
return GMM_CAUSE_INV_MAND_INFO;
att_req->ms_network_cap = cur;
att_req->ms_network_cap_len = len;
cur += len;
/* V: Update Type 10.5.5.18 */
att_req->attach_type = *cur & 0x07;
att_req->follow_up_req = !!(*cur & 0x08);
/* V: GPRS Ciphering Key Sequence 10.5.1.2 */
att_req->cksq = *cur >> 4;
cur++;
/* V: DRX parameter 10.5.5.6 */
att_req->drx_parms = osmo_load16le(cur);
cur += 2;
/* LV: Mobile identity 10.5.1.4 */
len = *cur++;
if (msgb_l3len(msg) < (len + 12 + (cur - msgb_gmmh(msg))))
return GMM_CAUSE_PROTO_ERR_UNSPEC;
ret = osmo_mobile_identity_decode(&att_req->mi, cur, len, false);
if (ret)
return GMM_CAUSE_PROTO_ERR_UNSPEC;
cur += len;
/* V: Old routing area identification 10.5.5.15 */
osmo_routing_area_id_decode(&att_req->old_rai, cur, 6);
cur += 6;
/* LV: MS radio cap 10.5.5.12a */
len = *cur++;
if (msgb_l3len(msg) < (len + (cur - msgb_gmmh(msg))))
return GMM_CAUSE_PROTO_ERR_UNSPEC;
/* 24.008 Rel 17 specifies min 5, but SGSN will still work with 4 */
if (len < 4)
return GMM_CAUSE_INV_MAND_INFO;
att_req->ms_radio_cap = cur;
att_req->ms_radio_cap_len = len;
cur += len;
mandatory_fields_len = (cur - msgb_gmmh(msg));
if (msgb_l3len(msg) == mandatory_fields_len)
return 0;
ret = tlv_parse(&att_req->tlv, &gsm48_gmm_att_ie_tlvdef,
cur, msgb_l3len(msg) - mandatory_fields_len, 0, 0);
/* gracefully handle unknown IEs (partial parsing) */
if (ret < 0 && ret != OSMO_TLVP_ERR_UNKNOWN_TLV_TYPE) {
LOGP(DMM, LOGL_NOTICE, "%s(): tlv_parse() failed (%d)\n", __func__, ret);
return GMM_CAUSE_COND_IE_ERR;
}
return 0;
}
/*! Parse 24.008 9.4.14 RAU Request
* \param[in] msg l3 pointers must point to gmm.
* \param[out] rau_req parsed RA update request
* \returns 0 on success or GMM cause
*/
int gprs_gmm_parse_ra_upd_req(const struct msgb *msg, struct gprs_gmm_ra_upd_req *rau_req)
{
uint8_t *cur, len;
size_t mandatory_fields_len;
struct gsm48_hdr *gh;
int ret;
OSMO_ASSERT(msg);
OSMO_ASSERT(rau_req);
memset(rau_req, 0, sizeof(struct gprs_gmm_ra_upd_req));
/* all mandatory fields + variable length MS Radio Cap (min value) would be 15 bytes.
* But even short radio capabilities we should handle with 14 bytes */
if (msgb_l3len(msg) < 14)
return GMM_CAUSE_PROTO_ERR_UNSPEC;
gh = (struct gsm48_hdr *) msgb_gmmh(msg);
cur = gh->data;
rau_req->skip_ind = gh->proto_discr >> 4;
/* V: Update Type 10.5.5.18 */
rau_req->update_type = *cur & 0x07;
rau_req->follow_up_req = !!(*cur & 0x08);
/* V: GPRS Ciphering Key Sequence 10.5.1.2 */
rau_req->cksq = *cur >> 4;
cur++;
/* V: Old routing area identification 10.5.5.15 */
osmo_routing_area_id_decode(&rau_req->old_rai, cur, 6);
cur += 6;
/* LV: MS radio cap 10.5.5.12a */
len = *cur++;
if (msgb_l3len(msg) < (len + (cur - msgb_gmmh(msg))))
return GMM_CAUSE_PROTO_ERR_UNSPEC;
rau_req->ms_radio_cap = cur;
rau_req->ms_radio_cap_len = len;
cur += len;
mandatory_fields_len = (cur - msgb_gmmh(msg));
if (msgb_l3len(msg) == mandatory_fields_len)
return 0;
ret = tlv_parse(&rau_req->tlv, &gsm48_gmm_rau_ie_tlvdef,
cur, msgb_l3len(msg) - mandatory_fields_len, 0, 0);
if (ret < 0)
return GMM_CAUSE_COND_IE_ERR;
return 0;
}

View File

@@ -1112,7 +1112,7 @@ int gprs_llgmm_assign(struct gprs_llc_llme *llme,
llme->state = GPRS_LLMS_ASSIGNED;
} else if (old_tlli != TLLI_UNASSIGNED && new_tlli == TLLI_UNASSIGNED) {
/* TLLI Unassignment 8.3.3) */
llme->tlli = llme->old_tlli = 0;
llme->tlli = llme->old_tlli = TLLI_UNASSIGNED;
llme->state = GPRS_LLMS_UNASSIGNED;
for (i = 0; i < ARRAY_SIZE(llme->lle); i++) {
struct gprs_llc_lle *l = &llme->lle[i];

View File

@@ -30,6 +30,7 @@
#include <osmocom/sgsn/sgsn.h>
#include <osmocom/sgsn/gprs_ranap.h>
#include <osmocom/sgsn/gtp.h>
#include <osmocom/sgsn/gtp_ggsn.h>
#include <osmocom/sgsn/pdpctx.h>
#include <osmocom/sgsn/mmctx.h>
@@ -44,17 +45,30 @@ static const struct osmo_tdef_state_timeout mm_state_iu_fsm_timeouts[32] = {
#define mm_state_iu_fsm_state_chg(fi, NEXT_STATE) \
osmo_tdef_fsm_inst_state_chg(fi, NEXT_STATE, mm_state_iu_fsm_timeouts, sgsn->cfg.T_defs, -1)
static void mmctx_change_gtpu_endpoints_to_sgsn(struct sgsn_mm_ctx *mm_ctx)
static void pdpctx_change_gtpu_endpoint_to_sgsn(const struct sgsn_mm_ctx *mm_ctx, struct sgsn_pdp_ctx *pdp)
{
char buf[INET_ADDRSTRLEN];
LOGMMCTXP(LOGL_INFO, mm_ctx, "Changing GTP-U endpoints %s/0x%08x -> %s/0x%08x\n",
sgsn_gtp_ntoa(&pdp->lib->gsnlu), pdp->lib->teid_own,
inet_ntop(AF_INET, &sgsn->cfg.gtp_listenaddr.sin_addr, buf, sizeof(buf)),
pdp->sgsn_teid_own);
pdp->lib->gsnlu.l = sizeof(sgsn->cfg.gtp_listenaddr.sin_addr);
memcpy(pdp->lib->gsnlu.v, &sgsn->cfg.gtp_listenaddr.sin_addr,
sizeof(sgsn->cfg.gtp_listenaddr.sin_addr));
pdp->lib->teid_own = pdp->sgsn_teid_own;
/* Disable Direct Tunnel Flags DTI. Other flags make no sense here, so also set to 0. */
pdp->lib->dir_tun_flags.l = 1;
pdp->lib->dir_tun_flags.v[0] = 0x00;
}
static void mmctx_change_gtpu_endpoints_to_sgsn(struct sgsn_mm_ctx *mm_ctx, struct sgsn_pdp_ctx *pdp_skip_gtp_upd_req)
{
struct sgsn_pdp_ctx *pdp;
llist_for_each_entry(pdp, &mm_ctx->pdp_list, list) {
LOGMMCTXP(LOGL_INFO, mm_ctx, "Changing GTP-U endpoints %s -> %s\n",
sgsn_gtp_ntoa(&pdp->lib->gsnlu),
inet_ntop(AF_INET, &sgsn->cfg.gtp_listenaddr.sin_addr, buf, sizeof(buf)));
sgsn_pdp_upd_gtp_u(pdp,
&sgsn->cfg.gtp_listenaddr.sin_addr,
sizeof(sgsn->cfg.gtp_listenaddr.sin_addr));
pdpctx_change_gtpu_endpoint_to_sgsn(mm_ctx, pdp);
if (pdp != pdp_skip_gtp_upd_req)
gtp_update_context(pdp->ggsn->gsn, pdp->lib, pdp, &pdp->lib->hisaddr0);
}
}
@@ -66,34 +80,41 @@ static void st_pmm_detached(struct osmo_fsm_inst *fi, uint32_t event, void *data
break;
case E_PMM_PS_DETACH:
break;
case E_PMM_RX_GGSN_GTPU_DT_EI:
/* This should in general not happen, since Direct Tunnel is not
* enabled during PMM-IDLE, but there may be a race condition of
* packets/events, so simply ignore it. */
break;
}
}
static void st_pmm_connected(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct sgsn_mm_ctx *ctx = fi->priv;
struct sgsn_pdp_ctx *pctx;
switch(event) {
case E_PMM_PS_CONN_RELEASE:
sgsn_ranap_iu_free(ctx);
sgsn_mm_ctx_iu_ranap_free(ctx);
mm_state_iu_fsm_state_chg(fi, ST_PMM_IDLE);
mmctx_change_gtpu_endpoints_to_sgsn(ctx, NULL);
break;
case E_PMM_PS_DETACH:
sgsn_ranap_iu_release_free(ctx, NULL);
sgsn_mm_ctx_iu_ranap_release_free(ctx, NULL);
mm_state_iu_fsm_state_chg(fi, ST_PMM_DETACHED);
break;
case E_PMM_RA_UPDATE:
break;
case E_PMM_RX_GGSN_GTPU_DT_EI:
/* GTPU Direct Tunnel (RNC<->GGSN): GGSN Received Error Indication when transmitting DL data*/
pctx = (struct sgsn_pdp_ctx *)data;
sgsn_mm_ctx_iu_ranap_free(ctx);
mm_state_iu_fsm_state_chg(fi, ST_PMM_IDLE);
mmctx_change_gtpu_endpoints_to_sgsn(ctx, pctx);
break;
}
}
static void st_pmm_idle_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
{
struct sgsn_mm_ctx *ctx = fi->priv;
mmctx_change_gtpu_endpoints_to_sgsn(ctx);
}
static void st_pmm_idle(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
switch(event) {
@@ -104,12 +125,19 @@ static void st_pmm_idle(struct osmo_fsm_inst *fi, uint32_t event, void *data)
case E_PMM_PS_DETACH:
mm_state_iu_fsm_state_chg(fi, ST_PMM_DETACHED);
break;
case E_PMM_RX_GGSN_GTPU_DT_EI:
/* This should in general not happen, since Direct Tunnel is not
* enabled during PMM-IDLE, but there may be a race condition of
* packets/events, so simply ignore it. */
break;
}
}
static struct osmo_fsm_state mm_state_iu_fsm_states[] = {
[ST_PMM_DETACHED] = {
.in_event_mask = X(E_PMM_PS_ATTACH) | X(E_PMM_PS_DETACH),
.in_event_mask = X(E_PMM_PS_ATTACH) |
X(E_PMM_PS_DETACH) |
X(E_PMM_RX_GGSN_GTPU_DT_EI),
.out_state_mask = X(ST_PMM_CONNECTED),
.name = "Detached",
.action = st_pmm_detached,
@@ -118,7 +146,8 @@ static struct osmo_fsm_state mm_state_iu_fsm_states[] = {
.in_event_mask =
X(E_PMM_PS_CONN_RELEASE) |
X(E_PMM_RA_UPDATE) |
X(E_PMM_PS_DETACH),
X(E_PMM_PS_DETACH) |
X(E_PMM_RX_GGSN_GTPU_DT_EI),
.out_state_mask = X(ST_PMM_DETACHED) | X(ST_PMM_IDLE),
.name = "Connected",
.action = st_pmm_connected,
@@ -127,10 +156,10 @@ static struct osmo_fsm_state mm_state_iu_fsm_states[] = {
.in_event_mask =
X(E_PMM_PS_DETACH) |
X(E_PMM_PS_CONN_ESTABLISH) |
X(E_PMM_PS_ATTACH),
X(E_PMM_PS_ATTACH) |
X(E_PMM_RX_GGSN_GTPU_DT_EI),
.out_state_mask = X(ST_PMM_DETACHED) | X(ST_PMM_CONNECTED),
.name = "Idle",
.onenter = st_pmm_idle_on_enter,
.action = st_pmm_idle,
},
};
@@ -141,6 +170,7 @@ const struct value_string mm_state_iu_fsm_event_names[] = {
OSMO_VALUE_STRING(E_PMM_PS_CONN_ESTABLISH),
OSMO_VALUE_STRING(E_PMM_PS_DETACH),
OSMO_VALUE_STRING(E_PMM_RA_UPDATE),
OSMO_VALUE_STRING(E_PMM_RX_GGSN_GTPU_DT_EI),
{ 0, NULL }
};

View File

@@ -28,6 +28,7 @@
#include <osmocom/gprs/gprs_ns2.h>
#include <osmocom/gprs/gprs_bssgp_bss.h>
#include <osmocom/sgsn/gprs_llc.h>
#include <osmocom/sgsn/gprs_routing_area.h>
#include "config.h"
@@ -52,6 +53,7 @@ void gprs_ns_prim_status_cb(struct osmo_gprs_ns2_prim *nsp)
break;
case GPRS_NS2_AFF_CAUSE_FAILURE:
LOGP(DGPRS, LOGL_NOTICE, "NS-E %d became unavailable\n", nsp->nsei);
sgsn_ra_geran_nsei_failure_ind(nsp->nsei);
break;
default:
LOGP(DGPRS, LOGL_NOTICE, "NS: %s Unknown prim %d from NS\n",

View File

@@ -22,13 +22,22 @@
*/
#include "config.h"
#include <gtp.h>
#include <asn1c/asn1helpers.h>
#include <osmocom/gtp/gtp.h>
#include <osmocom/core/rate_ctr.h>
#include <osmocom/core/tdef.h>
#include <osmocom/gsm/gsm23003.h>
#include <osmocom/gprs/gprs_msgb.h>
#include <osmocom/ranap/ranap_common.h>
#include <osmocom/ranap/ranap_common_cn.h>
#include <osmocom/ranap/ranap_ies_defs.h>
#include <osmocom/ranap/ranap_msg_factory.h>
#include <osmocom/ranap/iu_helpers.h>
#include <osmocom/sigtran/sccp_helpers.h>
#include <osmocom/sgsn/gprs_gmm.h>
#include <osmocom/sgsn/gprs_sm.h>
@@ -37,20 +46,38 @@
#include <osmocom/sgsn/gprs_ranap.h>
#include <osmocom/sgsn/gprs_gmm_attach.h>
#include <osmocom/sgsn/gprs_mm_state_iu_fsm.h>
#include <osmocom/sgsn/gprs_routing_area.h>
#include <osmocom/sgsn/gtp_ggsn.h>
#include <osmocom/sgsn/gtp.h>
#include <osmocom/sgsn/iu_rnc.h>
#include <osmocom/sgsn/iu_rnc_fsm.h>
#include <osmocom/sgsn/pdpctx.h>
#include <osmocom/sgsn/mmctx.h>
/* Send RAB activation requests for all PDP contexts */
void activate_pdp_rabs(struct sgsn_mm_ctx *ctx)
/* Parsed global RNC id. See also struct RANAP_GlobalRNC_ID, and note that the
* PLMN identity is a BCD representation of the MCC and MNC.
* See iu_grnc_id_parse(). */
static int iu_grnc_id_parse(struct osmo_rnc_id *dst, const struct RANAP_GlobalRNC_ID *src)
{
struct sgsn_pdp_ctx *pdp;
if (ctx->ran_type != MM_CTX_T_UTRAN_Iu)
return;
llist_for_each_entry(pdp, &ctx->pdp_list, list) {
iu_rab_act_ps(pdp->nsapi, pdp);
/* The size is coming from arbitrary sender, check it gracefully */
if (src->pLMNidentity.size != 3) {
LOGP(DRANAP, LOGL_ERROR, "Invalid PLMN Identity size: should be 3, is %d\n",
src->pLMNidentity.size);
return -1;
}
osmo_plmn_from_bcd(&src->pLMNidentity.buf[0], &dst->plmn);
dst->rnc_id = (uint16_t)src->rNC_ID;
return 0;
}
/* not used at present */
int sgsn_ranap_iu_grnc_id_compose(struct iu_grnc_id *dst, const struct osmo_rnc_id *src)
{
dst->grnc_id.pLMNidentity.buf = &dst->plmn_buf[0];
dst->grnc_id.pLMNidentity.size = 3;
osmo_plmn_to_bcd(dst->grnc_id.pLMNidentity.buf, &src->plmn);
dst->grnc_id.rNC_ID = src->rnc_id;
return 0;
}
/* Callback for RAB assignment response */
@@ -60,36 +87,44 @@ static int sgsn_ranap_rab_ass_resp(struct sgsn_mm_ctx *ctx, RANAP_RAB_SetupOrMod
bool require_pdp_update = false;
struct sgsn_pdp_ctx *pdp = NULL;
RANAP_RAB_SetupOrModifiedItem_t *item = &setup_ies->raB_SetupOrModifiedItem;
int rc;
rab_id = item->rAB_ID.buf[0];
pdp = sgsn_pdp_ctx_by_nsapi(ctx, rab_id);
if (!pdp) {
LOGP(DRANAP, LOGL_ERROR, "RAB Assignment Response for unknown RAB/NSAPI=%u\n", rab_id);
sgsn_mm_ctx_iu_ranap_release_free(ctx, NULL);
return -1;
}
if (item->transportLayerAddress) {
struct osmo_sockaddr addr;
LOGPC(DRANAP, LOGL_INFO, " Setup: (%u/%s)", rab_id, osmo_hexdump(item->transportLayerAddress->buf,
item->transportLayerAddress->size));
switch (item->transportLayerAddress->size) {
case 7:
/* It must be IPv4 inside a X213 NSAP */
memcpy(pdp->lib->gsnlu.v, &item->transportLayerAddress->buf[3], 4);
rc = ranap_transp_layer_addr_decode2(&addr, NULL, item->transportLayerAddress);
if (rc < 0) {
LOGP(DRANAP, LOGL_ERROR,
"RAB Assignment Resp: Unknown Transport Layer Address (size %u): %s\n",
item->transportLayerAddress->size,
osmo_hexdump(item->transportLayerAddress->buf, item->transportLayerAddress->size));
goto ret_error;
}
switch (addr.u.sa.sa_family) {
case AF_INET:
memcpy(pdp->lib->gsnlu.v, (uint8_t *)&addr.u.sin.sin_addr.s_addr, 4);
break;
case 4:
/* It must be a raw IPv4 address */
memcpy(pdp->lib->gsnlu.v, item->transportLayerAddress->buf, 4);
break;
case 16:
/* TODO: It must be a raw IPv6 address */
case 19:
/* TODO: It must be IPv6 inside a X213 NSAP */
case AF_INET6:
/* TODO: Support IPv6 address */
LOGP(DRANAP, LOGL_ERROR,
"RAB Assignment Resp: IPv6 transport layer address not supported!\n");
goto ret_error;
default:
LOGP(DRANAP, LOGL_ERROR, "RAB Assignment Resp: Unknown "
"transport layer address size %u\n",
item->transportLayerAddress->size);
return -1;
LOGP(DRANAP, LOGL_ERROR,
"RAB Assignment Resp: Unexpected transport layer address size %u\n",
item->transportLayerAddress->size);
goto ret_error;
}
require_pdp_update = true;
}
@@ -103,6 +138,8 @@ static int sgsn_ranap_rab_ass_resp(struct sgsn_mm_ctx *ctx, RANAP_RAB_SetupOrMod
LOGP(DRANAP, LOGL_DEBUG, "Updating TEID on RNC side from 0x%08x to 0x%08x\n",
pdp->lib->teid_own, tei);
pdp->lib->teid_own = tei;
pdp->lib->dir_tun_flags.l = 1;
pdp->lib->dir_tun_flags.v[0] = 0x01; /* Set DTI flag in Direct Tunnel Flags */
require_pdp_update = true;
}
@@ -115,18 +152,33 @@ static int sgsn_ranap_rab_ass_resp(struct sgsn_mm_ctx *ctx, RANAP_RAB_SetupOrMod
}
return 0;
ret_error:
if (pdp->state != PDP_STATE_CR_CONF) {
gsm48_tx_gsm_act_pdp_rej(ctx, pdp->ti, GSM_CAUSE_NET_FAIL,
0, NULL);
sgsn_delete_pdp_ctx(pdp);
} else {
gsm48_tx_gsm_deact_pdp_req(pdp, GSM_CAUSE_NET_FAIL, true);
}
return -1;
}
int sgsn_ranap_iu_event(struct ranap_ue_conn_ctx *ctx, enum ranap_iu_event_type type, void *data)
static int sgsn_ranap_iu_event_mmctx(struct ranap_ue_conn_ctx *ctx, enum ranap_iu_event_type type, void *data)
{
struct sgsn_mm_ctx *mm;
int rc = -1;
if (!ctx) {
LOGIUP(ctx, LOGL_ERROR, "NULL ctx given for IU event %s\n",
iu_client_event_type_str(type));
return rc;
}
mm = sgsn_mm_ctx_by_ue_ctx(ctx);
if (!mm) {
LOGIUP(ctx, LOGL_NOTICE, "Cannot find mm ctx for IU event %s\n",
ranap_iu_event_type_str(type));
ranap_iu_free_ue(ctx);
iu_client_event_type_str(type));
sgsn_ranap_iu_free_ue(ctx);
return rc;
}
@@ -138,10 +190,10 @@ int sgsn_ranap_iu_event(struct ranap_ue_conn_ctx *ctx, enum ranap_iu_event_type
/* fall thru */
case RANAP_IU_EVENT_LINK_INVALIDATED:
/* Clean up ranap_ue_conn_ctx here */
LOGMMCTXP(LOGL_INFO, mm, "IU release (cause=%s)\n", ranap_iu_event_type_str(type));
LOGMMCTXP(LOGL_INFO, mm, "IU release (cause=%s)\n", iu_client_event_type_str(type));
rc = osmo_fsm_inst_dispatch(mm->iu.mm_state_fsm, E_PMM_PS_CONN_RELEASE, NULL);
if (rc < 0)
sgsn_ranap_iu_free(mm);
sgsn_mm_ctx_iu_ranap_free(mm);
/* TODO: move this into FSM */
if (mm->ran_type == MM_CTX_T_UTRAN_Iu && mm->gmm_att_req.fsm->state != ST_INIT)
@@ -156,7 +208,7 @@ int sgsn_ranap_iu_event(struct ranap_ue_conn_ctx *ctx, enum ranap_iu_event_type
*/
/* Continue authentication here */
mm->iu.ue_ctx->integrity_active = 1;
ranap_iu_tx_common_id(mm->iu.ue_ctx, mm->imsi);
sgsn_ranap_iu_tx_common_id(mm->iu.ue_ctx, mm->imsi);
/* FIXME: remove gmm_authorize */
if (mm->pending_req != GSM48_MT_GMM_ATTACH_REQ)
@@ -166,105 +218,617 @@ int sgsn_ranap_iu_event(struct ranap_ue_conn_ctx *ctx, enum ranap_iu_event_type
rc = 0;
break;
default:
LOGMMCTXP(LOGL_NOTICE, mm, "Unknown event received: %i\n", type);
LOGMMCTXP(LOGL_NOTICE, mm, "Unknown event received: %d\n", type);
rc = -1;
break;
}
return rc;
}
void sgsn_ranap_iu_free(struct sgsn_mm_ctx *ctx)
int sgsn_ranap_iu_event(struct ranap_ue_conn_ctx *ctx, enum ranap_iu_event_type type, void *data)
{
if (!ctx)
return;
struct ranap_iu_event_new_area *new_area;
if (!ctx->iu.ue_ctx)
return;
switch (type) {
case RANAP_IU_EVENT_RAB_ASSIGN:
case RANAP_IU_EVENT_IU_RELEASE:
case RANAP_IU_EVENT_LINK_INVALIDATED:
case RANAP_IU_EVENT_SECURITY_MODE_COMPLETE:
return sgsn_ranap_iu_event_mmctx(ctx, type, data);
case RANAP_IU_EVENT_NEW_AREA:
/* inform the Routing Area code about a new RA for Iu */
new_area = data;
ranap_iu_free_ue(ctx->iu.ue_ctx);
ctx->iu.ue_ctx = NULL;
/* Only interesting in Routing Area changes, but not Location Area */
if (new_area->cell_type != RANAP_IU_NEW_RAC)
return 0;
return sgsn_ra_utran_register(new_area->u.rai, new_area->rnc_id);
default:
LOGP(DRANAP, LOGL_NOTICE, "Iu: Unknown event received: type: %d\n", type);
return -1;
}
}
void sgsn_ranap_iu_release_free(struct sgsn_mm_ctx *ctx,
const struct RANAP_Cause *cause)
{
unsigned long X1001;
if (!ctx)
return;
if (!ctx->iu.ue_ctx)
return;
X1001 = osmo_tdef_get(sgsn->cfg.T_defs, -1001, OSMO_TDEF_S, -1);
ranap_iu_tx_release_free(ctx->iu.ue_ctx,
cause,
(int) X1001);
ctx->iu.ue_ctx = NULL;
}
int iu_rab_act_ps(uint8_t rab_id, struct sgsn_pdp_ctx *pdp)
int sgsn_ranap_iu_tx_rab_ps_ass_req(struct ranap_ue_conn_ctx *ue_ctx,
uint8_t rab_id, uint32_t gtp_ip, uint32_t gtp_tei)
{
struct msgb *msg;
struct sgsn_mm_ctx *mm = pdp->mm;
struct ranap_ue_conn_ctx *uectx;
uint32_t ggsn_ip;
bool use_x213_nsap;
bool use_x213_nsap = (ue_ctx->rab_assign_addr_enc == RANAP_NSAP_ADDR_ENC_X213);
uectx = mm->iu.ue_ctx;
use_x213_nsap = (uectx->rab_assign_addr_enc == RANAP_NSAP_ADDR_ENC_X213);
LOGP(DRANAP, LOGL_DEBUG,
"Assigning RAB: rab_id=%u, ggsn_ip=%x, teid_gn=%x, use_x213_nsap=%d\n",
rab_id, gtp_ip, gtp_tei, use_x213_nsap);
/* Get the IP address for ggsn user plane */
memcpy(&ggsn_ip, pdp->lib->gsnru.v, pdp->lib->gsnru.l);
ggsn_ip = htonl(ggsn_ip);
LOGP(DRANAP, LOGL_DEBUG, "Assigning RAB: rab_id=%d, ggsn_ip=%x,"
" teid_gn=%x, use_x213_nsap=%d\n",
rab_id, ggsn_ip, pdp->lib->teid_gn, use_x213_nsap);
msg = ranap_new_msg_rab_assign_data(rab_id, ggsn_ip,
pdp->lib->teid_gn, use_x213_nsap);
msg->l2h = msg->data;
return ranap_iu_rab_act(uectx, msg);
msg = ranap_new_msg_rab_assign_data(rab_id, gtp_ip, gtp_tei, use_x213_nsap);
return sgsn_scu_iups_tx_data_req(ue_ctx->rnc->scu_iups, ue_ctx->conn_id, msg);
}
/* Main entry point for incoming 04.08 GPRS messages from Iu */
int gsm0408_gprs_rcvmsg_iu(struct msgb *msg, struct gprs_ra_id *ra_id,
uint16_t *sai)
int sgsn_ranap_iu_tx_sec_mode_cmd(struct ranap_ue_conn_ctx *uectx, struct osmo_auth_vector *vec,
int send_ck, int new_key)
{
struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(msg);
uint8_t pdisc = gsm48_hdr_pdisc(gh);
struct sgsn_mm_ctx *mmctx;
int rc = -EINVAL;
struct msgb *msg;
mmctx = sgsn_mm_ctx_by_ue_ctx(MSG_IU_UE_CTX(msg));
if (mmctx) {
rate_ctr_inc(rate_ctr_group_get_ctr(mmctx->ctrg, GMM_CTR_PKTS_SIG_IN));
if (ra_id)
memcpy(&mmctx->ra, ra_id, sizeof(mmctx->ra));
/* create RANAP message */
msg = ranap_new_msg_sec_mod_cmd(vec->ik, send_ck ? vec->ck : NULL,
new_key ? RANAP_KeyStatus_new : RANAP_KeyStatus_old);
return sgsn_scu_iups_tx_data_req(uectx->rnc->scu_iups, uectx->conn_id, msg);
}
int sgsn_ranap_iu_tx_common_id(struct ranap_ue_conn_ctx *uectx, const char *imsi)
{
struct msgb *msg;
LOGP(DRANAP, LOGL_INFO, "Transmitting RANAP CommonID (SCCP conn_id %u)\n",
uectx->conn_id);
msg = ranap_new_msg_common_id(imsi);
return sgsn_scu_iups_tx_data_req(uectx->rnc->scu_iups, uectx->conn_id, msg);
}
int sgsn_ranap_iu_tx(struct msgb *msg_nas, uint8_t sapi)
{
struct ranap_ue_conn_ctx *uectx = msg_nas->dst;
struct msgb *msg;
if (!uectx) {
LOGP(DRANAP, LOGL_ERROR,
"Discarding to-be-transmitted L3 Message as RANAP DT with unset dst SCCP conn_id!\n");
return -ENOTCONN;
}
/* MMCTX can be NULL */
LOGP(DRANAP, LOGL_INFO, "Transmitting L3 Message as RANAP DT (SCCP conn_id %u)\n",
uectx->conn_id);
switch (pdisc) {
case GSM48_PDISC_MM_GPRS:
rc = gsm0408_rcv_gmm(mmctx, msg, NULL, false);
#pragma message "set drop_cipherable arg for gsm0408_rcv_gmm() from IuPS?"
break;
case GSM48_PDISC_SM_GPRS:
rc = gsm0408_rcv_gsm(mmctx, msg, NULL);
break;
default:
LOGMMCTXP(LOGL_NOTICE, mmctx,
"Unknown GSM 04.08 discriminator 0x%02x: %s\n",
pdisc, osmo_hexdump((uint8_t *)gh, msgb_l3len(msg)));
/* FIXME: return status message */
break;
msg = ranap_new_msg_dt(sapi, msg_nas->data, msgb_length(msg_nas));
msgb_free(msg_nas);
return sgsn_scu_iups_tx_data_req(uectx->rnc->scu_iups, uectx->conn_id, msg);
}
/* Send CL RANAP message over SCCP: */
int sgsn_ranap_iu_tx_cl(struct sgsn_sccp_user_iups *scu_iups,
const struct osmo_sccp_addr *dst_addr,
struct msgb *msg)
{
msg->l2h = msg->data;
return osmo_sccp_tx_unitdata_msg(scu_iups->scu, &scu_iups->local_sccp_addr, dst_addr, msg);
}
/* Send RANAP Error Indication */
int sgsn_ranap_iu_tx_error_ind(struct sgsn_sccp_user_iups *scu_iups,
const struct osmo_sccp_addr *dst_addr,
const RANAP_Cause_t *cause)
{
RANAP_CN_DomainIndicator_t domain = RANAP_CN_DomainIndicator_ps_domain;
struct msgb *ranap_msg;
ranap_msg = ranap_new_msg_error_ind(cause, NULL, &domain, NULL);
if (!ranap_msg)
return -ENOMEM;
return sgsn_ranap_iu_tx_cl(scu_iups, dst_addr, ranap_msg);
}
/* Send Iu Release for the given UE connection.
* If cause is NULL, Normal Release cause is sent, otherwise
* the provided cause. */
int sgsn_ranap_iu_tx_release(struct ranap_ue_conn_ctx *uectx, const struct RANAP_Cause *cause)
{
struct msgb *msg;
static const struct RANAP_Cause default_cause = {
.present = RANAP_Cause_PR_nAS,
.choice.radioNetwork = RANAP_CauseNAS_normal_release,
};
if (!cause)
cause = &default_cause;
msg = ranap_new_msg_iu_rel_cmd(cause);
return sgsn_scu_iups_tx_data_req(uectx->rnc->scu_iups, uectx->conn_id, msg);
}
void sgsn_ranap_iu_tx_release_free(struct ranap_ue_conn_ctx *ctx,
const struct RANAP_Cause *cause,
int timeout)
{
ctx->notification = false;
ctx->free_on_release = true;
int ret = sgsn_ranap_iu_tx_release(ctx, cause);
/* On Tx failure, trigger timeout immediately, as the response will never arrive */
if (ret)
timeout = 0;
osmo_timer_schedule(&ctx->release_timeout, timeout, 0);
}
static int ranap_handle_co_initial_ue(struct ranap_iu_rnc *rnc,
uint32_t conn_id,
const RANAP_InitialUE_MessageIEs_t *ies)
{
struct gprs_ra_id ra_id = {};
struct osmo_routing_area_id ra_id2 = {};
struct osmo_rnc_id rnc_id = {};
uint16_t sai;
struct ranap_ue_conn_ctx *ue;
struct msgb *msg = msgb_alloc(256, "RANAP->NAS");
if (ranap_parse_lai(&ra_id, &ies->lai) != 0) {
LOGP(DRANAP, LOGL_ERROR, "Failed to parse RANAP LAI IE\n");
return -1;
}
/* MMCTX can be invalid */
if (!(ies->presenceMask & INITIALUE_MESSAGEIES_RANAP_RAC_PRESENT)) {
LOGP(DRANAP, LOGL_ERROR, "Rejecting InitialUE msg without RAC IE\n");
return -1;
}
ra_id.rac = asn1str_to_u8(&ies->rac);
if (ra_id.rac == OSMO_RESERVED_RAC) {
LOGP(DRANAP, LOGL_ERROR,
"Rejecting RNC with invalid/internally used RAC 0x%02x\n", ra_id.rac);
return -1;
}
if (iu_grnc_id_parse(&rnc_id, &ies->globalRNC_ID) != 0) {
LOGP(DRANAP, LOGL_ERROR,
"Failed to parse RANAP Global-RNC-ID IE\n");
return -1;
}
sai = asn1str_to_u16(&ies->sai.sAC);
msgb_gmmh(msg) = msgb_put(msg, ies->nas_pdu.size);
memcpy(msgb_gmmh(msg), ies->nas_pdu.buf, ies->nas_pdu.size);
gprs_rai_to_osmo(&ra_id2, &ra_id);
/* Make sure we update LAC+RAC coming in on this connection. */
iu_rnc_update_rai_seen(rnc, &ra_id2);
ue = ue_conn_ctx_alloc(rnc, conn_id);
OSMO_ASSERT(ue);
ue->ra_id = ra_id;
/* Feed into the MM layer */
msg->dst = ue;
gsm0408_gprs_rcvmsg_iu(msg, &ra_id, &sai);
msgb_free(msg);
return 0;
}
void sgsn_ranap_iu_handle_co_initial(struct ranap_iu_rnc *iu_rnc,
uint32_t conn_id,
const ranap_message *message)
{
int rc;
LOGP(DRANAP, LOGL_NOTICE, "handle_co_initial(dir=%u, proc=%u)\n", message->direction, message->procedureCode);
if (message->direction != RANAP_RANAP_PDU_PR_initiatingMessage
|| message->procedureCode != RANAP_ProcedureCode_id_InitialUE_Message) {
LOGP(DRANAP, LOGL_ERROR, "Expected direction 'InitiatingMessage',"
" procedureCode 'InitialUE_Message', instead got %u and %u\n",
message->direction, message->procedureCode);
rc = -1;
} else
rc = ranap_handle_co_initial_ue(iu_rnc, conn_id, &message->msg.initialUE_MessageIEs);
if (rc) {
LOGP(DRANAP, LOGL_ERROR, "Error in %s (%d)\n", __func__, rc);
/* TODO handling of the error? */
}
}
int sgsn_ranap_iu_rx_co_initial_msg(struct sgsn_sccp_user_iups *scu_iups,
const struct osmo_sccp_addr *rem_sccp_addr,
uint32_t conn_id,
const uint8_t *data, size_t len)
{
struct iu_rnc_ev_msg_up_co_initial_ctx ev_ctx = {
.conn_id = conn_id,
};
RANAP_Cause_t cause;
int rc;
rc = ranap_cn_rx_co_decode2(&ev_ctx.message, data, len);
if (rc != 0) {
LOGP(DRANAP, LOGL_ERROR, "Not calling cn_ranap_handle_co_initial() due to rc=%d\n", rc);
goto free_ret;
}
ev_ctx.rnc = iu_rnc_find_by_addr(rem_sccp_addr);
if (!ev_ctx.rnc)
goto tx_err_ind;
rc = osmo_fsm_inst_dispatch(ev_ctx.rnc->fi, IU_RNC_EV_MSG_UP_CO_INITIAL, &ev_ctx);
if (rc != 0)
goto tx_err_ind;
goto free_ret;
tx_err_ind:
cause = (RANAP_Cause_t){
.present = RANAP_Cause_PR_protocol,
.choice.protocol = RANAP_CauseProtocol_message_not_compatible_with_receiver_state,
};
sgsn_ranap_iu_tx_error_ind(scu_iups, rem_sccp_addr, &cause);
free_ret:
/* Free the asn1 structs in message */
ranap_cn_rx_co_free(&ev_ctx.message);
return rc;
}
static int ranap_handle_co_dt(struct ranap_ue_conn_ctx *ue_ctx, const RANAP_DirectTransferIEs_t *ies)
{
struct gprs_ra_id _ra_id, *ra_id = NULL;
uint16_t _sai, *sai = NULL;
struct msgb *msg = msgb_alloc(256, "RANAP->NAS");
if (ies->presenceMask & DIRECTTRANSFERIES_RANAP_LAI_PRESENT) {
if (ranap_parse_lai(&_ra_id, &ies->lai) != 0) {
LOGP(DRANAP, LOGL_ERROR, "Failed to parse RANAP LAI IE\n");
return -1;
}
ra_id = &_ra_id;
if (ies->presenceMask & DIRECTTRANSFERIES_RANAP_RAC_PRESENT)
_ra_id.rac = asn1str_to_u8(&ies->rac);
if (ies->presenceMask & DIRECTTRANSFERIES_RANAP_SAI_PRESENT) {
_sai = asn1str_to_u16(&ies->sai.sAC);
sai = &_sai;
}
}
msgb_gmmh(msg) = msgb_put(msg, ies->nas_pdu.size);
memcpy(msgb_gmmh(msg), ies->nas_pdu.buf, ies->nas_pdu.size);
/* Feed into the MM/CC/SMS-CP layer */
msg->dst = ue_ctx;
gsm0408_gprs_rcvmsg_iu(msg, ra_id, sai);
msgb_free(msg);
return 0;
}
static int ranap_handle_co_err_ind(struct ranap_ue_conn_ctx *ue_ctx, const RANAP_ErrorIndicationIEs_t *ies)
{
if (ies->presenceMask & ERRORINDICATIONIES_RANAP_CAUSE_PRESENT)
LOGP(DRANAP, LOGL_ERROR, "Rx Error Indication (%s)\n",
ranap_cause_str(&ies->cause));
else
LOGP(DRANAP, LOGL_ERROR, "Rx Error Indication\n");
return 0;
}
static int ranap_handle_co_iu_rel_req(struct ranap_ue_conn_ctx *ue_ctx, const RANAP_Iu_ReleaseRequestIEs_t *ies)
{
LOGP(DRANAP, LOGL_INFO, "Received Iu Release Request, Sending Release Command\n");
sgsn_ranap_iu_tx_release(ue_ctx, &ies->cause);
return 0;
}
static int ranap_handle_co_rab_ass_resp(struct ranap_ue_conn_ctx *ue_ctx, const RANAP_RAB_AssignmentResponseIEs_t *ies)
{
int rc = -1;
LOGP(DRANAP, LOGL_INFO,
"Rx RAB Assignment Response for UE conn_id %u\n", ue_ctx->conn_id);
if (ies->presenceMask & RAB_ASSIGNMENTRESPONSEIES_RANAP_RAB_SETUPORMODIFIEDLIST_PRESENT) {
/* TODO: Iterate over list of SetupOrModifiedList IEs and handle each one */
RANAP_IE_t *ranap_ie = ies->raB_SetupOrModifiedList.raB_SetupOrModifiedList_ies.list.array[0];
RANAP_RAB_SetupOrModifiedItemIEs_t setup_ies;
rc = ranap_decode_rab_setupormodifieditemies_fromlist(&setup_ies, &ranap_ie->value);
if (rc) {
LOGP(DRANAP, LOGL_ERROR, "Error in ranap_decode_rab_setupormodifieditemies()\n");
return rc;
}
rc = global_iu_event(ue_ctx, RANAP_IU_EVENT_RAB_ASSIGN, &setup_ies);
ranap_free_rab_setupormodifieditemies(&setup_ies);
}
/* FIXME: handle RAB Ass failure? */
return rc;
}
/* Entry point for connection-oriented RANAP message */
void sgsn_ranap_iu_handle_co(struct ranap_ue_conn_ctx *ue_ctx, const ranap_message *message)
{
int rc;
LOGP(DRANAP, LOGL_NOTICE, "handle_co(dir=%u, proc=%u)\n", message->direction, message->procedureCode);
switch (message->direction) {
case RANAP_RANAP_PDU_PR_initiatingMessage:
switch (message->procedureCode) {
case RANAP_ProcedureCode_id_InitialUE_Message:
LOGP(DRANAP, LOGL_ERROR, "Got InitialUE_Message but this is not a new conn\n");
rc = -1;
break;
case RANAP_ProcedureCode_id_DirectTransfer:
rc = ranap_handle_co_dt(ue_ctx, &message->msg.directTransferIEs);
break;
case RANAP_ProcedureCode_id_ErrorIndication:
rc = ranap_handle_co_err_ind(ue_ctx, &message->msg.errorIndicationIEs);
break;
case RANAP_ProcedureCode_id_Iu_ReleaseRequest:
/* Iu Release Request */
rc = ranap_handle_co_iu_rel_req(ue_ctx, &message->msg.iu_ReleaseRequestIEs);
break;
default:
LOGP(DRANAP, LOGL_ERROR, "Received Initiating Message: unknown Procedure Code %d\n",
message->procedureCode);
rc = -1;
break;
}
break;
case RANAP_RANAP_PDU_PR_successfulOutcome:
switch (message->procedureCode) {
case RANAP_ProcedureCode_id_SecurityModeControl:
/* Security Mode Complete */
rc = global_iu_event(ue_ctx, RANAP_IU_EVENT_SECURITY_MODE_COMPLETE, NULL);
break;
case RANAP_ProcedureCode_id_Iu_Release:
/* Iu Release Complete */
rc = global_iu_event(ue_ctx, RANAP_IU_EVENT_IU_RELEASE, NULL);
if (rc) {
LOGP(DRANAP, LOGL_ERROR, "Iu Release event: Iu Event callback returned %d\n",
rc);
}
break;
default:
LOGP(DRANAP, LOGL_ERROR, "Received Successful Outcome: unknown Procedure Code %d\n",
message->procedureCode);
rc = -1;
break;
}
break;
case RANAP_RANAP_PDU_PR_outcome:
switch (message->procedureCode) {
case RANAP_ProcedureCode_id_RAB_Assignment:
/* RAB Assignment Response */
rc = ranap_handle_co_rab_ass_resp(ue_ctx, &message->msg.raB_AssignmentResponseIEs);
break;
default:
LOGP(DRANAP, LOGL_ERROR, "Received Outcome: unknown Procedure Code %d\n",
message->procedureCode);
rc = -1;
break;
}
break;
case RANAP_RANAP_PDU_PR_unsuccessfulOutcome:
default:
LOGP(DRANAP, LOGL_ERROR, "Received Unsuccessful Outcome: Procedure Code %d\n",
message->procedureCode);
rc = -1;
break;
}
if (rc) {
LOGP(DRANAP, LOGL_ERROR, "Error in %s (%d)\n", __func__, rc);
/* TODO handling of the error? */
}
}
int sgsn_ranap_iu_rx_co_msg(struct ranap_ue_conn_ctx *ue_ctx, const uint8_t *data, size_t len)
{
struct iu_rnc_ev_msg_up_co_ctx ev_ctx = {
.ue_ctx = ue_ctx,
};
RANAP_Cause_t cause;
int rc;
rc = ranap_cn_rx_co_decode2(&ev_ctx.message, data, len);
if (rc != 0) {
LOGP(DRANAP, LOGL_ERROR, "Not calling cn_ranap_handle_co() due to rc=%d\n", rc);
goto free_ret;
}
rc = osmo_fsm_inst_dispatch(ue_ctx->rnc->fi, IU_RNC_EV_MSG_UP_CO, &ev_ctx);
if (rc != 0)
goto tx_err_ind;
goto free_ret;
tx_err_ind:
cause = (RANAP_Cause_t){
.present = RANAP_Cause_PR_protocol,
.choice.protocol = RANAP_CauseProtocol_message_not_compatible_with_receiver_state,
};
sgsn_ranap_iu_tx_error_ind(ue_ctx->rnc->scu_iups, &ue_ctx->rnc->sccp_addr, &cause);
free_ret:
/* Free the asn1 structs in message */
ranap_cn_rx_co_free(&ev_ctx.message);
return rc;
}
static int ranap_handle_cl_reset_req(struct sgsn_sccp_user_iups *scu_iups,
const struct osmo_scu_unitdata_param *ud_prim,
const RANAP_ResetIEs_t *ies)
{
const RANAP_GlobalRNC_ID_t *grnc_id = NULL;
RANAP_Cause_t cause;
struct osmo_rnc_id rnc_id = {};
struct ranap_iu_rnc *rnc;
int rc;
if (ies->presenceMask & ERRORINDICATIONIES_RANAP_CN_DOMAININDICATOR_PRESENT) {
if (ies->cN_DomainIndicator != RANAP_CN_DomainIndicator_ps_domain) {
LOGP(DRANAP, LOGL_ERROR, "Rx RESET: Unexpected CN Domain Indicator %d\n",
(int)ies->cN_DomainIndicator);
cause = (RANAP_Cause_t){
.present = RANAP_Cause_PR_protocol,
.choice.protocol = RANAP_CauseProtocol_semantic_error,
};
return sgsn_ranap_iu_tx_error_ind(scu_iups, &ud_prim->calling_addr, &cause);
}
} /* else: assume PS */
/* FIXME: support handling Extended RNC-ID instead of Global RNC-ID */
if (!(ies->presenceMask & RESETIES_RANAP_GLOBALRNC_ID_PRESENT)) {
LOGP(DRANAP, LOGL_ERROR,
"Rx RESET: Missing RANAP Global-RNC-ID IE\n");
cause = (RANAP_Cause_t){
.present = RANAP_Cause_PR_protocol,
.choice.protocol = RANAP_CauseProtocol_transfer_syntax_error,
};
return sgsn_ranap_iu_tx_error_ind(scu_iups, &ud_prim->calling_addr, &cause);
}
grnc_id = &ies->globalRNC_ID;
if (iu_grnc_id_parse(&rnc_id, grnc_id) != 0) {
LOGP(DRANAP, LOGL_ERROR,
"Rx RESET: Failed to parse RANAP Global-RNC-ID IE\n");
cause = (RANAP_Cause_t){
.present = RANAP_Cause_PR_protocol,
.choice.protocol = RANAP_CauseProtocol_transfer_syntax_error,
};
return sgsn_ranap_iu_tx_error_ind(scu_iups, &ud_prim->calling_addr, &cause);
}
rnc = iu_rnc_find_or_create(&rnc_id, scu_iups, &ud_prim->calling_addr);
OSMO_ASSERT(rnc);
rc = osmo_fsm_inst_dispatch(rnc->fi, IU_RNC_EV_RX_RESET, NULL);
if (rc != 0) {
cause = (RANAP_Cause_t){
.present = RANAP_Cause_PR_protocol,
.choice.protocol = RANAP_CauseProtocol_message_not_compatible_with_receiver_state,
};
return sgsn_ranap_iu_tx_error_ind(scu_iups, &ud_prim->calling_addr, &cause);
}
return 0;
}
static int ranap_handle_cl_reset_ack(struct sgsn_sccp_user_iups *scu_iups,
const struct osmo_scu_unitdata_param *ud_prim,
const RANAP_ResetAcknowledgeIEs_t *ies)
{
struct ranap_iu_rnc *rnc;
RANAP_Cause_t cause;
int rc;
rnc = iu_rnc_find_by_addr(&ud_prim->calling_addr);
if (!rnc)
goto tx_err_ind;
rc = osmo_fsm_inst_dispatch(rnc->fi, IU_RNC_EV_RX_RESET_ACK, NULL);
if (rc != 0)
goto tx_err_ind;
return 0;
tx_err_ind:
cause = (RANAP_Cause_t){
.present = RANAP_Cause_PR_protocol,
.choice.protocol = RANAP_CauseProtocol_message_not_compatible_with_receiver_state,
};
return sgsn_ranap_iu_tx_error_ind(scu_iups, &ud_prim->calling_addr, &cause);
}
static int ranap_handle_cl_err_ind(struct sgsn_sccp_user_iups *scu_iups,
const struct osmo_scu_unitdata_param *ud_prim,
const RANAP_ErrorIndicationIEs_t *ies)
{
if (ies->presenceMask & ERRORINDICATIONIES_RANAP_CAUSE_PRESENT)
LOGP(DRANAP, LOGL_ERROR, "Rx Error Indication (%s)\n",
ranap_cause_str(&ies->cause));
else
LOGP(DRANAP, LOGL_ERROR, "Rx Error Indication\n");
return 0;
}
/* Entry point for connection-less RANAP message */
static void cn_ranap_handle_cl(struct sgsn_sccp_user_iups *scu_iups,
const struct osmo_scu_unitdata_param *ud_prim,
const ranap_message *message)
{
int rc;
switch (message->direction) {
case RANAP_RANAP_PDU_PR_initiatingMessage:
switch (message->procedureCode) {
case RANAP_ProcedureCode_id_Reset:
/* received reset.req, send reset.resp */
rc = ranap_handle_cl_reset_req(scu_iups, ud_prim, &message->msg.resetIEs);
break;
case RANAP_ProcedureCode_id_ErrorIndication:
rc = ranap_handle_cl_err_ind(scu_iups, ud_prim, &message->msg.errorIndicationIEs);
break;
default:
rc = -1;
break;
}
break;
case RANAP_RANAP_PDU_PR_successfulOutcome:
switch (message->procedureCode) {
case RANAP_ProcedureCode_id_Reset:
rc = ranap_handle_cl_reset_ack(scu_iups, ud_prim, &message->msg.resetAcknowledgeIEs);
break;
default:
rc = -1;
break;
}
break;
case RANAP_RANAP_PDU_PR_unsuccessfulOutcome:
case RANAP_RANAP_PDU_PR_outcome:
default:
rc = -1;
break;
}
if (rc) {
LOGP(DRANAP, LOGL_ERROR, "Error in %s (%d)\n", __func__, rc);
/* TODO handling of the error? */
}
}
int sgsn_ranap_iu_rx_cl_msg(struct sgsn_sccp_user_iups *scu_iups,
const struct osmo_scu_unitdata_param *ud_prim,
const uint8_t *data, size_t len)
{
ranap_message message;
int rc;
rc = ranap_cn_rx_cl_decode2(&message, data, len);
if (rc != 0) {
LOGP(DRANAP, LOGL_ERROR, "Not calling cn_ranap_handle_cl() due to rc=%d\n", rc);
goto free_ret;
}
cn_ranap_handle_cl(scu_iups, ud_prim, &message);
free_ret:
/* Free the asn1 structs in message */
ranap_cn_rx_cl_free(&message);
return rc;
}

View File

@@ -0,0 +1,510 @@
/* SGSN Routing Area for 2G */
/* (C) 2024 sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* Author: Alexander Couzens <lynxis@fe80.eu>
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/logging.h>
#include <osmocom/core/rate_ctr.h>
#include <osmocom/core/utils.h>
#include <osmocom/gsm/gsm23003.h>
#include <osmocom/gsm/gsm48.h>
#include <osmocom/sgsn/debug.h>
#include <osmocom/sgsn/gprs_bssgp.h>
#include <osmocom/sgsn/mmctx.h>
#include <osmocom/sgsn/sgsn.h>
#include <osmocom/sgsn/gprs_routing_area.h>
const struct value_string sgsn_ra_ran_type_names[] = {
{ RA_TYPE_GERAN_Gb, "GERAN_Gb" },
{ RA_TYPE_UTRAN_Iu, "UTRAN_Iu" },
{ 0, NULL },
};
static void _sgsn_ra_cell_free(struct sgsn_ra_cell *cell, bool drop_empty_ra)
{
struct sgsn_ra *ra;
if (!cell)
return;
llist_del(&cell->list);
/* to prevent double free of the Cell when freeing a Routing Area */
if (!drop_empty_ra) {
talloc_free(cell);
return;
}
ra = cell->ra;
talloc_free(cell);
if (llist_empty(&ra->cells_alive_list))
sgsn_ra_free(ra);
}
void sgsn_ra_cell_free(struct sgsn_ra_cell *cell)
{
_sgsn_ra_cell_free(cell, true);
}
void sgsn_ra_free(struct sgsn_ra *ra)
{
struct sgsn_ra_cell *cell, *cell2;
if (!ra)
return;
llist_for_each_entry_safe(cell, cell2, &ra->cells_alive_list, list) {
_sgsn_ra_cell_free(cell, false);
}
llist_del(&ra->list);
talloc_free(ra);
}
struct sgsn_ra *sgsn_ra_alloc(const struct osmo_routing_area_id *rai, enum sgsn_ra_ran_type ran_type)
{
struct sgsn_ra *ra;
ra = talloc_zero(sgsn->routing_area, struct sgsn_ra);
if (!ra)
return NULL;
INIT_LLIST_HEAD(&ra->cells_alive_list);
ra->rai = *rai;
ra->ran_type = ran_type;
llist_add(&ra->list, &sgsn->routing_area->ra_list);
return ra;
}
struct sgsn_ra *sgsn_ra_find_or_create(const struct osmo_routing_area_id *rai, enum sgsn_ra_ran_type ran_type)
{
struct sgsn_ra *ra = sgsn_ra_get_ra(rai);
if (ra) {
if (ra->ran_type == ran_type) {
LOGRAI(LOGL_ERROR, rai, "Trying to create an already existing RA with the same ran type %s. Using old RA.\n",
get_value_string(sgsn_ra_ran_type_names, ra->ran_type));
return ra;
}
LOGRAI(LOGL_ERROR, rai, "Failed to create an already existing RA with a different ran type %s.\n",
get_value_string(sgsn_ra_ran_type_names, ra->ran_type));
return NULL;
}
return sgsn_ra_alloc(rai, ran_type);
}
struct sgsn_ra_cell *sgsn_ra_cell_alloc_geran(struct sgsn_ra *ra, uint16_t cell_id, uint16_t nsei, uint16_t bvci)
{
struct sgsn_ra_cell *cell;
cell = talloc_zero(ra, struct sgsn_ra_cell);
if (!cell)
return NULL;
cell->ra = ra;
cell->ran_type = RA_TYPE_GERAN_Gb;
cell->u.geran.cell_id = cell_id;
cell->u.geran.bvci = bvci;
cell->u.geran.nsei = nsei;
llist_add(&cell->list, &ra->cells_alive_list);
return cell;
}
struct sgsn_ra *sgsn_ra_get_ra(const struct osmo_routing_area_id *rai)
{
struct sgsn_ra *ra;
llist_for_each_entry(ra, &sgsn->routing_area->ra_list, list)
if (osmo_rai_cmp(&ra->rai, rai) == 0)
return ra;
return NULL;
}
struct sgsn_ra *sgsn_ra_geran_get_ra(const struct osmo_routing_area_id *rai)
{
struct sgsn_ra *ra = sgsn_ra_get_ra(rai);
if (!ra)
return ra;
if (ra->ran_type == RA_TYPE_GERAN_Gb)
return ra;
return NULL;
}
struct sgsn_ra_cell *sgsn_ra_geran_get_cell_by_gb(uint16_t nsei, uint16_t bvci)
{
struct sgsn_ra *ra;
struct sgsn_ra_cell *cell;
/* BVCI = 0 is invalid, only valid for signalling within the BSSGP, not for a single cell */
if (bvci == 0)
return NULL;
llist_for_each_entry(ra, &sgsn->routing_area->ra_list, list) {
if (ra->ran_type != RA_TYPE_GERAN_Gb)
continue;
llist_for_each_entry(cell, &ra->cells_alive_list, list) {
if (cell->ran_type != RA_TYPE_GERAN_Gb)
continue;
if (cell->u.geran.bvci == bvci && cell->u.geran.nsei == nsei)
return cell;
}
}
return NULL;
}
struct sgsn_ra *sgsn_ra_utran_get_ra(const struct osmo_routing_area_id *ra_id)
{
struct sgsn_ra *ra = sgsn_ra_get_ra(ra_id);
if (!ra)
return ra;
if (ra->ran_type == RA_TYPE_UTRAN_Iu)
return ra;
return NULL;
}
int sgsn_ra_foreach_cell(struct sgsn_ra *ra, sgsn_ra_cb_t *cb, void *cb_data)
{
struct sgsn_ra_cell *cell, *tmp;
int ret = -ENOENT;
OSMO_ASSERT(cb);
llist_for_each_entry_safe(cell, tmp, &ra->cells_alive_list, list) {
ret = cb(cell, cb_data);
switch (ret) {
case SGSN_RA_CB_CONT:
continue;
case SGSN_RA_CB_STOP:
return 0;
case SGSN_RA_CB_ERROR:
return -1;
default:
OSMO_ASSERT(0);
}
}
return ret;
}
int sgsn_ra_foreach_cell2(struct osmo_routing_area_id *rai, sgsn_ra_cb_t *cb, void *cb_data)
{
struct sgsn_ra *ra;
OSMO_ASSERT(rai);
OSMO_ASSERT(cb);
ra = sgsn_ra_get_ra(rai);
if (!ra)
return -ENOENT;
return sgsn_ra_foreach_cell(ra, cb, cb_data);
}
/* valid for GERAN */
struct sgsn_ra_cell *sgsn_ra_geran_get_cell_by_ra(const struct sgsn_ra *ra, uint16_t cell_id)
{
struct sgsn_ra_cell *cell;
if (ra->ran_type != RA_TYPE_GERAN_Gb)
return NULL;
llist_for_each_entry(cell, &ra->cells_alive_list, list) {
if (cell->ran_type != RA_TYPE_GERAN_Gb)
continue;
if (cell->u.geran.cell_id == cell_id)
return cell;
}
return NULL;
}
/* valid for GERAN */
struct sgsn_ra_cell *sgsn_ra_geran_get_cell_by_lai(const struct osmo_location_area_id *lai, uint16_t cell_id)
{
struct sgsn_ra *ra;
struct sgsn_ra_cell *cell;
/* This is a little bit in-efficient. A more performance way, but more complex would
* adding a llist for LAC on top of the routing areas */
llist_for_each_entry(ra, &sgsn->routing_area->ra_list, list) {
if (ra->ran_type != RA_TYPE_GERAN_Gb)
continue;
if (osmo_lai_cmp(&ra->rai.lac, lai) != 0)
continue;
llist_for_each_entry(cell, &ra->cells_alive_list, list) {
if (cell->ran_type != RA_TYPE_GERAN_Gb)
continue;
if (cell->u.geran.cell_id == cell_id)
return cell;
}
}
return NULL;
}
/*! Return the GERAN cell by searching for the RA, when found, search the cell within the RA
*
* \param cgi_ps
* \return the cell or NULL if not found
*/
struct sgsn_ra_cell *sgsn_ra_geran_get_cell_by_cgi_ps(const struct osmo_cell_global_id_ps *cgi_ps)
{
struct sgsn_ra *ra;
OSMO_ASSERT(cgi_ps);
ra = sgsn_ra_geran_get_ra(&cgi_ps->rai);
if (!ra)
return NULL;
return sgsn_ra_geran_get_cell_by_ra(ra, cgi_ps->cell_identity);
}
struct sgsn_ra_cell *sgsn_ra_geran_get_cell_by_cgi(const struct osmo_cell_global_id *cgi)
{
OSMO_ASSERT(cgi);
return sgsn_ra_geran_get_cell_by_lai(&cgi->lai, cgi->cell_identity);
}
/*! Callback from the BSSGP layer on NM RESET IND for a cell (PtP BVCI).
*
* \param nsei
* \param bvci
* \param cgi_ps
* \return 0 on success or -ENOMEM
*/
int sgsn_ra_geran_bvc_cell_reset_ind(uint16_t nsei, uint16_t bvci, struct osmo_cell_global_id_ps *cgi_ps)
{
struct sgsn_ra *ra;
struct sgsn_ra_cell *cell;
bool ra_created = false;
OSMO_ASSERT(cgi_ps);
/* TODO: do we have to move all MS to GMM IDLE state when this happens for a alive cell which got reseted? */
ra = sgsn_ra_geran_get_ra(&cgi_ps->rai);
if (!ra) {
/* TODO: check for UTRAN rai when introducing UTRAN support */
ra = sgsn_ra_find_or_create(&cgi_ps->rai, RA_TYPE_GERAN_Gb);
if (!ra)
return -ENOMEM;
ra_created = true;
}
if (!ra_created) {
cell = sgsn_ra_geran_get_cell_by_ra(ra, cgi_ps->cell_identity);
if (cell && cell->ran_type == RA_TYPE_GERAN_Gb) {
/* Cell already exist, update NSEI/BVCI */
if (cell->u.geran.bvci != bvci || cell->u.geran.nsei != nsei) {
LOGRA(LOGL_INFO, ra, "GERAN Cell changed DLCI. Old: nsei/bvci %05u/%05u New: nsei/bvci %05u/%05u\n",
cell->u.geran.nsei, cell->u.geran.bvci, nsei, bvci);
cell->u.geran.bvci = bvci;
cell->u.geran.nsei = nsei;
}
return 0;
}
if (cell && cell->ran_type != RA_TYPE_GERAN_Gb) {
/* How can we have here a RA change? Must be a configuration error. */
LOGRAI(LOGL_INFO, &cgi_ps->rai, "CGI %s: RAN change detected to GERAN!", osmo_cgi_ps_name(cgi_ps));
_sgsn_ra_cell_free(cell, false);
cell = NULL;
}
if (!cell) {
char old_ra[32];
char new_ra[32];
/* check for the same cell id within the location area. The cell id is also unique for the cell within the LAC
* This should only happen when a Cell is changing routing areas */
cell = sgsn_ra_geran_get_cell_by_lai(&cgi_ps->rai.lac, cgi_ps->cell_identity);
if (cell) {
LOGRAI(LOGL_INFO, &cgi_ps->rai, "CGI %s: changed Routing Area. Old: %s, New: %s\n",
osmo_cgi_ps_name(cgi_ps),
osmo_rai_name2_buf(old_ra, sizeof(old_ra), &cell->ra->rai),
osmo_rai_name2_buf(new_ra, sizeof(new_ra), &cgi_ps->rai));
OSMO_ASSERT(cell->ra != ra);
/* the old RA is definitive not our ra! Drop the old ra */
_sgsn_ra_cell_free(cell, true);
cell = NULL;
}
}
}
cell = sgsn_ra_cell_alloc_geran(ra, cgi_ps->cell_identity, nsei, bvci);
if (!cell)
return -ENOMEM;
LOGRA(LOGL_INFO, ra, "New cell registered %s via nsei/bvci %05u/%05u\n", osmo_cgi_ps_name(cgi_ps), nsei, bvci);
return 0;
}
/* FIXME: call it on BSSGP BLOCK + unavailable with BVCI */
int sgsn_ra_geran_nsei_failure_ind(uint16_t nsei)
{
struct sgsn_ra *ra, *ra2;
struct sgsn_ra_cell *cell, *cell2;
bool found = false;
llist_for_each_entry_safe(ra, ra2, &sgsn->routing_area->ra_list, list) {
if (ra->ran_type != RA_TYPE_GERAN_Gb)
continue;
llist_for_each_entry_safe(cell, cell2, &ra->cells_alive_list, list) {
if (cell->ran_type != RA_TYPE_GERAN_Gb)
continue;
if (cell->u.geran.nsei == nsei) {
found = true;
_sgsn_ra_cell_free(cell, false);
}
}
if (llist_empty(&ra->cells_alive_list))
sgsn_ra_free(ra);
}
return found ? 0 : -ENOENT;
}
void sgsn_ra_geran_bvc_sign_reset_ind(uint16_t nsei)
{
sgsn_ra_geran_nsei_failure_ind(nsei);
}
int sgsn_ra_geran_page_ra(const struct osmo_routing_area_id *rai, struct sgsn_mm_ctx *mmctx)
{
struct sgsn_ra *ra;
struct sgsn_ra_cell *cell;
int ret = -ENOENT;
rate_ctr_inc(rate_ctr_group_get_ctr(mmctx->ctrg, GMM_CTR_PAGING_PS));
ra = sgsn_ra_geran_get_ra(rai);
if (!ra)
return -ENOENT;
llist_for_each_entry(cell, &ra->cells_alive_list, list) {
if (cell->ran_type == RA_TYPE_GERAN_Gb) {
sgsn_bssgp_page_ps_bvci(mmctx, cell->u.geran.nsei, cell->u.geran.bvci);
ret = 0;
}
}
return ret;
}
#ifdef BUILD_IU
/* Register a new UTRAN Routing Area if possible.
* Return 0 on success and < 0 on failure. */
int sgsn_ra_utran_register(const struct osmo_routing_area_id *rai, const struct osmo_rnc_id *rnc_id)
{
struct sgsn_ra *ra = sgsn_ra_get_ra(rai);
if (!ra) {
ra = sgsn_ra_alloc(rai, RA_TYPE_UTRAN_Iu);
if (!ra) {
LOGP(DRA, LOGL_ERROR, "Couldn't create new RA for %s ran type %s\n",
osmo_rai_name2(rai), get_value_string(sgsn_ra_ran_type_names, ra->ran_type));
return -ENOMEM;
}
ra->u.utran.rnc_id = *rnc_id;
LOGRA(LOGL_INFO, ra, "New UTRAN RA by RNC %s\n", osmo_rnc_id_name(&ra->u.utran.rnc_id));
return 0;
}
if (ra->ran_type == RA_TYPE_GERAN_Gb) {
LOGRA(LOGL_ERROR, ra, "rejecting new RA of type %s, because already present RA has ran type %s\n",
get_value_string(sgsn_ra_ran_type_names, RA_TYPE_UTRAN_Iu),
get_value_string(sgsn_ra_ran_type_names, ra->ran_type));
return -ENOENT;
}
/* RA already known */
if (osmo_rnc_id_cmp(&ra->u.utran.rnc_id, rnc_id) == 0)
return 0;
/* RNC id changed */
char new_rnc_id_name[32];
osmo_rnc_id_name_buf(new_rnc_id_name, sizeof(new_rnc_id_name), rnc_id);
LOGRA(LOGL_INFO, ra, "RNC Id changed from %s to %s\n",
osmo_rnc_id_name(&ra->u.utran.rnc_id), new_rnc_id_name);
ra->u.utran.rnc_id = *rnc_id;
return 0;
}
int sgsn_ra_utran_page_ra(const struct osmo_routing_area_id *ra_id, const struct sgsn_mm_ctx *mmctx)
{
struct sgsn_ra *ra;
rate_ctr_inc(rate_ctr_group_get_ctr(mmctx->ctrg, GMM_CTR_PAGING_PS));
ra = sgsn_ra_utran_get_ra(ra_id);
if (!ra)
return -ENOENT;
/* Try to page by TMSI if possible */
if (mmctx->p_tmsi != GSM_RESERVED_TMSI)
return ranap_iu_page_ps2(mmctx->imsi, &mmctx->p_tmsi, ra_id);
if (mmctx->p_tmsi_old != GSM_RESERVED_TMSI)
return ranap_iu_page_ps2(mmctx->imsi, &mmctx->p_tmsi_old, ra_id);
/* Page by IMSI */
return ranap_iu_page_ps2(mmctx->imsi, NULL, ra_id);
}
#else
int sgsn_ra_utran_page_ra(const struct osmo_routing_area_id *ra_id, const struct sgsn_mm_ctx *mmctx)
{
return -1;
}
int sgsn_ra_utran_register(const struct osmo_routing_area_id *rai, const struct osmo_rnc_id *rnc_id)
{
return -1;
}
#endif /* BUILD_IU */
void sgsn_ra_init(struct sgsn_instance *inst)
{
inst->routing_area = talloc_zero(inst, struct sgsn_ra_global);
OSMO_ASSERT(inst->routing_area);
INIT_LLIST_HEAD(&inst->routing_area->ra_list);
}

View File

@@ -114,9 +114,14 @@ static void pdpctx_timer_start(struct sgsn_pdp_ctx *pdp, unsigned int T)
static void pdpctx_timer_stop(struct sgsn_pdp_ctx *pdp, unsigned int T)
{
if (pdp->T != T)
if (!osmo_timer_pending(&pdp->timer)) {
LOGPDPCTXP(LOGL_NOTICE, pdp, "Stopping *inactive* PDP timer %u\n", T);
return;
}
if (pdp->T != T) {
LOGPDPCTXP(LOGL_ERROR, pdp, "Stopping PDP timer %u but "
"%u is running\n", T, pdp->T);
}
osmo_timer_del(&pdp->timer);
}

View File

@@ -834,9 +834,9 @@ int sndcp_ll_unitdata_ind(struct msgb *msg, struct gprs_llc_lle *lle,
return -EIO;
}
/* FIXME: move this RA_ID up to the LLME or even higher */
bssgp_parse_cell_id(&sne->ra_id, msgb_bcid(msg));
bssgp_parse_cell_id2(&sne->rai, NULL, msgb_bcid(msg), 8);
mmctx = sgsn_mm_ctx_by_tlli(msgb_tlli(msg), &sne->ra_id);
mmctx = sgsn_mm_ctx_by_tlli(msgb_tlli(msg), &sne->rai);
if (!mmctx) {
LOGP(DSNDCP, LOGL_ERROR, "Message for non-existing MM ctx "
"(lle=%p, TLLI=%08x, SAPI=%u, NSAPI=%u)\n",
@@ -895,7 +895,7 @@ int sndcp_sn_unitdata_ind(struct gprs_sndcp_entity *sne,
struct msgb *msg, uint32_t npdu_len, uint8_t *npdu)
{
/* Hand it off N-PDU to the correct GTP tunnel + GGSN: */
return sgsn_gtp_data_req(&sne->ra_id, sne->lle->llme->tlli,
return sgsn_gtp_data_req(&sne->rai, sne->lle->llme->tlli,
sne->nsapi, msg, npdu_len, npdu);
}

View File

@@ -376,7 +376,8 @@ static void gprs_subscr_gsup_insert_data(struct gprs_subscr *subscr,
}
OSMO_ASSERT(pdp_data != NULL);
pdp_data->pdp_type = pdp_info->pdp_type;
pdp_data->pdp_type_org = pdp_info->pdp_type_org;
pdp_data->pdp_type_nr = pdp_info->pdp_type_nr;
osmo_apn_to_str(pdp_data->apn_str,
pdp_info->apn_enc, pdp_info->apn_enc_len);
@@ -876,8 +877,8 @@ struct gprs_subscr *gprs_subscr_get_or_create_by_mmctx(struct sgsn_mm_ctx *mmctx
osmo_strlcpy(subscr->imei, mmctx->imei, sizeof(subscr->imei));
if (subscr->lac != mmctx->ra.lac)
subscr->lac = mmctx->ra.lac;
if (subscr->lac != mmctx->ra.lac.lac)
subscr->lac = mmctx->ra.lac.lac;
subscr->sgsn_data->mm = mmctx;
mmctx->subscr = gprs_subscr_get(subscr);

View File

@@ -131,6 +131,16 @@ struct sgsn_mme_ctx *sgsn_mme_ctx_by_route(const struct sgsn_instance *sgsn, con
return NULL;
}
struct sgsn_mme_ctx *sgsn_mme_ctx_by_gummei(const struct sgsn_instance *sgsn, const struct osmo_gummei *gummei)
{
struct sgsn_mme_ctx *mme;
llist_for_each_entry(mme, &sgsn->mme_list, list) {
if (mme->gummei_valid && !osmo_gummei_cmp(&mme->gummei, gummei))
return mme;
}
return NULL;
}
struct sgsn_mme_ctx *sgsn_mme_ctx_by_default_route(const struct sgsn_instance *sgsn)
{
struct sgsn_mme_ctx *mme;

295
src/sgsn/iu_client.c Normal file
View File

@@ -0,0 +1,295 @@
/* Common parts of IuCS and IuPS interfaces implementation */
/* (C) 2016-2017 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "config.h"
#include <stdint.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <osmocom/gsm/protocol/gsm_04_08_gprs.h>
#include <osmocom/core/logging.h>
#include <osmocom/crypt/auth.h>
#include <osmocom/gprs/gprs_msgb.h>
#include <osmocom/sigtran/sccp_sap.h>
#include <osmocom/sigtran/sccp_helpers.h>
#include <osmocom/sccp/sccp_types.h>
#include <osmocom/sgsn/debug.h>
#include <osmocom/sgsn/gprs_ranap.h>
#include <osmocom/sgsn/iu_client.h>
#include <osmocom/sgsn/iu_rnc.h>
#include <osmocom/sgsn/sccp.h>
#include <osmocom/sgsn/sgsn.h>
const struct value_string iu_client_event_type_names[] = {
OSMO_VALUE_STRING(RANAP_IU_EVENT_RAB_ASSIGN),
OSMO_VALUE_STRING(RANAP_IU_EVENT_SECURITY_MODE_COMPLETE),
OSMO_VALUE_STRING(RANAP_IU_EVENT_IU_RELEASE),
OSMO_VALUE_STRING(RANAP_IU_EVENT_LINK_INVALIDATED),
OSMO_VALUE_STRING(RANAP_IU_EVENT_NEW_AREA),
{ 0, NULL }
};
int global_iu_event(struct ranap_ue_conn_ctx *ue_ctx,
enum ranap_iu_event_type type,
void *data)
{
if (ue_ctx && !ue_ctx->notification)
return 0;
LOGP(DRANAP, LOGL_DEBUG, "Submit Iu event to upper layer: %s\n", iu_client_event_type_str(type));
return sgsn_ranap_iu_event(ue_ctx, type, data);
}
static void ue_conn_ctx_release_timeout_cb(void *ctx_)
{
struct ranap_ue_conn_ctx *ctx = (struct ranap_ue_conn_ctx *)ctx_;
global_iu_event(ctx, RANAP_IU_EVENT_IU_RELEASE, NULL);
}
struct ranap_ue_conn_ctx *ue_conn_ctx_alloc(struct ranap_iu_rnc *rnc, uint32_t conn_id)
{
struct ranap_ue_conn_ctx *ctx = talloc_zero(sgsn, struct ranap_ue_conn_ctx);
ctx->rnc = rnc;
ctx->conn_id = conn_id;
ctx->notification = true;
ctx->free_on_release = false;
osmo_timer_setup(&ctx->release_timeout,
ue_conn_ctx_release_timeout_cb,
ctx);
llist_add(&ctx->list, &rnc->scu_iups->ue_conn_ctx_list);
return ctx;
}
void sgsn_ranap_iu_free_ue(struct ranap_ue_conn_ctx *ue_ctx)
{
if (!ue_ctx)
return;
osmo_timer_del(&ue_ctx->release_timeout);
osmo_sccp_tx_disconn(ue_ctx->rnc->scu_iups->scu, ue_ctx->conn_id, NULL, 0);
llist_del(&ue_ctx->list);
talloc_free(ue_ctx);
}
void ue_conn_ctx_link_invalidated_free(struct ranap_ue_conn_ctx *ue)
{
uint32_t conn_id = ue->conn_id;
struct sgsn_sccp_user_iups *scu_iups = ue->rnc->scu_iups;
global_iu_event(ue, RANAP_IU_EVENT_LINK_INVALIDATED, NULL);
/* A RANAP_IU_EVENT_LINK_INVALIDATED, can lead to a free */
ue = sgsn_scu_iups_ue_conn_ctx_find(scu_iups, conn_id);
if (!ue)
return;
if (ue->free_on_release)
sgsn_ranap_iu_free_ue(ue);
}
/***********************************************************************
* Paging
***********************************************************************/
/* legacy, do a first match with ignoring PLMN */
static struct ranap_iu_rnc *iu_rnc_lac_rac_find_legacy(uint16_t lac, uint8_t rac)
{
struct ranap_iu_rnc *rnc;
struct iu_lac_rac_entry *e;
llist_for_each_entry(rnc, &sgsn->rnc_list, entry) {
llist_for_each_entry(e, &rnc->lac_rac_list, entry) {
if (e->rai.lac.lac == lac && e->rai.rac == rac)
return rnc;
}
}
return NULL;
}
/*! Old paging() doesn't use PLMN and transmit paging command only to the first RNC */
int ranap_iu_page_cs(const char *imsi, const uint32_t *tmsi, uint16_t lac)
{
struct ranap_iu_rnc *rnc;
char log_msg[32] = {};
int rc;
if (tmsi)
snprintf(log_msg, sizeof(log_msg), "TMSI %08x\n", *tmsi);
else
snprintf(log_msg, sizeof(log_msg), "IMSI %s\n", imsi);
rnc = iu_rnc_lac_rac_find_legacy(lac, 0);
if (!rnc) {
LOGP(DRANAP, LOGL_INFO, "Found no RNC to Page CS on LAC %u for %s",
lac, log_msg);
return 0;
}
rc = iu_rnc_tx_paging_cmd(rnc, imsi, tmsi, false, 0);
if (rc != 0) {
LOG_RNC(rnc, LOGL_ERROR, "Failed to tx Paging CS for LAC %u for %s",
lac, log_msg);
return 0;
}
return 1;
}
/*! Old paging() doesn't use PLMN and transmit paging command only to the first RNC */
int ranap_iu_page_ps(const char *imsi, const uint32_t *ptmsi, uint16_t lac, uint8_t rac)
{
struct ranap_iu_rnc *rnc;
char log_msg[32] = {};
int rc;
if (ptmsi)
snprintf(log_msg, sizeof(log_msg), "P-TMSI %08x\n", *ptmsi);
else
snprintf(log_msg, sizeof(log_msg), "IMSI %s\n", imsi);
rnc = iu_rnc_lac_rac_find_legacy(lac, rac);
if (!rnc) {
LOGP(DRANAP, LOGL_INFO, "Found no RNC to Page PS on LAC %u RAC %u for %s",
lac, rac, log_msg);
return 0;
}
rc = iu_rnc_tx_paging_cmd(rnc, imsi, ptmsi, true, 0);
if (rc != 0) {
LOG_RNC(rnc, LOGL_ERROR, "Failed to tx Paging PS for LAC %u RAC %u for %s",
lac, rac, log_msg);
return 0;
}
return 1;
}
/*! Transmit a single page request towards all RNCs serving the specific LAI (no page retransmission).
*
* \param imsi the imsi as human readable string
* \param tmsi NULL or pointer to the tmsi
* \param lai full Location Area Identifier
* \return amount of paged RNCs. 0 when no RNC found.
*/
int ranap_iu_page_cs2(const char *imsi, const uint32_t *tmsi, const struct osmo_location_area_id *lai)
{
struct ranap_iu_rnc *rnc;
struct iu_lac_rac_entry *entry;
char log_msg[32] = {};
unsigned int paged = 0;
int rc;
if (tmsi)
snprintf(log_msg, sizeof(log_msg), "TMSI %08x\n", *tmsi);
else
snprintf(log_msg, sizeof(log_msg), "IMSI %s\n", imsi);
/* find all RNCs which are serving this LA */
llist_for_each_entry(rnc, &sgsn->rnc_list, entry) {
llist_for_each_entry(entry, &rnc->lac_rac_list, entry) {
if (osmo_lai_cmp(&entry->rai.lac, lai))
continue;
rc = iu_rnc_tx_paging_cmd(rnc, imsi, tmsi, false, 0);
if (rc != 0) {
LOG_RNC(rnc, LOGL_ERROR, "Failed to tx Paging CS for LAI %s for %s",
osmo_lai_name(lai), log_msg);
} else {
paged++;
}
break;
}
}
if (paged)
LOGP(DRANAP, LOGL_DEBUG, "Paged CS %u RNCs on LAI %s for %s",
paged, osmo_lai_name(lai), log_msg);
else
LOGP(DRANAP, LOGL_INFO, "Found no RNC to Page CS on LAI %s for %s",
osmo_lai_name(lai), log_msg);
return paged;
}
/*! Transmit a single page request towards all RNCs serving the specific RAI (no page retransmission).
*
* \param imsi the imsi as human readable string
* \param ptmsi NULL or pointer to the ptmsi
* \param rai full Location Area Identifier
* \return amount of paged RNCs. 0 when no RNC found.
*/
int ranap_iu_page_ps2(const char *imsi, const uint32_t *ptmsi, const struct osmo_routing_area_id *rai)
{
struct ranap_iu_rnc *rnc;
struct iu_lac_rac_entry *entry;
char log_msg[32] = {};
unsigned int paged = 0;
int rc;
if (ptmsi)
snprintf(log_msg, sizeof(log_msg), "P-TMSI %08x\n", *ptmsi);
else
snprintf(log_msg, sizeof(log_msg), "IMSI %s\n", imsi);
/* find all RNCs which are serving this RAC */
llist_for_each_entry(rnc, &sgsn->rnc_list, entry) {
llist_for_each_entry(entry, &rnc->lac_rac_list, entry) {
if (osmo_rai_cmp(&entry->rai, rai))
continue;
rc = iu_rnc_tx_paging_cmd(rnc, imsi, ptmsi, true, 0);
if (rc != 0) {
LOG_RNC(rnc, LOGL_ERROR, "Failed to tx Paging PS for RAI %s for %s",
osmo_rai_name2(rai), log_msg);
} else {
paged++;
}
break;
}
}
if (paged)
LOGP(DRANAP, LOGL_DEBUG, "Paged PS %u RNCs on RAI %s for %s",
paged, osmo_rai_name2(rai), log_msg);
else
LOGP(DRANAP, LOGL_INFO, "Found no RNC to Page PS on RAI %s for %s",
osmo_rai_name2(rai), log_msg);
return paged;
}
/***********************************************************************
*
***********************************************************************/
int ranap_iu_init(void *ctx)
{
talloc_asn1_ctx = talloc_named_const(ctx, 1, "asn1");
return 0;
}

255
src/sgsn/iu_rnc.c Normal file
View File

@@ -0,0 +1,255 @@
/* A remote RNC (Radio Network Controller), connected over IuPS */
/* (C) 2016-2017 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "config.h"
#include <stdint.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <osmocom/gsm/protocol/gsm_04_08_gprs.h>
#include <osmocom/core/logging.h>
#include <osmocom/crypt/auth.h>
#include <osmocom/gprs/gprs_msgb.h>
#include <osmocom/sigtran/sccp_sap.h>
#include <osmocom/sigtran/sccp_helpers.h>
#include <osmocom/sccp/sccp_types.h>
#include <osmocom/sgsn/debug.h>
#include <osmocom/sgsn/gprs_ranap.h>
#include <osmocom/sgsn/iu_client.h>
#include <osmocom/sgsn/iu_rnc.h>
#include <osmocom/sgsn/iu_rnc_fsm.h>
#include <osmocom/sgsn/sccp.h>
#include <osmocom/sgsn/sgsn.h>
static struct ranap_iu_rnc *iu_rnc_alloc(const struct osmo_rnc_id *rnc_id,
struct sgsn_sccp_user_iups *scu_iups,
const struct osmo_sccp_addr *rnc_sccp_addr)
{
struct ranap_iu_rnc *rnc;
char *addr_str, *pos;
rnc = talloc_zero(sgsn, struct ranap_iu_rnc);
OSMO_ASSERT(rnc);
INIT_LLIST_HEAD(&rnc->lac_rac_list);
rnc->rnc_id = *rnc_id;
rnc->scu_iups = scu_iups;
rnc->sccp_addr = *rnc_sccp_addr;
rnc->fi = osmo_fsm_inst_alloc(&iu_rnc_fsm, rnc, rnc, LOGL_INFO, NULL);
OSMO_ASSERT(rnc->fi);
/* Unfortunately, osmo_sccp_inst_addr_name() returns "RI=SSN_PC,PC=0.24.1,SSN=BSSAP" but neither commas nor
* full-stops are allowed as FSM inst id. Make it "RI-SSN_PC:PC-0-24-1:SSN-BSSAP". */
addr_str = osmo_sccp_addr_dump(rnc_sccp_addr);
for (pos = addr_str; *pos; pos++) {
if (*pos == ',')
*pos = ':';
else if (*pos == '.' || *pos == '=')
*pos = '-';
}
osmo_fsm_inst_update_id_f(rnc->fi, "RNC_ID-%s:%s",
osmo_rnc_id_name(rnc_id), addr_str);
llist_add(&rnc->entry, &sgsn->rnc_list);
sgsn_stat_inc(SGSN_STAT_IU_PEERS_TOTAL, 1);
LOGP(DRANAP, LOGL_NOTICE, "New RNC %s at %s\n",
osmo_rnc_id_name(&rnc->rnc_id), osmo_sccp_addr_dump(rnc_sccp_addr));
return rnc;
}
static struct ranap_iu_rnc *iu_rnc_find_by_id(const struct osmo_rnc_id *rnc_id)
{
struct ranap_iu_rnc *rnc;
llist_for_each_entry(rnc, &sgsn->rnc_list, entry) {
if (!osmo_rnc_id_cmp(&rnc->rnc_id, rnc_id))
return rnc;
}
return NULL;
}
struct ranap_iu_rnc *iu_rnc_find_by_addr(const struct osmo_sccp_addr *rnc_sccp_addr)
{
struct ranap_iu_rnc *rnc;
llist_for_each_entry(rnc, &sgsn->rnc_list, entry) {
if (osmo_sccp_addr_ri_cmp(rnc_sccp_addr, &rnc->sccp_addr))
continue;
return rnc;
}
return NULL;
}
struct ranap_iu_rnc *iu_rnc_find_or_create(const struct osmo_rnc_id *rnc_id,
struct sgsn_sccp_user_iups *scu_iups,
const struct osmo_sccp_addr *addr)
{
struct ranap_iu_rnc *rnc;
/* Make sure we know this rnc_id and that this SCCP address is in our records */
rnc = iu_rnc_find_by_id(rnc_id);
if (rnc) {
if (!osmo_sccp_addr_ri_cmp(&rnc->sccp_addr, addr)) {
LOGP(DRANAP, LOGL_NOTICE, "RNC %s changed its SCCP addr to %s\n",
osmo_rnc_id_name(&rnc->rnc_id), osmo_sccp_addr_dump(addr));
rnc->sccp_addr = *addr;
}
} else {
rnc = iu_rnc_alloc(rnc_id, scu_iups, addr);
}
return rnc;
}
/* Find a match for the given LAC (and RAC). For CS, pass rac as 0.
* If rnc and lre pointers are not NULL, *rnc / *lre are set to NULL if no match is found, or to the
* match if a match is found. Return true if a match is found. */
static bool iu_rnc_lac_rac_find(struct ranap_iu_rnc **rnc, struct iu_lac_rac_entry **lre,
const struct osmo_routing_area_id *ra_id)
{
struct ranap_iu_rnc *r;
struct iu_lac_rac_entry *e;
if (rnc)
*rnc = NULL;
if (lre)
*lre = NULL;
llist_for_each_entry(r, &sgsn->rnc_list, entry) {
llist_for_each_entry(e, &r->lac_rac_list, entry) {
if (!osmo_rai_cmp(&e->rai, ra_id)) {
if (rnc)
*rnc = r;
if (lre)
*lre = e;
return true;
}
}
}
return false;
}
static void global_iu_event_new_area(const struct osmo_rnc_id *rnc_id, const struct osmo_routing_area_id *rai)
{
struct ranap_iu_event_new_area new_area = (struct ranap_iu_event_new_area) {
.rnc_id = rnc_id,
.cell_type = RANAP_IU_NEW_RAC
};
if (rai->rac == OSMO_RESERVED_RAC) {
new_area.cell_type = RANAP_IU_NEW_LAC;
new_area.u.lai = &rai->lac;
} else {
new_area.cell_type = RANAP_IU_NEW_RAC;
new_area.u.rai = rai;
}
global_iu_event(NULL, RANAP_IU_EVENT_NEW_AREA, &new_area);
}
void iu_rnc_update_rai_seen(struct ranap_iu_rnc *rnc, const struct osmo_routing_area_id *rai)
{
struct ranap_iu_rnc *old_rnc;
struct iu_lac_rac_entry *lre;
/* Detect whether the LAC,RAC is already recorded in another RNC */
iu_rnc_lac_rac_find(&old_rnc, &lre, rai);
if (old_rnc && old_rnc != rnc) {
/* LAC, RAC already exists in a different RNC */
LOGP(DRANAP, LOGL_NOTICE, "LAC/RAC %s moved from RNC %s %s",
osmo_rai_name2(rai),
osmo_rnc_id_name(&old_rnc->rnc_id), osmo_sccp_addr_dump(&old_rnc->sccp_addr));
LOGPC(DRANAP, LOGL_NOTICE, " to RNC %s %s\n",
osmo_rnc_id_name(&rnc->rnc_id), osmo_sccp_addr_dump(&rnc->sccp_addr));
llist_del(&lre->entry);
llist_add(&lre->entry, &rnc->lac_rac_list);
global_iu_event_new_area(&rnc->rnc_id, rai);
} else if (!old_rnc) {
/* LAC, RAC not recorded yet */
LOGP(DRANAP, LOGL_NOTICE, "RNC %s: new LAC/RAC %s\n",
osmo_rnc_id_name(&rnc->rnc_id), osmo_rai_name2(rai));
lre = talloc_zero(rnc, struct iu_lac_rac_entry);
lre->rai = *rai;
llist_add(&lre->entry, &rnc->lac_rac_list);
global_iu_event_new_area(&rnc->rnc_id, rai);
}
/* else, LAC,RAC already recorded with the current RNC. */
}
void iu_rnc_discard_all_ue_ctx(struct ranap_iu_rnc *rnc)
{
struct ranap_ue_conn_ctx *ue_ctx, *ue_ctx_tmp;
llist_for_each_entry_safe(ue_ctx, ue_ctx_tmp, &sgsn->sccp.scu_iups->ue_conn_ctx_list, list) {
if (ue_ctx->rnc != rnc)
continue;
ue_conn_ctx_link_invalidated_free(ue_ctx);
}
}
/* Send a paging command down a given SCCP User. tmsi_or_ptmsi and paging_cause are
* optional and may be passed NULL and 0, respectively, to disable their use.
* See enum RANAP_PagingCause.
*
* If tmsi_or_ptmsi is given, the imsi is not sent over the air interface.
* Nevertheless, the IMSI is still required for resolution in the HNB-GW
* and/or(?) RNC.
*
* returns negative if paging couldn't be sent (eg. because RNC is currently
* unreachable in lower layers).
**/
int iu_rnc_tx_paging_cmd(struct ranap_iu_rnc *rnc,
const char *imsi,
const uint32_t *tmsi_or_ptmsi,
bool is_ps,
uint32_t paging_cause)
{
struct msgb *ranap_msg;
int rc;
/* rnc is not ready for paging (link not ready). */
if (rnc->fi->state != IU_RNC_ST_READY)
return -ENOLINK;
LOG_RNC(rnc, LOGL_DEBUG, "Paging %s for %s=%08x IMSI=%s\n",
is_ps ? "PS" : "CS",
is_ps ? "P-TMSI" : "TMSI",
tmsi_or_ptmsi ? *tmsi_or_ptmsi : GSM_RESERVED_TMSI,
imsi);
ranap_msg = ranap_new_msg_paging_cmd(imsi, tmsi_or_ptmsi, is_ps ? 1 : 0, paging_cause);
if (!ranap_msg)
return -EINVAL;
rc = osmo_fsm_inst_dispatch(rnc->fi, IU_RNC_EV_MSG_DOWN_CL, ranap_msg);
if (rc != 0)
msgb_free(ranap_msg);
return rc;
}

374
src/sgsn/iu_rnc_fsm.c Normal file
View File

@@ -0,0 +1,374 @@
/* A remote RNC (Radio Network Controller) FSM */
/* (C) 2025 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "config.h"
#include <stdint.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <osmocom/core/logging.h>
#include <osmocom/core/fsm.h>
#include <osmocom/core/tdef.h>
#include <osmocom/sigtran/sccp_helpers.h>
#include <osmocom/sgsn/debug.h>
#include <osmocom/sgsn/gprs_ranap.h>
#include <osmocom/sgsn/iu_rnc_fsm.h>
#include <osmocom/sgsn/iu_rnc.h>
#include <osmocom/sgsn/sgsn.h>
#define S(x) (1 << (x))
struct osmo_fsm iu_rnc_fsm;
static const struct osmo_tdef_state_timeout iu_rnc_fsm_timeouts[32] = {
[IU_RNC_ST_WAIT_RX_RESET_ACK] = { .T = -1002 },
[IU_RNC_ST_DISCARDING] = { .T = -1002 },
};
#define iu_rnc_state_chg(iu_rnc, next_st) \
osmo_tdef_fsm_inst_state_chg((iu_rnc)->fi, next_st, iu_rnc_fsm_timeouts, sgsn_T_defs, 5)
static const struct value_string iu_rnc_fsm_event_names[] = {
OSMO_VALUE_STRING(IU_RNC_EV_MSG_UP_CO_INITIAL),
OSMO_VALUE_STRING(IU_RNC_EV_MSG_UP_CO),
OSMO_VALUE_STRING(IU_RNC_EV_RX_RESET),
OSMO_VALUE_STRING(IU_RNC_EV_RX_RESET_ACK),
OSMO_VALUE_STRING(IU_RNC_EV_MSG_DOWN_CL),
OSMO_VALUE_STRING(IU_RNC_EV_AVAILABLE),
OSMO_VALUE_STRING(IU_RNC_EV_UNAVAILABLE),
{}
};
/* Drop all SCCP connections for this iu_rnc, respond with RESET ACKNOWLEDGE and move to READY state. */
static void iu_rnc_rx_reset(struct ranap_iu_rnc *rnc)
{
struct msgb *reset_ack;
struct iu_grnc_id grnc_id;
sgsn_ranap_iu_grnc_id_compose(&grnc_id, &rnc->rnc_id);
iu_rnc_discard_all_ue_ctx(rnc);
reset_ack = ranap_new_msg_reset_ack(RANAP_CN_DomainIndicator_ps_domain, &grnc_id.grnc_id);
if (!reset_ack) {
LOG_RNC(rnc, LOGL_ERROR, "Failed to compose RESET ACKNOWLEDGE message\n");
iu_rnc_state_chg(rnc, IU_RNC_ST_WAIT_RX_RESET);
return;
}
if (sgsn_ranap_iu_tx_cl(rnc->scu_iups, &rnc->sccp_addr, reset_ack) < 0) {
LOG_RNC(rnc, LOGL_ERROR, "Failed to send RESET ACKNOWLEDGE message\n");
iu_rnc_state_chg(rnc, IU_RNC_ST_WAIT_RX_RESET);
return;
}
LOG_RNC(rnc, LOGL_INFO, "Sent RESET ACKNOWLEDGE\n");
iu_rnc_state_chg(rnc, IU_RNC_ST_READY);
}
static void iu_rnc_reset(struct ranap_iu_rnc *rnc)
{
struct msgb *reset;
const RANAP_Cause_t cause = {
.present = RANAP_Cause_PR_protocol,
.choice = {
.protocol = RANAP_CauseProtocol_message_not_compatible_with_receiver_state,
},
};
iu_rnc_state_chg(rnc, IU_RNC_ST_WAIT_RX_RESET_ACK);
iu_rnc_discard_all_ue_ctx(rnc);
reset = ranap_new_msg_reset(RANAP_CN_DomainIndicator_ps_domain, &cause);
if (!reset) {
LOG_RNC(rnc, LOGL_ERROR, "Failed to compose RESET message\n");
iu_rnc_state_chg(rnc, IU_RNC_ST_WAIT_RX_RESET);
return;
}
if (sgsn_ranap_iu_tx_cl(rnc->scu_iups, &rnc->sccp_addr, reset) < 0) {
LOG_RNC(rnc, LOGL_ERROR, "Failed to send RESET message\n");
iu_rnc_state_chg(rnc, IU_RNC_ST_WAIT_RX_RESET);
return;
}
}
static void iu_rnc_st_wait_rx_reset(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct ranap_iu_rnc *rnc = fi->priv;
switch (event) {
case IU_RNC_EV_MSG_UP_CO:
case IU_RNC_EV_MSG_UP_CO_INITIAL:
OSMO_ASSERT(data);
#define LEGACY_BEHAVIOR
#ifdef LEGACY_BEHAVIOR
LOG_RNC(rnc, LOGL_ERROR, "Receiving CO message on RAN peer that has not done a proper RESET yet."
" Accepting RAN peer implicitly (legacy compat)\n");
iu_rnc_state_chg(rnc, IU_RNC_ST_READY);
osmo_fsm_inst_dispatch(rnc->fi, event, data);
return;
#else
LOG_RNC(rnc, LOGL_ERROR, "Receiving CO message on RAN peer that has not done a proper RESET yet."
" Disconnecting on incoming message, sending RESET to RAN peer.\n");
/* No valid RESET procedure has happened here yet. Usually, we're expecting the RAN peer (BSC,
* RNC) to first send a RESET message before sending Connection Oriented messages. So if we're
* getting a CO message, likely we've just restarted or something. Send a RESET to the peer. */
/* Make sure the MS / UE properly disconnects. */
clear_and_disconnect(rnc, ctx->conn_id);
iu_rnc_reset(rnc);
return;
#endif
case IU_RNC_EV_RX_RESET:
iu_rnc_rx_reset(rnc);
return;
case IU_RNC_EV_AVAILABLE:
/* Send a RESET to the peer. */
iu_rnc_reset(rnc);
return;
case IU_RNC_EV_UNAVAILABLE:
/* Do nothing, wait for peer to come up again. */
return;
default:
LOG_RNC(rnc, LOGL_ERROR, "Unhandled event: %s\n", osmo_fsm_event_name(&iu_rnc_fsm, event));
return;
}
}
static void iu_rnc_st_wait_rx_reset_ack(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct ranap_iu_rnc *rnc = fi->priv;
struct iu_rnc_ev_msg_up_co_initial_ctx *ev_msg_up_co_initial_ctx;
struct iu_rnc_ev_msg_up_co_ctx *ev_msg_up_co_ctx;
switch (event) {
case IU_RNC_EV_RX_RESET_ACK:
iu_rnc_state_chg(rnc, IU_RNC_ST_READY);
return;
case IU_RNC_EV_MSG_UP_CO_INITIAL:
ev_msg_up_co_initial_ctx = data;
OSMO_ASSERT(ev_msg_up_co_initial_ctx);
LOG_RNC(rnc, LOGL_ERROR, "Receiving CO Initial message on RAN peer that has not done a proper RESET yet."
" Disconnecting on incoming message, sending RESET to RAN peer.\n");
osmo_sccp_tx_disconn(ev_msg_up_co_initial_ctx->rnc->scu_iups->scu,
ev_msg_up_co_initial_ctx->conn_id, NULL, 0);
/* No valid RESET procedure has happened here yet. */
iu_rnc_reset(rnc);
return;
return;
case IU_RNC_EV_MSG_UP_CO:
ev_msg_up_co_ctx = data;
OSMO_ASSERT(ev_msg_up_co_ctx);
LOG_RNC(rnc, LOGL_ERROR, "Receiving CO message on RAN peer that has not done a proper RESET yet."
" Disconnecting on incoming message, sending RESET to RAN peer.\n");
ue_conn_ctx_link_invalidated_free(ev_msg_up_co_ctx->ue_ctx);
/* No valid RESET procedure has happened here yet. */
iu_rnc_reset(rnc);
return;
case IU_RNC_EV_RX_RESET:
iu_rnc_rx_reset(rnc);
return;
case IU_RNC_EV_AVAILABLE:
/* Send a RESET to the peer. */
iu_rnc_reset(rnc);
return;
case IU_RNC_EV_UNAVAILABLE:
iu_rnc_state_chg(rnc, IU_RNC_ST_WAIT_RX_RESET);
return;
default:
LOG_RNC(rnc, LOGL_ERROR, "Unhandled event: %s\n", osmo_fsm_event_name(&iu_rnc_fsm, event));
return;
}
}
static void iu_rnc_st_ready_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
{
if (prev_state != IU_RNC_ST_READY)
sgsn_stat_inc(SGSN_STAT_IU_PEERS_ACTIVE, 1);
}
static void iu_rnc_st_ready(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct ranap_iu_rnc *rnc = fi->priv;
struct iu_rnc_ev_msg_up_co_initial_ctx *ev_msg_up_co_initial_ctx;
struct iu_rnc_ev_msg_up_co_ctx *ev_msg_up_co_ctx;
switch (event) {
case IU_RNC_EV_MSG_UP_CO_INITIAL:
ev_msg_up_co_initial_ctx = data;
OSMO_ASSERT(ev_msg_up_co_initial_ctx);
OSMO_ASSERT(ev_msg_up_co_initial_ctx->rnc);
sgsn_ranap_iu_handle_co_initial(ev_msg_up_co_initial_ctx->rnc,
ev_msg_up_co_initial_ctx->conn_id,
&ev_msg_up_co_initial_ctx->message);
return;
case IU_RNC_EV_MSG_UP_CO:
ev_msg_up_co_ctx = data;
OSMO_ASSERT(ev_msg_up_co_ctx);
OSMO_ASSERT(ev_msg_up_co_ctx->ue_ctx);
sgsn_ranap_iu_handle_co(ev_msg_up_co_ctx->ue_ctx, &ev_msg_up_co_ctx->message);
return;
case IU_RNC_EV_RX_RESET:
iu_rnc_rx_reset(rnc);
return;
case IU_RNC_EV_MSG_DOWN_CL:
OSMO_ASSERT(data);
sgsn_ranap_iu_tx_cl(rnc->scu_iups, &rnc->sccp_addr, (struct msgb *)data);
return;
case IU_RNC_EV_AVAILABLE:
/* Do nothing, we were already up. */
return;
case IU_RNC_EV_UNAVAILABLE:
iu_rnc_discard_all_ue_ctx(rnc);
iu_rnc_state_chg(rnc, IU_RNC_ST_WAIT_RX_RESET);
return;
default:
LOG_RNC(rnc, LOGL_ERROR, "Unhandled event: %s\n", osmo_fsm_event_name(&iu_rnc_fsm, event));
return;
}
}
static void iu_rnc_st_ready_onleave(struct osmo_fsm_inst *fi, uint32_t next_state)
{
if (next_state != IU_RNC_ST_READY)
sgsn_stat_dec(SGSN_STAT_IU_PEERS_ACTIVE, 1);
}
static int iu_rnc_fsm_timer_cb(struct osmo_fsm_inst *fi)
{
struct ranap_iu_rnc *rnc = fi->priv;
iu_rnc_state_chg(rnc, IU_RNC_ST_WAIT_RX_RESET);
return 0;
}
static void iu_rnc_fsm_cleanup(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause)
{
struct ranap_iu_rnc *rnc = fi->priv;
iu_rnc_discard_all_ue_ctx(rnc);
if (rnc->fi->state == IU_RNC_ST_READY)
sgsn_stat_dec(SGSN_STAT_IU_PEERS_ACTIVE, 1);
sgsn_stat_dec(SGSN_STAT_IU_PEERS_TOTAL, 1);
}
static const struct osmo_fsm_state iu_rnc_fsm_states[] = {
[IU_RNC_ST_WAIT_RX_RESET] = {
.name = "WAIT_RX_RESET",
.action = iu_rnc_st_wait_rx_reset,
.in_event_mask = 0
| S(IU_RNC_EV_RX_RESET)
| S(IU_RNC_EV_MSG_UP_CO_INITIAL)
| S(IU_RNC_EV_MSG_UP_CO)
| S(IU_RNC_EV_AVAILABLE)
| S(IU_RNC_EV_UNAVAILABLE)
,
.out_state_mask = 0
| S(IU_RNC_ST_WAIT_RX_RESET)
| S(IU_RNC_ST_WAIT_RX_RESET_ACK)
| S(IU_RNC_ST_READY)
| S(IU_RNC_ST_DISCARDING)
,
},
[IU_RNC_ST_WAIT_RX_RESET_ACK] = {
.name = "WAIT_RX_RESET_ACK",
.action = iu_rnc_st_wait_rx_reset_ack,
.in_event_mask = 0
| S(IU_RNC_EV_RX_RESET)
| S(IU_RNC_EV_RX_RESET_ACK)
| S(IU_RNC_EV_MSG_UP_CO_INITIAL)
| S(IU_RNC_EV_MSG_UP_CO)
| S(IU_RNC_EV_AVAILABLE)
| S(IU_RNC_EV_UNAVAILABLE)
,
.out_state_mask = 0
| S(IU_RNC_ST_WAIT_RX_RESET)
| S(IU_RNC_ST_WAIT_RX_RESET_ACK)
| S(IU_RNC_ST_READY)
| S(IU_RNC_ST_DISCARDING)
,
},
[IU_RNC_ST_READY] = {
.name = "READY",
.action = iu_rnc_st_ready,
.onenter = iu_rnc_st_ready_onenter,
.onleave = iu_rnc_st_ready_onleave,
.in_event_mask = 0
| S(IU_RNC_EV_RX_RESET)
| S(IU_RNC_EV_MSG_UP_CO_INITIAL)
| S(IU_RNC_EV_MSG_UP_CO)
| S(IU_RNC_EV_MSG_DOWN_CL)
| S(IU_RNC_EV_AVAILABLE)
| S(IU_RNC_EV_UNAVAILABLE)
,
.out_state_mask = 0
| S(IU_RNC_ST_WAIT_RX_RESET)
| S(IU_RNC_ST_WAIT_RX_RESET_ACK)
| S(IU_RNC_ST_READY)
| S(IU_RNC_ST_DISCARDING)
,
},
[IU_RNC_ST_DISCARDING] = {
.name = "DISCARDING",
},
};
struct osmo_fsm iu_rnc_fsm = {
.name = "iu_rnc",
.states = iu_rnc_fsm_states,
.num_states = ARRAY_SIZE(iu_rnc_fsm_states),
.log_subsys = DRANAP,
.event_names = iu_rnc_fsm_event_names,
.timer_cb = iu_rnc_fsm_timer_cb,
.cleanup = iu_rnc_fsm_cleanup,
};
static __attribute__((constructor)) void iu_rnc_init(void)
{
OSMO_ASSERT(osmo_fsm_register(&iu_rnc_fsm) == 0);
}

View File

@@ -19,6 +19,8 @@
*
*/
#include "config.h"
#include <stdint.h>
#include <osmocom/core/linuxlist.h>
@@ -36,6 +38,8 @@
#include <osmocom/gsm/gsm_utils.h>
#include <osmocom/gsm/gsup.h>
#include <osmocom/gtp/pdp.h>
#include <osmocom/sgsn/gprs_subscriber.h>
#include <osmocom/sgsn/debug.h>
#include <osmocom/sgsn/mmctx.h>
@@ -49,17 +53,14 @@
#include <osmocom/sgsn/gprs_mm_state_iu_fsm.h>
#include <osmocom/sgsn/gprs_gmm_fsm.h>
#include <osmocom/sgsn/gprs_llc.h>
#include <osmocom/sgsn/gprs_ranap.h>
#include <osmocom/sgsn/gprs_sndcp.h>
#include <osmocom/sgsn/gtp_ggsn.h>
#include <osmocom/sgsn/gtp.h>
#include <osmocom/sgsn/pdpctx.h>
#include <pdp.h>
#include <time.h>
#include "../../config.h"
const struct value_string sgsn_ran_type_names[] = {
{ MM_CTX_T_GERAN_Gb, "GPRS/EDGE via Gb" },
{ MM_CTX_T_UTRAN_Iu, "UMTS via Iu" },
@@ -105,15 +106,30 @@ struct sgsn_mm_ctx *sgsn_mm_ctx_by_ue_ctx(const void *uectx)
return NULL;
}
/* look-up an SGSN MM context based on Gb LLME context (struct gprs_llc_llme)*/
struct sgsn_mm_ctx *sgsn_mm_ctx_by_llme(const struct gprs_llc_llme *llme)
{
struct sgsn_mm_ctx *ctx;
llist_for_each_entry (ctx, &sgsn->mm_list, list) {
if (ctx->ran_type == MM_CTX_T_GERAN_Gb
&& llme == ctx->gb.llme)
return ctx;
}
return NULL;
}
/* look-up a SGSN MM context based on TLLI + RAI */
struct sgsn_mm_ctx *sgsn_mm_ctx_by_tlli(uint32_t tlli,
const struct gprs_ra_id *raid)
const struct osmo_routing_area_id *raid)
{
struct sgsn_mm_ctx *ctx;
llist_for_each_entry(ctx, &sgsn->mm_list, list) {
if ((tlli == ctx->gb.tlli || tlli == ctx->gb.tlli_new) &&
gprs_ra_id_equals(raid, &ctx->ra))
!osmo_rai_cmp(raid, &ctx->ra))
return ctx;
}
@@ -121,7 +137,7 @@ struct sgsn_mm_ctx *sgsn_mm_ctx_by_tlli(uint32_t tlli,
}
struct sgsn_mm_ctx *sgsn_mm_ctx_by_tlli_and_ptmsi(uint32_t tlli,
const struct gprs_ra_id *raid)
const struct osmo_routing_area_id *raid)
{
struct sgsn_mm_ctx *ctx;
int tlli_type;
@@ -138,7 +154,7 @@ struct sgsn_mm_ctx *sgsn_mm_ctx_by_tlli_and_ptmsi(uint32_t tlli,
llist_for_each_entry(ctx, &sgsn->mm_list, list) {
if ((gprs_tmsi2tlli(ctx->p_tmsi, tlli_type) == tlli ||
gprs_tmsi2tlli(ctx->p_tmsi_old, tlli_type) == tlli) &&
gprs_ra_id_equals(raid, &ctx->ra))
!osmo_rai_cmp(raid, &ctx->ra))
return ctx;
}
@@ -224,7 +240,7 @@ out:
}
/* Allocate a new SGSN MM context for GERAN_Gb */
struct sgsn_mm_ctx *sgsn_mm_ctx_alloc_gb(uint32_t tlli,
const struct gprs_ra_id *raid)
const struct osmo_routing_area_id *raid)
{
struct sgsn_mm_ctx *ctx;
@@ -252,7 +268,7 @@ struct sgsn_mm_ctx *sgsn_mm_ctx_alloc_iu(void *uectx)
return NULL;
/* Need to get RAID from IU conn */
ctx->ra = ue_ctx->ra_id;
gprs_rai_to_osmo(&ctx->ra, &ue_ctx->ra_id);
ctx->ran_type = MM_CTX_T_UTRAN_Iu;
ctx->iu.ue_ctx = ue_ctx;
ctx->iu.ue_ctx->rab_assign_addr_enc = sgsn->cfg.iu.rab_assign_addr_enc;
@@ -582,3 +598,57 @@ struct sgsn_ggsn_ctx *sgsn_mm_ctx_find_ggsn_ctx(struct sgsn_mm_ctx *mmctx,
return ggsn;
}
/* determine if the MS/UE supports R99 or later */
bool sgsn_mm_ctx_is_r99(const struct sgsn_mm_ctx *mm)
{
if (mm->ms_network_capa.len < 1)
return false;
if (mm->ms_network_capa.buf[0] & 0x01)
return true;
return false;
}
#if BUILD_IU
/* Send RAB activation requests for all PDP contexts */
void sgsn_mm_ctx_iu_activate_rabs(struct sgsn_mm_ctx *ctx)
{
struct sgsn_pdp_ctx *pdp;
OSMO_ASSERT(ctx->ran_type == MM_CTX_T_UTRAN_Iu);
llist_for_each_entry(pdp, &ctx->pdp_list, list)
sgsn_pdp_ctx_iu_rab_activate(pdp, pdp->nsapi);
}
/* send a Iu Release Command and free afterwards the UE context */
void sgsn_mm_ctx_iu_ranap_release_free(struct sgsn_mm_ctx *mmctx,
const struct RANAP_Cause *cause)
{
unsigned long X1001;
if (!mmctx)
return;
if (!mmctx->iu.ue_ctx)
return;
X1001 = osmo_tdef_get(sgsn->cfg.T_defs, -1001, OSMO_TDEF_S, -1);
sgsn_ranap_iu_tx_release_free(mmctx->iu.ue_ctx, cause, (int) X1001);
mmctx->iu.ue_ctx = NULL;
}
/* free the Iu UE context */
void sgsn_mm_ctx_iu_ranap_free(struct sgsn_mm_ctx *mmctx)
{
if (!mmctx)
return;
if (!mmctx->iu.ue_ctx)
return;
sgsn_ranap_iu_free_ue(mmctx->iu.ue_ctx);
mmctx->iu.ue_ctx = NULL;
}
#endif

View File

@@ -19,6 +19,8 @@
*
*/
#include "config.h"
#include <stdint.h>
#include <osmocom/core/linuxlist.h>
@@ -36,6 +38,7 @@
#include <osmocom/sgsn/gprs_llc_xid.h>
#include <osmocom/sgsn/gprs_sndcp.h>
#include <osmocom/sgsn/gprs_llc.h>
#include <osmocom/sgsn/gprs_ranap.h>
#include <osmocom/sgsn/gprs_sm.h>
#include <osmocom/sgsn/gtp.h>
@@ -156,3 +159,36 @@ void sgsn_pdp_ctx_free(struct sgsn_pdp_ctx *pdp)
talloc_free(pdp);
}
#ifdef BUILD_IU
int sgsn_pdp_ctx_iu_rab_activate(struct sgsn_pdp_ctx *pdp, uint8_t rab_id)
{
struct sgsn_mm_ctx *mm = pdp->mm;
struct ranap_ue_conn_ctx *ue_ctx;
uint32_t ggsn_ip;
OSMO_ASSERT(mm->ran_type == MM_CTX_T_UTRAN_Iu);
ue_ctx = mm->iu.ue_ctx;
/* Get the IP address for ggsn user plane */
memcpy(&ggsn_ip, pdp->lib->gsnru.v, pdp->lib->gsnru.l);
ggsn_ip = htonl(ggsn_ip);
LOGPDPCTXP(LOGL_INFO, pdp, "Activate RAB: rab_id=%u, ggsn_ip=%x, teid_gn=%x\n",
rab_id, ggsn_ip, pdp->lib->teid_gn);
return sgsn_ranap_iu_tx_rab_ps_ass_req(ue_ctx, rab_id, ggsn_ip, pdp->lib->teid_gn);
}
int sgsn_pdp_ctx_iu_rab_deactivate(struct sgsn_pdp_ctx *pdp, uint8_t rab_id)
{
struct sgsn_mm_ctx *mm = pdp->mm;
OSMO_ASSERT(mm->ran_type == MM_CTX_T_UTRAN_Iu);
LOGPDPCTXP(LOGL_NOTICE, pdp, "Release RAB: rab_id=%u not supported!\n", rab_id);
//struct ranap_ue_conn_ctx *ue_ctx = mm->iu.ue_ctx;
// TODO: add new function similar to sgsn_ranap_iu_tx_rab_ps_ass_req() but requesting relese of RAB.
return -ENOTSUP;
}
#endif /* ifdef BUILD_IU */

424
src/sgsn/sccp.c Normal file
View File

@@ -0,0 +1,424 @@
/* SCCP Handling */
/* (C) 2025 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "config.h"
#include <stdint.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <osmocom/sigtran/sccp_sap.h>
#include <osmocom/sigtran/sccp_helpers.h>
#include <osmocom/sccp/sccp_types.h>
#include <osmocom/sgsn/debug.h>
#include <osmocom/sgsn/iu_client.h>
#include <osmocom/sgsn/iu_rnc.h>
#include <osmocom/sgsn/iu_rnc_fsm.h>
#include <osmocom/sgsn/gprs_ranap.h>
#include <osmocom/sgsn/sccp.h>
#include <osmocom/sgsn/sgsn.h>
/* Entry to cache conn_id <-> sccp_addr mapping in case we receive an empty CR */
struct iu_new_ctx_entry {
struct llist_head list;
uint32_t conn_id;
struct osmo_sccp_addr sccp_addr;
};
static int sccp_sap_up(struct osmo_prim_hdr *oph, void *_scu);
struct sgsn_sccp_user_iups *sgsn_scu_iups_inst_alloc(struct sgsn_instance *sgsn, struct osmo_sccp_instance *sccp)
{
struct sgsn_sccp_user_iups *scu_iups;
scu_iups = talloc_zero(sgsn, struct sgsn_sccp_user_iups);
OSMO_ASSERT(scu_iups);
scu_iups->sgsn = sgsn;
scu_iups->sccp = sccp;
INIT_LLIST_HEAD(&scu_iups->ue_conn_ctx_list);
INIT_LLIST_HEAD(&scu_iups->ue_conn_sccp_addr_list);
osmo_sccp_local_addr_by_instance(&scu_iups->local_sccp_addr, scu_iups->sccp, OSMO_SCCP_SSN_RANAP);
scu_iups->scu = osmo_sccp_user_bind(scu_iups->sccp, "OsmoSGSN-IuPS", sccp_sap_up, OSMO_SCCP_SSN_RANAP);
osmo_sccp_user_set_priv(scu_iups->scu, scu_iups);
return scu_iups;
}
void sgsn_scu_iups_free(struct sgsn_sccp_user_iups *scu_iups)
{
if (!scu_iups)
return;
if (scu_iups->scu)
osmo_sccp_user_unbind(scu_iups->scu);
talloc_free(scu_iups);
}
/* wrap RANAP message in SCCP N-DATA.req
* ranap_msg becomes owned by the callee. */
int sgsn_scu_iups_tx_data_req(struct sgsn_sccp_user_iups *scu_iups, uint32_t conn_id, struct msgb *ranap_msg)
{
struct osmo_scu_prim *prim;
int rc;
if (!scu_iups) {
LOGP(DSUA, LOGL_ERROR, "Failed to send SCCP N-DATA.req(%u): no SCCP User\n", conn_id);
return -1;
}
ranap_msg->l2h = ranap_msg->data;
prim = (struct osmo_scu_prim *)msgb_push(ranap_msg, sizeof(*prim));
osmo_prim_init(&prim->oph, SCCP_SAP_USER, OSMO_SCU_PRIM_N_DATA, PRIM_OP_REQUEST, ranap_msg);
prim->u.data.conn_id = conn_id;
rc = osmo_sccp_user_sap_down(scu_iups->scu, &prim->oph);
if (rc)
LOGP(DSUA, LOGL_ERROR, "Failed to send SCCP N-DATA.req(%u)\n", conn_id);
return rc;
}
struct ranap_ue_conn_ctx *sgsn_scu_iups_ue_conn_ctx_find(struct sgsn_sccp_user_iups *scu_iups, uint32_t conn_id)
{
struct ranap_ue_conn_ctx *ctx;
llist_for_each_entry(ctx, &scu_iups->ue_conn_ctx_list, list) {
if (ctx->conn_id == conn_id)
return ctx;
}
return NULL;
}
static void ue_conn_sccp_addr_add(struct sgsn_sccp_user_iups *scu_iups, uint32_t conn_id, const struct osmo_sccp_addr *calling_addr)
{
struct iu_new_ctx_entry *entry = talloc_zero(scu_iups, struct iu_new_ctx_entry);
entry->conn_id = conn_id;
entry->sccp_addr = *calling_addr;
llist_add(&entry->list, &scu_iups->ue_conn_sccp_addr_list);
}
static const struct osmo_sccp_addr *ue_conn_sccp_addr_find(struct sgsn_sccp_user_iups *scu_iups, uint32_t conn_id)
{
struct iu_new_ctx_entry *entry;
llist_for_each_entry(entry, &scu_iups->ue_conn_sccp_addr_list, list) {
if (entry->conn_id == conn_id)
return &entry->sccp_addr;
}
return NULL;
}
static void ue_conn_sccp_addr_del(struct sgsn_sccp_user_iups *scu_iups, uint32_t conn_id)
{
struct iu_new_ctx_entry *entry;
llist_for_each_entry(entry, &scu_iups->ue_conn_sccp_addr_list, list) {
if (entry->conn_id == conn_id) {
llist_del(&entry->list);
talloc_free(entry);
return;
}
}
}
static void handle_notice_ind(struct sgsn_sccp_user_iups *scu_iups, const struct osmo_scu_notice_param *ni)
{
struct ranap_iu_rnc *rnc;
rnc = iu_rnc_find_by_addr(&ni->calling_addr);
if (!rnc) {
LOGP(DSUA, LOGL_DEBUG,
"(calling_addr=%s) N-NOTICE.ind cause=%u='%s' importance=%u didn't match any RNC, ignoring\n",
osmo_sccp_addr_dump(&ni->calling_addr),
ni->cause, osmo_sccp_return_cause_name(ni->cause),
ni->importance);
return;
}
LOG_RNC(rnc, LOGL_NOTICE,
"N-NOTICE.ind cause=%u='%s' importance=%u\n",
ni->cause, osmo_sccp_return_cause_name(ni->cause),
ni->importance);
switch (ni->cause) {
case SCCP_RETURN_CAUSE_SUBSYSTEM_CONGESTION:
case SCCP_RETURN_CAUSE_NETWORK_CONGESTION:
/* Transient failures (hopefully), keep going. */
return;
default:
break;
}
/* Messages are not arriving to rnc. Signal it is unavailable to update local state. */
osmo_fsm_inst_dispatch(rnc->fi, IU_RNC_EV_UNAVAILABLE, NULL);
}
static void handle_pcstate_ind(struct sgsn_sccp_user_iups *scu_iups, const struct osmo_scu_pcstate_param *pcst)
{
struct osmo_ss7_instance *cs7 = osmo_sccp_get_ss7(scu_iups->sccp);
struct osmo_sccp_addr rem_addr;
struct ranap_iu_rnc *rnc;
bool connected;
bool disconnected;
LOGP(DSUA, LOGL_DEBUG, "N-PCSTATE ind: affected_pc=%u=%s sp_status=%s remote_sccp_status=%s\n",
pcst->affected_pc, osmo_ss7_pointcode_print(cs7, pcst->affected_pc),
osmo_sccp_sp_status_name(pcst->sp_status),
osmo_sccp_rem_sccp_status_name(pcst->remote_sccp_status));
osmo_sccp_make_addr_pc_ssn(&rem_addr, pcst->affected_pc, OSMO_SCCP_SSN_RANAP);
rnc = iu_rnc_find_by_addr(&rem_addr);
if (!rnc) {
LOGP(DSUA, LOGL_DEBUG, "No RNC found under pc=%u=s%s\n",
pcst->affected_pc, osmo_ss7_pointcode_print(cs7, pcst->affected_pc));
return;
}
/* See if this marks the point code to have become available, or to have been lost.
*
* I want to detect two events:
* - connection event (both indicators say PC is reachable).
* - disconnection event (at least one indicator says the PC is not reachable).
*
* There are two separate incoming indicators with various possible values -- the incoming events can be:
*
* - neither connection nor disconnection indicated -- just indicating congestion
* connected == false, disconnected == false --> do nothing.
* - both incoming values indicate that we are connected
* --> trigger connected
* - both indicate we are disconnected
* --> trigger disconnected
* - one value indicates 'connected', the other indicates 'disconnected'
* --> trigger disconnected
*
* Congestion could imply that we're connected, but it does not indicate
* that a PC's reachability changed, so no need to trigger on that.
*/
connected = false;
disconnected = false;
switch (pcst->sp_status) {
case OSMO_SCCP_SP_S_ACCESSIBLE:
connected = true;
break;
case OSMO_SCCP_SP_S_INACCESSIBLE:
disconnected = true;
break;
default:
case OSMO_SCCP_SP_S_CONGESTED:
/* Neither connecting nor disconnecting */
break;
}
switch (pcst->remote_sccp_status) {
case OSMO_SCCP_REM_SCCP_S_AVAILABLE:
if (!disconnected)
connected = true;
break;
case OSMO_SCCP_REM_SCCP_S_UNAVAILABLE_UNKNOWN:
case OSMO_SCCP_REM_SCCP_S_UNEQUIPPED:
case OSMO_SCCP_REM_SCCP_S_INACCESSIBLE:
disconnected = true;
connected = false;
break;
default:
case OSMO_SCCP_REM_SCCP_S_CONGESTED:
/* Neither connecting nor disconnecting */
break;
}
if (disconnected) {
LOG_RNC(rnc, LOGL_NOTICE,
"now unreachable: N-PCSTATE ind: pc=%u=%s sp_status=%s remote_sccp_status=%s\n",
pcst->affected_pc, osmo_ss7_pointcode_print(cs7, pcst->affected_pc),
osmo_sccp_sp_status_name(pcst->sp_status),
osmo_sccp_rem_sccp_status_name(pcst->remote_sccp_status));
osmo_fsm_inst_dispatch(rnc->fi, IU_RNC_EV_UNAVAILABLE, NULL);
} else if (connected) {
LOG_RNC(rnc, LOGL_NOTICE,
"now available: N-PCSTATE ind: pc=%u=%s sp_status=%s remote_sccp_status=%s\n",
pcst->affected_pc, osmo_ss7_pointcode_print(cs7, pcst->affected_pc),
osmo_sccp_sp_status_name(pcst->sp_status),
osmo_sccp_rem_sccp_status_name(pcst->remote_sccp_status));
osmo_fsm_inst_dispatch(rnc->fi, IU_RNC_EV_AVAILABLE, NULL);
}
}
static struct osmo_prim_hdr *make_conn_resp(struct osmo_scu_connect_param *param)
{
struct msgb *msg = msgb_alloc(1024, "conn_resp");
struct osmo_scu_prim *prim;
prim = (struct osmo_scu_prim *) msgb_put(msg, sizeof(*prim));
osmo_prim_init(&prim->oph, SCCP_SAP_USER,
OSMO_SCU_PRIM_N_CONNECT,
PRIM_OP_RESPONSE, msg);
memcpy(&prim->u.connect, param, sizeof(prim->u.connect));
return &prim->oph;
}
static int sccp_sap_up(struct osmo_prim_hdr *oph, void *_scu)
{
struct osmo_sccp_user *scu = _scu;
struct osmo_scu_prim *prim = (struct osmo_scu_prim *) oph;
struct sgsn_sccp_user_iups *scu_iups = osmo_sccp_user_get_priv(scu);
struct osmo_prim_hdr *resp = NULL;
int rc = -1;
struct ranap_ue_conn_ctx *ue;
uint32_t conn_id;
LOGP(DSUA, LOGL_DEBUG, "sccp_sap_up(%s)\n", osmo_scu_prim_name(oph));
switch (OSMO_PRIM_HDR(oph)) {
case OSMO_PRIM(OSMO_SCU_PRIM_N_CONNECT, PRIM_OP_CONFIRM):
/* confirmation of outbound connection */
break;
case OSMO_PRIM(OSMO_SCU_PRIM_N_CONNECT, PRIM_OP_INDICATION):
/* indication of new inbound connection request*/
conn_id = prim->u.connect.conn_id;
LOGP(DSUA, LOGL_DEBUG, "N-CONNECT.ind(X->%u)\n", conn_id);
/* first ensure the local SCCP socket is ACTIVE */
resp = make_conn_resp(&prim->u.connect);
osmo_sccp_user_sap_down(scu, resp);
/* then handle the RANAP payload */
if (/* prim->u.connect.called_addr.ssn != OSMO_SCCP_SSN_RANAP || */
!msgb_l2(oph->msg) || msgb_l2len(oph->msg) == 0) {
LOGP(DSUA, LOGL_DEBUG,
"Received N-CONNECT.ind without data\n");
ue_conn_sccp_addr_add(scu_iups, conn_id, &prim->u.connect.calling_addr);
} else {
rc = sgsn_ranap_iu_rx_co_initial_msg(scu_iups, &prim->u.connect.calling_addr,
conn_id,
msgb_l2(oph->msg), msgb_l2len(oph->msg));
}
break;
case OSMO_PRIM(OSMO_SCU_PRIM_N_DISCONNECT, PRIM_OP_INDICATION):
/* indication of disconnect */
conn_id = prim->u.disconnect.conn_id;
LOGP(DSUA, LOGL_DEBUG, "N-DISCONNECT.ind(%u)\n", conn_id);
ue_conn_sccp_addr_del(scu_iups, conn_id);
ue = sgsn_scu_iups_ue_conn_ctx_find(scu_iups, conn_id);
if (!ue)
break;
rc = 0;
if (msgb_l2len(oph->msg) > 0)
rc = sgsn_ranap_iu_rx_co_msg(ue, msgb_l2(oph->msg), msgb_l2len(oph->msg));
/* A Iu Release event might be used to free the UE in cn_ranap_handle_co(). */
ue = sgsn_scu_iups_ue_conn_ctx_find(scu_iups, conn_id);
if (!ue)
break;
ue_conn_ctx_link_invalidated_free(ue);
break;
case OSMO_PRIM(OSMO_SCU_PRIM_N_DATA, PRIM_OP_INDICATION):
/* connection-oriented data received */
conn_id = prim->u.data.conn_id;
LOGP(DSUA, LOGL_DEBUG, "N-DATA.ind(%u, %s)\n", conn_id,
osmo_hexdump(msgb_l2(oph->msg), msgb_l2len(oph->msg)));
/* resolve UE context */
ue = sgsn_scu_iups_ue_conn_ctx_find(scu_iups, conn_id);
if (!ue) {
/* Could be an InitialUE-Message after an empty CR, recreate new_ctx */
const struct osmo_sccp_addr *sccp_addr = ue_conn_sccp_addr_find(scu_iups, conn_id);
if (!sccp_addr) {
LOGP(DSUA, LOGL_NOTICE,
"N-DATA.ind for unknown conn_id (%u)\n", conn_id);
break;
}
/* Hold copy of address before deleting it: */
struct osmo_sccp_addr rem_sccp_addr = *sccp_addr;
ue_conn_sccp_addr_del(scu_iups, conn_id);
rc = sgsn_ranap_iu_rx_co_initial_msg(scu_iups, &rem_sccp_addr, conn_id,
msgb_l2(oph->msg), msgb_l2len(oph->msg));
break;
}
rc = sgsn_ranap_iu_rx_co_msg(ue, msgb_l2(oph->msg), msgb_l2len(oph->msg));
break;
case OSMO_PRIM(OSMO_SCU_PRIM_N_UNITDATA, PRIM_OP_INDICATION):
/* connection-less data received */
LOGP(DSUA, LOGL_DEBUG, "N-UNITDATA.ind(%s)\n",
osmo_hexdump(msgb_l2(oph->msg), msgb_l2len(oph->msg)));
rc = sgsn_ranap_iu_rx_cl_msg(scu_iups, &prim->u.unitdata, msgb_l2(oph->msg), msgb_l2len(oph->msg));
break;
case OSMO_PRIM(OSMO_SCU_PRIM_N_NOTICE, PRIM_OP_INDICATION):
LOGP(DSUA, LOGL_DEBUG, "N-NOTICE.ind(%s)\n",
osmo_hexdump(msgb_l2(oph->msg), msgb_l2len(oph->msg)));
handle_notice_ind(scu_iups, &prim->u.notice);
break;
case OSMO_PRIM(OSMO_SCU_PRIM_N_PCSTATE, PRIM_OP_INDICATION):
handle_pcstate_ind(scu_iups, &prim->u.pcstate);
break;
case OSMO_PRIM(OSMO_SCU_PRIM_N_STATE, PRIM_OP_INDICATION):
LOGP(DSUA, LOGL_DEBUG, "SCCP-User-SAP: Ignoring %s.%s\n",
osmo_scu_prim_type_name(oph->primitive),
get_value_string(osmo_prim_op_names, oph->operation));
break;
default:
break;
}
msgb_free(oph->msg);
return rc;
}
int sgsn_sccp_init(struct sgsn_instance *sgi)
{
/* Note that these are mostly defaults and can be overridden from the VTY */
sgi->sccp.sccp = osmo_sccp_simple_client_on_ss7_id(tall_sgsn_ctx,
sgi->cfg.iu.cs7_instance,
"OsmoSGSN",
(23 << 3) + 4,
OSMO_SS7_ASP_PROT_M3UA,
0, "localhost",
0, "localhost");
if (!sgi->sccp.sccp) {
LOGP(DGPRS, LOGL_ERROR, "Setting up SCCP instance on cs7 instance %d failed!\n",
sgi->cfg.iu.cs7_instance);
return -EINVAL;
}
osmo_sccp_set_priv(sgi->sccp.sccp, sgsn);
sgi->sccp.scu_iups = sgsn_scu_iups_inst_alloc(sgsn, sgi->sccp.sccp);
OSMO_ASSERT(sgi->sccp.scu_iups);
return 0;
}
void sgsn_sccp_release(struct sgsn_instance *sgi)
{
sgsn_scu_iups_free(sgi->sccp.scu_iups);
sgi->sccp.scu_iups = NULL;
if (sgi->sccp.sccp) {
osmo_sccp_instance_destroy(sgi->sccp.sccp);
sgi->sccp.sccp = NULL;
}
}

View File

@@ -21,6 +21,8 @@
#include <stdint.h>
#include "config.h"
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/timer.h>
@@ -36,6 +38,11 @@
#include <osmocom/gsm/gsm_utils.h>
#include <osmocom/gsm/gsup.h>
#include <osmocom/crypt/gprs_cipher.h>
#include <osmocom/crypt/utran_cipher.h>
#include <osmocom/gtp/pdp.h>
#include <osmocom/sgsn/gprs_subscriber.h>
#include <osmocom/sgsn/debug.h>
#include <osmocom/sgsn/sgsn.h>
@@ -52,15 +59,16 @@
#include <osmocom/sgsn/gtp_ggsn.h>
#include <osmocom/sgsn/gtp.h>
#include <osmocom/sgsn/pdpctx.h>
#include <pdp.h>
#include <osmocom/sgsn/gprs_routing_area.h>
#if BUILD_IU
#include <osmocom/sgsn/iu_rnc_fsm.h>
#include <osmocom/sgsn/sccp.h>
#endif /* #if BUILD_IU */
#include <time.h>
#define GPRS_LLME_CHECK_TICK 30
extern struct osmo_tdef sgsn_T_defs[];
static const struct rate_ctr_desc sgsn_ctr_description[] = {
{ "llc:dl_bytes", "Count sent LLC bytes before giving it to the bssgp layer" },
{ "llc:ul_bytes", "Count successful received LLC bytes (encrypt & fcs correct)" },
@@ -95,6 +103,20 @@ static const struct rate_ctr_group_desc sgsn_ctrg_desc = {
sgsn_ctr_description,
};
static const struct osmo_stat_item_desc sgsn_stat_item_description[] = {
[SGSN_STAT_IU_PEERS_TOTAL] = { "iu_peers:total", "Total Iu peers (RNC, HNBGW) seen since startup", OSMO_STAT_ITEM_NO_UNIT, 4, 0},
[SGSN_STAT_IU_PEERS_ACTIVE] = { "iu_peers:active", "Currently active Iu peers (RANAP ready)", OSMO_STAT_ITEM_NO_UNIT, 4, 0},
};
static const struct osmo_stat_item_group_desc sgsn_statg_desc = {
"sgsn",
"serving GPRS support node statistics",
OSMO_STATS_CLASS_GLOBAL,
ARRAY_SIZE(sgsn_stat_item_description),
sgsn_stat_item_description,
};
static void sgsn_llme_cleanup_free(struct gprs_llc_llme *llme)
{
struct sgsn_mm_ctx *mmctx = NULL;
@@ -148,8 +170,12 @@ static void sgsn_llme_check_cb(void *data_)
static int sgsn_instance_talloc_destructor(struct sgsn_instance *sgi)
{
sgsn_cdr_release(sgi);
#if BUILD_IU
sgsn_sccp_release(sgi);
#endif /* #if BUILD_IU */
osmo_timer_del(&sgi->llme_timer);
rate_ctr_group_free(sgi->rate_ctrs);
osmo_stat_item_group_free(sgi->statg);
return 0;
}
@@ -162,6 +188,8 @@ struct sgsn_instance *sgsn_instance_alloc(void *talloc_ctx)
inst->cfg.gtp_statedir = talloc_strdup(inst, "./");
inst->cfg.auth_policy = SGSN_AUTH_POLICY_CLOSED;
inst->cfg.gea_encryption_mask = (1 << GPRS_ALGO_GEA0); /* no encryption */
inst->cfg.uea_encryption_mask = (1 << OSMO_UTRAN_UEA2) | (1 << OSMO_UTRAN_UEA1);
inst->cfg.require_authentication = true; /* only applies if auth_policy is REMOTE */
inst->cfg.gsup_server_port = OSMO_GSUP_PORT;
@@ -172,18 +200,24 @@ struct sgsn_instance *sgsn_instance_alloc(void *talloc_ctx)
inst->rate_ctrs = rate_ctr_group_alloc(inst, &sgsn_ctrg_desc, 0);
OSMO_ASSERT(inst->rate_ctrs);
inst->statg = osmo_stat_item_group_alloc(inst, &sgsn_statg_desc, 0);
OSMO_ASSERT(inst->statg);
INIT_LLIST_HEAD(&inst->apn_list);
INIT_LLIST_HEAD(&inst->ggsn_list);
INIT_LLIST_HEAD(&inst->mme_list);
INIT_LLIST_HEAD(&inst->mm_list);
INIT_LLIST_HEAD(&inst->pdp_list);
#if BUILD_IU
INIT_LLIST_HEAD(&inst->rnc_list);
#endif /* #if BUILD_IU */
osmo_timer_setup(&inst->llme_timer, sgsn_llme_check_cb, NULL);
osmo_timer_schedule(&inst->llme_timer, GPRS_LLME_CHECK_TICK, 0);
/* These are mostly setting up stuff not related to VTY cfg, so they can be set up here: */
sgsn_auth_init(inst);
sgsn_cdr_init(inst);
sgsn_ra_init(inst);
return inst;
}
@@ -211,5 +245,13 @@ int sgsn_inst_init(struct sgsn_instance *sgsn)
LOGP(DGPRS, LOGL_FATAL, "Cannot set up SGSN\n");
return rc;
}
#if BUILD_IU
rc = sgsn_sccp_init(sgsn);
if (rc < 0) {
LOGP(DGPRS, LOGL_FATAL, "Cannot set up SGSN SCCP layer\n");
return rc;
}
#endif /* #if BUILD_IU */
return 0;
}

View File

@@ -133,9 +133,9 @@ enum sgsn_auth_state sgsn_auth_state(struct sgsn_mm_ctx *mmctx)
/* We simply assume that the IMSI exists, as long as it is part
* of 'our' network */
snprintf(mccmnc, sizeof(mccmnc), "%s%s",
osmo_mcc_name(mmctx->ra.mcc),
osmo_mnc_name(mmctx->ra.mnc, mmctx->ra.mnc_3_digits));
if (strncmp(mccmnc, mmctx->imsi, mmctx->ra.mnc_3_digits ? 6 : 5) == 0)
osmo_mcc_name(mmctx->ra.lac.plmn.mcc),
osmo_mnc_name(mmctx->ra.lac.plmn.mnc, mmctx->ra.lac.plmn.mnc_3_digits));
if (strncmp(mccmnc, mmctx->imsi, mmctx->ra.lac.plmn.mnc_3_digits ? 6 : 5) == 0)
return SGSN_AUTH_ACCEPTED;
}

View File

@@ -20,6 +20,9 @@
#include <osmocom/ctrl/control_if.h>
#include <osmocom/gtp/gtp.h>
#include <osmocom/gtp/pdp.h>
#include <osmocom/sgsn/sgsn.h>
#include <osmocom/sgsn/signal.h>
#include <osmocom/sgsn/gprs_utils.h>
@@ -31,9 +34,6 @@
#include <osmocom/sgsn/pdpctx.h>
#include <osmocom/sgsn/mmctx.h>
#include <gtp.h>
#include <pdp.h>
#include <arpa/inet.h>
#include <time.h>
@@ -92,7 +92,7 @@ static int cdr_snprintf_mm(char *buf, size_t size, const char *ev,
mmctx->imei,
mmctx->msisdn,
mmctx->gb.cell_id,
mmctx->ra.lac,
mmctx->ra.lac.lac,
mmctx->hlr,
ev);
return ret;
@@ -194,7 +194,7 @@ static int cdr_snprintf_pdp(char *buf, size_t size, const char *ev,
pdp->mm ? pdp->mm->imei : "N/A",
pdp->mm ? pdp->mm->msisdn : "N/A",
pdp->mm ? pdp->mm->gb.cell_id : -1,
pdp->mm ? pdp->mm->ra.lac : -1,
pdp->mm ? pdp->mm->ra.lac.lac : -1,
pdp->mm ? pdp->mm->hlr : "N/A",
ev,
(unsigned long ) duration,

View File

@@ -21,12 +21,14 @@
#include <osmocom/ctrl/control_if.h>
#include <osmocom/ctrl/control_cmd.h>
#include <osmocom/gtp/pdp.h>
#include <osmocom/sgsn/mmctx.h>
#include <osmocom/sgsn/pdpctx.h>
#include <osmocom/sgsn/sgsn.h>
#include <osmocom/sgsn/debug.h>
#include <pdp.h>
static int get_subscriber_list(struct ctrl_cmd *cmd, void *d)
{

View File

@@ -42,11 +42,15 @@
#include <osmocom/gprs/gprs_bssgp.h>
#include <osmocom/gsm/protocol/gsm_04_08_gprs.h>
#include <osmocom/gtp/gtp.h>
#include <osmocom/gtp/pdp.h>
#include <osmocom/sgsn/signal.h>
#include <osmocom/sgsn/debug.h>
#include <osmocom/sgsn/sgsn.h>
#include <osmocom/sgsn/gprs_ns.h>
#include <osmocom/sgsn/gprs_llc.h>
#include <osmocom/sgsn/gprs_routing_area.h>
#include <osmocom/sgsn/mmctx.h>
#include <osmocom/sgsn/gprs_gmm.h>
#include <osmocom/sgsn/gprs_sm.h>
@@ -55,15 +59,13 @@
#include <osmocom/sgsn/gprs_ranap.h>
#include <osmocom/sgsn/gprs_gmm_fsm.h>
#include <osmocom/sgsn/gprs_mm_state_gb_fsm.h>
#include <osmocom/sgsn/gprs_mm_state_iu_fsm.h>
#include <osmocom/sgsn/gtp_ggsn.h>
#include <osmocom/sgsn/gtp_mme.h>
#include <osmocom/sgsn/sgsn_rim.h>
#include <osmocom/sgsn/gprs_bssgp.h>
#include <osmocom/sgsn/pdpctx.h>
#include <gtp.h>
#include <pdp.h>
/* TS 23.003: The MSISDN shall take the dummy MSISDN value composed of
* 15 digits set to 0 (encoded as an E.164 international number) when
* the MSISDN is not available in messages in which the presence of the
@@ -142,7 +144,7 @@ struct sgsn_pdp_ctx *sgsn_create_pdp_ctx(struct sgsn_ggsn_ctx *ggsn,
uint16_t nsapi,
struct tlv_parsed *tp)
{
struct gprs_ra_id raid;
struct osmo_routing_area_id rai = {};
struct sgsn_pdp_ctx *pctx;
struct pdp_t *pdp;
uint64_t imsi_ui64;
@@ -166,6 +168,9 @@ struct sgsn_pdp_ctx *sgsn_create_pdp_ctx(struct sgsn_ggsn_ctx *ggsn,
pdp->priv = pctx;
pctx->lib = pdp;
/* Back up our own local TEID in case we update the library one with RNC TEID when setting up Direct Tunnel: */
pctx->sgsn_teid_own = pdp->teid_own;
//pdp->peer = /* sockaddr_in of GGSN (receive) */
//pdp->ipif = /* not used by library */
pdp->version = ggsn->gtp_version;
@@ -268,10 +273,10 @@ struct sgsn_pdp_ctx *sgsn_create_pdp_ctx(struct sgsn_ggsn_ctx *ggsn,
/* Routing Area Identifier with LAC and RAC fixed values, as
* requested in 29.006 7.3.1 */
raid = mmctx->ra;
raid.lac = 0xFFFE;
raid.rac = 0xFF;
gsm48_encode_ra((struct gsm48_ra_id *)pdp->rai.v, &raid);
rai = mmctx->ra;
rai.lac.lac = 0xFFFE;
rai.rac = 0xFF;
osmo_routing_area_id_encode_buf(pdp->rai.v, pdp->rai.l, &rai);
/* Encode User Location Information accordint to TS 29.060 7.7.51 */
pdp->userloc_given = 1;
@@ -286,20 +291,23 @@ struct sgsn_pdp_ctx *sgsn_create_pdp_ctx(struct sgsn_ggsn_ctx *ggsn,
pdp->userloc_given = 1;
pdp->userloc.l = 8;
pdp->userloc.v[0] = 0; /* CGI for GERAN */
bssgp_create_cell_id(&pdp->userloc.v[1], &mmctx->ra, mmctx->gb.cell_id);
bssgp_create_cell_id2(&pdp->userloc.v[1], 8, &mmctx->ra, mmctx->gb.cell_id);
break;
case MM_CTX_T_UTRAN_Iu:
pdp->userloc.v[0] = 1; /* SAI for UTRAN */
/* SAI is like CGI but with SAC instead of CID, so we can abuse this function */
bssgp_create_cell_id(&pdp->userloc.v[1], &mmctx->ra, mmctx->iu.sac);
bssgp_create_cell_id2(&pdp->userloc.v[1], 8, &mmctx->ra, mmctx->iu.sac);
break;
}
/* include the IMEI(SV) */
pdp->imeisv_given = 1;
gsm48_encode_bcd_number(&pdp->imeisv.v[0], 8, 0, mmctx->imei);
pdp->imeisv.l = pdp->imeisv.v[0];
memmove(&pdp->imeisv.v[0], &pdp->imeisv.v[1], 8);
/* optional include the IMEI(SV) */
if (mmctx->imei[0] != '\0') {
memset(&pdp->imeisv.v[0], 0, 8);
pdp->imeisv_given = 1;
gsm48_encode_bcd_number(&pdp->imeisv.v[0], 8, 0, mmctx->imei);
pdp->imeisv.l = 8;
memmove(&pdp->imeisv.v[0], &pdp->imeisv.v[1], 8);
}
/* change pdp state to 'requested' */
pctx->state = PDP_STATE_CR_REQ;
@@ -378,6 +386,7 @@ int send_act_pdp_cont_acc(struct sgsn_pdp_ctx *pctx)
rc = gsm48_tx_gsm_act_pdp_acc(pctx);
if (rc < 0)
return rc;
pctx->ue_pdp_active = true;
if (pctx->mm->ran_type == MM_CTX_T_GERAN_Gb) {
/* Send SNDCP XID to MS */
@@ -430,7 +439,7 @@ static int create_pdp_conf(struct pdp_t *pdp, void *cbp, int cause)
} else if (pctx->mm->ran_type == MM_CTX_T_UTRAN_Iu) {
#ifdef BUILD_IU
/* Activate a radio bearer */
iu_rab_act_ps(pdp->nsapi, pctx);
sgsn_pdp_ctx_iu_rab_activate(pctx, pdp->nsapi);
return 0;
#else
return -ENOTSUP;
@@ -464,13 +473,6 @@ reject:
return EOF;
}
void sgsn_pdp_upd_gtp_u(struct sgsn_pdp_ctx *pdp, void *addr, size_t alen)
{
pdp->lib->gsnlu.l = alen;
memcpy(pdp->lib->gsnlu.v, addr, alen);
gtp_update_context(pdp->ggsn->gsn, pdp->lib, pdp, &pdp->lib->hisaddr0);
}
void sgsn_ggsn_echo_req(struct sgsn_ggsn_ctx *ggc)
{
LOGGGSN(ggc, LOGL_INFO, "GTP Tx Echo Request\n");
@@ -562,14 +564,16 @@ static int delete_pdp_conf(struct pdp_t *pdp, void *cbp, int cause)
} else {
#ifdef BUILD_IU
/* Deactivate radio bearer */
ranap_iu_rab_deact(pctx->mm->iu.ue_ctx, 1);
sgsn_pdp_ctx_iu_rab_deactivate(pctx, 1);
#else
return -ENOTSUP;
#endif
}
/* Confirm deactivation of PDP context to MS */
rc = gsm48_tx_gsm_deact_pdp_acc(pctx);
if (pctx->ue_pdp_active) {
/* Confirm deactivation of PDP context to MS */
rc = gsm48_tx_gsm_deact_pdp_acc(pctx);
pctx->ue_pdp_active = false;
}
} else {
LOGPDPCTXP(LOGL_NOTICE, pctx,
"Not deactivating SNDCP layer since the MM context "
@@ -649,6 +653,52 @@ static int cb_conf(int type, int cause, struct pdp_t *pdp, void *cbp)
return 0;
}
/* Called whenever a PDP context is updated from the GGSN for any reason */
static int cb_update_context_ind(struct pdp_t *pdp)
{
struct sgsn_pdp_ctx *pctx;
struct sgsn_mm_ctx *mm;
int rc;
LOGPDPX(DGPRS, LOGL_INFO, pdp, "Context %p was updated\n", pdp);
pctx = pdp->priv;
if (!pctx) {
LOGP(DGPRS, LOGL_NOTICE,
"GTP DATA IND from GGSN for unknown PDP\n");
return -EIO;
}
mm = pctx->mm;
if (!mm) {
LOGP(DGPRS, LOGL_ERROR,
"PDP context (address=%u) without MM context!\n",
pctx->address);
return -EIO;
}
if (mm->ran_type == MM_CTX_T_UTRAN_Iu) {
#ifdef BUILD_IU
if (pdp->dir_tun_flags.v[0] & 0x04) { /* EI bit set ? */
/* GGSN informed us that it received an Error Indication when sending DL data to the RNC.
* This probably means the RNC lost its state, aka crashed or was rebooted.
* Page the UE so it re-creates the state at the RNC. */
LOGMMCTXP(LOGL_INFO, mm,
"GGSN received ErrorInd from RNC while tx DL data. Paging UE in state %s\n",
osmo_fsm_inst_state_name(mm->gmm_fsm));
rc = osmo_fsm_inst_dispatch(mm->iu.mm_state_fsm, E_PMM_RX_GGSN_GTPU_DT_EI, pctx);
rc = gtp_update_context_resp(sgsn->gsn, pdp,
GTPCAUSE_ACC_REQ);
ranap_iu_page_ps(mm->imsi, &mm->p_tmsi, mm->ra.lac.lac, mm->ra.rac);
return rc;
}
#endif
}
rc = gtp_update_context_resp(sgsn->gsn, pdp,
GTPCAUSE_ACC_REQ);
return rc;
}
/* Called whenever a PDP context is deleted for any reason */
static int cb_delete_context(struct pdp_t *pdp)
{
@@ -780,7 +830,9 @@ static int cb_data_ind(struct pdp_t *lib, void *packet, unsigned int len)
#ifdef BUILD_IU
/* Ignore the packet for now and page the UE to get the RAB
* reestablished */
ranap_iu_page_ps(mm->imsi, &mm->p_tmsi, mm->ra.lac, mm->ra.rac);
LOGMMCTXP(LOGL_INFO, mm, "Rx GTP for UE in PMM state %s, paging it\n",
osmo_fsm_inst_state_name(mm->iu.mm_state_fsm));
ranap_iu_page_ps(mm->imsi, &mm->p_tmsi, mm->ra.lac.lac, mm->ra.rac);
return 0;
#else
@@ -816,7 +868,7 @@ static int cb_data_ind(struct pdp_t *lib, void *packet, unsigned int len)
LOGMMCTXP(LOGL_INFO, mm, "Paging MS in GMM state %s, MM state %s\n",
osmo_fsm_inst_state_name(mm->gmm_fsm),
osmo_fsm_inst_state_name(mm->gb.mm_state_fsm));
sgsn_bssgp_page_ps_ra(mm);
sgsn_ra_geran_page_ra(&mm->ra, mm);
/* FIXME: queue the packet we received from GTP */
break;
@@ -842,14 +894,14 @@ static int cb_data_ind(struct pdp_t *lib, void *packet, unsigned int len)
}
/* Called by SNDCP when it has received/re-assembled a N-PDU */
int sgsn_gtp_data_req(struct gprs_ra_id *ra_id, int32_t tlli, uint8_t nsapi,
int sgsn_gtp_data_req(struct osmo_routing_area_id *rai, int32_t tlli, uint8_t nsapi,
struct msgb *msg, uint32_t npdu_len, uint8_t *npdu)
{
struct sgsn_mm_ctx *mmctx;
struct sgsn_pdp_ctx *pdp;
/* look-up the MM context for this message */
mmctx = sgsn_mm_ctx_by_tlli(tlli, ra_id);
mmctx = sgsn_mm_ctx_by_tlli(tlli, rai);
if (!mmctx) {
LOGP(DGPRS, LOGL_ERROR,
"Cannot find MM CTX for TLLI %08x\n", tlli);
@@ -943,6 +995,7 @@ int sgsn_gtp_init(struct sgsn_instance *sgi)
}
/* Register callbackcs with libgtp */
gtp_set_cb_update_context_ind(gsn, cb_update_context_ind);
gtp_set_cb_delete_context(gsn, cb_delete_context);
gtp_set_cb_conf(gsn, cb_conf);
gtp_set_cb_recovery3(gsn, cb_recovery3);

View File

@@ -45,6 +45,8 @@
#include <osmocom/gprs/gprs_bssgp.h>
#include <osmocom/gprs/gprs_bssgp_bss.h>
#include <osmocom/gtp/gtp.h>
#include <osmocom/vty/telnet_interface.h>
#include <osmocom/vty/logging.h>
#include <osmocom/vty/stats.h>
@@ -64,10 +66,10 @@
#include <osmocom/sgsn/gprs_ranap.h>
#include <osmocom/sgsn/gprs_ns.h>
#include <osmocom/sgsn/gprs_bssgp.h>
#include <osmocom/sgsn/gprs_routing_area.h>
#include <osmocom/sgsn/gprs_subscriber.h>
#include <osmocom/sgsn/gtp.h>
#include <gtp.h>
#include <osmocom/sgsn/sgsn_rim.h>
#include "../../config.h"
@@ -75,7 +77,7 @@
#if BUILD_IU
#include <osmocom/sigtran/osmo_ss7.h>
#include <osmocom/sigtran/protocol/m3ua.h>
#include <osmocom/ranap/iu_client.h>
#include <osmocom/sgsn/iu_client.h>
#endif
#define _GNU_SOURCE
@@ -348,6 +350,11 @@ static struct log_info_cat gprs_categories[] = {
.description = "RAN Information Management (RIM)",
.enabled = 1, .loglevel = LOGL_NOTICE,
},
[DRA] = {
.name = "DRA",
.description = "Routing Area",
.enabled = 1, .loglevel = LOGL_NOTICE,
},
};
static const struct log_info gprs_log_info = {
@@ -365,9 +372,6 @@ static bool file_exists(const char *path)
int main(int argc, char **argv)
{
int rc;
#if BUILD_IU
struct osmo_sccp_instance *sccp;
#endif
srand(time(NULL));
tall_sgsn_ctx = talloc_named_const(NULL, 0, "osmo_sgsn");
@@ -470,20 +474,7 @@ int main(int argc, char **argv)
}
#if BUILD_IU
/* Note that these are mostly defaults and can be overriden from the VTY */
sccp = osmo_sccp_simple_client_on_ss7_id(tall_sgsn_ctx,
sgsn->cfg.iu.cs7_instance,
"OsmoSGSN",
(23 << 3) + 4,
OSMO_SS7_ASP_PROT_M3UA,
0, "localhost",
0, "localhost");
if (!sccp) {
printf("Setting up SCCP client failed.\n");
return 8;
}
ranap_iu_init(tall_sgsn_ctx, DRANAP, "OsmoSGSN-IuPS", sccp, gsm0408_gprs_rcvmsg_iu, sgsn_ranap_iu_event);
ranap_iu_init(tall_sgsn_ctx);
#endif
if (daemonize) {

View File

@@ -24,6 +24,8 @@
#include <arpa/inet.h>
#include <time.h>
#include <inttypes.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/utils.h>
@@ -32,6 +34,9 @@
#include <osmocom/gsm/protocol/gsm_04_08_gprs.h>
#include <osmocom/gsm/apn.h>
#include <osmocom/gtp/gtp.h>
#include <osmocom/gtp/pdp.h>
#include <osmocom/sgsn/debug.h>
#include <osmocom/sgsn/sgsn.h>
#include <osmocom/gprs/gprs_ns2.h>
@@ -39,6 +44,7 @@
#include <osmocom/sgsn/gprs_gmm.h>
#include <osmocom/sgsn/gprs_bssgp.h>
#include <osmocom/sgsn/mmctx.h>
#include <osmocom/sgsn/gprs_routing_area.h>
#include <osmocom/sgsn/gtp_ggsn.h>
#include <osmocom/sgsn/gtp_mme.h>
#include <osmocom/sgsn/vty.h>
@@ -51,17 +57,13 @@
#include <osmocom/vty/misc.h>
#include <osmocom/crypt/gprs_cipher.h>
#include <osmocom/crypt/utran_cipher.h>
#include <osmocom/abis/ipa.h>
#include <osmocom/gprs/gprs_bssgp.h>
#include <pdp.h>
#include <gtp.h>
#include "../../config.h"
#ifdef BUILD_IU
#include <osmocom/ranap/iu_client.h>
#include <osmocom/sgsn/iu_client.h>
#endif
static struct sgsn_config *g_cfg = NULL;
@@ -96,7 +98,7 @@ const struct value_string sgsn_auth_pol_strs[] = {
/* Non spec timer */
#define NONSPEC_X1001_SECS 5 /* wait for a RANAP Release Complete */
#define RANAP_TRafR_SECS 5 /* wait for a RANAP Release Complete */
struct osmo_tdef sgsn_T_defs[] = {
{ .T=3312, .default_val=GSM0408_T3312_SECS, .desc="Periodic RA Update timer (s)" },
@@ -114,6 +116,7 @@ struct osmo_tdef sgsn_T_defs[] = {
/* non spec timers */
{ .T=-1001, .default_val=NONSPEC_X1001_SECS, .desc="RANAP Release timeout. Wait for RANAP Release Complete."
"On expiry release Iu connection (s)" },
{ .T=-1002, .default_val=RANAP_TRafR_SECS, .desc="TRafR, Maximum time for Reset procedure in the CN (s)" },
{}
};
@@ -233,6 +236,12 @@ static void config_write_mme(struct vty *vty, const struct sgsn_mme_ctx *mme, co
osmo_mcc_name(rt->tai.mcc), osmo_mnc_name(rt->tai.mnc, rt->tai.mnc_3_digits),
rt->tai.tac, VTY_NEWLINE);
}
if (mme->gummei_valid)
vty_out(vty, "%s gummei %s %s %d %d%s",
prefix,
osmo_mcc_name(mme->gummei.plmn.mcc),
osmo_mnc_name(mme->gummei.plmn.mnc, mme->gummei.plmn.mnc_3_digits),
mme->gummei.mme.group_id, mme->gummei.mme.code, VTY_NEWLINE);
}
static int config_write_sgsn(struct vty *vty)
@@ -404,6 +413,11 @@ DEFUN(cfg_sgsn_state_dir, cfg_sgsn_state_dir_cmd,
"Set the directory for the GTP State file\n"
"Local Directory\n")
{
if (mkdir(argv[0], 0755) == -1 && errno != EEXIST) {
vty_out(vty, "%% Failed to create state-dir: %s%s", argv[0], VTY_NEWLINE);
return CMD_WARNING;
}
osmo_talloc_replace_string(sgsn, &sgsn->cfg.gtp_statedir, argv[0]);
return CMD_SUCCESS;
@@ -644,7 +658,7 @@ static void vty_dump_mmctx(struct vty *vty, const char *pfx,
pfx, mm->msisdn, id, mm->hlr, VTY_NEWLINE);
vty_out(vty, "%s GMM State: %s, Routeing Area: %s, Cell ID: %u%s",
pfx, osmo_fsm_inst_state_name(mm->gmm_fsm),
osmo_rai_name(&mm->ra), mm->gb.cell_id, VTY_NEWLINE);
osmo_rai_name2(&mm->ra), mm->gb.cell_id, VTY_NEWLINE);
vty_out(vty, "%s MM State: %s, RAN Type: %s%s", pfx, mm_state_name,
get_value_string(sgsn_ran_type_names, mm->ran_type), VTY_NEWLINE);
@@ -662,11 +676,10 @@ DEFUN(show_sgsn, show_sgsn_cmd, "show sgsn",
SHOW_STR "Display information about the SGSN")
{
if (sgsn->gsup_client) {
struct ipa_client_conn *link = sgsn->gsup_client->link;
vty_out(vty,
" Remote authorization: %sconnected to %s:%d via GSUP%s",
sgsn->gsup_client->is_connected ? "" : "not ",
link->addr, link->port,
vty_out(vty, " Remote authorization: %sconnected to %s:%d via GSUP%s",
osmo_gsup_client_is_connected(sgsn->gsup_client) ? "" : "not ",
osmo_gsup_client_get_rem_addr(sgsn->gsup_client),
osmo_gsup_client_get_rem_port(sgsn->gsup_client),
VTY_NEWLINE);
}
if (sgsn->gsn)
@@ -990,8 +1003,17 @@ static void subscr_dump_full_vty(struct vty *vty, struct gprs_subscr *gsub, int
}
llist_for_each_entry(pdp, &gsub->sgsn_data->pdp_list, list) {
vty_out(vty, " PDP info: Id: %d, Type: 0x%04x, APN: '%s'",
pdp->context_id, pdp->pdp_type, pdp->apn_str);
char ip_str[INET6_ADDRSTRLEN] = { 0 };
vty_out(vty, " PDP info: Id: %d, Addr(Org: 0x%02x Type: 0x%02x",
pdp->context_id, pdp->pdp_type_org, pdp->pdp_type_nr);
if (pdp->pdp_address[0].u.sa.sa_family != AF_UNSPEC)
vty_out(vty, " Addr[0]: %s", osmo_sockaddr_ntop(&pdp->pdp_address[0].u.sa, ip_str));
if (pdp->pdp_address[0].u.sa.sa_family != AF_UNSPEC)
vty_out(vty, " Addr[1]: %s", osmo_sockaddr_ntop(&pdp->pdp_address[1].u.sa, ip_str));
vty_out(vty, ") APN: '%s'", pdp->apn_str);
if (pdp->qos_subscribed_len)
vty_out(vty, " QoS: %s", osmo_hexdump(pdp->qos_subscribed, pdp->qos_subscribed_len));
@@ -1317,7 +1339,7 @@ DEFUN(page_subscr, page_subscr_info_cmd,
return CMD_WARNING;
}
sgsn_bssgp_page_ps_ra(mm);
sgsn_ra_geran_page_ra(&mm->ra, mm);
return CMD_SUCCESS;
}
@@ -1766,6 +1788,44 @@ DEFUN(cfg_mme_no_ran_info_relay_default, cfg_mme_no_ran_info_relay_default_cmd,
return CMD_SUCCESS;
}
DEFUN(cfg_mme_mmei, cfg_mme_mmei_cmd,
"gummei <0-999> <0-999> <0-65535> <0-254>",
"Configure the mme\n" "MCC\n" "MNC\n" "MME GroupId\n" "MME Code")
{
struct sgsn_mme_ctx *mme = (struct sgsn_mme_ctx *) vty->index;
const char *mcc = argv[0];
const char *mnc = argv[1];
const char *group_id = argv[2];
const char *code = argv[3];
if (osmo_mcc_from_str(mcc, &mme->gummei.plmn.mcc)) {
vty_out(vty, "%% Error decoding MCC: %s%s", mcc, VTY_NEWLINE);
return CMD_WARNING;
}
if (osmo_mnc_from_str(mnc, &mme->gummei.plmn.mnc, &mme->gummei.plmn.mnc_3_digits)) {
vty_out(vty, "%% Error decoding MNC: %s%s", mnc, VTY_NEWLINE);
return CMD_WARNING;
}
mme->gummei.mme.code = atoi(code);
mme->gummei.mme.group_id = atoi(group_id);
mme->gummei_valid = true;
return CMD_SUCCESS;
}
DEFUN(cfg_no_mme_mmei, cfg_no_mme_mmei_cmd,
"no gummei",
NO_STR "Remove gummei")
{
struct sgsn_mme_ctx *mme = (struct sgsn_mme_ctx *) vty->index;
mme->gummei_valid = false;
return CMD_SUCCESS;
}
int sgsn_vty_init(struct sgsn_config *cfg)
{
g_cfg = cfg;
@@ -1842,6 +1902,8 @@ int sgsn_vty_init(struct sgsn_config *cfg)
install_element(MME_NODE, &cfg_mme_remote_ip_cmd);
install_element(MME_NODE, &cfg_mme_ran_info_relay_default_cmd);
install_element(MME_NODE, &cfg_mme_no_ran_info_relay_default_cmd);
install_element(MME_NODE, &cfg_mme_mmei_cmd);
install_element(MME_NODE, &cfg_no_mme_mmei_cmd);
install_element(MME_NODE, &cfg_mme_ran_info_relay_tai_cmd);
install_element(MME_NODE, &cfg_mme_no_ran_info_relay_tai_cmd);
@@ -1859,9 +1921,6 @@ int sgsn_parse_config(const char *config_file)
/* make sure sgsn_vty_init() was called before this */
OSMO_ASSERT(g_cfg);
g_cfg->gea_encryption_mask = 0x1; /* support GEA0 by default unless specific encryption config exists */
g_cfg->uea_encryption_mask = (1 << OSMO_UTRAN_UEA0); /* support UEA0 by default unless specific encryption config exists */
rc = vty_read_config_file(config_file, NULL);
if (rc < 0) {
fprintf(stderr, "Failed to parse the config file: '%s'\n", config_file);

View File

@@ -388,7 +388,7 @@ found:
DEBUGP(DSLHC, "slhc_compress(): Unexpected change: th->doff != cs->cs_tcp.doff\n");
if(ip->ihl > 5 && memcmp(ip+1,cs->cs_ipopt,((ip->ihl)-5)*4) != 0) {
DEBUGP(DSLHC, "slhc_compress(): Unexpected change: (ip->ihl > 5 && memcmp(ip+1,cs->cs_ipopt,((ip->ihl)-5)*4) != 0)\n");
DEBUGP(DSLHC, "slhc_compress(): ip->ihl = %i\n", ip->ihl);
DEBUGP(DSLHC, "slhc_compress(): ip->ihl = %d\n", ip->ihl);
DEBUGP(DSLHC, "slhc_compress(): ip+1 = %s\n",
osmo_hexdump_nospc((uint8_t*)(ip+1),((ip->ihl)-5)*4));
DEBUGP(DSLHC, "slhc_compress(): Unexpected change: cs->cs_ipopt = %s\n",
@@ -396,7 +396,7 @@ found:
}
if(th->doff > 5 && memcmp(th+1,cs->cs_tcpopt,((th->doff)-5)*4) != 0) {
DEBUGP(DSLHC, "slhc_compress(): Unexpected change: (th->doff > 5 && memcmp(th+1,cs->cs_tcpopt,((th->doff)-5)*4) != 0)\n");
DEBUGP(DSLHC, "slhc_compress(): th->doff = %i\n", th->doff);
DEBUGP(DSLHC, "slhc_compress(): th->doff = %d\n", th->doff);
DEBUGP(DSLHC, "slhc_compress(): th+1 = %s\n",
osmo_hexdump_nospc((uint8_t*)(th+1),((th->doff)-5)*4));
DEBUGP(DSLHC, "slhc_compress(): cs->cs_tcpopt = %s\n",
@@ -538,8 +538,8 @@ found:
cp += 2;
/* deltaS is now the size of the change section of the compressed header */
DEBUGP(DSLHC, "slhc_compress(): Delta-list length (deltaS) = %li\n",deltaS);
DEBUGP(DSLHC, "slhc_compress(): Original header len (hlen) = %i\n",hlen);
DEBUGP(DSLHC, "slhc_compress(): Delta-list length (deltaS) = %ld\n", deltaS);
DEBUGP(DSLHC, "slhc_compress(): Original header len (hlen) = %d\n", hlen);
memcpy(cp,new_seq,deltaS); /* Write list of deltas */
memcpy(cp+deltaS,icp+hlen,isize-hlen);

View File

@@ -1,6 +1,7 @@
SUBDIRS = \
gprs \
gtphub \
gprs_routing_area \
sgsn \
xid \
sndcp_xid \
@@ -32,6 +33,8 @@ EXTRA_DIST = \
$(TESTSUITE) \
vty_test_runner.py \
ctrl_test_runner.py \
osmo-sgsn-accept-all.cfg \
osmo-sgsn.cfg \
osmo-sgsn_test-nodes.vty \
$(NULL)
@@ -62,7 +65,7 @@ vty-python-test: $(top_builddir)/src/sgsn/osmo-sgsn
vty-transcript-test: $(top_builddir)/src/sgsn/osmo-sgsn
osmo_verify_transcript_vty.py -v \
-n OsmoSGSN -p 4245 \
-r "$(top_builddir)/src/sgsn/osmo-sgsn -c $(top_srcdir)/doc/examples/osmo-sgsn/osmo-sgsn.cfg" \
-r "$(top_builddir)/src/sgsn/osmo-sgsn -c $(top_srcdir)/tests/osmo-sgsn.cfg" \
$(U) $${T:-$(srcdir)/osmo-sgsn*.vty}
rm -f $(builddir)/gsn_restart

View File

@@ -145,8 +145,7 @@ class TestCtrlBase(unittest.TestCase):
class TestCtrlSGSN(TestCtrlBase):
def ctrl_command(self):
return ["./src/sgsn/osmo-sgsn", "-c",
"doc/examples/osmo-sgsn/osmo-sgsn.cfg"]
return ["./src/sgsn/osmo-sgsn", "-c", "tests/osmo-sgsn.cfg"]
def ctrl_app(self):
return (4251, "./src/sgsn/osmo-sgsn", "OsmoSGSN", "sgsn")

View File

@@ -0,0 +1,98 @@
AM_CPPFLAGS = \
$(all_includes) \
-I$(top_srcdir)/include \
$(NULL)
AM_CFLAGS = \
-Wall \
-ggdb3 \
$(LIBOSMOCORE_CFLAGS) \
$(LIBOSMOCTRL_CFLAGS) \
$(LIBOSMOABIS_CFLAGS) \
$(LIBOSMOGSM_CFLAGS) \
$(LIBOSMOGSUPCLIENT_CFLAGS) \
$(LIBCARES_CFLAGS) \
$(LIBGTP_CFLAGS) \
$(NULL)
if BUILD_IU
AM_CFLAGS += \
$(LIBASN1C_CFLAGS) \
$(LIBOSMOSIGTRAN_CFLAGS) \
$(LIBOSMORANAP_CFLAGS) \
$(NULL)
endif
AM_LDFLAGS = -no-install
EXTRA_DIST = \
gprs_routing_area_test.ok \
$(NULL)
check_PROGRAMS = \
gprs_routing_area_test \
$(NULL)
gprs_routing_area_test_SOURCES = \
gprs_routing_area_test.c \
$(NULL)
gprs_routing_area_test_LDADD = \
$(top_builddir)/src/sgsn/apn.o \
$(top_builddir)/src/sgsn/gprs_bssgp.o \
$(top_builddir)/src/sgsn/gprs_llc.o \
$(top_builddir)/src/sgsn/gprs_ns.o \
$(top_builddir)/src/sgsn/gprs_sndcp.o \
$(top_builddir)/src/sgsn/gprs_gmm_attach.o \
$(top_builddir)/src/sgsn/gprs_gmm.o \
$(top_builddir)/src/sgsn/gprs_gmm_fsm.o \
$(top_builddir)/src/sgsn/gprs_gmm_util.o \
$(top_builddir)/src/sgsn/gprs_mm_state_gb_fsm.o \
$(top_builddir)/src/sgsn/gprs_routing_area.o \
$(top_builddir)/src/sgsn/gtp_ggsn.o \
$(top_builddir)/src/sgsn/gtp_mme.o \
$(top_builddir)/src/sgsn/mmctx.o \
$(top_builddir)/src/sgsn/pdpctx.o \
$(top_builddir)/src/sgsn/sgsn.o \
$(top_builddir)/src/sgsn/sgsn_cdr.o \
$(top_builddir)/src/sgsn/sgsn_ctrl.o \
$(top_builddir)/src/sgsn/sgsn_vty.o \
$(top_builddir)/src/sgsn/sgsn_libgtp.o \
$(top_builddir)/src/sgsn/sgsn_auth.o \
$(top_builddir)/src/sgsn/gprs_subscriber.o \
$(top_builddir)/src/sgsn/gprs_llc_xid.o \
$(top_builddir)/src/sgsn/gprs_sndcp_xid.o \
$(top_builddir)/src/sgsn/slhc.o \
$(top_builddir)/src/sgsn/gprs_sm.o \
$(top_builddir)/src/sgsn/gprs_sndcp_comp.o \
$(top_builddir)/src/sgsn/gprs_sndcp_pcomp.o \
$(top_builddir)/src/sgsn/v42bis.o \
$(top_builddir)/src/sgsn/gprs_sndcp_dcomp.o \
$(top_builddir)/src/sgsn/sgsn_rim.o \
$(top_builddir)/src/gprs/gprs_utils.o \
$(top_builddir)/src/gprs/gprs_llc_parse.o \
$(top_builddir)/src/gprs/crc24.o \
$(top_builddir)/src/gprs/sgsn_ares.o \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOCTRL_LIBS) \
$(LIBOSMOGSM_LIBS) \
$(LIBOSMOGB_LIBS) \
$(LIBOSMOGSUPCLIENT_LIBS) \
$(LIBCARES_LIBS) \
$(LIBGTP_LIBS) \
-lrt \
-lm \
$(NULL)
if BUILD_IU
gprs_routing_area_test_LDADD += \
$(top_builddir)/src/sgsn/gprs_ranap.o \
$(top_builddir)/src/sgsn/gprs_mm_state_iu_fsm.o \
$(top_builddir)/src/sgsn/iu_client.o \
$(top_builddir)/src/sgsn/iu_rnc.o \
$(top_builddir)/src/sgsn/iu_rnc_fsm.o \
$(top_builddir)/src/sgsn/sccp.o \
$(LIBOSMORANAP_LIBS) \
$(LIBOSMOSIGTRAN_LIBS) \
$(LIBASN1C_LIBS) \
$(NULL)
endif

View File

@@ -0,0 +1,715 @@
/* Test the SGSN routing ares */
/*
* (C) 2024 by sysmocom s.f.m.c. GmbH
* All Rights Reserved
* Author: Alexander Couzens <lynxis@fe80.eu>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <osmocom/core/application.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/rate_ctr.h>
#include <osmocom/core/utils.h>
#include <osmocom/gsm/apn.h>
#include <osmocom/gsm/gsm23003.h>
#include <osmocom/gsm/gsm_utils.h>
#include <osmocom/gsm/gsup.h>
#include <osmocom/gprs/gprs_bssgp.h>
#include <osmocom/vty/vty.h>
#include <osmocom/gsupclient/gsup_client.h>
#include <osmocom/sgsn/gprs_llc.h>
#include <osmocom/sgsn/mmctx.h>
#include <osmocom/sgsn/sgsn.h>
#include <osmocom/sgsn/gprs_gmm.h>
#include <osmocom/sgsn/debug.h>
#include <osmocom/sgsn/gprs_routing_area.h>
#include <stdio.h>
void *tall_sgsn_ctx;
struct sgsn_instance *sgsn;
struct paging_exp {
uint16_t nsei;
uint16_t bvci;
/* paged when we send one paging request */
bool paged;
/* valid when this entry contains valid data */
bool valid;
};
struct paging_exp g_paging[4];
static void cleanup_test(void)
{
TALLOC_FREE(sgsn);
}
/* Create RA, free RA */
static void test_routing_area_create(void)
{
struct sgsn_ra *ra;
struct osmo_routing_area_id rai = {
.lac = {
.plmn = { .mcc = 262, .mnc = 42, .mnc_3_digits = false },
.lac = 23
},
.rac = 42
};
printf("Testing Routing Area create/free\n");
sgsn = sgsn_instance_alloc(tall_sgsn_ctx);
ra = sgsn_ra_find_or_create(&rai, RA_TYPE_GERAN_Gb);
OSMO_ASSERT(ra);
OSMO_ASSERT(llist_count(&sgsn->routing_area->ra_list) == 1);
sgsn_ra_free(ra);
OSMO_ASSERT(llist_empty(&sgsn->routing_area->ra_list));
/* Cleanup */
cleanup_test();
}
static void test_routing_area_free_empty(void)
{
struct sgsn_ra *ra;
struct sgsn_ra_cell *cell_a;
struct osmo_routing_area_id rai = {
.lac = {
.plmn = { .mcc = 262, .mnc = 42, .mnc_3_digits = false },
.lac = 24
},
.rac = 43
};
uint16_t cell_id = 9999;
uint16_t nsei = 2, bvci = 3;
printf("Testing Routing Area create/free\n");
sgsn = sgsn_instance_alloc(tall_sgsn_ctx);
ra = sgsn_ra_find_or_create(&rai, RA_TYPE_GERAN_Gb);
OSMO_ASSERT(ra);
OSMO_ASSERT(llist_count(&sgsn->routing_area->ra_list) == 1);
cell_a = sgsn_ra_cell_alloc_geran(ra, cell_id, nsei, bvci);
OSMO_ASSERT(cell_a);
OSMO_ASSERT(llist_count(&sgsn->routing_area->ra_list) == 1);
OSMO_ASSERT(llist_count(&ra->cells_alive_list) == 1);
sgsn_ra_free(ra);
OSMO_ASSERT(llist_empty(&sgsn->routing_area->ra_list));
ra = sgsn_ra_find_or_create(&rai, RA_TYPE_GERAN_Gb);
OSMO_ASSERT(ra);
OSMO_ASSERT(llist_count(&sgsn->routing_area->ra_list) == 1);
cell_a = sgsn_ra_cell_alloc_geran(ra, cell_id, nsei, bvci);
OSMO_ASSERT(cell_a);
OSMO_ASSERT(llist_count(&sgsn->routing_area->ra_list) == 1);
OSMO_ASSERT(llist_count(&ra->cells_alive_list) == 1);
sgsn_ra_free(ra);
OSMO_ASSERT(llist_empty(&sgsn->routing_area->ra_list));
cleanup_test();
}
/* Create RA, use different find functiosn, free RA */
static void test_routing_area_find(void)
{
struct sgsn_ra *ra_a, *ra_b;
struct sgsn_ra_cell *cell_a, *cell_b;
struct osmo_routing_area_id rai = {
.lac = {
.plmn = { .mcc = 262, .mnc = 42, .mnc_3_digits = false },
.lac = 24
},
.rac = 43
};
uint16_t cell_id = 9999, cell_id_not_found = 44;
struct osmo_cell_global_id_ps cgi_ps = {
.rai = rai,
.cell_identity = cell_id,
};
struct osmo_cell_global_id cgi = {
.lai = rai.lac,
.cell_identity = cell_id
};
uint16_t nsei = 2, bvci = 3;
printf("Testing Routing Area find\n");
sgsn = sgsn_instance_alloc(tall_sgsn_ctx);
ra_a = sgsn_ra_find_or_create(&rai, RA_TYPE_GERAN_Gb);
OSMO_ASSERT(ra_a);
OSMO_ASSERT(llist_count(&sgsn->routing_area->ra_list) == 1);
ra_b = sgsn_ra_get_ra(&rai);
OSMO_ASSERT(ra_a == ra_b);
cell_a = sgsn_ra_cell_alloc_geran(ra_a, cell_id, nsei, bvci);
OSMO_ASSERT(cell_a);
OSMO_ASSERT(llist_count(&sgsn->routing_area->ra_list) == 1);
cell_b = sgsn_ra_geran_get_cell_by_cgi_ps(&cgi_ps);
OSMO_ASSERT(cell_b);
OSMO_ASSERT(cell_b == cell_a);
cell_b = sgsn_ra_geran_get_cell_by_ra(ra_a, cgi.cell_identity);
OSMO_ASSERT(cell_b);
OSMO_ASSERT(cell_b == cell_a);
cell_b = sgsn_ra_geran_get_cell_by_cgi(&cgi);
OSMO_ASSERT(cell_b);
OSMO_ASSERT(cell_b == cell_a);
cell_b = sgsn_ra_geran_get_cell_by_lai(&cgi.lai, cgi.cell_identity);
OSMO_ASSERT(cell_b);
OSMO_ASSERT(cell_b == cell_a);
sgsn_ra_free(ra_a);
OSMO_ASSERT(llist_empty(&sgsn->routing_area->ra_list));
/* try to search for a cell id which isn't present */
cgi.cell_identity = cell_id_not_found;
cgi_ps.cell_identity = cell_id_not_found;
ra_a = sgsn_ra_find_or_create(&rai, RA_TYPE_GERAN_Gb);
OSMO_ASSERT(ra_a);
OSMO_ASSERT(llist_count(&sgsn->routing_area->ra_list) == 1);
cell_a = sgsn_ra_cell_alloc_geran(ra_a, cell_id, nsei, bvci);
OSMO_ASSERT(cell_a);
OSMO_ASSERT(llist_count(&sgsn->routing_area->ra_list) == 1);
cell_b = sgsn_ra_geran_get_cell_by_cgi_ps(&cgi_ps);
OSMO_ASSERT(!cell_b);
cell_b = sgsn_ra_geran_get_cell_by_ra(ra_a, cgi_ps.cell_identity);
OSMO_ASSERT(!cell_b);
cell_b = sgsn_ra_geran_get_cell_by_cgi(&cgi);
OSMO_ASSERT(!cell_b);
cell_b = sgsn_ra_geran_get_cell_by_lai(&cgi.lai, cgi.cell_identity);
OSMO_ASSERT(!cell_b);
/* try to find for a different RAC */
cgi_ps.rai.rac = 45;
rai.rac = 46;
cell_b = sgsn_ra_geran_get_cell_by_cgi_ps(&cgi_ps);
OSMO_ASSERT(!cell_b);
ra_b = sgsn_ra_get_ra(&rai);
OSMO_ASSERT(!ra_b);
/* try to find for different LAC */
cgi.lai.lac = 46;
cell_b = sgsn_ra_geran_get_cell_by_cgi(&cgi);
OSMO_ASSERT(!cell_b);
sgsn_ra_free(ra_a);
OSMO_ASSERT(llist_empty(&sgsn->routing_area->ra_list));
cleanup_test();
}
static void test_routing_area_reset_ind(void)
{
struct sgsn_ra *ra_a;
struct sgsn_ra_cell *cell_a, *cell_b;
struct osmo_routing_area_id rai = {
.lac = {
.plmn = { .mcc = 262, .mnc = 42, .mnc_3_digits = false },
.lac = 24
},
.rac = 43
};
uint16_t cell_id = 9999;
struct osmo_cell_global_id_ps cgi_ps = {
.rai = rai,
.cell_identity = cell_id,
};
struct osmo_cell_global_id cgi = {
.lai = rai.lac,
.cell_identity = cell_id
};
uint16_t nsei = 2, bvci = 3;
int rc;
printf("Testing Routing Area BSSGP BVC RESET IND\n");
sgsn = sgsn_instance_alloc(tall_sgsn_ctx);
ra_a = sgsn_ra_find_or_create(&rai, RA_TYPE_GERAN_Gb);
OSMO_ASSERT(ra_a);
OSMO_ASSERT(llist_count(&sgsn->routing_area->ra_list) == 1);
OSMO_ASSERT(llist_count(&ra_a->cells_alive_list) == 0);
rc = sgsn_ra_geran_bvc_cell_reset_ind(nsei, bvci, &cgi_ps);
OSMO_ASSERT(rc == 0);
OSMO_ASSERT(llist_count(&ra_a->cells_alive_list) == 1);
cell_a = sgsn_ra_geran_get_cell_by_cgi(&cgi);
OSMO_ASSERT(cell_a);
rc = sgsn_ra_geran_bvc_cell_reset_ind(nsei, bvci, &cgi_ps);
OSMO_ASSERT(rc == 0);
cell_b = sgsn_ra_geran_get_cell_by_cgi(&cgi);
OSMO_ASSERT(cell_b);
OSMO_ASSERT(cell_a == cell_b);
sgsn_ra_free(ra_a);
OSMO_ASSERT(llist_empty(&sgsn->routing_area->ra_list));
rc = sgsn_ra_geran_bvc_cell_reset_ind(nsei, bvci, &cgi_ps);
OSMO_ASSERT(rc == 0);
OSMO_ASSERT(llist_count(&sgsn->routing_area->ra_list) == 1);
ra_a = sgsn_ra_get_ra(&cgi_ps.rai);
sgsn_ra_free(ra_a);
OSMO_ASSERT(llist_empty(&sgsn->routing_area->ra_list));
cleanup_test();
}
void test_routing_area_nsei_free(void)
{
struct sgsn_ra *ra_a;
struct osmo_routing_area_id rai = {
.lac = {
.plmn = { .mcc = 262, .mnc = 42, .mnc_3_digits = false },
.lac = 24
},
.rac = 43
};
uint16_t cell_id = 9999;
struct osmo_cell_global_id_ps cgi_ps = {
.rai = rai,
.cell_identity = cell_id,
};
uint16_t nsei = 2, bvci = 3;
int rc;
printf("Testing Routing Area nsei failure\n");
sgsn = sgsn_instance_alloc(tall_sgsn_ctx);
rc = sgsn_ra_geran_bvc_cell_reset_ind(nsei, bvci, &cgi_ps);
OSMO_ASSERT(rc == 0);
ra_a = sgsn_ra_get_ra(&cgi_ps.rai);
OSMO_ASSERT(llist_count(&ra_a->cells_alive_list) == 1);
rc = sgsn_ra_geran_nsei_failure_ind(nsei);
OSMO_ASSERT(rc == 0);
OSMO_ASSERT(llist_empty(&sgsn->routing_area->ra_list));
rc = sgsn_ra_geran_nsei_failure_ind(nsei);
OSMO_ASSERT(rc == -ENOENT);
OSMO_ASSERT(llist_empty(&sgsn->routing_area->ra_list));
cleanup_test();
}
/* BSSGP Paging RA */
int bssgp_tx_paging(uint16_t nsei, uint16_t _bvci,
struct bssgp_paging_info *pinfo)
{
bool found = false;
OSMO_ASSERT(pinfo);
fprintf(stderr, "Tx paging for nsei %05u / bvci %05u\n", nsei, pinfo->bvci);
/* match against list of expect pagings */
for (int i = 0; i < ARRAY_SIZE(g_paging); i++) {
struct paging_exp *exp = &g_paging[i];
if (exp->paged || !exp->valid)
continue;
if (exp->nsei == nsei && exp->bvci == pinfo->bvci) {
exp->paged = true;
found = true;
break;
}
}
OSMO_ASSERT(found);
return 0;
}
static void check_paging(void)
{
for (int i = 0; i < ARRAY_SIZE(g_paging); i++) {
struct paging_exp *exp = &g_paging[i];
if (!exp->valid)
continue;
OSMO_ASSERT(exp->paged)
}
}
void test_routing_area_paging(void)
{
struct sgsn_mm_ctx *mmctx;
struct osmo_routing_area_id rai = {
.lac = {
.plmn = { .mcc = 262, .mnc = 42, .mnc_3_digits = false },
.lac = 24
},
.rac = 43
};
uint16_t cell_id = 9999;
struct osmo_cell_global_id_ps cgi_ps = {
.rai = rai,
.cell_identity = cell_id,
};
uint16_t nsei = 2, bvci = 3;
int rc;
printf("Testing Routing Area paging\n");
sgsn = sgsn_instance_alloc(tall_sgsn_ctx);
memset(g_paging, 0, sizeof(g_paging));
g_paging[0].bvci = bvci;
g_paging[0].nsei = nsei;
g_paging[0].valid = true;
g_paging[0].paged = false;
rc = sgsn_ra_geran_bvc_cell_reset_ind(nsei, bvci, &cgi_ps);
OSMO_ASSERT(rc == 0);
cgi_ps.cell_identity++;
rc = sgsn_ra_geran_bvc_cell_reset_ind(nsei, bvci+1, &cgi_ps);
OSMO_ASSERT(rc == 0);
g_paging[1].bvci = bvci+1;
g_paging[1].nsei = nsei;
g_paging[1].valid = true;
g_paging[1].paged = false;
mmctx = sgsn_mm_ctx_alloc_gb(0xc0001234, &rai);
sgsn_ra_geran_page_ra(&rai, mmctx);
check_paging();
sgsn_mm_ctx_cleanup_free(mmctx);
cleanup_test();
}
/* check if a GERAN cell got removed when sending a Reset Ind on Sig BVCI */
void test_routing_area_geran_geran_sig_reset(void)
{
int rc;
/* GERAN */
struct osmo_routing_area_id geran_rai = {
.lac = {
.plmn = { .mcc = 262, .mnc = 42, .mnc_3_digits = false },
.lac = 24
},
.rac = 43
};
struct osmo_cell_global_id_ps cgi_ps = {
.rai = geran_rai,
.cell_identity = 9998,
};
uint16_t nsei = 2, bvci = 5;
struct sgsn_ra_cell *cell;
sgsn = sgsn_instance_alloc(tall_sgsn_ctx);
printf("Testing Routing Area GERAN BVCI Signalling Reset Ind\n");
printf(" Registering GERAN RA/cell via BVCI 5/BVC Reset Ind\n");
sgsn_ra_geran_bvc_sign_reset_ind(nsei);
rc = sgsn_ra_geran_bvc_cell_reset_ind(nsei, bvci, &cgi_ps);
OSMO_ASSERT(rc == 0);
printf(" Checking cell on BVCI 5\n");
cell = sgsn_ra_geran_get_cell_by_cgi_ps(&cgi_ps);
OSMO_ASSERT(cell);
OSMO_ASSERT(cell->ran_type == RA_TYPE_GERAN_Gb);
OSMO_ASSERT(cell->u.geran.bvci == bvci);
printf(" Ensure only 1 RA is present\n");
OSMO_ASSERT(llist_count(&sgsn->routing_area->ra_list) == 1);
printf(" Ensure only 1 cell is present\n");
OSMO_ASSERT(llist_count(&cell->ra->cells_alive_list) == 1);
printf(" Drop all cells via BVC Reset Ind on Signalling BVCI\n");
sgsn_ra_geran_bvc_sign_reset_ind(nsei);
printf(" Ensure only 0 RAs are present\n");
OSMO_ASSERT(llist_count(&sgsn->routing_area->ra_list) == 0);
cleanup_test();
}
/* check if a GERAN cell X can changed it's BVCI by BVC Reset Ind */
void test_routing_area_geran_geran_bvci_change(void)
{
int rc;
/* GERAN */
struct osmo_routing_area_id geran_rai = {
.lac = {
.plmn = { .mcc = 262, .mnc = 42, .mnc_3_digits = false },
.lac = 24
},
.rac = 43
};
struct osmo_cell_global_id_ps cgi_ps = {
.rai = geran_rai,
.cell_identity = 9998,
};
uint16_t nsei = 2, bvci_a = 3, bvci_b = 4;
struct sgsn_ra_cell *cell_a, *cell_b;
sgsn = sgsn_instance_alloc(tall_sgsn_ctx);
printf("Testing Routing Area GERAN to GERAN (BVCI change)\n");
printf(" Registering GERAN RA/cell via BVCI A/BVC Reset Ind\n");
rc = sgsn_ra_geran_bvc_cell_reset_ind(nsei, bvci_a, &cgi_ps);
OSMO_ASSERT(rc == 0);
printf(" Checking cell on BVCI A\n");
cell_a = sgsn_ra_geran_get_cell_by_cgi_ps(&cgi_ps);
OSMO_ASSERT(cell_a);
OSMO_ASSERT(cell_a->ran_type == RA_TYPE_GERAN_Gb);
OSMO_ASSERT(cell_a->u.geran.bvci == bvci_a);
printf(" Ensure only 1 RA is present\n");
OSMO_ASSERT(llist_count(&sgsn->routing_area->ra_list) == 1);
printf(" Ensure only 1 cell is present\n");
OSMO_ASSERT(llist_count(&cell_a->ra->cells_alive_list) == 1);
printf(" Registering GERAN RA/cell via BVCI B/BVC Reset Ind\n");
rc = sgsn_ra_geran_bvc_cell_reset_ind(nsei, bvci_b, &cgi_ps);
OSMO_ASSERT(rc == 0);
printf(" Checking cell on BVCI B\n");
cell_b = sgsn_ra_geran_get_cell_by_cgi_ps(&cgi_ps);
OSMO_ASSERT(cell_b);
OSMO_ASSERT(cell_b->ran_type == RA_TYPE_GERAN_Gb);
OSMO_ASSERT(cell_b->u.geran.bvci == bvci_b);
printf(" Ensure only 1 RA is present\n");
OSMO_ASSERT(llist_count(&sgsn->routing_area->ra_list) == 1);
printf(" Ensure only 1 cell is present\n");
OSMO_ASSERT(llist_count(&cell_b->ra->cells_alive_list) == 1);
cleanup_test();
}
/* check if UTRAN RA gets rejected, if a GERAN RA/cell with the same LAC is already registered
* The SGSN does not support the same LAC/RA for GERAN and UTRAN at the same time.
*/
void test_routing_area_mv_utran_geran_reject(void)
{
int rc;
/* GERAN */
struct osmo_routing_area_id geran_rai = {
.lac = {
.plmn = { .mcc = 262, .mnc = 42, .mnc_3_digits = false },
.lac = 24
},
.rac = 43
};
struct osmo_cell_global_id_ps cgi_ps = {
.rai = geran_rai,
.cell_identity = 9998,
};
uint16_t nsei = 2, bvci = 3;
/* UTRAN */
struct osmo_routing_area_id utran_rai = {
.lac = {
.plmn = { .mcc = 262, .mnc = 42, .mnc_3_digits = false },
.lac = 24
},
.rac = 43
};
struct osmo_rnc_id rnc_id = {
.plmn = utran_rai.lac.plmn,
.rnc_id = 2222
};
sgsn = sgsn_instance_alloc(tall_sgsn_ctx);
/* Registering UTRAN RA */
rc = sgsn_ra_utran_register(&utran_rai, &rnc_id);
OSMO_ASSERT(rc == 0);
/* Registering GERAN RA/cell via BVC Reset Ind (should fail) */
rc = sgsn_ra_geran_bvc_cell_reset_ind(nsei, bvci, &cgi_ps);
OSMO_ASSERT(rc != 0);
cleanup_test();
}
/* check if a UTRAN RA with the same LAC as an already register GERAN RA gets rejected */
void test_routing_area_mv_geran_utran_reject(void)
{
int rc;
/* GERAN */
struct osmo_routing_area_id geran_rai = {
.lac = {
.plmn = { .mcc = 262, .mnc = 42, .mnc_3_digits = false },
.lac = 24
},
.rac = 43
};
struct osmo_cell_global_id_ps cgi_ps = {
.rai = geran_rai,
.cell_identity = 9998,
};
uint16_t nsei = 2, bvci = 3;
/* UTRAN */
struct osmo_routing_area_id utran_rai = {
.lac = {
.plmn = { .mcc = 262, .mnc = 42, .mnc_3_digits = false },
.lac = 24
},
.rac = 43
};
struct osmo_rnc_id rnc_id = {
.plmn = utran_rai.lac.plmn,
.rnc_id = 2222
};
sgsn = sgsn_instance_alloc(tall_sgsn_ctx);
/* Registering GERAN RA/cell via BVC Reset Ind */
rc = sgsn_ra_geran_bvc_cell_reset_ind(nsei, bvci, &cgi_ps);
OSMO_ASSERT(rc == 0);
/* Registering UTRAN RA (should fail) */
rc = sgsn_ra_utran_register(&utran_rai, &rnc_id);
OSMO_ASSERT(rc != 0);
cleanup_test();
}
static struct log_info_cat gprs_categories[] = {
[DMM] = {
.name = "DMM",
.description = "Layer3 Mobility Management (MM)",
.color = "\033[1;33m",
.enabled = 1, .loglevel = LOGL_DEBUG,
},
[DPAG] = {
.name = "DPAG",
.description = "Paging Subsystem",
.color = "\033[1;38m",
.enabled = 1, .loglevel = LOGL_NOTICE,
},
[DREF] = {
.name = "DREF",
.description = "Reference Counting",
.enabled = 0, .loglevel = LOGL_NOTICE,
},
[DGPRS] = {
.name = "DGPRS",
.description = "GPRS Packet Service",
.enabled = 1, .loglevel = LOGL_DEBUG,
},
[DLLC] = {
.name = "DLLC",
.description = "GPRS Logical Link Control Protocol (LLC)",
.enabled = 1, .loglevel = LOGL_DEBUG,
},
[DRA] = {
.name = "DRA",
.description = "Routing Area",
.enabled = 1, .loglevel = LOGL_DEBUG,
},
};
static struct log_info info = {
.cat = gprs_categories,
.num_cat = ARRAY_SIZE(gprs_categories),
};
static struct vty_app_info vty_info = {
.name = "testSGSN",
};
int main(int argc, char **argv)
{
void *osmo_sgsn_ctx;
void *msgb_ctx;
osmo_sgsn_ctx = talloc_named_const(NULL, 0, "osmo_sgsn");
osmo_init_logging2(osmo_sgsn_ctx, &info);
tall_sgsn_ctx = talloc_named_const(osmo_sgsn_ctx, 0, "sgsn");
msgb_ctx = msgb_talloc_ctx_init(osmo_sgsn_ctx, 0);
vty_init(&vty_info);
test_routing_area_create();
test_routing_area_find();
test_routing_area_free_empty();
test_routing_area_reset_ind();
test_routing_area_nsei_free();
test_routing_area_paging();
test_routing_area_geran_geran_sig_reset();
test_routing_area_geran_geran_bvci_change();
#ifdef BUILD_IU
test_routing_area_mv_geran_utran_reject();
test_routing_area_mv_utran_geran_reject();
#endif
printf("Done\n");
talloc_report_full(osmo_sgsn_ctx, stderr);
OSMO_ASSERT(talloc_total_blocks(msgb_ctx) == 1);
OSMO_ASSERT(talloc_total_blocks(tall_sgsn_ctx) == 1);
return 0;
}
/* stubs */
struct osmo_prim_hdr;
int bssgp_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
{
abort();
}

View File

@@ -0,0 +1,23 @@
Testing Routing Area create/free
Testing Routing Area find
Testing Routing Area create/free
Testing Routing Area BSSGP BVC RESET IND
Testing Routing Area nsei failure
Testing Routing Area paging
Testing Routing Area GERAN BVCI Signalling Reset Ind
Registering GERAN RA/cell via BVCI 5/BVC Reset Ind
Checking cell on BVCI 5
Ensure only 1 RA is present
Ensure only 1 cell is present
Drop all cells via BVC Reset Ind on Signalling BVCI
Ensure only 0 RAs are present
Testing Routing Area GERAN to GERAN (BVCI change)
Registering GERAN RA/cell via BVCI A/BVC Reset Ind
Checking cell on BVCI A
Ensure only 1 RA is present
Ensure only 1 cell is present
Registering GERAN RA/cell via BVCI B/BVC Reset Ind
Checking cell on BVCI B
Ensure only 1 RA is present
Ensure only 1 cell is present
Done

View File

@@ -29,11 +29,12 @@
#include <osmocom/core/application.h>
#include <osmocom/gsm/protocol/gsm_23_003.h>
#include <osmocom/gtp/gtp.h>
#include <osmocom/gtp/gtpie.h>
#include <osmocom/sgsn/debug.h>
#include <osmocom/gtphub/gtphub.h>
#include <gtp.h>
#include <gtpie.h>
#define ZERO_STRUCT(struct_pointer) memset(struct_pointer, '\0', \
sizeof(*(struct_pointer)))

View File

@@ -0,0 +1,38 @@
# Same as doc/examples/osmo-sgsn/osmo-sgsn-accept-all.cfg, but without 'gtp state-dir'
!
! Osmocom SGSN configuration
!
!
log stderr
logging color 1
logging print category-hex 0
logging print category 1
logging timestamp 0
logging print file basename last
logging print level 1
line vty
no login
!
sgsn
gtp local-ip 127.0.0.1
ggsn 0 remote-ip 127.0.0.2
ggsn 0 gtp-version 1
ggsn 0 echo-interval 60
authentication optional
auth-policy accept-all
!
ns
timer tns-block 3
timer tns-block-retries 3
timer tns-reset 3
timer tns-reset-retries 3
timer tns-test 30
timer tns-alive 3
timer tns-alive-retries 10
bind udp local
listen 127.0.0.1 23000
accept-ipaccess
!
bssgp
!

40
tests/osmo-sgsn.cfg Normal file
View File

@@ -0,0 +1,40 @@
# Same as doc/examples/osmo-sgsn/osmo-sgsn.cfg, but without 'gtp state-dir'
!
! Osmocom SGSN configuration
!
!
log stderr
logging color 1
logging print category-hex 0
logging print category 1
logging timestamp 0
logging print file basename last
logging print level 1
line vty
no login
!
sgsn
gtp local-ip 127.0.0.1
ggsn 0 remote-ip 127.0.0.2
ggsn 0 gtp-version 1
ggsn 0 echo-interval 60
authentication required
auth-policy remote
gsup remote-ip 127.0.0.1
gsup remote-port 4222
!
ns
timer tns-block 3
timer tns-block-retries 3
timer tns-reset 3
timer tns-reset-retries 3
timer tns-test 30
timer tns-alive 3
timer tns-alive-retries 10
bind udp local
listen 127.0.0.1 23000
accept-ipaccess
!
bssgp
!

View File

@@ -13,6 +13,7 @@ T3386 = 8 s Wait for MODIFY PDP CTX ACK timer (s) (default: 8 s)
T3395 = 8 s Wait for DEACT PDP CTX ACK timer (s) (default: 8 s)
T3397 = 8 s Wait for DEACT AA PDP CTX ACK timer (s) (default: 8 s)
X1001 = 5 s RANAP Release timeout. Wait for RANAP Release Complete.On expiry release Iu connection (s) (default: 5 s)
X1002 = 5 s TRafR, Maximum time for Reset procedure in the CN (s) (default: 5 s)
OsmoSGSN# configure terminal
OsmoSGSN(config)# list
...

View File

@@ -59,7 +59,9 @@ sgsn_test_LDADD = \
$(top_builddir)/src/sgsn/gprs_gmm_attach.o \
$(top_builddir)/src/sgsn/gprs_gmm.o \
$(top_builddir)/src/sgsn/gprs_gmm_fsm.o \
$(top_builddir)/src/sgsn/gprs_gmm_util.o \
$(top_builddir)/src/sgsn/gprs_mm_state_gb_fsm.o \
$(top_builddir)/src/sgsn/gprs_routing_area.o \
$(top_builddir)/src/sgsn/gtp_ggsn.o \
$(top_builddir)/src/sgsn/gtp_mme.o \
$(top_builddir)/src/sgsn/mmctx.o \
@@ -84,7 +86,6 @@ sgsn_test_LDADD = \
$(top_builddir)/src/gprs/gprs_llc_parse.o \
$(top_builddir)/src/gprs/crc24.o \
$(top_builddir)/src/gprs/sgsn_ares.o \
$(LIBOSMOABIS_LIBS) \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOCTRL_LIBS) \
$(LIBOSMOGSM_LIBS) \
@@ -100,6 +101,10 @@ if BUILD_IU
sgsn_test_LDADD += \
$(top_builddir)/src/sgsn/gprs_ranap.o \
$(top_builddir)/src/sgsn/gprs_mm_state_iu_fsm.o \
$(top_builddir)/src/sgsn/iu_client.o \
$(top_builddir)/src/sgsn/iu_rnc.o \
$(top_builddir)/src/sgsn/iu_rnc_fsm.o \
$(top_builddir)/src/sgsn/sccp.o \
$(LIBOSMORANAP_LIBS) \
$(LIBOSMOSIGTRAN_LIBS) \
$(LIBASN1C_LIBS) \

View File

@@ -181,7 +181,7 @@ static struct msgb *create_msg(const uint8_t *data, size_t len)
/*
* Create a context and search for it
*/
static struct sgsn_mm_ctx *alloc_mm_ctx(uint32_t tlli, struct gprs_ra_id *raid)
static struct sgsn_mm_ctx *alloc_mm_ctx(uint32_t tlli, struct osmo_routing_area_id *raid)
{
struct sgsn_mm_ctx *ctx, *ictx;
struct gprs_llc_lle *lle;
@@ -200,7 +200,7 @@ static struct sgsn_mm_ctx *alloc_mm_ctx(uint32_t tlli, struct gprs_ra_id *raid)
}
static void send_0408_message(struct gprs_llc_llme *llme, uint32_t tlli,
const struct gprs_ra_id *bssgp_raid,
const struct osmo_routing_area_id *bssgp_raid,
const uint8_t *data, size_t data_len)
{
struct msgb *msg;
@@ -210,7 +210,7 @@ static void send_0408_message(struct gprs_llc_llme *llme, uint32_t tlli,
msg = create_msg(data, data_len);
msgb_tlli(msg) = tlli;
bssgp_create_cell_id(msgb_bcid(msg), bssgp_raid, 0);
bssgp_create_cell_id2(msgb_bcid(msg), 8, bssgp_raid, 0);
gsm0408_gprs_rcvmsg_gb(msg, llme, false);
msgb_free(msg);
}
@@ -375,7 +375,7 @@ static void test_auth_triplets(void)
const char *imsi1 = "1234567890";
struct gsm_auth_tuple *at;
struct sgsn_mm_ctx *ctx;
struct gprs_ra_id raid = { 0, };
struct osmo_routing_area_id raid = { 0, };
uint32_t local_tlli = 0xffeeddcc;
printf("Testing authentication triplet handling\n");
@@ -455,7 +455,7 @@ static void test_subscriber_gsup(void)
struct gprs_subscr *s1, *s1found;
const char *imsi1 = "1234567890";
struct sgsn_mm_ctx *ctx;
struct gprs_ra_id raid = { 0, };
struct osmo_routing_area_id raid = { 0, };
uint32_t local_tlli = 0xffeeddcc;
struct sgsn_subscriber_pdp_data *pdpd;
int rc;
@@ -740,7 +740,7 @@ int my_gsup_client_send_dummy(struct osmo_gsup_client *gsupc, struct msgb *msg)
*/
static void test_gmm_detach(void)
{
struct gprs_ra_id raid = { 0, };
struct osmo_routing_area_id raid = { 0, };
struct sgsn_mm_ctx *ctx, *ictx;
uint32_t local_tlli;
@@ -782,7 +782,7 @@ static void test_gmm_detach(void)
*/
static void test_gmm_detach_power_off(void)
{
struct gprs_ra_id raid = { 0, };
struct osmo_routing_area_id raid = { 0, };
struct sgsn_mm_ctx *ctx, *ictx;
uint32_t local_tlli;
@@ -823,7 +823,7 @@ static void test_gmm_detach_power_off(void)
*/
static void test_gmm_detach_no_mmctx(void)
{
struct gprs_ra_id raid = { 0, };
struct osmo_routing_area_id raid = { 0, };
struct gprs_llc_lle *lle;
uint32_t local_tlli;
@@ -860,7 +860,7 @@ static void test_gmm_detach_no_mmctx(void)
*/
static void test_gmm_detach_accept_unexpected(void)
{
struct gprs_ra_id raid = { 0, };
struct osmo_routing_area_id raid = { 0, };
struct gprs_llc_lle *lle;
uint32_t local_tlli;
@@ -897,7 +897,7 @@ static void test_gmm_detach_accept_unexpected(void)
*/
static void test_gmm_status_no_mmctx(void)
{
struct gprs_ra_id raid = { 0, };
struct osmo_routing_area_id raid = { 0, };
struct gprs_llc_lle *lle;
uint32_t local_tlli;
@@ -1092,7 +1092,7 @@ int my_gsup_client_send(struct osmo_gsup_client *gsupc, struct msgb *msg)
*/
static void test_gmm_reject(void)
{
struct gprs_ra_id raid = { 0, };
struct osmo_routing_area_id raid = { 0, };
struct sgsn_mm_ctx *ctx = NULL;
uint32_t foreign_tlli;
struct gprs_llc_lle *lle;
@@ -1237,7 +1237,7 @@ static void test_gmm_reject(void)
*/
static void test_gmm_cancel(void)
{
struct gprs_ra_id raid = { 0, };
struct osmo_routing_area_id raid = { 0, };
struct sgsn_mm_ctx *ctx = NULL;
struct sgsn_mm_ctx *ictx;
uint32_t ptmsi1;
@@ -1446,7 +1446,7 @@ static void test_ggsn_selection(void)
struct gprs_subscr *s1;
const char *imsi1 = "1234567890";
struct sgsn_mm_ctx *ctx;
struct gprs_ra_id raid = { 0, };
struct osmo_routing_area_id raid = { 0, };
uint32_t local_tlli = 0xffeeddcc;
enum gsm48_gsm_cause gsm_cause;
struct tlv_parsed tp;
@@ -1489,7 +1489,8 @@ static void test_ggsn_selection(void)
pdp_data = sgsn_subscriber_pdp_data_alloc(s1->sgsn_data);
pdp_data->context_id = 1;
pdp_data->pdp_type = 0x0121;
pdp_data->pdp_type_org = PDP_TYPE_ORG_IETF;
pdp_data->pdp_type_nr = PDP_TYPE_N_IETF_IPv4;
osmo_strlcpy(pdp_data->apn_str, "*", sizeof(pdp_data->apn_str));
/* Resolve GGSNs */
@@ -1581,20 +1582,21 @@ static void test_ggsn_selection(void)
cleanup_test();
}
bool pdp_status_has_active_nsapis(const uint8_t *pdp_status, const size_t pdp_status_len);
bool pdp_status_has_active_nsapis(uint16_t pdp_status);
static void test_pdp_status_has_active_nsapis(void)
{
const size_t pdp_status_len = 2;
const uint8_t pdp_status1[] = { 0b00100000, 0b00000000 }; /* PDP NSAPI 5 active */
const uint8_t pdp_status2[] = { 0b00000000, 0b00000000 }; /* no active PDP NSAPI */
const uint8_t pdp_status3[] = { 0b00000000, 0b00000001 }; /* PDP NSAPI 8 active */
uint16_t pdp_status1 = 0b0000000000100000; /* PDP NSAPI 5 active */
uint16_t pdp_status2 = 0b0000000000000000; /* no active PDP NSAPI */
uint16_t pdp_status3 = 0b0000000100000000; /* PDP NSAPI 8 active */
uint16_t pdp_status4 = 0b0000000000000001; /* reserved NSAPI 0 active -> no active */
printf("Testing pdp_status_has_active_nsapis\n");
OSMO_ASSERT(pdp_status_has_active_nsapis(pdp_status1, pdp_status_len));
OSMO_ASSERT(!pdp_status_has_active_nsapis(pdp_status2, pdp_status_len));
OSMO_ASSERT(pdp_status_has_active_nsapis(pdp_status3, pdp_status_len));
OSMO_ASSERT(pdp_status_has_active_nsapis(pdp_status1));
OSMO_ASSERT(!pdp_status_has_active_nsapis(pdp_status2));
OSMO_ASSERT(pdp_status_has_active_nsapis(pdp_status3));
OSMO_ASSERT(!pdp_status_has_active_nsapis(pdp_status4));
}
static struct log_info_cat gprs_categories[] = {

View File

@@ -209,12 +209,12 @@ static void test_slhc(const void *ctx)
packet_len = DISP_MAX_BYTES;
if (packet_decompr_len > DISP_MAX_BYTES)
packet_decompr_len = DISP_MAX_BYTES;
printf("Original Packet: (%i bytes) %s\n", packet_len,
printf("Original Packet: (%d bytes) %s\n", packet_len,
osmo_hexdump_nospc(packet, packet_len));
printf("DecompressedPacket: (%i bytes) %s\n",
printf("DecompressedPacket: (%d bytes) %s\n",
packet_decompr_len, osmo_hexdump_nospc(packet_decompr,
packet_decompr_len));
printf("CompressedPacket: (%i bytes) %s\n", packet_compr_len,
printf("CompressedPacket: (%d bytes) %s\n", packet_compr_len,
osmo_hexdump_nospc(packet_compr, packet_compr_len));
slhc_o_status(comp);
slhc_o_status(comp);

View File

@@ -10,7 +10,6 @@ sndcp_xid_test_SOURCES = sndcp_xid_test.c
sndcp_xid_test_LDADD = \
$(top_builddir)/src/sgsn/gprs_sndcp_xid.o \
$(LIBOSMOABIS_LIBS) \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOGSM_LIBS) \
$(LIBOSMOGB_LIBS) \

View File

@@ -55,7 +55,7 @@ static void test_xid_decode_realworld(const void *ctx)
/* Encode comp-fields again */
rc = gprs_sndcp_compile_xid(xid_r,sizeof(xid_r), comp_fields,
DEFAULT_SNDCP_VERSION);
printf("Result length=%i\n",rc);
printf("Result length=%d\n", rc);
printf("Encoded: %s\n", osmo_hexdump_nospc(xid, sizeof(xid)));
printf("Rencoded: %s\n", osmo_hexdump_nospc(xid_r, rc));
@@ -231,7 +231,7 @@ static void test_xid_encode_decode(const void *ctx)
DEFAULT_SNDCP_VERSION);
OSMO_ASSERT(rc > 0);
printf("Encoded: %s (%i bytes)\n", osmo_hexdump_nospc(xid, rc), rc);
printf("Encoded: %s (%d bytes)\n", osmo_hexdump_nospc(xid, rc), rc);
/* Parse and show contained comp fields */
comp_fields_dec = gprs_sndcp_parse_xid(NULL, ctx, xid, rc, NULL);

View File

@@ -14,6 +14,13 @@ cat $abs_srcdir/sgsn/sgsn_test.ok > expout
AT_CHECK([$abs_top_builddir/tests/sgsn/sgsn_test], [], [expout], [ignore])
AT_CLEANUP
AT_SETUP([gprs_routing_area])
AT_KEYWORDS([gprs_routing_area])
AT_CHECK([test "$enable_gprs_routing_area_test" != no || exit 77])
cat $abs_srcdir/gprs_routing_area/gprs_routing_area_test.ok > expout
AT_CHECK([$abs_top_builddir/tests/gprs_routing_area/gprs_routing_area_test], [], [expout], [ignore])
AT_CLEANUP
AT_SETUP([gtphub])
AT_KEYWORDS([gtphub])
AT_CHECK([test "$enable_gtphub_test" != no || exit 77])

View File

@@ -268,7 +268,7 @@ static void v42bis(const void *ctx, int mode, uint8_t *testvec, int len)
rc_sum += rc;
/* Check results */
printf("Mode: %i\n", mode);
printf("Mode: %d\n", mode);
printf("uncompressed_original= %s ASCII:",
osmo_hexdump_nospc(uncompressed_original, len));
@@ -314,7 +314,7 @@ static void test_v42bis_tcpip(const void *ctx, int packet_id)
int len;
printf
("Testing compression/decompression with realistic TCP/IP packets:\n");
printf("Packet No.: %i\n", packet_id);
printf("Packet No.: %d\n", packet_id);
len = strlen(uncompr_packets[packet_id]);
testvec = talloc_zero_size(ctx, len);
len = osmo_hexparse(uncompr_packets[packet_id], testvec, len);
@@ -339,7 +339,7 @@ static void test_v42bis_tcpip_decompress(const void *ctx, int packet_id)
printf
("Testing decompression with sniffed compressed TCP/IP packets:\n");
printf("Packet No.: %i\n", packet_id);
printf("Packet No.: %d\n", packet_id);
len = strlen(compr_packets[packet_id]);
uncompressed = talloc_zero_size(ctx, len);

View File

@@ -70,8 +70,7 @@ class TestVTYBase(unittest.TestCase):
class TestVTYSGSN(TestVTYBase):
def vty_command(self):
return ["./src/sgsn/osmo-sgsn", "-c",
"doc/examples/osmo-sgsn/osmo-sgsn-accept-all.cfg"]
return ["./src/sgsn/osmo-sgsn", "-c", "tests/osmo-sgsn-accept-all.cfg"]
def vty_app(self):
return (4245, "./src/sgsn/osmo-sgsn", "OsmoSGSN", "sgsn")

View File

@@ -27,7 +27,6 @@ xid_test_SOURCES = \
xid_test_LDADD = \
$(top_builddir)/src/sgsn/gprs_llc_xid.o \
$(LIBOSMOABIS_LIBS) \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOGSM_LIBS) \
$(LIBOSMOGB_LIBS) \

View File

@@ -73,8 +73,8 @@ static void test_xid_encode(const void *ctx)
/* Encode data */
rc = gprs_llc_compile_xid(xid, sizeof(xid), &xid_fields);
OSMO_ASSERT(rc == 14);
printf("Encoded: %s (%i bytes)\n", osmo_hexdump_nospc(xid, rc), rc);
printf("Expected: %s (%i bytes)\n",
printf("Encoded: %s (%d bytes)\n", osmo_hexdump_nospc(xid, rc), rc);
printf("Expected: %s (%d bytes)\n",
osmo_hexdump_nospc(xid_expected, sizeof(xid_expected)),
(int)sizeof(xid_expected));
@@ -112,7 +112,7 @@ static void test_xid_decode(const void *ctx)
/* Encode xid-fields again */
rc = gprs_llc_compile_xid(xid_r, sizeof(xid_r), xid_fields);
printf("Result length=%i\n",rc);
printf("Result length=%d\n", rc);
printf("Encoded: %s\n", osmo_hexdump_nospc(xid, sizeof(xid)));
printf("Rencoded: %s\n", osmo_hexdump_nospc(xid_r, rc));