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
This commit is contained in:
parent
94a346adfc
commit
9b3ca64928
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -26,4 +26,5 @@ Testing P-TMSI allocation
|
|||
- sgsn_alloc_ptmsi
|
||||
- Repeated Attach Request
|
||||
- Repeated RA Update Request
|
||||
Testing APN matching
|
||||
Done
|
||||
|
|
|
@ -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")
|
||||
|
|
Loading…
Reference in New Issue