mirror of
				https://gitea.osmocom.org/cellular-infrastructure/osmo-mgw.git
				synced 2025-11-03 21:43:32 +00:00 
			
		
		
		
	Merge remote-tracking branch 'origin/zecke/barr-subscribers'
Allow to merge barr certain subscribers with a given error code
This commit is contained in:
		@@ -287,6 +287,10 @@ struct bsc_nat {
 | 
				
			|||||||
	/* filter */
 | 
						/* filter */
 | 
				
			||||||
	char *acc_lst_name;
 | 
						char *acc_lst_name;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Barring of subscribers with a rb tree */
 | 
				
			||||||
 | 
						struct rb_root imsi_black_list;
 | 
				
			||||||
 | 
						char *imsi_black_list_fn;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* number rewriting */
 | 
						/* number rewriting */
 | 
				
			||||||
	char *num_rewr_name;
 | 
						char *num_rewr_name;
 | 
				
			||||||
	struct llist_head num_rewr;
 | 
						struct llist_head num_rewr;
 | 
				
			||||||
@@ -327,6 +331,11 @@ struct bsc_nat_ussd_con {
 | 
				
			|||||||
	struct osmo_timer_list auth_timeout;
 | 
						struct osmo_timer_list auth_timeout;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct bsc_nat_reject_cause {
 | 
				
			||||||
 | 
						int lu_reject_cause;
 | 
				
			||||||
 | 
						int cm_reject_cause;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* create and init the structures */
 | 
					/* create and init the structures */
 | 
				
			||||||
struct bsc_config *bsc_config_alloc(struct bsc_nat *nat, const char *token);
 | 
					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);
 | 
					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.
 | 
					 * Content filtering.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
int bsc_nat_filter_sccp_cr(struct bsc_connection *bsc, struct msgb *msg,
 | 
					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,
 | 
					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
 | 
					 * 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 *);
 | 
					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);
 | 
					struct ctrl_handle *bsc_nat_controlif_setup(struct bsc_nat *nat, int port);
 | 
				
			||||||
void bsc_nat_ctrl_del_pending(struct bsc_cmd_list *pending);
 | 
					void bsc_nat_ctrl_del_pending(struct bsc_cmd_list *pending);
 | 
				
			||||||
int bsc_nat_handle_ctrlif_msg(struct bsc_connection *bsc, struct msgb *msg);
 | 
					int bsc_nat_handle_ctrlif_msg(struct bsc_connection *bsc, struct msgb *msg);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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 \
 | 
					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 \
 | 
					osmo_bsc_nat_LDADD = $(top_builddir)/src/libcommon/libcommon.a \
 | 
				
			||||||
		$(top_builddir)/src/libmgcp/libmgcp.a \
 | 
							$(top_builddir)/src/libmgcp/libmgcp.a \
 | 
				
			||||||
		$(top_builddir)/src/libbsc/libbsc.a \
 | 
							$(top_builddir)/src/libbsc/libbsc.a \
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,8 +1,8 @@
 | 
				
			|||||||
/* BSC Multiplexer/NAT */
 | 
					/* BSC Multiplexer/NAT */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * (C) 2010-2011 by Holger Hans Peter Freyther <zecke@selfish.org>
 | 
					 * (C) 2010-2012 by Holger Hans Peter Freyther <zecke@selfish.org>
 | 
				
			||||||
 * (C) 2010-2011 by On-Waves
 | 
					 * (C) 2010-2012 by On-Waves
 | 
				
			||||||
 * (C) 2009 by Harald Welte <laforge@gnumonks.org>
 | 
					 * (C) 2009 by Harald Welte <laforge@gnumonks.org>
 | 
				
			||||||
 * All Rights Reserved
 | 
					 * 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.) Give up on the BSC side
 | 
				
			||||||
 *  2.1) Depending on the con type reject the service, or just close it
 | 
					 *  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;
 | 
						struct msgb *rlsd;
 | 
				
			||||||
	/* 1. release the network */
 | 
						/* 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 */
 | 
						/* 2. release the BSC side */
 | 
				
			||||||
	if (con->con_type == NAT_CON_TYPE_LU) {
 | 
						if (con->con_type == NAT_CON_TYPE_LU) {
 | 
				
			||||||
		struct msgb *payload, *udt;
 | 
							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) {
 | 
							if (payload) {
 | 
				
			||||||
			gsm0808_prepend_dtap_header(payload, 0);
 | 
								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,
 | 
					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 *payload;
 | 
				
			||||||
	struct msgb *refuse;
 | 
						struct msgb *refuse;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (con_type == NAT_CON_TYPE_LU)
 | 
						if (con_type == NAT_CON_TYPE_LU)
 | 
				
			||||||
		payload = gsm48_create_loc_upd_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)
 | 
						else if (con_type == NAT_CON_TYPE_CM_SERV_REQ || con_type == NAT_CON_TYPE_SSA)
 | 
				
			||||||
		payload = gsm48_create_mm_serv_rej(GSM48_REJECT_PLMN_NOT_ALLOWED);
 | 
							payload = gsm48_create_mm_serv_rej(cause->cm_reject_cause);
 | 
				
			||||||
	else {
 | 
						else {
 | 
				
			||||||
		LOGP(DNAT, LOGL_ERROR, "Unknown connection type: %d\n", con_type);
 | 
							LOGP(DNAT, LOGL_ERROR, "Unknown connection type: %d\n", con_type);
 | 
				
			||||||
		payload = NULL;
 | 
							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;
 | 
						struct bsc_connection *con_bsc = NULL;
 | 
				
			||||||
	int con_type;
 | 
						int con_type;
 | 
				
			||||||
	struct bsc_nat_parsed *parsed;
 | 
						struct bsc_nat_parsed *parsed;
 | 
				
			||||||
 | 
						struct bsc_nat_reject_cause cause;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Parse and filter messages */
 | 
						/* Parse and filter messages */
 | 
				
			||||||
	parsed = bsc_nat_parse(msg);
 | 
						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;
 | 
							struct sccp_connections *con;
 | 
				
			||||||
		switch (parsed->sccp_type) {
 | 
							switch (parsed->sccp_type) {
 | 
				
			||||||
		case SCCP_MSG_TYPE_CR:
 | 
							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) {
 | 
								if (filter < 0) {
 | 
				
			||||||
				bsc_stat_reject(filter, bsc, 0);
 | 
									bsc_stat_reject(filter, bsc, 0);
 | 
				
			||||||
				goto exit3;
 | 
									goto exit3;
 | 
				
			||||||
@@ -1038,10 +1044,12 @@ static int forward_sccp_to_msc(struct bsc_connection *bsc, struct msgb *msg)
 | 
				
			|||||||
			if (con) {
 | 
								if (con) {
 | 
				
			||||||
				/* only filter non local connections */
 | 
									/* only filter non local connections */
 | 
				
			||||||
				if (!con->con_local) {
 | 
									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) {
 | 
										if (filter < 0) {
 | 
				
			||||||
						bsc_stat_reject(filter, bsc, 1);
 | 
											bsc_stat_reject(filter, bsc, 1);
 | 
				
			||||||
						bsc_send_con_release(bsc, con);
 | 
											bsc_send_con_release(bsc, con, &cause);
 | 
				
			||||||
						con = NULL;
 | 
											con = NULL;
 | 
				
			||||||
						goto exit2;
 | 
											goto exit2;
 | 
				
			||||||
					}
 | 
										}
 | 
				
			||||||
@@ -1156,7 +1164,7 @@ exit3:
 | 
				
			|||||||
	/* send a SCCP Connection Refused */
 | 
						/* send a SCCP Connection Refused */
 | 
				
			||||||
	if (imsi)
 | 
						if (imsi)
 | 
				
			||||||
		talloc_free(imsi);
 | 
							talloc_free(imsi);
 | 
				
			||||||
	bsc_send_con_refuse(bsc, parsed, con_type);
 | 
						bsc_send_con_refuse(bsc, parsed, con_type, &cause);
 | 
				
			||||||
	talloc_free(parsed);
 | 
						talloc_free(parsed);
 | 
				
			||||||
	msgb_free(msg);
 | 
						msgb_free(msg);
 | 
				
			||||||
	return -1;
 | 
						return -1;
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										435
									
								
								openbsc/src/osmo-bsc_nat/bsc_nat_filter.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										435
									
								
								openbsc/src/osmo-bsc_nat/bsc_nat_filter.c
									
									
									
									
									
										Normal file
									
								
							@@ -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);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										628
									
								
								openbsc/src/osmo-bsc_nat/bsc_nat_rewrite.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										628
									
								
								openbsc/src/osmo-bsc_nat/bsc_nat_rewrite.c
									
									
									
									
									
										Normal file
									
								
							@@ -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);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -374,259 +374,6 @@ int bsc_nat_lst_check_allow(struct bsc_nat_acc_lst *lst, const char *mi_string)
 | 
				
			|||||||
	return 1;
 | 
						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 gsm48_hdr *bsc_unpack_dtap(struct bsc_nat_parsed *parsed,
 | 
				
			||||||
				  struct msgb *msg, uint32_t *len)
 | 
									  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;
 | 
						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 [] = {
 | 
					static const char *con_types [] = {
 | 
				
			||||||
	[NAT_CON_TYPE_NONE] = "n/a",
 | 
						[NAT_CON_TYPE_NONE] = "n/a",
 | 
				
			||||||
	[NAT_CON_TYPE_LU] = "Location Update",
 | 
						[NAT_CON_TYPE_LU] = "Location Update",
 | 
				
			||||||
@@ -782,592 +501,3 @@ int bsc_write_cb(struct osmo_fd *bfd, struct msgb *msg)
 | 
				
			|||||||
	return rc;
 | 
						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);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,6 @@
 | 
				
			|||||||
/* OpenBSC NAT interface to quagga VTY */
 | 
					/* OpenBSC NAT interface to quagga VTY */
 | 
				
			||||||
/* (C) 2010-2011 by Holger Hans Peter Freyther
 | 
					/* (C) 2010-2012 by Holger Hans Peter Freyther
 | 
				
			||||||
 * (C) 2010-2011 by On-Waves
 | 
					 * (C) 2010-2012 by On-Waves
 | 
				
			||||||
 * All Rights Reserved
 | 
					 * All Rights Reserved
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * This program is free software; you can redistribute it and/or modify
 | 
					 * 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);
 | 
						vty_out(vty, " ip-dscp %d%s", _nat->bsc_ip_dscp, VTY_NEWLINE);
 | 
				
			||||||
	if (_nat->acc_lst_name)
 | 
						if (_nat->acc_lst_name)
 | 
				
			||||||
		vty_out(vty, " access-list-name %s%s", _nat->acc_lst_name, VTY_NEWLINE);
 | 
							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)
 | 
						if (_nat->ussd_lst_name)
 | 
				
			||||||
		vty_out(vty, " ussd-list-name %s%s", _nat->ussd_lst_name, VTY_NEWLINE);
 | 
							vty_out(vty, " ussd-list-name %s%s", _nat->ussd_lst_name, VTY_NEWLINE);
 | 
				
			||||||
	if (_nat->ussd_query)
 | 
						if (_nat->ussd_query)
 | 
				
			||||||
@@ -486,6 +489,42 @@ DEFUN(cfg_nat_no_acc_lst_name,
 | 
				
			|||||||
	return CMD_SUCCESS;
 | 
						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,
 | 
					static int replace_rules(struct bsc_nat *nat, char **name,
 | 
				
			||||||
			 struct llist_head *head, const char *file)
 | 
								 struct llist_head *head, const char *file)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@@ -774,6 +813,27 @@ DEFUN(show_acc_lst,
 | 
				
			|||||||
	return CMD_SUCCESS;
 | 
						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,
 | 
					DEFUN(cfg_bsc_acc_lst_name,
 | 
				
			||||||
      cfg_bsc_acc_lst_name_cmd,
 | 
					      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(&test_regex_cmd);
 | 
				
			||||||
	install_element_ve(&show_bsc_mgcp_cmd);
 | 
						install_element_ve(&show_bsc_mgcp_cmd);
 | 
				
			||||||
	install_element_ve(&show_acc_lst_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, &set_last_endp_cmd);
 | 
				
			||||||
	install_element(ENABLE_NODE, &block_new_conn_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_bsc_ip_tos_cmd);
 | 
				
			||||||
	install_element(NAT_NODE, &cfg_nat_acc_lst_name_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_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_lst_name_cmd);
 | 
				
			||||||
	install_element(NAT_NODE, &cfg_nat_ussd_query_cmd);
 | 
						install_element(NAT_NODE, &cfg_nat_ussd_query_cmd);
 | 
				
			||||||
	install_element(NAT_NODE, &cfg_nat_ussd_token_cmd);
 | 
						install_element(NAT_NODE, &cfg_nat_ussd_token_cmd);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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_CFLAGS=-Wall -ggdb3 $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOSCCP_CFLAGS) $(LIBOSMOABIS_CFLAGS) $(COVERAGE_CFLAGS)
 | 
				
			||||||
AM_LDFLAGS = $(COVERAGE_LDFLAGS)
 | 
					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
 | 
					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_filter.c \
 | 
				
			||||||
			$(top_srcdir)/src/osmo-bsc_nat/bsc_sccp.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_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
 | 
								$(top_srcdir)/src/osmo-bsc_nat/bsc_mgcp_utils.c
 | 
				
			||||||
bsc_nat_test_LDADD = $(top_builddir)/src/libbsc/libbsc.a \
 | 
					bsc_nat_test_LDADD = $(top_builddir)/src/libbsc/libbsc.a \
 | 
				
			||||||
			$(top_srcdir)/src/libmgcp/libmgcp.a \
 | 
								$(top_srcdir)/src/libmgcp/libmgcp.a \
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										12
									
								
								openbsc/tests/bsc-nat/barr.cfg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								openbsc/tests/bsc-nat/barr.cfg
									
									
									
									
									
										Normal file
									
								
							@@ -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:
 | 
				
			||||||
							
								
								
									
										2
									
								
								openbsc/tests/bsc-nat/barr_dup.cfg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								openbsc/tests/bsc-nat/barr_dup.cfg
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,2 @@
 | 
				
			|||||||
 | 
					12123124:3:2:
 | 
				
			||||||
 | 
					12123124:3:2:
 | 
				
			||||||
@@ -1,8 +1,8 @@
 | 
				
			|||||||
/*
 | 
					/*
 | 
				
			||||||
 * BSC NAT Message filtering
 | 
					 * BSC NAT Message filtering
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
 | 
					 * (C) 2010-2012 by Holger Hans Peter Freyther <zecke@selfish.org>
 | 
				
			||||||
 * (C) 2010 by On-Waves
 | 
					 * (C) 2010-2012 by On-Waves
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * All Rights Reserved
 | 
					 * All Rights Reserved
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
@@ -761,6 +761,7 @@ static void test_cr_filter()
 | 
				
			|||||||
	struct bsc_nat_parsed *parsed;
 | 
						struct bsc_nat_parsed *parsed;
 | 
				
			||||||
	struct bsc_nat_acc_lst *nat_lst, *bsc_lst;
 | 
						struct bsc_nat_acc_lst *nat_lst, *bsc_lst;
 | 
				
			||||||
	struct bsc_nat_acc_lst_entry *nat_entry, *bsc_entry;
 | 
						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_nat *nat = bsc_nat_alloc();
 | 
				
			||||||
	struct bsc_connection *bsc = bsc_connection_alloc(nat);
 | 
						struct bsc_connection *bsc = bsc_connection_alloc(nat);
 | 
				
			||||||
@@ -802,7 +803,8 @@ static void test_cr_filter()
 | 
				
			|||||||
			abort();
 | 
								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) {
 | 
							if (res != cr_filter[i].result) {
 | 
				
			||||||
			printf("FAIL: Wrong result %d for test %d.\n", res, i);
 | 
								printf("FAIL: Wrong result %d for test %d.\n", res, i);
 | 
				
			||||||
			abort();
 | 
								abort();
 | 
				
			||||||
@@ -825,6 +827,7 @@ static void test_dt_filter()
 | 
				
			|||||||
	int i;
 | 
						int i;
 | 
				
			||||||
	struct msgb *msg = msgb_alloc(4096, "test_dt_filter");
 | 
						struct msgb *msg = msgb_alloc(4096, "test_dt_filter");
 | 
				
			||||||
	struct bsc_nat_parsed *parsed;
 | 
						struct bsc_nat_parsed *parsed;
 | 
				
			||||||
 | 
						struct bsc_nat_reject_cause cause;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	struct bsc_nat *nat = bsc_nat_alloc();
 | 
						struct bsc_nat *nat = bsc_nat_alloc();
 | 
				
			||||||
	struct bsc_connection *bsc = bsc_connection_alloc(nat);
 | 
						struct bsc_connection *bsc = bsc_connection_alloc(nat);
 | 
				
			||||||
@@ -854,7 +857,8 @@ static void test_dt_filter()
 | 
				
			|||||||
		abort();
 | 
							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");
 | 
							printf("FAIL: Should have passed..\n");
 | 
				
			||||||
		abort();
 | 
							abort();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -869,7 +873,8 @@ static void test_dt_filter()
 | 
				
			|||||||
			continue;
 | 
								continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		con->imsi_checked = 0;
 | 
							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);
 | 
						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)
 | 
					int main(int argc, char **argv)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	sccp_set_log_area(DSCCP);
 | 
						sccp_set_log_area(DSCCP);
 | 
				
			||||||
@@ -1166,6 +1237,7 @@ int main(int argc, char **argv)
 | 
				
			|||||||
	test_sms_smsc_rewrite();
 | 
						test_sms_smsc_rewrite();
 | 
				
			||||||
	test_sms_number_rewrite();
 | 
						test_sms_number_rewrite();
 | 
				
			||||||
	test_mgcp_allocations();
 | 
						test_mgcp_allocations();
 | 
				
			||||||
 | 
						test_barr_list_parsing();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	printf("Testing execution completed.\n");
 | 
						printf("Testing execution completed.\n");
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -22,4 +22,17 @@ Testing SMSC rewriting.
 | 
				
			|||||||
Attempting to only rewrite the HDR
 | 
					Attempting to only rewrite the HDR
 | 
				
			||||||
Attempting to change nothing.
 | 
					Attempting to change nothing.
 | 
				
			||||||
Testing SMS TP-DA rewriting.
 | 
					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.
 | 
					Testing execution completed.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -34,6 +34,8 @@ AT_CLEANUP
 | 
				
			|||||||
AT_SETUP([bsc-nat])
 | 
					AT_SETUP([bsc-nat])
 | 
				
			||||||
AT_KEYWORDS([bsc-nat])
 | 
					AT_KEYWORDS([bsc-nat])
 | 
				
			||||||
AT_CHECK([test "$enable_nat_test" != no || exit 77])
 | 
					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
 | 
					cat $abs_srcdir/bsc-nat/bsc_nat_test.ok > expout
 | 
				
			||||||
AT_CHECK([$abs_top_builddir/tests/bsc-nat/bsc_nat_test], [], [expout], [ignore])
 | 
					AT_CHECK([$abs_top_builddir/tests/bsc-nat/bsc_nat_test], [], [expout], [ignore])
 | 
				
			||||||
AT_CLEANUP
 | 
					AT_CLEANUP
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user