diff options
Diffstat (limited to 'src/sgsn/apn.c')
-rw-r--r-- | src/sgsn/apn.c | 123 |
1 files changed, 123 insertions, 0 deletions
diff --git a/src/sgsn/apn.c b/src/sgsn/apn.c new file mode 100644 index 000000000..a89d15866 --- /dev/null +++ b/src/sgsn/apn.c @@ -0,0 +1,123 @@ +/* APN contexts */ + +/* (C) 2009-2015 by Harald Welte <laforge@gnumonks.org> + * (C) 2010 by On-Waves + * (C) 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#include <string.h> +#include <talloc.h> + +#include <osmocom/sgsn/apn.h> +#include <osmocom/sgsn/sgsn.h> + +static struct apn_ctx *sgsn_apn_ctx_alloc(const char *ap_name, const char *imsi_prefix) +{ + struct apn_ctx *actx; + + actx = talloc_zero(sgsn, 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_list); + + return actx; +} + +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_list, 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_list, list) { + if (strcasecmp(name, actx->name) == 0 && + strcasecmp(imsi_prefix, actx->imsi_prefix) == 0) + return actx; + } + return NULL; +} + +struct apn_ctx *sgsn_apn_ctx_find_alloc(const char *name, const char *imsi_prefix) +{ + struct apn_ctx *actx; + + actx = sgsn_apn_ctx_by_name(name, imsi_prefix); + if (!actx) + actx = sgsn_apn_ctx_alloc(name, imsi_prefix); + + return actx; +} |