diff options
author | Holger Hans Peter Freyther <zecke@selfish.org> | 2013-01-07 15:03:29 +0100 |
---|---|---|
committer | Holger Hans Peter Freyther <zecke@selfish.org> | 2013-01-07 15:03:29 +0100 |
commit | ea46b77f3ed5ab4fbf8bbfee27525f9ae83253fe (patch) | |
tree | 33c7cdb41c0a2c2c137d0571c7f5bfd43aa4f21e | |
parent | b9b828b1e5f512bb1852a5ca0b2a1ccd254c3f45 (diff) | |
parent | aa191adce6447e64538468001dd168572e5055eb (diff) |
Merge remote-tracking branch 'origin/zecke/barr-subscribers'
Allow to merge barr certain subscribers with a given error code
-rw-r--r-- | openbsc/include/openbsc/bsc_nat.h | 26 | ||||
-rw-r--r-- | openbsc/src/osmo-bsc_nat/Makefile.am | 3 | ||||
-rw-r--r-- | openbsc/src/osmo-bsc_nat/bsc_nat.c | 32 | ||||
-rw-r--r-- | openbsc/src/osmo-bsc_nat/bsc_nat_filter.c | 435 | ||||
-rw-r--r-- | openbsc/src/osmo-bsc_nat/bsc_nat_rewrite.c | 628 | ||||
-rw-r--r-- | openbsc/src/osmo-bsc_nat/bsc_nat_utils.c | 870 | ||||
-rw-r--r-- | openbsc/src/osmo-bsc_nat/bsc_nat_vty.c | 67 | ||||
-rw-r--r-- | openbsc/tests/bsc-nat/Makefile.am | 4 | ||||
-rw-r--r-- | openbsc/tests/bsc-nat/barr.cfg | 12 | ||||
-rw-r--r-- | openbsc/tests/bsc-nat/barr_dup.cfg | 2 | ||||
-rw-r--r-- | openbsc/tests/bsc-nat/bsc_nat_test.c | 82 | ||||
-rw-r--r-- | openbsc/tests/bsc-nat/bsc_nat_test.ok | 13 | ||||
-rw-r--r-- | openbsc/tests/testsuite.at | 2 |
13 files changed, 1283 insertions, 893 deletions
diff --git a/openbsc/include/openbsc/bsc_nat.h b/openbsc/include/openbsc/bsc_nat.h index 7e59c6318..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; @@ -327,6 +331,11 @@ struct bsc_nat_ussd_con { struct osmo_timer_list auth_timeout; }; +struct bsc_nat_reject_cause { + int lu_reject_cause; + int cm_reject_cause; +}; + /* create and init the structures */ struct bsc_config *bsc_config_alloc(struct bsc_nat *nat, const char *token); struct bsc_config *bsc_config_num(struct bsc_nat *nat, int num); @@ -360,9 +369,11 @@ int bsc_nat_find_paging(struct msgb *msg, const uint8_t **,int *len); * Content filtering. */ int bsc_nat_filter_sccp_cr(struct bsc_connection *bsc, struct msgb *msg, - struct bsc_nat_parsed *, int *con_type, char **imsi); + struct bsc_nat_parsed *, int *con_type, char **imsi, + struct bsc_nat_reject_cause *cause); int bsc_nat_filter_dt(struct bsc_connection *bsc, struct msgb *msg, - struct sccp_connections *con, struct bsc_nat_parsed *parsed); + struct sccp_connections *con, struct bsc_nat_parsed *parsed, + struct bsc_nat_reject_cause *cause); /** * SCCP patching and handling @@ -441,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/Makefile.am b/openbsc/src/osmo-bsc_nat/Makefile.am index d5b06da6a..d1a9bd57f 100644 --- a/openbsc/src/osmo-bsc_nat/Makefile.am +++ b/openbsc/src/osmo-bsc_nat/Makefile.am @@ -6,7 +6,8 @@ bin_PROGRAMS = osmo-bsc_nat osmo_bsc_nat_SOURCES = bsc_filter.c bsc_mgcp_utils.c bsc_nat.c bsc_nat_utils.c \ - bsc_nat_vty.c bsc_sccp.c bsc_ussd.c bsc_nat_ctrl.c + bsc_nat_vty.c bsc_sccp.c bsc_ussd.c bsc_nat_ctrl.c \ + bsc_nat_rewrite.c bsc_nat_filter.c osmo_bsc_nat_LDADD = $(top_builddir)/src/libcommon/libcommon.a \ $(top_builddir)/src/libmgcp/libmgcp.a \ $(top_builddir)/src/libbsc/libbsc.a \ diff --git a/openbsc/src/osmo-bsc_nat/bsc_nat.c b/openbsc/src/osmo-bsc_nat/bsc_nat.c index 466dc72dc..d5a3a1f3f 100644 --- a/openbsc/src/osmo-bsc_nat/bsc_nat.c +++ b/openbsc/src/osmo-bsc_nat/bsc_nat.c @@ -1,8 +1,8 @@ /* BSC Multiplexer/NAT */ /* - * (C) 2010-2011 by Holger Hans Peter Freyther <zecke@selfish.org> - * (C) 2010-2011 by On-Waves + * (C) 2010-2012 by Holger Hans Peter Freyther <zecke@selfish.org> + * (C) 2010-2012 by On-Waves * (C) 2009 by Harald Welte <laforge@gnumonks.org> * All Rights Reserved * @@ -416,7 +416,9 @@ static void bsc_stat_reject(int filter, struct bsc_connection *bsc, int normal) * 2.) Give up on the BSC side * 2.1) Depending on the con type reject the service, or just close it */ -static void bsc_send_con_release(struct bsc_connection *bsc, struct sccp_connections *con) +static void bsc_send_con_release(struct bsc_connection *bsc, + struct sccp_connections *con, + struct bsc_nat_reject_cause *cause) { struct msgb *rlsd; /* 1. release the network */ @@ -434,7 +436,7 @@ static void bsc_send_con_release(struct bsc_connection *bsc, struct sccp_connect /* 2. release the BSC side */ if (con->con_type == NAT_CON_TYPE_LU) { struct msgb *payload, *udt; - payload = gsm48_create_loc_upd_rej(GSM48_REJECT_PLMN_NOT_ALLOWED); + payload = gsm48_create_loc_upd_rej(cause->lu_reject_cause); if (payload) { gsm0808_prepend_dtap_header(payload, 0); @@ -465,15 +467,16 @@ static void bsc_send_con_release(struct bsc_connection *bsc, struct sccp_connect } static void bsc_send_con_refuse(struct bsc_connection *bsc, - struct bsc_nat_parsed *parsed, int con_type) + struct bsc_nat_parsed *parsed, int con_type, + struct bsc_nat_reject_cause *cause) { struct msgb *payload; struct msgb *refuse; if (con_type == NAT_CON_TYPE_LU) - payload = gsm48_create_loc_upd_rej(GSM48_REJECT_PLMN_NOT_ALLOWED); - else if (con_type == NAT_CON_TYPE_CM_SERV_REQ) - payload = gsm48_create_mm_serv_rej(GSM48_REJECT_PLMN_NOT_ALLOWED); + payload = gsm48_create_loc_upd_rej(cause->lu_reject_cause); + else if (con_type == NAT_CON_TYPE_CM_SERV_REQ || con_type == NAT_CON_TYPE_SSA) + payload = gsm48_create_mm_serv_rej(cause->cm_reject_cause); else { LOGP(DNAT, LOGL_ERROR, "Unknown connection type: %d\n", con_type); payload = NULL; @@ -980,6 +983,7 @@ static int forward_sccp_to_msc(struct bsc_connection *bsc, struct msgb *msg) struct bsc_connection *con_bsc = NULL; int con_type; struct bsc_nat_parsed *parsed; + struct bsc_nat_reject_cause cause; /* Parse and filter messages */ parsed = bsc_nat_parse(msg); @@ -1010,7 +1014,9 @@ static int forward_sccp_to_msc(struct bsc_connection *bsc, struct msgb *msg) struct sccp_connections *con; switch (parsed->sccp_type) { case SCCP_MSG_TYPE_CR: - filter = bsc_nat_filter_sccp_cr(bsc, msg, parsed, &con_type, &imsi); + memset(&cause, 0, sizeof(cause)); + filter = bsc_nat_filter_sccp_cr(bsc, msg, parsed, + &con_type, &imsi, &cause); if (filter < 0) { bsc_stat_reject(filter, bsc, 0); goto exit3; @@ -1038,10 +1044,12 @@ static int forward_sccp_to_msc(struct bsc_connection *bsc, struct msgb *msg) if (con) { /* only filter non local connections */ if (!con->con_local) { - filter = bsc_nat_filter_dt(bsc, msg, con, parsed); + memset(&cause, 0, sizeof(cause)); + filter = bsc_nat_filter_dt(bsc, msg, + con, parsed, &cause); if (filter < 0) { bsc_stat_reject(filter, bsc, 1); - bsc_send_con_release(bsc, con); + bsc_send_con_release(bsc, con, &cause); con = NULL; goto exit2; } @@ -1156,7 +1164,7 @@ exit3: /* send a SCCP Connection Refused */ if (imsi) talloc_free(imsi); - bsc_send_con_refuse(bsc, parsed, con_type); + bsc_send_con_refuse(bsc, parsed, con_type, &cause); talloc_free(parsed); msgb_free(msg); return -1; diff --git a/openbsc/src/osmo-bsc_nat/bsc_nat_filter.c b/openbsc/src/osmo-bsc_nat/bsc_nat_filter.c new file mode 100644 index 000000000..5ab3a971d --- /dev/null +++ b/openbsc/src/osmo-bsc_nat/bsc_nat_filter.c @@ -0,0 +1,435 @@ +/* + * Access filtering + */ +/* + * (C) 2010-2012 by Holger Hans Peter Freyther <zecke@selfish.org> + * (C) 2010-2012 by On-Waves + * 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 <openbsc/bsc_nat.h> +#include <openbsc/bsc_nat_sccp.h> +#include <openbsc/bsc_msc.h> +#include <openbsc/gsm_data.h> +#include <openbsc/debug.h> +#include <openbsc/ipaccess.h> + +#include <osmocom/core/linuxlist.h> +#include <osmocom/core/talloc.h> +#include <osmocom/gsm/gsm0808.h> + +#include <osmocom/gsm/protocol/gsm_08_08.h> +#include <osmocom/gsm/protocol/gsm_04_11.h> + +#include <osmocom/sccp/sccp.h> + +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; + + llist_for_each_entry(entry, &lst->fltr_list, list) { + if (!entry->imsi_deny) + continue; + if (regexec(&entry->imsi_deny_re, mi_string, 0, NULL, 0) == 0) + return 0; + } + + return 1; +} + +/* apply white/black list */ +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.) 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) { + /* 2. BSC allow */ + if (bsc_nat_lst_check_allow(bsc_lst, imsi) == 0) + return 1; + + /* 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", imsi, bsc->cfg->nr); + rate_ctr_inc(&bsc_lst->stats->ctr[ACC_LIST_BSC_FILTER]); + return -2; + } + + } + + /* 4. NAT deny */ + if (nat_lst) { + if (lst_check_deny(nat_lst, imsi) == 0) { + LOGP(DNAT, LOGL_ERROR, + "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; + } + } + + return 1; +} + +static int _cr_check_loc_upd(struct bsc_connection *bsc, + uint8_t *data, unsigned int length, + char **imsi) +{ + uint8_t mi_type; + struct gsm48_loc_upd_req *lu; + char mi_string[GSM48_MI_SIZE]; + + if (length < sizeof(*lu)) { + LOGP(DNAT, LOGL_ERROR, + "LU does not fit. Length is %d \n", length); + return -1; + } + + lu = (struct gsm48_loc_upd_req *) data; + mi_type = lu->mi[0] & GSM_MI_TYPE_MASK; + + /* + * We can only deal with the IMSI. This will fail for a phone that + * will send the TMSI of a previous network to us. + */ + if (mi_type != GSM_MI_TYPE_IMSI) + return 0; + + gsm48_mi_to_string(mi_string, sizeof(mi_string), lu->mi, lu->mi_len); + *imsi = talloc_strdup(bsc, mi_string); + return 1; +} + +static int _cr_check_cm_serv_req(struct bsc_connection *bsc, + uint8_t *data, unsigned int length, + int *con_type, char **imsi) +{ + static const uint32_t classmark_offset = + offsetof(struct gsm48_service_request, classmark); + + char mi_string[GSM48_MI_SIZE]; + uint8_t mi_type; + int rc; + struct gsm48_service_request *req; + + /* unfortunately in Phase1 the classmark2 length is variable */ + + if (length < sizeof(*req)) { + LOGP(DNAT, LOGL_ERROR, + "CM Serv Req does not fit. Length is %d\n", length); + return -1; + } + + req = (struct gsm48_service_request *) data; + if (req->cm_service_type == 0x8) + *con_type = NAT_CON_TYPE_SSA; + rc = gsm48_extract_mi((uint8_t *) &req->classmark, + length - classmark_offset, mi_string, &mi_type); + if (rc < 0) { + LOGP(DNAT, LOGL_ERROR, "Failed to parse the classmark2/mi. error: %d\n", rc); + return -1; + } + + /* we have to let the TMSI or such pass */ + if (mi_type != GSM_MI_TYPE_IMSI) + return 0; + + *imsi = talloc_strdup(bsc, mi_string); + return 1; +} + +static int _cr_check_pag_resp(struct bsc_connection *bsc, + uint8_t *data, unsigned int length, char **imsi) +{ + struct gsm48_pag_resp *resp; + char mi_string[GSM48_MI_SIZE]; + uint8_t mi_type; + + if (length < sizeof(*resp)) { + LOGP(DNAT, LOGL_ERROR, "PAG RESP does not fit. Length was %d.\n", length); + return -1; + } + + resp = (struct gsm48_pag_resp *) data; + if (gsm48_paging_extract_mi(resp, length, mi_string, &mi_type) < 0) { + LOGP(DNAT, LOGL_ERROR, "Failed to extract the MI.\n"); + return -1; + } + + /* we need to let it pass for now */ + if (mi_type != GSM_MI_TYPE_IMSI) + return 0; + + *imsi = talloc_strdup(bsc, mi_string); + return 1; +} + +static int _dt_check_id_resp(struct bsc_connection *bsc, + uint8_t *data, unsigned int length, + struct sccp_connections *con, + struct bsc_nat_reject_cause *cause) +{ + char mi_string[GSM48_MI_SIZE]; + uint8_t mi_type; + + if (length < 2) { + LOGP(DNAT, LOGL_ERROR, "mi does not fit.\n"); + return -1; + } + + if (data[0] < length - 1) { + LOGP(DNAT, LOGL_ERROR, "mi length too big.\n"); + return -2; + } + + mi_type = data[1] & GSM_MI_TYPE_MASK; + gsm48_mi_to_string(mi_string, sizeof(mi_string), &data[1], data[0]); + + if (mi_type != GSM_MI_TYPE_IMSI) + return 0; + + con->imsi_checked = 1; + con->imsi = talloc_strdup(con, mi_string); + return auth_imsi(bsc, mi_string, cause); +} + + +/* Filter out CR data... */ +int bsc_nat_filter_sccp_cr(struct bsc_connection *bsc, struct msgb *msg, + struct bsc_nat_parsed *parsed, int *con_type, + char **imsi, struct bsc_nat_reject_cause *cause) +{ + struct tlv_parsed tp; + struct gsm48_hdr *hdr48; + int hdr48_len; + int len, ret = 0; + uint8_t msg_type, proto; + + *con_type = NAT_CON_TYPE_NONE; + cause->cm_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED; + cause->lu_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED; + *imsi = NULL; + + if (parsed->gsm_type != BSS_MAP_MSG_COMPLETE_LAYER_3) { + LOGP(DNAT, LOGL_ERROR, + "Rejecting CR message due wrong GSM Type %d\n", parsed->gsm_type); + return -1; + } + + /* the parsed has had some basic l3 length check */ + len = msg->l3h[1]; + if (msgb_l3len(msg) - 3 < len) { + LOGP(DNAT, LOGL_ERROR, + "The CR Data has not enough space...\n"); + return -1; + } + + msg->l4h = &msg->l3h[3]; + len -= 1; + + tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l4h, len, 0, 0); + + if (!TLVP_PRESENT(&tp, GSM0808_IE_LAYER_3_INFORMATION)) { + LOGP(DNAT, LOGL_ERROR, "CR Data does not contain layer3 information.\n"); + return -1; + } + + hdr48_len = TLVP_LEN(&tp, GSM0808_IE_LAYER_3_INFORMATION); + + if (hdr48_len < sizeof(*hdr48)) { + LOGP(DNAT, LOGL_ERROR, "GSM48 header does not fit.\n"); + return -1; + } + + hdr48 = (struct gsm48_hdr *) TLVP_VAL(&tp, GSM0808_IE_LAYER_3_INFORMATION); + + proto = hdr48->proto_discr & 0x0f; + msg_type = hdr48->msg_type & 0xbf; + if (proto == GSM48_PDISC_MM && + msg_type == GSM48_MT_MM_LOC_UPD_REQUEST) { + *con_type = NAT_CON_TYPE_LU; + ret = _cr_check_loc_upd(bsc, &hdr48->data[0], + hdr48_len - sizeof(*hdr48), imsi); + } else if (proto == GSM48_PDISC_MM && + msg_type == GSM48_MT_MM_CM_SERV_REQ) { + *con_type = NAT_CON_TYPE_CM_SERV_REQ; + ret = _cr_check_cm_serv_req(bsc, &hdr48->data[0], + hdr48_len - sizeof(*hdr48), + con_type, imsi); + } else if (proto == GSM48_PDISC_RR && + msg_type == GSM48_MT_RR_PAG_RESP) { + *con_type = NAT_CON_TYPE_PAG_RESP; + ret = _cr_check_pag_resp(bsc, &hdr48->data[0], + hdr48_len - sizeof(*hdr48), imsi); + } else { + /* We only want to filter the above, let other things pass */ + *con_type = NAT_CON_TYPE_OTHER; + return 0; + } + + /* check if we are done */ + if (ret != 1) + return ret; + + /* the memory allocation failed */ + if (!*imsi) + return -1; + + /* now check the imsi */ + return auth_imsi(bsc, *imsi, cause); +} + +int bsc_nat_filter_dt(struct bsc_connection *bsc, struct msgb *msg, + struct sccp_connections *con, struct bsc_nat_parsed *parsed, + struct bsc_nat_reject_cause *cause) +{ + uint32_t len; + uint8_t msg_type, proto; + struct gsm48_hdr *hdr48; + + cause->cm_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED; + cause->lu_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED; + + if (con->imsi_checked) + return 0; + + /* only care about DTAP messages */ + if (parsed->bssap != BSSAP_MSG_DTAP) + return 0; + + hdr48 = bsc_unpack_dtap(parsed, msg, &len); + if (!hdr48) + return -1; + + proto = hdr48->proto_discr & 0x0f; + msg_type = hdr48->msg_type & 0xbf; + if (proto != GSM48_PDISC_MM || msg_type != GSM48_MT_MM_ID_RESP) + return 0; + + return _dt_check_id_resp(bsc, &hdr48->data[0], + len - sizeof(*hdr48), con, cause); +} diff --git a/openbsc/src/osmo-bsc_nat/bsc_nat_rewrite.c b/openbsc/src/osmo-bsc_nat/bsc_nat_rewrite.c new file mode 100644 index 000000000..1eed149a5 --- /dev/null +++ b/openbsc/src/osmo-bsc_nat/bsc_nat_rewrite.c @@ -0,0 +1,628 @@ +/* + * Message rewriting functionality + */ +/* + * (C) 2010-2011 by Holger Hans Peter Freyther <zecke@selfish.org> + * (C) 2010-2011 by On-Waves + * 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 <openbsc/bsc_nat.h> +#include <openbsc/bsc_nat_sccp.h> +#include <openbsc/bsc_msc.h> +#include <openbsc/gsm_data.h> +#include <openbsc/debug.h> +#include <openbsc/ipaccess.h> + +#include <osmocom/core/linuxlist.h> +#include <osmocom/core/talloc.h> +#include <osmocom/gsm/gsm0808.h> + +#include <osmocom/gsm/protocol/gsm_08_08.h> +#include <osmocom/gsm/protocol/gsm_04_11.h> + +#include <osmocom/sccp/sccp.h> + +static char *match_and_rewrite_number(void *ctx, const char *number, + const char *imsi, + struct llist_head *list) +{ + struct bsc_nat_num_rewr_entry *entry; + char *new_number = NULL; + + /* need to find a replacement and then fix it */ + llist_for_each_entry(entry, list, list) { + regmatch_t matches[2]; + + /* check the IMSI match */ + if (regexec(&entry->msisdn_reg, imsi, 0, NULL, 0) != 0) + continue; + + /* this regexp matches... */ + if (regexec(&entry->num_reg, number, 2, matches, 0) == 0 && + matches[1].rm_eo != -1) + new_number = talloc_asprintf(ctx, "%s%s", + entry->replace, + &number[matches[1].rm_so]); + if (new_number) + break; + } + + return new_number; +} + +static char *rewrite_non_international(struct bsc_nat *nat, void *ctx, const char *imsi, + struct gsm_mncc_number *called) +{ + if (llist_empty(&nat->num_rewr)) + return NULL; + + if (called->plan != 1) + return NULL; + if (called->type == 1) + return NULL; + + return match_and_rewrite_number(ctx, called->number, + imsi, &nat->num_rewr); +} + + +/** + * Rewrite non global numbers... according to rules based on the IMSI + */ +static struct msgb *rewrite_setup(struct bsc_nat *nat, struct msgb *msg, + struct bsc_nat_parsed *parsed, const char *imsi, + struct gsm48_hdr *hdr48, const uint32_t len) +{ + struct tlv_parsed tp; + unsigned int payload_len; + struct gsm_mncc_number called; + struct msgb *out; + char *new_number = NULL; + uint8_t *outptr; + const uint8_t *msgptr; + int sec_len; + + /* decode and rewrite the message */ + payload_len = len - sizeof(*hdr48); + tlv_parse(&tp, &gsm48_att_tlvdef, hdr48->data, payload_len, 0, 0); + + /* no number, well let us ignore it */ + if (!TLVP_PRESENT(&tp, GSM48_IE_CALLED_BCD)) + return NULL; + + memset(&called, 0, sizeof(called)); + gsm48_decode_called(&called, + TLVP_VAL(&tp, GSM48_IE_CALLED_BCD) - 1); + + /* check if it looks international and stop */ + new_number = rewrite_non_international(nat, msg, imsi, &called); + + if (!new_number) { + LOGP(DNAT, LOGL_DEBUG, "No IMSI match found, returning message.\n"); + return NULL; + } + + if (strlen(new_number) > sizeof(called.number)) { + LOGP(DNAT, LOGL_ERROR, "Number is too long for structure.\n"); + talloc_free(new_number); + return NULL; + } + + /* + * Need to create a new message now based on the old onew + * with a new number. We can sadly not patch this in place + * so we will need to regenerate it. + */ + + out = msgb_alloc_headroom(4096, 128, "changed-setup"); + if (!out) { + LOGP(DNAT, LOGL_ERROR, "Failed to allocate.\n"); + talloc_free(new_number); + return NULL; + } + + /* copy the header */ + outptr = msgb_put(out, sizeof(*hdr48)); + memcpy(outptr, hdr48, sizeof(*hdr48)); + + /* copy everything up to the number */ + sec_len = TLVP_VAL(&tp, GSM48_IE_CALLED_BCD) - 2 - &hdr48->data[0]; + outptr = msgb_put(out, sec_len); + memcpy(outptr, &hdr48->data[0], sec_len); + + /* create the new number */ + if (strncmp(new_number, "00", 2) == 0) { + called.type = 1; + strncpy(called.number, new_number + 2, sizeof(called.number)); + } else { + strncpy(called.number, new_number, sizeof(called.number)); + } + gsm48_encode_called(out, &called); + + /* copy thre rest */ + msgptr = TLVP_VAL(&tp, GSM48_IE_CALLED_BCD) + + TLVP_LEN(&tp, GSM48_IE_CALLED_BCD); + sec_len = payload_len - (msgptr - &hdr48->data[0]); + outptr = msgb_put(out, sec_len); + memcpy(outptr, msgptr, sec_len); + + talloc_free(new_number); + return out; +} + +/** + * Find a new SMSC address, returns an allocated string that needs to be + * freed or is NULL. + */ +static char *find_new_smsc(struct bsc_nat *nat, void *ctx, const char *imsi, + const char *smsc_addr, const char *dest_nr) +{ + struct bsc_nat_num_rewr_entry *entry; + char *new_number = NULL; + uint8_t dest_match = llist_empty(&nat->tpdest_match); + + /* We will find a new number now */ + llist_for_each_entry(entry, &nat->smsc_rewr, list) { + regmatch_t matches[2]; + + /* check the IMSI match */ + if (regexec(&entry->msisdn_reg, imsi, 0, NULL, 0) != 0) + continue; + + /* this regexp matches... */ + if (regexec(&entry->num_reg, smsc_addr, 2, matches, 0) == 0 && + matches[1].rm_eo != -1) + new_number = talloc_asprintf(ctx, "%s%s", + entry->replace, + &smsc_addr[matches[1].rm_so]); + if (new_number) + break; + } + + if (!new_number) + return NULL; + + /* + * now match the number against another list + */ + llist_for_each_entry(entry, &nat->tpdest_match, list) { + /* check the IMSI match */ + if (regexec(&entry->msisdn_reg, imsi, 0, NULL, 0) != 0) + continue; + + if (regexec(&entry->num_reg, dest_nr, 0, NULL, 0) == 0) { + dest_match = 1; + break; + } + } + + if (!dest_match) { + talloc_free(new_number); + return NULL; + } + + return new_number; +} + +/** + * Clear the TP-SRR from the TPDU header + */ +static uint8_t sms_new_tpdu_hdr(struct bsc_nat *nat, const char *imsi, + const char *dest_nr, uint8_t hdr) +{ + struct bsc_nat_num_rewr_entry *entry; + + /* We will find a new number now */ + llist_for_each_entry(entry, &nat->sms_clear_tp_srr, list) { + /* check the IMSI match */ + if (regexec(&entry->msisdn_reg, imsi, 0, NULL, 0) != 0) + continue; + if (regexec(&entry->num_reg, dest_nr, 0, NULL, 0) != 0) + continue; + + /* matched phone number and imsi */ + return hdr & ~0x20; + } + + return hdr; +} + +/** + * Check if we need to rewrite the number. For this SMS. + */ +static char *sms_new_dest_nr(struct bsc_nat *nat, void *ctx, + const char *imsi, const char *dest_nr) +{ + return match_and_rewrite_number(ctx, dest_nr, imsi, + &nat->sms_num_rewr); +} + +/** + * This is a helper for GSM 04.11 8.2.5.2 Destination address element + */ +void sms_encode_addr_element(struct msgb *out, const char *new_number, + int format, int tp_data) +{ + uint8_t new_addr_len; + uint8_t new_addr[26]; + + /* + * Copy the new number. We let libosmocore encode it, then set + * the extension followed after the length. Depending on if + * we want to write RP we will let the TLV code add the + * length for us or we need to use strlen... This is not very clear + * as of 03.40 and 04.11. + */ + new_addr_len = gsm48_encode_bcd_number(new_addr, ARRAY_SIZE(new_addr), + 1, new_number); + new_addr[1] = format; + if (tp_data) { + uint8_t *data = msgb_put(out, new_addr_len); + memcpy(data, new_addr, new_addr_len); + data[0] = strlen(new_number); + } else { + msgb_lv_put(out, new_addr_len - 1, new_addr + 1); + } +} + +static struct msgb *sms_create_new(uint8_t type, uint8_t ref, + struct gsm48_hdr *old_hdr48, + const uint8_t *orig_addr_ptr, + int orig_addr_len, const char *new_number, + const uint8_t *data_ptr, int data_len, + uint8_t tpdu_first_byte, + const int old_dest_len, const char *new_dest_nr) +{ + struct gsm48_hdr *new_hdr48; + struct msgb *out; + + /* + * We need to re-create the patched structure. This is why we have + * saved the above pointers. + */ + out = msgb_alloc_headroom(4096, 128, "changed-smsc"); + if (!out) { + LOGP(DNAT, LOGL_ERROR, "Failed to allocate.\n"); + return NULL; + } + + out->l2h = out->data; + msgb_v_put(out, GSM411_MT_RP_DATA_MO); + msgb_v_put(out, ref); + msgb_lv_put(out, orig_addr_len, orig_addr_ptr); + + sms_encode_addr_element(out, new_number, 0x91, 0); + + + /* Patch the TPDU from here on */ + + /** + * Do we need to put a new TP-Destination-Address (TP-DA) here or + * can we copy the old thing? For the TP-DA we need to find out the + * new size. + */ + if (new_dest_nr) { + uint8_t *data, *new_size; + + /* reserve the size and write the header */ + new_size = msgb_put(out, 1); + out->l3h = new_size + 1; + msgb_v_put(out, tpdu_first_byte); + msgb_v_put(out, data_ptr[1]); + + /* encode the new number and put it */ + if (strncmp(new_dest_nr, "00", 2) == 0) + sms_encode_addr_element(out, new_dest_nr + 2, 0x91, 1); + else + sms_encode_addr_element(out, new_dest_nr, 0x81, 1); + + /* Copy the rest after the TP-DS */ + data = msgb_put(out, data_len - 2 - 1 - old_dest_len); + memcpy(data, &data_ptr[2 + 1 + old_dest_len], data_len - 2 - 1 - old_dest_len); + + /* fill in the new size */ + new_size[0] = msgb_l3len(out); + } else { + msgb_v_put(out, data_len); + msgb_tv_fixed_put(out, tpdu_first_byte, data_len - 1, &data_ptr[1]); + } + + /* prepend GSM 04.08 header */ + new_hdr48 = (struct gsm48_hdr *) msgb_push(out, sizeof(*new_hdr48) + 1); + memcpy(new_hdr48, old_hdr48, sizeof(*old_hdr48)); + new_hdr48->data[0] = msgb_l2len(out); + + return out; +} + +/** + * Parse the SMS and check if it needs to be rewritten + */ +static struct msgb *rewrite_sms(struct bsc_nat *nat, struct msgb *msg, + struct bsc_nat_parsed *parsed, const char *imsi, + struct gsm48_hdr *hdr48, const uint32_t len) +{ + unsigned int payload_len; + unsigned int cp_len; + + uint8_t ref; + uint8_t orig_addr_len, *orig_addr_ptr; + uint8_t dest_addr_len, *dest_addr_ptr; + uint8_t data_len, *data_ptr; + char smsc_addr[30]; + + + uint8_t dest_len, orig_dest_len; + char _dest_nr[30]; + char *dest_nr; + char *new_dest_nr; + + char *new_number = NULL; + uint8_t tpdu_hdr; + struct msgb *out; + + payload_len = len - sizeof(*hdr48); + if (payload_len < 1) { + LOGP(DNAT, LOGL_ERROR, "SMS too short for things. %d\n", payload_len); + return NULL; + } + + cp_len = hdr48->data[0]; + if (payload_len + 1 < cp_len) { + LOGP(DNAT, LOGL_ERROR, "SMS RPDU can not fit in: %d %d\n", cp_len, payload_len); + return NULL; + } + + if (hdr48->data[1] != GSM411_MT_RP_DATA_MO) + return NULL; + + if (cp_len < 5) { + LOGP(DNAT, LOGL_ERROR, "RD-DATA can not fit in the CP len: %d\n", cp_len); + return NULL; + } + + /* RP */ + ref = hdr48->data[2]; + orig_addr_len = hdr48->data[3]; + orig_addr_ptr = &hdr48->data[4]; + + /* the +1 is for checking if the following element has some space */ + if (cp_len < 3 + orig_addr_len + 1) { + LOGP(DNAT, LOGL_ERROR, "RP-Originator addr does not fit: %d\n", orig_addr_len); + return NULL; + } + + dest_addr_len = hdr48->data[3 + orig_addr_len + 1]; + dest_addr_ptr = &hdr48->data[3 + orig_addr_len + 2]; + + if (cp_len < 3 + orig_addr_len + 1 + dest_addr_len + 1) { + LOGP(DNAT, LOGL_ERROR, "RP-Destination addr does not fit: %d\n", dest_addr_len); + return NULL; + } + gsm48_decode_bcd_number(smsc_addr, ARRAY_SIZE(smsc_addr), dest_addr_ptr - 1, 1); + + data_len = hdr48->data[3 + orig_addr_len + 1 + dest_addr_len + 1]; + data_ptr = &hdr48->data[3 + orig_addr_len + 1 + dest_addr_len + 2]; + + if (cp_len < 3 + orig_addr_len + 1 + dest_addr_len + 1 + data_len) { + LOGP(DNAT, LOGL_ERROR, "RP-Data does not fit: %d\n", data_len); + return NULL; + } + + if (data_len < 3) { + LOGP(DNAT, LOGL_ERROR, "SMS-SUBMIT is too short.\n"); + return NULL; + } + + /* TP-PDU starts here */ + if ((data_ptr[0] & 0x03) != GSM340_SMS_SUBMIT_MS2SC) + return NULL; + + /* + * look into the phone number. The length is in semi-octets, we will + * need to add the byte for the number type as well. + */ + orig_dest_len = data_ptr[2]; + dest_len = ((orig_dest_len + 1) / 2) + 1; + if (data_len < dest_len + 3 || dest_len < 2) { + LOGP(DNAT, LOGL_ERROR, "SMS-SUBMIT can not have TP-DestAddr.\n"); + return NULL; + } + + if ((data_ptr[3] & 0x80) == 0) { + LOGP(DNAT, LOGL_ERROR, "TP-DestAddr has extension. Not handled.\n"); + return NULL; + } + + if ((data_ptr[3] & 0x0F) == 0) { + LOGP(DNAT, LOGL_ERROR, "TP-DestAddr is of unknown type.\n"); + return NULL; + } + + /** + * Besides of what I think I read in GSM 03.40 and 04.11 the TP-DA + * contains the semi-octets as length (strlen), change it to the + * the number of bytes, but then change it back. + */ + data_ptr[2] = dest_len; + gsm48_decode_bcd_number(_dest_nr + 2, ARRAY_SIZE(_dest_nr) - 2, + &data_ptr[2], 1); + data_ptr[2] = orig_dest_len; + if ((data_ptr[3] & 0x70) == 0x10) { + _dest_nr[0] = _dest_nr[1] = '0'; + dest_nr = &_dest_nr[0]; + } else { + dest_nr = &_dest_nr[2]; + } + + /** + * Call functions to rewrite the data + */ + tpdu_hdr = sms_new_tpdu_hdr(nat, imsi, dest_nr, data_ptr[0]); + new_number = find_new_smsc(nat, msg, imsi, smsc_addr, dest_nr); + new_dest_nr = sms_new_dest_nr(nat, msg, imsi, dest_nr); + + if (tpdu_hdr == data_ptr[0] && !new_number && !new_dest_nr) + return NULL; + + out = sms_create_new(GSM411_MT_RP_DATA_MO, ref, hdr48, + orig_addr_ptr, orig_addr_len, + new_number ? new_number : smsc_addr, + data_ptr, data_len, tpdu_hdr, + dest_len, new_dest_nr); + talloc_free(new_number); + talloc_free(new_dest_nr); + return out; +} + +struct msgb *bsc_nat_rewrite_msg(struct bsc_nat *nat, struct msgb *msg, struct bsc_nat_parsed *parsed, const char *imsi) +{ + struct gsm48_hdr *hdr48; + uint32_t len; + uint8_t msg_type, proto; + struct msgb *new_msg = NULL, *sccp; + uint8_t link_id; + + if (!imsi || strlen(imsi) < 5) + return msg; + + /* only care about DTAP messages */ + if (parsed->bssap != BSSAP_MSG_DTAP) + return msg; + if (!parsed->dest_local_ref) + return msg; + + hdr48 = bsc_unpack_dtap(parsed, msg, &len); + if (!hdr48) + return msg; + + link_id = msg->l3h[1]; + proto = hdr48->proto_discr & 0x0f; + msg_type = hdr48->msg_type & 0xbf; + + if (proto == GSM48_PDISC_CC && msg_type == GSM48_MT_CC_SETUP) + new_msg = rewrite_setup(nat, msg, parsed, imsi, hdr48, len); + else if (proto == GSM48_PDISC_SMS && msg_type == GSM411_MT_CP_DATA) + new_msg = rewrite_sms(nat, msg, parsed, imsi, hdr48, len); + + if (!new_msg) + return msg; + + /* wrap with DTAP, SCCP, then IPA. TODO: Stop copying */ + gsm0808_prepend_dtap_header(new_msg, link_id); + sccp = sccp_create_dt1(parsed->dest_local_ref, new_msg->data, new_msg->len); + talloc_free(new_msg); + + if (!sccp) { + LOGP(DNAT, LOGL_ERROR, "Failed to allocate.\n"); + return msg; + } + + ipaccess_prepend_header(sccp, IPAC_PROTO_SCCP); + + /* the parsed hangs off from msg but it needs to survive */ + talloc_steal(sccp, parsed); + msgb_free(msg); + return sccp; +} + +static void num_rewr_free_data(struct bsc_nat_num_rewr_entry *entry) +{ + regfree(&entry->msisdn_reg); + regfree(&entry->num_reg); + talloc_free(entry->replace); +} + +void bsc_nat_num_rewr_entry_adapt(void *ctx, struct llist_head *head, + const struct osmo_config_list *list) +{ + struct bsc_nat_num_rewr_entry *entry, *tmp; + struct osmo_config_entry *cfg_entry; + + /* free the old data */ + llist_for_each_entry_safe(entry, tmp, head, list) { + num_rewr_free_data(entry); + llist_del(&entry->list); + talloc_free(entry); + } + + + if (!list) + return; + + llist_for_each_entry(cfg_entry, &list->entry, list) { + char *regexp; + if (cfg_entry->text[0] == '+') { + LOGP(DNAT, LOGL_ERROR, + "Plus is not allowed in the number\n"); + continue; + } + + entry = talloc_zero(ctx, struct bsc_nat_num_rewr_entry); + if (!entry) { + LOGP(DNAT, LOGL_ERROR, + "Allocation of the num_rewr entry failed.\n"); + continue; + } + + entry->replace = talloc_strdup(entry, cfg_entry->text); + if (!entry->replace) { + LOGP(DNAT, LOGL_ERROR, + "Failed to copy the replacement text.\n"); + talloc_free(entry); + continue; + } + + /* we will now build a regexp string */ + if (cfg_entry->mcc[0] == '^') { + regexp = talloc_strdup(entry, cfg_entry->mcc); + } else { + regexp = talloc_asprintf(entry, "^%s%s", + cfg_entry->mcc[0] == '*' ? + "[0-9][0-9][0-9]" : cfg_entry->mcc, + cfg_entry->mnc[0] == '*' ? + "[0-9][0-9]" : cfg_entry->mnc); + } + + if (!regexp) { + LOGP(DNAT, LOGL_ERROR, "Failed to create a regexp string.\n"); + talloc_free(entry); + continue; + } + + if (regcomp(&entry->msisdn_reg, regexp, 0) != 0) { + LOGP(DNAT, LOGL_ERROR, + "Failed to compile regexp '%s'\n", regexp); + talloc_free(regexp); + talloc_free(entry); + continue; + } + + talloc_free(regexp); + if (regcomp(&entry->num_reg, cfg_entry->option, REG_EXTENDED) != 0) { + LOGP(DNAT, LOGL_ERROR, + "Failed to compile regexp '%s'\n", cfg_entry->option); + regfree(&entry->msisdn_reg); + talloc_free(entry); + continue; + } + + /* we have copied the number */ + llist_add_tail(&entry->list, head); + } +} diff --git a/openbsc/src/osmo-bsc_nat/bsc_nat_utils.c b/openbsc/src/osmo-bsc_nat/bsc_nat_utils.c index 96b937c2b..0c56a8cd9 100644 --- a/openbsc/src/osmo-bsc_nat/bsc_nat_utils.c +++ b/openbsc/src/osmo-bsc_nat/bsc_nat_utils.c @@ -374,259 +374,6 @@ int bsc_nat_lst_check_allow(struct bsc_nat_acc_lst *lst, const char *mi_string) return 1; } -static int lst_check_deny(struct bsc_nat_acc_lst *lst, const char *mi_string) -{ - struct bsc_nat_acc_lst_entry *entry; - - llist_for_each_entry(entry, &lst->fltr_list, list) { - if (!entry->imsi_deny) - continue; - if (regexec(&entry->imsi_deny_re, mi_string, 0, NULL, 0) == 0) - return 0; - } - - return 1; -} - -/* apply white/black list */ -static int auth_imsi(struct bsc_connection *bsc, const char *mi_string) -{ - /* - * 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 - */ - struct bsc_nat_acc_lst *nat_lst = NULL; - struct bsc_nat_acc_lst *bsc_lst = NULL; - - 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) - return 1; - - /* 2. BSC deny */ - if (lst_check_deny(bsc_lst, mi_string) == 0) { - LOGP(DNAT, LOGL_ERROR, - "Filtering %s by imsi_deny on bsc nr: %d.\n", mi_string, bsc->cfg->nr); - rate_ctr_inc(&bsc_lst->stats->ctr[ACC_LIST_BSC_FILTER]); - return -2; - } - - } - - /* 3. NAT deny */ - if (nat_lst) { - if (lst_check_deny(nat_lst, mi_string) == 0) { - LOGP(DNAT, LOGL_ERROR, - "Filtering %s by nat imsi_deny on bsc nr: %d.\n", mi_string, bsc->cfg->nr); - rate_ctr_inc(&nat_lst->stats->ctr[ACC_LIST_NAT_FILTER]); - return -3; - } - } - - return 1; -} - -static int _cr_check_loc_upd(struct bsc_connection *bsc, - uint8_t *data, unsigned int length, - char **imsi) -{ - uint8_t mi_type; - struct gsm48_loc_upd_req *lu; - char mi_string[GSM48_MI_SIZE]; - - if (length < sizeof(*lu)) { - LOGP(DNAT, LOGL_ERROR, - "LU does not fit. Length is %d \n", length); - return -1; - } - - lu = (struct gsm48_loc_upd_req *) data; - mi_type = lu->mi[0] & GSM_MI_TYPE_MASK; - - /* - * We can only deal with the IMSI. This will fail for a phone that - * will send the TMSI of a previous network to us. - */ - if (mi_type != GSM_MI_TYPE_IMSI) - return 0; - - gsm48_mi_to_string(mi_string, sizeof(mi_string), lu->mi, lu->mi_len); - *imsi = talloc_strdup(bsc, mi_string); - return auth_imsi(bsc, mi_string); -} - -static int _cr_check_cm_serv_req(struct bsc_connection *bsc, - uint8_t *data, unsigned int length, - int *con_type, char **imsi) -{ - static const uint32_t classmark_offset = - offsetof(struct gsm48_service_request, classmark); - - char mi_string[GSM48_MI_SIZE]; - uint8_t mi_type; - int rc; - struct gsm48_service_request *req; - - /* unfortunately in Phase1 the classmark2 length is variable */ - - if (length < sizeof(*req)) { - LOGP(DNAT, LOGL_ERROR, - "CM Serv Req does not fit. Length is %d\n", length); - return -1; - } - - req = (struct gsm48_service_request *) data; - if (req->cm_service_type == 0x8) - *con_type = NAT_CON_TYPE_SSA; - rc = gsm48_extract_mi((uint8_t *) &req->classmark, - length - classmark_offset, mi_string, &mi_type); - if (rc < 0) { - LOGP(DNAT, LOGL_ERROR, "Failed to parse the classmark2/mi. error: %d\n", rc); - return -1; - } - - /* we have to let the TMSI or such pass */ - if (mi_type != GSM_MI_TYPE_IMSI) - return 0; - - *imsi = talloc_strdup(bsc, mi_string); - return auth_imsi(bsc, mi_string); -} - -static int _cr_check_pag_resp(struct bsc_connection *bsc, - uint8_t *data, unsigned int length, - char **imsi) -{ - struct gsm48_pag_resp *resp; - char mi_string[GSM48_MI_SIZE]; - uint8_t mi_type; - - if (length < sizeof(*resp)) { - LOGP(DNAT, LOGL_ERROR, "PAG RESP does not fit. Length was %d.\n", length); - return -1; - } - - resp = (struct gsm48_pag_resp *) data; - if (gsm48_paging_extract_mi(resp, length, mi_string, &mi_type) < 0) { - LOGP(DNAT, LOGL_ERROR, "Failed to extract the MI.\n"); - return -1; - } - - /* we need to let it pass for now */ - if (mi_type != GSM_MI_TYPE_IMSI) - return 0; - - *imsi = talloc_strdup(bsc, mi_string); - return auth_imsi(bsc, mi_string); -} - -static int _dt_check_id_resp(struct bsc_connection *bsc, - uint8_t *data, unsigned int length, - struct sccp_connections *con) -{ - char mi_string[GSM48_MI_SIZE]; - uint8_t mi_type; - int ret; - - if (length < 2) { - LOGP(DNAT, LOGL_ERROR, "mi does not fit.\n"); - return -1; - } - - if (data[0] < length - 1) { - LOGP(DNAT, LOGL_ERROR, "mi length too big.\n"); - return -2; - } - - mi_type = data[1] & GSM_MI_TYPE_MASK; - gsm48_mi_to_string(mi_string, sizeof(mi_string), &data[1], data[0]); - - if (mi_type != GSM_MI_TYPE_IMSI) - return 0; - - ret = auth_imsi(bsc, mi_string); - con->imsi_checked = 1; - con->imsi = talloc_strdup(con, mi_string); - return ret; -} - -/* Filter out CR data... */ -int bsc_nat_filter_sccp_cr(struct bsc_connection *bsc, struct msgb *msg, - struct bsc_nat_parsed *parsed, int *con_type, - char **imsi) -{ - struct tlv_parsed tp; - struct gsm48_hdr *hdr48; - int hdr48_len; - int len; - uint8_t msg_type, proto; - - *con_type = NAT_CON_TYPE_NONE; - *imsi = NULL; - - if (parsed->gsm_type != BSS_MAP_MSG_COMPLETE_LAYER_3) { - LOGP(DNAT, LOGL_ERROR, - "Rejecting CR message due wrong GSM Type %d\n", parsed->gsm_type); - return -1; - } - - /* the parsed has had some basic l3 length check */ - len = msg->l3h[1]; - if (msgb_l3len(msg) - 3 < len) { - LOGP(DNAT, LOGL_ERROR, - "The CR Data has not enough space...\n"); - return -1; - } - - msg->l4h = &msg->l3h[3]; - len -= 1; - - tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l4h, len, 0, 0); - - if (!TLVP_PRESENT(&tp, GSM0808_IE_LAYER_3_INFORMATION)) { - LOGP(DNAT, LOGL_ERROR, "CR Data does not contain layer3 information.\n"); - return -1; - } - - hdr48_len = TLVP_LEN(&tp, GSM0808_IE_LAYER_3_INFORMATION); - - if (hdr48_len < sizeof(*hdr48)) { - LOGP(DNAT, LOGL_ERROR, "GSM48 header does not fit.\n"); - return -1; - } - - hdr48 = (struct gsm48_hdr *) TLVP_VAL(&tp, GSM0808_IE_LAYER_3_INFORMATION); - - proto = hdr48->proto_discr & 0x0f; - msg_type = hdr48->msg_type & 0xbf; - if (proto == GSM48_PDISC_MM && - msg_type == GSM48_MT_MM_LOC_UPD_REQUEST) { - *con_type = NAT_CON_TYPE_LU; - return _cr_check_loc_upd(bsc, &hdr48->data[0], hdr48_len - sizeof(*hdr48), imsi); - } else if (proto == GSM48_PDISC_MM && - msg_type == GSM48_MT_MM_CM_SERV_REQ) { - *con_type = NAT_CON_TYPE_CM_SERV_REQ; - return _cr_check_cm_serv_req(bsc, &hdr48->data[0], - hdr48_len - sizeof(*hdr48), - con_type, imsi); - } else if (proto == GSM48_PDISC_RR && - msg_type == GSM48_MT_RR_PAG_RESP) { - *con_type = NAT_CON_TYPE_PAG_RESP; - return _cr_check_pag_resp(bsc, &hdr48->data[0], hdr48_len - sizeof(*hdr48), imsi); - } else { - /* We only want to filter the above, let other things pass */ - *con_type = NAT_CON_TYPE_OTHER; - return 0; - } -} - struct gsm48_hdr *bsc_unpack_dtap(struct bsc_nat_parsed *parsed, struct msgb *msg, uint32_t *len) { @@ -648,34 +395,6 @@ struct gsm48_hdr *bsc_unpack_dtap(struct bsc_nat_parsed *parsed, return (struct gsm48_hdr *) msg->l4h; } -int bsc_nat_filter_dt(struct bsc_connection *bsc, struct msgb *msg, - struct sccp_connections *con, struct bsc_nat_parsed *parsed) -{ - uint32_t len; - uint8_t msg_type, proto; - struct gsm48_hdr *hdr48; - - if (con->imsi_checked) - return 0; - - /* only care about DTAP messages */ - if (parsed->bssap != BSSAP_MSG_DTAP) - return 0; - - hdr48 = bsc_unpack_dtap(parsed, msg, &len); - if (!hdr48) - return -1; - - proto = hdr48->proto_discr & 0x0f; - msg_type = hdr48->msg_type & 0xbf; - if (proto == GSM48_PDISC_MM && - msg_type == GSM48_MT_MM_ID_RESP) { - return _dt_check_id_resp(bsc, &hdr48->data[0], len - sizeof(*hdr48), con); - } else { - return 0; - } -} - static const char *con_types [] = { [NAT_CON_TYPE_NONE] = "n/a", [NAT_CON_TYPE_LU] = "Location Update", @@ -782,592 +501,3 @@ int bsc_write_cb(struct osmo_fd *bfd, struct msgb *msg) return rc; } -static char *match_and_rewrite_number(void *ctx, const char *number, - const char *imsi, - struct llist_head *list) -{ - struct bsc_nat_num_rewr_entry *entry; - char *new_number = NULL; - - /* need to find a replacement and then fix it */ - llist_for_each_entry(entry, list, list) { - regmatch_t matches[2]; - - /* check the IMSI match */ - if (regexec(&entry->msisdn_reg, imsi, 0, NULL, 0) != 0) - continue; - - /* this regexp matches... */ - if (regexec(&entry->num_reg, number, 2, matches, 0) == 0 && - matches[1].rm_eo != -1) - new_number = talloc_asprintf(ctx, "%s%s", - entry->replace, - &number[matches[1].rm_so]); - if (new_number) - break; - } - - return new_number; -} - -static char *rewrite_non_international(struct bsc_nat *nat, void *ctx, const char *imsi, - struct gsm_mncc_number *called) -{ - if (llist_empty(&nat->num_rewr)) - return NULL; - - if (called->plan != 1) - return NULL; - if (called->type == 1) - return NULL; - - return match_and_rewrite_number(ctx, called->number, - imsi, &nat->num_rewr); -} - - -/** - * Rewrite non global numbers... according to rules based on the IMSI - */ -static struct msgb *rewrite_setup(struct bsc_nat *nat, struct msgb *msg, - struct bsc_nat_parsed *parsed, const char *imsi, - struct gsm48_hdr *hdr48, const uint32_t len) -{ - struct tlv_parsed tp; - unsigned int payload_len; - struct gsm_mncc_number called; - struct msgb *out; - char *new_number = NULL; - uint8_t *outptr; - const uint8_t *msgptr; - int sec_len; - - /* decode and rewrite the message */ - payload_len = len - sizeof(*hdr48); - tlv_parse(&tp, &gsm48_att_tlvdef, hdr48->data, payload_len, 0, 0); - - /* no number, well let us ignore it */ - if (!TLVP_PRESENT(&tp, GSM48_IE_CALLED_BCD)) - return NULL; - - memset(&called, 0, sizeof(called)); - gsm48_decode_called(&called, - TLVP_VAL(&tp, GSM48_IE_CALLED_BCD) - 1); - - /* check if it looks international and stop */ - new_number = rewrite_non_international(nat, msg, imsi, &called); - - if (!new_number) { - LOGP(DNAT, LOGL_DEBUG, "No IMSI match found, returning message.\n"); - return NULL; - } - - if (strlen(new_number) > sizeof(called.number)) { - LOGP(DNAT, LOGL_ERROR, "Number is too long for structure.\n"); - talloc_free(new_number); - return NULL; - } - - /* - * Need to create a new message now based on the old onew - * with a new number. We can sadly not patch this in place - * so we will need to regenerate it. - */ - - out = msgb_alloc_headroom(4096, 128, "changed-setup"); - if (!out) { - LOGP(DNAT, LOGL_ERROR, "Failed to allocate.\n"); - talloc_free(new_number); - return NULL; - } - - /* copy the header */ - outptr = msgb_put(out, sizeof(*hdr48)); - memcpy(outptr, hdr48, sizeof(*hdr48)); - - /* copy everything up to the number */ - sec_len = TLVP_VAL(&tp, GSM48_IE_CALLED_BCD) - 2 - &hdr48->data[0]; - outptr = msgb_put(out, sec_len); - memcpy(outptr, &hdr48->data[0], sec_len); - - /* create the new number */ - if (strncmp(new_number, "00", 2) == 0) { - called.type = 1; - strncpy(called.number, new_number + 2, sizeof(called.number)); - } else { - strncpy(called.number, new_number, sizeof(called.number)); - } - gsm48_encode_called(out, &called); - - /* copy thre rest */ - msgptr = TLVP_VAL(&tp, GSM48_IE_CALLED_BCD) + - TLVP_LEN(&tp, GSM48_IE_CALLED_BCD); - sec_len = payload_len - (msgptr - &hdr48->data[0]); - outptr = msgb_put(out, sec_len); - memcpy(outptr, msgptr, sec_len); - - talloc_free(new_number); - return out; -} - -/** - * Find a new SMSC address, returns an allocated string that needs to be - * freed or is NULL. - */ -static char *find_new_smsc(struct bsc_nat *nat, void *ctx, const char *imsi, - const char *smsc_addr, const char *dest_nr) -{ - struct bsc_nat_num_rewr_entry *entry; - char *new_number = NULL; - uint8_t dest_match = llist_empty(&nat->tpdest_match); - - /* We will find a new number now */ - llist_for_each_entry(entry, &nat->smsc_rewr, list) { - regmatch_t matches[2]; - - /* check the IMSI match */ - if (regexec(&entry->msisdn_reg, imsi, 0, NULL, 0) != 0) - continue; - - /* this regexp matches... */ - if (regexec(&entry->num_reg, smsc_addr, 2, matches, 0) == 0 && - matches[1].rm_eo != -1) - new_number = talloc_asprintf(ctx, "%s%s", - entry->replace, - &smsc_addr[matches[1].rm_so]); - if (new_number) - break; - } - - if (!new_number) - return NULL; - - /* - * now match the number against another list - */ - llist_for_each_entry(entry, &nat->tpdest_match, list) { - /* check the IMSI match */ - if (regexec(&entry->msisdn_reg, imsi, 0, NULL, 0) != 0) - continue; - - if (regexec(&entry->num_reg, dest_nr, 0, NULL, 0) == 0) { - dest_match = 1; - break; - } - } - - if (!dest_match) { - talloc_free(new_number); - return NULL; - } - - return new_number; -} - -/** - * Clear the TP-SRR from the TPDU header - */ -static uint8_t sms_new_tpdu_hdr(struct bsc_nat *nat, const char *imsi, - const char *dest_nr, uint8_t hdr) -{ - struct bsc_nat_num_rewr_entry *entry; - - /* We will find a new number now */ - llist_for_each_entry(entry, &nat->sms_clear_tp_srr, list) { - /* check the IMSI match */ - if (regexec(&entry->msisdn_reg, imsi, 0, NULL, 0) != 0) - continue; - if (regexec(&entry->num_reg, dest_nr, 0, NULL, 0) != 0) - continue; - - /* matched phone number and imsi */ - return hdr & ~0x20; - } - - return hdr; -} - -/** - * Check if we need to rewrite the number. For this SMS. - */ -static char *sms_new_dest_nr(struct bsc_nat *nat, void *ctx, - const char *imsi, const char *dest_nr) -{ - return match_and_rewrite_number(ctx, dest_nr, imsi, - &nat->sms_num_rewr); -} - -/** - * This is a helper for GSM 04.11 8.2.5.2 Destination address element - */ -void sms_encode_addr_element(struct msgb *out, const char *new_number, - int format, int tp_data) -{ - uint8_t new_addr_len; - uint8_t new_addr[26]; - - /* - * Copy the new number. We let libosmocore encode it, then set - * the extension followed after the length. Depending on if - * we want to write RP we will let the TLV code add the - * length for us or we need to use strlen... This is not very clear - * as of 03.40 and 04.11. - */ - new_addr_len = gsm48_encode_bcd_number(new_addr, ARRAY_SIZE(new_addr), - 1, new_number); - new_addr[1] = format; - if (tp_data) { - uint8_t *data = msgb_put(out, new_addr_len); - memcpy(data, new_addr, new_addr_len); - data[0] = strlen(new_number); - } else { - msgb_lv_put(out, new_addr_len - 1, new_addr + 1); - } -} - -static struct msgb *sms_create_new(uint8_t type, uint8_t ref, - struct gsm48_hdr *old_hdr48, - const uint8_t *orig_addr_ptr, - int orig_addr_len, const char *new_number, - const uint8_t *data_ptr, int data_len, - uint8_t tpdu_first_byte, - const int old_dest_len, const char *new_dest_nr) -{ - struct gsm48_hdr *new_hdr48; - struct msgb *out; - - /* - * We need to re-create the patched structure. This is why we have - * saved the above pointers. - */ - out = msgb_alloc_headroom(4096, 128, "changed-smsc"); - if (!out) { - LOGP(DNAT, LOGL_ERROR, "Failed to allocate.\n"); - return NULL; - } - - out->l2h = out->data; - msgb_v_put(out, GSM411_MT_RP_DATA_MO); - msgb_v_put(out, ref); - msgb_lv_put(out, orig_addr_len, orig_addr_ptr); - - sms_encode_addr_element(out, new_number, 0x91, 0); - - - /* Patch the TPDU from here on */ - - /** - * Do we need to put a new TP-Destination-Address (TP-DA) here or - * can we copy the old thing? For the TP-DA we need to find out the - * new size. - */ - if (new_dest_nr) { - uint8_t *data, *new_size; - - /* reserve the size and write the header */ - new_size = msgb_put(out, 1); - out->l3h = new_size + 1; - msgb_v_put(out, tpdu_first_byte); - msgb_v_put(out, data_ptr[1]); - - /* encode the new number and put it */ - if (strncmp(new_dest_nr, "00", 2) == 0) - sms_encode_addr_element(out, new_dest_nr + 2, 0x91, 1); - else - sms_encode_addr_element(out, new_dest_nr, 0x81, 1); - - /* Copy the rest after the TP-DS */ - data = msgb_put(out, data_len - 2 - 1 - old_dest_len); - memcpy(data, &data_ptr[2 + 1 + old_dest_len], data_len - 2 - 1 - old_dest_len); - - /* fill in the new size */ - new_size[0] = msgb_l3len(out); - } else { - msgb_v_put(out, data_len); - msgb_tv_fixed_put(out, tpdu_first_byte, data_len - 1, &data_ptr[1]); - } - - /* prepend GSM 04.08 header */ - new_hdr48 = (struct gsm48_hdr *) msgb_push(out, sizeof(*new_hdr48) + 1); - memcpy(new_hdr48, old_hdr48, sizeof(*old_hdr48)); - new_hdr48->data[0] = msgb_l2len(out); - - return out; -} - -/** - * Parse the SMS and check if it needs to be rewritten - */ -static struct msgb *rewrite_sms(struct bsc_nat *nat, struct msgb *msg, - struct bsc_nat_parsed *parsed, const char *imsi, - struct gsm48_hdr *hdr48, const uint32_t len) -{ - unsigned int payload_len; - unsigned int cp_len; - - uint8_t ref; - uint8_t orig_addr_len, *orig_addr_ptr; - uint8_t dest_addr_len, *dest_addr_ptr; - uint8_t data_len, *data_ptr; - char smsc_addr[30]; - - - uint8_t dest_len, orig_dest_len; - char _dest_nr[30]; - char *dest_nr; - char *new_dest_nr; - - char *new_number = NULL; - uint8_t tpdu_hdr; - struct msgb *out; - - payload_len = len - sizeof(*hdr48); - if (payload_len < 1) { - LOGP(DNAT, LOGL_ERROR, "SMS too short for things. %d\n", payload_len); - return NULL; - } - - cp_len = hdr48->data[0]; - if (payload_len + 1 < cp_len) { - LOGP(DNAT, LOGL_ERROR, "SMS RPDU can not fit in: %d %d\n", cp_len, payload_len); - return NULL; - } - - if (hdr48->data[1] != GSM411_MT_RP_DATA_MO) - return NULL; - - if (cp_len < 5) { - LOGP(DNAT, LOGL_ERROR, "RD-DATA can not fit in the CP len: %d\n", cp_len); - return NULL; - } - - /* RP */ - ref = hdr48->data[2]; - orig_addr_len = hdr48->data[3]; - orig_addr_ptr = &hdr48->data[4]; - - /* the +1 is for checking if the following element has some space */ - if (cp_len < 3 + orig_addr_len + 1) { - LOGP(DNAT, LOGL_ERROR, "RP-Originator addr does not fit: %d\n", orig_addr_len); - return NULL; - } - - dest_addr_len = hdr48->data[3 + orig_addr_len + 1]; - dest_addr_ptr = &hdr48->data[3 + orig_addr_len + 2]; - - if (cp_len < 3 + orig_addr_len + 1 + dest_addr_len + 1) { - LOGP(DNAT, LOGL_ERROR, "RP-Destination addr does not fit: %d\n", dest_addr_len); - return NULL; - } - gsm48_decode_bcd_number(smsc_addr, ARRAY_SIZE(smsc_addr), dest_addr_ptr - 1, 1); - - data_len = hdr48->data[3 + orig_addr_len + 1 + dest_addr_len + 1]; - data_ptr = &hdr48->data[3 + orig_addr_len + 1 + dest_addr_len + 2]; - - if (cp_len < 3 + orig_addr_len + 1 + dest_addr_len + 1 + data_len) { - LOGP(DNAT, LOGL_ERROR, "RP-Data does not fit: %d\n", data_len); - return NULL; - } - - if (data_len < 3) { - LOGP(DNAT, LOGL_ERROR, "SMS-SUBMIT is too short.\n"); - return NULL; - } - - /* TP-PDU starts here */ - if ((data_ptr[0] & 0x03) != GSM340_SMS_SUBMIT_MS2SC) - return NULL; - - /* - * look into the phone number. The length is in semi-octets, we will - * need to add the byte for the number type as well. - */ - orig_dest_len = data_ptr[2]; - dest_len = ((orig_dest_len + 1) / 2) + 1; - if (data_len < dest_len + 3 || dest_len < 2) { - LOGP(DNAT, LOGL_ERROR, "SMS-SUBMIT can not have TP-DestAddr.\n"); - return NULL; - } - - if ((data_ptr[3] & 0x80) == 0) { - LOGP(DNAT, LOGL_ERROR, "TP-DestAddr has extension. Not handled.\n"); - return NULL; - } - - if ((data_ptr[3] & 0x0F) == 0) { - LOGP(DNAT, LOGL_ERROR, "TP-DestAddr is of unknown type.\n"); - return NULL; - } - - /** - * Besides of what I think I read in GSM 03.40 and 04.11 the TP-DA - * contains the semi-octets as length (strlen), change it to the - * the number of bytes, but then change it back. - */ - data_ptr[2] = dest_len; - gsm48_decode_bcd_number(_dest_nr + 2, ARRAY_SIZE(_dest_nr) - 2, - &data_ptr[2], 1); - data_ptr[2] = orig_dest_len; - if ((data_ptr[3] & 0x70) == 0x10) { - _dest_nr[0] = _dest_nr[1] = '0'; - dest_nr = &_dest_nr[0]; - } else { - dest_nr = &_dest_nr[2]; - } - - /** - * Call functions to rewrite the data - */ - tpdu_hdr = sms_new_tpdu_hdr(nat, imsi, dest_nr, data_ptr[0]); - new_number = find_new_smsc(nat, msg, imsi, smsc_addr, dest_nr); - new_dest_nr = sms_new_dest_nr(nat, msg, imsi, dest_nr); - - if (tpdu_hdr == data_ptr[0] && !new_number && !new_dest_nr) - return NULL; - - out = sms_create_new(GSM411_MT_RP_DATA_MO, ref, hdr48, - orig_addr_ptr, orig_addr_len, - new_number ? new_number : smsc_addr, - data_ptr, data_len, tpdu_hdr, - dest_len, new_dest_nr); - talloc_free(new_number); - talloc_free(new_dest_nr); - return out; -} - -struct msgb *bsc_nat_rewrite_msg(struct bsc_nat *nat, struct msgb *msg, struct bsc_nat_parsed *parsed, const char *imsi) -{ - struct gsm48_hdr *hdr48; - uint32_t len; - uint8_t msg_type, proto; - struct msgb *new_msg = NULL, *sccp; - uint8_t link_id; - - if (!imsi || strlen(imsi) < 5) - return msg; - - /* only care about DTAP messages */ - if (parsed->bssap != BSSAP_MSG_DTAP) - return msg; - if (!parsed->dest_local_ref) - return msg; - - hdr48 = bsc_unpack_dtap(parsed, msg, &len); - if (!hdr48) - return msg; - - link_id = msg->l3h[1]; - proto = hdr48->proto_discr & 0x0f; - msg_type = hdr48->msg_type & 0xbf; - - if (proto == GSM48_PDISC_CC && msg_type == GSM48_MT_CC_SETUP) - new_msg = rewrite_setup(nat, msg, parsed, imsi, hdr48, len); - else if (proto == GSM48_PDISC_SMS && msg_type == GSM411_MT_CP_DATA) - new_msg = rewrite_sms(nat, msg, parsed, imsi, hdr48, len); - - if (!new_msg) - return msg; - - /* wrap with DTAP, SCCP, then IPA. TODO: Stop copying */ - gsm0808_prepend_dtap_header(new_msg, link_id); - sccp = sccp_create_dt1(parsed->dest_local_ref, new_msg->data, new_msg->len); - talloc_free(new_msg); - - if (!sccp) { - LOGP(DNAT, LOGL_ERROR, "Failed to allocate.\n"); - return msg; - } - - ipaccess_prepend_header(sccp, IPAC_PROTO_SCCP); - - /* the parsed hangs off from msg but it needs to survive */ - talloc_steal(sccp, parsed); - msgb_free(msg); - return sccp; -} - -static void num_rewr_free_data(struct bsc_nat_num_rewr_entry *entry) -{ - regfree(&entry->msisdn_reg); - regfree(&entry->num_reg); - talloc_free(entry->replace); -} - -void bsc_nat_num_rewr_entry_adapt(void *ctx, struct llist_head *head, - const struct osmo_config_list *list) -{ - struct bsc_nat_num_rewr_entry *entry, *tmp; - struct osmo_config_entry *cfg_entry; - - /* free the old data */ - llist_for_each_entry_safe(entry, tmp, head, list) { - num_rewr_free_data(entry); - llist_del(&entry->list); - talloc_free(entry); - } - - - if (!list) - return; - - llist_for_each_entry(cfg_entry, &list->entry, list) { - char *regexp; - if (cfg_entry->text[0] == '+') { - LOGP(DNAT, LOGL_ERROR, - "Plus is not allowed in the number\n"); - continue; - } - - entry = talloc_zero(ctx, struct bsc_nat_num_rewr_entry); - if (!entry) { - LOGP(DNAT, LOGL_ERROR, - "Allication of the num_rewr entry failed.\n"); - continue; - } - - entry->replace = talloc_strdup(entry, cfg_entry->text); - if (!entry->replace) { - LOGP(DNAT, LOGL_ERROR, - "Failed to copy the replacement text.\n"); - talloc_free(entry); - continue; - } - - /* we will now build a regexp string */ - if (cfg_entry->mcc[0] == '^') { - regexp = talloc_strdup(entry, cfg_entry->mcc); - } else { - regexp = talloc_asprintf(entry, "^%s%s", - cfg_entry->mcc[0] == '*' ? - "[0-9][0-9][0-9]" : cfg_entry->mcc, - cfg_entry->mnc[0] == '*' ? - "[0-9][0-9]" : cfg_entry->mnc); - } - - if (!regexp) { - LOGP(DNAT, LOGL_ERROR, "Failed to create a regexp string.\n"); - talloc_free(entry); - continue; - } - - if (regcomp(&entry->msisdn_reg, regexp, 0) != 0) { - LOGP(DNAT, LOGL_ERROR, - "Failed to compile regexp '%s'\n", regexp); - talloc_free(regexp); - talloc_free(entry); - continue; - } - - talloc_free(regexp); - if (regcomp(&entry->num_reg, cfg_entry->option, REG_EXTENDED) != 0) { - LOGP(DNAT, LOGL_ERROR, - "Failed to compile regexp '%s'\n", cfg_entry->option); - regfree(&entry->msisdn_reg); - talloc_free(entry); - continue; - } - - /* we have copied the number */ - llist_add_tail(&entry->list, head); - } -} 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 6c6f4337c..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 @@ -10,6 +10,8 @@ bsc_nat_test_SOURCES = bsc_nat_test.c \ $(top_srcdir)/src/osmo-bsc_nat/bsc_filter.c \ $(top_srcdir)/src/osmo-bsc_nat/bsc_sccp.c \ $(top_srcdir)/src/osmo-bsc_nat/bsc_nat_utils.c \ + $(top_srcdir)/src/osmo-bsc_nat/bsc_nat_filter.c \ + $(top_srcdir)/src/osmo-bsc_nat/bsc_nat_rewrite.c \ $(top_srcdir)/src/osmo-bsc_nat/bsc_mgcp_utils.c bsc_nat_test_LDADD = $(top_builddir)/src/libbsc/libbsc.a \ $(top_srcdir)/src/libmgcp/libmgcp.a \ 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 f8ba57b8a..66b4ff5a7 100644 --- a/openbsc/tests/bsc-nat/bsc_nat_test.c +++ b/openbsc/tests/bsc-nat/bsc_nat_test.c @@ -1,8 +1,8 @@ /* * BSC NAT Message filtering * - * (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org> - * (C) 2010 by On-Waves + * (C) 2010-2012 by Holger Hans Peter Freyther <zecke@selfish.org> + * (C) 2010-2012 by On-Waves * * All Rights Reserved * @@ -761,6 +761,7 @@ static void test_cr_filter() struct bsc_nat_parsed *parsed; struct bsc_nat_acc_lst *nat_lst, *bsc_lst; struct bsc_nat_acc_lst_entry *nat_entry, *bsc_entry; + struct bsc_nat_reject_cause cause; struct bsc_nat *nat = bsc_nat_alloc(); struct bsc_connection *bsc = bsc_connection_alloc(nat); @@ -802,7 +803,8 @@ static void test_cr_filter() abort(); } - res = bsc_nat_filter_sccp_cr(bsc, msg, parsed, &contype, &imsi); + memset(&cause, 0, sizeof(cause)); + res = bsc_nat_filter_sccp_cr(bsc, msg, parsed, &contype, &imsi, &cause); if (res != cr_filter[i].result) { printf("FAIL: Wrong result %d for test %d.\n", res, i); abort(); @@ -825,6 +827,7 @@ static void test_dt_filter() int i; struct msgb *msg = msgb_alloc(4096, "test_dt_filter"); struct bsc_nat_parsed *parsed; + struct bsc_nat_reject_cause cause; struct bsc_nat *nat = bsc_nat_alloc(); struct bsc_connection *bsc = bsc_connection_alloc(nat); @@ -854,7 +857,8 @@ static void test_dt_filter() abort(); } - if (bsc_nat_filter_dt(bsc, msg, con, parsed) != 1) { + memset(&cause, 0, sizeof(cause)); + if (bsc_nat_filter_dt(bsc, msg, con, parsed, &cause) != 1) { printf("FAIL: Should have passed..\n"); abort(); } @@ -869,7 +873,8 @@ static void test_dt_filter() continue; con->imsi_checked = 0; - bsc_nat_filter_dt(bsc, msg, con, parsed); + memset(&cause, 0, sizeof(cause)); + bsc_nat_filter_dt(bsc, msg, con, parsed, &cause); } } @@ -1148,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); @@ -1166,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 |