diff options
authorPau Espin Pedrol <pespin@sysmocom.de>2018-07-17 15:56:53 +0200
committerPau Espin Pedrol <pespin@sysmocom.de>2018-07-24 11:39:25 +0200
commitef6d78ff7f96ce898e976f1c2bc638ff5d2d29fb (patch)
parent03dc773e081c608524a0907cb1d867f792156157 (diff)
sgsn: Fix crash using new libgtp cb_recovery2 API
When PDP CTX CREATE ACK is received with an increased RestartCtr, cb_recovery2 is called first, which will dettach ggsn from al pdp ctx (free the pdp_t). But when giving control back from the ctrl, libgtp still uses that freed ctx and sends it back to osmo-sgsn through cb_conf(). As specs state in any case that we need to handle the message containing the increased RestartCtr as valid, we then need to avoid freeing the pdp ctx and leave handling for later in cb_conf. Depends: osmo-ggsn (libgtp) Change-Id I53e92298f2f6b84d662a3300d922e8c2ccb178bc. Change-Id: I0989c00e18ca95a099e1a312940eaac71957b444
3 files changed, 16 insertions, 9 deletions
diff --git a/include/osmocom/sgsn/gprs_sgsn.h b/include/osmocom/sgsn/gprs_sgsn.h
index 8eba2d41..6f16dc75 100644
--- a/include/osmocom/sgsn/gprs_sgsn.h
+++ b/include/osmocom/sgsn/gprs_sgsn.h
@@ -362,7 +362,7 @@ struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_by_id(uint32_t id);
struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_by_addr(struct in_addr *addr);
struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_find_alloc(uint32_t id);
void sgsn_ggsn_ctx_drop_pdp(struct sgsn_pdp_ctx *pctx);
-int sgsn_ggsn_ctx_drop_all_pdp(struct sgsn_ggsn_ctx *ggsn);
+int sgsn_ggsn_ctx_drop_all_pdp_except(struct sgsn_ggsn_ctx *ggsn, struct sgsn_pdp_ctx *except);
void sgsn_ggsn_ctx_add_pdp(struct sgsn_ggsn_ctx *ggc, struct sgsn_pdp_ctx *pdp);
void sgsn_ggsn_ctx_remove_pdp(struct sgsn_ggsn_ctx *ggc, struct sgsn_pdp_ctx *pdp);
diff --git a/src/gprs/gprs_sgsn.c b/src/gprs/gprs_sgsn.c
index e6d88e3c..90461576 100644
--- a/src/gprs/gprs_sgsn.c
+++ b/src/gprs/gprs_sgsn.c
@@ -714,13 +714,17 @@ void sgsn_ggsn_ctx_drop_pdp(struct sgsn_pdp_ctx *pctx)
/* High-level function to be called in case a GGSN has disappeared or
- * otherwise lost state (recovery procedure) */
-int sgsn_ggsn_ctx_drop_all_pdp(struct sgsn_ggsn_ctx *ggsn)
+ * otherwise lost state (recovery procedure). It will detach all related pdp ctx
+ * from a ggsn and communicate deact to MS. Optionally (!NULL), one pdp ctx can
+ * be kept alive to allow handling later message which contained the Recovery IE. */
+int sgsn_ggsn_ctx_drop_all_pdp_except(struct sgsn_ggsn_ctx *ggsn, struct sgsn_pdp_ctx *except)
int num = 0;
struct sgsn_pdp_ctx *pdp, *pdp2;
llist_for_each_entry_safe(pdp, pdp2, &ggsn->pdp_list, ggsn_list) {
+ if (pdp == except)
+ continue;
diff --git a/src/gprs/sgsn_libgtp.c b/src/gprs/sgsn_libgtp.c
index 38133971..78297967 100644
--- a/src/gprs/sgsn_libgtp.c
+++ b/src/gprs/sgsn_libgtp.c
@@ -591,9 +591,10 @@ static int echo_conf(struct pdp_t *pdp, void *cbp, int recovery)
/* Any message received by GGSN contains a recovery IE */
-static int cb_recovery(struct sockaddr_in *peer, uint8_t recovery)
+static int cb_recovery2(struct sockaddr_in *peer, struct pdp_t *pdp, uint8_t recovery)
struct sgsn_ggsn_ctx *ggsn;
+ struct sgsn_pdp_ctx *pctx = NULL;
ggsn = sgsn_ggsn_ctx_by_addr(&peer->sin_addr);
if (!ggsn) {
@@ -606,11 +607,13 @@ static int cb_recovery(struct sockaddr_in *peer, uint8_t recovery)
ggsn->remote_restart_ctr = recovery;
} else if (ggsn->remote_restart_ctr != recovery) {
/* counter has changed (GGSN restart): release all PDP */
- LOGP(DGPRS, LOGL_NOTICE, "GGSN recovery (%u->%u), "
- "releasing all PDP contexts\n",
- ggsn->remote_restart_ctr, recovery);
+ LOGP(DGPRS, LOGL_NOTICE, "GGSN recovery (%u->%u) pdp=%p, "
+ "releasing all%s PDP contexts\n",
+ ggsn->remote_restart_ctr, recovery, pdp, pdp ? " other" : "");
ggsn->remote_restart_ctr = recovery;
- sgsn_ggsn_ctx_drop_all_pdp(ggsn);
+ if (pdp)
+ pctx = pdp->priv;
+ sgsn_ggsn_ctx_drop_all_pdp_except(ggsn, pctx);
return 0;
@@ -896,7 +899,7 @@ int sgsn_gtp_init(struct sgsn_instance *sgi)
/* Register callbackcs with libgtp */
gtp_set_cb_delete_context(gsn, cb_delete_context);
gtp_set_cb_conf(gsn, cb_conf);
- gtp_set_cb_recovery(gsn, cb_recovery);
+ gtp_set_cb_recovery2(gsn, cb_recovery2);
gtp_set_cb_data_ind(gsn, cb_data_ind);
gtp_set_cb_unsup_ind(gsn, cb_unsup_ind);
gtp_set_cb_extheader_ind(gsn, cb_extheader_ind);