mirror of
https://gitea.osmocom.org/cellular-infrastructure/osmo-mgw.git
synced 2025-10-23 08:12:01 +00:00
Compare commits
74 Commits
stsp/show_
...
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