diff options
author | Harald Welte <laforge@gnumonks.org> | 2010-12-24 21:13:26 +0100 |
---|---|---|
committer | Harald Welte <laforge@gnumonks.org> | 2010-12-26 19:20:03 +0100 |
commit | a9b473a3c25d5b0f0993c3c53f6004b0e86fca5c (patch) | |
tree | d41b0f0bf4756737e0aa896b50c21f21e1fc7263 /openbsc/src/gprs/gprs_gmm.c | |
parent | 3357add225140a2e87a9d69150bf19cf578e24f7 (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.c | 99 |
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; |