Merge remote-tracking branch 'origin/zecke/barr-subscribers'

Allow to merge barr certain subscribers with a given error code
diff --git a/openbsc/include/openbsc/bsc_nat.h b/openbsc/include/openbsc/bsc_nat.h
index 7e59c63..1698fa4 100644
--- a/openbsc/include/openbsc/bsc_nat.h
+++ b/openbsc/include/openbsc/bsc_nat.h
@@ -287,6 +287,10 @@
 	/* 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 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 @@
  * 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 @@
 
 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 d5b06da..d1a9bd5 100644
--- a/openbsc/src/osmo-bsc_nat/Makefile.am
+++ b/openbsc/src/osmo-bsc_nat/Makefile.am
@@ -6,7 +6,8 @@
 
 
 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 466dc72..d5a3a1f 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 @@
  * 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 @@
 	/* 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_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 @@
 	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 @@
 		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 @@
 			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 @@
 	/* 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 0000000..5ab3a97
--- /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 0000000..1eed149
--- /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 96b937c..0c56a8c 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 @@
 	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 @@
 	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 @@
 	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 220e960..7bbc890 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 @@
 	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 @@
 	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 @@
 	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 @@
 	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 @@
 	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 6c6f433..e284851 100644
--- a/openbsc/tests/bsc-nat/Makefile.am
+++ b/openbsc/tests/bsc-nat/Makefile.am
@@ -2,7 +2,7 @@
 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 @@
 			$(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 0000000..a9a4a2b
--- /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 0000000..ea94631
--- /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 f8ba57b..66b4ff5 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 @@
 	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 @@
 			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 @@
 	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 @@
 		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 @@
 			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 @@
 	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 @@
 	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 db37ffa..cbedc85 100644
--- a/openbsc/tests/bsc-nat/bsc_nat_test.ok
+++ b/openbsc/tests/bsc-nat/bsc_nat_test.ok
@@ -22,4 +22,17 @@
 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 4c5de8d..e649f03 100644
--- a/openbsc/tests/testsuite.at
+++ b/openbsc/tests/testsuite.at
@@ -34,6 +34,8 @@
 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