aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJacob Erlbeck <jerlbeck@sysmocom.de>2014-06-06 18:49:23 +0200
committerJacob Erlbeck <jerlbeck@sysmocom.de>2014-06-19 17:58:14 +0200
commitbdb6dcb01741e08c7f8f2591b53c8345300a2e3a (patch)
tree905c07e993e02b88f697d2025c0f0515865b4af4
parent21726404bdf1a739a35af0fe2816e20c88339a14 (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.c341
-rw-r--r--openbsc/src/gprs/gb_proxy_vty.c160
-rw-r--r--openbsc/tests/gbproxy/gbproxy_test.c8
-rw-r--r--openbsc/tests/gbproxy/gbproxy_test.ok12
5 files changed, 481 insertions, 45 deletions
diff --git a/openbsc/include/openbsc/gb_proxy.h b/openbsc/include/openbsc/gb_proxy.h
index 2ecd49f71..b26dd2a5c 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 bead954e5..3f5d0ba29 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,292 @@ 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);
+
+#ifdef ENABLE_SAME_IMSI_OPTIMIZATION
+ /* Optimization: Check whether the binary representation of the
+ * IMSI didn't change. If it didn't change, assume patching is enabled.
+ */
+ /* TODO: This duplicates code and only avoids the IMSI lookup and the
+ * realloc/memcpy() below. It's probably not worth the effort. */
+ if (tlli_info &&
+ imsi_len == tlli_info->mi_data_len &&
+ memcmp(imsi, tlli_info->mi_data, imsi_len) == 0) {
+ /* Move the entry to the start of the list */
+ llist_del(&tlli_info->list);
+ llist_add(&tlli_info->list, &state->enabled_tllis);
+ tlli_info->timestamp = time(NULL);
+ return;
+ }
+#endif
+
+ /* 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;
+}
+
/* patch RA identifier in place, update peer accordingly */
static void gbprox_patch_raid(uint8_t *raid_enc, struct gbprox_peer *peer,
int to_bss, const char *log_text)
@@ -697,6 +1002,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,
enum gbproxy_patch_mode patch_mode, int to_bss,
+ uint32_t tlli,
int *len_change)
{
struct gsm48_hdr *g48h;
@@ -749,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;
};
@@ -760,7 +1074,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,
- enum gbproxy_patch_mode patch_mode, int to_bss)
+ enum gbproxy_patch_mode patch_mode, int to_bss,
+ struct bssgp_ud_hdr *budh,
+ struct tlv_parsed *bssgp_tp)
{
struct gprs_llc_hdr_parsed ghp = {0};
int rc;
@@ -770,6 +1086,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);
@@ -789,6 +1106,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;
@@ -808,7 +1131,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, patch_mode, to_bss,
- &len_change);
+ tlli, &len_change);
if (rc > 0) {
llc_len += len_change;
@@ -854,10 +1177,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;
enum gbproxy_patch_mode patch_mode;
@@ -866,7 +1188,6 @@ 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;
patch_mode = gbcfg.patch_mode;
if (patch_mode == GBPROX_PATCH_DEFAULT)
@@ -874,6 +1195,8 @@ static void gbprox_patch_bssgp_message(struct msgb *msg,
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 {
@@ -903,11 +1226,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");
@@ -921,7 +1239,8 @@ static void gbprox_patch_bssgp_message(struct msgb *msg,
patch_mode >= 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, patch_mode, to_bss);
+ gbprox_patch_llc(msg, llc, llc_len, peer, patch_mode,
+ 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 8d3ff25ce..94cc35e08 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,49 +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;
+ 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)",
@@ -230,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 52835c54e..9bd2941e3 100644
--- a/openbsc/tests/gbproxy/gbproxy_test.c
+++ b/openbsc/tests/gbproxy/gbproxy_test.c
@@ -841,6 +841,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;
@@ -850,6 +851,13 @@ static void test_gbproxy_ra_patching()
gbcfg.core_apn = talloc_zero_size(NULL, 100);
gbcfg.core_apn_size = gbprox_str_to_apn(gbcfg.core_apn, "foo.bar", 100);
+ 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);
+ }
+
sgsn_peer.sin_family = AF_INET;
sgsn_peer.sin_port = htons(32000);
sgsn_peer.sin_addr.s_addr = htonl(REMOTE_SGSN_ADDR);
diff --git a/openbsc/tests/gbproxy/gbproxy_test.ok b/openbsc/tests/gbproxy/gbproxy_test.ok
index d5c3c3f9a..f56749ce9 100644
--- a/openbsc/tests/gbproxy/gbproxy_test.ok
+++ b/openbsc/tests/gbproxy/gbproxy_test.ok
@@ -1811,13 +1811,13 @@ PROCESSING UNITDATA from 0x01020304:1111
CALLBACK, event 0, msg length 76, bvci 0x1002
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
-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
+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
-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
+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 (UNITDATA) = 75
+result (UNITDATA) = 80
PROCESSING BVC_RESET_ACK from 0x05060708:32000
00 00 00 00 23 04 82 1e ee
@@ -1849,6 +1849,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