gmm: Initial implementation of GPRS Attach

This patch contains further work on several areas for the GMM layer,
like fixes and improvements in existing primitives, initial FSM
implementation, initial Tx and Rx of some GMM messages, etc.

Related: OS#5501
Change-Id: I212053b3a3f27ef7d63503c3d5ef08453b2d2056
This commit is contained in:
Pau Espin 2023-03-15 19:10:35 +01:00
parent a5d5444aa7
commit 8a72adf9cb
18 changed files with 1865 additions and 32 deletions

View File

@ -1,6 +1,8 @@
noinst_HEADERS = \
gmm_ms_fsm.h \
gmm_private.h \
gmm_pdu.h \
$(NULL)
gmm_HEADERS = \

View File

@ -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);

View File

@ -0,0 +1,51 @@
#pragma once
#include <osmocom/core/fsm.h>
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);

View File

@ -0,0 +1,33 @@
/* GMM PDUs, 3GPP TS 9.4 24.008 GPRS Mobility Management Messages */
#pragma once
#include <osmocom/core/msgb.h>
#include <osmocom/gsm/tlv.h>
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);

View File

@ -13,12 +13,15 @@
#include <osmocom/core/utils.h>
#include <osmocom/core/prim.h>
#include <osmocom/gsm/protocol/gsm_23_003.h>
#include <osmocom/gprs/gmm/gmm.h>
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);

View File

@ -10,8 +10,12 @@
#include <osmocom/core/msgb.h>
#include <osmocom/core/logging.h>
#include <osmocom/core/endian.h>
#include <osmocom/gsm/protocol/gsm_23_003.h>
#include <osmocom/gprs/llc/llc_prim.h>
#include <osmocom/gprs/gmm/gmm.h>
#include <osmocom/gprs/gmm/gmm_prim.h>
#include <osmocom/gprs/gmm/gmm_ms_fsm.h>
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)

View File

@ -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 {

View File

@ -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}/

View File

@ -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 \

View File

@ -24,24 +24,108 @@
#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/gmm/gmm.h>
#include <osmocom/gprs/gmm/gmm_prim.h>
#include <osmocom/gprs/gmm/gmm_private.h>
#include <osmocom/gprs/gmm/gmm_ms_fsm.h>
#include <osmocom/gprs/gmm/gmm_pdu.h>
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;
}

296
src/gmm/gmm_ms_fsm.c Normal file
View File

@ -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 <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 <osmocom/core/tdef.h>
#include <osmocom/gprs/gmm/gmm_ms_fsm.h>
#include <osmocom/gprs/gmm/gmm.h>
#include <osmocom/gprs/gmm/gmm_private.h>
#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;
}

330
src/gmm/gmm_pdu.c Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
*
*/
#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/gprs/gmm/gmm_private.h>
#include <osmocom/gprs/gmm/gmm_pdu.h>
/* 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;
}

View File

@ -29,6 +29,7 @@
#include <osmocom/gprs/gmm/gmm.h>
#include <osmocom/gprs/gmm/gmm_prim.h>
#include <osmocom/gprs/gmm/gmm_private.h>
#include <osmocom/gprs/gmm/gmm_pdu.h>
#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;
}

View File

@ -21,6 +21,7 @@
#include <osmocom/core/logging.h>
#include <osmocom/core/utils.h>
#include <osmocom/gprs/gmm/gmm.h>
#include <osmocom/gprs/gmm/gmm_ms_fsm.h>
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);
}

View File

@ -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)

View File

@ -26,18 +26,130 @@
#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/llc/llc_prim.h>
#include <osmocom/gprs/llc/llc_private.h>
#include <osmocom/gprs/gmm/gmm.h>
#include <osmocom/gprs/gmm/gmm_prim.h>
#include <osmocom/gprs/gmm/gmm_pdu.h>
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);

View File

@ -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

View File

@ -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] ====