mirror of
				https://gitea.osmocom.org/cellular-infrastructure/osmo-mgw.git
				synced 2025-11-04 05:53:26 +00:00 
			
		
		
		
	sgsn: Delete PDP contexts properly
Currently the PDP contexts are hard freed (via sgsn_pdp_ctx_free)
at some places in gprs_gmm.c on the reception of a Detach Req and on
re-use of an IMSI that is already associated with an MM context. This
can lead to segfaults when there is a pending request or a data
indication at libgtp.
This patch add a new function sgsn_pdp_ctx_terminate that de-associates
the PTP context from the MM context, deactivates SNDCP, sets pdp->mm
to NULL and then calls sgsn_delete_pdp_ctx. sgsn_libgtp is updated to
check for pdp->mm being non-NULL before dereferencing it. The
sgsn_pdp_ctx_terminate function will be called for each PDP context of
an MM context before this context is going to be deleted via
sgsn_mm_ctx_free. To ensure, that the ctx->llme (which is accessed
during the deactivation of SNDCP) remains valid, the call to
gprs_llgmm_assign is moved after the call to sgsn_mm_ctx_free. The
handling of re-used IMSIs is changed to mimic the processing of a
Detach Req.
Addresses:
<0002> gprs_gmm.c:654 MM(/f6b31ab0) Deleting old MM Context for same
    IMSI p_tmsi_old=0xc6f19134
<000f> gprs_sgsn.c:259 PDP freeing PDP context that still has a
    libgtp handle attached to it, this shouldn't happen!
