aboutsummaryrefslogtreecommitdiffstats
path: root/openbsc/src/gprs
diff options
context:
space:
mode:
Diffstat (limited to 'openbsc/src/gprs')
-rw-r--r--openbsc/src/gprs/gprs_gmm.c13
-rw-r--r--openbsc/src/gprs/gprs_sgsn.c110
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;