diff options
Diffstat (limited to 'openbsc/src/gprs')
-rw-r--r-- | openbsc/src/gprs/gprs_gmm.c | 13 | ||||
-rw-r--r-- | openbsc/src/gprs/gprs_sgsn.c | 110 |
2 files changed, 117 insertions, 6 deletions
diff --git a/openbsc/src/gprs/gprs_gmm.c b/openbsc/src/gprs/gprs_gmm.c index abda327f0..ace0c2ec0 100644 --- a/openbsc/src/gprs/gprs_gmm.c +++ b/openbsc/src/gprs/gprs_gmm.c @@ -1541,6 +1541,7 @@ static int gsm48_rx_gsm_act_pdp_req(struct sgsn_mm_ctx *mmctx, uint8_t transaction_id = (gh->proto_discr >> 4); struct sgsn_ggsn_ctx *ggsn; struct sgsn_pdp_ctx *pdp; + enum gsm48_gsm_cause gsm_cause; LOGMMCTXP(LOGL_INFO, mmctx, "-> ACTIVATE PDP CONTEXT REQ: SAPI=%u NSAPI=%u ", act_req->req_llc_sapi, act_req->req_nsapi); @@ -1599,9 +1600,6 @@ static int gsm48_rx_gsm_act_pdp_req(struct sgsn_mm_ctx *mmctx, tp.lv[OSMO_IE_GSM_REQ_PDP_ADDR].len = req_pdpa_len; tp.lv[OSMO_IE_GSM_REQ_PDP_ADDR].val = req_pdpa; - /* FIXME: determine GGSN based on APN and subscription options */ - if (TLVP_PRESENT(&tp, GSM48_IE_GSM_APN)) {} - /* Check if NSAPI is out of range (TS 04.65 / 7.2) */ if (act_req->req_nsapi < 5 || act_req->req_nsapi > 15) { /* Send reject with GSM_CAUSE_INV_MAND_INFO */ @@ -1631,11 +1629,14 @@ static int gsm48_rx_gsm_act_pdp_req(struct sgsn_mm_ctx *mmctx, * for re-transmissions */ rate_ctr_inc(&mmctx->ctrg->ctr[GMM_CTR_PDP_CTX_ACT]); - ggsn = sgsn_ggsn_ctx_by_id(0); + /* Determine GGSN based on APN and subscription options */ + ggsn = sgsn_mm_ctx_find_ggsn_ctx(mmctx, &tp, &gsm_cause); if (!ggsn) { - LOGP(DGPRS, LOGL_ERROR, "No GGSN context 0 found!\n"); - return -EIO; + LOGP(DGPRS, LOGL_ERROR, "No GGSN context found!\n"); + return gsm48_tx_gsm_act_pdp_rej(mmctx, transaction_id, + gsm_cause, 0, NULL); } + LOGMMCTXP(LOGL_DEBUG, mmctx, "Using GGSN %d\n", ggsn->id); ggsn->gsn = sgsn->gsn; pdp = sgsn_create_pdp_ctx(ggsn, mmctx, act_req->req_nsapi, &tp); if (!pdp) diff --git a/openbsc/src/gprs/gprs_sgsn.c b/openbsc/src/gprs/gprs_sgsn.c index 85f3381c3..94c2b6fbe 100644 --- a/openbsc/src/gprs/gprs_sgsn.c +++ b/openbsc/src/gprs/gprs_sgsn.c @@ -35,6 +35,7 @@ #include <openbsc/sgsn.h> #include <openbsc/gsm_04_08_gprs.h> #include <openbsc/gprs_gmm.h> +#include <openbsc/gprs_utils.h> #include "openbsc/gprs_llc.h" #include <time.h> @@ -581,6 +582,115 @@ void sgsn_update_subscriber_data(struct sgsn_mm_ctx *mmctx) sgsn_auth_update(mmctx); } +struct sgsn_ggsn_ctx *sgsn_mm_ctx_find_ggsn_ctx(struct sgsn_mm_ctx *mmctx, + struct tlv_parsed *tp, + enum gsm48_gsm_cause *gsm_cause) +{ + char req_apn_str[GSM_APN_LENGTH] = {0}; + const struct apn_ctx *apn_ctx = NULL; + const char *selected_apn_str = NULL; + struct sgsn_subscriber_pdp_data *pdp; + struct sgsn_ggsn_ctx *ggsn = NULL; + int allow_any_apn = 0; + + if (TLVP_PRESENT(tp, GSM48_IE_GSM_APN)) { + if (TLVP_LEN(tp, GSM48_IE_GSM_APN) >= GSM_APN_LENGTH - 1) { + LOGMMCTXP(LOGL_ERROR, mmctx, "APN IE too long\n"); + *gsm_cause = GSM_CAUSE_INV_MAND_INFO; + return NULL; + } + + gprs_apn_to_str(req_apn_str, + TLVP_VAL(tp, GSM48_IE_GSM_APN), + TLVP_LEN(tp, GSM48_IE_GSM_APN)); + + if (strcmp(req_apn_str, "*") == 0) + req_apn_str[0] = 0; + } + + if (mmctx->subscr == NULL || + llist_empty(&mmctx->subscr->sgsn_data->pdp_list)) + allow_any_apn = 1; + + if (strlen(req_apn_str) == 0 && !allow_any_apn) { + /* No specific APN requested, check for an APN that is both + * granted and configured */ + + llist_for_each_entry(pdp, &mmctx->subscr->sgsn_data->pdp_list, list) { + if (strcmp(pdp->apn_str, "*") == 0) + { + allow_any_apn = 1; + selected_apn_str = ""; + continue; + } + if (!llist_empty(&sgsn_apn_ctxts)) { + apn_ctx = sgsn_apn_ctx_match(req_apn_str, mmctx->imsi); + /* Not configured */ + if (apn_ctx == NULL) + continue; + } + selected_apn_str = pdp->apn_str; + break; + } + } else if (!allow_any_apn) { + /* Check whether the given APN is granted */ + llist_for_each_entry(pdp, &mmctx->subscr->sgsn_data->pdp_list, list) { + if (strcmp(pdp->apn_str, "*") == 0) { + selected_apn_str = req_apn_str; + allow_any_apn = 1; + continue; + } + if (strcasecmp(pdp->apn_str, req_apn_str) == 0) { + selected_apn_str = req_apn_str; + break; + } + } + } else if (strlen(req_apn_str) != 0) { + /* Any APN is allowed */ + selected_apn_str = req_apn_str; + } else { + /* Prefer the GGSN associated with the wildcard APN */ + selected_apn_str = ""; + } + + if (!allow_any_apn && selected_apn_str == NULL) { + /* Access not granted */ + LOGMMCTXP(LOGL_NOTICE, mmctx, + "The requested APN '%s' is not allowed\n", + req_apn_str); + *gsm_cause = GSM_CAUSE_REQ_SERV_OPT_NOTSUB; + return NULL; + } + + if (apn_ctx == NULL && selected_apn_str) + apn_ctx = sgsn_apn_ctx_match(selected_apn_str, mmctx->imsi); + + if (apn_ctx != NULL) { + ggsn = apn_ctx->ggsn; + } else if (llist_empty(&sgsn_apn_ctxts)) { + /* No configuration -> use GGSN 0 */ + ggsn = sgsn_ggsn_ctx_by_id(0); + } else if (allow_any_apn && + (selected_apn_str == NULL || strlen(selected_apn_str) == 0)) { + /* No APN given and no default configuration -> Use GGSN 0 */ + ggsn = sgsn_ggsn_ctx_by_id(0); + } else { + /* No matching configuration found */ + LOGMMCTXP(LOGL_NOTICE, mmctx, + "The selected APN '%s' has not been configured\n", + selected_apn_str); + *gsm_cause = GSM_CAUSE_MISSING_APN; + return NULL; + } + + LOGMMCTXP(LOGL_INFO, mmctx, + "Found GGSN %d for APN '%s' (requested '%s')\n", + ggsn->id, selected_apn_str ? selected_apn_str : "---", + req_apn_str); + + return ggsn; +} + static void sgsn_llme_cleanup_free(struct gprs_llc_llme *llme) { struct sgsn_mm_ctx *mmctx = NULL; |