diff options
author | Jacob Erlbeck <jerlbeck@sysmocom.de> | 2015-02-03 13:47:53 +0100 |
---|---|---|
committer | Holger Hans Peter Freyther <holger@moiji-mobile.com> | 2015-02-06 09:56:17 +0100 |
commit | cb1db8b6d5277a52ed553925d301bfdc514bb77c (patch) | |
tree | 62515e3ce5b4c0d1d13bcc02c787e1957e64e222 | |
parent | 0e8add601da35188eb155c8c279a9bdcaf4ba41b (diff) |
sgsn: Add functions to handle APN contexts
This commit adds the exported functions apn_ctx_find_alloc,
apn_ctx_free, apn_ctx_by_name, and apn_ctx_match to manage and
retrieve APN to GGSN mappings.
The following VTY commands are added to 'config-sgsn':
- apn APN ggsn <0-255>
- apn APN imsi-prefix PREFIX ggsn <0-255>
which maps an APN gateway string to an SGSN id. The SGSN must be
configured in advance. When matching an APN string, entries with a
leading '*' are used for suffix matching, otherwise an exact match is
done. When a prefix is given, it is matched against the IMSI. If
several entries match, a longer matching IMSI prefix has precedence.
If there are several matching entries with the same PREFIX, the entry
with longest matching APN is returned.
Ticket: OW#1334
Sponsored-by: On-Waves ehf
-rw-r--r-- | openbsc/include/openbsc/gprs_sgsn.h | 6 | ||||
-rw-r--r-- | openbsc/src/gprs/gprs_sgsn.c | 77 | ||||
-rw-r--r-- | openbsc/src/gprs/sgsn_vty.c | 64 | ||||
-rw-r--r-- | openbsc/tests/sgsn/sgsn_test.c | 93 | ||||
-rw-r--r-- | openbsc/tests/sgsn/sgsn_test.ok | 1 | ||||
-rw-r--r-- | openbsc/tests/vty_test_runner.py | 21 |
6 files changed, 249 insertions, 13 deletions
diff --git a/openbsc/include/openbsc/gprs_sgsn.h b/openbsc/include/openbsc/gprs_sgsn.h index 516b6cda9..b0d7c32b4 100644 --- a/openbsc/include/openbsc/gprs_sgsn.h +++ b/openbsc/include/openbsc/gprs_sgsn.h @@ -239,9 +239,15 @@ struct apn_ctx { struct llist_head list; struct sgsn_ggsn_ctx *ggsn; char *name; + char *imsi_prefix; char *description; }; +struct apn_ctx *sgsn_apn_ctx_find_alloc(const char *name, const char *imsi_prefix); +void sgsn_apn_ctx_free(struct apn_ctx *actx); +struct apn_ctx *sgsn_apn_ctx_by_name(const char *name, const char *imsi_prefix); +struct apn_ctx *sgsn_apn_ctx_match(const char *name, const char *imsi_prefix); + extern struct llist_head sgsn_mm_ctxts; extern struct llist_head sgsn_ggsn_ctxts; extern struct llist_head sgsn_apn_ctxts; diff --git a/openbsc/src/gprs/gprs_sgsn.c b/openbsc/src/gprs/gprs_sgsn.c index 555be57f5..54fe15cb1 100644 --- a/openbsc/src/gprs/gprs_sgsn.c +++ b/openbsc/src/gprs/gprs_sgsn.c @@ -387,41 +387,100 @@ struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_find_alloc(uint32_t id) /* APN contexts */ -#if 0 -struct apn_ctx *apn_ctx_alloc(const char *ap_name) +static struct apn_ctx *sgsn_apn_ctx_alloc(const char *ap_name, const char *imsi_prefix) { struct apn_ctx *actx; - actx = talloc_zero(talloc_bsc_ctx, struct apn_ctx); + actx = talloc_zero(tall_bsc_ctx, struct apn_ctx); if (!actx) return NULL; actx->name = talloc_strdup(actx, ap_name); + actx->imsi_prefix = talloc_strdup(actx, imsi_prefix); + + llist_add_tail(&actx->list, &sgsn_apn_ctxts); return actx; } -struct apn_ctx *apn_ctx_by_name(const char *name) +void sgsn_apn_ctx_free(struct apn_ctx *actx) +{ + llist_del(&actx->list); + talloc_free(actx); +} + +struct apn_ctx *sgsn_apn_ctx_match(const char *name, const char *imsi) +{ + struct apn_ctx *actx; + struct apn_ctx *found_actx = NULL; + size_t imsi_prio = 0; + size_t name_prio = 0; + size_t name_req_len = strlen(name); + + llist_for_each_entry(actx, &sgsn_apn_ctxts, list) { + size_t name_ref_len, imsi_ref_len; + const char *name_ref_start, *name_match_start; + + imsi_ref_len = strlen(actx->imsi_prefix); + if (strncmp(actx->imsi_prefix, imsi, imsi_ref_len) != 0) + continue; + + if (imsi_ref_len < imsi_prio) + continue; + + /* IMSI matches */ + + name_ref_start = &actx->name[0]; + if (name_ref_start[0] == '*') { + /* Suffix match */ + name_ref_start += 1; + name_ref_len = strlen(name_ref_start); + if (name_ref_len > name_req_len) + continue; + } else { + name_ref_len = strlen(name_ref_start); + if (name_ref_len != name_req_len) + continue; + } + + name_match_start = name + (name_req_len - name_ref_len); + if (strcasecmp(name_match_start, name_ref_start) != 0) + continue; + + /* IMSI and name match */ + + if (imsi_ref_len == imsi_prio && name_ref_len < name_prio) + /* Lower priority, skip */ + continue; + + imsi_prio = imsi_ref_len; + name_prio = name_ref_len; + found_actx = actx; + } + return found_actx; +} + +struct apn_ctx *sgsn_apn_ctx_by_name(const char *name, const char *imsi_prefix) { struct apn_ctx *actx; llist_for_each_entry(actx, &sgsn_apn_ctxts, list) { - if (!strcmp(name, actx->name)) + if (strcasecmp(name, actx->name) == 0 && + strcasecmp(imsi_prefix, actx->imsi_prefix) == 0) return actx; } return NULL; } -struct apn_ctx *apn_ctx_find_alloc(const char *name) +struct apn_ctx *sgsn_apn_ctx_find_alloc(const char *name, const char *imsi_prefix) { struct apn_ctx *actx; - actx = apn_ctx_by_name(name); + actx = sgsn_apn_ctx_by_name(name, imsi_prefix); if (!actx) - actx = apn_ctx_alloc(name); + actx = sgsn_apn_ctx_alloc(name, imsi_prefix); return actx; } -#endif uint32_t sgsn_alloc_ptmsi(void) { diff --git a/openbsc/src/gprs/sgsn_vty.c b/openbsc/src/gprs/sgsn_vty.c index 81b9d7f67..d85ea0192 100644 --- a/openbsc/src/gprs/sgsn_vty.c +++ b/openbsc/src/gprs/sgsn_vty.c @@ -126,6 +126,7 @@ static int config_write_sgsn(struct vty *vty) { struct sgsn_ggsn_ctx *gctx; struct imsi_acl_entry *acl; + struct apn_ctx *actx; vty_out(vty, "sgsn%s", VTY_NEWLINE); @@ -151,6 +152,18 @@ static int config_write_sgsn(struct vty *vty) llist_for_each_entry(acl, &g_cfg->imsi_acl, list) vty_out(vty, " imsi-acl add %s%s", acl->imsi, VTY_NEWLINE); + if (llist_empty(&sgsn_apn_ctxts)) + vty_out(vty, " ! apn * ggsn 0%s", VTY_NEWLINE); + llist_for_each_entry(actx, &sgsn_apn_ctxts, list) { + if (strlen(actx->imsi_prefix) > 0) + vty_out(vty, " apn %s imsi-prefix %s ggsn %d%s", + actx->name, actx->imsi_prefix, actx->ggsn->id, + VTY_NEWLINE); + else + vty_out(vty, " apn %s ggsn %d%s", actx->name, + actx->ggsn->id, VTY_NEWLINE); + } + return CMD_SUCCESS; } @@ -216,14 +229,55 @@ DEFUN(cfg_ggsn_gtp_version, cfg_ggsn_gtp_version_cmd, return CMD_SUCCESS; } -#if 0 +#define APN_STR "Configure the information per APN\n" +#define APN_GW_STR "The APN gateway name optionally prefixed by '*' (wildcard)\n" + +static int add_apn_ggsn_mapping(struct vty *vty, const char *apn_str, + const char *imsi_prefix, int ggsn_id) +{ + struct apn_ctx *actx; + struct sgsn_ggsn_ctx *ggsn; + + ggsn = sgsn_ggsn_ctx_by_id(ggsn_id); + if (ggsn == NULL) { + vty_out(vty, "%% a GGSN with id %d has not been defined%s", + ggsn_id, VTY_NEWLINE); + return CMD_WARNING; + } + + actx = sgsn_apn_ctx_find_alloc(apn_str, imsi_prefix); + if (!actx) { + vty_out(vty, "%% unable to create APN context for %s/%s%s", + apn_str, imsi_prefix, VTY_NEWLINE); + return CMD_WARNING; + } + + actx->ggsn = ggsn; + + return CMD_SUCCESS; +} + DEFUN(cfg_apn_ggsn, cfg_apn_ggsn_cmd, "apn APNAME ggsn <0-255>", - "") + APN_STR APN_GW_STR + "Select the GGSN to use when the APN gateway prefix matches\n" + "The GGSN id") { - struct apn_ctx ** + + return add_apn_ggsn_mapping(vty, argv[0], "", atoi(argv[1])); +} + +DEFUN(cfg_apn_imsi_ggsn, cfg_apn_imsi_ggsn_cmd, + "apn APNAME imsi-prefix IMSIPRE ggsn <0-255>", + APN_STR APN_GW_STR + "Restrict rule to a certain IMSI prefix\n" + "An IMSI prefix\n" + "Select the GGSN to use when APN gateway and IMSI prefix match\n" + "The GGSN id") +{ + + return add_apn_ggsn_mapping(vty, argv[0], argv[1], atoi(argv[2])); } -#endif const struct value_string gprs_mm_st_strs[] = { { GMM_DEREGISTERED, "DEREGISTERED" }, @@ -757,6 +811,8 @@ int sgsn_vty_init(void) install_element(SGSN_NODE, &cfg_auth_policy_cmd); install_element(SGSN_NODE, &cfg_gsup_remote_ip_cmd); install_element(SGSN_NODE, &cfg_gsup_remote_port_cmd); + install_element(SGSN_NODE, &cfg_apn_ggsn_cmd); + install_element(SGSN_NODE, &cfg_apn_imsi_ggsn_cmd); return 0; } diff --git a/openbsc/tests/sgsn/sgsn_test.c b/openbsc/tests/sgsn/sgsn_test.c index 7a14cdefa..5fa33a266 100644 --- a/openbsc/tests/sgsn/sgsn_test.c +++ b/openbsc/tests/sgsn/sgsn_test.c @@ -1784,6 +1784,98 @@ static void test_gmm_ptmsi_allocation(void) sgsn->cfg.auth_policy = saved_auth_policy; } +static void test_apn_matching(void) +{ + struct apn_ctx *actx, *actxs[9]; + + printf("Testing APN matching\n"); + + actxs[0] = sgsn_apn_ctx_find_alloc("*.test", ""); + actxs[1] = sgsn_apn_ctx_find_alloc("*.def.test", ""); + actxs[2] = sgsn_apn_ctx_find_alloc("abc.def.test", ""); + actxs[3] = NULL; + + actxs[4] = sgsn_apn_ctx_find_alloc("abc.def.test", "456"); + actxs[5] = sgsn_apn_ctx_find_alloc("abc.def.test", "456123"); + actxs[6] = sgsn_apn_ctx_find_alloc("*.def.test", "456"); + actxs[7] = sgsn_apn_ctx_find_alloc("*.def.test", "456123"); + + actxs[8] = sgsn_apn_ctx_find_alloc("ghi.def.test", "456"); + + actx = sgsn_apn_ctx_match("abc.def.test", "12345678"); + OSMO_ASSERT(actx == actxs[2]); + actx = sgsn_apn_ctx_match("aBc.dEf.test", "12345678"); + OSMO_ASSERT(actx == actxs[2]); + actx = sgsn_apn_ctx_match("xyz.def.test", "12345678"); + OSMO_ASSERT(actx == actxs[1]); + actx = sgsn_apn_ctx_match("xyz.dEf.test", "12345678"); + OSMO_ASSERT(actx == actxs[1]); + actx = sgsn_apn_ctx_match("xyz.uvw.test", "12345678"); + OSMO_ASSERT(actx == actxs[0]); + actx = sgsn_apn_ctx_match("xyz.uvw.foo", "12345678"); + OSMO_ASSERT(actx == NULL); + + actxs[3] = sgsn_apn_ctx_find_alloc("*", ""); + actx = sgsn_apn_ctx_match("xyz.uvw.foo", "12345678"); + OSMO_ASSERT(actx == actxs[3]); + + actx = sgsn_apn_ctx_match("abc.def.test", "45699900"); + OSMO_ASSERT(actx == actxs[4]); + + actx = sgsn_apn_ctx_match("xyz.def.test", "45699900"); + OSMO_ASSERT(actx == actxs[6]); + + actx = sgsn_apn_ctx_match("abc.def.test", "45612300"); + OSMO_ASSERT(actx == actxs[5]); + + actx = sgsn_apn_ctx_match("xyz.def.test", "45612300"); + OSMO_ASSERT(actx == actxs[7]); + + actx = sgsn_apn_ctx_match("ghi.def.test", "45699900"); + OSMO_ASSERT(actx == actxs[8]); + + actx = sgsn_apn_ctx_match("ghi.def.test", "45612300"); + OSMO_ASSERT(actx == actxs[7]); + + /* Free APN contexts and check how the matching changes */ + + sgsn_apn_ctx_free(actxs[7]); + actx = sgsn_apn_ctx_match("ghi.def.test", "45612300"); + OSMO_ASSERT(actx == actxs[8]); + + sgsn_apn_ctx_free(actxs[8]); + actx = sgsn_apn_ctx_match("ghi.def.test", "45612300"); + OSMO_ASSERT(actx == actxs[6]); + + sgsn_apn_ctx_free(actxs[6]); + actx = sgsn_apn_ctx_match("ghi.def.test", "45612300"); + OSMO_ASSERT(actx == actxs[1]); + + sgsn_apn_ctx_free(actxs[5]); + actx = sgsn_apn_ctx_match("abc.def.test", "45612300"); + OSMO_ASSERT(actx == actxs[4]); + + sgsn_apn_ctx_free(actxs[4]); + actx = sgsn_apn_ctx_match("abc.def.test", "45612300"); + OSMO_ASSERT(actx == actxs[2]); + + sgsn_apn_ctx_free(actxs[2]); + actx = sgsn_apn_ctx_match("abc.def.test", "12345678"); + OSMO_ASSERT(actx == actxs[1]); + + sgsn_apn_ctx_free(actxs[1]); + actx = sgsn_apn_ctx_match("abc.def.test", "12345678"); + OSMO_ASSERT(actx == actxs[0]); + + sgsn_apn_ctx_free(actxs[0]); + actx = sgsn_apn_ctx_match("abc.def.test", "12345678"); + OSMO_ASSERT(actx == actxs[3]); + + sgsn_apn_ctx_free(actxs[3]); + actx = sgsn_apn_ctx_match("abc.def.test", "12345678"); + OSMO_ASSERT(actx == NULL); +} + static struct log_info_cat gprs_categories[] = { [DMM] = { .name = "DMM", @@ -1871,6 +1963,7 @@ int main(int argc, char **argv) test_gmm_reject(); test_gmm_cancel(); test_gmm_ptmsi_allocation(); + test_apn_matching(); 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 e5df50482..9f14721af 100644 --- a/openbsc/tests/sgsn/sgsn_test.ok +++ b/openbsc/tests/sgsn/sgsn_test.ok @@ -26,4 +26,5 @@ Testing P-TMSI allocation - sgsn_alloc_ptmsi - Repeated Attach Request - Repeated RA Update Request +Testing APN matching Done diff --git a/openbsc/tests/vty_test_runner.py b/openbsc/tests/vty_test_runner.py index d87ebde0c..cae1c1414 100644 --- a/openbsc/tests/vty_test_runner.py +++ b/openbsc/tests/vty_test_runner.py @@ -794,6 +794,27 @@ class TestVTYSGSN(TestVTYGenericBSC): res = self.vty.command('show subscriber cache') self.assert_(res.find('1234567890') < 0) + def testVtyGgsn(self): + self.vty.enable() + self.assertTrue(self.vty.verify('configure terminal', [''])) + self.assertEquals(self.vty.node(), 'config') + self.assertTrue(self.vty.verify('sgsn', [''])) + self.assertEquals(self.vty.node(), 'config-sgsn') + self.assertTrue(self.vty.verify('ggsn 0 remote-ip 127.99.99.99', [''])) + self.assertTrue(self.vty.verify('ggsn 0 gtp-version 1', [''])) + self.assertTrue(self.vty.verify('apn * ggsn 0', [''])) + self.assertTrue(self.vty.verify('apn apn1.test ggsn 0', [''])) + self.assertTrue(self.vty.verify('apn apn1.test ggsn 1', ['% a GGSN with id 1 has not been defined'])) + self.assertTrue(self.vty.verify('apn apn1.test imsi-prefix 123456 ggsn 0', [''])) + self.assertTrue(self.vty.verify('apn apn2.test imsi-prefix 123456 ggsn 0', [''])) + res = self.vty.command("show running-config") + self.assert_(res.find('ggsn 0 remote-ip 127.99.99.99') >= 0) + self.assert_(res.find('ggsn 0 gtp-version 1') >= 0) + self.assert_(res.find('apn * ggsn 0') >= 0) + self.assert_(res.find('apn apn1.test ggsn 0') >= 0) + self.assert_(res.find('apn apn1.test imsi-prefix 123456 ggsn 0') >= 0) + self.assert_(res.find('apn apn2.test imsi-prefix 123456 ggsn 0') >= 0) + def add_nat_test(suite, workdir): if not os.path.isfile(os.path.join(workdir, "src/osmo-bsc_nat/osmo-bsc_nat")): print("Skipping the NAT test") |