aboutsummaryrefslogtreecommitdiffstats
path: root/openbsc/src/gprs/gprs_gmm.c
diff options
context:
space:
mode:
authorHarald Welte <laforge@gnumonks.org>2010-12-24 21:13:26 +0100
committerHarald Welte <laforge@gnumonks.org>2010-12-26 19:20:03 +0100
commita9b473a3c25d5b0f0993c3c53f6004b0e86fca5c (patch)
treed41b0f0bf4756737e0aa896b50c21f21e1fc7263 /openbsc/src/gprs/gprs_gmm.c
parent3357add225140a2e87a9d69150bf19cf578e24f7 (diff)
SGSN: Implement network-initiated PDP CTX DEACT when GGSN restarts
If the GGSN restarts, its restart counter will increase. We can detect that and accordingly release/delete all PDP contexts for that GGSN.
Diffstat (limited to 'openbsc/src/gprs/gprs_gmm.c')
-rw-r--r--openbsc/src/gprs/gprs_gmm.c99
1 files changed, 99 insertions, 0 deletions
diff --git a/openbsc/src/gprs/gprs_gmm.c b/openbsc/src/gprs/gprs_gmm.c
index 158f577a7..ee6e3665b 100644
--- a/openbsc/src/gprs/gprs_gmm.c
+++ b/openbsc/src/gprs/gprs_gmm.c
@@ -67,6 +67,12 @@
#define GSM0408_T3314_SECS 44 /* force to STBY on expiry */
#define GSM0408_T3316_SECS 44
+/* Section 11.3 / Table 11.2d Timers of Session Management - network side */
+#define GSM0408_T3385_SECS 8 /* wait for ACT PDP CTX REQ */
+#define GSM0408_T3386_SECS 8 /* wait for MODIFY PDP CTX ACK */
+#define GSM0408_T3395_SECS 8 /* wait for DEACT PDP CTX ACK */
+#define GSM0408_T3397_SECS 8 /* wait for DEACT AA PDP CTX ACK */
+
extern struct sgsn_instance *sgsn;
/* Protocol related stuff, should go into libosmocore */
@@ -1092,6 +1098,25 @@ static void mmctx_timer_cb(void *_mm)
/* GPRS SESSION MANAGEMENT */
+static void pdpctx_timer_cb(void *_mm);
+
+static void pdpctx_timer_start(struct sgsn_pdp_ctx *pdp, unsigned int T,
+ unsigned int seconds)
+{
+ if (bsc_timer_pending(&pdp->timer))
+ LOGP(DMM, LOGL_ERROR, "Starting MM timer %u while old "
+ "timer %u pending\n", T, pdp->T);
+ pdp->T = T;
+ pdp->num_T_exp = 0;
+
+ /* FIXME: we should do this only once ? */
+ pdp->timer.data = pdp;
+ pdp->timer.cb = &pdpctx_timer_cb;
+
+ bsc_schedule_timer(&pdp->timer, seconds, 0);
+}
+
+
static void msgb_put_pdp_addr_ipv4(struct msgb *msg, uint32_t ipaddr)
{
uint8_t v[6];
@@ -1179,6 +1204,33 @@ int gsm48_tx_gsm_act_pdp_rej(struct sgsn_mm_ctx *mm, uint8_t tid,
return gsm48_gmm_sendmsg(msg, 0, mm);
}
+/* Section 9.5.8: Deactivate PDP Context Request */
+static int _gsm48_tx_gsm_deact_pdp_req(struct sgsn_mm_ctx *mm, uint8_t tid,
+ uint8_t sm_cause)
+{
+ struct msgb *msg = gsm48_msgb_alloc();
+ struct gsm48_hdr *gh;
+ uint8_t transaction_id = tid ^ 0x8; /* flip */
+
+ DEBUGP(DMM, "<- DEACTIVATE PDP CONTEXT REQ\n");
+
+ mmctx2msgid(msg, mm);
+
+ gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
+ gh->proto_discr = GSM48_PDISC_SM_GPRS | (transaction_id << 4);
+ gh->msg_type = GSM48_MT_GSM_DEACT_PDP_REQ;
+
+ msgb_v_put(msg, sm_cause);
+
+ return gsm48_gmm_sendmsg(msg, 0, mm);
+}
+int gsm48_tx_gsm_deact_pdp_req(struct sgsn_pdp_ctx *pdp, uint8_t sm_cause)
+{
+ pdpctx_timer_start(pdp, 3395, GSM0408_T3395_SECS);
+
+ return _gsm48_tx_gsm_deact_pdp_req(pdp->mm, pdp->ti, sm_cause);
+}
+
/* Section 9.5.9: Deactivate PDP Context Accept */
static int _gsm48_tx_gsm_deact_pdp_acc(struct sgsn_mm_ctx *mm, uint8_t tid)
{
@@ -1341,6 +1393,26 @@ static int gsm48_rx_gsm_deact_pdp_req(struct sgsn_mm_ctx *mm, struct msgb *msg)
return sgsn_delete_pdp_ctx(pdp);
}
+/* Section 9.5.9: Deactivate PDP Context Accept */
+static int gsm48_rx_gsm_deact_pdp_ack(struct sgsn_mm_ctx *mm, struct msgb *msg)
+{
+ struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(msg);
+ uint8_t transaction_id = (gh->proto_discr >> 4);
+ struct sgsn_pdp_ctx *pdp;
+
+ DEBUGP(DMM, "-> DEACTIVATE PDP CONTEXT ACK\n");
+
+ pdp = sgsn_pdp_ctx_by_tid(mm, transaction_id);
+ if (!pdp) {
+ LOGP(DMM, LOGL_NOTICE, "Deactivate PDP Context Accept for "
+ "non-existing PDP Context (IMSI=%s, TI=%u)\n",
+ mm->imsi, transaction_id);
+ return 0;
+ }
+
+ return sgsn_delete_pdp_ctx(pdp);
+}
+
static int gsm48_rx_gsm_status(struct sgsn_mm_ctx *ctx, struct msgb *msg)
{
struct gsm48_hdr *gh = msgb_l3(msg);
@@ -1351,6 +1423,30 @@ static int gsm48_rx_gsm_status(struct sgsn_mm_ctx *ctx, struct msgb *msg)
return 0;
}
+static void pdpctx_timer_cb(void *_pdp)
+{
+ struct sgsn_pdp_ctx *pdp = _pdp;
+
+ pdp->num_T_exp++;
+
+ switch (pdp->T) {
+ case 3395: /* waiting for PDP CTX DEACT ACK */
+ if (pdp->num_T_exp >= 4) {
+ LOGP(DMM, LOGL_NOTICE, "T3395 expired >= 5 times\n");
+ pdp->state = PDP_STATE_INACTIVE;
+ sgsn_delete_pdp_ctx(pdp);
+ break;
+ }
+ gsm48_tx_gsm_deact_pdp_req(pdp, GSM_CAUSE_NET_FAIL);
+ bsc_schedule_timer(&pdp->timer, GSM0408_T3395_SECS, 0);
+ break;
+ default:
+ LOGP(DMM, LOGL_ERROR, "timer expired in unknown mode %u\n",
+ pdp->T);
+ }
+}
+
+
/* GPRS Session Management */
static int gsm0408_rcv_gsm(struct sgsn_mm_ctx *mmctx, struct msgb *msg,
struct gprs_llc_llme *llme)
@@ -1373,6 +1469,9 @@ static int gsm0408_rcv_gsm(struct sgsn_mm_ctx *mmctx, struct msgb *msg,
case GSM48_MT_GSM_DEACT_PDP_REQ:
rc = gsm48_rx_gsm_deact_pdp_req(mmctx, msg);
break;
+ case GSM48_MT_GSM_DEACT_PDP_ACK:
+ rc = gsm48_rx_gsm_deact_pdp_ack(mmctx, msg);
+ break;
case GSM48_MT_GSM_STATUS:
rc = gsm48_rx_gsm_status(mmctx, msg);
break;