diff --git a/include/osmocom/gprs/gmm/Makefile.am b/include/osmocom/gprs/gmm/Makefile.am index c8d9baf..2adfed1 100644 --- a/include/osmocom/gprs/gmm/Makefile.am +++ b/include/osmocom/gprs/gmm/Makefile.am @@ -1,6 +1,8 @@ noinst_HEADERS = \ + gmm_ms_fsm.h \ gmm_private.h \ + gmm_pdu.h \ $(NULL) gmm_HEADERS = \ diff --git a/include/osmocom/gprs/gmm/gmm.h b/include/osmocom/gprs/gmm/gmm.h index bbd2213..49954e8 100644 --- a/include/osmocom/gprs/gmm/gmm.h +++ b/include/osmocom/gprs/gmm/gmm.h @@ -20,3 +20,5 @@ enum osmo_gprs_gmm_log_cat { }; void osmo_gprs_gmm_set_log_cat(enum osmo_gprs_gmm_log_cat logc, int logc_num); + +void osmo_gprs_gmm_enable_gprs(bool enable_gprs); diff --git a/include/osmocom/gprs/gmm/gmm_ms_fsm.h b/include/osmocom/gprs/gmm/gmm_ms_fsm.h new file mode 100644 index 0000000..9639a9b --- /dev/null +++ b/include/osmocom/gprs/gmm/gmm_ms_fsm.h @@ -0,0 +1,51 @@ +#pragma once + +#include + +struct gprs_gmm_entity; + +/* 3GPP TS 24.008 § 4.1.3.1 GMM states in the MS */ +enum gprs_gmm_ms_fsm_states { + GPRS_GMM_MS_ST_NULL, /* 4.1.3.1.1.1 */ + GPRS_GMM_MS_ST_DEREGISTERED, /* 4.1.3.1.1.2 */ + GPRS_GMM_MS_ST_REGISTERED_INITIATED, /* 4.1.3.1.1.3 */ + GPRS_GMM_MS_ST_REGISTERED, /* 4.1.3.1.1.4 */ + GPRS_GMM_MS_ST_DEREGISTERED_INITIATED, /* 4.1.3.1.1.5 */ + GPRS_GMM_MS_ST_RAU_INITIATED, /* 4.1.3.1.1.6 */ + GPRS_GMM_MS_ST_SR_INITIATED, /* 4.1.3.1.1.7 (Iu only) */ + //GPRS_GMM_MS_ST_DEREGISTERED_NORMAL, /* 4.1.3.1.2.1 */ + //GPRS_GMM_MS_ST_DEREGISTERED_LIMITED, /* 4.1.3.1.2.2 */ + //GPRS_GMM_MS_ST_DEREGISTERED_ATTACH_NEEDED, /* 4.1.3.1.2.3 */ + //GPRS_GMM_MS_ST_DEREGISTERED_ATTEMTPING_ATTACH, /* 4.1.3.1.2.4 */ + //GPRS_GMM_MS_ST_DEREGISTERED_NO_IMSI, /* 4.1.3.1.2.5 */ + //GPRS_GMM_MS_ST_DEREGISTERED_NO_CELL_AVAIL, /* 4.1.3.1.2.6 */ + //GPRS_GMM_MS_ST_DEREGISTERED_PLMN_SEARCH, /* 4.1.3.1.2.7 */ + //GPRS_GMM_MS_ST_DEREGISTERED_SUSPENDED, /* 4.1.3.1.2.8 (Gb only) */ +}; + +enum gprs_gmm_ms_fsm_events { + GPRS_GMM_MS_EV_ENABLE_GPRS_MODE, + GPRS_GMM_MS_EV_DISABLE_GPRS_MODE, + GPRS_GMM_MS_EV_ATTACH_REQUESTED, + GPRS_GMM_MS_EV_ATTACH_REJECTED, + GPRS_GMM_MS_EV_ATTACH_ACCEPTED, + GPRS_GMM_MS_EV_DETACH_REQUESTED, /* poweroff or network initiated */ + GPRS_GMM_MS_EV_DETACH_ACCEPTED, + GPRS_GMM_MS_EV_SR_REQUESTED, /* (Iu only) */ + GPRS_GMM_MS_EV_SR_REJECTED, /* (Iu only) */ + GPRS_GMM_MS_EV_SR_ACCEPTED, /* (Iu only) */ + GPRS_GMM_MS_EV_RAU_REQUESTED, + GPRS_GMM_MS_EV_RAU_REJECTED, + GPRS_GMM_MS_EV_RAU_ACCEPTED, + GPRS_GMM_MS_EV_LOW_LVL_FAIL, +}; + +struct gprs_gmm_ms_fsm_ctx { + struct osmo_fsm_inst *fi; + struct gprs_gmm_entity *gmme; +}; + +int gprs_gmm_ms_fsm_init(void); +void gprs_gmm_ms_fsm_set_log_cat(int logcat); + +int gprs_gmm_ms_fsm_ctx_init(struct gprs_gmm_ms_fsm_ctx *ctx, struct gprs_gmm_entity *gmme); diff --git a/include/osmocom/gprs/gmm/gmm_pdu.h b/include/osmocom/gprs/gmm/gmm_pdu.h new file mode 100644 index 0000000..5a9faa1 --- /dev/null +++ b/include/osmocom/gprs/gmm/gmm_pdu.h @@ -0,0 +1,33 @@ +/* GMM PDUs, 3GPP TS 9.4 24.008 GPRS Mobility Management Messages */ +#pragma once + +#include +#include + +struct gprs_gmm_entity; + +#define GPRS_GMM_ALLOC_SIZE 2048 +#define GPRS_GMM_ALLOC_HEADROOM 256 + +static inline struct msgb *gprs_gmm_msgb_alloc_name(const char *name) +{ + return msgb_alloc_headroom(GPRS_GMM_ALLOC_SIZE, GPRS_GMM_ALLOC_HEADROOM, name); +} + +extern const struct tlv_definition gprs_gmm_att_tlvdef; +#define gprs_gmm_tlv_parse(dec, buf, len) \ + tlv_parse(dec, &gprs_gmm_att_tlvdef, buf, len, 0, 0) + +int gprs_gmm_build_attach_req(struct gprs_gmm_entity *gmme, + enum osmo_gprs_gmm_attach_type attach_type, + bool attach_with_imsi, + struct msgb *msg); + +int gprs_gmm_build_attach_compl(struct gprs_gmm_entity *gmme, struct msgb *msg); + +int gprs_gmm_build_identity_resp(struct gprs_gmm_entity *gmme, + uint8_t mi_type, + struct msgb *msg); + +int gprs_gmm_build_ciph_auth_resp(struct gprs_gmm_entity *gmme, bool imeisv_requested, + uint8_t ac_ref_nr, const uint8_t sres[4], struct msgb *msg); diff --git a/include/osmocom/gprs/gmm/gmm_prim.h b/include/osmocom/gprs/gmm/gmm_prim.h index a610986..6537075 100644 --- a/include/osmocom/gprs/gmm/gmm_prim.h +++ b/include/osmocom/gprs/gmm/gmm_prim.h @@ -13,12 +13,15 @@ #include #include +#include #include +struct osmo_gprs_llc_prim; + /* 3GPP TS 24.007 (index, "GMMR") */ enum osmo_gprs_gmm_prim_sap { OSMO_GPRS_GMM_SAP_GMMREG, /* 6.6 */ - OSMO_GPRS_GMM_SAP_GMMRR, /* GSM only */ + OSMO_GPRS_GMM_SAP_GMMRR, /* 9.3.2, GSM only */ OSMO_GPRS_GMM_SAP_GMMAS, /* UMTS only */ OSMO_GPRS_GMM_SAP_LLGMM, OSMO_GPRS_GMM_SAP_GMMSM, @@ -33,7 +36,7 @@ static inline const char *osmo_gprs_gmm_prim_sap_name(enum osmo_gprs_gmm_prim_sa return get_value_string(osmo_gprs_gmm_prim_sap_names, val); } -/* 6.6 Registration Services for GPRS-Services */ +/* 3GPP TS 24.007 Section 6.6 Registration Services for GPRS-Services */ enum osmo_gprs_gmm_gmmreg_prim_type { OSMO_GPRS_GMM_GMMREG_ATTACH, /* Req/Cnf/Rej */ OSMO_GPRS_GMM_GMMREG_DETACH, /* Req/Cnf/Ind */ @@ -44,6 +47,16 @@ static inline const char *osmo_gprs_gmm_gmmreg_prim_type_name(enum osmo_gprs_gmm return get_value_string(osmo_gprs_gmm_gmmreg_prim_type_names, val); } +/* Attach type 10.5.5.2 */ +enum osmo_gprs_gmm_attach_type { + /* 0 is reserved */ + OSMO_GPRS_GMM_ATTACH_TYPE_GPRS = 1, + OSMO_GPRS_GMM_ATTACH_TYPE_COMBINED_OLD = 2, /* Table 10.5.135b NOTE 1 */ + OSMO_GPRS_GMM_ATTACH_TYPE_COMBINED = 3, + OSMO_GPRS_GMM_ATTACH_TYPE_EMERGENCY = 4, + /* others: reserved, interpreted as OSMO_GPRS_GMM_ATTACH_TYPE_GPRS */ +}; + /* Parameters for OSMO_GPRS_GMM_GMMREG_* prims */ struct osmo_gprs_gmm_gmmreg_prim { /* Common fields */ @@ -51,6 +64,12 @@ struct osmo_gprs_gmm_gmmreg_prim { union { /* OSMO_GPRS_GMM_GMMREG_ATTACH | Req, 6.6.1.1 */ 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 */ } attach_req; /* OSMO_GPRS_GMM_GMMREG_ATTACH | Cnf 6.6.1.2 / Rej 6.6.1.3 */ @@ -80,10 +99,83 @@ struct osmo_gprs_gmm_gmmreg_prim { }; }; +/* TS 24.007 Section 9.3.2 "Service primitives for GMMRR-SAP (GSM only)" + * Same as enum osmo_gprs_rlcmac_gmmrr_prim_type. + */ +enum osmo_gprs_gmm_gmmrr_prim_type { + OSMO_GPRS_GMM_GMMRR_ASSIGN, /* Req: newTLLI */ + OSMO_GPRS_GMM_GMMRR_PAGE, /* Ind: TLLI */ +}; +extern const struct value_string osmo_gprs_gmm_gmmrr_prim_type_names[]; +static inline const char *osmo_gprs_gmm_gmmrr_prim_type_name(enum osmo_gprs_gmm_gmmrr_prim_type val) +{ + return get_value_string(osmo_gprs_gmm_gmmrr_prim_type_names, val); +} + +/* Parameters for OSMO_GPRS_GMM_GMMRR_* prims + * Same as struct osmo_gprs_rlcmac_gmmrr_prim. + */ +struct osmo_gprs_gmm_gmmrr_prim { + /* Common fields (none) */ + union { + /* OSMO_GPRS_GMM_GMMRR_ASSIGN | Req */ + struct { + uint32_t new_tlli; + } assign_req; + /* OSMO_GPRS_GMM_GMMRR_PAGE | Ind */ + struct { + uint32_t tlli; + } page_ind; + }; +}; + +/* TS 24.007 Section 9.5.1 "Service primitives for GMMSM-SAP" + */ +enum osmo_gprs_gmm_gmmsm_prim_type { + OSMO_GPRS_GMM_GMMSM_ESTABLISH, /* Req, Cnf/Rej */ + OSMO_GPRS_GMM_GMMSM_RELEASE, /* Ind */ + OSMO_GPRS_GMM_GMMSM_UNITDATA, /* Req, Ind */ +}; +extern const struct value_string osmo_gprs_gmm_gmmsm_prim_type_names[]; +static inline const char *osmo_gprs_gmm_gmmsm_prim_type_name(enum osmo_gprs_gmm_gmmsm_prim_type val) +{ + return get_value_string(osmo_gprs_gmm_gmmsm_prim_type_names, val); +} + +/* Parameters for OSMO_GPRS_GMM_GMMSM_* prims + */ +struct osmo_gprs_gmm_gmmsm_prim { + /* Common fields (none) */ + union { + /* OSMO_GPRS_GMM_GMMSM_ESTABLISH | Req */ + struct { + } establish_req; + /* OSMO_GPRS_GMM_GMMSM_ESTABLISH | Cnf/Rej */ + struct { + uint8_t cause; + } establish_cnf; + /* OSMO_GPRS_GMM_GMMSM_RELEASE | Ind */ + struct { + } page_ind; + /* OSMO_GPRS_GMM_GMMSM_UNITDATA | Req */ + struct { + uint8_t *smpdu; + uint16_t smpdu_len; + } unitdata_req; + /* OSMO_GPRS_GMM_GMMSM_UNITDATA | Ind */ + struct { + uint8_t *smpdu; + uint16_t smpdu_len; + } unitdata_ind; + }; +}; + struct osmo_gprs_gmm_prim { struct osmo_prim_hdr oph; union { struct osmo_gprs_gmm_gmmreg_prim gmmreg; + struct osmo_gprs_gmm_gmmrr_prim gmmrr; + struct osmo_gprs_gmm_gmmsm_prim gmmsm; }; }; @@ -93,10 +185,22 @@ void osmo_gprs_gmm_prim_set_up_cb(osmo_gprs_gmm_prim_up_cb up_cb, void *up_user_ typedef int (*osmo_gprs_gmm_prim_down_cb)(struct osmo_gprs_gmm_prim *gmm_prim, void *down_user_data); void osmo_gprs_gmm_prim_set_down_cb(osmo_gprs_gmm_prim_down_cb down_cb, void *down_user_data); + +typedef int (*osmo_gprs_gmm_prim_llc_down_cb)(struct osmo_gprs_llc_prim *llc_prim, void *llc_down_user_data); +void osmo_gprs_gmm_prim_set_llc_down_cb(osmo_gprs_gmm_prim_llc_down_cb llc_down_cb, void *llc_down_user_data); + int osmo_gprs_gmm_prim_upper_down(struct osmo_gprs_gmm_prim *gmm_prim); int osmo_gprs_gmm_prim_lower_up(struct osmo_gprs_gmm_prim *gmm_prim); +int osmo_gprs_gmm_prim_llc_lower_up(struct osmo_gprs_llc_prim *llc_prim); const char *osmo_gprs_gmm_prim_name(const struct osmo_gprs_gmm_prim *gmm_prim); /* Alloc primitive for GMMREG SAP: */ struct osmo_gprs_gmm_prim *osmo_gprs_gmm_prim_alloc_gmmreg_attach_req(void); + +/* Alloc primitive for GMMRR SAP: */ +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); diff --git a/include/osmocom/gprs/gmm/gmm_private.h b/include/osmocom/gprs/gmm/gmm_private.h index d0a40a7..87b9843 100644 --- a/include/osmocom/gprs/gmm/gmm_private.h +++ b/include/osmocom/gprs/gmm/gmm_private.h @@ -10,8 +10,12 @@ #include #include #include +#include +#include #include +#include +#include extern int g_gmm_log_cat[_OSMO_GPRS_GMM_LOGC_MAX]; @@ -27,6 +31,15 @@ struct gprs_gmm_ctx { osmo_gprs_gmm_prim_down_cb gmm_down_cb; void *gmm_down_cb_user_data; + osmo_gprs_gmm_prim_llc_down_cb gmm_llc_down_cb; + void *gmm_llc_down_cb_user_data; + + /* GPRS enabled in the system: 3GPP TS 24.008 Figure 4.1b, + * transition GMM-NULL<->GMM-DEREGISTERED */ + bool gprs_enabled; + + struct osmo_tdef *T_defs; /* timers controlled by GMM layer */ + struct llist_head gmme_list; /* list of struct gprs_gmm_entity->list */ }; @@ -35,18 +48,46 @@ extern struct gprs_gmm_ctx *g_ctx; /* GMM Entity: */ struct gprs_gmm_entity { struct llist_head list; /* item in (struct gprs_gmm_ctx)->gmme_list */ + + struct gprs_gmm_ms_fsm_ctx ms_fsm; + uint32_t ptmsi; + uint32_t old_ptmsi; + char imsi[OSMO_IMSI_BUF_SIZE]; + char imei[GSM23003_IMEI_NUM_DIGITS + 1]; + char imeisv[GSM23003_IMEISV_NUM_DIGITS+1]; + + uint8_t ra_upd_timer; /* TS 24.008 10.5.7.3 */ + uint8_t radio_prio; /* TS 24.008 10.5.7.2 */ + struct gprs_ra_id ra; /* TS 24.008 10.5.5.15 (decoded) */ + + uint8_t gea; /* GEA/0 = 0, GEA/1 = 1, ... */ + uint8_t kc[16]; /* max 16 * 8 = 128 bits */ }; /* gmm_prim.c: */ -struct osmo_gprs_gmm_prim *gprs_gmm_prim_alloc_gmm_gmmreg_attach_cnf(void); int gprs_gmm_prim_call_up_cb(struct osmo_gprs_gmm_prim *gmm_prim); int gprs_gmm_prim_call_down_cb(struct osmo_gprs_gmm_prim *gmm_prim); +int gprs_gmm_prim_call_llc_down_cb(struct osmo_gprs_llc_prim *llc_prim); + +struct osmo_gprs_gmm_prim *gprs_gmm_prim_alloc_gmmreg_attach_cnf(void); +struct osmo_gprs_gmm_prim *gprs_gmm_prim_alloc_gmmreg_detach_cnf(void); + +struct osmo_gprs_gmm_prim *gprs_gmm_prim_alloc_gmmrr_assign_req(uint32_t new_tlli); + +struct osmo_gprs_gmm_prim *gprs_gmm_prim_alloc_gmmsm_establish_cnf(uint8_t cause); +struct osmo_gprs_gmm_prim *gprs_gmm_prim_alloc_gmmrr_release_ind(void); +struct osmo_gprs_gmm_prim *gprs_gmm_prim_alloc_gmmsm_unitdata_ind(uint8_t *smpdu, unsigned int smpdu_len); /* gmm.c: */ struct gprs_gmm_entity *gprs_gmm_gmme_alloc(void); void gprs_gmm_gmme_free(struct gprs_gmm_entity *gmme); +struct gprs_gmm_entity *gprs_gmm_find_gmme_by_tlli(uint32_t tlli); +int gprs_gmm_rx(struct gprs_gmm_entity *gmme, struct gsm48_hdr *gh, unsigned int len); +int gprs_gmm_tx_att_req(struct gprs_gmm_entity *gmme, + enum osmo_gprs_gmm_attach_type attach_type, + bool attach_with_imsi); #define LOGGMME(snme, level, fmt, args...) \ - LOGGMM(level, "GMME(%08x) " fmt, \ - 23 /*TODO: use ID */, \ + LOGGMM(level, "GMME(PTMSI-%08x) " fmt, \ + gmme->ptmsi, \ ## args) diff --git a/include/osmocom/gprs/rlcmac/rlcmac_prim.h b/include/osmocom/gprs/rlcmac/rlcmac_prim.h index 40d2d9a..cca27f6 100644 --- a/include/osmocom/gprs/rlcmac/rlcmac_prim.h +++ b/include/osmocom/gprs/rlcmac/rlcmac_prim.h @@ -61,7 +61,9 @@ struct osmo_gprs_rlcmac_grr_prim { }; }; -/* TS 24.007 Section 9.3.2 "Service primitives for GMMRR-SAP (GSM only)" */ +/* TS 24.007 Section 9.3.2 "Service primitives for GMMRR-SAP (GSM only)" + * Same as enum osmo_gprs_gmm_gmmrr_prim_type. + */ enum osmo_gprs_rlcmac_gmmrr_prim_type { OSMO_GPRS_RLCMAC_GMMRR_ASSIGN, /* Req: newTLLI */ OSMO_GPRS_RLCMAC_GMMRR_PAGE, /* Ind: TLLI */ @@ -73,7 +75,9 @@ static inline const char *osmo_gprs_rlcmac_gmmrr_prim_type_name(enum osmo_gprs_r return get_value_string(osmo_gprs_rlcmac_gmmrr_prim_type_names, val); } -/* Parameters for OSMO_GPRS_RLCMAC_GRR_* prims */ +/* Parameters for OSMO_GPRS_RLCMAC_GRR_* prims. + * Same as struct osmo_gprs_gmm_gmmrr_prim. + */ struct osmo_gprs_rlcmac_gmmrr_prim { /* Common fields (none) */ union { diff --git a/libosmo-gprs-gmm.pc.in b/libosmo-gprs-gmm.pc.in index d53adad..f71f1f8 100644 --- a/libosmo-gprs-gmm.pc.in +++ b/libosmo-gprs-gmm.pc.in @@ -6,7 +6,7 @@ includedir=@includedir@ Name: Osmocom [E]GPRS GMM (GPRS Mobility Management) Library Description: C Utility Library Version: @VERSION@ -Requires: libosmocore +Requires: libosmocore libosmo-gprs-llc Libs: -L${libdir} -losmo-gprs-gmm Libs.private: -ltalloc Cflags: -I${includedir}/ diff --git a/src/gmm/Makefile.am b/src/gmm/Makefile.am index 7120b1f..2aa8bb4 100644 --- a/src/gmm/Makefile.am +++ b/src/gmm/Makefile.am @@ -20,6 +20,8 @@ lib_LTLIBRARIES = \ libosmo_gprs_gmm_la_SOURCES = \ gmm.c \ + gmm_ms_fsm.c \ + gmm_pdu.c \ gmm_prim.c \ misc.c \ $(NULL) @@ -31,6 +33,7 @@ libosmo_gprs_gmm_la_LDFLAGS = \ $(NULL) libosmo_gprs_gmm_la_LIBADD = \ + $(top_builddir)/src/llc/libosmo-gprs-llc.la \ $(LIBOSMOCORE_LIBS) \ $(LIBOSMOGSM_LIBS) \ -lm \ diff --git a/src/gmm/gmm.c b/src/gmm/gmm.c index afe0882..4abf3a7 100644 --- a/src/gmm/gmm.c +++ b/src/gmm/gmm.c @@ -24,24 +24,108 @@ #include #include +#include +#include #include #include #include +#include +#include + struct gprs_gmm_ctx *g_ctx; +/* Section 11.2.2 / Table 11.3 GPRS Mobility management timers - MS side */ +#define GSM0408_T3310_SECS 15 +#define GSM0408_T3311_SECS 15 +#define GSM0408_T3316_SECS 30 +#define GSM0408_T3318_SECS 20 +#define GSM0408_T3320_SECS 15 +#define GSM0408_T3321_SECS 15 +#define GSM0408_T3330_SECS 15 +#define GSM0408_T3340_SECS 10 + +/* Section 11.2.2 / Table 11.3a GPRS Mobility management timers – MS side */ +#define GSM0408_T3302_SECS (12 * 60) +#define GSM0408_T3312_SECS (54 * 60) +#define GSM0408_T3314_SECS 44 +#define GSM0408_T3317_SECS 15 +#define GSM0408_T3319_SECS 30 +#define GSM0408_T3323_SECS GSM0408_T3312_SECS /* NOTE 6 */ +#define GSM0408_T3325_SECS 60 +#define GSM0408_T3346_SECS (15*60) + +/* TS 24.008 */ +static struct osmo_tdef T_defs_gmm[] = { + { .T=3302, .default_val=GSM0408_T3302_SECS, .desc = "" }, + { .T=3310, .default_val=GSM0408_T3310_SECS, .desc = "" }, + { .T=3311, .default_val=GSM0408_T3311_SECS, .desc = "" }, + { .T=3312, .default_val=GSM0408_T3312_SECS, .desc = "" }, + { .T=3314, .default_val=GSM0408_T3314_SECS, .desc="READY timer. Forced to STANDBY on expiry timer (s)" }, + { .T=3316, .default_val=GSM0408_T3316_SECS, .desc = "" }, + { .T=3317, .default_val=GSM0408_T3317_SECS, .desc = "" }, + { .T=3318, .default_val=GSM0408_T3318_SECS, .desc = "" }, + { .T=3319, .default_val=GSM0408_T3319_SECS, .desc = "" }, + { .T=3320, .default_val=GSM0408_T3320_SECS, .desc = "" }, + { .T=3321, .default_val=GSM0408_T3321_SECS, .desc = "" }, + { .T=3323, .default_val=GSM0408_T3323_SECS, .desc = "" }, + { .T=3324, .default_val=0 /* provided by the network */, .desc = "" }, + { .T=3325, .default_val=GSM0408_T3323_SECS, .desc = "" }, + { .T=3330, .default_val=GSM0408_T3330_SECS, .desc = "" }, + { .T=3340, .default_val=GSM0408_T3340_SECS, .desc = "" }, + { .T=3346, .default_val=GSM0408_T3346_SECS /* updated by netowrk */, .desc = "" }, + { 0 } /* empty item at the end */ +}; + int osmo_gprs_gmm_init(enum osmo_gprs_gmm_location location) { - if (g_ctx) + bool first_init = true; + int rc; + OSMO_ASSERT(location == OSMO_GPRS_GMM_LOCATION_MS || location == OSMO_GPRS_GMM_LOCATION_NETWORK) + + if (g_ctx) { + first_init = false; talloc_free(g_ctx); + } g_ctx = talloc_zero(NULL, struct gprs_gmm_ctx); g_ctx->location = location; + g_ctx->T_defs = T_defs_gmm; INIT_LLIST_HEAD(&g_ctx->gmme_list); + + osmo_tdefs_reset(g_ctx->T_defs); + + if (first_init) { + rc = gprs_gmm_ms_fsm_init(); + if (rc != 0) { + TALLOC_FREE(g_ctx); + return rc; + } + } return 0; } +/* Whether GPRS is enabled on the phone. + * See transition GMM-NULL <-> GMM-* in + * "Figure 4.1b/3GPP TS 24.008:GMM main states in the MS" */ +void osmo_gprs_gmm_enable_gprs(bool enable_gprs) +{ + struct gprs_gmm_entity *gmme; + int ev; + + if (g_ctx->gprs_enabled == enable_gprs) + return; + + g_ctx->gprs_enabled = enable_gprs; + + /* Inform all existing MS: */ + ev = enable_gprs ? GPRS_GMM_MS_EV_ENABLE_GPRS_MODE : + GPRS_GMM_MS_EV_DISABLE_GPRS_MODE; + llist_for_each_entry(gmme, &g_ctx->gmme_list, list) + osmo_fsm_inst_dispatch(gmme->ms_fsm.fi, ev, NULL); +} + struct gprs_gmm_entity *gprs_gmm_gmme_alloc(void) { struct gprs_gmm_entity *gmme; @@ -50,6 +134,11 @@ struct gprs_gmm_entity *gprs_gmm_gmme_alloc(void) if (!gmme) return NULL; + if (gprs_gmm_ms_fsm_ctx_init(&gmme->ms_fsm, gmme) < 0) { + talloc_free(gmme); + return NULL; + } + llist_add(&gmme->list, &g_ctx->gmme_list); return gmme; @@ -64,3 +153,353 @@ void gprs_gmm_gmme_free(struct gprs_gmm_entity *gmme) llist_del(&gmme->list); talloc_free(gmme); } + +struct gprs_gmm_entity *gprs_gmm_find_gmme_by_tlli(uint32_t tlli) +{ + struct gprs_gmm_entity *gmme; + + llist_for_each_entry(gmme, &g_ctx->gmme_list, list) { + if (gmme->ptmsi == tlli || gmme->old_ptmsi == tlli) + return gmme; + } + return NULL; +} + +static 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; + + gmm_prim_tx = gprs_gmm_prim_alloc_gmmreg_attach_cnf(); + gmm_prim_tx->gmmreg.attach_cnf.accepted = accepted; + if (!accepted) + gmm_prim_tx->gmmreg.attach_cnf.rej.cause = cause; + + rc = gprs_gmm_prim_call_up_cb(gmm_prim_tx); + return rc; +} + +static int gprs_gmm_submit_gmmrr_assing_req(struct gprs_gmm_entity *gmme) +{ + struct osmo_gprs_gmm_prim *gmm_prim_tx; + int rc; + + gmm_prim_tx = gprs_gmm_prim_alloc_gmmrr_assign_req(gmme->ptmsi); + + rc = gprs_gmm_prim_call_down_cb(gmm_prim_tx); + return rc; +} + +static int gprs_gmm_submit_llgmm_assing_req(struct gprs_gmm_entity *gmme) +{ + struct osmo_gprs_llc_prim *llc_prim_tx; + int rc; + + llc_prim_tx = osmo_gprs_llc_prim_alloc_llgm_assign_req(gmme->old_ptmsi); + llc_prim_tx->llgmm.assign_req.tlli_new = gmme->ptmsi; + llc_prim_tx->llgmm.assign_req.gea = gmme->gea; + memcpy(llc_prim_tx->llgmm.assign_req.kc, gmme->kc, ARRAY_SIZE(gmme->kc)); + + rc = gprs_gmm_prim_call_llc_down_cb(llc_prim_tx); + return rc; +} + +/* Tx Identity Response, 9.2.11 */ +static int gprs_gmm_tx_id_resp(struct gprs_gmm_entity *gmme, + uint8_t mi_type) +{ + struct osmo_gprs_llc_prim *llc_prim; + int rc; + struct msgb *msg; + + LOGGMME(gmme, LOGL_INFO, "Tx GMM IDENTITY RESPONSE\n"); + + llc_prim = osmo_gprs_llc_prim_alloc_ll_unitdata_req( + gmme->ptmsi, OSMO_GPRS_LLC_SAPI_GMM, NULL, GPRS_GMM_ALLOC_SIZE); + msg = llc_prim->oph.msg; + msg->l3h = msg->tail; + rc = gprs_gmm_build_identity_resp(gmme, mi_type, msg); + if (rc < 0) { + msgb_free(msg); + return -EBADMSG; + } + llc_prim->ll.l3_pdu = msg->l3h; + llc_prim->ll.l3_pdu_len = msgb_l3len(msg); + /* TODO: + llc_prim->ll.qos_params[3]; + llc_prim->ll.radio_prio; + llc_prim->ll.apply_gea; + llc_prim->ll.apply_gia; + */ + + rc = gprs_gmm_prim_call_llc_down_cb(llc_prim); + if (rc < 0) + return rc; + return rc; +} + +/* Tx GMM Authentication and ciphering response, 9.4.10 */ +static int gprs_gmm_tx_ciph_auth_resp(struct gprs_gmm_entity *gmme, bool imeisv_requested, uint8_t ac_ref_nr, const uint8_t sres[4]) +{ + struct osmo_gprs_llc_prim *llc_prim; + int rc; + struct msgb *msg; + + LOGGMME(gmme, LOGL_INFO, "Tx GMM GMM AUTHENTICATION AND CIPHERING RESPONSE\n"); + + llc_prim = osmo_gprs_llc_prim_alloc_ll_unitdata_req( + gmme->ptmsi, OSMO_GPRS_LLC_SAPI_GMM, NULL, GPRS_GMM_ALLOC_SIZE); + msg = llc_prim->oph.msg; + msg->l3h = msg->tail; + rc = gprs_gmm_build_ciph_auth_resp(gmme, imeisv_requested, ac_ref_nr, sres, msg); + if (rc < 0) { + msgb_free(msg); + return -EBADMSG; + } + llc_prim->ll.l3_pdu = msg->l3h; + llc_prim->ll.l3_pdu_len = msgb_l3len(msg); + /* TODO: + llc_prim->ll.qos_params[3]; + llc_prim->ll.radio_prio; + llc_prim->ll.apply_gea; + llc_prim->ll.apply_gia; + */ + + rc = gprs_gmm_prim_call_llc_down_cb(llc_prim); + if (rc < 0) + return rc; + return rc; +} + +/* Tx GMM Atach Request, 9.4.1 */ +int gprs_gmm_tx_att_req(struct gprs_gmm_entity *gmme, + enum osmo_gprs_gmm_attach_type attach_type, + bool attach_with_imsi) +{ + struct osmo_gprs_llc_prim *llc_prim; + int rc; + struct msgb *msg; + + LOGGMME(gmme, LOGL_INFO, "Tx GMM ATTACH REQUEST (new P-TMSI=0x%08x)\n", gmme->ptmsi); + llc_prim = osmo_gprs_llc_prim_alloc_ll_unitdata_req( + gmme->ptmsi, OSMO_GPRS_LLC_SAPI_GMM, NULL, GPRS_GMM_ALLOC_SIZE); + msg = llc_prim->oph.msg; + msg->l3h = msg->tail; + rc = gprs_gmm_build_attach_req(gmme, + attach_type, + attach_with_imsi, + msg); + if (rc < 0) { + msgb_free(msg); + return -EBADMSG; + } + llc_prim->ll.l3_pdu = msg->l3h; + llc_prim->ll.l3_pdu_len = msgb_l3len(msg); + /* TODO: + llc_prim->ll.qos_params[3]; + llc_prim->ll.radio_prio; + llc_prim->ll.apply_gea; + llc_prim->ll.apply_gia; + */ + + rc = gprs_gmm_prim_call_llc_down_cb(llc_prim); + + return rc; +} + +/* Tx GMM Atach Complete, 9.4.3 */ +static int gprs_gmm_tx_att_compl(struct gprs_gmm_entity *gmme) +{ + struct osmo_gprs_llc_prim *llc_prim; + int rc; + struct msgb *msg; + + LOGGMME(gmme, LOGL_INFO, "Tx GMM ATTACH COMPL\n"); + + llc_prim = osmo_gprs_llc_prim_alloc_ll_unitdata_req( + gmme->ptmsi, OSMO_GPRS_LLC_SAPI_GMM, NULL, GPRS_GMM_ALLOC_SIZE); + msg = llc_prim->oph.msg; + msg->l3h = msg->tail; + rc = gprs_gmm_build_attach_compl(gmme, msg); + if (rc < 0) { + msgb_free(msg); + return -EBADMSG; + } + llc_prim->ll.l3_pdu = msg->l3h; + llc_prim->ll.l3_pdu_len = msgb_l3len(msg); + /* TODO: + llc_prim->ll.qos_params[3]; + llc_prim->ll.radio_prio; + llc_prim->ll.apply_gea; + llc_prim->ll.apply_gia; + */ + + rc = gprs_gmm_prim_call_llc_down_cb(llc_prim); + if (rc < 0) + return rc; + return rc; +} + +static int gprs_gmm_rx_att_ack(struct gprs_gmm_entity *gmme, struct gsm48_hdr *gh, unsigned int len) +{ + struct gsm48_attach_ack *aa; + struct tlv_parsed tp; + int rc; + + if (len < sizeof(*gh) + sizeof(*aa)) { + LOGGMME(gmme, LOGL_ERROR, "Rx GMM ATTACH ACCEPT with wrong size %u\n", len); + goto rejected; + } + + LOGGMME(gmme, LOGL_DEBUG, "Rx GMM ATTACH ACCEPT\n"); + aa = (struct gsm48_attach_ack *)&gh->data[0]; + + gmme->ra_upd_timer = aa->ra_upd_timer; + gmme->radio_prio = aa->radio_prio; + gsm48_parse_ra(&gmme->ra, (const uint8_t *)&aa->ra_id); + + if (len > sizeof(*gh) + sizeof(*aa)) { + rc = gprs_gmm_tlv_parse(&tp, &aa->data[0], + len - (sizeof(*gh) + sizeof(*aa))); + if (rc < 0) { + LOGGMME(gmme, LOGL_ERROR, "Rx GMM ATTACH ACCEPT: failed to parse TLVs %d\n", rc); + goto rejected; + } + + if (TLVP_PRESENT(&tp, GSM48_IE_GMM_ALLOC_PTMSI)) { + struct osmo_mobile_identity mi; + if (osmo_mobile_identity_decode(&mi, TLVP_VAL(&tp, GSM48_IE_GMM_ALLOC_PTMSI), + TLVP_LEN(&tp, GSM48_IE_GMM_ALLOC_PTMSI), false) + || mi.type != GSM_MI_TYPE_TMSI) { + LOGGMME(gmme, LOGL_ERROR, "Cannot decode P-TMSI\n"); + goto rejected; + } + gmme->old_ptmsi = gmme->ptmsi; + gmme->ptmsi = mi.tmsi; + } + } + + /* 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) + goto rejected; + + /* Submit GMMRR-ASSIGN-REQ as per TS 24.007 Annex C.1 */ + rc = gprs_gmm_submit_gmmrr_assing_req(gmme); + if (rc < 0) + goto rejected; + + rc = gprs_gmm_tx_att_compl(gmme); + if (rc < 0) + goto rejected; + + rc = osmo_fsm_inst_dispatch(gmme->ms_fsm.fi, GPRS_GMM_MS_EV_ATTACH_ACCEPTED, NULL); + + return rc; + +rejected: + return -EINVAL; /* TODO: what to do on error? */ +} + + +static int gprs_gmm_rx_att_rej(struct gprs_gmm_entity *gmme, struct gsm48_hdr *gh, unsigned int len) +{ + LOGGMME(gmme, LOGL_ERROR, "Rx GMM ATTACH REJECT not implemented!\n"); + return 0; /* TODO */ +} + +/* Rx GMM Identity Request, 9.2.10 */ +static int gprs_gmm_rx_id_req(struct gprs_gmm_entity *gmme, struct gsm48_hdr *gh, unsigned int len) +{ + /* 4.7.8.2: "An MS shall be ready to respond to an IDENTITY REQUEST message at any time." + * "Upon receipt of the IDENTITY REQUEST message the MS sends back an IDENTITY RESPONSE message. + * The IDENTITY RESPONSE message shall contain the identification parameters as requested by the network" + */ + uint8_t id_type; + + if (len < sizeof(struct gsm48_hdr) + 1) { + LOGGMME(gmme, LOGL_ERROR, "Rx GMM IDENTITY REQUEST with wrong size %u\n", len); + return -EINVAL; + } + + id_type = gh->data[0] & 0xf; + LOGGMME(gmme, LOGL_DEBUG, "Rx GMM IDENTITY REQUEST mi_type=%s\n", + gsm48_mi_type_name(id_type)); + + return gprs_gmm_tx_id_resp(gmme, id_type); +} + +/* Rx GMM Authentication and ciphering request, 9.4.9 */ +static int gprs_gmm_rx_auth_ciph_req(struct gprs_gmm_entity *gmme, struct gsm48_hdr *gh, unsigned int len) +{ + struct gsm48_auth_ciph_req *acreq; + struct tlv_parsed tp; + int rc; + bool imeisv_requested = false; + uint8_t sres[4] = {}; + + if (len < sizeof(*gh) + sizeof(*acreq)) { + LOGGMME(gmme, LOGL_ERROR, "Rx GMM AUTHENTICATION AND CIPHERING REQUEST with wrong size %u\n", len); + return -EINVAL; + } + + LOGGMME(gmme, LOGL_DEBUG, "Rx GMM AUTHENTICATION AND CIPHERING REQUEST\n"); + acreq = (struct gsm48_auth_ciph_req *)&gh->data[0]; + + if (len > sizeof(*gh) + sizeof(*acreq)) { + rc = gprs_gmm_tlv_parse(&tp, &acreq->data[0], + len - (sizeof(*gh) + sizeof(*acreq))); + if (rc < 0) { + LOGGMME(gmme, LOGL_ERROR, "Rx GMM AUTHENTICATION AND CIPHERING REQUEST: failed to parse TLVs %d\n", rc); + return -EINVAL; + } + if (TLVP_PRESENT(&tp, GSM48_IE_GMM_IMEISV)) + imeisv_requested = !!*((uint8_t *)TLVP_VAL(&tp, GSM48_IE_GMM_IMEISV)); + + } + + rc = gprs_gmm_tx_ciph_auth_resp(gmme, imeisv_requested, acreq->ac_ref_nr, sres); + if (rc < 0) + return rc; + + /* Submit LLGMM-ASSIGN-REQ as per TS 24.007 Annex C.1 */ + rc = gprs_gmm_submit_llgmm_assing_req(gmme); + return rc; +} + +/* Rx GPRS Mobility Management. */ +int gprs_gmm_rx(struct gprs_gmm_entity *gmme, struct gsm48_hdr *gh, unsigned int len) +{ + int rc = 0; + if (len < sizeof(struct gsm48_hdr)) { + LOGGMME(gmme, LOGL_ERROR, "Rx GMM message too short! len=%u\n", len); + return -EINVAL; + } + + switch (gh->msg_type) { + case GSM48_MT_GMM_ATTACH_ACK: + rc = gprs_gmm_rx_att_ack(gmme, gh, len); + break; + case GSM48_MT_GMM_ATTACH_REJ: + rc = gprs_gmm_rx_att_rej(gmme, gh, len); + break; + case GSM48_MT_GMM_ID_REQ: + rc = gprs_gmm_rx_id_req(gmme, gh, len); + break; + case GSM48_MT_GMM_AUTH_CIPH_REQ: + rc = gprs_gmm_rx_auth_ciph_req(gmme, gh, len); + break; + default: + LOGGMME(gmme, LOGL_ERROR, + "Rx GMM message not implemented! type=%u len=%u\n", + gh->msg_type, len); + rc = -EINVAL; + } + + return rc; +} diff --git a/src/gmm/gmm_ms_fsm.c b/src/gmm/gmm_ms_fsm.c new file mode 100644 index 0000000..adb73a1 --- /dev/null +++ b/src/gmm/gmm_ms_fsm.c @@ -0,0 +1,296 @@ +/* GMM states in the MS, 3GPP TS 24.008 § 4.1.3.1 */ +/* + * (C) 2023 by sysmocom - s.m.f.c. GmbH + * All Rights Reserved + * + * SPDX-License-Identifier: AGPL-3.0+ + * + * Author: Pau Espin Pedrol + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +#include + +#include +#include +#include + +#define X(s) (1 << (s)) + +static const struct osmo_tdef_state_timeout gmm_ms_fsm_timeouts[32] = {}; + +#define gmm_ms_fsm_state_chg(fi, NEXT_STATE) \ + osmo_tdef_fsm_inst_state_chg(fi, NEXT_STATE, gmm_ms_fsm_timeouts, g_ctx->T_defs, -1) + +static void st_gmm_ms_null(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + switch (event) { + case GPRS_GMM_MS_EV_ENABLE_GPRS_MODE: + gmm_ms_fsm_state_chg(fi, GPRS_GMM_MS_ST_DEREGISTERED); + break; + case GPRS_GMM_MS_EV_DETACH_REQUESTED: + gmm_ms_fsm_state_chg(fi, GPRS_GMM_MS_ST_DEREGISTERED); + break; + default: + OSMO_ASSERT(0); + } +} + +static void st_gmm_ms_deregistered(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + 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: + gmm_ms_fsm_state_chg(fi, GPRS_GMM_MS_ST_REGISTERED_INITIATED); + break; + case GPRS_GMM_MS_EV_DETACH_REQUESTED: + gmm_ms_fsm_state_chg(fi, GPRS_GMM_MS_ST_DEREGISTERED); + break; + default: + OSMO_ASSERT(0); + } +} + +static void st_gmm_ms_registered_initiated(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + switch (event) { + 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: + gmm_ms_fsm_state_chg(fi, GPRS_GMM_MS_ST_REGISTERED); + break; + case GPRS_GMM_MS_EV_DETACH_REQUESTED: + gmm_ms_fsm_state_chg(fi, GPRS_GMM_MS_ST_DEREGISTERED); + break; + default: + OSMO_ASSERT(0); + } +} + +static void st_gmm_ms_registered(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + switch (event) { + case GPRS_GMM_MS_EV_SR_REQUESTED: + gmm_ms_fsm_state_chg(fi, GPRS_GMM_MS_ST_REGISTERED); + break; + case GPRS_GMM_MS_EV_RAU_REQUESTED: + gmm_ms_fsm_state_chg(fi, GPRS_GMM_MS_ST_RAU_INITIATED); + break; + case GPRS_GMM_MS_EV_DETACH_REQUESTED: + gmm_ms_fsm_state_chg(fi, GPRS_GMM_MS_ST_DEREGISTERED_INITIATED); + break; + default: + OSMO_ASSERT(0); + } +} + +static void st_gmm_ms_deregistered_initiated(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + switch (event) { + case GPRS_GMM_MS_EV_DETACH_ACCEPTED: + 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_RAU_ACCEPTED: + gmm_ms_fsm_state_chg(fi, GPRS_GMM_MS_ST_REGISTERED); + break; + case GPRS_GMM_MS_EV_DETACH_REQUESTED: + gmm_ms_fsm_state_chg(fi, GPRS_GMM_MS_ST_DEREGISTERED); + break; + default: + OSMO_ASSERT(0); + } +} + +static void st_gmm_ms_rau_initiated(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + switch (event) { + case GPRS_GMM_MS_EV_RAU_REJECTED: + // causes #13, #15, #25 + gmm_ms_fsm_state_chg(fi, GPRS_GMM_MS_ST_REGISTERED); + // else + //mm_ms_fsm_state_chg(fi, GPRS_GMM_MS_ST_DEREGISTERED_INITIATED); + break; + case GPRS_GMM_MS_EV_RAU_ACCEPTED: + gmm_ms_fsm_state_chg(fi, GPRS_GMM_MS_ST_REGISTERED); + break; + case GPRS_GMM_MS_EV_DETACH_REQUESTED: + gmm_ms_fsm_state_chg(fi, GPRS_GMM_MS_ST_DEREGISTERED); + break; + default: + OSMO_ASSERT(0); + } +} + +static void st_gmm_ms_sr_initiated(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + switch (event) { + case GPRS_GMM_MS_EV_SR_REJECTED: + gmm_ms_fsm_state_chg(fi, GPRS_GMM_MS_ST_REGISTERED); + break; + case GPRS_GMM_MS_EV_SR_ACCEPTED: + gmm_ms_fsm_state_chg(fi, GPRS_GMM_MS_ST_REGISTERED); + break; + case GPRS_GMM_MS_EV_DETACH_REQUESTED: + gmm_ms_fsm_state_chg(fi, GPRS_GMM_MS_ST_DEREGISTERED); + break; + default: + OSMO_ASSERT(0); + } +} + +static struct osmo_fsm_state gmm_ms_fsm_states[] = { + [GPRS_GMM_MS_ST_NULL] = { + .in_event_mask = + X(GPRS_GMM_MS_EV_ENABLE_GPRS_MODE)| + X(GPRS_GMM_MS_EV_DETACH_REQUESTED), + .out_state_mask = + X(GPRS_GMM_MS_ST_DEREGISTERED), + .name = "Null", + .action = st_gmm_ms_null, + }, + [GPRS_GMM_MS_ST_DEREGISTERED] = { + .in_event_mask = + X(GPRS_GMM_MS_EV_DISABLE_GPRS_MODE) | + X(GPRS_GMM_MS_EV_ATTACH_REQUESTED) | + X(GPRS_GMM_MS_EV_DETACH_REQUESTED), + .out_state_mask = + X(GPRS_GMM_MS_ST_NULL) | + X(GPRS_GMM_MS_ST_REGISTERED_INITIATED)| + X(GPRS_GMM_MS_ST_DEREGISTERED), + .name = "Deregistered", + .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_ACCEPTED) | + X(GPRS_GMM_MS_EV_DETACH_REQUESTED), + .out_state_mask = + X(GPRS_GMM_MS_ST_DEREGISTERED) | + X(GPRS_GMM_MS_ST_REGISTERED), + .name = "RegisteredInitiated", + .action = st_gmm_ms_registered_initiated, + }, + [GPRS_GMM_MS_ST_REGISTERED] = { + .in_event_mask = + X(GPRS_GMM_MS_EV_SR_REQUESTED) | + X(GPRS_GMM_MS_EV_RAU_REQUESTED) | + X(GPRS_GMM_MS_EV_DETACH_REQUESTED) | + X(GPRS_GMM_MS_EV_DETACH_REQUESTED), + .out_state_mask = + X(GPRS_GMM_MS_ST_REGISTERED) | + X(GPRS_GMM_MS_ST_RAU_INITIATED) | + X(GPRS_GMM_MS_ST_DEREGISTERED_INITIATED) | + X(GPRS_GMM_MS_ST_DEREGISTERED), + .name = "Registered", + .action = st_gmm_ms_registered, + }, + [GPRS_GMM_MS_ST_DEREGISTERED_INITIATED] = { + .in_event_mask = + X(GPRS_GMM_MS_EV_DETACH_ACCEPTED) | + X(GPRS_GMM_MS_EV_LOW_LVL_FAIL) | + X(GPRS_GMM_MS_EV_RAU_ACCEPTED) | + X(GPRS_GMM_MS_EV_DETACH_REQUESTED), + .out_state_mask = + X(GPRS_GMM_MS_ST_REGISTERED) | + X(GPRS_GMM_MS_ST_DEREGISTERED), + .name = "DeregisteredInitiated", + .action = st_gmm_ms_sr_initiated, + }, + [GPRS_GMM_MS_ST_RAU_INITIATED] = { + .in_event_mask = + X(GPRS_GMM_MS_EV_RAU_REJECTED) | + X(GPRS_GMM_MS_EV_RAU_ACCEPTED) | + X(GPRS_GMM_MS_EV_DETACH_REQUESTED), + .out_state_mask = + X(GPRS_GMM_MS_ST_REGISTERED) | + X(GPRS_GMM_MS_ST_DEREGISTERED_INITIATED) | + X(GPRS_GMM_MS_ST_DEREGISTERED), + .name = "RAUInitidated", + .action = st_gmm_ms_rau_initiated, + }, + [GPRS_GMM_MS_ST_SR_INITIATED] = { + .in_event_mask = + X(GPRS_GMM_MS_EV_SR_REJECTED) | + X(GPRS_GMM_MS_EV_SR_ACCEPTED) | + X(GPRS_GMM_MS_EV_DETACH_REQUESTED), + .out_state_mask = + X(GPRS_GMM_MS_ST_REGISTERED) | + X(GPRS_GMM_MS_ST_DEREGISTERED), + .name = "DeregisteredInitiated", + .action = st_gmm_ms_deregistered_initiated, + }, +}; + +const struct value_string gmm_ms_fsm_event_names[] = { + { GPRS_GMM_MS_EV_ENABLE_GPRS_MODE, "ENABLE_GPRS_MODE" }, + { GPRS_GMM_MS_EV_DISABLE_GPRS_MODE, "DIOSABLE_GPRS_MODE" }, + { GPRS_GMM_MS_EV_ATTACH_REQUESTED, "ATTACH_REQUESTED" }, + { GPRS_GMM_MS_EV_ATTACH_REJECTED, "ATTACH_REJECTED" }, + { GPRS_GMM_MS_EV_ATTACH_ACCEPTED, "ATTACH_ACCEPTED" }, + { GPRS_GMM_MS_EV_DETACH_REQUESTED, "DETACH_REQUESTED" }, + { GPRS_GMM_MS_EV_DETACH_ACCEPTED, "DETACH_ACCEPTED" }, + { GPRS_GMM_MS_EV_SR_REQUESTED, "SR_REQUESTED" }, + { GPRS_GMM_MS_EV_SR_REJECTED, "SR_REJECTED" }, + { GPRS_GMM_MS_EV_SR_ACCEPTED, "SR_ACCEPTED" }, + { GPRS_GMM_MS_EV_RAU_REQUESTED, "RAU_REQUESTED" }, + { GPRS_GMM_MS_EV_RAU_REJECTED, "RAU_REJECTED" }, + { GPRS_GMM_MS_EV_RAU_ACCEPTED, "RAU_ACCEPTED" }, + { GPRS_GMM_MS_EV_LOW_LVL_FAIL, "LOW_LVL_FAIL" }, + { 0, NULL } +}; + +int gmm_ms_fsm_timer_cb(struct osmo_fsm_inst *fi) +{ + return 0; +} + +struct osmo_fsm gmm_ms_fsm = { + .name = "GMM_MS", + .states = gmm_ms_fsm_states, + .num_states = ARRAY_SIZE(gmm_ms_fsm_states), + .event_names = gmm_ms_fsm_event_names, + .log_subsys = DLGLOBAL, /* updated dynamically through gprs_gmm_ms_fsm_set_log_cat() */ + .timer_cb = gmm_ms_fsm_timer_cb, +}; + +int gprs_gmm_ms_fsm_init(void) +{ + return osmo_fsm_register(&gmm_ms_fsm); +} + +void gprs_gmm_ms_fsm_set_log_cat(int logcat) +{ + gmm_ms_fsm.log_subsys = logcat; +} + +int gprs_gmm_ms_fsm_ctx_init(struct gprs_gmm_ms_fsm_ctx *ctx, struct gprs_gmm_entity *gmme) +{ + ctx->gmme = gmme; + ctx->fi = osmo_fsm_inst_alloc(&gmm_ms_fsm, gmme, ctx, LOGL_INFO, NULL); + if (!ctx->fi) + return -ENODATA; + + /* Transition to state GMM-DEREGISTERED right away if GPRS is enabled: */ + if (g_ctx->gprs_enabled) + osmo_fsm_inst_dispatch(ctx->fi, GPRS_GMM_MS_EV_ENABLE_GPRS_MODE, NULL); + + return 0; +} diff --git a/src/gmm/gmm_pdu.c b/src/gmm/gmm_pdu.c new file mode 100644 index 0000000..141e4a5 --- /dev/null +++ b/src/gmm/gmm_pdu.c @@ -0,0 +1,330 @@ +/* GMM PDUs, 3GPP TS 9.4 24.008 GPRS Mobility Management Messages */ +/* (C) 2023 by Sysmocom s.f.m.c. GmbH + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +/* MS network capability 10.5.5.12*/ +struct gprs_gmm_ms_net_cap { +#if OSMO_IS_LITTLE_ENDIAN + uint8_t gea1:1, + sm_cap_dedicated:1, + sm_cap_gprs:1, + ucs2_support:1, + ss_screening_ind:2, + solsa_cap:1, + rev_level_ind:1; + uint8_t pfc_feature_mode:1, + gea2:1, + gea3:1, + gea4:1, + gea5:1, + gea6:1, + gea7:1, + lcs_va_cap:1; + uint8_t ps_inter_rat_ho_geran2utran:1, + ps_inter_rat_ho_geran2eutran:1, + emm_combined_proc_cap:1, + isr_support:1, + srvcc_cap:1, + epc_cap:1, + nf_capability:1, + geran_net_sharing_cap:1; + uint8_t user_plane_integrity_protection_sup:1, + gia4:1, + gia5:1, + gia6:1, + gia7:1, + epco_ie_ind:1, + restrict_use_enhanced_cov_cap:1, + dual_conn_eutra_nr_cap:1; +#elif OSMO_IS_BIG_ENDIAN +/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */ + uint8_t rev_level_ind:1, solsa_cap:1, ss_screening_ind:2, ucs2_support:1, sm_cap_gprs:1, sm_cap_dedicated:1, gea1:1; + uint8_t lcs_va_cap:1, gea7:1, gea6:1, gea5:1, gea4:1, gea3:1, gea2:1, pfc_feature_mode:1; + uint8_t geran_net_sharing_cap:1, nf_capability:1, epc_cap:1, srvcc_cap:1, isr_support:1, emm_combined_proc_cap:1, ps_inter_rat_ho_geran2eutran:1, ps_inter_rat_ho_geran2utran:1; + uint8_t dual_conn_eutra_nr_cap:1, restrict_use_enhanced_cov_cap:1, epco_ie_ind:1, gia7:1, gia6:1, gia5:1, gia4:1, user_plane_integrity_protection_sup:1; +#endif +} __attribute__((packed)); + +static const struct gprs_gmm_ms_net_cap ms_net_cap_def = { + .gea1 = 1, + .sm_cap_dedicated = 1, + .sm_cap_gprs = 1, + .ucs2_support = 0, + .ss_screening_ind = 1, + .solsa_cap = 0, + .rev_level_ind = 1, + .pfc_feature_mode = 1, + .gea2 = 1, + .gea3 = 1, + .gea4 = 0, + .gea5 = 0, + .gea6 = 0, + .gea7 = 0, + .lcs_va_cap = 0, + .ps_inter_rat_ho_geran2utran = 0, + .ps_inter_rat_ho_geran2eutran = 0, + .emm_combined_proc_cap = 0, + .isr_support = 0, + .srvcc_cap = 0, + .epc_cap = 0, + .nf_capability = 0, + .geran_net_sharing_cap = 0, + .user_plane_integrity_protection_sup = 0, + .gia4 = 0, + .gia5 = 0, + .gia6 = 0, + .gia7 = 0, + .epco_ie_ind = 0, + .restrict_use_enhanced_cov_cap = 0, + .dual_conn_eutra_nr_cap = 0, +}; + +/* 10.5.1.2 Ciphering Key Sequence Number */ +#define CIPH_CKSN_UNAVAIL 0x03 + +/* 10.5.5.6 DRX parameter */ +struct gprs_gmm_drx_param { +#if OSMO_IS_LITTLE_ENDIAN + uint8_t split_pg_cycle_code; + uint8_t coeff:4, + split_ccch:1, + non_drx_timer:3; +#elif OSMO_IS_BIG_ENDIAN +/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */ + uint8_t split_pg_cycle_code; + uint8_t non_drx_timer:3, split_ccch:1, coeff:4; +#endif +} __attribute__((packed)); + +static const struct gprs_gmm_drx_param drx_param_def = { + .split_pg_cycle_code = 10, + .coeff = 0, + .split_ccch = 0, + .non_drx_timer = 0, +}; + +const struct tlv_definition gprs_gmm_att_tlvdef = { + .def = { + [GSM48_IE_GMM_CIPH_CKSN] = { TLV_TYPE_SINGLE_TV, 1 }, + [GSM48_IE_GMM_TIMER_READY] = { TLV_TYPE_TV, 1 }, + [GSM48_IE_GMM_ALLOC_PTMSI] = { TLV_TYPE_TLV, 0 }, + [GSM48_IE_GMM_PTMSI_SIG] = { TLV_TYPE_FIXED, 3 }, + [GSM48_IE_GMM_AUTH_RAND] = { TLV_TYPE_FIXED, 16 }, + [GSM48_IE_GMM_AUTH_SRES] = { TLV_TYPE_FIXED, 4 }, + [GSM48_IE_GMM_AUTH_RES_EXT] = { TLV_TYPE_TLV, 0 }, + [GSM48_IE_GMM_AUTH_FAIL_PAR] = { TLV_TYPE_TLV, 0 }, + [GSM48_IE_GMM_IMEISV] = { TLV_TYPE_TLV, 0 }, + [GSM48_IE_GMM_DRX_PARAM] = { TLV_TYPE_FIXED, 2 }, + [GSM48_IE_GMM_MS_NET_CAPA] = { TLV_TYPE_TLV, 0 }, + [GSM48_IE_GMM_PDP_CTX_STATUS] = { TLV_TYPE_TLV, 0 }, + [GSM48_IE_GMM_PS_LCS_CAPA] = { TLV_TYPE_TLV, 0 }, + [GSM48_IE_GMM_GMM_MBMS_CTX_ST] = { TLV_TYPE_TLV, 0 }, + }, +}; + +static int encode_ms_ra_acc_cap(struct gprs_gmm_entity *gmme, struct msgb *msg) +{ + int rc; + uint8_t *l; /* len */ + struct bitvec bv = { + .data = msg->tail, + .data_len = GSM_MACBLOCK_LEN, + }; + + l = msgb_put(msg, 1); /* len */ + + /* TODO: we hardcode a MS Ra Cap for now. We may want to pass it from the app at some point: */ + rc = bitvec_unhex(&bv, "171933432b37159ef98879cba28c6621e72688b198879c00"); + *l = OSMO_BYTES_FOR_BITS(bv.cur_bit); + msgb_put(msg, *l); + return rc; +} + +/* Chapter 9.4.1: Attach request */ +int gprs_gmm_build_attach_req(struct gprs_gmm_entity *gmme, + enum osmo_gprs_gmm_attach_type attach_type, + bool attach_with_imsi, + struct msgb *msg) +{ + struct gsm48_hdr *gh; + uint8_t byte, cksn; + struct osmo_mobile_identity mi; + uint8_t *l; + int rc; + struct gsm48_ra_id *raid_enc; + struct gprs_ra_id raid; + + gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + gh->proto_discr = GSM48_PDISC_MM_GPRS; + gh->msg_type = GSM48_MT_GMM_ATTACH_REQ; + + /* 10.5.5.12 MS network capability */ + msgb_lv_put(msg, sizeof(ms_net_cap_def), (const uint8_t *)&ms_net_cap_def); + + /* Attach type 10.5.5.2 */ + /* Ciphering key sequence number 10.5.1.2 */ + cksn = 0; /* Use 0 as Ciphering Key Sequence Number */ + byte = (cksn << 4) | (attach_type & 0x0f); + msgb_put_u8(msg, byte); + + /* DRX parameter 10.5.5.6 */ + memcpy(msgb_put(msg, sizeof(drx_param_def)), + &drx_param_def, + sizeof(drx_param_def)); + + /* Mobile identity 10.5.1.4 */ + /* 4.7.3.1.1: If "AttachWithIMSI" is configured, use IMSI instead: */ + if (attach_with_imsi) { + mi = (struct osmo_mobile_identity){ + .type = GSM_MI_TYPE_IMSI, + }; + OSMO_STRLCPY_ARRAY(mi.imsi, gmme->imsi); + } else { + mi = (struct osmo_mobile_identity){ + .type = GSM_MI_TYPE_TMSI, + .tmsi = gmme->ptmsi, + }; + } + l = msgb_put(msg, 1); /* len */ + rc = osmo_mobile_identity_encode_msgb(msg, &mi, false); + if (rc < 0) + return -EINVAL; + *l = rc; + + /* Old routing area identification 0.5.5.15 */ + raid = (struct gprs_ra_id){ /* TODO: fill this correctly */ + .mcc = 0, + .mnc = 0, + .mnc_3_digits = false, + .lac = 0, + .rac = 0, + }; + raid_enc = (struct gsm48_ra_id *)msgb_put(msg, sizeof(struct gsm48_ra_id)); + gsm48_encode_ra(raid_enc, &raid); + + /* MS Radio Access capability 10.5.5.12a */ + rc = encode_ms_ra_acc_cap(gmme, msg); + if (rc < 0) + return -EINVAL; + + /* TODO: optional fields */ + return 0; +} + +/* 9.4.3 Attach complete */ +int gprs_gmm_build_attach_compl(struct gprs_gmm_entity *gmme, struct msgb *msg) +{ + struct gsm48_hdr *gh; + + gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + gh->proto_discr = GSM48_PDISC_MM_GPRS; + gh->msg_type = GSM48_MT_GMM_ATTACH_COMPL; + + /* TODO: Add optional IEs */ + return 0; +} + +/* 9.2.11 Identity response */ +int gprs_gmm_build_identity_resp(struct gprs_gmm_entity *gmme, + uint8_t mi_type, + struct msgb *msg) +{ + struct gsm48_hdr *gh; + struct osmo_mobile_identity mi; + uint8_t *l; + int rc; + + gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + gh->proto_discr = GSM48_PDISC_MM_GPRS; + gh->msg_type = GSM48_MT_GMM_ID_RESP; + + /* Mobile identity 10.5.1.4 */ + switch (mi_type) { + case GSM_MI_TYPE_IMSI: + mi = (struct osmo_mobile_identity){ + .type = GSM_MI_TYPE_IMSI, + }; + OSMO_STRLCPY_ARRAY(mi.imsi, gmme->imsi); + break; + case GSM_MI_TYPE_TMSI: + mi = (struct osmo_mobile_identity){ + .type = GSM_MI_TYPE_TMSI, + .tmsi = gmme->ptmsi, + }; + break; + case GSM_MI_TYPE_IMEI: + mi = (struct osmo_mobile_identity){ + .type = GSM_MI_TYPE_IMEI, + }; + OSMO_STRLCPY_ARRAY(mi.imei, gmme->imei); + break; + case GSM_MI_TYPE_IMEISV: + mi = (struct osmo_mobile_identity){ + .type = GSM_MI_TYPE_IMEISV, + }; + OSMO_STRLCPY_ARRAY(mi.imeisv, gmme->imeisv); + break; + default: + LOGGMME(gmme, LOGL_ERROR, "Tx GMM IDENTITY RESPONSE: mi_type=%s not supported!\n", + gsm48_mi_type_name(mi_type)); + return -EINVAL; + } + l = msgb_put(msg, 1); /* len */ + rc = osmo_mobile_identity_encode_msgb(msg, &mi, false); + if (rc < 0) + return -EINVAL; + *l = rc; + + /* TODO: Optional IEs */ + return 0; +} + +/* Tx GMM Authentication and ciphering response, 9.4.10 */ +int gprs_gmm_build_ciph_auth_resp(struct gprs_gmm_entity *gmme, bool imeisv_requested, + uint8_t ac_ref_nr, const uint8_t sres[4], struct msgb *msg) +{ + struct gsm48_hdr *gh; + struct gsm48_auth_ciph_resp *acr; + int rc = 0; + + gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + gh->proto_discr = GSM48_PDISC_MM_GPRS; + gh->msg_type = GSM48_MT_GMM_AUTH_CIPH_RESP; + + acr = (struct gsm48_auth_ciph_resp *) msgb_put(msg, sizeof(*acr)); + acr->ac_ref_nr = ac_ref_nr; + + if (imeisv_requested) { + (void)imeisv_requested; + /* TODO: IMEISV IE */ + } + + /* TODO: Optional IEs, eg Authentication parameter */ + return rc; +} diff --git a/src/gmm/gmm_prim.c b/src/gmm/gmm_prim.c index 87fb6e6..c18d9d5 100644 --- a/src/gmm/gmm_prim.c +++ b/src/gmm/gmm_prim.c @@ -29,6 +29,7 @@ #include #include #include +#include #define GMM_MSGB_HEADROOM 0 @@ -51,6 +52,18 @@ const struct value_string osmo_gprs_gmm_gmmreg_prim_type_names[] = { { 0, NULL } }; +const struct value_string osmo_gprs_gmm_gmmrr_prim_type_names[] = { + { OSMO_GPRS_GMM_GMMRR_ASSIGN, "ASSIGN" }, + { OSMO_GPRS_GMM_GMMRR_PAGE, "PAGE" }, + { 0, NULL } +}; + +const struct value_string osmo_gprs_gmm_gmmsm_prim_type_names[] = { + { OSMO_GPRS_GMM_GMMSM_ESTABLISH, "ESTABLISH" }, + { OSMO_GPRS_GMM_GMMSM_RELEASE, "RELEASE" }, + { OSMO_GPRS_GMM_GMMSM_UNITDATA, "UNITDATA" }, + { 0, NULL } +}; const char *osmo_gprs_gmm_prim_name(const struct osmo_gprs_gmm_prim *gmm_prim) { @@ -63,6 +76,12 @@ const char *osmo_gprs_gmm_prim_name(const struct osmo_gprs_gmm_prim *gmm_prim) case OSMO_GPRS_GMM_SAP_GMMREG: type = osmo_gprs_gmm_gmmreg_prim_type_name(gmm_prim->oph.primitive); break; + case OSMO_GPRS_GMM_SAP_GMMRR: + type = osmo_gprs_gmm_gmmrr_prim_type_name(gmm_prim->oph.primitive); + break; + case OSMO_GPRS_GMM_SAP_GMMSM: + type = osmo_gprs_gmm_gmmsm_prim_type_name(gmm_prim->oph.primitive); + break; default: type = "unsupported-gmm-sap"; } @@ -73,13 +92,19 @@ const char *osmo_gprs_gmm_prim_name(const struct osmo_gprs_gmm_prim *gmm_prim) static int gmm_up_cb_dummy(struct osmo_gprs_gmm_prim *gmm_prim, void *user_data) { - LOGGMM(LOGL_INFO, "gmm_up_cb_dummy(%s)\n", osmo_gprs_gmm_prim_name(gmm_prim)); + LOGGMM(LOGL_INFO, "%s(%s)\n", __func__, osmo_gprs_gmm_prim_name(gmm_prim)); return 0; } static int gmm_down_cb_dummy(struct osmo_gprs_gmm_prim *gmm_prim, void *user_data) { - LOGGMM(LOGL_INFO, "gmm_down_cb_dummy(%s)\n", osmo_gprs_gmm_prim_name(gmm_prim)); + LOGGMM(LOGL_INFO, "%s(%s)\n", __func__, osmo_gprs_gmm_prim_name(gmm_prim)); + return 0; +} + +static int gmm_llc_down_cb_dummy(struct osmo_gprs_llc_prim *llc_prim, void *user_data) +{ + LOGGMM(LOGL_INFO, "%s(%s)\n", __func__, osmo_gprs_llc_prim_name(llc_prim)); return 0; } @@ -97,6 +122,13 @@ void osmo_gprs_gmm_prim_set_down_cb(osmo_gprs_gmm_prim_down_cb down_cb, void *do g_ctx->gmm_down_cb_user_data = down_user_data; } +/* Set callback used by GMM layer to push primitives to LLC lower layer in protocol stack */ +void osmo_gprs_gmm_prim_set_llc_down_cb(osmo_gprs_gmm_prim_llc_down_cb llc_down_cb, void *llc_down_user_data) +{ + g_ctx->gmm_llc_down_cb = llc_down_cb; + g_ctx->gmm_llc_down_cb_user_data = llc_down_user_data; +} + /******************************** * Primitive allocation: ********************************/ @@ -127,7 +159,7 @@ struct osmo_gprs_gmm_prim *gprs_gmm_prim_alloc(unsigned int sap, unsigned int ty return gmm_prim; } -/*** SN ***/ +/*** GMMREG ***/ static inline struct osmo_gprs_gmm_prim *gmm_prim_gmmreg_alloc(enum osmo_gprs_gmm_gmmreg_prim_type type, enum osmo_prim_operation operation, @@ -157,7 +189,7 @@ struct osmo_gprs_gmm_prim *gprs_gmm_prim_alloc_gmmreg_attach_cnf(void) /* TODO: 6.6.1.3 GMMREG-ATTACH.rej */ /* TS 24.007 6.6.1.4 GMMREG-DETACH.request */ -struct osmo_gprs_gmm_prim *osmo_gprs_gmm_prim_alloc_detach_req(void) +struct osmo_gprs_gmm_prim *osmo_gprs_gmm_prim_alloc_gmmreg_detach_req(void) { struct osmo_gprs_gmm_prim *gmm_prim; gmm_prim = gmm_prim_gmmreg_alloc(OSMO_GPRS_GMM_GMMREG_DETACH, PRIM_OP_REQUEST, 0); @@ -180,6 +212,87 @@ struct osmo_gprs_gmm_prim *osmo_gprs_gmm_prim_alloc_gmmreg_detach_ind(void) return gmm_prim; } +/*** GMMRR ***/ + +static inline struct osmo_gprs_gmm_prim *gmm_prim_gmmrr_alloc(enum osmo_gprs_gmm_gmmrr_prim_type type, + enum osmo_prim_operation operation, + unsigned int extra_size) +{ + return gprs_gmm_prim_alloc(OSMO_GPRS_GMM_SAP_GMMRR, type, operation, extra_size); +} + +/* 3GPP TS 24.007 9.3.2.1 GMMRR-ASSIGN-REQ:*/ +struct osmo_gprs_gmm_prim *gprs_gmm_prim_alloc_gmmrr_assign_req(uint32_t new_tlli) +{ + struct osmo_gprs_gmm_prim *gmm_prim; + gmm_prim = gmm_prim_gmmrr_alloc(OSMO_GPRS_GMM_GMMRR_ASSIGN, PRIM_OP_REQUEST, 0); + gmm_prim->gmmrr.assign_req.new_tlli = new_tlli; + return gmm_prim; +} + +/* 3GPP TS 24.007 9.3.2.2 GMMRR-PAGE-IND:*/ +struct osmo_gprs_gmm_prim *osmo_gprs_gmm_prim_alloc_gmmrr_page_ind(uint32_t tlli) +{ + struct osmo_gprs_gmm_prim *gmm_prim; + gmm_prim = gmm_prim_gmmrr_alloc(OSMO_GPRS_GMM_GMMRR_PAGE, PRIM_OP_INDICATION, 0); + gmm_prim->gmmrr.page_ind.tlli = tlli; + return gmm_prim; +} + +/*** GMMSM ***/ + +static inline struct osmo_gprs_gmm_prim *gmm_prim_gmmsm_alloc(enum osmo_gprs_gmm_gmmrr_prim_type type, + enum osmo_prim_operation operation, + unsigned int extra_size) +{ + return gprs_gmm_prim_alloc(OSMO_GPRS_GMM_SAP_GMMSM, type, operation, extra_size); +} + +/* 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 *gmm_prim; + gmm_prim = gmm_prim_gmmrr_alloc(OSMO_GPRS_GMM_GMMSM_ESTABLISH, PRIM_OP_REQUEST, 0); + 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 *gmm_prim; + gmm_prim = gmm_prim_gmmrr_alloc(OSMO_GPRS_GMM_GMMSM_ESTABLISH, PRIM_OP_CONFIRM, 0); + gmm_prim->gmmsm.establish_cnf.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_gmmrr_release_ind(void) +{ + struct osmo_gprs_gmm_prim *gmm_prim; + gmm_prim = gmm_prim_gmmrr_alloc(OSMO_GPRS_GMM_GMMSM_RELEASE, PRIM_OP_INDICATION, 0); + 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) +{ + struct osmo_gprs_gmm_prim *gmm_prim; + gmm_prim = gmm_prim_gmmrr_alloc(OSMO_GPRS_GMM_GMMSM_UNITDATA, PRIM_OP_REQUEST, 0); + 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) +{ + struct osmo_gprs_gmm_prim *gmm_prim; + gmm_prim = gmm_prim_gmmrr_alloc(OSMO_GPRS_GMM_GMMSM_UNITDATA, PRIM_OP_INDICATION, 0); + gmm_prim->gmmsm.unitdata_ind.smpdu = smpdu; + gmm_prim->gmmsm.unitdata_ind.smpdu_len = smpdu_len; + return gmm_prim; +} + static int gprs_gmm_prim_handle_unsupported(struct osmo_gprs_gmm_prim *gmm_prim) { LOGGMM(LOGL_ERROR, "Unsupported gmm_prim! %s\n", osmo_gprs_gmm_prim_name(gmm_prim)); @@ -187,6 +300,13 @@ static int gprs_gmm_prim_handle_unsupported(struct osmo_gprs_gmm_prim *gmm_prim) return -ENOTSUP; } +static int gprs_gmm_prim_handle_llc_unsupported(struct osmo_gprs_llc_prim *llc_prim) +{ + LOGGMM(LOGL_ERROR, "Unsupported llc_prim! %s\n", osmo_gprs_llc_prim_name(llc_prim)); + msgb_free(llc_prim->oph.msg); + return -ENOTSUP; +} + /******************************** * Handling from/to upper layers: ********************************/ @@ -206,10 +326,30 @@ int gprs_gmm_prim_call_up_cb(struct osmo_gprs_gmm_prim *gmm_prim) static int gprs_gmm_prim_handle_gmmreg_attach_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->gmmreg.attach_req.ptmsi; + if (gmm_prim->gmmreg.attach_req.imsi[0] != '\0') + OSMO_STRLCPY_ARRAY(gmme->imsi, gmm_prim->gmmreg.attach_req.imsi); + if (gmm_prim->gmmreg.attach_req.imei[0] != '\0') + OSMO_STRLCPY_ARRAY(gmme->imei, gmm_prim->gmmreg.attach_req.imei); + 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); - msgb_free(gmm_prim->oph.msg); return rc; } @@ -220,20 +360,12 @@ static int gprs_gmm_prim_handle_gmmreg_detach_req(struct osmo_gprs_gmm_prim *gmm rc = gprs_gmm_prim_handle_unsupported(gmm_prim); - msgb_free(gmm_prim->oph.msg); return rc; } -/* GMM higher layers push GMM primitive down to GMM layer: */ -int osmo_gprs_gmm_prim_upper_down(struct osmo_gprs_gmm_prim *gmm_prim) +static int gprs_gmm_prim_handle_gmmreg(struct osmo_gprs_gmm_prim *gmm_prim) { int rc; - OSMO_ASSERT(g_ctx); - - LOGGMM(LOGL_INFO, "Rx from upper layers: %s\n", osmo_gprs_gmm_prim_name(gmm_prim)); - - if (gmm_prim->oph.sap != OSMO_GPRS_GMM_SAP_GMMREG) - return gprs_gmm_prim_handle_unsupported(gmm_prim); switch (OSMO_PRIM_HDR(&gmm_prim->oph)) { case OSMO_PRIM(OSMO_GPRS_GMM_GMMREG_ATTACH, PRIM_OP_REQUEST): @@ -248,6 +380,72 @@ int osmo_gprs_gmm_prim_upper_down(struct osmo_gprs_gmm_prim *gmm_prim) return rc; } +/* TS 24.007 9.5.1.1 GMMSM-Establish.request:*/ +static int gprs_gmm_prim_handle_gmmsm_establish_req(struct osmo_gprs_gmm_prim *gmm_prim) +{ + int rc; + + rc = gprs_gmm_prim_handle_unsupported(gmm_prim); + + return rc; +} + +/* TS 24.007 9.5.1.5 GMMSM-Unitdata.request:*/ +static int gprs_gmm_prim_handle_gmmsm_unitdata_req(struct osmo_gprs_gmm_prim *gmm_prim) +{ + int rc; + + rc = gprs_gmm_prim_handle_unsupported(gmm_prim); + + return rc; +} + +static int gprs_gmm_prim_handle_gmmsm(struct osmo_gprs_gmm_prim *gmm_prim) +{ + int rc; + + switch (OSMO_PRIM_HDR(&gmm_prim->oph)) { + case OSMO_PRIM(OSMO_GPRS_GMM_GMMSM_ESTABLISH, PRIM_OP_REQUEST): + rc = gprs_gmm_prim_handle_gmmsm_establish_req(gmm_prim); + break; + case OSMO_PRIM(OSMO_GPRS_GMM_GMMSM_UNITDATA, PRIM_OP_REQUEST): + rc = gprs_gmm_prim_handle_gmmsm_unitdata_req(gmm_prim); + break; + default: + rc = gprs_gmm_prim_handle_unsupported(gmm_prim); + rc = 1; + } + return rc; +} + +/* GMM higher layers push GMM primitive down to GMM layer: */ +int osmo_gprs_gmm_prim_upper_down(struct osmo_gprs_gmm_prim *gmm_prim) +{ + int rc; + + LOGGMM(LOGL_INFO, "Rx from upper layers: %s\n", osmo_gprs_gmm_prim_name(gmm_prim)); + + + switch (gmm_prim->oph.sap) { + case OSMO_GPRS_GMM_SAP_GMMREG: + rc = gprs_gmm_prim_handle_gmmreg(gmm_prim); + break; + case OSMO_GPRS_GMM_SAP_GMMSM: + rc = gprs_gmm_prim_handle_gmmsm(gmm_prim); + break; + default: + rc = gprs_gmm_prim_handle_unsupported(gmm_prim); + rc = 1; + } + + /* Special return value '1' means: do not free */ + if (rc != 1) + msgb_free(gmm_prim->oph.msg); + else + rc = 0; + return rc; +} + /******************************** * Handling from/to lower layers: ********************************/ @@ -263,6 +461,21 @@ int gprs_gmm_prim_call_down_cb(struct osmo_gprs_gmm_prim *gmm_prim) return rc; } +static int gprs_gmm_prim_handle_gmmrr(struct osmo_gprs_gmm_prim *gmm_prim) +{ + int rc = 0; + switch (OSMO_PRIM_HDR(&gmm_prim->oph)) { + case OSMO_PRIM(OSMO_GPRS_GMM_GMMRR_PAGE, PRIM_OP_INDICATION): + rc = gprs_gmm_prim_handle_unsupported(gmm_prim); + rc = 1; + break; + default: + rc = gprs_gmm_prim_handle_unsupported(gmm_prim); + rc = 1; + } + return rc; +} + /* GMM lower layers (LLC) push GMM primitive up to GMM layer: */ int osmo_gprs_gmm_prim_lower_up(struct osmo_gprs_gmm_prim *gmm_prim) { @@ -274,9 +487,8 @@ int osmo_gprs_gmm_prim_lower_up(struct osmo_gprs_gmm_prim *gmm_prim) LOGGMM(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_LLGMM: - rc = gprs_gmm_prim_handle_unsupported(gmm_prim); - rc = 1; + case OSMO_GPRS_GMM_SAP_GMMRR: + rc = gprs_gmm_prim_handle_gmmrr(gmm_prim); break; default: rc = gprs_gmm_prim_handle_unsupported(gmm_prim); @@ -290,3 +502,114 @@ int osmo_gprs_gmm_prim_lower_up(struct osmo_gprs_gmm_prim *gmm_prim) rc = 0; return rc; } + +int gprs_gmm_prim_call_llc_down_cb(struct osmo_gprs_llc_prim *llc_prim) +{ + int rc; + if (g_ctx->gmm_llc_down_cb) + rc = g_ctx->gmm_llc_down_cb(llc_prim, g_ctx->gmm_llc_down_cb_user_data); + else + rc = gmm_llc_down_cb_dummy(llc_prim, g_ctx->gmm_llc_down_cb_user_data); + /* Special return value '1' means: do not free */ + if (rc != 1) + msgb_free(llc_prim->oph.msg); + else + rc = 0; + return rc; +} + +static int gprs_gmm_prim_handle_llgmm(struct osmo_gprs_llc_prim *llc_prim) +{ + int rc = 0; + switch (OSMO_PRIM_HDR(&llc_prim->oph)) { + case OSMO_PRIM(OSMO_GPRS_LLC_LLGMM_PAGE, PRIM_OP_INDICATION): + rc = gprs_gmm_prim_handle_llc_unsupported(llc_prim); + rc = 1; + break; + case OSMO_PRIM(OSMO_GPRS_LLC_LLGMM_PSHO, PRIM_OP_INDICATION): + rc = gprs_gmm_prim_handle_llc_unsupported(llc_prim); + rc = 1; + break; + case OSMO_PRIM(OSMO_GPRS_LLC_LLGMM_PSHO, PRIM_OP_CONFIRM): + rc = gprs_gmm_prim_handle_llc_unsupported(llc_prim); + rc = 1; + break; + } + return rc; +} + +static int gprs_gmm_prim_handle_ll_unitdata_ind(struct osmo_gprs_llc_prim *llc_prim) +{ + struct gprs_gmm_entity *gmme; + int rc = 0; + gmme = gprs_gmm_find_gmme_by_tlli(llc_prim->ll.tlli); + if (!gmme) { + LOGGMM(LOGL_NOTICE, "Rx %s: Unknown TLLI 0x%08x\n", + osmo_gprs_llc_prim_name(llc_prim), llc_prim->ll.tlli); + return -ENOENT; + } + rc = gprs_gmm_rx(gmme, + (struct gsm48_hdr *)llc_prim->ll.l3_pdu, + llc_prim->ll.l3_pdu_len); + + return rc; +} + +static int gprs_gmm_prim_handle_ll(struct osmo_gprs_llc_prim *llc_prim) +{ + int rc; + + switch (OSMO_PRIM_HDR(&llc_prim->oph)) { + case OSMO_PRIM(OSMO_GPRS_LLC_LL_RESET, PRIM_OP_INDICATION): + case OSMO_PRIM(OSMO_GPRS_LLC_LL_ESTABLISH, PRIM_OP_INDICATION): + case OSMO_PRIM(OSMO_GPRS_LLC_LL_ESTABLISH, PRIM_OP_CONFIRM): + case OSMO_PRIM(OSMO_GPRS_LLC_LL_RELEASE, PRIM_OP_INDICATION): + case OSMO_PRIM(OSMO_GPRS_LLC_LL_RELEASE, PRIM_OP_CONFIRM): + case OSMO_PRIM(OSMO_GPRS_LLC_LL_DATA, PRIM_OP_INDICATION): + case OSMO_PRIM(OSMO_GPRS_LLC_LL_DATA, PRIM_OP_CONFIRM): + rc = gprs_gmm_prim_handle_llc_unsupported(llc_prim); + rc = 1; + break; + case OSMO_PRIM(OSMO_GPRS_LLC_LL_UNITDATA, PRIM_OP_INDICATION): + rc = gprs_gmm_prim_handle_ll_unitdata_ind(llc_prim); + break; + case OSMO_PRIM(OSMO_GPRS_LLC_LL_UNITDATA, PRIM_OP_CONFIRM): + case OSMO_PRIM(OSMO_GPRS_LLC_LL_STATUS, PRIM_OP_INDICATION): + default: + rc = gprs_gmm_prim_handle_llc_unsupported(llc_prim); + rc = 1; + break; + } + + return rc; +} + +/* GMM lower layers (LLC) push GMM primitive up to GMM layer: */ +int osmo_gprs_gmm_prim_llc_lower_up(struct osmo_gprs_llc_prim *llc_prim) +{ + OSMO_ASSERT(g_ctx); + OSMO_ASSERT(llc_prim); + struct msgb *msg = llc_prim->oph.msg; + int rc; + + LOGGMM(LOGL_INFO, "Rx from lower layers: %s\n", osmo_gprs_llc_prim_name(llc_prim)); + + switch (llc_prim->oph.sap) { + case OSMO_GPRS_LLC_SAP_LLGM: + rc = gprs_gmm_prim_handle_llgmm(llc_prim); + break; + case OSMO_GPRS_LLC_SAP_LL: + rc = gprs_gmm_prim_handle_ll(llc_prim); + break; + default: + rc = gprs_gmm_prim_handle_llc_unsupported(llc_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/src/gmm/misc.c b/src/gmm/misc.c index ac141ee..dec0c78 100644 --- a/src/gmm/misc.c +++ b/src/gmm/misc.c @@ -21,6 +21,7 @@ #include #include #include +#include int g_gmm_log_cat[_OSMO_GPRS_GMM_LOGC_MAX] = { [0 ... _OSMO_GPRS_GMM_LOGC_MAX - 1] = DLGLOBAL }; @@ -28,4 +29,6 @@ void osmo_gprs_gmm_set_log_cat(enum osmo_gprs_gmm_log_cat logc, int logc_num) { OSMO_ASSERT(logc < _OSMO_GPRS_GMM_LOGC_MAX); g_gmm_log_cat[logc] = logc_num; + + gprs_gmm_ms_fsm_set_log_cat(logc_num); } diff --git a/tests/gmm/Makefile.am b/tests/gmm/Makefile.am index 3885fb5..fb3396d 100644 --- a/tests/gmm/Makefile.am +++ b/tests/gmm/Makefile.am @@ -21,9 +21,11 @@ EXTRA_DIST = \ gmm_prim_test.ok \ $(NULL) +# libosmo-gprs-llc.a is used below to access non-exported private symbols used in the test: gmm_prim_test_SOURCES = gmm_prim_test.c gmm_prim_test_LDADD = \ $(top_builddir)/src/gmm/libosmo-gprs-gmm.la \ + $(top_builddir)/src/llc/.libs/libosmo-gprs-llc.a \ $(LIBOSMOCORE_LIBS) \ $(LIBOSMOGSM_LIBS) \ $(NULL) diff --git a/tests/gmm/gmm_prim_test.c b/tests/gmm/gmm_prim_test.c index c282d58..fdc50b7 100644 --- a/tests/gmm/gmm_prim_test.c +++ b/tests/gmm/gmm_prim_test.c @@ -26,18 +26,130 @@ #include #include #include +#include #include +#include +#include #include #include +#include static void *tall_ctx = NULL; +/* +GSM A-I/F DTAP - Identity Request + Protocol Discriminator: GPRS mobility management messages (8) + .... 1000 = Protocol discriminator: GPRS mobility management messages (0x8) + 0000 .... = Skip Indicator: No indication of selected PLMN (0) + DTAP GPRS Mobility Management Message Type: Identity Request (0x15) + Identity Type 2 + .... 0... = Spare bit(s): 0 + .... .010 = Type of identity: IMEI (2) + Force to Standby + 0... .... = Spare bit(s): 0 + .000 .... = Force to standby: Force to standby not indicated (0) +*/ +static uint8_t pdu_gmm_identity_req[] = { 0x08, 0x15, 0x02 }; + +/* +Frame 101: 127 bytes on wire (1016 bits), 127 bytes captured (1016 bits) on interface lo, id 0 +Ethernet II, Src: 00:00:00_00:00:00 (00:00:00:00:00:00), Dst: 00:00:00_00:00:00 (00:00:00:00:00:00) +Internet Protocol Version 4, Src: 192.168.1.140, Dst: 192.168.1.140 +User Datagram Protocol, Src Port: 23000, Dst Port: 23010 +GPRS Network Service, PDU type: NS_UNITDATA, BVCI 1800 +Base Station Subsystem GPRS Protocol +MS-SGSN LLC (Mobile Station - Serving GPRS Support Node Logical Link Control) SAPI: GPRS Mobility Management +GSM A-I/F DTAP - Authentication and Ciphering Req + Protocol Discriminator: GPRS mobility management messages (8) + .... 1000 = Protocol discriminator: GPRS mobility management messages (0x8) + 0000 .... = Skip Indicator: No indication of selected PLMN (0) + DTAP GPRS Mobility Management Message Type: Authentication and Ciphering Req (0x12) + Ciphering Algorithm + .... 0... = Spare bit(s): 0 + .... .000 = Type of ciphering algorithm: ciphering not used (0) + IMEISV Request + 0... .... = Spare bit(s): 0 + .001 .... = IMEISV request: IMEISV requested (1) + Force to Standby + .... 0... = Spare bit(s): 0 + .... .000 = Force to standby: Force to standby not indicated (0) + A&C Reference Number + 0010 .... = A&C reference number: 2 + Authentication Parameter RAND + Element ID: 0x21 + RAND value: e2a6f3f8bb9ea701e0ce4f3364a99175 + Ciphering Key Sequence Number + 1000 .... = Element ID: 0x8- + .... 0... = Spare bit(s): 0 + .... .000 = key sequence: Ciphering key sequence number (0) +*/ +static uint8_t pdu_gmm_auth_ciph_req[] = { + 0x08, 0x12, 0x10, 0x20, 0x21, 0xe2, 0xa6, 0xf3, 0xf8, 0xbb, 0x9e, 0xa7, 0x01, 0xe0, 0xce, 0x4f, + 0x33, 0x64, 0xa9, 0x91, 0x75, 0x80 +}; + +/* +GSM A-I/F DTAP - Attach Accept + Protocol Discriminator: GPRS mobility management messages (8) + .... 1000 = Protocol discriminator: GPRS mobility management messages (0x8) + 0000 .... = Skip Indicator: No indication of selected PLMN (0) + DTAP GPRS Mobility Management Message Type: Attach Accept (0x02) + Attach Result + .... 0... = Follow-on proceed: False + .... .001 = Result of attach: GPRS only attached (1) + Force to Standby + 0... .... = Spare bit(s): 0 + .000 .... = Force to standby: Force to standby not indicated (0) + GPRS Timer + GPRS Timer: 10 min + 001. .... = Unit: value is incremented in multiples of 1 minute (1) + ...0 1010 = Timer value: 10 + Radio Priority 2 - Radio priority for TOM8 + .100 .... = Radio Priority (TOM8): priority level 4 (lowest) (4) + Radio Priority - Radio priority for SMS + .... .100 = Radio Priority (PDP or SMS): priority level 4 (lowest) (4) + Routing Area Identification - RAI: 234-70-5-0 + Routing area identification: 234-70-5-0 + Mobile Country Code (MCC): United Kingdom (234) + Mobile Network Code (MNC): AMSUK Limited (70) + Location Area Code (LAC): 0x0005 (5) + Routing Area Code (RAC): 0x00 (0) + GPRS Timer - Negotiated Ready Timer + Element ID: 0x17 + GPRS Timer: 44 sec + 000. .... = Unit: value is incremented in multiples of 2 seconds (0) + ...1 0110 = Timer value: 22 + Mobile Identity - Allocated P-TMSI - TMSI/P-TMSI (0xea711b41) + Element ID: 0x18 + Length: 5 + 1111 .... = Unused: 0xf + .... 0... = Odd/even indication: Even number of identity digits + .... .100 = Mobile Identity Type: TMSI/P-TMSI/M-TMSI (4) + TMSI/P-TMSI/M-TMSI/5G-TMSI: 3933281089 (0xea711b41) +*/ +static uint8_t pdu_gmm_att_acc[] = { +0x08, 0x02, 0x01, 0x2a, 0x44, 0x32, 0xf4, 0x07, 0x00, 0x05, 0x00, 0x17, 0x16, 0x18, 0x05, 0xf4, +0xea, 0x71, 0x1b, 0x41 +}; + int test_gmm_prim_up_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_GMMREG: + switch (OSMO_PRIM_HDR(&gmm_prim->oph)) { + case OSMO_PRIM(OSMO_GPRS_GMM_GMMREG_ATTACH, PRIM_OP_CONFIRM): + printf("%s(): Rx %s accepted=%u rej_cause=%u\n", __func__, pdu_name, + gmm_prim->gmmreg.attach_cnf.accepted, + gmm_prim->gmmreg.attach_cnf.rej.cause); + 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); @@ -50,6 +162,31 @@ int test_gmm_prim_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_GMMRR: + OSMO_ASSERT(OSMO_PRIM_HDR(&gmm_prim->oph) == + OSMO_PRIM(OSMO_GPRS_GMM_GMMRR_ASSIGN, PRIM_OP_REQUEST)); + printf("%s(): Rx %s new_tlli=0x%08x\n", __func__, pdu_name, gmm_prim->gmmrr.assign_req.new_tlli); + break; + default: + printf("%s(): Unexpected Rx %s\n", __func__, pdu_name); + OSMO_ASSERT(0); + } + return 0; +} + +int test_gmm_prim_llc_down_cb(struct osmo_gprs_llc_prim *llc_prim, void *user_data) +{ + const char *pdu_name = osmo_gprs_llc_prim_name(llc_prim); + + switch (llc_prim->oph.sap) { + case OSMO_GPRS_LLC_SAP_LLGM: + printf("%s(): Rx %s TLLI=0x%08x\n", __func__, pdu_name, llc_prim->llgmm.tlli); + break; + case OSMO_GPRS_LLC_SAP_LL: + printf("%s(): Rx %s TLLI=0x%08x SAPI=%s l3=[%s]\n", __func__, pdu_name, + llc_prim->ll.tlli, osmo_gprs_llc_sapi_name(llc_prim->ll.sapi), + osmo_hexdump(llc_prim->ll.l3_pdu, llc_prim->ll.l3_pdu_len)); + break; default: printf("%s(): Unexpected Rx %s\n", __func__, pdu_name); OSMO_ASSERT(0); @@ -59,21 +196,57 @@ int test_gmm_prim_down_cb(struct osmo_gprs_gmm_prim *gmm_prim, void *user_data) static void test_gmm_prim_ms(void) { - //struct osmo_gprs_gmm_prim *gmm_prim; + 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"; 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); - //gmm_prim = osmo_gprs_gmm_prim_alloc_gmmreg_attach_req(); - //OSMO_ASSERT(gmm_prim); - //rc = osmo_gprs_gmm_prim_upper_down(gmm_prim); - //OSMO_ASSERT(rc == 0); + /* MS sends GMM Attach Req */ + gmm_prim = osmo_gprs_gmm_prim_alloc_gmmreg_attach_req(); + gmm_prim->gmmreg.attach_req.attach_type = OSMO_GPRS_GMM_ATTACH_TYPE_GPRS; + gmm_prim->gmmreg.attach_req.ptmsi = ptmsi; + OSMO_STRLCPY_ARRAY(gmm_prim->gmmreg.attach_req.imsi, imsi); + OSMO_STRLCPY_ARRAY(gmm_prim->gmmreg.attach_req.imei, imei); + OSMO_STRLCPY_ARRAY(gmm_prim->gmmreg.attach_req.imeisv, imeisv); + + OSMO_ASSERT(gmm_prim); + rc = osmo_gprs_gmm_prim_upper_down(gmm_prim); + OSMO_ASSERT(rc == 0); + + /* 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 */ printf("==== %s() [end] ====\n", __func__); } @@ -90,6 +263,7 @@ int main(int argc, char *argv[]) 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); diff --git a/tests/gmm/gmm_prim_test.err b/tests/gmm/gmm_prim_test.err index e69de29..0256edf 100644 --- a/tests/gmm/gmm_prim_test.err +++ b/tests/gmm/gmm_prim_test.err @@ -0,0 +1,18 @@ +DLGLOBAL INFO Rx from upper layers: GMMREG-ATTACH.request +DLGLOBAL INFO GMM_MS{Null}: Allocated +DLGLOBAL INFO GMM_MS{Null}: Received Event ENABLE_GPRS_MODE +DLGLOBAL INFO GMM_MS{Null}: state_chg to Deregistered +DLGLOBAL INFO GMME(PTMSI-00000000) Tx GMM ATTACH REQUEST (new P-TMSI=0x00000000) +DLGLOBAL INFO GMM_MS{Deregistered}: Received Event ATTACH_REQUESTED +DLGLOBAL INFO 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 40b5ffc..91bcaeb 100644 --- a/tests/gmm/gmm_prim_test.ok +++ b/tests/gmm/gmm_prim_test.ok @@ -1,2 +1,10 @@ ==== test_gmm_prim_ms() [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_ms() [end] ====