[...]
SEGFAULT
Ticket: OW#1311
Sponsored-by: On-Waves ehf
			
			
This commit is contained in:
		
				
					committed by
					
						
						Holger Hans Peter Freyther
					
				
			
			
				
	
			
			
			
						parent
						
							ae20b4b31b
						
					
				
				
					commit
					99985b5ea8
				
			@@ -180,7 +180,8 @@ struct sgsn_pdp_ctx {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define LOGPDPCTXP(level, pdp, fmt, args...) \
 | 
					#define LOGPDPCTXP(level, pdp, fmt, args...) \
 | 
				
			||||||
	LOGP(DGPRS, level, "PDP(%s/%u) " fmt, (pdp)->mm->imsi, (pdp)->ti, ## args)
 | 
						LOGP(DGPRS, level, "PDP(%s/%u) " \
 | 
				
			||||||
 | 
						     fmt, (pdp)->mm ? (pdp)->mm->imsi : "---", (pdp)->ti, ## args)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* look up PDP context by MM context and NSAPI */
 | 
					/* look up PDP context by MM context and NSAPI */
 | 
				
			||||||
struct sgsn_pdp_ctx *sgsn_pdp_ctx_by_nsapi(const struct sgsn_mm_ctx *mm,
 | 
					struct sgsn_pdp_ctx *sgsn_pdp_ctx_by_nsapi(const struct sgsn_mm_ctx *mm,
 | 
				
			||||||
@@ -191,6 +192,7 @@ struct sgsn_pdp_ctx *sgsn_pdp_ctx_by_tid(const struct sgsn_mm_ctx *mm,
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
struct sgsn_pdp_ctx *sgsn_pdp_ctx_alloc(struct sgsn_mm_ctx *mm,
 | 
					struct sgsn_pdp_ctx *sgsn_pdp_ctx_alloc(struct sgsn_mm_ctx *mm,
 | 
				
			||||||
					uint8_t nsapi);
 | 
										uint8_t nsapi);
 | 
				
			||||||
 | 
					void sgsn_pdp_ctx_terminate(struct sgsn_pdp_ctx *pdp);
 | 
				
			||||||
void sgsn_pdp_ctx_free(struct sgsn_pdp_ctx *pdp);
 | 
					void sgsn_pdp_ctx_free(struct sgsn_pdp_ctx *pdp);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -265,6 +265,28 @@ static void mmctx2msgid(struct msgb *msg, const struct sgsn_mm_ctx *mm)
 | 
				
			|||||||
	msgb_nsei(msg) = mm->nsei;
 | 
						msgb_nsei(msg) = mm->nsei;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						sgsn_mm_ctx_free(ctx);
 | 
				
			||||||
 | 
						ctx = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* TLLI unassignment, must be called after sgsn_mm_ctx_free */
 | 
				
			||||||
 | 
						gprs_llgmm_assign(llme, tlli, 0xffffffff, GPRS_ALGO_GEA0, NULL);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Chapter 9.4.18 */
 | 
					/* Chapter 9.4.18 */
 | 
				
			||||||
static int _tx_status(struct msgb *msg, uint8_t cause,
 | 
					static int _tx_status(struct msgb *msg, uint8_t cause,
 | 
				
			||||||
		      struct sgsn_mm_ctx *mmctx, int sm)
 | 
							      struct sgsn_mm_ctx *mmctx, int sm)
 | 
				
			||||||
@@ -603,15 +625,16 @@ static int gsm48_rx_gmm_id_resp(struct sgsn_mm_ctx *ctx, struct msgb *msg)
 | 
				
			|||||||
			struct sgsn_mm_ctx *ictx;
 | 
								struct sgsn_mm_ctx *ictx;
 | 
				
			||||||
			ictx = sgsn_mm_ctx_by_imsi(mi_string);
 | 
								ictx = sgsn_mm_ctx_by_imsi(mi_string);
 | 
				
			||||||
			if (ictx) {
 | 
								if (ictx) {
 | 
				
			||||||
 | 
									/* Handle it like in gsm48_rx_gmm_det_req,
 | 
				
			||||||
 | 
									 * except that no messages are sent to the BSS */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				LOGMMCTXP(LOGL_NOTICE, ctx, "Deleting old MM Context for same IMSI "
 | 
									LOGMMCTXP(LOGL_NOTICE, ctx, "Deleting old MM Context for same IMSI "
 | 
				
			||||||
				       "p_tmsi_old=0x%08x\n",
 | 
									       "p_tmsi_old=0x%08x\n",
 | 
				
			||||||
					ictx->p_tmsi);
 | 
										ictx->p_tmsi);
 | 
				
			||||||
				gprs_llgmm_assign(ictx->llme, ictx->tlli,
 | 
					
 | 
				
			||||||
						  0xffffffff, GPRS_ALGO_GEA0, NULL);
 | 
									ictx->mm_state = GMM_DEREGISTERED;
 | 
				
			||||||
				/* FIXME: this is a hard free, we don't
 | 
					
 | 
				
			||||||
				 * clean-up any PDP contexts on the
 | 
									mm_ctx_cleanup_free(ictx, "GPRS IMSI re-use");
 | 
				
			||||||
				 * libgtp side */
 | 
					 | 
				
			||||||
				sgsn_mm_ctx_free(ictx);
 | 
					 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		strncpy(ctx->imsi, mi_string, sizeof(ctx->imsi));
 | 
							strncpy(ctx->imsi, mi_string, sizeof(ctx->imsi));
 | 
				
			||||||
@@ -776,7 +799,6 @@ err_inval:
 | 
				
			|||||||
static int gsm48_rx_gmm_det_req(struct sgsn_mm_ctx *ctx, struct msgb *msg)
 | 
					static int gsm48_rx_gmm_det_req(struct sgsn_mm_ctx *ctx, struct msgb *msg)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(msg);
 | 
						struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(msg);
 | 
				
			||||||
	struct sgsn_pdp_ctx *pdp, *pdp2;
 | 
					 | 
				
			||||||
	uint8_t detach_type, power_off;
 | 
						uint8_t detach_type, power_off;
 | 
				
			||||||
	int rc;
 | 
						int rc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -789,26 +811,10 @@ static int gsm48_rx_gmm_det_req(struct sgsn_mm_ctx *ctx, struct msgb *msg)
 | 
				
			|||||||
		msgb_tlli(msg), get_value_string(gprs_det_t_mo_strs, detach_type),
 | 
							msgb_tlli(msg), get_value_string(gprs_det_t_mo_strs, detach_type),
 | 
				
			||||||
		power_off ? "Power-off" : "");
 | 
							power_off ? "Power-off" : "");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Mark MM state as deregistered */
 | 
					 | 
				
			||||||
	ctx->mm_state = GMM_DEREGISTERED;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* 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 GPRS DETACH REQUEST\n", pdp->nsapi);
 | 
					 | 
				
			||||||
		sgsn_delete_pdp_ctx(pdp);
 | 
					 | 
				
			||||||
		/* FIXME: the callback wants to transmit a DEACT PDP CTX ACK,
 | 
					 | 
				
			||||||
		 * which is quite stupid for a MS that has just detached.. */
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* force_stby = 0 */
 | 
						/* force_stby = 0 */
 | 
				
			||||||
	rc = gsm48_tx_gmm_det_ack(ctx, 0);
 | 
						rc = gsm48_tx_gmm_det_ack(ctx, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* TLLI unassignment */
 | 
						mm_ctx_cleanup_free(ctx, "GPRS DETACH REQUEST");
 | 
				
			||||||
	gprs_llgmm_assign(ctx->llme, ctx->tlli, 0xffffffff,
 | 
					 | 
				
			||||||
			  GPRS_ALGO_GEA0, NULL);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	sgsn_mm_ctx_free(ctx);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return rc;
 | 
						return rc;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -35,6 +35,7 @@
 | 
				
			|||||||
#include <openbsc/sgsn.h>
 | 
					#include <openbsc/sgsn.h>
 | 
				
			||||||
#include <openbsc/gsm_04_08_gprs.h>
 | 
					#include <openbsc/gsm_04_08_gprs.h>
 | 
				
			||||||
#include <openbsc/gprs_gmm.h>
 | 
					#include <openbsc/gprs_gmm.h>
 | 
				
			||||||
 | 
					#include "openbsc/gprs_llc.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
extern struct sgsn_instance *sgsn;
 | 
					extern struct sgsn_instance *sgsn;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -247,10 +248,39 @@ struct sgsn_pdp_ctx *sgsn_pdp_ctx_alloc(struct sgsn_mm_ctx *mm,
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <pdp.h>
 | 
					#include <pdp.h>
 | 
				
			||||||
/* you probably want to call sgsn_delete_pdp_ctx() instead */
 | 
					/*
 | 
				
			||||||
 | 
					 * This function will not trigger any GSM DEACT PDP ACK messages, so you
 | 
				
			||||||
 | 
					 * probably want to call sgsn_delete_pdp_ctx() instead if the connection
 | 
				
			||||||
 | 
					 * isn't detached already.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					void sgsn_pdp_ctx_terminate(struct sgsn_pdp_ctx *pdp)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						OSMO_ASSERT(pdp->mm != NULL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* There might still be pending callbacks in libgtp. So the parts of
 | 
				
			||||||
 | 
						 * this object relevant to GTP need to remain intact in this case. */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						LOGPDPCTXP(LOGL_INFO, pdp, "Forcing release of PDP context\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Force the deactivation of the SNDCP layer */
 | 
				
			||||||
 | 
						sndcp_sm_deactivate_ind(&pdp->mm->llme->lle[pdp->sapi], pdp->nsapi);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Detach from MM context */
 | 
				
			||||||
 | 
						llist_del(&pdp->list);
 | 
				
			||||||
 | 
						pdp->mm = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						sgsn_delete_pdp_ctx(pdp);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Don't call this function directly unless you know what you are doing.
 | 
				
			||||||
 | 
					 * In normal conditions use sgsn_delete_pdp_ctx and in unspecified or
 | 
				
			||||||
 | 
					 * implementation dependent abnormal ones sgsn_pdp_ctx_terminate.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
void sgsn_pdp_ctx_free(struct sgsn_pdp_ctx *pdp)
 | 
					void sgsn_pdp_ctx_free(struct sgsn_pdp_ctx *pdp)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	rate_ctr_group_free(pdp->ctrg);
 | 
						rate_ctr_group_free(pdp->ctrg);
 | 
				
			||||||
 | 
						if (pdp->mm)
 | 
				
			||||||
		llist_del(&pdp->list);
 | 
							llist_del(&pdp->list);
 | 
				
			||||||
	llist_del(&pdp->g_list);
 | 
						llist_del(&pdp->g_list);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -263,6 +263,12 @@ static int create_pdp_conf(struct pdp_t *pdp, void *cbp, int cause)
 | 
				
			|||||||
	LOGPDPCTXP(LOGL_INFO, pctx, "Received CREATE PDP CTX CONF, cause=%d(%s)\n",
 | 
						LOGPDPCTXP(LOGL_INFO, pctx, "Received CREATE PDP CTX CONF, cause=%d(%s)\n",
 | 
				
			||||||
		cause, get_value_string(gtp_cause_strs, cause));
 | 
							cause, get_value_string(gtp_cause_strs, cause));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!pctx->mm) {
 | 
				
			||||||
 | 
							LOGP(DGPRS, LOGL_INFO,
 | 
				
			||||||
 | 
							     "No MM context, aborting CREATE PDP CTX CONF\n");
 | 
				
			||||||
 | 
							return -EIO;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Check for cause value if it was really successful */
 | 
						/* Check for cause value if it was really successful */
 | 
				
			||||||
	if (cause < 0) {
 | 
						if (cause < 0) {
 | 
				
			||||||
		LOGP(DGPRS, LOGL_NOTICE, "Create PDP ctx req timed out\n");
 | 
							LOGP(DGPRS, LOGL_NOTICE, "Create PDP ctx req timed out\n");
 | 
				
			||||||
@@ -314,16 +320,22 @@ reject:
 | 
				
			|||||||
static int delete_pdp_conf(struct pdp_t *pdp, void *cbp, int cause)
 | 
					static int delete_pdp_conf(struct pdp_t *pdp, void *cbp, int cause)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct sgsn_pdp_ctx *pctx = cbp;
 | 
						struct sgsn_pdp_ctx *pctx = cbp;
 | 
				
			||||||
	int rc;
 | 
						int rc = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	LOGPDPCTXP(LOGL_INFO, pctx, "Received DELETE PDP CTX CONF, cause=%d(%s)\n",
 | 
						LOGPDPCTXP(LOGL_INFO, pctx, "Received DELETE PDP CTX CONF, cause=%d(%s)\n",
 | 
				
			||||||
		cause, get_value_string(gtp_cause_strs, cause));
 | 
							cause, get_value_string(gtp_cause_strs, cause));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (pctx->mm) {
 | 
				
			||||||
		/* Deactivate the SNDCP layer */
 | 
							/* Deactivate the SNDCP layer */
 | 
				
			||||||
		sndcp_sm_deactivate_ind(&pctx->mm->llme->lle[pctx->sapi], pctx->nsapi);
 | 
							sndcp_sm_deactivate_ind(&pctx->mm->llme->lle[pctx->sapi], pctx->nsapi);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		/* Confirm deactivation of PDP context to MS */
 | 
							/* Confirm deactivation of PDP context to MS */
 | 
				
			||||||
		rc = gsm48_tx_gsm_deact_pdp_acc(pctx);
 | 
							rc = gsm48_tx_gsm_deact_pdp_acc(pctx);
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							LOGPDPCTXP(LOGL_NOTICE, pctx,
 | 
				
			||||||
 | 
								   "Not deactivating SNDCP layer since the MM context "
 | 
				
			||||||
 | 
								   "is not available\n");
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* unlink the now non-existing library handle from the pdp
 | 
						/* unlink the now non-existing library handle from the pdp
 | 
				
			||||||
	 * context */
 | 
						 * context */
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -222,8 +222,9 @@ const struct value_string gprs_mm_st_strs[] = {
 | 
				
			|||||||
static void vty_dump_pdp(struct vty *vty, const char *pfx,
 | 
					static void vty_dump_pdp(struct vty *vty, const char *pfx,
 | 
				
			||||||
			 struct sgsn_pdp_ctx *pdp)
 | 
								 struct sgsn_pdp_ctx *pdp)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
						const char *imsi = pdp->mm ? pdp->mm->imsi : "(detaching)";
 | 
				
			||||||
	vty_out(vty, "%sPDP Context IMSI: %s, SAPI: %u, NSAPI: %u%s",
 | 
						vty_out(vty, "%sPDP Context IMSI: %s, SAPI: %u, NSAPI: %u%s",
 | 
				
			||||||
		pfx, pdp->mm->imsi, pdp->sapi, pdp->nsapi, VTY_NEWLINE);
 | 
							pfx, imsi, pdp->sapi, pdp->nsapi, VTY_NEWLINE);
 | 
				
			||||||
	vty_out(vty, "%s  APN: %s%s", pfx,
 | 
						vty_out(vty, "%s  APN: %s%s", pfx,
 | 
				
			||||||
		gprs_apn2str(pdp->lib->apn_use.v, pdp->lib->apn_use.l),
 | 
							gprs_apn2str(pdp->lib->apn_use.v, pdp->lib->apn_use.l),
 | 
				
			||||||
		VTY_NEWLINE);
 | 
							VTY_NEWLINE);
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user