From 5e205c8375fa3d2812e81508b9ea3523fb90ccb9 Mon Sep 17 00:00:00 2001 From: Pau Espin Pedrol Date: Fri, 24 Mar 2023 17:05:09 +0100 Subject: [PATCH] Introduce libosmo-gprs-sm This patch itnroduces all the boilerplate required to build and package a new libosmo-gprs-sm.so library, which aims to contain the SM layer implementation. A minimal implementation is provided together with a unit test, showcasing Activation of a PDP Context and requesting implicit GPRS attach to GMM if it is not yet attached. This commit also adds/improves code in GMM layer implementing the GMMSM SAP which is used to interact SM<->GMM. Related: OS#5501 Change-Id: I9b4a9a6364f7799540475e7e1d10ab2310768281 --- Makefile.am | 1 + configure.ac | 4 + contrib/libosmo-gprs.spec.in | 31 ++ debian/control | 25 ++ debian/libosmo-gprs-sm-dev.install | 5 + debian/libosmo-gprs-sm0.install | 1 + include/osmocom/gprs/Makefile.am | 1 + include/osmocom/gprs/gmm/gmm_ms_fsm.h | 32 +- include/osmocom/gprs/gmm/gmm_prim.h | 24 +- include/osmocom/gprs/gmm/gmm_private.h | 9 +- include/osmocom/gprs/sm/Makefile.am | 13 + include/osmocom/gprs/sm/sm.h | 54 +++ include/osmocom/gprs/sm/sm_ms_fsm.h | 44 +++ include/osmocom/gprs/sm/sm_pdu.h | 17 + include/osmocom/gprs/sm/sm_prim.h | 207 ++++++++++++ include/osmocom/gprs/sm/sm_private.h | 180 ++++++++++ libosmo-gprs-sm.pc.in | 12 + src/Makefile.am | 1 + src/gmm/gmm.c | 20 +- src/gmm/gmm_ms_fsm.c | 105 +++++- src/gmm/gmm_prim.c | 74 +++-- src/sm/Makefile.am | 40 +++ src/sm/misc.c | 34 ++ src/sm/sm.c | 304 +++++++++++++++++ src/sm/sm_ms_fsm.c | 270 +++++++++++++++ src/sm/sm_pdu.c | 131 ++++++++ src/sm/sm_prim.c | 437 +++++++++++++++++++++++++ tests/Makefile.am | 1 + tests/gmm/gmm_prim_test.c | 88 ++++- tests/gmm/gmm_prim_test.err | 22 +- tests/gmm/gmm_prim_test.ok | 16 +- tests/sm/Makefile.am | 33 ++ tests/sm/sm_prim_test.c | 283 ++++++++++++++++ tests/sm/sm_prim_test.err | 11 + tests/sm/sm_prim_test.ok | 5 + tests/testsuite.at | 7 + 36 files changed, 2482 insertions(+), 60 deletions(-) create mode 100644 debian/libosmo-gprs-sm-dev.install create mode 100644 debian/libosmo-gprs-sm0.install create mode 100644 include/osmocom/gprs/sm/Makefile.am create mode 100644 include/osmocom/gprs/sm/sm.h create mode 100644 include/osmocom/gprs/sm/sm_ms_fsm.h create mode 100644 include/osmocom/gprs/sm/sm_pdu.h create mode 100644 include/osmocom/gprs/sm/sm_prim.h create mode 100644 include/osmocom/gprs/sm/sm_private.h create mode 100644 libosmo-gprs-sm.pc.in create mode 100644 src/sm/Makefile.am create mode 100644 src/sm/misc.c create mode 100644 src/sm/sm.c create mode 100644 src/sm/sm_ms_fsm.c create mode 100644 src/sm/sm_pdu.c create mode 100644 src/sm/sm_prim.c create mode 100644 tests/sm/Makefile.am create mode 100644 tests/sm/sm_prim_test.c create mode 100644 tests/sm/sm_prim_test.err create mode 100644 tests/sm/sm_prim_test.ok diff --git a/Makefile.am b/Makefile.am index 4a55859..2c727b0 100644 --- a/Makefile.am +++ b/Makefile.am @@ -18,6 +18,7 @@ pkgconfig_DATA = \ libosmo-gprs-gmm.pc \ libosmo-gprs-llc.pc \ libosmo-gprs-rlcmac.pc \ + libosmo-gprs-sm.pc \ libosmo-gprs-sndcp.pc \ $(NULL) diff --git a/configure.ac b/configure.ac index 4631b8b..52b9d14 100644 --- a/configure.ac +++ b/configure.ac @@ -79,6 +79,7 @@ AC_CONFIG_FILES([libosmo-csn1.pc libosmo-gprs-llc.pc libosmo-gprs-rlcmac.pc libosmo-gprs-sndcp.pc + libosmo-gprs-sm.pc include/Makefile include/osmocom/Makefile include/osmocom/csn1/Makefile @@ -87,17 +88,20 @@ AC_CONFIG_FILES([libosmo-csn1.pc include/osmocom/gprs/llc/Makefile include/osmocom/gprs/rlcmac/Makefile include/osmocom/gprs/sndcp/Makefile + include/osmocom/gprs/sm/Makefile src/Makefile src/csn1/Makefile src/gmm/Makefile src/llc/Makefile src/rlcmac/Makefile src/sndcp/Makefile + src/sm/Makefile tests/Makefile tests/gmm/Makefile tests/llc/Makefile tests/rlcmac/Makefile tests/sndcp/Makefile + tests/sm/Makefile Makefile contrib/libosmo-gprs.spec]) AC_OUTPUT diff --git a/contrib/libosmo-gprs.spec.in b/contrib/libosmo-gprs.spec.in index 9ad4817..ed43968 100644 --- a/contrib/libosmo-gprs.spec.in +++ b/contrib/libosmo-gprs.spec.in @@ -114,6 +114,24 @@ Requires: libosmo-gprs-sndcp0 = %{version} This package provides development files for compiling a program using libosmo-gprs-sndcp - SNDCP (Subnetwork Dependent Convergence Protocol) layer for (E)GPRS. +%package -n libosmo-gprs-sm0 +Summary: Osmocom GPRS SM library +License: AGPL-3.0-or-later +Group: System/Libraries + +%description -n libosmo-gprs-sm0 +This package provides SM (Session Management) layer for (E)GPRS. + +%package -n libosmo-gprs-sm-devel +Summary: Development files for libosmo-gprs-sm +License: AGPL-3.0-or-later +Group: Development/Libraries/C and C++ +Requires: libosmo-gprs-sm0 = %{version} + +%description -n libosmo-gprs-sm-devel +This package provides development files for compiling a program using +libosmo-gprs-sm - SM (Session Management) layer for (E)GPRS. + %prep %setup -q @@ -140,6 +158,8 @@ make %{?_smp_mflags} check || (find . -name testsuite.log -exec cat {} +) %postun -n libosmo-gprs-rlcmac0 -p /sbin/ldconfig %post -n libosmo-gprs-sndcp0 -p /sbin/ldconfig %postun -n libosmo-gprs-sndcp0 -p /sbin/ldconfig +%post -n libosmo-gprs-sm0 -p /sbin/ldconfig +%postun -n libosmo-gprs-sm0 -p /sbin/ldconfig %files -n libosmo-csn1-0 %_libdir/libosmo-csn1.so.0* @@ -195,4 +215,15 @@ make %{?_smp_mflags} check || (find . -name testsuite.log -exec cat {} +) %_libdir/libosmo-gprs-sndcp.so %_libdir/pkgconfig/libosmo-gprs-sndcp.pc +%files -n libosmo-gprs-sm0 +%_libdir/libosmo-gprs-sm.so.0* + +%files -n libosmo-gprs-sm-devel +%dir %_includedir/%name +%dir %_includedir/%name/osmocom +%dir %_includedir/%name/osmocom/gprs +%_includedir/%name/osmocom/gprs/sm +%_libdir/libosmo-gprs-sm.so +%_libdir/pkgconfig/libosmo-gprs-sm.pc + %changelog diff --git a/debian/control b/debian/control index b2a5d5f..283ee26 100644 --- a/debian/control +++ b/debian/control @@ -142,3 +142,28 @@ Section: libdevel Depends: libosmo-gprs-sndcp0 (= ${binary:Version}), ${misc:Depends} Description: Development headers and libraries for libosmo-gprs-sndcp + +Package: libosmo-gprs-sm0 +Section: libs +Architecture: any +Multi-Arch: same +Pre-Depends: ${misc:Pre-Depends} +Depends: ${misc:Depends}, + ${shlibs:Depends} +Description: Osmocom SM (Session Management) layer for GPRS and EGPRS + +Package: libosmo-gprs-sm-dbg +Architecture: any +Section: debug +Multi-Arch: same +Depends: libosmo-gprs-sm0 (= ${binary:Version}), + ${misc:Depends} +Description: Debug symbols for libosmo-gprs-sm + +Package: libosmo-gprs-sm-dev +Architecture: any +Multi-Arch: same +Section: libdevel +Depends: libosmo-gprs-sm0 (= ${binary:Version}), + ${misc:Depends} +Description: Development headers and libraries for libosmo-gprs-sm diff --git a/debian/libosmo-gprs-sm-dev.install b/debian/libosmo-gprs-sm-dev.install new file mode 100644 index 0000000..aba99ff --- /dev/null +++ b/debian/libosmo-gprs-sm-dev.install @@ -0,0 +1,5 @@ +usr/include/osmocom/gprs/sm +usr/lib/*/libosmo-gprs-sm*.a +usr/lib/*/libosmo-gprs-sm*.so +usr/lib/*/libosmo-gprs-sm*.la +usr/lib/*/pkgconfig/libosmo-gprs-sm.pc diff --git a/debian/libosmo-gprs-sm0.install b/debian/libosmo-gprs-sm0.install new file mode 100644 index 0000000..a15113e --- /dev/null +++ b/debian/libosmo-gprs-sm0.install @@ -0,0 +1 @@ +usr/lib/*/libosmo-gprs-sm*.so.* diff --git a/include/osmocom/gprs/Makefile.am b/include/osmocom/gprs/Makefile.am index bb6c242..3b860c6 100644 --- a/include/osmocom/gprs/Makefile.am +++ b/include/osmocom/gprs/Makefile.am @@ -3,4 +3,5 @@ SUBDIRS = \ llc \ rlcmac \ sndcp \ + sm \ $(NULL) diff --git a/include/osmocom/gprs/gmm/gmm_ms_fsm.h b/include/osmocom/gprs/gmm/gmm_ms_fsm.h index dd22a32..3e6ac96 100644 --- a/include/osmocom/gprs/gmm/gmm_ms_fsm.h +++ b/include/osmocom/gprs/gmm/gmm_ms_fsm.h @@ -30,7 +30,7 @@ enum gprs_gmm_ms_fsm_events { GPRS_GMM_MS_EV_ATTACH_REQUESTED, GPRS_GMM_MS_EV_ATTACH_REJECTED, GPRS_GMM_MS_EV_ATTACH_ACCEPTED, - GPRS_GMM_MS_EV_DETACH_REQUESTED, /* also network initiated. data: ptr to enum osmo_gprs_gmm_detach_ms_type */ + GPRS_GMM_MS_EV_DETACH_REQUESTED, /* also network initiated. */ GPRS_GMM_MS_EV_DETACH_REQUESTED_POWEROFF, GPRS_GMM_MS_EV_DETACH_ACCEPTED, GPRS_GMM_MS_EV_SR_REQUESTED, /* (Iu only) */ @@ -45,8 +45,23 @@ enum gprs_gmm_ms_fsm_events { struct gprs_gmm_ms_fsm_ctx { struct osmo_fsm_inst *fi; struct gprs_gmm_entity *gmme; - /* Type of last initiated detach: */ - enum osmo_gprs_gmm_detach_ms_type detach_type; + + /* Info about last initiated attach: */ + struct { + enum osmo_gprs_gmm_attach_type type; + bool with_imsi; + bool explicit_att; /* true if by SMREG-ATTACH.req requested it */ + bool implicit_att; /* true if GMMSM-ESTABLISH.req requested it */ + /* Session Ids waiting for attach to happen during implicit_att: */ + uint32_t sess_id[16]; + uint8_t num_sess_id; + } attach; + + /* Info about last initiated detach: */ + struct { + enum osmo_gprs_gmm_detach_ms_type type; + enum osmo_gprs_gmm_detach_poweroff_type poweroff_type; + } detach; }; int gprs_gmm_ms_fsm_init(void); @@ -54,3 +69,14 @@ void gprs_gmm_ms_fsm_set_log_cat(int logcat); int gprs_gmm_ms_fsm_ctx_init(struct gprs_gmm_ms_fsm_ctx *ctx, struct gprs_gmm_entity *gmme); void gprs_gmm_ms_fsm_ctx_release(struct gprs_gmm_ms_fsm_ctx *ctx); + +int gprs_gmm_ms_fsm_ctx_request_attach(struct gprs_gmm_ms_fsm_ctx *ctx, + enum osmo_gprs_gmm_attach_type attach_type, + bool attach_with_imsi, + bool explicit_attach, + uint32_t sess_id); + +int gprs_gmm_ms_fsm_ctx_request_detach(struct gprs_gmm_ms_fsm_ctx *ctx, + enum osmo_gprs_gmm_detach_ms_type detach_type, + enum osmo_gprs_gmm_detach_poweroff_type poweroff_type); + diff --git a/include/osmocom/gprs/gmm/gmm_prim.h b/include/osmocom/gprs/gmm/gmm_prim.h index 16cd712..78c8dfb 100644 --- a/include/osmocom/gprs/gmm/gmm_prim.h +++ b/include/osmocom/gprs/gmm/gmm_prim.h @@ -184,14 +184,30 @@ static inline const char *osmo_gprs_gmm_gmmsm_prim_type_name(enum osmo_gprs_gmm_ /* Parameters for OSMO_GPRS_GMM_GMMSM_* prims */ struct osmo_gprs_gmm_gmmsm_prim { - /* Common fields (none) */ + /* Common fields */ + uint32_t sess_id; union { /* OSMO_GPRS_GMM_GMMSM_ESTABLISH | Req */ struct { + enum osmo_gprs_gmm_attach_type attach_type; + uint32_t ptmsi; + bool attach_with_imsi; + char imsi[OSMO_IMSI_BUF_SIZE]; + char imei[GSM23003_IMEI_NUM_DIGITS + 1]; + char imeisv[GSM23003_IMEISV_NUM_DIGITS+1]; + /* attach-type, READY-timer, STANDBY-timer */ } establish_req; /* OSMO_GPRS_GMM_GMMSM_ESTABLISH | Cnf/Rej */ struct { - uint8_t cause; + bool accepted; + union { + struct { + /* PLMNs MT-caps, attach-type. */ + } acc; + struct { + uint8_t cause; + } rej; + }; } establish_cnf; /* OSMO_GPRS_GMM_GMMSM_RELEASE | Ind */ struct { @@ -242,5 +258,5 @@ struct osmo_gprs_gmm_prim *osmo_gprs_gmm_prim_alloc_gmmreg_detach_req(void); struct osmo_gprs_gmm_prim *osmo_gprs_gmm_prim_alloc_gmmrr_page_ind(uint32_t tlli); /* Alloc primitive for GMMSM SAP: */ -struct osmo_gprs_gmm_prim *osmo_gprs_gmm_prim_alloc_gmmsm_establish_req(void); -struct osmo_gprs_gmm_prim *osmo_gprs_gmm_prim_alloc_gmmsm_unitdata_req(uint8_t *smpdu, unsigned int smpdu_len); +struct osmo_gprs_gmm_prim *osmo_gprs_gmm_prim_alloc_gmmsm_establish_req(uint32_t id); +struct osmo_gprs_gmm_prim *osmo_gprs_gmm_prim_alloc_gmmsm_unitdata_req(uint32_t id, uint8_t *smpdu, unsigned int smpdu_len); diff --git a/include/osmocom/gprs/gmm/gmm_private.h b/include/osmocom/gprs/gmm/gmm_private.h index 0f05dff..2f6eb8c 100644 --- a/include/osmocom/gprs/gmm/gmm_private.h +++ b/include/osmocom/gprs/gmm/gmm_private.h @@ -77,9 +77,9 @@ struct osmo_gprs_gmm_prim *gprs_gmm_prim_alloc_gmmreg_detach_cnf(void); struct osmo_gprs_gmm_prim *gprs_gmm_prim_alloc_gmmrr_assign_req(uint32_t new_tlli); -struct osmo_gprs_gmm_prim *gprs_gmm_prim_alloc_gmmsm_establish_cnf(uint8_t cause); -struct osmo_gprs_gmm_prim *gprs_gmm_prim_alloc_gmmrr_release_ind(void); -struct osmo_gprs_gmm_prim *gprs_gmm_prim_alloc_gmmsm_unitdata_ind(uint8_t *smpdu, unsigned int smpdu_len); +struct osmo_gprs_gmm_prim *gprs_gmm_prim_alloc_gmmsm_establish_cnf(uint32_t id, uint8_t cause); +struct osmo_gprs_gmm_prim *gprs_gmm_prim_alloc_gmmrr_release_ind(uint32_t id); +struct osmo_gprs_gmm_prim *gprs_gmm_prim_alloc_gmmsm_unitdata_ind(uint32_t id, uint8_t *smpdu, unsigned int smpdu_len); /* gmm.c: */ struct gprs_gmm_entity *gprs_gmm_gmme_alloc(void); @@ -93,6 +93,9 @@ int gprs_gmm_tx_detach_req(struct gprs_gmm_entity *gmme, enum osmo_gprs_gmm_detach_ms_type detach_type, enum osmo_gprs_gmm_detach_poweroff_type poweroff_type); +int gprs_gmm_submit_gmmreg_attach_cnf(struct gprs_gmm_entity *gmme, bool accepted, uint8_t cause); +int gprs_gmm_submit_gmmsm_establish_cnf(struct gprs_gmm_entity *gmme, uint32_t sess_id, bool accepted, uint8_t cause); + #define LOGGMME(snme, level, fmt, args...) \ LOGGMM(level, "GMME(PTMSI-%08x) " fmt, \ gmme->ptmsi, \ diff --git a/include/osmocom/gprs/sm/Makefile.am b/include/osmocom/gprs/sm/Makefile.am new file mode 100644 index 0000000..46a30cb --- /dev/null +++ b/include/osmocom/gprs/sm/Makefile.am @@ -0,0 +1,13 @@ + +noinst_HEADERS = \ + sm_ms_fsm.h \ + sm_pdu.h \ + sm_private.h \ + $(NULL) + +sm_HEADERS = \ + sm.h \ + sm_prim.h \ + $(NULL) + +smdir = $(includedir)/osmocom/gprs/sm diff --git a/include/osmocom/gprs/sm/sm.h b/include/osmocom/gprs/sm/sm.h new file mode 100644 index 0000000..37f86bc --- /dev/null +++ b/include/osmocom/gprs/sm/sm.h @@ -0,0 +1,54 @@ +#pragma once + +/* GPRS Session Management (SM) definitions from 3GPP TS 24.008 */ + +#include +#include + +/* 3GPP TS 24.008 10.5.6.1 */ +#define OSMO_GPRS_SM_APN_MAXLEN 100 + +/* Max number of NSAPI */ +#define OSMO_GPRS_SM_PDP_MAXNSAPI 16 + +/* 3GPP TS 24.008 10.5.6.3 Protocol configuration options */ +#define OSMO_GPRS_SM_PCO_MAXLEN 253 + +/* 3GPP TS 24.008 10.5.6.5 Quality of service */ +#define OSMO_GPRS_SM_QOS_MAXLEN 22 + +/* 3GPP TS 24.008 10.5.6.21 NBIFOM container (T=1,L=1,V=255 => 257) */ +#define OSMO_GPRS_SM_MBIFORM_MAXLEN 255 + +/* TS 24.008 10.5.6.9 "LLC service access point identifier" */ +enum osmo_gprs_sm_llc_sapi { + OSMO_GPRS_SM_LLC_SAPI_UNASSIGNED = 0, + OSMO_GPRS_SM_LLC_SAPI_SAPI3 = 3, + OSMO_GPRS_SM_LLC_SAPI_SAPI5 = 5, + OSMO_GPRS_SM_LLC_SAPI_SAPI9 = 9, + OSMO_GPRS_SM_LLC_SAPI_SAPI11 = 11, +}; + +/* 10.5.6.4 Packet data protocol address */ +enum osmo_gprs_sm_pdp_addr_ietf_type { + OSMO_GPRS_SM_PDP_ADDR_IETF_IPV4 = 0x21, /* used in earlier version of this protocol */ + OSMO_GPRS_SM_PDP_ADDR_IETF_IPV6 = 0x57, + OSMO_GPRS_SM_PDP_ADDR_IETF_IPV4V6 = 0x8D, + /* All other values shall be interpreted as IPv4 address in this version of the protocol */ +}; + +/* Use stack as MS or as network? */ +enum osmo_gprs_sm_location { + OSMO_GPRS_SM_LOCATION_UNSET, + OSMO_GPRS_SM_LOCATION_MS, + OSMO_GPRS_SM_LOCATION_NETWORK, +}; + +int osmo_gprs_sm_init(enum osmo_gprs_sm_location location); + +enum osmo_gprs_sm_log_cat { + OSMO_GPRS_SM_LOGC_SM, + _OSMO_GPRS_SM_LOGC_MAX, +}; + +void osmo_gprs_sm_set_log_cat(enum osmo_gprs_sm_log_cat logc, int logc_num); diff --git a/include/osmocom/gprs/sm/sm_ms_fsm.h b/include/osmocom/gprs/sm/sm_ms_fsm.h new file mode 100644 index 0000000..3970d73 --- /dev/null +++ b/include/osmocom/gprs/sm/sm_ms_fsm.h @@ -0,0 +1,44 @@ +#pragma once + +#include +#include + +struct gprs_sm_entity; + +/* 3GPP TS 24.008 § 6.1.2.1 Session management states in the MS */ +enum gprs_sm_ms_fsm_states { + GPRS_SM_MS_ST_PDP_INACTIVE, /* 6.1.2.1.1 */ + GPRS_SM_MS_ST_PDP_ACTIVE_PENDING, /* 6.1.2.1.2 */ + GPRS_SM_MS_ST_PDP_ACTIVE, /* 6.1.2.1.4 */ + GPRS_SM_MS_ST_PDP_MODIFY_PENDING, /* 6.1.2.1.5 */ + GPRS_SM_MS_ST_PDP_INACTIVE_PENDING, /* 6.1.2.1.3 */ + //GPRS_SM_MS_ST_MBMS_ACTIVE_PENDING, /* 6.1.2.1.6 */ + //GPRS_SM_MS_ST_MBMS_ACTIVE, /* 6.1.2.1.7 */ +}; + +enum gprs_sm_ms_fsm_events { + GPRS_SM_MS_EV_RX_GMM_ESTABLISH_CNF, + GPRS_SM_MS_EV_RX_GMM_ESTABLISH_REJ, + GPRS_SM_MS_EV_TX_ACT_PDP_CTX_REQ, + GPRS_SM_MS_EV_RX_ACT_PDP_CTX_REJ, /* data: enum gsm48_gsm_cause *cause */ + GPRS_SM_MS_EV_RX_ACT_PDP_CTX_ACC, + GPRS_SM_MS_EV_TX_DEACT_PDP_CTX_REQ, + GPRS_SM_MS_EV_RX_DEACT_PDP_CTX_REQ, + GPRS_SM_MS_EV_RX_DEACT_PDP_CTX_ACC, + GPRS_SM_MS_EV_TX_MOD_PDP_CTX_REQ, + GPRS_SM_MS_EV_RX_MOD_PDP_CTX_REJ, + GPRS_SM_MS_EV_RX_MOD_PDP_CTX_ACC, +}; + +struct gprs_sm_ms_fsm_ctx { + struct osmo_fsm_inst *fi; + struct gprs_sm_entity *sme; + /* TS 24.008 6.1.3.1.5 retrans counter for T3380: */ + uint8_t act_pdp_ctx_attempts; +}; + +int gprs_sm_ms_fsm_init(void); +void gprs_sm_ms_fsm_set_log_cat(int logcat); + +int gprs_sm_ms_fsm_ctx_init(struct gprs_sm_ms_fsm_ctx *ctx, struct gprs_sm_entity *sme); +void gprs_sm_ms_fsm_ctx_release(struct gprs_sm_ms_fsm_ctx *ctx); diff --git a/include/osmocom/gprs/sm/sm_pdu.h b/include/osmocom/gprs/sm/sm_pdu.h new file mode 100644 index 0000000..50e3ae9 --- /dev/null +++ b/include/osmocom/gprs/sm/sm_pdu.h @@ -0,0 +1,17 @@ +/* GMM PDUs, 3GPP TS 9.5 24.008 GPRS Session Management Messages */ +#pragma once + +#include +#include + +struct gprs_gmm_entity; + +#define GPRS_SM_ALLOC_SIZE 2048 +#define GPRS_SM_ALLOC_HEADROOM 256 + +extern const struct tlv_definition gprs_sm_att_tlvdef; +#define gprs_sm_tlv_parse(dec, buf, len) \ + tlv_parse(dec, &gprs_sm_att_tlvdef, buf, len, 0, 0) + +int gprs_sm_build_act_pdp_ctx_req(struct gprs_sm_entity *sme, + struct msgb *msg); diff --git a/include/osmocom/gprs/sm/sm_prim.h b/include/osmocom/gprs/sm/sm_prim.h new file mode 100644 index 0000000..57fc32e --- /dev/null +++ b/include/osmocom/gprs/sm/sm_prim.h @@ -0,0 +1,207 @@ +#pragma once + +/* 3GPP TS 24.007: + * section 6.5 "Session Management Services for GPRS-Services" + * section 9.4 "Services provided by the LLC entity for GPRS services (GSM only)" + * section 9.5 "Services provided by the SM for GPRS services" + */ + +#include +#include + +#include +#include +#include +#include + +#include + + +struct osmo_gprs_gmm_prim; + +/* 3GPP TS 24.007 */ +enum osmo_gprs_sm_prim_sap { + OSMO_GPRS_SM_SAP_SMREG, /* 6.5.1 */ +}; +extern const struct value_string osmo_gprs_sm_prim_sap_names[]; +static inline const char *osmo_gprs_sm_prim_sap_name(enum osmo_gprs_sm_prim_sap val) +{ + return get_value_string(osmo_gprs_sm_prim_sap_names, val); +} + +/* 3GPP TS 24.007 Section 6.6 Registration Services for GPRS-Services */ +enum osmo_gprs_sm_smreg_prim_type { + OSMO_GPRS_SM_SMREG_PDP_ACTIVATE, /* Req/Cnf/Rej/Ind/Rsp */ + OSMO_GPRS_SM_SMREG_PDP_DEACTIVATE, /* Req/Cnf/Ind */ + OSMO_GPRS_SM_SMREG_PDP_MODIFY, /* Req/Ind/Cnf/Rej */ + OSMO_GPRS_SM_SMREG_PDP_ACTIVATE_SEC, /* Req/Cnf/Rej */ + OSMO_GPRS_SM_SMREG_MBMS_ACTIVATE, /* Req/Cnf/Rej/Ind */ +}; +extern const struct value_string osmo_gprs_sm_smreg_prim_type_names[]; +static inline const char *osmo_gprs_sm_smreg_prim_type_name(enum osmo_gprs_sm_smreg_prim_type val) +{ + return get_value_string(osmo_gprs_sm_smreg_prim_type_names, val); +} + +/* Parameters for OSMO_GPRS_SM_SMREG_* prims */ +struct osmo_gprs_sm_smreg_prim { + /* Common fields */ + uint32_t ms_id; + /* Specific fields */ + union { + /* OSMO_GPRS_SM_SMREG_PDP_ACTIVATE | Req, 6.5.1.1 */ + struct { + uint8_t nsapi; + enum osmo_gprs_sm_llc_sapi llc_sapi; + enum osmo_gprs_sm_pdp_addr_ietf_type pdp_addr_ietf_type; + struct osmo_sockaddr pdp_addr_v4; + struct osmo_sockaddr pdp_addr_v6; + uint8_t qos[OSMO_GPRS_SM_QOS_MAXLEN]; + uint8_t qos_len; + char apn[OSMO_GPRS_SM_APN_MAXLEN]; + uint8_t pco[OSMO_GPRS_SM_PCO_MAXLEN]; + uint8_t pco_len; + struct { + uint32_t ptmsi; + bool attach_with_imsi; + char imsi[OSMO_IMSI_BUF_SIZE]; + char imei[GSM23003_IMEI_NUM_DIGITS + 1]; + char imeisv[GSM23003_IMEISV_NUM_DIGITS+1]; + } gmm; + } pdp_act_req; + + /* OSMO_GPRS_SM_SMREG_PDP_ACTIVATE | Cnf 6.5.1.2 / Rej 6.5.1.3 */ + struct { + bool accepted; + uint8_t nsapi; + uint8_t pco[OSMO_GPRS_SM_PCO_MAXLEN]; + uint8_t pco_len; + union { + struct { + struct osmo_sockaddr pdp_addr; + uint8_t qos[OSMO_GPRS_SM_QOS_MAXLEN]; + uint8_t qos_len; + } acc; + struct { + uint8_t cause; + } rej; + }; + } pdp_act_cnf; + + /* OSMO_GPRS_SM_SMREG_PDP_ACTIVATE | Ind, 6.5.1.4 */ + struct { + struct osmo_sockaddr pdp_addr; + char apn[OSMO_GPRS_SM_APN_MAXLEN]; + uint8_t pco[OSMO_GPRS_SM_PCO_MAXLEN]; + uint8_t pco_len; + } pdp_act_ind; + + /* OSMO_GPRS_SM_SMREG_PDP_ACTIVATE | Rsp, 6.5.1.14 */ + struct { + uint8_t cause; + struct osmo_sockaddr pdp_addr; + char apn[OSMO_GPRS_SM_APN_MAXLEN]; + uint8_t pco[OSMO_GPRS_SM_PCO_MAXLEN]; + uint8_t pco_len; + /* TODO: MBMS protocol configuration options*/ + } pdp_act_rej_rsp; + + /* OSMO_GPRS_SM_SMREG_PDP_DEACTIVATE, 6.5.1.5 */ + struct { + uint8_t nsapi[OSMO_GPRS_SM_PDP_MAXNSAPI]; + uint8_t num_nsapi; + uint8_t tear_down_ind; + uint8_t cause; + uint8_t pco[OSMO_GPRS_SM_PCO_MAXLEN]; + uint8_t pco_len; + /* TODO: MBMS protocol configuration options*/ + } deact_req; + + + /* OSMO_GPRS_SM_SMREG_PDP_DEACTIVATE | Cnf, 6.5.1.6 */ + struct { + uint8_t nsapi[OSMO_GPRS_SM_PDP_MAXNSAPI]; + uint8_t num_nsapi; + uint8_t pco[OSMO_GPRS_SM_PCO_MAXLEN]; + uint8_t pco_len; + /* TODO: MBMS protocol configuration options*/ + } deact_cnf; + + /* OSMO_GPRS_SM_SMREG_PDP_DEACTIVATE | Ind, 6.5.1.7 */ + struct { + uint8_t nsapi[OSMO_GPRS_SM_PDP_MAXNSAPI]; + uint8_t num_nsapi; + uint8_t tear_down_ind; + uint8_t cause; + uint8_t pco[OSMO_GPRS_SM_PCO_MAXLEN]; + uint8_t pco_len; + /* TODO: MBMS protocol configuration options */ + } deact_ind; + + /* OSMO_GPRS_SM_SMREG_PDP_MODIFY | Ind, 6.5.1.8 */ + struct { + uint8_t qos[OSMO_GPRS_SM_QOS_MAXLEN]; + uint8_t qos_len; + uint8_t nsapi; + uint8_t pco[OSMO_GPRS_SM_PCO_MAXLEN]; + uint8_t pco_len; + } pdp_mod_ind; + + /* OSMO_GPRS_SM_SMREG_PDP_MODIFY | Req 6.5.1.18 */ + struct { + uint8_t qos[OSMO_GPRS_SM_QOS_MAXLEN]; + uint8_t qos_len; + uint8_t nsapi; + uint8_t tft; /* TODO */ + uint8_t pco[OSMO_GPRS_SM_PCO_MAXLEN]; + uint8_t pco_len; + } pdp_mod_req; + + /* OSMO_GPRS_SM_SMREG_PDP_MODIFY | Cnf 6.5.1.19 / Rej 6.5.1.20 */ + struct { + bool accepted; + uint8_t nsapi; + uint8_t pco[OSMO_GPRS_SM_PCO_MAXLEN]; + union { + struct { + uint8_t qos[OSMO_GPRS_SM_QOS_MAXLEN]; + uint8_t qos_len; + } acc; + struct { + uint8_t cause; + } rej; + }; + } pdp_mod_cnf; + + /* TODO: + * OSMO_GPRS_SM_SMREG_PDP_ACTIVATE_SEC + * OSMO_GPRS_SM_SMREG_MBMS_ACTIVATE + */ + }; +}; + + +struct osmo_gprs_sm_prim { + struct osmo_prim_hdr oph; + union { + struct osmo_gprs_sm_smreg_prim smreg; + }; +}; + +typedef int (*osmo_gprs_sm_prim_up_cb)(struct osmo_gprs_sm_prim *sm_prim, void *up_user_data); +void osmo_gprs_sm_prim_set_up_cb(osmo_gprs_sm_prim_up_cb up_cb, void *up_user_data); + +typedef int (*osmo_gprs_sm_prim_down_cb)(struct osmo_gprs_sm_prim *sm_prim, void *down_user_data); +void osmo_gprs_sm_prim_set_down_cb(osmo_gprs_sm_prim_down_cb down_cb, void *down_user_data); + +typedef int (*osmo_gprs_sm_prim_gmm_down_cb)(struct osmo_gprs_gmm_prim *gmm_prim, void *gmm_down_user_data); +void osmo_gprs_sm_prim_set_gmm_down_cb(osmo_gprs_sm_prim_gmm_down_cb gmm_down_cb, void *gmm_down_user_data); + +int osmo_gprs_sm_prim_upper_down(struct osmo_gprs_sm_prim *sm_prim); +int osmo_gprs_sm_prim_lower_up(struct osmo_gprs_sm_prim *sm_prim); +int osmo_gprs_sm_prim_gmm_lower_up(struct osmo_gprs_gmm_prim *gmm_prim); + +const char *osmo_gprs_sm_prim_name(const struct osmo_gprs_sm_prim *sm_prim); + +/* Alloc primitive for SMREG SAP: */ +struct osmo_gprs_sm_prim *osmo_gprs_sm_prim_alloc_smreg_pdp_act_req(void); diff --git a/include/osmocom/gprs/sm/sm_private.h b/include/osmocom/gprs/sm/sm_private.h new file mode 100644 index 0000000..b0043e6 --- /dev/null +++ b/include/osmocom/gprs/sm/sm_private.h @@ -0,0 +1,180 @@ +#pragma once + +/* 3GPP TS 24.007, private header */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +extern int g_sm_log_cat[_OSMO_GPRS_SM_LOGC_MAX]; + +#define LOGSM(lvl, fmt, args...) LOGP(g_sm_log_cat[OSMO_GPRS_SM_LOGC_SM], lvl, fmt, ## args) + +#define msgb_sm_prim(msg) ((struct osmo_gprs_sm_prim *)(msg)->l1h) + +/* 10.5.6.4 Packet data protocol address */ +enum gprs_sm_pdp_addr_org { + GPRS_SM_PDP_ADDR_ORG_ETSI = 0x00, + GPRS_SM_PDP_ADDR_ORG_IETF = 0x01, + GPRS_SM_PDP_ADDR_ORG_EMPTY = 0x0f, + /* All other values are reserved. */ +}; + +/* 10.5.6.4 Packet data protocol address */ +enum gprs_sm_pdp_addr_etsi_type { + GPRS_SM_PDP_ADDR_ETSI_RESERVED = 00, /* used in earlier version of this protocol */ + GPRS_SM_PDP_ADDR_ETSI_PPP = 0x01, + GPRS_SM_PDP_ADDR_ETSI_NON_IP = 0x02, + /* All other values are reserved in this version of the protocol. */ +}; + +struct gprs_sm_pdp_addr { +#if OSMO_IS_LITTLE_ENDIAN + uint8_t spare:4, + organization:4; /* enum gprs_sm_pdp_addr_org */ + uint8_t type; /* osmo_gprs_sm_pdp_addr_{etsi,ietf}_type */ + union { + /* IPv4 */ + uint32_t addr; + + /* IPv6 */ + uint8_t addr6[16]; + + /* IPv4v6 */ +#elif OSMO_IS_BIG_ENDIAN +/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */ + uint8_t organization:4, spare:4; + uint8_t type; + union { + uint32_t addr; + uint8_t addr6[16]; +#endif + struct { + uint32_t addr; + uint8_t addr6[16]; + } __attribute__ ((packed)) both; + }; +} __attribute__ ((packed)); + +struct gprs_sm_ms; + +struct gprs_sm_ctx { + enum osmo_gprs_sm_location location; + osmo_gprs_sm_prim_up_cb sm_up_cb; + void *sm_up_cb_user_data; + + osmo_gprs_sm_prim_down_cb sm_down_cb; + void *sm_down_cb_user_data; + + osmo_gprs_sm_prim_gmm_down_cb sm_gmm_down_cb; + void *sm_gmm_down_cb_user_data; + + struct osmo_tdef *T_defs; /* timers controlled by SM layer */ + + struct llist_head ms_list; /* list of struct gprs_sm_ms->list */ + + uint32_t next_sess_id; +}; + +extern struct gprs_sm_ctx *g_ctx; + +/* SM Entity, PDP CTX */ +struct gprs_sm_entity { + struct gprs_sm_ms *ms; /* backpointer */ + + uint32_t sess_id; + + uint8_t nsapi; + enum osmo_gprs_sm_llc_sapi llc_sapi; + + enum osmo_gprs_sm_pdp_addr_ietf_type pdp_addr_ietf_type; + struct osmo_sockaddr pdp_addr_v4; + struct osmo_sockaddr pdp_addr_v6; + + uint8_t qos[OSMO_GPRS_SM_QOS_MAXLEN]; + uint8_t qos_len; + + char apn[OSMO_GPRS_SM_APN_MAXLEN]; + + uint8_t pco[OSMO_GPRS_SM_MBIFORM_MAXLEN]; + uint8_t pco_len; + + uint8_t ti; /* transaction identifier */ + + struct gprs_sm_ms_fsm_ctx ms_fsm; +}; + +/* Mobile Station: */ +struct gprs_sm_ms { + struct llist_head list; /* item in (struct gprs_sm_ctx)->ms_list */ + + uint32_t ms_id; + + struct gprs_sm_entity *pdp[OSMO_GPRS_SM_PDP_MAXNSAPI]; + + struct { + uint32_t ptmsi; + char imsi[OSMO_IMSI_BUF_SIZE]; + char imei[GSM23003_IMEI_NUM_DIGITS + 1]; + char imeisv[GSM23003_IMEISV_NUM_DIGITS+1]; + } gmm; +}; + +/* 10.5.6.2 Network service access point identifier */ +static inline bool gprs_sm_nsapi_is_valid(uint8_t nsapi) +{ + return nsapi >= 5 && nsapi <= 15; +} + +static inline struct gprs_sm_entity *gprs_sm_ms_get_pdp_ctx(struct gprs_sm_ms *ms, + uint8_t nsapi) { + OSMO_ASSERT(gprs_sm_nsapi_is_valid(nsapi)); + return ms->pdp[nsapi]; +} + +/* sm_prim.c: */ +int gprs_sm_prim_call_up_cb(struct osmo_gprs_sm_prim *sm_prim); +int gprs_sm_prim_call_down_cb(struct osmo_gprs_sm_prim *sm_prim); +int gprs_sm_prim_call_gmm_down_cb(struct osmo_gprs_gmm_prim *gmm_prim); + +struct osmo_gprs_sm_prim *gprs_sm_prim_alloc_smreg_pdp_act_cnf(void); +struct osmo_gprs_sm_prim *gprs_sm_prim_alloc_smreg_pdp_act_ind(void); + +/* sm.c: */ +struct gprs_sm_ms *gprs_sm_ms_alloc(uint32_t ms_id); +void gprs_sm_ms_free(struct gprs_sm_ms *ms); +struct gprs_sm_ms *gprs_sm_find_ms_by_id(uint32_t ms_id); + +struct gprs_sm_entity *gprs_sm_entity_alloc(struct gprs_sm_ms *ms, uint32_t nsapi); +void gprs_sm_entity_free(struct gprs_sm_entity *sme); +struct gprs_sm_entity *gprs_sm_find_sme_by_sess_id(uint32_t sess_id); + +int gprs_sm_submit_gmmsm_assign_req(const struct gprs_sm_entity *sme); +int gprs_sm_submit_smreg_pdp_act_cnf(const struct gprs_sm_entity *sme, enum gsm48_gsm_cause cause); +int gprs_sm_tx_act_pdp_ctx_req(struct gprs_sm_entity *sme); +int gprs_sm_rx(struct gprs_sm_entity *sme, struct gsm48_hdr *gh, unsigned int len); + + +#define LOGMS(snme, level, fmt, args...) \ + LOGSM(level, "MS(ID-%u) " fmt, \ + ms->ms_id, \ + ## args) + +#define LOGSME(sme, level, fmt, args...) \ + LOGSM(level, "PDP(ID-%u:NSAPI-%u) " fmt, \ + sme->ms->ms_id, \ + sme->nsapi, \ + ## args) diff --git a/libosmo-gprs-sm.pc.in b/libosmo-gprs-sm.pc.in new file mode 100644 index 0000000..85158f1 --- /dev/null +++ b/libosmo-gprs-sm.pc.in @@ -0,0 +1,12 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: Osmocom [E]GPRS SM (Session Management) Library +Description: C Utility Library +Version: @VERSION@ +Requires: libosmo-gprs-gmm libosmocore +Libs: -L${libdir} -losmo-gprs-sm +Libs.private: -ltalloc +Cflags: -I${includedir}/ diff --git a/src/Makefile.am b/src/Makefile.am index b0b95ff..69ce5a0 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -4,4 +4,5 @@ SUBDIRS = \ rlcmac \ sndcp \ gmm \ + sm \ $(NULL) diff --git a/src/gmm/gmm.c b/src/gmm/gmm.c index a8b8362..ed7b69f 100644 --- a/src/gmm/gmm.c +++ b/src/gmm/gmm.c @@ -176,7 +176,7 @@ struct gprs_gmm_entity *gprs_gmm_find_gmme_by_tlli(uint32_t tlli) return NULL; } -static int gprs_gmm_submit_gmmreg_attach_cnf(struct gprs_gmm_entity *gmme, bool accepted, uint8_t cause) +int gprs_gmm_submit_gmmreg_attach_cnf(struct gprs_gmm_entity *gmme, bool accepted, uint8_t cause) { struct osmo_gprs_gmm_prim *gmm_prim_tx; int rc; @@ -196,7 +196,18 @@ static int gprs_gmm_submit_gmmreg_detach_cnf(struct gprs_gmm_entity *gmme) int rc; gmm_prim_tx = gprs_gmm_prim_alloc_gmmreg_detach_cnf(); - gmm_prim_tx->gmmreg.detach_cnf.detach_type = gmme->ms_fsm.detach_type; + gmm_prim_tx->gmmreg.detach_cnf.detach_type = gmme->ms_fsm.detach.type; + + rc = gprs_gmm_prim_call_up_cb(gmm_prim_tx); + return rc; +} + +int gprs_gmm_submit_gmmsm_establish_cnf(struct gprs_gmm_entity *gmme, uint32_t sess_id, bool accepted, uint8_t cause) +{ + struct osmo_gprs_gmm_prim *gmm_prim_tx; + int rc; + + gmm_prim_tx = gprs_gmm_prim_alloc_gmmsm_establish_cnf(sess_id, cause); rc = gprs_gmm_prim_call_up_cb(gmm_prim_tx); return rc; @@ -435,11 +446,6 @@ static int gprs_gmm_rx_att_ack(struct gprs_gmm_entity *gmme, struct gsm48_hdr *g } } - /* Submit GMMREG-ATTACH-CNF as per TS 24.007 Annex C.1 */ - rc = gprs_gmm_submit_gmmreg_attach_cnf(gmme, true, 0); - if (rc < 0) - goto rejected; - /* Submit LLGMM-ASSIGN-REQ as per TS 24.007 Annex C.1 */ rc = gprs_gmm_submit_llgmm_assing_req(gmme); if (rc < 0) diff --git a/src/gmm/gmm_ms_fsm.c b/src/gmm/gmm_ms_fsm.c index 423d1d0..cdc2089 100644 --- a/src/gmm/gmm_ms_fsm.c +++ b/src/gmm/gmm_ms_fsm.c @@ -44,13 +44,28 @@ static void st_gmm_ms_null(struct osmo_fsm_inst *fi, uint32_t event, void *data) } } +static void st_gmm_ms_deregistered_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state) +{ + struct gprs_gmm_ms_fsm_ctx *ctx = (struct gprs_gmm_ms_fsm_ctx *)fi->priv; + + memset(&ctx->attach, 0, sizeof(ctx->attach)); +} + static void st_gmm_ms_deregistered(struct osmo_fsm_inst *fi, uint32_t event, void *data) { + struct gprs_gmm_ms_fsm_ctx *ctx = (struct gprs_gmm_ms_fsm_ctx *)fi->priv; + int rc; + switch (event) { case GPRS_GMM_MS_EV_DISABLE_GPRS_MODE: gmm_ms_fsm_state_chg(fi, GPRS_GMM_MS_ST_NULL); break; case GPRS_GMM_MS_EV_ATTACH_REQUESTED: + rc = gprs_gmm_tx_att_req(ctx->gmme, + ctx->attach.type, + ctx->attach.with_imsi); + if (rc < 0) + return; gmm_ms_fsm_state_chg(fi, GPRS_GMM_MS_ST_REGISTERED_INITIATED); break; default: @@ -60,12 +75,36 @@ static void st_gmm_ms_deregistered(struct osmo_fsm_inst *fi, uint32_t event, voi static void st_gmm_ms_registered_initiated(struct osmo_fsm_inst *fi, uint32_t event, void *data) { + struct gprs_gmm_ms_fsm_ctx *ctx = (struct gprs_gmm_ms_fsm_ctx *)fi->priv; + int rc; + switch (event) { + case GPRS_GMM_MS_EV_ATTACH_REQUESTED: + /* Upper layers request us to retry attaching: */ + rc = gprs_gmm_tx_att_req(ctx->gmme, + ctx->attach.type, + ctx->attach.with_imsi); + break; case GPRS_GMM_MS_EV_ATTACH_REJECTED: case GPRS_GMM_MS_EV_LOW_LVL_FAIL: gmm_ms_fsm_state_chg(fi, GPRS_GMM_MS_ST_DEREGISTERED); break; case GPRS_GMM_MS_EV_ATTACH_ACCEPTED: + if (ctx->attach.explicit_att) { + /* Submit GMMREG-ATTACH-CNF as per TS 24.007 Annex C.1 */ + rc = gprs_gmm_submit_gmmreg_attach_cnf(ctx->gmme, true, 0); + if (rc < 0) + return; + } + if (ctx->attach.implicit_att) { + /* Submit GMMSM-ESTABLISH-CNF as per TS 24.007 Annex C.3 */ + unsigned int i; + for (i = 0; i < ctx->attach.num_sess_id; i++) { + rc = gprs_gmm_submit_gmmsm_establish_cnf(ctx->gmme, ctx->attach.sess_id[i], true, 0); + if (rc < 0) + return; + } + } gmm_ms_fsm_state_chg(fi, GPRS_GMM_MS_ST_REGISTERED); break; case GPRS_GMM_MS_EV_DETACH_REQUESTED: @@ -78,7 +117,6 @@ static void st_gmm_ms_registered_initiated(struct osmo_fsm_inst *fi, uint32_t ev static void st_gmm_ms_registered(struct osmo_fsm_inst *fi, uint32_t event, void *data) { - struct gprs_gmm_ms_fsm_ctx *ctx = (struct gprs_gmm_ms_fsm_ctx *)fi->priv; switch (event) { case GPRS_GMM_MS_EV_SR_REQUESTED: gmm_ms_fsm_state_chg(fi, GPRS_GMM_MS_ST_REGISTERED); @@ -87,7 +125,6 @@ static void st_gmm_ms_registered(struct osmo_fsm_inst *fi, uint32_t event, void gmm_ms_fsm_state_chg(fi, GPRS_GMM_MS_ST_RAU_INITIATED); break; case GPRS_GMM_MS_EV_DETACH_REQUESTED: - ctx->detach_type = *((enum osmo_gprs_gmm_detach_ms_type *)data); gmm_ms_fsm_state_chg(fi, GPRS_GMM_MS_ST_DEREGISTERED_INITIATED); break; default: @@ -170,12 +207,14 @@ static struct osmo_fsm_state gmm_ms_fsm_states[] = { X(GPRS_GMM_MS_ST_REGISTERED_INITIATED)| X(GPRS_GMM_MS_ST_DEREGISTERED), .name = "Deregistered", + .onenter = st_gmm_ms_deregistered_on_enter, .action = st_gmm_ms_deregistered, }, [GPRS_GMM_MS_ST_REGISTERED_INITIATED] = { .in_event_mask = X(GPRS_GMM_MS_EV_ATTACH_REJECTED) | X(GPRS_GMM_MS_EV_LOW_LVL_FAIL) | + X(GPRS_GMM_MS_EV_ATTACH_REQUESTED) | X(GPRS_GMM_MS_EV_ATTACH_ACCEPTED) | X(GPRS_GMM_MS_EV_DETACH_REQUESTED), .out_state_mask = @@ -294,3 +333,65 @@ void gprs_gmm_ms_fsm_ctx_release(struct gprs_gmm_ms_fsm_ctx *ctx) { osmo_fsm_inst_free(ctx->fi); } + +int gprs_gmm_ms_fsm_ctx_request_attach(struct gprs_gmm_ms_fsm_ctx *ctx, + enum osmo_gprs_gmm_attach_type attach_type, + bool attach_with_imsi, + bool explicit_attach, + uint32_t sess_id) +{ + int rc; + + ctx->attach.type = attach_type; + ctx->attach.with_imsi = attach_with_imsi; + if (explicit_attach) + ctx->attach.explicit_att = true; + else + ctx->attach.implicit_att = true; + + if (!explicit_attach) { + unsigned int i; + bool found = false; + if (ctx->attach.num_sess_id == ARRAY_SIZE(ctx->attach.sess_id)) + return -ENOMEM; + for (i = 0; i < ctx->attach.num_sess_id; i++) { + if (sess_id == ctx->attach.sess_id[i]) { + found = true; + break; + } + } + if (!found) { + ctx->attach.sess_id[ctx->attach.num_sess_id] = sess_id; + ctx->attach.num_sess_id++; + } + } + + rc = osmo_fsm_inst_dispatch(ctx->fi, GPRS_GMM_MS_EV_ATTACH_REQUESTED, NULL); + return rc; +} + +int gprs_gmm_ms_fsm_ctx_request_detach(struct gprs_gmm_ms_fsm_ctx *ctx, + enum osmo_gprs_gmm_detach_ms_type detach_type, + enum osmo_gprs_gmm_detach_poweroff_type poweroff_type) +{ + int rc; + + ctx->detach.type = detach_type; + ctx->detach.poweroff_type = poweroff_type; + + switch (poweroff_type) { + case OSMO_GPRS_GMM_DETACH_POWEROFF_TYPE_NORMAL: + /* C.3 MS initiated DETACH, GPRS only */ + rc = osmo_fsm_inst_dispatch(ctx->fi, GPRS_GMM_MS_EV_DETACH_REQUESTED, NULL); + break; + case OSMO_GPRS_GMM_DETACH_POWEROFF_TYPE_POWEROFF: + /* C.4 POWER-OFF DETACH, GPRS only */ + rc = osmo_fsm_inst_dispatch(ctx->fi, GPRS_GMM_MS_EV_DETACH_REQUESTED_POWEROFF, NULL); + break; + default: + OSMO_ASSERT(0); + } + + rc = gprs_gmm_tx_detach_req(ctx->gmme, detach_type, poweroff_type); + return rc; +} diff --git a/src/gmm/gmm_prim.c b/src/gmm/gmm_prim.c index c4de625..ce7afb2 100644 --- a/src/gmm/gmm_prim.c +++ b/src/gmm/gmm_prim.c @@ -271,45 +271,51 @@ static inline struct osmo_gprs_gmm_prim *gmm_prim_gmmsm_alloc(enum osmo_gprs_gmm } /* 3GPP TS 24.007 9.5.1.1 GMMSM-ESTABLISH-REQ:*/ -struct osmo_gprs_gmm_prim *osmo_gprs_gmm_prim_alloc_gmmsm_establish_req(void) +struct osmo_gprs_gmm_prim *osmo_gprs_gmm_prim_alloc_gmmsm_establish_req(uint32_t id) { struct osmo_gprs_gmm_prim *gmm_prim; gmm_prim = gmm_prim_gmmsm_alloc(OSMO_GPRS_GMM_GMMSM_ESTABLISH, PRIM_OP_REQUEST, 0); + gmm_prim->gmmsm.sess_id = id; return gmm_prim; } /* 3GPP TS 24.007 9.5.1.2 GMMSM-ESTABLISH-CNF:*/ -struct osmo_gprs_gmm_prim *gprs_gmm_prim_alloc_gmmsm_establish_cnf(uint8_t cause) +struct osmo_gprs_gmm_prim *gprs_gmm_prim_alloc_gmmsm_establish_cnf(uint32_t id, uint8_t cause) { struct osmo_gprs_gmm_prim *gmm_prim; gmm_prim = gmm_prim_gmmsm_alloc(OSMO_GPRS_GMM_GMMSM_ESTABLISH, PRIM_OP_CONFIRM, 0); - gmm_prim->gmmsm.establish_cnf.cause = cause; + gmm_prim->gmmsm.sess_id = id; + gmm_prim->gmmsm.establish_cnf.accepted = (cause == 0); + gmm_prim->gmmsm.establish_cnf.rej.cause = cause; return gmm_prim; } /* 3GPP TS 24.007 9.5.1.4 GMMSM-RELEASE-IND:*/ -struct osmo_gprs_gmm_prim *gprs_gmm_prim_alloc_gmmsm_release_ind(void) +struct osmo_gprs_gmm_prim *gprs_gmm_prim_alloc_gmmsm_release_ind(uint32_t id) { struct osmo_gprs_gmm_prim *gmm_prim; gmm_prim = gmm_prim_gmmsm_alloc(OSMO_GPRS_GMM_GMMSM_RELEASE, PRIM_OP_INDICATION, 0); + gmm_prim->gmmsm.sess_id = id; return gmm_prim; } -/* 3GPP TS 24.007 9.5.1.5 GMMRSM-UNITDATA-REQ:*/ -struct osmo_gprs_gmm_prim *osmo_gprs_gmm_prim_alloc_gmmsm_unitdata_req(uint8_t *smpdu, unsigned int smpdu_len) +/* 3GPP TS 24.007 9.5.1.5 GMMSM-UNITDATA-REQ:*/ +struct osmo_gprs_gmm_prim *osmo_gprs_gmm_prim_alloc_gmmsm_unitdata_req(uint32_t id, uint8_t *smpdu, unsigned int smpdu_len) { struct osmo_gprs_gmm_prim *gmm_prim; gmm_prim = gmm_prim_gmmsm_alloc(OSMO_GPRS_GMM_GMMSM_UNITDATA, PRIM_OP_REQUEST, smpdu_len); + gmm_prim->gmmsm.sess_id = id; gmm_prim->gmmsm.unitdata_req.smpdu = smpdu; gmm_prim->gmmsm.unitdata_req.smpdu_len = smpdu_len; return gmm_prim; } -/* 3GPP TS 24.007 9.5.1.6 GMMRSM-UNITDATA-IND:*/ -struct osmo_gprs_gmm_prim *gprs_gmm_prim_alloc_gmmsm_unitdata_ind(uint8_t *smpdu, unsigned int smpdu_len) +/* 3GPP TS 24.007 9.5.1.6 GMMSM-UNITDATA-IND:*/ +struct osmo_gprs_gmm_prim *gprs_gmm_prim_alloc_gmmsm_unitdata_ind(uint32_t id, uint8_t *smpdu, unsigned int smpdu_len) { struct osmo_gprs_gmm_prim *gmm_prim; gmm_prim = gmm_prim_gmmsm_alloc(OSMO_GPRS_GMM_GMMSM_UNITDATA, PRIM_OP_INDICATION, smpdu_len); + gmm_prim->gmmsm.sess_id = id; gmm_prim->gmmsm.unitdata_ind.smpdu = smpdu; gmm_prim->gmmsm.unitdata_ind.smpdu_len = smpdu_len; return gmm_prim; @@ -363,15 +369,10 @@ static int gprs_gmm_prim_handle_gmmreg_attach_req(struct osmo_gprs_gmm_prim *gmm if (gmm_prim->gmmreg.attach_req.imeisv[0] != '\0') OSMO_STRLCPY_ARRAY(gmme->imeisv, gmm_prim->gmmreg.attach_req.imeisv); - rc = gprs_gmm_tx_att_req(gmme, - gmm_prim->gmmreg.attach_req.attach_type, - gmm_prim->gmmreg.attach_req.attach_with_imsi); - - if (rc < 0) - return rc; - - rc = osmo_fsm_inst_dispatch(gmme->ms_fsm.fi, GPRS_GMM_MS_EV_ATTACH_REQUESTED, NULL); - + rc = gprs_gmm_ms_fsm_ctx_request_attach(&gmme->ms_fsm, + gmm_prim->gmmreg.attach_req.attach_type, + gmm_prim->gmmreg.attach_req.attach_with_imsi, + true, 0); return rc; } @@ -387,23 +388,9 @@ static int gprs_gmm_prim_handle_gmmreg_detach_req(struct osmo_gprs_gmm_prim *gmm return -EINVAL; } - switch (gmm_prim->gmmreg.detach_req.poweroff_type) { - case OSMO_GPRS_GMM_DETACH_POWEROFF_TYPE_NORMAL: - /* C.3 MS initiated DETACH, GPRS only */ - rc = osmo_fsm_inst_dispatch(gmme->ms_fsm.fi, GPRS_GMM_MS_EV_DETACH_REQUESTED, - &gmm_prim->gmmreg.detach_req.detach_type); - break; - case OSMO_GPRS_GMM_DETACH_POWEROFF_TYPE_POWEROFF: - /* C.4 POWER-OFF DETACH, GPRS only */ - rc = osmo_fsm_inst_dispatch(gmme->ms_fsm.fi, GPRS_GMM_MS_EV_DETACH_REQUESTED_POWEROFF, NULL); - break; - default: - OSMO_ASSERT(0); - } - - rc = gprs_gmm_tx_detach_req(gmme, - gmm_prim->gmmreg.detach_req.detach_type, - gmm_prim->gmmreg.detach_req.poweroff_type); + rc = gprs_gmm_ms_fsm_ctx_request_detach(&gmme->ms_fsm, + gmm_prim->gmmreg.detach_req.detach_type, + gmm_prim->gmmreg.detach_req.poweroff_type); return rc; } @@ -428,9 +415,26 @@ static int gprs_gmm_prim_handle_gmmreg(struct osmo_gprs_gmm_prim *gmm_prim) static int gprs_gmm_prim_handle_gmmsm_establish_req(struct osmo_gprs_gmm_prim *gmm_prim) { int rc; + struct gprs_gmm_entity *gmme; - rc = gprs_gmm_prim_handle_unsupported(gmm_prim); + gmme = llist_first_entry_or_null(&g_ctx->gmme_list, struct gprs_gmm_entity, list); + if (!gmme) { + gmme = gprs_gmm_gmme_alloc(); + OSMO_ASSERT(gmme); + } + gmme->ptmsi = gmm_prim->gmmsm.establish_req.ptmsi; + if (gmm_prim->gmmsm.establish_req.imsi[0] != '\0') + OSMO_STRLCPY_ARRAY(gmme->imsi, gmm_prim->gmmsm.establish_req.imsi); + if (gmm_prim->gmmsm.establish_req.imei[0] != '\0') + OSMO_STRLCPY_ARRAY(gmme->imei, gmm_prim->gmmsm.establish_req.imei); + if (gmm_prim->gmmsm.establish_req.imeisv[0] != '\0') + OSMO_STRLCPY_ARRAY(gmme->imeisv, gmm_prim->gmmsm.establish_req.imeisv); + rc = gprs_gmm_ms_fsm_ctx_request_attach(&gmme->ms_fsm, + gmm_prim->gmmsm.establish_req.attach_type, + gmm_prim->gmmsm.establish_req.attach_with_imsi, + false, + gmm_prim->gmmsm.sess_id); return rc; } diff --git a/src/sm/Makefile.am b/src/sm/Makefile.am new file mode 100644 index 0000000..8fb9090 --- /dev/null +++ b/src/sm/Makefile.am @@ -0,0 +1,40 @@ +# This is _NOT_ the library release version, it's an API version. +# Please read Chapter 6 "Library interface versions" of the libtool +# documentation before making any modification +LIBVERSION=0:0:0 + +AM_CPPFLAGS = \ + $(all_includes) \ + -I$(top_srcdir)/include \ + $(NULL) + +AM_CFLAGS = \ + -Wall \ + $(LIBOSMOGSM_CFLAGS) \ + $(LIBOSMOCORE_CFLAGS) \ + $(NULL) + +lib_LTLIBRARIES = \ + libosmo-gprs-sm.la \ + $(NULL) + +libosmo_gprs_sm_la_SOURCES = \ + sm.c \ + sm_ms_fsm.c \ + sm_pdu.c \ + sm_prim.c \ + misc.c \ + $(NULL) + +libosmo_gprs_sm_la_LDFLAGS = \ + -export-symbols-regex '^osmo_' \ + -version-info $(LIBVERSION) \ + -no-undefined \ + $(NULL) + +libosmo_gprs_sm_la_LIBADD = \ + $(top_builddir)/src/gmm/libosmo-gprs-gmm.la \ + $(LIBOSMOCORE_LIBS) \ + $(LIBOSMOGSM_LIBS) \ + -lm \ + $(NULL) diff --git a/src/sm/misc.c b/src/sm/misc.c new file mode 100644 index 0000000..10cb0dd --- /dev/null +++ b/src/sm/misc.c @@ -0,0 +1,34 @@ +/* + * (C) 2023 by sysmocom - s.f.m.c. GmbH + * + * 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 . + * + */ + +#include +#include +#include +#include + +int g_sm_log_cat[_OSMO_GPRS_SM_LOGC_MAX] = { [0 ... _OSMO_GPRS_SM_LOGC_MAX - 1] = DLGLOBAL }; + +void osmo_gprs_sm_set_log_cat(enum osmo_gprs_sm_log_cat logc, int logc_num) +{ + OSMO_ASSERT(logc < _OSMO_GPRS_SM_LOGC_MAX); + g_sm_log_cat[logc] = logc_num; + + gprs_sm_ms_fsm_set_log_cat(logc_num); +} diff --git a/src/sm/sm.c b/src/sm/sm.c new file mode 100644 index 0000000..59a5e7b --- /dev/null +++ b/src/sm/sm.c @@ -0,0 +1,304 @@ +/* GPRS SM as per 3GPP TS 24.008, TS 24.007 */ +/* + * (C) 2023 by sysmocom - s.f.m.c. GmbH + * + * 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 . + * + */ + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +struct gprs_sm_ctx *g_ctx; + +/* TS 24.008 */ +static struct osmo_tdef T_defs_sm[] = { + { .T=3380, .default_val=30, .desc = "" }, + { 0 } /* empty item at the end */ +}; + +static void gprs_sm_ctx_free(void) +{ + struct gprs_sm_ms *ms; + + while ((ms = llist_first_entry_or_null(&g_ctx->ms_list, struct gprs_sm_ms, list))) + gprs_sm_ms_free(ms); + + talloc_free(g_ctx); +} + +int osmo_gprs_sm_init(enum osmo_gprs_sm_location location) +{ + bool first_init = true; + int rc; + OSMO_ASSERT(location == OSMO_GPRS_SM_LOCATION_MS || location == OSMO_GPRS_SM_LOCATION_NETWORK) + + if (g_ctx) { + first_init = false; + gprs_sm_ctx_free(); + } + + g_ctx = talloc_zero(NULL, struct gprs_sm_ctx); + g_ctx->location = location; + g_ctx->T_defs = T_defs_sm; + INIT_LLIST_HEAD(&g_ctx->ms_list); + + osmo_tdefs_reset(g_ctx->T_defs); + + if (first_init) { + rc = gprs_sm_ms_fsm_init(); + if (rc != 0) { + TALLOC_FREE(g_ctx); + return rc; + } + } + return 0; +} + +struct gprs_sm_ms *gprs_sm_ms_alloc(uint32_t ms_id) +{ + struct gprs_sm_ms *ms; + + ms = talloc_zero(g_ctx, struct gprs_sm_ms); + if (!ms) + return NULL; + + ms->ms_id = ms_id; + + llist_add(&ms->list, &g_ctx->ms_list); + + return ms; +} + +void gprs_sm_ms_free(struct gprs_sm_ms *ms) +{ + unsigned int i; + if (!ms) + return; + + LOGMS(ms, LOGL_DEBUG, "free()\n"); + + for (i = 0; i < ARRAY_SIZE(ms->pdp); i++) + gprs_sm_entity_free(ms->pdp[i]); + + llist_del(&ms->list); + talloc_free(ms); +} + +struct gprs_sm_ms *gprs_sm_find_ms_by_id(uint32_t ms_id) +{ + struct gprs_sm_ms *ms; + + llist_for_each_entry(ms, &g_ctx->ms_list, list) { + if (ms->ms_id == ms_id) + return ms; + } + return NULL; +} + +struct gprs_sm_entity *gprs_sm_entity_alloc(struct gprs_sm_ms *ms, uint32_t nsapi) +{ + struct gprs_sm_entity *sme; + sme = talloc_zero(g_ctx, struct gprs_sm_entity); + if (!sme) + return NULL; + + sme->ms = ms; + sme->sess_id = g_ctx->next_sess_id++; + sme->nsapi = nsapi; + + if (gprs_sm_ms_fsm_ctx_init(&sme->ms_fsm, sme) < 0) { + talloc_free(sme); + return NULL; + } + + OSMO_ASSERT(sme->ms->pdp[sme->nsapi] == NULL); + sme->ms->pdp[sme->nsapi] = sme; + return sme; +} + +void gprs_sm_entity_free(struct gprs_sm_entity *sme) +{ + if (!sme) + return; + + gprs_sm_ms_fsm_ctx_release(&sme->ms_fsm); + + sme->ms->pdp[sme->nsapi] = NULL; + talloc_free(sme); +} + +struct gprs_sm_entity *gprs_sm_find_sme_by_sess_id(uint32_t sess_id) +{ + struct gprs_sm_ms *ms; + unsigned int i; + + llist_for_each_entry(ms, &g_ctx->ms_list, list) { + for (i = 0; i < ARRAY_SIZE(ms->pdp); i++) { + if (!ms->pdp[i]) + continue; + if (ms->pdp[i]->sess_id != sess_id) + continue; + return ms->pdp[i]; + } + } + return NULL; +} + +int gprs_sm_submit_gmmsm_assign_req(const struct gprs_sm_entity *sme) +{ + struct gprs_sm_ms *ms = sme->ms; + struct osmo_gprs_gmm_prim *gmm_prim_tx; + int rc; + + gmm_prim_tx = osmo_gprs_gmm_prim_alloc_gmmsm_establish_req(sme->sess_id); + gmm_prim_tx->gmmsm.establish_req.attach_type = OSMO_GPRS_GMM_ATTACH_TYPE_GPRS; + gmm_prim_tx->gmmsm.establish_req.ptmsi = ms->gmm.ptmsi; + OSMO_STRLCPY_ARRAY(gmm_prim_tx->gmmsm.establish_req.imsi, ms->gmm.imsi); + OSMO_STRLCPY_ARRAY(gmm_prim_tx->gmmsm.establish_req.imei, ms->gmm.imei); + OSMO_STRLCPY_ARRAY(gmm_prim_tx->gmmsm.establish_req.imeisv, ms->gmm.imeisv); + + rc = gprs_sm_prim_call_gmm_down_cb(gmm_prim_tx); + return rc; +} + +int gprs_sm_submit_smreg_pdp_act_cnf(const struct gprs_sm_entity *sme, enum gsm48_gsm_cause cause) +{ + struct osmo_gprs_sm_prim *sm_prim_tx; + int rc; + + sm_prim_tx = gprs_sm_prim_alloc_smreg_pdp_act_cnf(); + sm_prim_tx->smreg.ms_id = sme->ms->ms_id; + sm_prim_tx->smreg.pdp_act_cnf.accepted = (cause != 0); + sm_prim_tx->smreg.pdp_act_cnf.nsapi = sme->nsapi; + if (!sm_prim_tx->smreg.pdp_act_cnf.accepted) + sm_prim_tx->smreg.pdp_act_cnf.rej.cause = cause; + + rc = gprs_sm_prim_call_up_cb(sm_prim_tx); + return rc; +} + +/* Tx SM Activate PDP context request, 9.5.1 */ +int gprs_sm_tx_act_pdp_ctx_req(struct gprs_sm_entity *sme) +{ + struct osmo_gprs_gmm_prim *gmm_prim; + int rc; + struct msgb *msg; + + LOGSME(sme, LOGL_INFO, "Tx SM Activate PDP Context Request\n"); + gmm_prim = osmo_gprs_gmm_prim_alloc_gmmsm_unitdata_req( + sme->ms->ms_id, NULL, GPRS_SM_ALLOC_SIZE); + msg = gmm_prim->oph.msg; + msg->l3h = msg->tail; + rc = gprs_sm_build_act_pdp_ctx_req(sme, msg); + if (rc < 0) { + msgb_free(msg); + return -EBADMSG; + } + gmm_prim->gmmsm.unitdata_req.smpdu = msg->l3h; + gmm_prim->gmmsm.unitdata_req.smpdu_len = msgb_l3len(msg); + + rc = gprs_sm_prim_call_gmm_down_cb(gmm_prim); + + return rc; +} + +/* 3GPP TS 24.008 § 9.5.2: Activate PDP Context Accept */ +static int gprs_sm_rx_act_pdp_ack(struct gprs_sm_entity *sme, + struct gsm48_hdr *gh, + unsigned int len) +{ + struct tlv_parsed tp; + int rc; + uint8_t radio_prio, llc_sapi; + uint8_t *ofs = (uint8_t *)gh; + uint8_t qos_len; + uint8_t *qos; + + ofs += sizeof(*gh); + //uint8_t transaction_id = gsm48_hdr_trans_id(gh); + + LOGSME(sme, LOGL_INFO, "Rx SM Activate PDP Context Accept\n"); + + if (len < (ofs + 2) - (uint8_t *)gh) + goto tooshort; + llc_sapi = *ofs++; + qos_len = *ofs++; + + if (len < (ofs + qos_len) - (uint8_t *)gh) + goto tooshort; + qos = ofs; + ofs += qos_len; + + if (len < (ofs + 1) - (uint8_t *)gh) + goto tooshort; + + radio_prio = *ofs++; + + rc = gprs_sm_tlv_parse(&tp, ofs, len - (ofs - (uint8_t *)gh)); + if (rc < 0) { + LOGSME(sme, LOGL_ERROR, "Rx SM Activate PDP Context Accept: failed to parse TLVs %d\n", rc); + goto rejected; + } + + (void)llc_sapi; + (void)qos; + (void)radio_prio; + + rc = osmo_fsm_inst_dispatch(sme->ms_fsm.fi, GPRS_SM_MS_EV_RX_ACT_PDP_CTX_ACC, NULL); + if (rc < 0) + goto rejected; + return rc; + +tooshort: + LOGSME(sme, LOGL_ERROR, "Rx GMM message too short! len=%u\n", len); +rejected: + return -EINVAL; /* TODO: what to do on error? */ +} + +/* Rx Session Management PDU */ +int gprs_sm_rx(struct gprs_sm_entity *sme, struct gsm48_hdr *gh, unsigned int len) +{ + int rc = 0; + if (len < sizeof(struct gsm48_hdr)) { + LOGSME(sme, LOGL_ERROR, "Rx GMM message too short! len=%u\n", len); + return -EINVAL; + } + + switch (gh->msg_type) { + case GSM48_MT_GSM_ACT_PDP_ACK: + rc = gprs_sm_rx_act_pdp_ack(sme, gh, len); + break; + default: + LOGSME(sme, LOGL_ERROR, + "Rx SM message not implemented! type=%u len=%u\n", + gh->msg_type, len); + rc = -EINVAL; + } + + return rc; +} diff --git a/src/sm/sm_ms_fsm.c b/src/sm/sm_ms_fsm.c new file mode 100644 index 0000000..b6fb79f --- /dev/null +++ b/src/sm/sm_ms_fsm.c @@ -0,0 +1,270 @@ +/* SM states in the MS, 3GPP TS 24.008 § 6.1.2.1 */ +/* + * (C) 2023 by sysmocom - s.m.f.c. GmbH + * All Rights Reserved + * + * SPDX-License-Identifier: AGPL-3.0+ + * + * Author: Pau Espin Pedrol + * + * 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 . + */ +#include +#include +#include + +#include +#include +#include + +#define X(s) (1 << (s)) + +static const struct osmo_tdef_state_timeout sm_ms_fsm_timeouts[32] = { + [GPRS_SM_MS_ST_PDP_INACTIVE] = {}, + [GPRS_SM_MS_ST_PDP_ACTIVE_PENDING] = { .T = 3380 }, + [GPRS_SM_MS_ST_PDP_ACTIVE] = {}, + [GPRS_SM_MS_ST_PDP_MODIFY_PENDING] = {}, + [GPRS_SM_MS_ST_PDP_INACTIVE_PENDING] = {}, +}; + +#define sm_ms_fsm_state_chg(fi, NEXT_STATE) \ + osmo_tdef_fsm_inst_state_chg(fi, NEXT_STATE, sm_ms_fsm_timeouts, g_ctx->T_defs, -1) + +static void st_sm_ms_pdp_inactive(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + switch (event) { + case GPRS_SM_MS_EV_TX_ACT_PDP_CTX_REQ: + sm_ms_fsm_state_chg(fi, GPRS_SM_MS_ST_PDP_ACTIVE_PENDING); + break; + default: + OSMO_ASSERT(0); + } +} + +static void st_sm_ms_pdp_active_pending_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state) +{ + struct gprs_sm_ms_fsm_ctx *ctx = (struct gprs_sm_ms_fsm_ctx *)fi->priv; + + gprs_sm_submit_gmmsm_assign_req(ctx->sme); +} + +static void st_sm_ms_pdp_active_pending(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct gprs_sm_ms_fsm_ctx *ctx = (struct gprs_sm_ms_fsm_ctx *)fi->priv; + + switch (event) { + case GPRS_SM_MS_EV_RX_GMM_ESTABLISH_CNF: + gprs_sm_tx_act_pdp_ctx_req(ctx->sme); + break; + case GPRS_SM_MS_EV_RX_GMM_ESTABLISH_REJ: + sm_ms_fsm_state_chg(fi, GPRS_SM_MS_ST_PDP_INACTIVE); + break; + case GPRS_SM_MS_EV_RX_ACT_PDP_CTX_REJ: + sm_ms_fsm_state_chg(fi, GPRS_SM_MS_ST_PDP_INACTIVE); + gprs_sm_submit_smreg_pdp_act_cnf(ctx->sme, *((enum gsm48_gsm_cause *)data)); + break; + case GPRS_SM_MS_EV_RX_ACT_PDP_CTX_ACC: + sm_ms_fsm_state_chg(fi, GPRS_SM_MS_ST_PDP_ACTIVE); + gprs_sm_submit_smreg_pdp_act_cnf(ctx->sme, (enum gsm48_gsm_cause)0); + break; + default: + OSMO_ASSERT(0); + } +} + +static void st_sm_ms_pdp_active(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + switch (event) { + case GPRS_SM_MS_EV_RX_DEACT_PDP_CTX_REQ: + /* TODO: Tx PDP DEACT ACC */ + sm_ms_fsm_state_chg(fi, GPRS_SM_MS_ST_PDP_INACTIVE); + break; + case GPRS_SM_MS_EV_TX_DEACT_PDP_CTX_REQ: + sm_ms_fsm_state_chg(fi, GPRS_SM_MS_ST_PDP_INACTIVE_PENDING); + break; + case GPRS_SM_MS_EV_TX_MOD_PDP_CTX_REQ: + sm_ms_fsm_state_chg(fi, GPRS_SM_MS_ST_PDP_MODIFY_PENDING); + break; + default: + OSMO_ASSERT(0); + } +} + +static void st_sm_ms_pdp_modify_pending(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + switch (event) { + case GPRS_SM_MS_EV_RX_DEACT_PDP_CTX_REQ: + /* TODO: Tx PDP DEACT ACC */ + sm_ms_fsm_state_chg(fi, GPRS_SM_MS_ST_PDP_INACTIVE); + break; + case GPRS_SM_MS_EV_RX_MOD_PDP_CTX_REJ: + sm_ms_fsm_state_chg(fi, GPRS_SM_MS_ST_PDP_INACTIVE_PENDING); + break; + case GPRS_SM_MS_EV_RX_MOD_PDP_CTX_ACC: + sm_ms_fsm_state_chg(fi, GPRS_SM_MS_ST_PDP_ACTIVE); + break; + default: + OSMO_ASSERT(0); + } +} + +static void st_sm_ms_pdp_inactive_pending(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + switch (event) { + case GPRS_SM_MS_EV_RX_DEACT_PDP_CTX_ACC: + sm_ms_fsm_state_chg(fi, GPRS_SM_MS_ST_PDP_INACTIVE); + break; + default: + OSMO_ASSERT(0); + } +} + +static int sm_ms_fsm_timer_cb(struct osmo_fsm_inst *fi) +{ + struct gprs_sm_ms_fsm_ctx *ctx = (struct gprs_sm_ms_fsm_ctx *)fi->priv; + + switch (fi->T) { + case 3380: + ctx->act_pdp_ctx_attempts++; + LOGPFSML(ctx->fi, LOGL_INFO, "T3380 timeout attempts=%u\n", ctx->act_pdp_ctx_attempts); + OSMO_ASSERT(fi->state == GPRS_SM_MS_ST_PDP_ACTIVE_PENDING); + if (ctx->act_pdp_ctx_attempts == 4) { + enum gsm48_gsm_cause cause = GSM_CAUSE_SERV_OPT_TEMP_OOO; + /* TS 24.008 6.1.3.1.5: + * "On the first expiry of the timer T3380, the MS shall resend the ACTIVATE PDP + * CONTEXT REQUEST and shall reset and restart timer T3380. This retransmission is + * repeated four times, i.e. on the fifth expiry of timer T3380, the MS shall release + * all resources possibly allocated for this invocation and shall abort the procedure" + */ + LOGPFSML(ctx->fi, LOGL_NOTICE, "TBF establishment failure (T3380 timeout attempts=%u)\n", + ctx->act_pdp_ctx_attempts); + + osmo_fsm_inst_dispatch(ctx->fi, GPRS_SM_MS_EV_RX_ACT_PDP_CTX_REJ, &cause); + return 0; + } + /* reinit tx of Act Pdp Ctx Req and rearm timer by re-entering state: */ + sm_ms_fsm_state_chg(ctx->fi, GPRS_SM_MS_ST_PDP_ACTIVE_PENDING); + break; + default: + OSMO_ASSERT(0); + } + return 0; +} + +static struct osmo_fsm_state sm_ms_fsm_states[] = { + [GPRS_SM_MS_ST_PDP_INACTIVE] = { + .in_event_mask = + X(GPRS_SM_MS_EV_TX_ACT_PDP_CTX_REQ), + .out_state_mask = + X(GPRS_SM_MS_ST_PDP_ACTIVE_PENDING), + .name = "INACTIVE", + .action = st_sm_ms_pdp_inactive, + }, + [GPRS_SM_MS_ST_PDP_ACTIVE_PENDING] = { + .in_event_mask = + X(GPRS_SM_MS_EV_RX_GMM_ESTABLISH_CNF) | + X(GPRS_SM_MS_EV_RX_GMM_ESTABLISH_REJ) | + X(GPRS_SM_MS_EV_RX_ACT_PDP_CTX_REJ) | + X(GPRS_SM_MS_EV_RX_ACT_PDP_CTX_ACC), + .out_state_mask = + X(GPRS_SM_MS_ST_PDP_INACTIVE) | + X(GPRS_SM_MS_ST_PDP_ACTIVE_PENDING) | + X(GPRS_SM_MS_ST_PDP_ACTIVE), + .name = "PDP_ACTIVE_PENDING", + .onenter = st_sm_ms_pdp_active_pending_on_enter, + .action = st_sm_ms_pdp_active_pending, + }, + [GPRS_SM_MS_ST_PDP_ACTIVE] = { + .in_event_mask = + X(GPRS_SM_MS_EV_RX_DEACT_PDP_CTX_REQ) | + X(GPRS_SM_MS_EV_TX_DEACT_PDP_CTX_REQ)| + X(GPRS_SM_MS_EV_TX_MOD_PDP_CTX_REQ), + .out_state_mask = + X(GPRS_SM_MS_ST_PDP_INACTIVE) | + X(GPRS_SM_MS_ST_PDP_INACTIVE_PENDING) | + X(GPRS_SM_MS_ST_PDP_MODIFY_PENDING), + .name = "PDP_ACTIVE", + .action = st_sm_ms_pdp_active, + }, + [GPRS_SM_MS_ST_PDP_MODIFY_PENDING] = { + .in_event_mask = + X(GPRS_SM_MS_EV_RX_DEACT_PDP_CTX_REQ) | + X(GPRS_SM_MS_EV_RX_MOD_PDP_CTX_REJ) | + X(GPRS_SM_MS_EV_RX_MOD_PDP_CTX_ACC), + .out_state_mask = + X(GPRS_SM_MS_ST_PDP_INACTIVE) | + X(GPRS_SM_MS_ST_PDP_ACTIVE) | + X(GPRS_SM_MS_ST_PDP_INACTIVE_PENDING), + .name = "PDP_MODIFY_PENDING", + .action = st_sm_ms_pdp_modify_pending, + }, + [GPRS_SM_MS_ST_PDP_INACTIVE_PENDING] = { + .in_event_mask = + X(GPRS_SM_MS_EV_RX_DEACT_PDP_CTX_ACC), + .out_state_mask = + X(GPRS_SM_MS_ST_PDP_INACTIVE), + .name = "PDP_INACTIVE_PENDING", + .action = st_sm_ms_pdp_inactive_pending, + }, +}; + +const struct value_string sm_ms_fsm_event_names[] = { + { GPRS_SM_MS_EV_RX_GMM_ESTABLISH_CNF, "RX GMM_ESTABLISH_CNF" }, + { GPRS_SM_MS_EV_RX_GMM_ESTABLISH_REJ, "RX GMM_ESTABLISH_REJ" }, + { GPRS_SM_MS_EV_TX_ACT_PDP_CTX_REQ, "TX_ACT_PDP_CTX_REQ" }, + { GPRS_SM_MS_EV_RX_ACT_PDP_CTX_REJ, "RX_ACT_PDP_CTX_REJ" }, + { GPRS_SM_MS_EV_RX_ACT_PDP_CTX_ACC, "RX_ACT_PDP_CTX_ACC" }, + { GPRS_SM_MS_EV_TX_DEACT_PDP_CTX_REQ, "TX_DEACT_PDP_CTX_REQ" }, + { GPRS_SM_MS_EV_RX_DEACT_PDP_CTX_REQ, "RX_DEACT_PDP_CTX_REQ" }, + { GPRS_SM_MS_EV_RX_DEACT_PDP_CTX_ACC, "RX_DEACT_PDP_CTX_ACC" }, + { GPRS_SM_MS_EV_TX_MOD_PDP_CTX_REQ, "TX_MOD_PDP_CTX_REQ" }, + { GPRS_SM_MS_EV_RX_MOD_PDP_CTX_REJ, "RX_MOD_PDP_CTX_REJ" }, + { GPRS_SM_MS_EV_RX_MOD_PDP_CTX_ACC, "RX_MOD_PDP_CTX_ACC" }, + { 0, NULL } +}; + +struct osmo_fsm sm_ms_fsm = { + .name = "SM_MS", + .states = sm_ms_fsm_states, + .num_states = ARRAY_SIZE(sm_ms_fsm_states), + .timer_cb = sm_ms_fsm_timer_cb, + .event_names = sm_ms_fsm_event_names, + .log_subsys = DLGLOBAL, /* updated dynamically through gprs_sm_ms_fsm_set_log_cat() */ + .timer_cb = sm_ms_fsm_timer_cb, +}; + +int gprs_sm_ms_fsm_init(void) +{ + return osmo_fsm_register(&sm_ms_fsm); +} + +void gprs_sm_ms_fsm_set_log_cat(int logcat) +{ + sm_ms_fsm.log_subsys = logcat; +} + +int gprs_sm_ms_fsm_ctx_init(struct gprs_sm_ms_fsm_ctx *ctx, struct gprs_sm_entity *sme) +{ + ctx->sme = sme; + ctx->fi = osmo_fsm_inst_alloc(&sm_ms_fsm, sme, ctx, LOGL_INFO, NULL); + if (!ctx->fi) + return -ENODATA; + + return 0; +} + +void gprs_sm_ms_fsm_ctx_release(struct gprs_sm_ms_fsm_ctx *ctx) +{ + osmo_fsm_inst_free(ctx->fi); +} diff --git a/src/sm/sm_pdu.c b/src/sm/sm_pdu.c new file mode 100644 index 0000000..3498a9c --- /dev/null +++ b/src/sm/sm_pdu.c @@ -0,0 +1,131 @@ +/* SM PDUs, 3GPP TS 9.5 24.008 Session Management Messages */ +/* (C) 2023 by Sysmocom s.f.m.c. GmbH + * + * 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 . + * + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + + +const struct tlv_definition gprs_sm_att_tlvdef = { + .def = { + [GSM48_IE_GSM_RADIO_PRIO] = {TLV_TYPE_SINGLE_TV, 1 }, + [GSM48_IE_GSM_APN] = { TLV_TYPE_TLV, 0 }, + [GSM48_IE_GSM_PROTO_CONF_OPT] = { TLV_TYPE_TLV, 0 }, + [GSM48_IE_GSM_PDP_ADDR] = { TLV_TYPE_TLV, 0 }, + [GSM48_IE_GSM_AA_TMR] = { TLV_TYPE_TV, 1 }, + [GSM48_IE_GSM_QOS] = { TLV_TYPE_TLV, 0 }, + [GSM48_IE_GSM_TFT] = { TLV_TYPE_TLV, 0 }, + [GSM48_IE_GSM_LLC_SAPI] = { TLV_TYPE_TV, 1 }, + [GSM48_IE_GSM_PFI] = { TLV_TYPE_TLV, 0 }, + [GSM48_IE_GSM_NAME_FULL] = { TLV_TYPE_TLV, 0 }, + [GSM48_IE_GSM_NAME_SHORT] = { TLV_TYPE_TLV, 0 }, + [GSM48_IE_GSM_TIMEZONE] = { TLV_TYPE_FIXED, 1 }, + [GSM48_IE_GSM_UTC_AND_TZ] = { TLV_TYPE_FIXED, 7 }, + [GSM48_IE_GSM_LSA_ID] = { TLV_TYPE_TLV, 0 }, + }, +}; + +/* 10.5.6.4 Packet data protocol address */ +static uint8_t gprs_sm_pdp_addr_enc_ietf(struct gprs_sm_pdp_addr *out, + enum osmo_gprs_sm_pdp_addr_ietf_type pdp_addr_ietf_type, + const struct osmo_sockaddr *pdp_addr_v4, + const struct osmo_sockaddr *pdp_addr_v6) +{ + memset(out, 0, sizeof(*out)); + + out->spare = 0x00; + out->organization = GPRS_SM_PDP_ADDR_ORG_IETF; + out->type = pdp_addr_ietf_type; + + switch (pdp_addr_ietf_type) { + case OSMO_GPRS_SM_PDP_ADDR_IETF_IPV6: + memcpy(out->addr6, pdp_addr_v6->u.sin6.sin6_addr.s6_addr, sizeof(out->addr6)); + return 2 + sizeof(out->addr6); + case OSMO_GPRS_SM_PDP_ADDR_IETF_IPV4V6: + out->both.addr = pdp_addr_v4->u.sin.sin_addr.s_addr; + memcpy(out->both.addr6, pdp_addr_v6->u.sin6.sin6_addr.s6_addr, sizeof(out->both.addr6)); + return 2 + sizeof(out->both.addr) + sizeof(out->both.addr6); + case OSMO_GPRS_SM_PDP_ADDR_IETF_IPV4: + default: + /* All other values shall be interpreted as IPv4 address in this version of the protocol */ + out->addr = pdp_addr_v4->u.sin.sin_addr.s_addr; + return 2 + sizeof(out->both.addr); + } +} + +/* Chapter 9.4.1: Attach request */ +int gprs_sm_build_act_pdp_ctx_req(struct gprs_sm_entity *sme, + struct msgb *msg) +{ + + struct gsm48_hdr *gh; + uint8_t *l; + int rc; + uint8_t transaction_id = sme->ti ^ 0x8; /* flip */ + + gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + gh->proto_discr = GSM48_PDISC_SM_GPRS | (transaction_id << 4); + gh->msg_type = GSM48_MT_GSM_ACT_PDP_REQ; + + /* 10.5.6.2 Requested NSAPI */ + msgb_v_put(msg, sme->nsapi); + + /* 10.5.6.9 Requested LLC SAPI */ + msgb_v_put(msg, sme->llc_sapi); + + /* 10.5.6.5 Requested QoS */ + msgb_lv_put(msg, sme->qos_len, (uint8_t *)&sme->qos); + + /* 10.5.6.4 Requested PDP address */ + l = msgb_put(msg, 1); /* len */ + *l = gprs_sm_pdp_addr_enc_ietf((struct gprs_sm_pdp_addr *)msg->tail, + sme->pdp_addr_ietf_type, + &sme->pdp_addr_v4, + &sme->pdp_addr_v6); + msgb_put(msg, *l); + + /* 10.5.6.1 Access point name (Optional) */ + if (sme->apn[0] != '\0') { + msgb_v_put(msg, GSM48_IE_GSM_APN); + l = msgb_put(msg, 1); /* len */ + rc = osmo_apn_from_str(msg->tail, msgb_tailroom(msg), sme->apn); + if (rc < 0) + return -EINVAL; + *l = rc; + } + + /* 10.5.6.3 Protocol configuration options (Optional) */ + if (sme->pco_len > 0) + msgb_tlv_put(msg, GSM48_IE_GSM_PROTO_CONF_OPT, + sme->pco_len, sme->pco); + + /* TODO: other optional fields */ + return 0; +} diff --git a/src/sm/sm_prim.c b/src/sm/sm_prim.c new file mode 100644 index 0000000..197c8a4 --- /dev/null +++ b/src/sm/sm_prim.c @@ -0,0 +1,437 @@ +/* SM service primitive implementation as per 3GPP TS 44.065 */ +/* + * (C) 2023 by sysmocom - s.f.m.c. GmbH + * + * 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 . + * + */ + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#define SM_MSGB_HEADROOM 0 + +const struct value_string osmo_gprs_sm_prim_sap_names[] = { + { OSMO_GPRS_SM_SAP_SMREG, "SMREG" }, + { 0, NULL } +}; + +const struct value_string osmo_gprs_sm_smreg_prim_type_names[] = { + { OSMO_GPRS_SM_SMREG_PDP_ACTIVATE, "PDP_ACTIVATE" }, + { OSMO_GPRS_SM_SMREG_PDP_DEACTIVATE, "PDP_DEACTIVATE" }, + { OSMO_GPRS_SM_SMREG_PDP_MODIFY, "PDP_MODIFY"}, + { OSMO_GPRS_SM_SMREG_PDP_ACTIVATE_SEC, "PDP_ACTIVATE_SEC" }, + { OSMO_GPRS_SM_SMREG_MBMS_ACTIVATE, "MBMS_ACTIVATE" }, + { 0, NULL } +}; + +const char *osmo_gprs_sm_prim_name(const struct osmo_gprs_sm_prim *sm_prim) +{ + static char name_buf[256]; + const char *sap = osmo_gprs_sm_prim_sap_name(sm_prim->oph.sap); + const char *op = get_value_string(osmo_prim_op_names, sm_prim->oph.operation); + const char *type; + + switch (sm_prim->oph.sap) { + case OSMO_GPRS_SM_SAP_SMREG: + type = osmo_gprs_sm_smreg_prim_type_name(sm_prim->oph.primitive); + break; + default: + type = "unsupported-sm-sap"; + } + + snprintf(name_buf, sizeof(name_buf), "%s-%s.%s", sap, type, op); + return name_buf; +} + +static int sm_up_cb_dummy(struct osmo_gprs_sm_prim *sm_prim, void *user_data) +{ + LOGSM(LOGL_INFO, "%s(%s)\n", __func__, osmo_gprs_sm_prim_name(sm_prim)); + return 0; +} + +static int sm_down_cb_dummy(struct osmo_gprs_sm_prim *sm_prim, void *user_data) +{ + LOGSM(LOGL_INFO, "%s(%s)\n", __func__, osmo_gprs_sm_prim_name(sm_prim)); + return 0; +} + +static int sm_gmm_down_cb_dummy(struct osmo_gprs_gmm_prim *gmm_prim, void *user_data) +{ + LOGSM(LOGL_INFO, "%s(%s)\n", __func__, osmo_gprs_gmm_prim_name(gmm_prim)); + return 0; +} + +/* Set callback used by SM layer to push primitives to higher layers in protocol stack */ +void osmo_gprs_sm_prim_set_up_cb(osmo_gprs_sm_prim_up_cb up_cb, void *up_user_data) +{ + g_ctx->sm_up_cb = up_cb; + g_ctx->sm_up_cb_user_data = up_user_data; +} + +/* Set callback used by SM layer to push primitives to lower layers in protocol stack */ +void osmo_gprs_sm_prim_set_down_cb(osmo_gprs_sm_prim_down_cb down_cb, void *down_user_data) +{ + g_ctx->sm_down_cb = down_cb; + g_ctx->sm_down_cb_user_data = down_user_data; +} + +/* Set callback used by SM layer to push primitives to GMM lower layer in protocol stack */ +void osmo_gprs_sm_prim_set_gmm_down_cb(osmo_gprs_sm_prim_gmm_down_cb gmm_down_cb, void *gmm_down_user_data) +{ + g_ctx->sm_gmm_down_cb = gmm_down_cb; + g_ctx->sm_gmm_down_cb_user_data = gmm_down_user_data; +} + +/******************************** + * Primitive allocation: + ********************************/ + +/* allocate a msgb containing a struct osmo_gprs_sm_prim + optional l3 data */ +static struct msgb *gprs_sm_prim_msgb_alloc(unsigned int npdu_len) +{ + const int headroom = SM_MSGB_HEADROOM; + const int size = headroom + sizeof(struct osmo_gprs_sm_prim) + npdu_len; + struct msgb *msg = msgb_alloc_headroom(size, headroom, "sm_prim"); + + if (!msg) + return NULL; + + msg->l1h = msgb_put(msg, sizeof(struct osmo_gprs_sm_prim)); + + return msg; +} + +struct osmo_gprs_sm_prim *gprs_sm_prim_alloc(unsigned int sap, unsigned int type, + enum osmo_prim_operation operation, + unsigned int extra_size) +{ + struct msgb *msg = gprs_sm_prim_msgb_alloc(extra_size); + struct osmo_gprs_sm_prim *sm_prim = msgb_sm_prim(msg); + + osmo_prim_init(&sm_prim->oph, sap, type, operation, msg); + return sm_prim; +} + +/*** SMREG ***/ + +static inline struct osmo_gprs_sm_prim *sm_prim_smreg_alloc(enum osmo_gprs_sm_smreg_prim_type type, + enum osmo_prim_operation operation, + unsigned int extra_size) +{ + return gprs_sm_prim_alloc(OSMO_GPRS_SM_SAP_SMREG, type, operation, extra_size); +} + +/* TS 24.007 6.5.1.1 SMREG-PDP-ACTIVATE-REQ */ +struct osmo_gprs_sm_prim *osmo_gprs_sm_prim_alloc_smreg_pdp_act_req(void) +{ + struct osmo_gprs_sm_prim *sm_prim; + sm_prim = sm_prim_smreg_alloc(OSMO_GPRS_SM_SMREG_PDP_ACTIVATE, PRIM_OP_REQUEST, 0); + return sm_prim; +} + + +/* TS 24.007 6.5.1.2 SMREG-PDP-ACTIVATE-CNF */ +struct osmo_gprs_sm_prim *gprs_sm_prim_alloc_smreg_pdp_act_cnf(void) +{ + struct osmo_gprs_sm_prim *sm_prim; + sm_prim = sm_prim_smreg_alloc(OSMO_GPRS_SM_SMREG_PDP_ACTIVATE, PRIM_OP_CONFIRM, 0); + sm_prim->smreg.pdp_act_cnf.accepted = true; + /* TODO: sm_prim->smreg.pdp_act_cnf.acc.* */ + return sm_prim; +} +/* TODO: TS 24.007 6.5.1.3 SMREG-PDP-ACTIVATE-REJ */ + +/* TS 24.007 6.5.1.4 SMREG-PDP-ACTIVATE-IND */ +struct osmo_gprs_sm_prim *gprs_sm_prim_alloc_smreg_pdp_act_ind(void) +{ + struct osmo_gprs_sm_prim *sm_prim; + sm_prim = sm_prim_smreg_alloc(OSMO_GPRS_SM_SMREG_PDP_ACTIVATE, PRIM_OP_INDICATION, 0); + return sm_prim; +} + +static int gprs_sm_prim_handle_unsupported(struct osmo_gprs_sm_prim *sm_prim) +{ + LOGSM(LOGL_ERROR, "Unsupported sm_prim! %s\n", osmo_gprs_sm_prim_name(sm_prim)); + msgb_free(sm_prim->oph.msg); + return -ENOTSUP; +} + +static int gprs_sm_prim_handle_gmm_unsupported(struct osmo_gprs_gmm_prim *gmm_prim) +{ + LOGSM(LOGL_ERROR, "Unsupported gmm_prim! %s\n", osmo_gprs_gmm_prim_name(gmm_prim)); + msgb_free(gmm_prim->oph.msg); + return -ENOTSUP; +} + +/******************************** + * Handling from/to upper layers: + ********************************/ + +int gprs_sm_prim_call_up_cb(struct osmo_gprs_sm_prim *sm_prim) +{ + int rc; + if (g_ctx->sm_up_cb) + rc = g_ctx->sm_up_cb(sm_prim, g_ctx->sm_up_cb_user_data); + else + rc = sm_up_cb_dummy(sm_prim, g_ctx->sm_up_cb_user_data); + msgb_free(sm_prim->oph.msg); + return rc; +} + +/* TS 24.007 6.6.1.1 SMREG-Attach.request:*/ +static int gprs_sm_prim_handle_smreg_pdp_act_req(struct osmo_gprs_sm_prim *sm_prim) +{ + int rc = 0; + struct gprs_sm_ms *ms; + struct gprs_sm_entity *sme = NULL; + struct osmo_gprs_sm_smreg_prim *smreg = &sm_prim->smreg; + + OSMO_ASSERT(smreg->pdp_act_req.qos_len <= sizeof(sme->qos)); + OSMO_ASSERT(smreg->pdp_act_req.pco_len <= sizeof(sme->pco)); + + ms = gprs_sm_find_ms_by_id(smreg->ms_id); + if (!ms) { + ms = gprs_sm_ms_alloc(smreg->ms_id); + OSMO_ASSERT(ms); + } else { + sme = gprs_sm_ms_get_pdp_ctx(ms, smreg->pdp_act_req.nsapi); + LOGSME(sme, LOGL_ERROR, "Rx SMREG-PDP-ACT.req for already existing PDP context\n"); + return -EINVAL; + } + + sme = gprs_sm_entity_alloc(ms, smreg->pdp_act_req.nsapi); + OSMO_ASSERT(sme); + + if (smreg->pdp_act_req.llc_sapi != OSMO_GPRS_SM_LLC_SAPI_UNASSIGNED) + sme->llc_sapi = smreg->pdp_act_req.llc_sapi; + else + sme->llc_sapi = OSMO_GPRS_SM_LLC_SAPI_SAPI3; /* default */ + + sme->pdp_addr_ietf_type = smreg->pdp_act_req.pdp_addr_ietf_type; + memcpy(&sme->pdp_addr_v4, &smreg->pdp_act_req.pdp_addr_v4, sizeof(sme->pdp_addr_v4)); + memcpy(&sme->pdp_addr_v6, &smreg->pdp_act_req.pdp_addr_v6, sizeof(sme->pdp_addr_v6)); + + OSMO_STRLCPY_ARRAY(sme->apn, smreg->pdp_act_req.apn); + + sme->qos_len = smreg->pdp_act_req.qos_len; + if (sme->qos_len > 0) + memcpy(&sme->qos, &smreg->pdp_act_req.qos, sme->qos_len); + + sme->pco_len = smreg->pdp_act_req.pco_len; + if (sme->pco_len > 0) + memcpy(&sme->pco, &smreg->pdp_act_req.pco, sme->pco_len); + + /* Info required to establish GMM: */ + ms->gmm.ptmsi = sm_prim->smreg.pdp_act_req.gmm.ptmsi; + if (sm_prim->smreg.pdp_act_req.gmm.imsi[0] != '\0') + OSMO_STRLCPY_ARRAY(ms->gmm.imsi, sm_prim->smreg.pdp_act_req.gmm.imsi); + if (sm_prim->smreg.pdp_act_req.gmm.imei[0] != '\0') + OSMO_STRLCPY_ARRAY(ms->gmm.imei, sm_prim->smreg.pdp_act_req.gmm.imei); + if (sm_prim->smreg.pdp_act_req.gmm.imeisv[0] != '\0') + OSMO_STRLCPY_ARRAY(ms->gmm.imeisv, sm_prim->smreg.pdp_act_req.gmm.imeisv); + + rc = osmo_fsm_inst_dispatch(sme->ms_fsm.fi, GPRS_SM_MS_EV_TX_ACT_PDP_CTX_REQ, NULL); + + return rc; +} + +static int gprs_sm_prim_handle_smreg(struct osmo_gprs_sm_prim *sm_prim) +{ + int rc; + + switch (OSMO_PRIM_HDR(&sm_prim->oph)) { + case OSMO_PRIM(OSMO_GPRS_SM_SMREG_PDP_ACTIVATE, PRIM_OP_REQUEST): + rc = gprs_sm_prim_handle_smreg_pdp_act_req(sm_prim); + break; + default: + rc = gprs_sm_prim_handle_unsupported(sm_prim); + } + return rc; +} + +/* SM higher layers push SM primitive down to SM layer: */ +int osmo_gprs_sm_prim_upper_down(struct osmo_gprs_sm_prim *sm_prim) +{ + int rc; + + LOGSM(LOGL_INFO, "Rx from upper layers: %s\n", osmo_gprs_sm_prim_name(sm_prim)); + + + switch (sm_prim->oph.sap) { + case OSMO_GPRS_SM_SAP_SMREG: + rc = gprs_sm_prim_handle_smreg(sm_prim); + break; + default: + rc = gprs_sm_prim_handle_unsupported(sm_prim); + rc = 1; + } + + /* Special return value '1' means: do not free */ + if (rc != 1) + msgb_free(sm_prim->oph.msg); + else + rc = 0; + return rc; +} + +/******************************** + * Handling from/to lower layers: + ********************************/ + +int gprs_sm_prim_call_down_cb(struct osmo_gprs_sm_prim *sm_prim) +{ + int rc; + if (g_ctx->sm_down_cb) + rc = g_ctx->sm_down_cb(sm_prim, g_ctx->sm_down_cb_user_data); + else + rc = sm_down_cb_dummy(sm_prim, g_ctx->sm_down_cb_user_data); + msgb_free(sm_prim->oph.msg); + return rc; +} + +/* SM lower layers (GMM) push SM primitive up to SM layer: */ +int osmo_gprs_sm_prim_lower_up(struct osmo_gprs_sm_prim *sm_prim) +{ + OSMO_ASSERT(g_ctx); + OSMO_ASSERT(sm_prim); + struct msgb *msg = sm_prim->oph.msg; + int rc; + + LOGSM(LOGL_INFO, "Rx from lower layers: %s\n", osmo_gprs_sm_prim_name(sm_prim)); + + switch (sm_prim->oph.sap) { + default: + rc = gprs_sm_prim_handle_unsupported(sm_prim); + rc = 1; + } + + /* Special return value '1' means: do not free */ + if (rc != 1) + msgb_free(msg); + else + rc = 0; + return rc; +} + +int gprs_sm_prim_call_gmm_down_cb(struct osmo_gprs_gmm_prim *gmm_prim) +{ + int rc; + if (g_ctx->sm_gmm_down_cb) + rc = g_ctx->sm_gmm_down_cb(gmm_prim, g_ctx->sm_gmm_down_cb_user_data); + else + rc = sm_gmm_down_cb_dummy(gmm_prim, g_ctx->sm_gmm_down_cb_user_data); + /* Special return value '1' means: do not free */ + if (rc != 1) + msgb_free(gmm_prim->oph.msg); + else + rc = 0; + return rc; +} + +/* TS 24.007 9.5.1.2 GMMSM-ESTABLISH-CNF */ +static int gprs_sm_prim_handle_gmmsm_establish_cnf(struct osmo_gprs_gmm_prim *gmm_prim) +{ + struct osmo_gprs_gmm_gmmsm_prim *gmmsm = &gmm_prim->gmmsm; + struct gprs_sm_entity *sme; + int rc, ev; + + sme = gprs_sm_find_sme_by_sess_id(gmmsm->sess_id); + if (!sme) { + LOGSM(LOGL_ERROR, "Rx GMMSM-ESTABLISH.cnf for non existing SM Entity\n"); + return -EINVAL; + } + + ev = gmmsm->establish_cnf.accepted ? + GPRS_SM_MS_EV_RX_GMM_ESTABLISH_CNF : GPRS_SM_MS_EV_RX_GMM_ESTABLISH_REJ; + rc = osmo_fsm_inst_dispatch(sme->ms_fsm.fi, ev, NULL); + + return rc; +} + +/* TS 24.007 9.5.1.6 GMMSM-UNITDATA-IND */ +static int gprs_sm_prim_handle_gmmsm_unitdata_ind(struct osmo_gprs_gmm_prim *gmm_prim) +{ + struct osmo_gprs_gmm_gmmsm_prim *gmmsm = &gmm_prim->gmmsm; + struct gprs_sm_entity *sme; + int rc; + + sme = gprs_sm_find_sme_by_sess_id(gmmsm->sess_id); + if (!sme) { + LOGSM(LOGL_ERROR, "Rx GMMSM-UNITDATA.ind for non existing SM Entity\n"); + return -EINVAL; + } + + rc = gprs_sm_rx(sme, + (struct gsm48_hdr *)gmm_prim->gmmsm.unitdata_ind.smpdu, + gmm_prim->gmmsm.unitdata_ind.smpdu_len); + + return rc; +} + +static int gprs_sm_prim_handle_gmmsm(struct osmo_gprs_gmm_prim *gmm_prim) +{ + int rc = 0; + switch (OSMO_PRIM_HDR(&gmm_prim->oph)) { + case OSMO_PRIM(OSMO_GPRS_GMM_GMMSM_ESTABLISH, PRIM_OP_CONFIRM): + rc = gprs_sm_prim_handle_gmmsm_establish_cnf(gmm_prim); + break; + case OSMO_PRIM(OSMO_GPRS_GMM_GMMSM_UNITDATA, PRIM_OP_INDICATION): + rc = gprs_sm_prim_handle_gmmsm_unitdata_ind(gmm_prim); + break; + default: + rc = gprs_sm_prim_handle_gmm_unsupported(gmm_prim); + rc = 1; + break; + } + return rc; +} + +/* SM lower layers (GMM) push SM primitive up to SM layer: */ +int osmo_gprs_sm_prim_gmm_lower_up(struct osmo_gprs_gmm_prim *gmm_prim) +{ + OSMO_ASSERT(g_ctx); + OSMO_ASSERT(gmm_prim); + struct msgb *msg = gmm_prim->oph.msg; + int rc; + + LOGSM(LOGL_INFO, "Rx from lower layers: %s\n", osmo_gprs_gmm_prim_name(gmm_prim)); + + switch (gmm_prim->oph.sap) { + case OSMO_GPRS_GMM_SAP_GMMSM: + rc = gprs_sm_prim_handle_gmmsm(gmm_prim); + break; + default: + rc = gprs_sm_prim_handle_gmm_unsupported(gmm_prim); + rc = 1; + } + + /* Special return value '1' means: do not free */ + if (rc != 1) + msgb_free(msg); + else + rc = 0; + return rc; +} diff --git a/tests/Makefile.am b/tests/Makefile.am index 9447b2d..d890847 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -3,6 +3,7 @@ SUBDIRS = \ llc \ rlcmac \ sndcp \ + sm \ $(NULL) # The `:;' works around a Bash 3.2 bug when the output is not writeable. diff --git a/tests/gmm/gmm_prim_test.c b/tests/gmm/gmm_prim_test.c index 70f8377..0053b94 100644 --- a/tests/gmm/gmm_prim_test.c +++ b/tests/gmm/gmm_prim_test.c @@ -158,6 +158,23 @@ int test_gmm_prim_up_cb(struct osmo_gprs_gmm_prim *gmm_prim, void *user_data) OSMO_ASSERT(0) } break; + case OSMO_GPRS_GMM_SAP_GMMSM: + switch (OSMO_PRIM_HDR(&gmm_prim->oph)) { + case OSMO_PRIM(OSMO_GPRS_GMM_GMMSM_ESTABLISH, PRIM_OP_CONFIRM): + printf("%s(): Rx %s sess_id=%u accepted=%u rej_cause=%u\n", __func__, pdu_name, + gmm_prim->gmmsm.sess_id, + gmm_prim->gmmsm.establish_cnf.accepted, + gmm_prim->gmmsm.establish_cnf.rej.cause); + break; + //case OSMO_PRIM(OSMO_GPRS_GMM_GMMSM_DETACH, PRIM_OP_CONFIRM): + // printf("%s(): Rx %s detach_type='%s'\n", __func__, pdu_name, + // osmo_gprs_gmm_detach_ms_type_name(gmm_prim->gmmsm.detach_cnf.detach_type)); + // break; + default: + printf("%s(): Unexpected Rx %s\n", __func__, pdu_name); + OSMO_ASSERT(0) + } + break; default: printf("%s(): Unexpected Rx %s\n", __func__, pdu_name); OSMO_ASSERT(0); @@ -202,7 +219,8 @@ int test_gmm_prim_llc_down_cb(struct osmo_gprs_llc_prim *llc_prim, void *user_da return 0; } -static void test_gmm_prim_ms(void) +/* Test explicit GPRS attach through GMMREG, TS 24.007 Annex C.1 */ +static void test_gmm_prim_ms_gmmreg(void) { struct osmo_gprs_gmm_prim *gmm_prim; struct osmo_gprs_llc_prim *llc_prim; @@ -254,6 +272,7 @@ static void test_gmm_prim_ms(void) rc = osmo_gprs_gmm_prim_llc_lower_up(llc_prim); OSMO_ASSERT(rc == 0); /* As a result, MS answers GMM Attach Complete */ + /* As a result, MS submits GMMREG ATTACH.cnf */ /* ... */ @@ -276,6 +295,70 @@ static void test_gmm_prim_ms(void) printf("==== %s() [end] ====\n", __func__); } +/* Test implicit GPRS attach through SM (ACT PDP CTX), TS 24.007 Annex C.3 */ +static void test_gmm_prim_ms_gmmsm(void) +{ + struct osmo_gprs_gmm_prim *gmm_prim; + struct osmo_gprs_llc_prim *llc_prim; + int rc; + uint32_t ptmsi = 0x00000000; + uint32_t tlli = 0x00000000; + char *imsi = "1234567890"; + char *imei = "42342342342342"; + char *imeisv = "4234234234234275"; + uint32_t sess_id = 1234; + + printf("==== %s() [start] ====\n", __func__); + + rc = osmo_gprs_gmm_init(OSMO_GPRS_GMM_LOCATION_MS); + OSMO_ASSERT(rc == 0); + osmo_gprs_gmm_enable_gprs(true); + + osmo_gprs_gmm_prim_set_up_cb(test_gmm_prim_up_cb, NULL); + osmo_gprs_gmm_prim_set_down_cb(test_gmm_prim_down_cb, NULL); + osmo_gprs_gmm_prim_set_llc_down_cb(test_gmm_prim_llc_down_cb, NULL); + + /* MS sends primitive to active a PDP ctx: */ + gmm_prim = osmo_gprs_gmm_prim_alloc_gmmsm_establish_req(sess_id); + OSMO_ASSERT(gmm_prim); + gmm_prim->gmmsm.establish_req.attach_type = OSMO_GPRS_GMM_ATTACH_TYPE_GPRS; + gmm_prim->gmmsm.establish_req.ptmsi = ptmsi; + OSMO_STRLCPY_ARRAY(gmm_prim->gmmsm.establish_req.imsi, imsi); + OSMO_STRLCPY_ARRAY(gmm_prim->gmmsm.establish_req.imei, imei); + OSMO_STRLCPY_ARRAY(gmm_prim->gmmsm.establish_req.imeisv, imeisv); + rc = osmo_gprs_gmm_prim_upper_down(gmm_prim); + OSMO_ASSERT(rc == 0); + /* MS sends GMM Attach Req first since its not eyt attached */ + + /* Network answers with GMM Identity Req: */ + llc_prim = gprs_llc_prim_alloc_ll_unitdata_ind(tlli, OSMO_GPRS_LLC_SAPI_GMM, (uint8_t *)pdu_gmm_identity_req, sizeof(pdu_gmm_identity_req)); + OSMO_ASSERT(llc_prim); + rc = osmo_gprs_gmm_prim_llc_lower_up(llc_prim); + OSMO_ASSERT(rc == 0); + /* As a result, MS answers GMM Identity Resp */ + + /* Network sends GMM Ciph Auth Req */ + llc_prim = gprs_llc_prim_alloc_ll_unitdata_ind(tlli, OSMO_GPRS_LLC_SAPI_GMM, (uint8_t *)pdu_gmm_auth_ciph_req, sizeof(pdu_gmm_auth_ciph_req)); + OSMO_ASSERT(llc_prim); + rc = osmo_gprs_gmm_prim_llc_lower_up(llc_prim); + OSMO_ASSERT(rc == 0); + /* As a result, MS answers GMM Ciph Auth Resp */ + + /* Network sends GMM Attach Accept */ + llc_prim = gprs_llc_prim_alloc_ll_unitdata_ind(tlli, OSMO_GPRS_LLC_SAPI_GMM, (uint8_t *)pdu_gmm_att_acc, sizeof(pdu_gmm_att_acc)); + OSMO_ASSERT(llc_prim); + rc = osmo_gprs_gmm_prim_llc_lower_up(llc_prim); + OSMO_ASSERT(rc == 0); + /* As a result, MS answers GMM Attach Complete */ + /* As a result, MS submits GMMSM Establish.cnf */ + + /* ... */ + + /* DEACT: TODO */ + + printf("==== %s() [end] ====\n", __func__); +} + static const struct log_info_cat test_log_categories[] = { }; static const struct log_info test_log_info = { .cat = test_log_categories, @@ -296,7 +379,8 @@ int main(int argc, char *argv[]) log_set_print_level(osmo_stderr_target, 1); log_set_use_color(osmo_stderr_target, 0); - test_gmm_prim_ms(); + test_gmm_prim_ms_gmmreg(); + test_gmm_prim_ms_gmmsm(); talloc_free(tall_ctx); } diff --git a/tests/gmm/gmm_prim_test.err b/tests/gmm/gmm_prim_test.err index 8e50caa..12d39da 100644 --- a/tests/gmm/gmm_prim_test.err +++ b/tests/gmm/gmm_prim_test.err @@ -2,8 +2,8 @@ DLGLOBAL INFO Rx from upper layers: GMMREG-ATTACH.request DLGLOBAL INFO GMM_MS{Null}: Allocated DLGLOBAL INFO GMM_MS{Null}: Received Event ENABLE_GPRS_MODE DLGLOBAL INFO GMM_MS{Null}: state_chg to Deregistered -DLGLOBAL INFO GMME(PTMSI-00000000) Tx GMM ATTACH REQUEST (new P-TMSI=0x00000000) DLGLOBAL INFO GMM_MS{Deregistered}: Received Event ATTACH_REQUESTED +DLGLOBAL INFO GMME(PTMSI-00000000) Tx GMM ATTACH REQUEST (new P-TMSI=0x00000000) DLGLOBAL INFO GMM_MS{Deregistered}: state_chg to RegisteredInitiated DLGLOBAL INFO Rx from lower layers: LL-UNITDATA.indication DLGLOBAL DEBUG GMME(PTMSI-00000000) Rx GMM IDENTITY REQUEST mi_type=IMEI @@ -24,3 +24,23 @@ DLGLOBAL INFO Rx from lower layers: LL-UNITDATA.indication DLGLOBAL DEBUG GMME(PTMSI-ea711b41) Rx GMM DETACH ACCEPT (MO) force_standby_indicated=false DLGLOBAL INFO GMM_MS{DeregisteredInitiated}: Received Event DETACH_ACCEPTED DLGLOBAL INFO GMM_MS{DeregisteredInitiated}: state_chg to Deregistered +DLGLOBAL DEBUG GMME(PTMSI-ffffffff) free() +DLGLOBAL INFO GMM_MS{Deregistered}: Deallocated +DLGLOBAL INFO Rx from upper layers: GMMSM-ESTABLISH.request +DLGLOBAL INFO GMM_MS{Null}: Allocated +DLGLOBAL INFO GMM_MS{Null}: Received Event ENABLE_GPRS_MODE +DLGLOBAL INFO GMM_MS{Null}: state_chg to Deregistered +DLGLOBAL INFO GMM_MS{Deregistered}: Received Event ATTACH_REQUESTED +DLGLOBAL INFO GMME(PTMSI-00000000) Tx GMM ATTACH REQUEST (new P-TMSI=0x00000000) +DLGLOBAL INFO GMM_MS{Deregistered}: state_chg to RegisteredInitiated +DLGLOBAL INFO Rx from lower layers: LL-UNITDATA.indication +DLGLOBAL DEBUG GMME(PTMSI-00000000) Rx GMM IDENTITY REQUEST mi_type=IMEI +DLGLOBAL INFO GMME(PTMSI-00000000) Tx GMM IDENTITY RESPONSE +DLGLOBAL INFO Rx from lower layers: LL-UNITDATA.indication +DLGLOBAL DEBUG GMME(PTMSI-00000000) Rx GMM AUTHENTICATION AND CIPHERING REQUEST +DLGLOBAL INFO GMME(PTMSI-00000000) Tx GMM GMM AUTHENTICATION AND CIPHERING RESPONSE +DLGLOBAL INFO Rx from lower layers: LL-UNITDATA.indication +DLGLOBAL DEBUG GMME(PTMSI-00000000) Rx GMM ATTACH ACCEPT +DLGLOBAL INFO GMME(PTMSI-ea711b41) Tx GMM ATTACH COMPL +DLGLOBAL INFO GMM_MS{RegisteredInitiated}: Received Event ATTACH_ACCEPTED +DLGLOBAL INFO GMM_MS{RegisteredInitiated}: state_chg to Registered diff --git a/tests/gmm/gmm_prim_test.ok b/tests/gmm/gmm_prim_test.ok index ed4f7e1..dbbb858 100644 --- a/tests/gmm/gmm_prim_test.ok +++ b/tests/gmm/gmm_prim_test.ok @@ -1,13 +1,23 @@ -==== test_gmm_prim_ms() [start] ==== +==== test_gmm_prim_ms_gmmreg() [start] ==== test_gmm_prim_llc_down_cb(): Rx LL-UNITDATA.request TLLI=0x00000000 SAPI=GMM l3=[08 01 04 97 07 00 00 01 0a 00 05 f4 00 00 00 00 00 f0 00 00 00 00 00 ] test_gmm_prim_llc_down_cb(): Rx LL-UNITDATA.request TLLI=0x00000000 SAPI=GMM l3=[08 16 08 42 32 24 43 32 24 43 f2 ] test_gmm_prim_llc_down_cb(): Rx LL-UNITDATA.request TLLI=0x00000000 SAPI=GMM l3=[08 13 02 ] test_gmm_prim_llc_down_cb(): Rx LLGM-ASSIGN.request TLLI=0x00000000 -test_gmm_prim_up_cb(): Rx GMMREG-ATTACH.confirm accepted=1 rej_cause=0 test_gmm_prim_llc_down_cb(): Rx LLGM-ASSIGN.request TLLI=0x00000000 test_gmm_prim_down_cb(): Rx GMRR-ASSIGN.request new_tlli=0xea711b41 test_gmm_prim_llc_down_cb(): Rx LL-UNITDATA.request TLLI=0xea711b41 SAPI=GMM l3=[08 03 ] +test_gmm_prim_up_cb(): Rx GMMREG-ATTACH.confirm accepted=1 rej_cause=0 test_gmm_prim_llc_down_cb(): Rx LL-UNITDATA.request TLLI=0xea711b41 SAPI=GMM l3=[08 05 20 0a 00 05 f4 ea 71 1b 41 ] test_gmm_prim_llc_down_cb(): Rx LLGM-ASSIGN.request TLLI=0x00000000 test_gmm_prim_up_cb(): Rx GMMREG-DETACH.confirm detach_type='GPRS detach' -==== test_gmm_prim_ms() [end] ==== +==== test_gmm_prim_ms_gmmreg() [end] ==== +==== test_gmm_prim_ms_gmmsm() [start] ==== +test_gmm_prim_llc_down_cb(): Rx LL-UNITDATA.request TLLI=0x00000000 SAPI=GMM l3=[08 01 04 97 07 00 00 01 0a 00 05 f4 00 00 00 00 00 f0 00 00 00 00 00 ] +test_gmm_prim_llc_down_cb(): Rx LL-UNITDATA.request TLLI=0x00000000 SAPI=GMM l3=[08 16 08 42 32 24 43 32 24 43 f2 ] +test_gmm_prim_llc_down_cb(): Rx LL-UNITDATA.request TLLI=0x00000000 SAPI=GMM l3=[08 13 02 ] +test_gmm_prim_llc_down_cb(): Rx LLGM-ASSIGN.request TLLI=0x00000000 +test_gmm_prim_llc_down_cb(): Rx LLGM-ASSIGN.request TLLI=0x00000000 +test_gmm_prim_down_cb(): Rx GMRR-ASSIGN.request new_tlli=0xea711b41 +test_gmm_prim_llc_down_cb(): Rx LL-UNITDATA.request TLLI=0xea711b41 SAPI=GMM l3=[08 03 ] +test_gmm_prim_up_cb(): Rx GMMSM-ESTABLISH.confirm sess_id=1234 accepted=1 rej_cause=0 +==== test_gmm_prim_ms_gmmsm() [end] ==== diff --git a/tests/sm/Makefile.am b/tests/sm/Makefile.am new file mode 100644 index 0000000..f9df676 --- /dev/null +++ b/tests/sm/Makefile.am @@ -0,0 +1,33 @@ +AM_CPPFLAGS = \ + -I$(top_srcdir)/include/ \ + $(NULL) + +AM_CFLAGS = \ + -Wall \ + $(LIBOSMOGSM_CFLAGS) \ + $(LIBOSMOCORE_CFLAGS) \ + $(NULL) + +AM_LDFLAGS = \ + -no-install \ + $(NULL) + +check_PROGRAMS = \ + sm_prim_test \ + $(NULL) + +EXTRA_DIST = \ + sm_prim_test.err \ + sm_prim_test.ok \ + $(NULL) + +# libosmo-gprs-gmm.a is used below to access non-exported private symbols used in the test: +# libosmo-gprs-gmm.a requires libosmo-gprs-llc.la +sm_prim_test_SOURCES = sm_prim_test.c +sm_prim_test_LDADD = \ + $(top_builddir)/src/sm/libosmo-gprs-sm.la \ + $(top_builddir)/src/gmm/.libs/libosmo-gprs-gmm.a \ + $(top_builddir)/src/llc/.libs/libosmo-gprs-llc.la \ + $(LIBOSMOGSM_LIBS) \ + $(LIBOSMOCORE_LIBS) \ + $(NULL) diff --git a/tests/sm/sm_prim_test.c b/tests/sm/sm_prim_test.c new file mode 100644 index 0000000..5703985 --- /dev/null +++ b/tests/sm/sm_prim_test.c @@ -0,0 +1,283 @@ +/* sm_prim tests + * + * (C) 2023 by sysmocom - s.f.m.c. GmbH + * Author: Pau espin Pedrol + * + * 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 . + * + */ + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +static void *tall_ctx = NULL; + +static uint32_t last_gmm_establish_sess_id = 0; + +/* +GSM A-I/F DTAP - Activate PDP Context Accept + Protocol Discriminator: GPRS session management messages (10) + .... 1010 = Protocol discriminator: GPRS session management messages (0xa) + 1... .... = TI flag: allocated by receiver + .000 .... = TIO: 0 + 01.. .... = Sequence number: 1 + DTAP GPRS Session Management Message Type: Activate PDP Context Accept (0x42) + LLC Service Access Point Identifier - Negotiated LLC SAPI + 0000 .... = Spare bit(s): 0 + .... 0011 = LLC SAPI: SAPI 3 (3) + Quality Of Service - Negotiated QoS + Length: 14 + 00.. .... = Spare bit(s): 0 + ..10 0... = Quality of Service Delay class: Delay class 4 (best effort) (4) + .... .011 = Reliability class: Unacknowledged GTP/LLC, Ack RLC, Protected data (3) + 0110 .... = Peak throughput: Up to 32 000 octet/s (6) + .... 0... = Spare bit(s): 0 + .... .010 = Precedence class: Normal priority (2) + 000. .... = Spare bit(s): 0 + ...1 1111 = Mean throughput: Best effort (31) + 011. .... = Traffic class: Interactive class (3) + ...1 0... = Delivery order: Without delivery order ('no') (2) + .... .010 = Delivery of erroneous SDUs: Erroneous SDUs are delivered('yes') (2) + Maximum SDU size: 1520 octets (153) + Maximum bitrate for uplink: 63 kbps (63) + Maximum bitrate for downlink: 63 kbps (63) + 0001 .... = Residual Bit Error Rate (BER): 5*10-2 (1) + .... 0001 = SDU error ratio: 1*10-2 (1) + 0100 00.. = Transfer delay: 200 ms (16) + .... ..11 = Traffic handling priority: Priority level 3 (3) + Guaranteed bitrate for uplink: 0 kbps (255) + Guaranteed bitrate for downlink: 0 kbps (255) + 000. .... = Spare bit(s): 0 + ...0 .... = Signalling indication: Not optimised for signalling traffic + .... 0000 = Source statistics description: unknown (0) + Maximum bitrate for downlink (extended): Use the value indicated by the Maximum bit rate for downlink (0) + Guaranteed bitrate for downlink (extended): Use the value indicated by the Guaranteed bit rate for downlink (0) + Radio Priority + .... .100 = Radio Priority (PDP or SMS): priority level 4 (lowest) (4) + Packet Data Protocol Address - PDP address + Element ID: 0x2b + Length: 6 + 0000 .... = Spare bit(s): 0 + .... 0001 = PDP type organization: IETF allocated address (1) + PDP type number: IPv4 address (33) + IPv4 address: 176.16.222.2 + Protocol Configuration Options + Element ID: 0x27 + Length: 20 + [Link direction: Network to MS (1)] + 1... .... = Extension: True + .... .000 = Configuration Protocol: PPP for use with IP PDP type or IP PDN type (0) + Protocol or Container ID: Internet Protocol Control Protocol (0x8021) + Length: 0x10 (16) + PPP IP Control Protocol + Code: Configuration Ack (2) + Identifier: 0 (0x00) + Length: 16 + Options: (12 bytes), Primary DNS Server IP Address, Secondary DNS Server IP Address + Primary DNS Server IP Address + Type: Primary DNS Server IP Address (129) + Length: 6 + Primary DNS Address: 8.8.8.8 + Secondary DNS Server IP Address + Type: Secondary DNS Server IP Address (131) + Length: 6 + Secondary DNS Address: 8.8.8.4 +*/ + +static uint8_t pdu_sm_act_pdp_ctx_acc[] = { +0x8a, 0x42, 0x03, 0x0e, 0x23, 0x62, 0x1f, 0x72, +0x99, 0x3f, 0x3f, 0x11, 0x43, 0xff, 0xff, 0x00, +0x00, 0x00, 0x04, 0x2b, 0x06, 0x01, 0x21, 0xb0, +0x10, 0xde, 0x02, 0x27, 0x14, 0x80, 0x80, 0x21, +0x10, 0x02, 0x00, 0x00, 0x10, 0x81, 0x06, 0x08, +0x08, 0x08, 0x08, 0x83, 0x06, 0x08, 0x08, 0x08, +0x04 +}; + +int test_sm_prim_up_cb(struct osmo_gprs_sm_prim *sm_prim, void *user_data) +{ + const char *pdu_name = osmo_gprs_sm_prim_name(sm_prim); + + switch (sm_prim->oph.sap) { + case OSMO_GPRS_SM_SAP_SMREG: + switch (OSMO_PRIM_HDR(&sm_prim->oph)) { + case OSMO_PRIM(OSMO_GPRS_SM_SMREG_PDP_ACTIVATE, PRIM_OP_CONFIRM): + printf("%s(): Rx %s\n", __func__, pdu_name); + break; + default: + printf("%s(): Unexpected Rx %s\n", __func__, pdu_name); + OSMO_ASSERT(0) + } + break; + default: + printf("%s(): Unexpected Rx %s\n", __func__, pdu_name); + OSMO_ASSERT(0); + } + return 0; +} + +int test_sm_prim_down_cb(struct osmo_gprs_sm_prim *sm_prim, void *user_data) +{ + const char *pdu_name = osmo_gprs_sm_prim_name(sm_prim); + + switch (sm_prim->oph.sap) { + case OSMO_GPRS_SM_SAP_SMREG: + switch (OSMO_PRIM_HDR(&sm_prim->oph)) { + case OSMO_PRIM(OSMO_GPRS_SM_SMREG_PDP_ACTIVATE, PRIM_OP_REQUEST): + printf("%s(): Rx %s\n", __func__, pdu_name); + break; + default: + printf("%s(): Unexpected Rx %s\n", __func__, pdu_name); + OSMO_ASSERT(0) + } + break; + default: + printf("%s(): Unexpected Rx %s\n", __func__, pdu_name); + OSMO_ASSERT(0); + } + return 0; +} + +int test_sm_prim_gmm_down_cb(struct osmo_gprs_gmm_prim *gmm_prim, void *user_data) +{ + const char *pdu_name = osmo_gprs_gmm_prim_name(gmm_prim); + + switch (gmm_prim->oph.sap) { + case OSMO_GPRS_GMM_SAP_GMMSM: + switch (OSMO_PRIM_HDR(&gmm_prim->oph)) { + case OSMO_PRIM(OSMO_GPRS_GMM_GMMSM_ESTABLISH, PRIM_OP_REQUEST): + printf("%s(): Rx %s sess_id=%u\n", __func__, pdu_name, + gmm_prim->gmmsm.sess_id); + last_gmm_establish_sess_id = gmm_prim->gmmsm.sess_id; + break; + case OSMO_PRIM(OSMO_GPRS_GMM_GMMSM_UNITDATA, PRIM_OP_REQUEST): + printf("%s(): Rx %s sess_id=%u SMPDU=[%s]\n", __func__, pdu_name, + gmm_prim->gmmsm.sess_id, + osmo_hexdump(gmm_prim->gmmsm.unitdata_req.smpdu, + gmm_prim->gmmsm.unitdata_req.smpdu_len)); + break; + default: + printf("%s(): Unexpected Rx %s\n", __func__, pdu_name); + OSMO_ASSERT(0); + } + break; + case OSMO_GPRS_GMM_SAP_GMMRR: + printf("%s(): Rx %s\n", __func__, pdu_name); + break; + default: + printf("%s(): Unexpected Rx %s\n", __func__, pdu_name); + OSMO_ASSERT(0); + } + return 0; +} + +static void test_sm_prim_ms(void) +{ + struct osmo_gprs_sm_prim *sm_prim; + struct osmo_gprs_gmm_prim *gmm_prim; + int rc; + uint8_t nsapi = 6; + enum osmo_gprs_sm_llc_sapi llc_sapi = OSMO_GPRS_SM_LLC_SAPI_SAPI3; + struct osmo_sockaddr pdp_addr_any = {0}; + uint8_t qos[OSMO_GPRS_SM_QOS_MAXLEN] = {0}; + uint8_t pco[OSMO_GPRS_SM_QOS_MAXLEN] = {0}; + char apn[OSMO_GPRS_SM_APN_MAXLEN] = "apn"; + uint32_t ptmsi = 0x00000000; + char *imsi = "1234567890"; + char *imei = "42342342342342"; + char *imeisv = "4234234234234275"; + + printf("==== %s() [start] ====\n", __func__); + + rc = osmo_gprs_sm_init(OSMO_GPRS_SM_LOCATION_MS); + OSMO_ASSERT(rc == 0); + + osmo_gprs_sm_prim_set_up_cb(test_sm_prim_up_cb, NULL); + osmo_gprs_sm_prim_set_down_cb(test_sm_prim_down_cb, NULL); + osmo_gprs_sm_prim_set_gmm_down_cb(test_sm_prim_gmm_down_cb, NULL); + + /* MS sends SM PDP Act Req (GMMSM-ESTABLISH.req is submitted down to GMM layer) */ + sm_prim = osmo_gprs_sm_prim_alloc_smreg_pdp_act_req(); + OSMO_ASSERT(sm_prim); + sm_prim->smreg.pdp_act_req.nsapi = nsapi; + sm_prim->smreg.pdp_act_req.llc_sapi = llc_sapi; + sm_prim->smreg.pdp_act_req.pdp_addr_ietf_type = OSMO_GPRS_SM_PDP_ADDR_IETF_IPV4; + sm_prim->smreg.pdp_act_req.pdp_addr_v4 = pdp_addr_any; + memcpy(sm_prim->smreg.pdp_act_req.qos, qos, sizeof(qos)); + sm_prim->smreg.pdp_act_req.qos_len = 1; + memcpy(sm_prim->smreg.pdp_act_req.pco, pco, sizeof(pco)); + sm_prim->smreg.pdp_act_req.pco_len = 1; + OSMO_STRLCPY_ARRAY(sm_prim->smreg.pdp_act_req.apn, apn); + sm_prim->smreg.pdp_act_req.gmm.ptmsi = ptmsi; + OSMO_STRLCPY_ARRAY(sm_prim->smreg.pdp_act_req.gmm.imsi, imsi); + OSMO_STRLCPY_ARRAY(sm_prim->smreg.pdp_act_req.gmm.imei, imei); + OSMO_STRLCPY_ARRAY(sm_prim->smreg.pdp_act_req.gmm.imeisv, imeisv); + rc = osmo_gprs_sm_prim_upper_down(sm_prim); + OSMO_ASSERT(rc == 0); + + /* GMM internaly does GMM Attach and confirms it is attached to SM: */ + gmm_prim = gprs_gmm_prim_alloc_gmmsm_establish_cnf(last_gmm_establish_sess_id, 0); + OSMO_ASSERT(gmm_prim); + rc = osmo_gprs_sm_prim_gmm_lower_up(gmm_prim); + OSMO_ASSERT(rc == 0); + + /* SM layer is now sending GMMSM-UNITDATA.req with the Active Pdp Context Req... */ + /* Network accepts the pdp ctx req with Activate Pdp Context Accept: */ + gmm_prim = gprs_gmm_prim_alloc_gmmsm_unitdata_ind(last_gmm_establish_sess_id, + (uint8_t *)pdu_sm_act_pdp_ctx_acc, + sizeof(pdu_sm_act_pdp_ctx_acc)); + OSMO_ASSERT(gmm_prim); + rc = osmo_gprs_sm_prim_gmm_lower_up(gmm_prim); + OSMO_ASSERT(rc == 0); + + printf("==== %s() [end] ====\n", __func__); +} + +static const struct log_info_cat test_log_categories[] = { }; +static const struct log_info test_log_info = { + .cat = test_log_categories, + .num_cat = ARRAY_SIZE(test_log_categories), +}; + +int main(int argc, char *argv[]) +{ + tall_ctx = talloc_named_const(NULL, 1, __FILE__); + + osmo_init_logging2(tall_ctx, &test_log_info); + log_parse_category_mask(osmo_stderr_target, "DLGLOBAL,1:"); + osmo_fsm_log_addr(false); + + log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_NONE); + log_set_print_category_hex(osmo_stderr_target, 0); + log_set_print_category(osmo_stderr_target, 1); + log_set_print_level(osmo_stderr_target, 1); + log_set_use_color(osmo_stderr_target, 0); + + test_sm_prim_ms(); + + talloc_free(tall_ctx); +} diff --git a/tests/sm/sm_prim_test.err b/tests/sm/sm_prim_test.err new file mode 100644 index 0000000..d9dc296 --- /dev/null +++ b/tests/sm/sm_prim_test.err @@ -0,0 +1,11 @@ +DLGLOBAL INFO Rx from upper layers: SMREG-PDP_ACTIVATE.request +DLGLOBAL INFO SM_MS{INACTIVE}: Allocated +DLGLOBAL INFO SM_MS{INACTIVE}: Received Event TX_ACT_PDP_CTX_REQ +DLGLOBAL INFO SM_MS{INACTIVE}: state_chg to PDP_ACTIVE_PENDING +DLGLOBAL INFO Rx from lower layers: GMMSM-ESTABLISH.confirm +DLGLOBAL INFO SM_MS{PDP_ACTIVE_PENDING}: Received Event RX GMM_ESTABLISH_CNF +DLGLOBAL INFO PDP(ID-0:NSAPI-6) Tx SM Activate PDP Context Request +DLGLOBAL INFO Rx from lower layers: GMMSM-UNITDATA.indication +DLGLOBAL INFO PDP(ID-0:NSAPI-6) Rx SM Activate PDP Context Accept +DLGLOBAL INFO SM_MS{PDP_ACTIVE_PENDING}: Received Event RX_ACT_PDP_CTX_ACC +DLGLOBAL INFO SM_MS{PDP_ACTIVE_PENDING}: state_chg to PDP_ACTIVE diff --git a/tests/sm/sm_prim_test.ok b/tests/sm/sm_prim_test.ok new file mode 100644 index 0000000..82d4a97 --- /dev/null +++ b/tests/sm/sm_prim_test.ok @@ -0,0 +1,5 @@ +==== test_sm_prim_ms() [start] ==== +test_sm_prim_gmm_down_cb(): Rx GMMSM-ESTABLISH.request sess_id=0 +test_sm_prim_gmm_down_cb(): Rx GMMSM-UNITDATA.request sess_id=0 SMPDU=[8a 41 06 03 01 00 06 10 21 00 00 00 00 28 04 27 01 00 ] +test_sm_prim_up_cb(): Rx SMREG-PDP_ACTIVATE.confirm +==== test_sm_prim_ms() [end] ==== diff --git a/tests/testsuite.at b/tests/testsuite.at index cfe25d4..55a54d7 100644 --- a/tests/testsuite.at +++ b/tests/testsuite.at @@ -57,6 +57,13 @@ cat $abs_srcdir/rlcmac/rlcmac_prim_test.err > experr AT_CHECK([$abs_top_builddir/tests/rlcmac/rlcmac_prim_test], [0], [expout], [experr]) AT_CLEANUP +AT_SETUP([sm/sm_prim]) +AT_KEYWORDS([sm sm_prim]) +cat $abs_srcdir/sm/sm_prim_test.ok > expout +cat $abs_srcdir/sm/sm_prim_test.err > experr +AT_CHECK([$abs_top_builddir/tests/sm/sm_prim_test], [0], [expout], [experr]) +AT_CLEANUP + AT_SETUP([sndcp/sndcp_prim]) AT_KEYWORDS([sndcp sndcp_prim]) cat $abs_srcdir/sndcp/sndcp_prim_test.ok > expout