aboutsummaryrefslogtreecommitdiffstats
path: root/openbsc
diff options
context:
space:
mode:
Diffstat (limited to 'openbsc')
-rw-r--r--openbsc/include/openbsc/gprs_sgsn.h5
-rw-r--r--openbsc/src/gprs/gprs_gmm.c13
-rw-r--r--openbsc/src/gprs/gprs_sgsn.c110
-rw-r--r--openbsc/tests/sgsn/sgsn_test.c135
-rw-r--r--openbsc/tests/sgsn/sgsn_test.ok1
5 files changed, 258 insertions, 6 deletions
diff --git a/openbsc/include/openbsc/gprs_sgsn.h b/openbsc/include/openbsc/gprs_sgsn.h
index 330cb9320..20d535205 100644
--- a/openbsc/include/openbsc/gprs_sgsn.h
+++ b/openbsc/include/openbsc/gprs_sgsn.h
@@ -21,6 +21,8 @@ struct gprs_llc_lle;
struct ctrl_handle;
struct gsm_subscriber;
+enum gsm48_gsm_cause;
+
/* TS 04.08 4.1.3.3 GMM mobility management states on the network side */
enum gprs_mm_state {
GMM_DEREGISTERED, /* 4.1.3.3.1.1 */
@@ -153,6 +155,9 @@ struct sgsn_mm_ctx *sgsn_mm_ctx_alloc(uint32_t tlli,
void sgsn_mm_ctx_free(struct sgsn_mm_ctx *mm);
void sgsn_mm_ctx_cleanup_free(struct sgsn_mm_ctx *ctx);
+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);
enum pdp_ctx_state {
PDP_STATE_NONE,
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;
diff --git a/openbsc/tests/sgsn/sgsn_test.c b/openbsc/tests/sgsn/sgsn_test.c
index 5fa33a266..d55a0fc6a 100644
--- a/openbsc/tests/sgsn/sgsn_test.c
+++ b/openbsc/tests/sgsn/sgsn_test.c
@@ -26,6 +26,7 @@
#include <openbsc/gsm_subscriber.h>
#include <openbsc/gprs_gsup_messages.h>
#include <openbsc/gprs_gsup_client.h>
+#include <openbsc/gprs_utils.h>
#include <osmocom/gprs/gprs_bssgp.h>
@@ -1876,6 +1877,139 @@ static void test_apn_matching(void)
OSMO_ASSERT(actx == NULL);
}
+struct sgsn_subscriber_pdp_data* sgsn_subscriber_pdp_data_alloc(
+ struct sgsn_subscriber_data *sdata);
+
+static void test_ggsn_selection(void)
+{
+ struct apn_ctx *actxs[4];
+ struct sgsn_ggsn_ctx *ggc, *ggcs[3];
+ struct gsm_subscriber *s1;
+ const char *imsi1 = "1234567890";
+ struct sgsn_mm_ctx *ctx;
+ struct gprs_ra_id raid = { 0, };
+ uint32_t local_tlli = 0xffeeddcc;
+ enum gsm48_gsm_cause gsm_cause;
+ struct tlv_parsed tp;
+ uint8_t apn_enc[GSM_APN_LENGTH + 10];
+ struct sgsn_subscriber_pdp_data *pdp_data;
+
+ printf("Testing GGSN selection\n");
+
+ gprs_gsup_client_send_cb = my_gprs_gsup_client_send_dummy;
+
+ /* Check for emptiness */
+ OSMO_ASSERT(gprs_subscr_get_by_imsi(imsi1) == NULL);
+
+ /* Create a context */
+ OSMO_ASSERT(count(gprs_llme_list()) == 0);
+ ctx = alloc_mm_ctx(local_tlli, &raid);
+ strncpy(ctx->imsi, imsi1, sizeof(ctx->imsi) - 1);
+
+ /* Allocate and attach a subscriber */
+ s1 = gprs_subscr_get_or_create_by_mmctx(ctx);
+ assert_subscr(s1, imsi1);
+
+ tp.lv[GSM48_IE_GSM_APN].len = 0;
+ tp.lv[GSM48_IE_GSM_APN].val = apn_enc;
+
+ /* TODO: Add PDP info entries to s1 */
+
+ ggcs[0] = sgsn_ggsn_ctx_find_alloc(0);
+ ggcs[1] = sgsn_ggsn_ctx_find_alloc(1);
+ ggcs[2] = sgsn_ggsn_ctx_find_alloc(2);
+
+ actxs[0] = sgsn_apn_ctx_find_alloc("test.apn", "123456");
+ actxs[0]->ggsn = ggcs[0];
+ actxs[1] = sgsn_apn_ctx_find_alloc("*.apn", "123456");
+ actxs[1]->ggsn = ggcs[1];
+ actxs[2] = sgsn_apn_ctx_find_alloc("*", "456789");
+ actxs[2]->ggsn = ggcs[2];
+
+ /* Resolve GGSNs */
+
+ tp.lv[GSM48_IE_GSM_APN].len =
+ gprs_str_to_apn(apn_enc, sizeof(apn_enc), "Test.Apn");
+
+ ggc = sgsn_mm_ctx_find_ggsn_ctx(ctx, &tp, &gsm_cause);
+ OSMO_ASSERT(ggc != NULL);
+ OSMO_ASSERT(ggc->id == 0);
+
+ tp.lv[GSM48_IE_GSM_APN].len =
+ gprs_str_to_apn(apn_enc, sizeof(apn_enc), "Other.Apn");
+
+ ggc = sgsn_mm_ctx_find_ggsn_ctx(ctx, &tp, &gsm_cause);
+ OSMO_ASSERT(ggc != NULL);
+ OSMO_ASSERT(ggc->id == 1);
+
+ tp.lv[GSM48_IE_GSM_APN].len = 0;
+ tp.lv[GSM48_IE_GSM_APN].val = NULL;
+
+ ggc = sgsn_mm_ctx_find_ggsn_ctx(ctx, &tp, &gsm_cause);
+ OSMO_ASSERT(ggc != NULL);
+ OSMO_ASSERT(ggc->id == 0);
+
+ actxs[3] = sgsn_apn_ctx_find_alloc("*", "123456");
+ actxs[3]->ggsn = ggcs[2];
+ ggc = sgsn_mm_ctx_find_ggsn_ctx(ctx, &tp, &gsm_cause);
+ OSMO_ASSERT(ggc != NULL);
+ OSMO_ASSERT(ggc->id == 2);
+
+ sgsn_apn_ctx_free(actxs[3]);
+ tp.lv[GSM48_IE_GSM_APN].val = apn_enc;
+
+ tp.lv[GSM48_IE_GSM_APN].len =
+ gprs_str_to_apn(apn_enc, sizeof(apn_enc), "Foo.Bar");
+
+ ggc = sgsn_mm_ctx_find_ggsn_ctx(ctx, &tp, &gsm_cause);
+ OSMO_ASSERT(ggc == NULL);
+ OSMO_ASSERT(gsm_cause == GSM_CAUSE_MISSING_APN);
+
+ tp.lv[GSM48_IE_GSM_APN].len = sizeof(apn_enc);
+ ggc = sgsn_mm_ctx_find_ggsn_ctx(ctx, &tp, &gsm_cause);
+ OSMO_ASSERT(ggc == NULL);
+ OSMO_ASSERT(gsm_cause == GSM_CAUSE_INV_MAND_INFO);
+
+ /* Add PDP data entry to subscriber */
+
+ pdp_data = sgsn_subscriber_pdp_data_alloc(s1->sgsn_data);
+ pdp_data->context_id = 1;
+
+ pdp_data->pdp_type = 0x0121;
+ strncpy(pdp_data->apn_str, "Test.Apn", sizeof(pdp_data->apn_str)-1);
+
+ tp.lv[GSM48_IE_GSM_APN].len =
+ gprs_str_to_apn(apn_enc, sizeof(apn_enc), "Test.Apn");
+
+ ggc = sgsn_mm_ctx_find_ggsn_ctx(ctx, &tp, &gsm_cause);
+ OSMO_ASSERT(ggc != NULL);
+ OSMO_ASSERT(ggc->id == 0);
+
+ tp.lv[GSM48_IE_GSM_APN].len =
+ gprs_str_to_apn(apn_enc, sizeof(apn_enc), "Other.Apn");
+
+ ggc = sgsn_mm_ctx_find_ggsn_ctx(ctx, &tp, &gsm_cause);
+ OSMO_ASSERT(ggc == NULL);
+ OSMO_ASSERT(gsm_cause == GSM_CAUSE_REQ_SERV_OPT_NOTSUB);
+
+ /* Cleanup */
+
+ subscr_put(s1);
+ sgsn_mm_ctx_cleanup_free(ctx);
+
+ assert_no_subscrs();
+
+ sgsn_apn_ctx_free(actxs[0]);
+ sgsn_apn_ctx_free(actxs[1]);
+ sgsn_apn_ctx_free(actxs[2]);
+
+ sgsn_ggsn_ctx_free(ggcs[0]);
+ sgsn_ggsn_ctx_free(ggcs[1]);
+ sgsn_ggsn_ctx_free(ggcs[2]);
+
+ gprs_gsup_client_send_cb = __real_gprs_gsup_client_send;
+}
+
static struct log_info_cat gprs_categories[] = {
[DMM] = {
.name = "DMM",
@@ -1964,6 +2098,7 @@ int main(int argc, char **argv)
test_gmm_cancel();
test_gmm_ptmsi_allocation();
test_apn_matching();
+ test_ggsn_selection();
printf("Done\n");
talloc_report_full(osmo_sgsn_ctx, stderr);
diff --git a/openbsc/tests/sgsn/sgsn_test.ok b/openbsc/tests/sgsn/sgsn_test.ok
index 9f14721af..bef8c9897 100644
--- a/openbsc/tests/sgsn/sgsn_test.ok
+++ b/openbsc/tests/sgsn/sgsn_test.ok
@@ -27,4 +27,5 @@ Testing P-TMSI allocation
- Repeated Attach Request
- Repeated RA Update Request
Testing APN matching
+Testing GGSN selection
Done