mirror of
				https://gitea.osmocom.org/cellular-infrastructure/osmo-mgw.git
				synced 2025-11-03 21:43:32 +00:00 
			
		
		
		
	Compare commits
	
		
			74 Commits
		
	
	
		
			1.4.0
			...
			openbsc/0.
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					503f445d2c | ||
| 
						 | 
					7c13f4b975 | ||
| 
						 | 
					158a382212 | ||
| 
						 | 
					82e5339504 | ||
| 
						 | 
					63c2c828c0 | ||
| 
						 | 
					1979e7227f | ||
| 
						 | 
					ff4ded5903 | ||
| 
						 | 
					684908e167 | ||
| 
						 | 
					bb976dada9 | ||
| 
						 | 
					614da5e88a | ||
| 
						 | 
					fee372e673 | ||
| 
						 | 
					34ae47f8d3 | ||
| 
						 | 
					649f5a41e3 | ||
| 
						 | 
					be20696c67 | ||
| 
						 | 
					44da0a3949 | ||
| 
						 | 
					4d888fc3ca | ||
| 
						 | 
					f87cc7156a | ||
| 
						 | 
					7b08c794e5 | ||
| 
						 | 
					aa54e28857 | ||
| 
						 | 
					a8a5ffa1dc | ||
| 
						 | 
					eca2b31f62 | ||
| 
						 | 
					45a833b4cd | ||
| 
						 | 
					7c3524922b | ||
| 
						 | 
					735c714a74 | ||
| 
						 | 
					935eac12aa | ||
| 
						 | 
					5135fbefd0 | ||
| 
						 | 
					934355a268 | ||
| 
						 | 
					6bceb8a5b0 | ||
| 
						 | 
					4084e87af9 | ||
| 
						 | 
					6545f7c6b7 | ||
| 
						 | 
					deafac1ad0 | ||
| 
						 | 
					ddb93a6e5e | ||
| 
						 | 
					6d447a765e | ||
| 
						 | 
					1b69ddc65f | ||
| 
						 | 
					6e7e0fe514 | ||
| 
						 | 
					6cadfa7328 | ||
| 
						 | 
					6c21ff3d2e | ||
| 
						 | 
					d7ff30eb62 | ||
| 
						 | 
					c4cc3aab64 | ||
| 
						 | 
					b59f450314 | ||
| 
						 | 
					68399ea77e | ||
| 
						 | 
					eabdf75936 | ||
| 
						 | 
					d9e70a3e07 | ||
| 
						 | 
					fc6fc13826 | ||
| 
						 | 
					7c6405b5ce | ||
| 
						 | 
					efda919e2d | ||
| 
						 | 
					1bb18c8e61 | ||
| 
						 | 
					67e2f74d01 | ||
| 
						 | 
					ccdc490c33 | ||
| 
						 | 
					65e4168e8e | ||
| 
						 | 
					b63c4be047 | ||
| 
						 | 
					5fff97fa1c | ||
| 
						 | 
					e3e4f9c4b4 | ||
| 
						 | 
					d70ea4dd0a | ||
| 
						 | 
					4caacdf15f | ||
| 
						 | 
					b09d1a8fa6 | ||
| 
						 | 
					40f917f3a6 | ||
| 
						 | 
					5c5bfc4e3c | ||
| 
						 | 
					91914cbec2 | ||
| 
						 | 
					4af112527a | ||
| 
						 | 
					1b7e0c0385 | ||
| 
						 | 
					c61beefa50 | ||
| 
						 | 
					691a68926d | ||
| 
						 | 
					9748a9c1c0 | ||
| 
						 | 
					d13f32ccd8 | ||
| 
						 | 
					505ccabd64 | ||
| 
						 | 
					58534aa408 | ||
| 
						 | 
					73f3f2866f | ||
| 
						 | 
					d280f2dfbd | ||
| 
						 | 
					72feed7c70 | ||
| 
						 | 
					e7a75b6be2 | ||
| 
						 | 
					81671d54b0 | ||
| 
						 | 
					145c58df93 | ||
| 
						 | 
					f997e56945 | 
@@ -93,6 +93,10 @@ while (len(data)>0):
 | 
			
		||||
if options.monitor:
 | 
			
		||||
	while (True):
 | 
			
		||||
		data = sock.recv(1024)
 | 
			
		||||
		if len(data) == 0:
 | 
			
		||||
			print "Connection is gone."
 | 
			
		||||
			break
 | 
			
		||||
 | 
			
		||||
		while (len(data)>0):
 | 
			
		||||
			(answer, data) = remove_ipa_ctrl_header(data)
 | 
			
		||||
			print "Got message:", answer
 | 
			
		||||
 
 | 
			
		||||
@@ -23,6 +23,13 @@ struct bsc_api {
 | 
			
		||||
			 uint8_t cause, uint8_t *rr_cause);
 | 
			
		||||
	int (*clear_request)(struct gsm_subscriber_connection *conn,
 | 
			
		||||
			      uint32_t cause);
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Configure the multirate setting on this channel. If it is
 | 
			
		||||
	 * not implemented AMR5.9 will be used.
 | 
			
		||||
	 */
 | 
			
		||||
	void (*mr_config)(struct gsm_subscriber_connection *conn,
 | 
			
		||||
			  struct gsm48_multi_rate_conf *conf);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
int bsc_api_init(struct gsm_network *network, struct bsc_api *api);
 | 
			
		||||
 
 | 
			
		||||
