From 1f8276e5885f070ec1b0d74c28fc9464bbfe5f8a Mon Sep 17 00:00:00 2001 From: Holger Hans Peter Freyther Date: Tue, 1 Jan 2013 11:25:09 +0100 Subject: [PATCH] nat: Introduce a global IMSI barr list using red-black trees --- openbsc/include/openbsc/bsc_nat.h | 15 +++ openbsc/src/osmo-bsc_nat/bsc_nat_filter.c | 124 +++++++++++++++++++--- openbsc/src/osmo-bsc_nat/bsc_nat_vty.c | 67 +++++++++++- openbsc/tests/bsc-nat/Makefile.am | 2 +- openbsc/tests/bsc-nat/barr.cfg | 12 +++ openbsc/tests/bsc-nat/barr_dup.cfg | 2 + openbsc/tests/bsc-nat/bsc_nat_test.c | 67 ++++++++++++ openbsc/tests/bsc-nat/bsc_nat_test.ok | 13 +++ openbsc/tests/testsuite.at | 2 + 9 files changed, 288 insertions(+), 16 deletions(-) create mode 100644 openbsc/tests/bsc-nat/barr.cfg create mode 100644 openbsc/tests/bsc-nat/barr_dup.cfg diff --git a/openbsc/include/openbsc/bsc_nat.h b/openbsc/include/openbsc/bsc_nat.h index 71cd121f4..1698fa47f 100644 --- a/openbsc/include/openbsc/bsc_nat.h +++ b/openbsc/include/openbsc/bsc_nat.h @@ -287,6 +287,10 @@ struct bsc_nat { /* filter */ char *acc_lst_name; + /* Barring of subscribers with a rb tree */ + struct rb_root imsi_black_list; + char *imsi_black_list_fn; + /* number rewriting */ char *num_rewr_name; struct llist_head num_rewr; @@ -448,6 +452,17 @@ struct bsc_nat_num_rewr_entry { void bsc_nat_num_rewr_entry_adapt(void *ctx, struct llist_head *head, const struct osmo_config_list *); +struct bsc_nat_barr_entry { + struct rb_node node; + + char *imsi; + int cm_reject_cause; + int lu_reject_cause; +}; + +int bsc_nat_barr_adapt(void *ctx, struct rb_root *rbtree, const struct osmo_config_list *); +int bsc_nat_barr_find(struct rb_root *root, const char *imsi, int *cm, int *lu); + struct ctrl_handle *bsc_nat_controlif_setup(struct bsc_nat *nat, int port); void bsc_nat_ctrl_del_pending(struct bsc_cmd_list *pending); int bsc_nat_handle_ctrlif_msg(struct bsc_connection *bsc, struct msgb *msg); diff --git a/openbsc/src/osmo-bsc_nat/bsc_nat_filter.c b/openbsc/src/osmo-bsc_nat/bsc_nat_filter.c index e3e63d13d..5ab3a971d 100644 --- a/openbsc/src/osmo-bsc_nat/bsc_nat_filter.c +++ b/openbsc/src/osmo-bsc_nat/bsc_nat_filter.c @@ -37,6 +37,91 @@ #include +int bsc_nat_barr_find(struct rb_root *root, const char *imsi, int *cm, int *lu) +{ + struct bsc_nat_barr_entry *n; + n = rb_entry(root->rb_node, struct bsc_nat_barr_entry, node); + + while (n) { + int rc = strcmp(imsi, n->imsi); + if (rc == 0) { + *cm = n->cm_reject_cause; + *lu = n->lu_reject_cause; + return 1; + } + + n = rb_entry( + (rc < 0) ? n->node.rb_left : n->node.rb_right, + struct bsc_nat_barr_entry, node); + }; + + return 0; +} + +static int insert_barr_node(struct bsc_nat_barr_entry *entry, struct rb_root *root) +{ + struct rb_node **new = &root->rb_node, *parent = NULL; + + while (*new) { + int rc; + struct bsc_nat_barr_entry *this; + this = rb_entry(*new, struct bsc_nat_barr_entry, node); + parent = *new; + + rc = strcmp(entry->imsi, this->imsi); + if (rc < 0) + new = &((*new)->rb_left); + else if (rc > 0) + new = &((*new)->rb_right); + else { + LOGP(DNAT, LOGL_ERROR, + "Duplicate entry for IMSI(%s)\n", entry->imsi); + talloc_free(entry); + return -1; + } + } + + rb_link_node(&entry->node, parent, new); + rb_insert_color(&entry->node, root); + return 0; +} + +int bsc_nat_barr_adapt(void *ctx, struct rb_root *root, + const struct osmo_config_list *list) +{ + struct osmo_config_entry *cfg_entry; + int err = 0; + + /* free the old data */ + while (!RB_EMPTY_ROOT(root)) { + struct rb_node *node = rb_first(root); + rb_erase(node, root); + talloc_free(node); + } + + if (!list) + return 0; + + /* now adapt the new list */ + llist_for_each_entry(cfg_entry, &list->entry, list) { + struct bsc_nat_barr_entry *entry; + entry = talloc_zero(ctx, struct bsc_nat_barr_entry); + if (!entry) { + LOGP(DNAT, LOGL_ERROR, + "Allocation of the barr entry failed.\n"); + continue; + } + + entry->imsi = talloc_strdup(entry, cfg_entry->mcc); + entry->cm_reject_cause = atoi(cfg_entry->mnc); + entry->lu_reject_cause = atoi(cfg_entry->option); + err |= insert_barr_node(entry, root); + } + + return err; +} + + static int lst_check_deny(struct bsc_nat_acc_lst *lst, const char *mi_string) { struct bsc_nat_acc_lst_entry *entry; @@ -52,43 +137,56 @@ static int lst_check_deny(struct bsc_nat_acc_lst *lst, const char *mi_string) } /* apply white/black list */ -static int auth_imsi(struct bsc_connection *bsc, const char *mi_string, +static int auth_imsi(struct bsc_connection *bsc, const char *imsi, struct bsc_nat_reject_cause *cause) { /* * Now apply blacklist/whitelist of the BSC and the NAT. - * 1.) Allow directly if the IMSI is allowed at the BSC - * 2.) Reject if the IMSI is not allowed at the BSC - * 3.) Reject if the IMSI not allowed at the global level. - * 4.) Allow directly if the IMSI is allowed at the global level + * 1.) Check the global IMSI barr list + * 2.) Allow directly if the IMSI is allowed at the BSC + * 3.) Reject if the IMSI is not allowed at the BSC + * 4.) Reject if the IMSI not allowed at the global level. + * 5.) Allow directly if the IMSI is allowed at the global level */ + int cm, lu; struct bsc_nat_acc_lst *nat_lst = NULL; struct bsc_nat_acc_lst *bsc_lst = NULL; + /* 1. global check for barred imsis */ + if (bsc_nat_barr_find(&bsc->nat->imsi_black_list, imsi, &cm, &lu)) { + cause->cm_reject_cause = cm; + cause->lu_reject_cause = lu; + LOGP(DNAT, LOGL_DEBUG, + "Blocking subscriber IMSI %s with CM: %d LU: %d\n", + imsi, cm, lu); + return -1; + } + + bsc_lst = bsc_nat_acc_lst_find(bsc->nat, bsc->cfg->acc_lst_name); nat_lst = bsc_nat_acc_lst_find(bsc->nat, bsc->nat->acc_lst_name); if (bsc_lst) { - /* 1. BSC allow */ - if (bsc_nat_lst_check_allow(bsc_lst, mi_string) == 0) + /* 2. BSC allow */ + if (bsc_nat_lst_check_allow(bsc_lst, imsi) == 0) return 1; - /* 2. BSC deny */ - if (lst_check_deny(bsc_lst, mi_string) == 0) { + /* 3. BSC deny */ + if (lst_check_deny(bsc_lst, imsi) == 0) { LOGP(DNAT, LOGL_ERROR, - "Filtering %s by imsi_deny on bsc nr: %d.\n", mi_string, bsc->cfg->nr); + "Filtering %s by imsi_deny on bsc nr: %d.\n", imsi, bsc->cfg->nr); rate_ctr_inc(&bsc_lst->stats->ctr[ACC_LIST_BSC_FILTER]); return -2; } } - /* 3. NAT deny */ + /* 4. NAT deny */ if (nat_lst) { - if (lst_check_deny(nat_lst, mi_string) == 0) { + if (lst_check_deny(nat_lst, imsi) == 0) { LOGP(DNAT, LOGL_ERROR, - "Filtering %s by nat imsi_deny on bsc nr: %d.\n", mi_string, bsc->cfg->nr); + "Filtering %s by nat imsi_deny on bsc nr: %d.\n", imsi, bsc->cfg->nr); rate_ctr_inc(&nat_lst->stats->ctr[ACC_LIST_NAT_FILTER]); return -3; } diff --git a/openbsc/src/osmo-bsc_nat/bsc_nat_vty.c b/openbsc/src/osmo-bsc_nat/bsc_nat_vty.c index 220e9606c..7bbc89059 100644 --- a/openbsc/src/osmo-bsc_nat/bsc_nat_vty.c +++ b/openbsc/src/osmo-bsc_nat/bsc_nat_vty.c @@ -1,6 +1,6 @@ /* OpenBSC NAT interface to quagga VTY */ -/* (C) 2010-2011 by Holger Hans Peter Freyther - * (C) 2010-2011 by On-Waves +/* (C) 2010-2012 by Holger Hans Peter Freyther + * (C) 2010-2012 by On-Waves * All Rights Reserved * * This program is free software; you can redistribute it and/or modify @@ -112,6 +112,9 @@ static int config_write_nat(struct vty *vty) vty_out(vty, " ip-dscp %d%s", _nat->bsc_ip_dscp, VTY_NEWLINE); if (_nat->acc_lst_name) vty_out(vty, " access-list-name %s%s", _nat->acc_lst_name, VTY_NEWLINE); + if (_nat->imsi_black_list_fn) + vty_out(vty, " imsi-black-list-file-name %s%s", + _nat->imsi_black_list_fn, VTY_NEWLINE); if (_nat->ussd_lst_name) vty_out(vty, " ussd-list-name %s%s", _nat->ussd_lst_name, VTY_NEWLINE); if (_nat->ussd_query) @@ -486,6 +489,42 @@ DEFUN(cfg_nat_no_acc_lst_name, return CMD_SUCCESS; } +DEFUN(cfg_nat_imsi_black_list_fn, + cfg_nat_imsi_black_list_fn_cmd, + "imsi-black-list-file-name NAME", + "IMSI black listing\n" "Filename IMSI and reject-cause\n") +{ + + bsc_replace_string(_nat, &_nat->imsi_black_list_fn, argv[0]); + if (_nat->imsi_black_list_fn) { + int rc; + struct osmo_config_list *rewr = NULL; + rewr = osmo_config_list_parse(_nat, _nat->imsi_black_list_fn); + rc = bsc_nat_barr_adapt(_nat, &_nat->imsi_black_list, rewr); + if (rc != 0) { + vty_out(vty, "%%There was an error parsing the list." + " Please see the error log.%s", VTY_NEWLINE); + return CMD_WARNING; + } + + return CMD_SUCCESS; + } + + bsc_nat_barr_adapt(_nat, &_nat->imsi_black_list, NULL); + return CMD_SUCCESS; +} + +DEFUN(cfg_nat_no_imsi_black_list_fn, + cfg_nat_no_imsi_black_list_fn_cmd, + "no imsi-black-list-file-name", + NO_STR "Remove the imsi-black-list\n") +{ + talloc_free(_nat->imsi_black_list_fn); + _nat->imsi_black_list_fn = NULL; + bsc_nat_barr_adapt(_nat, &_nat->imsi_black_list, NULL); + return CMD_SUCCESS; +} + static int replace_rules(struct bsc_nat *nat, char **name, struct llist_head *head, const char *file) { @@ -774,6 +813,27 @@ DEFUN(show_acc_lst, return CMD_SUCCESS; } +DEFUN(show_bar_lst, + show_bar_lst_cmd, + "show imsi-black-list", + SHOW_STR "IMSIs barred from the network\n") +{ + struct rb_node *node; + + vty_out(vty, "IMSIs barred from the network:%s", VTY_NEWLINE); + + for (node = rb_first(&_nat->imsi_black_list); node; node = rb_next(node)) { + struct bsc_nat_barr_entry *entry; + entry = rb_entry(node, struct bsc_nat_barr_entry, node); + + vty_out(vty, " IMSI(%s) CM-Reject-Cause(%d) LU-Reject-Cause(%d)%s", + entry->imsi, entry->cm_reject_cause, entry->lu_reject_cause, + VTY_NEWLINE); + } + + return CMD_SUCCESS; +} + DEFUN(cfg_bsc_acc_lst_name, cfg_bsc_acc_lst_name_cmd, @@ -999,6 +1059,7 @@ int bsc_nat_vty_init(struct bsc_nat *nat) install_element_ve(&test_regex_cmd); install_element_ve(&show_bsc_mgcp_cmd); install_element_ve(&show_acc_lst_cmd); + install_element_ve(&show_bar_lst_cmd); install_element(ENABLE_NODE, &set_last_endp_cmd); install_element(ENABLE_NODE, &block_new_conn_cmd); @@ -1019,6 +1080,8 @@ int bsc_nat_vty_init(struct bsc_nat *nat) install_element(NAT_NODE, &cfg_nat_bsc_ip_tos_cmd); install_element(NAT_NODE, &cfg_nat_acc_lst_name_cmd); install_element(NAT_NODE, &cfg_nat_no_acc_lst_name_cmd); + install_element(NAT_NODE, &cfg_nat_imsi_black_list_fn_cmd); + install_element(NAT_NODE, &cfg_nat_no_imsi_black_list_fn_cmd); install_element(NAT_NODE, &cfg_nat_ussd_lst_name_cmd); install_element(NAT_NODE, &cfg_nat_ussd_query_cmd); install_element(NAT_NODE, &cfg_nat_ussd_token_cmd); diff --git a/openbsc/tests/bsc-nat/Makefile.am b/openbsc/tests/bsc-nat/Makefile.am index bd3d33f9d..e284851a1 100644 --- a/openbsc/tests/bsc-nat/Makefile.am +++ b/openbsc/tests/bsc-nat/Makefile.am @@ -2,7 +2,7 @@ INCLUDES = $(all_includes) -I$(top_srcdir)/include AM_CFLAGS=-Wall -ggdb3 $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOSCCP_CFLAGS) $(LIBOSMOABIS_CFLAGS) $(COVERAGE_CFLAGS) AM_LDFLAGS = $(COVERAGE_LDFLAGS) -EXTRA_DIST = bsc_nat_test.ok bsc_data.c +EXTRA_DIST = bsc_nat_test.ok bsc_data.c barr.cfg barr_dup.cfg noinst_PROGRAMS = bsc_nat_test diff --git a/openbsc/tests/bsc-nat/barr.cfg b/openbsc/tests/bsc-nat/barr.cfg new file mode 100644 index 000000000..a9a4a2b31 --- /dev/null +++ b/openbsc/tests/bsc-nat/barr.cfg @@ -0,0 +1,12 @@ +12123124:3:2: +12123123:3:1: +12123128:3:6: +12123125:3:3: +12123127:3:5: +12123126:3:4: +12123120:3:4: +12123119:3:4: +12123118:3:4: +12123117:3:4: +12123116:3:4: +12123115:3:4: diff --git a/openbsc/tests/bsc-nat/barr_dup.cfg b/openbsc/tests/bsc-nat/barr_dup.cfg new file mode 100644 index 000000000..ea94631ce --- /dev/null +++ b/openbsc/tests/bsc-nat/barr_dup.cfg @@ -0,0 +1,2 @@ +12123124:3:2: +12123124:3:2: diff --git a/openbsc/tests/bsc-nat/bsc_nat_test.c b/openbsc/tests/bsc-nat/bsc_nat_test.c index 59d92bd07..66b4ff5a7 100644 --- a/openbsc/tests/bsc-nat/bsc_nat_test.c +++ b/openbsc/tests/bsc-nat/bsc_nat_test.c @@ -1153,6 +1153,72 @@ static void test_sms_number_rewrite(void) msgb_free(out); } +static void test_barr_list_parsing(void) +{ + int rc; + int cm, lu; + struct rb_node *node; + struct rb_root root = RB_ROOT; + struct osmo_config_list *lst = osmo_config_list_parse(NULL, "barr.cfg"); + if (lst == NULL) + abort(); + + rc = bsc_nat_barr_adapt(NULL, &root, lst); + if (rc != 0) + abort(); + talloc_free(lst); + + + for (node = rb_first(&root); node; node = rb_next(node)) { + struct bsc_nat_barr_entry *entry; + entry = rb_entry(node, struct bsc_nat_barr_entry, node); + printf("IMSI: %s CM: %d LU: %d\n", entry->imsi, + entry->cm_reject_cause, entry->lu_reject_cause); + } + + /* do the look up now.. */ + rc = bsc_nat_barr_find(&root, "12123119", &cm, &lu); + if (!rc) { + printf("Failed to find the IMSI.\n"); + abort(); + } + + if (cm != 3 || lu != 4) { + printf("Found CM(%d) and LU(%d)\n", cm, lu); + abort(); + } + + /* empty and check that it is empty */ + bsc_nat_barr_adapt(NULL, &root, NULL); + if (!RB_EMPTY_ROOT(&root)) { + printf("Failed to empty the list.\n"); + abort(); + } + + /* check that dup results in an error */ + lst = osmo_config_list_parse(NULL, "barr_dup.cfg"); + if (lst == NULL) { + printf("Failed to parse list with dups\n"); + abort(); + } + + rc = bsc_nat_barr_adapt(NULL, &root, lst); + if (rc != -1) { + printf("It should have failed due dup\n"); + abort(); + } + talloc_free(lst); + + /* dump for reference */ + for (node = rb_first(&root); node; node = rb_next(node)) { + struct bsc_nat_barr_entry *entry; + entry = rb_entry(node, struct bsc_nat_barr_entry, node); + printf("IMSI: %s CM: %d LU: %d\n", entry->imsi, + entry->cm_reject_cause, entry->lu_reject_cause); + + } +} + int main(int argc, char **argv) { sccp_set_log_area(DSCCP); @@ -1171,6 +1237,7 @@ int main(int argc, char **argv) test_sms_smsc_rewrite(); test_sms_number_rewrite(); test_mgcp_allocations(); + test_barr_list_parsing(); printf("Testing execution completed.\n"); return 0; diff --git a/openbsc/tests/bsc-nat/bsc_nat_test.ok b/openbsc/tests/bsc-nat/bsc_nat_test.ok index db37ffae6..cbedc85af 100644 --- a/openbsc/tests/bsc-nat/bsc_nat_test.ok +++ b/openbsc/tests/bsc-nat/bsc_nat_test.ok @@ -22,4 +22,17 @@ Testing SMSC rewriting. Attempting to only rewrite the HDR Attempting to change nothing. Testing SMS TP-DA rewriting. +IMSI: 12123115 CM: 3 LU: 4 +IMSI: 12123116 CM: 3 LU: 4 +IMSI: 12123117 CM: 3 LU: 4 +IMSI: 12123118 CM: 3 LU: 4 +IMSI: 12123119 CM: 3 LU: 4 +IMSI: 12123120 CM: 3 LU: 4 +IMSI: 12123123 CM: 3 LU: 1 +IMSI: 12123124 CM: 3 LU: 2 +IMSI: 12123125 CM: 3 LU: 3 +IMSI: 12123126 CM: 3 LU: 4 +IMSI: 12123127 CM: 3 LU: 5 +IMSI: 12123128 CM: 3 LU: 6 +IMSI: 12123124 CM: 3 LU: 2 Testing execution completed. diff --git a/openbsc/tests/testsuite.at b/openbsc/tests/testsuite.at index 4c5de8d6a..e649f039e 100644 --- a/openbsc/tests/testsuite.at +++ b/openbsc/tests/testsuite.at @@ -34,6 +34,8 @@ AT_CLEANUP AT_SETUP([bsc-nat]) AT_KEYWORDS([bsc-nat]) AT_CHECK([test "$enable_nat_test" != no || exit 77]) +cp $abs_srcdir/bsc-nat/barr.cfg . +cp $abs_srcdir/bsc-nat/barr_dup.cfg . cat $abs_srcdir/bsc-nat/bsc_nat_test.ok > expout AT_CHECK([$abs_top_builddir/tests/bsc-nat/bsc_nat_test], [], [expout], [ignore]) AT_CLEANUP