pespin submitted this change.

View Change


Approvals: Jenkins Builder: Verified fixeria: Looks good to me, approved
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
---
M Makefile.am
M configure.ac
M contrib/libosmo-gprs.spec.in
M debian/control
A debian/libosmo-gprs-sm-dev.install
A debian/libosmo-gprs-sm0.install
M include/osmocom/gprs/Makefile.am
M include/osmocom/gprs/gmm/gmm_ms_fsm.h
M include/osmocom/gprs/gmm/gmm_prim.h
M include/osmocom/gprs/gmm/gmm_private.h
A include/osmocom/gprs/sm/Makefile.am
A include/osmocom/gprs/sm/sm.h
A include/osmocom/gprs/sm/sm_ms_fsm.h
A include/osmocom/gprs/sm/sm_pdu.h
A include/osmocom/gprs/sm/sm_prim.h
A include/osmocom/gprs/sm/sm_private.h
A libosmo-gprs-sm.pc.in
M src/Makefile.am
M src/gmm/gmm.c
M src/gmm/gmm_ms_fsm.c
M src/gmm/gmm_prim.c
A src/sm/Makefile.am
A src/sm/misc.c
A src/sm/sm.c
A src/sm/sm_ms_fsm.c
A src/sm/sm_pdu.c
A src/sm/sm_prim.c
M tests/Makefile.am
M tests/gmm/gmm_prim_test.c
M tests/gmm/gmm_prim_test.err
M tests/gmm/gmm_prim_test.ok
A tests/sm/Makefile.am
A tests/sm/sm_prim_test.c
A tests/sm/sm_prim_test.err
A tests/sm/sm_prim_test.ok
M tests/testsuite.at
36 files changed, 2,501 insertions(+), 60 deletions(-)

diff --git a/Makefile.am b/Makefile.am
index 4a55859..2c727b0 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -18,6 +18,7 @@
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 @@
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 @@
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 @@
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 @@
%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 @@
%_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 @@
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 @@
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 @@
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 @@
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 @@

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 @@
/* 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_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_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 @@
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 <stdint.h>
+#include <stddef.h>
+
+/* 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 <osmocom/core/fsm.h>
+#include <osmocom/gprs/sm/sm_prim.h>
+
+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 <osmocom/core/msgb.h>
+#include <osmocom/gsm/tlv.h>
+
+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 <stdint.h>
+#include <stddef.h>
+
+#include <osmocom/core/utils.h>
+#include <osmocom/core/prim.h>
+#include <osmocom/core/socket.h>
+#include <osmocom/gsm/protocol/gsm_23_003.h>
+
+#include <osmocom/gprs/sm/sm.h>
+
+
+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 <stdint.h>
+#include <stddef.h>
+#include <stdbool.h>
+
+#include <osmocom/core/timer.h>
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/core/endian.h>
+#include <osmocom/gsm/protocol/gsm_04_08.h>
+#include <osmocom/gsm/protocol/gsm_04_08_gprs.h>
+
+#include <osmocom/gprs/gmm/gmm_prim.h>
+#include <osmocom/gprs/sm/sm.h>
+#include <osmocom/gprs/sm/sm_prim.h>
+#include <osmocom/gprs/sm/sm_ms_fsm.h>
+
+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 @@
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 @@
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 @@
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 @@
}
}

- /* 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_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_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(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 @@
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 @@
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 @@
{
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 @@
}

/* 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 @@
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 @@
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_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 <info@sysmocom.de>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <osmocom/core/logging.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/gprs/sm/sm.h>
+#include <osmocom/gprs/sm/sm_ms_fsm.h>
+
+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 <info@sysmocom.de>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <stdint.h>
+#include <errno.h>
+#include <arpa/inet.h>
+
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/tdef.h>
+#include <osmocom/gsm/protocol/gsm_04_08_gprs.h>
+
+#include <osmocom/gprs/sm/sm.h>
+#include <osmocom/gprs/sm/sm_prim.h>
+#include <osmocom/gprs/sm/sm_private.h>
+#include <osmocom/gprs/sm/sm_ms_fsm.h>
+#include <osmocom/gprs/sm/sm_pdu.h>
+
+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 <info@sysmocom.de>
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: AGPL-3.0+
+ *
+ * Author: Pau Espin Pedrol <pespin@sysmocom.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#include <errno.h>
+#include <osmocom/core/tdef.h>
+#include <osmocom/core/utils.h>
+
+#include <osmocom/gprs/sm/sm_ms_fsm.h>
+#include <osmocom/gprs/sm/sm.h>
+#include <osmocom/gprs/sm/sm_private.h>
+
+#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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <stdint.h>
+#include <errno.h>
+
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/bitvec.h>
+#include <osmocom/core/endian.h>
+#include <osmocom/gsm/tlv.h>
+#include <osmocom/gsm/protocol/gsm_04_08.h>
+#include <osmocom/gsm/protocol/gsm_04_08_gprs.h>
+#include <osmocom/gsm/apn.h>
+
+#include <osmocom/gprs/sm/sm_private.h>
+#include <osmocom/gprs/sm/sm_pdu.h>
+
+
+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 <info@sysmocom.de>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <stdint.h>
+#include <errno.h>
+
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/core/logging.h>
+
+#include <osmocom/gprs/sm/sm.h>
+#include <osmocom/gprs/sm/sm_prim.h>
+#include <osmocom/gprs/sm/sm_private.h>
+#include <osmocom/gprs/gmm/gmm_prim.h>
+
+#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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 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 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 <info@sysmocom.de>
+ * Author: Pau espin Pedrol <pespin@sysmocom.de>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+
+#include <osmocom/core/application.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/core/fsm.h>
+#include <osmocom/core/msgb.h>
+
+#include <osmocom/gprs/gmm/gmm_prim.h>
+#include <osmocom/gprs/gmm/gmm_private.h>
+#include <osmocom/gprs/sm/sm.h>
+#include <osmocom/gprs/sm/sm_prim.h>
+
+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 @@
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

To view, visit change 32154. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-Project: libosmo-gprs
Gerrit-Branch: master
Gerrit-Change-Id: I9b4a9a6364f7799540475e7e1d10ab2310768281
Gerrit-Change-Number: 32154
Gerrit-PatchSet: 3
Gerrit-Owner: pespin <pespin@sysmocom.de>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: fixeria <vyanitskiy@sysmocom.de>
Gerrit-Reviewer: pespin <pespin@sysmocom.de>
Gerrit-MessageType: merged