summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJacob Erlbeck <jerlbeck@sysmocom.de>2014-09-05 14:32:36 +0200
committerHolger Hans Peter Freyther <holger@moiji-mobile.com>2014-10-27 10:50:36 +0100
commit78ecaf0561bfed3f09ce9c180434da59e71202bd (patch)
treee90c99ff795d14d96ff37c358179a9e5581bbcab
parent99985b5ea8e2d69d1e63a9423fbe40b872b0c0f5 (diff)
sgsn: Send detach(re-attach) instead of gmm status if TLLI unknown
The osmo-sgsn sends Status messages (or nothing in case of non GMM/GSM) when the TLLI is unknown. This prevents the MS from reconnecting. This patch adds the initiation of an MT detach procedure to force a re-attach to set up a valid LLE context if an LLE or an MM context cannot be found. Since this can also be triggered by non-GMM SAPI messages, a GPRS application callback sgsn_force_reattach_oldmsg is added which in turn calls the GMM layer to generate the GSM 04.08 specific messages. Note that the MS can be left in REGISTERED state after initially wanting to detach itself, since it will receive a Detach Req (re-attach) when sending a DEACT PDP CTX REQ after the SGSN or gbproxy (P-TMSI patching enabled) has been restarted. This same behaviour has been observed with another SGSN. Sponsored-by: On-Waves ehf
-rw-r--r--openbsc/include/openbsc/gprs_gmm.h1
-rw-r--r--openbsc/include/openbsc/gprs_llc.h1
-rw-r--r--openbsc/include/openbsc/gprs_sgsn.h3
-rw-r--r--openbsc/include/openbsc/gsm_04_08_gprs.h1
-rw-r--r--openbsc/src/gprs/gprs_gmm.c132
-rw-r--r--openbsc/src/gprs/gprs_llc.c32
-rw-r--r--openbsc/src/gprs/gprs_sgsn.c6
7 files changed, 164 insertions, 12 deletions
diff --git a/openbsc/include/openbsc/gprs_gmm.h b/openbsc/include/openbsc/gprs_gmm.h
index f6b3e5e7c..e13b9dc65 100644
--- a/openbsc/include/openbsc/gprs_gmm.h
+++ b/openbsc/include/openbsc/gprs_gmm.h
@@ -11,6 +11,7 @@ int gsm48_tx_gsm_act_pdp_acc(struct sgsn_pdp_ctx *pdp);
int gsm48_tx_gsm_deact_pdp_acc(struct sgsn_pdp_ctx *pdp);
int gsm0408_gprs_rcvmsg(struct msgb *msg, struct gprs_llc_llme *llme);
+int gsm0408_gprs_force_reattach(struct msgb *msg, struct sgsn_mm_ctx *mmctx);
int gprs_gmm_rx_suspend(struct gprs_ra_id *raid, uint32_t tlli);
int gprs_gmm_rx_resume(struct gprs_ra_id *raid, uint32_t tlli,
diff --git a/openbsc/include/openbsc/gprs_llc.h b/openbsc/include/openbsc/gprs_llc.h
index 9689a3766..fc6216ccc 100644
--- a/openbsc/include/openbsc/gprs_llc.h
+++ b/openbsc/include/openbsc/gprs_llc.h
@@ -207,6 +207,7 @@ int gprs_llc_tx_ui(struct msgb *msg, uint8_t sapi, int command,
/* Chapter 7.2.1.2 LLGMM-RESET.req */
int gprs_llgmm_reset(struct gprs_llc_llme *llme);
+int gprs_llgmm_reset_oldmsg(struct msgb* oldmsg, uint8_t sapi);
/* 04.64 Chapter 7.2.1.1 LLGMM-ASSIGN */
int gprs_llgmm_assign(struct gprs_llc_llme *llme,
diff --git a/openbsc/include/openbsc/gprs_sgsn.h b/openbsc/include/openbsc/gprs_sgsn.h
index c9df82444..9226c23b9 100644
--- a/openbsc/include/openbsc/gprs_sgsn.h
+++ b/openbsc/include/openbsc/gprs_sgsn.h
@@ -229,6 +229,9 @@ int drop_all_pdp_for_ggsn(struct sgsn_ggsn_ctx *ggsn);
char *gprs_pdpaddr2str(uint8_t *pdpa, uint8_t len);
+/* Force re-attachment based on msgb meta data */
+int sgsn_force_reattach_oldmsg(struct msgb *oldmsg);
+
/*
* ctrl interface related work
*/
diff --git a/openbsc/include/openbsc/gsm_04_08_gprs.h b/openbsc/include/openbsc/gsm_04_08_gprs.h
index cd2a08c59..b96d22b87 100644
--- a/openbsc/include/openbsc/gsm_04_08_gprs.h
+++ b/openbsc/include/openbsc/gsm_04_08_gprs.h
@@ -70,6 +70,7 @@ enum gsm48_gprs_ie_mm {
GSM48_IE_GMM_AUTH_RAND = 0x21, /* 10.5.3.1 */
GSM48_IE_GMM_AUTH_SRES = 0x22, /* 10.5.3.2 */
GSM48_IE_GMM_IMEISV = 0x23, /* 10.5.1.4 */
+ GSM48_IE_GMM_CAUSE = 0x25, /* 10.5.5.14 */
GSM48_IE_GMM_DRX_PARAM = 0x27, /* 10.5.5.6 */
GSM48_IE_GMM_MS_NET_CAPA = 0x31, /* 10.5.5.12 */
GSM48_IE_GMM_PDP_CTX_STATUS = 0x32, /* 10.5.7.1 */
diff --git a/openbsc/src/gprs/gprs_gmm.c b/openbsc/src/gprs/gprs_gmm.c
index 9df0cc62f..c55c300c1 100644
--- a/openbsc/src/gprs/gprs_gmm.c
+++ b/openbsc/src/gprs/gprs_gmm.c
@@ -171,6 +171,13 @@ const struct value_string gprs_det_t_mo_strs[] = {
{ 0, NULL }
};
+const struct value_string gprs_det_t_mt_strs[] = {
+ { GPRS_DET_T_MT_REATT_REQ, "re-attach required" },
+ { GPRS_DET_T_MT_REATT_NOTREQ, "re-attach not required" },
+ { GPRS_DET_T_MT_IMSI, "IMSI detach (after VLR failure)" },
+ { 0, NULL }
+};
+
static const struct tlv_definition gsm48_gmm_att_tlvdef = {
.def = {
[GSM48_IE_GMM_CIPH_CKSN] = { TLV_TYPE_FIXED, 1 },
@@ -265,20 +272,28 @@ static void mmctx2msgid(struct msgb *msg, const struct sgsn_mm_ctx *mm)
msgb_nsei(msg) = mm->nsei;
}
+static void delete_pdp_contexts(struct sgsn_mm_ctx *ctx, const char *log_text)
+{
+ struct sgsn_pdp_ctx *pdp, *pdp2;
+
+ /* delete all existing PDP contexts for this MS */
+ llist_for_each_entry_safe(pdp, pdp2, &ctx->pdp_list, list) {
+ LOGMMCTXP(LOGL_NOTICE, ctx,
+ "Dropping PDP context for NSAPI=%u due to %s\n",
+ pdp->nsapi, log_text);
+ sgsn_pdp_ctx_terminate(pdp);
+ }
+}
+
static void mm_ctx_cleanup_free(struct sgsn_mm_ctx *ctx, const char *log_text)
{
struct gprs_llc_llme *llme = ctx->llme;
uint32_t tlli = ctx->tlli;
- struct sgsn_pdp_ctx *pdp, *pdp2;
/* Mark MM state as deregistered */
ctx->mm_state = GMM_DEREGISTERED;
- llist_for_each_entry_safe(pdp, pdp2, &ctx->pdp_list, list) {
- LOGMMCTXP(LOGL_NOTICE, ctx, "Dropping PDP context for NSAPI=%u "
- "due to %s\n", pdp->nsapi, log_text);
- sgsn_pdp_ctx_terminate(pdp);
- }
+ delete_pdp_contexts(ctx, log_text);
sgsn_mm_ctx_free(ctx);
ctx = NULL;
@@ -339,6 +354,45 @@ static int gsm48_tx_sm_status_oldmsg(struct msgb *oldmsg, uint8_t cause)
return _tx_status(msg, cause, NULL, 1);
}
+static int _tx_detach_req(struct msgb *msg, uint8_t detach_type, uint8_t cause,
+ struct sgsn_mm_ctx *mmctx)
+{
+ struct gsm48_hdr *gh;
+
+ /* MMCTX might be NULL! */
+
+ DEBUGP(DMM, "<- GPRS MM DETACH REQ (type: %s, cause: %s)\n",
+ get_value_string(gprs_det_t_mt_strs, detach_type),
+ get_value_string(gmm_cause_names, cause));
+
+ gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1);
+
+ gh->proto_discr = GSM48_PDISC_MM_GPRS;
+ gh->msg_type = GSM48_MT_GMM_DETACH_REQ;
+ gh->data[0] = detach_type & 0x07;
+
+ msgb_tv_put(msg, GSM48_IE_GMM_CAUSE, cause);
+
+ return gsm48_gmm_sendmsg(msg, 0, mmctx);
+}
+
+static int gsm48_tx_gmm_detach_req(struct sgsn_mm_ctx *mmctx,
+ uint8_t detach_type, uint8_t cause)
+{
+ struct msgb *msg = gsm48_msgb_alloc();
+
+ mmctx2msgid(msg, mmctx);
+ return _tx_detach_req(msg, detach_type, cause, mmctx);
+}
+
+static int gsm48_tx_gmm_detach_req_oldmsg(struct msgb *oldmsg,
+ uint8_t detach_type, uint8_t cause)
+{
+ struct msgb *msg = gsm48_msgb_alloc();
+
+ gmm_copy_id(msg, oldmsg);
+ return _tx_detach_req(msg, detach_type, cause, NULL);
+}
static struct gsm48_qos default_qos = {
.delay_class = 4, /* best effort */
@@ -963,8 +1017,9 @@ static int gsm48_rx_gmm_ra_upd_req(struct sgsn_mm_ctx *mmctx, struct msgb *msg,
LOGP(DMM, LOGL_NOTICE, "LLC XID RESET\n");
gprs_llgmm_reset(llme);
/* The MS has to perform GPRS attach */
- /* Device is still IMSI atached for CS but initiate GPRS ATTACH */
- return gsm48_tx_gmm_ra_upd_rej(msg, GMM_CAUSE_MS_ID_NOT_DERIVED);
+ /* Device is still IMSI attached for CS but initiate GPRS ATTACH,
+ * see GSM 04.08, 4.7.5.1.4 and G.6 */
+ return gsm48_tx_gmm_ra_upd_rej(msg, GMM_CAUSE_IMPL_DETACHED);
}
/* Store new BVCI/NSEI in MM context (FIXME: delay until we ack?) */
@@ -1033,8 +1088,33 @@ static int gsm0408_rcv_gmm(struct sgsn_mm_ctx *mmctx, struct msgb *msg,
gh->msg_type != GSM48_MT_GMM_ATTACH_REQ &&
gh->msg_type != GSM48_MT_GMM_RA_UPD_REQ) {
LOGP(DMM, LOGL_NOTICE, "Cannot handle GMM for unknown MM CTX\n");
+ /* 4.7.10 */
+ if (gh->msg_type == GSM48_MT_GMM_STATUS)
+ return 0;
+
gprs_llgmm_reset(llme);
- return gsm48_tx_gmm_status_oldmsg(msg, GMM_CAUSE_MS_ID_NOT_DERIVED);
+
+ /* Don't reply or establish a LLME on DETACH_ACK */
+ if (gh->msg_type == GSM48_MT_GMM_DETACH_ACK) {
+ /* TLLI unassignment */
+ return gprs_llgmm_assign(llme, llme->tlli, 0xffffffff,
+ GPRS_ALGO_GEA0, NULL);
+ }
+
+ /* Don't force it into re-attachment */
+ if (gh->msg_type == GSM48_MT_GMM_DETACH_REQ) {
+ rc = gsm48_tx_gmm_detach_req_oldmsg(
+ msg, GPRS_DET_T_MT_REATT_NOTREQ,
+ GMM_CAUSE_IMPL_DETACHED);
+
+ /* TLLI unassignment */
+ gprs_llgmm_assign(llme, llme->tlli, 0xffffffff,
+ GPRS_ALGO_GEA0, NULL);
+ return rc;
+ }
+
+ /* Force the MS to re-attach */
+ return sgsn_force_reattach_oldmsg(msg);
}
switch (gh->msg_type) {
@@ -1054,6 +1134,11 @@ static int gsm0408_rcv_gmm(struct sgsn_mm_ctx *mmctx, struct msgb *msg,
case GSM48_MT_GMM_DETACH_REQ:
rc = gsm48_rx_gmm_det_req(mmctx, msg);
break;
+ case GSM48_MT_GMM_DETACH_ACK:
+ LOGMMCTXP(LOGL_INFO, mmctx, "-> DETACH ACK\n");
+ mm_ctx_cleanup_free(mmctx, "GPRS DETACH ACK");
+ rc = 0;
+ break;
case GSM48_MT_GMM_ATTACH_COMPL:
/* only in case SGSN offered new P-TMSI */
LOGMMCTXP(LOGL_INFO, mmctx, "-> ATTACH COMPLETE\n");
@@ -1515,8 +1600,11 @@ static int gsm0408_rcv_gsm(struct sgsn_mm_ctx *mmctx, struct msgb *msg,
if (!mmctx) {
LOGP(DMM, LOGL_NOTICE, "Cannot handle SM for unknown MM CTX\n");
- gsm48_tx_gmm_status_oldmsg(msg, GMM_CAUSE_IMPL_DETACHED);
- return gsm48_tx_sm_status_oldmsg(msg, GSM_CAUSE_PROTO_ERR_UNSPEC);
+ /* 6.1.3.6 */
+ if (gh->msg_type == GSM48_MT_GSM_STATUS)
+ return 0;
+
+ return sgsn_force_reattach_oldmsg(msg);
}
switch (gh->msg_type) {
@@ -1550,6 +1638,28 @@ static int gsm0408_rcv_gsm(struct sgsn_mm_ctx *mmctx, struct msgb *msg,
return rc;
}
+int gsm0408_gprs_force_reattach(struct msgb *msg, struct sgsn_mm_ctx *mmctx)
+{
+ gprs_llgmm_reset_oldmsg(msg, GPRS_SAPI_GMM);
+
+ if (!mmctx)
+ return gsm48_tx_gmm_detach_req_oldmsg(
+ msg, GPRS_DET_T_MT_REATT_REQ, GMM_CAUSE_IMPL_DETACHED);
+
+ /* Mark MM state as deregistered initiated */
+ mmctx->mm_state = GMM_DEREGISTERED_INIT;
+
+ /* Delete all existing PDP contexts for this MS */
+ delete_pdp_contexts(mmctx, "forced reattach");
+
+ /* TODO:
+ * properly start detach procedure (timeout, wait for ACK) and
+ * do nothing if a re-attach is in progress */
+
+ return gsm48_tx_gmm_detach_req(
+ mmctx, GPRS_DET_T_MT_REATT_REQ, GMM_CAUSE_IMPL_DETACHED);
+}
+
/* Main entry point for incoming 04.08 GPRS messages */
int gsm0408_gprs_rcvmsg(struct msgb *msg, struct gprs_llc_llme *llme)
{
diff --git a/openbsc/src/gprs/gprs_llc.c b/openbsc/src/gprs/gprs_llc.c
index c88d16269..0b4613e0f 100644
--- a/openbsc/src/gprs/gprs_llc.c
+++ b/openbsc/src/gprs/gprs_llc.c
@@ -608,8 +608,19 @@ int gprs_llc_rcvmsg(struct msgb *msg, struct tlv_parsed *tv)
/* find the LLC Entity for this TLLI+SAPI tuple */
lle = lle_for_rx_by_tlli_sapi(msgb_tlli(msg), llhp.sapi, llhp.cmd);
- if (!lle)
+ if (!lle) {
+ switch (llhp.sapi) {
+ case GPRS_SAPI_SNDCP3:
+ case GPRS_SAPI_SNDCP5:
+ case GPRS_SAPI_SNDCP9:
+ case GPRS_SAPI_SNDCP11:
+ /* Ask an upper layer for help. */
+ return sgsn_force_reattach_oldmsg(msg);
+ default:
+ break;
+ }
return 0;
+ }
/* decrypt information field + FCS, if needed! */
if (llhp.is_encrypted) {
@@ -773,6 +784,25 @@ int gprs_llgmm_reset(struct gprs_llc_llme *llme)
return gprs_llc_tx_xid(lle, msg, 1);
}
+int gprs_llgmm_reset_oldmsg(struct msgb* oldmsg, uint8_t sapi)
+{
+ struct msgb *msg = msgb_alloc_headroom(4096, 1024, "LLC_XID");
+ int random = rand();
+
+ /* First XID component must be RESET */
+ msgb_put_xid_par(msg, GPRS_LLC_XID_T_RESET, 0, NULL);
+ /* randomly select new IOV-UI */
+ msgb_put_xid_par(msg, GPRS_LLC_XID_T_IOV_UI, 4, (uint8_t *) &random);
+
+ /* FIXME: Start T200, wait for XID response */
+
+ msgb_tlli(msg) = msgb_tlli(oldmsg);
+ msgb_bvci(msg) = msgb_bvci(oldmsg);
+ msgb_nsei(msg) = msgb_nsei(oldmsg);
+
+ return gprs_llc_tx_u(msg, sapi, 1, GPRS_LLC_U_XID, 1);
+}
+
int gprs_llc_init(const char *cipher_plugin_path)
{
return gprs_cipher_load(cipher_plugin_path);
diff --git a/openbsc/src/gprs/gprs_sgsn.c b/openbsc/src/gprs/gprs_sgsn.c
index 75f8ae5f0..15f475394 100644
--- a/openbsc/src/gprs/gprs_sgsn.c
+++ b/openbsc/src/gprs/gprs_sgsn.c
@@ -445,3 +445,9 @@ int drop_all_pdp_for_ggsn(struct sgsn_ggsn_ctx *ggsn)
return num;
}
+
+int sgsn_force_reattach_oldmsg(struct msgb *oldmsg)
+{
+ return gsm0408_gprs_force_reattach(oldmsg, NULL);
+}
+