aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJacob Erlbeck <jerlbeck@sysmocom.de>2014-06-06 18:49:23 +0200
committerHolger Hans Peter Freyther <holger@moiji-mobile.com>2014-07-22 16:56:33 +0200
commit7c101d922e54af3b990a2a8d86ded18c3f3b89c7 (patch)
treec4ac8dd4f4ac0d7024a81de5f87e89f4bc2f66d7
parent006c038212e1d79938810812c218a4fe4aad737c (diff)
gprs: Track IMSI/TLLI to control APN patching
This patch adds IMSI/TLLI connection tracking and uses it to control APN patching based on the IMSI. TLLI entries can expire based on age and/or by limiting the TLLI list size. VTY config-gbproxy: no core-access-point-name disable APN patching core-access-point-name none remove APN if present core-access-point-name APN replace APN if present core-access-point-name none match-imsi RE remove if IMSI matches core-access-point-name APN match-imsi RE replace if IMSI matches tlli-list max-age SECONDS expire after SECONDS no tlli-list max-age don't expire by age tlli-list max-length N keep N entries only no tlli-list max-length don't limit list length RE is an extended regular expression, e.g. ^12345|^23456 Ticket: OW#1192 Sponsored-by: On-Waves ehf
-rw-r--r--openbsc/include/openbsc/gb_proxy.h5
-rw-r--r--openbsc/src/gprs/gb_proxy.c324
-rw-r--r--openbsc/src/gprs/gb_proxy_vty.c161
-rw-r--r--openbsc/tests/gbproxy/gbproxy_test.c11
-rw-r--r--openbsc/tests/gbproxy/gbproxy_test.ok12
5 files changed, 465 insertions, 48 deletions
diff --git a/openbsc/include/openbsc/gb_proxy.h b/openbsc/include/openbsc/gb_proxy.h
index 9610ca6df..25e7265f1 100644
--- a/openbsc/include/openbsc/gb_proxy.h
+++ b/openbsc/include/openbsc/gb_proxy.h
@@ -29,7 +29,10 @@ struct gbproxy_config {
int core_mcc;
uint8_t* core_apn;
size_t core_apn_size;
+ char * match_re;
enum gbproxy_patch_mode patch_mode;
+ int tlli_max_age;
+ int tlli_max_len;
};
extern struct gbproxy_config gbcfg;
@@ -60,4 +63,6 @@ void gbprox_reset();
char *gbprox_apn_to_str(char *str, const uint8_t *apn_enc, size_t max_chars);
int gbprox_str_to_apn(uint8_t *apn_enc, const char *str, size_t max_chars);
+
+int gbprox_set_patch_filter(const char *filter, const char **err_msg);
#endif
diff --git a/openbsc/src/gprs/gb_proxy.c b/openbsc/src/gprs/gb_proxy.c
index 4f6ac24be..8fee4acbd 100644
--- a/openbsc/src/gprs/gb_proxy.c
+++ b/openbsc/src/gprs/gb_proxy.c
@@ -29,6 +29,7 @@
#include <sys/fcntl.h>
#include <sys/stat.h>
#include <arpa/inet.h>
+#include <time.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/select.h>
@@ -39,6 +40,8 @@
#include <osmocom/gprs/gprs_ns.h>
#include <osmocom/gprs/gprs_bssgp.h>
+#include <osmocom/gsm/gsm_utils.h>
+
#include <openbsc/signal.h>
#include <openbsc/debug.h>
#include <openbsc/gb_proxy.h>
@@ -128,9 +131,18 @@ static const struct rate_ctr_group_desc peer_ctrg_desc = {
.ctr_desc = peer_ctr_description,
};
+struct {
+ int check_imsi;
+ regex_t imsi_re_comp;
+} gbprox_global_patch_state = {0,};
+
struct gbprox_patch_state {
int local_mnc;
int local_mcc;
+
+ /* List of TLLIs for which patching is enabled */
+ struct llist_head enabled_tllis;
+ int enabled_tllis_count;
};
struct gbprox_peer {
@@ -155,6 +167,8 @@ struct gbprox_peer {
/* Linked list of all Gb peers (except SGSN) */
static LLIST_HEAD(gbprox_bts_peers);
+static void gbprox_delete_tllis(struct gbprox_peer *peer);
+
/* Find the gbprox_peer by its BVCI */
static struct gbprox_peer *peer_by_bvci(uint16_t bvci)
{
@@ -237,6 +251,8 @@ static struct gbprox_peer *peer_alloc(uint16_t bvci)
llist_add(&peer->list, &gbprox_bts_peers);
+ INIT_LLIST_HEAD(&peer->patch_state.enabled_tllis);
+
return peer;
}
@@ -244,6 +260,9 @@ static void peer_free(struct gbprox_peer *peer)
{
rate_ctr_group_free(peer->ctrg);
llist_del(&peer->list);
+
+ gbprox_delete_tllis(peer);
+
talloc_free(peer);
}
@@ -378,6 +397,275 @@ int gbprox_str_to_apn(uint8_t *apn_enc, const char *str, size_t max_chars)
return len;
}
+struct gbprox_tlli_info {
+ struct llist_head list;
+
+ uint32_t tlli;
+ time_t timestamp;
+ uint8_t *mi_data;
+ size_t mi_data_len;
+};
+
+static struct gbprox_tlli_info *gbprox_find_tlli(struct gbprox_peer *peer,
+ uint32_t tlli)
+{
+ struct gbprox_tlli_info *tlli_info;
+ struct gbprox_patch_state *state = &peer->patch_state;
+
+ llist_for_each_entry(tlli_info, &state->enabled_tllis, list)
+ if (tlli_info->tlli == tlli)
+ return tlli_info;
+
+ return NULL;
+}
+
+static struct gbprox_tlli_info *gbprox_find_tlli_by_mi(
+ struct gbprox_peer *peer,
+ const uint8_t *mi_data,
+ size_t mi_data_len)
+{
+ struct gbprox_tlli_info *tlli_info;
+ struct gbprox_patch_state *state = &peer->patch_state;
+
+ llist_for_each_entry(tlli_info, &state->enabled_tllis, list) {
+ if (tlli_info->mi_data_len != mi_data_len)
+ continue;
+ if (memcmp(tlli_info->mi_data, mi_data, mi_data_len) != 0)
+ continue;
+
+ return tlli_info;
+ }
+
+ return NULL;
+}
+
+static void gbprox_delete_tlli(struct gbprox_peer *peer,
+ struct gbprox_tlli_info *tlli_info)
+{
+ struct gbprox_patch_state *state = &peer->patch_state;
+
+ llist_del(&tlli_info->list);
+ talloc_free(tlli_info);
+ state->enabled_tllis_count -= 1;
+}
+
+static void gbprox_delete_tllis(struct gbprox_peer *peer)
+{
+ struct gbprox_tlli_info *tlli_info, *nxt;
+ struct gbprox_patch_state *state = &peer->patch_state;
+
+ llist_for_each_entry_safe(tlli_info, nxt, &state->enabled_tllis, list) {
+ llist_del(&tlli_info->list);
+ talloc_free(tlli_info);
+ }
+
+ OSMO_ASSERT(llist_empty(&state->enabled_tllis));
+}
+
+int gbprox_set_patch_filter(const char *filter, const char **err_msg)
+{
+ static char err_buf[300];
+ int rc;
+
+ if (gbprox_global_patch_state.check_imsi) {
+ regfree(&gbprox_global_patch_state.imsi_re_comp);
+ gbprox_global_patch_state.check_imsi = 0;
+ }
+
+ if (!filter)
+ return 0;
+
+ rc = regcomp(&gbprox_global_patch_state.imsi_re_comp, filter,
+ REG_EXTENDED | REG_NOSUB | REG_ICASE);
+
+ if (rc == 0) {
+ gbprox_global_patch_state.check_imsi = 1;
+ return 0;
+ }
+
+ if (err_msg) {
+ regerror(rc, &gbprox_global_patch_state.imsi_re_comp,
+ err_buf, sizeof(err_buf));
+ *err_msg = err_buf;
+ }
+
+ return -1;
+}
+
+static int gbprox_check_imsi(struct gbprox_peer *peer,
+ const uint8_t *imsi, size_t imsi_len)
+{
+ char mi_buf[200];
+ int rc;
+
+ if (!gbprox_global_patch_state.check_imsi)
+ return 1;
+
+ rc = gsm48_mi_to_string(mi_buf, sizeof(mi_buf), imsi, imsi_len);
+ if (rc < 1) {
+ LOGP(DGPRS, LOGL_NOTICE, "Invalid IMSI %s\n",
+ osmo_hexdump(imsi, imsi_len));
+ return -1;
+ }
+
+ LOGP(DGPRS, LOGL_DEBUG, "Checking IMSI '%s' (%d)\n", mi_buf, rc);
+
+ rc = regexec(&gbprox_global_patch_state.imsi_re_comp, mi_buf, 0, NULL, 0);
+ if (rc == REG_NOMATCH) {
+ LOGP(DGPRS, LOGL_INFO,
+ "IMSI '%s' doesn't match pattern '%s'\n",
+ mi_buf, gbcfg.match_re);
+ return 0;
+ }
+
+ return 1;
+}
+
+static int gbprox_remove_stale_ttlis(struct gbprox_peer *peer, time_t now)
+{
+ struct gbprox_patch_state *state = &peer->patch_state;
+ struct gbprox_tlli_info *tlli_info = NULL, *nxt;
+ int count = 0;
+ int deleted_count = 0;
+
+ llist_for_each_entry_safe(tlli_info, nxt, &state->enabled_tllis, list) {
+ int is_stale = 0;
+ time_t age = now - tlli_info->timestamp;
+
+ count += 1;
+
+ if (gbcfg.tlli_max_len > 0)
+ is_stale = is_stale || count > gbcfg.tlli_max_len;
+
+ if (gbcfg.tlli_max_age > 0)
+ is_stale = is_stale || age > gbcfg.tlli_max_age;
+
+ if (!is_stale)
+ continue;
+
+ LOGP(DGPRS, LOGL_INFO,
+ "Removing TLLI %08x from list (stale)\n",
+ tlli_info->tlli);
+
+ gbprox_delete_tlli(peer, tlli_info);
+ tlli_info = NULL;
+
+ deleted_count += 1;
+ }
+
+ return deleted_count;
+}
+
+static void gbprox_register_tlli(struct gbprox_peer *peer, uint32_t tlli,
+ const uint8_t *imsi, size_t imsi_len)
+{
+ struct gbprox_patch_state *state = &peer->patch_state;
+ struct gbprox_tlli_info *tlli_info;
+ int enable_patching;
+ time_t now = 0;
+
+ if (gprs_tlli_type(tlli) != TLLI_LOCAL)
+ return;
+
+ if (!imsi || (imsi[0] & GSM_MI_TYPE_MASK) != GSM_MI_TYPE_IMSI)
+ return;
+
+ if (!gbprox_global_patch_state.check_imsi)
+ return;
+
+ tlli_info = gbprox_find_tlli(peer, tlli);
+
+ /* Check, whether the IMSI matches */
+ enable_patching = gbprox_check_imsi(peer, imsi, imsi_len);
+
+ if (enable_patching < 0)
+ return;
+
+ if (!tlli_info) {
+ tlli_info = gbprox_find_tlli_by_mi(peer, imsi, imsi_len);
+
+ if (tlli_info) {
+ /* TLLI has changed somehow, adjust it */
+ LOGP(DGPRS, LOGL_INFO,
+ "The TLLI has changed from %08x to %08x\n",
+ tlli_info->tlli, tlli);
+ tlli_info->tlli = tlli;
+ }
+ }
+
+ if (!tlli_info) {
+ if (!enable_patching)
+ return;
+
+ LOGP(DGPRS, LOGL_INFO, "Adding TLLI %08x to list\n", tlli);
+ tlli_info = talloc_zero(peer, struct gbprox_tlli_info);
+ tlli_info->tlli = tlli;
+ } else {
+ llist_del(&tlli_info->list);
+ OSMO_ASSERT(state->enabled_tllis_count > 0);
+ state->enabled_tllis_count -= 1;
+ }
+
+ OSMO_ASSERT(tlli_info != NULL);
+
+ if (enable_patching) {
+ now = time(NULL);
+
+ tlli_info->timestamp = now;
+ llist_add(&tlli_info->list, &state->enabled_tllis);
+ state->enabled_tllis_count += 1;
+
+ gbprox_remove_stale_ttlis(peer, now);
+
+ if (tlli_info != llist_entry(state->enabled_tllis.next,
+ struct gbprox_tlli_info, list)) {
+ LOGP(DGPRS, LOGL_ERROR,
+ "Unexpectedly removed new TLLI entry as stale, "
+ "TLLI %08x\n", tlli);
+ tlli_info = NULL;
+ }
+ } else {
+ LOGP(DGPRS, LOGL_INFO,
+ "Removing TLLI %08x from list (patching no longer enabled)\n",
+ tlli);
+ talloc_free(tlli_info);
+ tlli_info = NULL;
+ }
+
+ if (tlli_info) {
+ tlli_info->mi_data_len = imsi_len;
+ tlli_info->mi_data =
+ talloc_realloc_size(tlli_info, tlli_info->mi_data, imsi_len);
+ OSMO_ASSERT(tlli_info->mi_data != NULL);
+ memcpy(tlli_info->mi_data, imsi, imsi_len);
+ }
+}
+
+static void gbprox_unregister_tlli(struct gbprox_peer *peer, uint32_t tlli)
+{
+ struct gbprox_tlli_info *tlli_info;
+
+ tlli_info = gbprox_find_tlli(peer, tlli);
+ if (tlli_info) {
+ LOGP(DGPRS, LOGL_INFO,
+ "Removing TLLI %08x from list\n",
+ tlli);
+ llist_del(&tlli_info->list);
+ talloc_free(tlli_info);
+ }
+}
+
+static int gbprox_check_tlli(struct gbprox_peer *peer, uint32_t tlli)
+{
+ LOGP(DGPRS, LOGL_INFO, "Checking TLLI %08x, class: %d\n",
+ tlli, gprs_tlli_type(tlli));
+ if (gprs_tlli_type(tlli) != TLLI_LOCAL)
+ return 0;
+
+ return !gbprox_global_patch_state.check_imsi ||
+ gbprox_find_tlli(peer, tlli) != NULL;
+}
+
/* check whether patching is enabled at this level */
static int patching_is_enabled(enum gbproxy_patch_mode need_at_least)
{
@@ -715,7 +1003,7 @@ struct gbprox_peer *peer_by_bssgp_tlv(struct tlv_parsed *tp)
static int gbprox_patch_dtap(struct msgb *msg, uint8_t *data, size_t data_len,
struct gbprox_peer *peer, int to_bss,
- int *len_change)
+ uint32_t tlli, int *len_change)
{
struct gsm48_hdr *g48h;
@@ -767,8 +1055,16 @@ static int gbprox_patch_dtap(struct msgb *msg, uint8_t *data, size_t data_len,
break;
if (gbcfg.core_apn == NULL)
break;
+ if (!gbprox_check_tlli(peer, tlli))
+ break;
return gbprox_patch_gsm_act_pdp_req(msg, data, data_len,
peer, to_bss, len_change);
+
+ case GSM48_MT_GMM_DETACH_ACK:
+ case GSM48_MT_GMM_DETACH_REQ:
+ gbprox_unregister_tlli(peer, tlli);
+ break;
+
default:
break;
};
@@ -777,7 +1073,9 @@ static int gbprox_patch_dtap(struct msgb *msg, uint8_t *data, size_t data_len,
}
static void gbprox_patch_llc(struct msgb *msg, uint8_t *llc, size_t llc_len,
- struct gbprox_peer *peer, int to_bss)
+ struct gbprox_peer *peer, int to_bss,
+ struct bssgp_ud_hdr *budh,
+ struct tlv_parsed *bssgp_tp)
{
struct gprs_llc_hdr_parsed ghp = {0};
int rc;
@@ -787,6 +1085,7 @@ static void gbprox_patch_llc(struct msgb *msg, uint8_t *llc, size_t llc_len,
int len_change = 0;
const char *err_info = NULL;
int err_ctr = -1;
+ uint32_t tlli = budh ? ntohl(budh->tlli) : 0;
/* parse LLC */
rc = gprs_llc_hdr_parse(&ghp, llc, llc_len);
@@ -806,6 +1105,12 @@ static void gbprox_patch_llc(struct msgb *msg, uint8_t *llc, size_t llc_len,
if (ghp.sapi != GPRS_SAPI_GMM)
return;
+ if (gbcfg.core_apn && to_bss && tlli &&
+ TLVP_PRESENT(bssgp_tp, BSSGP_IE_IMSI))
+ gbprox_register_tlli(peer, tlli,
+ TLVP_VAL(bssgp_tp, BSSGP_IE_IMSI),
+ TLVP_LEN(bssgp_tp, BSSGP_IE_IMSI));
+
if (ghp.cmd != GPRS_LLC_UI)
return;
@@ -829,7 +1134,7 @@ static void gbprox_patch_llc(struct msgb *msg, uint8_t *llc, size_t llc_len,
data_len = ghp.data_len;
rc = gbprox_patch_dtap(msg, data, data_len, peer, to_bss,
- &len_change);
+ tlli, &len_change);
if (rc > 0) {
llc_len += len_change;
@@ -875,10 +1180,9 @@ static void gbprox_patch_bssgp_message(struct msgb *msg,
struct gbprox_peer *peer, int to_bss)
{
struct bssgp_normal_hdr *bgph;
- struct bssgp_ud_hdr *budh;
+ struct bssgp_ud_hdr *budh = NULL;
struct tlv_parsed tp;
uint8_t pdu_type;
- struct gbprox_patch_state *state = NULL;
uint8_t *data;
size_t data_len;
@@ -886,11 +1190,12 @@ static void gbprox_patch_bssgp_message(struct msgb *msg,
return;
bgph = (struct bssgp_normal_hdr *) msgb_bssgph(msg);
- budh = (struct bssgp_ud_hdr *) msgb_bssgph(msg);
pdu_type = bgph->pdu_type;
if (pdu_type == BSSGP_PDUT_UL_UNITDATA ||
pdu_type == BSSGP_PDUT_DL_UNITDATA) {
+ budh = (struct bssgp_ud_hdr *) msgb_bssgph(msg);
+ bgph = NULL;
data = budh->data;
data_len = msgb_bssgp_len(msg) - sizeof(*budh);
} else {
@@ -919,11 +1224,6 @@ static void gbprox_patch_bssgp_message(struct msgb *msg,
return;
}
- state = &peer->patch_state;
-
- if (to_bss && !state->local_mcc && !state->local_mnc)
- return;
-
if (TLVP_PRESENT(&tp, BSSGP_IE_ROUTEING_AREA)) {
gbprox_patch_raid((uint8_t *)TLVP_VAL(&tp, BSSGP_IE_ROUTEING_AREA),
peer, to_bss, "ROUTING_AREA");
@@ -937,7 +1237,7 @@ static void gbprox_patch_bssgp_message(struct msgb *msg,
patching_is_enabled(GBPROX_PATCH_LLC_ATTACH_REQ)) {
uint8_t *llc = (uint8_t *)TLVP_VAL(&tp, BSSGP_IE_LLC_PDU);
size_t llc_len = TLVP_LEN(&tp, BSSGP_IE_LLC_PDU);
- gbprox_patch_llc(msg, llc, llc_len, peer, to_bss);
+ gbprox_patch_llc(msg, llc, llc_len, peer, to_bss, budh, &tp);
/* Note that the tp struct might contain invalid pointers here
* if the LLC field has changed its size */
}
diff --git a/openbsc/src/gprs/gb_proxy_vty.c b/openbsc/src/gprs/gb_proxy_vty.c
index 6d241653d..36d959a42 100644
--- a/openbsc/src/gprs/gb_proxy_vty.c
+++ b/openbsc/src/gprs/gb_proxy_vty.c
@@ -72,15 +72,24 @@ static int config_write_gbproxy(struct vty *vty)
if (g_cfg->core_apn != NULL) {
if (g_cfg->core_apn_size > 0) {
char str[500] = {0};
- vty_out(vty, " core-access-point-name %s%s",
+ vty_out(vty, " core-access-point-name %s",
gbprox_apn_to_str(str, g_cfg->core_apn,
- g_cfg->core_apn_size),
- VTY_NEWLINE);
+ g_cfg->core_apn_size));
} else {
- vty_out(vty, " core-access-point-name%s",
- VTY_NEWLINE);
+ vty_out(vty, " core-access-point-name none");
}
+ if (g_cfg->match_re)
+ vty_out(vty, " match-imsi %s%s",
+ g_cfg->match_re, VTY_NEWLINE);
+ else
+ vty_out(vty, "%s", VTY_NEWLINE);
}
+ if (g_cfg->tlli_max_age > 0)
+ vty_out(vty, " tlli-list max-age %d%s",
+ g_cfg->tlli_max_age, VTY_NEWLINE);
+ if (g_cfg->tlli_max_len > 0)
+ vty_out(vty, " tlli-list max-length %d%s",
+ g_cfg->tlli_max_len, VTY_NEWLINE);
if (g_cfg->patch_mode != GBPROX_PATCH_DEFAULT)
vty_out(vty, " patch-mode %s%s",
@@ -153,50 +162,140 @@ DEFUN(cfg_gbproxy_no_core_mcc,
}
#define GBPROXY_CORE_APN_STR "Use this access point name (APN) for the backbone\n"
+#define GBPROXY_CORE_APN_ARG_STR "Replace APN by this string\n" "Remove APN\n"
-DEFUN(cfg_gbproxy_core_apn_remove,
- cfg_gbproxy_core_apn_remove_cmd,
- "core-access-point-name",
- GBPROXY_CORE_APN_STR)
+static int set_core_apn(struct vty *vty, const char *apn, const char *filter)
{
- talloc_free(g_cfg->core_apn);
- /* TODO: replace NULL */
- g_cfg->core_apn = talloc_zero_size(NULL, 2);
- g_cfg->core_apn_size = 0;
- return CMD_SUCCESS;
-}
+ const char *err_msg = NULL;
+ int apn_len;
+
+ if (!apn) {
+ talloc_free(g_cfg->core_apn);
+ g_cfg->core_apn = NULL;
+ g_cfg->core_apn_size = 0;
+ gbprox_set_patch_filter(NULL, NULL);
+ return CMD_SUCCESS;
+ }
-DEFUN(cfg_gbproxy_core_apn,
- cfg_gbproxy_core_apn_cmd,
- "core-access-point-name APN",
- GBPROXY_CORE_APN_STR "Replacement APN\n")
-{
- int apn_len = strlen(argv[0]) + 1;
+ apn_len = strlen(apn);
- if (apn_len > 100) {
+ if (apn_len >= 100) {
vty_out(vty, "APN string too long (max 99 chars)%s",
VTY_NEWLINE);
return CMD_WARNING;
}
- /* TODO: replace NULL */
- g_cfg->core_apn = talloc_realloc_size(NULL, g_cfg->core_apn, apn_len);
- g_cfg->core_apn_size = gbprox_str_to_apn(g_cfg->core_apn, argv[0], apn_len);
+ if (!filter) {
+ gbprox_set_patch_filter(NULL, NULL);
+ } else if (gbprox_set_patch_filter(filter, &err_msg) != 0) {
+ vty_out(vty, "Match expression invalid: %s%s",
+ err_msg, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ talloc_free(g_cfg->match_re);
+ if (filter)
+ /* TODO: replace NULL */
+ g_cfg->match_re = talloc_strdup(NULL, filter);
+ else
+ g_cfg->match_re = NULL;
+
+ if (apn_len == 0) {
+ talloc_free(g_cfg->core_apn);
+ /* TODO: replace NULL */
+ g_cfg->core_apn = talloc_zero_size(NULL, 2);
+ g_cfg->core_apn_size = 0;
+ } else {
+ /* TODO: replace NULL */
+ g_cfg->core_apn =
+ talloc_realloc_size(NULL, g_cfg->core_apn, apn_len + 1);
+ g_cfg->core_apn_size =
+ gbprox_str_to_apn(g_cfg->core_apn, apn, apn_len + 1);
+ }
return CMD_SUCCESS;
}
+DEFUN(cfg_gbproxy_core_apn,
+ cfg_gbproxy_core_apn_cmd,
+ "core-access-point-name (APN|none)",
+ GBPROXY_CORE_APN_STR GBPROXY_CORE_APN_ARG_STR)
+{
+ if (strcmp(argv[0], "none") == 0)
+ return set_core_apn(vty, "", NULL);
+ else
+ return set_core_apn(vty, argv[0], NULL);
+}
+
+DEFUN(cfg_gbproxy_core_apn_match,
+ cfg_gbproxy_core_apn_match_cmd,
+ "core-access-point-name (APN|none) match-imsi .REGEXP",
+ GBPROXY_CORE_APN_STR GBPROXY_CORE_APN_ARG_STR
+ "Only modify if the IMSI matches\n"
+ "Regular expression for the match\n")
+{
+ if (strcmp(argv[0], "none") == 0)
+ return set_core_apn(vty, "", argv[1]);
+ else
+ return set_core_apn(vty, argv[0], argv[1]);
+}
+
DEFUN(cfg_gbproxy_no_core_apn,
cfg_gbproxy_no_core_apn_cmd,
"no core-access-point-name",
NO_STR GBPROXY_CORE_APN_STR)
{
- talloc_free(g_cfg->core_apn);
- g_cfg->core_apn = NULL;
- g_cfg->core_apn_size = 0;
+ return set_core_apn(vty, NULL, NULL);
+}
+
+#define GBPROXY_TLLI_LIST_STR "Set TLLI list parameters\n"
+#define GBPROXY_MAX_AGE_STR "Limit maximum age\n"
+
+DEFUN(cfg_gbproxy_tlli_list_max_age,
+ cfg_gbproxy_tlli_list_max_age_cmd,
+ "tlli-list max-age <1-999999>",
+ GBPROXY_TLLI_LIST_STR GBPROXY_MAX_AGE_STR
+ "Maximum age in seconds\n")
+{
+ g_cfg->tlli_max_age = atoi(argv[0]);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_gbproxy_tlli_list_no_max_age,
+ cfg_gbproxy_tlli_list_no_max_age_cmd,
+ "no tlli-list max-age",
+ NO_STR GBPROXY_TLLI_LIST_STR GBPROXY_MAX_AGE_STR)
+{
+ g_cfg->tlli_max_age = 0;
+
+ return CMD_SUCCESS;
+}
+
+#define GBPROXY_MAX_LEN_STR "Limit list length\n"
+
+DEFUN(cfg_gbproxy_tlli_list_max_len,
+ cfg_gbproxy_tlli_list_max_len_cmd,
+ "tlli-list max-length <1-99999>",
+ GBPROXY_TLLI_LIST_STR GBPROXY_MAX_LEN_STR
+ "Maximum number of TLLIs in the list\n")
+{
+ g_cfg->tlli_max_len = atoi(argv[0]);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_gbproxy_tlli_list_no_max_len,
+ cfg_gbproxy_tlli_list_no_max_len_cmd,
+ "no tlli-list max-length",
+ NO_STR GBPROXY_TLLI_LIST_STR GBPROXY_MAX_LEN_STR)
+{
+ g_cfg->tlli_max_len = 0;
+
return CMD_SUCCESS;
}
+
DEFUN(cfg_gbproxy_patch_mode,
cfg_gbproxy_patch_mode_cmd,
"patch-mode (default|bssgp|llc-attach-req|llc-attach|llc-gmm|llc-gsm|llc)",
@@ -231,11 +330,15 @@ int gbproxy_vty_init(void)
install_element(GBPROXY_NODE, &cfg_nsip_sgsn_nsei_cmd);
install_element(GBPROXY_NODE, &cfg_gbproxy_core_mcc_cmd);
install_element(GBPROXY_NODE, &cfg_gbproxy_core_mnc_cmd);
- install_element(GBPROXY_NODE, &cfg_gbproxy_core_apn_remove_cmd);
install_element(GBPROXY_NODE, &cfg_gbproxy_core_apn_cmd);
+ install_element(GBPROXY_NODE, &cfg_gbproxy_core_apn_match_cmd);
+ install_element(GBPROXY_NODE, &cfg_gbproxy_tlli_list_max_age_cmd);
+ install_element(GBPROXY_NODE, &cfg_gbproxy_tlli_list_max_len_cmd);
install_element(GBPROXY_NODE, &cfg_gbproxy_no_core_mcc_cmd);
install_element(GBPROXY_NODE, &cfg_gbproxy_no_core_mnc_cmd);
install_element(GBPROXY_NODE, &cfg_gbproxy_no_core_apn_cmd);
+ install_element(GBPROXY_NODE, &cfg_gbproxy_tlli_list_no_max_age_cmd);
+ install_element(GBPROXY_NODE, &cfg_gbproxy_tlli_list_no_max_len_cmd);
install_element(GBPROXY_NODE, &cfg_gbproxy_patch_mode_cmd);
return 0;
diff --git a/openbsc/tests/gbproxy/gbproxy_test.c b/openbsc/tests/gbproxy/gbproxy_test.c
index fa7e0bb12..a9d06551b 100644
--- a/openbsc/tests/gbproxy/gbproxy_test.c
+++ b/openbsc/tests/gbproxy/gbproxy_test.c
@@ -842,6 +842,7 @@ static void test_gbproxy_ra_patching()
{.mcc = 123, .mnc = 456, .lac = 16464, .rac = 96};
struct gprs_ra_id rai_unknown =
{.mcc = 1, .mnc = 99, .lac = 99, .rac = 96};
+ const char *err_msg = NULL;
bssgp_nsi = nsi;
gbcfg.nsi = bssgp_nsi;
@@ -854,6 +855,14 @@ static void test_gbproxy_ra_patching()
configure_sgsn_peer(&sgsn_peer);
configure_bss_peers(bss_peer, ARRAY_SIZE(bss_peer));
+ gbcfg.match_re = talloc_strdup(NULL, "^9898|^121314");
+ if (gbprox_set_patch_filter(gbcfg.match_re, &err_msg) != 0) {
+ fprintf(stderr, "Failed to compile RE '%s': %s\n",
+ gbcfg.match_re, err_msg);
+ exit(1);
+ }
+
+
printf("=== %s ===\n", __func__);
printf("--- Initialise SGSN ---\n\n");
@@ -917,7 +926,7 @@ static void test_gbproxy_ra_patching()
printf("--- Bad cases ---\n\n");
- printf("Invalid BVCI, shouldn't patch\n");
+ printf("TLLI is already detached, shouldn't patch\n");
send_ns_unitdata(nsi, "ACT PDP CTX REQ", &bss_peer[0], 0x1002,
bssgp_act_pdp_ctx_req, sizeof(bssgp_act_pdp_ctx_req));
diff --git a/openbsc/tests/gbproxy/gbproxy_test.ok b/openbsc/tests/gbproxy/gbproxy_test.ok
index da5cd4178..fb8991c2d 100644
--- a/openbsc/tests/gbproxy/gbproxy_test.ok
+++ b/openbsc/tests/gbproxy/gbproxy_test.ok
@@ -1680,18 +1680,18 @@ Peers:
APN patched : 2
--- Bad cases ---
-Invalid BVCI, shouldn't patch
+TLLI is already detached, shouldn't patch
PROCESSING ACT PDP CTX REQ from 0x01020304:1111
00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 75 30 00 80 0e 00 35 01 c0 0d 0a 41 05 03 0c 00 00 1f 10 00 00 00 00 00 00 00 00 02 01 21 28 03 02 61 62 27 14 80 80 21 10 01 00 00 10 81 06 00 00 00 00 83 06 00 00 00 00 5a ff 02
CALLBACK, event 0, msg length 76, bvci 0x1002
00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 75 30 00 80 0e 00 35 01 c0 0d 0a 41 05 03 0c 00 00 1f 10 00 00 00 00 00 00 00 00 02 01 21 28 03 02 61 62 27 14 80 80 21 10 01 00 00 10 81 06 00 00 00 00 83 06 00 00 00 00 5a ff 02
-NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 71 (gprs_ns_sendmsg)
-MESSAGE to SGSN at 0x05060708:32000, msg length 75
-00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 21 63 54 40 50 60 75 30 00 80 0e 00 30 01 c0 0d 0a 41 05 03 0c 00 00 1f 10 00 00 00 00 00 00 00 00 02 01 21 27 14 80 80 21 10 01 00 00 10 81 06 00 00 00 00 83 06 00 00 00 00 85 fa 60
+NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 76 (gprs_ns_sendmsg)
+MESSAGE to SGSN at 0x05060708:32000, msg length 80
+00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 21 63 54 40 50 60 75 30 00 80 0e 00 35 01 c0 0d 0a 41 05 03 0c 00 00 1f 10 00 00 00 00 00 00 00 00 02 01 21 28 03 02 61 62 27 14 80 80 21 10 01 00 00 10 81 06 00 00 00 00 83 06 00 00 00 00 5a ff 02
-result (ACT PDP CTX REQ) = 75
+result (ACT PDP CTX REQ) = 80
Invalid RAI, shouldn't patch
PROCESSING BVC_SUSPEND_ACK from 0x05060708:32000
@@ -1713,6 +1713,6 @@ Peers:
NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96
RAID patched (BSS ): 10
RAID patched (SGSN): 3
- APN patched : 3
+ APN patched : 2
===== GbProxy test END