@@ -310,6 +310,9 @@ struct bsc_nat {
 | 
			
		||||
 | 
			
		||||
	/* statistics */
 | 
			
		||||
	struct bsc_nat_statistics stats;
 | 
			
		||||
 | 
			
		||||
	/* control interface */
 | 
			
		||||
	struct ctrl_handle *ctrl;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct bsc_nat_ussd_con {
 | 
			
		||||
@@ -393,7 +396,6 @@ int bsc_write_msg(struct osmo_wqueue *queue, struct msgb *msg);
 | 
			
		||||
int bsc_write_cb(struct osmo_fd *bfd, struct msgb *msg);
 | 
			
		||||
 | 
			
		||||
/* IMSI allow/deny handling */
 | 
			
		||||
int bsc_parse_reg(void *ctx, regex_t *reg, char **imsi, int argc, const char **argv) __attribute__ ((warn_unused_result));
 | 
			
		||||
struct bsc_nat_acc_lst *bsc_nat_acc_lst_find(struct bsc_nat *nat, const char *name);
 | 
			
		||||
struct bsc_nat_acc_lst *bsc_nat_acc_lst_get(struct bsc_nat *nat, const char *name);
 | 
			
		||||
void bsc_nat_acc_lst_delete(struct bsc_nat_acc_lst *lst);
 | 
			
		||||
 
 | 
			
		||||
@@ -12,7 +12,6 @@
 | 
			
		||||
 | 
			
		||||
enum ctrl_node_type {
 | 
			
		||||
	CTRL_NODE_ROOT,	/* Root elements */
 | 
			
		||||
	CTRL_NODE_NET,	/* Network specific (net.) */
 | 
			
		||||
	CTRL_NODE_BTS,	/* BTS specific (net.btsN.) */
 | 
			
		||||
	CTRL_NODE_TRX,	/* TRX specific (net.btsN.trxM.) */
 | 
			
		||||
	CTRL_NODE_TS,	/* TS specific (net.btsN.trxM.tsI.) */
 | 
			
		||||
@@ -29,6 +28,14 @@ enum ctrl_type {
 | 
			
		||||
	CTRL_TYPE_ERROR
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct ctrl_handle {
 | 
			
		||||
	struct osmo_fd listen_fd;
 | 
			
		||||
	struct gsm_network *gsmnet;
 | 
			
		||||
 | 
			
		||||
	/* List of control connections */
 | 
			
		||||
	struct llist_head ccon_list;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct ctrl_connection {
 | 
			
		||||
	struct llist_head list_entry;
 | 
			
		||||
 | 
			
		||||
@@ -75,12 +82,15 @@ int ctrl_cmd_exec(vector vline, struct ctrl_cmd *command, vector node, void *dat
 | 
			
		||||
int ctrl_cmd_install(enum ctrl_node_type node, struct ctrl_cmd_element *cmd);
 | 
			
		||||
int ctrl_cmd_handle(struct ctrl_cmd *cmd, void *data);
 | 
			
		||||
int ctrl_cmd_send(struct osmo_wqueue *queue, struct ctrl_cmd *cmd);
 | 
			
		||||
int ctrl_cmd_send_to_all(struct ctrl_handle *ctrl, struct ctrl_cmd *cmd);
 | 
			
		||||
struct ctrl_cmd *ctrl_cmd_parse(void *ctx, struct msgb *msg);
 | 
			
		||||
struct msgb *ctrl_cmd_make(struct ctrl_cmd *cmd);
 | 
			
		||||
struct ctrl_cmd *ctrl_cmd_cpy(void *ctx, struct ctrl_cmd *cmd);
 | 
			
		||||
struct ctrl_cmd *ctrl_cmd_trap(struct ctrl_cmd *cmd);
 | 
			
		||||
struct ctrl_cmd *ctrl_cmd_create(void *ctx, enum ctrl_type);
 | 
			
		||||
 | 
			
		||||
#define CTRL_CMD_DEFINE_RANGE(cmdname, cmdstr, dtype, element, min, max) \
 | 
			
		||||
int get_##cmdname(struct ctrl_cmd *cmd, void *data) \
 | 
			
		||||
static int get_##cmdname(struct ctrl_cmd *cmd, void *data) \
 | 
			
		||||
{ \
 | 
			
		||||
	dtype *node = data; \
 | 
			
		||||
	cmd->reply = talloc_asprintf(cmd, "%i", node->element); \
 | 
			
		||||
@@ -90,14 +100,14 @@ int get_##cmdname(struct ctrl_cmd *cmd, void *data) \
 | 
			
		||||
	} \
 | 
			
		||||
	return CTRL_CMD_REPLY; \
 | 
			
		||||
} \
 | 
			
		||||
int set_##cmdname(struct ctrl_cmd *cmd, void *data) \
 | 
			
		||||
static int set_##cmdname(struct ctrl_cmd *cmd, void *data) \
 | 
			
		||||
{ \
 | 
			
		||||
	dtype *node = data; \
 | 
			
		||||
	int tmp = atoi(cmd->value); \
 | 
			
		||||
	node->element = tmp; \
 | 
			
		||||
	return get_##cmdname(cmd, data); \
 | 
			
		||||
} \
 | 
			
		||||
int verify_##cmdname(struct ctrl_cmd *cmd, const char *value, void *data) \
 | 
			
		||||
static int verify_##cmdname(struct ctrl_cmd *cmd, const char *value, void *data) \
 | 
			
		||||
{ \
 | 
			
		||||
	int tmp = atoi(value); \
 | 
			
		||||
	if ((tmp >= min)&&(tmp <= max)) { \
 | 
			
		||||
@@ -114,7 +124,7 @@ struct ctrl_cmd_element cmd_##cmdname = { \
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define CTRL_CMD_DEFINE_STRING(cmdname, cmdstr, dtype, element) \
 | 
			
		||||
int get_##cmdname(struct ctrl_cmd *cmd, dtype *data) \
 | 
			
		||||
static int get_##cmdname(struct ctrl_cmd *cmd, dtype *data) \
 | 
			
		||||
{ \
 | 
			
		||||
	cmd->reply = talloc_asprintf(cmd, "%s", data->element); \
 | 
			
		||||
	if (!cmd->reply) { \
 | 
			
		||||
@@ -123,7 +133,7 @@ int get_##cmdname(struct ctrl_cmd *cmd, dtype *data) \
 | 
			
		||||
	} \
 | 
			
		||||
	return CTRL_CMD_REPLY; \
 | 
			
		||||
} \
 | 
			
		||||
int set_##cmdname(struct ctrl_cmd *cmd, dtype *data) \
 | 
			
		||||
static int set_##cmdname(struct ctrl_cmd *cmd, dtype *data) \
 | 
			
		||||
{ \
 | 
			
		||||
	bsc_replace_string(cmd->node, &data->element, cmd->value); \
 | 
			
		||||
	return get_##cmdname(cmd, data); \
 | 
			
		||||
@@ -137,9 +147,9 @@ struct ctrl_cmd_element cmd_##cmdname = { \
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define CTRL_CMD_DEFINE(cmdname, cmdstr) \
 | 
			
		||||
int get_##cmdname(struct ctrl_cmd *cmd, void *data); \
 | 
			
		||||
int set_##cmdname(struct ctrl_cmd *cmd, void *data); \
 | 
			
		||||
int verify_##cmdname(struct ctrl_cmd *cmd, const char *value, void *data); \
 | 
			
		||||
static int get_##cmdname(struct ctrl_cmd *cmd, void *data); \
 | 
			
		||||
static int set_##cmdname(struct ctrl_cmd *cmd, void *data); \
 | 
			
		||||
static int verify_##cmdname(struct ctrl_cmd *cmd, const char *value, void *data); \
 | 
			
		||||
struct ctrl_cmd_element cmd_##cmdname = { \
 | 
			
		||||
	.name = cmdstr, \
 | 
			
		||||
	.param = NULL, \
 | 
			
		||||
@@ -149,6 +159,6 @@ struct ctrl_cmd_element cmd_##cmdname = { \
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct gsm_network;
 | 
			
		||||
int controlif_setup(struct gsm_network *gsmnet, uint16_t port);
 | 
			
		||||
struct ctrl_handle *controlif_setup(struct gsm_network *gsmnet, uint16_t port);
 | 
			
		||||
 | 
			
		||||
#endif /* _CONTROL_CMD_H */
 | 
			
		||||
 
 | 
			
		||||
@@ -282,12 +282,14 @@ struct gsm_network {
 | 
			
		||||
	int pag_any_tch;
 | 
			
		||||
 | 
			
		||||
	/* MSC data in case we are a true BSC */
 | 
			
		||||
	struct osmo_msc_data *msc_data;
 | 
			
		||||
	int hardcoded_rtp_payload;
 | 
			
		||||
	struct osmo_bsc_data *bsc_data;
 | 
			
		||||
 | 
			
		||||
	/* subscriber related features */
 | 
			
		||||
	int keep_subscr;
 | 
			
		||||
	struct gsm_sms_queue *sms_queue;
 | 
			
		||||
 | 
			
		||||
	/* control interface */
 | 
			
		||||
	struct ctrl_handle *ctrl;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define SMS_HDR_SIZE	128
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,7 @@
 | 
			
		||||
#ifndef _GSM_DATA_SHAREDH
 | 
			
		||||
#define _GSM_DATA_SHAREDH
 | 
			
		||||
 | 
			
		||||
#include <regex.h>
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
 | 
			
		||||
@@ -16,7 +17,7 @@
 | 
			
		||||
#include <osmocom/gsm/protocol/gsm_08_58.h>
 | 
			
		||||
#include <osmocom/gsm/protocol/gsm_12_21.h>
 | 
			
		||||
 | 
			
		||||
struct osmo_msc_data;
 | 
			
		||||
struct osmo_bsc_data;
 | 
			
		||||
struct osmo_bsc_sccp_con;
 | 
			
		||||
struct gsm_sms_queue;
 | 
			
		||||
 | 
			
		||||
@@ -400,11 +401,29 @@ enum neigh_list_manual_mode {
 | 
			
		||||
	NL_MODE_MANUAL_SI5SEP = 2, /* SI2 and SI5 have separate neighbor lists */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum bts_loc_fix {
 | 
			
		||||
	BTS_LOC_FIX_INVALID = 0,
 | 
			
		||||
	BTS_LOC_FIX_2D = 1,
 | 
			
		||||
	BTS_LOC_FIX_3D = 2,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct bts_location {
 | 
			
		||||
	struct llist_head list;
 | 
			
		||||
	time_t tstamp;
 | 
			
		||||
	enum bts_loc_fix valid;
 | 
			
		||||
	double lat;
 | 
			
		||||
	double lon;
 | 
			
		||||
	double height;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* One BTS */
 | 
			
		||||
struct gsm_bts {
 | 
			
		||||
	/* list header in net->bts_list */
 | 
			
		||||
	struct llist_head list;
 | 
			
		||||
 | 
			
		||||
	/* Geographical location of the BTS */
 | 
			
		||||
	struct llist_head loc_list;
 | 
			
		||||
 | 
			
		||||
	/* number of ths BTS in network */
 | 
			
		||||
	uint8_t nr;
 | 
			
		||||
	/* human readable name / description */
 | 
			
		||||
@@ -587,4 +606,12 @@ void gsm_bts_mo_reset(struct gsm_bts *bts);
 | 
			
		||||
uint8_t gsm_ts2chan_nr(const struct gsm_bts_trx_ts *ts, uint8_t lchan_nr);
 | 
			
		||||
uint8_t gsm_lchan2chan_nr(const struct gsm_lchan *lchan);
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * help with parsing regexps
 | 
			
		||||
 */
 | 
			
		||||
int gsm_parse_reg(void *ctx, regex_t *reg, char **str,
 | 
			
		||||
		int argc, const char **argv) __attribute__ ((warn_unused_result));
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
@@ -6,6 +6,8 @@
 | 
			
		||||
#include "bsc_api.h"
 | 
			
		||||
 | 
			
		||||
struct sccp_connection;
 | 
			
		||||
struct osmo_msc_data;
 | 
			
		||||
struct bsc_msc_connection;
 | 
			
		||||
 | 
			
		||||
struct osmo_bsc_sccp_con {
 | 
			
		||||
	struct llist_head entry;
 | 
			
		||||
@@ -15,7 +17,7 @@ struct osmo_bsc_sccp_con {
 | 
			
		||||
 | 
			
		||||
	/* SCCP connection realted */
 | 
			
		||||
	struct sccp_connection *sccp;
 | 
			
		||||
	struct bsc_msc_connection *msc_con;
 | 
			
		||||
	struct osmo_msc_data *msc;
 | 
			
		||||
	struct osmo_timer_list sccp_it_timeout;
 | 
			
		||||
	struct osmo_timer_list sccp_cc_timeout;
 | 
			
		||||
 | 
			
		||||
@@ -30,14 +32,17 @@ struct bsc_api *osmo_bsc_api();
 | 
			
		||||
 | 
			
		||||
int bsc_queue_for_msc(struct osmo_bsc_sccp_con *conn, struct msgb *msg);
 | 
			
		||||
int bsc_open_connection(struct osmo_bsc_sccp_con *sccp, struct msgb *msg);
 | 
			
		||||
int bsc_create_new_connection(struct gsm_subscriber_connection *conn);
 | 
			
		||||
int bsc_create_new_connection(struct gsm_subscriber_connection *conn,
 | 
			
		||||
			      struct osmo_msc_data *msc);
 | 
			
		||||
int bsc_delete_connection(struct osmo_bsc_sccp_con *sccp);
 | 
			
		||||
 | 
			
		||||
struct osmo_msc_data *bsc_find_msc(struct gsm_subscriber_connection *conn, struct msgb *);
 | 
			
		||||
int bsc_scan_bts_msg(struct gsm_subscriber_connection *conn, struct msgb *msg);
 | 
			
		||||
int bsc_scan_msc_msg(struct gsm_subscriber_connection *conn, struct msgb *msg);
 | 
			
		||||
 | 
			
		||||
int bsc_handle_udt(struct gsm_network *net, struct bsc_msc_connection *conn, struct msgb *msg, unsigned int length);
 | 
			
		||||
int bsc_handle_udt(struct osmo_msc_data *msc, struct msgb *msg, unsigned int length);
 | 
			
		||||
int bsc_handle_dt1(struct osmo_bsc_sccp_con *conn, struct msgb *msg, unsigned int len);
 | 
			
		||||
 | 
			
		||||
int bsc_ctrl_cmds_install();
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
@@ -1,9 +1,28 @@
 | 
			
		||||
#ifndef OSMO_BSC_RF
 | 
			
		||||
#define OSMO_BSC_RF
 | 
			
		||||
 | 
			
		||||
#include <openbsc/gsm_data.h>
 | 
			
		||||
#include <osmocom/core/write_queue.h>
 | 
			
		||||
#include <osmocom/core/timer.h>
 | 
			
		||||
 | 
			
		||||
enum osmo_bsc_rf_opstate {
 | 
			
		||||
	OSMO_BSC_RF_OPSTATE_INOPERATIONAL,
 | 
			
		||||
	OSMO_BSC_RF_OPSTATE_OPERATIONAL,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum osmo_bsc_rf_adminstate {
 | 
			
		||||
	OSMO_BSC_RF_ADMINSTATE_UNLOCKED,
 | 
			
		||||
	OSMO_BSC_RF_ADMINSTATE_LOCKED,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum osmo_bsc_rf_policy {
 | 
			
		||||
	OSMO_BSC_RF_POLICY_OFF,
 | 
			
		||||
	OSMO_BSC_RF_POLICY_ON,
 | 
			
		||||
	OSMO_BSC_RF_POLICY_GRACE,
 | 
			
		||||
	OSMO_BSC_RF_POLICY_UNKNOWN,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
struct gsm_network;
 | 
			
		||||
 | 
			
		||||
struct osmo_bsc_rf {
 | 
			
		||||
@@ -23,6 +42,9 @@ struct osmo_bsc_rf {
 | 
			
		||||
 | 
			
		||||
	/* some handling for the automatic grace switch */
 | 
			
		||||
	struct osmo_timer_list grace_timeout;
 | 
			
		||||
 | 
			
		||||
	/* auto RF switch-off due lack of MSC connection */
 | 
			
		||||
	struct osmo_timer_list auto_off_timer;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct osmo_bsc_rf_conn {
 | 
			
		||||
@@ -30,6 +52,10 @@ struct osmo_bsc_rf_conn {
 | 
			
		||||
	struct osmo_bsc_rf *rf;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum osmo_bsc_rf_opstate osmo_bsc_rf_get_opstate_by_bts(struct gsm_bts *bts);
 | 
			
		||||
enum osmo_bsc_rf_adminstate osmo_bsc_rf_get_adminstate_by_bts(struct gsm_bts *bts);
 | 
			
		||||
enum osmo_bsc_rf_policy osmo_bsc_rf_get_policy_by_bts(struct gsm_bts *bts);
 | 
			
		||||
struct osmo_bsc_rf *osmo_bsc_rf_create(const char *path, struct gsm_network *net);
 | 
			
		||||
void osmo_bsc_rf_schedule_lock(struct osmo_bsc_rf *rf, char cmd);
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,8 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Data for the true BSC
 | 
			
		||||
 *
 | 
			
		||||
 * (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
 | 
			
		||||
 * (C) 2010 by On-Waves
 | 
			
		||||
 * (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
 | 
			
		||||
@@ -26,6 +26,9 @@
 | 
			
		||||
#include "bsc_msc.h"
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/timer.h>
 | 
			
		||||
#include <osmocom/gsm/protocol/gsm_04_08.h>
 | 
			
		||||
 | 
			
		||||
#include <regex.h>
 | 
			
		||||
 | 
			
		||||
struct osmo_bsc_rf;
 | 
			
		||||
struct gsm_network;
 | 
			
		||||
@@ -35,10 +38,25 @@ struct gsm_audio_support {
 | 
			
		||||
                ver : 7;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum {
 | 
			
		||||
	MSC_CON_TYPE_NORMAL,
 | 
			
		||||
	MSC_CON_TYPE_LOCAL,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct osmo_msc_data {
 | 
			
		||||
	struct llist_head entry;
 | 
			
		||||
 | 
			
		||||
	/* Back pointer */
 | 
			
		||||
	struct gsm_network *network;
 | 
			
		||||
 | 
			
		||||
	int allow_emerg;
 | 
			
		||||
	int type;
 | 
			
		||||
 | 
			
		||||
	/* local call routing */
 | 
			
		||||
	char *local_pref;
 | 
			
		||||
	regex_t local_pref_reg;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	/* Connection data */
 | 
			
		||||
	char *bsc_token;
 | 
			
		||||
	int ping_timeout;
 | 
			
		||||
@@ -51,30 +69,48 @@ struct osmo_msc_data {
 | 
			
		||||
	int rtp_base;
 | 
			
		||||
 | 
			
		||||
	/* audio codecs */
 | 
			
		||||
	struct gsm48_multi_rate_conf amr_conf;
 | 
			
		||||
	struct gsm_audio_support **audio_support;
 | 
			
		||||
	int audio_length;
 | 
			
		||||
 | 
			
		||||
	/* destinations */
 | 
			
		||||
	struct llist_head dests;
 | 
			
		||||
 | 
			
		||||
	/* ussd welcome text */
 | 
			
		||||
	char *ussd_welcome_txt;
 | 
			
		||||
 | 
			
		||||
	/* mgcp agent */
 | 
			
		||||
	struct osmo_wqueue mgcp_agent;
 | 
			
		||||
 | 
			
		||||
	int nr;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Per BSC data.
 | 
			
		||||
 */
 | 
			
		||||
struct osmo_bsc_data {
 | 
			
		||||
	struct gsm_network *network;
 | 
			
		||||
 | 
			
		||||
	/* msc configuration */
 | 
			
		||||
	struct llist_head mscs;
 | 
			
		||||
 | 
			
		||||
	/* rf ctl related bits */
 | 
			
		||||
	char *mid_call_txt;
 | 
			
		||||
	int mid_call_timeout;
 | 
			
		||||
	char *rf_ctrl_name;
 | 
			
		||||
	struct osmo_bsc_rf *rf_ctrl;
 | 
			
		||||
 | 
			
		||||
	/* ussd welcome text */
 | 
			
		||||
	char *ussd_welcome_txt;
 | 
			
		||||
	int auto_off_timeout;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
int osmo_bsc_msc_init(struct gsm_network *network);
 | 
			
		||||
 | 
			
		||||
int osmo_bsc_msc_init(struct osmo_msc_data *msc);
 | 
			
		||||
int osmo_bsc_sccp_init(struct gsm_network *gsmnet);
 | 
			
		||||
int msc_queue_write(struct bsc_msc_connection *conn, struct msgb *msg, int proto);
 | 
			
		||||
 | 
			
		||||
int osmo_bsc_audio_init(struct gsm_network *network);
 | 
			
		||||
 | 
			
		||||
struct osmo_msc_data *osmo_msc_data_find(struct gsm_network *, int);
 | 
			
		||||
struct osmo_msc_data *osmo_msc_data_alloc(struct gsm_network *, int);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
@@ -32,8 +32,7 @@
 | 
			
		||||
#define RTP_PT_GSM_FULL 3
 | 
			
		||||
#define RTP_PT_GSM_HALF 96
 | 
			
		||||
#define RTP_PT_GSM_EFR 97
 | 
			
		||||
#define RTP_PT_AMR_FULL 98
 | 
			
		||||
#define RTP_PT_AMR_HALF 99
 | 
			
		||||
#define RTP_PT_AMR 98
 | 
			
		||||
 | 
			
		||||
enum rtp_rx_action {
 | 
			
		||||
	RTP_NONE,
 | 
			
		||||
 
 | 
			
		||||
@@ -243,6 +243,7 @@ struct ns_signal_data {
 | 
			
		||||
enum signal_msc {
 | 
			
		||||
	S_MSC_LOST,
 | 
			
		||||
	S_MSC_CONNECTED,
 | 
			
		||||
	S_MSC_AUTHENTICATED,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct osmo_msc_data;
 | 
			
		||||
 
 | 
			
		||||
@@ -36,6 +36,7 @@ enum bsc_vty_node {
 | 
			
		||||
	OM2K_NODE,
 | 
			
		||||
	TRUNK_NODE,
 | 
			
		||||
	PGROUP_NODE,
 | 
			
		||||
	BSC_NODE,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
extern int bsc_vty_is_config_node(struct vty *vty, int node);
 | 
			
		||||
 
 | 
			
		||||
@@ -1522,10 +1522,6 @@ static uint8_t ipa_rtp_pt_for_lchan(struct gsm_lchan *lchan)
 | 
			
		||||
{
 | 
			
		||||
	struct gsm_network *net = lchan->ts->trx->bts->network;
 | 
			
		||||
 | 
			
		||||
	/* allow to hardcode the rtp payload */
 | 
			
		||||
	if (net->hardcoded_rtp_payload != 0)
 | 
			
		||||
		return net->hardcoded_rtp_payload;
 | 
			
		||||
 | 
			
		||||
	switch (lchan->tch_mode) {
 | 
			
		||||
	case GSM48_CMODE_SPEECH_V1:
 | 
			
		||||
		switch (lchan->type) {
 | 
			
		||||
@@ -1547,9 +1543,8 @@ static uint8_t ipa_rtp_pt_for_lchan(struct gsm_lchan *lchan)
 | 
			
		||||
	case GSM48_CMODE_SPEECH_AMR:
 | 
			
		||||
		switch (lchan->type) {
 | 
			
		||||
		case GSM_LCHAN_TCH_F:
 | 
			
		||||
			return RTP_PT_AMR_FULL;
 | 
			
		||||
		case GSM_LCHAN_TCH_H:
 | 
			
		||||
			return RTP_PT_AMR_HALF;
 | 
			
		||||
			return RTP_PT_AMR;
 | 
			
		||||
		default:
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
/* GSM 08.08 like API for OpenBSC. The bridge from MSC to BSC */
 | 
			
		||||
 | 
			
		||||
/* (C) 2010 by Holger Hans Peter Freyther
 | 
			
		||||
 * (C) 2010 by On-Waves
 | 
			
		||||
/* (C) 2010-2011 by Holger Hans Peter Freyther
 | 
			
		||||
 * (C) 2010-2011 by On-Waves
 | 
			
		||||
 * (C) 2009 by Harald Welte <laforge@gnumonks.org>
 | 
			
		||||
 *
 | 
			
		||||
 * All Rights Reserved
 | 
			
		||||
@@ -145,6 +145,23 @@ static void assignment_t10_timeout(void *_conn)
 | 
			
		||||
	api->assign_fail(conn, GSM0808_CAUSE_NO_RADIO_RESOURCE_AVAILABLE, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Handle the multirate config
 | 
			
		||||
 */
 | 
			
		||||
static void handle_mr_config(struct gsm_subscriber_connection *conn,
 | 
			
		||||
			     struct gsm_lchan *lchan)
 | 
			
		||||
{
 | 
			
		||||
	struct bsc_api *api;
 | 
			
		||||
	api = conn->bts->network->bsc_api;
 | 
			
		||||
 | 
			
		||||
	if (api->mr_config)
 | 
			
		||||
		return api->mr_config(conn, &lchan->mr_conf);
 | 
			
		||||
 | 
			
		||||
	lchan->mr_conf.ver = 1;
 | 
			
		||||
	lchan->mr_conf.icmi = 1;
 | 
			
		||||
	lchan->mr_conf.m5_90 = 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Start a new assignment and make sure that it is completed within T10 either
 | 
			
		||||
 * positively, negatively or by the timeout.
 | 
			
		||||
@@ -184,11 +201,8 @@ static int handle_new_assignment(struct gsm_subscriber_connection *conn, int cha
 | 
			
		||||
	new_lchan->rsl_cmode = RSL_CMOD_SPD_SPEECH;
 | 
			
		||||
 | 
			
		||||
	/* handle AMR correctly */
 | 
			
		||||
	if (chan_mode == GSM48_CMODE_SPEECH_AMR) {
 | 
			
		||||
		new_lchan->mr_conf.ver = 1;
 | 
			
		||||
		new_lchan->mr_conf.icmi = 1;
 | 
			
		||||
		new_lchan->mr_conf.m5_90 = 1;
 | 
			
		||||
	}
 | 
			
		||||
	if (chan_mode == GSM48_CMODE_SPEECH_AMR)
 | 
			
		||||
		handle_mr_config(conn, new_lchan);
 | 
			
		||||
 | 
			
		||||
	if (rsl_chan_activate_lchan(new_lchan, 0x1, 0, 0) < 0) {
 | 
			
		||||
		LOGP(DHO, LOGL_ERROR, "could not activate channel\n");
 | 
			
		||||
@@ -299,8 +313,8 @@ int gsm0808_submit_dtap(struct gsm_subscriber_connection *conn,
 | 
			
		||||
 * Send a GSM08.08 Assignment Request. Right now this does not contain the
 | 
			
		||||
 * audio codec type or the allowed rates for the config. It is assumed that
 | 
			
		||||
 * this is for audio handling and that when we have a TCH it is capable of
 | 
			
		||||
 * handling the audio codec. On top of that it is assumed that we are using
 | 
			
		||||
 * AMR 5.9 when assigning a TCH/H.
 | 
			
		||||
 * handling the audio codec. In case AMR is used we will leave the multi
 | 
			
		||||
 * rate configuration to someone else.
 | 
			
		||||
 */
 | 
			
		||||
int gsm0808_assign_req(struct gsm_subscriber_connection *conn, int chan_mode, int full_rate)
 | 
			
		||||
{
 | 
			
		||||
@@ -313,11 +327,8 @@ int gsm0808_assign_req(struct gsm_subscriber_connection *conn, int chan_mode, in
 | 
			
		||||
	} else {
 | 
			
		||||
		LOGP(DMSC, LOGL_NOTICE,
 | 
			
		||||
			"Sending ChanModify for speech %d %d\n", chan_mode, full_rate);
 | 
			
		||||
		if (chan_mode == GSM48_CMODE_SPEECH_AMR) {
 | 
			
		||||
			conn->lchan->mr_conf.ver = 1;
 | 
			
		||||
			conn->lchan->mr_conf.icmi = 1;
 | 
			
		||||
			conn->lchan->mr_conf.m5_90 = 1;
 | 
			
		||||
		}
 | 
			
		||||
		if (chan_mode == GSM48_CMODE_SPEECH_AMR)
 | 
			
		||||
			handle_mr_config(conn, conn->lchan);
 | 
			
		||||
 | 
			
		||||
		gsm48_lchan_modify(conn->lchan, chan_mode);
 | 
			
		||||
	}
 | 
			
		||||
@@ -367,7 +378,8 @@ static void handle_ass_compl(struct gsm_subscriber_connection *conn,
 | 
			
		||||
	if (is_ipaccess_bts(conn->bts) && conn->lchan->tch_mode != GSM48_CMODE_SIGN)
 | 
			
		||||
		rsl_ipacc_crcx(conn->lchan);
 | 
			
		||||
 | 
			
		||||
	api->assign_compl(conn, gh->data[0],
 | 
			
		||||
	if (api->assign_compl)
 | 
			
		||||
		api->assign_compl(conn, gh->data[0],
 | 
			
		||||
			  lchan_to_chosen_channel(conn->lchan),
 | 
			
		||||
			  conn->lchan->encr.alg_id,
 | 
			
		||||
			  chan_mode_to_speech(conn->lchan));
 | 
			
		||||
 
 | 
			
		||||
@@ -189,9 +189,9 @@ static void net_dump_vty(struct vty *vty, struct gsm_network *net)
 | 
			
		||||
	dump_pchan_load_vty(vty, "    ", &pl);
 | 
			
		||||
 | 
			
		||||
	/* show rf */
 | 
			
		||||
	if (net->msc_data && net->msc_data->rf_ctrl)
 | 
			
		||||
	if (net->bsc_data && net->bsc_data->rf_ctrl)
 | 
			
		||||
		vty_out(vty, "  Last RF Command: %s%s",
 | 
			
		||||
			net->msc_data->rf_ctrl->last_state_command,
 | 
			
		||||
			net->bsc_data->rf_ctrl->last_state_command,
 | 
			
		||||
			VTY_NEWLINE);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -151,6 +151,7 @@ gDEFUN(ournode_exit,
 | 
			
		||||
	case NS_NODE:
 | 
			
		||||
	case BSSGP_NODE:
 | 
			
		||||
	case NAT_NODE:
 | 
			
		||||
	case BSC_NODE:
 | 
			
		||||
		vty->node = CONFIG_NODE;
 | 
			
		||||
		vty->index = NULL;
 | 
			
		||||
		break;
 | 
			
		||||
@@ -197,6 +198,7 @@ gDEFUN(ournode_end,
 | 
			
		||||
	case NAT_BSC_NODE:
 | 
			
		||||
	case PGROUP_NODE:
 | 
			
		||||
	case MSC_NODE:
 | 
			
		||||
	case BSC_NODE:
 | 
			
		||||
		vty_config_unlock(vty);
 | 
			
		||||
		vty->node = ENABLE_NODE;
 | 
			
		||||
		vty->index = NULL;
 | 
			
		||||
 
 | 
			
		||||
@@ -79,14 +79,16 @@ struct gsm_network *gsm_network_init(uint16_t country_code, uint16_t network_cod
 | 
			
		||||
	if (!net)
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	net->msc_data = talloc_zero(net, struct osmo_msc_data);
 | 
			
		||||
	if (!net->msc_data) {
 | 
			
		||||
	net->bsc_data = talloc_zero(net, struct osmo_bsc_data);
 | 
			
		||||
	if (!net->bsc_data) {
 | 
			
		||||
		talloc_free(net);
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Init back pointer */
 | 
			
		||||
	net->msc_data->network = net;
 | 
			
		||||
	net->bsc_data->auto_off_timeout = -1;
 | 
			
		||||
	net->bsc_data->network = net;
 | 
			
		||||
	INIT_LLIST_HEAD(&net->bsc_data->mscs);
 | 
			
		||||
 | 
			
		||||
	net->country_code = country_code;
 | 
			
		||||
	net->network_code = network_code;
 | 
			
		||||
@@ -141,13 +143,6 @@ struct gsm_network *gsm_network_init(uint16_t country_code, uint16_t network_cod
 | 
			
		||||
 | 
			
		||||
	net->mncc_recv = mncc_recv;
 | 
			
		||||
 | 
			
		||||
	INIT_LLIST_HEAD(&net->msc_data->dests);
 | 
			
		||||
	net->msc_data->ping_timeout = 20;
 | 
			
		||||
	net->msc_data->pong_timeout = 5;
 | 
			
		||||
	net->msc_data->core_ncc = -1;
 | 
			
		||||
	net->msc_data->core_mcc = -1;
 | 
			
		||||
	net->msc_data->rtp_base = 4000;
 | 
			
		||||
 | 
			
		||||
	gsm_net_update_ctype(net);
 | 
			
		||||
 | 
			
		||||
	return net;
 | 
			
		||||
@@ -399,6 +394,8 @@ struct gsm_bts *gsm_bts_alloc_register(struct gsm_network *net, enum gsm_bts_typ
 | 
			
		||||
 | 
			
		||||
	INIT_LLIST_HEAD(&bts->abis_queue);
 | 
			
		||||
 | 
			
		||||
	INIT_LLIST_HEAD(&bts->loc_list);
 | 
			
		||||
 | 
			
		||||
	return bts;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -418,3 +415,29 @@ int gsm48_ra_id_by_bts(uint8_t *buf, struct gsm_bts *bts)
 | 
			
		||||
 | 
			
		||||
	return gsm48_construct_ra(buf, &raid);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int gsm_parse_reg(void *ctx, regex_t *reg, char **str, int argc, const char **argv)
 | 
			
		||||
{
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	ret = 0;
 | 
			
		||||
	if (*str) {
 | 
			
		||||
		talloc_free(*str);
 | 
			
		||||
		*str = NULL;
 | 
			
		||||
	}
 | 
			
		||||
	regfree(reg);
 | 
			
		||||
 | 
			
		||||
	if (argc > 0) {
 | 
			
		||||
		*str = talloc_strdup(ctx, argv[0]);
 | 
			
		||||
		ret = regcomp(reg, argv[0], 0);
 | 
			
		||||
 | 
			
		||||
		/* handle compilation failures */
 | 
			
		||||
		if (ret != 0) {
 | 
			
		||||
			talloc_free(*str);
 | 
			
		||||
			*str = NULL;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -137,11 +137,13 @@ int ctrl_cmd_exec(vector vline, struct ctrl_cmd *command, vector node, void *dat
 | 
			
		||||
		if (cmd_el->verify) {
 | 
			
		||||
			if ((ret = cmd_el->verify(command, command->value, data))) {
 | 
			
		||||
				ret = CTRL_CMD_ERROR;
 | 
			
		||||
				command->reply = "Value failed verification.";
 | 
			
		||||
				/* If verify() set an appropriate error message, don't change it. */
 | 
			
		||||
				if (!command->reply)
 | 
			
		||||
					command->reply = "Value failed verification.";
 | 
			
		||||
				goto out;
 | 
			
		||||
			}
 | 
			
		||||
		} else if (cmd_el->param) {
 | 
			
		||||
			LOGP(DINP, LOGL_NOTICE, "Parameter verification unimplemented, continuing without\n");
 | 
			
		||||
			LOGP(DCTRL, LOGL_NOTICE, "Parameter verification unimplemented, continuing without\n");
 | 
			
		||||
		}
 | 
			
		||||
		ret =  cmd_el->set(command, data);
 | 
			
		||||
		goto out;
 | 
			
		||||
@@ -190,7 +192,7 @@ static void create_cmd_struct(struct ctrl_cmd_struct *cmd, const char *name)
 | 
			
		||||
	for (cur = name, word = NULL; cur[0] != '\0'; ++cur) {
 | 
			
		||||
		/* warn about optionals */
 | 
			
		||||
		if (cur[0] == '(' || cur[0] == ')' || cur[0] == '|') {
 | 
			
		||||
			LOGP(DINP, LOGL_ERROR,
 | 
			
		||||
			LOGP(DCTRL, LOGL_ERROR,
 | 
			
		||||
			     "Optionals are not supported in '%s'\n", name);
 | 
			
		||||
			goto failure;
 | 
			
		||||
		}
 | 
			
		||||
@@ -225,7 +227,7 @@ int ctrl_cmd_install(enum ctrl_node_type node, struct ctrl_cmd_element *cmd)
 | 
			
		||||
	if (!cmds_vec) {
 | 
			
		||||
		cmds_vec = vector_init(5);
 | 
			
		||||
		if (!cmds_vec) {
 | 
			
		||||
			LOGP(DINP, LOGL_ERROR, "vector_init failed.\n");
 | 
			
		||||
			LOGP(DCTRL, LOGL_ERROR, "vector_init failed.\n");
 | 
			
		||||
			return -ENOMEM;
 | 
			
		||||
		}
 | 
			
		||||
		vector_set_index(ctrl_node_vec, node, cmds_vec);
 | 
			
		||||
@@ -237,6 +239,18 @@ int ctrl_cmd_install(enum ctrl_node_type node, struct ctrl_cmd_element *cmd)
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct ctrl_cmd *ctrl_cmd_create(void *ctx, enum ctrl_type type)
 | 
			
		||||
{
 | 
			
		||||
	struct ctrl_cmd *cmd;
 | 
			
		||||
 | 
			
		||||
	cmd = talloc_zero(ctx, struct ctrl_cmd);
 | 
			
		||||
	if (!cmd)
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	cmd->type = type;
 | 
			
		||||
	return cmd;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct ctrl_cmd *ctrl_cmd_cpy(void *ctx, struct ctrl_cmd *cmd)
 | 
			
		||||
{
 | 
			
		||||
	struct ctrl_cmd *cmd2;
 | 
			
		||||
@@ -281,7 +295,7 @@ struct ctrl_cmd *ctrl_cmd_parse(void *ctx, struct msgb *msg)
 | 
			
		||||
 | 
			
		||||
	cmd = talloc_zero(ctx, struct ctrl_cmd);
 | 
			
		||||
	if (!cmd) {
 | 
			
		||||
		LOGP(DINP, LOGL_ERROR, "Failed to allocate.\n");
 | 
			
		||||
		LOGP(DCTRL, LOGL_ERROR, "Failed to allocate.\n");
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -323,11 +337,11 @@ struct ctrl_cmd *ctrl_cmd_parse(void *ctx, struct msgb *msg)
 | 
			
		||||
			if (!var) {
 | 
			
		||||
				cmd->type = CTRL_TYPE_ERROR;
 | 
			
		||||
				cmd->reply = "GET incomplete";
 | 
			
		||||
				LOGP(DINP, LOGL_NOTICE, "GET Command incomplete\n");
 | 
			
		||||
				LOGP(DCTRL, LOGL_NOTICE, "GET Command incomplete\n");
 | 
			
		||||
				goto err;
 | 
			
		||||
			}
 | 
			
		||||
			cmd->variable = talloc_strdup(cmd, var);
 | 
			
		||||
			LOGP(DINP, LOGL_DEBUG, "Command: GET %s\n", cmd->variable);
 | 
			
		||||
			LOGP(DCTRL, LOGL_DEBUG, "Command: GET %s\n", cmd->variable);
 | 
			
		||||
			break;
 | 
			
		||||
		case CTRL_TYPE_SET:
 | 
			
		||||
			var = strtok_r(NULL, " ", &saveptr);
 | 
			
		||||
@@ -335,14 +349,14 @@ struct ctrl_cmd *ctrl_cmd_parse(void *ctx, struct msgb *msg)
 | 
			
		||||
			if (!var || !val) {
 | 
			
		||||
				cmd->type = CTRL_TYPE_ERROR;
 | 
			
		||||
				cmd->reply = "SET incomplete";
 | 
			
		||||
				LOGP(DINP, LOGL_NOTICE, "SET Command incomplete\n");
 | 
			
		||||
				LOGP(DCTRL, LOGL_NOTICE, "SET Command incomplete\n");
 | 
			
		||||
				goto err;
 | 
			
		||||
			}
 | 
			
		||||
			cmd->variable = talloc_strdup(cmd, var);
 | 
			
		||||
			cmd->value = talloc_strdup(cmd, val);
 | 
			
		||||
			if (!cmd->variable || !cmd->value)
 | 
			
		||||
				goto oom;
 | 
			
		||||
			LOGP(DINP, LOGL_DEBUG, "Command: SET %s = %s\n", cmd->variable, cmd->value);
 | 
			
		||||
			LOGP(DCTRL, LOGL_DEBUG, "Command: SET %s = %s\n", cmd->variable, cmd->value);
 | 
			
		||||
			break;
 | 
			
		||||
		case CTRL_TYPE_GET_REPLY:
 | 
			
		||||
		case CTRL_TYPE_SET_REPLY:
 | 
			
		||||
@@ -352,14 +366,14 @@ struct ctrl_cmd *ctrl_cmd_parse(void *ctx, struct msgb *msg)
 | 
			
		||||
			if (!var || !val) {
 | 
			
		||||
				cmd->type = CTRL_TYPE_ERROR;
 | 
			
		||||
				cmd->reply = "Trap/Reply incomplete";
 | 
			
		||||
				LOGP(DINP, LOGL_NOTICE, "Trap/Reply incomplete\n");
 | 
			
		||||
				LOGP(DCTRL, LOGL_NOTICE, "Trap/Reply incomplete\n");
 | 
			
		||||
				goto err;
 | 
			
		||||
			}
 | 
			
		||||
			cmd->variable = talloc_strdup(cmd, var);
 | 
			
		||||
			cmd->reply = talloc_strdup(cmd, val);
 | 
			
		||||
			if (!cmd->variable || !cmd->reply)
 | 
			
		||||
				goto oom;
 | 
			
		||||
			LOGP(DINP, LOGL_DEBUG, "Command: TRAP/REPLY %s: %s\n", cmd->variable, cmd->reply);
 | 
			
		||||
			LOGP(DCTRL, LOGL_DEBUG, "Command: TRAP/REPLY %s: %s\n", cmd->variable, cmd->reply);
 | 
			
		||||
			break;
 | 
			
		||||
		case CTRL_TYPE_ERROR:
 | 
			
		||||
			var = strtok_r(NULL, "\0", &saveptr);
 | 
			
		||||
@@ -370,7 +384,7 @@ struct ctrl_cmd *ctrl_cmd_parse(void *ctx, struct msgb *msg)
 | 
			
		||||
			cmd->reply = talloc_strdup(cmd, var);
 | 
			
		||||
			if (!cmd->reply)
 | 
			
		||||
				goto oom;
 | 
			
		||||
			LOGP(DINP, LOGL_DEBUG, "Command: ERROR %s\n", cmd->reply);
 | 
			
		||||
			LOGP(DCTRL, LOGL_DEBUG, "Command: ERROR %s\n", cmd->reply);
 | 
			
		||||
			break;
 | 
			
		||||
		case CTRL_TYPE_UNKNOWN:
 | 
			
		||||
		default:
 | 
			
		||||
@@ -410,7 +424,7 @@ struct msgb *ctrl_cmd_make(struct ctrl_cmd *cmd)
 | 
			
		||||
 | 
			
		||||
		tmp = talloc_asprintf(cmd, "%s %s %s", type, cmd->id, cmd->variable);
 | 
			
		||||
		if (!tmp) {
 | 
			
		||||
			LOGP(DINP, LOGL_ERROR, "Failed to allocate cmd.\n");
 | 
			
		||||
			LOGP(DCTRL, LOGL_ERROR, "Failed to allocate cmd.\n");
 | 
			
		||||
			goto err;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@@ -425,7 +439,7 @@ struct msgb *ctrl_cmd_make(struct ctrl_cmd *cmd)
 | 
			
		||||
		tmp = talloc_asprintf(cmd, "%s %s %s %s", type, cmd->id, cmd->variable,
 | 
			
		||||
				cmd->value);
 | 
			
		||||
		if (!tmp) {
 | 
			
		||||
			LOGP(DINP, LOGL_ERROR, "Failed to allocate cmd.\n");
 | 
			
		||||
			LOGP(DCTRL, LOGL_ERROR, "Failed to allocate cmd.\n");
 | 
			
		||||
			goto err;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@@ -442,7 +456,7 @@ struct msgb *ctrl_cmd_make(struct ctrl_cmd *cmd)
 | 
			
		||||
		tmp = talloc_asprintf(cmd, "%s %s %s %s", type, cmd->id, cmd->variable,
 | 
			
		||||
				cmd->reply);
 | 
			
		||||
		if (!tmp) {
 | 
			
		||||
			LOGP(DINP, LOGL_ERROR, "Failed to allocate cmd.\n");
 | 
			
		||||
			LOGP(DCTRL, LOGL_ERROR, "Failed to allocate cmd.\n");
 | 
			
		||||
			goto err;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@@ -457,7 +471,7 @@ struct msgb *ctrl_cmd_make(struct ctrl_cmd *cmd)
 | 
			
		||||
		tmp = talloc_asprintf(cmd, "%s %s %s", type, cmd->id,
 | 
			
		||||
				cmd->reply);
 | 
			
		||||
		if (!tmp) {
 | 
			
		||||
			LOGP(DINP, LOGL_ERROR, "Failed to allocate cmd.\n");
 | 
			
		||||
			LOGP(DCTRL, LOGL_ERROR, "Failed to allocate cmd.\n");
 | 
			
		||||
			goto err;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@@ -466,7 +480,7 @@ struct msgb *ctrl_cmd_make(struct ctrl_cmd *cmd)
 | 
			
		||||
		talloc_free(tmp);
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		LOGP(DINP, LOGL_NOTICE, "Unknown command type %i\n", cmd->type);
 | 
			
		||||
		LOGP(DCTRL, LOGL_NOTICE, "Unknown command type %i\n", cmd->type);
 | 
			
		||||
		goto err;
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -60,13 +60,23 @@
 | 
			
		||||
#include <osmocom/vty/command.h>
 | 
			
		||||
#include <osmocom/vty/vector.h>
 | 
			
		||||
 | 
			
		||||
struct ctrl_handle {
 | 
			
		||||
	struct osmo_fd listen_fd;
 | 
			
		||||
	struct gsm_network *gsmnet;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
vector ctrl_node_vec;
 | 
			
		||||
 | 
			
		||||
/* Send command to all  */
 | 
			
		||||
int ctrl_cmd_send_to_all(struct ctrl_handle *ctrl, struct ctrl_cmd *cmd)
 | 
			
		||||
{
 | 
			
		||||
	struct ctrl_connection *ccon;
 | 
			
		||||
	int ret = 0;
 | 
			
		||||
 | 
			
		||||
	llist_for_each_entry(ccon, &ctrl->ccon_list, list_entry) {
 | 
			
		||||
		if (ccon == cmd->ccon)
 | 
			
		||||
			continue;
 | 
			
		||||
		if (ctrl_cmd_send(&ccon->write_queue, cmd))
 | 
			
		||||
			ret++;
 | 
			
		||||
	}
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int ctrl_cmd_send(struct osmo_wqueue *queue, struct ctrl_cmd *cmd)
 | 
			
		||||
{
 | 
			
		||||
	int ret;
 | 
			
		||||
@@ -74,7 +84,7 @@ int ctrl_cmd_send(struct osmo_wqueue *queue, struct ctrl_cmd *cmd)
 | 
			
		||||
 | 
			
		||||
	msg = ctrl_cmd_make(cmd);
 | 
			
		||||
	if (!msg) {
 | 
			
		||||
		LOGP(DINP, LOGL_ERROR, "Could not generate msg\n");
 | 
			
		||||
		LOGP(DCTRL, LOGL_ERROR, "Could not generate msg\n");
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -83,19 +93,52 @@ int ctrl_cmd_send(struct osmo_wqueue *queue, struct ctrl_cmd *cmd)
 | 
			
		||||
 | 
			
		||||
	ret = osmo_wqueue_enqueue(queue, msg);
 | 
			
		||||
	if (ret != 0) {
 | 
			
		||||
		LOGP(DINP, LOGL_ERROR, "Failed to enqueue the command.\n");
 | 
			
		||||
		LOGP(DCTRL, LOGL_ERROR, "Failed to enqueue the command.\n");
 | 
			
		||||
		msgb_free(msg);
 | 
			
		||||
	}
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct ctrl_cmd *ctrl_cmd_trap(struct ctrl_cmd *cmd)
 | 
			
		||||
{
 | 
			
		||||
	struct ctrl_cmd *trap;
 | 
			
		||||
 | 
			
		||||
	trap = ctrl_cmd_cpy(tall_bsc_ctx, cmd);
 | 
			
		||||
	if (!trap) {
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
	trap->ccon = cmd->ccon;
 | 
			
		||||
	trap->type = CTRL_TYPE_TRAP;
 | 
			
		||||
 | 
			
		||||
	return trap;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int get_num(vector vline, int i, long *num)
 | 
			
		||||
{
 | 
			
		||||
	char *token, *tmp;
 | 
			
		||||
 | 
			
		||||
	if (i >= vector_active(vline))
 | 
			
		||||
		return 0;
 | 
			
		||||
	token = vector_slot(vline, i);
 | 
			
		||||
 | 
			
		||||
	errno = 0;
 | 
			
		||||
	if (token[0] == '\0')
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	*num = strtol(token, &tmp, 10);
 | 
			
		||||
	if (tmp[0] != '\0' || errno != 0)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int ctrl_cmd_handle(struct ctrl_cmd *cmd, void *data)
 | 
			
		||||
{
 | 
			
		||||
	char *token, *request;
 | 
			
		||||
	int num, i, j, ret, node;
 | 
			
		||||
	struct gsm_network *gsmnet = data;
 | 
			
		||||
	long num;
 | 
			
		||||
	int i, j, ret, node;
 | 
			
		||||
 | 
			
		||||
	struct gsm_network *net = NULL;
 | 
			
		||||
	struct gsm_network *net = data;
 | 
			
		||||
	struct gsm_bts *bts = NULL;
 | 
			
		||||
	struct gsm_bts_trx *trx = NULL;
 | 
			
		||||
	struct gsm_bts_trx_ts *ts = NULL;
 | 
			
		||||
@@ -103,8 +146,8 @@ int ctrl_cmd_handle(struct ctrl_cmd *cmd, void *data)
 | 
			
		||||
 | 
			
		||||
	ret = CTRL_CMD_ERROR;
 | 
			
		||||
	cmd->reply = "Someone forgot to fill in the reply.";
 | 
			
		||||
	cmd->node = NULL;
 | 
			
		||||
	node = CTRL_NODE_ROOT;
 | 
			
		||||
	cmd->node = net;
 | 
			
		||||
 | 
			
		||||
	request = talloc_strdup(tall_bsc_ctx, cmd->variable);
 | 
			
		||||
	if (!request)
 | 
			
		||||
@@ -117,46 +160,51 @@ int ctrl_cmd_handle(struct ctrl_cmd *cmd, void *data)
 | 
			
		||||
 | 
			
		||||
	vline = cmd_make_strvec(request);
 | 
			
		||||
	talloc_free(request);
 | 
			
		||||
	if (!vline)
 | 
			
		||||
	if (!vline) {
 | 
			
		||||
		cmd->reply = "cmd_make_strvec failed.";
 | 
			
		||||
		goto err;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for (i=0;i<vector_active(vline);i++) {
 | 
			
		||||
		token = vector_slot(vline, i);
 | 
			
		||||
		/* TODO: We need to make sure that the following chars are digits
 | 
			
		||||
		 * and/or use strtol to check if number conversion was successful
 | 
			
		||||
		 * Right now something like net.bts_stats will not work */
 | 
			
		||||
		if (!strcmp(token, "net")) {
 | 
			
		||||
			net = gsmnet;
 | 
			
		||||
		if (!strcmp(token, "bts")) {
 | 
			
		||||
			if (!net)
 | 
			
		||||
				break;
 | 
			
		||||
			cmd->node = net;
 | 
			
		||||
			node = CTRL_NODE_NET;
 | 
			
		||||
		} else if (!strncmp(token, "bts", 3)) {
 | 
			
		||||
			if (!net)
 | 
			
		||||
				break;
 | 
			
		||||
			num = atoi(&token[3]);
 | 
			
		||||
				goto err_missing;
 | 
			
		||||
			i++;
 | 
			
		||||
			if (!get_num(vline, i, &num))
 | 
			
		||||
				goto err_index;
 | 
			
		||||
 | 
			
		||||
			bts = gsm_bts_num(net, num);
 | 
			
		||||
			if (!bts)
 | 
			
		||||
				break;
 | 
			
		||||
				goto err_missing;
 | 
			
		||||
			cmd->node = bts;
 | 
			
		||||
			node = CTRL_NODE_BTS;
 | 
			
		||||
		} else if (!strncmp(token, "trx", 3)) {
 | 
			
		||||
		} else if (!strcmp(token, "trx")) {
 | 
			
		||||
			if (!bts)
 | 
			
		||||
				break;
 | 
			
		||||
			num = atoi(&token[3]);
 | 
			
		||||
				goto err_missing;
 | 
			
		||||
			i++;
 | 
			
		||||
			if (!get_num(vline, i, &num))
 | 
			
		||||
				goto err_index;
 | 
			
		||||
 | 
			
		||||
			trx = gsm_bts_trx_num(bts, num);
 | 
			
		||||
			if (!trx)
 | 
			
		||||
				break;
 | 
			
		||||
				goto err_missing;
 | 
			
		||||
			cmd->node = trx;
 | 
			
		||||
			node = CTRL_NODE_TRX;
 | 
			
		||||
		} else if (!strncmp(token, "ts", 2)) {
 | 
			
		||||
		} else if (!strcmp(token, "ts")) {
 | 
			
		||||
			if (!trx)
 | 
			
		||||
				break;
 | 
			
		||||
			num = atoi(&token[2]);
 | 
			
		||||
				goto err_missing;
 | 
			
		||||
			i++;
 | 
			
		||||
			if (!get_num(vline, i, &num))
 | 
			
		||||
				goto err_index;
 | 
			
		||||
 | 
			
		||||
			if ((num >= 0) && (num < TRX_NR_TS))
 | 
			
		||||
				ts = &trx->ts[num];
 | 
			
		||||
			if (!ts)
 | 
			
		||||
				break;
 | 
			
		||||
				goto err_missing;
 | 
			
		||||
			cmd->node = ts;
 | 
			
		||||
			node = CTRL_NODE_TS;
 | 
			
		||||
		} else {
 | 
			
		||||
@@ -170,7 +218,7 @@ int ctrl_cmd_handle(struct ctrl_cmd *cmd, void *data)
 | 
			
		||||
			cmds_vec = vector_lookup(ctrl_node_vec, node);
 | 
			
		||||
 | 
			
		||||
			if (!cmds_vec) {
 | 
			
		||||
				cmd->reply = "Command not found";
 | 
			
		||||
				cmd->reply = "Command not found.";
 | 
			
		||||
				vector_free(cmdvec);
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
@@ -180,6 +228,9 @@ int ctrl_cmd_handle(struct ctrl_cmd *cmd, void *data)
 | 
			
		||||
			vector_free(cmdvec);
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (i+1 == vector_active(vline))
 | 
			
		||||
			cmd->reply = "Command not present.";
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cmd_free_strvec(vline);
 | 
			
		||||
@@ -188,12 +239,24 @@ err:
 | 
			
		||||
	if (ret == CTRL_CMD_ERROR)
 | 
			
		||||
		cmd->type = CTRL_TYPE_ERROR;
 | 
			
		||||
	return ret;
 | 
			
		||||
 | 
			
		||||
err_missing:
 | 
			
		||||
	cmd_free_strvec(vline);
 | 
			
		||||
	cmd->type = CTRL_TYPE_ERROR;
 | 
			
		||||
	cmd->reply = "Error while resolving object";
 | 
			
		||||
	return ret;
 | 
			
		||||
err_index:
 | 
			
		||||
	cmd_free_strvec(vline);
 | 
			
		||||
	cmd->type = CTRL_TYPE_ERROR;
 | 
			
		||||
	cmd->reply = "Error while parsing the index.";
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void control_close_conn(struct ctrl_connection *ccon)
 | 
			
		||||
{
 | 
			
		||||
	close(ccon->write_queue.bfd.fd);
 | 
			
		||||
	osmo_fd_unregister(&ccon->write_queue.bfd);
 | 
			
		||||
	llist_del(&ccon->list_entry);
 | 
			
		||||
	if (ccon->closed_cb)
 | 
			
		||||
		ccon->closed_cb(ccon);
 | 
			
		||||
	talloc_free(ccon);
 | 
			
		||||
@@ -217,27 +280,27 @@ static int handle_control_read(struct osmo_fd * bfd)
 | 
			
		||||
 | 
			
		||||
	if (!msg) {
 | 
			
		||||
		if (error == 0)
 | 
			
		||||
			LOGP(DINP, LOGL_INFO, "The control connection was closed\n");
 | 
			
		||||
			LOGP(DCTRL, LOGL_INFO, "The control connection was closed\n");
 | 
			
		||||
		else
 | 
			
		||||
			LOGP(DINP, LOGL_ERROR, "Failed to parse ip access message: %d\n", error);
 | 
			
		||||
			LOGP(DCTRL, LOGL_ERROR, "Failed to parse ip access message: %d\n", error);
 | 
			
		||||
 | 
			
		||||
		goto err;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (msg->len < sizeof(*iph) + sizeof(*iph_ext)) {
 | 
			
		||||
		LOGP(DINP, LOGL_ERROR, "The message is too short.\n");
 | 
			
		||||
		LOGP(DCTRL, LOGL_ERROR, "The message is too short.\n");
 | 
			
		||||
		goto err;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	iph = (struct ipaccess_head *) msg->data;
 | 
			
		||||
	if (iph->proto != IPAC_PROTO_OSMO) {
 | 
			
		||||
		LOGP(DINP, LOGL_ERROR, "Protocol mismatch. We got 0x%x\n", iph->proto);
 | 
			
		||||
		LOGP(DCTRL, LOGL_ERROR, "Protocol mismatch. We got 0x%x\n", iph->proto);
 | 
			
		||||
		goto err;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	iph_ext = (struct ipaccess_head_ext *) iph->data;
 | 
			
		||||
	if (iph_ext->proto != IPAC_PROTO_EXT_CTRL) {
 | 
			
		||||
		LOGP(DINP, LOGL_ERROR, "Extended protocol mismatch. We got 0x%x\n", iph_ext->proto);
 | 
			
		||||
		LOGP(DCTRL, LOGL_ERROR, "Extended protocol mismatch. We got 0x%x\n", iph_ext->proto);
 | 
			
		||||
		goto err;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -255,7 +318,7 @@ static int handle_control_read(struct osmo_fd * bfd)
 | 
			
		||||
		cmd = talloc_zero(ccon, struct ctrl_cmd);
 | 
			
		||||
		if (!cmd)
 | 
			
		||||
			goto err;
 | 
			
		||||
		LOGP(DINP, LOGL_ERROR, "Command parser error.\n");
 | 
			
		||||
		LOGP(DCTRL, LOGL_ERROR, "Command parser error.\n");
 | 
			
		||||
		cmd->type = CTRL_TYPE_ERROR;
 | 
			
		||||
		cmd->id = "err";
 | 
			
		||||
		cmd->reply = "Command parser error.";
 | 
			
		||||
@@ -278,7 +341,7 @@ static int control_write_cb(struct osmo_fd *bfd, struct msgb *msg)
 | 
			
		||||
 | 
			
		||||
	rc = write(bfd->fd, msg->data, msg->len);
 | 
			
		||||
	if (rc != msg->len)
 | 
			
		||||
		LOGP(DINP, LOGL_ERROR, "Failed to write message to the control connection.\n");
 | 
			
		||||
		LOGP(DCTRL, LOGL_ERROR, "Failed to write message to the control connection.\n");
 | 
			
		||||
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
@@ -299,6 +362,7 @@ static struct ctrl_connection *ctrl_connection_alloc(void *ctx)
 | 
			
		||||
static int listen_fd_cb(struct osmo_fd *listen_bfd, unsigned int what)
 | 
			
		||||
{
 | 
			
		||||
	int ret, fd, on;
 | 
			
		||||
	struct ctrl_handle *ctrl;
 | 
			
		||||
	struct ctrl_connection *ccon;
 | 
			
		||||
	struct sockaddr_in sa;
 | 
			
		||||
	socklen_t sa_len = sizeof(sa);
 | 
			
		||||
@@ -312,7 +376,7 @@ static int listen_fd_cb(struct osmo_fd *listen_bfd, unsigned int what)
 | 
			
		||||
		perror("accept");
 | 
			
		||||
		return fd;
 | 
			
		||||
	}
 | 
			
		||||
	LOGP(DINP, LOGL_INFO, "accept()ed new control connection from %s\n",
 | 
			
		||||
	LOGP(DCTRL, LOGL_INFO, "accept()ed new control connection from %s\n",
 | 
			
		||||
		inet_ntoa(sa.sin_addr));
 | 
			
		||||
 | 
			
		||||
	on = 1;
 | 
			
		||||
@@ -324,12 +388,13 @@ static int listen_fd_cb(struct osmo_fd *listen_bfd, unsigned int what)
 | 
			
		||||
	}
 | 
			
		||||
	ccon = ctrl_connection_alloc(listen_bfd->data);
 | 
			
		||||
	if (!ccon) {
 | 
			
		||||
		LOGP(DINP, LOGL_ERROR, "Failed to allocate.\n");
 | 
			
		||||
		LOGP(DCTRL, LOGL_ERROR, "Failed to allocate.\n");
 | 
			
		||||
		close(fd);
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ccon->write_queue.bfd.data = listen_bfd->data;
 | 
			
		||||
	ctrl = listen_bfd->data;
 | 
			
		||||
	ccon->write_queue.bfd.data = ctrl;
 | 
			
		||||
	ccon->write_queue.bfd.fd = fd;
 | 
			
		||||
	ccon->write_queue.bfd.when = BSC_FD_READ;
 | 
			
		||||
	ccon->write_queue.read_cb = handle_control_read;
 | 
			
		||||
@@ -337,11 +402,13 @@ static int listen_fd_cb(struct osmo_fd *listen_bfd, unsigned int what)
 | 
			
		||||
 | 
			
		||||
	ret = osmo_fd_register(&ccon->write_queue.bfd);
 | 
			
		||||
	if (ret < 0) {
 | 
			
		||||
		LOGP(DINP, LOGL_ERROR, "Could not register FD.\n");
 | 
			
		||||
		LOGP(DCTRL, LOGL_ERROR, "Could not register FD.\n");
 | 
			
		||||
		close(ccon->write_queue.bfd.fd);
 | 
			
		||||
		talloc_free(ccon);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	llist_add(&ccon->list_entry, &ctrl->ccon_list);
 | 
			
		||||
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -435,7 +502,7 @@ oom:
 | 
			
		||||
 | 
			
		||||
/* rate_ctr */
 | 
			
		||||
CTRL_CMD_DEFINE(rate_ctr, "rate_ctr *");
 | 
			
		||||
int get_rate_ctr(struct ctrl_cmd *cmd, void *data)
 | 
			
		||||
static int get_rate_ctr(struct ctrl_cmd *cmd, void *data)
 | 
			
		||||
{
 | 
			
		||||
	int intv;
 | 
			
		||||
	unsigned int idx;
 | 
			
		||||
@@ -517,7 +584,7 @@ int get_rate_ctr(struct ctrl_cmd *cmd, void *data)
 | 
			
		||||
 | 
			
		||||
	talloc_free(dup);
 | 
			
		||||
 | 
			
		||||
	cmd->reply = talloc_asprintf(cmd, "%lu", get_rate_ctr_value(ctr, intv));
 | 
			
		||||
	cmd->reply = talloc_asprintf(cmd, "%"PRIu64, get_rate_ctr_value(ctr, intv));
 | 
			
		||||
	if (!cmd->reply)
 | 
			
		||||
		goto oom;
 | 
			
		||||
 | 
			
		||||
@@ -528,21 +595,21 @@ err:
 | 
			
		||||
	return CTRL_CMD_ERROR;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int set_rate_ctr(struct ctrl_cmd *cmd, void *data)
 | 
			
		||||
static int set_rate_ctr(struct ctrl_cmd *cmd, void *data)
 | 
			
		||||
{
 | 
			
		||||
	cmd->reply = "Can't set rate counter.";
 | 
			
		||||
 | 
			
		||||
	return CTRL_CMD_ERROR;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int verify_rate_ctr(struct ctrl_cmd *cmd, const char *value, void *data)
 | 
			
		||||
static int verify_rate_ctr(struct ctrl_cmd *cmd, const char *value, void *data)
 | 
			
		||||
{
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* counter */
 | 
			
		||||
CTRL_CMD_DEFINE(counter, "counter *");
 | 
			
		||||
int get_counter(struct ctrl_cmd *cmd, void *data)
 | 
			
		||||
static int get_counter(struct ctrl_cmd *cmd, void *data)
 | 
			
		||||
{
 | 
			
		||||
	char *ctr_name, *tmp, *dup, *saveptr;
 | 
			
		||||
	struct osmo_counter *counter;
 | 
			
		||||
@@ -585,7 +652,7 @@ err:
 | 
			
		||||
	return CTRL_CMD_ERROR;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int set_counter(struct ctrl_cmd *cmd, void *data)
 | 
			
		||||
static int set_counter(struct ctrl_cmd *cmd, void *data)
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
	cmd->reply = "Can't set counter.";
 | 
			
		||||
@@ -593,36 +660,43 @@ int set_counter(struct ctrl_cmd *cmd, void *data)
 | 
			
		||||
	return CTRL_CMD_ERROR;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int verify_counter(struct ctrl_cmd *cmd, const char *value, void *data)
 | 
			
		||||
static int verify_counter(struct ctrl_cmd *cmd, const char *value, void *data)
 | 
			
		||||
{
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int controlif_setup(struct gsm_network *gsmnet, uint16_t port)
 | 
			
		||||
struct ctrl_handle *controlif_setup(struct gsm_network *gsmnet, uint16_t port)
 | 
			
		||||
{
 | 
			
		||||
	int ret;
 | 
			
		||||
	struct ctrl_handle *ctrl;
 | 
			
		||||
 | 
			
		||||
	ctrl = talloc_zero(tall_bsc_ctx, struct ctrl_handle);
 | 
			
		||||
	if (!ctrl)
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	INIT_LLIST_HEAD(&ctrl->ccon_list);
 | 
			
		||||
 | 
			
		||||
	ctrl->gsmnet = gsmnet;
 | 
			
		||||
 | 
			
		||||
	ctrl_node_vec = vector_init(5);
 | 
			
		||||
	if (!ctrl_node_vec)
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
		goto err;
 | 
			
		||||
 | 
			
		||||
	/* Listen for control connections */
 | 
			
		||||
	ret = make_sock(&ctrl->listen_fd, IPPROTO_TCP, 0, port,
 | 
			
		||||
	ret = make_sock(&ctrl->listen_fd, IPPROTO_TCP, INADDR_LOOPBACK, port,
 | 
			
		||||
			0, listen_fd_cb, ctrl);
 | 
			
		||||
	if (ret < 0) {
 | 
			
		||||
		talloc_free(ctrl);
 | 
			
		||||
		return ret;
 | 
			
		||||
	}
 | 
			
		||||
	if (ret < 0)
 | 
			
		||||
		goto err;
 | 
			
		||||
 | 
			
		||||
	ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_rate_ctr);
 | 
			
		||||
	ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_counter);
 | 
			
		||||
	ret = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_rate_ctr);
 | 
			
		||||
	if (ret)
 | 
			
		||||
		goto err;
 | 
			
		||||
	ret = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_counter);
 | 
			
		||||
	if (ret)
 | 
			
		||||
		goto err;
 | 
			
		||||
 | 
			
		||||
	return ret;
 | 
			
		||||
	return ctrl;
 | 
			
		||||
err:
 | 
			
		||||
	talloc_free(ctrl);
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -181,12 +181,14 @@ static struct msgb *create_response_with_sdp(struct mgcp_endpoint *endp,
 | 
			
		||||
	snprintf(sdp_record, sizeof(sdp_record) - 1,
 | 
			
		||||
			"I: %u\n\n"
 | 
			
		||||
			"v=0\r\n"
 | 
			
		||||
			"o=- %u 23 IN IP4 %s\r\n"
 | 
			
		||||
			"c=IN IP4 %s\r\n"
 | 
			
		||||
			"t=0 0\r\n"
 | 
			
		||||
			"m=audio %d RTP/AVP %d\r\n"
 | 
			
		||||
			"a=rtpmap:%d %s\r\n",
 | 
			
		||||
			endp->ci, addr, endp->net_end.local_port,
 | 
			
		||||
			endp->bts_end.payload_type, endp->bts_end.payload_type,
 | 
			
		||||
		        endp->tcfg->audio_name);
 | 
			
		||||
			endp->ci, endp->ci, addr, addr,
 | 
			
		||||
			endp->net_end.local_port, endp->bts_end.payload_type,
 | 
			
		||||
			endp->bts_end.payload_type, endp->tcfg->audio_name);
 | 
			
		||||
	return mgcp_create_response_with_data(200, " OK", msg, trans_id, sdp_record);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -508,12 +510,49 @@ static struct msgb *handle_create_con(struct mgcp_config *cfg, struct msgb *msg)
 | 
			
		||||
	struct mgcp_endpoint *endp;
 | 
			
		||||
	int error_code = 400;
 | 
			
		||||
 | 
			
		||||
	const char *local_options = NULL;
 | 
			
		||||
	const char *callid = NULL;
 | 
			
		||||
	const char *mode = NULL;
 | 
			
		||||
	
 | 
			
		||||
 | 
			
		||||
	found = mgcp_analyze_header(cfg, msg, data_ptrs, ARRAY_SIZE(data_ptrs), &trans_id, &endp);
 | 
			
		||||
	if (found != 0)
 | 
			
		||||
		return create_err_response(510, "CRCX", trans_id);
 | 
			
		||||
 | 
			
		||||
	tcfg = endp->tcfg;
 | 
			
		||||
 | 
			
		||||
	/* parse CallID C: and LocalParameters L: */
 | 
			
		||||
	MSG_TOKENIZE_START
 | 
			
		||||
	switch (msg->l3h[line_start]) {
 | 
			
		||||
	case 'L':
 | 
			
		||||
		local_options = (const char *) &msg->l3h[line_start + 3];
 | 
			
		||||
		break;
 | 
			
		||||
	case 'C':
 | 
			
		||||
		callid = (const char *) &msg->l3h[line_start + 3];
 | 
			
		||||
		break;
 | 
			
		||||
	case 'M':
 | 
			
		||||
		mode = (const char *) & msg->l3h[line_start + 3];
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		LOGP(DMGCP, LOGL_NOTICE, "Unhandled option: '%c'/%d on 0x%x\n",
 | 
			
		||||
			msg->l3h[line_start], msg->l3h[line_start],
 | 
			
		||||
			ENDPOINT_NUMBER(endp));
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
	MSG_TOKENIZE_END
 | 
			
		||||
 | 
			
		||||
	/* Check required data */
 | 
			
		||||
	if (!callid || !mode) {
 | 
			
		||||
		LOGP(DMGCP, LOGL_ERROR, "Missing callid and mode in CRCX on 0x%x\n",
 | 
			
		||||
		     ENDPOINT_NUMBER(endp));
 | 
			
		||||
		return create_err_response(400, "CRCX", trans_id);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* this appears to be a retransmission, maybe check trans id */
 | 
			
		||||
	if (endp->allocated &&
 | 
			
		||||
	    memcmp(endp->callid, callid, strlen(endp->callid)) == 0)
 | 
			
		||||
		return create_response_with_sdp(endp, "CRCX", trans_id);
 | 
			
		||||
 | 
			
		||||
	if (endp->allocated) {
 | 
			
		||||
		if (tcfg->force_realloc) {
 | 
			
		||||
			LOGP(DMGCP, LOGL_NOTICE, "Endpoint 0x%x already allocated. Forcing realloc.\n",
 | 
			
		||||
@@ -528,33 +567,16 @@ static struct msgb *handle_create_con(struct mgcp_config *cfg, struct msgb *msg)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* parse CallID C: and LocalParameters L: */
 | 
			
		||||
	MSG_TOKENIZE_START
 | 
			
		||||
	switch (msg->l3h[line_start]) {
 | 
			
		||||
	case 'L':
 | 
			
		||||
		endp->local_options = talloc_strdup(tcfg->endpoints,
 | 
			
		||||
			(const char *)&msg->l3h[line_start + 3]);
 | 
			
		||||
		break;
 | 
			
		||||
	case 'C':
 | 
			
		||||
		endp->callid = talloc_strdup(tcfg->endpoints,
 | 
			
		||||
			(const char *)&msg->l3h[line_start + 3]);
 | 
			
		||||
		break;
 | 
			
		||||
	case 'M':
 | 
			
		||||
		if (parse_conn_mode((const char *)&msg->l3h[line_start + 3],
 | 
			
		||||
			    &endp->conn_mode) != 0) {
 | 
			
		||||
	/* copy some parameters */
 | 
			
		||||
	endp->callid = talloc_strdup(tcfg->endpoints, callid);
 | 
			
		||||
 | 
			
		||||
	if (local_options)
 | 
			
		||||
		endp->local_options = talloc_strdup(tcfg->endpoints, local_options);
 | 
			
		||||
 | 
			
		||||
	if (parse_conn_mode(mode, &endp->conn_mode) != 0) {
 | 
			
		||||
		    error_code = 517;
 | 
			
		||||
		    goto error2;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		endp->orig_mode = endp->conn_mode;
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		LOGP(DMGCP, LOGL_NOTICE, "Unhandled option: '%c'/%d on 0x%x\n",
 | 
			
		||||
			msg->l3h[line_start], msg->l3h[line_start],
 | 
			
		||||
			ENDPOINT_NUMBER(endp));
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
	MSG_TOKENIZE_END
 | 
			
		||||
 | 
			
		||||
	/* initialize */
 | 
			
		||||
	endp->net_end.rtp_port = endp->net_end.rtcp_port = endp->bts_end.rtp_port = endp->bts_end.rtcp_port = 0;
 | 
			
		||||
 
 | 
			
		||||
@@ -7,7 +7,7 @@ bin_PROGRAMS = osmo-bsc
 | 
			
		||||
 | 
			
		||||
osmo_bsc_SOURCES = osmo_bsc_main.c osmo_bsc_rf.c osmo_bsc_vty.c osmo_bsc_api.c \
 | 
			
		||||
		   osmo_bsc_grace.c osmo_bsc_msc.c osmo_bsc_sccp.c \
 | 
			
		||||
		   osmo_bsc_filter.c osmo_bsc_bssap.c osmo_bsc_audio.c
 | 
			
		||||
		   osmo_bsc_filter.c osmo_bsc_bssap.c osmo_bsc_audio.c osmo_bsc_ctrl.c
 | 
			
		||||
# once again since TRAU uses CC symbol :(
 | 
			
		||||
osmo_bsc_LDADD = $(top_builddir)/src/libbsc/libbsc.a \
 | 
			
		||||
		 $(top_builddir)/src/libmsc/libmsc.a \
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/* (C) 2009-2010 by Holger Hans Peter Freyther <zecke@selfish.org>
 | 
			
		||||
 * (C) 2009-2010 by On-Waves
 | 
			
		||||
/* (C) 2009-2011 by Holger Hans Peter Freyther <zecke@selfish.org>
 | 
			
		||||
 * (C) 2009-2011 by On-Waves
 | 
			
		||||
 * All Rights Reserved
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or modify
 | 
			
		||||
@@ -45,18 +45,22 @@
 | 
			
		||||
	} \
 | 
			
		||||
	bsc_queue_for_msc(conn->sccp_con, resp);
 | 
			
		||||
 | 
			
		||||
static uint16_t get_network_code_for_msc(struct gsm_network *net)
 | 
			
		||||
static int bsc_clear_request(struct gsm_subscriber_connection *conn, uint32_t cause);
 | 
			
		||||
static int complete_layer3(struct gsm_subscriber_connection *conn,
 | 
			
		||||
			   struct msgb *msg, struct osmo_msc_data *msc);
 | 
			
		||||
 | 
			
		||||
static uint16_t get_network_code_for_msc(struct osmo_msc_data *msc)
 | 
			
		||||
{
 | 
			
		||||
	if (net->msc_data->core_ncc != -1)
 | 
			
		||||
		return net->msc_data->core_ncc;
 | 
			
		||||
	return net->network_code;
 | 
			
		||||
	if (msc->core_ncc != -1)
 | 
			
		||||
		return msc->core_ncc;
 | 
			
		||||
	return msc->network->network_code;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static uint16_t get_country_code_for_msc(struct gsm_network *net)
 | 
			
		||||
static uint16_t get_country_code_for_msc(struct osmo_msc_data *msc)
 | 
			
		||||
{
 | 
			
		||||
	if (net->msc_data->core_mcc != -1)
 | 
			
		||||
		return net->msc_data->core_mcc;
 | 
			
		||||
	return net->country_code;
 | 
			
		||||
	if (msc->core_mcc != -1)
 | 
			
		||||
		return msc->core_mcc;
 | 
			
		||||
	return msc->network->country_code;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void bsc_sapi_n_reject(struct gsm_subscriber_connection *conn, int dlci)
 | 
			
		||||
@@ -88,17 +92,36 @@ static void bsc_cipher_mode_compl(struct gsm_subscriber_connection *conn,
 | 
			
		||||
static int bsc_compl_l3(struct gsm_subscriber_connection *conn, struct msgb *msg,
 | 
			
		||||
			uint16_t chosen_channel)
 | 
			
		||||
{
 | 
			
		||||
	struct msgb *resp;
 | 
			
		||||
	uint16_t network_code = get_network_code_for_msc(conn->bts->network);
 | 
			
		||||
	uint16_t country_code = get_country_code_for_msc(conn->bts->network);
 | 
			
		||||
	struct osmo_msc_data *msc;
 | 
			
		||||
 | 
			
		||||
	LOGP(DMSC, LOGL_INFO, "Tx MSC COMPL L3\n");
 | 
			
		||||
 | 
			
		||||
	/* find the MSC link we want to use */
 | 
			
		||||
	msc = bsc_find_msc(conn, msg);
 | 
			
		||||
	if (!msc) {
 | 
			
		||||
		LOGP(DMSC, LOGL_ERROR, "Failed to find a MSC for a connection.\n");
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return complete_layer3(conn, msg, msc);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int complete_layer3(struct gsm_subscriber_connection *conn,
 | 
			
		||||
			   struct msgb *msg, struct osmo_msc_data *msc)
 | 
			
		||||
{
 | 
			
		||||
	struct msgb *resp;
 | 
			
		||||
	uint16_t network_code;
 | 
			
		||||
	uint16_t country_code;
 | 
			
		||||
 | 
			
		||||
	/* allocate resource for a new connection */
 | 
			
		||||
	if (bsc_create_new_connection(conn) != 0)
 | 
			
		||||
	if (bsc_create_new_connection(conn, msc) != 0)
 | 
			
		||||
		return BSC_API_CONN_POL_REJECT;
 | 
			
		||||
 | 
			
		||||
	bsc_scan_bts_msg(conn, msg);
 | 
			
		||||
 | 
			
		||||
	network_code = get_network_code_for_msc(conn->sccp_con->msc);
 | 
			
		||||
	country_code = get_country_code_for_msc(conn->sccp_con->msc);
 | 
			
		||||
 | 
			
		||||
	resp = gsm0808_create_layer3(msg, network_code, country_code,
 | 
			
		||||
				     conn->bts->location_area_code,
 | 
			
		||||
				     conn->bts->cell_identity);
 | 
			
		||||
@@ -119,6 +142,96 @@ static int bsc_compl_l3(struct gsm_subscriber_connection *conn, struct msgb *msg
 | 
			
		||||
	return BSC_API_CONN_POL_ACCEPT;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Plastic surgery... we want to give up the current connection
 | 
			
		||||
 */
 | 
			
		||||
static int move_to_msc(struct gsm_subscriber_connection *_conn,
 | 
			
		||||
		       struct msgb *msg, struct osmo_msc_data *msc)
 | 
			
		||||
{
 | 
			
		||||
	struct osmo_bsc_sccp_con *old_con = _conn->sccp_con;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * 1. Give up the old connection.
 | 
			
		||||
	 * This happens by sending a clear request to the MSC,
 | 
			
		||||
	 * it should end with the MSC releasing the connection.
 | 
			
		||||
	 */
 | 
			
		||||
	old_con->conn = NULL;
 | 
			
		||||
	bsc_clear_request(_conn, 0);
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * 2. Attempt to create a new connection to the local
 | 
			
		||||
	 * MSC. If it fails the caller will need to handle this
 | 
			
		||||
	 * properly.
 | 
			
		||||
	 */
 | 
			
		||||
	_conn->sccp_con = NULL;
 | 
			
		||||
	if (complete_layer3(_conn, msg, msc) != BSC_API_CONN_POL_ACCEPT) {
 | 
			
		||||
		gsm0808_clear(_conn);
 | 
			
		||||
		subscr_con_free(_conn);
 | 
			
		||||
		return 1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 2;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int handle_cc_setup(struct gsm_subscriber_connection *conn,
 | 
			
		||||
			   struct msgb *msg)
 | 
			
		||||
{
 | 
			
		||||
	struct gsm48_hdr *gh = msgb_l3(msg);
 | 
			
		||||
	uint8_t pdisc = gh->proto_discr & 0x0f;
 | 
			
		||||
	uint8_t mtype = gh->msg_type & 0xbf;
 | 
			
		||||
 | 
			
		||||
	struct osmo_msc_data *msc;
 | 
			
		||||
	struct gsm_mncc_number called;
 | 
			
		||||
	struct tlv_parsed tp;
 | 
			
		||||
	unsigned payload_len;
 | 
			
		||||
 | 
			
		||||
	char _dest_nr[35];
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Do we have a setup message here? if not return fast.
 | 
			
		||||
	 */
 | 
			
		||||
	if (pdisc != GSM48_PDISC_CC || mtype != GSM48_MT_CC_SETUP)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	payload_len = msgb_l3len(msg) - sizeof(*gh);
 | 
			
		||||
 | 
			
		||||
	tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0);
 | 
			
		||||
	if (!TLVP_PRESENT(&tp, GSM48_IE_CALLED_BCD)) {
 | 
			
		||||
		LOGP(DMSC, LOGL_ERROR, "Called BCD not present in setup.\n");
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	memset(&called, 0, sizeof(called));
 | 
			
		||||
	gsm48_decode_called(&called,
 | 
			
		||||
			    TLVP_VAL(&tp, GSM48_IE_CALLED_BCD) - 1);
 | 
			
		||||
 | 
			
		||||
	if (called.plan != 1)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	if (called.type == 1) {
 | 
			
		||||
		_dest_nr[0] = _dest_nr[1] = '0';
 | 
			
		||||
		memcpy(_dest_nr + 2, called.number, sizeof(called.number));
 | 
			
		||||
	} else
 | 
			
		||||
		memcpy(_dest_nr, called.number, sizeof(called.number));
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Check if the connection should be moved...
 | 
			
		||||
	 */
 | 
			
		||||
	llist_for_each_entry(msc, &conn->bts->network->bsc_data->mscs, entry) {
 | 
			
		||||
		if (msc->type != MSC_CON_TYPE_LOCAL)
 | 
			
		||||
			continue;
 | 
			
		||||
		if (!msc->local_pref)
 | 
			
		||||
			continue;
 | 
			
		||||
		if (regexec(&msc->local_pref_reg, _dest_nr, 0, NULL, 0) != 0)
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		return move_to_msc(conn, msg, msc);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void bsc_dtap(struct gsm_subscriber_connection *conn, uint8_t link_id, struct msgb *msg)
 | 
			
		||||
{
 | 
			
		||||
	struct msgb *resp;
 | 
			
		||||
@@ -126,7 +239,16 @@ static void bsc_dtap(struct gsm_subscriber_connection *conn, uint8_t link_id, st
 | 
			
		||||
 | 
			
		||||
	LOGP(DMSC, LOGL_INFO, "Tx MSC DTAP LINK_ID=0x%02x\n", link_id);
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * We might want to move this connection to a new MSC. Ask someone
 | 
			
		||||
	 * to handle it. If it was handled we will return.
 | 
			
		||||
	 */
 | 
			
		||||
	if (handle_cc_setup(conn, msg) >= 1)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	bsc_scan_bts_msg(conn, msg);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	resp = gsm0808_create_dtap(msg, link_id);
 | 
			
		||||
	queue_msg_or_return(resp);
 | 
			
		||||
}
 | 
			
		||||
@@ -159,19 +281,56 @@ static void bsc_assign_fail(struct gsm_subscriber_connection *conn,
 | 
			
		||||
 | 
			
		||||
static int bsc_clear_request(struct gsm_subscriber_connection *conn, uint32_t cause)
 | 
			
		||||
{
 | 
			
		||||
	struct osmo_bsc_sccp_con *sccp;
 | 
			
		||||
	struct msgb *resp;
 | 
			
		||||
	return_when_not_connected_val(conn, 1);
 | 
			
		||||
 | 
			
		||||
	LOGP(DMSC, LOGL_INFO, "Tx MSC CLEAR REQUEST\n");
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Remove the connection from BSC<->SCCP part, the SCCP part
 | 
			
		||||
	 * will either be cleared by channel release or MSC disconnect
 | 
			
		||||
	 */
 | 
			
		||||
	sccp = conn->sccp_con;
 | 
			
		||||
	sccp->conn = NULL;
 | 
			
		||||
	conn->sccp_con = NULL;
 | 
			
		||||
 | 
			
		||||
	resp = gsm0808_create_clear_rqst(GSM0808_CAUSE_RADIO_INTERFACE_FAILURE);
 | 
			
		||||
	if (!resp) {
 | 
			
		||||
		LOGP(DMSC, LOGL_ERROR, "Failed to allocate response.\n");
 | 
			
		||||
		return 0;
 | 
			
		||||
		return 1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bsc_queue_for_msc(conn->sccp_con, resp);
 | 
			
		||||
	return 0;
 | 
			
		||||
	bsc_queue_for_msc(sccp, resp);
 | 
			
		||||
	return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void bsc_mr_config(struct gsm_subscriber_connection *conn,
 | 
			
		||||
			  struct gsm48_multi_rate_conf *conf)
 | 
			
		||||
{
 | 
			
		||||
	struct osmo_msc_data *msc;
 | 
			
		||||
 | 
			
		||||
	if (!conn->sccp_con) {
 | 
			
		||||
		LOGP(DMSC, LOGL_ERROR,
 | 
			
		||||
		     "No msc data available on conn %p. Audio will be broken.\n",
 | 
			
		||||
		     conn);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	msc = conn->sccp_con->msc;
 | 
			
		||||
 | 
			
		||||
	conf->ver = 1;
 | 
			
		||||
	conf->icmi = 1;
 | 
			
		||||
 | 
			
		||||
	/* maybe gcc see's it is copy of _one_ byte */
 | 
			
		||||
	conf->m4_75 = msc->amr_conf.m4_75;
 | 
			
		||||
	conf->m5_15 = msc->amr_conf.m5_15;
 | 
			
		||||
	conf->m5_90 = msc->amr_conf.m5_90;
 | 
			
		||||
	conf->m6_70 = msc->amr_conf.m6_70;
 | 
			
		||||
	conf->m7_40 = msc->amr_conf.m7_40;
 | 
			
		||||
	conf->m7_95 = msc->amr_conf.m7_95;
 | 
			
		||||
	conf->m10_2 = msc->amr_conf.m10_2;
 | 
			
		||||
	conf->m12_2 = msc->amr_conf.m12_2;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct bsc_api bsc_handler = {
 | 
			
		||||
@@ -182,6 +341,7 @@ static struct bsc_api bsc_handler = {
 | 
			
		||||
	.assign_compl = bsc_assign_compl,
 | 
			
		||||
	.assign_fail = bsc_assign_fail,
 | 
			
		||||
	.clear_request = bsc_clear_request,
 | 
			
		||||
	.mr_config = bsc_mr_config,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct bsc_api *osmo_bsc_api()
 | 
			
		||||
 
 | 
			
		||||
@@ -64,7 +64,6 @@ static int handle_abisip_signal(unsigned int subsys, unsigned int signal,
 | 
			
		||||
 | 
			
		||||
int osmo_bsc_audio_init(struct gsm_network *net)
 | 
			
		||||
{
 | 
			
		||||
	net->hardcoded_rtp_payload = 98;
 | 
			
		||||
	osmo_signal_register_handler(SS_ABISIP, handle_abisip_signal, net);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
/* GSM 08.08 BSSMAP handling						*/
 | 
			
		||||
/* (C) 2009-2010 by Holger Hans Peter Freyther <zecke@selfish.org>
 | 
			
		||||
 * (C) 2009-2010 by On-Waves
 | 
			
		||||
/* (C) 2009-2011 by Holger Hans Peter Freyther <zecke@selfish.org>
 | 
			
		||||
 * (C) 2009-2011 by On-Waves
 | 
			
		||||
 * All Rights Reserved
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or modify
 | 
			
		||||
@@ -98,7 +98,7 @@ enum gsm48_chan_mode gsm88_to_chan_mode(enum gsm0808_permitted_speech speech)
 | 
			
		||||
	return GSM48_CMODE_SPEECH_AMR;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int bssmap_handle_reset_ack(struct gsm_network *net,
 | 
			
		||||
static int bssmap_handle_reset_ack(struct osmo_msc_data *msc,
 | 
			
		||||
				   struct msgb *msg, unsigned int length)
 | 
			
		||||
{
 | 
			
		||||
	LOGP(DMSC, LOGL_NOTICE, "Reset ACK from MSC\n");
 | 
			
		||||
@@ -106,7 +106,7 @@ static int bssmap_handle_reset_ack(struct gsm_network *net,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* GSM 08.08 § 3.2.1.19 */
 | 
			
		||||
static int bssmap_handle_paging(struct gsm_network *net,
 | 
			
		||||
static int bssmap_handle_paging(struct osmo_msc_data *msc,
 | 
			
		||||
				struct msgb *msg, unsigned int payload_length)
 | 
			
		||||
{
 | 
			
		||||
	struct gsm_subscriber *subscr;
 | 
			
		||||
@@ -169,7 +169,7 @@ static int bssmap_handle_paging(struct gsm_network *net,
 | 
			
		||||
		LOGP(DMSC, LOGL_ERROR, "eMLPP is not handled\n");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	subscr = subscr_get_or_create(net, mi_string);
 | 
			
		||||
	subscr = subscr_get_or_create(msc->network, mi_string);
 | 
			
		||||
	if (!subscr) {
 | 
			
		||||
		LOGP(DMSC, LOGL_ERROR, "Failed to allocate a subscriber for %s\n", mi_string);
 | 
			
		||||
		return -1;
 | 
			
		||||
@@ -179,7 +179,7 @@ static int bssmap_handle_paging(struct gsm_network *net,
 | 
			
		||||
	subscr->tmsi = tmsi;
 | 
			
		||||
 | 
			
		||||
	LOGP(DMSC, LOGL_INFO, "Paging request from MSC IMSI: '%s' TMSI: '0x%x/%u' LAC: 0x%x\n", mi_string, tmsi, tmsi, lac);
 | 
			
		||||
	paging_request(net, subscr, chan_needed, NULL, NULL);
 | 
			
		||||
	paging_request(msc->network, subscr, chan_needed, NULL, msc);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -298,6 +298,7 @@ static int bssmap_handle_assignm_req(struct osmo_bsc_sccp_con *conn,
 | 
			
		||||
				     struct msgb *msg, unsigned int length)
 | 
			
		||||
{
 | 
			
		||||
	struct msgb *resp;
 | 
			
		||||
	struct osmo_msc_data *msc;
 | 
			
		||||
	struct gsm_network *network;
 | 
			
		||||
	struct tlv_parsed tp;
 | 
			
		||||
	uint8_t *data;
 | 
			
		||||
@@ -364,11 +365,12 @@ static int bssmap_handle_assignm_req(struct osmo_bsc_sccp_con *conn,
 | 
			
		||||
	 * the correct value.
 | 
			
		||||
	 */
 | 
			
		||||
	full_rate = 0;
 | 
			
		||||
	msc = conn->msc;
 | 
			
		||||
	for (supported = 0;
 | 
			
		||||
		chan_mode == GSM48_CMODE_SIGN && supported < network->msc_data->audio_length;
 | 
			
		||||
		chan_mode == GSM48_CMODE_SIGN && supported < msc->audio_length;
 | 
			
		||||
		++supported) {
 | 
			
		||||
 | 
			
		||||
		int perm_val = audio_support_to_gsm88(network->msc_data->audio_support[supported]);
 | 
			
		||||
		int perm_val = audio_support_to_gsm88(msc->audio_support[supported]);
 | 
			
		||||
		for (i = 2; i < TLVP_LEN(&tp, GSM0808_IE_CHANNEL_TYPE); ++i) {
 | 
			
		||||
			if ((data[i] & 0x7f) == perm_val) {
 | 
			
		||||
				chan_mode = gsm88_to_chan_mode(perm_val);
 | 
			
		||||
@@ -387,8 +389,7 @@ static int bssmap_handle_assignm_req(struct osmo_bsc_sccp_con *conn,
 | 
			
		||||
 | 
			
		||||
	/* map it to a MGCP Endpoint and a RTP port */
 | 
			
		||||
	port = mgcp_timeslot_to_endpoint(multiplex, timeslot);
 | 
			
		||||
	conn->rtp_port = rtp_calculate_port(port,
 | 
			
		||||
						network->msc_data->rtp_base);
 | 
			
		||||
	conn->rtp_port = rtp_calculate_port(port, msc->rtp_base);
 | 
			
		||||
 | 
			
		||||
	return gsm0808_assign_req(conn->conn, chan_mode, full_rate);
 | 
			
		||||
 | 
			
		||||
@@ -403,7 +404,7 @@ reject:
 | 
			
		||||
	return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int bssmap_rcvmsg_udt(struct gsm_network *net,
 | 
			
		||||
static int bssmap_rcvmsg_udt(struct osmo_msc_data *msc,
 | 
			
		||||
			     struct msgb *msg, unsigned int length)
 | 
			
		||||
{
 | 
			
		||||
	int ret = 0;
 | 
			
		||||
@@ -418,11 +419,11 @@ static int bssmap_rcvmsg_udt(struct gsm_network *net,
 | 
			
		||||
 | 
			
		||||
	switch (msg->l4h[0]) {
 | 
			
		||||
	case BSS_MAP_MSG_RESET_ACKNOWLEDGE:
 | 
			
		||||
		ret = bssmap_handle_reset_ack(net, msg, length);
 | 
			
		||||
		ret = bssmap_handle_reset_ack(msc, msg, length);
 | 
			
		||||
		break;
 | 
			
		||||
	case BSS_MAP_MSG_PAGING:
 | 
			
		||||
		if (bsc_grace_allow_new_connection(net))
 | 
			
		||||
			ret = bssmap_handle_paging(net, msg, length);
 | 
			
		||||
		if (bsc_grace_allow_new_connection(msc->network))
 | 
			
		||||
			ret = bssmap_handle_paging(msc, msg, length);
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -507,8 +508,7 @@ static int dtap_rcvmsg(struct osmo_bsc_sccp_con *conn,
 | 
			
		||||
	return gsm0808_submit_dtap(conn->conn, gsm48, header->link_id, 1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int bsc_handle_udt(struct gsm_network *network,
 | 
			
		||||
		   struct bsc_msc_connection *conn,
 | 
			
		||||
int bsc_handle_udt(struct osmo_msc_data *msc,
 | 
			
		||||
		   struct msgb *msgb, unsigned int length)
 | 
			
		||||
{
 | 
			
		||||
	struct bssmap_header *bs;
 | 
			
		||||
@@ -528,7 +528,7 @@ int bsc_handle_udt(struct gsm_network *network,
 | 
			
		||||
	switch (bs->type) {
 | 
			
		||||
	case BSSAP_MSG_BSS_MANAGEMENT:
 | 
			
		||||
		msgb->l4h = &msgb->l3h[sizeof(*bs)];
 | 
			
		||||
		bssmap_rcvmsg_udt(network, msgb, length - sizeof(*bs));
 | 
			
		||||
		bssmap_rcvmsg_udt(msc, msgb, length - sizeof(*bs));
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		LOGP(DMSC, LOGL_NOTICE, "Unimplemented msg type: %s\n",
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										355
									
								
								openbsc/src/osmo-bsc/osmo_bsc_ctrl.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										355
									
								
								openbsc/src/osmo-bsc/osmo_bsc_ctrl.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,355 @@
 | 
			
		||||
/* (C) 2011 by Daniel Willmann <daniel@totalueberwachung.de>
 | 
			
		||||
 * (C) 2011 by Holger Hans Peter Freyther
 | 
			
		||||
 * (C) 2011 by On-Waves
 | 
			
		||||
 * All Rights Reserved
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU Affero General Public License as published by
 | 
			
		||||
 * the Free Software Foundation; either version 3 of the License, or
 | 
			
		||||
 * (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU Affero General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU Affero General Public License
 | 
			
		||||
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <openbsc/control_cmd.h>
 | 
			
		||||
#include <openbsc/debug.h>
 | 
			
		||||
#include <openbsc/gsm_data.h>
 | 
			
		||||
#include <openbsc/osmo_bsc.h>
 | 
			
		||||
#include <openbsc/osmo_bsc_rf.h>
 | 
			
		||||
#include <openbsc/osmo_msc_data.h>
 | 
			
		||||
#include <openbsc/signal.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/linuxlist.h>
 | 
			
		||||
#include <osmocom/core/talloc.h>
 | 
			
		||||
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <time.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
 | 
			
		||||
void osmo_bsc_send_trap(struct ctrl_cmd *cmd, struct bsc_msc_connection *msc_con)
 | 
			
		||||
{
 | 
			
		||||
	struct ctrl_cmd *trap;
 | 
			
		||||
	struct ctrl_handle *ctrl;
 | 
			
		||||
	struct osmo_msc_data *msc_data;
 | 
			
		||||
 | 
			
		||||
	msc_data = (struct osmo_msc_data *) msc_con->write_queue.bfd.data;
 | 
			
		||||
	ctrl = msc_data->network->ctrl;
 | 
			
		||||
 | 
			
		||||
	trap = ctrl_cmd_trap(cmd);
 | 
			
		||||
	if (!trap) {
 | 
			
		||||
		LOGP(DCTRL, LOGL_ERROR, "Failed to create trap.\n");
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ctrl_cmd_send_to_all(ctrl, trap);
 | 
			
		||||
	ctrl_cmd_send(&msc_con->write_queue, trap);
 | 
			
		||||
 | 
			
		||||
	talloc_free(trap);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int get_bts_loc(struct ctrl_cmd *cmd, void *data);
 | 
			
		||||
 | 
			
		||||
static void generate_location_state_trap(struct gsm_bts *bts, struct bsc_msc_connection *msc_con)
 | 
			
		||||
{
 | 
			
		||||
	struct ctrl_cmd *cmd;
 | 
			
		||||
	char *oper, *admin, *policy;
 | 
			
		||||
 | 
			
		||||
	cmd = ctrl_cmd_create(msc_con, CTRL_TYPE_TRAP);
 | 
			
		||||
	if (!cmd) {
 | 
			
		||||
		LOGP(DCTRL, LOGL_ERROR, "Failed to create TRAP command.\n");
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cmd->id = "0";
 | 
			
		||||
	cmd->variable = talloc_asprintf(cmd, "bts.%i.location-state", bts->nr);
 | 
			
		||||
 | 
			
		||||
	/* Prepare the location reply */
 | 
			
		||||
	cmd->node = bts;
 | 
			
		||||
	get_bts_loc(cmd, NULL);
 | 
			
		||||
 | 
			
		||||
	if (osmo_bsc_rf_get_opstate_by_bts(bts) == OSMO_BSC_RF_OPSTATE_OPERATIONAL)
 | 
			
		||||
		oper = "operational";
 | 
			
		||||
	else
 | 
			
		||||
		oper = "inoperational";
 | 
			
		||||
 | 
			
		||||
	if (osmo_bsc_rf_get_adminstate_by_bts(bts) == OSMO_BSC_RF_ADMINSTATE_LOCKED)
 | 
			
		||||
		admin = "locked";
 | 
			
		||||
	else
 | 
			
		||||
		admin = "unlocked";
 | 
			
		||||
 | 
			
		||||
	switch (osmo_bsc_rf_get_policy_by_bts(bts)) {
 | 
			
		||||
	case OSMO_BSC_RF_POLICY_OFF:
 | 
			
		||||
		policy = "off";
 | 
			
		||||
		break;
 | 
			
		||||
	case OSMO_BSC_RF_POLICY_ON:
 | 
			
		||||
		policy = "on";
 | 
			
		||||
		break;
 | 
			
		||||
	case OSMO_BSC_RF_POLICY_GRACE:
 | 
			
		||||
		policy = "grace";
 | 
			
		||||
		break;
 | 
			
		||||
	case OSMO_BSC_RF_POLICY_UNKNOWN:
 | 
			
		||||
		policy = "unknown";
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cmd->reply = talloc_asprintf_append(cmd->reply, ",%s,%s,%s", oper, admin, policy);
 | 
			
		||||
 | 
			
		||||
	osmo_bsc_send_trap(cmd, msc_con);
 | 
			
		||||
	talloc_free(cmd);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const struct value_string valid_names[] = {
 | 
			
		||||
	{ BTS_LOC_FIX_INVALID,	"invalid" },
 | 
			
		||||
	{ BTS_LOC_FIX_2D,	"fix2d" },
 | 
			
		||||
	{ BTS_LOC_FIX_3D,	"fix3d" },
 | 
			
		||||
	{ 0, NULL }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int location_equal(struct bts_location *a, struct bts_location *b)
 | 
			
		||||
{
 | 
			
		||||
	return ((a->tstamp == b->tstamp) && (a->valid == b->valid) && (a->lat == b->lat) &&
 | 
			
		||||
		(a->lon == b->lon) && (a->height == b->height));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void cleanup_locations(struct llist_head *locations)
 | 
			
		||||
{
 | 
			
		||||
	struct bts_location *myloc, *tmp;
 | 
			
		||||
	int invalpos = 0, i = 0;
 | 
			
		||||
 | 
			
		||||
	LOGP(DCTRL, LOGL_DEBUG, "Checking position list.\n");
 | 
			
		||||
	llist_for_each_entry_safe(myloc, tmp, locations, list) {
 | 
			
		||||
		i++;
 | 
			
		||||
		if (i > 3) {
 | 
			
		||||
			LOGP(DCTRL, LOGL_DEBUG, "Deleting old position.\n");
 | 
			
		||||
			llist_del(&myloc->list);
 | 
			
		||||
			talloc_free(myloc);
 | 
			
		||||
		} else if (myloc->valid == BTS_LOC_FIX_INVALID) {
 | 
			
		||||
			/* Only capture the newest of subsequent invalid positions */
 | 
			
		||||
			invalpos++;
 | 
			
		||||
			if (invalpos > 1) {
 | 
			
		||||
				LOGP(DCTRL, LOGL_DEBUG, "Deleting subsequent invalid position.\n");
 | 
			
		||||
				invalpos--;
 | 
			
		||||
				i--;
 | 
			
		||||
				llist_del(&myloc->list);
 | 
			
		||||
				talloc_free(myloc);
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			invalpos = 0;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	LOGP(DCTRL, LOGL_DEBUG, "Found %i positions.\n", i);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
CTRL_CMD_DEFINE(bts_loc, "location");
 | 
			
		||||
static int get_bts_loc(struct ctrl_cmd *cmd, void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct bts_location *curloc;
 | 
			
		||||
	struct gsm_bts *bts = (struct gsm_bts *) cmd->node;
 | 
			
		||||
	if (!bts) {
 | 
			
		||||
		cmd->reply = "bts not found.";
 | 
			
		||||
		return CTRL_CMD_ERROR;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (llist_empty(&bts->loc_list)) {
 | 
			
		||||
		cmd->reply = talloc_asprintf(cmd, "0,invalid,0,0,0");
 | 
			
		||||
		return CTRL_CMD_REPLY;
 | 
			
		||||
	} else {
 | 
			
		||||
		curloc = llist_entry(bts->loc_list.next, struct bts_location, list);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cmd->reply = talloc_asprintf(cmd, "%lu,%s,%f,%f,%f", curloc->tstamp,
 | 
			
		||||
			get_value_string(valid_names, curloc->valid), curloc->lat, curloc->lon, curloc->height);
 | 
			
		||||
	if (!cmd->reply) {
 | 
			
		||||
		cmd->reply = "OOM";
 | 
			
		||||
		return CTRL_CMD_ERROR;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return CTRL_CMD_REPLY;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int set_bts_loc(struct ctrl_cmd *cmd, void *data)
 | 
			
		||||
{
 | 
			
		||||
	char *saveptr, *lat, *lon, *height, *tstamp, *valid, *tmp;
 | 
			
		||||
	struct bts_location *curloc, *lastloc;
 | 
			
		||||
	int ret;
 | 
			
		||||
	struct gsm_network *gsmnet = (struct gsm_network *)data;
 | 
			
		||||
	struct gsm_bts *bts = (struct gsm_bts *) cmd->node;
 | 
			
		||||
	struct osmo_msc_data *msc;
 | 
			
		||||
 | 
			
		||||
	if (!bts) {
 | 
			
		||||
		cmd->reply = "bts not found.";
 | 
			
		||||
		return CTRL_CMD_ERROR;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	tmp = talloc_strdup(cmd, cmd->value);
 | 
			
		||||
	if (!tmp)
 | 
			
		||||
		goto oom;
 | 
			
		||||
 | 
			
		||||
	curloc = talloc_zero(tall_bsc_ctx, struct bts_location);
 | 
			
		||||
	if (!curloc) {
 | 
			
		||||
		talloc_free(tmp);
 | 
			
		||||
		goto oom;
 | 
			
		||||
	}
 | 
			
		||||
	INIT_LLIST_HEAD(&curloc->list);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	tstamp = strtok_r(tmp, ",", &saveptr);
 | 
			
		||||
	valid = strtok_r(NULL, ",", &saveptr);
 | 
			
		||||
	lat = strtok_r(NULL, ",", &saveptr);
 | 
			
		||||
	lon = strtok_r(NULL, ",", &saveptr);
 | 
			
		||||
	height = strtok_r(NULL, "\0", &saveptr);
 | 
			
		||||
 | 
			
		||||
	curloc->tstamp = atol(tstamp);
 | 
			
		||||
	curloc->valid = get_string_value(valid_names, valid);
 | 
			
		||||
	curloc->lat = atof(lat);
 | 
			
		||||
	curloc->lon = atof(lon);
 | 
			
		||||
	curloc->height = atof(height);
 | 
			
		||||
	talloc_free(tmp);
 | 
			
		||||
 | 
			
		||||
	lastloc = llist_entry(bts->loc_list.next, struct bts_location, list);
 | 
			
		||||
 | 
			
		||||
	/* Add location to the end of the list */
 | 
			
		||||
	llist_add(&curloc->list, &bts->loc_list);
 | 
			
		||||
 | 
			
		||||
	ret = get_bts_loc(cmd, data);
 | 
			
		||||
 | 
			
		||||
	if (!location_equal(curloc, lastloc)) {
 | 
			
		||||
		llist_for_each_entry(msc, &gsmnet->bsc_data->mscs, entry)
 | 
			
		||||
			generate_location_state_trap(bts, msc->msc_con);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cleanup_locations(&bts->loc_list);
 | 
			
		||||
 | 
			
		||||
	return ret;
 | 
			
		||||
 | 
			
		||||
oom:
 | 
			
		||||
	cmd->reply = "OOM";
 | 
			
		||||
	return CTRL_CMD_ERROR;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int verify_bts_loc(struct ctrl_cmd *cmd, const char *value, void *data)
 | 
			
		||||
{
 | 
			
		||||
	char *saveptr, *latstr, *lonstr, *heightstr, *tstampstr, *validstr, *tmp;
 | 
			
		||||
	time_t tstamp;
 | 
			
		||||
	int valid;
 | 
			
		||||
	double lat, lon, height;
 | 
			
		||||
 | 
			
		||||
	tmp = talloc_strdup(cmd, value);
 | 
			
		||||
	if (!tmp)
 | 
			
		||||
		return 1;
 | 
			
		||||
 | 
			
		||||
	tstampstr = strtok_r(tmp, ",", &saveptr);
 | 
			
		||||
	validstr = strtok_r(NULL, ",", &saveptr);
 | 
			
		||||
	latstr = strtok_r(NULL, ",", &saveptr);
 | 
			
		||||
	lonstr = strtok_r(NULL, ",", &saveptr);
 | 
			
		||||
	heightstr = strtok_r(NULL, "\0", &saveptr);
 | 
			
		||||
 | 
			
		||||
	if ((tstampstr == NULL) || (validstr == NULL) || (latstr == NULL) ||
 | 
			
		||||
			(lonstr == NULL) || (heightstr == NULL))
 | 
			
		||||
		goto err;
 | 
			
		||||
 | 
			
		||||
	tstamp = atol(tstampstr);
 | 
			
		||||
	valid = get_string_value(valid_names, validstr);
 | 
			
		||||
	lat = atof(latstr);
 | 
			
		||||
	lon = atof(lonstr);
 | 
			
		||||
	height = atof(heightstr);
 | 
			
		||||
	talloc_free(tmp);
 | 
			
		||||
 | 
			
		||||
	if (((tstamp == 0) && (valid != BTS_LOC_FIX_INVALID)) || (lat < -90) || (lat > 90) ||
 | 
			
		||||
			(lon < -180) || (lon > 180) || (valid < 0)) {
 | 
			
		||||
		goto err;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
err:
 | 
			
		||||
		cmd->reply = talloc_strdup(cmd, "The format is <unixtime>,(invalid|fix2d|fix3d),<lat>,<lon>,<height>");
 | 
			
		||||
		return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
CTRL_CMD_DEFINE(net_rf_lock, "rf_locked");
 | 
			
		||||
static int get_net_rf_lock(struct ctrl_cmd *cmd, void *data)
 | 
			
		||||
{
 | 
			
		||||
	cmd->reply = "get only works for the individual trx properties.";
 | 
			
		||||
	return CTRL_CMD_ERROR;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int set_net_rf_lock(struct ctrl_cmd *cmd, void *data)
 | 
			
		||||
{
 | 
			
		||||
	int locked = atoi(cmd->value);
 | 
			
		||||
	struct gsm_network *net = cmd->node;
 | 
			
		||||
	if (!net) {
 | 
			
		||||
		cmd->reply = "net not found.";
 | 
			
		||||
		return CTRL_CMD_ERROR;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!net->bsc_data->rf_ctrl) {
 | 
			
		||||
		cmd->reply = "RF Ctrl not enabled";
 | 
			
		||||
		return CTRL_CMD_ERROR;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	osmo_bsc_rf_schedule_lock(net->bsc_data->rf_ctrl,
 | 
			
		||||
			locked == 1 ? '0' : '1');
 | 
			
		||||
 | 
			
		||||
	cmd->reply = talloc_asprintf(cmd, "%u", locked);
 | 
			
		||||
	if (!cmd->reply) {
 | 
			
		||||
		cmd->reply = "OOM.";
 | 
			
		||||
		return CTRL_CMD_ERROR;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return CTRL_CMD_REPLY;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int verify_net_rf_lock(struct ctrl_cmd *cmd, const char *value, void *data)
 | 
			
		||||
{
 | 
			
		||||
	int locked = atoi(cmd->value);
 | 
			
		||||
 | 
			
		||||
	if ((locked != 0) && (locked != 1))
 | 
			
		||||
		return 1;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int msc_signal_handler(unsigned int subsys, unsigned int signal,
 | 
			
		||||
			void *handler_data, void *signal_data)
 | 
			
		||||
{
 | 
			
		||||
	struct msc_signal_data *msc;
 | 
			
		||||
	struct gsm_network *net;
 | 
			
		||||
	struct gsm_bts *bts;
 | 
			
		||||
 | 
			
		||||
	if (subsys != SS_MSC)
 | 
			
		||||
		return 0;
 | 
			
		||||
	if (signal != S_MSC_AUTHENTICATED)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	msc = signal_data;
 | 
			
		||||
 | 
			
		||||
	net = msc->data->network;
 | 
			
		||||
	llist_for_each_entry(bts, &net->bts_list, list)
 | 
			
		||||
		generate_location_state_trap(bts, msc->data->msc_con);	
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int bsc_ctrl_cmds_install()
 | 
			
		||||
{
 | 
			
		||||
	int rc;
 | 
			
		||||
 | 
			
		||||
	rc = ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_loc);
 | 
			
		||||
	if (rc)
 | 
			
		||||
		goto end;
 | 
			
		||||
	rc = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_rf_lock);
 | 
			
		||||
	if (rc)
 | 
			
		||||
		goto end;
 | 
			
		||||
 | 
			
		||||
	osmo_signal_register_handler(SS_MSC, msc_signal_handler, NULL);
 | 
			
		||||
end:
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/* (C) 2009-2010 by Holger Hans Peter Freyther <zecke@selfish.org>
 | 
			
		||||
 * (C) 2009-2010 by On-Waves
 | 
			
		||||
/* (C) 2009-2011 by Holger Hans Peter Freyther <zecke@selfish.org>
 | 
			
		||||
 * (C) 2009-2011 by On-Waves
 | 
			
		||||
 * All Rights Reserved
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or modify
 | 
			
		||||
@@ -53,8 +53,9 @@ static void handle_lu_request(struct gsm_subscriber_connection *conn,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* we will need to stop the paging request */
 | 
			
		||||
static int handle_page_resp(struct gsm_subscriber_connection *conn, struct msgb *msg)
 | 
			
		||||
/* extract a subscriber from the paging response */
 | 
			
		||||
static struct gsm_subscriber *extract_sub(struct gsm_subscriber_connection *conn,
 | 
			
		||||
					  struct msgb *msg)
 | 
			
		||||
{
 | 
			
		||||
	uint8_t mi_type;
 | 
			
		||||
	char mi_string[GSM48_MI_SIZE];
 | 
			
		||||
@@ -64,7 +65,7 @@ static int handle_page_resp(struct gsm_subscriber_connection *conn, struct msgb
 | 
			
		||||
 | 
			
		||||
	if (msgb_l3len(msg) < sizeof(*gh) + sizeof(*resp)) {
 | 
			
		||||
		LOGP(DMSC, LOGL_ERROR, "PagingResponse too small: %u\n", msgb_l3len(msg));
 | 
			
		||||
		return -1;
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	gh = msgb_l3(msg);
 | 
			
		||||
@@ -88,6 +89,14 @@ static int handle_page_resp(struct gsm_subscriber_connection *conn, struct msgb
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return subscr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* we will need to stop the paging request */
 | 
			
		||||
static int handle_page_resp(struct gsm_subscriber_connection *conn, struct msgb *msg)
 | 
			
		||||
{
 | 
			
		||||
	struct gsm_subscriber *subscr = extract_sub(conn, msg);
 | 
			
		||||
 | 
			
		||||
	if (!subscr) {
 | 
			
		||||
		LOGP(DMSC, LOGL_ERROR, "Non active subscriber got paged.\n");
 | 
			
		||||
		return -1;
 | 
			
		||||
@@ -98,6 +107,102 @@ static int handle_page_resp(struct gsm_subscriber_connection *conn, struct msgb
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int is_cm_service_for_emerg(struct msgb *msg)
 | 
			
		||||
{
 | 
			
		||||
	struct gsm48_service_request *cm;
 | 
			
		||||
	struct gsm48_hdr *gh = msgb_l3(msg);
 | 
			
		||||
 | 
			
		||||
	if (msgb_l3len(msg) < sizeof(*gh) + sizeof(*cm)) {
 | 
			
		||||
		LOGP(DMSC, LOGL_ERROR, "CM ServiceRequest does not fit.\n");
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cm = (struct gsm48_service_request *) &gh->data[0];
 | 
			
		||||
	return cm->cm_service_type == GSM48_CMSERV_EMERGENCY;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct osmo_msc_data *bsc_find_msc(struct gsm_subscriber_connection *conn,
 | 
			
		||||
				   struct msgb *msg)
 | 
			
		||||
{
 | 
			
		||||
	struct gsm48_hdr *gh;
 | 
			
		||||
	int8_t pdisc;
 | 
			
		||||
	uint8_t mtype;
 | 
			
		||||
	struct osmo_bsc_data *bsc;
 | 
			
		||||
	struct osmo_msc_data *msc, *pag_msc;
 | 
			
		||||
	struct gsm_subscriber *subscr;
 | 
			
		||||
	int is_emerg = 0;
 | 
			
		||||
 | 
			
		||||
	bsc = conn->bts->network->bsc_data;
 | 
			
		||||
 | 
			
		||||
	if (msgb_l3len(msg) < sizeof(*gh)) {
 | 
			
		||||
		LOGP(DMSC, LOGL_ERROR, "There is no GSM48 header here.\n");
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	gh = msgb_l3(msg);
 | 
			
		||||
	pdisc = gh->proto_discr & 0x0f;
 | 
			
		||||
	mtype = gh->msg_type & 0xbf;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * We are asked to select a MSC here but they are not equal. We
 | 
			
		||||
	 * want to respond to a paging request on the MSC where we got the
 | 
			
		||||
	 * request from. This is where we need to decide where this connection
 | 
			
		||||
	 * will go.
 | 
			
		||||
	 */
 | 
			
		||||
	if (pdisc == GSM48_PDISC_RR && mtype == GSM48_MT_RR_PAG_RESP)
 | 
			
		||||
		goto paging;
 | 
			
		||||
	else if (pdisc == GSM48_PDISC_MM && mtype == GSM48_MT_MM_CM_SERV_REQ) {
 | 
			
		||||
		is_emerg = is_cm_service_for_emerg(msg);
 | 
			
		||||
		goto round_robin;
 | 
			
		||||
	} else
 | 
			
		||||
		goto round_robin;
 | 
			
		||||
 | 
			
		||||
round_robin:
 | 
			
		||||
	llist_for_each_entry(msc, &bsc->mscs, entry) {
 | 
			
		||||
		if (!msc->msc_con->is_authenticated)
 | 
			
		||||
			continue;
 | 
			
		||||
		if (!is_emerg && msc->type != MSC_CON_TYPE_NORMAL)
 | 
			
		||||
			continue;
 | 
			
		||||
		if (is_emerg && !msc->allow_emerg)
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		/* force round robin by moving it to the end */
 | 
			
		||||
		llist_move_tail(&msc->entry, &bsc->mscs);
 | 
			
		||||
		return msc;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return NULL;
 | 
			
		||||
 | 
			
		||||
paging:
 | 
			
		||||
	subscr = extract_sub(conn, msg);
 | 
			
		||||
 | 
			
		||||
	if (!subscr) {
 | 
			
		||||
		LOGP(DMSC, LOGL_ERROR, "Got paged but no subscriber found.\n");
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	pag_msc = paging_get_data(conn->bts, subscr);
 | 
			
		||||
	subscr_put(subscr);
 | 
			
		||||
 | 
			
		||||
	llist_for_each_entry(msc, &bsc->mscs, entry) {
 | 
			
		||||
		if (msc != pag_msc)
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		/*
 | 
			
		||||
		 * We don't check if the MSC is connected. In case it
 | 
			
		||||
		 * is not the connection will be dropped.
 | 
			
		||||
		 */
 | 
			
		||||
 | 
			
		||||
		/* force round robin by moving it to the end */
 | 
			
		||||
		llist_move_tail(&msc->entry, &bsc->mscs);
 | 
			
		||||
		return msc;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	LOGP(DMSC, LOGL_ERROR, "Got paged but no request found.\n");
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * This is used to scan a message for extra functionality of the BSC. This
 | 
			
		||||
 * includes scanning for location updating requests/acceptd and then send
 | 
			
		||||
@@ -122,13 +227,13 @@ int bsc_scan_bts_msg(struct gsm_subscriber_connection *conn, struct msgb *msg)
 | 
			
		||||
 | 
			
		||||
static void send_welcome_ussd(struct gsm_subscriber_connection *conn)
 | 
			
		||||
{
 | 
			
		||||
	struct gsm_network *net;
 | 
			
		||||
	net = conn->bts->network;
 | 
			
		||||
	struct osmo_bsc_sccp_con *bsc;
 | 
			
		||||
 | 
			
		||||
	if (!net->msc_data->ussd_welcome_txt)
 | 
			
		||||
	bsc = conn->sccp_con;
 | 
			
		||||
	if (!bsc || !bsc->msc->ussd_welcome_txt);
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	gsm0480_send_ussdNotify(conn, 1, net->msc_data->ussd_welcome_txt);
 | 
			
		||||
	gsm0480_send_ussdNotify(conn, 1, bsc->msc->ussd_welcome_txt);
 | 
			
		||||
	gsm0480_send_releaseComplete(conn);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -137,6 +242,7 @@ static void send_welcome_ussd(struct gsm_subscriber_connection *conn)
 | 
			
		||||
 */
 | 
			
		||||
int bsc_scan_msc_msg(struct gsm_subscriber_connection *conn, struct msgb *msg)
 | 
			
		||||
{
 | 
			
		||||
	struct osmo_msc_data *msc;
 | 
			
		||||
	struct gsm_network *net;
 | 
			
		||||
	struct gsm48_loc_area_id *lai;
 | 
			
		||||
	struct gsm48_hdr *gh;
 | 
			
		||||
@@ -150,10 +256,10 @@ int bsc_scan_msc_msg(struct gsm_subscriber_connection *conn, struct msgb *msg)
 | 
			
		||||
	gh = (struct gsm48_hdr *) msgb_l3(msg);
 | 
			
		||||
	mtype = gh->msg_type & 0xbf;
 | 
			
		||||
	net = conn->bts->network;
 | 
			
		||||
	msc = conn->sccp_con->msc;
 | 
			
		||||
 | 
			
		||||
	if (mtype == GSM48_MT_MM_LOC_UPD_ACCEPT) {
 | 
			
		||||
		if (net->msc_data->core_ncc != -1 ||
 | 
			
		||||
		    net->msc_data->core_mcc != -1) {
 | 
			
		||||
		if (msc->core_ncc != -1 || msc->core_mcc != -1) {
 | 
			
		||||
			if (msgb_l3len(msg) >= sizeof(*gh) + sizeof(*lai)) {
 | 
			
		||||
				lai = (struct gsm48_loc_area_id *) &gh->data[0];
 | 
			
		||||
				gsm48_generate_lai(lai, net->country_code,
 | 
			
		||||
 
 | 
			
		||||
@@ -26,9 +26,9 @@
 | 
			
		||||
 | 
			
		||||
int bsc_grace_allow_new_connection(struct gsm_network *network)
 | 
			
		||||
{
 | 
			
		||||
	if (!network->msc_data->rf_ctrl)
 | 
			
		||||
	if (!network->bsc_data->rf_ctrl)
 | 
			
		||||
		return 1;
 | 
			
		||||
	return network->msc_data->rf_ctrl->policy == S_RF_ON;
 | 
			
		||||
	return network->bsc_data->rf_ctrl->policy == S_RF_ON;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int handle_sub(struct gsm_lchan *lchan, const char *text)
 | 
			
		||||
@@ -68,7 +68,7 @@ static int handle_grace(struct gsm_network *network)
 | 
			
		||||
	struct gsm_bts *bts;
 | 
			
		||||
	struct gsm_bts_trx *trx;
 | 
			
		||||
 | 
			
		||||
	if (!network->msc_data->mid_call_txt)
 | 
			
		||||
	if (!network->bsc_data->mid_call_txt)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	llist_for_each_entry(bts, &network->bts_list, list) {
 | 
			
		||||
@@ -77,7 +77,7 @@ static int handle_grace(struct gsm_network *network)
 | 
			
		||||
				struct gsm_bts_trx_ts *ts = &trx->ts[ts_nr];
 | 
			
		||||
				for (lchan_nr = 0; lchan_nr < TS_MAX_LCHAN; ++lchan_nr) {
 | 
			
		||||
					handle_sub(&ts->lchan[lchan_nr],
 | 
			
		||||
						   network->msc_data->mid_call_txt);
 | 
			
		||||
						   network->bsc_data->mid_call_txt);
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 
 | 
			
		||||
@@ -141,6 +141,8 @@ static struct vty_app_info vty_info = {
 | 
			
		||||
extern int bsc_shutdown_net(struct gsm_network *net);
 | 
			
		||||
static void signal_handler(int signal)
 | 
			
		||||
{
 | 
			
		||||
	struct osmo_msc_data *msc;
 | 
			
		||||
 | 
			
		||||
	fprintf(stdout, "signal %u received\n", signal);
 | 
			
		||||
 | 
			
		||||
	switch (signal) {
 | 
			
		||||
@@ -158,237 +160,20 @@ static void signal_handler(int signal)
 | 
			
		||||
		talloc_report_full(tall_bsc_ctx, stderr);
 | 
			
		||||
		break;
 | 
			
		||||
	case SIGUSR2:
 | 
			
		||||
		if (!bsc_gsmnet->msc_data)
 | 
			
		||||
		if (!bsc_gsmnet->bsc_data)
 | 
			
		||||
			return;
 | 
			
		||||
		if (!bsc_gsmnet->msc_data->msc_con)
 | 
			
		||||
			return;
 | 
			
		||||
		if (!bsc_gsmnet->msc_data->msc_con->is_connected)
 | 
			
		||||
			return;
 | 
			
		||||
		bsc_msc_lost(bsc_gsmnet->msc_data->msc_con);
 | 
			
		||||
		llist_for_each_entry(msc, &bsc_gsmnet->bsc_data->mscs, entry)
 | 
			
		||||
			bsc_msc_lost(msc->msc_con);
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct location {
 | 
			
		||||
	struct llist_head list;
 | 
			
		||||
	unsigned long age;
 | 
			
		||||
	int valid;
 | 
			
		||||
	double lat;
 | 
			
		||||
	double lon;
 | 
			
		||||
	double height;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static LLIST_HEAD(locations);
 | 
			
		||||
 | 
			
		||||
void cleanup_locations()
 | 
			
		||||
{
 | 
			
		||||
	struct location *myloc, *tmp;
 | 
			
		||||
	int invalpos = 0, i = 0;
 | 
			
		||||
 | 
			
		||||
	LOGP(DCTRL, LOGL_DEBUG, "Checking position list.\n");
 | 
			
		||||
	llist_for_each_entry_safe(myloc, tmp, &locations, list) {
 | 
			
		||||
		i++;
 | 
			
		||||
		if (i > 3) {
 | 
			
		||||
			LOGP(DCTRL, LOGL_DEBUG, "Deleting old position.\n");
 | 
			
		||||
			llist_del(&myloc->list);
 | 
			
		||||
			talloc_free(myloc);
 | 
			
		||||
		} else if (!myloc->valid) { /* Only capture the newest of subsequent invalid positions */
 | 
			
		||||
			invalpos++;
 | 
			
		||||
			if (invalpos > 1) {
 | 
			
		||||
				LOGP(DCTRL, LOGL_DEBUG, "Deleting subsequent invalid position.\n");
 | 
			
		||||
				invalpos--;
 | 
			
		||||
				i--;
 | 
			
		||||
				llist_del(&myloc->list);
 | 
			
		||||
				talloc_free(myloc);
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			invalpos = 0;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	LOGP(DCTRL, LOGL_DEBUG, "Found %i positions.\n", i);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
CTRL_CMD_DEFINE(net_loc, "location");
 | 
			
		||||
int get_net_loc(struct ctrl_cmd *cmd, void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct location *myloc;
 | 
			
		||||
 | 
			
		||||
	if (llist_empty(&locations)) {
 | 
			
		||||
		cmd->reply = talloc_asprintf(cmd, "0,0,0,0,0");
 | 
			
		||||
		return CTRL_CMD_REPLY;
 | 
			
		||||
	} else {
 | 
			
		||||
		myloc = llist_entry(locations.next, struct location, list);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cmd->reply = talloc_asprintf(cmd, "%lu,%i,%f,%f,%f", myloc->age, myloc->valid, myloc->lat, myloc->lon, myloc->height);
 | 
			
		||||
	if (!cmd->reply) {
 | 
			
		||||
		cmd->reply = "OOM";
 | 
			
		||||
		return CTRL_CMD_ERROR;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return CTRL_CMD_REPLY;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int set_net_loc(struct ctrl_cmd *cmd, void *data)
 | 
			
		||||
{
 | 
			
		||||
	char *saveptr, *lat, *lon, *height, *age, *valid, *tmp;
 | 
			
		||||
	struct location *myloc;
 | 
			
		||||
 | 
			
		||||
	tmp = talloc_strdup(cmd, cmd->value);
 | 
			
		||||
	if (!tmp)
 | 
			
		||||
		goto oom;
 | 
			
		||||
 | 
			
		||||
	myloc = talloc_zero(tall_bsc_ctx, struct location);
 | 
			
		||||
	if (!myloc) {
 | 
			
		||||
		talloc_free(tmp);
 | 
			
		||||
		goto oom;
 | 
			
		||||
	}
 | 
			
		||||
	INIT_LLIST_HEAD(&myloc->list);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	age = strtok_r(tmp, ",", &saveptr);
 | 
			
		||||
	valid = strtok_r(NULL, ",", &saveptr);
 | 
			
		||||
	lat = strtok_r(NULL, ",", &saveptr);
 | 
			
		||||
	lon = strtok_r(NULL, ",", &saveptr);
 | 
			
		||||
	height = strtok_r(NULL, "\0", &saveptr);
 | 
			
		||||
 | 
			
		||||
	myloc->age = atol(age);
 | 
			
		||||
	myloc->valid = atoi(valid);
 | 
			
		||||
	myloc->lat = atof(lat);
 | 
			
		||||
	myloc->lon = atof(lon);
 | 
			
		||||
	myloc->height = atof(height);
 | 
			
		||||
	talloc_free(tmp);
 | 
			
		||||
 | 
			
		||||
	/* Add location to the end of the list */
 | 
			
		||||
	llist_add(&myloc->list, &locations);
 | 
			
		||||
	cleanup_locations();
 | 
			
		||||
 | 
			
		||||
	return get_net_loc(cmd, data);
 | 
			
		||||
oom:
 | 
			
		||||
	cmd->reply = "OOM";
 | 
			
		||||
	return CTRL_CMD_ERROR;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int verify_net_loc(struct ctrl_cmd *cmd, const char *value, void *data)
 | 
			
		||||
{
 | 
			
		||||
	char *saveptr, *latstr, *lonstr, *heightstr, *agestr, *validstr, *tmp;
 | 
			
		||||
	unsigned long age;
 | 
			
		||||
	int valid;
 | 
			
		||||
	double lat, lon, height;
 | 
			
		||||
 | 
			
		||||
	tmp = talloc_strdup(cmd, value);
 | 
			
		||||
	if (!tmp)
 | 
			
		||||
		return 1;
 | 
			
		||||
 | 
			
		||||
	agestr = strtok_r(tmp, ",", &saveptr);
 | 
			
		||||
	validstr = strtok_r(NULL, ",", &saveptr);
 | 
			
		||||
	latstr = strtok_r(NULL, ",", &saveptr);
 | 
			
		||||
	lonstr = strtok_r(NULL, ",", &saveptr);
 | 
			
		||||
	heightstr = strtok_r(NULL, "\0", &saveptr);
 | 
			
		||||
 | 
			
		||||
	if ((agestr == NULL) || (validstr == NULL) || (latstr == NULL) ||
 | 
			
		||||
			(lonstr == NULL) || (heightstr == NULL))
 | 
			
		||||
		return 1;
 | 
			
		||||
 | 
			
		||||
	age = atol(agestr);
 | 
			
		||||
	valid = atoi(validstr);
 | 
			
		||||
	lat = atof(latstr);
 | 
			
		||||
	lon = atof(lonstr);
 | 
			
		||||
	height = atof(heightstr);
 | 
			
		||||
	talloc_free(tmp);
 | 
			
		||||
 | 
			
		||||
	if ((age == 0) || (lat < -90) || (lat > 90) || (lon < -180) ||
 | 
			
		||||
			(lon > 180) || (valid < 0) || (valid > 2))
 | 
			
		||||
		return 1;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
CTRL_CMD_DEFINE(trx_rf_lock, "rf_locked");
 | 
			
		||||
int get_trx_rf_lock(struct ctrl_cmd *cmd, void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct gsm_bts_trx *trx = cmd->node;
 | 
			
		||||
	if (!trx) {
 | 
			
		||||
		cmd->reply = "trx not found.";
 | 
			
		||||
		return CTRL_CMD_ERROR;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cmd->reply = talloc_asprintf(cmd, "%u", trx->mo.nm_state.administrative == NM_STATE_LOCKED ? 1 : 0);
 | 
			
		||||
	return CTRL_CMD_REPLY;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int set_trx_rf_lock(struct ctrl_cmd *cmd, void *data)
 | 
			
		||||
{
 | 
			
		||||
	int locked = atoi(cmd->value);
 | 
			
		||||
	struct gsm_bts_trx *trx = cmd->node;
 | 
			
		||||
	if (!trx) {
 | 
			
		||||
		cmd->reply = "trx not found.";
 | 
			
		||||
		return CTRL_CMD_ERROR;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	gsm_trx_lock_rf(trx, locked);
 | 
			
		||||
 | 
			
		||||
	return get_trx_rf_lock(cmd, data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int verify_trx_rf_lock(struct ctrl_cmd *cmd, const char *value, void *data)
 | 
			
		||||
{
 | 
			
		||||
	int locked = atoi(cmd->value);
 | 
			
		||||
 | 
			
		||||
	if ((locked != 0) && (locked != 1))
 | 
			
		||||
		return 1;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
CTRL_CMD_DEFINE(net_rf_lock, "rf_locked");
 | 
			
		||||
int get_net_rf_lock(struct ctrl_cmd *cmd, void *data)
 | 
			
		||||
{
 | 
			
		||||
	cmd->reply = "get only works for the individual trx properties.";
 | 
			
		||||
	return CTRL_CMD_ERROR;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int set_net_rf_lock(struct ctrl_cmd *cmd, void *data)
 | 
			
		||||
{
 | 
			
		||||
	int locked = atoi(cmd->value);
 | 
			
		||||
	struct gsm_network *net = cmd->node;
 | 
			
		||||
	struct gsm_bts *bts;
 | 
			
		||||
	if (!net) {
 | 
			
		||||
		cmd->reply = "net not found.";
 | 
			
		||||
		return CTRL_CMD_ERROR;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	llist_for_each_entry(bts, &net->bts_list, list) {
 | 
			
		||||
		struct gsm_bts_trx *trx;
 | 
			
		||||
		llist_for_each_entry(trx, &bts->trx_list, list) {
 | 
			
		||||
			gsm_trx_lock_rf(trx, locked);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cmd->reply = talloc_asprintf(cmd, "%u", locked);
 | 
			
		||||
	if (!cmd->reply) {
 | 
			
		||||
		cmd->reply = "OOM.";
 | 
			
		||||
		return CTRL_CMD_ERROR;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return CTRL_CMD_REPLY;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int verify_net_rf_lock(struct ctrl_cmd *cmd, const char *value, void *data)
 | 
			
		||||
{
 | 
			
		||||
	int locked = atoi(cmd->value);
 | 
			
		||||
 | 
			
		||||
	if ((locked != 0) && (locked != 1))
 | 
			
		||||
		return 1;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int main(int argc, char **argv)
 | 
			
		||||
{
 | 
			
		||||
	struct osmo_msc_data *data;
 | 
			
		||||
	struct osmo_msc_data *msc;
 | 
			
		||||
	struct osmo_bsc_data *data;
 | 
			
		||||
	int rc;
 | 
			
		||||
 | 
			
		||||
	tall_bsc_ctx = talloc_named_const(NULL, 1, "openbsc");
 | 
			
		||||
@@ -422,12 +207,19 @@ int main(int argc, char **argv)
 | 
			
		||||
	}
 | 
			
		||||
	bsc_api_init(bsc_gsmnet, osmo_bsc_api());
 | 
			
		||||
 | 
			
		||||
	controlif_setup(bsc_gsmnet, 4249);
 | 
			
		||||
	ctrl_cmd_install(CTRL_NODE_NET, &cmd_net_loc);
 | 
			
		||||
	ctrl_cmd_install(CTRL_NODE_NET, &cmd_net_rf_lock);
 | 
			
		||||
	ctrl_cmd_install(CTRL_NODE_TRX, &cmd_trx_rf_lock);
 | 
			
		||||
	bsc_gsmnet->ctrl = controlif_setup(bsc_gsmnet, 4249);
 | 
			
		||||
	if (!bsc_gsmnet) {
 | 
			
		||||
		fprintf(stderr, "Failed to init the control interface. Exiting.\n");
 | 
			
		||||
		exit(1);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	data = bsc_gsmnet->msc_data;
 | 
			
		||||
	rc = bsc_ctrl_cmds_install();
 | 
			
		||||
	if (rc < 0) {
 | 
			
		||||
		fprintf(stderr, "Failed to install control commands. Exiting.\n");
 | 
			
		||||
		exit(1);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	data = bsc_gsmnet->bsc_data;
 | 
			
		||||
	if (rf_ctrl)
 | 
			
		||||
		bsc_replace_string(data, &data->rf_ctrl_name, rf_ctrl);
 | 
			
		||||
 | 
			
		||||
@@ -440,11 +232,14 @@ int main(int argc, char **argv)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (osmo_bsc_msc_init(bsc_gsmnet) != 0) {
 | 
			
		||||
		LOGP(DNAT, LOGL_ERROR, "Failed to start up. Exiting.\n");
 | 
			
		||||
		exit(1);
 | 
			
		||||
	llist_for_each_entry(msc, &bsc_gsmnet->bsc_data->mscs, entry) {
 | 
			
		||||
		if (osmo_bsc_msc_init(msc) != 0) {
 | 
			
		||||
			LOGP(DNAT, LOGL_ERROR, "Failed to start up. Exiting.\n");
 | 
			
		||||
			exit(1);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	if (osmo_bsc_sccp_init(bsc_gsmnet) != 0) {
 | 
			
		||||
		LOGP(DNM, LOGL_ERROR, "Failed to register SCCP.\n");
 | 
			
		||||
		exit(1);
 | 
			
		||||
 
 | 
			
		||||
@@ -267,6 +267,7 @@ static int ipaccess_a_fd_cb(struct osmo_fd *bfd)
 | 
			
		||||
	ipaccess_rcvmsg_base(msg, bfd);
 | 
			
		||||
 | 
			
		||||
	/* initialize the networking. This includes sending a GSM08.08 message */
 | 
			
		||||
	msg->cb[0] = (unsigned long) data;
 | 
			
		||||
	if (hh->proto == IPAC_PROTO_IPACCESS) {
 | 
			
		||||
		if (msg->l2h[0] == IPAC_MSGT_ID_ACK)
 | 
			
		||||
			initialize_if_needed(data->msc_con);
 | 
			
		||||
@@ -410,7 +411,7 @@ static void initialize_if_needed(struct bsc_msc_connection *conn)
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		sccp_write(msg, &sccp_ssn_bssap, &sccp_ssn_bssap, 0, NULL);
 | 
			
		||||
		sccp_write(msg, &sccp_ssn_bssap, &sccp_ssn_bssap, 0, conn);
 | 
			
		||||
		msgb_free(msg);
 | 
			
		||||
		conn->is_authenticated = 1;
 | 
			
		||||
	}
 | 
			
		||||
@@ -418,18 +419,20 @@ static void initialize_if_needed(struct bsc_msc_connection *conn)
 | 
			
		||||
 | 
			
		||||
static void send_id_get_response(struct osmo_msc_data *data, int fd)
 | 
			
		||||
{
 | 
			
		||||
	struct msc_signal_data sig;
 | 
			
		||||
	struct msgb *msg;
 | 
			
		||||
 | 
			
		||||
	msg = bsc_msc_id_get_resp(data->bsc_token);
 | 
			
		||||
	if (!msg)
 | 
			
		||||
		return;
 | 
			
		||||
	msc_queue_write(data->msc_con, msg, IPAC_PROTO_IPACCESS);
 | 
			
		||||
 | 
			
		||||
	sig.data = data;
 | 
			
		||||
	osmo_signal_dispatch(SS_MSC, S_MSC_AUTHENTICATED, &sig);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int osmo_bsc_msc_init(struct gsm_network *network)
 | 
			
		||||
int osmo_bsc_msc_init(struct osmo_msc_data *data)
 | 
			
		||||
{
 | 
			
		||||
	struct osmo_msc_data *data = network->msc_data;
 | 
			
		||||
 | 
			
		||||
	if (mgcp_create_port(data) != 0)
 | 
			
		||||
		return -1;
 | 
			
		||||
 | 
			
		||||
@@ -453,3 +456,47 @@ int osmo_bsc_msc_init(struct gsm_network *network)
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct osmo_msc_data *osmo_msc_data_find(struct gsm_network *net, int nr)
 | 
			
		||||
{
 | 
			
		||||
	struct osmo_msc_data *msc_data;
 | 
			
		||||
 | 
			
		||||
	llist_for_each_entry(msc_data, &net->bsc_data->mscs, entry)
 | 
			
		||||
		if (msc_data->nr == nr)
 | 
			
		||||
			return msc_data;
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct osmo_msc_data *osmo_msc_data_alloc(struct gsm_network *net, int nr)
 | 
			
		||||
{
 | 
			
		||||
	struct osmo_msc_data *msc_data;
 | 
			
		||||
 | 
			
		||||
	/* check if there is already one */
 | 
			
		||||
	msc_data = osmo_msc_data_find(net, nr);
 | 
			
		||||
	if (msc_data)
 | 
			
		||||
		return msc_data;
 | 
			
		||||
 | 
			
		||||
	msc_data = talloc_zero(net, struct osmo_msc_data);
 | 
			
		||||
	if (!msc_data)
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	llist_add_tail(&msc_data->entry, &net->bsc_data->mscs);
 | 
			
		||||
 | 
			
		||||
	/* Init back pointer */
 | 
			
		||||
	msc_data->network = net;
 | 
			
		||||
 | 
			
		||||
	INIT_LLIST_HEAD(&msc_data->dests);
 | 
			
		||||
	msc_data->ping_timeout = 20;
 | 
			
		||||
	msc_data->pong_timeout = 5;
 | 
			
		||||
	msc_data->core_ncc = -1;
 | 
			
		||||
	msc_data->core_mcc = -1;
 | 
			
		||||
	msc_data->rtp_base = 4000;
 | 
			
		||||
 | 
			
		||||
	msc_data->nr = nr;
 | 
			
		||||
	msc_data->allow_emerg = 1;
 | 
			
		||||
 | 
			
		||||
	/* Defaults for the audio setup */
 | 
			
		||||
	msc_data->amr_conf.m5_90 = 1;
 | 
			
		||||
 | 
			
		||||
	return msc_data;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,8 @@
 | 
			
		||||
/* RF Ctl handling socket */
 | 
			
		||||
 | 
			
		||||
/* (C) 2010 by Harald Welte <laforge@gnumonks.org>
 | 
			
		||||
 * (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
 | 
			
		||||
 * (C) 2010 by On-Waves
 | 
			
		||||
 * (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
 | 
			
		||||
@@ -42,6 +42,51 @@
 | 
			
		||||
#define RF_CMD_D_OFF 'd'
 | 
			
		||||
#define RF_CMD_ON_G  'g'
 | 
			
		||||
 | 
			
		||||
enum osmo_bsc_rf_opstate osmo_bsc_rf_get_opstate_by_bts(struct gsm_bts *bts)
 | 
			
		||||
{
 | 
			
		||||
	struct gsm_bts_trx *trx;
 | 
			
		||||
 | 
			
		||||
	llist_for_each_entry(trx, &bts->trx_list, list) {
 | 
			
		||||
		if (trx->mo.nm_state.operational == NM_OPSTATE_ENABLED)
 | 
			
		||||
			return OSMO_BSC_RF_OPSTATE_OPERATIONAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* No trx were active, so this bts is disabled */
 | 
			
		||||
	return OSMO_BSC_RF_OPSTATE_INOPERATIONAL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
enum osmo_bsc_rf_adminstate osmo_bsc_rf_get_adminstate_by_bts(struct gsm_bts *bts)
 | 
			
		||||
{
 | 
			
		||||
	struct gsm_bts_trx *trx;
 | 
			
		||||
 | 
			
		||||
	llist_for_each_entry(trx, &bts->trx_list, list) {
 | 
			
		||||
		if (trx->mo.nm_state.administrative == NM_STATE_UNLOCKED)
 | 
			
		||||
			return OSMO_BSC_RF_ADMINSTATE_UNLOCKED;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* All trx administrative states were locked */
 | 
			
		||||
	return OSMO_BSC_RF_ADMINSTATE_LOCKED;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
enum osmo_bsc_rf_policy osmo_bsc_rf_get_policy_by_bts(struct gsm_bts *bts)
 | 
			
		||||
{
 | 
			
		||||
	struct osmo_bsc_data *bsc_data = bts->network->bsc_data;
 | 
			
		||||
 | 
			
		||||
	if (!bsc_data || !bsc_data->rf_ctrl)
 | 
			
		||||
		return OSMO_BSC_RF_POLICY_UNKNOWN;
 | 
			
		||||
 | 
			
		||||
	switch (bsc_data->rf_ctrl->policy) {
 | 
			
		||||
	case S_RF_ON:
 | 
			
		||||
		return OSMO_BSC_RF_POLICY_ON;
 | 
			
		||||
	case S_RF_OFF:
 | 
			
		||||
		return OSMO_BSC_RF_POLICY_OFF;
 | 
			
		||||
	case S_RF_GRACE:
 | 
			
		||||
		return OSMO_BSC_RF_POLICY_GRACE;
 | 
			
		||||
	default:
 | 
			
		||||
		return OSMO_BSC_RF_POLICY_UNKNOWN;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int lock_each_trx(struct gsm_network *net, int lock)
 | 
			
		||||
{
 | 
			
		||||
	struct gsm_bts *bts;
 | 
			
		||||
@@ -97,7 +142,7 @@ static void handle_query(struct osmo_bsc_rf_conn *conn)
 | 
			
		||||
		struct gsm_bts_trx *trx;
 | 
			
		||||
		llist_for_each_entry(trx, &bts->trx_list, list) {
 | 
			
		||||
			if (trx->mo.nm_state.availability == NM_AVSTATE_OK &&
 | 
			
		||||
			    trx->mo.nm_state.operational != NM_STATE_LOCKED) {
 | 
			
		||||
			    trx->mo.nm_state.operational != NM_OPSTATE_DISABLED) {
 | 
			
		||||
					send = RF_CMD_ON;
 | 
			
		||||
					break;
 | 
			
		||||
			}
 | 
			
		||||
@@ -158,11 +203,16 @@ static void grace_timeout(void *_data)
 | 
			
		||||
 | 
			
		||||
static int enter_grace(struct osmo_bsc_rf *rf)
 | 
			
		||||
{
 | 
			
		||||
	if (osmo_timer_pending(&rf->grace_timeout)) {
 | 
			
		||||
		LOGP(DINP, LOGL_NOTICE, "RF Grace timer is pending. Not restarting.\n");
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rf->grace_timeout.cb = grace_timeout;
 | 
			
		||||
	rf->grace_timeout.data = rf;
 | 
			
		||||
	osmo_timer_schedule(&rf->grace_timeout, rf->gsm_network->msc_data->mid_call_timeout, 0);
 | 
			
		||||
	osmo_timer_schedule(&rf->grace_timeout, rf->gsm_network->bsc_data->mid_call_timeout, 0);
 | 
			
		||||
	LOGP(DINP, LOGL_NOTICE, "Going to switch RF off in %d seconds.\n",
 | 
			
		||||
	     rf->gsm_network->msc_data->mid_call_timeout);
 | 
			
		||||
	     rf->gsm_network->bsc_data->mid_call_timeout);
 | 
			
		||||
 | 
			
		||||
	send_signal(rf, S_RF_GRACE);
 | 
			
		||||
	return 0;
 | 
			
		||||
@@ -217,9 +267,7 @@ static int rf_read_cmd(struct osmo_fd *fd)
 | 
			
		||||
	case RF_CMD_D_OFF:
 | 
			
		||||
	case RF_CMD_ON:
 | 
			
		||||
	case RF_CMD_OFF:
 | 
			
		||||
		conn->rf->last_request = buf[0];
 | 
			
		||||
		if (!osmo_timer_pending(&conn->rf->delay_cmd))
 | 
			
		||||
			osmo_timer_schedule(&conn->rf->delay_cmd, 1, 0);
 | 
			
		||||
		osmo_bsc_rf_schedule_lock(conn->rf, buf[0]);
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		conn->rf->last_state_command = "Unknown command";
 | 
			
		||||
@@ -282,6 +330,52 @@ static int rf_ctrl_accept(struct osmo_fd *bfd, unsigned int what)
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void rf_auto_off_cb(void *_timer)
 | 
			
		||||
{
 | 
			
		||||
	struct osmo_bsc_rf *rf = _timer;
 | 
			
		||||
 | 
			
		||||
	LOGP(DINP, LOGL_NOTICE, "Going to switch off RF due lack of MSC.\n");
 | 
			
		||||
	osmo_bsc_rf_schedule_lock(rf, RF_CMD_D_OFF);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int msc_signal_handler(unsigned int subsys, unsigned int signal,
 | 
			
		||||
			void *handler_data, void *signal_data)
 | 
			
		||||
{
 | 
			
		||||
	struct gsm_network *net;
 | 
			
		||||
	struct msc_signal_data *msc;
 | 
			
		||||
	struct osmo_bsc_rf *rf;
 | 
			
		||||
 | 
			
		||||
	/* check if we want to handle this signal */
 | 
			
		||||
	if (subsys != SS_MSC)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	net = handler_data;
 | 
			
		||||
	msc = signal_data;
 | 
			
		||||
 | 
			
		||||
	/* check if we have the needed information */
 | 
			
		||||
	if (!net->bsc_data || !net->bsc_data->rf_ctrl)
 | 
			
		||||
		return 0;
 | 
			
		||||
	if (msc->data->type != MSC_CON_TYPE_NORMAL)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	rf = net->bsc_data->rf_ctrl;
 | 
			
		||||
	switch (signal) {
 | 
			
		||||
	case S_MSC_LOST:
 | 
			
		||||
		if (net->bsc_data->auto_off_timeout < 0)
 | 
			
		||||
			return 0;
 | 
			
		||||
		if (osmo_timer_pending(&rf->auto_off_timer))
 | 
			
		||||
			return 0;
 | 
			
		||||
		osmo_timer_schedule(&rf->auto_off_timer,
 | 
			
		||||
				net->bsc_data->auto_off_timeout, 0);
 | 
			
		||||
		break;
 | 
			
		||||
	case S_MSC_CONNECTED:
 | 
			
		||||
		osmo_timer_del(&rf->auto_off_timer);
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct osmo_bsc_rf *osmo_bsc_rf_create(const char *path, struct gsm_network *net)
 | 
			
		||||
{
 | 
			
		||||
	unsigned int namelen;
 | 
			
		||||
@@ -360,6 +454,18 @@ struct osmo_bsc_rf *osmo_bsc_rf_create(const char *path, struct gsm_network *net
 | 
			
		||||
	rf->delay_cmd.data = rf;
 | 
			
		||||
	rf->delay_cmd.cb = rf_delay_cmd_cb;
 | 
			
		||||
 | 
			
		||||
	rf->auto_off_timer.data = rf;
 | 
			
		||||
	rf->auto_off_timer.cb = rf_auto_off_cb;
 | 
			
		||||
 | 
			
		||||
	/* listen to RF signals */
 | 
			
		||||
	osmo_signal_register_handler(SS_MSC, msc_signal_handler, net);
 | 
			
		||||
 | 
			
		||||
	return rf;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void osmo_bsc_rf_schedule_lock(struct osmo_bsc_rf *rf, char cmd)
 | 
			
		||||
{
 | 
			
		||||
	rf->last_request = cmd;
 | 
			
		||||
	if (!osmo_timer_pending(&rf->delay_cmd))
 | 
			
		||||
		osmo_timer_schedule(&rf->delay_cmd, 1, 0);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
/* Interaction with the SCCP subsystem */
 | 
			
		||||
/*
 | 
			
		||||
 * (C) 2009-2010 by Holger Hans Peter Freyther <zecke@selfish.org>
 | 
			
		||||
 * (C) 2009-2010 by On-Waves
 | 
			
		||||
 * (C) 2009-2011 by Holger Hans Peter Freyther <zecke@selfish.org>
 | 
			
		||||
 * (C) 2009-2011 by On-Waves
 | 
			
		||||
 * All Rights Reserved
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or modify
 | 
			
		||||
@@ -81,7 +81,7 @@ static void msc_outgoing_sccp_state(struct sccp_connection *conn, int old_state)
 | 
			
		||||
		con_data = (struct osmo_bsc_sccp_con *) conn->data_ctx;
 | 
			
		||||
		if(con_data->conn) {
 | 
			
		||||
			LOGP(DMSC, LOGL_ERROR,
 | 
			
		||||
				"ERROR: The lchan is still associated\n.");
 | 
			
		||||
				"ERROR: The lchan is still associated.\n");
 | 
			
		||||
			gsm0808_clear(con_data->conn);
 | 
			
		||||
			subscr_con_free(con_data->conn);
 | 
			
		||||
			con_data->conn = NULL;
 | 
			
		||||
@@ -140,8 +140,16 @@ static void sccp_cc_timeout(void *_data)
 | 
			
		||||
static void msc_sccp_write_ipa(struct sccp_connection *conn, struct msgb *msg,
 | 
			
		||||
			      void *global_ctx, void *ctx)
 | 
			
		||||
{
 | 
			
		||||
	struct gsm_network *net = (struct gsm_network *) global_ctx;
 | 
			
		||||
	msc_queue_write(net->msc_data->msc_con, msg, IPAC_PROTO_SCCP);
 | 
			
		||||
	struct bsc_msc_connection *msc_con;
 | 
			
		||||
 | 
			
		||||
	if (conn) {
 | 
			
		||||
		struct osmo_bsc_sccp_con *bsc_con = conn->data_ctx;
 | 
			
		||||
		msc_con = bsc_con->msc->msc_con;
 | 
			
		||||
	} else {
 | 
			
		||||
		msc_con = ctx;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	msc_queue_write(msc_con, msg, IPAC_PROTO_SCCP);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int msc_sccp_accept(struct sccp_connection *connection, void *data)
 | 
			
		||||
@@ -152,8 +160,8 @@ static int msc_sccp_accept(struct sccp_connection *connection, void *data)
 | 
			
		||||
 | 
			
		||||
static int msc_sccp_read(struct msgb *msgb, unsigned int length, void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct gsm_network *net = (struct gsm_network *) data;
 | 
			
		||||
	return bsc_handle_udt(net, net->msc_data->msc_con, msgb, length);
 | 
			
		||||
	struct osmo_msc_data *msc = (struct osmo_msc_data *) msgb->cb[0];
 | 
			
		||||
	return bsc_handle_udt(msc, msgb, length);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int bsc_queue_for_msc(struct osmo_bsc_sccp_con *conn, struct msgb *msg)
 | 
			
		||||
@@ -179,15 +187,19 @@ int bsc_queue_for_msc(struct osmo_bsc_sccp_con *conn, struct msgb *msg)
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int bsc_create_new_connection(struct gsm_subscriber_connection *conn)
 | 
			
		||||
int bsc_create_new_connection(struct gsm_subscriber_connection *conn,
 | 
			
		||||
			      struct osmo_msc_data *msc)
 | 
			
		||||
{
 | 
			
		||||
	struct gsm_network *net;
 | 
			
		||||
	struct osmo_bsc_sccp_con *bsc_con;
 | 
			
		||||
	struct sccp_connection *sccp;
 | 
			
		||||
 | 
			
		||||
	net = conn->bts->network;
 | 
			
		||||
	if (!net->msc_data->msc_con->is_authenticated) {
 | 
			
		||||
		LOGP(DMSC, LOGL_ERROR, "Not connected to a MSC. Not forwarding data.\n");
 | 
			
		||||
 | 
			
		||||
	/* This should not trigger */
 | 
			
		||||
	if (!msc->msc_con->is_authenticated) {
 | 
			
		||||
		LOGP(DMSC, LOGL_ERROR,
 | 
			
		||||
		     "How did this happen? MSC is not connected. Dropping.\n");
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -223,9 +235,9 @@ int bsc_create_new_connection(struct gsm_subscriber_connection *conn)
 | 
			
		||||
	INIT_LLIST_HEAD(&bsc_con->sccp_queue);
 | 
			
		||||
 | 
			
		||||
	bsc_con->sccp = sccp;
 | 
			
		||||
	bsc_con->msc_con = net->msc_data->msc_con;
 | 
			
		||||
	bsc_con->msc = msc;
 | 
			
		||||
	bsc_con->conn = conn;
 | 
			
		||||
	llist_add(&bsc_con->entry, &active_connections);
 | 
			
		||||
	llist_add_tail(&bsc_con->entry, &active_connections);
 | 
			
		||||
	conn->sccp_con = bsc_con;
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
@@ -257,8 +269,10 @@ static void bsc_close_connections(struct bsc_msc_connection *msc_con)
 | 
			
		||||
{
 | 
			
		||||
	struct osmo_bsc_sccp_con *con, *tmp;
 | 
			
		||||
 | 
			
		||||
	llist_for_each_entry_safe(con, tmp, &active_connections, entry)
 | 
			
		||||
		bsc_sccp_force_free(con);
 | 
			
		||||
	llist_for_each_entry_safe(con, tmp, &active_connections, entry) {
 | 
			
		||||
		if (con->msc->msc_con == msc_con)
 | 
			
		||||
			bsc_sccp_force_free(con);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int handle_msc_signal(unsigned int subsys, unsigned int signal,
 | 
			
		||||
 
 | 
			
		||||
@@ -29,11 +29,22 @@
 | 
			
		||||
 | 
			
		||||
extern struct gsm_network *bsc_gsmnet;
 | 
			
		||||
 | 
			
		||||
static struct osmo_bsc_data *osmo_bsc_data(struct vty *vty)
 | 
			
		||||
{
 | 
			
		||||
	return bsc_gsmnet->bsc_data;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct osmo_msc_data *osmo_msc_data(struct vty *vty)
 | 
			
		||||
{
 | 
			
		||||
	return bsc_gsmnet->msc_data;
 | 
			
		||||
	return osmo_msc_data_find(bsc_gsmnet, (int) vty->index);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct cmd_node bsc_node = {
 | 
			
		||||
	BSC_NODE,
 | 
			
		||||
	"%s(bsc)#",
 | 
			
		||||
	1,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static struct cmd_node msc_node = {
 | 
			
		||||
	MSC_NODE,
 | 
			
		||||
	"%s(msc)#",
 | 
			
		||||
@@ -41,61 +52,126 @@ static struct cmd_node msc_node = {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_net_msc, cfg_net_msc_cmd,
 | 
			
		||||
      "msc", "Configure MSC details")
 | 
			
		||||
      "msc [<0-1000>]", "Configure MSC details\n" "MSC connection to configure\n")
 | 
			
		||||
{
 | 
			
		||||
	vty->index = bsc_gsmnet;
 | 
			
		||||
	vty->node = MSC_NODE;
 | 
			
		||||
	int index = argc == 1 ? atoi(argv[0]) : 0;
 | 
			
		||||
	struct osmo_msc_data *msc;
 | 
			
		||||
 | 
			
		||||
	msc = osmo_msc_data_alloc(bsc_gsmnet, index);
 | 
			
		||||
	if (!msc) {
 | 
			
		||||
		vty_out(vty, "%%Failed to allocate MSC data.%s", VTY_NEWLINE);
 | 
			
		||||
		return CMD_WARNING;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	vty->index = (void *) index;
 | 
			
		||||
	vty->node = MSC_NODE;
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int config_write_msc(struct vty *vty)
 | 
			
		||||
DEFUN(cfg_net_bsc, cfg_net_bsc_cmd,
 | 
			
		||||
      "bsc", "Configure BSC\n")
 | 
			
		||||
{
 | 
			
		||||
	vty->node = BSC_NODE;
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void write_msc_amr_options(struct vty *vty, struct osmo_msc_data *msc)
 | 
			
		||||
{
 | 
			
		||||
#define WRITE_AMR(vty, msc, name, var) \
 | 
			
		||||
	vty_out(vty, " amr-config %s %s%s", \
 | 
			
		||||
		name, msc->amr_conf.var ? "allowed" : "forbidden", \
 | 
			
		||||
		VTY_NEWLINE);
 | 
			
		||||
 | 
			
		||||
	WRITE_AMR(vty, msc, "12_2k", m12_2);
 | 
			
		||||
	WRITE_AMR(vty, msc, "10_2k", m10_2);
 | 
			
		||||
	WRITE_AMR(vty, msc, "7_95k", m7_95);
 | 
			
		||||
	WRITE_AMR(vty, msc, "7_40k", m7_40);
 | 
			
		||||
	WRITE_AMR(vty, msc, "6_70k", m6_70);
 | 
			
		||||
	WRITE_AMR(vty, msc, "5_90k", m5_90);
 | 
			
		||||
	WRITE_AMR(vty, msc, "5_15k", m5_15);
 | 
			
		||||
	WRITE_AMR(vty, msc, "4_75k", m4_75);
 | 
			
		||||
#undef WRITE_AMR
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void write_msc(struct vty *vty, struct osmo_msc_data *msc)
 | 
			
		||||
{
 | 
			
		||||
	struct bsc_msc_dest *dest;
 | 
			
		||||
	struct osmo_msc_data *data = osmo_msc_data(vty);
 | 
			
		||||
 | 
			
		||||
	vty_out(vty, "msc%s", VTY_NEWLINE);
 | 
			
		||||
	if (data->bsc_token)
 | 
			
		||||
		vty_out(vty, " token %s%s", data->bsc_token, VTY_NEWLINE);
 | 
			
		||||
	if (data->core_ncc != -1)
 | 
			
		||||
	vty_out(vty, "msc %d%s", msc->nr, VTY_NEWLINE);
 | 
			
		||||
	if (msc->bsc_token)
 | 
			
		||||
		vty_out(vty, " token %s%s", msc->bsc_token, VTY_NEWLINE);
 | 
			
		||||
	if (msc->core_ncc != -1)
 | 
			
		||||
		vty_out(vty, " core-mobile-network-code %d%s",
 | 
			
		||||
			data->core_ncc, VTY_NEWLINE);
 | 
			
		||||
	if (data->core_mcc != -1)
 | 
			
		||||
			msc->core_ncc, VTY_NEWLINE);
 | 
			
		||||
	if (msc->core_mcc != -1)
 | 
			
		||||
		vty_out(vty, " core-mobile-country-code %d%s",
 | 
			
		||||
			data->core_mcc, VTY_NEWLINE);
 | 
			
		||||
	vty_out(vty, " ip.access rtp-base %d%s", data->rtp_base, VTY_NEWLINE);
 | 
			
		||||
	vty_out(vty, " timeout-ping %d%s", data->ping_timeout, VTY_NEWLINE);
 | 
			
		||||
	vty_out(vty, " timeout-pong %d%s", data->pong_timeout, VTY_NEWLINE);
 | 
			
		||||
	if (data->mid_call_txt)
 | 
			
		||||
		vty_out(vty, " mid-call-text %s%s", data->mid_call_txt, VTY_NEWLINE);
 | 
			
		||||
	vty_out(vty, " mid-call-timeout %d%s", data->mid_call_timeout, VTY_NEWLINE);
 | 
			
		||||
	if (data->ussd_welcome_txt)
 | 
			
		||||
		vty_out(vty, " bsc-welcome-text %s%s", data->ussd_welcome_txt, VTY_NEWLINE);
 | 
			
		||||
	if (data->rf_ctrl_name)
 | 
			
		||||
		vty_out(vty, " bsc-rf-socket %s%s",
 | 
			
		||||
			data->rf_ctrl_name, VTY_NEWLINE);
 | 
			
		||||
			msc->core_mcc, VTY_NEWLINE);
 | 
			
		||||
	vty_out(vty, " ip.access rtp-base %d%s", msc->rtp_base, VTY_NEWLINE);
 | 
			
		||||
	vty_out(vty, " timeout-ping %d%s", msc->ping_timeout, VTY_NEWLINE);
 | 
			
		||||
	vty_out(vty, " timeout-pong %d%s", msc->pong_timeout, VTY_NEWLINE);
 | 
			
		||||
	if (msc->ussd_welcome_txt)
 | 
			
		||||
		vty_out(vty, " bsc-welcome-text %s%s", msc->ussd_welcome_txt, VTY_NEWLINE);
 | 
			
		||||
 | 
			
		||||
	if (data->audio_length != 0) {
 | 
			
		||||
	if (msc->audio_length != 0) {
 | 
			
		||||
		int i;
 | 
			
		||||
 | 
			
		||||
		vty_out(vty, " codec-list ");
 | 
			
		||||
		for (i = 0; i < data->audio_length; ++i) {
 | 
			
		||||
		for (i = 0; i < msc->audio_length; ++i) {
 | 
			
		||||
			if (i != 0)
 | 
			
		||||
				vty_out(vty, ", ");
 | 
			
		||||
 | 
			
		||||
			if (data->audio_support[i]->hr)
 | 
			
		||||
				vty_out(vty, "hr%.1u", data->audio_support[i]->ver);
 | 
			
		||||
			if (msc->audio_support[i]->hr)
 | 
			
		||||
				vty_out(vty, "hr%.1u", msc->audio_support[i]->ver);
 | 
			
		||||
			else
 | 
			
		||||
				vty_out(vty, "fr%.1u", data->audio_support[i]->ver);
 | 
			
		||||
				vty_out(vty, "fr%.1u", msc->audio_support[i]->ver);
 | 
			
		||||
		}
 | 
			
		||||
		vty_out(vty, "%s", VTY_NEWLINE);
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	llist_for_each_entry(dest, &data->dests, list)
 | 
			
		||||
	llist_for_each_entry(dest, &msc->dests, list)
 | 
			
		||||
		vty_out(vty, " dest %s %d %d%s", dest->ip, dest->port,
 | 
			
		||||
			dest->dscp, VTY_NEWLINE);
 | 
			
		||||
 | 
			
		||||
	vty_out(vty, " type %s%s", msc->type == MSC_CON_TYPE_NORMAL ?
 | 
			
		||||
					"normal" : "local", VTY_NEWLINE);
 | 
			
		||||
	vty_out(vty, " allow-emergency %s%s", msc->allow_emerg ?
 | 
			
		||||
					"allow" : "deny", VTY_NEWLINE);
 | 
			
		||||
 | 
			
		||||
	if (msc->local_pref)
 | 
			
		||||
		vty_out(vty, " local-prefix %s%s", msc->local_pref, VTY_NEWLINE);
 | 
			
		||||
 | 
			
		||||
	/* write amr options */
 | 
			
		||||
	write_msc_amr_options(vty, msc);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int config_write_msc(struct vty *vty)
 | 
			
		||||
{
 | 
			
		||||
	struct osmo_msc_data *msc;
 | 
			
		||||
	struct osmo_bsc_data *bsc = osmo_bsc_data(vty);
 | 
			
		||||
 | 
			
		||||
	llist_for_each_entry(msc, &bsc->mscs, entry)
 | 
			
		||||
		write_msc(vty, msc);
 | 
			
		||||
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int config_write_bsc(struct vty *vty)
 | 
			
		||||
{
 | 
			
		||||
	struct osmo_bsc_data *bsc = osmo_bsc_data(vty);
 | 
			
		||||
 | 
			
		||||
	vty_out(vty, "bsc%s", VTY_NEWLINE);
 | 
			
		||||
	if (bsc->mid_call_txt)
 | 
			
		||||
		vty_out(vty, " mid-call-text %s%s", bsc->mid_call_txt, VTY_NEWLINE);
 | 
			
		||||
	vty_out(vty, " mid-call-timeout %d%s", bsc->mid_call_timeout, VTY_NEWLINE);
 | 
			
		||||
	if (bsc->rf_ctrl_name)
 | 
			
		||||
		vty_out(vty, " bsc-rf-socket %s%s",
 | 
			
		||||
			bsc->rf_ctrl_name, VTY_NEWLINE);
 | 
			
		||||
 | 
			
		||||
	if (bsc->auto_off_timeout != -1)
 | 
			
		||||
		vty_out(vty, " bsc-auto-rf-off %d%s",
 | 
			
		||||
			bsc->auto_off_timeout, VTY_NEWLINE);
 | 
			
		||||
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -106,7 +182,7 @@ DEFUN(cfg_net_bsc_token,
 | 
			
		||||
{
 | 
			
		||||
	struct osmo_msc_data *data = osmo_msc_data(vty);
 | 
			
		||||
 | 
			
		||||
	bsc_replace_string(data, &data->bsc_token, argv[0]);
 | 
			
		||||
	bsc_replace_string(osmo_bsc_data(vty), &data->bsc_token, argv[0]);
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -163,7 +239,7 @@ DEFUN(cfg_net_bsc_codec_list,
 | 
			
		||||
 | 
			
		||||
	/* create a new array */
 | 
			
		||||
	data->audio_support =
 | 
			
		||||
		talloc_zero_array(data, struct gsm_audio_support *, argc);
 | 
			
		||||
		talloc_zero_array(osmo_bsc_data(vty), struct gsm_audio_support *, argc);
 | 
			
		||||
	data->audio_length = argc;
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < argc; ++i) {
 | 
			
		||||
@@ -211,13 +287,13 @@ DEFUN(cfg_net_msc_dest,
 | 
			
		||||
	struct bsc_msc_dest *dest;
 | 
			
		||||
	struct osmo_msc_data *data = osmo_msc_data(vty);
 | 
			
		||||
 | 
			
		||||
	dest = talloc_zero(data, struct bsc_msc_dest);
 | 
			
		||||
	dest = talloc_zero(osmo_bsc_data(vty), struct bsc_msc_dest);
 | 
			
		||||
	if (!dest) {
 | 
			
		||||
		vty_out(vty, "%%Failed to create structure.%s", VTY_NEWLINE);
 | 
			
		||||
		return CMD_WARNING;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	dest->ip = talloc_strdup(data, argv[0]);
 | 
			
		||||
	dest->ip = talloc_strdup(dest, argv[0]);
 | 
			
		||||
	if (!dest->ip) {
 | 
			
		||||
		vty_out(vty, "%%Failed to copy dest ip.%s", VTY_NEWLINE);
 | 
			
		||||
		talloc_free(dest);
 | 
			
		||||
@@ -274,31 +350,6 @@ DEFUN(cfg_net_msc_pong_time,
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_net_msc_mid_call_text,
 | 
			
		||||
      cfg_net_msc_mid_call_text_cmd,
 | 
			
		||||
      "mid-call-text .TEXT",
 | 
			
		||||
      "Set the USSD notifcation to be send.\n" "Text to be sent\n")
 | 
			
		||||
{
 | 
			
		||||
	struct osmo_msc_data *data = osmo_msc_data(vty);
 | 
			
		||||
	char *txt = argv_concat(argv, argc, 0);
 | 
			
		||||
	if (!txt)
 | 
			
		||||
		return CMD_WARNING;
 | 
			
		||||
 | 
			
		||||
	bsc_replace_string(data, &data->mid_call_txt, txt);
 | 
			
		||||
	talloc_free(txt);
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_net_msc_mid_call_timeout,
 | 
			
		||||
      cfg_net_msc_mid_call_timeout_cmd,
 | 
			
		||||
      "mid-call-timeout NR",
 | 
			
		||||
      "Switch from Grace to Off in NR seconds.\n" "Timeout in seconds\n")
 | 
			
		||||
{
 | 
			
		||||
	struct osmo_msc_data *data = osmo_msc_data(vty);
 | 
			
		||||
	data->mid_call_timeout = atoi(argv[0]);
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_net_msc_welcome_ussd,
 | 
			
		||||
      cfg_net_msc_welcome_ussd_cmd,
 | 
			
		||||
      "bsc-welcome-text .TEXT",
 | 
			
		||||
@@ -309,22 +360,132 @@ DEFUN(cfg_net_msc_welcome_ussd,
 | 
			
		||||
	if (!str)
 | 
			
		||||
		return CMD_WARNING;
 | 
			
		||||
 | 
			
		||||
	bsc_replace_string(data, &data->ussd_welcome_txt, str);
 | 
			
		||||
	bsc_replace_string(osmo_bsc_data(vty), &data->ussd_welcome_txt, str);
 | 
			
		||||
	talloc_free(str);
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_net_msc_type,
 | 
			
		||||
      cfg_net_msc_type_cmd,
 | 
			
		||||
      "type (normal|local)",
 | 
			
		||||
      "Select the MSC type\n"
 | 
			
		||||
      "Plain GSM MSC\n" "Special MSC for local call routing\n")
 | 
			
		||||
{
 | 
			
		||||
	struct osmo_msc_data *data = osmo_msc_data(vty);
 | 
			
		||||
 | 
			
		||||
	if (strcmp(argv[0], "normal") == 0)
 | 
			
		||||
		data->type = MSC_CON_TYPE_NORMAL;
 | 
			
		||||
	else if (strcmp(argv[0], "local") == 0)
 | 
			
		||||
		data->type = MSC_CON_TYPE_LOCAL;
 | 
			
		||||
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_net_msc_emerg,
 | 
			
		||||
      cfg_net_msc_emerg_cmd,
 | 
			
		||||
      "allow-emergency (allow|deny)",
 | 
			
		||||
      "Allow CM ServiceRequests with type emergency\n"
 | 
			
		||||
      "Allow\n" "Deny\n")
 | 
			
		||||
{
 | 
			
		||||
	struct osmo_msc_data *data = osmo_msc_data(vty);
 | 
			
		||||
	data->allow_emerg = strcmp("allow", argv[0]) == 0;
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_net_msc_local_prefix,
 | 
			
		||||
      cfg_net_msc_local_prefix_cmd,
 | 
			
		||||
      "local-prefix REGEXP",
 | 
			
		||||
      "Prefix for local numbers\n" "REGEXP used\n")
 | 
			
		||||
{
 | 
			
		||||
	struct osmo_msc_data *msc = osmo_msc_data(vty);
 | 
			
		||||
 | 
			
		||||
	if (gsm_parse_reg(msc, &msc->local_pref_reg, &msc->local_pref, argc, argv) != 0) {
 | 
			
		||||
		vty_out(vty, "%%Failed to parse the regexp: '%s'%s",
 | 
			
		||||
			argv[0], VTY_NEWLINE);
 | 
			
		||||
		return CMD_WARNING;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define AMR_CONF_STR "AMR Multirate Configuration\n"
 | 
			
		||||
#define AMR_COMMAND(name) \
 | 
			
		||||
	DEFUN(cfg_net_msc_amr_##name,					\
 | 
			
		||||
	  cfg_net_msc_amr_##name##_cmd,					\
 | 
			
		||||
	  "amr-config " #name "k (allowed|forbidden)",			\
 | 
			
		||||
	  AMR_CONF_STR "Bitrate\n" "Allowed\n" "Forbidden\n")		\
 | 
			
		||||
{									\
 | 
			
		||||
	struct osmo_msc_data *msc = osmo_msc_data(vty);			\
 | 
			
		||||
									\
 | 
			
		||||
	msc->amr_conf.m##name = strcmp(argv[0], "allowed") == 0; 	\
 | 
			
		||||
	return CMD_SUCCESS;						\
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AMR_COMMAND(12_2)
 | 
			
		||||
AMR_COMMAND(10_2)
 | 
			
		||||
AMR_COMMAND(7_95)
 | 
			
		||||
AMR_COMMAND(7_40)
 | 
			
		||||
AMR_COMMAND(6_70)
 | 
			
		||||
AMR_COMMAND(5_90)
 | 
			
		||||
AMR_COMMAND(5_15)
 | 
			
		||||
AMR_COMMAND(4_75)
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_net_bsc_mid_call_text,
 | 
			
		||||
      cfg_net_bsc_mid_call_text_cmd,
 | 
			
		||||
      "mid-call-text .TEXT",
 | 
			
		||||
      "Set the USSD notifcation to be send.\n" "Text to be sent\n")
 | 
			
		||||
{
 | 
			
		||||
	struct osmo_bsc_data *data = osmo_bsc_data(vty);
 | 
			
		||||
	char *txt = argv_concat(argv, argc, 0);
 | 
			
		||||
	if (!txt)
 | 
			
		||||
		return CMD_WARNING;
 | 
			
		||||
 | 
			
		||||
	bsc_replace_string(data, &data->mid_call_txt, txt);
 | 
			
		||||
	talloc_free(txt);
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_net_bsc_mid_call_timeout,
 | 
			
		||||
      cfg_net_bsc_mid_call_timeout_cmd,
 | 
			
		||||
      "mid-call-timeout NR",
 | 
			
		||||
      "Switch from Grace to Off in NR seconds.\n" "Timeout in seconds\n")
 | 
			
		||||
{
 | 
			
		||||
	struct osmo_bsc_data *data = osmo_bsc_data(vty);
 | 
			
		||||
	data->mid_call_timeout = atoi(argv[0]);
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_net_rf_socket,
 | 
			
		||||
      cfg_net_rf_socket_cmd,
 | 
			
		||||
      "bsc-rf-socket PATH",
 | 
			
		||||
      "Set the filename for the RF control interface.\n" "RF Control path\n")
 | 
			
		||||
{
 | 
			
		||||
	struct osmo_msc_data *data = osmo_msc_data(vty);
 | 
			
		||||
	struct osmo_bsc_data *data = osmo_bsc_data(vty);
 | 
			
		||||
 | 
			
		||||
	bsc_replace_string(data, &data->rf_ctrl_name, argv[0]);
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_net_rf_off_time,
 | 
			
		||||
      cfg_net_rf_off_time_cmd,
 | 
			
		||||
      "bsc-auto-rf-off <1-65000>",
 | 
			
		||||
      "Disable RF on MSC Connection\n" "Timeout\n")
 | 
			
		||||
{
 | 
			
		||||
	struct osmo_bsc_data *data = osmo_bsc_data(vty);
 | 
			
		||||
	data->auto_off_timeout = atoi(argv[0]);
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_net_no_rf_off_time,
 | 
			
		||||
      cfg_net_no_rf_off_time_cmd,
 | 
			
		||||
      "no bsc-auto-rf-off",
 | 
			
		||||
      NO_STR "Disable RF on MSC Connection\n")
 | 
			
		||||
{
 | 
			
		||||
	struct osmo_bsc_data *data = osmo_bsc_data(vty);
 | 
			
		||||
	data->auto_off_timeout = -1;
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN(show_statistics,
 | 
			
		||||
      show_statistics_cmd,
 | 
			
		||||
      "show statistics",
 | 
			
		||||
@@ -337,6 +498,16 @@ DEFUN(show_statistics,
 | 
			
		||||
int bsc_vty_init_extra(void)
 | 
			
		||||
{
 | 
			
		||||
	install_element(CONFIG_NODE, &cfg_net_msc_cmd);
 | 
			
		||||
	install_element(CONFIG_NODE, &cfg_net_bsc_cmd);
 | 
			
		||||
 | 
			
		||||
	install_node(&bsc_node, config_write_bsc);
 | 
			
		||||
	install_default(BSC_NODE);
 | 
			
		||||
	install_element(BSC_NODE, &cfg_net_bsc_mid_call_text_cmd);
 | 
			
		||||
	install_element(BSC_NODE, &cfg_net_bsc_mid_call_timeout_cmd);
 | 
			
		||||
	install_element(BSC_NODE, &cfg_net_rf_socket_cmd);
 | 
			
		||||
	install_element(BSC_NODE, &cfg_net_rf_off_time_cmd);
 | 
			
		||||
	install_element(BSC_NODE, &cfg_net_no_rf_off_time_cmd);
 | 
			
		||||
 | 
			
		||||
	install_node(&msc_node, config_write_msc);
 | 
			
		||||
	install_default(MSC_NODE);
 | 
			
		||||
	install_element(MSC_NODE, &cfg_net_bsc_token_cmd);
 | 
			
		||||
@@ -348,10 +519,18 @@ int bsc_vty_init_extra(void)
 | 
			
		||||
	install_element(MSC_NODE, &cfg_net_msc_no_dest_cmd);
 | 
			
		||||
	install_element(MSC_NODE, &cfg_net_msc_ping_time_cmd);
 | 
			
		||||
	install_element(MSC_NODE, &cfg_net_msc_pong_time_cmd);
 | 
			
		||||
	install_element(MSC_NODE, &cfg_net_msc_mid_call_text_cmd);
 | 
			
		||||
	install_element(MSC_NODE, &cfg_net_msc_mid_call_timeout_cmd);
 | 
			
		||||
	install_element(MSC_NODE, &cfg_net_msc_welcome_ussd_cmd);
 | 
			
		||||
	install_element(MSC_NODE, &cfg_net_rf_socket_cmd);
 | 
			
		||||
	install_element(MSC_NODE, &cfg_net_msc_type_cmd);
 | 
			
		||||
	install_element(MSC_NODE, &cfg_net_msc_emerg_cmd);
 | 
			
		||||
	install_element(MSC_NODE, &cfg_net_msc_local_prefix_cmd);
 | 
			
		||||
	install_element(MSC_NODE, &cfg_net_msc_amr_12_2_cmd);
 | 
			
		||||
	install_element(MSC_NODE, &cfg_net_msc_amr_10_2_cmd);
 | 
			
		||||
	install_element(MSC_NODE, &cfg_net_msc_amr_7_95_cmd);
 | 
			
		||||
	install_element(MSC_NODE, &cfg_net_msc_amr_7_40_cmd);
 | 
			
		||||
	install_element(MSC_NODE, &cfg_net_msc_amr_6_70_cmd);
 | 
			
		||||
	install_element(MSC_NODE, &cfg_net_msc_amr_5_90_cmd);
 | 
			
		||||
	install_element(MSC_NODE, &cfg_net_msc_amr_5_15_cmd);
 | 
			
		||||
	install_element(MSC_NODE, &cfg_net_msc_amr_4_75_cmd);
 | 
			
		||||
 | 
			
		||||
	install_element_ve(&show_statistics_cmd);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1204,11 +1204,8 @@ static int handle_ctrlif_msg(struct bsc_connection *bsc, struct msgb *msg)
 | 
			
		||||
 | 
			
		||||
	if (bsc->cfg && !llist_empty(&bsc->cfg->lac_list)) {
 | 
			
		||||
		if (cmd->variable) {
 | 
			
		||||
			struct bsc_lac_entry *bsc_lac;
 | 
			
		||||
			bsc_lac = llist_entry(bsc->cfg->lac_list.next,
 | 
			
		||||
					      struct bsc_lac_entry, entry);
 | 
			
		||||
			var = talloc_asprintf(cmd, "bsc.%i.%s", bsc_lac->lac,
 | 
			
		||||
					      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";
 | 
			
		||||
@@ -1218,6 +1215,13 @@ static int handle_ctrlif_msg(struct bsc_connection *bsc, struct msgb *msg)
 | 
			
		||||
			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) {
 | 
			
		||||
@@ -1529,34 +1533,39 @@ static struct vty_app_info vty_info = {
 | 
			
		||||
	.is_config_node	= bsc_vty_is_config_node,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define NAT_MAX_CTRL_ID 65535
 | 
			
		||||
 | 
			
		||||
static int get_next_free_bsc_id(struct bsc_connection *bsc)
 | 
			
		||||
{
 | 
			
		||||
	int new_id, overflow = 0;
 | 
			
		||||
	struct bsc_cmd_list *pending;
 | 
			
		||||
 | 
			
		||||
	new_id = bsc->last_id;
 | 
			
		||||
 | 
			
		||||
	do {
 | 
			
		||||
		new_id++;
 | 
			
		||||
		if (new_id <= 0) {
 | 
			
		||||
		if (new_id == NAT_MAX_CTRL_ID) {
 | 
			
		||||
			new_id = 1;
 | 
			
		||||
			overflow++;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		llist_for_each_entry(pending, &bsc->cmd_pending, list_entry) {
 | 
			
		||||
			if (pending->nat_id == new_id)
 | 
			
		||||
				continue;
 | 
			
		||||
		if (bsc_id_unused(new_id, bsc)) {
 | 
			
		||||
			bsc->last_id = new_id;
 | 
			
		||||
			return new_id;
 | 
			
		||||
		}
 | 
			
		||||
	} while (overflow != 2);
 | 
			
		||||
 | 
			
		||||
		/* ID is not in use */
 | 
			
		||||
		break;
 | 
			
		||||
	} while ((new_id != bsc->last_id) && (overflow < 2));
 | 
			
		||||
 | 
			
		||||
	if ((new_id == bsc->last_id) || (overflow == 2)) {
 | 
			
		||||
		return -1;
 | 
			
		||||
	} else {
 | 
			
		||||
		bsc->last_id = new_id;
 | 
			
		||||
		return new_id;
 | 
			
		||||
	}
 | 
			
		||||
	return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void pending_timeout_cb(void *data)
 | 
			
		||||
@@ -1589,17 +1598,19 @@ static int forward_to_bsc(struct ctrl_cmd *cmd)
 | 
			
		||||
	struct ctrl_cmd *bsc_cmd = NULL;
 | 
			
		||||
	struct bsc_connection *bsc;
 | 
			
		||||
	struct bsc_cmd_list *pending;
 | 
			
		||||
	unsigned int lac;
 | 
			
		||||
	char *lac_str, *tmp, *saveptr;
 | 
			
		||||
	unsigned int nr;
 | 
			
		||||
	char *nr_str, *tmp, *saveptr;
 | 
			
		||||
 | 
			
		||||
	/* Skip over the beginning (bsc.) */
 | 
			
		||||
	tmp = strtok_r(cmd->variable, ".", &saveptr);
 | 
			
		||||
	lac_str = strtok_r(NULL, ".", &saveptr);
 | 
			
		||||
	if (!lac_str) {
 | 
			
		||||
	tmp = strtok_r(NULL, ".", &saveptr);
 | 
			
		||||
	tmp = strtok_r(NULL, ".", &saveptr);
 | 
			
		||||
	nr_str = strtok_r(NULL, ".", &saveptr);
 | 
			
		||||
	if (!nr_str) {
 | 
			
		||||
		cmd->reply = "command incomplete";
 | 
			
		||||
		goto err;
 | 
			
		||||
	}
 | 
			
		||||
	lac = atoi(lac_str);
 | 
			
		||||
	nr = atoi(nr_str);
 | 
			
		||||
 | 
			
		||||
	tmp = strtok_r(NULL, "\0", &saveptr);
 | 
			
		||||
	if (!tmp) {
 | 
			
		||||
@@ -1612,7 +1623,7 @@ static int forward_to_bsc(struct ctrl_cmd *cmd)
 | 
			
		||||
			continue;
 | 
			
		||||
		if (!bsc->authenticated)
 | 
			
		||||
			continue;
 | 
			
		||||
		if (bsc_config_handles_lac(bsc->cfg, lac)) {
 | 
			
		||||
		if (bsc->cfg->nr == nr) {
 | 
			
		||||
			/* Add pending command to list */
 | 
			
		||||
			pending = talloc_zero(bsc, struct bsc_cmd_list);
 | 
			
		||||
			if (!pending) {
 | 
			
		||||
@@ -1665,7 +1676,7 @@ static int forward_to_bsc(struct ctrl_cmd *cmd)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	/* We end up here if there's no bsc to handle our LAC */
 | 
			
		||||
	cmd->reply = "no BSC with this LAC";
 | 
			
		||||
	cmd->reply = "no BSC with this nr";
 | 
			
		||||
err:
 | 
			
		||||
	ret = CTRL_CMD_ERROR;
 | 
			
		||||
done:
 | 
			
		||||
@@ -1675,18 +1686,18 @@ done:
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
CTRL_CMD_DEFINE(fwd_cmd, "bsc *");
 | 
			
		||||
int get_fwd_cmd(struct ctrl_cmd *cmd, void *data)
 | 
			
		||||
CTRL_CMD_DEFINE(fwd_cmd, "net 0 bsc *");
 | 
			
		||||
static int get_fwd_cmd(struct ctrl_cmd *cmd, void *data)
 | 
			
		||||
{
 | 
			
		||||
	return forward_to_bsc(cmd);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int set_fwd_cmd(struct ctrl_cmd *cmd, void *data)
 | 
			
		||||
static int set_fwd_cmd(struct ctrl_cmd *cmd, void *data)
 | 
			
		||||
{
 | 
			
		||||
	return forward_to_bsc(cmd);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int verify_fwd_cmd(struct ctrl_cmd *cmd, const char *value, void *data)
 | 
			
		||||
static int verify_fwd_cmd(struct ctrl_cmd *cmd, const char *value, void *data)
 | 
			
		||||
{
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
@@ -1750,8 +1761,17 @@ int main(int argc, char **argv)
 | 
			
		||||
		exit(1);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	controlif_setup(NULL, 4250);
 | 
			
		||||
	ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_fwd_cmd);
 | 
			
		||||
	nat->ctrl = controlif_setup(NULL, 4250);
 | 
			
		||||
	if (!nat->ctrl) {
 | 
			
		||||
		fprintf(stderr, "Failed to initialize the control interface. Exiting.\n");
 | 
			
		||||
		exit(1);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rc = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_fwd_cmd);
 | 
			
		||||
	if (rc) {
 | 
			
		||||
		fprintf(stderr, "Failed to install the control command. Exiting.\n");
 | 
			
		||||
		exit(1);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	nat->msc_con->connection_loss = msc_connection_was_lost;
 | 
			
		||||
	nat->msc_con->connected = msc_connection_connected;
 | 
			
		||||
 
 | 
			
		||||
@@ -672,31 +672,6 @@ int bsc_nat_filter_dt(struct bsc_connection *bsc, struct msgb *msg,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int bsc_parse_reg(void *ctx, regex_t *reg, char **imsi, int argc, const char **argv)
 | 
			
		||||
{
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	ret = 0;
 | 
			
		||||
	if (*imsi) {
 | 
			
		||||
		talloc_free(*imsi);
 | 
			
		||||
		*imsi = NULL;
 | 
			
		||||
	}
 | 
			
		||||
	regfree(reg);
 | 
			
		||||
 | 
			
		||||
	if (argc > 0) {
 | 
			
		||||
		*imsi = talloc_strdup(ctx, argv[0]);
 | 
			
		||||
		ret = regcomp(reg, argv[0], 0);
 | 
			
		||||
 | 
			
		||||
		/* handle compilation failures */
 | 
			
		||||
		if (ret != 0) {
 | 
			
		||||
			talloc_free(*imsi);
 | 
			
		||||
			*imsi = NULL;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const char *con_types [] = {
 | 
			
		||||
	[NAT_CON_TYPE_NONE] = "n/a",
 | 
			
		||||
	[NAT_CON_TYPE_LU] = "Location Update",
 | 
			
		||||
 
 | 
			
		||||
@@ -19,6 +19,7 @@
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <openbsc/vty.h>
 | 
			
		||||
#include <openbsc/gsm_data.h>
 | 
			
		||||
#include <openbsc/bsc_nat.h>
 | 
			
		||||
#include <openbsc/bsc_nat_sccp.h>
 | 
			
		||||
#include <openbsc/bsc_msc.h>
 | 
			
		||||
@@ -527,7 +528,7 @@ DEFUN(cfg_nat_ussd_query,
 | 
			
		||||
      "Set the USSD query to match with the ussd-list-name\n"
 | 
			
		||||
      "The query to match")
 | 
			
		||||
{
 | 
			
		||||
	if (bsc_parse_reg(_nat, &_nat->ussd_query_re, &_nat->ussd_query, argc, argv) != 0)
 | 
			
		||||
	if (gsm_parse_reg(_nat, &_nat->ussd_query_re, &_nat->ussd_query, argc, argv) != 0)
 | 
			
		||||
		return CMD_WARNING;
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
@@ -641,7 +642,7 @@ DEFUN(cfg_lst_imsi_allow,
 | 
			
		||||
	if (!entry)
 | 
			
		||||
		return CMD_WARNING;
 | 
			
		||||
 | 
			
		||||
	if (bsc_parse_reg(acc, &entry->imsi_allow_re, &entry->imsi_allow, argc - 1, &argv[1]) != 0)
 | 
			
		||||
	if (gsm_parse_reg(acc, &entry->imsi_allow_re, &entry->imsi_allow, argc - 1, &argv[1]) != 0)
 | 
			
		||||
		return CMD_WARNING;
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
@@ -664,7 +665,7 @@ DEFUN(cfg_lst_imsi_deny,
 | 
			
		||||
	if (!entry)
 | 
			
		||||
		return CMD_WARNING;
 | 
			
		||||
 | 
			
		||||
	if (bsc_parse_reg(acc, &entry->imsi_deny_re, &entry->imsi_deny, argc - 1, &argv[1]) != 0)
 | 
			
		||||
	if (gsm_parse_reg(acc, &entry->imsi_deny_re, &entry->imsi_deny, argc - 1, &argv[1]) != 0)
 | 
			
		||||
		return CMD_WARNING;
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
@@ -797,7 +798,7 @@ DEFUN(test_regex, test_regex_cmd,
 | 
			
		||||
	char *str = NULL;
 | 
			
		||||
 | 
			
		||||
	memset(®, 0, sizeof(reg));
 | 
			
		||||
	if (bsc_parse_reg(_nat, ®, &str, 1, argv) != 0)
 | 
			
		||||
	if (gsm_parse_reg(_nat, ®, &str, 1, argv) != 0)
 | 
			
		||||
		return CMD_WARNING;
 | 
			
		||||
 | 
			
		||||
	vty_out(vty, "String matches allow pattern: %d%s",
 | 
			
		||||
 
 | 
			
		||||
@@ -41,6 +41,7 @@
 | 
			
		||||
#include <openbsc/vty.h>
 | 
			
		||||
#include <openbsc/bss.h>
 | 
			
		||||
#include <openbsc/mncc.h>
 | 
			
		||||
#include <openbsc/control_cmd.h>
 | 
			
		||||
 | 
			
		||||
#include "../../bscconfig.h"
 | 
			
		||||
 | 
			
		||||
@@ -250,7 +251,12 @@ int main(int argc, char **argv)
 | 
			
		||||
		exit(1);
 | 
			
		||||
	bsc_api_init(bsc_gsmnet, msc_bsc_api());
 | 
			
		||||
 | 
			
		||||
	controlif_setup(bsc_gsmnet, 4249);
 | 
			
		||||
	bsc_gsmnet->ctrl = controlif_setup(bsc_gsmnet, 4249);
 | 
			
		||||
	if (!bsc_gsmnet->ctrl) {
 | 
			
		||||
		printf("Failed to initialize control interface. Exiting.\n");
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* seed the PRNG */
 | 
			
		||||
	srand(time(NULL));
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -745,15 +745,15 @@ static void test_cr_filter()
 | 
			
		||||
		nat_lst = bsc_nat_acc_lst_get(nat, "nat");
 | 
			
		||||
		bsc_lst = bsc_nat_acc_lst_get(nat, "bsc");
 | 
			
		||||
 | 
			
		||||
		if (bsc_parse_reg(nat_entry, &nat_entry->imsi_deny_re, &nat_entry->imsi_deny,
 | 
			
		||||
		if (gsm_parse_reg(nat_entry, &nat_entry->imsi_deny_re, &nat_entry->imsi_deny,
 | 
			
		||||
			      cr_filter[i].nat_imsi_deny ? 1 : 0,
 | 
			
		||||
			      &cr_filter[i].nat_imsi_deny) != 0)
 | 
			
		||||
			abort();
 | 
			
		||||
		if (bsc_parse_reg(bsc_entry, &bsc_entry->imsi_allow_re, &bsc_entry->imsi_allow,
 | 
			
		||||
		if (gsm_parse_reg(bsc_entry, &bsc_entry->imsi_allow_re, &bsc_entry->imsi_allow,
 | 
			
		||||
			      cr_filter[i].bsc_imsi_allow ? 1 : 0,
 | 
			
		||||
			      &cr_filter[i].bsc_imsi_allow) != 0)
 | 
			
		||||
			abort();
 | 
			
		||||
		if (bsc_parse_reg(bsc_entry, &bsc_entry->imsi_deny_re, &bsc_entry->imsi_deny,
 | 
			
		||||
		if (gsm_parse_reg(bsc_entry, &bsc_entry->imsi_deny_re, &bsc_entry->imsi_deny,
 | 
			
		||||
			      cr_filter[i].bsc_imsi_deny ? 1 : 0,
 | 
			
		||||
			      &cr_filter[i].bsc_imsi_deny) != 0)
 | 
			
		||||
			abort();
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user