remove remaining bits of osmo-bsc_nat
osmo-bsc_nat is too heavily tied into legacy SCCPlite code, as it is not using libosmo-sigtran/osmo_ss7 so far. It's also full of customer-specific code and it's shared use of some libbsc code here has been complicating osmo-bsc development. The current plan is to continue to use osmo-bsc_nat from openbsc.git for those legacy users that need it, and not use osmo-bsc_nat in new 3GPP AoIP setups. Should we ever get a strong demand for an AoIP based bsc_nat, we can still revisit this later. Change-Id: Ia05dc76336a64a7f08962843b9a7cc19f2c83387
This commit is contained in:
parent
7b897dfea5
commit
1c9b8b1917
|
@ -49,10 +49,6 @@ PKG_CHECK_MODULES(LIBOSMOSIGTRAN, libosmo-sigtran >= 0.9.0)
|
|||
PKG_CHECK_MODULES(LIBOSMOSCCP, libosmo-sccp >= 0.9.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOMGCPCLIENT, libosmo-mgcp-client >= 1.3.0)
|
||||
|
||||
#NOTE: osmo-bsc does not depend on libosmo-legacy-mgcp anymore, but we still
|
||||
# need the dependancy for osmo-bsc-nat, which still uses the old API.
|
||||
PKG_CHECK_MODULES(LIBOSMOLEGACYMGCP, libosmo-legacy-mgcp >= 1.3.0)
|
||||
|
||||
dnl checks for header files
|
||||
AC_HEADER_STDC
|
||||
|
||||
|
@ -182,8 +178,6 @@ AC_OUTPUT(
|
|||
tests/gsm0408/Makefile
|
||||
tests/channel/Makefile
|
||||
tests/bsc/Makefile
|
||||
tests/bsc-nat/Makefile
|
||||
tests/bsc-nat-trie/Makefile
|
||||
tests/abis/Makefile
|
||||
tests/subscr/Makefile
|
||||
tests/nanobts_omlattr/Makefile
|
||||
|
|
|
@ -16,7 +16,6 @@ Build-Depends: debhelper (>=9),
|
|||
libosmo-sigtran-dev (>= 0.8.0),
|
||||
libosmo-abis-dev (>= 0.3.2),
|
||||
libosmo-netif-dev (>= 0.1.0),
|
||||
libosmo-legacy-mgcp-dev (>= 1.0.0),
|
||||
libosmo-mgcp-client-dev (>= 1.2.0)
|
||||
Standards-Version: 3.9.8
|
||||
Vcs-Git: git://git.osmocom.org/osmo-bsc.git
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
678012512671923:6:6:
|
|
@ -1,13 +0,0 @@
|
|||
nat
|
||||
bsc 0
|
||||
token lol
|
||||
location_area_code 1234
|
||||
description bsc
|
||||
max-endpoints 32
|
||||
paging forbidden 0
|
||||
bsc 1
|
||||
token wat
|
||||
location_area_code 5678
|
||||
description bsc
|
||||
max-endpoints 32
|
||||
paging forbidden 0
|
|
@ -1,66 +0,0 @@
|
|||
!
|
||||
! OsmoBSCNAT (0.12.0.266-2daa9) configuration saved from vty
|
||||
!!
|
||||
!
|
||||
log stderr
|
||||
logging filter all 1
|
||||
logging color 1
|
||||
logging timestamp 0
|
||||
logging level all debug
|
||||
logging level rll notice
|
||||
logging level cc notice
|
||||
logging level mm notice
|
||||
logging level rr notice
|
||||
logging level rsl notice
|
||||
logging level nm info
|
||||
logging level mncc notice
|
||||
logging level pag notice
|
||||
logging level meas notice
|
||||
logging level sccp notice
|
||||
logging level msc notice
|
||||
logging level mgcp notice
|
||||
logging level ho notice
|
||||
logging level db notice
|
||||
logging level ref notice
|
||||
logging level gprs debug
|
||||
logging level ns info
|
||||
logging level bssgp debug
|
||||
logging level llc debug
|
||||
logging level sndcp debug
|
||||
logging level nat notice
|
||||
logging level ctrl notice
|
||||
logging level smpp debug
|
||||
logging level lglobal notice
|
||||
logging level llapd notice
|
||||
logging level linp notice
|
||||
logging level lmux notice
|
||||
logging level lmi notice
|
||||
logging level lmib notice
|
||||
logging level lsms notice
|
||||
!
|
||||
line vty
|
||||
no login
|
||||
!
|
||||
mgcp
|
||||
bind ip 0.0.0.0
|
||||
bind port 2427
|
||||
rtp bts-base 4000
|
||||
rtp net-base 16000
|
||||
rtp ip-dscp 0
|
||||
no rtcp-omit
|
||||
sdp audio-payload number 126
|
||||
sdp audio-payload name AMR/8000
|
||||
loop 0
|
||||
number endpoints 1
|
||||
call-agent ip 127.0.0.1
|
||||
rtp transcoder-base 0
|
||||
transcoder-remote-base 4000
|
||||
nat
|
||||
msc ip 127.0.0.1
|
||||
msc port 5000
|
||||
timeout auth 2
|
||||
timeout ping 20
|
||||
timeout pong 5
|
||||
ip-dscp 0
|
||||
bscs-config-file bscs.cfg
|
||||
access-list bla imsi-allow ^11$
|
|
@ -7,9 +7,6 @@ noinst_HEADERS = \
|
|||
arfcn_range_encode.h \
|
||||
bsc_msc.h \
|
||||
bsc_msg_filter.h \
|
||||
bsc_nat.h \
|
||||
bsc_nat_callstats.h \
|
||||
bsc_nat_sccp.h \
|
||||
bsc_rll.h \
|
||||
bsc_subscriber.h \
|
||||
bsc_subscr_conn_fsm.h \
|
||||
|
@ -32,7 +29,6 @@ noinst_HEADERS = \
|
|||
meas_feed.h \
|
||||
meas_rep.h \
|
||||
misdn.h \
|
||||
nat_rewrite_trie.h \
|
||||
network_listen.h \
|
||||
openbscdefines.h \
|
||||
osmo_bsc.h \
|
||||
|
|
|
@ -1,469 +0,0 @@
|
|||
/*
|
||||
* (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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef BSC_NAT_H
|
||||
#define BSC_NAT_H
|
||||
|
||||
#include <osmocom/legacy_mgcp/mgcp.h>
|
||||
|
||||
#include "bsc_msg_filter.h"
|
||||
|
||||
#include <osmocom/core/select.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/core/msgfile.h>
|
||||
#include <osmocom/core/timer.h>
|
||||
#include <osmocom/core/write_queue.h>
|
||||
#include <osmocom/core/rate_ctr.h>
|
||||
#include <osmocom/core/statistics.h>
|
||||
#include <osmocom/gsm/protocol/gsm_04_08.h>
|
||||
|
||||
#include <regex.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#define DIR_BSC 1
|
||||
#define DIR_MSC 2
|
||||
|
||||
#define PAGIN_GROUP_UNASSIGNED -1
|
||||
|
||||
struct sccp_source_reference;
|
||||
struct nat_sccp_connection;
|
||||
struct bsc_nat_parsed;
|
||||
struct bsc_nat;
|
||||
struct bsc_nat_ussd_con;
|
||||
struct nat_rewrite_rule;
|
||||
|
||||
/*
|
||||
* Is this terminated to the MSC, to the local machine (release
|
||||
* handling for IMSI filtering) or to a USSD provider?
|
||||
*/
|
||||
enum {
|
||||
NAT_CON_END_MSC,
|
||||
NAT_CON_END_LOCAL,
|
||||
NAT_CON_END_USSD,
|
||||
};
|
||||
|
||||
/*
|
||||
* Pending command entry
|
||||
*/
|
||||
struct bsc_cmd_list {
|
||||
struct llist_head list_entry;
|
||||
|
||||
struct osmo_timer_list timeout;
|
||||
|
||||
/* The NATed ID used on the bsc_con*/
|
||||
int nat_id;
|
||||
|
||||
/* The command from the control connection */
|
||||
struct ctrl_cmd *cmd;
|
||||
};
|
||||
|
||||
/*
|
||||
* Per BSC data structure
|
||||
*/
|
||||
struct bsc_connection {
|
||||
struct llist_head list_entry;
|
||||
|
||||
/* do we know anything about this BSC? */
|
||||
int authenticated;
|
||||
uint8_t last_rand[16];
|
||||
|
||||
/* the fd we use to communicate */
|
||||
struct osmo_wqueue write_queue;
|
||||
|
||||
/* incoming message buffer */
|
||||
struct msgb *pending_msg;
|
||||
|
||||
/* the BSS associated */
|
||||
struct bsc_config *cfg;
|
||||
|
||||
/* a timeout node */
|
||||
struct osmo_timer_list id_timeout;
|
||||
|
||||
/* pong timeout */
|
||||
struct osmo_timer_list ping_timeout;
|
||||
struct osmo_timer_list pong_timeout;
|
||||
|
||||
/* mgcp related code */
|
||||
char *_endpoint_status;
|
||||
int number_multiplexes;
|
||||
int max_endpoints;
|
||||
int last_endpoint;
|
||||
int next_transaction;
|
||||
uint32_t pending_dlcx_count;
|
||||
struct llist_head pending_dlcx;
|
||||
|
||||
/* track the pending commands for this BSC */
|
||||
struct llist_head cmd_pending;
|
||||
int last_id;
|
||||
|
||||
/* a back pointer */
|
||||
struct bsc_nat *nat;
|
||||
};
|
||||
|
||||
/**
|
||||
* Stats per BSC
|
||||
*/
|
||||
struct bsc_config_stats {
|
||||
struct rate_ctr_group *ctrg;
|
||||
};
|
||||
|
||||
enum bsc_cfg_ctr {
|
||||
BCFG_CTR_SCCP_CONN,
|
||||
BCFG_CTR_SCCP_CALLS,
|
||||
BCFG_CTR_NET_RECONN,
|
||||
BCFG_CTR_DROPPED_SCCP,
|
||||
BCFG_CTR_DROPPED_CALLS,
|
||||
BCFG_CTR_REJECTED_CR,
|
||||
BCFG_CTR_REJECTED_MSG,
|
||||
BCFG_CTR_ILL_PACKET,
|
||||
BCFG_CTR_CON_TYPE_LU,
|
||||
BCFG_CTR_CON_CMSERV_RQ,
|
||||
BCFG_CTR_CON_PAG_RESP,
|
||||
BCFG_CTR_CON_SSA,
|
||||
BCFG_CTR_CON_OTHER,
|
||||
};
|
||||
|
||||
/**
|
||||
* One BSC entry in the config
|
||||
*/
|
||||
struct bsc_config {
|
||||
struct llist_head entry;
|
||||
|
||||
uint8_t key[16];
|
||||
uint8_t key_present;
|
||||
char *token;
|
||||
int nr;
|
||||
|
||||
char *description;
|
||||
|
||||
/* imsi white and blacklist */
|
||||
char *acc_lst_name;
|
||||
|
||||
int forbid_paging;
|
||||
int paging_group;
|
||||
|
||||
/* audio handling */
|
||||
int max_endpoints;
|
||||
|
||||
/* used internally for reload handling */
|
||||
bool remove;
|
||||
bool token_updated;
|
||||
|
||||
/* backpointer */
|
||||
struct bsc_nat *nat;
|
||||
|
||||
struct bsc_config_stats stats;
|
||||
|
||||
struct llist_head lac_list;
|
||||
|
||||
/* Osmux is enabled/disabled per BSC */
|
||||
int osmux;
|
||||
|
||||
/* Use a jitterbuffer on the bts-side receiver */
|
||||
bool bts_use_jibuf;
|
||||
/* Minimum and maximum buffer size for the jitter buffer, in ms */
|
||||
uint32_t bts_jitter_delay_min;
|
||||
uint32_t bts_jitter_delay_max;
|
||||
/* Enabled if explicitly configured through VTY: */
|
||||
bool bts_use_jibuf_override;
|
||||
bool bts_jitter_delay_min_override;
|
||||
bool bts_jitter_delay_max_override;
|
||||
};
|
||||
|
||||
struct bsc_lac_entry {
|
||||
struct llist_head entry;
|
||||
uint16_t lac;
|
||||
};
|
||||
|
||||
struct bsc_nat_paging_group {
|
||||
struct llist_head entry;
|
||||
|
||||
/* list of lac entries */
|
||||
struct llist_head lists;
|
||||
int nr;
|
||||
};
|
||||
|
||||
/**
|
||||
* BSCs point of view of endpoints
|
||||
*/
|
||||
struct bsc_endpoint {
|
||||
/* the operation that is carried out */
|
||||
int transaction_state;
|
||||
/* the pending transaction id */
|
||||
char *transaction_id;
|
||||
/* the bsc we are talking to */
|
||||
struct bsc_connection *bsc;
|
||||
};
|
||||
|
||||
/**
|
||||
* Statistic for the nat.
|
||||
*/
|
||||
struct bsc_nat_statistics {
|
||||
struct {
|
||||
struct osmo_counter *conn;
|
||||
struct osmo_counter *calls;
|
||||
} sccp;
|
||||
|
||||
struct {
|
||||
struct osmo_counter *reconn;
|
||||
struct osmo_counter *auth_fail;
|
||||
} bsc;
|
||||
|
||||
struct {
|
||||
struct osmo_counter *reconn;
|
||||
} msc;
|
||||
|
||||
struct {
|
||||
struct osmo_counter *reconn;
|
||||
} ussd;
|
||||
};
|
||||
|
||||
/**
|
||||
* the structure of the "nat" network
|
||||
*/
|
||||
struct bsc_nat {
|
||||
/* active SCCP connections that need patching */
|
||||
struct llist_head sccp_connections;
|
||||
|
||||
/* active BSC connections that need patching */
|
||||
struct llist_head bsc_connections;
|
||||
|
||||
/* access lists */
|
||||
struct llist_head access_lists;
|
||||
|
||||
/* paging groups */
|
||||
struct llist_head paging_groups;
|
||||
|
||||
/* known BSC's */
|
||||
struct llist_head bsc_configs;
|
||||
int num_bsc;
|
||||
int bsc_ip_dscp;
|
||||
|
||||
/* MGCP config */
|
||||
struct mgcp_config *mgcp_cfg;
|
||||
uint8_t mgcp_msg[4096];
|
||||
int mgcp_length;
|
||||
int mgcp_ipa;
|
||||
int sdp_ensure_amr_mode_set;
|
||||
|
||||
/* msc things */
|
||||
struct llist_head dests;
|
||||
struct bsc_msc_dest *main_dest;
|
||||
struct bsc_msc_connection *msc_con;
|
||||
char *token;
|
||||
|
||||
/* timeouts */
|
||||
int auth_timeout;
|
||||
int ping_timeout;
|
||||
int pong_timeout;
|
||||
|
||||
struct bsc_endpoint *bsc_endpoints;
|
||||
|
||||
/* path to file with BSC config */
|
||||
char *include_file;
|
||||
char *include_base;
|
||||
char *resolved_path;
|
||||
|
||||
/* filter */
|
||||
char *acc_lst_name;
|
||||
|
||||
/* Barring of subscribers with a rb tree */
|
||||
struct rb_root imsi_black_list;
|
||||
char *imsi_black_list_fn;
|
||||
|
||||
/* number rewriting */
|
||||
char *num_rewr_name;
|
||||
struct llist_head num_rewr;
|
||||
char *num_rewr_post_name;
|
||||
struct llist_head num_rewr_post;
|
||||
|
||||
char *smsc_rewr_name;
|
||||
struct llist_head smsc_rewr;
|
||||
char *tpdest_match_name;
|
||||
struct llist_head tpdest_match;
|
||||
char *sms_clear_tp_srr_name;
|
||||
struct llist_head sms_clear_tp_srr;
|
||||
char *sms_num_rewr_name;
|
||||
struct llist_head sms_num_rewr;
|
||||
|
||||
/* more rewriting */
|
||||
char *num_rewr_trie_name;
|
||||
struct nat_rewrite *num_rewr_trie;
|
||||
|
||||
/* USSD messages we want to match */
|
||||
char *ussd_lst_name;
|
||||
char *ussd_query;
|
||||
regex_t ussd_query_re;
|
||||
char *ussd_token;
|
||||
char *ussd_local;
|
||||
struct osmo_fd ussd_listen;
|
||||
struct bsc_nat_ussd_con *ussd_con;
|
||||
|
||||
/* for maintainenance */
|
||||
int blocked;
|
||||
|
||||
/* statistics */
|
||||
struct bsc_nat_statistics stats;
|
||||
|
||||
/* control interface */
|
||||
struct ctrl_handle *ctrl;
|
||||
};
|
||||
|
||||
struct bsc_nat_ussd_con {
|
||||
struct osmo_wqueue queue;
|
||||
struct bsc_nat *nat;
|
||||
int authorized;
|
||||
|
||||
struct msgb *pending_msg;
|
||||
|
||||
struct osmo_timer_list auth_timeout;
|
||||
};
|
||||
|
||||
/* create and init the structures */
|
||||
struct bsc_config *bsc_config_alloc(struct bsc_nat *nat, const char *token,
|
||||
unsigned int number);
|
||||
struct bsc_config *bsc_config_num(struct bsc_nat *nat, int num);
|
||||
struct bsc_config *bsc_config_by_token(struct bsc_nat *nat, const char *token, int len);
|
||||
void bsc_config_free(struct bsc_config *);
|
||||
void bsc_config_add_lac(struct bsc_config *cfg, int lac);
|
||||
void bsc_config_del_lac(struct bsc_config *cfg, int lac);
|
||||
int bsc_config_handles_lac(struct bsc_config *cfg, int lac);
|
||||
|
||||
struct bsc_nat *bsc_nat_alloc(void);
|
||||
struct bsc_connection *bsc_connection_alloc(struct bsc_nat *nat);
|
||||
void bsc_nat_set_msc_ip(struct bsc_nat *bsc, const char *ip);
|
||||
|
||||
void sccp_connection_destroy(struct nat_sccp_connection *);
|
||||
void bsc_close_connection(struct bsc_connection *);
|
||||
|
||||
const char *bsc_con_type_to_string(int type);
|
||||
|
||||
/**
|
||||
* parse the given message into the above structure
|
||||
*/
|
||||
struct bsc_nat_parsed *bsc_nat_parse(struct msgb *msg);
|
||||
|
||||
/**
|
||||
* filter based on IP Access header in both directions
|
||||
*/
|
||||
int bsc_nat_filter_ipa(int direction, struct msgb *msg, struct bsc_nat_parsed *parsed);
|
||||
int bsc_nat_vty_init(struct bsc_nat *nat);
|
||||
int bsc_nat_find_paging(struct msgb *msg, const uint8_t **,int *len);
|
||||
|
||||
/**
|
||||
* SCCP patching and handling
|
||||
*/
|
||||
struct nat_sccp_connection *create_sccp_src_ref(struct bsc_connection *bsc, struct bsc_nat_parsed *parsed);
|
||||
int update_sccp_src_ref(struct nat_sccp_connection *sccp, struct bsc_nat_parsed *parsed);
|
||||
void remove_sccp_src_ref(struct bsc_connection *bsc, struct msgb *msg, struct bsc_nat_parsed *parsed);
|
||||
struct nat_sccp_connection *patch_sccp_src_ref_to_bsc(struct msgb *, struct bsc_nat_parsed *, struct bsc_nat *);
|
||||
struct nat_sccp_connection *patch_sccp_src_ref_to_msc(struct msgb *, struct bsc_nat_parsed *, struct bsc_connection *);
|
||||
struct nat_sccp_connection *bsc_nat_find_con_by_bsc(struct bsc_nat *, struct sccp_source_reference *);
|
||||
|
||||
/**
|
||||
* MGCP/Audio handling
|
||||
*/
|
||||
int bsc_mgcp_nr_multiplexes(int max_endpoints);
|
||||
int bsc_write_mgcp(struct bsc_connection *bsc, const uint8_t *data, unsigned int length);
|
||||
int bsc_mgcp_assign_patch(struct nat_sccp_connection *, struct msgb *msg);
|
||||
void bsc_mgcp_init(struct nat_sccp_connection *);
|
||||
void bsc_mgcp_dlcx(struct nat_sccp_connection *);
|
||||
void bsc_mgcp_free_endpoints(struct bsc_nat *nat);
|
||||
int bsc_mgcp_nat_init(struct bsc_nat *nat);
|
||||
|
||||
struct nat_sccp_connection *bsc_mgcp_find_con(struct bsc_nat *, int endpoint_number);
|
||||
struct msgb *bsc_mgcp_rewrite(char *input, int length, int endp, const char *ip,
|
||||
int port, int osmux, int *first_payload_type, int mode_set);
|
||||
void bsc_mgcp_forward(struct bsc_connection *bsc, struct msgb *msg);
|
||||
|
||||
void bsc_mgcp_clear_endpoints_for(struct bsc_connection *bsc);
|
||||
int bsc_mgcp_parse_response(const char *str, int *code, char transaction[60]);
|
||||
uint32_t bsc_mgcp_extract_ci(const char *resp);
|
||||
|
||||
|
||||
int bsc_write(struct bsc_connection *bsc, struct msgb *msg, int id);
|
||||
int bsc_do_write(struct osmo_wqueue *queue, struct msgb *msg, int id);
|
||||
int bsc_write_msg(struct osmo_wqueue *queue, struct msgb *msg);
|
||||
int bsc_write_cb(struct osmo_fd *bfd, struct msgb *msg);
|
||||
|
||||
int bsc_nat_msc_is_connected(struct bsc_nat *nat);
|
||||
|
||||
int bsc_conn_type_to_ctr(struct nat_sccp_connection *conn);
|
||||
|
||||
struct gsm48_hdr *bsc_unpack_dtap(struct bsc_nat_parsed *parsed, struct msgb *msg, uint32_t *len);
|
||||
|
||||
/** USSD filtering */
|
||||
int bsc_ussd_init(struct bsc_nat *nat);
|
||||
int bsc_ussd_check(struct nat_sccp_connection *con, struct bsc_nat_parsed *parsed, struct msgb *msg);
|
||||
int bsc_ussd_close_connections(struct bsc_nat *nat);
|
||||
|
||||
struct msgb *bsc_nat_rewrite_msg(struct bsc_nat *nat, struct msgb *msg, struct bsc_nat_parsed *, const char *imsi);
|
||||
|
||||
/** paging group handling */
|
||||
struct bsc_nat_paging_group *bsc_nat_paging_group_num(struct bsc_nat *nat, int group);
|
||||
struct bsc_nat_paging_group *bsc_nat_paging_group_create(struct bsc_nat *nat, int group);
|
||||
void bsc_nat_paging_group_delete(struct bsc_nat_paging_group *);
|
||||
void bsc_nat_paging_group_add_lac(struct bsc_nat_paging_group *grp, int lac);
|
||||
void bsc_nat_paging_group_del_lac(struct bsc_nat_paging_group *grp, int lac);
|
||||
|
||||
/**
|
||||
* Number rewriting support below
|
||||
*/
|
||||
struct bsc_nat_num_rewr_entry {
|
||||
struct llist_head list;
|
||||
|
||||
regex_t msisdn_reg;
|
||||
regex_t num_reg;
|
||||
|
||||
char *replace;
|
||||
uint8_t is_prefix_lookup;
|
||||
};
|
||||
|
||||
void bsc_nat_num_rewr_entry_adapt(void *ctx, struct llist_head *head, const struct osmo_config_list *);
|
||||
|
||||
void bsc_nat_send_mgcp_to_msc(struct bsc_nat *bsc_nat, struct msgb *msg);
|
||||
void bsc_nat_handle_mgcp(struct bsc_nat *bsc, struct msgb *msg);
|
||||
|
||||
struct ctrl_handle *bsc_nat_controlif_setup(struct bsc_nat *nat,
|
||||
const char *bind_addr, int port);
|
||||
void bsc_nat_ctrl_del_pending(struct bsc_cmd_list *pending);
|
||||
int bsc_nat_handle_ctrlif_msg(struct bsc_connection *bsc, struct msgb *msg);
|
||||
|
||||
int bsc_nat_extract_lac(struct bsc_connection *bsc, struct nat_sccp_connection *con,
|
||||
struct bsc_nat_parsed *parsed, 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_filter_reject_cause *cause);
|
||||
int bsc_nat_filter_dt(struct bsc_connection *bsc, struct msgb *msg,
|
||||
struct nat_sccp_connection *con, struct bsc_nat_parsed *parsed,
|
||||
struct bsc_filter_reject_cause *cause);
|
||||
|
||||
/**
|
||||
* CTRL interface helper
|
||||
*/
|
||||
void bsc_nat_inform_reject(struct bsc_connection *bsc, const char *imsi);
|
||||
|
||||
/*
|
||||
* Use for testing
|
||||
*/
|
||||
void bsc_nat_free(struct bsc_nat *nat);
|
||||
|
||||
#endif
|
|
@ -1,55 +0,0 @@
|
|||
/*
|
||||
* (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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef BSC_NAT_CALLSTATS_H
|
||||
#define BSC_NAT_CALLSTATS_H
|
||||
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
|
||||
#include <osmocom/sccp/sccp_types.h>
|
||||
|
||||
struct bsc_nat_call_stats {
|
||||
struct llist_head entry;
|
||||
|
||||
struct sccp_source_reference remote_ref;
|
||||
struct sccp_source_reference src_ref; /* as seen by the MSC */
|
||||
|
||||
/* mgcp options */
|
||||
uint32_t ci;
|
||||
int bts_rtp_port;
|
||||
int net_rtp_port;
|
||||
struct in_addr bts_addr;
|
||||
struct in_addr net_addr;
|
||||
|
||||
|
||||
/* as witnessed by the NAT */
|
||||
uint32_t net_ps;
|
||||
uint32_t net_os;
|
||||
uint32_t bts_pr;
|
||||
uint32_t bts_or;
|
||||
uint32_t bts_expected;
|
||||
uint32_t bts_jitter;
|
||||
int bts_loss;
|
||||
|
||||
uint32_t trans_id;
|
||||
int msc_endpoint;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -1,105 +0,0 @@
|
|||
/* NAT utilities using SCCP types */
|
||||
/*
|
||||
* (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
|
||||
* (C) 2010 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef BSC_NAT_SCCP_H
|
||||
#define BSC_NAT_SCCP_H
|
||||
|
||||
#include "bsc_msg_filter.h"
|
||||
|
||||
#include <osmocom/sccp/sccp_types.h>
|
||||
|
||||
/*
|
||||
* For the NAT we will need to analyze and later patch
|
||||
* the received message. This would require us to parse
|
||||
* the IPA and SCCP header twice. Instead of doing this
|
||||
* we will have one analyze structure and have the patching
|
||||
* and filter operate on the same structure.
|
||||
*/
|
||||
struct bsc_nat_parsed {
|
||||
/* ip access prototype */
|
||||
int ipa_proto;
|
||||
|
||||
/* source local reference */
|
||||
struct sccp_source_reference *src_local_ref;
|
||||
|
||||
/* destination local reference */
|
||||
struct sccp_source_reference *dest_local_ref;
|
||||
|
||||
/* original value */
|
||||
struct sccp_source_reference original_dest_ref;
|
||||
|
||||
/* called ssn number */
|
||||
int called_ssn;
|
||||
|
||||
/* calling ssn number */
|
||||
int calling_ssn;
|
||||
|
||||
/* sccp message type */
|
||||
int sccp_type;
|
||||
|
||||
/* bssap type, e.g. 0 for BSS Management */
|
||||
int bssap;
|
||||
|
||||
/* the gsm0808 message type */
|
||||
int gsm_type;
|
||||
};
|
||||
|
||||
/*
|
||||
* Per SCCP source local reference patch table. It needs to
|
||||
* be updated on new SCCP connections, connection confirm and reject,
|
||||
* and on the loss of the BSC connection.
|
||||
*/
|
||||
struct nat_sccp_connection {
|
||||
struct llist_head list_entry;
|
||||
|
||||
struct bsc_connection *bsc;
|
||||
struct bsc_msc_connection *msc_con;
|
||||
|
||||
struct sccp_source_reference real_ref;
|
||||
struct sccp_source_reference patched_ref;
|
||||
struct sccp_source_reference remote_ref;
|
||||
int has_remote_ref;
|
||||
|
||||
/* status */
|
||||
int con_local;
|
||||
int authorized;
|
||||
|
||||
struct bsc_filter_state filter_state;
|
||||
|
||||
uint16_t lac;
|
||||
uint16_t ci;
|
||||
|
||||
/* remember which Transactions we run over the bypass */
|
||||
char ussd_ti[8];
|
||||
|
||||
/*
|
||||
* audio handling. Remember if we have ever send a CRCX,
|
||||
* remember the endpoint used by the MSC and BSC.
|
||||
*/
|
||||
int msc_endp;
|
||||
int bsc_endp;
|
||||
|
||||
/* timeout handling */
|
||||
struct timespec creation_time;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
|
@ -1,47 +0,0 @@
|
|||
/*
|
||||
* (C) 2013 by On-Waves
|
||||
* (C) 2013 by Holger Hans Peter Freyther <zecke@selfish.org>
|
||||
* 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/>.
|
||||
*
|
||||
*/
|
||||
#ifndef NAT_REWRITE_FILE_H
|
||||
#define NAT_REWRITE_FILE_H
|
||||
|
||||
#include <osmocom/core/linuxrbtree.h>
|
||||
|
||||
struct vty;
|
||||
|
||||
struct nat_rewrite_rule {
|
||||
/* For digits 0-9 and + */
|
||||
struct nat_rewrite_rule *rules[11];
|
||||
|
||||
char empty;
|
||||
char prefix[14];
|
||||
char rewrite[6];
|
||||
};
|
||||
|
||||
struct nat_rewrite {
|
||||
struct nat_rewrite_rule rule;
|
||||
size_t prefixes;
|
||||
};
|
||||
|
||||
|
||||
struct nat_rewrite *nat_rewrite_parse(void *ctx, const char *filename);
|
||||
struct nat_rewrite_rule *nat_rewrite_lookup(struct nat_rewrite *, const char *prefix);
|
||||
void nat_rewrite_dump(struct nat_rewrite *rewr);
|
||||
void nat_rewrite_dump_vty(struct vty *vty, struct nat_rewrite *rewr);
|
||||
|
||||
#endif
|
|
@ -19,9 +19,10 @@
|
|||
*/
|
||||
|
||||
#include <osmocom/bsc/bsc_msg_filter.h>
|
||||
#include <osmocom/bsc/bsc_nat.h>
|
||||
#include <osmocom/bsc/debug.h>
|
||||
|
||||
#include <osmocom/gsm/protocol/gsm_04_08.h>
|
||||
|
||||
#include <osmocom/core/rate_ctr.h>
|
||||
#include <osmocom/core/stats.h>
|
||||
|
||||
|
|
|
@ -23,7 +23,6 @@
|
|||
|
||||
#include <osmocom/bsc/bsc_msg_filter.h>
|
||||
|
||||
#include <osmocom/bsc/bsc_nat.h>
|
||||
#include <osmocom/bsc/bsc_msc.h>
|
||||
#include <osmocom/bsc/gsm_data.h>
|
||||
#include <osmocom/bsc/debug.h>
|
||||
|
|
|
@ -20,7 +20,6 @@
|
|||
*
|
||||
*/
|
||||
|
||||
#include <osmocom/bsc/bsc_nat.h>
|
||||
#include <osmocom/ctrl/control_cmd.h>
|
||||
#include <osmocom/ctrl/control_if.h>
|
||||
#include <osmocom/crypt/auth.h>
|
||||
|
|
|
@ -1,51 +0,0 @@
|
|||
AM_CPPFLAGS = \
|
||||
$(all_includes) \
|
||||
-I$(top_srcdir)/include \
|
||||
-I$(top_builddir) \
|
||||
$(NULL)
|
||||
|
||||
AM_CFLAGS = \
|
||||
-Wall \
|
||||
$(LIBOSMOCORE_CFLAGS) \
|
||||
$(LIBOSMOGSM_CFLAGS) \
|
||||
$(LIBOSMOVTY_CFLAGS) \
|
||||
$(LIBOSMOCTRL_CFLAGS) \
|
||||
$(LIBOSMOSCCP_CFLAGS) \
|
||||
$(LIBOSMOLEGACYMGCP_CFLAGS) \
|
||||
$(COVERAGE_CFLAGS) \
|
||||
$(NULL)
|
||||
|
||||
AM_LDFLAGS = \
|
||||
$(COVERAGE_LDFLAGS) \
|
||||
$(NULL)
|
||||
|
||||
bin_PROGRAMS = \
|
||||
osmo-bsc_nat \
|
||||
$(NULL)
|
||||
|
||||
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_rewrite.c \
|
||||
bsc_nat_rewrite_trie.c \
|
||||
bsc_nat_filter.c \
|
||||
$(NULL)
|
||||
|
||||
osmo_bsc_nat_LDADD = \
|
||||
$(top_builddir)/src/libfilter/libfilter.a \
|
||||
$(top_builddir)/src/libbsc/libbsc.a \
|
||||
$(LIBOSMOSCCP_LIBS) \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(LIBOSMOGSM_LIBS) \
|
||||
$(LIBOSMOVTY_LIBS) \
|
||||
$(LIBOSMOCTRL_LIBS) \
|
||||
$(LIBOSMOSIGTRAN_LIBS) \
|
||||
$(LIBRARY_GSM) \
|
||||
-lrt \
|
||||
$(NULL)
|
|
@ -1,220 +0,0 @@
|
|||
/* BSC Multiplexer/NAT */
|
||||
|
||||
/*
|
||||
* (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
|
||||
* (C) 2010 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 <string.h>
|
||||
|
||||
#include <osmocom/bsc/bsc_nat.h>
|
||||
#include <osmocom/bsc/bsc_nat_sccp.h>
|
||||
#include <osmocom/bsc/ipaccess.h>
|
||||
#include <osmocom/bsc/debug.h>
|
||||
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/gsm/protocol/gsm_08_08.h>
|
||||
|
||||
#include <osmocom/sccp/sccp.h>
|
||||
|
||||
/*
|
||||
* The idea is to have a simple struct describing a IPA packet with
|
||||
* SCCP SSN and the GSM 08.08 payload and decide. We will both have
|
||||
* a white and a blacklist of packets we want to handle.
|
||||
*
|
||||
* TODO: Implement a "NOT" in the filter language.
|
||||
*/
|
||||
|
||||
#define ALLOW_ANY -1
|
||||
|
||||
#define FILTER_TO_BSC 1
|
||||
#define FILTER_TO_MSC 2
|
||||
#define FILTER_TO_BOTH 3
|
||||
|
||||
|
||||
struct bsc_pkt_filter {
|
||||
int ipa_proto;
|
||||
int dest_ssn;
|
||||
int bssap;
|
||||
int gsm;
|
||||
int filter_dir;
|
||||
};
|
||||
|
||||
static struct bsc_pkt_filter black_list[] = {
|
||||
/* filter reset messages to the MSC */
|
||||
{ IPAC_PROTO_SCCP, SCCP_SSN_BSSAP, 0, BSS_MAP_MSG_RESET, FILTER_TO_MSC },
|
||||
|
||||
/* filter reset ack messages to the BSC */
|
||||
{ IPAC_PROTO_SCCP, SCCP_SSN_BSSAP, 0, BSS_MAP_MSG_RESET_ACKNOWLEDGE, FILTER_TO_BSC },
|
||||
|
||||
/* filter ip access */
|
||||
{ IPAC_PROTO_IPACCESS, ALLOW_ANY, ALLOW_ANY, ALLOW_ANY, FILTER_TO_MSC },
|
||||
};
|
||||
|
||||
static struct bsc_pkt_filter white_list[] = {
|
||||
/* allow IPAC_PROTO_SCCP messages to both sides */
|
||||
{ IPAC_PROTO_SCCP, ALLOW_ANY, ALLOW_ANY, ALLOW_ANY, FILTER_TO_BOTH },
|
||||
|
||||
/* allow MGCP messages to both sides */
|
||||
{ IPAC_PROTO_MGCP_OLD, ALLOW_ANY, ALLOW_ANY, ALLOW_ANY, FILTER_TO_BOTH },
|
||||
};
|
||||
|
||||
struct bsc_nat_parsed *bsc_nat_parse(struct msgb *msg)
|
||||
{
|
||||
struct sccp_parse_result result;
|
||||
struct bsc_nat_parsed *parsed;
|
||||
struct ipaccess_head *hh;
|
||||
|
||||
/* quick fail */
|
||||
if (msg->len < 4)
|
||||
return NULL;
|
||||
|
||||
parsed = talloc_zero(msg, struct bsc_nat_parsed);
|
||||
if (!parsed)
|
||||
return NULL;
|
||||
|
||||
/* more init */
|
||||
parsed->ipa_proto = parsed->called_ssn = parsed->calling_ssn = -1;
|
||||
parsed->sccp_type = parsed->bssap = parsed->gsm_type = -1;
|
||||
|
||||
/* start parsing */
|
||||
hh = (struct ipaccess_head *) msg->data;
|
||||
parsed->ipa_proto = hh->proto;
|
||||
|
||||
msg->l2h = &hh->data[0];
|
||||
|
||||
/* do a size check on the input */
|
||||
if (ntohs(hh->len) != msgb_l2len(msg)) {
|
||||
LOGP(DLINP, LOGL_ERROR, "Wrong input length?\n");
|
||||
talloc_free(parsed);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* analyze sccp down here */
|
||||
if (parsed->ipa_proto == IPAC_PROTO_SCCP) {
|
||||
memset(&result, 0, sizeof(result));
|
||||
if (sccp_parse_header(msg, &result) != 0) {
|
||||
talloc_free(parsed);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (msg->l3h && msgb_l3len(msg) < 3) {
|
||||
LOGP(DNAT, LOGL_ERROR, "Not enough space or GSM payload\n");
|
||||
talloc_free(parsed);
|
||||
return 0;
|
||||
}
|
||||
|
||||
parsed->sccp_type = sccp_determine_msg_type(msg);
|
||||
parsed->src_local_ref = result.source_local_reference;
|
||||
parsed->dest_local_ref = result.destination_local_reference;
|
||||
if (parsed->dest_local_ref)
|
||||
parsed->original_dest_ref = *parsed->dest_local_ref;
|
||||
parsed->called_ssn = result.called.ssn;
|
||||
parsed->calling_ssn = result.calling.ssn;
|
||||
|
||||
/* in case of connection confirm we have no payload */
|
||||
if (msg->l3h) {
|
||||
parsed->bssap = msg->l3h[0];
|
||||
parsed->gsm_type = msg->l3h[2];
|
||||
}
|
||||
}
|
||||
|
||||
return parsed;
|
||||
}
|
||||
|
||||
int bsc_nat_filter_ipa(int dir, struct msgb *msg, struct bsc_nat_parsed *parsed)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* go through the blacklist now */
|
||||
for (i = 0; i < ARRAY_SIZE(black_list); ++i) {
|
||||
/* ignore the rule? */
|
||||
if (black_list[i].filter_dir != FILTER_TO_BOTH
|
||||
&& black_list[i].filter_dir != dir)
|
||||
continue;
|
||||
|
||||
/* the proto is not blacklisted */
|
||||
if (black_list[i].ipa_proto != ALLOW_ANY
|
||||
&& black_list[i].ipa_proto != parsed->ipa_proto)
|
||||
continue;
|
||||
|
||||
if (parsed->ipa_proto == IPAC_PROTO_SCCP) {
|
||||
/* the SSN is not blacklisted */
|
||||
if (black_list[i].dest_ssn != ALLOW_ANY
|
||||
&& black_list[i].dest_ssn != parsed->called_ssn)
|
||||
continue;
|
||||
|
||||
/* bssap */
|
||||
if (black_list[i].bssap != ALLOW_ANY
|
||||
&& black_list[i].bssap != parsed->bssap)
|
||||
continue;
|
||||
|
||||
/* gsm */
|
||||
if (black_list[i].gsm != ALLOW_ANY
|
||||
&& black_list[i].gsm != parsed->gsm_type)
|
||||
continue;
|
||||
|
||||
/* blacklisted */
|
||||
LOGP(DNAT, LOGL_INFO, "Blacklisted with rule %d\n", i);
|
||||
return 1;
|
||||
} else {
|
||||
/* blacklisted, we have no content sniffing yet */
|
||||
LOGP(DNAT, LOGL_INFO, "Blacklisted with rule %d\n", i);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* go through the whitelust now */
|
||||
for (i = 0; i < ARRAY_SIZE(white_list); ++i) {
|
||||
/* ignore the rule? */
|
||||
if (white_list[i].filter_dir != FILTER_TO_BOTH
|
||||
&& white_list[i].filter_dir != dir)
|
||||
continue;
|
||||
|
||||
/* the proto is not whitelisted */
|
||||
if (white_list[i].ipa_proto != ALLOW_ANY
|
||||
&& white_list[i].ipa_proto != parsed->ipa_proto)
|
||||
continue;
|
||||
|
||||
if (parsed->ipa_proto == IPAC_PROTO_SCCP) {
|
||||
/* the SSN is not whitelisted */
|
||||
if (white_list[i].dest_ssn != ALLOW_ANY
|
||||
&& white_list[i].dest_ssn != parsed->called_ssn)
|
||||
continue;
|
||||
|
||||
/* bssap */
|
||||
if (white_list[i].bssap != ALLOW_ANY
|
||||
&& white_list[i].bssap != parsed->bssap)
|
||||
continue;
|
||||
|
||||
/* gsm */
|
||||
if (white_list[i].gsm != ALLOW_ANY
|
||||
&& white_list[i].gsm != parsed->gsm_type)
|
||||
continue;
|
||||
|
||||
/* whitelisted */
|
||||
LOGP(DNAT, LOGL_INFO, "Whitelisted with rule %d\n", i);
|
||||
return 0;
|
||||
} else {
|
||||
/* whitelisted */
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -1,531 +0,0 @@
|
|||
/*
|
||||
* (C) 2011-2012 by Holger Hans Peter Freyther
|
||||
* (C) 2011-2012 by On-Waves
|
||||
* (C) 2011 by Daniel Willmann
|
||||
* 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 <osmocom/core/talloc.h>
|
||||
|
||||
#include <osmocom/ctrl/control_cmd.h>
|
||||
#include <osmocom/ctrl/control_if.h>
|
||||
#include <osmocom/ctrl/ports.h>
|
||||
|
||||
#include <osmocom/vty/misc.h>
|
||||
|
||||
#include <osmocom/bsc/ctrl.h>
|
||||
#include <osmocom/bsc/bsc_nat.h>
|
||||
#include <osmocom/bsc/bsc_msg_filter.h>
|
||||
#include <osmocom/bsc/vty.h>
|
||||
#include <osmocom/bsc/gsm_data.h>
|
||||
#include <osmocom/bsc/debug.h>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
|
||||
#define NAT_MAX_CTRL_ID 65535
|
||||
|
||||
static struct bsc_nat *g_nat;
|
||||
|
||||
static int bsc_id_unused(int id, struct bsc_connection *bsc)
|
||||
{
|
||||
struct bsc_cmd_list *pending;
|
||||
|
||||
llist_for_each_entry(pending, &bsc->cmd_pending, list_entry) {
|
||||
if (pending->nat_id == id)
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int get_next_free_bsc_id(struct bsc_connection *bsc)
|
||||
{
|
||||
int new_id, overflow = 0;
|
||||
|
||||
new_id = bsc->last_id;
|
||||
|
||||
do {
|
||||
new_id++;
|
||||
if (new_id == NAT_MAX_CTRL_ID) {
|
||||
new_id = 1;
|
||||
overflow++;
|
||||
}
|
||||
|
||||
if (bsc_id_unused(new_id, bsc)) {
|
||||
bsc->last_id = new_id;
|
||||
return new_id;
|
||||
}
|
||||
} while (overflow != 2);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void bsc_nat_ctrl_del_pending(struct bsc_cmd_list *pending)
|
||||
{
|
||||
llist_del(&pending->list_entry);
|
||||
osmo_timer_del(&pending->timeout);
|
||||
talloc_free(pending);
|
||||
}
|
||||
|
||||
static struct bsc_cmd_list *bsc_get_pending(struct bsc_connection *bsc, char *id_str)
|
||||
{
|
||||
struct bsc_cmd_list *cmd_entry;
|
||||
int id = atoi(id_str);
|
||||
if (id == 0)
|
||||
return NULL;
|
||||
|
||||
llist_for_each_entry(cmd_entry, &bsc->cmd_pending, list_entry) {
|
||||
if (cmd_entry->nat_id == id) {
|
||||
return cmd_entry;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int bsc_nat_handle_ctrlif_msg(struct bsc_connection *bsc, struct msgb *msg)
|
||||
{
|
||||
struct ctrl_cmd *cmd;
|
||||
struct bsc_cmd_list *pending;
|
||||
char *var;
|
||||
|
||||
cmd = ctrl_cmd_parse(bsc, msg);
|
||||
msgb_free(msg);
|
||||
|
||||
if (!cmd) {
|
||||
cmd = talloc_zero(bsc, struct ctrl_cmd);
|
||||
if (!cmd) {
|
||||
LOGP(DNAT, LOGL_ERROR, "OOM!\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
cmd->type = CTRL_TYPE_ERROR;
|
||||
cmd->id = "err";
|
||||
cmd->reply = "Failed to parse command.";
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (bsc->cfg && !llist_empty(&bsc->cfg->lac_list)) {
|
||||
if (cmd->variable) {
|
||||
var = talloc_asprintf(cmd, "net.0.bsc.%i.%s", bsc->cfg->nr,
|
||||
cmd->variable);
|
||||
if (!var) {
|
||||
cmd->type = CTRL_TYPE_ERROR;
|
||||
cmd->reply = "OOM";
|
||||
goto err;
|
||||
}
|
||||
talloc_free(cmd->variable);
|
||||
cmd->variable = var;
|
||||
}
|
||||
|
||||
/* We have to handle TRAPs before matching pending */
|
||||
if (cmd->type == CTRL_TYPE_TRAP) {
|
||||
ctrl_cmd_send_to_all(bsc->nat->ctrl, cmd);
|
||||
talloc_free(cmd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Find the pending command */
|
||||
pending = bsc_get_pending(bsc, cmd->id);
|
||||
if (pending) {
|
||||
osmo_talloc_replace_string(cmd, &cmd->id, pending->cmd->id);
|
||||
if (!cmd->id) {
|
||||
cmd->type = CTRL_TYPE_ERROR;
|
||||
cmd->reply = "OOM";
|
||||
goto err;
|
||||
}
|
||||
ctrl_cmd_send(&pending->cmd->ccon->write_queue, cmd);
|
||||
bsc_nat_ctrl_del_pending(pending);
|
||||
} else {
|
||||
/* We need to handle TRAPS here */
|
||||
if ((cmd->type != CTRL_TYPE_ERROR) &&
|
||||
(cmd->type != CTRL_TYPE_TRAP)) {
|
||||
LOGP(DNAT, LOGL_NOTICE, "Got control message "
|
||||
"from BSC without pending entry\n");
|
||||
cmd->type = CTRL_TYPE_ERROR;
|
||||
cmd->reply = "No request outstanding";
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
}
|
||||
talloc_free(cmd);
|
||||
return 0;
|
||||
err:
|
||||
ctrl_cmd_send(&bsc->write_queue, cmd);
|
||||
talloc_free(cmd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pending_timeout_cb(void *data)
|
||||
{
|
||||
struct bsc_cmd_list *pending = data;
|
||||
LOGP(DNAT, LOGL_ERROR, "Command timed out\n");
|
||||
pending->cmd->type = CTRL_TYPE_ERROR;
|
||||
pending->cmd->reply = "Command timed out";
|
||||
ctrl_cmd_send(&pending->cmd->ccon->write_queue, pending->cmd);
|
||||
|
||||
bsc_nat_ctrl_del_pending(pending);
|
||||
}
|
||||
|
||||
static void ctrl_conn_closed_cb(struct ctrl_connection *connection)
|
||||
{
|
||||
struct bsc_connection *bsc;
|
||||
struct bsc_cmd_list *pending, *tmp;
|
||||
|
||||
llist_for_each_entry(bsc, &g_nat->bsc_connections, list_entry) {
|
||||
llist_for_each_entry_safe(pending, tmp, &bsc->cmd_pending, list_entry) {
|
||||
if (pending->cmd->ccon == connection)
|
||||
bsc_nat_ctrl_del_pending(pending);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int extract_bsc_nr_variable(char *variable, unsigned int *nr, char **bsc_variable)
|
||||
{
|
||||
char *nr_str, *tmp, *saveptr = NULL;
|
||||
|
||||
tmp = strtok_r(variable, ".", &saveptr);
|
||||
tmp = strtok_r(NULL, ".", &saveptr);
|
||||
tmp = strtok_r(NULL, ".", &saveptr);
|
||||
nr_str = strtok_r(NULL, ".", &saveptr);
|
||||
if (!nr_str)
|
||||
return 0;
|
||||
*nr = atoi(nr_str);
|
||||
|
||||
tmp = strtok_r(NULL, "\0", &saveptr);
|
||||
if (!tmp)
|
||||
return 0;
|
||||
|
||||
*bsc_variable = tmp;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int forward_to_bsc(struct ctrl_cmd *cmd)
|
||||
{
|
||||
int ret = CTRL_CMD_HANDLED;
|
||||
struct ctrl_cmd *bsc_cmd = NULL;
|
||||
struct bsc_connection *bsc;
|
||||
struct bsc_cmd_list *pending = NULL;
|
||||
unsigned int nr;
|
||||
char *bsc_variable;
|
||||
|
||||
/* Skip over the beginning (bsc.) */
|
||||
if (!extract_bsc_nr_variable(cmd->variable, &nr, &bsc_variable)) {
|
||||
cmd->reply = "command incomplete";
|
||||
goto err;
|
||||
}
|
||||
|
||||
|
||||
llist_for_each_entry(bsc, &g_nat->bsc_connections, list_entry) {
|
||||
if (!bsc->cfg)
|
||||
continue;
|
||||
if (!bsc->authenticated)
|
||||
continue;
|
||||
if (bsc->cfg->nr != nr)
|
||||
continue;
|
||||
|
||||
/* Add pending command to list */
|
||||
pending = talloc_zero(bsc, struct bsc_cmd_list);
|
||||
if (!pending) {
|
||||
cmd->reply = "OOM";
|
||||
goto err;
|
||||
}
|
||||
|
||||
pending->nat_id = get_next_free_bsc_id(bsc);
|
||||
if (pending->nat_id < 0) {
|
||||
cmd->reply = "No free ID found";
|
||||
goto err;
|
||||
}
|
||||
|
||||
bsc_cmd = ctrl_cmd_cpy(bsc, cmd);
|
||||
if (!bsc_cmd) {
|
||||
cmd->reply = "Could not forward command";
|
||||
goto err;
|
||||
}
|
||||
|
||||
talloc_free(bsc_cmd->id);
|
||||
bsc_cmd->id = talloc_asprintf(bsc_cmd, "%i", pending->nat_id);
|
||||
if (!bsc_cmd->id) {
|
||||
cmd->reply = "OOM";
|
||||
goto err;
|
||||
}
|
||||
|
||||
talloc_free(bsc_cmd->variable);
|
||||
bsc_cmd->variable = talloc_strdup(bsc_cmd, bsc_variable);
|
||||
if (!bsc_cmd->variable) {
|
||||
cmd->reply = "OOM";
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (ctrl_cmd_send(&bsc->write_queue, bsc_cmd)) {
|
||||
cmd->reply = "Sending failed";
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* caller owns cmd param and will destroy it after we return */
|
||||
pending->cmd = ctrl_cmd_cpy(pending, cmd);
|
||||
if (!pending->cmd) {
|
||||
cmd->reply = "Could not answer command";
|
||||
goto err;
|
||||
}
|
||||
cmd->ccon->closed_cb = ctrl_conn_closed_cb;
|
||||
pending->cmd->ccon = cmd->ccon;
|
||||
|
||||
/* Setup the timeout */
|
||||
osmo_timer_setup(&pending->timeout, pending_timeout_cb,
|
||||
pending);
|
||||
/* TODO: Make timeout configurable */
|
||||
osmo_timer_schedule(&pending->timeout, 10, 0);
|
||||
llist_add_tail(&pending->list_entry, &bsc->cmd_pending);
|
||||
|
||||
goto done;
|
||||
}
|
||||
/* We end up here if there's no bsc to handle our LAC */
|
||||
cmd->reply = "no BSC with this nr";
|
||||
err:
|
||||
ret = CTRL_CMD_ERROR;
|
||||
talloc_free(pending);
|
||||
done:
|
||||
talloc_free(bsc_cmd);
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
|
||||
CTRL_CMD_DEFINE(fwd_cmd, "net 0 bsc *");
|
||||
static int get_fwd_cmd(struct ctrl_cmd *cmd, void *data)
|
||||
{
|
||||
return forward_to_bsc(cmd);
|
||||
}
|
||||
|
||||
static int set_fwd_cmd(struct ctrl_cmd *cmd, void *data)
|
||||
{
|
||||
return forward_to_bsc(cmd);
|
||||
}
|
||||
|
||||
static int verify_fwd_cmd(struct ctrl_cmd *cmd, const char *value, void *data)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int extract_bsc_cfg_variable(struct ctrl_cmd *cmd, struct bsc_config **cfg,
|
||||
char **bsc_variable)
|
||||
{
|
||||
unsigned int nr;
|
||||
|
||||
if (!extract_bsc_nr_variable(cmd->variable, &nr, bsc_variable)) {
|
||||
cmd->reply = "command incomplete";
|
||||
return 0;
|
||||
}
|
||||
|
||||
*cfg = bsc_config_num(g_nat, nr);
|
||||
if (!*cfg) {
|
||||
cmd->reply = "Unknown BSC";
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
CTRL_CMD_DEFINE(net_cfg_cmd, "net 0 bsc_cfg *");
|
||||
static int get_net_cfg_cmd(struct ctrl_cmd *cmd, void *data)
|
||||
{
|
||||
char *bsc_variable;
|
||||
struct bsc_config *bsc_cfg;
|
||||
|
||||
if (!extract_bsc_cfg_variable(cmd, &bsc_cfg, &bsc_variable))
|
||||
return CTRL_CMD_ERROR;
|
||||
|
||||
if (strcmp(bsc_variable, "access-list-name") == 0) {
|
||||
cmd->reply = talloc_asprintf(cmd, "%s",
|
||||
bsc_cfg->acc_lst_name ? bsc_cfg->acc_lst_name : "");
|
||||
return CTRL_CMD_REPLY;
|
||||
}
|
||||
|
||||
cmd->reply = "unknown command";
|
||||
return CTRL_CMD_ERROR;
|
||||
}
|
||||
|
||||
static int set_net_cfg_cmd(struct ctrl_cmd *cmd, void *data)
|
||||
{
|
||||
char *bsc_variable;
|
||||
struct bsc_config *bsc_cfg;
|
||||
|
||||
if (!extract_bsc_cfg_variable(cmd, &bsc_cfg, &bsc_variable))
|
||||
return CTRL_CMD_ERROR;
|
||||
|
||||
if (strcmp(bsc_variable, "access-list-name") == 0) {
|
||||
osmo_talloc_replace_string(bsc_cfg, &bsc_cfg->acc_lst_name, cmd->value);
|
||||
cmd->reply = talloc_asprintf(cmd, "%s",
|
||||
bsc_cfg->acc_lst_name ? bsc_cfg->acc_lst_name : "");
|
||||
return CTRL_CMD_REPLY;
|
||||
} else if (strcmp(bsc_variable, "no-access-list-name") == 0) {
|
||||
talloc_free(bsc_cfg->acc_lst_name);
|
||||
bsc_cfg->acc_lst_name = NULL;
|
||||
cmd->reply = "";
|
||||
return CTRL_CMD_REPLY;
|
||||
}
|
||||
|
||||
cmd->reply = "unknown command";
|
||||
return CTRL_CMD_ERROR;
|
||||
}
|
||||
|
||||
static int verify_net_cfg_cmd(struct ctrl_cmd *cmd, const char *value, void *data)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
CTRL_CMD_DEFINE(net_cfg_acc_cmd, "net 0 add allow access-list *");
|
||||
static const char *extract_acc_name(const char *var)
|
||||
{
|
||||
char *str;
|
||||
|
||||
str = strstr(var, "net.0.add.allow.access-list.");
|
||||
if (!str)
|
||||
return NULL;
|
||||
str += strlen("net.0.add.allow.access-list.");
|
||||
if (strlen(str) == 0)
|
||||
return NULL;
|
||||
return str;
|
||||
}
|
||||
|
||||
static int get_net_cfg_acc_cmd(struct ctrl_cmd *cmd, void *data)
|
||||
{
|
||||
cmd->reply = "Append only";
|
||||
return CTRL_CMD_ERROR;
|
||||
}
|
||||
|
||||
static int set_net_cfg_acc_cmd(struct ctrl_cmd *cmd, void *data)
|
||||
{
|
||||
const char *access_name = extract_acc_name(cmd->variable);
|
||||
struct bsc_msg_acc_lst *acc;
|
||||
struct bsc_msg_acc_lst_entry *entry;
|
||||
const char *value = cmd->value;
|
||||
int rc;
|
||||
|
||||
/* Should have been caught by verify_net_cfg_acc_cmd */
|
||||
acc = bsc_msg_acc_lst_find(&g_nat->access_lists, access_name);
|
||||
if (!acc) {
|
||||
cmd->reply = "Access list not found";
|
||||
return CTRL_CMD_ERROR;
|
||||
}
|
||||
|
||||
entry = bsc_msg_acc_lst_entry_create(acc);
|
||||
if (!entry) {
|
||||
cmd->reply = "OOM";
|
||||
return CTRL_CMD_ERROR;
|
||||
}
|
||||
|
||||
rc = gsm_parse_reg(acc, &entry->imsi_allow_re, &entry->imsi_allow, 1, &value);
|
||||
if (rc != 0) {
|
||||
cmd->reply = "Failed to compile expression";
|
||||
return CTRL_CMD_ERROR;
|
||||
}
|
||||
|
||||
cmd->reply = "IMSI allow added to access list";
|
||||
return CTRL_CMD_REPLY;
|
||||
}
|
||||
|
||||
static int verify_net_cfg_acc_cmd(struct ctrl_cmd *cmd, const char *value, void *data)
|
||||
{
|
||||
const char *access_name = extract_acc_name(cmd->variable);
|
||||
struct bsc_msg_acc_lst *acc = bsc_msg_acc_lst_find(&g_nat->access_lists, access_name);
|
||||
|
||||
if (!acc) {
|
||||
cmd->reply = "Access list not known";
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
CTRL_CMD_DEFINE_WO_NOVRF(net_save_cmd, "net 0 save-configuration");
|
||||
|
||||
static int set_net_save_cmd(struct ctrl_cmd *cmd, void *data)
|
||||
{
|
||||
int rc = osmo_vty_save_config_file();
|
||||
cmd->reply = talloc_asprintf(cmd, "%d", rc);
|
||||
if (!cmd->reply) {
|
||||
cmd->reply = "OOM";
|
||||
return CTRL_CMD_ERROR;
|
||||
}
|
||||
|
||||
return CTRL_CMD_REPLY;
|
||||
}
|
||||
|
||||
struct ctrl_handle *bsc_nat_controlif_setup(struct bsc_nat *nat,
|
||||
const char *bind_addr, int port)
|
||||
{
|
||||
struct ctrl_handle *ctrl;
|
||||
int rc;
|
||||
|
||||
|
||||
ctrl = bsc_controlif_setup(NULL, bind_addr, OSMO_CTRL_PORT_BSC_NAT);
|
||||
if (!ctrl) {
|
||||
fprintf(stderr, "Failed to initialize the control interface. Exiting.\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
rc = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_fwd_cmd);
|
||||
if (rc) {
|
||||
fprintf(stderr, "Failed to install the control command. Exiting.\n");
|
||||
goto error;
|
||||
}
|
||||
rc = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_cfg_cmd);
|
||||
if (rc) {
|
||||
fprintf(stderr, "Failed to install the net cfg command. Exiting.\n");
|
||||
goto error;
|
||||
}
|
||||
rc = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_cfg_acc_cmd);
|
||||
if (rc) {
|
||||
fprintf(stderr, "Failed to install the net acc command. Exiting.\n");
|
||||
goto error;
|
||||
}
|
||||
rc = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_save_cmd);
|
||||
if (rc) {
|
||||
fprintf(stderr, "Failed to install the net save command. Exiting.\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
g_nat = nat;
|
||||
return ctrl;
|
||||
|
||||
error:
|
||||
osmo_fd_unregister(&ctrl->listen_fd);
|
||||
close(ctrl->listen_fd.fd);
|
||||
talloc_free(ctrl);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void bsc_nat_inform_reject(struct bsc_connection *conn, const char *imsi)
|
||||
{
|
||||
struct ctrl_cmd *cmd;
|
||||
|
||||
cmd = ctrl_cmd_create(conn, CTRL_TYPE_TRAP);
|
||||
if (!cmd) {
|
||||
LOGP(DCTRL, LOGL_ERROR, "Failed to create TRAP command.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
cmd->id = "0";
|
||||
cmd->variable = talloc_asprintf(cmd, "net.0.bsc.%d.notification-rejection-v1",
|
||||
conn->cfg->nr);
|
||||
cmd->reply = talloc_asprintf(cmd, "imsi=%s", imsi);
|
||||
|
||||
ctrl_cmd_send_to_all(conn->cfg->nat->ctrl, cmd);
|
||||
talloc_free(cmd);
|
||||
}
|
|
@ -1,119 +0,0 @@
|
|||
/*
|
||||
* (C) 2010-2015 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 <osmocom/bsc/bsc_nat.h>
|
||||
#include <osmocom/bsc/bsc_nat_sccp.h>
|
||||
#include <osmocom/bsc/bsc_msg_filter.h>
|
||||
#include <osmocom/bsc/debug.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>
|
||||
|
||||
/* 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_filter_reject_cause *cause)
|
||||
{
|
||||
struct bsc_filter_request req;
|
||||
struct tlv_parsed tp;
|
||||
struct gsm48_hdr *hdr48;
|
||||
int hdr48_len;
|
||||
int len;
|
||||
|
||||
*con_type = FLT_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);
|
||||
req.ctx = bsc;
|
||||
req.black_list = &bsc->nat->imsi_black_list;
|
||||
req.access_lists = &bsc->nat->access_lists;
|
||||
req.local_lst_name = bsc->cfg->acc_lst_name;
|
||||
req.global_lst_name = bsc->nat->acc_lst_name;
|
||||
req.bsc_nr = bsc->cfg->nr;
|
||||
return bsc_msg_filter_initial(hdr48, hdr48_len, &req, con_type, imsi, cause);
|
||||
}
|
||||
|
||||
int bsc_nat_filter_dt(struct bsc_connection *bsc, struct msgb *msg,
|
||||
struct nat_sccp_connection *con, struct bsc_nat_parsed *parsed,
|
||||
struct bsc_filter_reject_cause *cause)
|
||||
{
|
||||
uint32_t len;
|
||||
struct gsm48_hdr *hdr48;
|
||||
struct bsc_filter_request req;
|
||||
|
||||
cause->cm_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED;
|
||||
cause->lu_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED;
|
||||
|
||||
if (con->filter_state.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;
|
||||
|
||||
req.ctx = con;
|
||||
req.black_list = &bsc->nat->imsi_black_list;
|
||||
req.access_lists = &bsc->nat->access_lists;
|
||||
req.local_lst_name = bsc->cfg->acc_lst_name;
|
||||
req.global_lst_name = bsc->nat->acc_lst_name;
|
||||
req.bsc_nr = bsc->cfg->nr;
|
||||
return bsc_msg_filter_data(hdr48, len, &req, &con->filter_state, cause);
|
||||
}
|
|
@ -1,730 +0,0 @@
|
|||
/*
|
||||
* Message rewriting functionality
|
||||
*/
|
||||
/*
|
||||
* (C) 2010-2013 by Holger Hans Peter Freyther <zecke@selfish.org>
|
||||
* (C) 2010-2013 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 <osmocom/bsc/bsc_nat.h>
|
||||
#include <osmocom/bsc/bsc_nat_sccp.h>
|
||||
#include <osmocom/bsc/bsc_msc.h>
|
||||
#include <osmocom/bsc/gsm_data.h>
|
||||
#include <osmocom/bsc/debug.h>
|
||||
#include <osmocom/bsc/ipaccess.h>
|
||||
#include <osmocom/bsc/nat_rewrite_trie.h>
|
||||
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/gsm/gsm0808.h>
|
||||
#include <osmocom/gsm/ipa.h>
|
||||
#include <osmocom/gsm/mncc.h>
|
||||
#include <osmocom/gsm/gsm48.h>
|
||||
|
||||
#include <osmocom/gsm/protocol/gsm_08_08.h>
|
||||
#include <osmocom/gsm/protocol/gsm_04_11.h>
|
||||
|
||||
#include <osmocom/sccp/sccp.h>
|
||||
|
||||
static char *trie_lookup(struct nat_rewrite *trie, const char *number,
|
||||
regoff_t off, void *ctx)
|
||||
{
|
||||
struct nat_rewrite_rule *rule;
|
||||
|
||||
if (!trie) {
|
||||
LOGP(DCC, LOGL_ERROR,
|
||||
"Asked to do a table lookup but no table.\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
rule = nat_rewrite_lookup(trie, number);
|
||||
if (!rule) {
|
||||
LOGP(DCC, LOGL_DEBUG,
|
||||
"Couldn't find a prefix rule for %s\n", number);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return talloc_asprintf(ctx, "%s%s", rule->rewrite, &number[off]);
|
||||
}
|
||||
|
||||
static char *match_and_rewrite_number(void *ctx, const char *number,
|
||||
const char *imsi, struct llist_head *list,
|
||||
struct nat_rewrite *trie)
|
||||
{
|
||||
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) {
|
||||
if (entry->is_prefix_lookup)
|
||||
new_number = trie_lookup(trie, number,
|
||||
matches[1].rm_so, ctx);
|
||||
else
|
||||
new_number = talloc_asprintf(ctx, "%s%s",
|
||||
entry->replace,
|
||||
&number[matches[1].rm_so]);
|
||||
}
|
||||
|
||||
if (new_number)
|
||||
break;
|
||||
}
|
||||
|
||||
return new_number;
|
||||
}
|
||||
|
||||
static char *rewrite_isdn_number(struct bsc_nat *nat, struct llist_head *rewr_list,
|
||||
void *ctx, const char *imsi,
|
||||
struct gsm_mncc_number *called)
|
||||
{
|
||||
char int_number[sizeof(called->number) + 2];
|
||||
char *number = called->number;
|
||||
|
||||
if (llist_empty(&nat->num_rewr)) {
|
||||
LOGP(DCC, LOGL_DEBUG, "Rewrite rules empty.\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* only ISDN plan */
|
||||
if (called->plan != 1) {
|
||||
LOGP(DCC, LOGL_DEBUG, "Called plan is not 1 it was %d\n",
|
||||
called->plan);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* international, prepend */
|
||||
if (called->type == 1) {
|
||||
int_number[0] = '+';
|
||||
memcpy(&int_number[1], number, strlen(number) + 1);
|
||||
number = int_number;
|
||||
}
|
||||
|
||||
return match_and_rewrite_number(ctx, number,
|
||||
imsi, rewr_list, nat->num_rewr_trie);
|
||||
}
|
||||
|
||||
static void update_called_number(struct gsm_mncc_number *called,
|
||||
const char *chosen_number)
|
||||
{
|
||||
if (strncmp(chosen_number, "00", 2) == 0) {
|
||||
called->type = 1;
|
||||
osmo_strlcpy(called->number, chosen_number + 2,
|
||||
sizeof(called->number));
|
||||
} else {
|
||||
/* rewrite international to unknown */
|
||||
if (called->type == 1)
|
||||
called->type = 0;
|
||||
osmo_strlcpy(called->number, chosen_number,
|
||||
sizeof(called->number));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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_pre = NULL, *new_number_post = NULL, *chosen_number;
|
||||
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 */
|
||||
LOGP(DCC, LOGL_DEBUG,
|
||||
"Pre-Rewrite for IMSI(%s) Plan(%d) Type(%d) Number(%s)\n",
|
||||
imsi, called.plan, called.type, called.number);
|
||||
new_number_pre = rewrite_isdn_number(nat, &nat->num_rewr, msg, imsi, &called);
|
||||
|
||||
if (!new_number_pre) {
|
||||
LOGP(DCC, LOGL_DEBUG, "No IMSI(%s) match found, returning message.\n",
|
||||
imsi);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (strlen(new_number_pre) > sizeof(called.number)) {
|
||||
LOGP(DCC, LOGL_ERROR, "Number %s is too long for structure.\n",
|
||||
new_number_pre);
|
||||
talloc_free(new_number_pre);
|
||||
return NULL;
|
||||
}
|
||||
update_called_number(&called, new_number_pre);
|
||||
|
||||
/* another run through the re-write engine with other rules */
|
||||
LOGP(DCC, LOGL_DEBUG,
|
||||
"Post-Rewrite for IMSI(%s) Plan(%d) Type(%d) Number(%s)\n",
|
||||
imsi, called.plan, called.type, called.number);
|
||||
new_number_post = rewrite_isdn_number(nat, &nat->num_rewr_post, msg,
|
||||
imsi, &called);
|
||||
chosen_number = new_number_post ? new_number_post : new_number_pre;
|
||||
|
||||
|
||||
if (strlen(chosen_number) > sizeof(called.number)) {
|
||||
LOGP(DCC, LOGL_ERROR, "Number %s is too long for structure.\n",
|
||||
chosen_number);
|
||||
talloc_free(new_number_pre);
|
||||
talloc_free(new_number_post);
|
||||
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(DCC, LOGL_ERROR, "Failed to allocate.\n");
|
||||
talloc_free(new_number_pre);
|
||||
talloc_free(new_number_post);
|
||||
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 */
|
||||
update_called_number(&called, chosen_number);
|
||||
LOGP(DCC, LOGL_DEBUG,
|
||||
"Chosen number for IMSI(%s) is Plan(%d) Type(%d) Number(%s)\n",
|
||||
imsi, called.plan, called.type, 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_pre);
|
||||
talloc_free(new_number_post);
|
||||
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, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a helper for GSM 04.11 8.2.5.2 Destination address element
|
||||
*/
|
||||
static bool sms_encode_addr_element(struct msgb *out, const char *new_number,
|
||||
int format, int tp_data)
|
||||
{
|
||||
int 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);
|
||||
if (new_addr_len < 0)
|
||||
return false;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
if (!sms_encode_addr_element(out, new_number, 0x91, 0)) {
|
||||
LOGP(DNAT, LOGL_ERROR, "Failed to encode SMS address.\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/* 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) {
|
||||
if (!sms_encode_addr_element(out, new_dest_nr + 2, 0x91, 1)) {
|
||||
LOGP(DNAT, LOGL_ERROR, "Failed to encode SMS address.\n");
|
||||
return NULL;
|
||||
}
|
||||
} else {
|
||||
if (!sms_encode_addr_element(out, new_dest_nr, 0x81, 1)) {
|
||||
LOGP(DNAT, LOGL_ERROR, "Failed to encode SMS address.\n");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
/* 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 = gsm48_hdr_pdisc(hdr48);
|
||||
msg_type = gsm48_hdr_msg_type(hdr48);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
ipa_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;
|
||||
}
|
||||
|
||||
if (strcmp("prefix_lookup", entry->replace) == 0)
|
||||
entry->is_prefix_lookup = 1;
|
||||
|
||||
/* 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,259 +0,0 @@
|
|||
/* Handling for loading a re-write file/database */
|
||||
/*
|
||||
* (C) 2013 by On-Waves
|
||||
* (C) 2013 by Holger Hans Peter Freyther <zecke@selfish.org>
|
||||
* 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 <osmocom/bsc/nat_rewrite_trie.h>
|
||||
#include <osmocom/bsc/debug.h>
|
||||
#include <osmocom/bsc/vty.h>
|
||||
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#define CHECK_IS_DIGIT_OR_FAIL(prefix, pos) \
|
||||
if (!isdigit(prefix[pos]) && prefix[pos] != '+') { \
|
||||
LOGP(DNAT, LOGL_ERROR, \
|
||||
"Prefix(%s) contains non ascii text at(%d=%c)\n", \
|
||||
prefix, pos, prefix[pos]); \
|
||||
goto fail; \
|
||||
}
|
||||
#define TO_INT(c) \
|
||||
((c) == '+' ? 10 : ((c - '0') % 10))
|
||||
|
||||
static void insert_rewrite_node(struct nat_rewrite_rule *rule, struct nat_rewrite *root)
|
||||
{
|
||||
struct nat_rewrite_rule *new = &root->rule;
|
||||
|
||||
const int len = strlen(rule->prefix);
|
||||
int i;
|
||||
|
||||
if (len <= 0) {
|
||||
LOGP(DNAT, LOGL_ERROR, "An empty prefix does not make sense.\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
for (i = 0; i < len - 1; ++i) {
|
||||
int pos;
|
||||
|
||||
/* check if the input is valid */
|
||||
CHECK_IS_DIGIT_OR_FAIL(rule->prefix, i);
|
||||
|
||||
/* check if the next node is already valid */
|
||||
pos = TO_INT(rule->prefix[i]);
|
||||
if (!new->rules[pos]) {
|
||||
new->rules[pos] = talloc_zero(root, struct nat_rewrite_rule);
|
||||
if (!new->rules[pos]) {
|
||||
LOGP(DNAT, LOGL_ERROR,
|
||||
"Failed to allocate memory.\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
new->rules[pos]->empty = 1;
|
||||
}
|
||||
|
||||
/* we continue here */
|
||||
new = new->rules[pos];
|
||||
}
|
||||
|
||||
/* new now points to the place where we want to add it */
|
||||
int pos;
|
||||
|
||||
/* check if the input is valid */
|
||||
CHECK_IS_DIGIT_OR_FAIL(rule->prefix, (len - 1));
|
||||
|
||||
/* check if the next node is already valid */
|
||||
pos = TO_INT(rule->prefix[len - 1]);
|
||||
if (!new->rules[pos])
|
||||
new->rules[pos] = rule;
|
||||
else if (new->rules[pos]->empty) {
|
||||
/* copy over entries */
|
||||
new->rules[pos]->empty = 0;
|
||||
memcpy(new->rules[pos]->prefix, rule->prefix, sizeof(rule->prefix));
|
||||
memcpy(new->rules[pos]->rewrite, rule->rewrite, sizeof(rule->rewrite));
|
||||
talloc_free(rule);
|
||||
} else {
|
||||
LOGP(DNAT, LOGL_ERROR,
|
||||
"Prefix(%s) is already installed\n", rule->prefix);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
root->prefixes += 1;
|
||||
return;
|
||||
|
||||
fail:
|
||||
talloc_free(rule);
|
||||
return;
|
||||
}
|
||||
|
||||
static void handle_line(struct nat_rewrite *rewrite, char *line)
|
||||
{
|
||||
char *split;
|
||||
struct nat_rewrite_rule *rule;
|
||||
size_t size_prefix, size_end, len;
|
||||
|
||||
|
||||
/* Find the ',' in the line */
|
||||
len = strlen(line);
|
||||
split = strstr(line, ",");
|
||||
if (!split) {
|
||||
LOGP(DNAT, LOGL_ERROR, "Line doesn't contain ','\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Check if there is space for the rewrite rule */
|
||||
size_prefix = split - line;
|
||||
if (len - size_prefix <= 2) {
|
||||
LOGP(DNAT, LOGL_ERROR, "No rewrite available.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Continue after the ',' to the end */
|
||||
split = &line[size_prefix + 1];
|
||||
size_end = strlen(split) - 1;
|
||||
|
||||
/* Check if both strings can fit into the static array */
|
||||
if (size_prefix > sizeof(rule->prefix) - 1) {
|
||||
LOGP(DNAT, LOGL_ERROR,
|
||||
"Prefix is too long with %zu\n", size_prefix);
|
||||
return;
|
||||
}
|
||||
|
||||
if (size_end > sizeof(rule->rewrite) - 1) {
|
||||
LOGP(DNAT, LOGL_ERROR,
|
||||
"Rewrite is too long with %zu on %s\n",
|
||||
size_end, &line[size_prefix + 1]);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Now create the entry and insert it into the trie */
|
||||
rule = talloc_zero(rewrite, struct nat_rewrite_rule);
|
||||
if (!rule) {
|
||||
LOGP(DNAT, LOGL_ERROR, "Can not allocate memory\n");
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(rule->prefix, line, size_prefix);
|
||||
assert(size_prefix < sizeof(rule->prefix));
|
||||
rule->prefix[size_prefix] = '\0';
|
||||
|
||||
memcpy(rule->rewrite, split, size_end);
|
||||
assert(size_end < sizeof(rule->rewrite));
|
||||
rule->rewrite[size_end] = '\0';
|
||||
|
||||
/* now insert and balance the tree */
|
||||
insert_rewrite_node(rule, rewrite);
|
||||
}
|
||||
|
||||
struct nat_rewrite *nat_rewrite_parse(void *ctx, const char *filename)
|
||||
{
|
||||
FILE *file;
|
||||
char *line = NULL;
|
||||
size_t n = 0;
|
||||
struct nat_rewrite *res;
|
||||
|
||||
file = fopen(filename, "r");
|
||||
if (!file)
|
||||
return NULL;
|
||||
|
||||
res = talloc_zero(ctx, struct nat_rewrite);
|
||||
if (!res) {
|
||||
fclose(file);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* mark the root as empty */
|
||||
res->rule.empty = 1;
|
||||
|
||||
while (getline(&line, &n, file) != -1) {
|
||||
handle_line(res, line);
|
||||
}
|
||||
|
||||
free(line);
|
||||
fclose(file);
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple find that tries to do a longest match...
|
||||
*/
|
||||
struct nat_rewrite_rule *nat_rewrite_lookup(struct nat_rewrite *rewrite,
|
||||
const char *prefix)
|
||||
{
|
||||
struct nat_rewrite_rule *rule = &rewrite->rule;
|
||||
struct nat_rewrite_rule *last = NULL;
|
||||
const int len = OSMO_MIN(strlen(prefix), (sizeof(rule->prefix) - 1));
|
||||
int i;
|
||||
|
||||
for (i = 0; rule && i < len; ++i) {
|
||||
int pos;
|
||||
|
||||
CHECK_IS_DIGIT_OR_FAIL(prefix, i);
|
||||
pos = TO_INT(prefix[i]);
|
||||
|
||||
rule = rule->rules[pos];
|
||||
if (rule && !rule->empty)
|
||||
last = rule;
|
||||
}
|
||||
|
||||
return last;
|
||||
|
||||
fail:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void nat_rewrite_dump_rec(struct nat_rewrite_rule *rule)
|
||||
{
|
||||
int i;
|
||||
if (!rule->empty)
|
||||
printf("%s,%s\n", rule->prefix, rule->rewrite);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(rule->rules); ++i) {
|
||||
if (!rule->rules[i])
|
||||
continue;
|
||||
nat_rewrite_dump_rec(rule->rules[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void nat_rewrite_dump(struct nat_rewrite *rewrite)
|
||||
{
|
||||
nat_rewrite_dump_rec(&rewrite->rule);
|
||||
}
|
||||
|
||||
static void nat_rewrite_dump_rec_vty(struct vty *vty, struct nat_rewrite_rule *rule)
|
||||
{
|
||||
int i;
|
||||
if (!rule->empty)
|
||||
vty_out(vty, "%s,%s%s", rule->prefix, rule->rewrite, VTY_NEWLINE);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(rule->rules); ++i) {
|
||||
if (!rule->rules[i])
|
||||
continue;
|
||||
nat_rewrite_dump_rec_vty(vty, rule->rules[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void nat_rewrite_dump_vty(struct vty *vty, struct nat_rewrite *rewrite)
|
||||
{
|
||||
nat_rewrite_dump_rec_vty(vty, &rewrite->rule);
|
||||
}
|
|
@ -1,535 +0,0 @@
|
|||
|
||||
/* BSC Multiplexer/NAT Utilities */
|
||||
|
||||
/*
|
||||
* (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 <osmocom/bsc/bsc_nat.h>
|
||||
#include <osmocom/bsc/bsc_nat_sccp.h>
|
||||
#include <osmocom/bsc/bsc_msg_filter.h>
|
||||
#include <osmocom/bsc/bsc_msc.h>
|
||||
#include <osmocom/bsc/gsm_data.h>
|
||||
#include <osmocom/bsc/debug.h>
|
||||
#include <osmocom/bsc/ipaccess.h>
|
||||
#include <osmocom/bsc/vty.h>
|
||||
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/core/stats.h>
|
||||
#include <osmocom/gsm/gsm0808.h>
|
||||
#include <osmocom/gsm/ipa.h>
|
||||
|
||||
#include <osmocom/gsm/protocol/gsm_08_08.h>
|
||||
#include <osmocom/gsm/protocol/gsm_04_11.h>
|
||||
|
||||
#include <osmocom/sccp/sccp.h>
|
||||
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static const struct rate_ctr_desc bsc_cfg_ctr_description[] = {
|
||||
[BCFG_CTR_SCCP_CONN] = { "sccp.conn", "SCCP Connections "},
|
||||
[BCFG_CTR_SCCP_CALLS] = { "sccp.calls", "SCCP Assignment Commands "},
|
||||
[BCFG_CTR_NET_RECONN] = { "net.reconnects", "Network reconnects "},
|
||||
[BCFG_CTR_DROPPED_SCCP] = { "dropped.sccp", "Dropped SCCP connections."},
|
||||
[BCFG_CTR_DROPPED_CALLS] = { "dropped.calls", "Dropped active calls. "},
|
||||
[BCFG_CTR_REJECTED_CR] = { "rejected.cr", "Rejected CR due filter "},
|
||||
[BCFG_CTR_REJECTED_MSG] = { "rejected.msg", "Rejected MSG due filter "},
|
||||
[BCFG_CTR_ILL_PACKET] = { "rejected.ill", "Rejected due parse error "},
|
||||
[BCFG_CTR_CON_TYPE_LU] = { "conn.lu", "Conn Location Update "},
|
||||
[BCFG_CTR_CON_CMSERV_RQ] = { "conn.rq", "Conn CM Service Req "},
|
||||
[BCFG_CTR_CON_PAG_RESP] = { "conn.pag", "Conn Paging Response "},
|
||||
[BCFG_CTR_CON_SSA] = { "conn.ssa", "Conn USSD "},
|
||||
[BCFG_CTR_CON_OTHER] = { "conn.other", "Conn Other "},
|
||||
};
|
||||
|
||||
static const struct rate_ctr_group_desc bsc_cfg_ctrg_desc = {
|
||||
.group_name_prefix = "nat.bsc",
|
||||
.group_description = "NAT BSC Statistics",
|
||||
.num_ctr = ARRAY_SIZE(bsc_cfg_ctr_description),
|
||||
.ctr_desc = bsc_cfg_ctr_description,
|
||||
.class_id = OSMO_STATS_CLASS_PEER,
|
||||
};
|
||||
|
||||
struct bsc_nat *bsc_nat_alloc(void)
|
||||
{
|
||||
struct bsc_nat *nat = talloc_zero(tall_bsc_ctx, struct bsc_nat);
|
||||
if (!nat)
|
||||
return NULL;
|
||||
|
||||
nat->main_dest = talloc_zero(nat, struct bsc_msc_dest);
|
||||
if (!nat->main_dest) {
|
||||
talloc_free(nat);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
INIT_LLIST_HEAD(&nat->sccp_connections);
|
||||
INIT_LLIST_HEAD(&nat->bsc_connections);
|
||||
INIT_LLIST_HEAD(&nat->paging_groups);
|
||||
INIT_LLIST_HEAD(&nat->bsc_configs);
|
||||
INIT_LLIST_HEAD(&nat->access_lists);
|
||||
INIT_LLIST_HEAD(&nat->dests);
|
||||
INIT_LLIST_HEAD(&nat->num_rewr);
|
||||
INIT_LLIST_HEAD(&nat->num_rewr_post);
|
||||
INIT_LLIST_HEAD(&nat->smsc_rewr);
|
||||
INIT_LLIST_HEAD(&nat->tpdest_match);
|
||||
INIT_LLIST_HEAD(&nat->sms_clear_tp_srr);
|
||||
INIT_LLIST_HEAD(&nat->sms_num_rewr);
|
||||
|
||||
nat->stats.sccp.conn = osmo_counter_alloc("nat.sccp.conn");
|
||||
nat->stats.sccp.calls = osmo_counter_alloc("nat.sccp.calls");
|
||||
nat->stats.bsc.reconn = osmo_counter_alloc("nat.bsc.conn");
|
||||
nat->stats.bsc.auth_fail = osmo_counter_alloc("nat.bsc.auth_fail");
|
||||
nat->stats.msc.reconn = osmo_counter_alloc("nat.msc.conn");
|
||||
nat->stats.ussd.reconn = osmo_counter_alloc("nat.ussd.conn");
|
||||
nat->auth_timeout = 2;
|
||||
nat->ping_timeout = 20;
|
||||
nat->pong_timeout = 5;
|
||||
|
||||
llist_add(&nat->main_dest->list, &nat->dests);
|
||||
nat->main_dest->ip = talloc_strdup(nat, "127.0.0.1");
|
||||
nat->main_dest->port = 5000;
|
||||
|
||||
return nat;
|
||||
}
|
||||
|
||||
void bsc_nat_free(struct bsc_nat *nat)
|
||||
{
|
||||
struct bsc_config *cfg, *tmp;
|
||||
struct bsc_msg_acc_lst *lst, *tmp_lst;
|
||||
|
||||
llist_for_each_entry_safe(cfg, tmp, &nat->bsc_configs, entry)
|
||||
bsc_config_free(cfg);
|
||||
llist_for_each_entry_safe(lst, tmp_lst, &nat->access_lists, list)
|
||||
bsc_msg_acc_lst_delete(lst);
|
||||
|
||||
bsc_nat_num_rewr_entry_adapt(nat, &nat->num_rewr, NULL);
|
||||
bsc_nat_num_rewr_entry_adapt(nat, &nat->num_rewr_post, NULL);
|
||||
bsc_nat_num_rewr_entry_adapt(nat, &nat->sms_clear_tp_srr, NULL);
|
||||
bsc_nat_num_rewr_entry_adapt(nat, &nat->sms_num_rewr, NULL);
|
||||
bsc_nat_num_rewr_entry_adapt(nat, &nat->tpdest_match, NULL);
|
||||
|
||||
osmo_counter_free(nat->stats.sccp.conn);
|
||||
osmo_counter_free(nat->stats.sccp.calls);
|
||||
osmo_counter_free(nat->stats.bsc.reconn);
|
||||
osmo_counter_free(nat->stats.bsc.auth_fail);
|
||||
osmo_counter_free(nat->stats.msc.reconn);
|
||||
osmo_counter_free(nat->stats.ussd.reconn);
|
||||
talloc_free(nat->mgcp_cfg);
|
||||
talloc_free(nat);
|
||||
}
|
||||
|
||||
void bsc_nat_set_msc_ip(struct bsc_nat *nat, const char *ip)
|
||||
{
|
||||
osmo_talloc_replace_string(nat, &nat->main_dest->ip, ip);
|
||||
}
|
||||
|
||||
struct bsc_connection *bsc_connection_alloc(struct bsc_nat *nat)
|
||||
{
|
||||
struct bsc_connection *con = talloc_zero(nat, struct bsc_connection);
|
||||
if (!con)
|
||||
return NULL;
|
||||
|
||||
con->nat = nat;
|
||||
osmo_wqueue_init(&con->write_queue, 100);
|
||||
INIT_LLIST_HEAD(&con->cmd_pending);
|
||||
INIT_LLIST_HEAD(&con->pending_dlcx);
|
||||
return con;
|
||||
}
|
||||
|
||||
struct bsc_config *bsc_config_alloc(struct bsc_nat *nat, const char *token,
|
||||
unsigned int number)
|
||||
{
|
||||
struct bsc_config *conf = talloc_zero(nat, struct bsc_config);
|
||||
if (!conf)
|
||||
return NULL;
|
||||
|
||||
conf->token = talloc_strdup(conf, token);
|
||||
conf->nr = number;
|
||||
conf->nat = nat;
|
||||
conf->max_endpoints = 32;
|
||||
conf->paging_group = PAGIN_GROUP_UNASSIGNED;
|
||||
|
||||
INIT_LLIST_HEAD(&conf->lac_list);
|
||||
|
||||
llist_add_tail(&conf->entry, &nat->bsc_configs);
|
||||
++nat->num_bsc;
|
||||
|
||||
conf->stats.ctrg = rate_ctr_group_alloc(conf, &bsc_cfg_ctrg_desc, conf->nr);
|
||||
if (!conf->stats.ctrg) {
|
||||
llist_del(&conf->entry);
|
||||
talloc_free(conf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return conf;
|
||||
}
|
||||
|
||||
struct bsc_config *bsc_config_by_token(struct bsc_nat *nat, const char *token, int len)
|
||||
{
|
||||
struct bsc_config *conf;
|
||||
|
||||
llist_for_each_entry(conf, &nat->bsc_configs, entry) {
|
||||
/*
|
||||
* Add the '\0' of the token for the memcmp, the IPA messages
|
||||
* for some reason added null termination.
|
||||
*/
|
||||
const int token_len = strlen(conf->token) + 1;
|
||||
|
||||
if (token_len == len && memcmp(conf->token, token, token_len) == 0)
|
||||
return conf;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void bsc_config_free(struct bsc_config *cfg)
|
||||
{
|
||||
llist_del(&cfg->entry);
|
||||
rate_ctr_group_free(cfg->stats.ctrg);
|
||||
cfg->nat->num_bsc--;
|
||||
OSMO_ASSERT(cfg->nat->num_bsc >= 0);
|
||||
talloc_free(cfg);
|
||||
}
|
||||
|
||||
static void _add_lac(void *ctx, struct llist_head *list, int _lac)
|
||||
{
|
||||
struct bsc_lac_entry *lac;
|
||||
|
||||
llist_for_each_entry(lac, list, entry)
|
||||
if (lac->lac == _lac)
|
||||
return;
|
||||
|
||||
lac = talloc_zero(ctx, struct bsc_lac_entry);
|
||||
if (!lac) {
|
||||
LOGP(DNAT, LOGL_ERROR, "Failed to allocate.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
lac->lac = _lac;
|
||||
llist_add_tail(&lac->entry, list);
|
||||
}
|
||||
|
||||
static void _del_lac(struct llist_head *list, int _lac)
|
||||
{
|
||||
struct bsc_lac_entry *lac;
|
||||
|
||||
llist_for_each_entry(lac, list, entry)
|
||||
if (lac->lac == _lac) {
|
||||
llist_del(&lac->entry);
|
||||
talloc_free(lac);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void bsc_config_add_lac(struct bsc_config *cfg, int _lac)
|
||||
{
|
||||
_add_lac(cfg, &cfg->lac_list, _lac);
|
||||
}
|
||||
|
||||
void bsc_config_del_lac(struct bsc_config *cfg, int _lac)
|
||||
{
|
||||
_del_lac(&cfg->lac_list, _lac);
|
||||
}
|
||||
|
||||
struct bsc_nat_paging_group *bsc_nat_paging_group_create(struct bsc_nat *nat, int group)
|
||||
{
|
||||
struct bsc_nat_paging_group *pgroup;
|
||||
|
||||
pgroup = talloc_zero(nat, struct bsc_nat_paging_group);
|
||||
if (!pgroup) {
|
||||
LOGP(DNAT, LOGL_ERROR, "Failed to allocate a paging group.\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pgroup->nr = group;
|
||||
INIT_LLIST_HEAD(&pgroup->lists);
|
||||
llist_add_tail(&pgroup->entry, &nat->paging_groups);
|
||||
return pgroup;
|
||||
}
|
||||
|
||||
void bsc_nat_paging_group_delete(struct bsc_nat_paging_group *pgroup)
|
||||
{
|
||||
llist_del(&pgroup->entry);
|
||||
talloc_free(pgroup);
|
||||
}
|
||||
|
||||
struct bsc_nat_paging_group *bsc_nat_paging_group_num(struct bsc_nat *nat, int group)
|
||||
{
|
||||
struct bsc_nat_paging_group *pgroup;
|
||||
|
||||
llist_for_each_entry(pgroup, &nat->paging_groups, entry)
|
||||
if (pgroup->nr == group)
|
||||
return pgroup;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void bsc_nat_paging_group_add_lac(struct bsc_nat_paging_group *pgroup, int lac)
|
||||
{
|
||||
_add_lac(pgroup, &pgroup->lists, lac);
|
||||
}
|
||||
|
||||
void bsc_nat_paging_group_del_lac(struct bsc_nat_paging_group *pgroup, int lac)
|
||||
{
|
||||
_del_lac(&pgroup->lists, lac);
|
||||
}
|
||||
|
||||
int bsc_config_handles_lac(struct bsc_config *cfg, int lac_nr)
|
||||
{
|
||||
struct bsc_nat_paging_group *pgroup;
|
||||
struct bsc_lac_entry *entry;
|
||||
|
||||
llist_for_each_entry(entry, &cfg->lac_list, entry)
|
||||
if (entry->lac == lac_nr)
|
||||
return 1;
|
||||
|
||||
/* now lookup the paging group */
|
||||
pgroup = bsc_nat_paging_group_num(cfg->nat, cfg->paging_group);
|
||||
if (!pgroup)
|
||||
return 0;
|
||||
|
||||
llist_for_each_entry(entry, &pgroup->lists, entry)
|
||||
if (entry->lac == lac_nr)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void sccp_connection_destroy(struct nat_sccp_connection *conn)
|
||||
{
|
||||
LOGP(DNAT, LOGL_DEBUG, "Destroy 0x%x <-> 0x%x mapping for con %p\n",
|
||||
sccp_src_ref_to_int(&conn->real_ref),
|
||||
sccp_src_ref_to_int(&conn->patched_ref), conn->bsc);
|
||||
bsc_mgcp_dlcx(conn);
|
||||
llist_del(&conn->list_entry);
|
||||
talloc_free(conn);
|
||||
}
|
||||
|
||||
|
||||
int bsc_nat_find_paging(struct msgb *msg,
|
||||
const uint8_t **out_data, int *out_leng)
|
||||
{
|
||||
int data_length;
|
||||
const uint8_t *data;
|
||||
struct tlv_parsed tp;
|
||||
|
||||
if (!msg->l3h || msgb_l3len(msg) < 3) {
|
||||
LOGP(DNAT, LOGL_ERROR, "Paging message is too short.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l3h + 3, msgb_l3len(msg) - 3, 0, 0);
|
||||
if (!TLVP_PRESENT(&tp, GSM0808_IE_CELL_IDENTIFIER_LIST)) {
|
||||
LOGP(DNAT, LOGL_ERROR, "No CellIdentifier List inside paging msg.\n");
|
||||
return -2;
|
||||
}
|
||||
|
||||
data_length = TLVP_LEN(&tp, GSM0808_IE_CELL_IDENTIFIER_LIST);
|
||||
data = TLVP_VAL(&tp, GSM0808_IE_CELL_IDENTIFIER_LIST);
|
||||
|
||||
/* No need to try a different BSS */
|
||||
if (data[0] == CELL_IDENT_BSS) {
|
||||
return -3;
|
||||
} else if (data[0] != CELL_IDENT_LAC) {
|
||||
LOGP(DNAT, LOGL_ERROR, "Unhandled cell ident discrminator: %d\n", data[0]);
|
||||
return -4;
|
||||
}
|
||||
|
||||
*out_data = &data[1];
|
||||
*out_leng = data_length - 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bsc_write_mgcp(struct bsc_connection *bsc, const uint8_t *data, unsigned int length)
|
||||
{
|
||||
struct msgb *msg;
|
||||
|
||||
if (length > 4096 - 128) {
|
||||
LOGP(DLINP, LOGL_ERROR, "Can not send message of that size.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
msg = msgb_alloc_headroom(4096, 128, "to-bsc");
|
||||
if (!msg) {
|
||||
LOGP(DLINP, LOGL_ERROR, "Failed to allocate memory for BSC msg.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* copy the data */
|
||||
msg->l3h = msgb_put(msg, length);
|
||||
memcpy(msg->l3h, data, length);
|
||||
|
||||
return bsc_write(bsc, msg, IPAC_PROTO_MGCP_OLD);
|
||||
}
|
||||
|
||||
int bsc_write(struct bsc_connection *bsc, struct msgb *msg, int proto)
|
||||
{
|
||||
return bsc_do_write(&bsc->write_queue, msg, proto);
|
||||
}
|
||||
|
||||
int bsc_do_write(struct osmo_wqueue *queue, struct msgb *msg, int proto)
|
||||
{
|
||||
/* prepend the header */
|
||||
ipa_prepend_header(msg, proto);
|
||||
return bsc_write_msg(queue, msg);
|
||||
}
|
||||
|
||||
int bsc_write_msg(struct osmo_wqueue *queue, struct msgb *msg)
|
||||
{
|
||||
if (osmo_wqueue_enqueue(queue, msg) != 0) {
|
||||
LOGP(DLINP, LOGL_ERROR, "Failed to enqueue the write.\n");
|
||||
msgb_free(msg);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct gsm48_hdr *bsc_unpack_dtap(struct bsc_nat_parsed *parsed,
|
||||
struct msgb *msg, uint32_t *len)
|
||||
{
|
||||
/* gsm_type is actually the size of the dtap */
|
||||
*len = parsed->gsm_type;
|
||||
if (*len < msgb_l3len(msg) - 3) {
|
||||
LOGP(DNAT, LOGL_ERROR, "Not enough space for DTAP.\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (msgb_l3len(msg) - 3 < msg->l3h[2]) {
|
||||
LOGP(DNAT, LOGL_ERROR,
|
||||
"GSM48 payload does not fit: %d %d\n",
|
||||
msg->l3h[2], msgb_l3len(msg) - 3);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
msg->l4h = &msg->l3h[3];
|
||||
return (struct gsm48_hdr *) msg->l4h;
|
||||
}
|
||||
|
||||
static const char *con_types [] = {
|
||||
[FLT_CON_TYPE_NONE] = "n/a",
|
||||
[FLT_CON_TYPE_LU] = "Location Update",
|
||||
[FLT_CON_TYPE_CM_SERV_REQ] = "CM Serv Req",
|
||||
[FLT_CON_TYPE_PAG_RESP] = "Paging Response",
|
||||
[FLT_CON_TYPE_SSA] = "Supplementar Service Activation",
|
||||
[FLT_CON_TYPE_LOCAL_REJECT] = "Local Reject",
|
||||
[FLT_CON_TYPE_OTHER] = "Other",
|
||||
};
|
||||
|
||||
const char *bsc_con_type_to_string(int type)
|
||||
{
|
||||
return con_types[type];
|
||||
}
|
||||
|
||||
int bsc_nat_msc_is_connected(struct bsc_nat *nat)
|
||||
{
|
||||
return nat->msc_con->is_connected;
|
||||
}
|
||||
|
||||
static const int con_to_ctr[] = {
|
||||
[FLT_CON_TYPE_NONE] = -1,
|
||||
[FLT_CON_TYPE_LU] = BCFG_CTR_CON_TYPE_LU,
|
||||
[FLT_CON_TYPE_CM_SERV_REQ] = BCFG_CTR_CON_CMSERV_RQ,
|
||||
[FLT_CON_TYPE_PAG_RESP] = BCFG_CTR_CON_PAG_RESP,
|
||||
[FLT_CON_TYPE_SSA] = BCFG_CTR_CON_SSA,
|
||||
[FLT_CON_TYPE_LOCAL_REJECT] = -1,
|
||||
[FLT_CON_TYPE_OTHER] = BCFG_CTR_CON_OTHER,
|
||||
};
|
||||
|
||||
int bsc_conn_type_to_ctr(struct nat_sccp_connection *conn)
|
||||
{
|
||||
return con_to_ctr[conn->filter_state.con_type];
|
||||
}
|
||||
|
||||
int bsc_write_cb(struct osmo_fd *bfd, struct msgb *msg)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = write(bfd->fd, msg->data, msg->len);
|
||||
if (rc != msg->len)
|
||||
LOGP(DNAT, LOGL_ERROR, "Failed to write message to the BSC.\n");
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void extract_lac(const uint8_t *data, uint16_t *lac, uint16_t *ci)
|
||||
{
|
||||
memcpy(lac, &data[0], sizeof(*lac));
|
||||
memcpy(ci, &data[2], sizeof(*ci));
|
||||
|
||||
*lac = ntohs(*lac);
|
||||
*ci = ntohs(*ci);
|
||||
}
|
||||
|
||||
int bsc_nat_extract_lac(struct bsc_connection *bsc,
|
||||
struct nat_sccp_connection *con,
|
||||
struct bsc_nat_parsed *parsed, struct msgb *msg)
|
||||
{
|
||||
int data_length;
|
||||
const uint8_t *data;
|
||||
struct tlv_parsed tp;
|
||||
uint16_t lac, ci;
|
||||
|
||||
if (parsed->gsm_type != BSS_MAP_MSG_COMPLETE_LAYER_3) {
|
||||
LOGP(DNAT, LOGL_ERROR, "Can only extract LAC from Complete Layer3\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!msg->l3h || msgb_l3len(msg) < 3) {
|
||||
LOGP(DNAT, LOGL_ERROR, "Complete Layer3 mssage is too short.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l3h + 3, msgb_l3len(msg) - 3, 0, 0);
|
||||
if (!TLVP_PRESENT(&tp, GSM0808_IE_CELL_IDENTIFIER)) {
|
||||
LOGP(DNAT, LOGL_ERROR, "No CellIdentifier List inside paging msg.\n");
|
||||
return -2;
|
||||
}
|
||||
|
||||
data_length = TLVP_LEN(&tp, GSM0808_IE_CELL_IDENTIFIER);
|
||||
data = TLVP_VAL(&tp, GSM0808_IE_CELL_IDENTIFIER);
|
||||
|
||||
/* Attemt to get the LAC/CI from it */
|
||||
if (data[0] == CELL_IDENT_WHOLE_GLOBAL) {
|
||||
if (data_length != 8) {
|
||||
LOGP(DNAT, LOGL_ERROR,
|
||||
"Ident too short: %d\n", data_length);
|
||||
return -3;
|
||||
}
|
||||
extract_lac(&data[1 + 3], &lac, &ci);
|
||||
} else if (data[0] == CELL_IDENT_LAC_AND_CI) {
|
||||
if (data_length != 5) {
|
||||
LOGP(DNAT, LOGL_ERROR,
|
||||
"Ident too short: %d\n", data_length);
|
||||
return -3;
|
||||
}
|
||||
extract_lac(&data[1], &lac, &ci);
|
||||
} else {
|
||||
LOGP(DNAT, LOGL_ERROR,
|
||||
"Unhandled cell identifier: %d\n", data[0]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
con->lac = lac;
|
||||
con->ci = ci;
|
||||
return 0;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1,247 +0,0 @@
|
|||
/* SCCP patching and handling routines */
|
||||
/*
|
||||
* (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
|
||||
* (C) 2010 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 <osmocom/bsc/debug.h>
|
||||
#include <osmocom/bsc/bsc_nat.h>
|
||||
#include <osmocom/bsc/bsc_nat_sccp.h>
|
||||
|
||||
#include <osmocom/sccp/sccp.h>
|
||||
|
||||
#include <osmocom/core/talloc.h>
|
||||
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
static int equal(struct sccp_source_reference *ref1, struct sccp_source_reference *ref2)
|
||||
{
|
||||
return memcmp(ref1, ref2, sizeof(*ref1)) == 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* SCCP patching below
|
||||
*/
|
||||
|
||||
/* check if we are using this ref for patched already */
|
||||
static int sccp_ref_is_free(struct sccp_source_reference *ref, struct bsc_nat *nat)
|
||||
{
|
||||
struct nat_sccp_connection *conn;
|
||||
|
||||
llist_for_each_entry(conn, &nat->sccp_connections, list_entry) {
|
||||
if (equal(ref, &conn->patched_ref))
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* copied from sccp.c */
|
||||
static int assign_src_local_reference(struct sccp_source_reference *ref, struct bsc_nat *nat)
|
||||
{
|
||||
static uint32_t last_ref = 0x50000;
|
||||
int wrapped = 0;
|
||||
|
||||
do {
|
||||
struct sccp_source_reference reference;
|
||||
reference.octet1 = (last_ref >> 0) & 0xff;
|
||||
reference.octet2 = (last_ref >> 8) & 0xff;
|
||||
reference.octet3 = (last_ref >> 16) & 0xff;
|
||||
|
||||
++last_ref;
|
||||
/* do not use the reversed word and wrap around */
|
||||
if ((last_ref & 0x00FFFFFF) == 0x00FFFFFF) {
|
||||
LOGP(DNAT, LOGL_NOTICE, "Wrapped searching for a free code\n");
|
||||
last_ref = 0;
|
||||
++wrapped;
|
||||
}
|
||||
|
||||
if (sccp_ref_is_free(&reference, nat) == 0) {
|
||||
*ref = reference;
|
||||
return 0;
|
||||
}
|
||||
} while (wrapped != 2);
|
||||
|
||||
LOGP(DNAT, LOGL_ERROR, "Finding a free reference failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct nat_sccp_connection *create_sccp_src_ref(struct bsc_connection *bsc,
|
||||
struct bsc_nat_parsed *parsed)
|
||||
{
|
||||
struct nat_sccp_connection *conn;
|
||||
|
||||
/* Some commercial BSCs like to reassign there SRC ref */
|
||||
llist_for_each_entry(conn, &bsc->nat->sccp_connections, list_entry) {
|
||||
if (conn->bsc != bsc)
|
||||
continue;
|
||||
if (!equal(parsed->src_local_ref, &conn->real_ref))
|
||||
continue;
|
||||
|
||||
/* the BSC has reassigned the SRC ref and we failed to keep track */
|
||||
memset(&conn->remote_ref, 0, sizeof(conn->remote_ref));
|
||||
if (assign_src_local_reference(&conn->patched_ref, bsc->nat) != 0) {
|
||||
LOGP(DNAT, LOGL_ERROR, "BSC %d reused src ref: %d and we failed to generate a new id.\n",
|
||||
bsc->cfg->nr, sccp_src_ref_to_int(parsed->src_local_ref));
|
||||
bsc_mgcp_dlcx(conn);
|
||||
llist_del(&conn->list_entry);
|
||||
talloc_free(conn);
|
||||
return NULL;
|
||||
} else {
|
||||
clock_gettime(CLOCK_MONOTONIC, &conn->creation_time);
|
||||
bsc_mgcp_dlcx(conn);
|
||||
return conn;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
conn = talloc_zero(bsc->nat, struct nat_sccp_connection);
|
||||
if (!conn) {
|
||||
LOGP(DNAT, LOGL_ERROR, "Memory allocation failure.\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
conn->bsc = bsc;
|
||||
clock_gettime(CLOCK_MONOTONIC, &conn->creation_time);
|
||||
conn->real_ref = *parsed->src_local_ref;
|
||||
if (assign_src_local_reference(&conn->patched_ref, bsc->nat) != 0) {
|
||||
LOGP(DNAT, LOGL_ERROR, "Failed to assign a ref.\n");
|
||||
talloc_free(conn);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bsc_mgcp_init(conn);
|
||||
llist_add_tail(&conn->list_entry, &bsc->nat->sccp_connections);
|
||||
rate_ctr_inc(&bsc->cfg->stats.ctrg->ctr[BCFG_CTR_SCCP_CONN]);
|
||||
osmo_counter_inc(bsc->cfg->nat->stats.sccp.conn);
|
||||
|
||||
LOGP(DNAT, LOGL_DEBUG, "Created 0x%x <-> 0x%x mapping for con %p\n",
|
||||
sccp_src_ref_to_int(&conn->real_ref),
|
||||
sccp_src_ref_to_int(&conn->patched_ref), bsc);
|
||||
|
||||
return conn;
|
||||
}
|
||||
|
||||
int update_sccp_src_ref(struct nat_sccp_connection *sccp, struct bsc_nat_parsed *parsed)
|
||||
{
|
||||
if (!parsed->dest_local_ref || !parsed->src_local_ref) {
|
||||
LOGP(DNAT, LOGL_ERROR, "CC MSG should contain both local and dest address.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
sccp->remote_ref = *parsed->src_local_ref;
|
||||
sccp->has_remote_ref = 1;
|
||||
LOGP(DNAT, LOGL_DEBUG, "Updating 0x%x to remote 0x%x on %p\n",
|
||||
sccp_src_ref_to_int(&sccp->patched_ref),
|
||||
sccp_src_ref_to_int(&sccp->remote_ref), sccp->bsc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void remove_sccp_src_ref(struct bsc_connection *bsc, struct msgb *msg, struct bsc_nat_parsed *parsed)
|
||||
{
|
||||
struct nat_sccp_connection *conn;
|
||||
|
||||
llist_for_each_entry(conn, &bsc->nat->sccp_connections, list_entry) {
|
||||
if (equal(parsed->src_local_ref, &conn->patched_ref)) {
|
||||
sccp_connection_destroy(conn);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
LOGP(DNAT, LOGL_ERROR, "Can not remove connection: 0x%x\n",
|
||||
sccp_src_ref_to_int(parsed->src_local_ref));
|
||||
}
|
||||
|
||||
/*
|
||||
* We have a message from the MSC to the BSC. The MSC is using
|
||||
* an address that was assigned by the MUX, we need to update the
|
||||
* dest reference to the real network.
|
||||
*/
|
||||
struct nat_sccp_connection *patch_sccp_src_ref_to_bsc(struct msgb *msg,
|
||||
struct bsc_nat_parsed *parsed,
|
||||
struct bsc_nat *nat)
|
||||
{
|
||||
struct nat_sccp_connection *conn;
|
||||
|
||||
if (!parsed->dest_local_ref) {
|
||||
LOGP(DNAT, LOGL_ERROR, "MSG should contain dest_local_ref.\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
llist_for_each_entry(conn, &nat->sccp_connections, list_entry) {
|
||||
if (!equal(parsed->dest_local_ref, &conn->patched_ref))
|
||||
continue;
|
||||
|
||||
/* Change the dest address to the real one */
|
||||
*parsed->dest_local_ref = conn->real_ref;
|
||||
return conn;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* These are message to the MSC. We will need to find the BSC
|
||||
* Connection by either the SRC or the DST local reference.
|
||||
*
|
||||
* In case of a CR we need to work by the SRC local reference
|
||||
* in all other cases we need to work by the destination local
|
||||
* reference..
|
||||
*/
|
||||
struct nat_sccp_connection *patch_sccp_src_ref_to_msc(struct msgb *msg,
|
||||
struct bsc_nat_parsed *parsed,
|
||||
struct bsc_connection *bsc)
|
||||
{
|
||||
struct nat_sccp_connection *conn;
|
||||
|
||||
llist_for_each_entry(conn, &bsc->nat->sccp_connections, list_entry) {
|
||||
if (conn->bsc != bsc)
|
||||
continue;
|
||||
|
||||
if (parsed->src_local_ref) {
|
||||
if (equal(parsed->src_local_ref, &conn->real_ref)) {
|
||||
*parsed->src_local_ref = conn->patched_ref;
|
||||
return conn;
|
||||
}
|
||||
} else if (parsed->dest_local_ref) {
|
||||
if (equal(parsed->dest_local_ref, &conn->remote_ref))
|
||||
return conn;
|
||||
} else {
|
||||
LOGP(DNAT, LOGL_ERROR, "Header has neither loc/dst ref.\n");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct nat_sccp_connection *bsc_nat_find_con_by_bsc(struct bsc_nat *nat,
|
||||
struct sccp_source_reference *ref)
|
||||
{
|
||||
struct nat_sccp_connection *conn;
|
||||
|
||||
llist_for_each_entry(conn, &nat->sccp_connections, list_entry) {
|
||||
if (equal(ref, &conn->real_ref))
|
||||
return conn;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
|
@ -1,452 +0,0 @@
|
|||
/* USSD Filter Code */
|
||||
|
||||
/*
|
||||
* (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 <osmocom/core/socket.h>
|
||||
|
||||
#include <osmocom/bsc/bsc_nat.h>
|
||||
#include <osmocom/bsc/bsc_nat_sccp.h>
|
||||
#include <osmocom/bsc/bsc_msg_filter.h>
|
||||
#include <osmocom/bsc/ipaccess.h>
|
||||
#include <osmocom/bsc/debug.h>
|
||||
|
||||
#include <osmocom/gsm/protocol/gsm_08_08.h>
|
||||
#include <osmocom/gsm/gsm0480.h>
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/gsm/tlv.h>
|
||||
#include <osmocom/gsm/ipa.h>
|
||||
#include <osmocom/gsm/gsm0480.h>
|
||||
|
||||
#include <osmocom/sccp/sccp.h>
|
||||
|
||||
#include <osmocom/abis/ipa.h>
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
|
||||
#define USSD_LAC_IE 0
|
||||
#define USSD_CI_IE 1
|
||||
|
||||
static void ussd_auth_con(struct tlv_parsed *, struct bsc_nat_ussd_con *);
|
||||
|
||||
static struct bsc_nat_ussd_con *bsc_nat_ussd_alloc(struct bsc_nat *nat)
|
||||
{
|
||||
struct bsc_nat_ussd_con *con;
|
||||
|
||||
con = talloc_zero(nat, struct bsc_nat_ussd_con);
|
||||
if (!con)
|
||||
return NULL;
|
||||
|
||||
con->nat = nat;
|
||||
return con;
|
||||
}
|
||||
|
||||
static void bsc_nat_ussd_destroy(struct bsc_nat_ussd_con *con)
|
||||
{
|
||||
if (con->nat->ussd_con == con) {
|
||||
bsc_ussd_close_connections(con->nat);
|
||||
con->nat->ussd_con = NULL;
|
||||
}
|
||||
|
||||
close(con->queue.bfd.fd);
|
||||
osmo_fd_unregister(&con->queue.bfd);
|
||||
osmo_timer_del(&con->auth_timeout);
|
||||
osmo_wqueue_clear(&con->queue);
|
||||
|
||||
msgb_free(con->pending_msg);
|
||||
talloc_free(con);
|
||||
}
|
||||
|
||||
static void ussd_pong(struct bsc_nat_ussd_con *conn)
|
||||
{
|
||||
struct msgb *msg;
|
||||
|
||||
msg = msgb_alloc_headroom(4096, 128, "pong message");
|
||||
if (!msg) {
|
||||
LOGP(DNAT, LOGL_ERROR, "Failed to allocate pong msg\n");
|
||||
return;
|
||||
}
|
||||
|
||||
msgb_v_put(msg, IPAC_MSGT_PONG);
|
||||
bsc_do_write(&conn->queue, msg, IPAC_PROTO_IPACCESS);
|
||||
}
|
||||
|
||||
static int forward_sccp(struct bsc_nat *nat, struct msgb *msg)
|
||||
{
|
||||
struct nat_sccp_connection *con;
|
||||
struct bsc_nat_parsed *parsed;
|
||||
|
||||
|
||||
parsed = bsc_nat_parse(msg);
|
||||
if (!parsed) {
|
||||
LOGP(DNAT, LOGL_ERROR, "Can not parse msg from USSD.\n");
|
||||
msgb_free(msg);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!parsed->dest_local_ref) {
|
||||
LOGP(DNAT, LOGL_ERROR, "No destination local reference.\n");
|
||||
msgb_free(msg);
|
||||
return -1;
|
||||
}
|
||||
|
||||
con = bsc_nat_find_con_by_bsc(nat, parsed->dest_local_ref);
|
||||
if (!con || !con->bsc) {
|
||||
LOGP(DNAT, LOGL_ERROR, "No active connection found.\n");
|
||||
msgb_free(msg);
|
||||
return -1;
|
||||
}
|
||||
|
||||
talloc_free(parsed);
|
||||
bsc_write_msg(&con->bsc->write_queue, msg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ussd_read_cb(struct osmo_fd *bfd)
|
||||
{
|
||||
struct bsc_nat_ussd_con *conn = bfd->data;
|
||||
struct msgb *msg = NULL;
|
||||
struct ipaccess_head *hh;
|
||||
int ret;
|
||||
|
||||
ret = ipa_msg_recv_buffered(bfd->fd, &msg, &conn->pending_msg);
|
||||
if (ret <= 0) {
|
||||
if (ret == -EAGAIN)
|
||||
return 0;
|
||||
LOGP(DNAT, LOGL_ERROR, "USSD Connection was lost.\n");
|
||||
bsc_nat_ussd_destroy(conn);
|
||||
return -1;
|
||||
}
|
||||
|
||||
LOGP(DNAT, LOGL_NOTICE, "MSG from USSD: %s proto: %d\n",
|
||||
osmo_hexdump(msg->data, msg->len), msg->l2h[0]);
|
||||
hh = (struct ipaccess_head *) msg->data;
|
||||
|
||||
if (hh->proto == IPAC_PROTO_IPACCESS) {
|
||||
if (msg->l2h[0] == IPAC_MSGT_ID_RESP) {
|
||||
struct tlv_parsed tvp;
|
||||
int ret;
|
||||
ret = ipa_ccm_idtag_parse(&tvp,
|
||||
(unsigned char *) msg->l2h + 2,
|
||||
msgb_l2len(msg) - 2);
|
||||
if (ret < 0) {
|
||||
LOGP(DNAT, LOGL_ERROR, "ignoring IPA response "
|
||||
"message with malformed TLVs\n");
|
||||
msgb_free(msg);
|
||||
return ret;
|
||||
}
|
||||
if (TLVP_PRESENT(&tvp, IPAC_IDTAG_UNITNAME))
|
||||
ussd_auth_con(&tvp, conn);
|
||||
} else if (msg->l2h[0] == IPAC_MSGT_PING) {
|
||||
LOGP(DNAT, LOGL_DEBUG, "Got USSD ping request.\n");
|
||||
ussd_pong(conn);
|
||||
} else {
|
||||
LOGP(DNAT, LOGL_NOTICE, "Got unknown IPACCESS message 0x%02x.\n", msg->l2h[0]);
|
||||
}
|
||||
|
||||
msgb_free(msg);
|
||||
} else if (hh->proto == IPAC_PROTO_SCCP) {
|
||||
forward_sccp(conn->nat, msg);
|
||||
} else {
|
||||
msgb_free(msg);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ussd_auth_cb(void *_data)
|
||||
{
|
||||
LOGP(DNAT, LOGL_ERROR, "USSD module didn't authenticate\n");
|
||||
bsc_nat_ussd_destroy((struct bsc_nat_ussd_con *) _data);
|
||||
}
|
||||
|
||||
static void ussd_auth_con(struct tlv_parsed *tvp, struct bsc_nat_ussd_con *conn)
|
||||
{
|
||||
const char *token;
|
||||
int len;
|
||||
if (!conn->nat->ussd_token) {
|
||||
LOGP(DNAT, LOGL_ERROR, "No USSD token set. Closing\n");
|
||||
bsc_nat_ussd_destroy(conn);
|
||||
return;
|
||||
}
|
||||
|
||||
token = (const char *) TLVP_VAL(tvp, IPAC_IDTAG_UNITNAME);
|
||||
len = TLVP_LEN(tvp, IPAC_IDTAG_UNITNAME);
|
||||
|
||||
/* last byte should be a NULL */
|
||||
if (strlen(conn->nat->ussd_token) != len - 1)
|
||||
goto disconnect;
|
||||
/* compare everything including the null byte */
|
||||
if (memcmp(conn->nat->ussd_token, token, len) != 0)
|
||||
goto disconnect;
|
||||
|
||||
/* it is authenticated now */
|
||||
if (conn->nat->ussd_con && conn->nat->ussd_con != conn)
|
||||
bsc_nat_ussd_destroy(conn->nat->ussd_con);
|
||||
|
||||
LOGP(DNAT, LOGL_ERROR, "USSD token specified. USSD provider is connected.\n");
|
||||
osmo_timer_del(&conn->auth_timeout);
|
||||
conn->authorized = 1;
|
||||
conn->nat->ussd_con = conn;
|
||||
return;
|
||||
|
||||
disconnect:
|
||||
LOGP(DNAT, LOGL_ERROR, "Wrong USSD token by client: %d\n",
|
||||
conn->queue.bfd.fd);
|
||||
bsc_nat_ussd_destroy(conn);
|
||||
}
|
||||
|
||||
static void ussd_start_auth(struct bsc_nat_ussd_con *conn)
|
||||
{
|
||||
struct msgb *msg;
|
||||
|
||||
osmo_timer_setup(&conn->auth_timeout, ussd_auth_cb, conn);
|
||||
osmo_timer_schedule(&conn->auth_timeout, conn->nat->auth_timeout, 0);
|
||||
|
||||
msg = msgb_alloc_headroom(4096, 128, "auth message");
|
||||
if (!msg) {
|
||||
LOGP(DNAT, LOGL_ERROR, "Failed to allocate auth msg\n");
|
||||
return;
|
||||
}
|
||||
|
||||
msgb_v_put(msg, IPAC_MSGT_ID_GET);
|
||||
bsc_do_write(&conn->queue, msg, IPAC_PROTO_IPACCESS);
|
||||
}
|
||||
|
||||
static int ussd_listen_cb(struct osmo_fd *bfd, unsigned int what)
|
||||
{
|
||||
struct bsc_nat_ussd_con *conn;
|
||||
struct bsc_nat *nat;
|
||||
struct sockaddr_in sa;
|
||||
socklen_t sa_len = sizeof(sa);
|
||||
int fd;
|
||||
|
||||
if (!(what & BSC_FD_READ))
|
||||
return 0;
|
||||
|
||||
fd = accept(bfd->fd, (struct sockaddr *) &sa, &sa_len);
|
||||
if (fd < 0) {
|
||||
perror("accept");
|
||||
return fd;
|
||||
}
|
||||
|
||||
nat = (struct bsc_nat *) bfd->data;
|
||||
osmo_counter_inc(nat->stats.ussd.reconn);
|
||||
|
||||
conn = bsc_nat_ussd_alloc(nat);
|
||||
if (!conn) {
|
||||
LOGP(DNAT, LOGL_ERROR, "Failed to allocate USSD con struct.\n");
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
osmo_wqueue_init(&conn->queue, 10);
|
||||
conn->queue.bfd.data = conn;
|
||||
conn->queue.bfd.fd = fd;
|
||||
conn->queue.bfd.when = BSC_FD_READ;
|
||||
conn->queue.read_cb = ussd_read_cb;
|
||||
conn->queue.write_cb = bsc_write_cb;
|
||||
|
||||
if (osmo_fd_register(&conn->queue.bfd) < 0) {
|
||||
LOGP(DNAT, LOGL_ERROR, "Failed to register USSD fd.\n");
|
||||
bsc_nat_ussd_destroy(conn);
|
||||
return -1;
|
||||
}
|
||||
|
||||
LOGP(DNAT, LOGL_NOTICE, "USSD Connection on %d with IP: %s\n",
|
||||
fd, inet_ntoa(sa.sin_addr));
|
||||
|
||||
/* do authentication */
|
||||
ussd_start_auth(conn);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bsc_ussd_init(struct bsc_nat *nat)
|
||||
{
|
||||
nat->ussd_listen.cb = ussd_listen_cb;
|
||||
nat->ussd_listen.data = nat;
|
||||
return osmo_sock_init_ofd(&nat->ussd_listen, AF_INET, SOCK_STREAM, IPPROTO_TCP,
|
||||
nat->ussd_local, 5001, OSMO_SOCK_F_BIND);
|
||||
}
|
||||
|
||||
static int forward_ussd_simple(struct nat_sccp_connection *con, struct msgb *input)
|
||||
{
|
||||
struct msgb *copy;
|
||||
struct bsc_nat_ussd_con *ussd;
|
||||
|
||||
if (!con->bsc->nat->ussd_con)
|
||||
return -1;
|
||||
|
||||
copy = msgb_alloc_headroom(4096, 128, "forward bts");
|
||||
if (!copy) {
|
||||
LOGP(DNAT, LOGL_ERROR, "Allocation failed, not forwarding.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* copy the data into the copy */
|
||||
copy->l2h = msgb_put(copy, msgb_l2len(input));
|
||||
memcpy(copy->l2h, input->l2h, msgb_l2len(input));
|
||||
|
||||
/* send it out */
|
||||
ussd = con->bsc->nat->ussd_con;
|
||||
bsc_do_write(&ussd->queue, copy, IPAC_PROTO_SCCP);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int forward_ussd(struct nat_sccp_connection *con, const struct ussd_request *req,
|
||||
struct msgb *input)
|
||||
{
|
||||
struct msgb *msg, *copy;
|
||||
struct ipac_msgt_sccp_state *state;
|
||||
struct bsc_nat_ussd_con *ussd;
|
||||
uint16_t lac, ci;
|
||||
|
||||
if (!con->bsc->nat->ussd_con)
|
||||
return -1;
|
||||
|
||||
msg = msgb_alloc_headroom(4096, 128, "forward ussd");
|
||||
if (!msg) {
|
||||
LOGP(DNAT, LOGL_ERROR, "Allocation failed, not forwarding.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
copy = msgb_alloc_headroom(4096, 128, "forward bts");
|
||||
if (!copy) {
|
||||
LOGP(DNAT, LOGL_ERROR, "Allocation failed, not forwarding.\n");
|
||||
msgb_free(msg);
|
||||
return -1;
|
||||
}
|
||||
|
||||
copy->l2h = msgb_put(copy, msgb_l2len(input));
|
||||
memcpy(copy->l2h, input->l2h, msgb_l2len(input));
|
||||
|
||||
msg->l2h = msgb_put(msg, 1);
|
||||
msg->l2h[0] = IPAC_MSGT_SCCP_OLD;
|
||||
|
||||
/* fill out the data */
|
||||
state = (struct ipac_msgt_sccp_state *) msgb_put(msg, sizeof(*state));
|
||||
state->trans_id = req->transaction_id;
|
||||
state->invoke_id = req->invoke_id;
|
||||
memcpy(&state->src_ref, &con->remote_ref, sizeof(con->remote_ref));
|
||||
memcpy(&state->dst_ref, &con->real_ref, sizeof(con->real_ref));
|
||||
memcpy(state->imsi, con->filter_state.imsi, strlen(con->filter_state.imsi));
|
||||
|
||||
/* add additional tag/values */
|
||||
lac = htons(con->lac);
|
||||
ci = htons(con->ci);
|
||||
msgb_tv_fixed_put(msg, USSD_LAC_IE, sizeof(lac), (const uint8_t *) &lac);
|
||||
msgb_tv_fixed_put(msg, USSD_CI_IE, sizeof(ci), (const uint8_t *) &ci);
|
||||
|
||||
ussd = con->bsc->nat->ussd_con;
|
||||
bsc_do_write(&ussd->queue, msg, IPAC_PROTO_IPACCESS);
|
||||
bsc_do_write(&ussd->queue, copy, IPAC_PROTO_SCCP);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bsc_ussd_check(struct nat_sccp_connection *con, struct bsc_nat_parsed *parsed,
|
||||
struct msgb *msg)
|
||||
{
|
||||
uint32_t len;
|
||||
uint8_t msg_type;
|
||||
uint8_t proto;
|
||||
uint8_t ti;
|
||||
struct gsm48_hdr *hdr48;
|
||||
struct bsc_msg_acc_lst *lst;
|
||||
struct ussd_request req;
|
||||
|
||||
/*
|
||||
* various checks to avoid the decoding work. Right now we only want to
|
||||
* decode if the connection was created for USSD, we do have a USSD access
|
||||
* list, a query, a IMSI and such...
|
||||
*/
|
||||
if (con->filter_state.con_type != FLT_CON_TYPE_SSA)
|
||||
return 0;
|
||||
|
||||
if (!con->filter_state.imsi)
|
||||
return 0;
|
||||
|
||||
/* We have not verified the IMSI yet */
|
||||
if (!con->authorized)
|
||||
return 0;
|
||||
|
||||
if (!con->bsc->nat->ussd_lst_name)
|
||||
return 0;
|
||||
if (!con->bsc->nat->ussd_query)
|
||||
return 0;
|
||||
|
||||
if (parsed->bssap != BSSAP_MSG_DTAP)
|
||||
return 0;
|
||||
|
||||
if (strlen(con->filter_state.imsi) > GSM23003_IMSI_MAX_DIGITS)
|
||||
return 0;
|
||||
|
||||
hdr48 = bsc_unpack_dtap(parsed, msg, &len);
|
||||
if (!hdr48)
|
||||
return 0;
|
||||
|
||||
proto = gsm48_hdr_pdisc(hdr48);
|
||||
msg_type = gsm48_hdr_msg_type(hdr48);
|
||||
ti = gsm48_hdr_trans_id_no_ti(hdr48);
|
||||
if (proto != GSM48_PDISC_NC_SS)
|
||||
return 0;
|
||||
|
||||
if (msg_type == GSM0480_MTYPE_REGISTER) {
|
||||
|
||||
/* now check if it is a IMSI we care about */
|
||||
lst = bsc_msg_acc_lst_find(&con->bsc->nat->access_lists,
|
||||
con->bsc->nat->ussd_lst_name);
|
||||
if (!lst)
|
||||
return 0;
|
||||
|
||||
if (bsc_msg_acc_lst_check_allow(lst, con->filter_state.imsi) != 0)
|
||||
return 0;
|
||||
|
||||
/* now decode the message and see if we really want to handle it */
|
||||
memset(&req, 0, sizeof(req));
|
||||
if (gsm0480_decode_ussd_request(hdr48, len, &req) != 1)
|
||||
return 0;
|
||||
if (req.text[0] == 0xff)
|
||||
return 0;
|
||||
|
||||
if (regexec(&con->bsc->nat->ussd_query_re,
|
||||
req.text, 0, NULL, 0) == REG_NOMATCH)
|
||||
return 0;
|
||||
|
||||
/* found a USSD query for our subscriber */
|
||||
LOGP(DNAT, LOGL_NOTICE, "Found USSD query for %s\n",
|
||||
con->filter_state.imsi);
|
||||
con->ussd_ti[ti] = 1;
|
||||
if (forward_ussd(con, &req, msg) != 0)
|
||||
return 0;
|
||||
return 1;
|
||||
} else if (msg_type == GSM0480_MTYPE_FACILITY && con->ussd_ti[ti]) {
|
||||
LOGP(DNAT, LOGL_NOTICE, "Forwarding message part of TI: %d %s\n",
|
||||
ti, con->filter_state.imsi);
|
||||
if (forward_ussd_simple(con, msg) != 0)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -5,8 +5,6 @@ SUBDIRS = \
|
|||
abis \
|
||||
subscr \
|
||||
nanobts_omlattr \
|
||||
bsc-nat \
|
||||
bsc-nat-trie \
|
||||
bssap \
|
||||
handover \
|
||||
$(NULL)
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include
|
||||
AM_CFLAGS=-Wall -ggdb3 $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOSCCP_CFLAGS) $(LIBOSMOABIS_CFLAGS) $(COVERAGE_CFLAGS) \
|
||||
$(LIBOSMOLEGACYMGCP_CFLAGS) \
|
||||
$(NULL)
|
||||
AM_LDFLAGS = $(COVERAGE_LDFLAGS)
|
||||
|
||||
EXTRA_DIST = bsc_nat_trie_test.ok prefixes.csv
|
||||
|
||||
noinst_PROGRAMS = bsc_nat_trie_test
|
||||
|
||||
bsc_nat_trie_test_SOURCES = bsc_nat_trie_test.c \
|
||||
$(top_srcdir)/src/osmo-bsc_nat/bsc_nat_rewrite_trie.c
|
||||
bsc_nat_trie_test_LDADD = $(top_builddir)/src/libbsc/libbsc.a \
|
||||
$(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) -lrt \
|
||||
$(LIBOSMOSCCP_LIBS) $(LIBOSMOVTY_LIBS) \
|
||||
$(LIBOSMOABIS_LIBS) \
|
||||
$(LIBOSMOLEGACYMGCP_LIBS) \
|
||||
$(LIBRARY_GSM) \
|
||||
$(NULL)
|
|
@ -1,95 +0,0 @@
|
|||
/*
|
||||
* (C) 2013 by On-Waves
|
||||
* (C) 2013 by Holger Hans Peter Freyther <zecke@selfish.org>
|
||||
* 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 <osmocom/bsc/nat_rewrite_trie.h>
|
||||
#include <osmocom/bsc/debug.h>
|
||||
|
||||
#include <osmocom/core/application.h>
|
||||
#include <osmocom/core/backtrace.h>
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
static const struct log_info_cat log_categories[] = {
|
||||
};
|
||||
|
||||
static const struct log_info log_info = {
|
||||
.cat = log_categories,
|
||||
.num_cat = ARRAY_SIZE(log_categories),
|
||||
};
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
struct nat_rewrite *trie;
|
||||
void *tall_ctx = talloc_named_const(NULL, 1, "bsc_nat_trie_test");
|
||||
osmo_init_logging2(tall_ctx, &log_info);
|
||||
|
||||
printf("Testing the trie\n");
|
||||
|
||||
trie = nat_rewrite_parse(NULL, "prefixes.csv");
|
||||
OSMO_ASSERT(trie);
|
||||
|
||||
/* verify that it has been parsed */
|
||||
OSMO_ASSERT(trie->prefixes == 17);
|
||||
printf("Dumping the internal trie\n");
|
||||
nat_rewrite_dump(trie);
|
||||
|
||||
/* now do the matching... */
|
||||
OSMO_ASSERT(!nat_rewrite_lookup(trie, ""));
|
||||
OSMO_ASSERT(!nat_rewrite_lookup(trie, "2"));
|
||||
|
||||
OSMO_ASSERT(strcmp(nat_rewrite_lookup(trie, "1")->rewrite, "1") == 0);
|
||||
OSMO_ASSERT(strcmp(nat_rewrite_lookup(trie, "12")->rewrite, "2") == 0);
|
||||
OSMO_ASSERT(strcmp(nat_rewrite_lookup(trie, "123")->rewrite, "3") == 0);
|
||||
OSMO_ASSERT(strcmp(nat_rewrite_lookup(trie, "1234")->rewrite, "4") == 0);
|
||||
OSMO_ASSERT(strcmp(nat_rewrite_lookup(trie, "12345")->rewrite, "5") == 0);
|
||||
OSMO_ASSERT(strcmp(nat_rewrite_lookup(trie, "123456")->rewrite, "6") == 0);
|
||||
OSMO_ASSERT(strcmp(nat_rewrite_lookup(trie, "1234567")->rewrite, "7") == 0);
|
||||
OSMO_ASSERT(strcmp(nat_rewrite_lookup(trie, "12345678")->rewrite, "8") == 0);
|
||||
OSMO_ASSERT(strcmp(nat_rewrite_lookup(trie, "123456789")->rewrite, "9") == 0);
|
||||
OSMO_ASSERT(strcmp(nat_rewrite_lookup(trie, "1234567890")->rewrite, "10") == 0);
|
||||
OSMO_ASSERT(strcmp(nat_rewrite_lookup(trie, "13")->rewrite, "11") == 0);
|
||||
OSMO_ASSERT(strcmp(nat_rewrite_lookup(trie, "14")->rewrite, "12") == 0);
|
||||
OSMO_ASSERT(strcmp(nat_rewrite_lookup(trie, "15")->rewrite, "13") == 0);
|
||||
OSMO_ASSERT(strcmp(nat_rewrite_lookup(trie, "16")->rewrite, "14") == 0);
|
||||
OSMO_ASSERT(strcmp(nat_rewrite_lookup(trie, "823455")->rewrite, "15") == 0);
|
||||
OSMO_ASSERT(strcmp(nat_rewrite_lookup(trie, "82")->rewrite, "16") == 0);
|
||||
OSMO_ASSERT(strcmp(nat_rewrite_lookup(trie, "+49123445")->rewrite, "17") == 0);
|
||||
|
||||
/* match a prefix */
|
||||
OSMO_ASSERT(strcmp(nat_rewrite_lookup(trie, "121")->rewrite, "2") == 0);
|
||||
OSMO_ASSERT(strcmp(nat_rewrite_lookup(trie, "1292323")->rewrite, "2") == 0);
|
||||
OSMO_ASSERT(strcmp(nat_rewrite_lookup(trie, "12345678901")->rewrite, "10") == 0);
|
||||
OSMO_ASSERT(strcmp(nat_rewrite_lookup(trie, "160")->rewrite, "14") == 0);
|
||||
|
||||
OSMO_ASSERT(strcmp(nat_rewrite_lookup(trie, "12345678901123452123123")->rewrite, "10") == 0);
|
||||
|
||||
/* invalid input */
|
||||
OSMO_ASSERT(!nat_rewrite_lookup(trie, "12abc"));
|
||||
|
||||
talloc_free(trie);
|
||||
|
||||
trie = nat_rewrite_parse(NULL, "does_not_exist.csv");
|
||||
OSMO_ASSERT(!trie);
|
||||
|
||||
printf("Done with the tests.\n");
|
||||
return 0;
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
Testing the trie
|
||||
Dumping the internal trie
|
||||
1,1
|
||||
12,2
|
||||
123,3
|
||||
1234,4
|
||||
12345,5
|
||||
123456,6
|
||||
1234567,7
|
||||
12345678,8
|
||||
123456789,9
|
||||
1234567890,10
|
||||
13,11
|
||||
14,12
|
||||
15,13
|
||||
16,14
|
||||
82,16
|
||||
823455,15
|
||||
+49123,17
|
||||
Done with the tests.
|
|
@ -1,25 +0,0 @@
|
|||
1,1
|
||||
12,2
|
||||
123,3
|
||||
1234,4
|
||||
12345,5
|
||||
123456,6
|
||||
1234567,7
|
||||
12345678,8
|
||||
123456789,9
|
||||
1234567890,10
|
||||
13,11
|
||||
14,12
|
||||
15,13
|
||||
16,14
|
||||
823455,15
|
||||
82,16
|
||||
+49123,17
|
||||
1ABC,18
|
||||
12345678901234567890,19
|
||||
,20
|
||||
14A,21
|
||||
124,324324324234
|
||||
1234567890,10
|
||||
no line
|
||||
99,
|
Can't render this file because it has a wrong number of fields in line 24.
|
|
@ -1,58 +0,0 @@
|
|||
AM_CPPFLAGS = \
|
||||
$(all_includes) \
|
||||
-I$(top_srcdir)/include \
|
||||
$(NULL)
|
||||
|
||||
AM_CFLAGS = \
|
||||
-Wall \
|
||||
-ggdb3 \
|
||||
$(LIBOSMOCORE_CFLAGS) \
|
||||
$(LIBOSMOGSM_CFLAGS) \
|
||||
$(LIBOSMOCTRL_LIBS) \
|
||||
$(LIBOSMOSCCP_CFLAGS) \
|
||||
$(LIBOSMOABIS_CFLAGS) \
|
||||
$(LIBOSMONETIF_CFLAGS) \
|
||||
$(LIBOSMOLEGACYMGCP_CFLAGS) \
|
||||
$(COVERAGE_CFLAGS) \
|
||||
$(NULL)
|
||||
|
||||
AM_LDFLAGS = \
|
||||
$(COVERAGE_LDFLAGS) \
|
||||
$(NULL)
|
||||
|
||||
EXTRA_DIST = \
|
||||
bsc_nat_test.ok \
|
||||
bsc_data.c \
|
||||
barr.cfg \
|
||||
barr_dup.cfg \
|
||||
prefixes.csv \
|
||||
$(NULL)
|
||||
|
||||
noinst_PROGRAMS = \
|
||||
bsc_nat_test \
|
||||
$(NULL)
|
||||
|
||||
bsc_nat_test_SOURCES = \
|
||||
bsc_nat_test.c \
|
||||
$(top_srcdir)/src/osmo-bsc_nat/bsc_filter.c \
|
||||
$(top_srcdir)/src/osmo-bsc_nat/bsc_sccp.c \
|
||||
$(top_srcdir)/src/osmo-bsc_nat/bsc_nat_utils.c \
|
||||
$(top_srcdir)/src/osmo-bsc_nat/bsc_nat_rewrite.c \
|
||||
$(top_srcdir)/src/osmo-bsc_nat/bsc_nat_rewrite_trie.c \
|
||||
$(top_srcdir)/src/osmo-bsc_nat/bsc_mgcp_utils.c \
|
||||
$(top_srcdir)/src/osmo-bsc_nat/bsc_nat_filter.c
|
||||
|
||||
bsc_nat_test_LDADD = \
|
||||
$(top_builddir)/src/libfilter/libfilter.a \
|
||||
$(top_builddir)/src/libbsc/libbsc.a \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(LIBOSMOGSM_LIBS) \
|
||||
$(LIBOSMOSCCP_LIBS) \
|
||||
$(LIBOSMOVTY_LIBS) \
|
||||
$(LIBOSMOABIS_LIBS) \
|
||||
$(LIBOSMONETIF_LIBS) \
|
||||
$(LIBOSMOCTRL_LIBS) \
|
||||
$(LIBOSMOLEGACYMGCP_LIBS) \
|
||||
$(LIBRARY_GSM) \
|
||||
-lrt \
|
||||
$(NULL)
|
|
@ -1,12 +0,0 @@
|
|||
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:
|
|
@ -1,2 +0,0 @@
|
|||
12123124:3:2:
|
||||
12123124:3:2:
|
|
@ -1,275 +0,0 @@
|
|||
/* test data */
|
||||
|
||||
/* BSC -> MSC, CR */
|
||||
static const uint8_t bsc_cr[] = {
|
||||
0x00, 0x2e, 0xfd,
|
||||
0x01, 0x00, 0x00, 0x15, 0x02, 0x02, 0x04, 0x02,
|
||||
0x42, 0xfe, 0x0f, 0x21, 0x00, 0x1f, 0x57, 0x05,
|
||||
0x08, 0x00, 0x72, 0xf4, 0x80, 0x20, 0x1c, 0xc3,
|
||||
0x51, 0x17, 0x12, 0x05, 0x08, 0x20, 0x72, 0xf4,
|
||||
0x90, 0x20, 0x1d, 0x50, 0x08, 0x29, 0x47, 0x80,
|
||||
0x00, 0x00, 0x00, 0x00, 0x80, 0x00 };
|
||||
|
||||
static const uint8_t bsc_cr_patched[] = {
|
||||
0x00, 0x2e, 0xfd,
|
||||
0x01, 0x00, 0x00, 0x05, 0x02, 0x02, 0x04, 0x02,
|
||||
0x42, 0xfe, 0x0f, 0x21, 0x00, 0x1f, 0x57, 0x05,
|
||||
0x08, 0x00, 0x72, 0xf4, 0x80, 0x20, 0x1c, 0xc3,
|
||||
0x51, 0x17, 0x12, 0x05, 0x08, 0x20, 0x72, 0xf4,
|
||||
0x90, 0x20, 0x1d, 0x50, 0x08, 0x29, 0x47, 0x80,
|
||||
0x00, 0x00, 0x00, 0x00, 0x80, 0x00 };
|
||||
|
||||
/* CC, MSC -> BSC */
|
||||
static const uint8_t msc_cc[] = {
|
||||
0x00, 0x0a, 0xfd,
|
||||
0x02, 0x00, 0x00, 0x05, 0x01, 0x1f, 0xe4, 0x02,
|
||||
0x01, 0x00 };
|
||||
static const uint8_t msc_cc_patched[] = {
|
||||
0x00, 0x0a, 0xfd,
|
||||
0x02, 0x00, 0x00, 0x15, 0x01, 0x1f, 0xe4, 0x02,
|
||||
0x01, 0x00 };
|
||||
|
||||
/* Classmark, BSC -> MSC */
|
||||
static const uint8_t bsc_dtap[] = {
|
||||
0x00, 0x17, 0xfd,
|
||||
0x06, 0x01, 0x1f, 0xe4, 0x00, 0x01, 0x10, 0x00,
|
||||
0x0e, 0x54, 0x12, 0x03, 0x50, 0x18, 0x93, 0x13,
|
||||
0x06, 0x60, 0x14, 0x45, 0x00, 0x81, 0x00 };
|
||||
|
||||
static const uint8_t bsc_dtap_patched[] = {
|
||||
0x00, 0x17, 0xfd,
|
||||
0x06, 0x01, 0x1f, 0xe4, 0x00, 0x01, 0x10, 0x00,
|
||||
0x0e, 0x54, 0x12, 0x03, 0x50, 0x18, 0x93, 0x13,
|
||||
0x06, 0x60, 0x14, 0x45, 0x00, 0x81, 0x00 };
|
||||
|
||||
/* Clear command, MSC -> BSC */
|
||||
static const uint8_t msc_dtap[] = {
|
||||
0x00, 0x0d, 0xfd,
|
||||
0x06, 0x00, 0x00, 0x05, 0x00, 0x01, 0x06, 0x00,
|
||||
0x04, 0x20, 0x04, 0x01, 0x09 };
|
||||
static const uint8_t msc_dtap_patched[] = {
|
||||
0x00, 0x0d, 0xfd,
|
||||
0x06, 0x00, 0x00, 0x15, 0x00, 0x01, 0x06, 0x00,
|
||||
0x04, 0x20, 0x04, 0x01, 0x09 };
|
||||
|
||||
/*RLSD, MSC -> BSC */
|
||||
static const uint8_t msc_rlsd[] = {
|
||||
0x00, 0x0a, 0xfd,
|
||||
0x04, 0x00, 0x00, 0x05, 0x01, 0x1f, 0xe4, 0x00,
|
||||
0x01, 0x00 };
|
||||
static const uint8_t msc_rlsd_patched[] = {
|
||||
0x00, 0x0a, 0xfd,
|
||||
0x04, 0x00, 0x00, 0x15, 0x01, 0x1f, 0xe4, 0x00,
|
||||
0x01, 0x00 };
|
||||
|
||||
/* RLC, BSC -> MSC */
|
||||
static const uint8_t bsc_rlc[] = {
|
||||
0x00, 0x07, 0xfd,
|
||||
0x05, 0x01, 0x1f, 0xe4, 0x00, 0x00, 0x15 };
|
||||
|
||||
static const uint8_t bsc_rlc_patched[] = {
|
||||
0x00, 0x07, 0xfd,
|
||||
0x05, 0x01, 0x1f, 0xe4, 0x00, 0x00, 0x05 };
|
||||
|
||||
|
||||
/* a paging command */
|
||||
static const uint8_t paging_by_lac_cmd[] = {
|
||||
0x00, 0x22, 0xfd, 0x09,
|
||||
0x00, 0x03, 0x07, 0x0b, 0x04, 0x43, 0x02, 0x00,
|
||||
0xfe, 0x04, 0x43, 0x5c, 0x00, 0xfe, 0x12, 0x00,
|
||||
0x10, 0x52, 0x08, 0x08, 0x29, 0x47, 0x10, 0x02,
|
||||
0x01, 0x50, 0x02, 0x30, 0x1a, 0x03, 0x05, 0x20,
|
||||
0x15 };
|
||||
|
||||
/* an assignment command */
|
||||
static const uint8_t ass_cmd[] = {
|
||||
0x00, 0x12, 0xfd, 0x06,
|
||||
0x00, 0x00, 0x49, 0x00, 0x01, 0x0b, 0x00, 0x09,
|
||||
0x01, 0x0b, 0x03, 0x01, 0x0a, 0x11, 0x01, 0x00,
|
||||
0x01 };
|
||||
|
||||
/* identity response */
|
||||
static const uint8_t id_resp[] = {
|
||||
0x00, 0x15, 0xfd, 0x06, 0x01, 0x1c, 0xdc,
|
||||
0x00, 0x01, 0x0e, 0x01, 0x00, 0x0b, 0x05, 0x59,
|
||||
0x08, 0x29, 0x40, 0x21, 0x03, 0x07, 0x48, 0x66,
|
||||
0x31
|
||||
};
|
||||
|
||||
/* sms code msg */
|
||||
static const uint8_t smsc_rewrite[] = {
|
||||
0x00, 0x30, 0xfd, 0x06, 0x01, 0x13, 0x1e, 0x00,
|
||||
0x01, 0x29, 0x01, 0x03, 0x26, 0x09, 0x01, 0x23,
|
||||
0x00, 0x0c, 0x00, 0x07, 0x91, 0x36, 0x19, 0x08,
|
||||
0x00, 0x10, 0x50, 0x17, 0x21, 0x0c, 0x0f, 0x81,
|
||||
0x00, 0x94, 0x51, 0x87, 0x86, 0x78, 0x46, 0xf5,
|
||||
0x00, 0x00, 0x09, 0xcc, 0xb7, 0xbd, 0x0c, 0xca,
|
||||
0xbf, 0xeb, 0x20
|
||||
};
|
||||
|
||||
static const uint8_t smsc_rewrite_patched[] = {
|
||||
0x00, 0x31, 0xfd, 0x06, 0x01, 0x13, 0x1e, 0x00,
|
||||
0x01, 0x2a, 0x01, 0x03, 0x27, 0x09, 0x01, 0x24,
|
||||
0x00, 0x0c, 0x00, 0x08, 0x91, 0x66, 0x66, 0x66,
|
||||
0x66, 0x66, 0x66, 0xf7, 0x17, 0x01, 0x0c, 0x0f,
|
||||
0x81, 0x00, 0x94, 0x51, 0x87, 0x86, 0x78, 0x46,
|
||||
0xf5, 0x00, 0x00, 0x09, 0xcc, 0xb7, 0xbd, 0x0c,
|
||||
0xca, 0xbf, 0xeb, 0x20
|
||||
};
|
||||
|
||||
static const uint8_t smsc_rewrite_patched_hdr[] = {
|
||||
0x00, 0x30, 0xfd, 0x06, 0x01, 0x13, 0x1e, 0x00,
|
||||
0x01, 0x29, 0x01, 0x03, 0x26, 0x09, 0x01, 0x23,
|
||||
0x00, 0x0c, 0x00, 0x07, 0x91, 0x36, 0x19, 0x08,
|
||||
0x00, 0x10, 0x50, 0x17, 0x01, 0x0c, 0x0f, 0x81,
|
||||
0x00, 0x94, 0x51, 0x87, 0x86, 0x78, 0x46, 0xf5,
|
||||
0x00, 0x00, 0x09, 0xcc, 0xb7, 0xbd, 0x0c, 0xca,
|
||||
0xbf, 0xeb, 0x20
|
||||
};
|
||||
|
||||
static const uint8_t smsc_rewrite_num_patched[] = {
|
||||
0x00, 0x2f, 0xfd, 0x06, 0x01, 0x13, 0x1e, 0x00,
|
||||
0x01, 0x28, 0x01, 0x03, 0x25, 0x09, 0x01, 0x22,
|
||||
0x00, 0x0c, 0x00, 0x07, 0x91, 0x36, 0x19, 0x08,
|
||||
0x00, 0x10, 0x50, 0x16, 0x21, 0x0c, 0x0d, 0x91,
|
||||
0x23, 0x51, 0x87, 0x86, 0x78, 0x46, 0xf5,
|
||||
0x00, 0x00, 0x09, 0xcc, 0xb7, 0xbd, 0x0c, 0xca,
|
||||
0xbf, 0xeb, 0x20
|
||||
};
|
||||
|
||||
static const uint8_t smsc_rewrite_num_patched_tp_srr[] = {
|
||||
0x00, 0x2f, 0xfd, 0x06, 0x01, 0x13, 0x1e, 0x00,
|
||||
0x01, 0x28, 0x01, 0x03, 0x25, 0x09, 0x01, 0x22,
|
||||
0x00, 0x0c, 0x00, 0x07, 0x91, 0x36, 0x19, 0x08,
|
||||
0x00, 0x10, 0x50, 0x16, 0x01, 0x0c, 0x0d, 0x91,
|
||||
0x23, 0x51, 0x87, 0x86, 0x78, 0x46, 0xf5,
|
||||
0x00, 0x00, 0x09, 0xcc, 0xb7, 0xbd, 0x0c, 0xca,
|
||||
0xbf, 0xeb, 0x20
|
||||
};
|
||||
|
||||
/*
|
||||
* MGCP messages
|
||||
*/
|
||||
|
||||
/* nothing to patch */
|
||||
static const char crcx[] = "CRCX 23265295 8@mgw MGCP 1.0\r\nC: 394b0439fb\r\nL: p:20, a:AMR, nt:IN\r\nM: recvonly\r\n";
|
||||
static const char crcx_patched[] = "CRCX 23265295 1e@mgw MGCP 1.0\r\nC: 394b0439fb\r\nL: p:20, a:AMR, nt:IN\r\nM: recvonly\r\n";
|
||||
|
||||
|
||||
/* patch the ip and port */
|
||||
static const char crcx_resp[] = "200 23265295\r\nI: 1\r\n\r\nv=0\r\nc=IN IP4 172.16.18.2\r\nm=audio 4002 RTP/AVP 98 3\r\na=rtpmap:98 AMR/8000\r\n";
|
||||
static const char crcx_resp_patched[] = "200 23265295\r\nI: 1\r\n\r\nv=0\r\nc=IN IP4 10.0.0.1\r\nm=audio 999 RTP/AVP 98 3\r\na=rtpmap:98 AMR/8000\r\na=fmtp:98 mode-set=2\r\n";
|
||||
|
||||
/* patch the ip and port */
|
||||
static const char mdcx[] = "MDCX 23330829 8@mgw MGCP 1.0\r\nC: 394b0439fb\r\nI: 1\r\nL: p:20, a:AMR, nt:IN\r\nM: recvonly\r\n\r\nv=0\r\no=- 1049380491 0 IN IP4 172.16.18.2\r\ns=-\r\nc=IN IP4 172.16.18.2\r\nt=0 0\r\nm=audio 4410 RTP/AVP 126\r\na=rtpmap:126 AMR/8000/1\r\na=fmtp:126 mode-set=2;start-mode=0\r\na=ptime:20\r\na=recvonly\r\nm=image 4412 udptl t38\r\na=T38FaxVersion:0\r\na=T38MaxBitRate:14400\r\n";
|
||||
static const char mdcx_patched[] = "MDCX 23330829 1e@mgw MGCP 1.0\r\nC: 394b0439fb\r\nI: 1\r\nL: p:20, a:AMR, nt:IN\r\nM: recvonly\r\n\r\nv=0\r\no=- 1049380491 0 IN IP4 172.16.18.2\r\ns=-\r\nc=IN IP4 10.0.0.23\r\nt=0 0\r\nm=audio 6666 RTP/AVP 126\r\na=rtpmap:126 AMR/8000/1\r\na=fmtp:126 mode-set=2;start-mode=0\r\na=ptime:20\r\na=recvonly\r\nm=image 4412 udptl t38\r\na=T38FaxVersion:0\r\na=T38MaxBitRate:14400\r\n";
|
||||
|
||||
|
||||
static const char mdcx_resp[] = "200 23330829\r\n\r\nv=0\r\nc=IN IP4 172.16.18.2\r\nm=audio 4002 RTP/AVP 98\r\na=rtpmap:98 AMR/8000\r\n";
|
||||
static const char mdcx_resp_patched[] = "200 23330829\r\n\r\nv=0\r\nc=IN IP4 10.0.0.23\r\nm=audio 5555 RTP/AVP 98\r\na=rtpmap:98 AMR/8000\r\na=fmtp:98 mode-set=2\r\n";
|
||||
|
||||
/* different line ending */
|
||||
static const char mdcx_resp2[] = "200 33330829\n\nv=0\nc=IN IP4 172.16.18.2\nm=audio 4002 RTP/AVP 98\na=rtpmap:98 AMR/8000\n";
|
||||
static const char mdcx_resp_patched2[] = "200 33330829\n\nv=0\nc=IN IP4 10.0.0.23\nm=audio 5555 RTP/AVP 98\na=rtpmap:98 AMR/8000\na=fmtp:98 mode-set=2\n";
|
||||
static const char mdcx_resp_patched2_noamr[] = "200 33330829\n\nv=0\nc=IN IP4 10.0.0.23\nm=audio 5555 RTP/AVP 98\na=rtpmap:98 AMR/8000\n";
|
||||
|
||||
struct mgcp_patch_test {
|
||||
const char *orig;
|
||||
const char *patch;
|
||||
const char *ip;
|
||||
const int port;
|
||||
const int payload_type;
|
||||
const int ensure_mode_set;
|
||||
};
|
||||
|
||||
static const struct mgcp_patch_test mgcp_messages[] = {
|
||||
{
|
||||
.orig = crcx,
|
||||
.patch = crcx_patched,
|
||||
.ip = "0.0.0.0",
|
||||
.port = 2323,
|
||||
.ensure_mode_set = 1,
|
||||
},
|
||||
{
|
||||
.orig = crcx_resp,
|
||||
.patch = crcx_resp_patched,
|
||||
.ip = "10.0.0.1",
|
||||
.port = 999,
|
||||
.payload_type = 98,
|
||||
.ensure_mode_set = 1,
|
||||
},
|
||||
{
|
||||
.orig = mdcx,
|
||||
.patch = mdcx_patched,
|
||||
.ip = "10.0.0.23",
|
||||
.port = 6666,
|
||||
.payload_type = 126,
|
||||
.ensure_mode_set = 1,
|
||||
},
|
||||
{
|
||||
.orig = mdcx_resp,
|
||||
.patch = mdcx_resp_patched,
|
||||
.ip = "10.0.0.23",
|
||||
.port = 5555,
|
||||
.payload_type = 98,
|
||||
.ensure_mode_set = 1,
|
||||
},
|
||||
{
|
||||
.orig = mdcx_resp2,
|
||||
.patch = mdcx_resp_patched2,
|
||||
.ip = "10.0.0.23",
|
||||
.port = 5555,
|
||||
.payload_type = 98,
|
||||
.ensure_mode_set = 1,
|
||||
},
|
||||
{
|
||||
.orig = mdcx_resp2,
|
||||
.patch = mdcx_resp_patched2_noamr,
|
||||
.ip = "10.0.0.23",
|
||||
.port = 5555,
|
||||
.payload_type = 98,
|
||||
.ensure_mode_set = 0,
|
||||
},
|
||||
};
|
||||
|
||||
/* CC Setup messages */
|
||||
static const uint8_t cc_setup_national[] = {
|
||||
0x00, 0x20, 0xfd, 0x06, 0x01, 0x12,
|
||||
0x6d, 0x00, 0x01, 0x19, 0x01, 0x00, 0x16, 0x03,
|
||||
0x05, 0x04, 0x06, 0x60, 0x04, 0x02, 0x00, 0x05,
|
||||
0x81, 0x5e, 0x06, 0x81, 0x10, 0x27, 0x33, 0x63,
|
||||
0x66, 0x15, 0x02, 0x11, 0x01
|
||||
};
|
||||
|
||||
static const uint8_t cc_setup_national_patched[] = {
|
||||
0x00, 0x21, 0xfd, 0x06, 0x01, 0x12,
|
||||
0x6d, 0x00, 0x01, 0x1a, 0x01, 0x00, 0x17, 0x03,
|
||||
0x05, 0x04, 0x06, 0x60, 0x04, 0x02, 0x00, 0x05,
|
||||
0x81, 0x5e, 0x07, 0x91, 0x94, 0x71, 0x32, 0x33,
|
||||
0x66, 0xf6, 0x15, 0x02, 0x11, 0x01
|
||||
};
|
||||
|
||||
/* patch the phone number of cc_setup_national_patched */
|
||||
static const uint8_t cc_setup_national_patched_patched[] = {
|
||||
0x00, 0x21, 0xfd, 0x06, 0x01, 0x12,
|
||||
0x6d, 0x00, 0x01, 0x1a, 0x01, 0x00, 0x17, 0x03,
|
||||
0x05, 0x04, 0x06, 0x60, 0x04, 0x02, 0x00, 0x05,
|
||||
0x81, 0x5e, 0x07, 0x91, 0x63, 0x71, 0x32, 0x33,
|
||||
0x66, 0xf6, 0x15, 0x02, 0x11, 0x01
|
||||
};
|
||||
|
||||
static const uint8_t cc_setup_international[] = {
|
||||
0x00, 0x22, 0xfd, 0x06, 0x01, 0x13,
|
||||
0xe7, 0x00, 0x01, 0x1b, 0x01, 0x00, 0x18, 0x03,
|
||||
0x45, 0x04, 0x06, 0x60, 0x04, 0x02, 0x00, 0x05,
|
||||
0x81, 0x5e, 0x08, 0x81, 0x00, 0x94, 0x71, 0x33,
|
||||
0x63, 0x66, 0x03, 0x15, 0x02, 0x11, 0x01
|
||||
};
|
||||
|
||||
static const uint8_t cc_setup_national_again[] = {
|
||||
0x00, 0x22, 0xfd, 0x06, 0x01, 0x12, 0x6d, 0x00,
|
||||
0x01, 0x1b, 0x01, 0x00, 0x18, 0x03, 0x05, 0x04,
|
||||
0x06, 0x60, 0x04, 0x02, 0x00, 0x05, 0x81, 0x5e,
|
||||
0x08, 0x81, 0x63, 0x94, 0x71, 0x32, 0x33, 0x66,
|
||||
0xf6, 0x15, 0x02, 0x11, 0x01
|
||||
};
|
File diff suppressed because it is too large
Load Diff
|
@ -1,39 +0,0 @@
|
|||
Testing BSS Filtering.
|
||||
Going to test item: 0
|
||||
Going to test item: 1
|
||||
Going to test item: 2
|
||||
Going to test item: 3
|
||||
Going to test item: 4
|
||||
Going to test item: 5
|
||||
Going to test item: 6
|
||||
Going to test item: 7
|
||||
Going to test item: 8
|
||||
Going to test item: 9
|
||||
Going to test item: 10
|
||||
Going to test item: 11
|
||||
Going to test item: 12
|
||||
Testing connection tracking.
|
||||
Testing paging by lac.
|
||||
Testing MGCP.
|
||||
Testing finding of a BSC Connection
|
||||
Testing rewriting MGCP messages.
|
||||
Testing MGCP response parsing.
|
||||
Testing SMSC rewriting.
|
||||
Attempting to only rewrite the HDR
|
||||
Attempting to change nothing.
|
||||
Testing SMS TP-DA rewriting.
|
||||
IMSI: 12123115 CM: 3 LU: 4
|
||||
IMSI: 12123116 CM: 3 LU: 4
|
||||
IMSI: 12123117 CM: 3 LU: 4
|
||||
IMSI: 12123118 CM: 3 LU: 4
|
||||
IMSI: 12123119 CM: 3 LU: 4
|
||||
IMSI: 12123120 CM: 3 LU: 4
|
||||
IMSI: 12123123 CM: 3 LU: 1
|
||||
IMSI: 12123124 CM: 3 LU: 2
|
||||
IMSI: 12123125 CM: 3 LU: 3
|
||||
IMSI: 12123126 CM: 3 LU: 4
|
||||
IMSI: 12123127 CM: 3 LU: 5
|
||||
IMSI: 12123128 CM: 3 LU: 6
|
||||
IMSI: 12123124 CM: 3 LU: 2
|
||||
Testing LAC extraction from SCCP CR
|
||||
Testing execution completed.
|
|
@ -1,2 +0,0 @@
|
|||
0172,0049
|
||||
+49,0
|
|
|
@ -20,13 +20,6 @@ cat $abs_srcdir/channel/channel_test.ok > expout
|
|||
AT_CHECK([$abs_top_builddir/tests/channel/channel_test], [], [expout], [ignore])
|
||||
AT_CLEANUP
|
||||
|
||||
AT_SETUP([bsc-nat-trie])
|
||||
AT_KEYWORDS([bsc-nat-trie])
|
||||
cp $abs_srcdir/bsc-nat-trie/prefixes.csv .
|
||||
cat $abs_srcdir/bsc-nat-trie/bsc_nat_trie_test.ok > expout
|
||||
AT_CHECK([$abs_top_builddir/tests/bsc-nat-trie/bsc_nat_trie_test], [], [expout], [ignore])
|
||||
AT_CLEANUP
|
||||
|
||||
AT_SETUP([abis])
|
||||
AT_KEYWORDS([abis])
|
||||
cat $abs_srcdir/abis/abis_test.ok > expout
|
||||
|
|
|
@ -249,357 +249,6 @@ class TestVTYBSC(TestVTYGenericBSC):
|
|||
self.assert_(res.find("core-location-area-code 666") > 0)
|
||||
self.assert_(res.find("core-cell-identity 333") > 0)
|
||||
|
||||
class TestVTYNAT(TestVTYGenericBSC):
|
||||
|
||||
def vty_command(self):
|
||||
return ["./src/osmo-bsc_nat/osmo-bsc_nat", "-l", "127.0.0.1", "-c",
|
||||
"doc/examples/osmo-bsc_nat/osmo-bsc_nat.cfg"]
|
||||
|
||||
def vty_app(self):
|
||||
return (4244, "src/osmo-bsc_nat/osmo-bsc_nat", "OsmoBSCNAT", "nat")
|
||||
|
||||
def testBSCreload(self):
|
||||
# Use different port for the mock msc to avoid clashing with
|
||||
# the osmo-bsc_nat itself
|
||||
ip = "127.0.0.1"
|
||||
port = 5522
|
||||
self.vty.enable()
|
||||
bscs1 = self.vty.command("show bscs-config")
|
||||
nat_bsc_reload(self)
|
||||
bscs2 = self.vty.command("show bscs-config")
|
||||
# check that multiple calls to bscs-config-file give the same result
|
||||
self.assertEquals(bscs1, bscs2)
|
||||
|
||||
# add new bsc
|
||||
self.vty.command("configure terminal")
|
||||
self.vty.command("nat")
|
||||
self.vty.command("bsc 5")
|
||||
self.vty.command("token key")
|
||||
self.vty.command("location_area_code 666")
|
||||
self.vty.command("end")
|
||||
|
||||
# update bsc token
|
||||
self.vty.command("configure terminal")
|
||||
self.vty.command("nat")
|
||||
self.vty.command("bsc 1")
|
||||
self.vty.command("token xyu")
|
||||
self.vty.command("end")
|
||||
|
||||
nat_msc_ip(self, ip, port)
|
||||
msc_socket, msc = nat_msc_test(self, ip, port, verbose=True)
|
||||
try:
|
||||
b0 = nat_bsc_sock_test(0, "lol", verbose=True, proc=self.proc)
|
||||
b1 = nat_bsc_sock_test(1, "xyu", verbose=True, proc=self.proc)
|
||||
b2 = nat_bsc_sock_test(5, "key", verbose=True, proc=self.proc)
|
||||
|
||||
self.assertEquals("3 BSCs configured", self.vty.command("show nat num-bscs-configured"))
|
||||
self.assertTrue(3 == nat_bsc_num_con(self))
|
||||
self.assertEquals("MSC is connected: 1", self.vty.command("show msc connection"))
|
||||
|
||||
nat_bsc_reload(self)
|
||||
bscs2 = self.vty.command("show bscs-config")
|
||||
# check that the reset to initial config succeeded
|
||||
self.assertEquals(bscs1, bscs2)
|
||||
|
||||
self.assertEquals("2 BSCs configured", self.vty.command("show nat num-bscs-configured"))
|
||||
self.assertTrue(1 == nat_bsc_num_con(self))
|
||||
rem = self.vty.command("show bsc connections").split(' ')
|
||||
# remaining connection is for BSC0
|
||||
self.assertEquals('0', rem[2])
|
||||
# remaining connection is authorized
|
||||
self.assertEquals('1', rem[4])
|
||||
self.assertEquals("MSC is connected: 1", self.vty.command("show msc connection"))
|
||||
finally:
|
||||
msc.close()
|
||||
msc_socket.close()
|
||||
|
||||
def testVtyTree(self):
|
||||
self.vty.enable()
|
||||
self.assertTrue(self.vty.verify('configure terminal', ['']))
|
||||
self.assertEquals(self.vty.node(), 'config')
|
||||
self.checkForEndAndExit()
|
||||
self.assertTrue(self.vty.verify('mgcp', ['']))
|
||||
self.assertEquals(self.vty.node(), 'config-mgcp')
|
||||
self.checkForEndAndExit()
|
||||
self.assertTrue(self.vty.verify('exit', ['']))
|
||||
self.assertEquals(self.vty.node(), 'config')
|
||||
self.assertTrue(self.vty.verify('nat', ['']))
|
||||
self.assertEquals(self.vty.node(), 'config-nat')
|
||||
self.checkForEndAndExit()
|
||||
self.assertTrue(self.vty.verify('bsc 0', ['']))
|
||||
self.assertEquals(self.vty.node(), 'config-nat-bsc')
|
||||
self.checkForEndAndExit()
|
||||
self.assertTrue(self.vty.verify('exit', ['']))
|
||||
self.assertEquals(self.vty.node(), 'config-nat')
|
||||
self.assertTrue(self.vty.verify('exit', ['']))
|
||||
self.assertEquals(self.vty.node(), 'config')
|
||||
self.assertTrue(self.vty.verify('exit', ['']))
|
||||
self.assertTrue(self.vty.node() is None)
|
||||
|
||||
def testRewriteNoRewrite(self):
|
||||
self.vty.enable()
|
||||
res = self.vty.command("configure terminal")
|
||||
res = self.vty.command("nat")
|
||||
res = self.vty.command("number-rewrite rewrite.cfg")
|
||||
res = self.vty.command("no number-rewrite")
|
||||
|
||||
def testEnsureNoEnsureModeSet(self):
|
||||
self.vty.enable()
|
||||
res = self.vty.command("configure terminal")
|
||||
res = self.vty.command("nat")
|
||||
|
||||
# Ensure the default
|
||||
res = self.vty.command("show running-config")
|
||||
self.assert_(res.find('\n sdp-ensure-amr-mode-set') > 0)
|
||||
|
||||
self.vty.command("sdp-ensure-amr-mode-set")
|
||||
res = self.vty.command("show running-config")
|
||||
self.assert_(res.find('\n sdp-ensure-amr-mode-set') > 0)
|
||||
|
||||
self.vty.command("no sdp-ensure-amr-mode-set")
|
||||
res = self.vty.command("show running-config")
|
||||
self.assert_(res.find('\n no sdp-ensure-amr-mode-set') > 0)
|
||||
|
||||
def testRewritePostNoRewrite(self):
|
||||
self.vty.enable()
|
||||
self.vty.command("configure terminal")
|
||||
self.vty.command("nat")
|
||||
self.vty.verify("number-rewrite-post rewrite.cfg", [''])
|
||||
self.vty.verify("no number-rewrite-post", [''])
|
||||
|
||||
|
||||
def testPrefixTreeLoading(self):
|
||||
cfg = os.path.join(confpath, "tests/bsc-nat-trie/prefixes.csv")
|
||||
|
||||
self.vty.enable()
|
||||
self.vty.command("configure terminal")
|
||||
self.vty.command("nat")
|
||||
res = self.vty.command("prefix-tree %s" % cfg)
|
||||
self.assertEqual(res, "% prefix-tree loaded 17 rules.")
|
||||
self.vty.command("end")
|
||||
|
||||
res = self.vty.command("show prefix-tree")
|
||||
self.assertEqual(res, '1,1\r\n12,2\r\n123,3\r\n1234,4\r\n12345,5\r\n123456,6\r\n1234567,7\r\n12345678,8\r\n123456789,9\r\n1234567890,10\r\n13,11\r\n14,12\r\n15,13\r\n16,14\r\n82,16\r\n823455,15\r\n+49123,17')
|
||||
|
||||
self.vty.command("configure terminal")
|
||||
self.vty.command("nat")
|
||||
self.vty.command("no prefix-tree")
|
||||
self.vty.command("end")
|
||||
|
||||
res = self.vty.command("show prefix-tree")
|
||||
self.assertEqual(res, "% there is now prefix tree loaded.")
|
||||
|
||||
def testUssdSideChannelProvider(self):
|
||||
self.vty.command("end")
|
||||
self.vty.enable()
|
||||
self.vty.command("configure terminal")
|
||||
self.vty.command("nat")
|
||||
self.vty.command("ussd-token key")
|
||||
self.vty.command("end")
|
||||
|
||||
res = self.vty.verify("show ussd-connection", ['The USSD side channel provider is not connected and not authorized.'])
|
||||
self.assertTrue(res)
|
||||
|
||||
ussdSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
ussdSocket.connect(('127.0.0.1', 5001))
|
||||
ussdSocket.settimeout(2.0)
|
||||
print "Connected to %s:%d" % ussdSocket.getpeername()
|
||||
|
||||
print "Expecting ID_GET request"
|
||||
data = ussdSocket.recv(4)
|
||||
self.assertEqual(data, "\x00\x01\xfe\x04")
|
||||
|
||||
print "Going to send ID_RESP response"
|
||||
res = ussdSocket.send(IPA().id_resp(IPA().tag_name('key')))
|
||||
self.assertEqual(res, 10)
|
||||
|
||||
# initiating PING/PONG cycle to know, that the ID_RESP message has been processed
|
||||
|
||||
print "Going to send PING request"
|
||||
res = ussdSocket.send(IPA().ping())
|
||||
self.assertEqual(res, 4)
|
||||
|
||||
print "Expecting PONG response"
|
||||
data = ussdSocket.recv(4)
|
||||
self.assertEqual(data, "\x00\x01\xfe\x01")
|
||||
|
||||
res = self.vty.verify("show ussd-connection", ['The USSD side channel provider is connected and authorized.'])
|
||||
self.assertTrue(res)
|
||||
|
||||
print "Going to shut down connection"
|
||||
ussdSocket.shutdown(socket.SHUT_WR)
|
||||
|
||||
print "Expecting EOF"
|
||||
data = ussdSocket.recv(4)
|
||||
self.assertEqual(data, "")
|
||||
|
||||
ussdSocket.close()
|
||||
|
||||
res = self.vty.verify("show ussd-connection", ['The USSD side channel provider is not connected and not authorized.'])
|
||||
self.assertTrue(res)
|
||||
|
||||
def testAccessList(self):
|
||||
"""
|
||||
Verify that the imsi-deny can have a reject cause or no reject cause
|
||||
"""
|
||||
self.vty.enable()
|
||||
self.vty.command("configure terminal")
|
||||
self.vty.command("nat")
|
||||
|
||||
# Old default
|
||||
self.vty.command("access-list test-default imsi-deny ^123[0-9]*$")
|
||||
res = self.vty.command("show running-config").split("\r\n")
|
||||
asserted = False
|
||||
for line in res:
|
||||
if line.startswith(" access-list test-default"):
|
||||
self.assertEqual(line, " access-list test-default imsi-deny ^123[0-9]*$ 11 11")
|
||||
asserted = True
|
||||
self.assert_(asserted)
|
||||
|
||||
# Check the optional CM Service Reject Cause
|
||||
self.vty.command("access-list test-cm-deny imsi-deny ^123[0-9]*$ 42").split("\r\n")
|
||||
res = self.vty.command("show running-config").split("\r\n")
|
||||
asserted = False
|
||||
for line in res:
|
||||
if line.startswith(" access-list test-cm"):
|
||||
self.assertEqual(line, " access-list test-cm-deny imsi-deny ^123[0-9]*$ 42 11")
|
||||
asserted = True
|
||||
self.assert_(asserted)
|
||||
|
||||
# Check the optional LU Reject Cause
|
||||
self.vty.command("access-list test-lu-deny imsi-deny ^123[0-9]*$ 23 42").split("\r\n")
|
||||
res = self.vty.command("show running-config").split("\r\n")
|
||||
asserted = False
|
||||
for line in res:
|
||||
if line.startswith(" access-list test-lu"):
|
||||
self.assertEqual(line, " access-list test-lu-deny imsi-deny ^123[0-9]*$ 23 42")
|
||||
asserted = True
|
||||
self.assert_(asserted)
|
||||
|
||||
|
||||
def add_nat_test(suite, workdir):
|
||||
if not os.path.isfile(os.path.join(workdir, "src/osmo-bsc_nat/osmo-bsc_nat")):
|
||||
print("Skipping the NAT test")
|
||||
return
|
||||
test = unittest.TestLoader().loadTestsFromTestCase(TestVTYNAT)
|
||||
suite.addTest(test)
|
||||
|
||||
def nat_bsc_reload(x):
|
||||
x.vty.command("configure terminal")
|
||||
x.vty.command("nat")
|
||||
x.vty.command("bscs-config-file bscs.cfg")
|
||||
x.vty.command("end")
|
||||
|
||||
def nat_msc_ip(x, ip, port):
|
||||
x.vty.command("configure terminal")
|
||||
x.vty.command("nat")
|
||||
x.vty.command("msc ip " + ip)
|
||||
x.vty.command("msc port " + str(port))
|
||||
x.vty.command("end")
|
||||
|
||||
def data2str(d):
|
||||
return d.encode('hex').lower()
|
||||
|
||||
def nat_msc_test(x, ip, port, verbose = False):
|
||||
msc = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
msc.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
msc.settimeout(5)
|
||||
msc.bind((ip, port))
|
||||
msc.listen(5)
|
||||
if (verbose):
|
||||
print "MSC is ready at " + ip
|
||||
conn = None
|
||||
while True:
|
||||
vty_response = x.vty.command("show msc connection")
|
||||
print "'show msc connection' says: %r" % vty_response
|
||||
if vty_response == "MSC is connected: 1":
|
||||
# success
|
||||
break;
|
||||
if vty_response != "MSC is connected: 0":
|
||||
raise Exception("Unexpected response to 'show msc connection'"
|
||||
" vty command: %r" % vty_response)
|
||||
|
||||
timeout_retries = 6
|
||||
while timeout_retries > 0:
|
||||
try:
|
||||
conn, addr = msc.accept()
|
||||
print "MSC got connection from ", addr
|
||||
break
|
||||
except socket.timeout:
|
||||
print "socket timed out."
|
||||
timeout_retries -= 1
|
||||
continue
|
||||
|
||||
if not conn:
|
||||
raise Exception("VTY reports MSC is connected, but I haven't"
|
||||
" connected yet: %r %r" % (ip, port))
|
||||
return msc, conn
|
||||
|
||||
def ipa_handle_small(x, verbose = False):
|
||||
s = data2str(x.recv(4))
|
||||
if len(s) != 4*2:
|
||||
raise Exception("expected to receive 4 bytes, but got %d (%r)" % (len(s)/2, s))
|
||||
if "0001fe00" == s:
|
||||
if (verbose):
|
||||
print "\tBSC <- NAT: PING?"
|
||||
x.send(IPA().pong())
|
||||
elif "0001fe06" == s:
|
||||
if (verbose):
|
||||
print "\tBSC <- NAT: IPA ID ACK"
|
||||
x.send(IPA().id_ack())
|
||||
elif "0001fe00" == s:
|
||||
if (verbose):
|
||||
print "\tBSC <- NAT: PONG!"
|
||||
else:
|
||||
if (verbose):
|
||||
print "\tBSC <- NAT: ", s
|
||||
|
||||
def ipa_handle_resp(x, tk, verbose = False, proc=None):
|
||||
s = data2str(x.recv(38))
|
||||
if "0023fe040108010701020103010401050101010011" in s:
|
||||
retries = 3
|
||||
while True:
|
||||
print "\tsending IPA identity(%s) at %s" % (tk, time.strftime("%T"))
|
||||
try:
|
||||
x.send(IPA().id_resp(IPA().identity(name = tk.encode('utf-8'))))
|
||||
print "\tdone sending IPA identity(%s) at %s" % (tk,
|
||||
time.strftime("%T"))
|
||||
break
|
||||
except:
|
||||
print "\tfailed sending IPA identity at", time.strftime("%T")
|
||||
if proc:
|
||||
print "\tproc.poll() = %r" % proc.poll()
|
||||
if retries < 1:
|
||||
print "\tgiving up"
|
||||
raise
|
||||
print "\tretrying (%d attempts left)" % retries
|
||||
retries -= 1
|
||||
else:
|
||||
if (verbose):
|
||||
print "\tBSC <- NAT: ", s
|
||||
|
||||
def nat_bsc_num_con(x):
|
||||
return len(x.vty.command("show bsc connections").split('\n'))
|
||||
|
||||
def nat_bsc_sock_test(nr, tk, verbose = False, proc=None):
|
||||
bsc = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
bsc.bind(('127.0.0.1', 0))
|
||||
bsc.connect(('127.0.0.1', 5000))
|
||||
if (verbose):
|
||||
print "BSC%d " %nr
|
||||
print "\tconnected to %s:%d" % bsc.getpeername()
|
||||
if proc:
|
||||
print "\tproc.poll() = %r" % proc.poll()
|
||||
print "\tproc.pid = %r" % proc.pid
|
||||
ipa_handle_small(bsc, verbose)
|
||||
ipa_handle_resp(bsc, tk, verbose, proc=proc)
|
||||
if proc:
|
||||
print "\tproc.poll() = %r" % proc.poll()
|
||||
bsc.recv(27) # MGCP msg
|
||||
if proc:
|
||||
print "\tproc.poll() = %r" % proc.poll()
|
||||
ipa_handle_small(bsc, verbose)
|
||||
return bsc
|
||||
|
||||
def add_bsc_test(suite, workdir):
|
||||
if not os.path.isfile(os.path.join(workdir, "src/osmo-bsc/osmo-bsc")):
|
||||
|
@ -639,7 +288,6 @@ if __name__ == '__main__':
|
|||
print "Running tests for specific VTY commands"
|
||||
suite = unittest.TestSuite()
|
||||
add_bsc_test(suite, workdir)
|
||||
add_nat_test(suite, workdir)
|
||||
|
||||
if args.test_name:
|
||||
osmoutil.pick_tests(suite, *args.test_name)
|
||||
|
|
Loading…
Reference in New Issue