aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHolger Hans Peter Freyther <holger@moiji-mobile.com>2015-05-25 12:26:49 +0800
committerHolger Hans Peter Freyther <holger@moiji-mobile.com>2015-05-25 15:57:57 +0800
commit39c430ee2929f0671203974db11dfdd4ff4841cb (patch)
treef7fedeadef2803179a16e187589f5647917279cf
parent66e7106d393368b2dd0d04c08f31781ed997380b (diff)
sgsn: Allow to resolve the IPv4 address of a GGSN through DNS
For real networks we need to check if the requested APN string is allowed and then resolve the GGSN address through DNS. There are countries with two or three digit MNCs and one could either try to keep a list of countries that have two/three digits or just try both of them. I have opted for the later for the ease of the implementation. C-Ares doesn't allow to cancel a request so we will need to have the MMCTX and the Lookup have different lifetimes. We simply set ->mmctx to NULL in case the MMCTX dies more early. The selected and verified apn_str will be copied into the out parameter. In case no static APN/GGSN config is present and the dynamic mode is enabled a request will be made.
-rw-r--r--openbsc/include/openbsc/gprs_sgsn.h30
-rw-r--r--openbsc/include/openbsc/sgsn.h3
-rw-r--r--openbsc/src/gprs/gprs_gmm.c151
-rw-r--r--openbsc/src/gprs/gprs_sgsn.c18
-rw-r--r--openbsc/src/gprs/sgsn_main.c8
-rw-r--r--openbsc/src/gprs/sgsn_vty.c15
-rw-r--r--openbsc/tests/sgsn/Makefile.am4
-rw-r--r--openbsc/tests/sgsn/sgsn_test.c24
8 files changed, 232 insertions, 21 deletions
diff --git a/openbsc/include/openbsc/gprs_sgsn.h b/openbsc/include/openbsc/gprs_sgsn.h
index 8abe97c51..61120b1e3 100644
--- a/openbsc/include/openbsc/gprs_sgsn.h
+++ b/openbsc/include/openbsc/gprs_sgsn.h
@@ -70,6 +70,29 @@ enum sgsn_auth_state {
#define MS_RADIO_ACCESS_CAPA
+enum sgsn_ggsn_lookup_state {
+ SGSN_GGSN_2DIGIT,
+ SGSN_GGSN_3DIGIT,
+};
+
+struct sgsn_ggsn_lookup {
+ int state;
+
+ struct sgsn_mm_ctx *mmctx;
+
+ /* APN string */
+ char apn_str[GSM_APN_LENGTH];
+
+ /* the original data */
+ struct msgb *orig_msg;
+ struct tlv_parsed tp;
+
+ /* for dealing with re-transmissions */
+ uint8_t nsapi;
+ uint8_t sapi;
+ uint8_t ti;
+};
+
/* According to TS 03.60, Table 5: SGSN MM and PDP Contexts */
/* Extended by 3GPP TS 23.060, Table 6: SGSN MM and PDP Contexts */
struct sgsn_mm_ctx {
@@ -139,6 +162,9 @@ struct sgsn_mm_ctx {
/* the string representation of the current hlr */
char hlr[GSM_EXTENSION_LENGTH];
+ /* the current GGSN look-up operation */
+ struct sgsn_ggsn_lookup *ggsn_lookup;
+
struct gsm_subscriber *subscr;
};
@@ -159,7 +185,8 @@ 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 gsm48_gsm_cause *gsm_cause,
+ char *apn_str);
enum pdp_ctx_state {
PDP_STATE_NONE,
@@ -182,6 +209,7 @@ struct sgsn_pdp_ctx {
struct llist_head list; /* list_head for mmctx->pdp_list */
struct llist_head g_list; /* list_head for global list */
struct sgsn_mm_ctx *mm; /* back pointer to MM CTX */
+ int destroy_ggsn; /* destroy it on destruction */
struct sgsn_ggsn_ctx *ggsn; /* which GGSN serves this PDP */
struct rate_ctr_group *ctrg;
diff --git a/openbsc/include/openbsc/sgsn.h b/openbsc/include/openbsc/sgsn.h
index 8e4d532bf..a92bfc65d 100644
--- a/openbsc/include/openbsc/sgsn.h
+++ b/openbsc/include/openbsc/sgsn.h
@@ -10,6 +10,7 @@
#include <ares.h>
struct gprs_gsup_client;
+struct hostent;
enum sgsn_auth_policy {
SGSN_AUTH_POLICY_OPEN,
@@ -43,6 +44,8 @@ struct sgsn_config {
/* CDR configuration */
struct sgsn_cdr cdr;
+
+ int dynamic_lookup;
};
struct sgsn_instance {
diff --git a/openbsc/src/gprs/gprs_gmm.c b/openbsc/src/gprs/gprs_gmm.c
index d7ba5b4f5..07fd1dcfd 100644
--- a/openbsc/src/gprs/gprs_gmm.c
+++ b/openbsc/src/gprs/gprs_gmm.c
@@ -29,6 +29,7 @@
#include <netinet/in.h>
#include <arpa/inet.h>
+#include <netdb.h>
#include <openbsc/db.h>
#include <osmocom/core/msgb.h>
@@ -37,6 +38,7 @@
#include <osmocom/core/signal.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/rate_ctr.h>
+#include <osmocom/gsm/apn.h>
#include <osmocom/gprs/gprs_bssgp.h>
@@ -1633,7 +1635,7 @@ int gsm48_tx_gsm_deact_pdp_acc(struct sgsn_pdp_ctx *pdp)
static int activate_ggsn(struct sgsn_mm_ctx *mmctx,
struct sgsn_ggsn_ctx *ggsn, const uint8_t transaction_id,
const uint8_t req_nsapi, const uint8_t req_llc_sapi,
- struct tlv_parsed *tp)
+ struct tlv_parsed *tp, int destroy_ggsn)
{
struct sgsn_pdp_ctx *pdp;
@@ -1646,10 +1648,94 @@ static int activate_ggsn(struct sgsn_mm_ctx *mmctx,
/* Store SAPI and Transaction Identifier */
pdp->sapi = req_llc_sapi;
pdp->ti = transaction_id;
+ pdp->destroy_ggsn = destroy_ggsn;
return 0;
}
+static void ggsn_lookup_cb(void *arg, int status, int timeouts, struct hostent *hostent)
+{
+ struct sgsn_ggsn_ctx *ggsn;
+ struct sgsn_ggsn_lookup *lookup = arg;
+ struct in_addr *addr = NULL;
+ int i;
+
+ /* The context is gone while we made a request */
+ if (!lookup->mmctx) {
+ talloc_free(lookup);
+ return;
+ }
+
+ if (status != ARES_SUCCESS) {
+ struct sgsn_mm_ctx *mmctx = lookup->mmctx;
+
+ LOGMMCTXP(LOGL_ERROR, mmctx, "DNS query failed.\n");
+
+ /* Need to try with three digits now */
+ if (lookup->state == SGSN_GGSN_2DIGIT) {
+ char *hostname;
+ int rc;
+
+ lookup->state = SGSN_GGSN_3DIGIT;
+ hostname = osmo_apn_qualify_from_imsi(mmctx->imsi,
+ lookup->apn_str, 1);
+ LOGMMCTXP(LOGL_DEBUG, mmctx,
+ "Going to query %s\n", hostname);
+ rc = sgsn_ares_query(sgsn, hostname,
+ ggsn_lookup_cb, lookup);
+ if (rc != 0) {
+ LOGP(DGPRS, LOGL_ERROR, "Couldn't start GGSN\n");
+ goto reject_due_failure;
+ }
+ return;
+ }
+
+ LOGMMCTXP(LOGL_ERROR, mmctx, "Couldn't resolve GGSN\n");
+ goto reject_due_failure;
+ }
+
+ if (hostent->h_length != sizeof(struct in_addr)) {
+ LOGMMCTXP(LOGL_ERROR, lookup->mmctx,
+ "Wrong addr size(%d)\n", sizeof(struct in_addr));
+ goto reject_due_failure;
+ }
+
+ /* get the address */
+ for (i = 0, addr = (struct in_addr *) hostent->h_addr_list[i]; addr;
+ i++, addr = (struct in_addr *) hostent->h_addr_list[i]) {
+ break;
+ }
+
+ if (!addr) {
+ LOGMMCTXP(LOGL_ERROR, lookup->mmctx, "No host address.\n");
+ goto reject_due_failure;
+ }
+
+ ggsn = sgsn_ggsn_ctx_alloc(UINT32_MAX);
+ if (!ggsn) {
+ LOGMMCTXP(LOGL_ERROR, lookup->mmctx, "Failed to create ggsn.\n");
+ goto reject_due_failure;
+ }
+ ggsn->remote_addr = *addr;
+ LOGMMCTXP(LOGL_NOTICE, lookup->mmctx,
+ "Selected %s as GGSN.\n", inet_ntoa(*addr));
+
+ /* forget about the ggsn look-up */
+ lookup->mmctx->ggsn_lookup = NULL;
+
+ activate_ggsn(lookup->mmctx, ggsn, lookup->ti, lookup->nsapi,
+ lookup->sapi, &lookup->tp, 1);
+
+ /* Now free it */
+ talloc_free(lookup);
+ return;
+
+reject_due_failure:
+ gsm48_tx_gsm_act_pdp_rej(lookup->mmctx, lookup->ti,
+ GMM_CAUSE_NET_FAIL, 0, NULL);
+ lookup->mmctx->ggsn_lookup = NULL;
+ talloc_free(lookup);
+}
static int do_act_pdp_req(struct sgsn_mm_ctx *mmctx, struct msgb *msg)
{
@@ -1662,6 +1748,9 @@ static int do_act_pdp_req(struct sgsn_mm_ctx *mmctx, struct msgb *msg)
struct sgsn_ggsn_ctx *ggsn;
struct sgsn_pdp_ctx *pdp;
enum gsm48_gsm_cause gsm_cause;
+ char apn_str[GSM_APN_LENGTH] = { 0, };
+ char *hostname;
+ int rc;
LOGMMCTXP(LOGL_INFO, mmctx, "-> ACTIVATE PDP CONTEXT REQ: SAPI=%u NSAPI=%u ",
act_req->req_llc_sapi, act_req->req_nsapi);
@@ -1746,22 +1835,64 @@ static int do_act_pdp_req(struct sgsn_mm_ctx *mmctx, struct msgb *msg)
0, NULL);
}
+ if (mmctx->ggsn_lookup) {
+ if (mmctx->ggsn_lookup->sapi == act_req->req_llc_sapi &&
+ mmctx->ggsn_lookup->ti == transaction_id) {
+ LOGMMCTXP(LOGL_NOTICE, mmctx,
+ "Re-transmission while doing look-up. Ignoring.\n");
+ return 0;
+ }
+ }
+
/* Only increment counter for a real activation, after we checked
* for re-transmissions */
rate_ctr_inc(&mmctx->ctrg->ctr[GMM_CTR_PDP_CTX_ACT]);
/* 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 found!\n");
- return gsm48_tx_gsm_act_pdp_rej(mmctx, transaction_id,
- gsm_cause, 0, NULL);
+ ggsn = sgsn_mm_ctx_find_ggsn_ctx(mmctx, &tp, &gsm_cause, apn_str);
+ if (ggsn)
+ return activate_ggsn(mmctx, ggsn, transaction_id,
+ act_req->req_nsapi, act_req->req_llc_sapi,
+ &tp, 0);
+
+ if (strlen(apn_str) == 0)
+ goto no_context;
+ if (!sgsn->cfg.dynamic_lookup)
+ goto no_context;
+
+ /* schedule a dynamic look-up */
+ mmctx->ggsn_lookup = talloc_zero(tall_bsc_ctx, struct sgsn_ggsn_lookup);
+ if (!mmctx->ggsn_lookup)
+ goto no_context;
+
+ mmctx->ggsn_lookup->state = SGSN_GGSN_2DIGIT;
+ mmctx->ggsn_lookup->mmctx = mmctx;
+ strcpy(mmctx->ggsn_lookup->apn_str, apn_str);
+
+ mmctx->ggsn_lookup->orig_msg = msg;
+ mmctx->ggsn_lookup->tp = tp;
+
+ mmctx->ggsn_lookup->ti = transaction_id;
+ mmctx->ggsn_lookup->nsapi = act_req->req_nsapi;
+ mmctx->ggsn_lookup->sapi = act_req->req_llc_sapi;
+
+ hostname = osmo_apn_qualify_from_imsi(mmctx->imsi,
+ mmctx->ggsn_lookup->apn_str, 0);
+
+ LOGMMCTXP(LOGL_DEBUG, mmctx, "Going to query %s\n", hostname);
+ rc = sgsn_ares_query(sgsn, hostname,
+ ggsn_lookup_cb, mmctx->ggsn_lookup);
+ if (rc != 0) {
+ LOGMMCTXP(LOGL_ERROR, mmctx, "Failed to start ares query.\n");
+ goto no_context;
}
- /* Now activate the GGSN */
- return activate_ggsn(mmctx, ggsn, transaction_id,
- act_req->req_nsapi, act_req->req_llc_sapi,
- &tp);
+ return 0;
+
+no_context:
+ LOGP(DGPRS, LOGL_ERROR, "No GGSN context found!\n");
+ return gsm48_tx_gsm_act_pdp_rej(mmctx, transaction_id,
+ gsm_cause, 0, NULL);
}
/* Section 9.5.1: Activate PDP Context Request */
diff --git a/openbsc/src/gprs/gprs_sgsn.c b/openbsc/src/gprs/gprs_sgsn.c
index 3c25840ef..cc1ae4d25 100644
--- a/openbsc/src/gprs/gprs_sgsn.c
+++ b/openbsc/src/gprs/gprs_sgsn.c
@@ -207,6 +207,14 @@ void sgsn_mm_ctx_cleanup_free(struct sgsn_mm_ctx *mm)
struct sgsn_pdp_ctx *pdp, *pdp2;
struct sgsn_signal_data sig_data;
+ /* Forget about ongoing look-ups */
+ if (mm->ggsn_lookup) {
+ LOGMMCTXP(LOGL_NOTICE, mm,
+ "Cleaning mmctx with on-going query.\n");
+ mm->ggsn_lookup->mmctx = NULL;
+ mm->ggsn_lookup = NULL;
+ }
+
/* delete all existing PDP contexts for this MS */
llist_for_each_entry_safe(pdp, pdp2, &mm->pdp_list, list) {
LOGMMCTXP(LOGL_NOTICE, mm,
@@ -349,6 +357,8 @@ void sgsn_pdp_ctx_free(struct sgsn_pdp_ctx *pdp)
lib->priv = NULL;
}
+ if (pdp->destroy_ggsn)
+ sgsn_ggsn_ctx_free(pdp->ggsn);
talloc_free(pdp);
}
@@ -614,7 +624,8 @@ static void insert_qos(struct tlv_parsed *tp, struct sgsn_subscriber_pdp_data *p
*/
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 gsm48_gsm_cause *gsm_cause,
+ char *out_apn_str)
{
char req_apn_str[GSM_APN_LENGTH] = {0};
const struct apn_ctx *apn_ctx = NULL;
@@ -623,6 +634,8 @@ struct sgsn_ggsn_ctx *sgsn_mm_ctx_find_ggsn_ctx(struct sgsn_mm_ctx *mmctx,
struct sgsn_ggsn_ctx *ggsn = NULL;
int allow_any_apn = 0;
+ out_apn_str[0] = '\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");
@@ -695,6 +708,9 @@ struct sgsn_ggsn_ctx *sgsn_mm_ctx_find_ggsn_ctx(struct sgsn_mm_ctx *mmctx,
return NULL;
}
+ /* copy the selected apn_str */
+ strcpy(out_apn_str, selected_apn_str);
+
if (apn_ctx == NULL && selected_apn_str)
apn_ctx = sgsn_apn_ctx_match(selected_apn_str, mmctx->imsi);
diff --git a/openbsc/src/gprs/sgsn_main.c b/openbsc/src/gprs/sgsn_main.c
index d5f7f6588..8cb749939 100644
--- a/openbsc/src/gprs/sgsn_main.c
+++ b/openbsc/src/gprs/sgsn_main.c
@@ -378,6 +378,14 @@ int main(int argc, char **argv)
exit(2);
}
+ if (sgsn->cfg.dynamic_lookup) {
+ if (sgsn_ares_init(sgsn) != 0) {
+ LOGP(DGPRS, LOGL_FATAL,
+ "Failed to initialize c-ares(%d)\n", rc);
+ exit(4);
+ }
+ }
+
if (daemonize) {
rc = osmo_daemonize();
if (rc < 0) {
diff --git a/openbsc/src/gprs/sgsn_vty.c b/openbsc/src/gprs/sgsn_vty.c
index b7023adb2..65b5a39e1 100644
--- a/openbsc/src/gprs/sgsn_vty.c
+++ b/openbsc/src/gprs/sgsn_vty.c
@@ -135,12 +135,18 @@ static int config_write_sgsn(struct vty *vty)
inet_ntoa(g_cfg->gtp_listenaddr.sin_addr), VTY_NEWLINE);
llist_for_each_entry(gctx, &sgsn_ggsn_ctxts, list) {
+ if (gctx->id == UINT32_MAX)
+ continue;
+
vty_out(vty, " ggsn %u remote-ip %s%s", gctx->id,
inet_ntoa(gctx->remote_addr), VTY_NEWLINE);
vty_out(vty, " ggsn %u gtp-version %u%s", gctx->id,
gctx->gtp_version, VTY_NEWLINE);
}
+ if (sgsn->cfg.dynamic_lookup)
+ vty_out(vty, " ggsn dynamic%s", VTY_NEWLINE);
+
vty_out(vty, " auth-policy %s%s",
get_value_string(sgsn_auth_pol_strs, g_cfg->auth_policy),
VTY_NEWLINE);
@@ -236,6 +242,14 @@ DEFUN(cfg_ggsn_gtp_version, cfg_ggsn_gtp_version_cmd,
return CMD_SUCCESS;
}
+DEFUN(cfg_ggsn_dynamic_lookup, cfg_ggsn_dynamic_lookup_cmd,
+ "ggsn dynamic",
+ GGSN_STR "Enable dynamic GRX based look-up (requires restart)\n")
+{
+ sgsn->cfg.dynamic_lookup = 1;
+ return CMD_SUCCESS;
+}
+
#define APN_STR "Configure the information per APN\n"
#define APN_GW_STR "The APN gateway name optionally prefixed by '*' (wildcard)\n"
@@ -878,6 +892,7 @@ int sgsn_vty_init(void)
install_element(SGSN_NODE, &cfg_cdr_filename_cmd);
install_element(SGSN_NODE, &cfg_no_cdr_filename_cmd);
install_element(SGSN_NODE, &cfg_cdr_interval_cmd);
+ install_element(SGSN_NODE, &cfg_ggsn_dynamic_lookup_cmd);
return 0;
}
diff --git a/openbsc/tests/sgsn/Makefile.am b/openbsc/tests/sgsn/Makefile.am
index 693cf792a..3c202ddf2 100644
--- a/openbsc/tests/sgsn/Makefile.am
+++ b/openbsc/tests/sgsn/Makefile.am
@@ -1,5 +1,5 @@
AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include
-AM_CFLAGS=-Wall -ggdb3 $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS)
+AM_CFLAGS=-Wall -ggdb3 $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBCARES_CFLAGS)
EXTRA_DIST = sgsn_test.ok
@@ -22,6 +22,7 @@ sgsn_test_LDADD = \
$(top_builddir)/src/gprs/sgsn_vty.o \
$(top_builddir)/src/gprs/sgsn_libgtp.o \
$(top_builddir)/src/gprs/sgsn_auth.o \
+ $(top_builddir)/src/gprs/sgsn_ares.o \
$(top_builddir)/src/gprs/gprs_gsup_messages.o \
$(top_builddir)/src/gprs/gprs_gsup_client.o \
$(top_builddir)/src/gprs/gprs_utils.o \
@@ -32,5 +33,6 @@ sgsn_test_LDADD = \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOGSM_LIBS) \
$(LIBOSMOGB_LIBS) \
+ $(LIBCARES_LIBS) \
-lgtp -lrt
diff --git a/openbsc/tests/sgsn/sgsn_test.c b/openbsc/tests/sgsn/sgsn_test.c
index 64570038e..7e5ab1a91 100644
--- a/openbsc/tests/sgsn/sgsn_test.c
+++ b/openbsc/tests/sgsn/sgsn_test.c
@@ -1838,6 +1838,7 @@ static void test_ggsn_selection(void)
struct tlv_parsed tp;
uint8_t apn_enc[GSM_APN_LENGTH + 10];
struct sgsn_subscriber_pdp_data *pdp_data;
+ char apn_str[GSM_APN_LENGTH];
printf("Testing GGSN selection\n");
@@ -1881,29 +1882,33 @@ static void test_ggsn_selection(void)
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);
+ ggc = sgsn_mm_ctx_find_ggsn_ctx(ctx, &tp, &gsm_cause, apn_str);
OSMO_ASSERT(ggc != NULL);
OSMO_ASSERT(ggc->id == 0);
+ OSMO_ASSERT(strcmp(apn_str, "Test.Apn") == 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);
+ ggc = sgsn_mm_ctx_find_ggsn_ctx(ctx, &tp, &gsm_cause, apn_str);
OSMO_ASSERT(ggc != NULL);
OSMO_ASSERT(ggc->id == 1);
+ OSMO_ASSERT(strcmp(apn_str, "Other.Apn") == 0);
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);
+ ggc = sgsn_mm_ctx_find_ggsn_ctx(ctx, &tp, &gsm_cause, apn_str);
OSMO_ASSERT(ggc != NULL);
OSMO_ASSERT(ggc->id == 0);
+ OSMO_ASSERT(strcmp(apn_str, "") == 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);
+ ggc = sgsn_mm_ctx_find_ggsn_ctx(ctx, &tp, &gsm_cause, apn_str);
OSMO_ASSERT(ggc != NULL);
OSMO_ASSERT(ggc->id == 2);
+ OSMO_ASSERT(strcmp(apn_str, "") == 0);
sgsn_apn_ctx_free(actxs[3]);
tp.lv[GSM48_IE_GSM_APN].val = apn_enc;
@@ -1911,12 +1916,13 @@ static void test_ggsn_selection(void)
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);
+ ggc = sgsn_mm_ctx_find_ggsn_ctx(ctx, &tp, &gsm_cause, apn_str);
OSMO_ASSERT(ggc == NULL);
OSMO_ASSERT(gsm_cause == GSM_CAUSE_MISSING_APN);
+ OSMO_ASSERT(strcmp(apn_str, "Foo.Bar") == 0);
tp.lv[GSM48_IE_GSM_APN].len = sizeof(apn_enc);
- ggc = sgsn_mm_ctx_find_ggsn_ctx(ctx, &tp, &gsm_cause);
+ ggc = sgsn_mm_ctx_find_ggsn_ctx(ctx, &tp, &gsm_cause, apn_str);
OSMO_ASSERT(ggc == NULL);
OSMO_ASSERT(gsm_cause == GSM_CAUSE_INV_MAND_INFO);
@@ -1927,16 +1933,18 @@ static void test_ggsn_selection(void)
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);
+ ggc = sgsn_mm_ctx_find_ggsn_ctx(ctx, &tp, &gsm_cause, apn_str);
OSMO_ASSERT(ggc != NULL);
OSMO_ASSERT(ggc->id == 0);
+ OSMO_ASSERT(strcmp(apn_str, "Test.Apn") == 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);
+ ggc = sgsn_mm_ctx_find_ggsn_ctx(ctx, &tp, &gsm_cause, apn_str);
OSMO_ASSERT(ggc == NULL);
OSMO_ASSERT(gsm_cause == GSM_CAUSE_REQ_SERV_OPT_NOTSUB);
+ OSMO_ASSERT(strcmp(apn_str, "") == 0);
/* Cleanup */