Merge branch 'zecke/osmo-bsc'
This commit is contained in:
commit
e19d6fc1ee
|
@ -62,6 +62,20 @@ AC_COMPILE_IFELSE([char foo;],
|
|||
CFLAGS="$saved_CFLAGS"
|
||||
AC_SUBST(SYMBOL_VISIBILITY)
|
||||
|
||||
# Coverage build taken from WebKit's configure.in
|
||||
AC_MSG_CHECKING([whether to enable code coverage support])
|
||||
AC_ARG_ENABLE(coverage,
|
||||
AC_HELP_STRING([--enable-coverage],
|
||||
[enable code coverage support [default=no]]),
|
||||
[],[enable_coverage="no"])
|
||||
AC_MSG_RESULT([$enable_coverage])
|
||||
if test "$enable_coverage" = "yes"; then
|
||||
COVERAGE_CFLAGS="-ftest-coverage -fprofile-arcs"
|
||||
COVERAGE_LDFLAGS="-ftest-coverage -fprofile-arcs"
|
||||
AC_SUBST([COVERAGE_CFLAGS])
|
||||
AC_SUBST([COVERAGE_LDFLAGS])
|
||||
fi
|
||||
|
||||
|
||||
dnl Generate the output
|
||||
AM_CONFIG_HEADER(bscconfig.h)
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
Some crazy scripts call testing... and MSC link failure simulation
|
|
@ -0,0 +1,8 @@
|
|||
ABORT BUSY
|
||||
ABORT 'NO CARRIER'
|
||||
ABORT 'OK'
|
||||
|
||||
'' AT
|
||||
SAY "Dialing a number\n"
|
||||
'OK' ATD05660066;
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
#!/bin/sh
|
||||
# Evil dial script..
|
||||
|
||||
while true;
|
||||
do
|
||||
chat -v -f all_dial < /dev/ttyACM0 > /dev/ttyACM0
|
||||
sleep 5s
|
||||
chat -v -f hangup < /dev/ttyACM0 > /dev/ttyACM0
|
||||
sleep 2s
|
||||
done
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
TIMEOUT 10
|
||||
'' ^Z
|
||||
SAY "Waiting for hangup confirm\n"
|
||||
'' ATH;
|
|
@ -0,0 +1,8 @@
|
|||
#!/bin/sh
|
||||
|
||||
while true;
|
||||
do
|
||||
echo "Kill the osmo-bsc"
|
||||
/usr/bin/kill -s SIGUSR2 `pidof osmo-bsc`
|
||||
sleep 58s
|
||||
done
|
|
@ -12,25 +12,24 @@ struct bsc_api {
|
|||
void (*sapi_n_reject)(struct gsm_subscriber_connection *conn, int dlci);
|
||||
void (*cipher_mode_compl)(struct gsm_subscriber_connection *conn,
|
||||
struct msgb *msg, uint8_t chosen_encr);
|
||||
void (*cipher_mode_reject)(struct gsm_subscriber_connection *conn,
|
||||
struct msgb *msg, uint16_t reason);
|
||||
int (*compl_l3)(struct gsm_subscriber_connection *conn,
|
||||
struct msgb *msg, uint16_t chosen_channel);
|
||||
void (*dtap)(struct gsm_subscriber_connection *conn, struct msgb *msg);
|
||||
void (*dtap)(struct gsm_subscriber_connection *conn, uint8_t link_id,
|
||||
struct msgb *msg);
|
||||
void (*assign_compl)(struct gsm_subscriber_connection *conn,
|
||||
uint16_t rr_cause);
|
||||
uint8_t rr_cause, uint8_t chosen_channel,
|
||||
uint8_t encr_alg_id, uint8_t speech_mode);
|
||||
void (*assign_fail)(struct gsm_subscriber_connection *conn,
|
||||
uint16_t rr_cause);
|
||||
void (*clear_request)(struct gsm_subscriber_connection *conn,
|
||||
uint8_t cause, uint8_t *rr_cause);
|
||||
int (*clear_request)(struct gsm_subscriber_connection *conn,
|
||||
uint32_t cause);
|
||||
void (*clear_compl)(struct gsm_subscriber_connection *conn);
|
||||
};
|
||||
|
||||
int bsc_api_init(struct gsm_network *network, struct bsc_api *api);
|
||||
int gsm0808_submit_dtap(struct gsm_subscriber_connection *conn, struct msgb *msg, int link_id);
|
||||
int gsm0808_assign_req(struct gsm_subscriber_connection *conn, int chan_type, int audio);
|
||||
int gsm0808_submit_dtap(struct gsm_subscriber_connection *conn, struct msgb *msg, int link_id, int allow_sach);
|
||||
int gsm0808_assign_req(struct gsm_subscriber_connection *conn, int chan_mode, int full_rate);
|
||||
int gsm0808_cipher_mode(struct gsm_subscriber_connection *conn, int cipher,
|
||||
uint8_t *key, int len);
|
||||
const uint8_t *key, int len, int include_imeisv);
|
||||
int gsm0808_page(struct gsm_bts *bts, unsigned int page_group,
|
||||
unsigned int mi_len, uint8_t *mi, int chan_type);
|
||||
int gsm0808_clear(struct gsm_subscriber_connection *conn);
|
||||
|
|
|
@ -68,6 +68,9 @@ enum gsm_chreq_reason_t {
|
|||
#define HARDCODED_BTS1_TS 6
|
||||
#define HARDCODED_BTS2_TS 11
|
||||
|
||||
/* reserved according to GSM 03.03 § 2.4 */
|
||||
#define GSM_RESERVED_TMSI 0xFFFFFFFF
|
||||
|
||||
enum gsm_hooks {
|
||||
GSM_HOOK_NM_SWLOAD,
|
||||
GSM_HOOK_RR_PAGING,
|
||||
|
@ -252,6 +255,11 @@ struct gsm_subscriber_connection {
|
|||
struct gsm_lchan *lchan;
|
||||
struct gsm_lchan *ho_lchan;
|
||||
struct gsm_bts *bts;
|
||||
|
||||
/* for assignment handling */
|
||||
struct timer_list T10;
|
||||
struct gsm_lchan *secondary_lchan;
|
||||
|
||||
};
|
||||
|
||||
struct gsm_lchan {
|
||||
|
@ -428,28 +436,6 @@ enum gsm_bts_features {
|
|||
BTS_FEAT_HOPPING,
|
||||
};
|
||||
|
||||
/**
|
||||
* A pending paging request
|
||||
*/
|
||||
struct gsm_paging_request {
|
||||
/* list_head for list of all paging requests */
|
||||
struct llist_head entry;
|
||||
/* the subscriber which we're paging. Later gsm_paging_request
|
||||
* should probably become a part of the gsm_subscriber struct? */
|
||||
struct gsm_subscriber *subscr;
|
||||
/* back-pointer to the BTS on which we are paging */
|
||||
struct gsm_bts *bts;
|
||||
/* what kind of channel type do we ask the MS to establish */
|
||||
int chan_type;
|
||||
|
||||
/* Timer 3113: how long do we try to page? */
|
||||
struct timer_list T3113;
|
||||
|
||||
/* callback to be called in case paging completes */
|
||||
gsm_cbfn *cbfn;
|
||||
void *cbfn_param;
|
||||
};
|
||||
|
||||
/*
|
||||
* This keeps track of the paging status of one BTS. It
|
||||
* includes a number of pending requests, a back pointer
|
||||
|
@ -732,6 +718,7 @@ struct gsm_network {
|
|||
|
||||
/* MSC data in case we are a true BSC */
|
||||
struct osmo_msc_data *msc_data;
|
||||
int hardcoded_rtp_payload;
|
||||
};
|
||||
|
||||
#define SMS_HDR_SIZE 128
|
||||
|
|
|
@ -13,10 +13,6 @@
|
|||
#define GSM_MIN_EXTEN 20000
|
||||
#define GSM_MAX_EXTEN 49999
|
||||
|
||||
/* reserved according to GSM 03.03 § 2.4 */
|
||||
#define GSM_RESERVED_TMSI 0xFFFFFFFF
|
||||
|
||||
|
||||
#define GSM_SUBSCRIBER_FIRST_CONTACT 0x00000001
|
||||
#define tmsi_from_string(str) strtoul(str, NULL, 10)
|
||||
|
||||
|
@ -81,10 +77,16 @@ struct gsm_subscriber *subscr_get_by_extension(struct gsm_network *net,
|
|||
const char *ext);
|
||||
struct gsm_subscriber *subscr_get_by_id(struct gsm_network *net,
|
||||
unsigned long long id);
|
||||
struct gsm_subscriber *subscr_get_or_create(struct gsm_network *net,
|
||||
const char *imsi);
|
||||
int subscr_update(struct gsm_subscriber *s, struct gsm_bts *bts, int reason);
|
||||
void subscr_put_channel(struct gsm_subscriber_connection *conn);
|
||||
void subscr_get_channel(struct gsm_subscriber *subscr,
|
||||
int type, gsm_cbfn *cbfn, void *param);
|
||||
struct gsm_subscriber *subscr_active_by_tmsi(struct gsm_network *net,
|
||||
uint32_t tmsi);
|
||||
struct gsm_subscriber *subscr_active_by_imsi(struct gsm_network *net,
|
||||
const char *imsi);
|
||||
|
||||
char *subscr_name(struct gsm_subscriber *subscr);
|
||||
|
||||
|
|
|
@ -5,6 +5,39 @@
|
|||
|
||||
#include "bsc_api.h"
|
||||
|
||||
struct sccp_connection;
|
||||
|
||||
struct osmo_bsc_sccp_con {
|
||||
struct llist_head entry;
|
||||
|
||||
int ciphering_handled;
|
||||
int rtp_port;
|
||||
|
||||
/* SCCP connection realted */
|
||||
struct sccp_connection *sccp;
|
||||
struct bsc_msc_connection *msc_con;
|
||||
struct timer_list sccp_it_timeout;
|
||||
struct timer_list sccp_cc_timeout;
|
||||
|
||||
struct llist_head sccp_queue;
|
||||
unsigned int sccp_queue_size;
|
||||
|
||||
struct gsm_subscriber_connection *conn;
|
||||
uint8_t new_subscriber;
|
||||
};
|
||||
|
||||
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_delete_connection(struct osmo_bsc_sccp_con *sccp);
|
||||
|
||||
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_dt1(struct osmo_bsc_sccp_con *conn, struct msgb *msg, unsigned int len);
|
||||
|
||||
|
||||
#endif
|
||||
|
|
|
@ -29,10 +29,11 @@
|
|||
#include <osmocore/timer.h>
|
||||
|
||||
struct osmo_bsc_rf;
|
||||
struct gsm_network;
|
||||
|
||||
struct gsm_audio_support {
|
||||
u_int8_t hr : 1,
|
||||
ver : 7;
|
||||
uint8_t hr : 1,
|
||||
ver : 7;
|
||||
};
|
||||
|
||||
struct osmo_msc_data {
|
||||
|
@ -47,7 +48,7 @@ struct osmo_msc_data {
|
|||
struct timer_list pong_timer;
|
||||
struct bsc_msc_connection *msc_con;
|
||||
int core_ncc;
|
||||
int rtp_payload;
|
||||
int core_mcc;
|
||||
int rtp_base;
|
||||
|
||||
/* audio codecs */
|
||||
|
@ -61,10 +62,15 @@ struct osmo_msc_data {
|
|||
/* rf ctl related bits */
|
||||
char *ussd_grace_txt;
|
||||
struct osmo_bsc_rf *rf_ctl;
|
||||
|
||||
/* ussd welcome text */
|
||||
char *ussd_welcome_txt;
|
||||
};
|
||||
|
||||
int osmo_bsc_msc_init(struct gsm_network *network);
|
||||
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);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -29,6 +29,28 @@
|
|||
#include "gsm_subscriber.h"
|
||||
#include <osmocore/timer.h>
|
||||
|
||||
/**
|
||||
* A pending paging request
|
||||
*/
|
||||
struct gsm_paging_request {
|
||||
/* list_head for list of all paging requests */
|
||||
struct llist_head entry;
|
||||
/* the subscriber which we're paging. Later gsm_paging_request
|
||||
* should probably become a part of the gsm_subscriber struct? */
|
||||
struct gsm_subscriber *subscr;
|
||||
/* back-pointer to the BTS on which we are paging */
|
||||
struct gsm_bts *bts;
|
||||
/* what kind of channel type do we ask the MS to establish */
|
||||
int chan_type;
|
||||
|
||||
/* Timer 3113: how long do we try to page? */
|
||||
struct timer_list T3113;
|
||||
|
||||
/* callback to be called in case paging completes */
|
||||
gsm_cbfn *cbfn;
|
||||
void *cbfn_param;
|
||||
};
|
||||
|
||||
/* call once for every gsm_bts... */
|
||||
void paging_init(struct gsm_bts *bts);
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
INCLUDES = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir)
|
||||
AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOVTY_CFLAGS)
|
||||
AM_LDFLAGS = $(LIBOSMOCORE_LIBS)
|
||||
AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(COVERAGE_CFLAGS)
|
||||
AM_LDFLAGS = $(LIBOSMOCORE_LIBS) $(COVERAGE_LDFLAGS)
|
||||
|
||||
# build current directory before building gprs
|
||||
SUBDIRS = . ipaccess gprs
|
||||
|
|
|
@ -1505,6 +1505,12 @@ static u_int8_t ipa_smod_s_for_lchan(struct gsm_lchan *lchan)
|
|||
|
||||
static u_int8_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) {
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
INCLUDES = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir)
|
||||
AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(LIBOSMOSCCP_CFLAGS)
|
||||
AM_LDFLAGS = $(LIBOSMOCORE_LIBS) $(LIBOSMOVTY_LIBS)
|
||||
AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(LIBOSMOSCCP_CFLAGS) $(COVERAGE_CFLAGS)
|
||||
AM_LDFLAGS = $(LIBOSMOCORE_LIBS) $(LIBOSMOVTY_LIBS) $(COVERAGE_LDFLAGS)
|
||||
|
||||
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 \
|
||||
$(top_srcdir)/src/debug.c $(top_srcdir)/src/bsc_msc.c \
|
||||
$(top_srcdir)/src/bsc_init.c
|
||||
osmo_bsc_LDADD = $(top_builddir)/src/libvty.a \
|
||||
|
|
|
@ -19,57 +19,154 @@
|
|||
*/
|
||||
|
||||
#include <openbsc/osmo_bsc.h>
|
||||
#include <openbsc/osmo_msc_data.h>
|
||||
#include <openbsc/debug.h>
|
||||
|
||||
#include <osmocore/protocol/gsm_08_08.h>
|
||||
#include <osmocore/gsm0808.h>
|
||||
|
||||
#define return_when_not_connected(conn) \
|
||||
if (!conn->sccp_con) {\
|
||||
LOGP(DMSC, LOGL_ERROR, "MSC Connection not present.\n"); \
|
||||
return; \
|
||||
}
|
||||
|
||||
#define return_when_not_connected_val(conn, ret) \
|
||||
if (!conn->sccp_con) {\
|
||||
LOGP(DMSC, LOGL_ERROR, "MSC Connection not present.\n"); \
|
||||
return ret; \
|
||||
}
|
||||
|
||||
#define queue_msg_or_return(resp) \
|
||||
if (!resp) { \
|
||||
LOGP(DMSC, LOGL_ERROR, "Failed to allocate response.\n"); \
|
||||
return; \
|
||||
} \
|
||||
bsc_queue_for_msc(conn->sccp_con, resp);
|
||||
|
||||
static uint16_t get_network_code_for_msc(struct gsm_network *net)
|
||||
{
|
||||
if (net->msc_data->core_ncc != -1)
|
||||
return net->msc_data->core_ncc;
|
||||
return net->network_code;
|
||||
}
|
||||
|
||||
static uint16_t get_country_code_for_msc(struct gsm_network *net)
|
||||
{
|
||||
if (net->msc_data->core_mcc != -1)
|
||||
return net->msc_data->core_mcc;
|
||||
return net->country_code;
|
||||
}
|
||||
|
||||
static void bsc_sapi_n_reject(struct gsm_subscriber_connection *conn, int dlci)
|
||||
{
|
||||
struct msgb *resp;
|
||||
return_when_not_connected(conn);
|
||||
|
||||
resp = gsm0808_create_sapi_reject(dlci);
|
||||
queue_msg_or_return(resp);
|
||||
}
|
||||
|
||||
static void bsc_cipher_mode_compl(struct gsm_subscriber_connection *conn,
|
||||
struct msgb *msg, uint8_t chosen_encr)
|
||||
{
|
||||
struct msgb *resp;
|
||||
return_when_not_connected(conn);
|
||||
|
||||
LOGP(DMSC, LOGL_DEBUG, "CIPHER MODE COMPLETE from MS, forwarding to MSC\n");
|
||||
resp = gsm0808_create_cipher_complete(msg, chosen_encr);
|
||||
queue_msg_or_return(resp);
|
||||
}
|
||||
|
||||
static void bsc_cipher_mode_reject(struct gsm_subscriber_connection *conn,
|
||||
struct msgb *msg, uint16_t reason)
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
* Instruct to reserve data for a new connectiom, create the complete
|
||||
* layer three message, send it to open the connection.
|
||||
*/
|
||||
static int bsc_compl_l3(struct gsm_subscriber_connection *conn, struct msgb *msg,
|
||||
uint16_t chosen_channel)
|
||||
{
|
||||
return BSC_API_CONN_POL_REJECT;
|
||||
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);
|
||||
|
||||
/* allocate resource for a new connection */
|
||||
if (bsc_create_new_connection(conn) != 0)
|
||||
return BSC_API_CONN_POL_REJECT;
|
||||
|
||||
bsc_scan_bts_msg(conn, msg);
|
||||
resp = gsm0808_create_layer3(msg, network_code, country_code,
|
||||
conn->bts->location_area_code,
|
||||
conn->bts->cell_identity);
|
||||
if (!resp) {
|
||||
LOGP(DMSC, LOGL_DEBUG, "Failed to create layer3 message.\n");
|
||||
bsc_delete_connection(conn->sccp_con);
|
||||
return BSC_API_CONN_POL_REJECT;
|
||||
}
|
||||
|
||||
if (bsc_open_connection(conn->sccp_con, resp) != 0) {
|
||||
bsc_delete_connection(conn->sccp_con);
|
||||
msgb_free(resp);
|
||||
return BSC_API_CONN_POL_REJECT;
|
||||
}
|
||||
|
||||
return BSC_API_CONN_POL_ACCEPT;
|
||||
}
|
||||
|
||||
static void bsc_dtap(struct gsm_subscriber_connection *conn, struct msgb *msg)
|
||||
static void bsc_dtap(struct gsm_subscriber_connection *conn, uint8_t link_id, struct msgb *msg)
|
||||
{
|
||||
struct msgb *resp;
|
||||
return_when_not_connected(conn);
|
||||
|
||||
bsc_scan_bts_msg(conn, msg);
|
||||
resp = gsm0808_create_dtap(msg, link_id);
|
||||
queue_msg_or_return(resp);
|
||||
}
|
||||
|
||||
static void bsc_assign_compl(struct gsm_subscriber_connection *conn, uint16_t rr_cause)
|
||||
static void bsc_assign_compl(struct gsm_subscriber_connection *conn, uint8_t rr_cause,
|
||||
uint8_t chosen_channel, uint8_t encr_alg_id,
|
||||
uint8_t speech_model)
|
||||
{
|
||||
struct msgb *resp;
|
||||
return_when_not_connected(conn);
|
||||
|
||||
resp = gsm0808_create_assignment_completed(rr_cause, chosen_channel,
|
||||
encr_alg_id, speech_model);
|
||||
queue_msg_or_return(resp);
|
||||
}
|
||||
|
||||
static void bsc_assign_fail(struct gsm_subscriber_connection *conn, uint32_t cause)
|
||||
static void bsc_assign_fail(struct gsm_subscriber_connection *conn,
|
||||
uint8_t cause, uint8_t *rr_cause)
|
||||
{
|
||||
struct msgb *resp;
|
||||
return_when_not_connected(conn);
|
||||
|
||||
resp = gsm0808_create_assignment_failure(cause, rr_cause);
|
||||
queue_msg_or_return(resp);
|
||||
}
|
||||
|
||||
static void bsc_clear_request(struct gsm_subscriber_connection *conn, uint32_t cause)
|
||||
static int bsc_clear_request(struct gsm_subscriber_connection *conn, uint32_t cause)
|
||||
{
|
||||
}
|
||||
struct msgb *resp;
|
||||
return_when_not_connected_val(conn, 1);
|
||||
|
||||
static void bsc_clear_compl(struct gsm_subscriber_connection *conn)
|
||||
{
|
||||
resp = gsm0808_create_clear_rqst(GSM0808_CAUSE_RADIO_INTERFACE_FAILURE);
|
||||
if (!resp) {
|
||||
LOGP(DMSC, LOGL_ERROR, "Failed to allocate response.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
bsc_queue_for_msc(conn->sccp_con, resp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct bsc_api bsc_handler = {
|
||||
.sapi_n_reject = bsc_sapi_n_reject,
|
||||
.cipher_mode_compl = bsc_cipher_mode_compl,
|
||||
.cipher_mode_reject = bsc_cipher_mode_reject,
|
||||
.compl_l3 = bsc_compl_l3,
|
||||
.dtap = bsc_dtap,
|
||||
.assign_compl = bsc_assign_compl,
|
||||
.assign_fail = bsc_assign_fail,
|
||||
.clear_request = bsc_clear_request,
|
||||
.clear_compl = bsc_clear_compl,
|
||||
};
|
||||
|
||||
struct bsc_api *osmo_bsc_api()
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* ipaccess audio handling
|
||||
*
|
||||
* (C) 2009-2010 by Holger Hans Peter Freyther <zecke@selfish.org>
|
||||
* (C) 2009-2010 by On-Waves
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <openbsc/osmo_msc_data.h>
|
||||
#include <openbsc/osmo_bsc.h>
|
||||
#include <openbsc/gsm_data.h>
|
||||
#include <openbsc/debug.h>
|
||||
#include <openbsc/signal.h>
|
||||
|
||||
#include <arpa/inet.h>
|
||||
|
||||
static int handle_abisip_signal(unsigned int subsys, unsigned int signal,
|
||||
void *handler_data, void *signal_data)
|
||||
{
|
||||
struct gsm_subscriber_connection *con;
|
||||
struct gsm_lchan *lchan = signal_data;
|
||||
int rc;
|
||||
|
||||
if (subsys != SS_ABISIP)
|
||||
return 0;
|
||||
|
||||
con = lchan->conn;
|
||||
if (!con || !con->sccp_con)
|
||||
return 0;
|
||||
|
||||
switch (signal) {
|
||||
case S_ABISIP_CRCX_ACK:
|
||||
/* we can ask it to connect now */
|
||||
LOGP(DMSC, LOGL_DEBUG, "Connecting BTS to port: %d conn: %d\n",
|
||||
con->sccp_con->rtp_port, lchan->abis_ip.conn_id);
|
||||
|
||||
rc = rsl_ipacc_mdcx(lchan, ntohl(INADDR_ANY),
|
||||
con->sccp_con->rtp_port,
|
||||
lchan->abis_ip.rtp_payload2);
|
||||
if (rc < 0) {
|
||||
LOGP(DMSC, LOGL_ERROR, "Failed to send MDCX: %d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int osmo_bsc_audio_init(struct gsm_network *net)
|
||||
{
|
||||
net->hardcoded_rtp_payload = 98;
|
||||
register_signal_handler(SS_ABISIP, handle_abisip_signal, net);
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,549 @@
|
|||
/* GSM 08.08 BSSMAP handling */
|
||||
/* (C) 2009-2010 by Holger Hans Peter Freyther <zecke@selfish.org>
|
||||
* (C) 2009-2010 by On-Waves
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <openbsc/osmo_bsc.h>
|
||||
#include <openbsc/osmo_bsc_grace.h>
|
||||
#include <openbsc/osmo_msc_data.h>
|
||||
#include <openbsc/debug.h>
|
||||
#include <openbsc/gsm_subscriber.h>
|
||||
#include <openbsc/mgcp.h>
|
||||
#include <openbsc/paging.h>
|
||||
|
||||
#include <osmocore/gsm0808.h>
|
||||
#include <osmocore/protocol/gsm_08_08.h>
|
||||
|
||||
#include <arpa/inet.h>
|
||||
|
||||
static uint16_t read_data16(const uint8_t *data)
|
||||
{
|
||||
uint16_t res;
|
||||
|
||||
memcpy(&res, data, sizeof(res));
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
* helpers for the assignment command
|
||||
*/
|
||||
enum gsm0808_permitted_speech audio_support_to_gsm88(struct gsm_audio_support *audio)
|
||||
{
|
||||
if (audio->hr) {
|
||||
switch (audio->ver) {
|
||||
case 1:
|
||||
return GSM0808_PERM_HR1;
|
||||
break;
|
||||
case 2:
|
||||
return GSM0808_PERM_HR2;
|
||||
break;
|
||||
case 3:
|
||||
return GSM0808_PERM_HR3;
|
||||
break;
|
||||
default:
|
||||
LOGP(DMSC, LOGL_ERROR, "Wrong speech mode: %d\n", audio->ver);
|
||||
return GSM0808_PERM_FR1;
|
||||
}
|
||||
} else {
|
||||
switch (audio->ver) {
|
||||
case 1:
|
||||
return GSM0808_PERM_FR1;
|
||||
break;
|
||||
case 2:
|
||||
return GSM0808_PERM_FR2;
|
||||
break;
|
||||
case 3:
|
||||
return GSM0808_PERM_FR3;
|
||||
break;
|
||||
default:
|
||||
LOGP(DMSC, LOGL_ERROR, "Wrong speech mode: %d\n", audio->ver);
|
||||
return GSM0808_PERM_HR1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum gsm48_chan_mode gsm88_to_chan_mode(enum gsm0808_permitted_speech speech)
|
||||
{
|
||||
switch (speech) {
|
||||
case GSM0808_PERM_HR1:
|
||||
case GSM0808_PERM_FR1:
|
||||
return GSM48_CMODE_SPEECH_V1;
|
||||
break;
|
||||
case GSM0808_PERM_HR2:
|
||||
case GSM0808_PERM_FR2:
|
||||
return GSM48_CMODE_SPEECH_EFR;
|
||||
break;
|
||||
case GSM0808_PERM_HR3:
|
||||
case GSM0808_PERM_FR3:
|
||||
return GSM48_CMODE_SPEECH_AMR;
|
||||
break;
|
||||
}
|
||||
|
||||
LOGP(DMSC, LOGL_FATAL, "Should not be reached.\n");
|
||||
return GSM48_CMODE_SPEECH_AMR;
|
||||
}
|
||||
|
||||
static int bssmap_handle_reset_ack(struct gsm_network *net,
|
||||
struct msgb *msg, unsigned int length)
|
||||
{
|
||||
LOGP(DMSC, LOGL_NOTICE, "Reset ACK from MSC\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* GSM 08.08 § 3.2.1.19 */
|
||||
static int bssmap_handle_paging(struct gsm_network *net,
|
||||
struct msgb *msg, unsigned int payload_length)
|
||||
{
|
||||
struct gsm_subscriber *subscr;
|
||||
struct tlv_parsed tp;
|
||||
char mi_string[GSM48_MI_SIZE];
|
||||
uint32_t tmsi = GSM_RESERVED_TMSI;
|
||||
unsigned int lac = GSM_LAC_RESERVED_ALL_BTS;
|
||||
uint8_t data_length;
|
||||
const uint8_t *data;
|
||||
uint8_t chan_needed = RSL_CHANNEED_ANY;
|
||||
|
||||
tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l4h + 1, payload_length - 1, 0, 0);
|
||||
|
||||
if (!TLVP_PRESENT(&tp, GSM0808_IE_IMSI)) {
|
||||
LOGP(DMSC, LOGL_ERROR, "Mandantory IMSI not present.\n");
|
||||
return -1;
|
||||
} else if ((TLVP_VAL(&tp, GSM0808_IE_IMSI)[0] & GSM_MI_TYPE_MASK) != GSM_MI_TYPE_IMSI) {
|
||||
LOGP(DMSC, LOGL_ERROR, "Wrong content in the IMSI\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!TLVP_PRESENT(&tp, GSM0808_IE_CELL_IDENTIFIER_LIST)) {
|
||||
LOGP(DMSC, LOGL_ERROR, "Mandantory CELL IDENTIFIER LIST not present.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (TLVP_PRESENT(&tp, GSM0808_IE_TMSI)) {
|
||||
gsm48_mi_to_string(mi_string, sizeof(mi_string),
|
||||
TLVP_VAL(&tp, GSM0808_IE_TMSI), TLVP_LEN(&tp, GSM0808_IE_TMSI));
|
||||
tmsi = strtoul(mi_string, NULL, 10);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* parse the IMSI
|
||||
*/
|
||||
gsm48_mi_to_string(mi_string, sizeof(mi_string),
|
||||
TLVP_VAL(&tp, GSM0808_IE_IMSI), TLVP_LEN(&tp, GSM0808_IE_IMSI));
|
||||
|
||||
/*
|
||||
* parse the cell identifier list
|
||||
*/
|
||||
data_length = TLVP_LEN(&tp, GSM0808_IE_CELL_IDENTIFIER_LIST);
|
||||
data = TLVP_VAL(&tp, GSM0808_IE_CELL_IDENTIFIER_LIST);
|
||||
|
||||
/*
|
||||
* Support paging to all network or one BTS at one LAC
|
||||
*/
|
||||
if (data_length == 3 && data[0] == CELL_IDENT_LAC) {
|
||||
lac = ntohs(read_data16(&data[1]));
|
||||
} else if (data_length > 1 || (data[0] & 0x0f) != CELL_IDENT_BSS) {
|
||||
LOGP(DMSC, LOGL_ERROR, "Unsupported Cell Identifier List: %s\n", hexdump(data, data_length));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (TLVP_PRESENT(&tp, GSM0808_IE_CHANNEL_NEEDED) && TLVP_LEN(&tp, GSM0808_IE_CHANNEL_NEEDED) == 1)
|
||||
chan_needed = TLVP_VAL(&tp, GSM0808_IE_CHANNEL_NEEDED)[0] & 0x03;
|
||||
|
||||
if (TLVP_PRESENT(&tp, GSM0808_IE_EMLPP_PRIORITY)) {
|
||||
LOGP(DMSC, LOGL_ERROR, "eMLPP is not handled\n");
|
||||
}
|
||||
|
||||
subscr = subscr_get_or_create(net, mi_string);
|
||||
if (!subscr) {
|
||||
LOGP(DMSC, LOGL_ERROR, "Failed to allocate a subscriber for %s\n", mi_string);
|
||||
return -1;
|
||||
}
|
||||
|
||||
subscr->lac = lac;
|
||||
subscr->tmsi = tmsi;
|
||||
|
||||
LOGP(DMSC, LOGL_DEBUG, "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);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* GSM 08.08 § 3.1.9.1 and 3.2.1.21...
|
||||
* release our gsm_subscriber_connection and send message
|
||||
*/
|
||||
static int bssmap_handle_clear_command(struct osmo_bsc_sccp_con *conn,
|
||||
struct msgb *msg, unsigned int payload_length)
|
||||
{
|
||||
struct msgb *resp;
|
||||
|
||||
/* TODO: handle the cause of this package */
|
||||
|
||||
if (conn->conn) {
|
||||
LOGP(DMSC, LOGL_DEBUG, "Releasing all transactions on %p\n", conn);
|
||||
gsm0808_clear(conn->conn);
|
||||
subscr_con_free(conn->conn);
|
||||
conn->conn = NULL;
|
||||
}
|
||||
|
||||
/* send the clear complete message */
|
||||
resp = gsm0808_create_clear_complete();
|
||||
if (!resp) {
|
||||
LOGP(DMSC, LOGL_ERROR, "Sending clear complete failed.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
bsc_queue_for_msc(conn, resp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* GSM 08.08 § 3.4.7 cipher mode handling. We will have to pick
|
||||
* the cipher to be used for this. In case we are already using
|
||||
* a cipher we will have to send cipher mode reject to the MSC,
|
||||
* otherwise we will have to pick something that we and the MS
|
||||
* is supporting. Currently we are doing it in a rather static
|
||||
* way by picking one ecnryption or no encrytpion.
|
||||
*/
|
||||
static int bssmap_handle_cipher_mode(struct osmo_bsc_sccp_con *conn,
|
||||
struct msgb *msg, unsigned int payload_length)
|
||||
{
|
||||
uint16_t len;
|
||||
struct gsm_network *network = NULL;
|
||||
const uint8_t *data;
|
||||
struct tlv_parsed tp;
|
||||
struct msgb *resp;
|
||||
int reject_cause = -1;
|
||||
int include_imeisv = 1;
|
||||
|
||||
if (!conn->conn) {
|
||||
LOGP(DMSC, LOGL_ERROR, "No lchan/msc_data in cipher mode command.\n");
|
||||
goto reject;
|
||||
}
|
||||
|
||||
if (conn->ciphering_handled) {
|
||||
LOGP(DMSC, LOGL_ERROR, "Already seen ciphering command. Protocol Error.\n");
|
||||
goto reject;
|
||||
}
|
||||
|
||||
conn->ciphering_handled = 1;
|
||||
|
||||
tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l4h + 1, payload_length - 1, 0, 0);
|
||||
if (!TLVP_PRESENT(&tp, GSM0808_IE_ENCRYPTION_INFORMATION)) {
|
||||
LOGP(DMSC, LOGL_ERROR, "IE Encryption Information missing.\n");
|
||||
goto reject;
|
||||
}
|
||||
|
||||
/*
|
||||
* check if our global setting is allowed
|
||||
* - Currently we check for A5/0 and A5/1
|
||||
* - Copy the key if that is necessary
|
||||
* - Otherwise reject
|
||||
*/
|
||||
len = TLVP_LEN(&tp, GSM0808_IE_ENCRYPTION_INFORMATION);
|
||||
if (len < 1) {
|
||||
LOGP(DMSC, LOGL_ERROR, "IE Encryption Information is too short.\n");
|
||||
goto reject;
|
||||
}
|
||||
|
||||
network = conn->conn->bts->network;
|
||||
data = TLVP_VAL(&tp, GSM0808_IE_ENCRYPTION_INFORMATION);
|
||||
|
||||
if (TLVP_PRESENT(&tp, GSM0808_IE_CIPHER_RESPONSE_MODE))
|
||||
include_imeisv = TLVP_VAL(&tp, GSM0808_IE_CIPHER_RESPONSE_MODE)[0] & 0x1;
|
||||
|
||||
if (network->a5_encryption == 0 && (data[0] & 0x1) == 0x1) {
|
||||
gsm0808_cipher_mode(conn->conn, 0, NULL, 0, include_imeisv);
|
||||
} else if (network->a5_encryption != 0 && (data[0] & 0x2) == 0x2) {
|
||||
gsm0808_cipher_mode(conn->conn, 1, &data[1], len - 1, include_imeisv);
|
||||
} else {
|
||||
LOGP(DMSC, LOGL_ERROR, "Can not select encryption...\n");
|
||||
goto reject;
|
||||
}
|
||||
|
||||
reject:
|
||||
resp = gsm0808_create_cipher_reject(reject_cause);
|
||||
if (!resp) {
|
||||
LOGP(DMSC, LOGL_ERROR, "Sending the cipher reject failed.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
bsc_queue_for_msc(conn, resp);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle the assignment request message.
|
||||
*
|
||||
* See §3.2.1.1 for the message type
|
||||
*/
|
||||
static int bssmap_handle_assignm_req(struct osmo_bsc_sccp_con *conn,
|
||||
struct msgb *msg, unsigned int length)
|
||||
{
|
||||
struct msgb *resp;
|
||||
struct gsm_network *network;
|
||||
struct tlv_parsed tp;
|
||||
uint8_t *data;
|
||||
uint16_t cic;
|
||||
uint8_t timeslot;
|
||||
uint8_t multiplex;
|
||||
enum gsm48_chan_mode chan_mode = GSM48_CMODE_SIGN;
|
||||
int i, supported, port, full_rate = -1;
|
||||
|
||||
if (!conn->conn) {
|
||||
LOGP(DMSC, LOGL_ERROR, "No lchan/msc_data in cipher mode command.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
network = conn->conn->bts->network;
|
||||
tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l4h + 1, length - 1, 0, 0);
|
||||
|
||||
if (!TLVP_PRESENT(&tp, GSM0808_IE_CHANNEL_TYPE)) {
|
||||
LOGP(DMSC, LOGL_ERROR, "Mandantory channel type not present.\n");
|
||||
goto reject;
|
||||
}
|
||||
|
||||
if (!TLVP_PRESENT(&tp, GSM0808_IE_CIRCUIT_IDENTITY_CODE)) {
|
||||
LOGP(DMSC, LOGL_ERROR, "Identity code missing. Audio routing will not work.\n");
|
||||
goto reject;
|
||||
}
|
||||
|
||||
cic = ntohs(read_data16(TLVP_VAL(&tp, GSM0808_IE_CIRCUIT_IDENTITY_CODE)));
|
||||
timeslot = cic & 0x1f;
|
||||
multiplex = (cic & ~0x1f) >> 5;
|
||||
|
||||
/*
|
||||
* Currently we only support a limited subset of all
|
||||
* possible channel types. The limitation ends by not using
|
||||
* multi-slot, limiting the channel coding, speech...
|
||||
*/
|
||||
if (TLVP_LEN(&tp, GSM0808_IE_CHANNEL_TYPE) < 3) {
|
||||
LOGP(DMSC, LOGL_ERROR, "ChannelType len !=3 not supported: %d\n",
|
||||
TLVP_LEN(&tp, GSM0808_IE_CHANNEL_TYPE));
|
||||
goto reject;
|
||||
}
|
||||
|
||||
/*
|
||||
* Try to figure out if we support the proposed speech codecs. For
|
||||
* now we will always pick the full rate codecs.
|
||||
*/
|
||||
|
||||
data = (uint8_t *) TLVP_VAL(&tp, GSM0808_IE_CHANNEL_TYPE);
|
||||
if ((data[0] & 0xf) != 0x1) {
|
||||
LOGP(DMSC, LOGL_ERROR, "ChannelType != speech: %d\n", data[0]);
|
||||
goto reject;
|
||||
}
|
||||
|
||||
if (data[1] != GSM0808_SPEECH_FULL_PREF && data[1] != GSM0808_SPEECH_HALF_PREF) {
|
||||
LOGP(DMSC, LOGL_ERROR, "ChannelType full not allowed: %d\n", data[1]);
|
||||
goto reject;
|
||||
}
|
||||
|
||||
/*
|
||||
* go through the list of preferred codecs of our gsm network
|
||||
* and try to find it among the permitted codecs. If we found
|
||||
* it we will send chan_mode to the right mode and break the
|
||||
* inner loop. The outer loop will exit due chan_mode having
|
||||
* the correct value.
|
||||
*/
|
||||
full_rate = 0;
|
||||
for (supported = 0;
|
||||
chan_mode == GSM48_CMODE_SIGN && supported < network->msc_data->audio_length;
|
||||
++supported) {
|
||||
|
||||
int perm_val = audio_support_to_gsm88(network->msc_data->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);
|
||||
full_rate = (data[i] & 0x4) == 0;
|
||||
break;
|
||||
} else if ((data[i] & 0x80) == 0x00) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (chan_mode == GSM48_CMODE_SIGN) {
|
||||
LOGP(DMSC, LOGL_ERROR, "No supported audio type found.\n");
|
||||
goto reject;
|
||||
}
|
||||
|
||||
/* 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);
|
||||
|
||||
return gsm0808_assign_req(conn->conn, chan_mode, full_rate);
|
||||
|
||||
reject:
|
||||
resp = gsm0808_create_assignment_failure(GSM0808_CAUSE_NO_RADIO_RESOURCE_AVAILABLE, NULL);
|
||||
if (!resp) {
|
||||
LOGP(DMSC, LOGL_ERROR, "Channel allocation failure.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
bsc_queue_for_msc(conn, resp);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int bssmap_rcvmsg_udt(struct gsm_network *net,
|
||||
struct msgb *msg, unsigned int length)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (length < 1) {
|
||||
LOGP(DMSC, LOGL_ERROR, "Not enough room: %d\n", length);
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (msg->l4h[0]) {
|
||||
case BSS_MAP_MSG_RESET_ACKNOWLEDGE:
|
||||
ret = bssmap_handle_reset_ack(net, msg, length);
|
||||
break;
|
||||
case BSS_MAP_MSG_PAGING:
|
||||
if (bsc_grace_allow_new_connection(net))
|
||||
ret = bssmap_handle_paging(net, msg, length);
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int bssmap_rcvmsg_dt1(struct osmo_bsc_sccp_con *conn,
|
||||
struct msgb *msg, unsigned int length)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (length < 1) {
|
||||
LOGP(DMSC, LOGL_ERROR, "Not enough room: %d\n", length);
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (msg->l4h[0]) {
|
||||
case BSS_MAP_MSG_CLEAR_CMD:
|
||||
ret = bssmap_handle_clear_command(conn, msg, length);
|
||||
break;
|
||||
case BSS_MAP_MSG_CIPHER_MODE_CMD:
|
||||
ret = bssmap_handle_cipher_mode(conn, msg, length);
|
||||
break;
|
||||
case BSS_MAP_MSG_ASSIGMENT_RQST:
|
||||
ret = bssmap_handle_assignm_req(conn, msg, length);
|
||||
break;
|
||||
default:
|
||||
LOGP(DMSC, LOGL_DEBUG, "Unimplemented msg type: %d\n", msg->l4h[0]);
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dtap_rcvmsg(struct osmo_bsc_sccp_con *conn,
|
||||
struct msgb *msg, unsigned int length)
|
||||
{
|
||||
struct dtap_header *header;
|
||||
struct msgb *gsm48;
|
||||
uint8_t *data;
|
||||
|
||||
if (!conn->conn) {
|
||||
LOGP(DMSC, LOGL_ERROR, "No subscriber connection available\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
header = (struct dtap_header *) msg->l3h;
|
||||
if (sizeof(*header) >= length) {
|
||||
LOGP(DMSC, LOGL_ERROR, "The DTAP header does not fit. Wanted: %u got: %u\n", sizeof(*header), length);
|
||||
LOGP(DMSC, LOGL_ERROR, "hex: %s\n", hexdump(msg->l3h, length));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (header->length > length - sizeof(*header)) {
|
||||
LOGP(DMSC, LOGL_ERROR, "The DTAP l4 information does not fit: header: %u length: %u\n", header->length, length);
|
||||
LOGP(DMSC, LOGL_ERROR, "hex: %s\n", hexdump(msg->l3h, length));
|
||||
return -1;
|
||||
}
|
||||
|
||||
LOGP(DMSC, LOGL_DEBUG, "DTAP message: SAPI: %u CHAN: %u\n", header->link_id & 0x07, header->link_id & 0xC0);
|
||||
|
||||
/* forward the data */
|
||||
gsm48 = gsm48_msgb_alloc();
|
||||
if (!gsm48) {
|
||||
LOGP(DMSC, LOGL_ERROR, "Allocation of the message failed.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
gsm48->l3h = gsm48->data;
|
||||
data = msgb_put(gsm48, length - sizeof(*header));
|
||||
memcpy(data, msg->l3h + sizeof(*header), length - sizeof(*header));
|
||||
|
||||
/* pass it to the filter for extra actions */
|
||||
bsc_scan_msc_msg(conn->conn, gsm48);
|
||||
return gsm0808_submit_dtap(conn->conn, gsm48, header->link_id, 1);
|
||||
}
|
||||
|
||||
int bsc_handle_udt(struct gsm_network *network,
|
||||
struct bsc_msc_connection *conn,
|
||||
struct msgb *msgb, unsigned int length)
|
||||
{
|
||||
struct bssmap_header *bs;
|
||||
|
||||
LOGP(DMSC, LOGL_DEBUG, "Incoming SCCP message ftom MSC: %s\n",
|
||||
hexdump(msgb->l3h, length));
|
||||
|
||||
if (length < sizeof(*bs)) {
|
||||
LOGP(DMSC, LOGL_ERROR, "The header is too short.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
bs = (struct bssmap_header *) msgb->l3h;
|
||||
if (bs->length < length - sizeof(*bs))
|
||||
return -1;
|
||||
|
||||
switch (bs->type) {
|
||||
case BSSAP_MSG_BSS_MANAGEMENT:
|
||||
msgb->l4h = &msgb->l3h[sizeof(*bs)];
|
||||
bssmap_rcvmsg_udt(network, msgb, length - sizeof(*bs));
|
||||
break;
|
||||
default:
|
||||
LOGP(DMSC, LOGL_ERROR, "Unimplemented msg type: %d\n", bs->type);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bsc_handle_dt1(struct osmo_bsc_sccp_con *conn,
|
||||
struct msgb *msg, unsigned int len)
|
||||
{
|
||||
if (len < sizeof(struct bssmap_header)) {
|
||||
LOGP(DMSC, LOGL_ERROR, "The header is too short.\n");
|
||||
}
|
||||
|
||||
switch (msg->l3h[0]) {
|
||||
case BSSAP_MSG_BSS_MANAGEMENT:
|
||||
msg->l4h = &msg->l3h[sizeof(struct bssmap_header)];
|
||||
bssmap_rcvmsg_dt1(conn, msg, len - sizeof(struct bssmap_header));
|
||||
break;
|
||||
case BSSAP_MSG_DTAP:
|
||||
dtap_rcvmsg(conn, msg, len);
|
||||
break;
|
||||
default:
|
||||
LOGP(DMSC, LOGL_DEBUG, "Unimplemented msg type: %d\n", msg->l3h[0]);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
|
@ -0,0 +1,168 @@
|
|||
/* (C) 2009-2010 by Holger Hans Peter Freyther <zecke@selfish.org>
|
||||
* (C) 2009-2010 by On-Waves
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <openbsc/osmo_bsc.h>
|
||||
#include <openbsc/osmo_msc_data.h>
|
||||
#include <openbsc/gsm_04_80.h>
|
||||
#include <openbsc/gsm_subscriber.h>
|
||||
#include <openbsc/debug.h>
|
||||
#include <openbsc/paging.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
static void handle_lu_request(struct gsm_subscriber_connection *conn,
|
||||
struct msgb *msg)
|
||||
{
|
||||
struct gsm48_hdr *gh;
|
||||
struct gsm48_loc_upd_req *lu;
|
||||
struct gsm48_loc_area_id lai;
|
||||
struct gsm_network *net;
|
||||
|
||||
if (msgb_l3len(msg) < sizeof(*gh) + sizeof(*lu)) {
|
||||
LOGP(DMSC, LOGL_ERROR, "LU too small to look at: %u\n", msgb_l3len(msg));
|
||||
return;
|
||||
}
|
||||
|
||||
net = conn->bts->network;
|
||||
|
||||
gh = msgb_l3(msg);
|
||||
lu = (struct gsm48_loc_upd_req *) gh->data;
|
||||
|
||||
gsm48_generate_lai(&lai, net->country_code, net->network_code,
|
||||
conn->bts->location_area_code);
|
||||
|
||||
if (memcmp(&lai, &lu->lai, sizeof(lai)) != 0) {
|
||||
LOGP(DMSC, LOGL_DEBUG, "Marking con for welcome USSD.\n");
|
||||
conn->sccp_con->new_subscriber = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* we will need to stop the paging request */
|
||||
static int handle_page_resp(struct gsm_subscriber_connection *conn, struct msgb *msg)
|
||||
{
|
||||
uint8_t mi_type;
|
||||
char mi_string[GSM48_MI_SIZE];
|
||||
struct gsm48_hdr *gh;
|
||||
struct gsm48_pag_resp *resp;
|
||||
struct gsm_subscriber *subscr;
|
||||
|
||||
if (msgb_l3len(msg) < sizeof(*gh) + sizeof(*resp)) {
|
||||
LOGP(DMSC, LOGL_ERROR, "PagingResponse too small: %u\n", msgb_l3len(msg));
|
||||
return -1;
|
||||
}
|
||||
|
||||
gh = msgb_l3(msg);
|
||||
resp = (struct gsm48_pag_resp *) &gh->data[0];
|
||||
|
||||
gsm48_paging_extract_mi(resp, msgb_l3len(msg) - sizeof(*gh),
|
||||
mi_string, &mi_type);
|
||||
DEBUGP(DRR, "PAGING RESPONSE: mi_type=0x%02x MI(%s)\n",
|
||||
mi_type, mi_string);
|
||||
|
||||
switch (mi_type) {
|
||||
case GSM_MI_TYPE_TMSI:
|
||||
subscr = subscr_active_by_tmsi(conn->bts->network,
|
||||
tmsi_from_string(mi_string));
|
||||
break;
|
||||
case GSM_MI_TYPE_IMSI:
|
||||
subscr = subscr_active_by_imsi(conn->bts->network, mi_string);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!subscr) {
|
||||
LOGP(DMSC, LOGL_ERROR, "Non active subscriber got paged.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
paging_request_stop(conn->bts, subscr, conn);
|
||||
subscr_put(subscr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is used to scan a message for extra functionality of the BSC. This
|
||||
* includes scanning for location updating requests/acceptd and then send
|
||||
* a welcome USSD message to the subscriber.
|
||||
*/
|
||||
int bsc_scan_bts_msg(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;
|
||||
|
||||
if (pdisc == GSM48_PDISC_MM) {
|
||||
if (mtype == GSM48_MT_MM_LOC_UPD_REQUEST)
|
||||
handle_lu_request(conn, msg);
|
||||
} else if (pdisc == GSM48_PDISC_RR) {
|
||||
if (mtype == GSM48_MT_RR_PAG_RESP)
|
||||
handle_page_resp(conn, msg);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void send_welcome_ussd(struct gsm_subscriber_connection *conn)
|
||||
{
|
||||
struct gsm_network *net;
|
||||
net = conn->bts->network;
|
||||
|
||||
if (!net->msc_data->ussd_welcome_txt)
|
||||
return;
|
||||
|
||||
gsm0480_send_ussdNotify(conn, 1, net->msc_data->ussd_welcome_txt);
|
||||
gsm0480_send_releaseComplete(conn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Messages coming back from the MSC.
|
||||
*/
|
||||
int bsc_scan_msc_msg(struct gsm_subscriber_connection *conn, struct msgb *msg)
|
||||
{
|
||||
struct gsm_network *net;
|
||||
struct gsm48_loc_area_id *lai;
|
||||
struct gsm48_hdr *gh;
|
||||
uint8_t mtype;
|
||||
|
||||
if (msgb_l3len(msg) < sizeof(*gh)) {
|
||||
LOGP(DMSC, LOGL_ERROR, "GSM48 header does not fit.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
gh = (struct gsm48_hdr *) msgb_l3(msg);
|
||||
mtype = gh->msg_type & 0xbf;
|
||||
net = conn->bts->network;
|
||||
|
||||
if (mtype == GSM48_MT_MM_LOC_UPD_ACCEPT) {
|
||||
if (net->msc_data->core_ncc != -1 ||
|
||||
net->msc_data->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,
|
||||
net->network_code,
|
||||
conn->bts->location_area_code);
|
||||
}
|
||||
}
|
||||
|
||||
if (conn->sccp_con->new_subscriber)
|
||||
send_welcome_ussd(conn);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -236,6 +236,11 @@ int main(int argc, char **argv)
|
|||
exit(1);
|
||||
}
|
||||
|
||||
if (osmo_bsc_audio_init(bsc_gsmnet) != 0) {
|
||||
LOGP(DMSC, LOGL_ERROR, "Failed to register audio support.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
signal(SIGINT, &signal_handler);
|
||||
signal(SIGABRT, &signal_handler);
|
||||
signal(SIGUSR1, &signal_handler);
|
||||
|
|
|
@ -21,18 +21,127 @@
|
|||
*/
|
||||
|
||||
#include <openbsc/gsm_data.h>
|
||||
#include <openbsc/osmo_bsc.h>
|
||||
#include <openbsc/osmo_bsc_grace.h>
|
||||
#include <openbsc/osmo_msc_data.h>
|
||||
#include <openbsc/debug.h>
|
||||
#include <openbsc/ipaccess.h>
|
||||
#include <openbsc/signal.h>
|
||||
|
||||
#include <osmocore/gsm0808.h>
|
||||
#include <osmocore/talloc.h>
|
||||
#include <osmocore/protocol/gsm_08_08.h>
|
||||
|
||||
#include <osmocom/sccp/sccp.h>
|
||||
|
||||
/* SCCP helper */
|
||||
#define SCCP_IT_TIMER 60
|
||||
|
||||
static LLIST_HEAD(active_connections);
|
||||
|
||||
static void free_queued(struct osmo_bsc_sccp_con *conn)
|
||||
{
|
||||
struct msgb *msg;
|
||||
|
||||
while (!llist_empty(&conn->sccp_queue)) {
|
||||
/* this is not allowed to fail */
|
||||
msg = msgb_dequeue(&conn->sccp_queue);
|
||||
msgb_free(msg);
|
||||
}
|
||||
|
||||
conn->sccp_queue_size = 0;
|
||||
}
|
||||
|
||||
static void send_queued(struct osmo_bsc_sccp_con *conn)
|
||||
{
|
||||
struct msgb *msg;
|
||||
|
||||
while (!llist_empty(&conn->sccp_queue)) {
|
||||
/* this is not allowed to fail */
|
||||
msg = msgb_dequeue(&conn->sccp_queue);
|
||||
sccp_connection_write(conn->sccp, msg);
|
||||
msgb_free(msg);
|
||||
conn->sccp_queue_size -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void msc_outgoing_sccp_data(struct sccp_connection *conn,
|
||||
struct msgb *msg, unsigned int len)
|
||||
{
|
||||
struct osmo_bsc_sccp_con *bsc_con =
|
||||
(struct osmo_bsc_sccp_con *) conn->data_ctx;
|
||||
|
||||
bsc_handle_dt1(bsc_con, msg, len);
|
||||
}
|
||||
|
||||
static void msc_outgoing_sccp_state(struct sccp_connection *conn, int old_state)
|
||||
{
|
||||
struct osmo_bsc_sccp_con *con_data;
|
||||
|
||||
if (conn->connection_state >= SCCP_CONNECTION_STATE_RELEASE_COMPLETE) {
|
||||
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.");
|
||||
gsm0808_clear(con_data->conn);
|
||||
subscr_con_free(con_data->conn);
|
||||
con_data->conn = NULL;
|
||||
}
|
||||
|
||||
con_data->sccp = NULL;
|
||||
free_queued(con_data);
|
||||
sccp_connection_free(conn);
|
||||
bsc_delete_connection(con_data);
|
||||
} else if (conn->connection_state == SCCP_CONNECTION_STATE_ESTABLISHED) {
|
||||
LOGP(DMSC, LOGL_DEBUG, "Connection established: %p\n", conn);
|
||||
con_data = (struct osmo_bsc_sccp_con *) conn->data_ctx;
|
||||
|
||||
bsc_del_timer(&con_data->sccp_cc_timeout);
|
||||
bsc_schedule_timer(&con_data->sccp_it_timeout, SCCP_IT_TIMER, 0);
|
||||
|
||||
send_queued(con_data);
|
||||
}
|
||||
}
|
||||
|
||||
static void bsc_sccp_force_free(struct osmo_bsc_sccp_con *data)
|
||||
{
|
||||
if (data->conn) {
|
||||
gsm0808_clear(data->conn);
|
||||
subscr_con_free(data->conn);
|
||||
data->conn = NULL;
|
||||
}
|
||||
|
||||
free_queued(data);
|
||||
sccp_connection_force_free(data->sccp);
|
||||
data->sccp = NULL;
|
||||
bsc_delete_connection(data);
|
||||
}
|
||||
|
||||
static void sccp_it_timeout(void *_data)
|
||||
{
|
||||
struct osmo_bsc_sccp_con *data =
|
||||
(struct osmo_bsc_sccp_con *) _data;
|
||||
|
||||
sccp_connection_send_it(data->sccp);
|
||||
bsc_schedule_timer(&data->sccp_it_timeout, SCCP_IT_TIMER, 0);
|
||||
}
|
||||
|
||||
static void sccp_cc_timeout(void *_data)
|
||||
{
|
||||
struct osmo_bsc_sccp_con *data =
|
||||
(struct osmo_bsc_sccp_con *) _data;
|
||||
|
||||
if (data->sccp->connection_state >= SCCP_CONNECTION_STATE_ESTABLISHED)
|
||||
return;
|
||||
|
||||
LOGP(DMSC, LOGL_ERROR, "The connection was never established.\n");
|
||||
bsc_sccp_force_free(data);
|
||||
}
|
||||
|
||||
static void msc_sccp_write_ipa(struct sccp_connection *conn, struct msgb *msg, void *data)
|
||||
{
|
||||
LOGP(DMSC, LOGL_ERROR, "Writing is not implemented.\n");
|
||||
msgb_free(msg);
|
||||
struct gsm_network *net = (struct gsm_network *) data;
|
||||
msc_queue_write(net->msc_data->msc_con, msg, IPAC_PROTO_SCCP);
|
||||
}
|
||||
|
||||
static int msc_sccp_accept(struct sccp_connection *connection, void *data)
|
||||
|
@ -43,27 +152,127 @@ 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 bssmap_header *bs;
|
||||
struct gsm_network *net = (struct gsm_network *) data;
|
||||
return bsc_handle_udt(net, net->msc_data->msc_con, msgb, length);
|
||||
}
|
||||
|
||||
LOGP(DMSC, LOGL_DEBUG, "Incoming SCCP message ftom MSC: %s\n",
|
||||
hexdump(msgb->l3h, length));
|
||||
int bsc_queue_for_msc(struct osmo_bsc_sccp_con *conn, struct msgb *msg)
|
||||
{
|
||||
struct sccp_connection *sccp = conn->sccp;
|
||||
|
||||
if (length < sizeof(*bs)) {
|
||||
LOGP(DMSC, LOGL_ERROR, "The header is too short.\n");
|
||||
if (sccp->connection_state > SCCP_CONNECTION_STATE_ESTABLISHED) {
|
||||
LOGP(DMSC, LOGL_ERROR, "Connection closing, dropping packet on: %p\n", sccp);
|
||||
msgb_free(msg);
|
||||
} else if (sccp->connection_state == SCCP_CONNECTION_STATE_ESTABLISHED
|
||||
&& conn->sccp_queue_size == 0) {
|
||||
sccp_connection_write(sccp, msg);
|
||||
msgb_free(msg);
|
||||
} else if (conn->sccp_queue_size > 10) {
|
||||
LOGP(DMSC, LOGL_ERROR, "Connection closing, dropping packet on: %p\n", sccp);
|
||||
msgb_free(msg);
|
||||
} else {
|
||||
LOGP(DMSC, LOGL_DEBUG, "Queueing packet on %p. Queue size: %d\n", sccp, conn->sccp_queue_size);
|
||||
conn->sccp_queue_size += 1;
|
||||
msgb_enqueue(&conn->sccp_queue, msg);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bsc_create_new_connection(struct gsm_subscriber_connection *conn)
|
||||
{
|
||||
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");
|
||||
return -1;
|
||||
}
|
||||
|
||||
bs = (struct bssmap_header *) msgb->l3h;
|
||||
if (bs->length < length - sizeof(*bs))
|
||||
if (!bsc_grace_allow_new_connection(net)) {
|
||||
LOGP(DMSC, LOGL_NOTICE, "BSC in grace period. No new connections.\n");
|
||||
return -1;
|
||||
|
||||
switch (bs->type) {
|
||||
case BSSAP_MSG_BSS_MANAGEMENT:
|
||||
break;
|
||||
default:
|
||||
LOGP(DMSC, LOGL_ERROR, "Unimplemented msg type: %d\n", bs->type);
|
||||
}
|
||||
|
||||
sccp = sccp_connection_socket();
|
||||
if (!sccp) {
|
||||
LOGP(DMSC, LOGL_ERROR, "Failed to allocate memory.\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
bsc_con = talloc_zero(conn->bts, struct osmo_bsc_sccp_con);
|
||||
if (!bsc_con) {
|
||||
LOGP(DMSC, LOGL_ERROR, "Failed to allocate.\n");
|
||||
sccp_connection_free(sccp);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* callbacks */
|
||||
sccp->state_cb = msc_outgoing_sccp_state;
|
||||
sccp->data_cb = msc_outgoing_sccp_data;
|
||||
sccp->data_ctx = bsc_con;
|
||||
|
||||
/* prepare the timers */
|
||||
bsc_con->sccp_it_timeout.cb = sccp_it_timeout;
|
||||
bsc_con->sccp_it_timeout.data = bsc_con;
|
||||
bsc_con->sccp_cc_timeout.cb = sccp_cc_timeout;
|
||||
bsc_con->sccp_cc_timeout.data = bsc_con;
|
||||
|
||||
INIT_LLIST_HEAD(&bsc_con->sccp_queue);
|
||||
|
||||
bsc_con->sccp = sccp;
|
||||
bsc_con->msc_con = net->msc_data->msc_con;
|
||||
bsc_con->conn = conn;
|
||||
llist_add(&bsc_con->entry, &active_connections);
|
||||
conn->sccp_con = bsc_con;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bsc_open_connection(struct osmo_bsc_sccp_con *conn, struct msgb *msg)
|
||||
{
|
||||
bsc_schedule_timer(&conn->sccp_cc_timeout, 10, 0);
|
||||
sccp_connection_connect(conn->sccp, &sccp_ssn_bssap, msg);
|
||||
msgb_free(msg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bsc_delete_connection(struct osmo_bsc_sccp_con *sccp)
|
||||
{
|
||||
if (!sccp)
|
||||
return 0;
|
||||
|
||||
if (sccp->conn)
|
||||
LOGP(DMSC, LOGL_ERROR, "Should have been cleared.\n");
|
||||
|
||||
llist_del(&sccp->entry);
|
||||
bsc_del_timer(&sccp->sccp_it_timeout);
|
||||
bsc_del_timer(&sccp->sccp_cc_timeout);
|
||||
talloc_free(sccp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
static int handle_msc_signal(unsigned int subsys, unsigned int signal,
|
||||
void *handler_data, void *signal_data)
|
||||
{
|
||||
struct osmo_msc_data *data;
|
||||
|
||||
if (subsys != SS_MSC)
|
||||
return 0;
|
||||
|
||||
data = (struct osmo_msc_data *) signal_data;
|
||||
if (signal == S_MSC_LOST)
|
||||
bsc_close_connections(data->msc_con);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -72,7 +281,9 @@ int osmo_bsc_sccp_init(struct gsm_network *gsmnet)
|
|||
sccp_set_log_area(DSCCP);
|
||||
sccp_system_init(msc_sccp_write_ipa, gsmnet);
|
||||
sccp_connection_set_incoming(&sccp_ssn_bssap, msc_sccp_accept, NULL);
|
||||
sccp_set_read(&sccp_ssn_bssap, msc_sccp_read, NULL);
|
||||
sccp_set_read(&sccp_ssn_bssap, msc_sccp_read, gsmnet);
|
||||
|
||||
register_signal_handler(SS_MSC, handle_msc_signal, gsmnet);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -60,8 +60,9 @@ static int config_write_msc(struct vty *vty)
|
|||
if (data->core_ncc != -1)
|
||||
vty_out(vty, " core-mobile-network-code %d%s",
|
||||
data->core_ncc, VTY_NEWLINE);
|
||||
vty_out(vty, " ip.access rtp-payload %d%s",
|
||||
data->rtp_payload, VTY_NEWLINE);
|
||||
if (data->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, " ip %s%s", data->msc_ip, VTY_NEWLINE);
|
||||
vty_out(vty, " port %d%s", data->msc_port, VTY_NEWLINE);
|
||||
|
@ -112,15 +113,13 @@ DEFUN(cfg_net_bsc_ncc,
|
|||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_net_bsc_rtp_payload,
|
||||
cfg_net_bsc_rtp_payload_cmd,
|
||||
"ip.access rtp-payload <0-255>",
|
||||
IPA_STR
|
||||
"Set the rtp-payload for the RTP stream\n"
|
||||
"RTP payload number\n")
|
||||
DEFUN(cfg_net_bsc_mcc,
|
||||
cfg_net_bsc_mcc_cmd,
|
||||
"core-mobile-country-code <0-255>",
|
||||
"Use this country code for the backbone\n" "MCC value\n")
|
||||
{
|
||||
struct osmo_msc_data *data = osmo_msc_data(vty);
|
||||
data->rtp_payload = atoi(argv[0]);
|
||||
data->core_mcc = atoi(argv[0]);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
|
@ -269,7 +268,7 @@ int bsc_vty_init_extra(void)
|
|||
install_default(MSC_NODE);
|
||||
install_element(MSC_NODE, &cfg_net_bsc_token_cmd);
|
||||
install_element(MSC_NODE, &cfg_net_bsc_ncc_cmd);
|
||||
install_element(MSC_NODE, &cfg_net_bsc_rtp_payload_cmd);
|
||||
install_element(MSC_NODE, &cfg_net_bsc_mcc_cmd);
|
||||
install_element(MSC_NODE, &cfg_net_bsc_rtp_base_cmd);
|
||||
install_element(MSC_NODE, &cfg_net_bsc_codec_list_cmd);
|
||||
install_element(MSC_NODE, &cfg_net_msc_ip_cmd);
|
||||
|
|
|
@ -30,13 +30,179 @@
|
|||
#include <openbsc/chan_alloc.h>
|
||||
#include <openbsc/handover.h>
|
||||
#include <openbsc/debug.h>
|
||||
#include <openbsc/gsm_04_08.h>
|
||||
|
||||
#include <osmocore/protocol/gsm_08_08.h>
|
||||
|
||||
#include <osmocore/talloc.h>
|
||||
|
||||
#define GSM0808_T10_VALUE 6, 0
|
||||
|
||||
static LLIST_HEAD(sub_connections);
|
||||
|
||||
static void rll_ind_cb(struct gsm_lchan *, uint8_t, void *, enum bsc_rllr_ind);
|
||||
static void send_sapi_reject(struct gsm_subscriber_connection *conn, int link_id);
|
||||
static void handle_release(struct gsm_subscriber_connection *conn, struct bsc_api *bsc, struct gsm_lchan *lchan);
|
||||
static void handle_chan_ack(struct gsm_subscriber_connection *conn, struct bsc_api *bsc, struct gsm_lchan *lchan);
|
||||
static void handle_chan_nack(struct gsm_subscriber_connection *conn, struct bsc_api *bsc, struct gsm_lchan *lchan);
|
||||
|
||||
/* GSM 08.08 3.2.2.33 */
|
||||
static u_int8_t lchan_to_chosen_channel(struct gsm_lchan *lchan)
|
||||
{
|
||||
u_int8_t channel_mode = 0, channel = 0;
|
||||
|
||||
switch (lchan->tch_mode) {
|
||||
case GSM48_CMODE_SPEECH_V1:
|
||||
case GSM48_CMODE_SPEECH_EFR:
|
||||
case GSM48_CMODE_SPEECH_AMR:
|
||||
channel_mode = 0x9;
|
||||
break;
|
||||
case GSM48_CMODE_SIGN:
|
||||
channel_mode = 0x8;
|
||||
break;
|
||||
case GSM48_CMODE_DATA_14k5:
|
||||
channel_mode = 0xe;
|
||||
break;
|
||||
case GSM48_CMODE_DATA_12k0:
|
||||
channel_mode = 0xb;
|
||||
break;
|
||||
case GSM48_CMODE_DATA_6k0:
|
||||
channel_mode = 0xc;
|
||||
break;
|
||||
case GSM48_CMODE_DATA_3k6:
|
||||
channel_mode = 0xd;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (lchan->type) {
|
||||
case GSM_LCHAN_NONE:
|
||||
channel = 0x0;
|
||||
break;
|
||||
case GSM_LCHAN_SDCCH:
|
||||
channel = 0x1;
|
||||
break;
|
||||
case GSM_LCHAN_TCH_F:
|
||||
channel = 0x8;
|
||||
break;
|
||||
case GSM_LCHAN_TCH_H:
|
||||
channel = 0x9;
|
||||
break;
|
||||
case GSM_LCHAN_UNKNOWN:
|
||||
LOGP(DMSC, LOGL_ERROR, "Unknown lchan type: %p\n", lchan);
|
||||
break;
|
||||
}
|
||||
|
||||
return channel_mode << 4 | channel;
|
||||
}
|
||||
|
||||
static u_int8_t chan_mode_to_speech(struct gsm_lchan *lchan)
|
||||
{
|
||||
int mode = 0;
|
||||
|
||||
switch (lchan->tch_mode) {
|
||||
case GSM48_CMODE_SPEECH_V1:
|
||||
mode = 1;
|
||||
break;
|
||||
case GSM48_CMODE_SPEECH_EFR:
|
||||
mode = 0x11;
|
||||
break;
|
||||
case GSM48_CMODE_SPEECH_AMR:
|
||||
mode = 0x21;
|
||||
break;
|
||||
case GSM48_CMODE_SIGN:
|
||||
case GSM48_CMODE_DATA_14k5:
|
||||
case GSM48_CMODE_DATA_12k0:
|
||||
case GSM48_CMODE_DATA_6k0:
|
||||
case GSM48_CMODE_DATA_3k6:
|
||||
default:
|
||||
LOGP(DMSC, LOGL_ERROR, "Using non speech mode: %d\n", mode);
|
||||
return 0;
|
||||
break;
|
||||
}
|
||||
|
||||
/* assume to always do AMR HR on any TCH type */
|
||||
if (lchan->type == GSM_LCHAN_TCH_H ||
|
||||
lchan->tch_mode == GSM48_CMODE_SPEECH_AMR)
|
||||
mode |= 0x4;
|
||||
|
||||
return mode;
|
||||
}
|
||||
|
||||
static void assignment_t10_timeout(void *_conn)
|
||||
{
|
||||
struct bsc_api *api;
|
||||
struct gsm_subscriber_connection *conn =
|
||||
(struct gsm_subscriber_connection *) _conn;
|
||||
|
||||
LOGP(DMSC, LOGL_ERROR, "Assigment T10 timeout on %p\n", conn);
|
||||
|
||||
/* normal release on the secondary channel */
|
||||
lchan_release(conn->secondary_lchan, 0, 1);
|
||||
conn->secondary_lchan = NULL;
|
||||
|
||||
/* inform them about the failure */
|
||||
api = conn->bts->network->bsc_api;
|
||||
api->assign_fail(conn, GSM0808_CAUSE_NO_RADIO_RESOURCE_AVAILABLE, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Start a new assignment and make sure that it is completed within T10 either
|
||||
* positively, negatively or by the timeout.
|
||||
*
|
||||
* 1.) allocate a new lchan
|
||||
* 2.) copy the encryption key and other data from the
|
||||
* old to the new channel.
|
||||
* 3.) RSL Channel Activate this channel and wait
|
||||
*
|
||||
* -> Signal handler for the LCHAN
|
||||
* 4.) Send GSM 04.08 assignment command to the MS
|
||||
*
|
||||
* -> Assignment Complete/Assignment Failure
|
||||
* 5.) Release the SDCCH, continue signalling on the new link
|
||||
*/
|
||||
static int handle_new_assignment(struct gsm_subscriber_connection *conn, int chan_mode, int full_rate)
|
||||
{
|
||||
struct gsm_lchan *new_lchan;
|
||||
int chan_type;
|
||||
|
||||
chan_type = full_rate ? GSM_LCHAN_TCH_F : GSM_LCHAN_TCH_H;
|
||||
|
||||
new_lchan = lchan_alloc(conn->bts, chan_type, 0);
|
||||
|
||||
if (!new_lchan) {
|
||||
LOGP(DMSC, LOGL_NOTICE, "No free channel.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* copy old data to the new channel */
|
||||
memcpy(&new_lchan->encr, &conn->lchan->encr, sizeof(new_lchan->encr));
|
||||
new_lchan->ms_power = conn->lchan->ms_power;
|
||||
new_lchan->bs_power = conn->lchan->bs_power;
|
||||
|
||||
/* copy new data to it */
|
||||
new_lchan->tch_mode = chan_mode;
|
||||
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 (rsl_chan_activate_lchan(new_lchan, 0x1, 0, 0) < 0) {
|
||||
LOGP(DHO, LOGL_ERROR, "could not activate channel\n");
|
||||
lchan_free(new_lchan);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* remember that we have the channel */
|
||||
conn->secondary_lchan = new_lchan;
|
||||
new_lchan->conn = conn;
|
||||
|
||||
rsl_lchan_set_state(new_lchan, LCHAN_S_ACT_REQ);
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct gsm_subscriber_connection *subscr_con_allocate(struct gsm_lchan *lchan)
|
||||
{
|
||||
|
@ -57,9 +223,6 @@ struct gsm_subscriber_connection *subscr_con_allocate(struct gsm_lchan *lchan)
|
|||
/* TODO: move subscriber put here... */
|
||||
void subscr_con_free(struct gsm_subscriber_connection *conn)
|
||||
{
|
||||
struct gsm_lchan *lchan;
|
||||
|
||||
|
||||
if (!conn)
|
||||
return;
|
||||
|
||||
|
@ -70,16 +233,23 @@ void subscr_con_free(struct gsm_subscriber_connection *conn)
|
|||
}
|
||||
|
||||
|
||||
if (conn->ho_lchan)
|
||||
if (conn->ho_lchan) {
|
||||
LOGP(DNM, LOGL_ERROR, "The ho_lchan should have been cleared.\n");
|
||||
conn->ho_lchan->conn = NULL;
|
||||
}
|
||||
|
||||
if (conn->lchan) {
|
||||
LOGP(DNM, LOGL_ERROR, "The lchan should have been cleared.\n");
|
||||
conn->lchan->conn = NULL;
|
||||
}
|
||||
|
||||
if (conn->secondary_lchan) {
|
||||
LOGP(DNM, LOGL_ERROR, "The secondary_lchan should have been cleared.\n");
|
||||
conn->secondary_lchan->conn = NULL;
|
||||
}
|
||||
|
||||
llist_del(&conn->entry);
|
||||
|
||||
lchan = conn->lchan;
|
||||
talloc_free(conn);
|
||||
|
||||
if (lchan)
|
||||
lchan->conn = NULL;
|
||||
}
|
||||
|
||||
int bsc_api_init(struct gsm_network *network, struct bsc_api *api)
|
||||
|
@ -89,12 +259,18 @@ int bsc_api_init(struct gsm_network *network, struct bsc_api *api)
|
|||
}
|
||||
|
||||
int gsm0808_submit_dtap(struct gsm_subscriber_connection *conn,
|
||||
struct msgb *msg, int link_id)
|
||||
struct msgb *msg, int link_id, int allow_sach)
|
||||
{
|
||||
uint8_t sapi = link_id & 0x7;
|
||||
msg->lchan = conn->lchan;
|
||||
msg->trx = msg->lchan->ts->trx;
|
||||
|
||||
/* If we are on a TCH and need to submit a SMS (on SAPI=3) we need to use the SACH */
|
||||
if (allow_sach && sapi != 0) {
|
||||
if (conn->lchan->type == GSM_LCHAN_TCH_F || conn->lchan->type == GSM_LCHAN_TCH_H)
|
||||
link_id |= 0x40;
|
||||
}
|
||||
|
||||
msg->l3h = msg->data;
|
||||
if (conn->lchan->sapis[sapi] == LCHAN_SAPI_UNUSED) {
|
||||
OBSC_LINKID_CB(msg) = link_id;
|
||||
|
@ -111,15 +287,40 @@ 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.
|
||||
* 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.
|
||||
*/
|
||||
int gsm0808_assign_req(struct gsm_subscriber_connection *conn, int chan_type, int audio)
|
||||
int gsm0808_assign_req(struct gsm_subscriber_connection *conn, int chan_mode, int full_rate)
|
||||
{
|
||||
struct bsc_api *api;
|
||||
api = conn->bts->network->bsc_api;
|
||||
|
||||
api->assign_fail(conn, 0);
|
||||
if (conn->lchan->type == GSM_LCHAN_SDCCH) {
|
||||
if (handle_new_assignment(conn, chan_mode, full_rate) != 0)
|
||||
goto error;
|
||||
} 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;
|
||||
}
|
||||
|
||||
gsm48_lchan_modify(conn->lchan, chan_mode);
|
||||
}
|
||||
|
||||
/* we will now start the timer to complete the assignment */
|
||||
conn->T10.cb = assignment_t10_timeout;
|
||||
conn->T10.data = conn;
|
||||
bsc_schedule_timer(&conn->T10, GSM0808_T10_VALUE);
|
||||
return 0;
|
||||
|
||||
error:
|
||||
api->assign_fail(conn, 0, NULL);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int gsm0808_page(struct gsm_bts *bts, unsigned int page_group, unsigned int mi_len,
|
||||
|
@ -147,6 +348,126 @@ int bsc_upqueue(struct gsm_network *net)
|
|||
return work;
|
||||
}
|
||||
|
||||
static void handle_ass_compl(struct gsm_subscriber_connection *conn,
|
||||
struct msgb *msg)
|
||||
{
|
||||
struct gsm48_hdr *gh;
|
||||
struct bsc_api *api = conn->bts->network->bsc_api;
|
||||
|
||||
if (conn->secondary_lchan != msg->lchan) {
|
||||
LOGP(DMSC, LOGL_ERROR, "Assignment Compl should occur on second lchan.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
gh = msgb_l3(msg);
|
||||
if (msgb_l3len(msg) - sizeof(*gh) != 1) {
|
||||
LOGP(DMSC, LOGL_ERROR, "Assignment Compl invalid: %d\n",
|
||||
msgb_l3len(msg) - sizeof(*gh));
|
||||
return;
|
||||
}
|
||||
|
||||
/* swap channels */
|
||||
bsc_del_timer(&conn->T10);
|
||||
|
||||
lchan_release(conn->lchan, 0, 1);
|
||||
conn->lchan = conn->secondary_lchan;
|
||||
conn->secondary_lchan = NULL;
|
||||
|
||||
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],
|
||||
lchan_to_chosen_channel(conn->lchan),
|
||||
conn->lchan->encr.alg_id,
|
||||
chan_mode_to_speech(conn->lchan));
|
||||
}
|
||||
|
||||
static void handle_ass_fail(struct gsm_subscriber_connection *conn,
|
||||
struct msgb *msg)
|
||||
{
|
||||
struct bsc_api *api = conn->bts->network->bsc_api;
|
||||
uint8_t *rr_failure;
|
||||
struct gsm48_hdr *gh;
|
||||
|
||||
|
||||
if (conn->lchan != msg->lchan) {
|
||||
LOGP(DMSC, LOGL_ERROR, "Assignment failure should occur on primary lchan.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* stop the timer and release it */
|
||||
bsc_del_timer(&conn->T10);
|
||||
lchan_release(conn->secondary_lchan, 0, 1);
|
||||
conn->secondary_lchan = NULL;
|
||||
|
||||
gh = msgb_l3(msg);
|
||||
if (msgb_l3len(msg) - sizeof(*gh) != 1) {
|
||||
LOGP(DMSC, LOGL_ERROR, "assignemnt failure unhandled: %d\n",
|
||||
msgb_l3len(msg) - sizeof(*gh));
|
||||
rr_failure = NULL;
|
||||
} else {
|
||||
rr_failure = &gh->data[0];
|
||||
}
|
||||
|
||||
api->assign_fail(conn,
|
||||
GSM0808_CAUSE_RADIO_INTERFACE_MESSAGE_FAILURE,
|
||||
rr_failure);
|
||||
}
|
||||
|
||||
static void dispatch_dtap(struct gsm_subscriber_connection *conn,
|
||||
uint8_t link_id, struct msgb *msg)
|
||||
{
|
||||
struct bsc_api *api = msg->lchan->ts->trx->bts->network->bsc_api;
|
||||
struct gsm48_hdr *gh;
|
||||
uint8_t pdisc;
|
||||
int rc;
|
||||
|
||||
if (msgb_l3len(msg) < sizeof(*gh)) {
|
||||
LOGP(DMSC, LOGL_ERROR, "Message too short for a GSM48 header.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
gh = msgb_l3(msg);
|
||||
pdisc = gh->proto_discr & 0x0f;
|
||||
switch (pdisc) {
|
||||
case GSM48_PDISC_RR:
|
||||
switch (gh->msg_type) {
|
||||
case GSM48_MT_RR_CIPH_M_COMPL:
|
||||
if (api->cipher_mode_compl)
|
||||
return api->cipher_mode_compl(conn, msg,
|
||||
conn->lchan->encr.alg_id);
|
||||
break;
|
||||
case GSM48_MT_RR_ASS_COMPL:
|
||||
handle_ass_compl(conn, msg);
|
||||
break;
|
||||
case GSM48_MT_RR_ASS_FAIL:
|
||||
handle_ass_fail(conn, msg);
|
||||
break;
|
||||
case GSM48_MT_RR_CHAN_MODE_MODIF_ACK:
|
||||
bsc_del_timer(&conn->T10);
|
||||
rc = gsm48_rx_rr_modif_ack(msg);
|
||||
if (rc < 0 && api->assign_fail) {
|
||||
api->assign_fail(conn,
|
||||
GSM0808_CAUSE_NO_RADIO_RESOURCE_AVAILABLE,
|
||||
NULL);
|
||||
} else if (rc >= 0 && api->assign_compl)
|
||||
api->assign_compl(conn, 0,
|
||||
lchan_to_chosen_channel(conn->lchan),
|
||||
conn->lchan->encr.alg_id,
|
||||
chan_mode_to_speech(conn->lchan));
|
||||
return;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case GSM48_PDISC_MM:
|
||||
break;
|
||||
}
|
||||
|
||||
/* default case */
|
||||
if (api->dtap)
|
||||
api->dtap(conn, link_id, msg);
|
||||
}
|
||||
|
||||
int gsm0408_rcvmsg(struct msgb *msg, uint8_t link_id)
|
||||
{
|
||||
int rc;
|
||||
|
@ -161,7 +482,7 @@ int gsm0408_rcvmsg(struct msgb *msg, uint8_t link_id)
|
|||
|
||||
|
||||
if (lchan->conn) {
|
||||
api->dtap(lchan->conn, msg);
|
||||
dispatch_dtap(lchan->conn, link_id, msg);
|
||||
} else {
|
||||
rc = BSC_API_CONN_POL_REJECT;
|
||||
lchan->conn = subscr_con_allocate(msg->lchan);
|
||||
|
@ -170,6 +491,7 @@ int gsm0408_rcvmsg(struct msgb *msg, uint8_t link_id)
|
|||
rc = api->compl_l3(lchan->conn, msg, 0);
|
||||
|
||||
if (rc != BSC_API_CONN_POL_ACCEPT) {
|
||||
lchan->conn->lchan = NULL;
|
||||
subscr_con_free(lchan->conn);
|
||||
lchan_release(lchan, 0, 0);
|
||||
}
|
||||
|
@ -179,9 +501,25 @@ int gsm0408_rcvmsg(struct msgb *msg, uint8_t link_id)
|
|||
}
|
||||
|
||||
int gsm0808_cipher_mode(struct gsm_subscriber_connection *conn, int cipher,
|
||||
uint8_t *key, int len)
|
||||
const uint8_t *key, int len, int include_imeisv)
|
||||
{
|
||||
return -1;
|
||||
if (cipher > 0 && key == NULL) {
|
||||
LOGP(DRSL, LOGL_ERROR, "Need to have an encrytpion key.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (len > MAX_A5_KEY_LEN) {
|
||||
LOGP(DRSL, LOGL_ERROR, "The key is too long: %d\n", len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
conn->lchan->encr.alg_id = RSL_ENC_ALG_A5(cipher);
|
||||
if (key) {
|
||||
conn->lchan->encr.key_len = len;
|
||||
memcpy(conn->lchan->encr.key, key, len);
|
||||
}
|
||||
|
||||
return gsm48_send_rr_ciph_mode(conn->lchan, include_imeisv);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -192,15 +530,19 @@ int gsm0808_clear(struct gsm_subscriber_connection *conn)
|
|||
if (conn->ho_lchan)
|
||||
bsc_clear_handover(conn);
|
||||
|
||||
if (conn->lchan) {
|
||||
if (conn->secondary_lchan)
|
||||
lchan_release(conn->secondary_lchan, 0, 1);
|
||||
|
||||
if (conn->lchan)
|
||||
lchan_release(conn->lchan, 1, 0);
|
||||
conn->lchan->conn = NULL;
|
||||
}
|
||||
|
||||
conn->lchan = NULL;
|
||||
conn->secondary_lchan = NULL;
|
||||
conn->ho_lchan = NULL;
|
||||
conn->bts = NULL;
|
||||
|
||||
bsc_del_timer(&conn->T10);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -222,6 +564,14 @@ static void rll_ind_cb(struct gsm_lchan *lchan, uint8_t link_id, void *_data, en
|
|||
{
|
||||
struct msgb *msg = _data;
|
||||
|
||||
/*
|
||||
* There seems to be a small window that the RLL timer can
|
||||
* fire after a lchan_release call and before the S_CHALLOC_FREED
|
||||
* is called. Check if a conn is set before proceeding.
|
||||
*/
|
||||
if (!lchan->conn)
|
||||
return;
|
||||
|
||||
switch (rllr_ind) {
|
||||
case BSC_RLLR_IND_EST_CONF:
|
||||
rsl_data_request(msg, OBSC_LINKID_CB(msg));
|
||||
|
@ -240,9 +590,8 @@ static int bsc_handle_lchan_signal(unsigned int subsys, unsigned int signal,
|
|||
{
|
||||
struct bsc_api *bsc;
|
||||
struct gsm_lchan *lchan;
|
||||
struct gsm_subscriber_connection *conn;
|
||||
|
||||
if (subsys != SS_LCHAN || signal != S_LCHAN_UNEXPECTED_RELEASE)
|
||||
if (subsys != SS_LCHAN)
|
||||
return 0;
|
||||
|
||||
lchan = (struct gsm_lchan *)signal_data;
|
||||
|
@ -253,18 +602,72 @@ static int bsc_handle_lchan_signal(unsigned int subsys, unsigned int signal,
|
|||
if (!bsc)
|
||||
return 0;
|
||||
|
||||
conn = lchan->conn;
|
||||
if (bsc->clear_request)
|
||||
bsc->clear_request(conn, 0);
|
||||
switch (signal) {
|
||||
case S_LCHAN_UNEXPECTED_RELEASE:
|
||||
handle_release(lchan->conn, bsc, lchan);
|
||||
break;
|
||||
case S_LCHAN_ACTIVATE_ACK:
|
||||
handle_chan_ack(lchan->conn, bsc, lchan);
|
||||
break;
|
||||
case S_LCHAN_ACTIVATE_NACK:
|
||||
handle_chan_nack(lchan->conn, bsc, lchan);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void handle_release(struct gsm_subscriber_connection *conn,
|
||||
struct bsc_api *bsc, struct gsm_lchan *lchan)
|
||||
{
|
||||
int destruct = 1;
|
||||
|
||||
/* now give up all channels */
|
||||
if (conn->lchan == lchan)
|
||||
conn->lchan = NULL;
|
||||
if (conn->ho_lchan == lchan)
|
||||
conn->ho_lchan = NULL;
|
||||
if (conn->secondary_lchan == lchan) {
|
||||
bsc_del_timer(&conn->T10);
|
||||
conn->secondary_lchan = NULL;
|
||||
|
||||
bsc->assign_fail(conn,
|
||||
GSM0808_CAUSE_RADIO_INTERFACE_FAILURE,
|
||||
NULL);
|
||||
}
|
||||
|
||||
lchan->conn = NULL;
|
||||
|
||||
/* clear the connection now */
|
||||
if (bsc->clear_request)
|
||||
destruct = bsc->clear_request(conn, 0);
|
||||
|
||||
|
||||
gsm0808_clear(conn);
|
||||
|
||||
return 0;
|
||||
if (destruct)
|
||||
subscr_con_free(conn);
|
||||
}
|
||||
|
||||
static void handle_chan_ack(struct gsm_subscriber_connection *conn,
|
||||
struct bsc_api *api, struct gsm_lchan *lchan)
|
||||
{
|
||||
if (conn->secondary_lchan != lchan)
|
||||
return;
|
||||
|
||||
LOGP(DMSC, LOGL_NOTICE, "Sending assignment on chan: %p\n", lchan);
|
||||
gsm48_send_rr_ass_cmd(conn->lchan, lchan, 0x3);
|
||||
}
|
||||
|
||||
static void handle_chan_nack(struct gsm_subscriber_connection *conn,
|
||||
struct bsc_api *api, struct gsm_lchan *lchan)
|
||||
{
|
||||
if (conn->secondary_lchan != lchan)
|
||||
return;
|
||||
|
||||
LOGP(DMSC, LOGL_ERROR, "Channel activation failed. Waiting for timeout now\n");
|
||||
conn->secondary_lchan->conn = NULL;
|
||||
conn->secondary_lchan = NULL;
|
||||
}
|
||||
|
||||
static __attribute__((constructor)) void on_dso_load_bsc(void)
|
||||
|
|
|
@ -44,6 +44,7 @@
|
|||
#include <openbsc/gprs_ns.h>
|
||||
#include <openbsc/system_information.h>
|
||||
#include <openbsc/debug.h>
|
||||
#include <openbsc/paging.h>
|
||||
|
||||
#include "../bscconfig.h"
|
||||
|
||||
|
|
|
@ -281,13 +281,6 @@ struct gsm_lchan *lchan_alloc(struct gsm_bts *bts, enum gsm_chan_t type,
|
|||
|
||||
/* clear multi rate config */
|
||||
memset(&lchan->mr_conf, 0, sizeof(lchan->mr_conf));
|
||||
|
||||
/* clear per MSC/BSC data */
|
||||
if (lchan->conn) {
|
||||
LOGP(DRLL, LOGL_ERROR, "lchan->conn should be NULL.\n");
|
||||
subscr_con_free(lchan->conn);
|
||||
lchan->conn = NULL;
|
||||
}
|
||||
} else {
|
||||
struct challoc_signal_data sig;
|
||||
sig.bts = bts;
|
||||
|
@ -338,7 +331,6 @@ void lchan_free(struct gsm_lchan *lchan)
|
|||
|
||||
if (lchan->conn) {
|
||||
LOGP(DRLL, LOGL_ERROR, "the subscriber connection should be gone.\n");
|
||||
subscr_con_free(lchan->conn);
|
||||
lchan->conn = NULL;
|
||||
}
|
||||
|
||||
|
@ -422,6 +414,7 @@ int lchan_release(struct gsm_lchan *lchan, int sach_deact, int reason)
|
|||
DEBUGP(DRLL, "%s starting release sequence\n", gsm_lchan_name(lchan));
|
||||
rsl_lchan_set_state(lchan, LCHAN_S_REL_REQ);
|
||||
|
||||
lchan->conn = NULL;
|
||||
lchan->release_reason = reason;
|
||||
lchan->sach_deact = sach_deact;
|
||||
_lchan_handle_release(lchan);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
INCLUDES = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir)
|
||||
AM_CFLAGS=-Wall -fno-strict-aliasing $(LIBOSMOCORE_CFLAGS) $(LIBOSMOVTY_CFLAGS)
|
||||
AM_LDFLAGS = $(LIBOSMOCORE_LIBS) $(LIBOSMOVTY_LIBS)
|
||||
AM_CFLAGS=-Wall -fno-strict-aliasing $(LIBOSMOCORE_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(COVERAGE_CFLAGS)
|
||||
AM_LDFLAGS = $(LIBOSMOCORE_LIBS) $(LIBOSMOVTY_LIBS) $(COVERAGE_LDFLAGS)
|
||||
|
||||
noinst_LIBRARIES = libgb.a
|
||||
noinst_HEADERS = gprs_sndcp.h
|
||||
|
|
|
@ -104,7 +104,7 @@ static int gsm48_conn_sendmsg(struct msgb *msg, struct gsm_subscriber_connection
|
|||
gh->proto_discr, gh->msg_type);
|
||||
}
|
||||
|
||||
return gsm0808_submit_dtap(conn, msg, 0);
|
||||
return gsm0808_submit_dtap(conn, msg, 0, 0);
|
||||
}
|
||||
|
||||
int gsm48_cc_tx_notify_ss(struct gsm_trans *trans, const char *message)
|
||||
|
@ -328,8 +328,6 @@ void gsm0408_clear_request(struct gsm_subscriber_connection *conn, uint32_t caus
|
|||
if (trans->conn == conn)
|
||||
trans_free(trans);
|
||||
}
|
||||
|
||||
subscr_con_free(conn);
|
||||
}
|
||||
|
||||
/* Chapter 9.2.14 : Send LOCATION UPDATING REJECT */
|
||||
|
@ -1200,9 +1198,6 @@ static int gsm0408_rcv_rr(struct gsm_subscriber_connection *conn, struct msgb *m
|
|||
case GSM48_MT_RR_PAG_RESP:
|
||||
rc = gsm48_rx_rr_pag_resp(conn, msg);
|
||||
break;
|
||||
case GSM48_MT_RR_CHAN_MODE_MODIF_ACK:
|
||||
rc = gsm48_rx_rr_modif_ack(msg);
|
||||
break;
|
||||
case GSM48_MT_RR_STATUS:
|
||||
rc = gsm48_rx_rr_status(msg);
|
||||
break;
|
||||
|
|
|
@ -142,7 +142,7 @@ static int gsm411_sendmsg(struct gsm_subscriber_connection *conn, struct msgb *m
|
|||
{
|
||||
DEBUGP(DSMS, "GSM4.11 TX %s\n", hexdump(msg->data, msg->len));
|
||||
msg->l3h = msg->data;
|
||||
return gsm0808_submit_dtap(conn, msg, link_id);
|
||||
return gsm0808_submit_dtap(conn, msg, link_id, 1);
|
||||
}
|
||||
|
||||
/* SMC TC1* is expired */
|
||||
|
|
|
@ -107,7 +107,7 @@ int gsm0480_send_ussd_response(struct gsm_subscriber_connection *conn,
|
|||
| (1<<7); /* TI direction = 1 */
|
||||
gh->msg_type = GSM0480_MTYPE_RELEASE_COMPLETE;
|
||||
|
||||
return gsm0808_submit_dtap(conn, msg, 0);
|
||||
return gsm0808_submit_dtap(conn, msg, 0, 0);
|
||||
}
|
||||
|
||||
int gsm0480_send_ussd_reject(struct gsm_subscriber_connection *conn,
|
||||
|
@ -136,7 +136,7 @@ int gsm0480_send_ussd_reject(struct gsm_subscriber_connection *conn,
|
|||
gh->proto_discr |= req->transaction_id | (1<<7); /* TI direction = 1 */
|
||||
gh->msg_type = GSM0480_MTYPE_RELEASE_COMPLETE;
|
||||
|
||||
return gsm0808_submit_dtap(conn, msg, 0);
|
||||
return gsm0808_submit_dtap(conn, msg, 0, 0);
|
||||
}
|
||||
|
||||
int gsm0480_send_ussdNotify(struct gsm_subscriber_connection *conn, int level, const char *text)
|
||||
|
@ -156,7 +156,7 @@ int gsm0480_send_ussdNotify(struct gsm_subscriber_connection *conn, int level, c
|
|||
gh->proto_discr = GSM48_PDISC_NC_SS;
|
||||
gh->msg_type = GSM0480_MTYPE_REGISTER;
|
||||
|
||||
return gsm0808_submit_dtap(conn, msg, 0);
|
||||
return gsm0808_submit_dtap(conn, msg, 0, 0);
|
||||
}
|
||||
|
||||
int gsm0480_send_releaseComplete(struct gsm_subscriber_connection *conn)
|
||||
|
@ -172,5 +172,5 @@ int gsm0480_send_releaseComplete(struct gsm_subscriber_connection *conn)
|
|||
gh->proto_discr = GSM48_PDISC_NC_SS;
|
||||
gh->msg_type = GSM0480_MTYPE_RELEASE_COMPLETE;
|
||||
|
||||
return gsm0808_submit_dtap(conn, msg, 0);
|
||||
return gsm0808_submit_dtap(conn, msg, 0, 0);
|
||||
}
|
||||
|
|
|
@ -318,7 +318,7 @@ struct gsm_network *gsm_network_init(u_int16_t country_code, u_int16_t network_c
|
|||
net->msc_data->ping_timeout = 20;
|
||||
net->msc_data->pong_timeout = 5;
|
||||
net->msc_data->core_ncc = -1;
|
||||
net->msc_data->rtp_payload = 126;
|
||||
net->msc_data->core_mcc = -1;
|
||||
net->msc_data->rtp_base = 4000;
|
||||
|
||||
gsm_net_update_ctype(net);
|
||||
|
|
|
@ -219,3 +219,45 @@ void subscr_put_channel(struct gsm_subscriber_connection *conn)
|
|||
subscr_send_paging_request(conn->subscr);
|
||||
}
|
||||
|
||||
struct gsm_subscriber *subscr_get_or_create(struct gsm_network *net,
|
||||
const char *imsi)
|
||||
{
|
||||
struct gsm_subscriber *subscr;
|
||||
|
||||
llist_for_each_entry(subscr, subscr_bsc_active_subscriber(), entry) {
|
||||
if (strcmp(subscr->imsi, imsi) == 0 && subscr->net == net)
|
||||
return subscr_get(subscr);
|
||||
}
|
||||
|
||||
subscr = subscr_alloc();
|
||||
if (!subscr)
|
||||
return NULL;
|
||||
|
||||
strcpy(subscr->imsi, imsi);
|
||||
subscr->net = net;
|
||||
return subscr;
|
||||
}
|
||||
|
||||
struct gsm_subscriber *subscr_active_by_tmsi(struct gsm_network *net, uint32_t tmsi)
|
||||
{
|
||||
struct gsm_subscriber *subscr;
|
||||
|
||||
llist_for_each_entry(subscr, subscr_bsc_active_subscriber(), entry) {
|
||||
if (subscr->tmsi == tmsi && subscr->net == net)
|
||||
return subscr_get(subscr);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct gsm_subscriber *subscr_active_by_imsi(struct gsm_network *net, const char *imsi)
|
||||
{
|
||||
struct gsm_subscriber *subscr;
|
||||
|
||||
llist_for_each_entry(subscr, subscr_bsc_active_subscriber(), entry) {
|
||||
if (strcmp(subscr->imsi, imsi) == 0 && subscr->net == net)
|
||||
return subscr_get(subscr);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
INCLUDES = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir)
|
||||
AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS)
|
||||
AM_LDFLAGS = $(LIBOSMOCORE_LIBS)
|
||||
AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(COVERAGE_CFLAGS)
|
||||
AM_LDFLAGS = $(LIBOSMOCORE_LIBS) $(COVERAGE_LDFLAGS)
|
||||
|
||||
sbin_PROGRAMS = ipaccess-find ipaccess-config ipaccess-proxy
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
INCLUDES = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir)
|
||||
AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(LIBOSMOSCCP_CFLAGS)
|
||||
AM_LDFLAGS = $(LIBOSMOCORE_LIBS) $(LIBOSMOVTY_LIBS)
|
||||
AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(LIBOSMOSCCP_CFLAGS) $(COVERAGE_CFLAGS)
|
||||
AM_LDFLAGS = $(LIBOSMOCORE_LIBS) $(LIBOSMOVTY_LIBS) $(COVERAGE_LDFLAGS)
|
||||
|
||||
bin_PROGRAMS = bsc_nat
|
||||
|
||||
|
|
|
@ -36,9 +36,10 @@ static void msc_sapi_n_reject(struct gsm_subscriber_connection *conn, int dlci)
|
|||
gsm411_sapi_n_reject(conn);
|
||||
}
|
||||
|
||||
static void msc_clear_request(struct gsm_subscriber_connection *conn, uint32_t cause)
|
||||
static int msc_clear_request(struct gsm_subscriber_connection *conn, uint32_t cause)
|
||||
{
|
||||
gsm0408_clear_request(conn, cause);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int msc_compl_l3(struct gsm_subscriber_connection *conn, struct msgb *msg,
|
||||
|
@ -51,7 +52,7 @@ static int msc_compl_l3(struct gsm_subscriber_connection *conn, struct msgb *msg
|
|||
return BSC_API_CONN_POL_ACCEPT;
|
||||
}
|
||||
|
||||
static void msc_dtap(struct gsm_subscriber_connection *conn, struct msgb *msg)
|
||||
static void msc_dtap(struct gsm_subscriber_connection *conn, uint8_t link_id, struct msgb *msg)
|
||||
{
|
||||
gsm0408_dispatch(conn, msg);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
INCLUDES = $(all_includes) -I$(top_srcdir)/include
|
||||
AM_CFLAGS=-Wall -ggdb3 $(LIBOSMOCORE_CFLAGS) $(LIBOSMOSCCP_CFLAGS)
|
||||
AM_CFLAGS=-Wall -ggdb3 $(LIBOSMOCORE_CFLAGS) $(LIBOSMOSCCP_CFLAGS) $(COVERAGE_CFLAGS)
|
||||
AM_LDFLAGS = $(COVERAGE_LDFLAGS)
|
||||
|
||||
EXTRA_DIST = bsc_data.c
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
INCLUDES = $(all_includes) -I$(top_srcdir)/include
|
||||
AM_CFLAGS=-Wall -ggdb3 $(LIBOSMOCORE_CFLAGS)
|
||||
AM_CFLAGS=-Wall -ggdb3 $(LIBOSMOCORE_CFLAGS) $(COVERAGE_CFLAGS)
|
||||
AM_LDFLAGS = $(COVERAGE_LDFLAGS)
|
||||
|
||||
noinst_PROGRAMS = db_test
|
||||
|
||||
|
|
Loading…
Reference in New Issue