/* APN contexts */ /* (C) 2009-2015 by Harald Welte * (C) 2010 by On-Waves * (C) 2019 by sysmocom s.f.m.c. GmbH * * 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 . * */ #include #include #include #include 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; }