aboutsummaryrefslogtreecommitdiffstats
path: root/openbsc/src
diff options
context:
space:
mode:
authorHolger Hans Peter Freyther <zecke@selfish.org>2010-02-03 18:15:05 +0100
committerHolger Hans Peter Freyther <zecke@selfish.org>2010-02-03 18:15:05 +0100
commit67f5003caa830bf51f1286c0419225075b17516c (patch)
treed646a38d08486203f6998c607c774eeb1eb3aa94 /openbsc/src
parentcaf49b4c141f2b623da77bd7984a37e1b62fc75f (diff)
parent9377528c70fbe708d0fa7a6bb224fdc9e206dd22 (diff)
Merge branch 'master' into on-waves/sccp
Diffstat (limited to 'openbsc/src')
-rw-r--r--openbsc/src/Makefile.am24
-rw-r--r--[-rwxr-xr-x]openbsc/src/abis_nm.c475
-rw-r--r--openbsc/src/abis_rsl.c636
-rw-r--r--openbsc/src/bitvec.c170
-rw-r--r--openbsc/src/bs11_config.c68
-rw-r--r--openbsc/src/bsc_hack.c64
-rw-r--r--openbsc/src/bsc_init.c439
-rw-r--r--openbsc/src/bts_ipaccess_nanobts.c84
-rw-r--r--openbsc/src/bts_siemens_bs11.c66
-rw-r--r--openbsc/src/bts_unknown.c40
-rw-r--r--openbsc/src/chan_alloc.c116
-rw-r--r--openbsc/src/comp128.c230
-rw-r--r--openbsc/src/db.c474
-rw-r--r--openbsc/src/debug.c355
-rw-r--r--openbsc/src/e1_config.c5
-rw-r--r--openbsc/src/e1_input.c41
-rw-r--r--openbsc/src/gsm_04_08.c456
-rw-r--r--openbsc/src/gsm_04_08_utils.c173
-rw-r--r--openbsc/src/gsm_04_11.c373
-rw-r--r--openbsc/src/gsm_data.c224
-rw-r--r--openbsc/src/gsm_subscriber.c11
-rw-r--r--openbsc/src/gsm_subscriber_base.c4
-rw-r--r--openbsc/src/gsm_utils.c38
-rw-r--r--openbsc/src/handover_decision.c298
-rw-r--r--openbsc/src/handover_logic.c376
-rw-r--r--openbsc/src/input/ipaccess.c53
-rw-r--r--openbsc/src/ipaccess/ipaccess-config.c (renamed from openbsc/src/ipaccess-config.c)315
-rw-r--r--openbsc/src/ipaccess/ipaccess-find.c (renamed from openbsc/src/ipaccess-find.c)0
-rw-r--r--openbsc/src/ipaccess/ipaccess-firmware.c123
-rw-r--r--openbsc/src/ipaccess/ipaccess-proxy.c1127
-rw-r--r--openbsc/src/meas_rep.c114
-rw-r--r--openbsc/src/mgcp.cfg19
-rw-r--r--openbsc/src/mgcp/mgcp_main.c228
-rw-r--r--openbsc/src/mgcp/mgcp_protocol.c1144
-rw-r--r--openbsc/src/mncc.c103
-rw-r--r--openbsc/src/msgb.c12
-rw-r--r--openbsc/src/openbsc.cfg.1-14
-rw-r--r--openbsc/src/openbsc.cfg.1-22
-rw-r--r--openbsc/src/openbsc.cfg.2-24
-rw-r--r--openbsc/src/openbsc.cfg.nanobts2
-rw-r--r--openbsc/src/paging.c18
-rw-r--r--openbsc/src/rest_octets.c396
-rw-r--r--openbsc/src/rrlp.c48
-rw-r--r--openbsc/src/rtp_proxy.c275
-rw-r--r--openbsc/src/select.c12
-rw-r--r--openbsc/src/silent_call.c54
-rw-r--r--openbsc/src/statistics.c70
-rw-r--r--openbsc/src/system_information.c469
-rw-r--r--openbsc/src/talloc.c9
-rw-r--r--openbsc/src/talloc_ctx.c4
-rw-r--r--openbsc/src/telnet_interface.c23
-rw-r--r--openbsc/src/tlv_parser.c13
-rw-r--r--openbsc/src/token_auth.c8
-rw-r--r--openbsc/src/transaction.c37
-rw-r--r--openbsc/src/trau_frame.c18
-rw-r--r--openbsc/src/trau_mux.c96
-rw-r--r--openbsc/src/vty/command.c44
-rw-r--r--openbsc/src/vty/vector.c12
-rw-r--r--openbsc/src/vty/vty.c4
-rw-r--r--openbsc/src/vty_interface.c664
-rw-r--r--openbsc/src/vty_interface_layer3.c198
61 files changed, 9569 insertions, 1393 deletions
diff --git a/openbsc/src/Makefile.am b/openbsc/src/Makefile.am
index 79120081d..161c283f6 100644
--- a/openbsc/src/Makefile.am
+++ b/openbsc/src/Makefile.am
@@ -1,7 +1,8 @@
INCLUDES = $(all_includes) -I$(top_srcdir)/include
AM_CFLAGS=-Wall
-sbin_PROGRAMS = bsc_hack bs11_config ipaccess-find ipaccess-config isdnsync
+sbin_PROGRAMS = bsc_hack bs11_config ipaccess-find ipaccess-config \
+ isdnsync bsc_mgcp ipaccess-proxy
noinst_LIBRARIES = libbsc.a libmsc.a libvty.a libsccp.a
noinst_HEADERS = vty/cardshell.h
@@ -10,11 +11,14 @@ libbsc_a_SOURCES = abis_rsl.c abis_nm.c gsm_data.c gsm_04_08_utils.c \
gsm_subscriber_base.c subchan_demux.c bsc_rll.c transaction.c \
trau_frame.c trau_mux.c paging.c e1_config.c e1_input.c tlv_parser.c \
input/misdn.c input/ipaccess.c signal.c gsm_utils.c talloc.c \
- talloc_ctx.c
+ talloc_ctx.c system_information.c bitvec.c rest_octets.c \
+ rtp_proxy.c statistics.c bts_siemens_bs11.c bts_ipaccess_nanobts.c \
+ bts_unknown.c
libmsc_a_SOURCES = gsm_subscriber.c db.c telnet_interface.c \
- mncc.c rtp_proxy.c gsm_04_08.c gsm_04_11.c transaction.c \
- token_auth.c rrlp.c gsm_04_80.c ussd.c silent_call.c
+ mncc.c gsm_04_08.c gsm_04_11.c transaction.c \
+ token_auth.c rrlp.c gsm_04_80.c ussd.c silent_call.c \
+ handover_logic.c handover_decision.c meas_rep.c comp128.c
libvty_a_SOURCES = vty/buffer.c vty/command.c vty/vector.c vty/vty.c
@@ -24,11 +28,17 @@ bsc_hack_SOURCES = bsc_hack.c bsc_init.c vty_interface.c vty_interface_layer3.c
bsc_hack_LDADD = libmsc.a libbsc.a libmsc.a libvty.a -ldl -ldbi $(LIBCRYPT)
bs11_config_SOURCES = bs11_config.c abis_nm.c gsm_data.c msgb.c debug.c \
- select.c timer.c rs232.c tlv_parser.c signal.c talloc.c
+ select.c timer.c rs232.c tlv_parser.c signal.c talloc.c \
+ bts_siemens_bs11.c
-ipaccess_find_SOURCES = ipaccess-find.c select.c timer.c
+ipaccess_find_SOURCES = ipaccess/ipaccess-find.c select.c timer.c
-ipaccess_config_SOURCES = ipaccess-config.c
+ipaccess_config_SOURCES = ipaccess/ipaccess-config.c ipaccess/ipaccess-firmware.c
ipaccess_config_LDADD = libbsc.a libmsc.a libbsc.a libvty.a -ldl -ldbi $(LIBCRYPT)
isdnsync_SOURCES = isdnsync.c
+
+bsc_mgcp_SOURCES = mgcp/mgcp_main.c mgcp/mgcp_protocol.c msgb.c talloc.c debug.c select.c timer.c telnet_interface.c
+bsc_mgcp_LDADD = libvty.a
+
+ipaccess_proxy_SOURCES = ipaccess/ipaccess-proxy.c msgb.c select.c talloc.c debug.c timer.c
diff --git a/openbsc/src/abis_nm.c b/openbsc/src/abis_nm.c
index 4d4cec0a3..82d7b0254 100755..100644
--- a/openbsc/src/abis_nm.c
+++ b/openbsc/src/abis_nm.c
@@ -47,6 +47,7 @@
#define OM_ALLOC_SIZE 1024
#define OM_HEADROOM_SIZE 128
+#define IPACC_SEGMENT_SIZE 245
/* unidirectional messages from BTS to BSC */
static const enum abis_nm_msgtype reports[] = {
@@ -263,7 +264,7 @@ static const enum abis_nm_attr nm_att_settable[] = {
NM_ATT_MEAS_TYPE,
};
-static const struct tlv_definition nm_att_tlvdef = {
+const struct tlv_definition nm_att_tlvdef = {
.def = {
[NM_ATT_ABIS_CHANNEL] = { TLV_TYPE_FIXED, 3 },
[NM_ATT_ADD_INFO] = { TLV_TYPE_TL16V },
@@ -329,77 +330,6 @@ static const struct tlv_definition nm_att_tlvdef = {
[NM_ATT_HW_CONF_CHG] = { TLV_TYPE_TL16V },
[NM_ATT_OUTST_ALARM] = { TLV_TYPE_TV },
[NM_ATT_MEAS_RES] = { TLV_TYPE_TL16V },
- /* BS11 specifics */
- [NM_ATT_BS11_ESN_FW_CODE_NO] = { TLV_TYPE_TLV },
- [NM_ATT_BS11_ESN_HW_CODE_NO] = { TLV_TYPE_TLV },
- [NM_ATT_BS11_ESN_PCB_SERIAL] = { TLV_TYPE_TLV },
- [NM_ATT_BS11_BOOT_SW_VERS] = { TLV_TYPE_TLV },
- [0xd5] = { TLV_TYPE_TLV },
- [0xa8] = { TLV_TYPE_TLV },
- [NM_ATT_BS11_PASSWORD] = { TLV_TYPE_TLV },
- [NM_ATT_BS11_TXPWR] = { TLV_TYPE_TLV },
- [NM_ATT_BS11_RSSI_OFFS] = { TLV_TYPE_TLV },
- [NM_ATT_BS11_LINE_CFG] = { TLV_TYPE_TV },
- [NM_ATT_BS11_L1_PROT_TYPE] = { TLV_TYPE_TV },
- [NM_ATT_BS11_BIT_ERR_THESH] = { TLV_TYPE_FIXED, 2 },
- [NM_ATT_BS11_DIVERSITY] = { TLV_TYPE_TLV },
- [NM_ATT_BS11_LMT_LOGON_SESSION]={ TLV_TYPE_TLV },
- [NM_ATT_BS11_LMT_LOGIN_TIME] = { TLV_TYPE_TLV },
- [NM_ATT_BS11_LMT_USER_ACC_LEV] ={ TLV_TYPE_TLV },
- [NM_ATT_BS11_LMT_USER_NAME] = { TLV_TYPE_TLV },
- [NM_ATT_BS11_BTS_STATE] = { TLV_TYPE_TLV },
- [NM_ATT_BS11_E1_STATE] = { TLV_TYPE_TLV },
- [NM_ATT_BS11_PLL_MODE] = { TLV_TYPE_TLV },
- [NM_ATT_BS11_PLL] = { TLV_TYPE_TLV },
- [NM_ATT_BS11_CCLK_ACCURACY] = { TLV_TYPE_TV },
- [NM_ATT_BS11_CCLK_TYPE] = { TLV_TYPE_TV },
- /* ip.access specifics */
- [NM_ATT_IPACC_DST_IP] = { TLV_TYPE_FIXED, 4 },
- [NM_ATT_IPACC_DST_IP_PORT] = { TLV_TYPE_FIXED, 2 },
- [NM_ATT_IPACC_STREAM_ID] = { TLV_TYPE_TV, },
- [NM_ATT_IPACC_FREQ_CTRL] = { TLV_TYPE_TV, },
- [NM_ATT_IPACC_SEC_OML_CFG] = { TLV_TYPE_FIXED, 6 },
- [NM_ATT_IPACC_IP_IF_CFG] = { TLV_TYPE_FIXED, 8 },
- [NM_ATT_IPACC_IP_GW_CFG] = { TLV_TYPE_FIXED, 12 },
- [NM_ATT_IPACC_IN_SERV_TIME] = { TLV_TYPE_FIXED, 4 },
- [NM_ATT_IPACC_LOCATION] = { TLV_TYPE_TL16V },
- [NM_ATT_IPACC_PAGING_CFG] = { TLV_TYPE_FIXED, 2 },
- [NM_ATT_IPACC_UNIT_ID] = { TLV_TYPE_TL16V },
- [NM_ATT_IPACC_UNIT_NAME] = { TLV_TYPE_TL16V },
- [NM_ATT_IPACC_SNMP_CFG] = { TLV_TYPE_TL16V },
- [NM_ATT_IPACC_PRIM_OML_CFG_LIST] = { TLV_TYPE_TL16V },
- [NM_ATT_IPACC_NV_FLAGS] = { TLV_TYPE_TL16V },
- [NM_ATT_IPACC_FREQ_CTRL] = { TLV_TYPE_FIXED, 2 },
- [NM_ATT_IPACC_PRIM_OML_FB_TOUT] = { TLV_TYPE_TL16V },
- [NM_ATT_IPACC_CUR_SW_CFG] = { TLV_TYPE_TL16V },
- [NM_ATT_IPACC_TIMING_BUS] = { TLV_TYPE_TL16V },
- [NM_ATT_IPACC_CGI] = { TLV_TYPE_TL16V },
- [NM_ATT_IPACC_RAC] = { TLV_TYPE_TL16V },
- [NM_ATT_IPACC_OBJ_VERSION] = { TLV_TYPE_TL16V },
- [NM_ATT_IPACC_GPRS_PAGING_CFG]= { TLV_TYPE_TL16V },
- [NM_ATT_IPACC_NSEI] = { TLV_TYPE_TL16V },
- [NM_ATT_IPACC_BVCI] = { TLV_TYPE_TL16V },
- [NM_ATT_IPACC_NSVCI] = { TLV_TYPE_TL16V },
- [NM_ATT_IPACC_NS_CFG] = { TLV_TYPE_TL16V },
- [NM_ATT_IPACC_BSSGP_CFG] = { TLV_TYPE_TL16V },
- [NM_ATT_IPACC_NS_LINK_CFG] = { TLV_TYPE_TL16V },
- [NM_ATT_IPACC_RLC_CFG] = { TLV_TYPE_TL16V },
- [NM_ATT_IPACC_ALM_THRESH_LIST]= { TLV_TYPE_TL16V },
- [NM_ATT_IPACC_MONIT_VAL_LIST] = { TLV_TYPE_TL16V },
- [NM_ATT_IPACC_TIB_CONTROL] = { TLV_TYPE_TL16V },
- [NM_ATT_IPACC_SUPP_FEATURES] = { TLV_TYPE_TL16V },
- [NM_ATT_IPACC_CODING_SCHEMES] = { TLV_TYPE_TL16V },
- [NM_ATT_IPACC_RLC_CFG_2] = { TLV_TYPE_TL16V },
- [NM_ATT_IPACC_HEARTB_TOUT] = { TLV_TYPE_TL16V },
- [NM_ATT_IPACC_UPTIME] = { TLV_TYPE_TL16V },
- [NM_ATT_IPACC_RLC_CFG_3] = { TLV_TYPE_TL16V },
- [NM_ATT_IPACC_SSL_CFG] = { TLV_TYPE_TL16V },
- [NM_ATT_IPACC_SEC_POSSIBLE] = { TLV_TYPE_TL16V },
- [NM_ATT_IPACC_IML_SSL_STATE] = { TLV_TYPE_TL16V },
- [NM_ATT_IPACC_REVOC_DATE] = { TLV_TYPE_TL16V },
- //[0x95] = { TLV_TYPE_FIXED, 2 },
- [0x85] = { TLV_TYPE_TV },
-
},
};
@@ -422,9 +352,11 @@ int abis_nm_chcomb4pchan(enum gsm_phys_chan_config pchan)
return -EINVAL;
}
-int abis_nm_tlv_parse(struct tlv_parsed *tp, const u_int8_t *buf, int len)
+int abis_nm_tlv_parse(struct tlv_parsed *tp, struct gsm_bts *bts, const u_int8_t *buf, int len)
{
- return tlv_parse(tp, &nm_att_tlvdef, buf, len, 0, 0);
+ if (!bts->model)
+ return -EIO;
+ return tlv_parse(tp, &bts->model->nm_att_tlvdef, buf, len, 0, 0);
}
static int is_in_arr(enum abis_nm_msgtype mt, const enum abis_nm_msgtype *arr, int size)
@@ -540,11 +472,11 @@ static const char *obj_class_name(u_int8_t oc)
const char *nm_opstate_name(u_int8_t os)
{
switch (os) {
- case 1:
+ case NM_OPSTATE_DISABLED:
return "Disabled";
- case 2:
+ case NM_OPSTATE_ENABLED:
return "Enabled";
- case 0xff:
+ case NM_OPSTATE_NULL:
return "NULL";
default:
return "RFU";
@@ -598,6 +530,13 @@ const char *nm_adm_name(u_int8_t adm)
}
}
+int nm_is_running(struct gsm_nm_state *s) {
+ return (s->operational == NM_OPSTATE_ENABLED) && (
+ (s->availability == NM_AVSTATE_OK) ||
+ (s->availability == 0xff)
+ );
+}
+
static void debugp_foh(struct abis_om_fom_hdr *foh)
{
DEBUGP(DNM, "OC=%s(%02x) INST=(%02x,%02x,%02x) ",
@@ -635,7 +574,7 @@ objclass2nmstate(struct gsm_bts *bts, u_int8_t obj_class,
nm_state = &trx->bb_transc.nm_state;
break;
case NM_OC_CHANNEL:
- if (obj_inst->trx_nr > bts->num_trx) {
+ if (obj_inst->trx_nr >= bts->num_trx) {
DEBUGPC(DNM, "TRX %u does not exist ", obj_inst->trx_nr);
return NULL;
}
@@ -671,7 +610,7 @@ objclass2nmstate(struct gsm_bts *bts, u_int8_t obj_class,
nm_state = &bts->bs11.rack.nm_state;
break;
case NM_OC_BS11_ENVABTSE:
- if (obj_inst->trx_nr > ARRAY_SIZE(bts->bs11.envabtse))
+ if (obj_inst->trx_nr >= ARRAY_SIZE(bts->bs11.envabtse))
return NULL;
nm_state = &bts->bs11.envabtse[obj_inst->trx_nr].nm_state;
break;
@@ -682,7 +621,7 @@ objclass2nmstate(struct gsm_bts *bts, u_int8_t obj_class,
nm_state = &bts->gprs.cell.nm_state;
break;
case NM_OC_GPRS_NSVC:
- if (obj_inst->trx_nr > ARRAY_SIZE(bts->gprs.nsvc))
+ if (obj_inst->trx_nr >= ARRAY_SIZE(bts->gprs.nsvc))
return NULL;
nm_state = &bts->gprs.nsvc[obj_inst->trx_nr].nm_state;
break;
@@ -719,7 +658,7 @@ objclass2obj(struct gsm_bts *bts, u_int8_t obj_class,
obj = &trx->bb_transc;
break;
case NM_OC_CHANNEL:
- if (obj_inst->trx_nr > bts->num_trx) {
+ if (obj_inst->trx_nr >= bts->num_trx) {
DEBUGPC(DNM, "TRX %u does not exist ", obj_inst->trx_nr);
return NULL;
}
@@ -738,7 +677,7 @@ objclass2obj(struct gsm_bts *bts, u_int8_t obj_class,
obj = &bts->gprs.cell;
break;
case NM_OC_GPRS_NSVC:
- if (obj_inst->trx_nr > ARRAY_SIZE(bts->gprs.nsvc))
+ if (obj_inst->trx_nr >= ARRAY_SIZE(bts->gprs.nsvc))
return NULL;
obj = &bts->gprs.nsvc[obj_inst->trx_nr];
break;
@@ -793,7 +732,7 @@ static int abis_nm_rx_statechg_rep(struct msgb *mb)
new_state = *nm_state;
- abis_nm_tlv_parse(&tp, foh->data, oh->length-sizeof(*foh));
+ abis_nm_tlv_parse(&tp, bts, foh->data, oh->length-sizeof(*foh));
if (TLVP_PRESENT(&tp, NM_ATT_OPER_STATE)) {
new_state.operational = *TLVP_VAL(&tp, NM_ATT_OPER_STATE);
DEBUGPC(DNM, "OP_STATE=%s ", nm_opstate_name(new_state.operational));
@@ -805,19 +744,25 @@ static int abis_nm_rx_statechg_rep(struct msgb *mb)
new_state.availability = *TLVP_VAL(&tp, NM_ATT_AVAIL_STATUS);
DEBUGPC(DNM, "AVAIL=%s(%02x) ", nm_avail_name(new_state.availability),
new_state.availability);
- }
+ } else
+ new_state.availability = 0xff;
if (TLVP_PRESENT(&tp, NM_ATT_ADM_STATE)) {
new_state.administrative = *TLVP_VAL(&tp, NM_ATT_ADM_STATE);
DEBUGPC(DNM, "ADM=%2s ", nm_adm_name(new_state.administrative));
}
DEBUGPC(DNM, "\n");
- if (memcmp(&new_state, nm_state, sizeof(new_state))) {
+ if ((new_state.administrative != 0 && nm_state->administrative == 0) ||
+ new_state.operational != nm_state->operational ||
+ new_state.availability != nm_state->availability) {
/* Update the operational state of a given object in our in-memory data
* structures and send an event to the higher layer */
void *obj = objclass2obj(bts, foh->obj_class, &foh->obj_inst);
rc = nm_state_event(EVT_STATECHG_OPER, foh->obj_class, obj, nm_state, &new_state);
- *nm_state = new_state;
+ nm_state->operational = new_state.operational;
+ nm_state->availability = new_state.availability;
+ if (nm_state->administrative == 0)
+ nm_state->administrative = new_state.administrative;
}
#if 0
if (op_state == 1) {
@@ -839,7 +784,7 @@ static int rx_fail_evt_rep(struct msgb *mb)
DEBUGPC(DNM, "Failure Event Report ");
- abis_nm_tlv_parse(&tp, foh->data, oh->length-sizeof(*foh));
+ abis_nm_tlv_parse(&tp, mb->trx->bts, foh->data, oh->length-sizeof(*foh));
if (TLVP_PRESENT(&tp, NM_ATT_EVENT_TYPE))
DEBUGPC(DNM, "Type=%s ", event_type_name(*TLVP_VAL(&tp, NM_ATT_EVENT_TYPE)));
@@ -933,7 +878,7 @@ static int abis_nm_rx_sw_act_req(struct msgb *mb)
if (nack)
return ret;
- abis_nm_tlv_parse(&tp, foh->data, oh->length-sizeof(*foh));
+ abis_nm_tlv_parse(&tp, mb->trx->bts, foh->data, oh->length-sizeof(*foh));
sw_config = TLVP_VAL(&tp, NM_ATT_SW_CONFIG);
sw_config_len = TLVP_LEN(&tp, NM_ATT_SW_CONFIG);
if (!TLVP_PRESENT(&tp, NM_ATT_SW_CONFIG)) {
@@ -967,7 +912,7 @@ static int abis_nm_rx_chg_adm_state_ack(struct msgb *mb)
struct tlv_parsed tp;
u_int8_t adm_state;
- abis_nm_tlv_parse(&tp, foh->data, oh->length-sizeof(*foh));
+ abis_nm_tlv_parse(&tp, mb->trx->bts, foh->data, oh->length-sizeof(*foh));
if (!TLVP_PRESENT(&tp, NM_ATT_ADM_STATE))
return -EINVAL;
@@ -983,7 +928,7 @@ static int abis_nm_rx_lmt_event(struct msgb *mb)
struct tlv_parsed tp;
DEBUGP(DNM, "LMT Event ");
- abis_nm_tlv_parse(&tp, foh->data, oh->length-sizeof(*foh));
+ abis_nm_tlv_parse(&tp, mb->trx->bts, foh->data, oh->length-sizeof(*foh));
if (TLVP_PRESENT(&tp, NM_ATT_BS11_LMT_LOGON_SESSION) &&
TLVP_LEN(&tp, NM_ATT_BS11_LMT_LOGON_SESSION) >= 1) {
u_int8_t onoff = *TLVP_VAL(&tp, NM_ATT_BS11_LMT_LOGON_SESSION);
@@ -1029,7 +974,7 @@ static int abis_nm_rcvmsg_fom(struct msgb *mb)
else
DEBUGPC(DNM, "NACK 0x%02x ", mt);
- abis_nm_tlv_parse(&tp, foh->data, oh->length-sizeof(*foh));
+ abis_nm_tlv_parse(&tp, mb->trx->bts, foh->data, oh->length-sizeof(*foh));
if (TLVP_PRESENT(&tp, NM_ATT_NACK_CAUSES))
DEBUGPC(DNM, "CAUSE=%s\n",
nack_cause_name(*TLVP_VAL(&tp, NM_ATT_NACK_CAUSES)));
@@ -1043,13 +988,11 @@ static int abis_nm_rcvmsg_fom(struct msgb *mb)
/* check if last message is to be acked */
if (is_ack_nack(nmh->last_msgtype)) {
if (mt == MT_ACK(nmh->last_msgtype)) {
- fprintf(stderr, "received ACK (0x%x)\n",
- foh->msg_type);
+ DEBUGP(DNM, "received ACK (0x%x)\n", foh->msg_type);
/* we got our ACK, continue sending the next msg */
} else if (mt == MT_NACK(nmh->last_msgtype)) {
/* we got a NACK, signal this to the caller */
- fprintf(stderr, "received NACK (0x%x)\n",
- foh->msg_type);
+ DEBUGP(DNM, "received NACK (0x%x)\n", foh->msg_type);
/* FIXME: somehow signal this to the caller */
} else {
/* really strange things happen */
@@ -1071,6 +1014,12 @@ static int abis_nm_rcvmsg_fom(struct msgb *mb)
case NM_MT_CONN_MDROP_LINK_ACK:
DEBUGP(DNM, "CONN MDROP LINK ACK\n");
break;
+ case NM_MT_IPACC_RESTART_ACK:
+ dispatch_signal(SS_NM, S_NM_IPACC_RESTART_ACK, NULL);
+ break;
+ case NM_MT_IPACC_RESTART_NACK:
+ dispatch_signal(SS_NM, S_NM_IPACC_RESTART_NACK, NULL);
+ break;
}
return 0;
@@ -1088,8 +1037,8 @@ static int abis_nm_rcvmsg_manuf(struct msgb *mb)
rc = abis_nm_rx_ipacc(mb);
break;
default:
- fprintf(stderr, "don't know how to parse OML for this "
- "BTS type (%u)\n", bts_type);
+ LOGP(DNM, LOGL_ERROR, "don't know how to parse OML for this "
+ "BTS type (%u)\n", bts_type);
rc = 0;
break;
}
@@ -1106,12 +1055,12 @@ int abis_nm_rcvmsg(struct msgb *msg)
/* Various consistency checks */
if (oh->placement != ABIS_OM_PLACEMENT_ONLY) {
- fprintf(stderr, "ABIS OML placement 0x%x not supported\n",
+ LOGP(DNM, LOGL_ERROR, "ABIS OML placement 0x%x not supported\n",
oh->placement);
return -EINVAL;
}
if (oh->sequence != 0) {
- fprintf(stderr, "ABIS OML sequence 0x%x != 0x00\n",
+ LOGP(DNM, LOGL_ERROR, "ABIS OML sequence 0x%x != 0x00\n",
oh->sequence);
return -EINVAL;
}
@@ -1119,12 +1068,12 @@ int abis_nm_rcvmsg(struct msgb *msg)
unsigned int l2_len = msg->tail - (u_int8_t *)msgb_l2(msg);
unsigned int hlen = sizeof(*oh) + sizeof(struct abis_om_fom_hdr);
if (oh->length + hlen > l2_len) {
- fprintf(stderr, "ABIS OML truncated message (%u > %u)\n",
+ LOGP(DNM, LOGL_ERROR, "ABIS OML truncated message (%u > %u)\n",
oh->length + sizeof(*oh), l2_len);
return -EINVAL;
}
if (oh->length + hlen < l2_len)
- fprintf(stderr, "ABIS OML message with extra trailer?!? (oh->len=%d, sizeof_oh=%d l2_len=%d\n", oh->length, sizeof(*oh), l2_len);
+ LOGP(DNM, LOGL_ERROR, "ABIS OML message with extra trailer?!? (oh->len=%d, sizeof_oh=%d l2_len=%d\n", oh->length, sizeof(*oh), l2_len);
#endif
msg->l3h = (unsigned char *)oh + sizeof(*oh);
@@ -1137,11 +1086,11 @@ int abis_nm_rcvmsg(struct msgb *msg)
break;
case ABIS_OM_MDISC_MMI:
case ABIS_OM_MDISC_TRAU:
- fprintf(stderr, "unimplemented ABIS OML message discriminator 0x%x\n",
+ LOGP(DNM, LOGL_ERROR, "unimplemented ABIS OML message discriminator 0x%x\n",
oh->mdisc);
break;
default:
- fprintf(stderr, "unknown ABIS OML message discriminator 0x%x\n",
+ LOGP(DNM, LOGL_ERROR, "unknown ABIS OML message discriminator 0x%x\n",
oh->mdisc);
return -EINVAL;
}
@@ -1216,6 +1165,22 @@ struct abis_nm_sw {
static struct abis_nm_sw g_sw;
+static void sw_add_file_id_and_ver(struct abis_nm_sw *sw, struct msgb *msg)
+{
+ if (sw->bts->type == GSM_BTS_TYPE_NANOBTS) {
+ msgb_v_put(msg, NM_ATT_SW_DESCR);
+ msgb_tl16v_put(msg, NM_ATT_FILE_ID, sw->file_id_len, sw->file_id);
+ msgb_tl16v_put(msg, NM_ATT_FILE_VERSION, sw->file_version_len,
+ sw->file_version);
+ } else if (sw->bts->type == GSM_BTS_TYPE_BS11) {
+ msgb_tlv_put(msg, NM_ATT_FILE_ID, sw->file_id_len, sw->file_id);
+ msgb_tlv_put(msg, NM_ATT_FILE_VERSION, sw->file_version_len,
+ sw->file_version);
+ } else {
+ LOGP(DNM, LOGL_ERROR, "Please implement this for the BTS.\n");
+ }
+}
+
/* 6.2.1 / 8.3.1: Load Data Initiate */
static int sw_load_init(struct abis_nm_sw *sw)
{
@@ -1227,11 +1192,8 @@ static int sw_load_init(struct abis_nm_sw *sw)
fill_om_fom_hdr(oh, len, NM_MT_LOAD_INIT, sw->obj_class,
sw->obj_instance[0], sw->obj_instance[1],
sw->obj_instance[2]);
-
- /* FIXME: this is BS11 specific format */
- msgb_tlv_put(msg, NM_ATT_FILE_ID, sw->file_id_len, sw->file_id);
- msgb_tlv_put(msg, NM_ATT_FILE_VERSION, sw->file_version_len,
- sw->file_version);
+
+ sw_add_file_id_and_ver(sw, msg);
msgb_tv_put(msg, NM_ATT_WINDOW_SIZE, sw->window_size);
return abis_nm_sendmsg(sw->bts, msg);
@@ -1289,7 +1251,24 @@ static int sw_load_segment(struct abis_nm_sw *sw)
/* we only now know the exact length for the OM hdr */
len = strlen(line_buf)+2;
break;
+ case GSM_BTS_TYPE_NANOBTS: {
+ static_assert(sizeof(seg_buf) >= IPACC_SEGMENT_SIZE, buffer_big_enough);
+ len = read(sw->fd, &seg_buf, IPACC_SEGMENT_SIZE);
+ if (len < 0) {
+ perror("read failed");
+ return -EINVAL;
+ }
+
+ if (len != IPACC_SEGMENT_SIZE)
+ sw->last_seg = 1;
+
+ ++sw->seg_in_window;
+ msgb_tl16v_put(msg, NM_ATT_IPACC_FILE_DATA, len, (const u_int8_t *) seg_buf);
+ len += 3;
+ break;
+ }
default:
+ LOGP(DNM, LOGL_ERROR, "sw_load_segment needs implementation for the BTS.\n");
/* FIXME: Other BTS types */
return -1;
}
@@ -1313,11 +1292,7 @@ static int sw_load_end(struct abis_nm_sw *sw)
sw->obj_instance[0], sw->obj_instance[1],
sw->obj_instance[2]);
- /* FIXME: this is BS11 specific format */
- msgb_tlv_put(msg, NM_ATT_FILE_ID, sw->file_id_len, sw->file_id);
- msgb_tlv_put(msg, NM_ATT_FILE_VERSION, sw->file_version_len,
- sw->file_version);
-
+ sw_add_file_id_and_ver(sw, msg);
return abis_nm_sendmsg(sw->bts, msg);
}
@@ -1341,6 +1316,59 @@ static int sw_activate(struct abis_nm_sw *sw)
return abis_nm_sendmsg(sw->bts, msg);
}
+struct sdp_firmware {
+ char magic[4];
+ char more_magic[4];
+ unsigned int header_length;
+ unsigned int file_length;
+} __attribute__ ((packed));
+
+static int parse_sdp_header(struct abis_nm_sw *sw)
+{
+ struct sdp_firmware firmware_header;
+ int rc;
+ struct stat stat;
+
+ rc = read(sw->fd, &firmware_header, sizeof(firmware_header));
+ if (rc != sizeof(firmware_header)) {
+ LOGP(DNM, LOGL_ERROR, "Could not read SDP file header.\n");
+ return -1;
+ }
+
+ if (strncmp(firmware_header.magic, " SDP", 4) != 0) {
+ LOGP(DNM, LOGL_ERROR, "The magic number1 is wrong.\n");
+ return -1;
+ }
+
+ if (firmware_header.more_magic[0] != 0x10 ||
+ firmware_header.more_magic[1] != 0x02 ||
+ firmware_header.more_magic[2] != 0x00 ||
+ firmware_header.more_magic[3] != 0x00) {
+ LOGP(DNM, LOGL_ERROR, "The more magic number is wrong.\n");
+ return -1;
+ }
+
+
+ if (fstat(sw->fd, &stat) == -1) {
+ LOGP(DNM, LOGL_ERROR, "Could not stat the file.\n");
+ return -1;
+ }
+
+ if (ntohl(firmware_header.file_length) != stat.st_size) {
+ LOGP(DNM, LOGL_ERROR, "The filesizes do not match.\n");
+ return -1;
+ }
+
+ /* go back to the start as we checked the whole filesize.. */
+ lseek(sw->fd, 0l, SEEK_SET);
+ LOGP(DNM, LOGL_NOTICE, "The ipaccess SDP header is not fully understood.\n"
+ "There might be checksums in the file that are not\n"
+ "verified and incomplete firmware might be flashed.\n"
+ "There is absolutely no WARRANTY that flashing will\n"
+ "work.\n");
+ return 0;
+}
+
static int sw_open_file(struct abis_nm_sw *sw, const char *fname)
{
char file_id[12+1];
@@ -1372,6 +1400,19 @@ static int sw_open_file(struct abis_nm_sw *sw, const char *fname)
/* rewind to start of file */
rewind(sw->stream);
break;
+ case GSM_BTS_TYPE_NANOBTS:
+ /* TODO: extract that from the filename or content */
+ rc = parse_sdp_header(sw);
+ if (rc < 0) {
+ fprintf(stderr, "Could not parse the ipaccess SDP header\n");
+ return -1;
+ }
+
+ strcpy((char *)sw->file_id, "id");
+ sw->file_id_len = 3;
+ strcpy((char *)sw->file_version, "version");
+ sw->file_version_len = 8;
+ break;
default:
/* We don't know how to treat them yet */
close(sw->fd);
@@ -1470,6 +1511,12 @@ static int abis_nm_rcvmsg_sw(struct msgb *mb)
rc = sw_load_end(sw);
}
break;
+ case NM_MT_LOAD_ABORT:
+ if (sw->cbfn)
+ sw->cbfn(GSM_HOOK_NM_SWLOAD,
+ NM_MT_LOAD_ABORT, mb,
+ sw->cb_data, NULL);
+ break;
}
break;
case SW_STATE_WAIT_ENDACK:
@@ -1483,6 +1530,7 @@ static int abis_nm_rcvmsg_sw(struct msgb *mb)
sw->cbfn(GSM_HOOK_NM_SWLOAD,
NM_MT_LOAD_END_ACK, mb,
sw->cb_data, NULL);
+ rc = 0;
break;
case NM_MT_LOAD_END_NACK:
if (sw->forced) {
@@ -1559,10 +1607,26 @@ int abis_nm_software_load(struct gsm_bts *bts, const char *fname,
return -EBUSY;
sw->bts = bts;
- sw->obj_class = NM_OC_SITE_MANAGER;
- sw->obj_instance[0] = 0xff;
- sw->obj_instance[1] = 0xff;
- sw->obj_instance[2] = 0xff;
+
+ switch (bts->type) {
+ case GSM_BTS_TYPE_BS11:
+ sw->obj_class = NM_OC_SITE_MANAGER;
+ sw->obj_instance[0] = 0xff;
+ sw->obj_instance[1] = 0xff;
+ sw->obj_instance[2] = 0xff;
+ break;
+ case GSM_BTS_TYPE_NANOBTS:
+ sw->obj_class = NM_OC_BASEB_TRANSC;
+ sw->obj_instance[0] = 0x00;
+ sw->obj_instance[1] = 0x00;
+ sw->obj_instance[2] = 0xff;
+ break;
+ case GSM_BTS_TYPE_UNKNOWN:
+ default:
+ LOGPC(DNM, LOGL_ERROR, "Software Load not properly implemented.\n");
+ return -1;
+ break;
+ }
sw->window_size = win_size;
sw->state = SW_STATE_WAIT_INITACK;
sw->cbfn = cbfn;
@@ -1590,7 +1654,10 @@ int abis_nm_software_load_status(struct gsm_bts *bts)
return rc;
}
- percent = (ftell(sw->stream) * 100) / st.st_size;
+ if (sw->stream)
+ percent = (ftell(sw->stream) * 100) / st.st_size;
+ else
+ percent = (lseek(sw->fd, 0, SEEK_CUR) * 100) / st.st_size;
return percent;
}
@@ -1755,7 +1822,8 @@ static int verify_chan_comb(struct gsm_bts_trx_ts *ts, u_int8_t chan_comb)
/* As it turns out, the BS-11 has some very peculiar restrictions
* on the channel combinations it allows */
- if (ts->trx->bts->type == GSM_BTS_TYPE_BS11) {
+ switch (ts->trx->bts->type) {
+ case GSM_BTS_TYPE_BS11:
switch (chan_comb) {
case NM_CHANC_TCHHalf:
case NM_CHANC_TCHHalf2:
@@ -1801,6 +1869,83 @@ static int verify_chan_comb(struct gsm_bts_trx_ts *ts, u_int8_t chan_comb)
/* FIXME: only one CBCH allowed per cell */
break;
}
+ break;
+ case GSM_BTS_TYPE_NANOBTS:
+ switch (ts->nr) {
+ case 0:
+ if (ts->trx->nr == 0) {
+ /* only on TRX0 */
+ switch (chan_comb) {
+ case NM_CHANC_BCCH:
+ case NM_CHANC_mainBCCH:
+ case NM_CHANC_BCCHComb:
+ return 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+ } else {
+ switch (chan_comb) {
+ case NM_CHANC_TCHFull:
+ case NM_CHANC_TCHHalf:
+ case NM_CHANC_IPAC_TCHFull_TCHHalf:
+ return 0;
+ default:
+ return -EINVAL;
+ }
+ }
+ break;
+ case 1:
+ if (ts->trx->nr == 0) {
+ switch (chan_comb) {
+ case NM_CHANC_SDCCH_CBCH:
+ if (ts->trx->ts[0].nm_chan_comb ==
+ NM_CHANC_mainBCCH)
+ return 0;
+ return -EINVAL;
+ case NM_CHANC_SDCCH:
+ case NM_CHANC_TCHFull:
+ case NM_CHANC_TCHHalf:
+ case NM_CHANC_IPAC_TCHFull_TCHHalf:
+ case NM_CHANC_IPAC_TCHFull_PDCH:
+ return 0;
+ }
+ } else {
+ switch (chan_comb) {
+ case NM_CHANC_SDCCH:
+ case NM_CHANC_TCHFull:
+ case NM_CHANC_TCHHalf:
+ case NM_CHANC_IPAC_TCHFull_TCHHalf:
+ return 0;
+ default:
+ return -EINVAL;
+ }
+ }
+ break;
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ switch (chan_comb) {
+ case NM_CHANC_TCHFull:
+ case NM_CHANC_TCHHalf:
+ case NM_CHANC_IPAC_TCHFull_TCHHalf:
+ return 0;
+ case NM_CHANC_IPAC_PDCH:
+ case NM_CHANC_IPAC_TCHFull_PDCH:
+ if (ts->trx->nr == 0)
+ return 0;
+ else
+ return -EINVAL;
+ }
+ break;
+ }
+ return -EINVAL;
+ default:
+ /* unknown BTS type */
+ return 0;
}
return 0;
}
@@ -2203,11 +2348,19 @@ int abis_nm_bs11_get_cclk(struct gsm_bts *bts)
}
//static const u_int8_t bs11_logon_c7[] = { 0x07, 0xd9, 0x01, 0x11, 0x0d, 0x10, 0x20 };
-static const u_int8_t bs11_logon_c8[] = { 0x02 };
-static const u_int8_t bs11_logon_c9[] = "FACTORY";
int abis_nm_bs11_factory_logon(struct gsm_bts *bts, int on)
{
+ return abis_nm_bs11_logon(bts, 0x02, "FACTORY", on);
+}
+
+int abis_nm_bs11_infield_logon(struct gsm_bts *bts, int on)
+{
+ return abis_nm_bs11_logon(bts, 0x03, "FIELD ", on);
+}
+
+int abis_nm_bs11_logon(struct gsm_bts *bts, u_int8_t level, const char *name, int on)
+{
struct abis_om_hdr *oh;
struct msgb *msg = nm_msgb_alloc();
struct bs11_date_time bdt;
@@ -2217,15 +2370,15 @@ int abis_nm_bs11_factory_logon(struct gsm_bts *bts, int on)
oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
if (on) {
u_int8_t len = 3*2 + sizeof(bdt)
- + sizeof(bs11_logon_c8) + sizeof(bs11_logon_c9);
+ + 1 + strlen(name);
fill_om_fom_hdr(oh, len, NM_MT_BS11_LMT_LOGON,
NM_OC_BS11_BTSE, 0xff, 0xff, 0xff);
msgb_tlv_put(msg, NM_ATT_BS11_LMT_LOGIN_TIME,
sizeof(bdt), (u_int8_t *) &bdt);
msgb_tlv_put(msg, NM_ATT_BS11_LMT_USER_ACC_LEV,
- sizeof(bs11_logon_c8), bs11_logon_c8);
+ 1, &level);
msgb_tlv_put(msg, NM_ATT_BS11_LMT_USER_NAME,
- sizeof(bs11_logon_c9), bs11_logon_c9);
+ strlen(name), (u_int8_t *)name);
} else {
fill_om_fom_hdr(oh, 0, NM_MT_BS11_LMT_LOGOFF,
NM_OC_BS11_BTSE, 0xff, 0xff, 0xff);
@@ -2273,6 +2426,27 @@ int abis_nm_bs11_set_pll_locked(struct gsm_bts *bts, int locked)
return abis_nm_sendmsg(bts, msg);
}
+/* Set the calibration value of the PLL (work value/set value)
+ * It depends on the login which one is changed */
+int abis_nm_bs11_set_pll(struct gsm_bts *bts, int value)
+{
+ struct abis_om_hdr *oh;
+ struct msgb *msg;
+ u_int8_t tlv_value[2];
+
+ msg = nm_msgb_alloc();
+ oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
+ fill_om_fom_hdr(oh, 3, NM_MT_BS11_SET_ATTR, NM_OC_BS11,
+ BS11_OBJ_TRX1, 0x00, 0x00);
+
+ tlv_value[0] = value>>8;
+ tlv_value[1] = value&0xff;
+
+ msgb_tlv_put(msg, NM_ATT_BS11_PLL, 2, tlv_value);
+
+ return abis_nm_sendmsg(bts, msg);
+}
+
int abis_nm_bs11_get_state(struct gsm_bts *bts)
{
return __simple_cmd(bts, NM_MT_BS11_GET_STATE);
@@ -2519,14 +2693,15 @@ static int abis_nm_rx_ipacc(struct msgb *msg)
struct abis_om_fom_hdr *foh;
u_int8_t idstrlen = oh->data[0];
struct tlv_parsed tp;
+ struct ipacc_ack_signal_data signal;
if (strncmp((char *)&oh->data[1], ipaccess_magic, idstrlen)) {
- DEBUGP(DNM, "id string is not com.ipaccess !?!\n");
+ LOGP(DNM, LOGL_ERROR, "id string is not com.ipaccess !?!\n");
return -EINVAL;
}
foh = (struct abis_om_fom_hdr *) (oh->data + 1 + idstrlen);
- abis_nm_tlv_parse(&tp, foh->data, oh->length-sizeof(*foh));
+ abis_nm_tlv_parse(&tp, msg->trx->bts, foh->data, oh->length-sizeof(*foh));
debugp_foh(foh);
@@ -2549,7 +2724,7 @@ static int abis_nm_rx_ipacc(struct msgb *msg)
DEBUGPC(DNM, "\n");
break;
case NM_MT_IPACC_RSL_CONNECT_NACK:
- DEBUGPC(DNM, "RSL CONNECT NACK ");
+ LOGP(DNM, LOGL_ERROR, "RSL CONNECT NACK ");
if (TLVP_PRESENT(&tp, NM_ATT_NACK_CAUSES))
DEBUGPC(DNM, " CAUSE=%s\n",
nack_cause_name(*TLVP_VAL(&tp, NM_ATT_NACK_CAUSES)));
@@ -2561,35 +2736,35 @@ static int abis_nm_rx_ipacc(struct msgb *msg)
/* FIXME: decode and show the actual attributes */
break;
case NM_MT_IPACC_SET_NVATTR_NACK:
- DEBUGPC(DNM, "SET NVATTR NACK ");
+ LOGP(DNM, LOGL_ERROR, "SET NVATTR NACK ");
if (TLVP_PRESENT(&tp, NM_ATT_NACK_CAUSES))
- DEBUGPC(DNM, " CAUSE=%s\n",
+ LOGPC(DNM, LOGL_ERROR, " CAUSE=%s\n",
nack_cause_name(*TLVP_VAL(&tp, NM_ATT_NACK_CAUSES)));
else
- DEBUGPC(DNM, "\n");
+ LOGPC(DNM, LOGL_ERROR, "\n");
break;
case NM_MT_IPACC_GET_NVATTR_ACK:
DEBUGPC(DNM, "GET NVATTR ACK\n");
/* FIXME: decode and show the actual attributes */
break;
case NM_MT_IPACC_GET_NVATTR_NACK:
- DEBUGPC(DNM, "GET NVATTR NACK ");
+ LOGPC(DNM, LOGL_ERROR, "GET NVATTR NACK ");
if (TLVP_PRESENT(&tp, NM_ATT_NACK_CAUSES))
- DEBUGPC(DNM, " CAUSE=%s\n",
+ LOGPC(DNM, LOGL_ERROR, " CAUSE=%s\n",
nack_cause_name(*TLVP_VAL(&tp, NM_ATT_NACK_CAUSES)));
else
- DEBUGPC(DNM, "\n");
+ LOGPC(DNM, LOGL_ERROR, "\n");
break;
case NM_MT_IPACC_SET_ATTR_ACK:
DEBUGPC(DNM, "SET ATTR ACK\n");
break;
case NM_MT_IPACC_SET_ATTR_NACK:
- DEBUGPC(DNM, "SET ATTR NACK ");
+ LOGPC(DNM, LOGL_ERROR, "SET ATTR NACK ");
if (TLVP_PRESENT(&tp, NM_ATT_NACK_CAUSES))
- DEBUGPC(DNM, " CAUSE=%s\n",
+ LOGPC(DNM, LOGL_ERROR, " CAUSE=%s\n",
nack_cause_name(*TLVP_VAL(&tp, NM_ATT_NACK_CAUSES)));
else
- DEBUGPC(DNM, "\n");
+ LOGPC(DNM, LOGL_ERROR, "\n");
break;
default:
DEBUGPC(DNM, "unknown\n");
@@ -2601,7 +2776,14 @@ static int abis_nm_rx_ipacc(struct msgb *msg)
case NM_MT_IPACC_RSL_CONNECT_NACK:
case NM_MT_IPACC_SET_NVATTR_NACK:
case NM_MT_IPACC_GET_NVATTR_NACK:
- dispatch_signal(SS_NM, S_NM_IPACC_NACK, &foh->msg_type);
+ signal.bts = msg->trx->bts;
+ signal.msg_type = foh->msg_type;
+ dispatch_signal(SS_NM, S_NM_IPACC_NACK, &signal);
+ break;
+ case NM_MT_IPACC_SET_NVATTR_ACK:
+ signal.bts = msg->trx->bts;
+ signal.msg_type = foh->msg_type;
+ dispatch_signal(SS_NM, S_NM_IPACC_ACK, &signal);
break;
default:
break;
@@ -2648,11 +2830,11 @@ int abis_nm_ipaccess_msg(struct gsm_bts *bts, u_int8_t msg_type,
}
/* set some attributes in NVRAM */
-int abis_nm_ipaccess_set_nvattr(struct gsm_bts *bts, u_int8_t *attr,
+int abis_nm_ipaccess_set_nvattr(struct gsm_bts_trx *trx, u_int8_t *attr,
int attr_len)
{
- return abis_nm_ipaccess_msg(bts, NM_MT_IPACC_SET_NVATTR,
- NM_OC_BASEB_TRANSC, 0, 0, 0xff, attr,
+ return abis_nm_ipaccess_msg(trx->bts, NM_MT_IPACC_SET_NVATTR,
+ NM_OC_BASEB_TRANSC, 0, trx->nr, 0xff, attr,
attr_len);
}
@@ -2699,6 +2881,19 @@ int abis_nm_ipaccess_set_attr(struct gsm_bts *bts, u_int8_t obj_class,
attr, attr_len);
}
+void gsm_trx_lock_rf(struct gsm_bts_trx *trx, int locked)
+{
+ int new_state = locked ? NM_STATE_LOCKED : NM_STATE_UNLOCKED;
+
+ trx->nm_state.administrative = new_state;
+ if (!trx->bts || !trx->bts->oml_link)
+ return;
+
+ abis_nm_chg_adm_state(trx->bts, NM_OC_RADIO_CARRIER,
+ trx->bts->bts_nr, trx->nr, 0xff,
+ new_state);
+}
+
static const char *ipacc_testres_names[] = {
[NM_IPACC_TESTRES_SUCCESS] = "SUCCESS",
[NM_IPACC_TESTRES_TIMEOUT] = "TIMEOUT",
diff --git a/openbsc/src/abis_rsl.c b/openbsc/src/abis_rsl.c
index 0dee79b17..10ebd6d44 100644
--- a/openbsc/src/abis_rsl.c
+++ b/openbsc/src/abis_rsl.c
@@ -38,6 +38,8 @@
#include <openbsc/tlv.h>
#include <openbsc/paging.h>
#include <openbsc/signal.h>
+#include <openbsc/meas_rep.h>
+#include <openbsc/rtp_proxy.h>
#define RSL_ALLOC_SIZE 1024
#define RSL_ALLOC_HEADROOM 128
@@ -205,42 +207,44 @@ struct gsm_lchan *lchan_lookup(struct gsm_bts_trx *trx, u_int8_t chan_nr)
if (ts->pchan != GSM_PCHAN_TCH_F &&
ts->pchan != GSM_PCHAN_PDCH &&
ts->pchan != GSM_PCHAN_TCH_F_PDCH)
- fprintf(stderr, "chan_nr=0x%02x but pchan=%u\n",
+ LOGP(DRSL, LOGL_ERROR, "chan_nr=0x%02x but pchan=%u\n",
chan_nr, ts->pchan);
} else if ((cbits & 0x1e) == 0x02) {
lch_idx = cbits & 0x1; /* TCH/H */
if (ts->pchan != GSM_PCHAN_TCH_H)
- fprintf(stderr, "chan_nr=0x%02x but pchan=%u\n",
+ LOGP(DRSL, LOGL_ERROR, "chan_nr=0x%02x but pchan=%u\n",
chan_nr, ts->pchan);
} else if ((cbits & 0x1c) == 0x04) {
lch_idx = cbits & 0x3; /* SDCCH/4 */
if (ts->pchan != GSM_PCHAN_CCCH_SDCCH4)
- fprintf(stderr, "chan_nr=0x%02x but pchan=%u\n",
+ LOGP(DRSL, LOGL_ERROR, "chan_nr=0x%02x but pchan=%u\n",
chan_nr, ts->pchan);
} else if ((cbits & 0x18) == 0x08) {
lch_idx = cbits & 0x7; /* SDCCH/8 */
if (ts->pchan != GSM_PCHAN_SDCCH8_SACCH8C)
- fprintf(stderr, "chan_nr=0x%02x but pchan=%u\n",
+ LOGP(DRSL, LOGL_ERROR, "chan_nr=0x%02x but pchan=%u\n",
chan_nr, ts->pchan);
} else if (cbits == 0x10 || cbits == 0x11 || cbits == 0x12) {
lch_idx = 0;
if (ts->pchan != GSM_PCHAN_CCCH &&
ts->pchan != GSM_PCHAN_CCCH_SDCCH4)
- fprintf(stderr, "chan_nr=0x%02x but pchan=%u\n",
+ LOGP(DRSL, LOGL_ERROR, "chan_nr=0x%02x but pchan=%u\n",
chan_nr, ts->pchan);
/* FIXME: we should not return first sdcch4 !!! */
} else {
- fprintf(stderr, "unknown chan_nr=0x%02x\n", chan_nr);
+ LOGP(DRSL, LOGL_ERROR, "unknown chan_nr=0x%02x\n", chan_nr);
return NULL;
}
lchan = &ts->lchan[lch_idx];
+ debug_set_context(BSC_CTX_LCHAN, lchan);
+ debug_set_context(BSC_CTX_SUBSCR, lchan->subscr);
return lchan;
}
/* See Table 10.5.25 of GSM04.08 */
-u_int8_t lchan2chan_nr(struct gsm_lchan *lchan)
+u_int8_t lchan2chan_nr(const struct gsm_lchan *lchan)
{
struct gsm_bts_trx_ts *ts = lchan->ts;
u_int8_t cbits, chan_nr;
@@ -366,6 +370,24 @@ static const char *rsl_err_vals[0xff] = {
[RSL_ERR_INTERWORKING] = "Interworking error, unspecified",
};
+static const struct value_string rlm_cause_strs[] = {
+ { RLL_CAUSE_T200_EXPIRED, "Timer T200 expired (N200+1) times" },
+ { RLL_CAUSE_REEST_REQ, "Re-establishment request" },
+ { RLL_CAUSE_UNSOL_UA_RESP, "Unsolicited UA response" },
+ { RLL_CAUSE_UNSOL_DM_RESP, "Unsolicited DM response" },
+ { RLL_CAUSE_UNSOL_DM_RESP_MF, "Unsolicited DM response, multiple frame" },
+ { RLL_CAUSE_UNSOL_SPRV_RESP, "Unsolicited supervisory response" },
+ { RLL_CAUSE_SEQ_ERR, "Sequence Error" },
+ { RLL_CAUSE_UFRM_INC_PARAM, "U-Frame with incorrect parameters" },
+ { RLL_CAUSE_SFRM_INC_PARAM, "S-Frame with incorrect parameters" },
+ { RLL_CAUSE_IFRM_INC_MBITS, "I-Frame with incorrect use of M bit" },
+ { RLL_CAUSE_IFRM_INC_LEN, "I-Frame with incorrect length" },
+ { RLL_CAUSE_FRM_UNIMPL, "Fraeme not implemented" },
+ { RLL_CAUSE_SABM_MF, "SABM command, multiple frame established state" },
+ { RLL_CAUSE_SABM_INFO_NOTALL, "SABM frame with information not allowed in this state" },
+ { 0, NULL },
+};
+
static const char *rsl_err_name(u_int8_t err)
{
if (rsl_err_vals[err])
@@ -374,14 +396,14 @@ static const char *rsl_err_name(u_int8_t err)
return "unknown";
}
-static void print_rsl_cause(const u_int8_t *cause_v, u_int8_t cause_len)
+static void print_rsl_cause(int lvl, const u_int8_t *cause_v, u_int8_t cause_len)
{
int i;
- DEBUGPC(DRSL, "CAUSE=0x%02x(%s) ",
+ LOGPC(DRSL, lvl, "CAUSE=0x%02x(%s) ",
cause_v[0], rsl_err_name(cause_v[0]));
for (i = 1; i < cause_len-1; i++)
- DEBUGPC(DRSL, "%02x ", cause_v[i]);
+ LOGPC(DRSL, lvl, "%02x ", cause_v[i]);
}
/* Send a BCCH_INFO message as per Chapter 8.5.1 */
@@ -488,6 +510,11 @@ static int channel_mode_from_lchan(struct rsl_ie_chan_mode *cm,
/* set TCH Speech/Data */
cm->spd_ind = lchan->rsl_cmode;
+ if (lchan->rsl_cmode == RSL_CMOD_SPD_SIGN &&
+ lchan->tch_mode != GSM48_CMODE_SIGN)
+ LOGP(DRSL, LOGL_ERROR, "unsupported: rsl_mode == signalling, "
+ "but tch_mode != signalling\n");
+
switch (lchan->type) {
case GSM_LCHAN_SDCCH:
cm->chan_rt = RSL_CMOD_CRT_SDCCH;
@@ -570,7 +597,7 @@ int rsl_chan_activate(struct gsm_bts_trx *trx, u_int8_t chan_nr,
#endif
int rsl_chan_activate_lchan(struct gsm_lchan *lchan, u_int8_t act_type,
- u_int8_t ta)
+ u_int8_t ta, u_int8_t ho_ref)
{
struct abis_rsl_dchan_hdr *dh;
struct msgb *msg;
@@ -597,9 +624,9 @@ int rsl_chan_activate_lchan(struct gsm_lchan *lchan, u_int8_t act_type,
dh->chan_nr = chan_nr;
msgb_tv_put(msg, RSL_IE_ACT_TYPE, act_type);
- /* For compatibility with Phase 1 */
msgb_tlv_put(msg, RSL_IE_CHAN_MODE, sizeof(cm),
(u_int8_t *) &cm);
+ /* For compatibility with Phase 1 */
msgb_tlv_put(msg, RSL_IE_CHAN_IDENT, 4,
(u_int8_t *) &ci);
@@ -610,10 +637,23 @@ int rsl_chan_activate_lchan(struct gsm_lchan *lchan, u_int8_t act_type,
msgb_tlv_put(msg, RSL_IE_ENCR_INFO, rc, encr_info);
}
+ switch (act_type) {
+ case RSL_ACT_INTER_ASYNC:
+ case RSL_ACT_INTER_SYNC:
+ msgb_tv_put(msg, RSL_IE_HANDO_REF, ho_ref);
+ break;
+ default:
+ break;
+ }
+
msgb_tv_put(msg, RSL_IE_BS_POWER, lchan->bs_power);
msgb_tv_put(msg, RSL_IE_MS_POWER, lchan->ms_power);
msgb_tv_put(msg, RSL_IE_TIMING_ADVANCE, ta);
+ if (lchan->tch_mode == GSM48_CMODE_SPEECH_AMR)
+ msgb_tlv_put(msg, RSL_IE_MR_CONFIG, sizeof(lchan->mr_conf),
+ (u_int8_t *) &lchan->mr_conf);
+
msg->trx = lchan->ts->trx;
return abis_rsl_sendmsg(msg);
@@ -703,8 +743,7 @@ int rsl_deact_sacch(struct gsm_lchan *lchan)
msg->lchan = lchan;
msg->trx = lchan->ts->trx;
- DEBUGP(DRSL, "DEACTivate SACCH CMD channel=%s chan_nr=0x%02x\n",
- gsm_ts_name(lchan->ts), dh->chan_nr);
+ DEBUGP(DRSL, "%s DEACTivate SACCH CMD\n", gsm_lchan_name(lchan));
return abis_rsl_sendmsg(msg);
}
@@ -722,8 +761,7 @@ int rsl_rf_chan_release(struct gsm_lchan *lchan)
msg->lchan = lchan;
msg->trx = lchan->ts->trx;
- DEBUGP(DRSL, "RF Channel Release CMD channel=%s chan_nr=0x%02x\n",
- gsm_ts_name(lchan->ts), dh->chan_nr);
+ DEBUGP(DRSL, "%s RF Channel Release CMD\n", gsm_lchan_name(lchan));
/* BTS will respond by RF CHAN REL ACK */
return abis_rsl_sendmsg(msg);
@@ -816,8 +854,8 @@ int rsl_siemens_mrpci(struct gsm_lchan *lchan, struct rsl_mrpci *mrpci)
dh->chan_nr = lchan2chan_nr(lchan);
msgb_tv_put(msg, RSL_IE_SIEMENS_MRPCI, *(u_int8_t *)mrpci);
- DEBUGP(DRSL, "channel=%s chan_nr=0x%02x TX Siemens MRPCI 0x%02x\n",
- gsm_ts_name(lchan->ts), dh->chan_nr, *(u_int8_t *)mrpci);
+ DEBUGP(DRSL, "%s TX Siemens MRPCI 0x%02x\n",
+ gsm_lchan_name(lchan), *(u_int8_t *)mrpci);
msg->trx = lchan->ts->trx;
@@ -833,7 +871,7 @@ int rsl_data_request(struct msgb *msg, u_int8_t link_id)
struct abis_rsl_rll_hdr *rh;
if (msg->lchan == NULL) {
- fprintf(stderr, "cannot send DATA REQUEST to unknown lchan\n");
+ LOGP(DRSL, LOGL_ERROR, "cannot send DATA REQUEST to unknown lchan\n");
return -EINVAL;
}
@@ -887,6 +925,9 @@ int rsl_release_request(struct gsm_lchan *lchan, u_int8_t link_id)
rh->link_id = link_id;
msgb_tv_put(msg, RSL_IE_RELEASE_MODE, 0); /* normal release */
+ lchan->state = LCHAN_S_REL_REQ;
+ /* FIXME: start some timer in case we don't receive a REL ACK ? */
+
msg->trx = lchan->ts->trx;
return abis_rsl_sendmsg(msg);
@@ -901,7 +942,15 @@ static int rsl_rx_chan_act_ack(struct msgb *msg)
* to assign the activated channel to the MS */
if (rslh->ie_chan != RSL_IE_CHAN_NR)
return -EINVAL;
-
+
+ if (msg->lchan->state != LCHAN_S_ACT_REQ)
+ LOGP(DRSL, LOGL_NOTICE, "%s CHAN ACT ACK, but state %s\n",
+ gsm_lchan_name(msg->lchan),
+ gsm_lchans_name(msg->lchan->state));
+ msg->lchan->state = LCHAN_S_ACTIVE;
+
+ dispatch_signal(SS_LCHAN, S_LCHAN_ACTIVATE_ACK, msg->lchan);
+
return 0;
}
@@ -911,14 +960,26 @@ static int rsl_rx_chan_act_nack(struct msgb *msg)
struct abis_rsl_dchan_hdr *dh = msgb_l2(msg);
struct tlv_parsed tp;
+ LOGP(DRSL, LOGL_ERROR, "%s CHANNEL ACTIVATE NACK",
+ gsm_lchan_name(msg->lchan));
+
/* BTS has rejected channel activation ?!? */
if (dh->ie_chan != RSL_IE_CHAN_NR)
return -EINVAL;
rsl_tlv_parse(&tp, dh->data, msgb_l2len(msg)-sizeof(*dh));
- if (TLVP_PRESENT(&tp, RSL_IE_CAUSE))
- print_rsl_cause(TLVP_VAL(&tp, RSL_IE_CAUSE),
+ if (TLVP_PRESENT(&tp, RSL_IE_CAUSE)) {
+ const u_int8_t *cause = TLVP_VAL(&tp, RSL_IE_CAUSE);
+ print_rsl_cause(LOGL_ERROR, cause,
TLVP_LEN(&tp, RSL_IE_CAUSE));
+ if (*cause != RSL_ERR_RCH_ALR_ACTV_ALLOC)
+ msg->lchan->state = LCHAN_S_NONE;
+ } else
+ msg->lchan->state = LCHAN_S_NONE;
+
+ LOGPC(DRSL, LOGL_ERROR, "\n");
+
+ dispatch_signal(SS_LCHAN, S_LCHAN_ACTIVATE_NACK, msg->lchan);
lchan_free(msg->lchan);
return 0;
@@ -930,61 +991,158 @@ static int rsl_rx_conn_fail(struct msgb *msg)
struct abis_rsl_dchan_hdr *dh = msgb_l2(msg);
struct tlv_parsed tp;
- DEBUGPC(DRSL, "CONNECTION FAIL: ");
+ /* FIXME: print which channel */
+ LOGP(DRSL, LOGL_NOTICE, "%s CONNECTION FAIL: RELEASING ",
+ gsm_lchan_name(msg->lchan));
rsl_tlv_parse(&tp, dh->data, msgb_l2len(msg)-sizeof(*dh));
if (TLVP_PRESENT(&tp, RSL_IE_CAUSE))
- print_rsl_cause(TLVP_VAL(&tp, RSL_IE_CAUSE),
+ print_rsl_cause(LOGL_NOTICE, TLVP_VAL(&tp, RSL_IE_CAUSE),
TLVP_LEN(&tp, RSL_IE_CAUSE));
- DEBUGPC(DRSL, "RELEASING.\n");
-
+ LOGPC(DRSL, LOGL_NOTICE, "\n");
/* FIXME: only free it after channel release ACK */
return rsl_rf_chan_release(msg->lchan);
}
+static void print_meas_rep_uni(struct gsm_meas_rep_unidir *mru,
+ const char *prefix)
+{
+ DEBUGPC(DMEAS, "RXL-FULL-%s=%3ddBm RXL-SUB-%s=%3ddBm ",
+ prefix, rxlev2dbm(mru->full.rx_lev),
+ prefix, rxlev2dbm(mru->sub.rx_lev));
+ DEBUGPC(DMEAS, "RXQ-FULL-%s=%d RXQ-SUB-%s=%d ",
+ prefix, mru->full.rx_qual, prefix, mru->sub.rx_qual);
+}
+
+static void print_meas_rep(struct gsm_meas_rep *mr)
+{
+ int i;
+
+ DEBUGP(DMEAS, "MEASUREMENT RESULT NR=%d ", mr->nr);
+
+ if (mr->flags & MEAS_REP_F_DL_DTX)
+ DEBUGPC(DMEAS, "DTXd ");
+
+ print_meas_rep_uni(&mr->ul, "ul");
+ DEBUGPC(DMEAS, "BS_POWER=%d ", mr->bs_power);
+ if (mr->flags & MEAS_REP_F_MS_TO)
+ DEBUGPC(DMEAS, "MS_TO=%d ", mr->ms_timing_offset);
+
+ if (mr->flags & MEAS_REP_F_MS_L1) {
+ DEBUGPC(DMEAS, "L1_MS_PWR=%3ddBm ", mr->ms_l1.pwr);
+ DEBUGPC(DMEAS, "L1_FPC=%u ",
+ mr->flags & MEAS_REP_F_FPC ? 1 : 0);
+ DEBUGPC(DMEAS, "L1_TA=%u ", mr->ms_l1.ta);
+ }
+
+ if (mr->flags & MEAS_REP_F_UL_DTX)
+ DEBUGPC(DMEAS, "DTXu ");
+ if (mr->flags & MEAS_REP_F_BA1)
+ DEBUGPC(DMEAS, "BA1 ");
+ if (!(mr->flags & MEAS_REP_F_DL_VALID))
+ DEBUGPC(DMEAS, "NOT VALID ");
+ else
+ print_meas_rep_uni(&mr->dl, "dl");
+
+ DEBUGPC(DMEAS, "NUM_NEIGH=%u\n", mr->num_cell);
+ if (mr->num_cell == 7)
+ return;
+ for (i = 0; i < mr->num_cell; i++) {
+ struct gsm_meas_rep_cell *mrc = &mr->cell[i];
+ DEBUGP(DMEAS, "IDX=%u ARFCN=%u BSIC=%u => %d dBm\n",
+ mrc->neigh_idx, mrc->arfcn, mrc->bsic, rxlev2dbm(mrc->rxlev));
+ }
+}
+
static int rsl_rx_meas_res(struct msgb *msg)
{
struct abis_rsl_dchan_hdr *dh = msgb_l2(msg);
struct tlv_parsed tp;
+ struct gsm_meas_rep *mr = lchan_next_meas_rep(msg->lchan);
+ u_int8_t len;
+ const u_int8_t *val;
+ int rc;
+
+ /* check if this channel is actually active */
+ /* FIXME: maybe this check should be way more generic/centralized */
+ if (msg->lchan->state != LCHAN_S_ACTIVE) {
+ LOGP(DRSL, LOGL_NOTICE, "%s: MEAS RES for inactive channel\n",
+ gsm_lchan_name(msg->lchan));
+ return 0;
+ }
+
+ memset(mr, 0, sizeof(*mr));
+ mr->lchan = msg->lchan;
- DEBUGPC(DMEAS, "MEASUREMENT RESULT ");
rsl_tlv_parse(&tp, dh->data, msgb_l2len(msg)-sizeof(*dh));
- if (TLVP_PRESENT(&tp, RSL_IE_MEAS_RES_NR))
- DEBUGPC(DMEAS, "NR=%d ", *TLVP_VAL(&tp, RSL_IE_MEAS_RES_NR));
- if (TLVP_PRESENT(&tp, RSL_IE_UPLINK_MEAS)) {
- u_int8_t len = TLVP_LEN(&tp, RSL_IE_UPLINK_MEAS);
- const u_int8_t *val = TLVP_VAL(&tp, RSL_IE_UPLINK_MEAS);
- if (len >= 3) {
- if (val[0] & 0x40)
- DEBUGPC(DMEAS, "DTXd ");
- DEBUGPC(DMEAS, "RXL-FULL-up=%d RXL-SUB-up=%d ",
- val[0] & 0x3f, val[1] & 0x3f);
- DEBUGPC(DMEAS, "RXQ-FULL-up=%d RXQ-SUB-up=%d ",
- val[2]>>3 & 0x7, val[2] & 0x7);
- }
+ if (!TLVP_PRESENT(&tp, RSL_IE_MEAS_RES_NR) ||
+ !TLVP_PRESENT(&tp, RSL_IE_UPLINK_MEAS) ||
+ !TLVP_PRESENT(&tp, RSL_IE_BS_POWER))
+ return -EIO;
+
+ /* Mandatory Parts */
+ mr->nr = *TLVP_VAL(&tp, RSL_IE_MEAS_RES_NR);
+
+ len = TLVP_LEN(&tp, RSL_IE_UPLINK_MEAS);
+ val = TLVP_VAL(&tp, RSL_IE_UPLINK_MEAS);
+ if (len >= 3) {
+ if (val[0] & 0x40)
+ mr->flags |= MEAS_REP_F_DL_DTX;
+ mr->ul.full.rx_lev = val[0] & 0x3f;
+ mr->ul.sub.rx_lev = val[1] & 0x3f;
+ mr->ul.full.rx_qual = val[2]>>3 & 0x7;
+ mr->ul.sub.rx_qual = val[2] & 0x7;
}
- if (TLVP_PRESENT(&tp, RSL_IE_BS_POWER))
- DEBUGPC(DMEAS, "BS_POWER=%d ", *TLVP_VAL(&tp, RSL_IE_BS_POWER));
+
+ mr->bs_power = *TLVP_VAL(&tp, RSL_IE_BS_POWER);
+
+ /* Optional Parts */
if (TLVP_PRESENT(&tp, RSL_IE_MS_TIMING_OFFSET))
- DEBUGPC(DMEAS, "MS_TO=%d ",
- *TLVP_VAL(&tp, RSL_IE_MS_TIMING_OFFSET));
+ mr->ms_timing_offset =
+ *TLVP_VAL(&tp, RSL_IE_MS_TIMING_OFFSET);
+
if (TLVP_PRESENT(&tp, RSL_IE_L1_INFO)) {
- const u_int8_t *val = TLVP_VAL(&tp, RSL_IE_L1_INFO);
- u_int8_t pwr_lvl = val[0] >> 3;
- DEBUGPC(DMEAS, "L1_MS_PWR=%ddBm ",
- ms_pwr_dbm(msg->trx->bts->band, pwr_lvl));
- DEBUGPC(DMEAS, "L1_FPC=%u ", val[0] & 0x04 ? 1 : 0);
- DEBUGPC(DMEAS, "L1_TA=%u ", val[1]);
+ val = TLVP_VAL(&tp, RSL_IE_L1_INFO);
+ mr->flags |= MEAS_REP_F_MS_L1;
+ mr->ms_l1.pwr = ms_pwr_dbm(msg->trx->bts->band, val[0] >> 3);
+ if (val[0] & 0x04)
+ mr->flags |= MEAS_REP_F_FPC;
+ mr->ms_l1.ta = val[1];
}
if (TLVP_PRESENT(&tp, RSL_IE_L3_INFO)) {
- DEBUGPC(DMEAS, "L3\n");
msg->l3h = (u_int8_t *) TLVP_VAL(&tp, RSL_IE_L3_INFO);
- return gsm0408_rcvmsg(msg, 0);
- } else
- DEBUGPC(DMEAS, "\n");
+ rc = gsm48_parse_meas_rep(mr, msg);
+ if (rc < 0)
+ return rc;
+ }
+
+ print_meas_rep(mr);
+
+ dispatch_signal(SS_LCHAN, S_LCHAN_MEAS_REP, mr);
+
+ return 0;
+}
+
+/* Chapter 8.4.7 */
+static int rsl_rx_hando_det(struct msgb *msg)
+{
+ struct abis_rsl_dchan_hdr *dh = msgb_l2(msg);
+ struct tlv_parsed tp;
+
+ DEBUGP(DRSL, "%s HANDOVER DETECT ", gsm_lchan_name(msg->lchan));
+
+ rsl_tlv_parse(&tp, dh->data, msgb_l2len(msg)-sizeof(*dh));
+
+ if (TLVP_PRESENT(&tp, RSL_IE_ACCESS_DELAY))
+ DEBUGPC(DRSL, "access delay = %u\n",
+ *TLVP_VAL(&tp, RSL_IE_ACCESS_DELAY));
+ else
+ DEBUGPC(DRSL, "\n");
+
+ dispatch_signal(SS_LCHAN, S_LCHAN_HANDOVER_DETECT, msg->lchan);
return 0;
}
@@ -996,18 +1154,14 @@ static int abis_rsl_rx_dchan(struct msgb *msg)
char *ts_name;
msg->lchan = lchan_lookup(msg->trx, rslh->chan_nr);
- ts_name = gsm_ts_name(msg->lchan->ts);
-
- if (rslh->c.msg_type != RSL_MT_MEAS_RES)
- DEBUGP(DRSL, "channel=%s chan_nr=0x%02x ", ts_name, rslh->chan_nr);
+ ts_name = gsm_lchan_name(msg->lchan);
switch (rslh->c.msg_type) {
case RSL_MT_CHAN_ACTIV_ACK:
- DEBUGPC(DRSL, "CHANNEL ACTIVATE ACK\n");
+ DEBUGP(DRSL, "%s CHANNEL ACTIVATE ACK\n", ts_name);
rc = rsl_rx_chan_act_ack(msg);
break;
case RSL_MT_CHAN_ACTIV_NACK:
- DEBUGPC(DRSL, "CHANNEL ACTIVATE NACK\n");
rc = rsl_rx_chan_act_nack(msg);
break;
case RSL_MT_CONN_FAIL:
@@ -1016,27 +1170,35 @@ static int abis_rsl_rx_dchan(struct msgb *msg)
case RSL_MT_MEAS_RES:
rc = rsl_rx_meas_res(msg);
break;
+ case RSL_MT_HANDO_DET:
+ rc = rsl_rx_hando_det(msg);
+ break;
case RSL_MT_RF_CHAN_REL_ACK:
- DEBUGPC(DRSL, "RF CHANNEL RELEASE ACK\n");
+ DEBUGP(DRSL, "%s RF CHANNEL RELEASE ACK\n", ts_name);
+ if (msg->lchan->state != LCHAN_S_REL_REQ)
+ LOGP(DRSL, LOGL_NOTICE, "%s CHAN REL ACK but state %s\n",
+ gsm_lchan_name(msg->lchan),
+ gsm_lchans_name(msg->lchan->state));
+ msg->lchan->state = LCHAN_S_NONE;
lchan_free(msg->lchan);
break;
case RSL_MT_MODE_MODIFY_ACK:
- DEBUGPC(DRSL, "CHANNEL MODE MODIFY ACK\n");
+ DEBUGP(DRSL, "%s CHANNEL MODE MODIFY ACK\n", ts_name);
break;
case RSL_MT_MODE_MODIFY_NACK:
- DEBUGPC(DRSL, "CHANNEL MODE MODIFY NACK\n");
+ LOGP(DRSL, LOGL_ERROR, "%s CHANNEL MODE MODIFY NACK\n", ts_name);
break;
case RSL_MT_IPAC_PDCH_ACT_ACK:
- DEBUGPC(DRSL, "IPAC PDCH ACT ACK\n");
+ DEBUGPC(DRSL, "%s IPAC PDCH ACT ACK\n", ts_name);
break;
case RSL_MT_IPAC_PDCH_ACT_NACK:
- DEBUGPC(DRSL, "IPAC PDCH ACT NACK\n");
+ LOGP(DRSL, LOGL_ERROR, "%s IPAC PDCH ACT NACK\n", ts_name);
break;
case RSL_MT_IPAC_PDCH_DEACT_ACK:
- DEBUGPC(DRSL, "IPAC PDCH DEACT ACK\n");
+ DEBUGP(DRSL, "%s IPAC PDCH DEACT ACK\n", ts_name);
break;
case RSL_MT_IPAC_PDCH_DEACT_NACK:
- DEBUGPC(DRSL, "IPAC PDCH DEACT NACK\n");
+ LOGP(DRSL, LOGL_ERROR, "%s IPAC PDCH DEACT NACK\n", ts_name);
break;
case RSL_MT_PHY_CONTEXT_CONF:
case RSL_MT_PREPROC_MEAS_RES:
@@ -1046,12 +1208,12 @@ static int abis_rsl_rx_dchan(struct msgb *msg)
case RSL_MT_MR_CODEC_MOD_ACK:
case RSL_MT_MR_CODEC_MOD_NACK:
case RSL_MT_MR_CODEC_MOD_PER:
- DEBUGPC(DRSL, "Unimplemented Abis RSL DChan msg 0x%02x\n",
- rslh->c.msg_type);
+ LOGP(DRSL, LOGL_NOTICE, "%s Unimplemented Abis RSL DChan "
+ "msg 0x%02x\n", ts_name, rslh->c.msg_type);
break;
default:
- DEBUGPC(DRSL, "unknown Abis RSL DChan msg 0x%02x\n",
- rslh->c.msg_type);
+ LOGP(DRSL, LOGL_NOTICE, "%s unknown Abis RSL DChan msg 0x%02x\n",
+ ts_name, rslh->c.msg_type);
return -EINVAL;
}
@@ -1063,15 +1225,15 @@ static int rsl_rx_error_rep(struct msgb *msg)
struct abis_rsl_common_hdr *rslh = msgb_l2(msg);
struct tlv_parsed tp;
- DEBUGP(DRSL, "ERROR REPORT ");
+ LOGP(DRSL, LOGL_ERROR, "%s ERROR REPORT ", gsm_trx_name(msg->trx));
rsl_tlv_parse(&tp, rslh->data, msgb_l2len(msg)-sizeof(*rslh));
if (TLVP_PRESENT(&tp, RSL_IE_CAUSE))
- print_rsl_cause(TLVP_VAL(&tp, RSL_IE_CAUSE),
+ print_rsl_cause(LOGL_ERROR, TLVP_VAL(&tp, RSL_IE_CAUSE),
TLVP_LEN(&tp, RSL_IE_CAUSE));
- DEBUGPC(DRSL, "\n");
+ LOGPC(DRSL, LOGL_ERROR, "\n");
return 0;
}
@@ -1087,15 +1249,16 @@ static int abis_rsl_rx_trx(struct msgb *msg)
break;
case RSL_MT_RF_RES_IND:
/* interference on idle channels of TRX */
- //DEBUGP(DRSL, "TRX: RF Interference Indication\n");
+ //DEBUGP(DRSL, "%s RF Resource Indication\n", gsm_trx_name(msg->trx));
break;
case RSL_MT_OVERLOAD:
/* indicate CCCH / ACCH / processor overload */
- DEBUGP(DRSL, "TRX: CCCH/ACCH/CPU Overload\n");
+ LOGP(DRSL, LOGL_ERROR, "%s CCCH/ACCH/CPU Overload\n",
+ gsm_trx_name(msg->trx));
break;
default:
- DEBUGP(DRSL, "Unknown Abis RSL TRX message type 0x%02x\n",
- rslh->msg_type);
+ LOGP(DRSL, LOGL_NOTICE, "%s Unknown Abis RSL TRX message "
+ "type 0x%02x\n", gsm_trx_name(msg->trx), rslh->msg_type);
return -EINVAL;
}
return rc;
@@ -1141,15 +1304,24 @@ static int rsl_rx_chan_rqd(struct msgb *msg)
lctype = get_ctype_by_chreq(bts, rqd_ref->ra, bts->network->neci);
chreq_reason = get_reason_by_chreq(bts, rqd_ref->ra, bts->network->neci);
+ counter_inc(bts->network->stats.chreq.total);
+
/* check availability / allocate channel */
lchan = lchan_alloc(bts, lctype);
if (!lchan) {
- DEBUGP(DRSL, "CHAN RQD: no resources for %u 0x%x\n",
- lctype, rqd_ref->ra);
+ LOGP(DRSL, LOGL_NOTICE, "BTS %d CHAN RQD: no resources for %s 0x%x\n",
+ msg->lchan->ts->trx->bts->nr, gsm_lchant_name(lctype), rqd_ref->ra);
+ counter_inc(bts->network->stats.chreq.no_channel);
/* FIXME: send some kind of reject ?!? */
return -ENOMEM;
}
+ if (lchan->state != LCHAN_S_NONE)
+ LOGP(DRSL, LOGL_NOTICE, "%s lchan_alloc() returned channel "
+ "in state %s\n", gsm_lchan_name(lchan),
+ gsm_lchans_name(lchan->state));
+ lchan->state = LCHAN_S_ACT_REQ;
+
ts_number = lchan->ts->nr;
arfcn = lchan->ts->trx->arfcn;
subch = lchan->nr;
@@ -1159,7 +1331,7 @@ static int rsl_rx_chan_rqd(struct msgb *msg)
lchan->bs_power = 0; /* 0dB reduction, output power = Pn */
lchan->rsl_cmode = RSL_CMOD_SPD_SIGN;
lchan->tch_mode = GSM48_CMODE_SIGN;
- rsl_chan_activate_lchan(lchan, 0x00, rqd_ta);
+ rsl_chan_activate_lchan(lchan, 0x00, rqd_ta, 0);
/* create IMMEDIATE ASSIGN 04.08 messge */
memset(&ia, 0, sizeof(ia));
@@ -1177,16 +1349,15 @@ static int rsl_rx_chan_rqd(struct msgb *msg)
ia.timing_advance = rqd_ta;
ia.mob_alloc_len = 0;
- DEBUGP(DRSL, "Activating ARFCN(%u) TS(%u) SS(%u) lctype %s "
- "chan_nr=0x%02x r=%s ra=0x%02x\n",
- arfcn, ts_number, subch, gsm_lchan_name(lchan->type),
- ia.chan_desc.chan_nr, gsm_chreq_name(chreq_reason),
+ DEBUGP(DRSL, "%s Activating ARFCN(%u) SS(%u) lctype %s "
+ "r=%s ra=0x%02x\n", gsm_lchan_name(lchan), arfcn, subch,
+ gsm_lchant_name(lchan->type), gsm_chreq_name(chreq_reason),
rqd_ref->ra);
/* Start timer T3101 to wait for GSM48_MT_RR_PAG_RESP */
lchan->T3101.cb = t3101_expired;
lchan->T3101.data = lchan;
- bsc_schedule_timer(&lchan->T3101, 10, 0);
+ bsc_schedule_timer(&lchan->T3101, bts->network->T3101, 0);
/* send IMMEDIATE ASSIGN CMD on RSL to BTS (to send on CCCH to MS) */
ret = rsl_imm_assign_cmd(bts, sizeof(ia), (u_int8_t *) &ia);
@@ -1242,12 +1413,12 @@ static int abis_rsl_rx_cchan(struct msgb *msg)
/* CCCH overloaded, IMM_ASSIGN was dropped */
case RSL_MT_CBCH_LOAD_IND:
/* current load on the CBCH */
- fprintf(stderr, "Unimplemented Abis RSL TRX message type "
- "0x%02x\n", rslh->c.msg_type);
+ LOGP(DRSL, LOGL_NOTICE, "Unimplemented Abis RSL TRX message "
+ "type 0x%02x\n", rslh->c.msg_type);
break;
default:
- fprintf(stderr, "Unknown Abis RSL TRX message type 0x%02x\n",
- rslh->c.msg_type);
+ LOGP(DRSL, LOGL_NOTICE, "Unknown Abis RSL TRX message type "
+ "0x%02x\n", rslh->c.msg_type);
return -EINVAL;
}
@@ -1259,10 +1430,12 @@ static int rsl_rx_rll_err_ind(struct msgb *msg)
struct abis_rsl_rll_hdr *rllh = msgb_l2(msg);
u_int8_t *rlm_cause = rllh->data;
- DEBUGPC(DRLL, "ERROR INDICATION cause=0x%02x\n", rlm_cause[1]);
+ LOGP(DRLL, LOGL_ERROR, "%s ERROR INDICATION cause=%s\n",
+ gsm_lchan_name(msg->lchan),
+ get_value_string(rlm_cause_strs, rlm_cause[1]));
rll_indication(msg->lchan, rllh->link_id, BSC_RLLR_IND_ERR_IND);
-
+
if (rlm_cause[1] == RLL_CAUSE_T200_EXPIRED)
return rsl_rf_chan_release(msg->lchan);
@@ -1283,9 +1456,8 @@ static int abis_rsl_rx_rll(struct msgb *msg)
u_int8_t sapi = rllh->link_id & 7;
msg->lchan = lchan_lookup(msg->trx, rllh->chan_nr);
- ts_name = gsm_ts_name(msg->lchan->ts);
- DEBUGP(DRLL, "channel=%s chan_nr=0x%02x sapi=%u ", ts_name,
- rllh->chan_nr, sapi);
+ ts_name = gsm_lchan_name(msg->lchan);
+ DEBUGP(DRLL, "%s SAPI=%u ", ts_name, sapi);
switch (rllh->c.msg_type) {
case RSL_MT_DATA_IND:
@@ -1340,38 +1512,111 @@ static int abis_rsl_rx_rll(struct msgb *msg)
rc = rsl_rx_rll_err_ind(msg);
break;
case RSL_MT_UNIT_DATA_IND:
- DEBUGPC(DRLL, "unimplemented Abis RLL message type 0x%02x\n",
- rllh->c.msg_type);
+ LOGP(DRLL, LOGL_NOTICE, "unimplemented Abis RLL message "
+ "type 0x%02x\n", rllh->c.msg_type);
break;
default:
- DEBUGPC(DRLL, "unknown Abis RLL message type 0x%02x\n",
- rllh->c.msg_type);
+ LOGP(DRLL, LOGL_NOTICE, "unknown Abis RLL message "
+ "type 0x%02x\n", rllh->c.msg_type);
}
return rc;
}
-static u_int8_t ipa_smod_s_for_tch_mode(u_int8_t tch_mode)
+static u_int8_t ipa_smod_s_for_lchan(struct gsm_lchan *lchan)
{
- switch (tch_mode) {
+ switch (lchan->tch_mode) {
case GSM48_CMODE_SPEECH_V1:
- return 0x00;
+ switch (lchan->type) {
+ case GSM_LCHAN_TCH_F:
+ return 0x00;
+ case GSM_LCHAN_TCH_H:
+ return 0x03;
+ default:
+ break;
+ }
case GSM48_CMODE_SPEECH_EFR:
- return 0x01;
+ switch (lchan->type) {
+ case GSM_LCHAN_TCH_F:
+ return 0x01;
+ /* there's no half-rate EFR */
+ default:
+ break;
+ }
case GSM48_CMODE_SPEECH_AMR:
- return 0x02;
- /* FIXME: Type1 half-rate and type3 half-rate */
+ switch (lchan->type) {
+ case GSM_LCHAN_TCH_F:
+ return 0x02;
+ case GSM_LCHAN_TCH_H:
+ return 0x05;
+ default:
+ break;
+ }
+ default:
+ break;
}
- DEBUGPC(DRSL, "Cannot determine ip.access speech mode for "
- "tch_mode == 0x%02x\n", tch_mode);
+ LOGP(DRSL, LOGL_ERROR, "Cannot determine ip.access speech mode for "
+ "tch_mode == 0x%02x\n", lchan->tch_mode);
return 0;
}
/* ip.access specific RSL extensions */
+static void ipac_parse_rtp(struct gsm_lchan *lchan, struct tlv_parsed *tv)
+{
+ struct in_addr ip;
+ u_int16_t port, conn_id;
+
+ if (TLVP_PRESENT(tv, RSL_IE_IPAC_LOCAL_IP)) {
+ ip.s_addr = *((u_int32_t *) TLVP_VAL(tv, RSL_IE_IPAC_LOCAL_IP));
+ DEBUGPC(DRSL, "LOCAL_IP=%s ", inet_ntoa(ip));
+ lchan->abis_ip.bound_ip = ntohl(ip.s_addr);
+ }
+
+ if (TLVP_PRESENT(tv, RSL_IE_IPAC_LOCAL_PORT)) {
+ port = *((u_int16_t *) TLVP_VAL(tv, RSL_IE_IPAC_LOCAL_PORT));
+ port = ntohs(port);
+ DEBUGPC(DRSL, "LOCAL_PORT=%u ", port);
+ lchan->abis_ip.bound_port = port;
+ }
+
+ if (TLVP_PRESENT(tv, RSL_IE_IPAC_CONN_ID)) {
+ conn_id = *((u_int16_t *) TLVP_VAL(tv, RSL_IE_IPAC_CONN_ID));
+ conn_id = ntohs(conn_id);
+ DEBUGPC(DRSL, "CON_ID=%u ", conn_id);
+ lchan->abis_ip.conn_id = conn_id;
+ }
+
+ if (TLVP_PRESENT(tv, RSL_IE_IPAC_RTP_PAYLOAD2)) {
+ lchan->abis_ip.rtp_payload2 =
+ *TLVP_VAL(tv, RSL_IE_IPAC_RTP_PAYLOAD2);
+ DEBUGPC(DRSL, "RTP_PAYLOAD2=0x%02x ",
+ lchan->abis_ip.rtp_payload2);
+ }
+
+ if (TLVP_PRESENT(tv, RSL_IE_IPAC_SPEECH_MODE)) {
+ lchan->abis_ip.speech_mode =
+ *TLVP_VAL(tv, RSL_IE_IPAC_SPEECH_MODE);
+ DEBUGPC(DRSL, "speech_mode=0x%02x ",
+ lchan->abis_ip.speech_mode);
+ }
+
+ if (TLVP_PRESENT(tv, RSL_IE_IPAC_REMOTE_IP)) {
+ ip.s_addr = *((u_int32_t *) TLVP_VAL(tv, RSL_IE_IPAC_REMOTE_IP));
+ DEBUGPC(DRSL, "REMOTE_IP=%s ", inet_ntoa(ip));
+ lchan->abis_ip.connect_ip = ntohl(ip.s_addr);
+ }
+
+ if (TLVP_PRESENT(tv, RSL_IE_IPAC_REMOTE_PORT)) {
+ port = *((u_int16_t *) TLVP_VAL(tv, RSL_IE_IPAC_REMOTE_PORT));
+ port = ntohs(port);
+ DEBUGPC(DRSL, "REMOTE_PORT=%u ", port);
+ lchan->abis_ip.connect_port = port;
+ }
+}
+
int rsl_ipacc_crcx(struct gsm_lchan *lchan)
{
struct msgb *msg = rsl_msgb_alloc();
struct abis_rsl_dchan_hdr *dh;
- u_int8_t speech_mode;
dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh));
init_dchan_hdr(dh, RSL_MT_IPAC_CRCX);
@@ -1379,12 +1624,11 @@ int rsl_ipacc_crcx(struct gsm_lchan *lchan)
dh->chan_nr = lchan2chan_nr(lchan);
/* 0x1- == receive-only, 0x-1 == EFR codec */
- speech_mode = 0x10 | ipa_smod_s_for_tch_mode(lchan->tch_mode);
- msgb_tv_put(msg, RSL_IE_IPAC_SPEECH_MODE, speech_mode);
+ lchan->abis_ip.speech_mode = 0x10 | ipa_smod_s_for_lchan(lchan);
+ msgb_tv_put(msg, RSL_IE_IPAC_SPEECH_MODE, lchan->abis_ip.speech_mode);
- DEBUGP(DRSL, "channel=%s chan_nr=0x%02x IPAC_BIND "
- "speech_mode=0x%02x\n", gsm_ts_name(lchan->ts),
- dh->chan_nr, speech_mode);
+ DEBUGP(DRSL, "%s IPAC_BIND speech_mode=0x%02x\n",
+ gsm_lchan_name(lchan), lchan->abis_ip.speech_mode);
msg->trx = lchan->ts->trx;
@@ -1392,12 +1636,11 @@ int rsl_ipacc_crcx(struct gsm_lchan *lchan)
}
int rsl_ipacc_mdcx(struct gsm_lchan *lchan, u_int32_t ip, u_int16_t port,
- u_int16_t conn_id, u_int8_t rtp_payload2)
+ u_int8_t rtp_payload2)
{
struct msgb *msg = rsl_msgb_alloc();
struct abis_rsl_dchan_hdr *dh;
- u_int8_t *att_f8, *att_ip, *att_port;
- u_int8_t speech_mode;
+ u_int32_t *att_ip;
struct in_addr ia;
dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh));
@@ -1405,34 +1648,25 @@ int rsl_ipacc_mdcx(struct gsm_lchan *lchan, u_int32_t ip, u_int16_t port,
dh->c.msg_discr = ABIS_RSL_MDISC_IPACCESS;
dh->chan_nr = lchan2chan_nr(lchan);
+ /* we need to store these now as MDCX_ACK does not return them :( */
+ lchan->abis_ip.rtp_payload2 = rtp_payload2;
+ lchan->abis_ip.connect_port = port;
+ lchan->abis_ip.connect_ip = ip;
+
/* 0x0- == both directions, 0x-1 == EFR codec */
- speech_mode = 0x00 | ipa_smod_s_for_tch_mode(lchan->tch_mode);
+ lchan->abis_ip.speech_mode = 0x00 | ipa_smod_s_for_lchan(lchan);
ia.s_addr = htonl(ip);
- DEBUGP(DRSL, "channel=%s chan_nr=0x%02x IPAC_MDCX "
- "IP=%s PORT=%d RTP_PAYLOAD2=%d CONN_ID=%d speech_mode=0x%02x\n",
- gsm_ts_name(lchan->ts), dh->chan_nr,
- inet_ntoa(ia), port, rtp_payload2, conn_id, speech_mode);
-
- att_f8 = msgb_put(msg, sizeof(conn_id)+1);
- att_f8[0] = RSL_IE_IPAC_CONN_ID;
- att_f8[1] = conn_id >> 8;
- att_f8[2] = conn_id & 0xff;
-
- att_ip = msgb_put(msg, sizeof(ip)+1);
- att_ip[0] = RSL_IE_IPAC_REMOTE_IP;
- att_ip[1] = ip >> 24;
- att_ip[2] = ip >> 16;
- att_ip[3] = ip >> 8;
- att_ip[4] = ip & 0xff;
- //att_ip[4] = 11;
-
- att_port = msgb_put(msg, sizeof(port)+1);
- att_port[0] = RSL_IE_IPAC_REMOTE_PORT;
- att_port[1] = port >> 8;
- att_port[2] = port & 0xff;
-
- msgb_tv_put(msg, RSL_IE_IPAC_SPEECH_MODE, speech_mode);
+ DEBUGP(DRSL, "%s IPAC_MDCX IP=%s PORT=%d RTP_PAYLOAD2=%d CONN_ID=%d "
+ "speech_mode=0x%02x\n", gsm_lchan_name(lchan), inet_ntoa(ia), port,
+ rtp_payload2, lchan->abis_ip.conn_id, lchan->abis_ip.speech_mode);
+
+ msgb_tv16_put(msg, RSL_IE_IPAC_CONN_ID, lchan->abis_ip.conn_id);
+ msgb_v_put(msg, RSL_IE_IPAC_REMOTE_IP);
+ att_ip = (u_int32_t *) msgb_put(msg, sizeof(ip));
+ *att_ip = ia.s_addr;
+ msgb_tv16_put(msg, RSL_IE_IPAC_REMOTE_PORT, port);
+ msgb_tv_put(msg, RSL_IE_IPAC_SPEECH_MODE, lchan->abis_ip.speech_mode);
if (rtp_payload2)
msgb_tv_put(msg, RSL_IE_IPAC_RTP_PAYLOAD2, rtp_payload2);
@@ -1441,6 +1675,20 @@ int rsl_ipacc_mdcx(struct gsm_lchan *lchan, u_int32_t ip, u_int16_t port,
return abis_rsl_sendmsg(msg);
}
+/* tell BTS to connect RTP stream to our local RTP socket */
+int rsl_ipacc_mdcx_to_rtpsock(struct gsm_lchan *lchan)
+{
+ struct rtp_socket *rs = lchan->abis_ip.rtp_socket;
+ int rc;
+
+ rc = rsl_ipacc_mdcx(lchan, ntohl(rs->rtp.sin_local.sin_addr.s_addr),
+ ntohs(rs->rtp.sin_local.sin_port),
+ /* FIXME: use RTP payload of bound socket, not BTS*/
+ lchan->abis_ip.rtp_payload2);
+
+ return rc;
+}
+
int rsl_ipacc_pdch_activate(struct gsm_lchan *lchan)
{
struct msgb *msg = rsl_msgb_alloc();
@@ -1451,8 +1699,7 @@ int rsl_ipacc_pdch_activate(struct gsm_lchan *lchan)
dh->c.msg_discr = ABIS_RSL_MDISC_DED_CHAN;
dh->chan_nr = lchan2chan_nr(lchan);
- DEBUGP(DRSL, "channel=%s chan_nr=0x%02x IPAC_PDCH_ACT\n",
- gsm_ts_name(lchan->ts), dh->chan_nr);
+ DEBUGP(DRSL, "%s IPAC_PDCH_ACT\n", gsm_lchan_name(lchan));
msg->trx = lchan->ts->trx;
@@ -1463,9 +1710,7 @@ static int abis_rsl_rx_ipacc_crcx_ack(struct msgb *msg)
{
struct abis_rsl_dchan_hdr *dh = msgb_l2(msg);
struct tlv_parsed tv;
- struct gsm_bts_trx_ts *ts = msg->lchan->ts;
- struct in_addr ip;
- u_int16_t port, attr_f8;
+ struct gsm_lchan *lchan = msg->lchan;
/* the BTS has acknowledged a local bind, it now tells us the IP
* address and port number to which it has bound the given logical
@@ -1475,44 +1720,75 @@ static int abis_rsl_rx_ipacc_crcx_ack(struct msgb *msg)
if (!TLVP_PRESENT(&tv, RSL_IE_IPAC_LOCAL_PORT) ||
!TLVP_PRESENT(&tv, RSL_IE_IPAC_LOCAL_IP) ||
!TLVP_PRESENT(&tv, RSL_IE_IPAC_CONN_ID)) {
- DEBUGPC(DRSL, "mandatory IE missing");
+ LOGP(DRSL, LOGL_NOTICE, "mandatory IE missing");
return -EINVAL;
}
- ip.s_addr = *((u_int32_t *) TLVP_VAL(&tv, RSL_IE_IPAC_LOCAL_IP));
- port = *((u_int16_t *) TLVP_VAL(&tv, RSL_IE_IPAC_LOCAL_PORT));
- attr_f8 = *((u_int16_t *) TLVP_VAL(&tv, 0xf8));
- DEBUGPC(DRSL, "IP=%s PORT=%d CONN_ID=%d ",
- inet_ntoa(ip), ntohs(port), ntohs(attr_f8));
+ ipac_parse_rtp(lchan, &tv);
- if (TLVP_PRESENT(&tv, RSL_IE_IPAC_RTP_PAYLOAD2)) {
- ts->abis_ip.rtp_payload2 =
- *TLVP_VAL(&tv, RSL_IE_IPAC_RTP_PAYLOAD2);
- DEBUGPC(DRSL, "RTP_PAYLOAD2=0x%02x ",
- ts->abis_ip.rtp_payload2);
- }
+ /* in case we don't use direct BTS-to-BTS RTP */
+ if (!ipacc_rtp_direct) {
+ int rc;
+ /* the BTS has successfully bound a TCH to a local ip/port,
+ * which means we can connect our UDP socket to it */
+ if (lchan->abis_ip.rtp_socket) {
+ rtp_socket_free(lchan->abis_ip.rtp_socket);
+ lchan->abis_ip.rtp_socket = NULL;
+ }
+
+ lchan->abis_ip.rtp_socket = rtp_socket_create();
+ if (!lchan->abis_ip.rtp_socket)
+ goto out_err;
- /* update our local information about this TS */
- ts->abis_ip.bound_ip = ntohl(ip.s_addr);
- ts->abis_ip.bound_port = ntohs(port);
- ts->abis_ip.conn_id = ntohs(attr_f8);
+ rc = rtp_socket_connect(lchan->abis_ip.rtp_socket,
+ lchan->abis_ip.bound_ip,
+ lchan->abis_ip.bound_port);
+ if (rc < 0)
+ goto out_err;
+ }
dispatch_signal(SS_ABISIP, S_ABISIP_CRCX_ACK, msg->lchan);
return 0;
+out_err:
+ return -EIO;
+}
+
+static int abis_rsl_rx_ipacc_mdcx_ack(struct msgb *msg)
+{
+ struct abis_rsl_dchan_hdr *dh = msgb_l2(msg);
+ struct tlv_parsed tv;
+ struct gsm_lchan *lchan = msg->lchan;
+
+ /* the BTS has acknowledged a remote connect request and
+ * it now tells us the IP address and port number to which it has
+ * connected the given logical channel */
+
+ rsl_tlv_parse(&tv, dh->data, msgb_l2len(msg)-sizeof(*dh));
+ ipac_parse_rtp(lchan, &tv);
+ dispatch_signal(SS_ABISIP, S_ABISIP_MDCX_ACK, msg->lchan);
+
+ return 0;
}
static int abis_rsl_rx_ipacc_dlcx_ind(struct msgb *msg)
{
struct abis_rsl_dchan_hdr *dh = msgb_l2(msg);
struct tlv_parsed tv;
+ struct gsm_lchan *lchan = msg->lchan;
rsl_tlv_parse(&tv, dh->data, msgb_l2len(msg)-sizeof(*dh));
if (TLVP_PRESENT(&tv, RSL_IE_CAUSE))
- print_rsl_cause(TLVP_VAL(&tv, RSL_IE_CAUSE),
+ print_rsl_cause(LOGL_DEBUG, TLVP_VAL(&tv, RSL_IE_CAUSE),
TLVP_LEN(&tv, RSL_IE_CAUSE));
+ /* the BTS tells us a RTP stream has been disconnected */
+ if (lchan->abis_ip.rtp_socket) {
+ rtp_socket_free(lchan->abis_ip.rtp_socket);
+ lchan->abis_ip.rtp_socket = NULL;
+ }
+
dispatch_signal(SS_ABISIP, S_ABISIP_DLCX_IND, msg->lchan);
return 0;
@@ -1521,37 +1797,39 @@ static int abis_rsl_rx_ipacc_dlcx_ind(struct msgb *msg)
static int abis_rsl_rx_ipacc(struct msgb *msg)
{
struct abis_rsl_rll_hdr *rllh = msgb_l2(msg);
+ char *ts_name;
int rc = 0;
msg->lchan = lchan_lookup(msg->trx, rllh->chan_nr);
- DEBUGP(DRSL, "channel=%s chan_nr=0x%02x ",
- gsm_ts_name(msg->lchan->ts), rllh->chan_nr);
+ ts_name = gsm_lchan_name(msg->lchan);
switch (rllh->c.msg_type) {
case RSL_MT_IPAC_CRCX_ACK:
- DEBUGPC(DRSL, "IPAC_CRCX_ACK ");
+ DEBUGP(DRSL, "%s IPAC_CRCX_ACK ", ts_name);
rc = abis_rsl_rx_ipacc_crcx_ack(msg);
break;
case RSL_MT_IPAC_CRCX_NACK:
/* somehow the BTS was unable to bind the lchan to its local
* port?!? */
- DEBUGPC(DRSL, "IPAC_CRCX_NACK ");
+ LOGP(DRSL, LOGL_ERROR, "%s IPAC_CRCX_NACK\n", ts_name);
break;
case RSL_MT_IPAC_MDCX_ACK:
/* the BTS tells us that a connect operation was successful */
- DEBUGPC(DRSL, "IPAC_MDCX_ACK ");
+ DEBUGP(DRSL, "%s IPAC_MDCX_ACK ", ts_name);
+ rc = abis_rsl_rx_ipacc_mdcx_ack(msg);
break;
case RSL_MT_IPAC_MDCX_NACK:
/* somehow the BTS was unable to connect the lchan to a remote
* port */
- DEBUGPC(DRSL, "IPAC_MDCX_NACK ");
+ LOGP(DRSL, LOGL_ERROR, "%s IPAC_MDCX_NACK\n", ts_name);
break;
case RSL_MT_IPAC_DLCX_IND:
- DEBUGPC(DRSL, "IPAC_DLCX_IND ");
+ DEBUGP(DRSL, "%s IPAC_DLCX_IND ", ts_name);
rc = abis_rsl_rx_ipacc_dlcx_ind(msg);
break;
default:
- DEBUGPC(DRSL, "Unknown ip.access msg_type 0x%02x", rllh->c.msg_type);
+ LOGP(DRSL, LOGL_NOTICE, "Unknown ip.access msg_type 0x%02x\n",
+ rllh->c.msg_type);
break;
}
DEBUGPC(DRSL, "\n");
@@ -1580,15 +1858,15 @@ int abis_rsl_rcvmsg(struct msgb *msg)
rc = abis_rsl_rx_trx(msg);
break;
case ABIS_RSL_MDISC_LOC:
- fprintf(stderr, "unimplemented RSL msg disc 0x%02x\n",
+ LOGP(DRSL, LOGL_NOTICE, "unimplemented RSL msg disc 0x%02x\n",
rslh->msg_discr);
break;
case ABIS_RSL_MDISC_IPACCESS:
rc = abis_rsl_rx_ipacc(msg);
break;
default:
- fprintf(stderr, "unknown RSL message discriminator 0x%02x\n",
- rslh->msg_discr);
+ LOGP(DRSL, LOGL_NOTICE, "unknown RSL message discriminator "
+ "0x%02x\n", rslh->msg_discr);
return -EINVAL;
}
msgb_free(msg);
@@ -1637,11 +1915,11 @@ int rsl_ccch_conf_to_bs_ccch_sdcch_comb(int ccch_conf)
/* From Table 10.5.33 of GSM 04.08 */
int rsl_number_of_paging_subchannels(struct gsm_bts *bts)
{
- if (bts->chan_desc.ccch_conf == RSL_BCCH_CCCH_CONF_1_C) {
- return MAX(1, (3 - bts->chan_desc.bs_ag_blks_res))
- * (bts->chan_desc.bs_pa_mfrms + 2);
+ if (bts->si_common.chan_desc.ccch_conf == RSL_BCCH_CCCH_CONF_1_C) {
+ return MAX(1, (3 - bts->si_common.chan_desc.bs_ag_blks_res))
+ * (bts->si_common.chan_desc.bs_pa_mfrms + 2);
} else {
- return (9 - bts->chan_desc.bs_ag_blks_res)
- * (bts->chan_desc.bs_pa_mfrms + 2);
+ return (9 - bts->si_common.chan_desc.bs_ag_blks_res)
+ * (bts->si_common.chan_desc.bs_pa_mfrms + 2);
}
}
diff --git a/openbsc/src/bitvec.c b/openbsc/src/bitvec.c
new file mode 100644
index 000000000..d6f5679cf
--- /dev/null
+++ b/openbsc/src/bitvec.c
@@ -0,0 +1,170 @@
+/* bit vector utility routines */
+
+/* (C) 2009 by Harald Welte <laforge@gnumonks.org>
+ *
+ * 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 <errno.h>
+#include <sys/types.h>
+
+#include <openbsc/bitvec.h>
+
+#define BITNUM_FROM_COMP(byte, bit) ((byte*8)+bit)
+
+static inline unsigned int bytenum_from_bitnum(unsigned int bitnum)
+{
+ unsigned int bytenum = bitnum / 8;
+
+ return bytenum;
+}
+
+/* convert ZERO/ONE/L/H to a bitmask at given pos in a byte */
+static u_int8_t bitval2mask(enum bit_value bit, u_int8_t bitnum)
+{
+ int bitval;
+
+ switch (bit) {
+ case ZERO:
+ bitval = (0 << bitnum);
+ break;
+ case ONE:
+ bitval = (1 << bitnum);
+ break;
+ case L:
+ bitval = ((0x2b ^ (0 << bitnum)) & (1 << bitnum));
+ break;
+ case H:
+ bitval = ((0x2b ^ (1 << bitnum)) & (1 << bitnum));
+ break;
+ default:
+ return 0;
+ }
+ return bitval;
+}
+
+/* check if the bit is 0 or 1 for a given position inside a bitvec */
+enum bit_value bitvec_get_bit_pos(struct bitvec *bv, unsigned int bitnr)
+{
+ unsigned int bytenum = bytenum_from_bitnum(bitnr);
+ unsigned int bitnum = 7 - (bitnr % 8);
+ u_int8_t bitval;
+
+ if (bytenum >= bv->data_len)
+ return -EINVAL;
+
+ bitval = bitval2mask(ONE, bitnum);
+
+ if (bv->data[bytenum] & bitval)
+ return ONE;
+
+ return ZERO;
+}
+
+/* get the Nth set bit inside the bit vector */
+unsigned int bitvec_get_nth_set_bit(struct bitvec *bv, unsigned int n)
+{
+ unsigned int i, k = 0;
+
+ for (i = 0; i < bv->data_len*8; i++) {
+ if (bitvec_get_bit_pos(bv, i) == ONE) {
+ k++;
+ if (k == n)
+ return i;
+ }
+ }
+
+ return 0;
+}
+
+/* set the bit at a given position inside a bitvec */
+int bitvec_set_bit_pos(struct bitvec *bv, unsigned int bitnr,
+ enum bit_value bit)
+{
+ unsigned int bytenum = bytenum_from_bitnum(bitnr);
+ unsigned int bitnum = 7 - (bitnr % 8);
+ u_int8_t bitval;
+
+ if (bytenum >= bv->data_len)
+ return -EINVAL;
+
+ /* first clear the bit */
+ bitval = bitval2mask(ONE, bitnum);
+ bv->data[bytenum] &= ~bitval;
+
+ /* then set it to desired value */
+ bitval = bitval2mask(bit, bitnum);
+ bv->data[bytenum] |= bitval;
+
+ return 0;
+}
+
+/* set the next bit inside a bitvec */
+int bitvec_set_bit(struct bitvec *bv, enum bit_value bit)
+{
+ int rc;
+
+ rc = bitvec_set_bit_pos(bv, bv->cur_bit, bit);
+ if (!rc)
+ bv->cur_bit++;
+
+ return rc;
+}
+
+/* set multiple bits (based on array of bitvals) at current pos */
+int bitvec_set_bits(struct bitvec *bv, enum bit_value *bits, int count)
+{
+ int i, rc;
+
+ for (i = 0; i < count; i++) {
+ rc = bitvec_set_bit(bv, bits[i]);
+ if (rc)
+ return rc;
+ }
+
+ return 0;
+}
+
+/* set multiple bits (based on numeric value) at current pos */
+int bitvec_set_uint(struct bitvec *bv, unsigned int ui, int num_bits)
+{
+ int i, rc;
+
+ for (i = 0; i < num_bits; i++) {
+ int bit = 0;
+ if (ui & (1 << (num_bits - i - 1)))
+ bit = 1;
+ rc = bitvec_set_bit(bv, bit);
+ if (rc)
+ return rc;
+ }
+
+ return 0;
+}
+
+/* pad all remaining bits up to num_bits */
+int bitvec_spare_padding(struct bitvec *bv, unsigned int up_to_bit)
+{
+ unsigned int i;
+
+ for (i = bv->cur_bit; i <= up_to_bit; i++)
+ bitvec_set_bit(bv, L);
+
+ return 0;
+}
diff --git a/openbsc/src/bs11_config.c b/openbsc/src/bs11_config.c
index 3e8bf88a3..703591eed 100644
--- a/openbsc/src/bs11_config.c
+++ b/openbsc/src/bs11_config.c
@@ -51,7 +51,7 @@ enum bs11cfg_state {
STATE_QUERY,
};
static enum bs11cfg_state bs11cfg_state = STATE_NONE;
-static char *command;
+static char *command, *value;
struct timer_list status_timer;
static const u_int8_t obj_li_attr[] = {
@@ -71,6 +71,13 @@ static const char *trx1_password = "1111111111";
static const u_int8_t too_fast[] = { 0x12, 0x80, 0x00, 0x00, 0x02, 0x02 };
+static struct debug_target *stderr_target;
+
+/* dummy function to keep gsm_data.c happy */
+struct counter *counter_alloc(const char *name)
+{
+ return NULL;
+}
int handle_serial_msg(struct msgb *rx_msg);
@@ -533,6 +540,21 @@ static int handle_state_resp(enum abis_bs11_phase state)
sleep(1);
abis_nm_bs11_factory_logon(g_bts, 0);
command = NULL;
+ } else if (!strcmp(command, "pll-setvalue")) {
+ abis_nm_bs11_set_pll(g_bts, atoi(value));
+ sleep(1);
+ abis_nm_bs11_factory_logon(g_bts, 0);
+ command = NULL;
+ } else if (!strcmp(command, "pll-workvalue")) {
+ /* To set the work value we need to login as FIELD */
+ abis_nm_bs11_factory_logon(g_bts, 0);
+ sleep(1);
+ abis_nm_bs11_infield_logon(g_bts, 1);
+ sleep(1);
+ abis_nm_bs11_set_pll(g_bts, atoi(value));
+ sleep(1);
+ abis_nm_bs11_infield_logon(g_bts, 0);
+ command = NULL;
} else if (!strcmp(command, "oml-tei")) {
abis_nm_bs11_conn_oml_tei(g_bts, 0, 1, 0xff, TEI_OML);
command = NULL;
@@ -627,7 +649,7 @@ int handle_serial_msg(struct msgb *rx_msg)
exit(0);
break;
case NM_MT_BS11_GET_STATE_ACK:
- rc = abis_nm_tlv_parse(&tp, foh->data, oh->length-sizeof(*foh));
+ rc = abis_nm_tlv_parse(&tp, g_bts, foh->data, oh->length-sizeof(*foh));
print_state(&tp);
if (TLVP_PRESENT(&tp, NM_ATT_BS11_BTS_STATE) &&
TLVP_LEN(&tp, NM_ATT_BS11_BTS_STATE) >= 1)
@@ -635,7 +657,7 @@ int handle_serial_msg(struct msgb *rx_msg)
break;
case NM_MT_GET_ATTR_RESP:
printf("\n%sATTRIBUTES:\n", obj_name(foh));
- abis_nm_tlv_parse(&tp, foh->data, oh->length-sizeof(*foh));
+ abis_nm_tlv_parse(&tp, g_bts, foh->data, oh->length-sizeof(*foh));
rc = print_attr(&tp);
//hexdump(foh->data, oh->length-sizeof(*foh));
break;
@@ -699,25 +721,27 @@ static void print_help(void)
printf("\t-p --port </dev/ttyXXX>\t\tSpecify serial port\n");
printf("\t-s --software <file>\t\tSpecify Software file\n");
printf("\t-S --safety <file>\t\tSpecify Safety Load file\n");
- printf("\t-d --delay <ms>\t\tSpecify delay in milliseconds\n");
+ printf("\t-d --delay <ms>\t\t\tSpecify delay in milliseconds\n");
printf("\t-D --disconnect\t\t\tDisconnect BTS from BSC\n");
printf("\t-w --win-size <num>\t\tSpecify Window Size\n");
printf("\t-f --forced\t\t\tForce Software Load\n");
printf("\nSupported commands:\n");
- printf("\tquery\t\tQuery the BS-11 about serial number and configuration\n");
- printf("\tdisconnect\tDisconnect A-bis link (go into administrative state)\n");
- printf("\tresconnect\tReconnect A-bis link (go into normal state)\n");
- printf("\trestart\t\tRestart the BTS\n");
- printf("\tsoftware\tDownload Software (only in administrative state)\n");
- printf("\tcreate-trx1\tCreate objects for TRX1 (Danger: Your BS-11 might overheat)\n");
- printf("\tdelete-trx1\tDelete objects for TRX1\n");
- printf("\tpll-e1-locked\tSet the PLL to be locked to E1 clock\n");
- printf("\tpll-standalone\tSet the PLL to be in standalone mode\n");
- printf("\toml-tei\tSet OML E1 TS and TEI\n");
- printf("\tbport0-star\tSet BPORT0 line config to star\n");
+ printf("\tquery\t\t\tQuery the BS-11 about serial number and configuration\n");
+ printf("\tdisconnect\t\tDisconnect A-bis link (go into administrative state)\n");
+ printf("\tresconnect\t\tReconnect A-bis link (go into normal state)\n");
+ printf("\trestart\t\t\tRestart the BTS\n");
+ printf("\tsoftware\t\tDownload Software (only in administrative state)\n");
+ printf("\tcreate-trx1\t\tCreate objects for TRX1 (Danger: Your BS-11 might overheat)\n");
+ printf("\tdelete-trx1\t\tDelete objects for TRX1\n");
+ printf("\tpll-e1-locked\t\tSet the PLL to be locked to E1 clock\n");
+ printf("\tpll-standalone\t\tSet the PLL to be in standalone mode\n");
+ printf("\tpll-setvalue <value>\tSet the PLL set value\n");
+ printf("\tpll-workvalue <value>\tSet the PLL work value\n");
+ printf("\toml-tei\t\t\tSet OML E1 TS and TEI\n");
+ printf("\tbport0-star\t\tSet BPORT0 line config to star\n");
printf("\tbport0-multiport\tSet BPORT0 line config to multiport\n");
- printf("\tcreate-bport1\tCreate BPORT1 object\n");
- printf("\tdelete-bport1\tDelete BPORT1 object\n");
+ printf("\tcreate-bport1\t\tCreate BPORT1 object\n");
+ printf("\tdelete-bport1\t\tDelete BPORT1 object\n");
}
static void handle_options(int argc, char **argv)
@@ -754,7 +778,7 @@ static void handle_options(int argc, char **argv)
serial_port = optarg;
break;
case 'b':
- debug_parse_category_mask(optarg);
+ debug_parse_category_mask(stderr_target, optarg);
break;
case 's':
fname_software = optarg;
@@ -784,6 +808,9 @@ static void handle_options(int argc, char **argv)
}
if (optind < argc)
command = argv[optind];
+ if (optind+1 < argc)
+ value = argv[optind+1];
+
}
static int num_sigint;
@@ -807,7 +834,12 @@ int main(int argc, char **argv)
struct gsm_network *gsmnet;
int rc;
+ debug_init();
+ stderr_target = debug_target_create_stderr();
+ debug_add_target(stderr_target);
+ debug_set_all_filter(stderr_target, 1);
handle_options(argc, argv);
+ bts_model_bs11_init();
gsmnet = gsm_network_init(1, 1, NULL);
if (!gsmnet) {
diff --git a/openbsc/src/bsc_hack.c b/openbsc/src/bsc_hack.c
index c256f864e..7755726c9 100644
--- a/openbsc/src/bsc_hack.c
+++ b/openbsc/src/bsc_hack.c
@@ -1,6 +1,6 @@
/* A hackish minimal BSC (+MSC +HLR) implementation */
-/* (C) 2008-2009 by Harald Welte <laforge@gnumonks.org>
+/* (C) 2008-2010 by Harald Welte <laforge@gnumonks.org>
* (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
* All Rights Reserved
*
@@ -35,12 +35,18 @@
#include <openbsc/debug.h>
#include <openbsc/e1_input.h>
#include <openbsc/talloc.h>
+#include <openbsc/signal.h>
/* MCC and MNC for the Location Area Identifier */
+static struct debug_target *stderr_target;
struct gsm_network *bsc_gsmnet = 0;
static const char *database_name = "hlr.sqlite3";
static const char *config_file = "openbsc.cfg";
-extern int ipacc_rtp_direct;
+
+
+/* timer to store statistics */
+#define DB_SYNC_INTERVAL 60, 0
+static struct timer_list db_sync_timer;
extern int bsc_bootstrap_network(int (*mmc_rev)(struct gsm_network *, int, void *),
const char *cfg_file);
@@ -72,9 +78,9 @@ static void print_help()
printf(" -s --disable-color\n");
printf(" -c --config-file filename The config file to use.\n");
printf(" -l --database db-name The database to use\n");
- printf(" -r --reject-cause number The reject cause for LOCATION UPDATING REJECT.\n");
printf(" -p --pcap file The filename of the pcap file\n");
printf(" -T --timestamp Prefix every log line with a timestamp\n");
+ printf(" -P --rtp-proxy Enable the RTP Proxy code inside OpenBSC\n");
}
static void handle_options(int argc, char** argv)
@@ -88,7 +94,6 @@ static void handle_options(int argc, char** argv)
{"disable-color", 0, 0, 's'},
{"database", 1, 0, 'l'},
{"authorize-everyone", 0, 0, 'a'},
- {"reject-cause", 1, 0, 'r'},
{"pcap", 1, 0, 'p'},
{"timestamp", 0, 0, 'T'},
{"rtp-proxy", 0, 0, 'P'},
@@ -106,10 +111,10 @@ static void handle_options(int argc, char** argv)
print_help();
exit(0);
case 's':
- debug_use_color(0);
+ debug_set_use_color(stderr_target, 0);
break;
case 'd':
- debug_parse_category_mask(optarg);
+ debug_parse_category_mask(stderr_target, optarg);
break;
case 'l':
database_name = strdup(optarg);
@@ -117,14 +122,11 @@ static void handle_options(int argc, char** argv)
case 'c':
config_file = strdup(optarg);
break;
- case 'r':
- gsm0408_set_reject_cause(atoi(optarg));
- break;
case 'p':
create_pcap_file(optarg);
break;
case 'T':
- debug_timestamp(1);
+ debug_set_print_timestamp(stderr_target, 1);
break;
case 'P':
ipacc_rtp_direct = 0;
@@ -136,6 +138,7 @@ static void handle_options(int argc, char** argv)
}
}
+extern void *tall_vty_ctx;
static void signal_handler(int signal)
{
fprintf(stdout, "signal %u received\n", signal);
@@ -143,6 +146,7 @@ static void signal_handler(int signal)
switch (signal) {
case SIGINT:
bsc_shutdown_net(bsc_gsmnet);
+ dispatch_signal(SS_GLOBAL, S_GLOBAL_SHUTDOWN, NULL);
sleep(3);
exit(0);
break;
@@ -150,21 +154,53 @@ static void signal_handler(int signal)
/* in case of abort, we want to obtain a talloc report
* and then return to the caller, who will abort the process */
case SIGUSR1:
+ talloc_report(tall_vty_ctx, stderr);
talloc_report_full(tall_bsc_ctx, stderr);
break;
+ case SIGUSR2:
+ talloc_report_full(tall_vty_ctx, stderr);
+ break;
default:
break;
}
}
+/* timer handling */
+static int _db_store_counter(struct counter *counter, void *data)
+{
+ return db_store_counter(counter);
+}
+
+static void db_sync_timer_cb(void *data)
+{
+ /* store counters to database and re-schedule */
+ counters_for_each(_db_store_counter, NULL);
+ bsc_schedule_timer(&db_sync_timer, DB_SYNC_INTERVAL);
+}
+
+extern int bts_model_unknown_init(void);
+extern int bts_model_bs11_init(void);
+extern int bts_model_nanobts_init(void);
+
int main(int argc, char **argv)
{
int rc;
+ debug_init();
tall_bsc_ctx = talloc_named_const(NULL, 1, "openbsc");
talloc_ctx_init();
on_dso_load_token();
on_dso_load_rrlp();
+ on_dso_load_ho_dec();
+ stderr_target = debug_target_create_stderr();
+ debug_add_target(stderr_target);
+
+ bts_model_unknown_init();
+ bts_model_bs11_init();
+ bts_model_nanobts_init();
+
+ /* enable filters */
+ debug_set_all_filter(stderr_target, 1);
/* parse options */
handle_options(argc, argv);
@@ -184,6 +220,11 @@ int main(int argc, char **argv)
}
printf("DB: Database prepared.\n");
+ /* setup the timer */
+ db_sync_timer.cb = db_sync_timer_cb;
+ db_sync_timer.data = NULL;
+ bsc_schedule_timer(&db_sync_timer, DB_SYNC_INTERVAL);
+
rc = bsc_bootstrap_network(mncc_recv, config_file);
if (rc < 0)
exit(1);
@@ -191,9 +232,12 @@ int main(int argc, char **argv)
signal(SIGINT, &signal_handler);
signal(SIGABRT, &signal_handler);
signal(SIGUSR1, &signal_handler);
+ signal(SIGUSR2, &signal_handler);
+ signal(SIGPIPE, SIG_IGN);
while (1) {
bsc_upqueue(bsc_gsmnet);
+ debug_reset_context();
bsc_select_main(0);
}
}
diff --git a/openbsc/src/bsc_init.c b/openbsc/src/bsc_init.c
index d11cde578..622fb98cd 100644
--- a/openbsc/src/bsc_init.c
+++ b/openbsc/src/bsc_init.c
@@ -28,16 +28,15 @@
#include <openbsc/debug.h>
#include <openbsc/misdn.h>
#include <openbsc/telnet_interface.h>
+#include <openbsc/system_information.h>
#include <openbsc/paging.h>
#include <openbsc/signal.h>
#include <openbsc/talloc.h>
/* global pointer to the gsm network data structure */
extern struct gsm_network *bsc_gsmnet;
-extern int ipacc_rtp_direct;
static void patch_nm_tables(struct gsm_bts *bts);
-static void patch_si_tables(struct gsm_bts *bts);
/* The following definitions are for OM and NM packets that we cannot yet
* generate by code but we just pass on */
@@ -356,8 +355,10 @@ int nm_state_event(enum nm_evt evt, u_int8_t obj_class, void *obj,
switch (obj_class) {
case NM_OC_SITE_MANAGER:
bts = container_of(obj, struct gsm_bts, site_mgr);
- if (new_state->operational == 2 &&
- new_state->availability == NM_AVSTATE_OK)
+ if ((new_state->operational == 2 &&
+ new_state->availability == NM_AVSTATE_OK) ||
+ (new_state->operational == 1 &&
+ new_state->availability == NM_AVSTATE_OFF_LINE))
abis_nm_opstart(bts, obj_class, 0xff, 0xff, 0xff);
break;
case NM_OC_BTS:
@@ -392,37 +393,10 @@ int nm_state_event(enum nm_evt evt, u_int8_t obj_class, void *obj,
case NM_OC_RADIO_CARRIER:
trx = obj;
if (new_state->operational == 1 &&
- new_state->availability == NM_AVSTATE_OFF_LINE) {
- /* Patch ARFCN into radio attribute */
- nanobts_attr_radio[5] &= 0xf0;
- nanobts_attr_radio[5] |= trx->arfcn >> 8;
- nanobts_attr_radio[6] = trx->arfcn & 0xff;
- abis_nm_set_radio_attr(trx, nanobts_attr_radio,
- sizeof(nanobts_attr_radio));
- abis_nm_chg_adm_state(trx->bts, obj_class,
- trx->bts->bts_nr, trx->nr, 0xff,
- NM_STATE_UNLOCKED);
- abis_nm_opstart(trx->bts, obj_class, trx->bts->bts_nr,
- trx->nr, 0xff);
- }
- if (new_state->operational == 1 &&
new_state->availability == NM_AVSTATE_OK)
abis_nm_opstart(trx->bts, obj_class, trx->bts->bts_nr,
trx->nr, 0xff);
break;
- case NM_OC_BASEB_TRANSC:
- trx = container_of(obj, struct gsm_bts_trx, bb_transc);
- if (new_state->operational == 1 &&
- new_state->availability == NM_AVSTATE_DEPENDENCY) {
- abis_nm_chg_adm_state(trx->bts, obj_class,
- trx->bts->bts_nr, trx->nr, 0xff,
- NM_STATE_UNLOCKED);
- abis_nm_opstart(trx->bts, obj_class,
- trx->bts->bts_nr, trx->nr, 0xff);
- /* TRX software is active, tell it to initiate RSL Link */
- abis_nm_ipaccess_rsl_connect(trx, 0, 3003, trx->rsl_tei);
- }
- break;
default:
break;
}
@@ -436,8 +410,43 @@ static int sw_activ_rep(struct msgb *mb)
struct gsm_bts *bts = mb->trx->bts;
struct gsm_bts_trx *trx = gsm_bts_trx_num(bts, foh->obj_inst.trx_nr);
+ if (!trx)
+ return -EINVAL;
switch (foh->obj_class) {
+ case NM_OC_BASEB_TRANSC:
+ abis_nm_chg_adm_state(trx->bts, foh->obj_class,
+ trx->bts->bts_nr, trx->nr, 0xff,
+ NM_STATE_UNLOCKED);
+ abis_nm_opstart(trx->bts, foh->obj_class,
+ trx->bts->bts_nr, trx->nr, 0xff);
+ /* TRX software is active, tell it to initiate RSL Link */
+ abis_nm_ipaccess_rsl_connect(trx, 0, 3003, trx->rsl_tei);
+ break;
+ case NM_OC_RADIO_CARRIER: {
+ /*
+ * Locking the radio carrier will make it go
+ * offline again and we would come here. The
+ * framework should determine that there was
+ * no change and avoid recursion.
+ *
+ * This code is here to make sure that on start
+ * a TRX remains locked.
+ */
+ int rc_state = trx->nm_state.administrative;
+ /* Patch ARFCN into radio attribute */
+ nanobts_attr_radio[5] &= 0xf0;
+ nanobts_attr_radio[5] |= trx->arfcn >> 8;
+ nanobts_attr_radio[6] = trx->arfcn & 0xff;
+ abis_nm_set_radio_attr(trx, nanobts_attr_radio,
+ sizeof(nanobts_attr_radio));
+ abis_nm_chg_adm_state(trx->bts, foh->obj_class,
+ trx->bts->bts_nr, trx->nr, 0xff,
+ rc_state);
+ abis_nm_opstart(trx->bts, foh->obj_class, trx->bts->bts_nr,
+ trx->nr, 0xff);
+ break;
+ }
}
return 0;
}
@@ -446,7 +455,7 @@ static int sw_activ_rep(struct msgb *mb)
static int oml_msg_nack(u_int8_t mt)
{
if (mt == NM_MT_SET_BTS_ATTR_NACK) {
- fprintf(stderr, "Failed to set BTS attributes. That is fatal. "
+ LOGP(DNM, LOGL_FATAL, "Failed to set BTS attributes. That is fatal. "
"Was the bts type and frequency properly specified?\n");
exit(-1);
}
@@ -549,7 +558,7 @@ static void nm_reconfig_trx(struct gsm_bts_trx *trx)
trx->nominal_power = 23;
break;
default:
- fprintf(stderr, "Unsupported nanoBTS GSM band %s\n",
+ LOGP(DNM, LOGL_ERROR, "Unsupported nanoBTS GSM band %s\n",
gsm_band_name(trx->bts->band));
break;
}
@@ -568,6 +577,7 @@ static void nm_reconfig_bts(struct gsm_bts *bts)
switch (bts->type) {
case GSM_BTS_TYPE_BS11:
+ patch_nm_tables(bts);
abis_nm_raw_msg(bts, sizeof(msg_1), msg_1); /* set BTS SiteMgr attr*/
abis_nm_set_bts_attr(bts, bs11_attr_bts, sizeof(bs11_attr_bts));
abis_nm_raw_msg(bts, sizeof(msg_3), msg_3); /* set BTS handover attr */
@@ -613,7 +623,7 @@ static void bootstrap_om_bs11(struct gsm_bts *bts)
static void bootstrap_om(struct gsm_bts *bts)
{
- fprintf(stdout, "bootstrapping OML for BTS %u\n", bts->nr);
+ LOGP(DNM, LOGL_NOTICE, "bootstrapping OML for BTS %u\n", bts->nr);
switch (bts->type) {
case GSM_BTS_TYPE_BS11:
@@ -623,13 +633,13 @@ static void bootstrap_om(struct gsm_bts *bts)
bootstrap_om_nanobts(bts);
break;
default:
- fprintf(stderr, "Unable to bootstrap OML: Unknown BTS type %d\n", bts->type);
+ LOGP(DNM, LOGL_ERROR, "Unable to bootstrap OML: Unknown BTS type %d\n", bts->type);
}
}
static int shutdown_om(struct gsm_bts *bts)
{
- fprintf(stdout, "shutting down OML for BTS %u\n", bts->nr);
+ LOGP(DNM, LOGL_NOTICE, "shutting down OML for BTS %u\n", bts->nr);
/* stop sending event reports */
abis_nm_event_reports(bts, 0);
@@ -660,219 +670,55 @@ int bsc_shutdown_net(struct gsm_network *net)
return 0;
}
-struct bcch_info {
- u_int8_t type;
- u_int8_t len;
- const u_int8_t *data;
-};
-
-/*
-SYSTEM INFORMATION TYPE 1
- Cell channel description
- Format-ID bit map 0
- CA-ARFCN Bit 124...001 (Hex): 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01
- RACH Control Parameters
- maximum 7 retransmissions
- 8 slots used to spread transmission
- cell not barred for access
- call reestablishment not allowed
- Access Control Class = 0000
-*/
-static u_int8_t si1[] = {
- /* header */0x55, 0x06, 0x19,
- /* ccdesc */0x04 /*0x00*/, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 /*0x01*/,
- /* rach */0xD5, 0x04, 0x00,
- /* s1 reset*/0x2B
-};
-
-/*
- SYSTEM INFORMATION TYPE 2
- Neighbour Cells Description
- EXT-IND: Carries the complete BA
- BA-IND = 0
- Format-ID bit map 0
- CA-ARFCN Bit 124...001 (Hex): 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
- NCC permitted (NCC) = FF
- RACH Control Parameters
- maximum 7 retransmissions
- 8 slots used to spread transmission
- cell not barred for access
- call reestablishment not allowed
- Access Control Class = 0000
-*/
-static u_int8_t si2[] = {
- /* header */0x59, 0x06, 0x1A,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- /* ncc */0xFF,
- /* rach*/0xD5, 0x04, 0x00
-};
-
-/*
-SYSTEM INFORMATION TYPE 3
- Cell identity = 00001 (1h)
- Location area identification
- Mobile Country Code (MCC): 001
- Mobile Network Code (MNC): 01
- Location Area Code (LAC): 00001 (1h)
- Control Channel Description
- Attach-detach: MSs in the cell are not allowed to apply IMSI attach /detach
- 0 blocks reserved for access grant
- 1 channel used for CCCH, with SDCCH
- 5 multiframes period for PAGING REQUEST
- Time-out T3212 = 0
- Cell Options BCCH
- Power control indicator: not set
- MSs shall not use uplink DTX
- Radio link timeout = 36
- Cell Selection Parameters
- Cell reselect hysteresis = 6 dB RXLEV hysteresis for LA re-selection
- max.TX power level MS may use for CCH = 2 <- according to GSM05.05 39dBm (max)
- Additional Reselect Parameter Indication (ACS) = only SYSTEM INFO 4: The SI rest octets, if present, shall be used to derive the value of PI and possibly C2 parameters
- Half rate support (NECI): New establishment causes are not supported
- min.RX signal level for MS = 0
- RACH Control Parameters
- maximum 7 retransmissions
- 8 slots used to spread transmission
- cell not barred for access
- call reestablishment not allowed
- Access Control Class = 0000
- SI 3 Rest Octets (not present)
-*/
-static u_int8_t si3[] = {
- /* header */0x49, 0x06, 0x1B,
- /* cell */0x00, 0x01,
- /* lai */0x00, 0xF1, 0x10, 0x00, 0x01,
- /* desc */0x01, 0x03, 0x00,
- /* option*/0x28,
- /* selection*/0x62, 0x00,
- /* rach */0xD5, 0x04, 0x00,
- /* rest */ 0x2B, 0x2B, 0x2B, 0x2B
-};
-
-/*
-SYSTEM INFORMATION TYPE 4
- Location area identification
- Mobile Country Code (MCC): 001
- Mobile Network Code (MNC): 01
- Location Area Code (LAC): 00001 (1h)
- Cell Selection Parameters
- Cell reselect hysteresis = 6 dB RXLEV hysteresis for LA re-selection
- max.TX power level MS may use for CCH = 2
- Additional Reselect Parameter Indication (ACS) = only SYSTEM INFO 4: The SI rest octets, if present, shall be used to derive the value of PI and possibly C2 parameters
- Half rate support (NECI): New establishment causes are not supported
- min.RX signal level for MS = 0
- RACH Control Parameters
- maximum 7 retransmissions
- 8 slots used to spread transmission
- cell not barred for access
- call reestablishment not allowed
- Access Control Class = 0000
- CBCH Channel Description
- Type = SDCCH/4[2]
- Timeslot Number: 0
- Training Sequence Code: 7h
- ARFCN: 1
- SI Rest Octets (not present)
-*/
-static u_int8_t si4[] = {
- /* header */0x41, 0x06, 0x1C,
- /* lai */0x00, 0xF1, 0x10, 0x00, 0x01,
- /* sel */0x62, 0x00,
- /* rach*/0xD5, 0x04, 0x00,
- /* cbch chan desc */ 0x64, 0x30, 0xE0, HARDCODED_ARFCN/*0x01*/,
- /* rest octets */ 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B
-};
-
-/*
- SYSTEM INFORMATION TYPE 5
- Neighbour Cells Description
- EXT-IND: Carries the complete BA
- BA-IND = 0
- Format-ID bit map 0
- CA-ARFCN Bit 124...001 (Hex): 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
-*/
-
-static u_int8_t si5[] = {
- /* header without l2 len*/0x06, 0x1D,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-};
-
-// SYSTEM INFORMATION TYPE 6
-
-/*
-SACCH FILLING
- System Info Type: SYSTEM INFORMATION 6
- L3 Information (Hex): 06 1E 00 01 xx xx 10 00 01 28 FF
-
-SYSTEM INFORMATION TYPE 6
- Cell identity = 00001 (1h)
- Location area identification
- Mobile Country Code (MCC): 001
- Mobile Network Code (MNC): 01
- Location Area Code (LAC): 00001 (1h)
- Cell Options SACCH
- Power control indicator: not set
- MSs shall not use uplink DTX on a TCH-F. MS shall not use uplink DTX on TCH-H.
- Radio link timeout = 36
- NCC permitted (NCC) = FF
-*/
-
-static u_int8_t si6[] = {
- /* header */0x06, 0x1E,
- /* cell id*/ 0x00, 0x01,
- /* lai */ 0x00, 0xF1, 0x10, 0x00, 0x01,
- /* options */ 0x28,
- /* ncc */ 0xFF,
-};
-
-
-
-static const struct bcch_info bcch_infos[] = {
- {
- .type = RSL_SYSTEM_INFO_1,
- .len = sizeof(si1),
- .data = si1,
- }, {
- .type = RSL_SYSTEM_INFO_2,
- .len = sizeof(si2),
- .data = si2,
- }, {
- .type = RSL_SYSTEM_INFO_3,
- .len = sizeof(si3),
- .data = si3,
- }, {
- .type = RSL_SYSTEM_INFO_4,
- .len = sizeof(si4),
- .data = si4,
- },
-};
-
-static_assert(sizeof(si1) == sizeof(struct gsm48_system_information_type_1), type1)
-static_assert(sizeof(si2) == sizeof(struct gsm48_system_information_type_2), type2)
-static_assert(sizeof(si3) == sizeof(struct gsm48_system_information_type_3), type3)
-static_assert(sizeof(si4) >= sizeof(struct gsm48_system_information_type_4), type4)
-static_assert(sizeof(si5) == sizeof(struct gsm48_system_information_type_5), type5)
-static_assert(sizeof(si6) >= sizeof(struct gsm48_system_information_type_6), type6)
-
/* set all system information types */
static int set_system_infos(struct gsm_bts_trx *trx)
{
- int i;
+ int i, rc;
+ u_int8_t si_tmp[23];
+ struct gsm_bts *bts = trx->bts;
+
+ bts->si_common.cell_sel_par.ms_txpwr_max_ccch =
+ ms_pwr_ctl_lvl(bts->band, bts->ms_max_power);
+ bts->si_common.cell_sel_par.neci = bts->network->neci;
if (trx == trx->bts->c0) {
- for (i = 0; i < ARRAY_SIZE(bcch_infos); i++) {
- rsl_bcch_info(trx, bcch_infos[i].type,
- bcch_infos[i].data,
- bcch_infos[i].len);
+ for (i = 1; i <= 4; i++) {
+ rc = gsm_generate_si(si_tmp, trx->bts, i);
+ if (rc < 0)
+ goto err_out;
+ DEBUGP(DRR, "SI%2u: %s\n", i, hexdump(si_tmp, rc));
+ rsl_bcch_info(trx, i, si_tmp, sizeof(si_tmp));
}
+#ifdef GPRS
+ i = 13;
+ rc = gsm_generate_si(si_tmp, trx->bts, RSL_SYSTEM_INFO_13);
+ if (rc < 0)
+ goto err_out;
+ DEBUGP(DRR, "SI%2u: %s\n", i, hexdump(si_tmp, rc));
+ rsl_bcch_info(trx, RSL_SYSTEM_INFO_13, si_tmp, rc);
+#endif
}
- rsl_sacch_filling(trx, RSL_SYSTEM_INFO_5, si5, sizeof(si5));
- rsl_sacch_filling(trx, RSL_SYSTEM_INFO_6, si6, sizeof(si6));
+
+ i = 5;
+ rc = gsm_generate_si(si_tmp, trx->bts, RSL_SYSTEM_INFO_5);
+ if (rc < 0)
+ goto err_out;
+ DEBUGP(DRR, "SI%2u: %s\n", i, hexdump(si_tmp, rc));
+ rsl_sacch_filling(trx, RSL_SYSTEM_INFO_5, si_tmp, rc);
+
+ i = 6;
+ rc = gsm_generate_si(si_tmp, trx->bts, RSL_SYSTEM_INFO_6);
+ if (rc < 0)
+ goto err_out;
+ DEBUGP(DRR, "SI%2u: %s\n", i, hexdump(si_tmp, rc));
+ rsl_sacch_filling(trx, RSL_SYSTEM_INFO_6, si_tmp, rc);
return 0;
+err_out:
+ LOGP(DRR, LOGL_ERROR, "Cannot generate SI %u for BTS %u, most likely "
+ "a problem with neighbor cell list generation\n",
+ i, trx->bts->nr);
+ return rc;
}
/*
@@ -906,81 +752,13 @@ static void patch_nm_tables(struct gsm_bts *bts)
nanobts_attr_radio[1] = bts->c0->max_power_red / 2;
}
-/*
- * Patch the various SYSTEM INFORMATION tables to update
- * the LAI
- */
-static void patch_si_tables(struct gsm_bts *bts)
-{
- u_int8_t arfcn_low = bts->c0->arfcn & 0xff;
- u_int8_t arfcn_high = (bts->c0->arfcn >> 8) & 0x0f;
-
- /* covert the raw packet to the struct */
- struct gsm48_system_information_type_1 *type_1 =
- (struct gsm48_system_information_type_1*)&si1;
- struct gsm48_system_information_type_2 *type_2 =
- (struct gsm48_system_information_type_2*)&si2;
- struct gsm48_system_information_type_3 *type_3 =
- (struct gsm48_system_information_type_3*)&si3;
- struct gsm48_system_information_type_4 *type_4 =
- (struct gsm48_system_information_type_4*)&si4;
- struct gsm48_system_information_type_6 *type_6 =
- (struct gsm48_system_information_type_6*)&si6;
- struct gsm48_loc_area_id lai;
-
- gsm0408_generate_lai(&lai, bts->network->country_code,
- bts->network->network_code,
- bts->location_area_code);
-
- /* assign the MCC and MNC */
- type_3->lai = lai;
- type_4->lai = lai;
- type_6->lai = lai;
-
- /* set the CI */
- type_3->cell_identity = htons(bts->cell_identity);
- type_6->cell_identity = htons(bts->cell_identity);
-
- type_4->data[2] &= 0xf0;
- type_4->data[2] |= arfcn_high;
- type_4->data[3] = arfcn_low;
-
- /* patch Control Channel Description 10.5.2.11 */
- type_3->control_channel_desc = bts->chan_desc;
-
- /* patch TSC */
- si4[15] &= ~0xe0;
- si4[15] |= (bts->tsc & 7) << 5;
-
- /* patch MS max power for CCH */
- type_4->cell_sel_par.ms_txpwr_max_ccch =
- ms_pwr_ctl_lvl(bts->band, bts->ms_max_power);
-
- /* Set NECI to influence channel request */
- type_3->cell_sel_par.neci = bts->network->neci;
- type_4->cell_sel_par.neci = bts->network->neci;
-
- if (bts->cell_barred) {
- type_1->rach_control.cell_bar = 1;
- type_2->rach_control.cell_bar = 1;
- type_3->rach_control.cell_bar = 1;
- type_4->rach_control.cell_bar = 1;
- } else {
- type_1->rach_control.cell_bar = 0;
- type_2->rach_control.cell_bar = 0;
- type_3->rach_control.cell_bar = 0;
- type_4->rach_control.cell_bar = 0;
- }
-}
-
-
static void bootstrap_rsl(struct gsm_bts_trx *trx)
{
- fprintf(stdout, "bootstrapping RSL for BTS/TRX (%u/%u) "
- "using MCC=%u MNC=%u BSIC=%u TSC=%u\n",
- trx->bts->nr, trx->nr, bsc_gsmnet->country_code,
- bsc_gsmnet->network_code, trx->bts->bsic, trx->bts->tsc);
- patch_si_tables(trx->bts);
+ LOGP(DRSL, LOGL_NOTICE, "bootstrapping RSL for BTS/TRX (%u/%u) "
+ "on ARFCN %u using MCC=%u MNC=%u LAC=%u CID=%u BSIC=%u TSC=%u\n",
+ trx->bts->nr, trx->nr, trx->arfcn, bsc_gsmnet->country_code,
+ bsc_gsmnet->network_code, trx->bts->location_area_code,
+ trx->bts->cell_identity, trx->bts->bsic, trx->bts->tsc);
set_system_infos(trx);
}
@@ -1000,7 +778,7 @@ void input_event(int event, enum e1inp_sign_type type, struct gsm_bts_trx *trx)
}
break;
case EVT_E1_TEI_DN:
- fprintf(stderr, "Lost some E1 TEI link\n");
+ LOGP(DMI, LOGL_NOTICE, "Lost some E1 TEI link\n");
/* FIXME: deal with TEI or L1 link loss */
break;
default:
@@ -1013,33 +791,50 @@ static int bootstrap_bts(struct gsm_bts *bts)
switch (bts->band) {
case GSM_BAND_1800:
if (bts->c0->arfcn < 512 || bts->c0->arfcn > 885) {
- fprintf(stderr, "GSM1800 channel must be between 512-885.\n");
+ LOGP(DNM, LOGL_ERROR, "GSM1800 channel must be between 512-885.\n");
return -EINVAL;
}
break;
case GSM_BAND_1900:
if (bts->c0->arfcn < 512 || bts->c0->arfcn > 810) {
- fprintf(stderr, "GSM1900 channel must be between 512-810.\n");
+ LOGP(DNM, LOGL_ERROR, "GSM1900 channel must be between 512-810.\n");
return -EINVAL;
}
break;
case GSM_BAND_900:
if (bts->c0->arfcn < 1 || bts->c0->arfcn > 124) {
- fprintf(stderr, "GSM900 channel must be between 1-124.\n");
+ LOGP(DNM, LOGL_ERROR, "GSM900 channel must be between 1-124.\n");
return -EINVAL;
}
break;
default:
- fprintf(stderr, "Unsupported frequency band.\n");
+ LOGP(DNM, LOGL_ERROR, "Unsupported frequency band.\n");
return -EINVAL;
}
+ if (bts->network->auth_policy == GSM_AUTH_POLICY_ACCEPT_ALL &&
+ !bts->si_common.rach_control.cell_bar)
+ LOGP(DNM, LOGL_ERROR, "\nWARNING: You are running an 'accept-all' "
+ "network on a BTS that is not barred. This "
+ "configuration is likely to interfere with production "
+ "GSM networks and should only be used in a RF "
+ "shielded environment such as a faraday cage!\n\n");
+
/* Control Channel Description */
- bts->chan_desc.att = 1;
- bts->chan_desc.ccch_conf = RSL_BCCH_CCCH_CONF_1_C;
- bts->chan_desc.bs_pa_mfrms = RSL_BS_PA_MFRMS_5;
+ bts->si_common.chan_desc.att = 1;
+ bts->si_common.chan_desc.ccch_conf = RSL_BCCH_CCCH_CONF_1_C;
+ bts->si_common.chan_desc.bs_pa_mfrms = RSL_BS_PA_MFRMS_5;
/* T3212 is set from vty/config */
+ /* some defaults for our system information */
+ bts->si_common.cell_options.radio_link_timeout = 2; /* 12 */
+ bts->si_common.cell_options.dtx = 2; /* MS shall not use upplink DTX */
+ bts->si_common.cell_options.pwrc = 0; /* PWRC not set */
+
+ bts->si_common.cell_sel_par.acs = 0;
+
+ bts->si_common.ncc_permitted = 0xff;
+
paging_init(bts);
return 0;
@@ -1062,7 +857,7 @@ int bsc_bootstrap_network(int (*mncc_recv)(struct gsm_network *, int, void *),
telnet_init(bsc_gsmnet, 4242);
rc = vty_read_config_file(config_file);
if (rc < 0) {
- fprintf(stderr, "Failed to parse the config file: '%s'\n", config_file);
+ LOGP(DNM, LOGL_FATAL, "Failed to parse the config file: '%s'\n", config_file);
return rc;
}
diff --git a/openbsc/src/bts_ipaccess_nanobts.c b/openbsc/src/bts_ipaccess_nanobts.c
new file mode 100644
index 000000000..6765517b3
--- /dev/null
+++ b/openbsc/src/bts_ipaccess_nanobts.c
@@ -0,0 +1,84 @@
+/* ip.access nanoBTS specific code */
+
+/* (C) 2009-2010 by Harald Welte <laforge@gnumonks.org>
+ *
+ * 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 <sys/types.h>
+
+#include <openbsc/gsm_data.h>
+#include <openbsc/tlv.h>
+#include <openbsc/abis_nm.h>
+
+static struct gsm_bts_model model_nanobts = {
+ .type = GSM_BTS_TYPE_NANOBTS,
+ .nm_att_tlvdef = {
+ .def = {
+ /* ip.access specifics */
+ [NM_ATT_IPACC_DST_IP] = { TLV_TYPE_FIXED, 4 },
+ [NM_ATT_IPACC_DST_IP_PORT] = { TLV_TYPE_FIXED, 2 },
+ [NM_ATT_IPACC_STREAM_ID] = { TLV_TYPE_TV, },
+ [NM_ATT_IPACC_FREQ_CTRL] = { TLV_TYPE_TV, },
+ [NM_ATT_IPACC_SEC_OML_CFG] = { TLV_TYPE_FIXED, 6 },
+ [NM_ATT_IPACC_IP_IF_CFG] = { TLV_TYPE_FIXED, 8 },
+ [NM_ATT_IPACC_IP_GW_CFG] = { TLV_TYPE_FIXED, 12 },
+ [NM_ATT_IPACC_IN_SERV_TIME] = { TLV_TYPE_FIXED, 4 },
+ [NM_ATT_IPACC_LOCATION] = { TLV_TYPE_TL16V },
+ [NM_ATT_IPACC_PAGING_CFG] = { TLV_TYPE_FIXED, 2 },
+ [NM_ATT_IPACC_UNIT_ID] = { TLV_TYPE_TL16V },
+ [NM_ATT_IPACC_UNIT_NAME] = { TLV_TYPE_TL16V },
+ [NM_ATT_IPACC_SNMP_CFG] = { TLV_TYPE_TL16V },
+ [NM_ATT_IPACC_PRIM_OML_CFG_LIST] = { TLV_TYPE_TL16V },
+ [NM_ATT_IPACC_NV_FLAGS] = { TLV_TYPE_TL16V },
+ [NM_ATT_IPACC_FREQ_CTRL] = { TLV_TYPE_FIXED, 2 },
+ [NM_ATT_IPACC_PRIM_OML_FB_TOUT] = { TLV_TYPE_TL16V },
+ [NM_ATT_IPACC_CUR_SW_CFG] = { TLV_TYPE_TL16V },
+ [NM_ATT_IPACC_TIMING_BUS] = { TLV_TYPE_TL16V },
+ [NM_ATT_IPACC_CGI] = { TLV_TYPE_TL16V },
+ [NM_ATT_IPACC_RAC] = { TLV_TYPE_TL16V },
+ [NM_ATT_IPACC_OBJ_VERSION] = { TLV_TYPE_TL16V },
+ [NM_ATT_IPACC_GPRS_PAGING_CFG]= { TLV_TYPE_TL16V },
+ [NM_ATT_IPACC_NSEI] = { TLV_TYPE_TL16V },
+ [NM_ATT_IPACC_BVCI] = { TLV_TYPE_TL16V },
+ [NM_ATT_IPACC_NSVCI] = { TLV_TYPE_TL16V },
+ [NM_ATT_IPACC_NS_CFG] = { TLV_TYPE_TL16V },
+ [NM_ATT_IPACC_BSSGP_CFG] = { TLV_TYPE_TL16V },
+ [NM_ATT_IPACC_NS_LINK_CFG] = { TLV_TYPE_TL16V },
+ [NM_ATT_IPACC_RLC_CFG] = { TLV_TYPE_TL16V },
+ [NM_ATT_IPACC_ALM_THRESH_LIST]= { TLV_TYPE_TL16V },
+ [NM_ATT_IPACC_MONIT_VAL_LIST] = { TLV_TYPE_TL16V },
+ [NM_ATT_IPACC_TIB_CONTROL] = { TLV_TYPE_TL16V },
+ [NM_ATT_IPACC_SUPP_FEATURES] = { TLV_TYPE_TL16V },
+ [NM_ATT_IPACC_CODING_SCHEMES] = { TLV_TYPE_TL16V },
+ [NM_ATT_IPACC_RLC_CFG_2] = { TLV_TYPE_TL16V },
+ [NM_ATT_IPACC_HEARTB_TOUT] = { TLV_TYPE_TL16V },
+ [NM_ATT_IPACC_UPTIME] = { TLV_TYPE_TL16V },
+ [NM_ATT_IPACC_RLC_CFG_3] = { TLV_TYPE_TL16V },
+ [NM_ATT_IPACC_SSL_CFG] = { TLV_TYPE_TL16V },
+ [NM_ATT_IPACC_SEC_POSSIBLE] = { TLV_TYPE_TL16V },
+ [NM_ATT_IPACC_IML_SSL_STATE] = { TLV_TYPE_TL16V },
+ [NM_ATT_IPACC_REVOC_DATE] = { TLV_TYPE_TL16V },
+ },
+ },
+};
+
+int bts_model_nanobts_init(void)
+{
+ return gsm_bts_model_register(&model_nanobts);
+}
diff --git a/openbsc/src/bts_siemens_bs11.c b/openbsc/src/bts_siemens_bs11.c
new file mode 100644
index 000000000..1c8f889e1
--- /dev/null
+++ b/openbsc/src/bts_siemens_bs11.c
@@ -0,0 +1,66 @@
+/* Siemens BS-11 specific code */
+
+/* (C) 2009-2010 by Harald Welte <laforge@gnumonks.org>
+ *
+ * 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 <sys/types.h>
+
+#include <openbsc/gsm_data.h>
+#include <openbsc/tlv.h>
+#include <openbsc/abis_nm.h>
+
+static struct gsm_bts_model model_bs11 = {
+ .type = GSM_BTS_TYPE_BS11,
+ .nm_att_tlvdef = {
+ .def = {
+ [NM_ATT_AVAIL_STATUS] = { TLV_TYPE_TLV },
+ /* BS11 specifics */
+ [NM_ATT_BS11_ESN_FW_CODE_NO] = { TLV_TYPE_TLV },
+ [NM_ATT_BS11_ESN_HW_CODE_NO] = { TLV_TYPE_TLV },
+ [NM_ATT_BS11_ESN_PCB_SERIAL] = { TLV_TYPE_TLV },
+ [NM_ATT_BS11_BOOT_SW_VERS] = { TLV_TYPE_TLV },
+ [0xd5] = { TLV_TYPE_TLV },
+ [0xa8] = { TLV_TYPE_TLV },
+ [NM_ATT_BS11_PASSWORD] = { TLV_TYPE_TLV },
+ [NM_ATT_BS11_TXPWR] = { TLV_TYPE_TLV },
+ [NM_ATT_BS11_RSSI_OFFS] = { TLV_TYPE_TLV },
+ [NM_ATT_BS11_LINE_CFG] = { TLV_TYPE_TV },
+ [NM_ATT_BS11_L1_PROT_TYPE] = { TLV_TYPE_TV },
+ [NM_ATT_BS11_BIT_ERR_THESH] = { TLV_TYPE_FIXED, 2 },
+ [NM_ATT_BS11_DIVERSITY] = { TLV_TYPE_TLV },
+ [NM_ATT_BS11_LMT_LOGON_SESSION]={ TLV_TYPE_TLV },
+ [NM_ATT_BS11_LMT_LOGIN_TIME] = { TLV_TYPE_TLV },
+ [NM_ATT_BS11_LMT_USER_ACC_LEV] ={ TLV_TYPE_TLV },
+ [NM_ATT_BS11_LMT_USER_NAME] = { TLV_TYPE_TLV },
+ [NM_ATT_BS11_BTS_STATE] = { TLV_TYPE_TLV },
+ [NM_ATT_BS11_E1_STATE] = { TLV_TYPE_TLV },
+ [NM_ATT_BS11_PLL_MODE] = { TLV_TYPE_TLV },
+ [NM_ATT_BS11_PLL] = { TLV_TYPE_TLV },
+ [NM_ATT_BS11_CCLK_ACCURACY] = { TLV_TYPE_TV },
+ [NM_ATT_BS11_CCLK_TYPE] = { TLV_TYPE_TV },
+ [0x95] = { TLV_TYPE_FIXED, 2 },
+ },
+ },
+};
+
+int bts_model_bs11_init(void)
+{
+ return gsm_bts_model_register(&model_bs11);
+}
diff --git a/openbsc/src/bts_unknown.c b/openbsc/src/bts_unknown.c
new file mode 100644
index 000000000..1e604a9e4
--- /dev/null
+++ b/openbsc/src/bts_unknown.c
@@ -0,0 +1,40 @@
+/* Generic BTS - VTY code tries to allocate this BTS before type is known */
+
+/* (C) 2010 by Daniel Willmann <daniel@totalueberwachung.de>
+ *
+ * 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 <sys/types.h>
+
+#include <openbsc/gsm_data.h>
+#include <openbsc/tlv.h>
+#include <openbsc/abis_nm.h>
+
+static struct gsm_bts_model model_unknown = {
+ .type = GSM_BTS_TYPE_UNKNOWN,
+ .nm_att_tlvdef = {
+ .def = {
+ },
+ },
+};
+
+int bts_model_unknown_init(void)
+{
+ return gsm_bts_model_register(&model_unknown);
+}
diff --git a/openbsc/src/chan_alloc.c b/openbsc/src/chan_alloc.c
index 7ba679c87..2e885241c 100644
--- a/openbsc/src/chan_alloc.c
+++ b/openbsc/src/chan_alloc.c
@@ -35,6 +35,29 @@
static void auto_release_channel(void *_lchan);
+static int ts_is_usable(struct gsm_bts_trx_ts *ts)
+{
+ /* FIXME: How does this behave for BS-11 ? */
+ if (is_ipaccess_bts(ts->trx->bts)) {
+ if (!nm_is_running(&ts->nm_state))
+ return 0;
+ }
+
+ return 1;
+}
+
+int trx_is_usable(struct gsm_bts_trx *trx)
+{
+ /* FIXME: How does this behave for BS-11 ? */
+ if (is_ipaccess_bts(trx->bts)) {
+ if (!nm_is_running(&trx->nm_state) ||
+ !nm_is_running(&trx->bb_transc.nm_state))
+ return 0;
+ }
+
+ return 1;
+}
+
struct gsm_bts_trx_ts *ts_c0_alloc(struct gsm_bts *bts,
enum gsm_phys_chan_config pchan)
{
@@ -63,6 +86,9 @@ struct gsm_bts_trx_ts *ts_alloc(struct gsm_bts *bts,
llist_for_each_entry(trx, &bts->trx_list, list) {
int from, to;
+ if (!trx_is_usable(trx))
+ continue;
+
/* the following constraints are pure policy,
* no requirement to put this restriction in place */
if (trx == bts->c0) {
@@ -97,6 +123,10 @@ struct gsm_bts_trx_ts *ts_alloc(struct gsm_bts *bts,
for (j = from; j <= to; j++) {
struct gsm_bts_trx_ts *ts = &trx->ts[j];
+
+ if (!ts_is_usable(ts))
+ continue;
+
if (ts->pchan == GSM_PCHAN_NONE) {
ts->pchan = pchan;
/* set channel attribute on OML */
@@ -121,6 +151,7 @@ static const u_int8_t subslots_per_pchan[] = {
[GSM_PCHAN_TCH_F] = 1,
[GSM_PCHAN_TCH_H] = 2,
[GSM_PCHAN_SDCCH8_SACCH8C] = 8,
+ /* FIXME: what about dynamic TCH_F_TCH_H ? */
};
static struct gsm_lchan *
@@ -129,14 +160,20 @@ _lc_find_trx(struct gsm_bts_trx *trx, enum gsm_phys_chan_config pchan)
struct gsm_bts_trx_ts *ts;
int j, ss;
+ if (!trx_is_usable(trx))
+ return NULL;
+
for (j = 0; j < 8; j++) {
ts = &trx->ts[j];
+ if (!ts_is_usable(ts))
+ continue;
if (ts->pchan != pchan)
continue;
/* check if all sub-slots are allocated yet */
for (ss = 0; ss < subslots_per_pchan[pchan]; ss++) {
struct gsm_lchan *lc = &ts->lchan[ss];
- if (lc->type == GSM_LCHAN_NONE)
+ if (lc->type == GSM_LCHAN_NONE &&
+ lc->state == LCHAN_S_NONE)
return lc;
}
}
@@ -203,9 +240,14 @@ struct gsm_lchan *lchan_alloc(struct gsm_bts *bts, enum gsm_chan_t type)
break;
case GSM_LCHAN_TCH_H:
lchan =_lc_find_bts(bts, GSM_PCHAN_TCH_H);
+ /* If we don't have TCH/H available, fall-back to TCH/F */
+ if (!lchan) {
+ lchan = _lc_find_bts(bts, GSM_PCHAN_TCH_F);
+ type = GSM_LCHAN_TCH_F;
+ }
break;
default:
- fprintf(stderr, "Unknown gsm_chan_t %u\n", type);
+ LOGP(DRLL, LOGL_ERROR, "Unknown gsm_chan_t %u\n", type);
}
if (lchan) {
@@ -230,6 +272,8 @@ struct gsm_lchan *lchan_alloc(struct gsm_bts *bts, enum gsm_chan_t type)
/* Free a logical channel */
void lchan_free(struct gsm_lchan *lchan)
{
+ int i;
+
lchan->type = GSM_LCHAN_NONE;
if (lchan->subscr) {
subscr_put(lchan->subscr);
@@ -244,6 +288,18 @@ void lchan_free(struct gsm_lchan *lchan)
/* stop the timer */
bsc_del_timer(&lchan->release_timer);
+ bsc_del_timer(&lchan->T3101);
+
+ /* clear cached measuement reports */
+ lchan->meas_rep_idx = 0;
+ for (i = 0; i < ARRAY_SIZE(lchan->meas_rep); i++) {
+ lchan->meas_rep[i].flags = 0;
+ lchan->meas_rep[i].nr = 0;
+ }
+ for (i = 0; i < ARRAY_SIZE(lchan->neigh_meas); i++)
+ lchan->neigh_meas[i].arfcn = 0;
+
+ lchan->silent_call = 0;
/* FIXME: ts_free() the timeslot, if we're the last logical
* channel using it */
@@ -262,11 +318,11 @@ int lchan_auto_release(struct gsm_lchan *lchan)
}
/* spoofed? message */
- if (lchan->use_count < 0) {
- DEBUGP(DRLL, "Channel count is negative: %d\n", lchan->use_count);
- }
+ if (lchan->use_count < 0)
+ LOGP(DRLL, LOGL_ERROR, "Channel count is negative: %d\n",
+ lchan->use_count);
- DEBUGP(DRLL, "Recycling the channel with: %d (%x)\n", lchan->nr, lchan->nr);
+ DEBUGP(DRLL, "%s Recycling Channel\n", gsm_lchan_name(lchan));
rsl_release_request(lchan, 0);
return 1;
}
@@ -312,3 +368,51 @@ struct gsm_lchan *lchan_for_subscr(struct gsm_subscriber *subscr)
return NULL;
}
+
+void bts_chan_load(struct pchan_load *cl, const struct gsm_bts *bts)
+{
+ struct gsm_bts_trx *trx;
+
+ llist_for_each_entry(trx, &bts->trx_list, list) {
+ int i;
+
+ /* skip administratively deactivated tranxsceivers */
+ if (!nm_is_running(&trx->nm_state) ||
+ !nm_is_running(&trx->bb_transc.nm_state))
+ continue;
+
+ for (i = 0; i < ARRAY_SIZE(trx->ts); i++) {
+ struct gsm_bts_trx_ts *ts = &trx->ts[i];
+ struct load_counter *pl = &cl->pchan[ts->pchan];
+ int j;
+
+ /* skip administratively deactivated timeslots */
+ if (!nm_is_running(&ts->nm_state))
+ continue;
+
+ for (j = 0; j < subslots_per_pchan[ts->pchan]; j++) {
+ struct gsm_lchan *lchan = &ts->lchan[j];
+
+ pl->total++;
+
+ switch (lchan->state) {
+ case LCHAN_S_NONE:
+ break;
+ default:
+ pl->used++;
+ break;
+ }
+ }
+ }
+ }
+}
+
+void network_chan_load(struct pchan_load *pl, struct gsm_network *net)
+{
+ struct gsm_bts *bts;
+
+ memset(pl, 0, sizeof(*pl));
+
+ llist_for_each_entry(bts, &net->bts_list, list)
+ bts_chan_load(pl, bts);
+}
diff --git a/openbsc/src/comp128.c b/openbsc/src/comp128.c
new file mode 100644
index 000000000..9df545256
--- /dev/null
+++ b/openbsc/src/comp128.c
@@ -0,0 +1,230 @@
+/*
+ * COMP128 implementation
+ *
+ *
+ * This code is inspired by original code from :
+ * Marc Briceno <marc@scard.org>, Ian Goldberg <iang@cs.berkeley.edu>,
+ * and David Wagner <daw@cs.berkeley.edu>
+ *
+ * But it has been fully rewritten from various PDFs found online describing
+ * the algorithm because the licence of the code referenced above was unclear.
+ * A comment snippet from the original code is included below, it describes
+ * where the doc came from and how the algorithm was reverse engineered.
+ *
+ *
+ * (C) 2009 by Sylvain Munaut <tnt@246tNt.com>
+ *
+ * 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.
+ *
+ */
+
+/*
+ * --- SNIP ---
+ *
+ * This code derived from a leaked document from the GSM standards.
+ * Some missing pieces were filled in by reverse-engineering a working SIM.
+ * We have verified that this is the correct COMP128 algorithm.
+ *
+ * The first page of the document identifies it as
+ * _Technical Information: GSM System Security Study_.
+ * 10-1617-01, 10th June 1988.
+ * The bottom of the title page is marked
+ * Racal Research Ltd.
+ * Worton Drive, Worton Grange Industrial Estate,
+ * Reading, Berks. RG2 0SB, England.
+ * Telephone: Reading (0734) 868601 Telex: 847152
+ * The relevant bits are in Part I, Section 20 (pages 66--67). Enjoy!
+ *
+ * Note: There are three typos in the spec (discovered by
+ * reverse-engineering).
+ * First, "z = (2 * x[n] + x[n]) mod 2^(9-j)" should clearly read
+ * "z = (2 * x[m] + x[n]) mod 2^(9-j)".
+ * Second, the "k" loop in the "Form bits from bytes" section is severely
+ * botched: the k index should run only from 0 to 3, and clearly the range
+ * on "the (8-k)th bit of byte j" is also off (should be 0..7, not 1..8,
+ * to be consistent with the subsequent section).
+ * Third, SRES is taken from the first 8 nibbles of x[], not the last 8 as
+ * claimed in the document. (And the document doesn't specify how Kc is
+ * derived, but that was also easily discovered with reverse engineering.)
+ * All of these typos have been corrected in the following code.
+ *
+ * --- /SNIP ---
+ */
+
+#include <string.h>
+#include <sys/types.h>
+
+/* The compression tables (just copied ...) */
+static const u_int8_t table_0[512] = {
+ 102, 177, 186, 162, 2, 156, 112, 75, 55, 25, 8, 12, 251, 193, 246, 188,
+ 109, 213, 151, 53, 42, 79, 191, 115, 233, 242, 164, 223, 209, 148, 108, 161,
+ 252, 37, 244, 47, 64, 211, 6, 237, 185, 160, 139, 113, 76, 138, 59, 70,
+ 67, 26, 13, 157, 63, 179, 221, 30, 214, 36, 166, 69, 152, 124, 207, 116,
+ 247, 194, 41, 84, 71, 1, 49, 14, 95, 35, 169, 21, 96, 78, 215, 225,
+ 182, 243, 28, 92, 201, 118, 4, 74, 248, 128, 17, 11, 146, 132, 245, 48,
+ 149, 90, 120, 39, 87, 230, 106, 232, 175, 19, 126, 190, 202, 141, 137, 176,
+ 250, 27, 101, 40, 219, 227, 58, 20, 51, 178, 98, 216, 140, 22, 32, 121,
+ 61, 103, 203, 72, 29, 110, 85, 212, 180, 204, 150, 183, 15, 66, 172, 196,
+ 56, 197, 158, 0, 100, 45, 153, 7, 144, 222, 163, 167, 60, 135, 210, 231,
+ 174, 165, 38, 249, 224, 34, 220, 229, 217, 208, 241, 68, 206, 189, 125, 255,
+ 239, 54, 168, 89, 123, 122, 73, 145, 117, 234, 143, 99, 129, 200, 192, 82,
+ 104, 170, 136, 235, 93, 81, 205, 173, 236, 94, 105, 52, 46, 228, 198, 5,
+ 57, 254, 97, 155, 142, 133, 199, 171, 187, 50, 65, 181, 127, 107, 147, 226,
+ 184, 218, 131, 33, 77, 86, 31, 44, 88, 62, 238, 18, 24, 43, 154, 23,
+ 80, 159, 134, 111, 9, 114, 3, 91, 16, 130, 83, 10, 195, 240, 253, 119,
+ 177, 102, 162, 186, 156, 2, 75, 112, 25, 55, 12, 8, 193, 251, 188, 246,
+ 213, 109, 53, 151, 79, 42, 115, 191, 242, 233, 223, 164, 148, 209, 161, 108,
+ 37, 252, 47, 244, 211, 64, 237, 6, 160, 185, 113, 139, 138, 76, 70, 59,
+ 26, 67, 157, 13, 179, 63, 30, 221, 36, 214, 69, 166, 124, 152, 116, 207,
+ 194, 247, 84, 41, 1, 71, 14, 49, 35, 95, 21, 169, 78, 96, 225, 215,
+ 243, 182, 92, 28, 118, 201, 74, 4, 128, 248, 11, 17, 132, 146, 48, 245,
+ 90, 149, 39, 120, 230, 87, 232, 106, 19, 175, 190, 126, 141, 202, 176, 137,
+ 27, 250, 40, 101, 227, 219, 20, 58, 178, 51, 216, 98, 22, 140, 121, 32,
+ 103, 61, 72, 203, 110, 29, 212, 85, 204, 180, 183, 150, 66, 15, 196, 172,
+ 197, 56, 0, 158, 45, 100, 7, 153, 222, 144, 167, 163, 135, 60, 231, 210,
+ 165, 174, 249, 38, 34, 224, 229, 220, 208, 217, 68, 241, 189, 206, 255, 125,
+ 54, 239, 89, 168, 122, 123, 145, 73, 234, 117, 99, 143, 200, 129, 82, 192,
+ 170, 104, 235, 136, 81, 93, 173, 205, 94, 236, 52, 105, 228, 46, 5, 198,
+ 254, 57, 155, 97, 133, 142, 171, 199, 50, 187, 181, 65, 107, 127, 226, 147,
+ 218, 184, 33, 131, 86, 77, 44, 31, 62, 88, 18, 238, 43, 24, 23, 154,
+ 159, 80, 111, 134, 114, 9, 91, 3, 130, 16, 10, 83, 240, 195, 119, 253,
+}, table_1[256] = {
+ 19, 11, 80, 114, 43, 1, 69, 94, 39, 18, 127, 117, 97, 3, 85, 43,
+ 27, 124, 70, 83, 47, 71, 63, 10, 47, 89, 79, 4, 14, 59, 11, 5,
+ 35, 107, 103, 68, 21, 86, 36, 91, 85, 126, 32, 50, 109, 94, 120, 6,
+ 53, 79, 28, 45, 99, 95, 41, 34, 88, 68, 93, 55, 110, 125, 105, 20,
+ 90, 80, 76, 96, 23, 60, 89, 64, 121, 56, 14, 74, 101, 8, 19, 78,
+ 76, 66, 104, 46, 111, 50, 32, 3, 39, 0, 58, 25, 92, 22, 18, 51,
+ 57, 65, 119, 116, 22, 109, 7, 86, 59, 93, 62, 110, 78, 99, 77, 67,
+ 12, 113, 87, 98, 102, 5, 88, 33, 38, 56, 23, 8, 75, 45, 13, 75,
+ 95, 63, 28, 49, 123, 120, 20, 112, 44, 30, 15, 98, 106, 2, 103, 29,
+ 82, 107, 42, 124, 24, 30, 41, 16, 108, 100, 117, 40, 73, 40, 7, 114,
+ 82, 115, 36, 112, 12, 102, 100, 84, 92, 48, 72, 97, 9, 54, 55, 74,
+ 113, 123, 17, 26, 53, 58, 4, 9, 69, 122, 21, 118, 42, 60, 27, 73,
+ 118, 125, 34, 15, 65, 115, 84, 64, 62, 81, 70, 1, 24, 111, 121, 83,
+ 104, 81, 49, 127, 48, 105, 31, 10, 6, 91, 87, 37, 16, 54, 116, 126,
+ 31, 38, 13, 0, 72, 106, 77, 61, 26, 67, 46, 29, 96, 37, 61, 52,
+ 101, 17, 44, 108, 71, 52, 66, 57, 33, 51, 25, 90, 2, 119, 122, 35,
+}, table_2[128] = {
+ 52, 50, 44, 6, 21, 49, 41, 59, 39, 51, 25, 32, 51, 47, 52, 43,
+ 37, 4, 40, 34, 61, 12, 28, 4, 58, 23, 8, 15, 12, 22, 9, 18,
+ 55, 10, 33, 35, 50, 1, 43, 3, 57, 13, 62, 14, 7, 42, 44, 59,
+ 62, 57, 27, 6, 8, 31, 26, 54, 41, 22, 45, 20, 39, 3, 16, 56,
+ 48, 2, 21, 28, 36, 42, 60, 33, 34, 18, 0, 11, 24, 10, 17, 61,
+ 29, 14, 45, 26, 55, 46, 11, 17, 54, 46, 9, 24, 30, 60, 32, 0,
+ 20, 38, 2, 30, 58, 35, 1, 16, 56, 40, 23, 48, 13, 19, 19, 27,
+ 31, 53, 47, 38, 63, 15, 49, 5, 37, 53, 25, 36, 63, 29, 5, 7,
+}, table_3[64] = {
+ 1, 5, 29, 6, 25, 1, 18, 23, 17, 19, 0, 9, 24, 25, 6, 31,
+ 28, 20, 24, 30, 4, 27, 3, 13, 15, 16, 14, 18, 4, 3, 8, 9,
+ 20, 0, 12, 26, 21, 8, 28, 2, 29, 2, 15, 7, 11, 22, 14, 10,
+ 17, 21, 12, 30, 26, 27, 16, 31, 11, 7, 13, 23, 10, 5, 22, 19,
+}, table_4[32] = {
+ 15, 12, 10, 4, 1, 14, 11, 7, 5, 0, 14, 7, 1, 2, 13, 8,
+ 10, 3, 4, 9, 6, 0, 3, 2, 5, 6, 8, 9, 11, 13, 15, 12,
+};
+
+static const u_int8_t *_comp128_table[5] = { table_0, table_1, table_2, table_3, table_4 };
+
+
+static inline void
+_comp128_compression_round(u_int8_t *x, int n, const u_int8_t *tbl)
+{
+ int i, j, m, a, b, y, z;
+ m = 4 - n;
+ for (i=0; i<(1<<n); i++)
+ for (j=0; j<(1<<m); j++) {
+ a = j + i * (2<<m);
+ b = a + (1<<m);
+ y = (x[a] + (x[b]<<1)) & ((32<<m)-1);
+ z = ((x[a]<<1) + x[b]) & ((32<<m)-1);
+ x[a] = tbl[y];
+ x[b] = tbl[z];
+ }
+}
+
+static inline void
+_comp128_compression(u_int8_t *x)
+{
+ int n;
+ for (n=0; n<5; n++)
+ _comp128_compression_round(x, n, _comp128_table[n]);
+}
+
+static inline void
+_comp128_bitsfrombytes(u_int8_t *x, u_int8_t *bits)
+{
+ int i;
+ memset(bits, 0x00, 128);
+ for (i=0; i<128; i++)
+ if (x[i>>2] & (1<<(3-(i&3))))
+ bits[i] = 1;
+}
+
+static inline void
+_comp128_permutation(u_int8_t *x, u_int8_t *bits)
+{
+ int i;
+ memset(&x[16], 0x00, 16);
+ for (i=0; i<128; i++)
+ x[(i>>3)+16] |= bits[(i*17) & 127] << (7-(i&7));
+}
+
+void
+comp128(u_int8_t *ki, u_int8_t *rand, u_int8_t *sres, u_int8_t *kc)
+{
+ int i;
+ u_int8_t x[32], bits[128];
+
+ /* x[16-31] = RAND */
+ memcpy(&x[16], rand, 16);
+
+ /* Round 1-7 */
+ for (i=0; i<7; i++) {
+ /* x[0-15] = Ki */
+ memcpy(x, ki, 16);
+
+ /* Compression */
+ _comp128_compression(x);
+
+ /* FormBitFromBytes */
+ _comp128_bitsfrombytes(x, bits);
+
+ /* Permutation */
+ _comp128_permutation(x, bits);
+ }
+
+ /* Round 8 (final) */
+ /* x[0-15] = Ki */
+ memcpy(x, ki, 16);
+
+ /* Compression */
+ _comp128_compression(x);
+
+ /* Output stage */
+ for (i=0; i<8; i+=2)
+ sres[i>>1] = x[i]<<4 | x[i+1];
+
+ for (i=0; i<12; i+=2)
+ kc[i>>1] = (x[i + 18] << 6) |
+ (x[i + 19] << 2) |
+ (x[i + 20] >> 2);
+
+ kc[6] = (x[30]<<6) | (x[31]<<2);
+ kc[7] = 0;
+}
+
diff --git a/openbsc/src/db.c b/openbsc/src/db.c
index 369505a2c..5be47ff64 100644
--- a/openbsc/src/db.c
+++ b/openbsc/src/db.c
@@ -25,6 +25,7 @@
#include <openbsc/db.h>
#include <openbsc/talloc.h>
#include <openbsc/debug.h>
+#include <openbsc/statistics.h>
#include <libgen.h>
#include <stdio.h>
@@ -117,12 +118,35 @@ static char *create_stmts[] = {
"subscriber_id INTEGER NOT NULL, "
"apdu BLOB "
")",
+ "CREATE TABLE IF NOT EXISTS Counters ("
+ "id INTEGER PRIMARY KEY AUTOINCREMENT, "
+ "timestamp TIMESTAMP NOT NULL, "
+ "value INTEGER NOT NULL, "
+ "name TEXT NOT NULL "
+ ")",
+ "CREATE TABLE IF NOT EXISTS AuthKeys ("
+ "id INTEGER PRIMARY KEY AUTOINCREMENT, "
+ "subscriber_id INTEGER UNIQUE NOT NULL, "
+ "algorithm_id INTEGER NOT NULL, "
+ "a3a8_ki BLOB "
+ ")",
+ "CREATE TABLE IF NOT EXISTS AuthTuples ("
+ "id INTEGER PRIMARY KEY AUTOINCREMENT, "
+ "subscriber_id NUMERIC UNIQUE NOT NULL, "
+ "issued TIMESTAMP NOT NULL, "
+ "use_count INTEGER NOT NULL DEFAULT 0, "
+ "key_seq INTEGER NOT NULL, "
+ "rand BLOB NOT NULL, "
+ "sres BLOB NOT NULL, "
+ "kc BLOB NOT NULL "
+ ")",
};
-void db_error_func(dbi_conn conn, void* data) {
- const char* msg;
+void db_error_func(dbi_conn conn, void *data)
+{
+ const char *msg;
dbi_conn_error(conn, &msg);
- printf("DBI: %s\n", msg);
+ LOGP(DDB, LOGL_ERROR, "DBI: %s\n", msg);
}
static int check_db_revision(void)
@@ -149,11 +173,13 @@ static int check_db_revision(void)
return 0;
}
-int db_init(const char *name) {
+int db_init(const char *name)
+{
dbi_initialize(NULL);
+
conn = dbi_conn_new("sqlite3");
- if (conn==NULL) {
- printf("DB: Failed to create connection.\n");
+ if (conn == NULL) {
+ LOGP(DDB, LOGL_FATAL, "Failed to create connection.\n");
return 1;
}
@@ -186,21 +212,23 @@ out_err:
}
-int db_prepare() {
+int db_prepare()
+{
dbi_result result;
int i;
for (i = 0; i < ARRAY_SIZE(create_stmts); i++) {
result = dbi_conn_query(conn, create_stmts[i]);
- if (result==NULL) {
- printf("DB: Failed to create some table.\n");
+ if (!result) {
+ LOGP(DDB, LOGL_ERROR,
+ "Failed to create some table.\n");
return 1;
}
dbi_result_free(result);
}
if (check_db_revision() < 0) {
- fprintf(stderr, "Database schema revision invalid, "
+ LOGP(DDB, LOGL_FATAL, "Database schema revision invalid, "
"please update your database schema\n");
return -1;
}
@@ -208,7 +236,8 @@ int db_prepare() {
return 0;
}
-int db_fini() {
+int db_fini()
+{
dbi_conn_close(conn);
dbi_shutdown();
@@ -219,10 +248,10 @@ int db_fini() {
return 0;
}
-struct gsm_subscriber* db_create_subscriber(struct gsm_network *net, char *imsi)
+struct gsm_subscriber *db_create_subscriber(struct gsm_network *net, char *imsi)
{
dbi_result result;
- struct gsm_subscriber* subscr;
+ struct gsm_subscriber *subscr;
/* Is this subscriber known in the db? */
subscr = db_get_subscriber(net, GSM_SUBSCRIBER_IMSI, imsi);
@@ -230,11 +259,10 @@ struct gsm_subscriber* db_create_subscriber(struct gsm_network *net, char *imsi)
result = dbi_conn_queryf(conn,
"UPDATE Subscriber set updated = datetime('now') "
"WHERE imsi = %s " , imsi);
- if (result==NULL) {
- printf("DB: failed to update timestamp\n");
- } else {
+ if (!result)
+ LOGP(DDB, LOGL_ERROR, "failed to update timestamp\n");
+ else
dbi_result_free(result);
- }
return subscr;
}
@@ -249,14 +277,13 @@ struct gsm_subscriber* db_create_subscriber(struct gsm_network *net, char *imsi)
"(%s, datetime('now'), datetime('now')) ",
imsi
);
- if (result==NULL) {
- printf("DB: Failed to create Subscriber by IMSI.\n");
- }
+ if (!result)
+ LOGP(DDB, LOGL_ERROR, "Failed to create Subscriber by IMSI.\n");
subscr->net = net;
subscr->id = dbi_conn_sequence_last(conn, NULL);
strncpy(subscr->imsi, imsi, GSM_IMSI_LENGTH-1);
dbi_result_free(result);
- printf("DB: New Subscriber: ID %llu, IMSI %s\n", subscr->id, subscr->imsi);
+ LOGP(DDB, LOGL_INFO, "New Subscriber: ID %llu, IMSI %s\n", subscr->id, subscr->imsi);
db_subscriber_alloc_exten(subscr);
return subscr;
}
@@ -265,7 +292,7 @@ static int get_equipment_by_subscr(struct gsm_subscriber *subscr)
{
dbi_result result;
const char *string;
- unsigned int cm1;
+ unsigned char cm1;
const unsigned char *cm2, *cm3;
struct gsm_equipment *equip = &subscr->equipment;
@@ -288,7 +315,9 @@ static int get_equipment_by_subscr(struct gsm_subscriber *subscr)
if (string)
strncpy(equip->imei, string, sizeof(equip->imei));
- cm1 = dbi_result_get_uint(result, "classmark1") & 0xff;
+ string = dbi_result_get_string(result, "classmark1");
+ if (string)
+ cm1 = atoi(string) & 0xff;
equip->classmark1 = *((struct gsm48_classmark1 *) &cm1);
equip->classmark2_len = dbi_result_get_field_length(result, "classmark2");
@@ -307,6 +336,214 @@ static int get_equipment_by_subscr(struct gsm_subscriber *subscr)
return 0;
}
+
+int get_authinfo_by_subscr(struct gsm_auth_info *ainfo,
+ struct gsm_subscriber *subscr)
+{
+ dbi_result result;
+ const unsigned char *a3a8_ki;
+
+ result = dbi_conn_queryf(conn,
+ "SELECT * FROM AuthKeys WHERE subscriber_id=%u",
+ subscr->id);
+ if (!result)
+ return -EIO;
+
+ if (!dbi_result_next_row(result)) {
+ dbi_result_free(result);
+ return -ENOENT;
+ }
+
+ ainfo->auth_algo = dbi_result_get_ulonglong(result, "algorithm_id");
+ ainfo->a3a8_ki_len = dbi_result_get_field_length(result, "a3a8_ki");
+ a3a8_ki = dbi_result_get_binary(result, "a3a8_ki");
+ if (ainfo->a3a8_ki_len > sizeof(ainfo->a3a8_ki))
+ ainfo->a3a8_ki_len = sizeof(ainfo->a3a8_ki_len);
+ memcpy(ainfo->a3a8_ki, a3a8_ki, ainfo->a3a8_ki_len);
+
+ dbi_result_free(result);
+
+ return 0;
+}
+
+int set_authinfo_for_subscr(struct gsm_auth_info *ainfo,
+ struct gsm_subscriber *subscr)
+{
+ dbi_result result;
+ struct gsm_auth_info ainfo_old;
+ int rc, upd;
+ unsigned char *ki_str;
+
+ /* Deletion ? */
+ if (ainfo == NULL) {
+ result = dbi_conn_queryf(conn,
+ "DELETE FROM AuthKeys WHERE subscriber_id=%u",
+ subscr->id);
+
+ if (!result)
+ return -EIO;
+
+ dbi_result_free(result);
+
+ return 0;
+ }
+
+ /* Check if already existing */
+ rc = get_authinfo_by_subscr(&ainfo_old, subscr);
+ if (rc && rc != -ENOENT)
+ return rc;
+ upd = rc ? 0 : 1;
+
+ /* Update / Insert */
+ dbi_conn_quote_binary_copy(conn,
+ ainfo->a3a8_ki, ainfo->a3a8_ki_len, &ki_str);
+
+ if (!upd) {
+ result = dbi_conn_queryf(conn,
+ "INSERT INTO AuthKeys "
+ "(subscriber_id, algorithm_id, a3a8_ki) "
+ "VALUES (%u, %u, %s)",
+ subscr->id, ainfo->auth_algo, ki_str);
+ } else {
+ result = dbi_conn_queryf(conn,
+ "UPDATE AuthKeys "
+ "SET algorithm_id=%u, a3a8_ki=%s "
+ "WHERE subscriber_id=%u",
+ ainfo->auth_algo, ki_str, subscr->id);
+ }
+
+ free(ki_str);
+
+ if (!result)
+ return -EIO;
+
+ dbi_result_free(result);
+
+ return 0;
+}
+
+int get_authtuple_by_subscr(struct gsm_auth_tuple *atuple,
+ struct gsm_subscriber *subscr)
+{
+ dbi_result result;
+ int len;
+ const unsigned char *blob;
+
+ result = dbi_conn_queryf(conn,
+ "SELECT * FROM AuthTuples WHERE subscriber_id=%u",
+ subscr->id);
+ if (!result)
+ return -EIO;
+
+ if (!dbi_result_next_row(result)) {
+ dbi_result_free(result);
+ return -ENOENT;
+ }
+
+ memset(atuple, 0, sizeof(atuple));
+
+ atuple->use_count = dbi_result_get_ulonglong(result, "use_count");
+ atuple->key_seq = dbi_result_get_ulonglong(result, "key_seq");
+
+ len = dbi_result_get_field_length(result, "rand");
+ if (len != sizeof(atuple->rand))
+ goto err_size;
+
+ blob = dbi_result_get_binary(result, "rand");
+ memcpy(atuple->rand, blob, len);
+
+ len = dbi_result_get_field_length(result, "sres");
+ if (len != sizeof(atuple->sres))
+ goto err_size;
+
+ blob = dbi_result_get_binary(result, "sres");
+ memcpy(atuple->sres, blob, len);
+
+ len = dbi_result_get_field_length(result, "kc");
+ if (len != sizeof(atuple->kc))
+ goto err_size;
+
+ blob = dbi_result_get_binary(result, "kc");
+ memcpy(atuple->kc, blob, len);
+
+ dbi_result_free(result);
+
+ return 0;
+
+err_size:
+ dbi_result_free(result);
+ return -EIO;
+}
+
+int set_authtuple_for_subscr(struct gsm_auth_tuple *atuple,
+ struct gsm_subscriber *subscr)
+{
+ dbi_result result;
+ int rc, upd;
+ struct gsm_auth_tuple atuple_old;
+ unsigned char *rand_str, *sres_str, *kc_str;
+
+ /* Deletion ? */
+ if (atuple == NULL) {
+ result = dbi_conn_queryf(conn,
+ "DELETE FROM AuthTuples WHERE subscriber_id=%u",
+ subscr->id);
+
+ if (!result)
+ return -EIO;
+
+ dbi_result_free(result);
+
+ return 0;
+ }
+
+ /* Check if already existing */
+ rc = get_authtuple_by_subscr(&atuple_old, subscr);
+ if (rc && rc != -ENOENT)
+ return rc;
+ upd = rc ? 0 : 1;
+
+ /* Update / Insert */
+ dbi_conn_quote_binary_copy(conn,
+ atuple->rand, sizeof(atuple->rand), &rand_str);
+ dbi_conn_quote_binary_copy(conn,
+ atuple->sres, sizeof(atuple->sres), &sres_str);
+ dbi_conn_quote_binary_copy(conn,
+ atuple->kc, sizeof(atuple->kc), &kc_str);
+
+ if (!upd) {
+ result = dbi_conn_queryf(conn,
+ "INSERT INTO AuthTuples "
+ "(subscriber_id, issued, use_count, "
+ "key_seq, rand, sres, kc) "
+ "VALUES (%u, datetime('now'), %u, "
+ "%u, %s, %s, %s ) ",
+ subscr->id, atuple->use_count, atuple->key_seq,
+ rand_str, sres_str, kc_str);
+ } else {
+ char *issued = atuple->key_seq == atuple_old.key_seq ?
+ "issued" : "datetime('now')";
+ result = dbi_conn_queryf(conn,
+ "UPDATE AuthKeys "
+ "SET issued=%s, use_count=%u, "
+ "key_seq=%u, rand=%s, sres=%s, kc=%s "
+ "WHERE subscriber_id = %u",
+ issued, atuple->use_count, atuple->key_seq,
+ rand_str, sres_str, kc_str, subscr->id);
+ }
+
+ free(rand_str);
+ free(sres_str);
+ free(kc_str);
+
+ if (!result)
+ return -EIO;
+
+ dbi_result_free(result);
+
+ return 0;
+}
+
#define BASE_QUERY "SELECT * FROM Subscriber "
struct gsm_subscriber *db_get_subscriber(struct gsm_network *net,
enum gsm_subscriber_field field,
@@ -353,15 +590,15 @@ struct gsm_subscriber *db_get_subscriber(struct gsm_network *net,
free(quoted);
break;
default:
- printf("DB: Unknown query selector for Subscriber.\n");
+ LOGP(DDB, LOGL_NOTICE, "Unknown query selector for Subscriber.\n");
return NULL;
}
- if (result==NULL) {
- printf("DB: Failed to query Subscriber.\n");
+ if (!result) {
+ LOGP(DDB, LOGL_ERROR, "Failed to query Subscriber.\n");
return NULL;
}
if (!dbi_result_next_row(result)) {
- printf("DB: Failed to find the Subscriber. '%u' '%s'\n",
+ DEBUGP(DDB, "Failed to find the Subscriber. '%u' '%s'\n",
field, id);
dbi_result_free(result);
return NULL;
@@ -388,7 +625,7 @@ struct gsm_subscriber *db_get_subscriber(struct gsm_network *net,
subscr->lac = dbi_result_get_uint(result, "lac");
subscr->authorized = dbi_result_get_uint(result, "authorized");
- printf("DB: Found Subscriber: ID %llu, IMSI %s, NAME '%s', TMSI %u, EXTEN '%s', LAC %hu, AUTH %u\n",
+ DEBUGP(DDB, "Found Subscriber: ID %llu, IMSI %s, NAME '%s', TMSI %u, EXTEN '%s', LAC %hu, AUTH %u\n",
subscr->id, subscr->imsi, subscr->name, subscr->tmsi, subscr->extension,
subscr->lac, subscr->authorized);
dbi_result_free(result);
@@ -398,7 +635,8 @@ struct gsm_subscriber *db_get_subscriber(struct gsm_network *net,
return subscr;
}
-int db_sync_subscriber(struct gsm_subscriber* subscriber) {
+int db_sync_subscriber(struct gsm_subscriber *subscriber)
+{
dbi_result result;
char tmsi[14];
char *q_tmsi;
@@ -410,6 +648,7 @@ int db_sync_subscriber(struct gsm_subscriber* subscriber) {
&q_tmsi);
} else
q_tmsi = strdup("NULL");
+
result = dbi_conn_queryf(conn,
"UPDATE Subscriber "
"SET updated = datetime('now'), "
@@ -424,14 +663,17 @@ int db_sync_subscriber(struct gsm_subscriber* subscriber) {
subscriber->authorized,
q_tmsi,
subscriber->lac,
- subscriber->imsi
- );
+ subscriber->imsi);
+
free(q_tmsi);
- if (result==NULL) {
- printf("DB: Failed to update Subscriber (by IMSI).\n");
+
+ if (!result) {
+ LOGP(DDB, LOGL_ERROR, "Failed to update Subscriber (by IMSI).\n");
return 1;
}
+
dbi_result_free(result);
+
return 0;
}
@@ -442,15 +684,15 @@ int db_sync_equipment(struct gsm_equipment *equip)
u_int8_t classmark1;
memcpy(&classmark1, &equip->classmark1, sizeof(classmark1));
- printf("DB: Sync Equipment IMEI=%s, classmark1=%02x",
+ DEBUGP(DDB, "Sync Equipment IMEI=%s, classmark1=%02x",
equip->imei, classmark1);
if (equip->classmark2_len)
- printf(", classmark2=%s",
+ DEBUGPC(DDB, ", classmark2=%s",
hexdump(equip->classmark2, equip->classmark2_len));
if (equip->classmark3_len)
- printf(", classmark3=%s",
+ DEBUGPC(DDB, ", classmark3=%s",
hexdump(equip->classmark3, equip->classmark3_len));
- printf("\n");
+ DEBUGPC(DDB, "\n");
dbi_conn_quote_binary_copy(conn, equip->classmark2,
equip->classmark2_len, &cm2);
@@ -470,7 +712,7 @@ int db_sync_equipment(struct gsm_equipment *equip)
free(cm3);
if (!result) {
- printf("DB: Failed to update Equipment\n");
+ LOGP(DDB, LOGL_ERROR, "Failed to update Equipment\n");
return -EIO;
}
@@ -478,10 +720,12 @@ int db_sync_equipment(struct gsm_equipment *equip)
return 0;
}
-int db_subscriber_alloc_tmsi(struct gsm_subscriber* subscriber) {
- dbi_result result=NULL;
+int db_subscriber_alloc_tmsi(struct gsm_subscriber *subscriber)
+{
+ dbi_result result = NULL;
char tmsi[14];
char* tmsi_quoted;
+
for (;;) {
subscriber->tmsi = rand();
if (subscriber->tmsi == GSM_RESERVED_TMSI)
@@ -492,20 +736,23 @@ int db_subscriber_alloc_tmsi(struct gsm_subscriber* subscriber) {
result = dbi_conn_queryf(conn,
"SELECT * FROM Subscriber "
"WHERE tmsi = %s ",
- tmsi_quoted
- );
+ tmsi_quoted);
+
free(tmsi_quoted);
- if (result==NULL) {
- printf("DB: Failed to query Subscriber while allocating new TMSI.\n");
+
+ if (!result) {
+ LOGP(DDB, LOGL_ERROR, "Failed to query Subscriber "
+ "while allocating new TMSI.\n");
return 1;
}
- if (dbi_result_get_numrows(result)){
+ if (dbi_result_get_numrows(result)) {
dbi_result_free(result);
continue;
}
if (!dbi_result_next_row(result)) {
dbi_result_free(result);
- printf("DB: Allocated TMSI %u for IMSI %s.\n", subscriber->tmsi, subscriber->imsi);
+ DEBUGP(DDB, "Allocated TMSI %u for IMSI %s.\n",
+ subscriber->tmsi, subscriber->imsi);
return db_sync_subscriber(subscriber);
}
dbi_result_free(result);
@@ -513,9 +760,11 @@ int db_subscriber_alloc_tmsi(struct gsm_subscriber* subscriber) {
return 0;
}
-int db_subscriber_alloc_exten(struct gsm_subscriber* subscriber) {
- dbi_result result=NULL;
+int db_subscriber_alloc_exten(struct gsm_subscriber *subscriber)
+{
+ dbi_result result = NULL;
u_int32_t try;
+
for (;;) {
try = (rand()%(GSM_MAX_EXTEN-GSM_MIN_EXTEN+1)+GSM_MIN_EXTEN);
result = dbi_conn_queryf(conn,
@@ -523,8 +772,9 @@ int db_subscriber_alloc_exten(struct gsm_subscriber* subscriber) {
"WHERE extension = %i",
try
);
- if (result==NULL) {
- printf("DB: Failed to query Subscriber while allocating new extension.\n");
+ if (!result) {
+ LOGP(DDB, LOGL_ERROR, "Failed to query Subscriber "
+ "while allocating new extension.\n");
return 1;
}
if (dbi_result_get_numrows(result)){
@@ -538,7 +788,7 @@ int db_subscriber_alloc_exten(struct gsm_subscriber* subscriber) {
dbi_result_free(result);
}
sprintf(subscriber->extension, "%i", try);
- printf("DB: Allocated extension %i for IMSI %s.\n", try, subscriber->imsi);
+ DEBUGP(DDB, "Allocated extension %i for IMSI %s.\n", try, subscriber->imsi);
return db_sync_subscriber(subscriber);
}
/*
@@ -547,7 +797,7 @@ int db_subscriber_alloc_exten(struct gsm_subscriber* subscriber) {
* an error.
*/
-int db_subscriber_alloc_token(struct gsm_subscriber* subscriber, u_int32_t* token)
+int db_subscriber_alloc_token(struct gsm_subscriber *subscriber, u_int32_t *token)
{
dbi_result result;
u_int32_t try;
@@ -561,7 +811,8 @@ int db_subscriber_alloc_token(struct gsm_subscriber* subscriber, u_int32_t* toke
"WHERE subscriber_id = %llu OR token = \"%08X\" ",
subscriber->id, try);
if (!result) {
- printf("DB: Failed to query AuthToken while allocating new token.\n");
+ LOGP(DDB, LOGL_ERROR, "Failed to query AuthToken "
+ "while allocating new token.\n");
return 1;
}
if (dbi_result_get_numrows(result)) {
@@ -581,17 +832,19 @@ int db_subscriber_alloc_token(struct gsm_subscriber* subscriber, u_int32_t* toke
"(%llu, datetime('now'), \"%08X\") ",
subscriber->id, try);
if (!result) {
- printf("DB: Failed to create token %08X for IMSI %s.\n", try, subscriber->imsi);
+ LOGP(DDB, LOGL_ERROR, "Failed to create token %08X for "
+ "IMSI %s.\n", try, subscriber->imsi);
return 1;
}
dbi_result_free(result);
*token = try;
- printf("DB: Allocated token %08X for IMSI %s.\n", try, subscriber->imsi);
+ DEBUGP(DDB, "Allocated token %08X for IMSI %s.\n", try, subscriber->imsi);
return 0;
}
-int db_subscriber_assoc_imei(struct gsm_subscriber* subscriber, char imei[GSM_IMEI_LENGTH]) {
+int db_subscriber_assoc_imei(struct gsm_subscriber *subscriber, char imei[GSM_IMEI_LENGTH])
+{
unsigned long long equipment_id, watch_id;
dbi_result result;
@@ -603,32 +856,32 @@ int db_subscriber_assoc_imei(struct gsm_subscriber* subscriber, char imei[GSM_IM
"(imei, created, updated) "
"VALUES "
"(%s, datetime('now'), datetime('now')) ",
- imei
- );
- if (result==NULL) {
- printf("DB: Failed to create Equipment by IMEI.\n");
+ imei);
+ if (!result) {
+ LOGP(DDB, LOGL_ERROR, "Failed to create Equipment by IMEI.\n");
return 1;
}
+
equipment_id = 0;
if (dbi_result_get_numrows_affected(result)) {
equipment_id = dbi_conn_sequence_last(conn, NULL);
}
dbi_result_free(result);
- if (equipment_id) {
- printf("DB: New Equipment: ID %llu, IMEI %s\n", equipment_id, imei);
- }
+
+ if (equipment_id)
+ DEBUGP(DDB, "New Equipment: ID %llu, IMEI %s\n", equipment_id, imei);
else {
result = dbi_conn_queryf(conn,
"SELECT id FROM Equipment "
"WHERE imei = %s ",
imei
);
- if (result==NULL) {
- printf("DB: Failed to query Equipment by IMEI.\n");
+ if (!result) {
+ LOGP(DDB, LOGL_ERROR, "Failed to query Equipment by IMEI.\n");
return 1;
}
if (!dbi_result_next_row(result)) {
- printf("DB: Failed to find the Equipment.\n");
+ LOGP(DDB, LOGL_ERROR, "Failed to find the Equipment.\n");
dbi_result_free(result);
return 1;
}
@@ -641,33 +894,33 @@ int db_subscriber_assoc_imei(struct gsm_subscriber* subscriber, char imei[GSM_IM
"(subscriber_id, equipment_id, created, updated) "
"VALUES "
"(%llu, %llu, datetime('now'), datetime('now')) ",
- subscriber->id, equipment_id
- );
- if (result==NULL) {
- printf("DB: Failed to create EquipmentWatch.\n");
+ subscriber->id, equipment_id);
+ if (!result) {
+ LOGP(DDB, LOGL_ERROR, "Failed to create EquipmentWatch.\n");
return 1;
}
+
watch_id = 0;
- if (dbi_result_get_numrows_affected(result)) {
+ if (dbi_result_get_numrows_affected(result))
watch_id = dbi_conn_sequence_last(conn, NULL);
- }
+
dbi_result_free(result);
- if (watch_id) {
- printf("DB: New EquipmentWatch: ID %llu, IMSI %s, IMEI %s\n", equipment_id, subscriber->imsi, imei);
- }
+ if (watch_id)
+ DEBUGP(DDB, "New EquipmentWatch: ID %llu, IMSI %s, IMEI %s\n",
+ equipment_id, subscriber->imsi, imei);
else {
result = dbi_conn_queryf(conn,
"UPDATE EquipmentWatch "
"SET updated = datetime('now') "
"WHERE subscriber_id = %llu AND equipment_id = %llu ",
- subscriber->id, equipment_id
- );
- if (result==NULL) {
- printf("DB: Failed to update EquipmentWatch.\n");
+ subscriber->id, equipment_id);
+ if (!result) {
+ LOGP(DDB, LOGL_ERROR, "Failed to update EquipmentWatch.\n");
return 1;
}
dbi_result_free(result);
- printf("DB: Updated EquipmentWatch: ID %llu, IMSI %s, IMEI %s\n", equipment_id, subscriber->imsi, imei);
+ DEBUGP(DDB, "Updated EquipmentWatch: ID %llu, IMSI %s, IMEI %s\n",
+ equipment_id, subscriber->imsi, imei);
}
return 0;
@@ -769,8 +1022,9 @@ struct gsm_sms *db_sms_get_unsent(struct gsm_network *net, int min_id)
result = dbi_conn_queryf(conn,
"SELECT * FROM SMS,Subscriber "
"WHERE sms.id >= %llu AND sms.sent is NULL "
+ "AND sms.receiver_id = subscriber.id "
"AND subscriber.lac > 0 "
- "ORDER BY id",
+ "ORDER BY sms.id LIMIT 1",
min_id);
if (!result)
return NULL;
@@ -787,7 +1041,34 @@ struct gsm_sms *db_sms_get_unsent(struct gsm_network *net, int min_id)
return sms;
}
-/* retrieve the next unsent SMS with ID >= min_id */
+struct gsm_sms *db_sms_get_unsent_by_subscr(struct gsm_network *net, int min_subscr_id)
+{
+ dbi_result result;
+ struct gsm_sms *sms;
+
+ result = dbi_conn_queryf(conn,
+ "SELECT * FROM SMS,Subscriber "
+ "WHERE sms.receiver_id >= %llu AND sms.sent is NULL "
+ "AND sms.receiver_id = subscriber.id "
+ "AND subscriber.lac > 0 "
+ "ORDER BY sms.receiver_id, id LIMIT 1",
+ min_subscr_id);
+ if (!result)
+ return NULL;
+
+ if (!dbi_result_next_row(result)) {
+ dbi_result_free(result);
+ return NULL;
+ }
+
+ sms = sms_from_result(net, result);
+
+ dbi_result_free(result);
+
+ return sms;
+}
+
+/* retrieve the next unsent SMS for a given subscriber */
struct gsm_sms *db_sms_get_unsent_for_subscr(struct gsm_subscriber *subscr)
{
dbi_result result;
@@ -796,8 +1077,9 @@ struct gsm_sms *db_sms_get_unsent_for_subscr(struct gsm_subscriber *subscr)
result = dbi_conn_queryf(conn,
"SELECT * FROM SMS,Subscriber "
"WHERE sms.receiver_id = %llu AND sms.sent is NULL "
+ "AND sms.receiver_id = subscriber.id "
"AND subscriber.lac > 0 "
- "ORDER BY id",
+ "ORDER BY sms.id LIMIT 1",
subscr->id);
if (!result)
return NULL;
@@ -824,7 +1106,7 @@ int db_sms_mark_sent(struct gsm_sms *sms)
"SET sent = datetime('now') "
"WHERE id = %llu", sms->id);
if (!result) {
- printf("DB: Failed to mark SMS %llu as sent.\n", sms->id);
+ LOGP(DDB, LOGL_ERROR, "Failed to mark SMS %llu as sent.\n", sms->id);
return 1;
}
@@ -842,7 +1124,8 @@ int db_sms_inc_deliver_attempts(struct gsm_sms *sms)
"SET deliver_attempts = deliver_attempts + 1 "
"WHERE id = %llu", sms->id);
if (!result) {
- printf("DB: Failed to inc deliver attempts for SMS %llu.\n", sms->id);
+ LOGP(DDB, LOGL_ERROR, "Failed to inc deliver attempts for "
+ "SMS %llu.\n", sms->id);
return 1;
}
@@ -873,3 +1156,24 @@ int db_apdu_blob_store(struct gsm_subscriber *subscr,
dbi_result_free(result);
return 0;
}
+
+int db_store_counter(struct counter *ctr)
+{
+ dbi_result result;
+ char *q_name;
+
+ dbi_conn_quote_string_copy(conn, ctr->name, &q_name);
+
+ result = dbi_conn_queryf(conn,
+ "INSERT INTO Counters "
+ "(timestamp,name,value) VALUES "
+ "(datetime('now'),%s,%lu)", q_name, ctr->value);
+
+ free(q_name);
+
+ if (!result)
+ return -EIO;
+
+ dbi_result_free(result);
+ return 0;
+}
diff --git a/openbsc/src/debug.c b/openbsc/src/debug.c
index 5dc2e0ff7..90a9fc7e3 100644
--- a/openbsc/src/debug.c
+++ b/openbsc/src/debug.c
@@ -25,23 +25,84 @@
#include <string.h>
#include <strings.h>
#include <time.h>
+#include <errno.h>
#include <openbsc/debug.h>
+#include <openbsc/talloc.h>
+#include <openbsc/gsm_data.h>
+#include <openbsc/gsm_subscriber.h>
-unsigned int debug_mask = 0xffffffff & ~(DMI|DMIB|DMEAS);
+/* default categories */
+static struct debug_category default_categories[Debug_LastEntry] = {
+ [DRLL] = { .enabled = 1, .loglevel = LOGL_NOTICE },
+ [DCC] = { .enabled = 1, .loglevel = LOGL_NOTICE },
+ [DNM] = { .enabled = 1, .loglevel = LOGL_NOTICE },
+ [DRR] = { .enabled = 1, .loglevel = LOGL_NOTICE },
+ [DRSL] = { .enabled = 1, .loglevel = LOGL_NOTICE },
+ [DMM] = { .enabled = 1, .loglevel = LOGL_INFO },
+ [DMNCC] = { .enabled = 1, .loglevel = LOGL_NOTICE },
+ [DSMS] = { .enabled = 1, .loglevel = LOGL_NOTICE },
+ [DPAG] = { .enabled = 1, .loglevel = LOGL_NOTICE },
+ [DMEAS] = { .enabled = 0, .loglevel = LOGL_NOTICE },
+ [DMI] = { .enabled = 0, .loglevel = LOGL_NOTICE },
+ [DMIB] = { .enabled = 0, .loglevel = LOGL_NOTICE },
+ [DMUX] = { .enabled = 1, .loglevel = LOGL_NOTICE },
+ [DINP] = { .enabled = 1, .loglevel = LOGL_NOTICE },
+ [DSCCP] = { .enabled = 1, .loglevel = LOGL_NOTICE },
+ [DMSC] = { .enabled = 1, .loglevel = LOGL_NOTICE },
+ [DMGCP] = { .enabled = 1, .loglevel = LOGL_NOTICE },
+ [DHO] = { .enabled = 1, .loglevel = LOGL_NOTICE },
+ [DDB] = { .enabled = 1, .loglevel = LOGL_NOTICE },
+ [DREF] = { .enabled = 0, .loglevel = LOGL_NOTICE },
+};
+
+const char *get_value_string(const struct value_string *vs, u_int32_t val)
+{
+ int i;
+
+ for (i = 0;; i++) {
+ if (vs[i].value == 0 && vs[i].str == NULL)
+ break;
+ if (vs[i].value == val)
+ return vs[i].str;
+ }
+ return "unknown";
+}
+
+int get_string_value(const struct value_string *vs, const char *str)
+{
+ int i;
+
+ for (i = 0;; i++) {
+ if (vs[i].value == 0 && vs[i].str == NULL)
+ break;
+ if (!strcasecmp(vs[i].str, str))
+ return vs[i].value;
+ }
+ return -EINVAL;
+}
struct debug_info {
const char *name;
const char *color;
const char *description;
int number;
+ int position;
};
+struct debug_context {
+ struct gsm_lchan *lchan;
+ struct gsm_subscriber *subscr;
+ struct gsm_bts *bts;
+};
+
+static struct debug_context debug_context;
+static void *tall_dbg_ctx = NULL;
+static LLIST_HEAD(target_list);
+
#define DEBUG_CATEGORY(NUMBER, NAME, COLOR, DESCRIPTION) \
{ .name = NAME, .color = COLOR, .description = DESCRIPTION, .number = NUMBER },
-#define ARRAY_SIZE(array) (sizeof(array)/sizeof(array[0]))
-
static const struct debug_info debug_info[] = {
DEBUG_CATEGORY(DRLL, "DRLL", "\033[1;31m", "")
DEBUG_CATEGORY(DCC, "DCC", "\033[1;32m", "")
@@ -60,52 +121,83 @@ static const struct debug_info debug_info[] = {
DEBUG_CATEGORY(DSCCP, "DSCCP", "", "")
DEBUG_CATEGORY(DMSC, "DMSC", "", "")
DEBUG_CATEGORY(DMGCP, "DMGCP", "", "")
+ DEBUG_CATEGORY(DHO, "DHO", "", "")
+ DEBUG_CATEGORY(DDB, "DDB", "", "")
+ DEBUG_CATEGORY(DDB, "DREF", "", "")
};
-static int use_color = 1;
+static const struct value_string loglevel_strs[] = {
+ { 0, "EVERYTHING" },
+ { 1, "DEBUG" },
+ { 3, "INFO" },
+ { 5, "NOTICE" },
+ { 7, "ERROR" },
+ { 8, "FATAL" },
+ { 0, NULL },
+};
-void debug_use_color(int color)
+int debug_parse_level(const char *lvl)
{
- use_color = color;
+ return get_string_value(loglevel_strs, lvl);
}
-static int print_timestamp = 0;
-
-void debug_timestamp(int enable)
+int debug_parse_category(const char *category)
{
- print_timestamp = enable;
-}
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(debug_info); ++i) {
+ if (!strcasecmp(debug_info[i].name+1, category))
+ return debug_info[i].number;
+ }
+ return -EINVAL;
+}
/*
* Parse the category mask.
- * category1:category2:category3
+ * The format can be this: category1:category2:category3
+ * or category1,2:category2,3:...
*/
-void debug_parse_category_mask(const char *_mask)
+void debug_parse_category_mask(struct debug_target* target, const char *_mask)
{
- unsigned int new_mask = 0;
int i = 0;
char *mask = strdup(_mask);
char *category_token = NULL;
+ /* Disable everything to enable it afterwards */
+ for (i = 0; i < ARRAY_SIZE(target->categories); ++i)
+ target->categories[i].enabled = 0;
+
category_token = strtok(mask, ":");
do {
for (i = 0; i < ARRAY_SIZE(debug_info); ++i) {
- if (strcasecmp(debug_info[i].name, category_token) == 0)
- new_mask |= debug_info[i].number;
+ char* colon = strstr(category_token, ",");
+ int length = strlen(category_token);
+
+ if (colon)
+ length = colon - category_token;
+
+ if (strncasecmp(debug_info[i].name, category_token, length) == 0) {
+ int number = debug_info[i].number;
+ int level = 0;
+
+ if (colon)
+ level = atoi(colon+1);
+
+ target->categories[number].enabled = 1;
+ target->categories[number].loglevel = level;
+ }
}
} while ((category_token = strtok(NULL, ":")));
-
free(mask);
- debug_mask = new_mask;
}
-const char* color(int subsys)
+static const char* color(int subsys)
{
int i = 0;
- for (i = 0; use_color && i < ARRAY_SIZE(debug_info); ++i) {
+ for (i = 0; i < ARRAY_SIZE(debug_info); ++i) {
if (debug_info[i].number == subsys)
return debug_info[i].color;
}
@@ -113,35 +205,111 @@ const char* color(int subsys)
return "";
}
-void debugp(unsigned int subsys, char *file, int line, int cont, const char *format, ...)
+static void _output(struct debug_target *target, unsigned int subsys, char *file, int line,
+ int cont, const char *format, va_list ap)
{
- va_list ap;
- FILE *outfd = stderr;
-
- if (!(debug_mask & subsys))
- return;
+ char col[30];
+ char sub[30];
+ char tim[30];
+ char buf[4096];
+ char final[4096];
- va_start(ap, format);
+ /* prepare the data */
+ col[0] = '\0';
+ sub[0] = '\0';
+ tim[0] = '\0';
+ buf[0] = '\0';
- fprintf(outfd, "%s", color(subsys));
+ /* are we using color */
+ if (target->use_color) {
+ snprintf(col, sizeof(col), "%s", color(subsys));
+ col[sizeof(col)-1] = '\0';
+ }
+ vsnprintf(buf, sizeof(buf), format, ap);
+ buf[sizeof(buf)-1] = '\0';
if (!cont) {
- if (print_timestamp) {
+ if (target->print_timestamp) {
char *timestr;
time_t tm;
tm = time(NULL);
timestr = ctime(&tm);
timestr[strlen(timestr)-1] = '\0';
- fprintf(outfd, "%s ", timestr);
+ snprintf(tim, sizeof(tim), "%s ", timestr);
+ tim[sizeof(tim)-1] = '\0';
+ }
+ snprintf(sub, sizeof(sub), "<%4.4x> %s:%d ", subsys, file, line);
+ sub[sizeof(sub)-1] = '\0';
+ }
+
+ snprintf(final, sizeof(final), "%s%s%s%s\033[0;m", col, tim, sub, buf);
+ final[sizeof(final)-1] = '\0';
+ target->output(target, final);
+}
+
+
+static void _debugp(unsigned int subsys, int level, char *file, int line,
+ int cont, const char *format, va_list ap)
+{
+ struct debug_target *tar;
+
+ llist_for_each_entry(tar, &target_list, entry) {
+ struct debug_category *category;
+ int output = 0;
+
+ category = &tar->categories[subsys];
+ /* subsystem is not supposed to be debugged */
+ if (!category->enabled)
+ continue;
+
+ /* Check the global log level */
+ if (tar->loglevel != 0 && level < tar->loglevel)
+ continue;
+
+ /* Check the category log level */
+ if (category->loglevel != 0 && level < category->loglevel)
+ continue;
+
+ /*
+ * Apply filters here... if that becomes messy we will need to put
+ * filters in a list and each filter will say stop, continue, output
+ */
+ if ((tar->filter_map & DEBUG_FILTER_ALL) != 0) {
+ output = 1;
+ } else if ((tar->filter_map & DEBUG_FILTER_IMSI) != 0
+ && debug_context.subscr && strcmp(debug_context.subscr->imsi, tar->imsi_filter) == 0) {
+ output = 1;
+ }
+
+ if (output) {
+ /* FIXME: copying the va_list is an ugly workaround against a bug
+ * hidden somewhere in _output. If we do not copy here, the first
+ * call to _output() will corrupt the va_list contents, and any
+ * further _output() calls with the same va_list will segfault */
+ va_list bp;
+ va_copy(bp, ap);
+ _output(tar, subsys, file, line, cont, format, bp);
+ va_end(bp);
}
- fprintf(outfd, "<%4.4x> %s:%d ", subsys, file, line);
}
- vfprintf(outfd, format, ap);
- fprintf(outfd, "\033[0;m");
+}
+
+void debugp(unsigned int subsys, char *file, int line, int cont, const char *format, ...)
+{
+ va_list ap;
+ va_start(ap, format);
+ _debugp(subsys, LOGL_DEBUG, file, line, cont, format, ap);
va_end(ap);
+}
+
+void debugp2(unsigned int subsys, unsigned int level, char *file, int line, int cont, const char *format, ...)
+{
+ va_list ap;
- fflush(outfd);
+ va_start(ap, format);
+ _debugp(subsys, level, file, line, cont, format, ap);
+ va_end(ap);
}
static char hexd_buff[4096];
@@ -163,3 +331,122 @@ char *hexdump(const unsigned char *buf, int len)
return hexd_buff;
}
+
+
+void debug_add_target(struct debug_target *target)
+{
+ llist_add_tail(&target->entry, &target_list);
+}
+
+void debug_del_target(struct debug_target *target)
+{
+ llist_del(&target->entry);
+}
+
+void debug_reset_context(void)
+{
+ memset(&debug_context, 0, sizeof(debug_context));
+}
+
+/* currently we are not reffing these */
+void debug_set_context(int ctx, void *value)
+{
+ switch (ctx) {
+ case BSC_CTX_LCHAN:
+ debug_context.lchan = (struct gsm_lchan *) value;
+ break;
+ case BSC_CTX_SUBSCR:
+ debug_context.subscr = (struct gsm_subscriber *) value;
+ break;
+ case BSC_CTX_BTS:
+ debug_context.bts = (struct gsm_bts *) value;
+ break;
+ case BSC_CTX_SCCP:
+ break;
+ default:
+ break;
+ }
+}
+
+void debug_set_imsi_filter(struct debug_target *target, const char *imsi)
+{
+ if (imsi) {
+ target->filter_map |= DEBUG_FILTER_IMSI;
+ target->imsi_filter = talloc_strdup(target, imsi);
+ } else if (target->imsi_filter) {
+ target->filter_map &= ~DEBUG_FILTER_IMSI;
+ talloc_free(target->imsi_filter);
+ target->imsi_filter = NULL;
+ }
+}
+
+void debug_set_all_filter(struct debug_target *target, int all)
+{
+ if (all)
+ target->filter_map |= DEBUG_FILTER_ALL;
+ else
+ target->filter_map &= ~DEBUG_FILTER_ALL;
+}
+
+void debug_set_use_color(struct debug_target *target, int use_color)
+{
+ target->use_color = use_color;
+}
+
+void debug_set_print_timestamp(struct debug_target *target, int print_timestamp)
+{
+ target->print_timestamp = print_timestamp;
+}
+
+void debug_set_log_level(struct debug_target *target, int log_level)
+{
+ target->loglevel = log_level;
+}
+
+void debug_set_category_filter(struct debug_target *target, int category, int enable, int level)
+{
+ if (category >= Debug_LastEntry)
+ return;
+ target->categories[category].enabled = !!enable;
+ target->categories[category].loglevel = level;
+}
+
+static void _stderr_output(struct debug_target *target, const char *log)
+{
+ fprintf(target->tgt_stdout.out, "%s", log);
+ fflush(target->tgt_stdout.out);
+}
+
+struct debug_target *debug_target_create(void)
+{
+ struct debug_target *target;
+
+ target = talloc_zero(tall_dbg_ctx, struct debug_target);
+ if (!target)
+ return NULL;
+
+ INIT_LLIST_HEAD(&target->entry);
+ memcpy(target->categories, default_categories, sizeof(default_categories));
+ target->use_color = 1;
+ target->print_timestamp = 0;
+ target->loglevel = 0;
+ return target;
+}
+
+struct debug_target *debug_target_create_stderr(void)
+{
+ struct debug_target *target;
+
+ target = debug_target_create();
+ if (!target)
+ return NULL;
+
+ target->tgt_stdout.out = stderr;
+ target->output = _stderr_output;
+ return target;
+}
+
+void debug_init(void)
+{
+ tall_dbg_ctx = talloc_named_const(NULL, 1, "debug");
+}
diff --git a/openbsc/src/e1_config.c b/openbsc/src/e1_config.c
index 62bacf2ca..6a2abd85b 100644
--- a/openbsc/src/e1_config.c
+++ b/openbsc/src/e1_config.c
@@ -10,6 +10,7 @@
#include <openbsc/misdn.h>
#include <openbsc/ipaccess.h>
#include <openbsc/talloc.h>
+#include <openbsc/debug.h>
#define SAPI_L2ML 0
#define SAPI_OML 62
@@ -25,7 +26,7 @@ int e1_reconfig_ts(struct gsm_bts_trx_ts *ts)
struct e1inp_line *line;
struct e1inp_ts *e1_ts;
- printf("e1_reconfig_ts(%u,%u,%u)\n", ts->trx->bts->nr, ts->trx->nr, ts->nr);
+ DEBUGP(DMI, "e1_reconfig_ts(%u,%u,%u)\n", ts->trx->bts->nr, ts->trx->nr, ts->nr);
if (!e1_link->e1_ts)
return 0;
@@ -87,7 +88,7 @@ int e1_reconfig_bts(struct gsm_bts *bts)
struct e1inp_sign_link *oml_link;
struct gsm_bts_trx *trx;
- printf("e1_reconfig_bts(%u)\n", bts->nr);
+ DEBUGP(DMI, "e1_reconfig_bts(%u)\n", bts->nr);
if (!e1_link->e1_ts)
return -EINVAL;
diff --git a/openbsc/src/e1_input.c b/openbsc/src/e1_input.c
index e46533838..c894fe40f 100644
--- a/openbsc/src/e1_input.c
+++ b/openbsc/src/e1_input.c
@@ -52,6 +52,7 @@
#include <openbsc/trau_frame.h>
#include <openbsc/trau_mux.h>
#include <openbsc/talloc.h>
+#include <openbsc/signal.h>
#include <openbsc/misdn.h>
#define NUM_E1_TS 32
@@ -233,10 +234,16 @@ int abis_rsl_sendmsg(struct msgb *msg)
msg->l2h = msg->data;
- if (!msg->trx || !msg->trx->rsl_link) {
- fprintf(stderr, "rsl_sendmsg: msg->trx == NULL\n");
+ if (!msg->trx) {
+ LOGP(DRSL, LOGL_ERROR, "rsl_sendmsg: msg->trx == NULL: %s\n",
+ hexdump(msg->data, msg->len));
talloc_free(msg);
return -EINVAL;
+ } else if (!msg->trx->rsl_link) {
+ LOGP(DRSL, LOGL_ERROR, "rsl_sendmsg: msg->trx->rsl_link == NULL: %s\n",
+ hexdump(msg->data, msg->len));
+ talloc_free(msg);
+ return -EIO;
}
sign_link = msg->trx->rsl_link;
@@ -263,7 +270,7 @@ int _abis_nm_sendmsg(struct msgb *msg)
msg->l2h = msg->data;
if (!msg->trx || !msg->trx->bts || !msg->trx->bts->oml_link) {
- fprintf(stderr, "nm_sendmsg: msg->trx == NULL\n");
+ LOGP(DRSL, LOGL_ERROR, "nm_sendmsg: msg->trx == NULL\n");
return -EINVAL;
}
@@ -305,7 +312,7 @@ int e1inp_ts_config(struct e1inp_ts *ts, struct e1inp_line *line,
subch_demux_init(&ts->trau.demux);
break;
default:
- fprintf(stderr, "unsupported E1 timeslot type %u\n",
+ LOGP(DMI, LOGL_ERROR, "unsupported E1 timeslot type %u\n",
ts->type);
return -EINVAL;
}
@@ -430,10 +437,12 @@ int e1inp_rx_ts(struct e1inp_ts *ts, struct msgb *msg,
write_pcap_packet(PCAP_INPUT, sapi, tei, msg);
link = e1inp_lookup_sign_link(ts, tei, sapi);
if (!link) {
- fprintf(stderr, "didn't find signalling link for "
+ LOGP(DMI, LOGL_ERROR, "didn't find signalling link for "
"tei %d, sapi %d\n", tei, sapi);
return -EINVAL;
}
+
+ debug_set_context(BSC_CTX_BTS, link->trx->bts);
switch (link->type) {
case E1INP_SIGN_OML:
msg->trx = link->trx;
@@ -445,7 +454,7 @@ int e1inp_rx_ts(struct e1inp_ts *ts, struct msgb *msg,
break;
default:
ret = -EINVAL;
- fprintf(stderr, "unknown link type %u\n", link->type);
+ LOGP(DMI, LOGL_ERROR, "unknown link type %u\n", link->type);
break;
}
break;
@@ -454,7 +463,7 @@ int e1inp_rx_ts(struct e1inp_ts *ts, struct msgb *msg,
break;
default:
ret = -EINVAL;
- fprintf(stderr, "unknown TS type %u\n", ts->type);
+ LOGP(DMI, LOGL_ERROR, "unknown TS type %u\n", ts->type);
break;
}
@@ -491,7 +500,7 @@ struct msgb *e1inp_tx_ts(struct e1inp_ts *e1i_ts,
msgb_put(msg, 40);
break;
default:
- fprintf(stderr, "unsupported E1 TS type %u\n", e1i_ts->type);
+ LOGP(DMI, LOGL_ERROR, "unsupported E1 TS type %u\n", e1i_ts->type);
return NULL;
}
return msg;
@@ -523,8 +532,24 @@ int e1inp_line_update(struct e1inp_line *line)
return mi_e1_line_update(line);
}
+static int e1i_sig_cb(unsigned int subsys, unsigned int signal,
+ void *handler_data, void *signal_data)
+{
+ if (subsys != SS_GLOBAL ||
+ signal != S_GLOBAL_SHUTDOWN)
+ return 0;
+
+ if (pcap_fd) {
+ close(pcap_fd);
+ pcap_fd = -1;
+ }
+
+ return 0;
+}
+
static __attribute__((constructor)) void on_dso_load_e1_inp(void)
{
tall_sigl_ctx = talloc_named_const(tall_bsc_ctx, 1,
"e1inp_sign_link");
+ register_signal_handler(SS_GLOBAL, e1i_sig_cb, NULL);
}
diff --git a/openbsc/src/gsm_04_08.c b/openbsc/src/gsm_04_08.c
index 1f8235411..61eba2c5a 100644
--- a/openbsc/src/gsm_04_08.c
+++ b/openbsc/src/gsm_04_08.c
@@ -32,6 +32,7 @@
#include <openbsc/db.h>
#include <openbsc/msgb.h>
+#include <openbsc/bitvec.h>
#include <openbsc/tlv.h>
#include <openbsc/debug.h>
#include <openbsc/gsm_data.h>
@@ -49,6 +50,7 @@
#include <openbsc/talloc.h>
#include <openbsc/transaction.h>
#include <openbsc/ussd.h>
+#include <openbsc/silent_call.h>
#define GSM_MAX_FACILITY 128
#define GSM_MAX_SSVERSION 128
@@ -56,8 +58,6 @@
void *tall_locop_ctx;
-extern int ipacc_rtp_direct;
-
static const struct tlv_definition rsl_att_tlvdef = {
.def = {
[GSM48_IE_MOBILE_ID] = { TLV_TYPE_TLV },
@@ -166,63 +166,6 @@ static const char *rr_cause_name(u_int8_t cause)
return strbuf;
}
-static void parse_meas_rep(struct gsm_meas_rep *rep, const u_int8_t *data,
- int len)
-{
- memset(rep, 0, sizeof(*rep));
-
- if (data[0] & 0x80)
- rep->flags |= MEAS_REP_F_BA1;
- if (data[0] & 0x40)
- rep->flags |= MEAS_REP_F_DTX;
- if ((data[1] & 0x40) == 0x00)
- rep->flags |= MEAS_REP_F_VALID;
-
- rep->rxlev_full = data[0] & 0x3f;
- rep->rxlev_sub = data[1] & 0x3f;
- rep->rxqual_full = (data[3] >> 4) & 0x7;
- rep->rxqual_sub = (data[3] >> 1) & 0x7;
- rep->num_cell = data[4] >> 6 | ((data[3] & 0x01) << 2);
- if (rep->num_cell < 1)
- return;
-
- /* an encoding nightmare in perfection */
-
- rep->cell[0].rxlev = data[4] & 0x3f;
- rep->cell[0].bcch_freq = data[5] >> 2;
- rep->cell[0].bsic = ((data[5] & 0x03) << 3) | (data[6] >> 5);
- if (rep->num_cell < 2)
- return;
-
- rep->cell[1].rxlev = ((data[6] & 0x1f) << 1) | (data[7] >> 7);
- rep->cell[1].bcch_freq = (data[7] >> 2) & 0x1f;
- rep->cell[1].bsic = ((data[7] & 0x03) << 4) | (data[8] >> 4);
- if (rep->num_cell < 3)
- return;
-
- rep->cell[2].rxlev = ((data[8] & 0x0f) << 2) | (data[9] >> 6);
- rep->cell[2].bcch_freq = (data[9] >> 1) & 0x1f;
- rep->cell[2].bsic = ((data[9] & 0x01) << 6) | (data[10] >> 3);
- if (rep->num_cell < 4)
- return;
-
- rep->cell[3].rxlev = ((data[10] & 0x07) << 3) | (data[11] >> 5);
- rep->cell[3].bcch_freq = data[11] & 0x1f;
- rep->cell[3].bsic = data[12] >> 2;
- if (rep->num_cell < 5)
- return;
-
- rep->cell[4].rxlev = ((data[12] & 0x03) << 4) | (data[13] >> 4);
- rep->cell[4].bcch_freq = ((data[13] & 0xf) << 1) | (data[14] >> 7);
- rep->cell[4].bsic = (data[14] >> 1) & 0x3f;
- if (rep->num_cell < 6)
- return;
-
- rep->cell[5].rxlev = ((data[14] & 0x01) << 5) | (data[15] >> 3);
- rep->cell[5].bcch_freq = ((data[15] & 0x07) << 2) | (data[16] >> 6);
- rep->cell[5].bsic = data[16] & 0x3f;
-}
-
int gsm0408_loc_upd_acc(struct gsm_lchan *lchan, u_int32_t tmsi);
static int gsm48_tx_simple(struct gsm_lchan *lchan,
u_int8_t pdisc, u_int8_t msg_type);
@@ -234,12 +177,6 @@ struct gsm_lai {
u_int16_t lac;
};
-static int reject_cause = 0;
-void gsm0408_set_reject_cause(int cause)
-{
- reject_cause = cause;
-}
-
static u_int32_t new_callref = 0x80000001;
static int authorize_subscriber(struct gsm_loc_updating_operation *loc,
@@ -298,11 +235,18 @@ static int gsm0408_authorize(struct gsm_lchan *lchan, struct msgb *msg)
db_subscriber_alloc_tmsi(lchan->subscr);
release_loc_updating_req(lchan);
rc = gsm0408_loc_upd_acc(msg->lchan, lchan->subscr->tmsi);
+ if (lchan->ts->trx->bts->network->send_mm_info) {
+ /* send MM INFO with network name */
+ rc = gsm48_tx_mm_info(msg->lchan);
+ }
+
/* call subscr_update after putting the loc_upd_acc
* in the transmit queue, since S_SUBSCR_ATTACHED might
* trigger further action like SMS delivery */
subscr_update(lchan->subscr, msg->trx->bts,
GSM_SUBSCRIBER_UPDATE_ATTACHED);
+ /* try to close channel ASAP */
+ lchan_auto_release(lchan);
return rc;
}
@@ -433,18 +377,29 @@ static int decode_bearer_cap(struct gsm_mncc_bearer_cap *bcap,
bcap->coding = (lv[1] & 0x10) >> 4;
bcap->radio = (lv[1] & 0x60) >> 5;
- i = 1;
- s = 0;
- while(!(lv[i] & 0x80)) {
- i++; /* octet 3a etc */
- if (in_len < i)
- return 0;
- bcap->speech_ver[s++] = lv[i] & 0x0f;
- bcap->speech_ver[s] = -1; /* end of list */
- if (i == 2) /* octet 3a */
- bcap->speech_ctm = (lv[i] & 0x20) >> 5;
- if (s == 7) /* maximum speech versions + end of list */
- return 0;
+ if (bcap->transfer == GSM_MNCC_BCAP_SPEECH) {
+ i = 1;
+ s = 0;
+ while(!(lv[i] & 0x80)) {
+ i++; /* octet 3a etc */
+ if (in_len < i)
+ return 0;
+ bcap->speech_ver[s++] = lv[i] & 0x0f;
+ bcap->speech_ver[s] = -1; /* end of list */
+ if (i == 2) /* octet 3a */
+ bcap->speech_ctm = (lv[i] & 0x20) >> 5;
+ if (s == 7) /* maximum speech versions + end of list */
+ return 0;
+ }
+ } else {
+ i = 1;
+ while (!(lv[i] & 0x80)) {
+ i++; /* octet 3a etc */
+ if (in_len < i)
+ return 0;
+ /* ignore them */
+ }
+ /* FIXME: implement OCTET 4+ parsing */
}
return 0;
@@ -455,21 +410,24 @@ static int encode_bearer_cap(struct msgb *msg, int lv_only,
const struct gsm_mncc_bearer_cap *bcap)
{
u_int8_t lv[32 + 1];
- int i, s;
+ int i = 1, s;
lv[1] = bcap->transfer;
lv[1] |= bcap->mode << 3;
lv[1] |= bcap->coding << 4;
lv[1] |= bcap->radio << 5;
- i = 1;
- for (s = 0; bcap->speech_ver[s] >= 0; s++) {
- i++; /* octet 3a etc */
- lv[i] = bcap->speech_ver[s];
- if (i == 2) /* octet 3a */
- lv[i] |= bcap->speech_ctm << 5;
+ if (bcap->transfer == GSM_MNCC_BCAP_SPEECH) {
+ for (s = 0; bcap->speech_ver[s] >= 0; s++) {
+ i++; /* octet 3a etc */
+ lv[i] = bcap->speech_ver[s];
+ if (i == 2) /* octet 3a */
+ lv[i] |= bcap->speech_ctm << 5;
+ }
+ lv[i] |= 0x80; /* last IE of octet 3 etc */
+ } else {
+ /* FIXME: implement OCTET 4+ encoding */
}
- lv[i] |= 0x80; /* last IE of octet 3 etc */
lv[0] = i;
if (lv_only)
@@ -861,6 +819,7 @@ static int encode_more(struct msgb *msg)
/* Chapter 9.2.14 : Send LOCATION UPDATING REJECT */
int gsm0408_loc_upd_rej(struct gsm_lchan *lchan, u_int8_t cause)
{
+ struct gsm_bts *bts = lchan->ts->trx->bts;
struct msgb *msg = gsm48_msgb_alloc();
struct gsm48_hdr *gh;
@@ -871,7 +830,12 @@ int gsm0408_loc_upd_rej(struct gsm_lchan *lchan, u_int8_t cause)
gh->msg_type = GSM48_MT_MM_LOC_UPD_REJECT;
gh->data[0] = cause;
- DEBUGP(DMM, "-> LOCATION UPDATING REJECT on channel: %d\n", lchan->nr);
+ LOGP(DMM, LOGL_INFO, "Subscriber %s: LOCATION UPDATING REJECT "
+ "LAC=%u BTS=%u\n", lchan->subscr ?
+ subscr_name(lchan->subscr) : "unknown",
+ lchan->ts->trx->bts->location_area_code, lchan->ts->trx->bts->nr);
+
+ counter_inc(bts->network->stats.loc_upd_resp.reject);
return gsm48_sendmsg(msg, NULL);
}
@@ -884,7 +848,6 @@ int gsm0408_loc_upd_acc(struct gsm_lchan *lchan, u_int32_t tmsi)
struct gsm48_hdr *gh;
struct gsm48_loc_area_id *lai;
u_int8_t *mid;
- int ret;
msg->lchan = lchan;
@@ -901,12 +864,9 @@ int gsm0408_loc_upd_acc(struct gsm_lchan *lchan, u_int32_t tmsi)
DEBUGP(DMM, "-> LOCATION UPDATE ACCEPT\n");
- ret = gsm48_sendmsg(msg, NULL);
-
- /* send MM INFO with network name */
- ret = gsm48_tx_mm_info(lchan);
+ counter_inc(bts->network->stats.loc_upd_resp.accept);
- return ret;
+ return gsm48_sendmsg(msg, NULL);
}
/* Transmit Chapter 9.2.10 Identity Request */
@@ -940,6 +900,8 @@ static int mm_rx_id_resp(struct msgb *msg)
DEBUGP(DMM, "IDENTITY RESPONSE: mi_type=0x%02x MI(%s)\n",
mi_type, mi_string);
+ dispatch_signal(SS_SUBSCR, S_SUBSCR_IDENTITY, gh->data);
+
switch (mi_type) {
case GSM_MI_TYPE_IMSI:
/* look up subscriber based on IMSI, create if not found */
@@ -971,9 +933,10 @@ static int mm_rx_id_resp(struct msgb *msg)
static void loc_upd_rej_cb(void *data)
{
struct gsm_lchan *lchan = data;
+ struct gsm_bts *bts = lchan->ts->trx->bts;
release_loc_updating_req(lchan);
- gsm0408_loc_upd_rej(lchan, reject_cause);
+ gsm0408_loc_upd_rej(lchan, bts->network->reject_cause);
lchan_auto_release(lchan);
}
@@ -1019,6 +982,20 @@ static int mm_rx_loc_upd_req(struct msgb *msg)
DEBUGPC(DMM, "mi_type=0x%02x MI(%s) type=%s ", mi_type, mi_string,
lupd_name(lu->type));
+ dispatch_signal(SS_SUBSCR, S_SUBSCR_IDENTITY, &lu->mi_len);
+
+ switch (lu->type) {
+ case GSM48_LUPD_NORMAL:
+ counter_inc(bts->network->stats.loc_upd_type.normal);
+ break;
+ case GSM48_LUPD_IMSI_ATT:
+ counter_inc(bts->network->stats.loc_upd_type.attach);
+ break;
+ case GSM48_LUPD_PERIODIC:
+ counter_inc(bts->network->stats.loc_upd_type.periodic);
+ break;
+ }
+
/*
* Pseudo Spoof detection: Just drop a second/concurrent
* location updating request.
@@ -1198,20 +1175,19 @@ int gsm48_tx_mm_info(struct gsm_lchan *lchan)
}
/* Section 9.2.2 */
-int gsm48_tx_mm_auth_req(struct gsm_lchan *lchan, u_int8_t *rand)
+int gsm48_tx_mm_auth_req(struct gsm_lchan *lchan, u_int8_t *rand, int key_seq)
{
struct msgb *msg = gsm48_msgb_alloc();
struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
struct gsm48_auth_req *ar = (struct gsm48_auth_req *) msgb_put(msg, sizeof(*ar));
- DEBUGP(DMM, "-> AUTH REQ\n");
+ DEBUGP(DMM, "-> AUTH REQ (rand = %s)\n", hexdump(rand, 16));
msg->lchan = lchan;
gh->proto_discr = GSM48_PDISC_MM;
gh->msg_type = GSM48_MT_MM_AUTH_REQ;
- /* Key Sequence: FIXME fixed to 0 */
- ar->key_seq = 0;
+ ar->key_seq = key_seq;
/* 16 bytes RAND parameters */
if (rand)
@@ -1302,6 +1278,8 @@ static int gsm48_rx_mm_serv_req(struct msgb *msg)
DEBUGPC(DMM, "serv_type=0x%02x mi_type=0x%02x M(%s)\n",
req->cm_service_type, mi_type, mi_string);
+ dispatch_signal(SS_SUBSCR, S_SUBSCR_IDENTITY, (classmark2 + classmark2_len));
+
if (is_siemens_bts(bts))
send_siemens_mrpci(msg->lchan, classmark2-1);
@@ -1315,7 +1293,9 @@ static int gsm48_rx_mm_serv_req(struct msgb *msg)
if (!msg->lchan->subscr)
msg->lchan->subscr = subscr;
- else if (msg->lchan->subscr != subscr) {
+ else if (msg->lchan->subscr == subscr)
+ subscr_put(subscr); /* lchan already has a ref, don't need another one */
+ else {
DEBUGP(DMM, "<- CM Channel already owned by someone else?\n");
subscr_put(subscr);
}
@@ -1341,6 +1321,8 @@ static int gsm48_rx_mm_imsi_detach_ind(struct msgb *msg)
DEBUGP(DMM, "IMSI DETACH INDICATION: mi_type=0x%02x MI(%s): ",
mi_type, mi_string);
+ counter_inc(bts->network->stats.loc_upd_type.detach);
+
switch (mi_type) {
case GSM_MI_TYPE_TMSI:
subscr = subscr_get_by_tmsi(bts->network,
@@ -1362,8 +1344,7 @@ static int gsm48_rx_mm_imsi_detach_ind(struct msgb *msg)
if (subscr) {
subscr_update(subscr, msg->trx->bts,
GSM_SUBSCRIBER_UPDATE_DETACHED);
- DEBUGP(DMM, "Subscriber: %s\n",
- subscr->name ? subscr->name : subscr->imsi);
+ DEBUGP(DMM, "Subscriber: %s\n", subscr_name(subscr));
subscr->equipment.classmark1 = idi->classmark1;
db_sync_equipment(&subscr->equipment);
@@ -1372,6 +1353,12 @@ static int gsm48_rx_mm_imsi_detach_ind(struct msgb *msg)
} else
DEBUGP(DMM, "Unknown Subscriber ?!?\n");
+ /* FIXME: iterate over all transactions and release them,
+ * imagine an IMSI DETACH happening during an active call! */
+
+ /* subscriber is detached: should we release lchan? */
+ lchan_auto_release(msg->lchan);
+
return 0;
}
@@ -1407,7 +1394,7 @@ static int gsm0408_rcv_mm(struct msgb *msg)
case GSM48_MT_MM_TMSI_REALL_COMPL:
DEBUGP(DMM, "TMSI Reallocation Completed. Subscriber: %s\n",
msg->lchan->subscr ?
- msg->lchan->subscr->imsi :
+ subscr_name(msg->lchan->subscr) :
"unknown subscriber");
break;
case GSM48_MT_MM_IMSI_DETACH_IND:
@@ -1420,7 +1407,7 @@ static int gsm0408_rcv_mm(struct msgb *msg)
DEBUGP(DMM, "AUTHENTICATION RESPONSE: Not implemented\n");
break;
default:
- fprintf(stderr, "Unknown GSM 04.08 MM msg type 0x%02x\n",
+ LOGP(DMM, LOGL_NOTICE, "Unknown GSM 04.08 MM msg type 0x%02x\n",
gh->msg_type);
break;
}
@@ -1528,26 +1515,13 @@ static int gsm48_rx_rr_status(struct msgb *msg)
static int gsm48_rx_rr_meas_rep(struct msgb *msg)
{
- struct gsm48_hdr *gh = msgb_l3(msg);
- unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
- static struct gsm_meas_rep meas_rep;
-
- DEBUGP(DMEAS, "MEASUREMENT REPORT ");
- parse_meas_rep(&meas_rep, gh->data, payload_len);
- if (meas_rep.flags & MEAS_REP_F_DTX)
- DEBUGPC(DMEAS, "DTX ");
- if (meas_rep.flags & MEAS_REP_F_BA1)
- DEBUGPC(DMEAS, "BA1 ");
- if (!(meas_rep.flags & MEAS_REP_F_VALID))
- DEBUGPC(DMEAS, "NOT VALID ");
- else
- DEBUGPC(DMEAS, "FULL(lev=%u, qual=%u) SUB(lev=%u, qual=%u) ",
- meas_rep.rxlev_full, meas_rep.rxqual_full, meas_rep.rxlev_sub,
- meas_rep.rxqual_sub);
-
- DEBUGPC(DMEAS, "NUM_NEIGH=%u\n", meas_rep.num_cell);
+ struct gsm_meas_rep *meas_rep = lchan_next_meas_rep(msg->lchan);
- /* FIXME: put the results somwhere */
+ /* This shouldn't actually end up here, as RSL treats
+ * L3 Info of 08.58 MEASUREMENT REPORT different by calling
+ * directly into gsm48_parse_meas_rep */
+ DEBUGP(DMEAS, "DIRECT GSM48 MEASUREMENT REPORT ?!? ");
+ gsm48_parse_meas_rep(meas_rep, msg);
return 0;
}
@@ -1569,6 +1543,33 @@ static int gsm48_rx_rr_app_info(struct msgb *msg)
return db_apdu_blob_store(msg->lchan->subscr, apdu_id_flags, apdu_len, apdu_data);
}
+/* Chapter 9.1.16 Handover complete */
+static int gsm48_rx_rr_ho_compl(struct msgb *msg)
+{
+ struct gsm48_hdr *gh = msgb_l3(msg);
+
+ DEBUGP(DRR, "HANDOVER COMPLETE cause = %s\n",
+ rr_cause_name(gh->data[0]));
+
+ dispatch_signal(SS_LCHAN, S_LCHAN_HANDOVER_COMPL, msg->lchan);
+ /* FIXME: release old channel */
+
+ return 0;
+}
+
+/* Chapter 9.1.17 Handover Failure */
+static int gsm48_rx_rr_ho_fail(struct msgb *msg)
+{
+ struct gsm48_hdr *gh = msgb_l3(msg);
+
+ DEBUGP(DRR, "HANDOVER FAILED cause = %s\n",
+ rr_cause_name(gh->data[0]));
+
+ dispatch_signal(SS_LCHAN, S_LCHAN_HANDOVER_FAIL, msg->lchan);
+ /* FIXME: release allocated new channel */
+
+ return 0;
+}
/* Receive a GSM 04.08 Radio Resource (RR) message */
static int gsm0408_rcv_rr(struct msgb *msg)
@@ -1602,9 +1603,15 @@ static int gsm0408_rcv_rr(struct msgb *msg)
DEBUGP(DRR, "CIPHERING MODE COMPLETE\n");
/* FIXME: check for MI (if any) */
break;
+ case GSM48_MT_RR_HANDO_COMPL:
+ rc = gsm48_rx_rr_ho_compl(msg);
+ break;
+ case GSM48_MT_RR_HANDO_FAIL:
+ rc = gsm48_rx_rr_ho_fail(msg);
+ break;
default:
- fprintf(stderr, "Unimplemented GSM 04.08 RR msg type 0x%02x\n",
- gh->msg_type);
+ LOGP(DRR, LOGL_NOTICE, "Unimplemented "
+ "GSM 04.08 RR msg type 0x%02x\n", gh->msg_type);
break;
}
@@ -1812,13 +1819,16 @@ static int setup_trig_pag_evt(unsigned int hooknum, unsigned int event,
return 0;
}
+static int tch_recv_mncc(struct gsm_network *net, u_int32_t callref, int enable);
+
/* some other part of the code sends us a signal */
static int handle_abisip_signal(unsigned int subsys, unsigned int signal,
void *handler_data, void *signal_data)
{
struct gsm_lchan *lchan = signal_data;
- struct gsm_bts_trx_ts *ts;
int rc;
+ struct gsm_network *net;
+ struct gsm_trans *trans;
if (subsys != SS_ABISIP)
return 0;
@@ -1827,56 +1837,21 @@ static int handle_abisip_signal(unsigned int subsys, unsigned int signal,
if (ipacc_rtp_direct)
return 0;
- ts = lchan->ts;
-
switch (signal) {
case S_ABISIP_CRCX_ACK:
- /* the BTS has successfully bound a TCH to a local ip/port,
- * which means we can connect our UDP socket to it */
- if (ts->abis_ip.rtp_socket) {
- rtp_socket_free(ts->abis_ip.rtp_socket);
- ts->abis_ip.rtp_socket = NULL;
- }
-
- ts->abis_ip.rtp_socket = rtp_socket_create();
- if (!ts->abis_ip.rtp_socket)
- goto out_err;
-
- rc = rtp_socket_connect(ts->abis_ip.rtp_socket,
- ts->abis_ip.bound_ip,
- ts->abis_ip.bound_port);
- if (rc < 0)
- goto out_err;
- break;
- case S_ABISIP_DLCX_IND:
- /* the BTS tells us a RTP stream has been disconnected */
- if (ts->abis_ip.rtp_socket) {
- rtp_socket_free(ts->abis_ip.rtp_socket);
- ts->abis_ip.rtp_socket = NULL;
+ /* check if any transactions on this lchan still have
+ * a tch_recv_mncc request pending */
+ net = lchan->ts->trx->bts->network;
+ llist_for_each_entry(trans, &net->trans_list, entry) {
+ if (trans->lchan == lchan && trans->tch_recv) {
+ DEBUGP(DCC, "pending tch_recv_mncc request\n");
+ tch_recv_mncc(net, trans->callref, 1);
+ }
}
break;
}
return 0;
-out_err:
- /* FIXME: do something */
- return 0;
-}
-
-/* bind rtp proxy to local IP/port and tell BTS to connect to it */
-static int ipacc_connect_proxy_bind(struct gsm_lchan *lchan)
-{
- struct gsm_bts_trx_ts *ts = lchan->ts;
- struct rtp_socket *rs = ts->abis_ip.rtp_socket;
- int rc;
-
- rc = rsl_ipacc_mdcx(lchan, ntohl(rs->rtp.sin_local.sin_addr.s_addr),
- ntohs(rs->rtp.sin_local.sin_port),
- ts->abis_ip.conn_id,
- /* FIXME: use RTP payload of bound socket, not BTS*/
- ts->abis_ip.rtp_payload2);
-
- return rc;
}
/* map two ipaccess RTP streams onto each other */
@@ -1884,7 +1859,6 @@ static int tch_map(struct gsm_lchan *lchan, struct gsm_lchan *remote_lchan)
{
struct gsm_bts *bts = lchan->ts->trx->bts;
struct gsm_bts *remote_bts = remote_lchan->ts->trx->bts;
- struct gsm_bts_trx_ts *ts;
int rc;
DEBUGP(DCC, "Setting up TCH map between (bts=%u,trx=%u,ts=%u) and (bts=%u,trx=%u,ts=%u)\n",
@@ -1895,33 +1869,31 @@ static int tch_map(struct gsm_lchan *lchan, struct gsm_lchan *remote_lchan)
DEBUGP(DCC, "Cannot switch calls between different BTS types yet\n");
return -EINVAL;
}
-
+
+ // todo: map between different bts types
switch (bts->type) {
case GSM_BTS_TYPE_NANOBTS:
if (!ipacc_rtp_direct) {
/* connect the TCH's to our RTP proxy */
- rc = ipacc_connect_proxy_bind(lchan);
+ rc = rsl_ipacc_mdcx_to_rtpsock(lchan);
if (rc < 0)
return rc;
- rc = ipacc_connect_proxy_bind(remote_lchan);
+ rc = rsl_ipacc_mdcx_to_rtpsock(remote_lchan);
+#warning do we need a check of rc here?
/* connect them with each other */
- rtp_socket_proxy(lchan->ts->abis_ip.rtp_socket,
- remote_lchan->ts->abis_ip.rtp_socket);
+ rtp_socket_proxy(lchan->abis_ip.rtp_socket,
+ remote_lchan->abis_ip.rtp_socket);
} else {
/* directly connect TCH RTP streams to each other */
- ts = remote_lchan->ts;
- rc = rsl_ipacc_mdcx(lchan, ts->abis_ip.bound_ip,
- ts->abis_ip.bound_port,
- lchan->ts->abis_ip.conn_id,
- ts->abis_ip.rtp_payload2);
+ rc = rsl_ipacc_mdcx(lchan, remote_lchan->abis_ip.bound_ip,
+ remote_lchan->abis_ip.bound_port,
+ remote_lchan->abis_ip.rtp_payload2);
if (rc < 0)
return rc;
- ts = lchan->ts;
- rc = rsl_ipacc_mdcx(remote_lchan, ts->abis_ip.bound_ip,
- ts->abis_ip.bound_port,
- remote_lchan->ts->abis_ip.conn_id,
- ts->abis_ip.rtp_payload2);
+ rc = rsl_ipacc_mdcx(remote_lchan, lchan->abis_ip.bound_ip,
+ lchan->abis_ip.bound_port,
+ lchan->abis_ip.rtp_payload2);
}
break;
case GSM_BTS_TYPE_BS11:
@@ -1929,8 +1901,7 @@ static int tch_map(struct gsm_lchan *lchan, struct gsm_lchan *remote_lchan)
break;
default:
DEBUGP(DCC, "Unknown BTS type %u\n", bts->type);
- rc = -EINVAL;
- break;
+ return -EINVAL;
}
return 0;
@@ -1952,45 +1923,61 @@ static int tch_bridge(struct gsm_network *net, u_int32_t *refs)
return tch_map(trans1->lchan, trans2->lchan);
}
-/* enable receive of channels to upqueue */
-static int tch_recv(struct gsm_network *net, struct gsm_mncc *data, int enable)
+/* enable receive of channels to MNCC upqueue */
+static int tch_recv_mncc(struct gsm_network *net, u_int32_t callref, int enable)
{
struct gsm_trans *trans;
+ struct gsm_lchan *lchan;
+ struct gsm_bts *bts;
+ int rc;
/* Find callref */
- trans = trans_find_by_callref(net, data->callref);
+ trans = trans_find_by_callref(net, callref);
if (!trans)
return -EIO;
if (!trans->lchan)
return 0;
+ lchan = trans->lchan;
+ bts = lchan->ts->trx->bts;
- // todo IPACCESS
- if (enable)
- return trau_recv_lchan(trans->lchan, data->callref);
- return trau_mux_unmap(NULL, data->callref);
-}
-
-/* send a frame to channel */
-static int tch_frame(struct gsm_network *net, struct gsm_trau_frame *frame)
-{
- struct gsm_trans *trans;
-
- /* Find callref */
- trans = trans_find_by_callref(net, frame->callref);
- if (!trans)
- return -EIO;
- if (!trans->lchan)
- return 0;
- if (trans->lchan->type != GSM_LCHAN_TCH_F &&
- trans->lchan->type != GSM_LCHAN_TCH_H)
- return 0;
+ switch (bts->type) {
+ case GSM_BTS_TYPE_NANOBTS:
+ if (ipacc_rtp_direct) {
+ DEBUGP(DCC, "Error: RTP proxy is disabled\n");
+ return -EINVAL;
+ }
+ /* in case, we don't have a RTP socket yet, we note this
+ * in the transaction and try later */
+ if (!lchan->abis_ip.rtp_socket) {
+ trans->tch_recv = enable;
+ DEBUGP(DCC, "queue tch_recv_mncc request (%d)\n", enable);
+ return 0;
+ }
+ if (enable) {
+ /* connect the TCH's to our RTP proxy */
+ rc = rsl_ipacc_mdcx_to_rtpsock(lchan);
+ if (rc < 0)
+ return rc;
+ /* assign socket to application interface */
+ rtp_socket_upstream(lchan->abis_ip.rtp_socket,
+ net, callref);
+ } else
+ rtp_socket_upstream(lchan->abis_ip.rtp_socket,
+ net, 0);
+ break;
+ case GSM_BTS_TYPE_BS11:
+ if (enable)
+ return trau_recv_lchan(lchan, callref);
+ return trau_mux_unmap(NULL, callref);
+ break;
+ default:
+ DEBUGP(DCC, "Unknown BTS type %u\n", bts->type);
+ return -EINVAL;
+ }
- // todo IPACCESS
- return trau_send_lchan(trans->lchan,
- (struct decoded_trau_frame *)frame->data);
+ return 0;
}
-
static int gsm48_cc_rx_status_enq(struct gsm_trans *trans, struct msgb *msg)
{
DEBUGP(DCC, "-> STATUS ENQ\n");
@@ -2163,6 +2150,10 @@ static int gsm48_cc_rx_setup(struct gsm_trans *trans, struct msgb *msg)
new_cc_state(trans, GSM_CSTATE_INITIATED);
+ LOGP(DCC, LOGL_INFO, "Subscriber %s (%s) sends SETUP to %s\n",
+ subscr_name(trans->subscr), trans->subscr->extension,
+ setup.called.number);
+
/* indicate setup to MNCC */
mncc_recvmsg(trans->subscr->net, trans, MNCC_SETUP_IND, &setup);
@@ -2711,6 +2702,9 @@ static int gsm48_cc_rx_release_compl(struct gsm_trans *trans, struct msgb *msg)
case GSM_CSTATE_RELEASE_REQ:
rc = mncc_recvmsg(trans->subscr->net, trans,
MNCC_REL_CNF, &rel);
+ /* FIXME: in case of multiple calls, we can't simply
+ * hang up here ! */
+ lchan_auto_release(msg->lchan);
break;
default:
rc = mncc_recvmsg(trans->subscr->net, trans,
@@ -3217,11 +3211,30 @@ int mncc_send(struct gsm_network *net, int msg_type, void *arg)
case MNCC_BRIDGE:
return tch_bridge(net, arg);
case MNCC_FRAME_DROP:
- return tch_recv(net, arg, 0);
+ return tch_recv_mncc(net, data->callref, 0);
case MNCC_FRAME_RECV:
- return tch_recv(net, arg, 1);
- case GSM_TRAU_FRAME:
- return tch_frame(net, arg);
+ return tch_recv_mncc(net, data->callref, 1);
+ case GSM_TCHF_FRAME:
+ /* Find callref */
+ trans = trans_find_by_callref(net, data->callref);
+ if (!trans)
+ return -EIO;
+ if (!trans->lchan)
+ return 0;
+ if (trans->lchan->type != GSM_LCHAN_TCH_F)
+ return 0;
+ bts = trans->lchan->ts->trx->bts;
+ switch (bts->type) {
+ case GSM_BTS_TYPE_NANOBTS:
+ if (!trans->lchan->abis_ip.rtp_socket)
+ return 0;
+ return rtp_send_frame(trans->lchan->abis_ip.rtp_socket, arg);
+ case GSM_BTS_TYPE_BS11:
+ return trau_send_frame(trans->lchan, arg);
+ default:
+ DEBUGP(DCC, "Unknown BTS type %u\n", bts->type);
+ }
+ return -EINVAL;
}
memset(&rel, 0, sizeof(struct gsm_mncc));
@@ -3487,6 +3500,9 @@ int gsm0408_rcvmsg(struct msgb *msg, u_int8_t link_id)
struct gsm48_hdr *gh = msgb_l3(msg);
u_int8_t pdisc = gh->proto_discr & 0x0f;
int rc = 0;
+
+ if (silent_call_reroute(msg))
+ return silent_call_rx(msg);
switch (pdisc) {
case GSM48_PDISC_CC:
@@ -3503,15 +3519,15 @@ int gsm0408_rcvmsg(struct msgb *msg, u_int8_t link_id)
break;
case GSM48_PDISC_MM_GPRS:
case GSM48_PDISC_SM_GPRS:
- fprintf(stderr, "Unimplemented GSM 04.08 discriminator 0x%02x\n",
- pdisc);
+ LOGP(DRLL, LOGL_NOTICE, "Unimplemented "
+ "GSM 04.08 discriminator 0x%02x\n", pdisc);
break;
case GSM48_PDISC_NC_SS:
rc = handle_rcv_ussd(msg);
break;
default:
- fprintf(stderr, "Unknown GSM 04.08 discriminator 0x%02x\n",
- pdisc);
+ LOGP(DRLL, LOGL_NOTICE, "Unknown "
+ "GSM 04.08 discriminator 0x%02x\n", pdisc);
break;
}
diff --git a/openbsc/src/gsm_04_08_utils.c b/openbsc/src/gsm_04_08_utils.c
index b2fbdc2c3..821bde266 100644
--- a/openbsc/src/gsm_04_08_utils.c
+++ b/openbsc/src/gsm_04_08_utils.c
@@ -258,6 +258,11 @@ static const struct chreq chreq_type_neci1[] = {
{ 0x80, 0xe0, CHREQ_T_PAG_R_ANY_NECI1 },
{ 0x20, 0xf0, CHREQ_T_PAG_R_TCH_F },
{ 0x30, 0xf0, CHREQ_T_PAG_R_TCH_FH },
+ { 0x67, 0xff, CHREQ_T_LMU },
+ { 0x60, 0xf9, CHREQ_T_RESERVED_SDCCH },
+ { 0x61, 0xfb, CHREQ_T_RESERVED_SDCCH },
+ { 0x63, 0xff, CHREQ_T_RESERVED_SDCCH },
+ { 0x7f, 0xff, CHREQ_T_RESERVED_IGNORE },
};
/* If SYSTEM INFORMATION TYPE 4 NECI bit == 0 */
@@ -270,6 +275,11 @@ static const struct chreq chreq_type_neci0[] = {
{ 0x80, 0xe0, CHREQ_T_PAG_R_ANY_NECI0 },
{ 0x20, 0xf0, CHREQ_T_PAG_R_TCH_F },
{ 0x30, 0xf0, CHREQ_T_PAG_R_TCH_FH },
+ { 0x67, 0xff, CHREQ_T_LMU },
+ { 0x60, 0xf9, CHREQ_T_RESERVED_SDCCH },
+ { 0x61, 0xfb, CHREQ_T_RESERVED_SDCCH },
+ { 0x63, 0xff, CHREQ_T_RESERVED_SDCCH },
+ { 0x7f, 0xff, CHREQ_T_RESERVED_IGNORE },
};
static const enum gsm_chan_t ctype_by_chreq[] = {
@@ -286,6 +296,9 @@ static const enum gsm_chan_t ctype_by_chreq[] = {
[CHREQ_T_PAG_R_ANY_NECI0] = GSM_LCHAN_SDCCH,
[CHREQ_T_PAG_R_TCH_F] = GSM_LCHAN_TCH_F,
[CHREQ_T_PAG_R_TCH_FH] = GSM_LCHAN_TCH_F,
+ [CHREQ_T_LMU] = GSM_LCHAN_SDCCH,
+ [CHREQ_T_RESERVED_SDCCH] = GSM_LCHAN_SDCCH,
+ [CHREQ_T_RESERVED_IGNORE] = GSM_LCHAN_UNKNOWN,
};
static const enum gsm_chreq_reason_t reason_by_chreq[] = {
@@ -295,13 +308,16 @@ static const enum gsm_chreq_reason_t reason_by_chreq[] = {
[CHREQ_T_CALL_REEST_TCH_H_DBL] = GSM_CHREQ_REASON_CALL,
[CHREQ_T_SDCCH] = GSM_CHREQ_REASON_OTHER,
[CHREQ_T_TCH_F] = GSM_CHREQ_REASON_OTHER,
- [CHREQ_T_VOICE_CALL_TCH_H] = GSM_CHREQ_REASON_OTHER,
+ [CHREQ_T_VOICE_CALL_TCH_H] = GSM_CHREQ_REASON_CALL,
[CHREQ_T_DATA_CALL_TCH_H] = GSM_CHREQ_REASON_OTHER,
[CHREQ_T_LOCATION_UPD] = GSM_CHREQ_REASON_LOCATION_UPD,
[CHREQ_T_PAG_R_ANY_NECI1] = GSM_CHREQ_REASON_PAG,
[CHREQ_T_PAG_R_ANY_NECI0] = GSM_CHREQ_REASON_PAG,
[CHREQ_T_PAG_R_TCH_F] = GSM_CHREQ_REASON_PAG,
[CHREQ_T_PAG_R_TCH_FH] = GSM_CHREQ_REASON_PAG,
+ [CHREQ_T_LMU] = GSM_CHREQ_REASON_OTHER,
+ [CHREQ_T_RESERVED_SDCCH] = GSM_CHREQ_REASON_OTHER,
+ [CHREQ_T_RESERVED_IGNORE] = GSM_CHREQ_REASON_OTHER,
};
enum gsm_chan_t get_ctype_by_chreq(struct gsm_bts *bts, u_int8_t ra, int neci)
@@ -324,7 +340,7 @@ enum gsm_chan_t get_ctype_by_chreq(struct gsm_bts *bts, u_int8_t ra, int neci)
if ((ra & chr->mask) == chr->val)
return ctype_by_chreq[chr->type];
}
- fprintf(stderr, "Unknown CHANNEL REQUEST RQD 0x%02x\n", ra);
+ LOGP(DRR, LOGL_ERROR, "Unknown CHANNEL REQUEST RQD 0x%02x\n", ra);
return GSM_LCHAN_SDCCH;
}
@@ -347,7 +363,7 @@ enum gsm_chreq_reason_t get_reason_by_chreq(struct gsm_bts *bts, u_int8_t ra, in
if ((ra & chr->mask) == chr->val)
return reason_by_chreq[chr->type];
}
- fprintf(stderr, "Unknown CHANNEL REQUEST REASON 0x%02x\n", ra);
+ LOGP(DRR, LOGL_ERROR, "Unknown CHANNEL REQUEST REASON 0x%02x\n", ra);
return GSM_CHREQ_REASON_OTHER;
}
@@ -459,7 +475,7 @@ int gsm48_handle_paging_resp(struct msgb *msg, struct gsm_subscriber *subscr)
if (!msg->lchan->subscr) {
msg->lchan->subscr = subscr;
} else if (msg->lchan->subscr != subscr) {
- DEBUGP(DRR, "<- Channel already owned by someone else?\n");
+ LOGP(DRR, LOGL_ERROR, "<- Channel already owned by someone else?\n");
subscr_put(subscr);
return -EINVAL;
} else {
@@ -472,7 +488,9 @@ int gsm48_handle_paging_resp(struct msgb *msg, struct gsm_subscriber *subscr)
sig_data.bts = msg->lchan->ts->trx->bts;
sig_data.lchan = msg->lchan;
- dispatch_signal(SS_PAGING, S_PAGING_COMPLETED, &sig_data);
+ bts->network->stats.paging.completed++;
+
+ dispatch_signal(SS_PAGING, S_PAGING_SUCCEEDED, &sig_data);
/* Stop paging on the bts we received the paging response */
paging_request_stop(msg->trx->bts, subscr, msg->lchan);
@@ -503,18 +521,62 @@ int gsm48_send_rr_ciph_mode(struct gsm_lchan *lchan, int want_imeisv)
return rsl_encryption_cmd(msg);
}
+static void gsm48_cell_desc(struct gsm48_cell_desc *cd,
+ const struct gsm_bts *bts)
+{
+ cd->ncc = (bts->bsic >> 3 & 0x7);
+ cd->bcc = (bts->bsic & 0x7);
+ cd->arfcn_hi = bts->c0->arfcn >> 8;
+ cd->arfcn_lo = bts->c0->arfcn & 0xff;
+}
+
+static void gsm48_chan_desc(struct gsm48_chan_desc *cd,
+ const struct gsm_lchan *lchan)
+{
+ u_int16_t arfcn = lchan->ts->trx->arfcn & 0x3ff;
+
+ cd->chan_nr = lchan2chan_nr(lchan);
+ cd->h0.tsc = lchan->ts->trx->bts->tsc;
+ cd->h0.h = 0;
+ cd->h0.arfcn_high = arfcn >> 8;
+ cd->h0.arfcn_low = arfcn & 0xff;
+}
+
+/* Chapter 9.1.15: Handover Command */
+int gsm48_send_ho_cmd(struct gsm_lchan *old_lchan, struct gsm_lchan *new_lchan,
+ u_int8_t power_command, u_int8_t ho_ref)
+{
+ struct msgb *msg = gsm48_msgb_alloc();
+ struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
+ struct gsm48_ho_cmd *ho =
+ (struct gsm48_ho_cmd *) msgb_put(msg, sizeof(*ho));
+
+ msg->lchan = old_lchan;
+ gh->proto_discr = GSM48_PDISC_RR;
+ gh->msg_type = GSM48_MT_RR_HANDO_CMD;
+
+ /* mandatory bits */
+ gsm48_cell_desc(&ho->cell_desc, new_lchan->ts->trx->bts);
+ gsm48_chan_desc(&ho->chan_desc, new_lchan);
+ ho->ho_ref = ho_ref;
+ ho->power_command = power_command;
+
+ /* FIXME: optional bits for type of synchronization? */
+
+ return gsm48_sendmsg(msg, NULL);
+}
+
/* Chapter 9.1.2: Assignment Command */
-int gsm48_send_rr_ass_cmd(struct gsm_lchan *lchan, u_int8_t power_command)
+int gsm48_send_rr_ass_cmd(struct gsm_lchan *dest_lchan, struct gsm_lchan *lchan, u_int8_t power_command)
{
struct msgb *msg = gsm48_msgb_alloc();
struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
struct gsm48_ass_cmd *ass =
(struct gsm48_ass_cmd *) msgb_put(msg, sizeof(*ass));
- u_int16_t arfcn = lchan->ts->trx->arfcn & 0x3ff;
DEBUGP(DRR, "-> ASSIGNMENT COMMAND tch_mode=0x%02x\n", lchan->tch_mode);
- msg->lchan = lchan;
+ msg->lchan = dest_lchan;
gh->proto_discr = GSM48_PDISC_RR;
gh->msg_type = GSM48_MT_RR_ASS_CMD;
@@ -526,17 +588,16 @@ int gsm48_send_rr_ass_cmd(struct gsm_lchan *lchan, u_int8_t power_command)
* the chan_desc. But as long as multi-slot configurations
* are not used we seem to be fine.
*/
- ass->chan_desc.chan_nr = lchan2chan_nr(lchan);
- ass->chan_desc.h0.tsc = lchan->ts->trx->bts->tsc;
- ass->chan_desc.h0.h = 0;
- ass->chan_desc.h0.arfcn_high = arfcn >> 8;
- ass->chan_desc.h0.arfcn_low = arfcn & 0xff;
+ gsm48_chan_desc(&ass->chan_desc, lchan);
ass->power_command = power_command;
+ msgb_tv_put(msg, GSM48_IE_CHANMODE_1, lchan->tch_mode);
+
/* in case of multi rate we need to attach a config */
if (lchan->tch_mode == GSM48_CMODE_SPEECH_AMR) {
if (lchan->mr_conf.ver == 0) {
- DEBUGP(DRR, "BUG: Using multirate codec without multirate config.\n");
+ LOGP(DRR, LOGL_ERROR, "BUG: Using multirate codec "
+ "without multirate config.\n");
} else {
u_int8_t *data = msgb_put(msg, 4);
data[0] = GSM48_IE_MUL_RATE_CFG;
@@ -576,7 +637,8 @@ int gsm48_tx_chan_mode_modify(struct gsm_lchan *lchan, u_int8_t mode)
/* in case of multi rate we need to attach a config */
if (lchan->tch_mode == GSM48_CMODE_SPEECH_AMR) {
if (lchan->mr_conf.ver == 0) {
- DEBUGP(DRR, "BUG: Using multirate codec without multirate config.\n");
+ LOGP(DRR, LOGL_ERROR, "BUG: Using multirate codec "
+ "without multirate config.\n");
} else {
u_int8_t *data = msgb_put(msg, 4);
data[0] = GSM48_IE_MUL_RATE_CFG;
@@ -609,7 +671,7 @@ int gsm48_rx_rr_modif_ack(struct msgb *msg)
DEBUGP(DRR, "CHANNEL MODE MODIFY ACK\n");
if (mod->mode != msg->lchan->tch_mode) {
- DEBUGP(DRR, "CHANNEL MODE change failed. Wanted: %d Got: %d\n",
+ LOGP(DRR, LOGL_ERROR, "CHANNEL MODE change failed. Wanted: %d Got: %d\n",
msg->lchan->tch_mode, mod->mode);
return -1;
}
@@ -642,3 +704,82 @@ int gsm48_rx_rr_modif_ack(struct msgb *msg)
rsl_ipacc_crcx(msg->lchan);
return rc;
}
+
+int gsm48_parse_meas_rep(struct gsm_meas_rep *rep, struct msgb *msg)
+{
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
+ u_int8_t *data = gh->data;
+ struct gsm_bts *bts = msg->lchan->ts->trx->bts;
+ struct bitvec *nbv = &bts->si_common.neigh_list;
+ struct gsm_meas_rep_cell *mrc;
+
+ if (gh->msg_type != GSM48_MT_RR_MEAS_REP)
+ return -EINVAL;
+
+ if (data[0] & 0x80)
+ rep->flags |= MEAS_REP_F_BA1;
+ if (data[0] & 0x40)
+ rep->flags |= MEAS_REP_F_UL_DTX;
+ if ((data[1] & 0x40) == 0x00)
+ rep->flags |= MEAS_REP_F_DL_VALID;
+
+ rep->dl.full.rx_lev = data[0] & 0x3f;
+ rep->dl.sub.rx_lev = data[1] & 0x3f;
+ rep->dl.full.rx_qual = (data[3] >> 4) & 0x7;
+ rep->dl.sub.rx_qual = (data[3] >> 1) & 0x7;
+
+ rep->num_cell = ((data[3] >> 6) & 0x3) | ((data[2] & 0x01) << 2);
+ if (rep->num_cell < 1 || rep->num_cell > 6)
+ return 0;
+
+ /* an encoding nightmare in perfection */
+ mrc = &rep->cell[0];
+ mrc->rxlev = data[3] & 0x3f;
+ mrc->neigh_idx = data[4] >> 3;
+ mrc->arfcn = bitvec_get_nth_set_bit(nbv, mrc->neigh_idx + 1);
+ mrc->bsic = ((data[4] & 0x07) << 3) | (data[5] >> 5);
+ if (rep->num_cell < 2)
+ return 0;
+
+ mrc = &rep->cell[1];
+ mrc->rxlev = ((data[5] & 0x1f) << 1) | (data[6] >> 7);
+ mrc->neigh_idx = (data[6] >> 2) & 0x1f;
+ mrc->arfcn = bitvec_get_nth_set_bit(nbv, mrc->neigh_idx + 1);
+ mrc->bsic = ((data[6] & 0x03) << 4) | (data[7] >> 4);
+ if (rep->num_cell < 3)
+ return 0;
+
+ mrc = &rep->cell[2];
+ mrc->rxlev = ((data[7] & 0x0f) << 2) | (data[8] >> 6);
+ mrc->neigh_idx = (data[8] >> 1) & 0x1f;
+ mrc->arfcn = bitvec_get_nth_set_bit(nbv, mrc->neigh_idx + 1);
+ mrc->bsic = ((data[8] & 0x01) << 5) | (data[9] >> 3);
+ if (rep->num_cell < 4)
+ return 0;
+
+ mrc = &rep->cell[3];
+ mrc->rxlev = ((data[9] & 0x07) << 3) | (data[10] >> 5);
+ mrc->neigh_idx = data[10] & 0x1f;
+ mrc->arfcn = bitvec_get_nth_set_bit(nbv, mrc->neigh_idx + 1);
+ mrc->bsic = data[11] >> 2;
+ if (rep->num_cell < 5)
+ return 0;
+
+ mrc = &rep->cell[4];
+ mrc->rxlev = ((data[11] & 0x03) << 4) | (data[12] >> 4);
+ mrc->neigh_idx = ((data[12] & 0xf) << 1) | (data[13] >> 7);
+ mrc->arfcn = bitvec_get_nth_set_bit(nbv, mrc->neigh_idx + 1);
+ mrc->bsic = (data[13] >> 1) & 0x3f;
+ if (rep->num_cell < 6)
+ return 0;
+
+ mrc = &rep->cell[5];
+ mrc->rxlev = ((data[13] & 0x01) << 5) | (data[14] >> 3);
+ mrc->neigh_idx = ((data[14] & 0x07) << 2) | (data[15] >> 6);
+ mrc->arfcn = bitvec_get_nth_set_bit(nbv, mrc->neigh_idx + 1);
+ mrc->bsic = data[15] & 0x3f;
+
+ return 0;
+}
+
diff --git a/openbsc/src/gsm_04_11.c b/openbsc/src/gsm_04_11.c
index 75ddf9dd9..7f570b8e5 100644
--- a/openbsc/src/gsm_04_11.c
+++ b/openbsc/src/gsm_04_11.c
@@ -173,7 +173,7 @@ static int gsm411_cp_sendmsg(struct msgb *msg, struct gsm_trans *trans,
DEBUGP(DSMS, "TX: CP-ACK ");
break;
case GSM411_MT_CP_ERROR:
- DEBUGP(DSMS, "TX: CP-ACK ");
+ DEBUGP(DSMS, "TX: CP-ERROR ");
break;
}
@@ -198,48 +198,174 @@ static int gsm411_rp_sendmsg(struct msgb *msg, struct gsm_trans *trans,
return gsm411_cp_sendmsg(msg, trans, GSM411_MT_CP_DATA);
}
-static time_t gsm340_scts(u_int8_t *scts);
+/* Turn int into semi-octet representation: 98 => 0x89 */
+static u_int8_t bcdify(u_int8_t value)
+{
+ u_int8_t ret;
-static unsigned long gsm340_validity_period(u_int8_t sms_vpf, u_int8_t *sms_vp)
+ ret = value / 10;
+ ret |= (value % 10) << 4;
+
+ return ret;
+}
+
+/* Turn semi-octet representation into int: 0x89 => 98 */
+static u_int8_t unbcdify(u_int8_t value)
+{
+ u_int8_t ret;
+
+ if ((value & 0x0F) > 9 || (value >> 4) > 9)
+ LOGP(DSMS, LOGL_ERROR,
+ "unbcdify got too big nibble: 0x%02X\n", value);
+
+ ret = (value&0x0F)*10;
+ ret += value>>4;
+
+ return ret;
+}
+
+/* Generate 03.40 TP-SCTS */
+static void gsm340_gen_scts(u_int8_t *scts, time_t time)
{
+ struct tm *tm = localtime(&time);
+
+ *scts++ = bcdify(tm->tm_year % 100);
+ *scts++ = bcdify(tm->tm_mon + 1);
+ *scts++ = bcdify(tm->tm_mday);
+ *scts++ = bcdify(tm->tm_hour);
+ *scts++ = bcdify(tm->tm_min);
+ *scts++ = bcdify(tm->tm_sec);
+ *scts++ = bcdify(tm->tm_gmtoff/(60*15));
+}
+
+/* Decode 03.40 TP-SCTS (into utc/gmt timestamp) */
+static time_t gsm340_scts(u_int8_t *scts)
+{
+ struct tm tm;
+
+ u_int8_t yr = unbcdify(*scts++);
+
+ if (yr <= 80)
+ tm.tm_year = 100 + yr;
+ else
+ tm.tm_year = yr;
+ tm.tm_mon = unbcdify(*scts++) - 1;
+ tm.tm_mday = unbcdify(*scts++);
+ tm.tm_hour = unbcdify(*scts++);
+ tm.tm_min = unbcdify(*scts++);
+ tm.tm_sec = unbcdify(*scts++);
+ /* according to gsm 03.40 time zone is
+ "expressed in quarters of an hour" */
+ tm.tm_gmtoff = unbcdify(*scts++) * 15*60;
+
+ return mktime(&tm);
+}
+
+/* Return the default validity period in minutes */
+static unsigned long gsm340_vp_default(void)
+{
+ unsigned long minutes;
+ /* Default validity: two days */
+ minutes = 24 * 60 * 2;
+ return minutes;
+}
+
+/* Decode validity period format 'relative' */
+static unsigned long gsm340_vp_relative(u_int8_t *sms_vp)
+{
+ /* Chapter 9.2.3.12.1 */
u_int8_t vp;
unsigned long minutes;
- time_t expires;
- time_t now;
+
+ vp = *(sms_vp);
+ if (vp <= 143)
+ minutes = vp + 1 * 5;
+ else if (vp <= 167)
+ minutes = 12*60 + (vp-143) * 30;
+ else if (vp <= 196)
+ minutes = vp-166 * 60 * 24;
+ else
+ minutes = vp-192 * 60 * 24 * 7;
+ return minutes;
+}
+
+/* Decode validity period format 'absolute' */
+static unsigned long gsm340_vp_absolute(u_int8_t *sms_vp)
+{
+ /* Chapter 9.2.3.12.2 */
+ time_t expires, now;
+ unsigned long minutes;
+
+ expires = gsm340_scts(sms_vp);
+ now = mktime(gmtime(NULL));
+ if (expires <= now)
+ minutes = 0;
+ else
+ minutes = (expires-now)/60;
+ return minutes;
+}
+
+/* Decode validity period format 'relative in integer representation' */
+static unsigned long gsm340_vp_relative_integer(u_int8_t *sms_vp)
+{
+ u_int8_t vp;
+ unsigned long minutes;
+ vp = *(sms_vp);
+ if (vp == 0) {
+ LOGP(DSMS, LOGL_ERROR,
+ "reserved relative_integer validity period\n");
+ return gsm340_vp_default();
+ }
+ minutes = vp/60;
+ return minutes;
+}
+
+/* Decode validity period format 'relative in semi-octet representation' */
+static unsigned long gsm340_vp_relative_semioctet(u_int8_t *sms_vp)
+{
+ unsigned long minutes;
+ minutes = unbcdify(*sms_vp++)*60; /* hours */
+ minutes += unbcdify(*sms_vp++); /* minutes */
+ minutes += unbcdify(*sms_vp++)/60; /* seconds */
+ return minutes;
+}
+
+/* decode validity period. return minutes */
+static unsigned long gsm340_validity_period(u_int8_t sms_vpf, u_int8_t *sms_vp)
+{
+ u_int8_t fi; /* functionality indicator */
switch (sms_vpf) {
case GSM340_TP_VPF_RELATIVE:
- /* Chapter 9.2.3.12.1 */
- vp = *(sms_vp);
- if (vp <= 143)
- minutes = vp + 1 * 5;
- else if (vp <= 167)
- minutes = 12*60 + (vp-143) * 30;
- else if (vp <= 196)
- minutes = vp-166 * 60 * 24;
- else
- minutes = vp-192 * 60 * 24 * 7;
- break;
+ return gsm340_vp_relative(sms_vp);
case GSM340_TP_VPF_ABSOLUTE:
- /* Chapter 9.2.3.12.2 */
- expires = gsm340_scts(sms_vp);
- now = mktime(gmtime(NULL));
- if (expires <= now)
- minutes = 0;
- else
- minutes = (expires-now)/60;
- break;
+ return gsm340_vp_absolute(sms_vp);
case GSM340_TP_VPF_ENHANCED:
/* Chapter 9.2.3.12.3 */
- /* FIXME: implementation */
- DEBUGP(DSMS, "VPI enhanced not implemented yet\n");
- break;
+ fi = *sms_vp++;
+ /* ignore additional fi */
+ if (fi & (1<<7)) sms_vp++;
+ /* read validity period format */
+ switch (fi & 0b111) {
+ case 0b000:
+ return gsm340_vp_default(); /* no vpf specified */
+ case 0b001:
+ return gsm340_vp_relative(sms_vp);
+ case 0b010:
+ return gsm340_vp_relative_integer(sms_vp);
+ case 0b011:
+ return gsm340_vp_relative_semioctet(sms_vp);
+ default:
+ /* The GSM spec says that the SC should reject any
+ unsupported and/or undefined values. FIXME */
+ LOGP(DSMS, LOGL_ERROR,
+ "Reserved enhanced validity period format\n");
+ return gsm340_vp_default();
+ }
case GSM340_TP_VPF_NONE:
- /* Default validity: two days */
- minutes = 24 * 60 * 2;
- break;
+ default:
+ return gsm340_vp_default();
}
- return minutes;
}
/* determine coding alphabet dependent on GSM 03.38 Section 4 DCS */
@@ -250,7 +376,8 @@ enum sms_alphabet gsm338_get_sms_alphabet(u_int8_t dcs)
if ((cgbits & 0xc) == 0) {
if (cgbits & 2)
- DEBUGP(DSMS, "Compressed SMS not supported yet\n");
+ LOGP(DSMS, LOGL_NOTICE,
+ "Compressed SMS not supported yet\n");
switch ((dcs >> 2)&0x03) {
case 0:
@@ -280,7 +407,7 @@ enum sms_alphabet gsm338_get_sms_alphabet(u_int8_t dcs)
static int gsm340_rx_sms_submit(struct msgb *msg, struct gsm_sms *gsms)
{
if (db_sms_store(gsms) != 0) {
- DEBUGP(DSMS, "Failed to store SMS in Database\n");
+ LOGP(DSMS, LOGL_ERROR, "Failed to store SMS in Database\n");
return GSM411_RP_CAUSE_MO_NET_OUT_OF_ORDER;
}
/* dispatch a signal to tell higher level about it */
@@ -307,69 +434,6 @@ static int gsm340_gen_oa(u_int8_t *oa, unsigned int oa_len,
return len_in_bytes;
}
-/* Turn int into semi-octet representation: 98 => 0x89 */
-static u_int8_t bcdify(u_int8_t value)
-{
- u_int8_t ret;
-
- ret = value / 10;
- ret |= (value % 10) << 4;
-
- return ret;
-}
-
-/* Turn semi-octet representation into int: 0x89 => 98 */
-static u_int8_t unbcdify(u_int8_t value)
-{
- u_int8_t ret;
-
- if ((value & 0x0F) > 9 || (value >> 4) > 9)
- DEBUGP(DSMS, "unbcdify got too big nibble: 0x%02X\n", value);
-
- ret = (value&0x0F)*10;
- if (ret > 90)
- ret += value>>4;
-
- return ret;
-}
-
-/* Generate 03.40 TP-SCTS */
-static void gsm340_gen_scts(u_int8_t *scts, time_t time)
-{
- struct tm *tm = localtime(&time);
-
- *scts++ = bcdify(tm->tm_year % 100);
- *scts++ = bcdify(tm->tm_mon + 1);
- *scts++ = bcdify(tm->tm_mday);
- *scts++ = bcdify(tm->tm_hour);
- *scts++ = bcdify(tm->tm_min);
- *scts++ = bcdify(tm->tm_sec);
- *scts++ = bcdify(tm->tm_gmtoff/(60*15));
-}
-
-/* Decode 03.40 TP-SCTS (into utc/gmt timestamp) */
-static time_t gsm340_scts(u_int8_t *scts)
-{
- struct tm tm;
-
- u_int8_t yr = unbcdify(*scts++);
-
- if (yr <= 80)
- tm.tm_year = 100 + yr;
- else
- tm.tm_year = yr;
- tm.tm_mon = unbcdify(*scts++) - 1;
- tm.tm_mday = unbcdify(*scts++);
- tm.tm_hour = unbcdify(*scts++);
- tm.tm_min = unbcdify(*scts++);
- tm.tm_sec = unbcdify(*scts++);
- /* according to gsm 03.40 time zone is
- "expressed in quarters of an hour" */
- tm.tm_gmtoff = unbcdify(*scts++) * 15*60;
-
- return mktime(&tm);
-}
-
/* generate a msgb containing a TPDU derived from struct gsm_sms,
* returns total size of TPDU */
static int gsm340_gen_tpdu(struct msgb *msg, struct gsm_sms *sms)
@@ -437,7 +501,8 @@ static int gsm340_gen_tpdu(struct msgb *msg, struct gsm_sms *sms)
memcpy(smsp, sms->user_data, sms->user_data_len);
break;
default:
- DEBUGP(DSMS, "Unhandled Data Coding Scheme: 0x%02X\n", sms->data_coding_scheme);
+ LOGP(DSMS, LOGL_NOTICE, "Unhandled Data Coding Scheme: 0x%02X\n",
+ sms->data_coding_scheme);
break;
}
@@ -457,6 +522,8 @@ static int gsm340_rx_tpdu(struct msgb *msg)
u_int8_t address_lv[12]; /* according to 03.40 / 9.1.2.5 */
int rc = 0;
+ counter_inc(bts->network->stats.sms.submitted);
+
gsms = sms_alloc();
if (!gsms)
return GSM411_RP_CAUSE_MO_NET_OUT_OF_ORDER;
@@ -475,7 +542,7 @@ static int gsm340_rx_tpdu(struct msgb *msg)
/* length in bytes of the destination address */
da_len_bytes = 2 + *smsp/2 + *smsp%2;
if (da_len_bytes > 12) {
- DEBUGP(DSMS, "Destination Address > 12 bytes ?!?\n");
+ LOGP(DSMS, LOGL_ERROR, "Destination Address > 12 bytes ?!?\n");
rc = GSM411_RP_CAUSE_SEMANT_INC_MSG;
goto out;
}
@@ -499,14 +566,17 @@ static int gsm340_rx_tpdu(struct msgb *msg)
case GSM340_TP_VPF_ABSOLUTE:
case GSM340_TP_VPF_ENHANCED:
sms_vp = smsp;
+ /* the additional functionality indicator... */
+ if (sms_vpf == GSM340_TP_VPF_ENHANCED && *smsp & (1<<7))
+ smsp++;
smsp += 7;
break;
case GSM340_TP_VPF_NONE:
sms_vp = 0;
break;
default:
- DEBUGP(DSMS, "SMS Validity period not implemented: 0x%02x\n",
- sms_vpf);
+ LOGP(DSMS, LOGL_NOTICE,
+ "SMS Validity period not implemented: 0x%02x\n", sms_vpf);
return GSM411_RP_CAUSE_MO_NET_OUT_OF_ORDER;
}
gsms->user_data_len = *smsp++;
@@ -524,16 +594,17 @@ static int gsm340_rx_tpdu(struct msgb *msg)
}
}
- DEBUGP(DSMS, "SMS:\nMTI: 0x%02x, VPF: 0x%02x, MR: 0x%02x "
- "PID: 0x%02x, DCS: 0x%02x, DA: %s, UserDataLength: 0x%02x "
- "UserData: \"%s\"\n", sms_mti, sms_vpf, gsms->msg_ref,
- gsms->protocol_id, gsms->data_coding_scheme,
- gsms->dest_addr, gsms->user_data_len,
+ gsms->sender = subscr_get(msg->lchan->subscr);
+
+ LOGP(DSMS, LOGL_INFO, "RX SMS: Sender: %s, MTI: 0x%02x, VPF: 0x%02x, "
+ "MR: 0x%02x PID: 0x%02x, DCS: 0x%02x, DA: %s, "
+ "UserDataLength: 0x%02x, UserData: \"%s\"\n",
+ subscr_name(gsms->sender), sms_mti, sms_vpf, gsms->msg_ref,
+ gsms->protocol_id, gsms->data_coding_scheme, gsms->dest_addr,
+ gsms->user_data_len,
sms_alphabet == DCS_7BIT_DEFAULT ? gsms->text :
hexdump(gsms->user_data, gsms->user_data_len));
- gsms->sender = subscr_get(msg->lchan->subscr);
-
gsms->validity_minutes = gsm340_validity_period(sms_vpf, sms_vp);
dispatch_signal(SS_SMS, 0, gsms);
@@ -542,6 +613,7 @@ static int gsm340_rx_tpdu(struct msgb *msg)
gsms->receiver = subscr_get_by_extension(bts->network, gsms->dest_addr);
if (!gsms->receiver) {
rc = 1; /* cause 1: unknown subscriber */
+ counter_inc(bts->network->stats.sms.no_receiver);
goto out;
}
@@ -552,11 +624,11 @@ static int gsm340_rx_tpdu(struct msgb *msg)
break;
case GSM340_SMS_COMMAND_MS2SC:
case GSM340_SMS_DELIVER_REP_MS2SC:
- DEBUGP(DSMS, "Unimplemented MTI 0x%02x\n", sms_mti);
+ LOGP(DSMS, LOGL_NOTICE, "Unimplemented MTI 0x%02x\n", sms_mti);
rc = GSM411_RP_CAUSE_IE_NOTEXIST;
break;
default:
- DEBUGP(DSMS, "Undefined MTI 0x%02x\n", sms_mti);
+ LOGP(DSMS, LOGL_NOTICE, "Undefined MTI 0x%02x\n", sms_mti);
rc = GSM411_RP_CAUSE_IE_NOTEXIST;
break;
}
@@ -586,7 +658,7 @@ static int gsm411_send_rp_error(struct gsm_trans *trans,
msgb_tv_put(msg, 1, cause);
- DEBUGP(DSMS, "TX: SMS RP ERROR, cause %d (%s)\n", cause,
+ LOGP(DSMS, LOGL_NOTICE, "TX: SMS RP ERROR, cause %d (%s)\n", cause,
get_value_string(rp_cause_strs, cause));
return gsm411_rp_sendmsg(msg, trans, GSM411_MT_RP_ERROR_MT, msg_ref);
@@ -602,10 +674,11 @@ static int gsm411_rx_rp_ud(struct msgb *msg, struct gsm_trans *trans,
int rc = 0;
if (src_len && src)
- DEBUGP(DSMS, "RP-DATA (MO) with SRC ?!?\n");
+ LOGP(DSMS, LOGL_ERROR, "RP-DATA (MO) with SRC ?!?\n");
if (!dst_len || !dst || !tpdu_len || !tpdu) {
- DEBUGP(DSMS, "RP-DATA (MO) without DST or TPDU ?!?\n");
+ LOGP(DSMS, LOGL_ERROR,
+ "RP-DATA (MO) without DST or TPDU ?!?\n");
gsm411_send_rp_error(trans, rph->msg_ref,
GSM411_RP_CAUSE_INV_MAND_INF);
return -EIO;
@@ -660,13 +733,13 @@ static int gsm411_rx_rp_ack(struct msgb *msg, struct gsm_trans *trans,
* transmitted */
if (!trans->sms.is_mt) {
- DEBUGP(DSMS, "RX RP-ACK on a MO transfer ?\n");
+ LOGP(DSMS, LOGL_ERROR, "RX RP-ACK on a MO transfer ?\n");
return gsm411_send_rp_error(trans, rph->msg_ref,
GSM411_RP_CAUSE_MSG_INCOMP_STATE);
}
if (!sms) {
- DEBUGP(DSMS, "RX RP-ACK but no sms in transaction?!?\n");
+ LOGP(DSMS, LOGL_ERROR, "RX RP-ACK but no sms in transaction?!?\n");
return gsm411_send_rp_error(trans, rph->msg_ref,
GSM411_RP_CAUSE_PROTOCOL_ERR);
}
@@ -679,14 +752,16 @@ static int gsm411_rx_rp_ack(struct msgb *msg, struct gsm_trans *trans,
sms_free(sms);
trans->sms.sms = NULL;
- /* free the transaction here */
- trans_free(trans);
-
/* check for more messages for this subscriber */
sms = db_sms_get_unsent_for_subscr(msg->lchan->subscr);
if (sms)
gsm411_send_sms_lchan(msg->lchan, sms);
- else
+
+ /* free the transaction here */
+ trans_free(trans);
+
+ /* release channel if done */
+ if (!sms)
rsl_release_request(msg->lchan, trans->sms.link_id);
return 0;
@@ -695,6 +770,7 @@ static int gsm411_rx_rp_ack(struct msgb *msg, struct gsm_trans *trans,
static int gsm411_rx_rp_error(struct msgb *msg, struct gsm_trans *trans,
struct gsm411_rp_hdr *rph)
{
+ struct gsm_network *net = trans->lchan->ts->trx->bts->network;
struct gsm_sms *sms = trans->sms.sms;
u_int8_t cause_len = rph->data[0];
u_int8_t cause = rph->data[1];
@@ -703,11 +779,12 @@ static int gsm411_rx_rp_error(struct msgb *msg, struct gsm_trans *trans,
* successfully receive the SMS. We need to investigate
* the cause and take action depending on it */
- DEBUGP(DSMS, "RX SMS RP-ERROR, cause %d:%d (%s)\n", cause_len, cause,
- get_value_string(rp_cause_strs, cause));
+ LOGP(DSMS, LOGL_NOTICE, "%s: RX SMS RP-ERROR, cause %d:%d (%s)\n",
+ subscr_name(msg->lchan->subscr), cause_len, cause,
+ get_value_string(rp_cause_strs, cause));
if (!trans->sms.is_mt) {
- DEBUGP(DSMS, "RX RP-ERR on a MO transfer ?\n");
+ LOGP(DSMS, LOGL_ERROR, "RX RP-ERR on a MO transfer ?\n");
#if 0
return gsm411_send_rp_error(trans, rph->msg_ref,
GSM411_RP_CAUSE_MSG_INCOMP_STATE);
@@ -715,7 +792,8 @@ static int gsm411_rx_rp_error(struct msgb *msg, struct gsm_trans *trans,
}
if (!sms) {
- DEBUGP(DSMS, "RX RP-ERR, but no sms in transaction?!?\n");
+ LOGP(DSMS, LOGL_ERROR,
+ "RX RP-ERR, but no sms in transaction?!?\n");
return -EINVAL;
#if 0
return gsm411_send_rp_error(trans, rph->msg_ref,
@@ -728,7 +806,9 @@ static int gsm411_rx_rp_error(struct msgb *msg, struct gsm_trans *trans,
* to store this in our database and wati for a SMMA message */
/* FIXME */
dispatch_signal(SS_SMS, S_SMS_MEM_EXCEEDED, trans->subscr);
- }
+ counter_inc(net->stats.sms.rp_err_mem);
+ } else
+ counter_inc(net->stats.sms.rp_err_other);
sms_free(sms);
trans->sms.sms = NULL;
@@ -790,7 +870,7 @@ static int gsm411_rx_cp_data(struct msgb *msg, struct gsm48_hdr *gh,
rc = gsm411_rx_rp_error(msg, trans, rp_data);
break;
default:
- DEBUGP(DSMS, "Invalid RP type 0x%02x\n", msg_type);
+ LOGP(DSMS, LOGL_NOTICE, "Invalid RP type 0x%02x\n", msg_type);
rc = gsm411_send_rp_error(trans, rp_data->msg_ref,
GSM411_RP_CAUSE_MSGTYPE_NOTEXIST);
break;
@@ -821,7 +901,7 @@ static int gsm411_tx_cp_error(struct gsm_trans *trans, u_int8_t cause)
struct msgb *msg = gsm411_msgb_alloc();
u_int8_t *causep;
- DEBUGP(DSMS, "TX CP-ERROR, cause %d (%s)\n", cause,
+ LOGP(DSMS, LOGL_NOTICE, "TX CP-ERROR, cause %d (%s)\n", cause,
get_value_string(cp_cause_strs, cause));
causep = msgb_put(msg, 1);
@@ -844,11 +924,11 @@ int gsm0411_rcv_sms(struct msgb *msg, u_int8_t link_id)
return -EIO;
/* FIXME: send some error message */
- DEBUGP(DSMS, "trans_id=%x ", gh->proto_discr >> 4);
+ DEBUGP(DSMS, "trans_id=%x ", transaction_id);
trans = trans_find_by_id(lchan->subscr, GSM48_PDISC_SMS,
transaction_id);
if (!trans) {
- DEBUGPC(DSMS, "(unknown) ");
+ DEBUGPC(DSMS, "(new) ");
trans = trans_alloc(lchan->subscr, GSM48_PDISC_SMS,
transaction_id, new_callref++);
if (!trans) {
@@ -868,6 +948,33 @@ int gsm0411_rcv_sms(struct msgb *msg, u_int8_t link_id)
switch(msg_type) {
case GSM411_MT_CP_DATA:
DEBUGPC(DSMS, "RX SMS CP-DATA\n");
+
+ /* 5.4: For MO, if a CP-DATA is received for a new
+ * transaction, equals reception of an implicit
+ * last CP-ACK for previous transaction */
+ if (trans->sms.cp_state == GSM411_CPS_IDLE) {
+ int i;
+ struct gsm_trans *ptrans;
+
+ /* Scan through all remote initiated transactions */
+ for (i=8; i<15; i++) {
+ if (i == transaction_id)
+ continue;
+
+ ptrans = trans_find_by_id(lchan->subscr,
+ GSM48_PDISC_SMS, i);
+ if (!ptrans)
+ continue;
+
+ DEBUGP(DSMS, "Implicit CP-ACK for trans_id=%x\n", i);
+
+ /* Finish it for good */
+ bsc_del_timer(&ptrans->sms.cp_timer);
+ ptrans->sms.cp_state = GSM411_CPS_IDLE;
+ trans_free(ptrans);
+ }
+ }
+
/* 5.2.3.1.3: MO state exists when SMC has received
* CP-DATA, including sending of the assoc. CP-ACK */
/* 5.2.3.2.4: MT state exists when SMC has received
@@ -940,10 +1047,14 @@ int gsm411_send_sms_lchan(struct gsm_lchan *lchan, struct gsm_sms *sms)
struct gsm_trans *trans;
u_int8_t *data, *rp_ud_len;
u_int8_t msg_ref = 42;
- u_int8_t transaction_id;
+ int transaction_id;
int rc;
- transaction_id = 4; /* FIXME: we always use 4 for now */
+ transaction_id = trans_assign_trans_id(lchan->subscr, GSM48_PDISC_SMS, 0);
+ if (transaction_id == -1) {
+ LOGP(DSMS, LOGL_ERROR, "No available transaction ids\n");
+ return -EBUSY;
+ }
msg->lchan = lchan;
@@ -953,7 +1064,7 @@ int gsm411_send_sms_lchan(struct gsm_lchan *lchan, struct gsm_sms *sms)
trans = trans_alloc(lchan->subscr, GSM48_PDISC_SMS,
transaction_id, new_callref++);
if (!trans) {
- DEBUGP(DSMS, "No memory for trans\n");
+ LOGP(DSMS, LOGL_ERROR, "No memory for trans\n");
/* FIXME: send some error message */
return -ENOMEM;
}
@@ -1001,6 +1112,8 @@ int gsm411_send_sms_lchan(struct gsm_lchan *lchan, struct gsm_sms *sms)
DEBUGP(DSMS, "TX: SMS DELIVER\n");
+ counter_inc(lchan->ts->trx->bts->network->stats.sms.delivered);
+
return gsm411_rp_sendmsg(msg, trans, GSM411_MT_RP_DATA_MT, msg_ref);
/* FIXME: enter 'wait for RP-ACK' state, start TR1N */
}
diff --git a/openbsc/src/gsm_data.c b/openbsc/src/gsm_data.c
index 69a9096ca..1f2e1a1fc 100644
--- a/openbsc/src/gsm_data.c
+++ b/openbsc/src/gsm_data.c
@@ -27,21 +27,12 @@
#include <openbsc/gsm_data.h>
#include <openbsc/talloc.h>
+#include <openbsc/abis_nm.h>
+#include <openbsc/statistics.h>
void *tall_bsc_ctx;
-const char *get_value_string(const struct value_string *vs, u_int32_t val)
-{
- int i;
-
- for (i = 0;; i++) {
- if (vs[i].value == 0 && vs[i].str == NULL)
- break;
- if (vs[i].value == val)
- return vs[i].str;
- }
- return "unknown";
-}
+static LLIST_HEAD(bts_models);
void set_ts_e1link(struct gsm_bts_trx_ts *ts, u_int8_t e1_nr,
u_int8_t e1_ts, u_int8_t e1_ts_ss)
@@ -91,7 +82,7 @@ static const char *lchan_names[] = {
[GSM_LCHAN_UNKNOWN] = "UNKNOWN",
};
-const char *gsm_lchan_name(enum gsm_chan_t c)
+const char *gsm_lchant_name(enum gsm_chan_t c)
{
if (c >= ARRAY_SIZE(lchan_names))
return "INVALID";
@@ -99,6 +90,20 @@ const char *gsm_lchan_name(enum gsm_chan_t c)
return lchan_names[c];
}
+static const struct value_string lchan_s_names[] = {
+ { LCHAN_S_NONE, "NONE" },
+ { LCHAN_S_ACT_REQ, "ACTIVATION REQUESTED" },
+ { LCHAN_S_ACTIVE, "ACTIVE" },
+ { LCHAN_S_INACTIVE, "INACTIVE" },
+ { LCHAN_S_REL_REQ, "RELEASE REQUESTED" },
+ { 0, NULL },
+};
+
+const char *gsm_lchans_name(enum gsm_lchan_state s)
+{
+ return get_value_string(lchan_s_names, s);
+}
+
static const char *chreq_names[] = {
[GSM_CHREQ_REASON_EMERG] = "EMERGENCY",
[GSM_CHREQ_REASON_PAG] = "PAGING",
@@ -115,6 +120,29 @@ const char *gsm_chreq_name(enum gsm_chreq_reason_t c)
return chreq_names[c];
}
+static struct gsm_bts_model *bts_model_find(enum gsm_bts_type type)
+{
+ struct gsm_bts_model *model;
+
+ llist_for_each_entry(model, &bts_models, list) {
+ if (model->type == type)
+ return model;
+ }
+
+ return NULL;
+}
+
+int gsm_bts_model_register(struct gsm_bts_model *model)
+{
+ if (bts_model_find(model->type))
+ return -EEXIST;
+
+ tlv_def_patch(&model->nm_att_tlvdef, &nm_att_tlvdef);
+ llist_add_tail(&model->list, &bts_models);
+ return 0;
+}
+
+
struct gsm_bts_trx *gsm_bts_trx_alloc(struct gsm_bts *bts)
{
struct gsm_bts_trx *trx = talloc_zero(bts, struct gsm_bts_trx);
@@ -125,6 +153,7 @@ struct gsm_bts_trx *gsm_bts_trx_alloc(struct gsm_bts *bts)
trx->bts = bts;
trx->nr = bts->num_trx++;
+ trx->nm_state.administrative = NM_STATE_UNLOCKED;
for (k = 0; k < TRX_NR_TS; k++) {
struct gsm_bts_trx_ts *ts = &trx->ts[k];
@@ -144,6 +173,9 @@ struct gsm_bts_trx *gsm_bts_trx_alloc(struct gsm_bts *bts)
}
}
+ if (trx->nr != 0)
+ trx->nominal_power = bts->c0->nominal_power;
+
llist_add_tail(&trx->list, &bts->trx_list);
return trx;
@@ -153,19 +185,38 @@ struct gsm_bts *gsm_bts_alloc(struct gsm_network *net, enum gsm_bts_type type,
u_int8_t tsc, u_int8_t bsic)
{
struct gsm_bts *bts = talloc_zero(net, struct gsm_bts);
+ struct gsm_bts_model *model = bts_model_find(type);
int i;
if (!bts)
return NULL;
+ if (!model && type != GSM_BTS_TYPE_UNKNOWN) {
+ talloc_free(bts);
+ return NULL;
+ }
+
bts->network = net;
bts->nr = net->num_bts++;
bts->type = type;
+ bts->model = model;
bts->tsc = tsc;
bts->bsic = bsic;
bts->num_trx = 0;
INIT_LLIST_HEAD(&bts->trx_list);
bts->ms_max_power = 15; /* dBm */
+ bts->si_common.cell_sel_par.cell_resel_hyst = 2; /* 4 dB */
+ bts->si_common.cell_sel_par.rxlev_acc_min = 0;
+ bts->si_common.neigh_list.data = bts->si_common.data.neigh_list;
+ bts->si_common.neigh_list.data_len =
+ sizeof(bts->si_common.data.neigh_list);
+ bts->si_common.cell_alloc.data = bts->si_common.data.cell_alloc;
+ bts->si_common.cell_alloc.data_len =
+ sizeof(bts->si_common.data.cell_alloc);
+ bts->si_common.rach_control.re = 1; /* no re-establishment */
+ bts->si_common.rach_control.tx_integer = 9; /* 12 slots spread - 217/115 slots delay */
+ bts->si_common.rach_control.max_trans = 3; /* 7 retransmissions */
+ bts->si_common.rach_control.t2 = 4; /* no emergency calls */
for (i = 0; i < ARRAY_SIZE(bts->gprs.nsvc); i++) {
bts->gprs.nsvc[i].bts = bts;
@@ -197,11 +248,49 @@ struct gsm_network *gsm_network_init(u_int16_t country_code, u_int16_t network_c
net->country_code = country_code;
net->network_code = network_code;
net->num_bts = 0;
+ net->reject_cause = GSM48_REJECT_ROAMING_NOT_ALLOWED;
+ net->T3101 = GSM_T3101_DEFAULT;
+ net->T3113 = GSM_T3113_DEFAULT;
+ /* FIXME: initialize all other timers! */
+
+ /* default set of handover parameters */
+ net->handover.win_rxlev_avg = 10;
+ net->handover.win_rxqual_avg = 1;
+ net->handover.win_rxlev_avg_neigh = 10;
+ net->handover.pwr_interval = 6;
+ net->handover.pwr_hysteresis = 3;
+ net->handover.max_distance = 9999;
INIT_LLIST_HEAD(&net->trans_list);
INIT_LLIST_HEAD(&net->upqueue);
INIT_LLIST_HEAD(&net->bts_list);
+ net->stats.chreq.total = counter_alloc("net.chreq.total");
+ net->stats.chreq.no_channel = counter_alloc("net.chreq.no_channel");
+ net->stats.handover.attempted = counter_alloc("net.handover.attempted");
+ net->stats.handover.no_channel = counter_alloc("net.handover.no_channel");
+ net->stats.handover.timeout = counter_alloc("net.handover.timeout");
+ net->stats.handover.completed = counter_alloc("net.handover.completed");
+ net->stats.handover.failed = counter_alloc("net.handover.failed");
+ net->stats.loc_upd_type.attach = counter_alloc("net.loc_upd_type.attach");
+ net->stats.loc_upd_type.normal = counter_alloc("net.loc_upd_type.normal");
+ net->stats.loc_upd_type.periodic = counter_alloc("net.loc_upd_type.periodic");
+ net->stats.loc_upd_type.detach = counter_alloc("net.imsi_detach.count");
+ net->stats.loc_upd_resp.reject = counter_alloc("net.loc_upd_resp.reject");
+ net->stats.loc_upd_resp.accept = counter_alloc("net.loc_upd_resp.accept");
+ net->stats.paging.attempted = counter_alloc("net.paging.attempted");
+ net->stats.paging.detached = counter_alloc("net.paging.detached");
+ net->stats.paging.completed = counter_alloc("net.paging.completed");
+ net->stats.paging.expired = counter_alloc("net.paging.expired");
+ net->stats.sms.submitted = counter_alloc("net.sms.submitted");
+ net->stats.sms.no_receiver = counter_alloc("net.sms.no_receiver");
+ net->stats.sms.delivered = counter_alloc("net.sms.delivered");
+ net->stats.sms.rp_err_mem = counter_alloc("net.sms.rp_err_mem");
+ net->stats.sms.rp_err_other = counter_alloc("net.sms.rp_err_other");
+ net->stats.call.dialled = counter_alloc("net.call.dialled");
+ net->stats.call.alerted = counter_alloc("net.call.alerted");
+ net->stats.call.connected = counter_alloc("net.call.connected");
+
net->mncc_recv = mncc_recv;
return net;
@@ -222,6 +311,25 @@ struct gsm_bts *gsm_bts_num(struct gsm_network *net, int num)
return NULL;
}
+/* Get reference to a neighbor cell on a given BCCH ARFCN */
+struct gsm_bts *gsm_bts_neighbor(const struct gsm_bts *bts,
+ u_int16_t arfcn, u_int8_t bsic)
+{
+ struct gsm_bts *neigh;
+ /* FIXME: use some better heuristics here to determine which cell
+ * using this ARFCN really is closest to the target cell. For
+ * now we simply assume that each ARFCN will only be used by one
+ * cell */
+
+ llist_for_each_entry(neigh, &bts->network->bts_list, list) {
+ if (neigh->c0->arfcn == arfcn &&
+ neigh->bsic == bsic)
+ return neigh;
+ }
+
+ return NULL;
+}
+
struct gsm_bts_trx *gsm_bts_trx_num(struct gsm_bts *bts, int num)
{
struct gsm_bts_trx *trx;
@@ -239,6 +347,15 @@ struct gsm_bts_trx *gsm_bts_trx_num(struct gsm_bts *bts, int num)
static char ts2str[255];
+char *gsm_trx_name(struct gsm_bts_trx *trx)
+{
+ snprintf(ts2str, sizeof(ts2str), "(bts=%d,trx=%d)",
+ trx->bts->nr, trx->nr);
+
+ return ts2str;
+}
+
+
char *gsm_ts_name(struct gsm_bts_trx_ts *ts)
{
snprintf(ts2str, sizeof(ts2str), "(bts=%d,trx=%d,ts=%d)",
@@ -247,6 +364,16 @@ char *gsm_ts_name(struct gsm_bts_trx_ts *ts)
return ts2str;
}
+char *gsm_lchan_name(struct gsm_lchan *lchan)
+{
+ struct gsm_bts_trx_ts *ts = lchan->ts;
+
+ snprintf(ts2str, sizeof(ts2str), "(bts=%d,trx=%d,ts=%d,ss=%d)",
+ ts->trx->bts->nr, ts->trx->nr, ts->nr, lchan->nr);
+
+ return ts2str;
+}
+
static const char *bts_types[] = {
[GSM_BTS_TYPE_UNKNOWN] = "unknown",
[GSM_BTS_TYPE_BS11] = "bs11",
@@ -270,6 +397,17 @@ const char *btstype2str(enum gsm_bts_type type)
return bts_types[type];
}
+struct gsm_bts_trx *gsm_bts_trx_by_nr(struct gsm_bts *bts, int nr)
+{
+ struct gsm_bts_trx *trx;
+
+ llist_for_each_entry(trx, &bts->trx_list, list) {
+ if (trx->nr == nr)
+ return trx;
+ }
+ return NULL;
+}
+
/* Search for a BTS in the given Location Area; optionally start searching
* with start_bts (for continuing to search after the first result) */
struct gsm_bts *gsm_bts_by_lac(struct gsm_network *net, unsigned int lac,
@@ -361,3 +499,63 @@ const char *gsm_auth_policy_name(enum gsm_auth_policy policy)
return gsm_auth_policy_names[policy];
}
+static const char *rrlp_mode_names[] = {
+ [RRLP_MODE_NONE] = "none",
+ [RRLP_MODE_MS_BASED] = "ms-based",
+ [RRLP_MODE_MS_PREF] = "ms-preferred",
+ [RRLP_MODE_ASS_PREF] = "ass-preferred",
+};
+
+enum rrlp_mode rrlp_mode_parse(const char *arg)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(rrlp_mode_names); i++) {
+ if (!strcmp(arg, rrlp_mode_names[i]))
+ return i;
+ }
+ return RRLP_MODE_NONE;
+}
+
+const char *rrlp_mode_name(enum rrlp_mode mode)
+{
+ if (mode > ARRAY_SIZE(rrlp_mode_names))
+ return "none";
+ return rrlp_mode_names[mode];
+}
+
+struct gsm_meas_rep *lchan_next_meas_rep(struct gsm_lchan *lchan)
+{
+ struct gsm_meas_rep *meas_rep;
+
+ meas_rep = &lchan->meas_rep[lchan->meas_rep_idx];
+ memset(meas_rep, 0, sizeof(*meas_rep));
+ meas_rep->lchan = lchan;
+ lchan->meas_rep_idx = (lchan->meas_rep_idx + 1)
+ % ARRAY_SIZE(lchan->meas_rep);
+
+ return meas_rep;
+}
+
+int gsm_set_bts_type(struct gsm_bts *bts, enum gsm_bts_type type)
+{
+ struct gsm_bts_model *model;
+
+ model = bts_model_find(type);
+ if (!model)
+ return -EINVAL;
+
+ bts->type = type;
+ bts->model = model;
+
+ switch (bts->type) {
+ case GSM_BTS_TYPE_NANOBTS:
+ /* Set the default OML Stream ID to 0xff */
+ bts->oml_tei = 0xff;
+ bts->c0->nominal_power = 23;
+ break;
+ case GSM_BTS_TYPE_BS11:
+ break;
+ }
+
+ return 0;
+}
diff --git a/openbsc/src/gsm_subscriber.c b/openbsc/src/gsm_subscriber.c
index c81b522d4..692508753 100644
--- a/openbsc/src/gsm_subscriber.c
+++ b/openbsc/src/gsm_subscriber.c
@@ -34,6 +34,14 @@
extern struct llist_head *subscr_bsc_active_subscriber(void);
+char *subscr_name(struct gsm_subscriber *subscr)
+{
+ if (strlen(subscr->name))
+ return subscr->name;
+
+ return subscr->imsi;
+}
+
struct gsm_subscriber *subscr_get_by_tmsi(struct gsm_network *net,
u_int32_t tmsi)
{
@@ -100,12 +108,15 @@ int subscr_update(struct gsm_subscriber *s, struct gsm_bts *bts, int reason)
s->net = bts->network;
/* Indicate "attached to LAC" */
s->lac = bts->location_area_code;
+ LOGP(DMM, LOGL_INFO, "Subscriber %s ATTACHED LAC=%u\n",
+ subscr_name(s), s->lac);
dispatch_signal(SS_SUBSCR, S_SUBSCR_ATTACHED, s);
break;
case GSM_SUBSCRIBER_UPDATE_DETACHED:
/* Only detach if we are currently in this area */
if (bts->location_area_code == s->lac)
s->lac = GSM_LAC_RESERVED_DETACHED;
+ LOGP(DMM, LOGL_INFO, "Subscriber %s DETACHED\n", subscr_name(s));
dispatch_signal(SS_SUBSCR, S_SUBSCR_DETACHED, s);
break;
default:
diff --git a/openbsc/src/gsm_subscriber_base.c b/openbsc/src/gsm_subscriber_base.c
index 48374eae5..0570061a2 100644
--- a/openbsc/src/gsm_subscriber_base.c
+++ b/openbsc/src/gsm_subscriber_base.c
@@ -137,7 +137,7 @@ static void subscr_free(struct gsm_subscriber *subscr)
struct gsm_subscriber *subscr_get(struct gsm_subscriber *subscr)
{
subscr->use_count++;
- DEBUGP(DCC, "subscr %s usage increases usage to: %d\n",
+ DEBUGP(DREF, "subscr %s usage increases usage to: %d\n",
subscr->extension, subscr->use_count);
return subscr;
}
@@ -145,7 +145,7 @@ struct gsm_subscriber *subscr_get(struct gsm_subscriber *subscr)
struct gsm_subscriber *subscr_put(struct gsm_subscriber *subscr)
{
subscr->use_count--;
- DEBUGP(DCC, "subscr %s usage decreased usage to: %d\n",
+ DEBUGP(DREF, "subscr %s usage decreased usage to: %d\n",
subscr->extension, subscr->use_count);
if (subscr->use_count <= 0)
subscr_free(subscr);
diff --git a/openbsc/src/gsm_utils.c b/openbsc/src/gsm_utils.c
index ddfd7f3de..9439993db 100644
--- a/openbsc/src/gsm_utils.c
+++ b/openbsc/src/gsm_utils.c
@@ -90,8 +90,10 @@ int ms_pwr_ctl_lvl(enum gsm_band band, unsigned int dbm)
return 0;
else if (dbm < 5)
return 19;
- else
+ else {
+ /* we are guaranteed to have (5 <= dbm < 39) */
return 2 + ((39 - dbm) / 2);
+ }
break;
case GSM_BAND_1800:
if (dbm >= 36)
@@ -100,16 +102,24 @@ int ms_pwr_ctl_lvl(enum gsm_band band, unsigned int dbm)
return 30;
else if (dbm >= 32)
return 31;
- else
+ else if (dbm == 31)
+ return 0;
+ else {
+ /* we are guaranteed to have (0 <= dbm < 31) */
return (30 - dbm) / 2;
+ }
break;
case GSM_BAND_1900:
if (dbm >= 33)
return 30;
else if (dbm >= 32)
return 31;
- else
+ else if (dbm == 31)
+ return 0;
+ else {
+ /* we are guaranteed to have (0 <= dbm < 31) */
return (30 - dbm) / 2;
+ }
break;
}
return -EINVAL;
@@ -150,6 +160,28 @@ int ms_pwr_dbm(enum gsm_band band, u_int8_t lvl)
return -EINVAL;
}
+/* According to TS 08.05 Chapter 8.1.4 */
+int rxlev2dbm(u_int8_t rxlev)
+{
+ if (rxlev > 63)
+ rxlev = 63;
+
+ return -110 + rxlev;
+}
+
+/* According to TS 08.05 Chapter 8.1.4 */
+u_int8_t dbm2rxlev(int dbm)
+{
+ int rxlev = dbm + 110;
+
+ if (rxlev > 63)
+ rxlev = 63;
+ else if (rxlev < 0)
+ rxlev = 0;
+
+ return rxlev;
+}
+
void generate_backtrace()
{
int i, nptrs;
diff --git a/openbsc/src/handover_decision.c b/openbsc/src/handover_decision.c
new file mode 100644
index 000000000..b37cecddb
--- /dev/null
+++ b/openbsc/src/handover_decision.c
@@ -0,0 +1,298 @@
+/* Handover Decision making for Inter-BTS (Intra-BSC) Handover. This
+ * only implements the handover algorithm/decision, but not execution
+ * of it */
+
+/* (C) 2009 by Harald Welte <laforge@gnumonks.org>
+ *
+ * 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 <stdlib.h>
+#include <errno.h>
+
+#include <openbsc/msgb.h>
+#include <openbsc/debug.h>
+#include <openbsc/gsm_data.h>
+#include <openbsc/meas_rep.h>
+#include <openbsc/signal.h>
+#include <openbsc/talloc.h>
+#include <openbsc/handover.h>
+#include <openbsc/gsm_utils.h>
+
+/* issue handover to a cell identified by ARFCN and BSIC */
+static int handover_to_arfcn_bsic(struct gsm_lchan *lchan,
+ u_int16_t arfcn, u_int8_t bsic)
+{
+ struct gsm_bts *new_bts;
+
+ /* resolve the gsm_bts structure for the best neighbor */
+ new_bts = gsm_bts_neighbor(lchan->ts->trx->bts, arfcn, bsic);
+ if (!new_bts) {
+ LOGP(DHO, LOGL_NOTICE, "unable to determine neighbor BTS "
+ "for ARFCN %u BSIC %u ?!?\n", arfcn, bsic);
+ return -EINVAL;
+ }
+
+ /* and actually try to handover to that cell */
+ return bsc_handover_start(lchan, new_bts);
+}
+
+/* did we get a RXLEV for a given cell in the given report? */
+static int rxlev_for_cell_in_rep(struct gsm_meas_rep *mr,
+ u_int16_t arfcn, u_int8_t bsic)
+{
+ int i;
+
+ for (i = 0; i < mr->num_cell; i++) {
+ struct gsm_meas_rep_cell *mrc = &mr->cell[i];
+
+ /* search for matching report */
+ if (!(mrc->arfcn == arfcn && mrc->bsic == bsic))
+ continue;
+
+ mrc->flags |= MRC_F_PROCESSED;
+ return mrc->rxlev;
+ }
+ return -ENODEV;
+}
+
+/* obtain averaged rxlev for given neighbor */
+static int neigh_meas_avg(struct neigh_meas_proc *nmp, int window)
+{
+ unsigned int i, idx;
+ int avg = 0;
+
+ idx = calc_initial_idx(ARRAY_SIZE(nmp->rxlev),
+ nmp->rxlev_cnt % ARRAY_SIZE(nmp->rxlev),
+ window);
+
+ for (i = 0; i < window; i++) {
+ int j = (idx+i) % ARRAY_SIZE(nmp->rxlev);
+
+ avg += nmp->rxlev[j];
+ }
+
+ return avg / window;
+}
+
+/* find empty or evict bad neighbor */
+static struct neigh_meas_proc *find_evict_neigh(struct gsm_lchan *lchan)
+{
+ int j, worst = 999999;
+ struct neigh_meas_proc *nmp_worst;
+
+ /* first try to find an empty/unused slot */
+ for (j = 0; j < ARRAY_SIZE(lchan->neigh_meas); j++) {
+ struct neigh_meas_proc *nmp = &lchan->neigh_meas[j];
+ if (!nmp->arfcn)
+ return nmp;
+ }
+
+ /* no empty slot found. evict worst neighbor from list */
+ for (j = 0; j < ARRAY_SIZE(lchan->neigh_meas); j++) {
+ struct neigh_meas_proc *nmp = &lchan->neigh_meas[j];
+ int avg = neigh_meas_avg(nmp, MAX_WIN_NEIGH_AVG);
+ if (avg < worst) {
+ worst = avg;
+ nmp_worst = nmp;
+ }
+ }
+
+ return nmp_worst;
+}
+
+/* process neighbor cell measurement reports */
+static void process_meas_neigh(struct gsm_meas_rep *mr)
+{
+ int i, j, idx;
+
+ /* for each reported cell, try to update global state */
+ for (j = 0; j < ARRAY_SIZE(mr->lchan->neigh_meas); j++) {
+ struct neigh_meas_proc *nmp = &mr->lchan->neigh_meas[j];
+ unsigned int idx;
+ int rxlev;
+
+ /* skip unused entries */
+ if (!nmp->arfcn)
+ continue;
+
+ rxlev = rxlev_for_cell_in_rep(mr, nmp->arfcn, nmp->bsic);
+ idx = nmp->rxlev_cnt % ARRAY_SIZE(nmp->rxlev);
+ if (rxlev >= 0) {
+ nmp->rxlev[idx] = rxlev;
+ nmp->last_seen_nr = mr->nr;
+ } else
+ nmp->rxlev[idx] = 0;
+ nmp->rxlev_cnt++;
+ }
+
+ /* iterate over list of reported cells, check if we did not
+ * process all of them */
+ for (i = 0; i < mr->num_cell; i++) {
+ struct gsm_meas_rep_cell *mrc = &mr->cell[i];
+ struct neigh_meas_proc *nmp;
+
+ if (mrc->flags & MRC_F_PROCESSED)
+ continue;
+
+ nmp = find_evict_neigh(mr->lchan);
+
+ nmp->arfcn = mrc->arfcn;
+ nmp->bsic = mrc->bsic;
+
+ idx = nmp->rxlev_cnt % ARRAY_SIZE(nmp->rxlev);
+ nmp->rxlev[idx] = mrc->rxlev;
+ nmp->rxlev_cnt++;
+ nmp->last_seen_nr = mr->nr;
+
+ mrc->flags |= MRC_F_PROCESSED;
+ }
+}
+
+/* attempt to do a handover */
+static int attempt_handover(struct gsm_meas_rep *mr)
+{
+ struct gsm_network *net = mr->lchan->ts->trx->bts->network;
+ struct neigh_meas_proc *best_cell = NULL;
+ unsigned int best_better_db = 0;
+ int i, rc;
+
+ /* find the best cell in this report that is at least RXLEV_HYST
+ * better than the current serving cell */
+
+ for (i = 0; i < ARRAY_SIZE(mr->lchan->neigh_meas); i++) {
+ struct neigh_meas_proc *nmp = &mr->lchan->neigh_meas[i];
+ int avg, better;
+
+ /* skip empty slots */
+ if (nmp->arfcn == 0)
+ continue;
+
+ /* caculate average rxlev for this cell over the window */
+ avg = neigh_meas_avg(nmp, net->handover.win_rxlev_avg_neigh);
+
+ /* check if hysteresis is fulfilled */
+ if (avg < mr->dl.full.rx_lev + net->handover.pwr_hysteresis)
+ continue;
+
+ better = avg - mr->dl.full.rx_lev;
+ if (better > best_better_db) {
+ best_cell = nmp;
+ best_better_db = better;
+ }
+ }
+
+ if (!best_cell)
+ return 0;
+
+ LOGP(DHO, LOGL_INFO, "%s: Cell on ARFCN %u is better: ",
+ gsm_ts_name(mr->lchan->ts), best_cell->arfcn);
+ if (!net->handover.active) {
+ LOGPC(DHO, LOGL_INFO, "Skipping, Handover disabled\n");
+ return 0;
+ }
+
+ rc = handover_to_arfcn_bsic(mr->lchan, best_cell->arfcn, best_cell->bsic);
+ switch (rc) {
+ case 0:
+ LOGPC(DHO, LOGL_INFO, "Starting handover\n");
+ break;
+ case -ENOSPC:
+ LOGPC(DHO, LOGL_INFO, "No channel available\n");
+ break;
+ case -EBUSY:
+ LOGPC(DHO, LOGL_INFO, "Handover already active\n");
+ break;
+ default:
+ LOGPC(DHO, LOGL_ERROR, "Unknown error\n");
+ }
+ return rc;
+}
+
+/* process an already parsed measurement report and decide if we want to
+ * attempt a handover */
+static int process_meas_rep(struct gsm_meas_rep *mr)
+{
+ struct gsm_network *net = mr->lchan->ts->trx->bts->network;
+ int av_rxlev;
+
+ /* we currently only do handover for TCH channels */
+ switch (mr->lchan->type) {
+ case GSM_LCHAN_TCH_F:
+ case GSM_LCHAN_TCH_H:
+ break;
+ default:
+ return 0;
+ }
+
+ /* parse actual neighbor cell info */
+ if (mr->num_cell > 0 && mr->num_cell < 7)
+ process_meas_neigh(mr);
+
+ av_rxlev = get_meas_rep_avg(mr->lchan, MEAS_REP_DL_RXLEV_FULL,
+ net->handover.win_rxlev_avg);
+
+ /* Interference HO */
+ if (rxlev2dbm(av_rxlev) > -85 &&
+ meas_rep_n_out_of_m_be(mr->lchan, MEAS_REP_DL_RXQUAL_FULL,
+ 3, 4, 5))
+ return attempt_handover(mr);
+
+ /* Bad Quality */
+ if (meas_rep_n_out_of_m_be(mr->lchan, MEAS_REP_DL_RXQUAL_FULL,
+ 3, 4, 5))
+ return attempt_handover(mr);
+
+ /* Low Level */
+ if (rxlev2dbm(av_rxlev) <= -110)
+ return attempt_handover(mr);
+
+ /* Distance */
+ if (mr->ms_l1.ta > net->handover.max_distance)
+ return attempt_handover(mr);
+
+ /* Power Budget AKA Better Cell */
+ if ((mr->nr % net->handover.pwr_interval) == 0)
+ return attempt_handover(mr);
+
+ return 0;
+
+}
+
+static int ho_dec_sig_cb(unsigned int subsys, unsigned int signal,
+ void *handler_data, void *signal_data)
+{
+ struct gsm_meas_rep *mr;
+
+ if (subsys != SS_LCHAN)
+ return 0;
+
+ switch (signal) {
+ case S_LCHAN_MEAS_REP:
+ mr = signal_data;
+ process_meas_rep(mr);
+ break;
+ }
+
+ return 0;
+}
+
+void on_dso_load_ho_dec(void)
+{
+ register_signal_handler(SS_LCHAN, ho_dec_sig_cb, NULL);
+}
diff --git a/openbsc/src/handover_logic.c b/openbsc/src/handover_logic.c
new file mode 100644
index 000000000..1bf048fed
--- /dev/null
+++ b/openbsc/src/handover_logic.c
@@ -0,0 +1,376 @@
+/* Handover Logic for Inter-BTS (Intra-BSC) Handover. This does not
+ * actually implement the handover algorithm/decision, but executes a
+ * handover decision */
+
+/* (C) 2009 by Harald Welte <laforge@gnumonks.org>
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+#include <netinet/in.h>
+
+#include <openbsc/msgb.h>
+#include <openbsc/debug.h>
+#include <openbsc/gsm_data.h>
+#include <openbsc/gsm_utils.h>
+#include <openbsc/gsm_subscriber.h>
+#include <openbsc/gsm_04_08.h>
+#include <openbsc/abis_rsl.h>
+#include <openbsc/chan_alloc.h>
+#include <openbsc/signal.h>
+#include <openbsc/talloc.h>
+#include <openbsc/transaction.h>
+#include <openbsc/rtp_proxy.h>
+
+struct bsc_handover {
+ struct llist_head list;
+
+ struct gsm_lchan *old_lchan;
+ struct gsm_lchan *new_lchan;
+
+ struct timer_list T3103;
+
+ u_int8_t ho_ref;
+};
+
+static LLIST_HEAD(bsc_handovers);
+
+static struct bsc_handover *bsc_ho_by_new_lchan(struct gsm_lchan *new_lchan)
+{
+ struct bsc_handover *ho;
+
+ llist_for_each_entry(ho, &bsc_handovers, list) {
+ if (ho->new_lchan == new_lchan)
+ return ho;
+ }
+
+ return NULL;
+}
+
+static struct bsc_handover *bsc_ho_by_old_lchan(struct gsm_lchan *old_lchan)
+{
+ struct bsc_handover *ho;
+
+ llist_for_each_entry(ho, &bsc_handovers, list) {
+ if (ho->old_lchan == old_lchan)
+ return ho;
+ }
+
+ return NULL;
+}
+
+/* Hand over the specified logical channel to the specified new BTS.
+ * This is the main entry point for the actual handover algorithm,
+ * after it has decided it wants to initiate HO to a specific BTS */
+int bsc_handover_start(struct gsm_lchan *old_lchan, struct gsm_bts *bts)
+{
+ struct gsm_lchan *new_lchan;
+ struct bsc_handover *ho;
+ static u_int8_t ho_ref;
+ int rc;
+
+ /* don't attempt multiple handovers for the same lchan at
+ * the same time */
+ if (bsc_ho_by_old_lchan(old_lchan))
+ return -EBUSY;
+
+ DEBUGP(DHO, "(old_lchan on BTS %u, new BTS %u)\n",
+ old_lchan->ts->trx->bts->nr, bts->nr);
+
+ counter_inc(bts->network->stats.handover.attempted);
+
+ new_lchan = lchan_alloc(bts, old_lchan->type);
+ if (!new_lchan) {
+ LOGP(DHO, LOGL_NOTICE, "No free channel\n");
+ counter_inc(bts->network->stats.handover.no_channel);
+ return -ENOSPC;
+ }
+
+ ho = talloc_zero(NULL, struct bsc_handover);
+ if (!ho) {
+ LOGP(DHO, LOGL_FATAL, "Out of Memory\n");
+ lchan_free(new_lchan);
+ return -ENOMEM;
+ }
+ ho->old_lchan = old_lchan;
+ ho->new_lchan = new_lchan;
+ ho->ho_ref = ho_ref++;
+
+ /* copy some parameters from old lchan */
+ memcpy(&new_lchan->encr, &old_lchan->encr, sizeof(new_lchan->encr));
+ new_lchan->ms_power = old_lchan->ms_power;
+ new_lchan->bs_power = old_lchan->bs_power;
+ new_lchan->rsl_cmode = old_lchan->rsl_cmode;
+ new_lchan->tch_mode = old_lchan->tch_mode;
+ new_lchan->subscr = subscr_get(old_lchan->subscr);
+
+ /* FIXME: do we have a better idea of the timing advance? */
+ rc = rsl_chan_activate_lchan(new_lchan, RSL_ACT_INTER_ASYNC, 0,
+ ho->ho_ref);
+ if (rc < 0) {
+ LOGP(DHO, LOGL_ERROR, "could not activate channel\n");
+ talloc_free(ho);
+ lchan_free(new_lchan);
+ return rc;
+ }
+
+ llist_add(&ho->list, &bsc_handovers);
+ /* we continue in the SS_LCHAN handler / ho_chan_activ_ack */
+
+ return 0;
+}
+
+/* T3103 expired: Handover has failed without HO COMPLETE or HO FAIL */
+static void ho_T3103_cb(void *_ho)
+{
+ struct bsc_handover *ho = _ho;
+ struct gsm_network *net = ho->new_lchan->ts->trx->bts->network;
+
+ DEBUGP(DHO, "HO T3103 expired\n");
+ counter_inc(net->stats.handover.timeout);
+
+ lchan_free(ho->new_lchan);
+ llist_del(&ho->list);
+ talloc_free(ho);
+}
+
+/* RSL has acknowledged activation of the new lchan */
+static int ho_chan_activ_ack(struct gsm_lchan *new_lchan)
+{
+ struct bsc_handover *ho;
+ int rc;
+
+ /* we need to check if this channel activation is related to
+ * a handover at all (and if, which particular handover) */
+ ho = bsc_ho_by_new_lchan(new_lchan);
+ if (!ho)
+ return -ENODEV;
+
+ DEBUGP(DHO, "handover activate ack, send HO Command\n");
+
+ /* we can now send the 04.08 HANDOVER COMMAND to the MS
+ * using the old lchan */
+
+ rc = gsm48_send_ho_cmd(ho->old_lchan, new_lchan, 0, ho->ho_ref);
+
+ /* start T3103. We can continue either with T3103 expiration,
+ * 04.08 HANDOVER COMPLETE or 04.08 HANDOVER FAIL */
+ ho->T3103.cb = ho_T3103_cb;
+ ho->T3103.data = ho;
+ bsc_schedule_timer(&ho->T3103, 10, 0);
+
+ /* create a RTP connection */
+ if (is_ipaccess_bts(new_lchan->ts->trx->bts))
+ rsl_ipacc_crcx(new_lchan);
+
+ return 0;
+}
+
+/* RSL has not acknowledged activation of the new lchan */
+static int ho_chan_activ_nack(struct gsm_lchan *new_lchan)
+{
+ struct bsc_handover *ho;
+
+ ho = bsc_ho_by_new_lchan(new_lchan);
+ if (!ho) {
+ LOGP(DHO, LOGL_ERROR, "unable to find HO record\n");
+ return -ENODEV;
+ }
+
+ llist_del(&ho->list);
+ talloc_free(ho);
+
+ /* FIXME: maybe we should try to allocate a new LCHAN here? */
+
+ return 0;
+}
+
+/* GSM 04.08 HANDOVER COMPLETE has been received on new channel */
+static int ho_gsm48_ho_compl(struct gsm_lchan *new_lchan)
+{
+ struct gsm_network *net = new_lchan->ts->trx->bts->network;
+ struct bsc_handover *ho;
+
+ ho = bsc_ho_by_new_lchan(new_lchan);
+ if (!ho) {
+ LOGP(DHO, LOGL_ERROR, "unable to find HO record\n");
+ return -ENODEV;
+ }
+
+ LOGP(DHO, LOGL_INFO, "Subscriber %s HO from BTS %u->%u on ARFCN "
+ "%u->%u\n", subscr_name(ho->old_lchan->subscr),
+ ho->old_lchan->ts->trx->bts->nr, new_lchan->ts->trx->bts->nr,
+ ho->old_lchan->ts->trx->arfcn, new_lchan->ts->trx->arfcn);
+
+ counter_inc(net->stats.handover.completed);
+
+ bsc_del_timer(&ho->T3103);
+
+ /* update lchan pointer of transaction */
+ trans_lchan_change(ho->old_lchan, new_lchan);
+
+ ho->old_lchan->state = LCHAN_S_INACTIVE;
+ lchan_auto_release(ho->old_lchan);
+
+ /* do something to re-route the actual speech frames ! */
+
+ llist_del(&ho->list);
+ talloc_free(ho);
+
+ return 0;
+}
+
+/* GSM 04.08 HANDOVER FAIL has been received */
+static int ho_gsm48_ho_fail(struct gsm_lchan *old_lchan)
+{
+ struct gsm_network *net = old_lchan->ts->trx->bts->network;
+ struct bsc_handover *ho;
+
+ ho = bsc_ho_by_old_lchan(old_lchan);
+ if (!ho) {
+ LOGP(DHO, LOGL_ERROR, "unable to find HO record\n");
+ return -ENODEV;
+ }
+
+ counter_inc(net->stats.handover.failed);
+
+ bsc_del_timer(&ho->T3103);
+ llist_del(&ho->list);
+ put_lchan(ho->new_lchan);
+ talloc_free(ho);
+
+ return 0;
+}
+
+/* GSM 08.58 HANDOVER DETECT has been received */
+static int ho_rsl_detect(struct gsm_lchan *new_lchan)
+{
+ struct bsc_handover *ho;
+
+ ho = bsc_ho_by_new_lchan(new_lchan);
+ if (!ho) {
+ LOGP(DHO, LOGL_ERROR, "unable to find HO record\n");
+ return -ENODEV;
+ }
+
+ /* FIXME: do we actually want to do something here ? */
+
+ return 0;
+}
+
+static int ho_ipac_crcx_ack(struct gsm_lchan *new_lchan)
+{
+ struct bsc_handover *ho;
+ struct rtp_socket *old_rs, *new_rs, *other_rs;
+
+ ho = bsc_ho_by_new_lchan(new_lchan);
+ if (!ho) {
+ /* it is perfectly normal, we have CRCX even in non-HO cases */
+ return 0;
+ }
+
+ if (ipacc_rtp_direct) {
+ LOGP(DHO, LOGL_ERROR, "unable to handover in direct RTP mode\n");
+ return 0;
+ }
+
+ /* RTP Proxy mode */
+ new_rs = new_lchan->abis_ip.rtp_socket;
+ old_rs = ho->old_lchan->abis_ip.rtp_socket;
+
+ if (!new_rs) {
+ LOGP(DHO, LOGL_ERROR, "no RTP socket for new_lchan\n");
+ return -EIO;
+ }
+
+ rsl_ipacc_mdcx_to_rtpsock(new_lchan);
+
+ if (!old_rs) {
+ LOGP(DHO, LOGL_ERROR, "no RTP socekt for old_lchan\n");
+ return -EIO;
+ }
+
+ /* copy rx_action and reference to other sock */
+ new_rs->rx_action = old_rs->rx_action;
+ new_rs->tx_action = old_rs->tx_action;
+ new_rs->transmit = old_rs->transmit;
+
+ switch (ho->old_lchan->abis_ip.rtp_socket->rx_action) {
+ case RTP_PROXY:
+ other_rs = old_rs->proxy.other_sock;
+ rtp_socket_proxy(new_rs, other_rs);
+ /* delete reference to other end socket to prevent
+ * rtp_socket_free() from removing the inverse reference */
+ old_rs->proxy.other_sock = NULL;
+ break;
+ case RTP_RECV_UPSTREAM:
+ new_rs->receive = old_rs->receive;
+ break;
+ case RTP_NONE:
+ break;
+ }
+
+ return 0;
+}
+
+static int ho_logic_sig_cb(unsigned int subsys, unsigned int signal,
+ void *handler_data, void *signal_data)
+{
+ struct gsm_lchan *lchan;
+
+ switch (subsys) {
+ case SS_LCHAN:
+ lchan = signal_data;
+ switch (signal) {
+ case S_LCHAN_ACTIVATE_ACK:
+ return ho_chan_activ_ack(lchan);
+ case S_LCHAN_ACTIVATE_NACK:
+ return ho_chan_activ_nack(lchan);
+ case S_LCHAN_HANDOVER_DETECT:
+ return ho_rsl_detect(lchan);
+ case S_LCHAN_HANDOVER_COMPL:
+ return ho_gsm48_ho_compl(lchan);
+ case S_LCHAN_HANDOVER_FAIL:
+ return ho_gsm48_ho_fail(lchan);
+ }
+ break;
+ case SS_ABISIP:
+ lchan = signal_data;
+ switch (signal) {
+ case S_ABISIP_CRCX_ACK:
+ return ho_ipac_crcx_ack(lchan);
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static __attribute__((constructor)) void on_dso_load_ho_logic(void)
+{
+ register_signal_handler(SS_LCHAN, ho_logic_sig_cb, NULL);
+ register_signal_handler(SS_ABISIP, ho_logic_sig_cb, NULL);
+}
diff --git a/openbsc/src/input/ipaccess.c b/openbsc/src/input/ipaccess.c
index 2d9f51ef9..90d7cea85 100644
--- a/openbsc/src/input/ipaccess.c
+++ b/openbsc/src/input/ipaccess.c
@@ -213,7 +213,7 @@ static int ipaccess_rcvmsg(struct e1inp_line *line, struct msgb *msg,
&site_id, &bts_id, &trx_id);
bts = find_bts_by_unitid(e1h->gsmnet, site_id, bts_id);
if (!bts) {
- DEBUGP(DINP, "Unable to find BTS configuration for "
+ LOGP(DINP, LOGL_ERROR, "Unable to find BTS configuration for "
" %u/%u/%u, disconnecting\n", site_id, bts_id,
trx_id);
return -EIO;
@@ -238,6 +238,7 @@ static int ipaccess_rcvmsg(struct e1inp_line *line, struct msgb *msg,
trx->rsl_tei, 0);
/* get rid of our old temporary bfd */
memcpy(newbfd, bfd, sizeof(*newbfd));
+ newbfd->priv_nr = 2+trx_id;
bsc_unregister_fd(bfd);
bsc_register_fd(newbfd);
talloc_free(bfd);
@@ -247,9 +248,8 @@ static int ipaccess_rcvmsg(struct e1inp_line *line, struct msgb *msg,
return 0;
}
-/* FIXME: this is per BTS */
-static int oml_up = 0;
-static int rsl_up = 0;
+#define OML_UP 0x0001
+#define RSL_UP 0x0002
/*
* read one ipa message from the socket
@@ -270,7 +270,8 @@ struct msgb *ipaccess_read_msg(struct bsc_fd *bfd, int *error)
hh = (struct ipaccess_head *) msg->data;
ret = recv(bfd->fd, msg->data, 3, 0);
if (ret < 0) {
- fprintf(stderr, "recv error %s\n", strerror(errno));
+ if (errno != EAGAIN)
+ LOGP(DINP, LOGL_ERROR, "recv error %d %s\n", ret, strerror(errno));
msgb_free(msg);
*error = ret;
return NULL;
@@ -287,7 +288,7 @@ struct msgb *ipaccess_read_msg(struct bsc_fd *bfd, int *error)
len = ntohs(hh->len);
ret = recv(bfd->fd, msg->l2h, len, 0);
if (ret < len) {
- fprintf(stderr, "short read!\n");
+ LOGP(DINP, LOGL_ERROR, "short read!\n");
msgb_free(msg);
*error = -EIO;
return NULL;
@@ -310,7 +311,13 @@ static int handle_ts1_read(struct bsc_fd *bfd)
msg = ipaccess_read_msg(bfd, &error);
if (!msg) {
if (error == 0) {
- fprintf(stderr, "BTS disappeared, dead socket\n");
+ link = e1inp_lookup_sign_link(e1i_ts, IPAC_PROTO_OML, 0);
+ if (link) {
+ link->trx->bts->ip_access.flags = 0;
+ LOGP(DINP, LOGL_NOTICE, "BTS %u disappeared, dead socket\n",
+ link->trx->bts->nr);
+ } else
+ LOGP(DINP, LOGL_NOTICE, "unknown BTS disappeared, dead socket\n");
e1inp_event(e1i_ts, EVT_E1_TEI_DN, 0, IPAC_PROTO_RSL);
e1inp_event(e1i_ts, EVT_E1_TEI_DN, 0, IPAC_PROTO_OML);
bsc_unregister_fd(bfd);
@@ -340,7 +347,8 @@ static int handle_ts1_read(struct bsc_fd *bfd)
link = e1inp_lookup_sign_link(e1i_ts, hh->proto, 0);
if (!link) {
- printf("no matching signalling link for hh->proto=0x%02x\n", hh->proto);
+ LOGP(DINP, LOGL_ERROR, "no matching signalling link for "
+ "hh->proto=0x%02x\n", hh->proto);
msgb_free(msg);
return -EIO;
}
@@ -348,21 +356,21 @@ static int handle_ts1_read(struct bsc_fd *bfd)
switch (link->type) {
case E1INP_SIGN_RSL:
- if (!rsl_up) {
+ if (!(msg->trx->bts->ip_access.flags & (RSL_UP << msg->trx->nr))) {
e1inp_event(e1i_ts, EVT_E1_TEI_UP, link->tei, link->sapi);
- rsl_up = 1;
+ msg->trx->bts->ip_access.flags |= (RSL_UP << msg->trx->nr);
}
ret = abis_rsl_rcvmsg(msg);
break;
case E1INP_SIGN_OML:
- if (!oml_up) {
+ if (!(msg->trx->bts->ip_access.flags & OML_UP)) {
e1inp_event(e1i_ts, EVT_E1_TEI_UP, link->tei, link->sapi);
- oml_up = 1;
+ msg->trx->bts->ip_access.flags |= OML_UP;
}
ret = abis_nm_rcvmsg(msg);
break;
default:
- DEBUGP(DMI, "Unknown IP.access protocol proto=0x%02x\n", hh->proto);
+ LOGP(DINP, LOGL_NOTICE, "Unknown IP.access protocol proto=0x%02x\n", hh->proto);
msgb_free(msg);
break;
}
@@ -462,7 +470,7 @@ static int ipaccess_fd_cb(struct bsc_fd *bfd, unsigned int what)
if (what & BSC_FD_WRITE)
rc = handle_ts1_write(bfd);
} else
- fprintf(stderr, "unknown E1 TS type %u\n", e1i_ts->type);
+ LOGP(DINP, LOGL_ERROR, "unknown E1 TS type %u\n", e1i_ts->type);
return rc;
}
@@ -492,7 +500,8 @@ static int listen_fd_cb(struct bsc_fd *listen_bfd, unsigned int what)
perror("accept");
return ret;
}
- DEBUGP(DINP, "accept()ed new OML link from %s\n", inet_ntoa(sa.sin_addr));
+ LOGP(DINP, LOGL_NOTICE, "accept()ed new OML link from %s\n",
+ inet_ntoa(sa.sin_addr));
line = talloc_zero(tall_bsc_ctx, struct e1inp_line);
if (!line) {
@@ -514,7 +523,7 @@ static int listen_fd_cb(struct bsc_fd *listen_bfd, unsigned int what)
bfd->when = BSC_FD_READ;
ret = bsc_register_fd(bfd);
if (ret < 0) {
- fprintf(stderr, "could not register FD\n");
+ LOGP(DINP, LOGL_ERROR, "could not register FD\n");
close(bfd->fd);
talloc_free(line);
return ret;
@@ -550,13 +559,13 @@ static int rsl_listen_fd_cb(struct bsc_fd *listen_bfd, unsigned int what)
perror("accept");
return bfd->fd;
}
- DEBUGP(DINP, "accept()ed new RSL link from %s\n", inet_ntoa(sa.sin_addr));
+ LOGP(DINP, LOGL_NOTICE, "accept()ed new RSL link from %s\n", inet_ntoa(sa.sin_addr));
bfd->priv_nr = 2;
bfd->cb = ipaccess_fd_cb;
bfd->when = BSC_FD_READ;
ret = bsc_register_fd(bfd);
if (ret < 0) {
- fprintf(stderr, "could not register FD\n");
+ LOGP(DINP, LOGL_ERROR, "could not register FD\n");
close(bfd->fd);
talloc_free(bfd);
return ret;
@@ -587,7 +596,7 @@ static int make_sock(struct bsc_fd *bfd, u_int16_t port,
ret = bind(bfd->fd, (struct sockaddr *) &addr, sizeof(addr));
if (ret < 0) {
- fprintf(stderr, "could not bind l2 socket %s\n",
+ LOGP(DINP, LOGL_ERROR, "could not bind l2 socket %s\n",
strerror(errno));
return -EIO;
}
@@ -623,7 +632,7 @@ int ipaccess_connect(struct e1inp_line *line, struct sockaddr_in *sa)
ret = connect(bfd->fd, (struct sockaddr *) sa, sizeof(*sa));
if (ret < 0) {
- fprintf(stderr, "could not connect socket\n");
+ LOGP(DINP, LOGL_ERROR, "could not connect socket\n");
close(bfd->fd);
return ret;
}
@@ -657,12 +666,12 @@ int ipaccess_setup(struct gsm_network *gsmnet)
e1h->gsmnet = gsmnet;
/* Listen for OML connections */
- ret = make_sock(&e1h->listen_fd, 3002, listen_fd_cb);
+ ret = make_sock(&e1h->listen_fd, IPA_TCP_PORT_OML, listen_fd_cb);
if (ret < 0)
return ret;
/* Listen for RSL connections */
- ret = make_sock(&e1h->rsl_listen_fd, 3003, rsl_listen_fd_cb);
+ ret = make_sock(&e1h->rsl_listen_fd, IPA_TCP_PORT_RSL, rsl_listen_fd_cb);
return ret;
}
diff --git a/openbsc/src/ipaccess-config.c b/openbsc/src/ipaccess/ipaccess-config.c
index c50a46581..7d559b03a 100644
--- a/openbsc/src/ipaccess-config.c
+++ b/openbsc/src/ipaccess/ipaccess-config.c
@@ -1,6 +1,8 @@
/* ip.access nanoBTS configuration tool */
/* (C) 2009 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2009 by Holger Hans Peter Freyther
+ * (C) 2009 by On Waves
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
@@ -24,6 +26,8 @@
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
+#include <sys/fcntl.h>
+#include <sys/stat.h>
#include <sys/types.h>
#include <sys/socket.h>
@@ -39,6 +43,7 @@
#include <openbsc/abis_nm.h>
#include <openbsc/signal.h>
#include <openbsc/debug.h>
+#include <openbsc/talloc.h>
static struct gsm_network *gsmnet;
@@ -48,6 +53,21 @@ static char *prim_oml_ip;
static char *unit_id;
static u_int16_t nv_flags;
static u_int16_t nv_mask;
+static char *software = NULL;
+static int sw_load_state = 0;
+static int oml_state = 0;
+
+struct sw_load {
+ u_int8_t file_id[255];
+ u_int8_t file_id_len;
+
+ u_int8_t file_version[255];
+ u_int8_t file_version_len;
+};
+
+static void *tall_ctx_config = NULL;
+static struct sw_load *sw_load1 = NULL;
+static struct sw_load *sw_load2 = NULL;
/*
static u_int8_t prim_oml_attr[] = { 0x95, 0x00, 7, 0x88, 192, 168, 100, 11, 0x00, 0x00 };
@@ -68,6 +88,28 @@ static int ipacc_msg_nack(u_int8_t mt)
return 0;
}
+static int ipacc_msg_ack(u_int8_t mt, struct gsm_bts *bts)
+{
+ if (sw_load_state == 1) {
+ fprintf(stderr, "The new software is activaed.\n");
+
+ if (restart) {
+ abis_nm_ipaccess_restart(bts);
+ } else {
+ exit(0);
+ }
+ } else if (oml_state == 1) {
+ fprintf(stderr, "Set the primary OML IP.\n");
+ if (restart) {
+ abis_nm_ipaccess_restart(bts);
+ } else {
+ exit(0);
+ }
+ }
+
+ return 0;
+}
+
struct ipacc_ferr_elem {
int16_t freq_err;
u_int8_t freq_qual;
@@ -149,14 +191,25 @@ static int test_rep(void *_msg)
static int nm_sig_cb(unsigned int subsys, unsigned int signal,
void *handler_data, void *signal_data)
{
- u_int8_t *msg_type;
+ struct ipacc_ack_signal_data *ipacc_data;
switch (signal) {
case S_NM_IPACC_NACK:
- msg_type = signal_data;
- return ipacc_msg_nack(*msg_type);
+ ipacc_data = signal_data;
+ return ipacc_msg_nack(ipacc_data->msg_type);
+ case S_NM_IPACC_ACK:
+ ipacc_data = signal_data;
+ return ipacc_msg_ack(ipacc_data->msg_type, ipacc_data->bts);
case S_NM_TEST_REP:
return test_rep(signal_data);
+ case S_NM_IPACC_RESTART_ACK:
+ printf("The BTS has acked the restart. Exiting.\n");
+ exit(0);
+ break;
+ case S_NM_IPACC_RESTART_NACK:
+ printf("The BTS has nacked the restart. Exiting.\n");
+ exit(0);
+ break;
default:
break;
}
@@ -164,6 +217,84 @@ static int nm_sig_cb(unsigned int subsys, unsigned int signal,
return 0;
}
+/* callback function passed to the ABIS OML code */
+static int percent;
+static int percent_old;
+static int swload_cbfn(unsigned int hook, unsigned int event, struct msgb *_msg,
+ void *data, void *param)
+{
+ struct msgb *msg;
+ struct gsm_bts *bts;
+
+ if (hook != GSM_HOOK_NM_SWLOAD)
+ return 0;
+
+ bts = (struct gsm_bts *) data;
+
+ switch (event) {
+ case NM_MT_LOAD_INIT_ACK:
+ fprintf(stdout, "Software Load Initiate ACK\n");
+ break;
+ case NM_MT_LOAD_INIT_NACK:
+ fprintf(stderr, "ERROR: Software Load Initiate NACK\n");
+ exit(5);
+ break;
+ case NM_MT_LOAD_END_ACK:
+ fprintf(stderr, "LOAD END ACK...");
+ /* now make it the default */
+ sw_load_state = 1;
+
+ msg = msgb_alloc(1024, "sw: nvattr");
+ msg->l2h = msgb_put(msg, 3);
+ msg->l3h = &msg->l2h[3];
+
+ /* activate software */
+ if (sw_load1) {
+ msgb_v_put(msg, NM_ATT_SW_DESCR);
+ msgb_tl16v_put(msg, NM_ATT_FILE_ID, sw_load1->file_id_len, sw_load1->file_id);
+ msgb_tl16v_put(msg, NM_ATT_FILE_VERSION, sw_load1->file_version_len,
+ sw_load1->file_version);
+ }
+
+ if (sw_load2) {
+ msgb_v_put(msg, NM_ATT_SW_DESCR);
+ msgb_tl16v_put(msg, NM_ATT_FILE_ID, sw_load2->file_id_len, sw_load2->file_id);
+ msgb_tl16v_put(msg, NM_ATT_FILE_VERSION, sw_load2->file_version_len,
+ sw_load2->file_version);
+ }
+
+ /* fill in the data */
+ msg->l2h[0] = NM_ATT_IPACC_CUR_SW_CFG;
+ msg->l2h[1] = msgb_l3len(msg) >> 8;
+ msg->l2h[2] = msgb_l3len(msg) & 0xff;
+ printf("Foo l2h: %p l3h: %p... length l2: %u l3: %u\n", msg->l2h, msg->l3h, msgb_l2len(msg), msgb_l3len(msg));
+ abis_nm_ipaccess_set_nvattr(bts->c0, msg->l2h, msgb_l2len(msg));
+ msgb_free(msg);
+ break;
+ case NM_MT_LOAD_END_NACK:
+ fprintf(stderr, "ERROR: Software Load End NACK\n");
+ exit(3);
+ break;
+ case NM_MT_ACTIVATE_SW_NACK:
+ fprintf(stderr, "ERROR: Activate Software NACK\n");
+ exit(4);
+ break;
+ case NM_MT_ACTIVATE_SW_ACK:
+ break;
+ case NM_MT_LOAD_SEG_ACK:
+ percent = abis_nm_software_load_status(bts);
+ if (percent > percent_old)
+ printf("Software Download Progress: %d%%\n", percent);
+ percent_old = percent;
+ break;
+ case NM_MT_LOAD_ABORT:
+ fprintf(stderr, "ERROR: Load aborted by the BTS.\n");
+ exit(6);
+ break;
+ }
+ return 0;
+}
+
static void bootstrap_om(struct gsm_bts *bts)
{
int len;
@@ -182,7 +313,7 @@ static void bootstrap_om(struct gsm_bts *bts)
memcpy(buf+3, unit_id, len);
buf[3+len] = 0;
printf("setting Unit ID to '%s'\n", unit_id);
- abis_nm_ipaccess_set_nvattr(bts, buf, 3+len+1);
+ abis_nm_ipaccess_set_nvattr(bts->c0, buf, 3+len+1);
}
if (prim_oml_ip) {
struct in_addr ia;
@@ -205,7 +336,8 @@ static void bootstrap_om(struct gsm_bts *bts)
*cur++ = 0;
*cur++ = 0;
printf("setting primary OML link IP to '%s'\n", inet_ntoa(ia));
- abis_nm_ipaccess_set_nvattr(bts, buf, 3+len);
+ oml_state = 1;
+ abis_nm_ipaccess_set_nvattr(bts->c0, buf, 3+len);
}
if (nv_mask) {
len = 4;
@@ -219,13 +351,14 @@ static void bootstrap_om(struct gsm_bts *bts)
*cur++ = nv_mask >> 8;
printf("setting NV Flags/Mask to 0x%04x/0x%04x\n",
nv_flags, nv_mask);
- abis_nm_ipaccess_set_nvattr(bts, buf, 3+len);
+ abis_nm_ipaccess_set_nvattr(bts->c0, buf, 3+len);
}
- if (restart) {
+ if (restart && !prim_oml_ip && !software) {
printf("restarting BTS\n");
abis_nm_ipaccess_restart(bts);
}
+
}
void input_event(int event, enum e1inp_sign_type type, struct gsm_bts_trx *trx)
@@ -257,17 +390,146 @@ int nm_state_event(enum nm_evt evt, u_int8_t obj_class, void *obj,
{
if (evt == EVT_STATECHG_OPER &&
obj_class == NM_OC_RADIO_CARRIER &&
- new_state->availability == 3 &&
- net_listen_testnr) {
+ new_state->availability == 3) {
struct gsm_bts_trx *trx = obj;
- u_int8_t phys_config[] = { 0x02, 0x0a, 0x00, 0x01, 0x02 };
- abis_nm_perform_test(trx->bts, 2, 0, 0, 0xff,
- net_listen_testnr, 1,
- phys_config, sizeof(phys_config));
+
+ if (net_listen_testnr) {
+ u_int8_t phys_config[] = { 0x02, 0x0a, 0x00, 0x01, 0x02 };
+ abis_nm_perform_test(trx->bts, 2, 0, 0, 0xff,
+ net_listen_testnr, 1,
+ phys_config, sizeof(phys_config));
+ } else if (software) {
+ int rc;
+ printf("Attempting software upload with '%s'\n", software);
+ rc = abis_nm_software_load(trx->bts, software, 19, 0, swload_cbfn, trx->bts);
+ if (rc < 0) {
+ fprintf(stderr, "Failed to start software load\n");
+ exit(-3);
+ }
+ }
}
return 0;
}
+static struct sw_load *create_swload(struct sdp_header *header)
+{
+ struct sw_load *load;
+
+ load = talloc_zero(tall_ctx_config, struct sw_load);
+
+ strncpy((char *)load->file_id, header->firmware_info.sw_part, 20);
+ load->file_id_len = strlen(header->firmware_info.sw_part) + 1;
+
+ strncpy((char *)load->file_version, header->firmware_info.version, 20);
+ load->file_version_len = strlen(header->firmware_info.version) + 1;
+
+ return load;
+}
+
+static void find_sw_load_params(const char *filename)
+{
+ struct stat stat;
+ struct sdp_header *header;
+ struct llist_head *entry;
+ int fd;
+ void *tall_firm_ctx = 0;
+
+ entry = talloc_zero(tall_firm_ctx, struct llist_head);
+ INIT_LLIST_HEAD(entry);
+
+ fd = open(filename, O_RDONLY);
+ if (!fd) {
+ perror("nada");
+ return;
+ }
+
+ /* verify the file */
+ if (fstat(fd, &stat) == -1) {
+ perror("Can not stat the file");
+ return;
+ }
+
+ ipaccess_analyze_file(fd, stat.st_size, 0, entry);
+ if (close(fd) != 0) {
+ perror("Close failed.\n");
+ return;
+ }
+
+ /* try to find what we are looking for */
+ llist_for_each_entry(header, entry, entry) {
+ if (ntohs(header->firmware_info.more_more_magic) == 0x1000) {
+ sw_load1 = create_swload(header);
+ } else if (ntohs(header->firmware_info.more_more_magic) == 0x2001) {
+ sw_load2 = create_swload(header);
+ }
+ }
+
+ talloc_free(tall_firm_ctx);
+}
+
+static void analyze_firmware(const char *filename)
+{
+ struct stat stat;
+ struct sdp_header *header;
+ struct sdp_header_item *sub_entry;
+ struct llist_head *entry;
+ int fd;
+ void *tall_firm_ctx = 0;
+
+ entry = talloc_zero(tall_firm_ctx, struct llist_head);
+ INIT_LLIST_HEAD(entry);
+
+ printf("Opening possible firmware '%s'\n", filename);
+ fd = open(filename, O_RDONLY);
+ if (!fd) {
+ perror("nada");
+ return;
+ }
+
+ /* verify the file */
+ if (fstat(fd, &stat) == -1) {
+ perror("Can not stat the file");
+ return;
+ }
+
+ ipaccess_analyze_file(fd, stat.st_size, 0, entry);
+ if (close(fd) != 0) {
+ perror("Close failed.\n");
+ return;
+ }
+
+ llist_for_each_entry(header, entry, entry) {
+ printf("Printing header information:\n");
+ printf("more_more_magic: 0x%x\n", ntohs(header->firmware_info.more_more_magic));
+ printf("header_length: %u\n", ntohl(header->firmware_info.header_length));
+ printf("file_length: %u\n", ntohl(header->firmware_info.file_length));
+ printf("sw_part: %.20s\n", header->firmware_info.sw_part);
+ printf("text1: %.64s\n", header->firmware_info.text1);
+ printf("time: %.12s\n", header->firmware_info.time);
+ printf("date: %.14s\n", header->firmware_info.date);
+ printf("text2: %.10s\n", header->firmware_info.text2);
+ printf("version: %.20s\n", header->firmware_info.version);
+ printf("subitems...\n");
+
+ llist_for_each_entry(sub_entry, &header->header_list, entry) {
+ printf("\tsomething1: %u\n", sub_entry->header_entry.something1);
+ printf("\ttext1: %.64s\n", sub_entry->header_entry.text1);
+ printf("\ttime: %.12s\n", sub_entry->header_entry.time);
+ printf("\tdate: %.14s\n", sub_entry->header_entry.date);
+ printf("\ttext2: %.10s\n", sub_entry->header_entry.text2);
+ printf("\tversion: %.20s\n", sub_entry->header_entry.version);
+ printf("\tlength: %u\n", ntohl(sub_entry->header_entry.length));
+ printf("\taddr1: 0x%x\n", ntohl(sub_entry->header_entry.addr1));
+ printf("\taddr2: 0x%x\n", ntohl(sub_entry->header_entry.addr2));
+ printf("\tstart: 0x%x\n", ntohl(sub_entry->header_entry.start));
+ printf("\n\n");
+ }
+ printf("\n\n");
+ }
+
+ talloc_free(tall_firm_ctx);
+}
+
static void print_usage(void)
{
printf("Usage: ipaccess-config\n");
@@ -282,6 +544,8 @@ static void print_help(void)
printf(" -l --listen testnr \tPerform specified test number\n");
printf(" -h --help this text\n");
printf(" -s --stream-id ID\n");
+ printf(" -d --software firmware\n");
+ printf(" -f --firmware firmware Provide firmware information\n");
}
int main(int argc, char **argv)
@@ -289,6 +553,15 @@ int main(int argc, char **argv)
struct gsm_bts *bts;
struct sockaddr_in sin;
int rc, option_index = 0, stream_id = 0xff;
+ struct debug_target *stderr_target;
+
+ debug_init();
+ stderr_target = debug_target_create_stderr();
+ debug_add_target(stderr_target);
+ debug_set_all_filter(stderr_target, 1);
+ debug_set_log_level(stderr_target, 0);
+ debug_parse_category_mask(stderr_target, "DNM,0");
+ bts_model_nanobts_init();
printf("ipaccess-config (C) 2009 by Harald Welte\n");
printf("This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY\n\n");
@@ -304,9 +577,11 @@ int main(int argc, char **argv)
{ "help", 0, 0, 'h' },
{ "listen", 1, 0, 'l' },
{ "stream-id", 1, 0, 's' },
+ { "software", 1, 0, 'd' },
+ { "firmware", 1, 0, 'f' },
};
- c = getopt_long(argc, argv, "u:o:rn:l:hs:", long_options,
+ c = getopt_long(argc, argv, "u:o:rn:l:hs:d:f:", long_options,
&option_index);
if (c == -1)
@@ -336,8 +611,14 @@ int main(int argc, char **argv)
break;
case 's':
stream_id = atoi(optarg);
- printf("foo: %d\n", stream_id);
break;
+ case 'd':
+ software = strdup(optarg);
+ find_sw_load_params(optarg);
+ break;
+ case 'f':
+ analyze_firmware(optarg);
+ exit(0);
case 'h':
print_usage();
print_help();
@@ -356,6 +637,10 @@ int main(int argc, char **argv)
bts = gsm_bts_alloc(gsmnet, GSM_BTS_TYPE_NANOBTS, HARDCODED_TSC,
HARDCODED_BSIC);
+ /* ip.access supports up to 4 chained TRX */
+ gsm_bts_trx_alloc(bts);
+ gsm_bts_trx_alloc(bts);
+ gsm_bts_trx_alloc(bts);
bts->oml_tei = stream_id;
register_signal_handler(SS_NM, nm_sig_cb, NULL);
diff --git a/openbsc/src/ipaccess-find.c b/openbsc/src/ipaccess/ipaccess-find.c
index f469b6788..f469b6788 100644
--- a/openbsc/src/ipaccess-find.c
+++ b/openbsc/src/ipaccess/ipaccess-find.c
diff --git a/openbsc/src/ipaccess/ipaccess-firmware.c b/openbsc/src/ipaccess/ipaccess-firmware.c
new file mode 100644
index 000000000..ed4bc9a0c
--- /dev/null
+++ b/openbsc/src/ipaccess/ipaccess-firmware.c
@@ -0,0 +1,123 @@
+/* Routines for parsing an ipacces SDP firmware file */
+
+/* (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU 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/debug.h>
+#include <openbsc/ipaccess.h>
+#include <openbsc/talloc.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define PART_LENGTH 138
+
+static_assert(sizeof(struct sdp_header_entry) == 138, right_entry);
+static_assert(sizeof(struct sdp_firmware) == 160, _right_header_length);
+
+/* more magic, the second "int" in the header */
+static char more_magic[] = { 0x10, 0x02 };
+
+int ipaccess_analyze_file(int fd, const unsigned int st_size, const unsigned int base_offset, struct llist_head *list)
+{
+ struct sdp_firmware *firmware_header = 0;
+ struct sdp_header *header;
+ char buf[4096];
+ int rc, i;
+
+ rc = read(fd, buf, sizeof(*firmware_header));
+ if (rc < 0) {
+ perror("Can not read header start.");
+ return -1;
+ }
+
+ firmware_header = (struct sdp_firmware *) &buf[0];
+ if (strncmp(firmware_header->magic, " SDP", 4) != 0) {
+ fprintf(stderr, "Wrong magic.\n");
+ return -1;
+ }
+
+ if (memcmp(firmware_header->more_magic, more_magic, 2) != 0) {
+ fprintf(stderr, "Wrong more magic. Got: 0x%x %x %x %x\n",
+ firmware_header->more_magic[0] & 0xff, firmware_header->more_magic[1] & 0xff,
+ firmware_header->more_magic[2] & 0xff, firmware_header->more_magic[3] & 0xff);
+ return -1;
+ }
+
+
+ if (!firmware_header)
+ return -1;
+
+ if (ntohl(firmware_header->file_length) != st_size) {
+ fprintf(stderr, "The filesize and the header do not match.\n");
+ return -1;
+ }
+
+ /* add the firmware */
+ header = talloc_zero(list, struct sdp_header);
+ header->firmware_info = *firmware_header;
+ INIT_LLIST_HEAD(&header->header_list);
+ llist_add(&header->entry, list);
+
+ /* this semantic appears to be only the case for 0x0000 */
+ if (firmware_header->more_more_magic != 0)
+ return -1;
+
+ if (ntohs(firmware_header->part_length) % PART_LENGTH != 0) {
+ fprintf(stderr, "The part length seems to be wrong.\n");
+ return -1;
+ }
+
+ /* look into each firmware now */
+ for (i = 0; i < ntohs(firmware_header->part_length) / PART_LENGTH; ++i) {
+ struct sdp_header_entry entry;
+ struct sdp_header_item *header_entry;
+ unsigned int offset = base_offset + sizeof(struct sdp_firmware);
+ offset += i * 138;
+
+ if (lseek(fd, offset, SEEK_SET) != offset) {
+ fprintf(stderr, "Can not seek to the offset: %u.\n", offset);
+ return -1;
+ }
+
+ rc = read(fd, &entry, sizeof(entry));
+ if (rc != sizeof(entry)) {
+ fprintf(stderr, "Can not read the header entry.\n");
+ return -1;
+ }
+
+ /* now we need to find the SDP file... */
+ offset = ntohl(entry.start) + 4 + base_offset;
+ if (lseek(fd, offset, SEEK_SET) != offset) {
+ perror("can't seek to sdp");
+ return -1;
+ }
+
+ header_entry = talloc_zero(header, struct sdp_header_item);
+ header_entry->header_entry = entry;
+ llist_add(&header_entry->entry, &header->header_list);
+
+ ipaccess_analyze_file(fd, ntohl(entry.length), offset, list);
+ }
+
+ return 0;
+}
+
diff --git a/openbsc/src/ipaccess/ipaccess-proxy.c b/openbsc/src/ipaccess/ipaccess-proxy.c
new file mode 100644
index 000000000..d018b6ebd
--- /dev/null
+++ b/openbsc/src/ipaccess/ipaccess-proxy.c
@@ -0,0 +1,1127 @@
+/* OpenBSC Abis/IP proxy ip.access nanoBTS */
+
+/* (C) 2009 by Harald Welte <laforge@gnumonks.org>
+ *
+ * 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 <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <signal.h>
+#include <time.h>
+#include <sys/fcntl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+
+#include <openbsc/gsm_data.h>
+#include <openbsc/select.h>
+#include <openbsc/tlv.h>
+#include <openbsc/msgb.h>
+#include <openbsc/debug.h>
+#include <openbsc/ipaccess.h>
+#include <openbsc/talloc.h>
+
+static struct debug_target *stderr_target;
+
+/* one instance of an ip.access protocol proxy */
+struct ipa_proxy {
+ /* socket where we listen for incoming OML from BTS */
+ struct bsc_fd oml_listen_fd;
+ /* socket where we listen for incoming RSL from BTS */
+ struct bsc_fd rsl_listen_fd;
+ /* list of BTS's (struct ipa_bts_conn */
+ struct llist_head bts_list;
+ /* the BSC reconnect timer */
+ struct timer_list reconn_timer;
+};
+
+/* global pointer to the proxy structure */
+static struct ipa_proxy *ipp;
+
+struct ipa_proxy_conn {
+ struct bsc_fd fd;
+ struct llist_head tx_queue;
+ struct ipa_bts_conn *bts_conn;
+};
+
+#define MAX_TRX 4
+
+/* represents a particular BTS in our proxy */
+struct ipa_bts_conn {
+ /* list of BTS's (ipa_proxy->bts_list) */
+ struct llist_head list;
+ /* back pointer to the proxy which we belong to */
+ struct ipa_proxy *ipp;
+ /* the unit ID as determined by CCM */
+ struct {
+ u_int16_t site_id;
+ u_int16_t bts_id;
+ } unit_id;
+
+ /* incoming connections from BTS */
+ struct ipa_proxy_conn *oml_conn;
+ struct ipa_proxy_conn *rsl_conn[MAX_TRX];
+
+ /* outgoing connections to BSC */
+ struct ipa_proxy_conn *bsc_oml_conn;
+ struct ipa_proxy_conn *bsc_rsl_conn[MAX_TRX];
+
+ /* UDP sockets for BTS and BSC injection */
+ struct bsc_fd udp_bts_fd;
+ struct bsc_fd udp_bsc_fd;
+
+ char *id_tags[0xff];
+ u_int8_t *id_resp;
+ unsigned int id_resp_len;
+};
+
+enum ipp_fd_type {
+ OML_FROM_BTS = 1,
+ RSL_FROM_BTS = 2,
+ OML_TO_BSC = 3,
+ RSL_TO_BSC = 4,
+ UDP_TO_BTS = 5,
+ UDP_TO_BSC = 6,
+};
+
+/* some of the code against we link from OpenBSC needs this */
+void *tall_bsc_ctx;
+
+static char *listen_ipaddr;
+static char *bsc_ipaddr;
+
+#define PROXY_ALLOC_SIZE 300
+
+static const u_int8_t pong[] = { 0, 1, IPAC_PROTO_IPACCESS, IPAC_MSGT_PONG };
+static const u_int8_t id_ack[] = { 0, 1, IPAC_PROTO_IPACCESS, IPAC_MSGT_ID_ACK };
+static const u_int8_t id_req[] = { 0, 17, IPAC_PROTO_IPACCESS, IPAC_MSGT_ID_GET,
+ 0x01, IPAC_IDTAG_UNIT,
+ 0x01, IPAC_IDTAG_MACADDR,
+ 0x01, IPAC_IDTAG_LOCATION1,
+ 0x01, IPAC_IDTAG_LOCATION2,
+ 0x01, IPAC_IDTAG_EQUIPVERS,
+ 0x01, IPAC_IDTAG_SWVERSION,
+ 0x01, IPAC_IDTAG_UNITNAME,
+ 0x01, IPAC_IDTAG_SERNR,
+ };
+
+static const char *idtag_names[] = {
+ [IPAC_IDTAG_SERNR] = "Serial_Number",
+ [IPAC_IDTAG_UNITNAME] = "Unit_Name",
+ [IPAC_IDTAG_LOCATION1] = "Location_1",
+ [IPAC_IDTAG_LOCATION2] = "Location_2",
+ [IPAC_IDTAG_EQUIPVERS] = "Equipment_Version",
+ [IPAC_IDTAG_SWVERSION] = "Software_Version",
+ [IPAC_IDTAG_IPADDR] = "IP_Address",
+ [IPAC_IDTAG_MACADDR] = "MAC_Address",
+ [IPAC_IDTAG_UNIT] = "Unit_ID",
+};
+
+static const char *ipac_idtag_name(int tag)
+{
+ if (tag >= ARRAY_SIZE(idtag_names))
+ return "unknown";
+
+ return idtag_names[tag];
+}
+
+static int ipac_idtag_parse(struct tlv_parsed *dec, unsigned char *buf, int len)
+{
+ u_int8_t t_len;
+ u_int8_t t_tag;
+ u_int8_t *cur = buf;
+
+ while (cur < buf + len) {
+ t_len = *cur++;
+ t_tag = *cur++;
+
+ DEBUGPC(DMI, "%s='%s' ", ipac_idtag_name(t_tag), cur);
+
+ dec->lv[t_tag].len = t_len;
+ dec->lv[t_tag].val = cur;
+
+ cur += t_len;
+ }
+ return 0;
+}
+
+static int parse_unitid(const char *str, u_int16_t *site_id, u_int16_t *bts_id,
+ u_int16_t *trx_id)
+{
+ unsigned long ul;
+ char *endptr;
+ const char *nptr;
+
+ nptr = str;
+ ul = strtoul(nptr, &endptr, 10);
+ if (endptr <= nptr)
+ return -EINVAL;
+ if (site_id)
+ *site_id = ul & 0xffff;
+
+ if (*endptr++ != '/')
+ return -EINVAL;
+
+ nptr = endptr;
+ ul = strtoul(nptr, &endptr, 10);
+ if (endptr <= nptr)
+ return -EINVAL;
+ if (bts_id)
+ *bts_id = ul & 0xffff;
+
+ if (*endptr++ != '/')
+ return -EINVAL;
+
+ nptr = endptr;
+ ul = strtoul(nptr, &endptr, 10);
+ if (endptr <= nptr)
+ return -EINVAL;
+ if (trx_id)
+ *trx_id = ul & 0xffff;
+
+ return 0;
+}
+
+static struct ipa_bts_conn *find_bts_by_unitid(struct ipa_proxy *ipp,
+ u_int16_t site_id,
+ u_int16_t bts_id)
+{
+ struct ipa_bts_conn *ipbc;
+
+ llist_for_each_entry(ipbc, &ipp->bts_list, list) {
+ if (ipbc->unit_id.site_id == site_id &&
+ ipbc->unit_id.bts_id == bts_id)
+ return ipbc;
+ }
+
+ return NULL;
+}
+
+struct ipa_proxy_conn *alloc_conn(void)
+{
+ struct ipa_proxy_conn *ipc;
+
+ ipc = talloc_zero(tall_bsc_ctx, struct ipa_proxy_conn);
+ if (!ipc)
+ return NULL;
+
+ INIT_LLIST_HEAD(&ipc->tx_queue);
+
+ return ipc;
+}
+
+static int store_idtags(struct ipa_bts_conn *ipbc, struct tlv_parsed *tlvp)
+{
+ unsigned int i, len;
+
+ for (i = 0; i <= 0xff; i++) {
+ if (!TLVP_PRESENT(tlvp, i))
+ continue;
+
+ len = TLVP_LEN(tlvp, i);
+#if 0
+ if (!ipbc->id_tags[i])
+ ipbc->id_tags[i] = talloc_size(tall_bsc_ctx, len);
+ else
+#endif
+ ipbc->id_tags[i] = talloc_realloc_size(ipbc,
+ ipbc->id_tags[i], len);
+ if (!ipbc->id_tags[i])
+ return -ENOMEM;
+
+ memset(ipbc->id_tags[i], 0, len);
+ //memcpy(ipbc->id_tags[i], TLVP_VAL(tlvp, i), len);
+ }
+ return 0;
+}
+
+
+static struct ipa_proxy_conn *connect_bsc(struct sockaddr_in *sa, int priv_nr, void *data);
+
+#define logp_ipbc_uid(ss, lvl, ipbc, trx_id) _logp_ipbc_uid(ss, lvl, __FILE__, __LINE__, ipbc, trx_id)
+
+static void _logp_ipbc_uid(unsigned int ss, unsigned int lvl, char *file, int line,
+ struct ipa_bts_conn *ipbc, u_int8_t trx_id)
+{
+ if (ipbc)
+ debugp2(ss, lvl, file, line, 0, "(%u/%u/%u) ", ipbc->unit_id.site_id,
+ ipbc->unit_id.bts_id, trx_id);
+ else
+ debugp2(ss, lvl, file, line, 0, "unknown ");
+}
+
+/* UDP socket handling */
+
+static int make_sock(struct bsc_fd *bfd, u_int16_t port, int proto, int priv_nr,
+ int (*cb)(struct bsc_fd *fd, unsigned int what),
+ void *data)
+{
+ struct sockaddr_in addr;
+ int ret, on = 1;
+
+ bfd->fd = socket(AF_INET, SOCK_DGRAM, proto);
+ bfd->cb = cb;
+ bfd->when = BSC_FD_READ;
+ bfd->data = data;
+ bfd->priv_nr = priv_nr;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(port);
+ addr.sin_addr.s_addr = INADDR_ANY;
+
+ setsockopt(bfd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
+
+ ret = bind(bfd->fd, (struct sockaddr *) &addr, sizeof(addr));
+ if (ret < 0) {
+ LOGP(DINP, LOGL_ERROR, "could not bind socket: %s\n",
+ strerror(errno));
+ return -EIO;
+ }
+
+ ret = bsc_register_fd(bfd);
+ if (ret < 0) {
+ perror("register UDP fd");
+ return ret;
+ }
+ return 0;
+}
+
+static int handle_udp_read(struct bsc_fd *bfd)
+{
+ struct ipa_bts_conn *ipbc = bfd->data;
+ struct ipa_proxy_conn *other_conn = NULL;
+ struct msgb *msg = msgb_alloc(PROXY_ALLOC_SIZE, "Abis/IP UDP");
+ struct ipaccess_head *hh;
+ int ret;
+
+ /* with UDP sockets, we cannot read partial packets but have to read
+ * all of it in one go */
+ hh = (struct ipaccess_head *) msg->data;
+ ret = recv(bfd->fd, msg->data, msg->data_len, 0);
+ if (ret < 0) {
+ if (errno != EAGAIN)
+ LOGP(DINP, LOGL_ERROR, "recv error %s\n", strerror(errno));
+ msgb_free(msg);
+ return ret;
+ }
+ if (ret == 0) {
+ DEBUGP(DINP, "UDP peer disappeared, dead socket\n");
+ bsc_unregister_fd(bfd);
+ close(bfd->fd);
+ bfd->fd = -1;
+ msgb_free(msg);
+ return -EIO;
+ }
+ if (ret < sizeof(*hh)) {
+ DEBUGP(DINP, "could not even read header!?!\n");
+ msgb_free(msg);
+ return -EIO;
+ }
+ msgb_put(msg, ret);
+ msg->l2h = msg->data + sizeof(*hh);
+ DEBUGP(DMI, "UDP RX: %s\n", hexdump(msg->data, msg->len));
+
+ if (hh->len != msg->len - sizeof(*hh)) {
+ DEBUGP(DINP, "length (%u/%u) disagrees with header(%u)\n",
+ msg->len, msg->len - 3, hh->len);
+ msgb_free(msg);
+ return -EIO;
+ }
+
+ switch (bfd->priv_nr & 0xff) {
+ case UDP_TO_BTS:
+ /* injection towards BTS */
+ switch (hh->proto) {
+ case IPAC_PROTO_RSL:
+ /* FIXME: what to do about TRX > 0 */
+ other_conn = ipbc->rsl_conn[0];
+ break;
+ default:
+ DEBUGP(DINP, "Unknown protocol 0x%02x, sending to "
+ "OML FD\n", hh->proto);
+ /* fall through */
+ case IPAC_PROTO_IPACCESS:
+ case IPAC_PROTO_OML:
+ other_conn = ipbc->oml_conn;
+ break;
+ }
+ break;
+ case UDP_TO_BSC:
+ /* injection towards BSC */
+ switch (hh->proto) {
+ case IPAC_PROTO_RSL:
+ /* FIXME: what to do about TRX > 0 */
+ other_conn = ipbc->bsc_rsl_conn[0];
+ break;
+ default:
+ DEBUGP(DINP, "Unknown protocol 0x%02x, sending to "
+ "OML FD\n", hh->proto);
+ case IPAC_PROTO_IPACCESS:
+ case IPAC_PROTO_OML:
+ other_conn = ipbc->bsc_oml_conn;
+ break;
+ }
+ break;
+ default:
+ DEBUGP(DINP, "Unknown filedescriptor priv_nr=%04x\n", bfd->priv_nr);
+ break;
+ }
+
+ if (other_conn) {
+ /* enqueue the message for TX on the respective FD */
+ msgb_enqueue(&other_conn->tx_queue, msg);
+ other_conn->fd.when |= BSC_FD_WRITE;
+ } else
+ msgb_free(msg);
+
+ return 0;
+}
+
+static int handle_udp_write(struct bsc_fd *bfd)
+{
+ /* not implemented yet */
+ bfd->when &= ~BSC_FD_WRITE;
+
+ return -EIO;
+}
+
+/* callback from select.c in case one of the fd's can be read/written */
+static int udp_fd_cb(struct bsc_fd *bfd, unsigned int what)
+{
+ int rc = 0;
+
+ if (what & BSC_FD_READ)
+ rc = handle_udp_read(bfd);
+ if (what & BSC_FD_WRITE)
+ rc = handle_udp_write(bfd);
+
+ return rc;
+}
+
+
+static int ipbc_alloc_connect(struct ipa_proxy_conn *ipc, struct bsc_fd *bfd,
+ u_int16_t site_id, u_int16_t bts_id,
+ u_int16_t trx_id, struct tlv_parsed *tlvp,
+ struct msgb *msg)
+{
+ struct ipa_bts_conn *ipbc;
+ u_int16_t udp_port;
+ int ret = 0;
+ struct sockaddr_in sin;
+
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ inet_aton(bsc_ipaddr, &sin.sin_addr);
+
+ DEBUGP(DINP, "(%u/%u/%u) New BTS connection: ",
+ site_id, bts_id, trx_id);
+
+ /* OML needs to be established before RSL */
+ if ((bfd->priv_nr & 0xff) != OML_FROM_BTS) {
+ DEBUGPC(DINP, "Not a OML connection ?!?\n");
+ return -EIO;
+ }
+
+ /* allocate new BTS connection data structure */
+ ipbc = talloc_zero(tall_bsc_ctx, struct ipa_bts_conn);
+ if (!ipbc) {
+ ret = -ENOMEM;
+ goto err_out;
+ }
+
+ DEBUGPC(DINP, "Created BTS Conn data structure\n");
+ ipbc->ipp = ipp;
+ ipbc->unit_id.site_id = site_id;
+ ipbc->unit_id.bts_id = bts_id;
+ ipbc->oml_conn = ipc;
+ ipc->bts_conn = ipbc;
+
+ /* store the content of the ID TAGS for later reference */
+ store_idtags(ipbc, tlvp);
+ ipbc->id_resp_len = msg->len;
+ ipbc->id_resp = talloc_size(tall_bsc_ctx, ipbc->id_resp_len);
+ memcpy(ipbc->id_resp, msg->data, ipbc->id_resp_len);
+
+ /* Create OML TCP connection towards BSC */
+ sin.sin_port = htons(IPA_TCP_PORT_OML);
+ ipbc->bsc_oml_conn = connect_bsc(&sin, OML_TO_BSC, ipbc);
+ if (!ipbc->bsc_oml_conn) {
+ ret = -EIO;
+ goto err_bsc_conn;
+ }
+
+ DEBUGP(DINP, "(%u/%u/%u) OML Connected to BSC\n",
+ site_id, bts_id, trx_id);
+
+ /* Create UDP socket for BTS packet injection */
+ udp_port = 10000 + (site_id % 1000)*100 + (bts_id % 100);
+ ret = make_sock(&ipbc->udp_bts_fd, udp_port, IPPROTO_UDP,
+ UDP_TO_BTS, udp_fd_cb, ipbc);
+ if (ret < 0)
+ goto err_udp_bts;
+ DEBUGP(DINP, "(%u/%u/%u) Created UDP socket for injection "
+ "towards BTS at port %u\n", site_id, bts_id, trx_id, udp_port);
+
+ /* Create UDP socket for BSC packet injection */
+ udp_port = 20000 + (site_id % 1000)*100 + (bts_id % 100);
+ ret = make_sock(&ipbc->udp_bsc_fd, udp_port, IPPROTO_UDP,
+ UDP_TO_BSC, udp_fd_cb, ipbc);
+ if (ret < 0)
+ goto err_udp_bsc;
+ DEBUGP(DINP, "(%u/%u/%u) Created UDP socket for injection "
+ "towards BSC at port %u\n", site_id, bts_id, trx_id, udp_port);
+ llist_add(&ipbc->list, &ipp->bts_list);
+
+ return 0;
+
+err_udp_bsc:
+ bsc_unregister_fd(&ipbc->udp_bts_fd);
+err_udp_bts:
+ bsc_unregister_fd(&ipbc->bsc_oml_conn->fd);
+ close(ipbc->bsc_oml_conn->fd.fd);
+ talloc_free(ipbc->bsc_oml_conn);
+ ipbc->bsc_oml_conn = NULL;
+err_bsc_conn:
+ talloc_free(ipbc->id_resp);
+ talloc_free(ipbc);
+#if 0
+ bsc_unregister_fd(bfd);
+ close(bfd->fd);
+ talloc_free(bfd);
+#endif
+err_out:
+ return ret;
+}
+
+static int ipaccess_rcvmsg(struct ipa_proxy_conn *ipc, struct msgb *msg,
+ struct bsc_fd *bfd)
+{
+ struct tlv_parsed tlvp;
+ u_int8_t msg_type = *(msg->l2h);
+ u_int16_t site_id, bts_id, trx_id;
+ struct ipa_bts_conn *ipbc;
+ int ret = 0;
+
+ switch (msg_type) {
+ case IPAC_MSGT_PING:
+ ret = write(bfd->fd, pong, sizeof(pong));
+ if (ret < 0)
+ return ret;
+ if (ret < sizeof(pong)) {
+ DEBUGP(DINP, "short write\n");
+ return -EIO;
+ }
+ break;
+ case IPAC_MSGT_PONG:
+ DEBUGP(DMI, "PONG!\n");
+ break;
+ case IPAC_MSGT_ID_RESP:
+ DEBUGP(DMI, "ID_RESP ");
+ /* parse tags, search for Unit ID */
+ ipac_idtag_parse(&tlvp, (u_int8_t *)msg->l2h + 2,
+ msgb_l2len(msg)-2);
+ DEBUGP(DMI, "\n");
+
+ if (!TLVP_PRESENT(&tlvp, IPAC_IDTAG_UNIT)) {
+ LOGP(DINP, LOGL_ERROR, "No Unit ID in ID RESPONSE !?!\n");
+ return -EIO;
+ }
+
+ /* lookup BTS, create sign_link, ... */
+ parse_unitid((char *)TLVP_VAL(&tlvp, IPAC_IDTAG_UNIT),
+ &site_id, &bts_id, &trx_id);
+ ipbc = find_bts_by_unitid(ipp, site_id, bts_id);
+ if (!ipbc) {
+ /* We have not found an ipbc (per-bts proxy instance)
+ * for this BTS yet. The first connection of a new BTS must
+ * be a OML connection. We allocate the associated data structures,
+ * and try to connect to the remote end */
+
+ return ipbc_alloc_connect(ipc, bfd, site_id, bts_id,
+ trx_id, &tlvp, msg);
+ /* if this fails, the caller will clean up bfd */
+ } else {
+ struct sockaddr_in sin;
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ inet_aton(bsc_ipaddr, &sin.sin_addr);
+
+ DEBUGP(DINP, "Identified BTS %u/%u/%u\n",
+ site_id, bts_id, trx_id);
+
+ if ((bfd->priv_nr & 0xff) != RSL_FROM_BTS) {
+ LOGP(DINP, LOGL_ERROR, "Second OML connection from "
+ "same BTS ?!?\n");
+ return 0;
+ }
+
+ if (trx_id > MAX_TRX) {
+ LOGP(DINP, LOGL_ERROR, "We don't support more "
+ "than %u TRX\n", MAX_TRX);
+ return -EINVAL;
+ }
+
+ ipc->bts_conn = ipbc;
+ /* store TRX number in higher 8 bit of the bfd private number */
+ bfd->priv_nr |= trx_id << 8;
+ ipbc->rsl_conn[trx_id] = ipc;
+
+ /* Create RSL TCP connection towards BSC */
+ sin.sin_port = htons(IPA_TCP_PORT_RSL);
+ ipbc->bsc_rsl_conn[trx_id] =
+ connect_bsc(&sin, RSL_TO_BSC | (trx_id << 8), ipbc);
+ if (!ipbc->bsc_oml_conn)
+ return -EIO;
+ DEBUGP(DINP, "(%u/%u/%u) Connected RSL to BSC\n",
+ site_id, bts_id, trx_id);
+ }
+ break;
+ case IPAC_MSGT_ID_GET:
+ DEBUGP(DMI, "ID_GET\n");
+ if ((bfd->priv_nr & 0xff) != OML_TO_BSC &&
+ (bfd->priv_nr & 0xff) != RSL_TO_BSC) {
+ DEBUGP(DINP, "IDentity REQuest from BTS ?!?\n");
+ return -EIO;
+ }
+ ipbc = ipc->bts_conn;
+ if (!ipbc) {
+ DEBUGP(DINP, "ID_GET from BSC before we have ID_RESP from BTS\n");
+ return -EIO;
+ }
+ ret = write(bfd->fd, ipbc->id_resp, ipbc->id_resp_len);
+ break;
+ case IPAC_MSGT_ID_ACK:
+ DEBUGP(DMI, "ID_ACK? -> ACK!\n");
+ ret = write(bfd->fd, id_ack, sizeof(id_ack));
+ break;
+ }
+ return 0;
+}
+
+struct msgb *ipaccess_read_msg(struct bsc_fd *bfd, int *error)
+{
+ struct msgb *msg = msgb_alloc(PROXY_ALLOC_SIZE, "Abis/IP");
+ struct ipaccess_head *hh;
+ int len, ret = 0;
+
+ if (!msg) {
+ *error = -ENOMEM;
+ return NULL;
+ }
+
+ /* first read our 3-byte header */
+ hh = (struct ipaccess_head *) msg->data;
+ ret = recv(bfd->fd, msg->data, 3, 0);
+ if (ret < 0) {
+ if (errno != EAGAIN)
+ LOGP(DINP, LOGL_ERROR, "recv error: %s\n", strerror(errno));
+ msgb_free(msg);
+ *error = ret;
+ return NULL;
+ } else if (ret == 0) {
+ msgb_free(msg);
+ *error = ret;
+ return NULL;
+ }
+
+ msgb_put(msg, ret);
+
+ /* then read te length as specified in header */
+ msg->l2h = msg->data + sizeof(*hh);
+ len = ntohs(hh->len);
+ ret = recv(bfd->fd, msg->l2h, len, 0);
+ if (ret < len) {
+ LOGP(DINP, LOGL_ERROR, "short read!\n");
+ msgb_free(msg);
+ *error = -EIO;
+ return NULL;
+ }
+ msgb_put(msg, ret);
+
+ return msg;
+}
+
+static struct ipa_proxy_conn *ipc_by_priv_nr(struct ipa_bts_conn *ipbc,
+ unsigned int priv_nr)
+{
+ struct ipa_proxy_conn *bsc_conn;
+ unsigned int trx_id = priv_nr >> 8;
+
+ switch (priv_nr & 0xff) {
+ case OML_FROM_BTS: /* incoming OML data from BTS, forward to BSC OML */
+ bsc_conn = ipbc->bsc_oml_conn;
+ break;
+ case RSL_FROM_BTS: /* incoming RSL data from BTS, forward to BSC RSL */
+ bsc_conn = ipbc->bsc_rsl_conn[trx_id];
+ break;
+ case OML_TO_BSC: /* incoming OML data from BSC, forward to BTS OML */
+ bsc_conn = ipbc->oml_conn;
+ break;
+ case RSL_TO_BSC: /* incoming RSL data from BSC, forward to BTS RSL */
+ bsc_conn = ipbc->rsl_conn[trx_id];
+ break;
+ default:
+ bsc_conn = NULL;
+ break;
+ }
+ return bsc_conn;
+}
+
+static void reconn_tmr_cb(void *data)
+{
+ struct ipa_proxy *ipp = data;
+ struct ipa_bts_conn *ipbc;
+ struct sockaddr_in sin;
+ int i;
+
+ DEBUGP(DINP, "Running reconnect timer\n");
+
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ inet_aton(bsc_ipaddr, &sin.sin_addr);
+
+ llist_for_each_entry(ipbc, &ipp->bts_list, list) {
+ /* if OML to BSC is dead, try to restore it */
+ if (ipbc->oml_conn && !ipbc->bsc_oml_conn) {
+ sin.sin_port = htons(IPA_TCP_PORT_OML);
+ logp_ipbc_uid(DINP, LOGL_NOTICE, ipbc, 0);
+ LOGPC(DINP, LOGL_NOTICE, "OML Trying to reconnect\n");
+ ipbc->bsc_oml_conn = connect_bsc(&sin, OML_TO_BSC, ipbc);
+ if (!ipbc->bsc_oml_conn)
+ goto reschedule;
+ logp_ipbc_uid(DINP, LOGL_NOTICE, ipbc, 0);
+ LOGPC(DINP, LOGL_NOTICE, "OML Reconnected\n");
+ }
+ /* if we (still) don't have a OML connection, skip RSL */
+ if (!ipbc->oml_conn || !ipbc->bsc_oml_conn)
+ continue;
+
+ for (i = 0; i < ARRAY_SIZE(ipbc->rsl_conn); i++) {
+ unsigned int priv_nr;
+ /* don't establish RSL links which we don't have */
+ if (!ipbc->rsl_conn[i])
+ continue;
+ if (ipbc->bsc_rsl_conn[i])
+ continue;
+ priv_nr = ipbc->rsl_conn[i]->fd.priv_nr;
+ priv_nr &= ~0xff;
+ priv_nr |= RSL_TO_BSC;
+ sin.sin_port = htons(IPA_TCP_PORT_RSL);
+ logp_ipbc_uid(DINP, LOGL_NOTICE, ipbc, priv_nr >> 8);
+ LOGPC(DINP, LOGL_NOTICE, "RSL Trying to reconnect\n");
+ ipbc->bsc_rsl_conn[i] = connect_bsc(&sin, priv_nr, ipbc);
+ if (!ipbc->bsc_rsl_conn)
+ goto reschedule;
+ logp_ipbc_uid(DINP, LOGL_NOTICE, ipbc, priv_nr >> 8);
+ LOGPC(DINP, LOGL_NOTICE, "RSL Reconnected\n");
+ }
+ }
+ return;
+
+reschedule:
+ bsc_schedule_timer(&ipp->reconn_timer, 5, 0);
+}
+
+static void handle_dead_socket(struct bsc_fd *bfd)
+{
+ struct ipa_proxy_conn *ipc = bfd->data; /* local conn */
+ struct ipa_proxy_conn *bsc_conn; /* remote conn */
+ struct ipa_bts_conn *ipbc = ipc->bts_conn;
+ unsigned int trx_id = bfd->priv_nr >> 8;
+ struct msgb *msg, *msg2;
+
+ bsc_unregister_fd(bfd);
+ close(bfd->fd);
+ bfd->fd = -1;
+
+ /* FIXME: clear tx_queue, remove all references, etc. */
+ llist_for_each_entry_safe(msg, msg2, &ipc->tx_queue, list)
+ msgb_free(msg);
+
+ switch (bfd->priv_nr & 0xff) {
+ case OML_FROM_BTS: /* incoming OML data from BTS, forward to BSC OML */
+ ipbc->oml_conn = NULL;
+ bsc_conn = ipbc->bsc_oml_conn;
+ /* close the connection to the BSC */
+ bsc_unregister_fd(&bsc_conn->fd);
+ close(bsc_conn->fd.fd);
+ llist_for_each_entry_safe(msg, msg2, &bsc_conn->tx_queue, list)
+ msgb_free(msg);
+ talloc_free(bsc_conn);
+ ipbc->bsc_oml_conn = NULL;
+ /* FIXME: do we need to delete the entire ipbc ? */
+ break;
+ case RSL_FROM_BTS: /* incoming RSL data from BTS, forward to BSC RSL */
+ ipbc->rsl_conn[trx_id] = NULL;
+ bsc_conn = ipbc->bsc_rsl_conn[trx_id];
+ /* close the connection to the BSC */
+ bsc_unregister_fd(&bsc_conn->fd);
+ close(bsc_conn->fd.fd);
+ llist_for_each_entry_safe(msg, msg2, &bsc_conn->tx_queue, list)
+ msgb_free(msg);
+ talloc_free(bsc_conn);
+ ipbc->bsc_rsl_conn[trx_id] = NULL;
+ break;
+ case OML_TO_BSC: /* incoming OML data from BSC, forward to BTS OML */
+ ipbc->bsc_oml_conn = NULL;
+ bsc_conn = ipbc->oml_conn;
+ /* start reconnect timer */
+ bsc_schedule_timer(&ipp->reconn_timer, 5, 0);
+ break;
+ case RSL_TO_BSC: /* incoming RSL data from BSC, forward to BTS RSL */
+ ipbc->bsc_rsl_conn[trx_id] = NULL;
+ bsc_conn = ipbc->rsl_conn[trx_id];
+ /* start reconnect timer */
+ bsc_schedule_timer(&ipp->reconn_timer, 5, 0);
+ break;
+ default:
+ bsc_conn = NULL;
+ break;
+ }
+
+ talloc_free(ipc);
+}
+
+static int handle_tcp_read(struct bsc_fd *bfd)
+{
+ struct ipa_proxy_conn *ipc = bfd->data;
+ struct ipa_bts_conn *ipbc = ipc->bts_conn;
+ struct ipa_proxy_conn *bsc_conn;
+ struct msgb *msg;
+ struct ipaccess_head *hh;
+ int ret = 0;
+ char *btsbsc;
+
+ if ((bfd->priv_nr & 0xff) <= 2)
+ btsbsc = "BTS";
+ else
+ btsbsc = "BSC";
+
+ msg = ipaccess_read_msg(bfd, &ret);
+ if (!msg) {
+ if (ret == 0) {
+ logp_ipbc_uid(DINP, LOGL_NOTICE, ipbc, bfd->priv_nr >> 8);
+ LOGPC(DINP, LOGL_NOTICE, "%s disappeared, "
+ "dead socket\n", btsbsc);
+ handle_dead_socket(bfd);
+ }
+ return ret;
+ }
+
+ msgb_put(msg, ret);
+ logp_ipbc_uid(DMI, LOGL_DEBUG, ipbc, bfd->priv_nr >> 8);
+ DEBUGPC(DMI, "RX<-%s: %s\n", btsbsc, hexdump(msg->data, msg->len));
+
+ hh = (struct ipaccess_head *) msg->data;
+ if (hh->proto == IPAC_PROTO_IPACCESS) {
+ ret = ipaccess_rcvmsg(ipc, msg, bfd);
+ if (ret < 0) {
+ bsc_unregister_fd(bfd);
+ close(bfd->fd);
+ bfd->fd = -1;
+ talloc_free(bfd);
+ }
+ /* we do not forward the CCM protocol through the
+ * proxy but rather terminate it ourselves */
+ msgb_free(msg);
+ return ret;
+ }
+
+ if (!ipbc) {
+ LOGP(DINP, LOGL_ERROR,
+ "received %s packet but no ipc->bts_conn?!?\n", btsbsc);
+ msgb_free(msg);
+ return -EIO;
+ }
+
+ bsc_conn = ipc_by_priv_nr(ipbc, bfd->priv_nr);
+ if (bsc_conn) {
+ /* enqueue packet towards BSC */
+ msgb_enqueue(&bsc_conn->tx_queue, msg);
+ /* mark respective filedescriptor as 'we want to write' */
+ bsc_conn->fd.when |= BSC_FD_WRITE;
+ } else {
+ logp_ipbc_uid(DINP, LOGL_INFO, ipbc, bfd->priv_nr >> 8);
+ LOGPC(DINP, LOGL_INFO, "Dropping packet from %s, "
+ "since remote connection is dead\n", btsbsc);
+ msgb_free(msg);
+ }
+
+ return ret;
+}
+
+/* a TCP socket is ready to be written to */
+static int handle_tcp_write(struct bsc_fd *bfd)
+{
+ struct ipa_proxy_conn *ipc = bfd->data;
+ struct ipa_bts_conn *ipbc = ipc->bts_conn;
+ struct llist_head *lh;
+ struct msgb *msg;
+ char *btsbsc;
+ int ret;
+
+ if ((bfd->priv_nr & 0xff) <= 2)
+ btsbsc = "BTS";
+ else
+ btsbsc = "BSC";
+
+
+ /* get the next msg for this timeslot */
+ if (llist_empty(&ipc->tx_queue)) {
+ bfd->when &= ~BSC_FD_WRITE;
+ return 0;
+ }
+ lh = ipc->tx_queue.next;
+ llist_del(lh);
+ msg = llist_entry(lh, struct msgb, list);
+
+ logp_ipbc_uid(DMI, LOGL_DEBUG, ipbc, bfd->priv_nr >> 8);
+ DEBUGPC(DMI, "TX %04x: %s\n", bfd->priv_nr,
+ hexdump(msg->data, msg->len));
+
+ ret = send(bfd->fd, msg->data, msg->len, 0);
+ msgb_free(msg);
+
+ if (ret == 0) {
+ logp_ipbc_uid(DINP, LOGL_NOTICE, ipbc, bfd->priv_nr >> 8);
+ LOGP(DINP, LOGL_NOTICE, "%s disappeared, dead socket\n", btsbsc);
+ handle_dead_socket(bfd);
+ }
+
+ return ret;
+}
+
+/* callback from select.c in case one of the fd's can be read/written */
+static int ipaccess_fd_cb(struct bsc_fd *bfd, unsigned int what)
+{
+ int rc = 0;
+
+ if (what & BSC_FD_READ) {
+ rc = handle_tcp_read(bfd);
+ if (rc < 0)
+ return rc;
+ }
+ if (what & BSC_FD_WRITE)
+ rc = handle_tcp_write(bfd);
+
+ return rc;
+}
+
+/* callback of the listening filedescriptor */
+static int listen_fd_cb(struct bsc_fd *listen_bfd, unsigned int what)
+{
+ int ret;
+ struct ipa_proxy_conn *ipc;
+ struct bsc_fd *bfd;
+ struct sockaddr_in sa;
+ socklen_t sa_len = sizeof(sa);
+
+ if (!(what & BSC_FD_READ))
+ return 0;
+
+ ret = accept(listen_bfd->fd, (struct sockaddr *) &sa, &sa_len);
+ if (ret < 0) {
+ perror("accept");
+ return ret;
+ }
+ DEBUGP(DINP, "accept()ed new %s link from %s\n",
+ (listen_bfd->priv_nr & 0xff) == OML_FROM_BTS ? "OML" : "RSL",
+ inet_ntoa(sa.sin_addr));
+
+ ipc = alloc_conn();
+ if (!ipc) {
+ close(ret);
+ return -ENOMEM;
+ }
+
+ bfd = &ipc->fd;
+ bfd->fd = ret;
+ bfd->data = ipc;
+ bfd->priv_nr = listen_bfd->priv_nr;
+ bfd->cb = ipaccess_fd_cb;
+ bfd->when = BSC_FD_READ;
+ ret = bsc_register_fd(bfd);
+ if (ret < 0) {
+ LOGP(DINP, LOGL_ERROR, "could not register FD\n");
+ close(bfd->fd);
+ talloc_free(ipc);
+ return ret;
+ }
+
+ /* Request ID. FIXME: request LOCATION, HW/SW VErsion, Unit Name, Serno */
+ ret = write(bfd->fd, id_req, sizeof(id_req));
+
+ return 0;
+}
+
+static int make_listen_sock(struct bsc_fd *bfd, u_int16_t port, int priv_nr,
+ int (*cb)(struct bsc_fd *fd, unsigned int what))
+{
+ struct sockaddr_in addr;
+ int ret, on = 1;
+
+ bfd->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ bfd->cb = cb;
+ bfd->when = BSC_FD_READ;
+ bfd->priv_nr = priv_nr;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(port);
+ if (!listen_ipaddr)
+ addr.sin_addr.s_addr = INADDR_ANY;
+ else
+ inet_aton(listen_ipaddr, &addr.sin_addr);
+
+ setsockopt(bfd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
+
+ ret = bind(bfd->fd, (struct sockaddr *) &addr, sizeof(addr));
+ if (ret < 0) {
+ LOGP(DINP, LOGL_ERROR, "could not bind listen socket %s\n",
+ strerror(errno));
+ return -EIO;
+ }
+
+ ret = listen(bfd->fd, 1);
+ if (ret < 0) {
+ perror("listen");
+ return ret;
+ }
+
+ ret = bsc_register_fd(bfd);
+ if (ret < 0) {
+ perror("register_listen_fd");
+ return ret;
+ }
+ return 0;
+}
+
+/* Actively connect to a BSC. */
+static struct ipa_proxy_conn *connect_bsc(struct sockaddr_in *sa, int priv_nr, void *data)
+{
+ struct ipa_proxy_conn *ipc;
+ struct bsc_fd *bfd;
+ int ret, on = 1;
+
+ ipc = alloc_conn();
+ if (!ipc)
+ return NULL;
+
+ ipc->bts_conn = data;
+
+ bfd = &ipc->fd;
+ bfd->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ bfd->cb = ipaccess_fd_cb;
+ bfd->when = BSC_FD_READ | BSC_FD_WRITE;
+ bfd->data = ipc;
+ bfd->priv_nr = priv_nr;
+
+ setsockopt(bfd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
+
+ ret = connect(bfd->fd, (struct sockaddr *) sa, sizeof(*sa));
+ if (ret < 0) {
+ LOGP(DINP, LOGL_ERROR, "could not connect socket\n");
+ close(bfd->fd);
+ talloc_free(ipc);
+ return NULL;
+ }
+
+ /* pre-fill tx_queue with identity request */
+ ret = bsc_register_fd(bfd);
+ if (ret < 0) {
+ close(bfd->fd);
+ talloc_free(ipc);
+ return NULL;
+ }
+
+ return ipc;
+}
+
+static int ipaccess_proxy_setup(void)
+{
+ int ret;
+
+ ipp = talloc_zero(tall_bsc_ctx, struct ipa_proxy);
+ if (!ipp)
+ return -ENOMEM;
+ INIT_LLIST_HEAD(&ipp->bts_list);
+ ipp->reconn_timer.cb = reconn_tmr_cb;
+ ipp->reconn_timer.data = ipp;
+
+ /* Listen for OML connections */
+ ret = make_listen_sock(&ipp->oml_listen_fd, IPA_TCP_PORT_OML,
+ OML_FROM_BTS, listen_fd_cb);
+ if (ret < 0)
+ return ret;
+
+ /* Listen for RSL connections */
+ ret = make_listen_sock(&ipp->rsl_listen_fd, IPA_TCP_PORT_RSL,
+ RSL_FROM_BTS, listen_fd_cb);
+
+ return ret;
+}
+
+static void signal_handler(int signal)
+{
+ fprintf(stdout, "signal %u received\n", signal);
+
+ switch (signal) {
+ case SIGABRT:
+ /* in case of abort, we want to obtain a talloc report
+ * and then return to the caller, who will abort the process */
+ case SIGUSR1:
+ talloc_report_full(tall_bsc_ctx, stderr);
+ break;
+ default:
+ break;
+ }
+}
+
+int main(int argc, char **argv)
+{
+ int rc;
+
+ listen_ipaddr = "192.168.100.11";
+ bsc_ipaddr = "192.168.100.239";
+
+ tall_bsc_ctx = talloc_named_const(NULL, 1, "ipaccess-proxy");
+
+ debug_init();
+ stderr_target = debug_target_create_stderr();
+ debug_add_target(stderr_target);
+ debug_set_all_filter(stderr_target, 1);
+ debug_parse_category_mask(stderr_target, "DINP:DMI");
+
+ rc = ipaccess_proxy_setup();
+ if (rc < 0)
+ exit(1);
+
+ signal(SIGUSR1, &signal_handler);
+ signal(SIGABRT, &signal_handler);
+
+ while (1) {
+ bsc_select_main(0);
+ }
+}
diff --git a/openbsc/src/meas_rep.c b/openbsc/src/meas_rep.c
new file mode 100644
index 000000000..4b9cc1a0c
--- /dev/null
+++ b/openbsc/src/meas_rep.c
@@ -0,0 +1,114 @@
+/* Measurement Report Processing */
+
+/* (C) 2009 by Harald Welte <laforge@gnumonks.org>
+ *
+ * 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 <sys/types.h>
+
+#include <openbsc/gsm_data.h>
+#include <openbsc/meas_rep.h>
+
+static int get_field(const struct gsm_meas_rep *rep,
+ enum meas_rep_field field)
+{
+ switch (field) {
+ case MEAS_REP_DL_RXLEV_FULL:
+ return rep->dl.full.rx_lev;
+ case MEAS_REP_DL_RXLEV_SUB:
+ return rep->dl.sub.rx_lev;
+ case MEAS_REP_DL_RXQUAL_FULL:
+ return rep->dl.full.rx_qual;
+ case MEAS_REP_DL_RXQUAL_SUB:
+ return rep->dl.sub.rx_qual;
+ case MEAS_REP_UL_RXLEV_FULL:
+ return rep->ul.full.rx_lev;
+ case MEAS_REP_UL_RXLEV_SUB:
+ return rep->ul.sub.rx_lev;
+ case MEAS_REP_UL_RXQUAL_FULL:
+ return rep->ul.full.rx_qual;
+ case MEAS_REP_UL_RXQUAL_SUB:
+ return rep->ul.sub.rx_qual;
+ }
+
+ return 0;
+}
+
+
+unsigned int calc_initial_idx(unsigned int array_size,
+ unsigned int meas_rep_idx,
+ unsigned int num_values)
+{
+ int offs, idx;
+
+ /* from which element do we need to start if we're interested
+ * in an average of 'num' elements */
+ offs = meas_rep_idx - num_values;
+
+ if (offs < 0)
+ idx = array_size + offs;
+ else
+ idx = offs;
+
+ return idx;
+}
+
+/* obtain an average over the last 'num' fields in the meas reps */
+int get_meas_rep_avg(const struct gsm_lchan *lchan,
+ enum meas_rep_field field, unsigned int num)
+{
+ unsigned int i, idx;
+ int avg = 0;
+
+ idx = calc_initial_idx(ARRAY_SIZE(lchan->meas_rep),
+ lchan->meas_rep_idx, num);
+
+ for (i = 0; i < num; i++) {
+ int j = (idx+i) % ARRAY_SIZE(lchan->meas_rep);
+
+ avg += get_field(&lchan->meas_rep[j], field);
+ }
+
+ return avg / num;
+}
+
+/* Check if N out of M last values for FIELD are >= bd */
+int meas_rep_n_out_of_m_be(const struct gsm_lchan *lchan,
+ enum meas_rep_field field,
+ unsigned int n, unsigned int m, int be)
+{
+ unsigned int i, idx;
+ int count = 0;
+
+ idx = calc_initial_idx(ARRAY_SIZE(lchan->meas_rep),
+ lchan->meas_rep_idx, m);
+
+ for (i = 0; i < m; i++) {
+ int j = (idx + i) % ARRAY_SIZE(lchan->meas_rep);
+ int val = get_field(&lchan->meas_rep[j], field);
+
+ if (val >= be)
+ count++;
+
+ if (count >= n)
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/openbsc/src/mgcp.cfg b/openbsc/src/mgcp.cfg
new file mode 100644
index 000000000..678f54637
--- /dev/null
+++ b/openbsc/src/mgcp.cfg
@@ -0,0 +1,19 @@
+!
+! MGCP configuration hand edited
+! !
+password foo
+!
+line vty
+ no login
+!
+mgcp
+! local ip 213.167.134.14
+ bts ip 172.16.252.43
+ bind ip 213.167.134.141
+ bind port 2427
+ bind early 1
+ rtp base 4000
+ sdp audio payload number 98
+ sdp audio payload name AMR/8000
+ number endpoints 31
+ loop 1
diff --git a/openbsc/src/mgcp/mgcp_main.c b/openbsc/src/mgcp/mgcp_main.c
new file mode 100644
index 000000000..7d318b191
--- /dev/null
+++ b/openbsc/src/mgcp/mgcp_main.c
@@ -0,0 +1,228 @@
+/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
+/* The main method to drive it as a standalone process */
+
+/*
+ * (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * (C) 2009 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 <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <limits.h>
+#include <unistd.h>
+
+#include <sys/socket.h>
+#include <arpa/inet.h>
+
+#include <openbsc/debug.h>
+#include <openbsc/msgb.h>
+#include <openbsc/talloc.h>
+#include <openbsc/gsm_data.h>
+#include <openbsc/select.h>
+#include <openbsc/mgcp.h>
+#include <openbsc/telnet_interface.h>
+
+#include <vty/command.h>
+#include <vty/vty.h>
+
+/* this is here for the vty... it will never be called */
+void subscr_put() { abort(); }
+
+#define _GNU_SOURCE
+#include <getopt.h>
+
+#warning "Make use of the rtp proxy code"
+
+static int source_port = 2427;
+static const char *source_addr = "0.0.0.0";
+static struct bsc_fd bfd;
+static int first_request = 1;
+
+static char *config_file = "mgcp.cfg";
+
+/* used by msgb and mgcp */
+void *tall_bsc_ctx = NULL;
+
+unsigned int rtp_base_port = RTP_PORT_DEFAULT;
+
+static void print_help()
+{
+ printf("Some useful help...\n");
+ printf(" -h --help is printing this text.\n");
+ printf(" -c --config-file filename The config file to use.\n");
+}
+
+static void handle_options(int argc, char** argv)
+{
+ while (1) {
+ int option_index = 0, c;
+ static struct option long_options[] = {
+ {"help", 0, 0, 'h'},
+ {"config-file", 1, 0, 'c'},
+ {0, 0, 0, 0},
+ };
+
+ c = getopt_long(argc, argv, "hc:", long_options, &option_index);
+
+ if (c == -1)
+ break;
+
+ switch(c) {
+ case 'h':
+ print_help();
+ exit(0);
+ break;
+ case 'c':
+ config_file = talloc_strdup(tall_bsc_ctx, optarg);
+ break;
+ default:
+ /* ignore */
+ break;
+ };
+ }
+}
+
+static int read_call_agent(struct bsc_fd *fd, unsigned int what)
+{
+ struct sockaddr_in addr;
+ socklen_t slen = sizeof(addr);
+ struct msgb *msg;
+ struct msgb *resp;
+
+ msg = (struct msgb *) fd->data;
+
+ /* read one less so we can use it as a \0 */
+ int rc = recvfrom(bfd.fd, msg->data, msg->data_len - 1, 0,
+ (struct sockaddr *) &addr, &slen);
+ if (rc < 0) {
+ perror("Gateway failed to read");
+ return -1;
+ } else if (slen > sizeof(addr)) {
+ fprintf(stderr, "Gateway received message from outerspace: %d %d\n",
+ slen, sizeof(addr));
+ return -1;
+ }
+
+ if (first_request) {
+ first_request = 0;
+ resp = mgcp_create_rsip();
+
+ if (resp) {
+ sendto(bfd.fd, resp->l2h, msgb_l2len(resp), 0,
+ (struct sockaddr *) &addr, sizeof(addr));
+ msgb_free(resp);
+ }
+ return 0;
+ }
+
+ /* handle message now */
+ msg->l2h = msgb_put(msg, rc);
+ resp = mgcp_handle_message(msg);
+ msgb_reset(msg);
+
+ if (resp) {
+ sendto(bfd.fd, resp->l2h, msgb_l2len(resp), 0, (struct sockaddr *) &addr, sizeof(addr));
+ msgb_free(resp);
+ }
+ return 0;
+}
+
+
+int bsc_vty_init(struct gsm_network *dummy)
+{
+ cmd_init(1);
+ vty_init();
+
+ mgcp_vty_init();
+ return 0;
+}
+
+int main(int argc, char** argv)
+{
+ struct gsm_network dummy_network;
+ struct sockaddr_in addr;
+ int on = 1, rc;
+ struct debug_target *stderr_target;
+
+ tall_bsc_ctx = talloc_named_const(NULL, 1, "mgcp-callagent");
+
+ debug_init();
+ stderr_target = debug_target_create_stderr();
+ debug_add_target(stderr_target);
+ debug_set_all_filter(stderr_target, 1);
+
+ handle_options(argc, argv);
+
+ telnet_init(&dummy_network, 4243);
+ rc = mgcp_parse_config(config_file, &dummy_network);
+ if (rc < 0)
+ return rc;
+
+
+ /* we need to bind a socket */
+ if (rc == 0) {
+ bfd.when = BSC_FD_READ;
+ bfd.cb = read_call_agent;
+ bfd.fd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (bfd.fd < 0) {
+ perror("Gateway failed to listen");
+ return -1;
+ }
+
+ setsockopt(bfd.fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(source_port);
+ inet_aton(source_addr, &addr.sin_addr);
+
+ if (bind(bfd.fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ perror("Gateway failed to bind");
+ return -1;
+ }
+
+ bfd.data = msgb_alloc(4096, "mgcp-msg");
+ if (!bfd.data) {
+ fprintf(stderr, "Gateway memory error.\n");
+ return -1;
+ }
+
+
+ if (bsc_register_fd(&bfd) != 0) {
+ DEBUGP(DMGCP, "Failed to register the fd\n");
+ return -1;
+ }
+
+ DEBUGP(DMGCP, "Configured for MGCP.\n");
+ }
+
+ /* initialisation */
+ srand(time(NULL));
+
+ /* main loop */
+ while (1) {
+ bsc_select_main(0);
+ }
+
+
+ return 0;
+}
diff --git a/openbsc/src/mgcp/mgcp_protocol.c b/openbsc/src/mgcp/mgcp_protocol.c
new file mode 100644
index 000000000..53573f3b2
--- /dev/null
+++ b/openbsc/src/mgcp/mgcp_protocol.c
@@ -0,0 +1,1144 @@
+/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
+/* The protocol implementation */
+
+/*
+ * (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * (C) 2009 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 <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <limits.h>
+#include <unistd.h>
+
+#include <sys/socket.h>
+#include <arpa/inet.h>
+
+#include <openbsc/debug.h>
+#include <openbsc/msgb.h>
+#include <openbsc/talloc.h>
+#include <openbsc/gsm_data.h>
+#include <openbsc/select.h>
+#include <openbsc/mgcp.h>
+#include <openbsc/telnet_interface.h>
+
+#include <vty/command.h>
+#include <vty/vty.h>
+
+#warning "Make use of the rtp proxy code"
+
+static int source_port = 2427;
+static const char *local_ip = NULL;
+static const char *source_addr = "0.0.0.0";
+static unsigned int number_endpoints = 0;
+static const char *bts_ip = NULL;
+static struct in_addr bts_in;
+static const char *audio_name = "GSM-EFR/8000";
+static int audio_payload = 97;
+static int audio_loop = 0;
+static int early_bind = 0;
+
+static char *forward_ip = NULL;
+static int forward_port = 0;
+
+enum mgcp_connection_mode {
+ MGCP_CONN_NONE = 0,
+ MGCP_CONN_RECV_ONLY = 1,
+ MGCP_CONN_SEND_ONLY = 2,
+ MGCP_CONN_RECV_SEND = MGCP_CONN_RECV_ONLY | MGCP_CONN_SEND_ONLY,
+};
+
+enum {
+ DEST_NETWORK = 0,
+ DEST_BTS = 1,
+};
+
+enum {
+ PROTO_RTP,
+ PROTO_RTCP,
+};
+
+#define CI_UNUSED 0
+static unsigned int last_call_id = 0;
+
+struct mgcp_endpoint {
+ int ci;
+ char *callid;
+ char *local_options;
+ int conn_mode;
+
+ /* the local rtp port we are binding to */
+ int rtp_port;
+
+ /*
+ * RTP mangling:
+ * - we get RTP and RTCP to us and need to forward to the BTS
+ * - we get RTP and RTCP from the BTS and forward to the network
+ */
+ struct bsc_fd local_rtp;
+ struct bsc_fd local_rtcp;
+
+ struct in_addr remote;
+ struct in_addr bts;
+
+ /* in network byte order */
+ int net_rtp, net_rtcp;
+ int bts_rtp, bts_rtcp;
+};
+
+static struct mgcp_endpoint *endpoints = NULL;
+#define ENDPOINT_NUMBER(endp) abs(endp - endpoints)
+
+/**
+ * Macro for tokenizing MGCP messages and SDP in one go.
+ *
+ */
+#define MSG_TOKENIZE_START \
+ line_start = 0; \
+ for (i = 0; i < msgb_l3len(msg); ++i) { \
+ /* we have a line end */ \
+ if (msg->l3h[i] == '\n') { \
+ /* skip the first line */ \
+ if (line_start == 0) { \
+ line_start = i + 1; \
+ continue; \
+ } \
+ \
+ /* check if we have a proper param */ \
+ if (i - line_start == 1 && msg->l3h[line_start] == '\r') { \
+ } else if (i - line_start > 2 \
+ && islower(msg->l3h[line_start]) \
+ && msg->l3h[line_start + 1] == '=') { \
+ } else if (i - line_start < 3 \
+ || msg->l3h[line_start + 1] != ':' \
+ || msg->l3h[line_start + 2] != ' ') \
+ goto error; \
+ \
+ msg->l3h[i] = '\0'; \
+ if (msg->l3h[i-1] == '\r') \
+ msg->l3h[i-1] = '\0';
+
+#define MSG_TOKENIZE_END \
+ line_start = i + 1; \
+ } \
+ }
+
+
+struct mgcp_msg_ptr {
+ unsigned int start;
+ unsigned int length;
+};
+
+struct mgcp_request {
+ char *name;
+ struct msgb *(*handle_request) (struct msgb *msg);
+ char *debug_name;
+};
+
+#define MGCP_REQUEST(NAME, REQ, DEBUG_NAME) \
+ { .name = NAME, .handle_request = REQ, .debug_name = DEBUG_NAME },
+
+static struct msgb *handle_audit_endpoint(struct msgb *msg);
+static struct msgb *handle_create_con(struct msgb *msg);
+static struct msgb *handle_delete_con(struct msgb *msg);
+static struct msgb *handle_modify_con(struct msgb *msg);
+
+static mgcp_change change_cb;
+static void *change_cb_data;
+
+static int generate_call_id()
+{
+ int i;
+
+ /* use the call id */
+ ++last_call_id;
+
+ /* handle wrap around */
+ if (last_call_id == CI_UNUSED)
+ ++last_call_id;
+
+ /* callstack can only be of size number_of_endpoints */
+ /* verify that the call id is free, e.g. in case of overrun */
+ for (i = 1; i < number_endpoints; ++i)
+ if (endpoints[i].ci == last_call_id)
+ return generate_call_id();
+
+ return last_call_id;
+}
+
+/* FIXIME/TODO: need to have a list of pending transactions and check that */
+static unsigned int generate_transaction_id()
+{
+ return abs(rand());
+}
+
+static int udp_send(int fd, struct in_addr *addr, int port, char *buf, int len)
+{
+ struct sockaddr_in out;
+ out.sin_family = AF_INET;
+ out.sin_port = port;
+ memcpy(&out.sin_addr, addr, sizeof(*addr));
+
+ return sendto(fd, buf, len, 0, (struct sockaddr *)&out, sizeof(out));
+}
+
+/*
+ * There is data coming. We will have to figure out if it
+ * came from the BTS or the MediaGateway of the MSC. On top
+ * of that we need to figure out if it was RTP or RTCP.
+ *
+ * Currently we do not communicate with the BSC so we have
+ * no idea where the BTS is listening for RTP and need to
+ * do the classic routing trick. Wait for the first packet
+ * from the BTS and then go ahead.
+ */
+static int rtp_data_cb(struct bsc_fd *fd, unsigned int what)
+{
+ char buf[4096];
+ struct sockaddr_in addr;
+ socklen_t slen = sizeof(addr);
+ struct mgcp_endpoint *endp;
+ int rc, dest, proto;
+
+ endp = (struct mgcp_endpoint *) fd->data;
+
+ rc = recvfrom(fd->fd, &buf, sizeof(buf), 0,
+ (struct sockaddr *) &addr, &slen);
+ if (rc < 0) {
+ LOGP(DMGCP, LOGL_ERROR, "Failed to receive message on: 0x%x\n",
+ ENDPOINT_NUMBER(endp));
+ return -1;
+ }
+
+ /* do not forward aynthing... maybe there is a packet from the bts */
+ if (endp->ci == CI_UNUSED) {
+ LOGP(DMGCP, LOGL_ERROR, "Unknown message on endpoint: 0x%x\n", ENDPOINT_NUMBER(endp));
+ return -1;
+ }
+
+ /*
+ * Figure out where to forward it to. This code assumes that we
+ * have received the Connection Modify and know who is a legitimate
+ * partner. According to the spec we could attempt to forward even
+ * after the Create Connection but we will not as we are not really
+ * able to tell if this is legitimate.
+ */
+ #warning "Slight spec violation. With connection mode recvonly we should attempt to forward."
+ dest = memcmp(&addr.sin_addr, &endp->remote, sizeof(addr.sin_addr)) == 0 &&
+ (endp->net_rtp == addr.sin_port || endp->net_rtcp == addr.sin_port)
+ ? DEST_BTS : DEST_NETWORK;
+ proto = fd == &endp->local_rtp ? PROTO_RTP : PROTO_RTCP;
+
+ /* We have no idea who called us, maybe it is the BTS. */
+ if (dest == DEST_NETWORK && (endp->bts_rtp == 0 || forward_ip)) {
+ /* it was the BTS... */
+ if (!bts_ip || memcmp(&addr.sin_addr, &bts_in, sizeof(bts_in)) == 0) {
+ if (fd == &endp->local_rtp) {
+ endp->bts_rtp = addr.sin_port;
+ } else {
+ endp->bts_rtcp = addr.sin_port;
+ }
+
+ endp->bts = addr.sin_addr;
+ LOGP(DMGCP, LOGL_NOTICE, "Found BTS for endpoint: 0x%x on port: %d/%d\n",
+ ENDPOINT_NUMBER(endp), ntohs(endp->bts_rtp), ntohs(endp->bts_rtcp));
+ }
+ }
+
+ /* dispatch */
+ if (audio_loop)
+ dest = !dest;
+
+ if (dest == DEST_NETWORK) {
+ return udp_send(fd->fd, &endp->remote,
+ proto == PROTO_RTP ? endp->net_rtp : endp->net_rtcp,
+ buf, rc);
+ } else {
+ return udp_send(fd->fd, &endp->bts,
+ proto == PROTO_RTP ? endp->bts_rtp : endp->bts_rtcp,
+ buf, rc);
+ }
+}
+
+static int create_bind(struct bsc_fd *fd, int port)
+{
+ struct sockaddr_in addr;
+ int on = 1;
+
+ fd->fd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (fd->fd < 0) {
+ LOGP(DMGCP, LOGL_ERROR, "Failed to create UDP port.\n");
+ return -1;
+ }
+
+ setsockopt(fd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(port);
+ inet_aton(source_addr, &addr.sin_addr);
+
+ if (bind(fd->fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static int bind_rtp(struct mgcp_endpoint *endp)
+{
+ if (create_bind(&endp->local_rtp, endp->rtp_port) != 0) {
+ LOGP(DMGCP, LOGL_ERROR, "Failed to create RTP port: %s:%d on 0x%x\n",
+ source_addr, endp->rtp_port, ENDPOINT_NUMBER(endp));
+ goto cleanup0;
+ }
+
+ if (create_bind(&endp->local_rtcp, endp->rtp_port + 1) != 0) {
+ LOGP(DMGCP, LOGL_ERROR, "Failed to create RTCP port: %s:%d on 0x%x\n",
+ source_addr, endp->rtp_port + 1, ENDPOINT_NUMBER(endp));
+ goto cleanup1;
+ }
+
+ endp->local_rtp.cb = rtp_data_cb;
+ endp->local_rtp.data = endp;
+ endp->local_rtp.when = BSC_FD_READ;
+ if (bsc_register_fd(&endp->local_rtp) != 0) {
+ LOGP(DMGCP, LOGL_ERROR, "Failed to register RTP port %d on 0x%x\n",
+ endp->rtp_port, ENDPOINT_NUMBER(endp));
+ goto cleanup2;
+ }
+
+ endp->local_rtcp.cb = rtp_data_cb;
+ endp->local_rtcp.data = endp;
+ endp->local_rtcp.when = BSC_FD_READ;
+ if (bsc_register_fd(&endp->local_rtcp) != 0) {
+ LOGP(DMGCP, LOGL_ERROR, "Failed to register RTCP port %d on 0x%x\n",
+ endp->rtp_port + 1, ENDPOINT_NUMBER(endp));
+ goto cleanup3;
+ }
+
+ return 0;
+
+cleanup3:
+ bsc_unregister_fd(&endp->local_rtp);
+cleanup2:
+ close(endp->local_rtcp.fd);
+ endp->local_rtcp.fd = -1;
+cleanup1:
+ close(endp->local_rtp.fd);
+ endp->local_rtp.fd = -1;
+cleanup0:
+ return -1;
+}
+
+/*
+ * array of function pointers for handling various
+ * messages. In the future this might be binary sorted
+ * for performance reasons.
+ */
+static const struct mgcp_request mgcp_requests [] = {
+ MGCP_REQUEST("AUEP", handle_audit_endpoint, "AuditEndpoint")
+ MGCP_REQUEST("CRCX", handle_create_con, "CreateConnection")
+ MGCP_REQUEST("DLCX", handle_delete_con, "DeleteConnection")
+ MGCP_REQUEST("MDCX", handle_modify_con, "ModifiyConnection")
+};
+
+static struct msgb *mgcp_msgb_alloc(void)
+{
+ struct msgb *msg;
+ msg = msgb_alloc_headroom(4096, 128, "MGCP msg");
+ if (!msg)
+ LOGP(DMGCP, LOGL_ERROR, "Failed to msgb for MGCP data.\n");
+
+ return msg;
+}
+
+static struct msgb *send_response_with_data(int code, const char *msg, const char *trans,
+ const char *data)
+{
+ int len;
+ struct msgb *res;
+
+ res = mgcp_msgb_alloc();
+ if (!res)
+ return NULL;
+
+ if (data) {
+ len = snprintf((char *) res->data, 2048, "%d %s\n%s", code, trans, data);
+ } else {
+ len = snprintf((char *) res->data, 2048, "%d %s\n", code, trans);
+ }
+
+ res->l2h = msgb_put(res, len);
+ LOGP(DMGCP, LOGL_NOTICE, "Sending response: code: %d for '%s'\n", code, res->l2h);
+ return res;
+}
+
+static struct msgb *send_response(int code, const char *msg, const char *trans)
+{
+ return send_response_with_data(code, msg, trans, NULL);
+}
+
+static struct msgb *send_with_sdp(struct mgcp_endpoint *endp, const char *msg, const char *trans_id)
+{
+ const char *addr = local_ip;
+ char sdp_record[4096];
+
+ if (!addr)
+ addr = source_addr;
+
+ snprintf(sdp_record, sizeof(sdp_record) - 1,
+ "I: %d\n\n"
+ "v=0\r\n"
+ "c=IN IP4 %s\r\n"
+ "m=audio %d RTP/AVP %d\r\n"
+ "a=rtpmap:%d %s\r\n",
+ endp->ci, addr, endp->rtp_port,
+ audio_payload, audio_payload, audio_name);
+ return send_response_with_data(200, msg, trans_id, sdp_record);
+}
+
+/* send a static record */
+struct msgb *mgcp_create_rsip(void)
+{
+ struct msgb *msg;
+ int len;
+
+ msg = mgcp_msgb_alloc();
+ if (!msg)
+ return NULL;
+
+ len = snprintf((char *) msg->data, 2048,
+ "RSIP %u *@mgw MGCP 1.0\n"
+ "RM: restart\n", generate_transaction_id());
+ msg->l2h = msgb_put(msg, len);
+ return msg;
+}
+
+/*
+ * handle incoming messages:
+ * - this can be a command (four letters, space, transaction id)
+ * - or a response (three numbers, space, transaction id)
+ */
+struct msgb *mgcp_handle_message(struct msgb *msg)
+{
+ int code;
+ struct msgb *resp = NULL;
+
+ if (msg->len < 4) {
+ LOGP(DMGCP, LOGL_ERROR, "mgs too short: %d\n", msg->len);
+ return NULL;
+ }
+
+ /* attempt to treat it as a response */
+ if (sscanf((const char *)&msg->data[0], "%3d %*s", &code) == 1) {
+ LOGP(DMGCP, LOGL_NOTICE, "Response: Code: %d\n", code);
+ } else {
+ int i, handled = 0;
+ msg->l3h = &msg->l2h[4];
+ for (i = 0; i < ARRAY_SIZE(mgcp_requests); ++i)
+ if (strncmp(mgcp_requests[i].name, (const char *) &msg->data[0], 4) == 0) {
+ handled = 1;
+ resp = mgcp_requests[i].handle_request(msg);
+ break;
+ }
+ if (!handled) {
+ LOGP(DMGCP, LOGL_NOTICE, "MSG with type: '%.4s' not handled\n", &msg->data[0]);
+ }
+ }
+
+ return resp;
+}
+
+/* string tokenizer for the poor */
+static int find_msg_pointers(struct msgb *msg, struct mgcp_msg_ptr *ptrs, int ptrs_length)
+{
+ int i, found = 0;
+
+ int whitespace = 1;
+ for (i = 0; i < msgb_l3len(msg) && ptrs_length > 0; ++i) {
+ /* if we have a space we found an end */
+ if (msg->l3h[i] == ' ' || msg->l3h[i] == '\r' || msg->l3h[i] == '\n') {
+ if (!whitespace) {
+ ++found;
+ whitespace = 1;
+ ptrs->length = i - ptrs->start - 1;
+ ++ptrs;
+ --ptrs_length;
+ } else {
+ /* skip any number of whitespace */
+ }
+
+ /* line end... stop */
+ if (msg->l3h[i] == '\r' || msg->l3h[i] == '\n')
+ break;
+ } else if (msg->l3h[i] == '\r' || msg->l3h[i] == '\n') {
+ /* line end, be done */
+ break;
+ } else if (whitespace) {
+ whitespace = 0;
+ ptrs->start = i;
+ }
+ }
+
+ if (ptrs_length == 0)
+ return -1;
+ return found;
+}
+
+static struct mgcp_endpoint *find_endpoint(const char *mgcp)
+{
+ char *endptr = NULL;
+ unsigned int gw = INT_MAX;
+
+ gw = strtoul(mgcp, &endptr, 16);
+ if (gw == 0 || gw >= number_endpoints || strcmp(endptr, "@mgw") != 0) {
+ LOGP(DMGCP, LOGL_ERROR, "Not able to find endpoint: '%s'\n", mgcp);
+ return NULL;
+ }
+
+ return &endpoints[gw];
+}
+
+static int analyze_header(struct msgb *msg, struct mgcp_msg_ptr *ptr, int size,
+ const char **transaction_id, struct mgcp_endpoint **endp)
+{
+ int found;
+
+ if (size < 3) {
+ LOGP(DMGCP, LOGL_ERROR, "Not enough space in ptr\n");
+ return -1;
+ }
+
+ found = find_msg_pointers(msg, ptr, size);
+
+ if (found < 3) {
+ LOGP(DMGCP, LOGL_ERROR, "Gateway: Not enough params. Found: %d\n", found);
+ return -1;
+ }
+
+ /*
+ * replace the space with \0. the main method gurantess that
+ * we still have + 1 for null termination
+ */
+ msg->l3h[ptr[3].start + ptr[3].length + 1] = '\0';
+ msg->l3h[ptr[2].start + ptr[2].length + 1] = '\0';
+ msg->l3h[ptr[1].start + ptr[1].length + 1] = '\0';
+ msg->l3h[ptr[0].start + ptr[0].length + 1] = '\0';
+
+ if (strncmp("1.0", (const char *)&msg->l3h[ptr[3].start], 3) != 0
+ || strncmp("MGCP", (const char *)&msg->l3h[ptr[2].start], 4) != 0) {
+ LOGP(DMGCP, LOGL_ERROR, "Wrong MGCP version. Not handling: '%s' '%s'\n",
+ (const char *)&msg->l3h[ptr[3].start],
+ (const char *)&msg->l3h[ptr[2].start]);
+ return -1;
+ }
+
+ *transaction_id = (const char *)&msg->l3h[ptr[0].start];
+ *endp = find_endpoint((const char *)&msg->l3h[ptr[1].start]);
+ return *endp == NULL;
+}
+
+static int verify_call_id(const struct mgcp_endpoint *endp,
+ const char *callid)
+{
+ if (strcmp(endp->callid, callid) != 0) {
+ LOGP(DMGCP, LOGL_ERROR, "CallIDs does not match on 0x%x. '%s' != '%s'\n",
+ ENDPOINT_NUMBER(endp), endp->callid, callid);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int verify_ci(const struct mgcp_endpoint *endp,
+ const char *ci)
+{
+ if (atoi(ci) != endp->ci) {
+ LOGP(DMGCP, LOGL_ERROR, "ConnectionIdentifiers do not match on 0x%x. %d != %s\n",
+ ENDPOINT_NUMBER(endp), endp->ci, ci);
+ return -1;
+ }
+
+ return 0;
+}
+
+static struct msgb *handle_audit_endpoint(struct msgb *msg)
+{
+ struct mgcp_msg_ptr data_ptrs[6];
+ int found, response;
+ const char *trans_id;
+ struct mgcp_endpoint *endp;
+
+ found = analyze_header(msg, data_ptrs, ARRAY_SIZE(data_ptrs), &trans_id, &endp);
+ if (found != 0)
+ response = 500;
+ else
+ response = 200;
+
+ return send_response(response, "AUEP", trans_id);
+}
+
+static int parse_conn_mode(const char* msg, int *conn_mode)
+{
+ int ret = 0;
+ if (strcmp(msg, "recvonly") == 0)
+ *conn_mode = MGCP_CONN_RECV_ONLY;
+ else if (strcmp(msg, "sendrecv") == 0)
+ *conn_mode = MGCP_CONN_RECV_SEND;
+ else {
+ LOGP(DMGCP, LOGL_ERROR, "Unknown connection mode: '%s'\n", msg);
+ ret = -1;
+ }
+
+ return ret;
+}
+
+static struct msgb *handle_create_con(struct msgb *msg)
+{
+ struct mgcp_msg_ptr data_ptrs[6];
+ int found, i, line_start;
+ const char *trans_id;
+ struct mgcp_endpoint *endp;
+ int error_code = 500;
+
+ found = analyze_header(msg, data_ptrs, ARRAY_SIZE(data_ptrs), &trans_id, &endp);
+ if (found != 0)
+ return send_response(500, "CRCX", trans_id);
+
+ if (endp->ci != CI_UNUSED) {
+ LOGP(DMGCP, LOGL_ERROR, "Endpoint is already used. 0x%x\n", ENDPOINT_NUMBER(endp));
+ return send_response(500, "CRCX", trans_id);
+ }
+
+ /* parse CallID C: and LocalParameters L: */
+ MSG_TOKENIZE_START
+ switch (msg->l3h[line_start]) {
+ case 'L':
+ endp->local_options = talloc_strdup(endpoints,
+ (const char *)&msg->l3h[line_start + 3]);
+ break;
+ case 'C':
+ endp->callid = talloc_strdup(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) {
+ error_code = 517;
+ goto error2;
+ }
+ 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_rtp = endp->net_rtcp = endp->bts_rtp = endp->bts_rtcp = 0;
+
+ /* set to zero until we get the info */
+ memset(&endp->remote, 0, sizeof(endp->remote));
+
+ /* bind to the port now */
+ endp->rtp_port = rtp_calculate_port(ENDPOINT_NUMBER(endp), rtp_base_port);
+ if (!early_bind && bind_rtp(endp) != 0)
+ goto error2;
+
+ /* assign a local call identifier or fail */
+ endp->ci = generate_call_id();
+ if (endp->ci == CI_UNUSED)
+ goto error2;
+
+ LOGP(DMGCP, LOGL_NOTICE, "Creating endpoint on: 0x%x CI: %u port: %u\n",
+ ENDPOINT_NUMBER(endp), endp->ci, endp->rtp_port);
+ if (change_cb)
+ change_cb(ENDPOINT_NUMBER(endp), MGCP_ENDP_CRCX, endp->rtp_port, change_cb_data);
+
+ return send_with_sdp(endp, "CRCX", trans_id);
+error:
+ LOGP(DMGCP, LOGL_ERROR, "Malformed line: %s on 0x%x with: line_start: %d %d\n",
+ hexdump(msg->l3h, msgb_l3len(msg)),
+ ENDPOINT_NUMBER(endp), line_start, i);
+ return send_response(error_code, "CRCX", trans_id);
+
+error2:
+ LOGP(DMGCP, LOGL_NOTICE, "Resource error on 0x%x\n", ENDPOINT_NUMBER(endp));
+ return send_response(error_code, "CRCX", trans_id);
+}
+
+static struct msgb *handle_modify_con(struct msgb *msg)
+{
+ struct mgcp_msg_ptr data_ptrs[6];
+ int found, i, line_start;
+ const char *trans_id;
+ struct mgcp_endpoint *endp;
+ int error_code = 500;
+
+ found = analyze_header(msg, data_ptrs, ARRAY_SIZE(data_ptrs), &trans_id, &endp);
+ if (found != 0)
+ return send_response(error_code, "MDCX", trans_id);
+
+ if (endp->ci == CI_UNUSED) {
+ LOGP(DMGCP, LOGL_ERROR, "Endpoint is not holding a connection. 0x%x\n", ENDPOINT_NUMBER(endp));
+ return send_response(error_code, "MDCX", trans_id);
+ }
+
+ MSG_TOKENIZE_START
+ switch (msg->l3h[line_start]) {
+ case 'C': {
+ if (verify_call_id(endp, (const char *)&msg->l3h[line_start + 3]) != 0)
+ goto error3;
+ break;
+ }
+ case 'I': {
+ if (verify_ci(endp, (const char *)&msg->l3h[line_start + 3]) != 0)
+ goto error3;
+ break;
+ }
+ case 'L':
+ /* skip */
+ break;
+ case 'M':
+ if (parse_conn_mode((const char *)&msg->l3h[line_start + 3],
+ &endp->conn_mode) != 0) {
+ error_code = 517;
+ goto error3;
+ }
+ break;
+ case '\0':
+ /* SDP file begins */
+ break;
+ case 'a':
+ case 'o':
+ case 's':
+ case 't':
+ case 'v':
+ /* skip these SDP attributes */
+ break;
+ case 'm': {
+ int port;
+ const char *param = (const char *)&msg->l3h[line_start];
+
+ if (sscanf(param, "m=audio %d RTP/AVP %*d", &port) == 1) {
+ endp->net_rtp = htons(port);
+ endp->net_rtcp = htons(port + 1);
+ }
+ break;
+ }
+ case 'c': {
+ char ipv4[16];
+ const char *param = (const char *)&msg->l3h[line_start];
+
+ if (sscanf(param, "c=IN IP4 %15s", ipv4) == 1) {
+ inet_aton(ipv4, &endp->remote);
+ }
+ 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
+
+ /* modify */
+ LOGP(DMGCP, LOGL_NOTICE, "Modified endpoint on: 0x%x Server: %s:%u\n",
+ ENDPOINT_NUMBER(endp), inet_ntoa(endp->remote), endp->net_rtp);
+ if (change_cb)
+ change_cb(ENDPOINT_NUMBER(endp), MGCP_ENDP_MDCX, endp->rtp_port, change_cb_data);
+ return send_with_sdp(endp, "MDCX", trans_id);
+
+error:
+ LOGP(DMGCP, LOGL_ERROR, "Malformed line: %s on 0x%x with: line_start: %d %d %d\n",
+ hexdump(msg->l3h, msgb_l3len(msg)),
+ ENDPOINT_NUMBER(endp), line_start, i, msg->l3h[line_start]);
+ return send_response(error_code, "MDCX", trans_id);
+
+error3:
+ return send_response(error_code, "MDCX", trans_id);
+}
+
+static struct msgb *handle_delete_con(struct msgb *msg)
+{
+ struct mgcp_msg_ptr data_ptrs[6];
+ int found, i, line_start;
+ const char *trans_id;
+ struct mgcp_endpoint *endp;
+ int error_code = 500;
+
+ found = analyze_header(msg, data_ptrs, ARRAY_SIZE(data_ptrs), &trans_id, &endp);
+ if (found != 0)
+ return send_response(error_code, "DLCX", trans_id);
+
+ if (endp->ci == CI_UNUSED) {
+ LOGP(DMGCP, LOGL_ERROR, "Endpoint is not used. 0x%x\n", ENDPOINT_NUMBER(endp));
+ return send_response(error_code, "DLCX", trans_id);
+ }
+
+ MSG_TOKENIZE_START
+ switch (msg->l3h[line_start]) {
+ case 'C': {
+ if (verify_call_id(endp, (const char *)&msg->l3h[line_start + 3]) != 0)
+ goto error3;
+ break;
+ }
+ case 'I': {
+ if (verify_ci(endp, (const char *)&msg->l3h[line_start + 3]) != 0)
+ goto error3;
+ 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
+
+
+ /* free the connection */
+ LOGP(DMGCP, LOGL_NOTICE, "Deleting endpoint on: 0x%x\n", ENDPOINT_NUMBER(endp));
+ endp->ci= CI_UNUSED;
+ talloc_free(endp->callid);
+ talloc_free(endp->local_options);
+
+ if (!early_bind) {
+ bsc_unregister_fd(&endp->local_rtp);
+ bsc_unregister_fd(&endp->local_rtcp);
+ }
+
+ endp->net_rtp = endp->net_rtcp = endp->bts_rtp = endp->bts_rtcp = 0;
+ if (change_cb)
+ change_cb(ENDPOINT_NUMBER(endp), MGCP_ENDP_DLCX, endp->rtp_port, change_cb_data);
+
+ return send_response(250, "DLCX", trans_id);
+
+error:
+ LOGP(DMGCP, LOGL_ERROR, "Malformed line: %s on 0x%x with: line_start: %d %d\n",
+ hexdump(msg->l3h, msgb_l3len(msg)),
+ ENDPOINT_NUMBER(endp), line_start, i);
+ return send_response(error_code, "DLCX", trans_id);
+
+error3:
+ return send_response(error_code, "DLCX", trans_id);
+}
+
+/*
+ * vty code for mgcp below
+ */
+struct cmd_node mgcp_node = {
+ MGCP_NODE,
+ "%s(mgcp)#",
+ 1,
+};
+
+static int config_write_mgcp(struct vty *vty)
+{
+ vty_out(vty, "mgcp%s", VTY_NEWLINE);
+ if (local_ip)
+ vty_out(vty, " local ip %s%s", local_ip, VTY_NEWLINE);
+ if (bts_ip)
+ vty_out(vty, " bts ip %s%s", bts_ip, VTY_NEWLINE);
+ vty_out(vty, " bind ip %s%s", source_addr, VTY_NEWLINE);
+ vty_out(vty, " bind port %u%s", source_port, VTY_NEWLINE);
+ vty_out(vty, " bind early %u%s", !!early_bind, VTY_NEWLINE);
+ vty_out(vty, " rtp base %u%s", rtp_base_port, VTY_NEWLINE);
+ vty_out(vty, " sdp audio payload number %u%s", audio_payload, VTY_NEWLINE);
+ vty_out(vty, " sdp audio payload name %s%s", audio_name, VTY_NEWLINE);
+ vty_out(vty, " loop %u%s", !!audio_loop, VTY_NEWLINE);
+ vty_out(vty, " endpoints %u%s", number_endpoints, VTY_NEWLINE);
+ if (forward_ip)
+ vty_out(vty, " forward audio ip %s%s", forward_ip, VTY_NEWLINE);
+ if (forward_port != 0)
+ vty_out(vty, " forward audio port %d%s", forward_port, VTY_NEWLINE);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(show_mcgp, show_mgcp_cmd, "show mgcp",
+ SHOW_STR "Display information about the MGCP Media Gateway")
+{
+ int i;
+
+ vty_out(vty, "MGCP is up and running with %u endpoints:%s", number_endpoints - 1, VTY_NEWLINE);
+ for (i = 1; i < number_endpoints; ++i) {
+ struct mgcp_endpoint *endp = &endpoints[i];
+ vty_out(vty, " Endpoint 0x%.2x: CI: %d net: %u/%u bts: %u/%u%s",
+ i, endp->ci,
+ ntohs(endp->net_rtp), ntohs(endp->net_rtcp),
+ ntohs(endp->bts_rtp), ntohs(endp->bts_rtcp), VTY_NEWLINE);
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_mgcp,
+ cfg_mgcp_cmd,
+ "mgcp",
+ "Configure the MGCP")
+{
+ vty->node = MGCP_NODE;
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_mgcp_local_ip,
+ cfg_mgcp_local_ip_cmd,
+ "local ip IP",
+ "Set the IP to be used in SDP records")
+{
+ local_ip = talloc_strdup(tall_bsc_ctx, argv[0]);
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_mgcp_bts_ip,
+ cfg_mgcp_bts_ip_cmd,
+ "bts ip IP",
+ "Set the IP of the BTS for RTP forwarding")
+{
+ bts_ip = talloc_strdup(tall_bsc_ctx, argv[0]);
+ inet_aton(bts_ip, &bts_in);
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_mgcp_bind_ip,
+ cfg_mgcp_bind_ip_cmd,
+ "bind ip IP",
+ "Bind the MGCP to this local addr")
+{
+ source_addr = talloc_strdup(tall_bsc_ctx, argv[0]);
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_mgcp_bind_port,
+ cfg_mgcp_bind_port_cmd,
+ "bind port <0-65534>",
+ "Bind the MGCP to this port")
+{
+ unsigned int port = atoi(argv[0]);
+ if (port > 65534) {
+ vty_out(vty, "%% wrong bind port '%s'%s", argv[0], VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ source_port = port;
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_mgcp_bind_early,
+ cfg_mgcp_bind_early_cmd,
+ "bind early (0|1)",
+ "Bind all RTP ports early")
+{
+ unsigned int bind = atoi(argv[0]);
+ if (bind != 0 && bind != 1) {
+ vty_out(vty, "%% param must be 0 or 1.%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ early_bind = bind == 1;
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_mgcp_rtp_base_port,
+ cfg_mgcp_rtp_base_port_cmd,
+ "rtp base <0-65534>",
+ "Base port to use")
+{
+ unsigned int port = atoi(argv[0]);
+ if (port > 65534) {
+ vty_out(vty, "%% wrong base port '%s'%s", argv[0], VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ rtp_base_port = port;
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_mgcp_sdp_payload_number,
+ cfg_mgcp_sdp_payload_number_cmd,
+ "sdp audio payload number <1-255>",
+ "Set the audio codec to use")
+{
+ unsigned int payload = atoi(argv[0]);
+ if (payload > 255) {
+ vty_out(vty, "%% wrong payload number '%s'%s", argv[0], VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ audio_payload = payload;
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_mgcp_sdp_payload_name,
+ cfg_mgcp_sdp_payload_name_cmd,
+ "sdp audio payload name NAME",
+ "Set the audio name to use")
+{
+ audio_name = talloc_strdup(tall_bsc_ctx, argv[0]);
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_mgcp_loop,
+ cfg_mgcp_loop_cmd,
+ "loop (0|1)",
+ "Loop the audio")
+{
+ audio_loop = atoi(argv[0]);
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_mgcp_number_endp,
+ cfg_mgcp_number_endp_cmd,
+ "number endpoints <0-65534>",
+ "The number of endpoints to allocate. This is not dynamic.")
+{
+ /* + 1 as we start counting at one */
+ number_endpoints = atoi(argv[0]) + 1;
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_mgcp_forward_ip,
+ cfg_mgcp_forward_ip_cmd,
+ "forward audio ip IP",
+ "Forward packets from and to the IP. This disables most of the MGCP feature.")
+{
+ if (forward_ip)
+ talloc_free(forward_ip);
+ forward_ip = talloc_strdup(tall_bsc_ctx, argv[0]);
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_mgcp_forward_port,
+ cfg_mgcp_forward_port_cmd,
+ "forward audio port <1-15000>",
+ "Forward packets from and to the port. This disables most of the MGCP feature.")
+{
+ forward_port = atoi(argv[0]);
+ return CMD_SUCCESS;
+}
+
+int mgcp_vty_init(void)
+{
+ install_element(VIEW_NODE, &show_mgcp_cmd);
+
+ install_element(CONFIG_NODE, &cfg_mgcp_cmd);
+ install_node(&mgcp_node, config_write_mgcp);
+ install_default(MGCP_NODE);
+ install_element(MGCP_NODE, &cfg_mgcp_local_ip_cmd);
+ install_element(MGCP_NODE, &cfg_mgcp_bts_ip_cmd);
+ install_element(MGCP_NODE, &cfg_mgcp_bind_ip_cmd);
+ install_element(MGCP_NODE, &cfg_mgcp_bind_port_cmd);
+ install_element(MGCP_NODE, &cfg_mgcp_bind_early_cmd);
+ install_element(MGCP_NODE, &cfg_mgcp_rtp_base_port_cmd);
+ install_element(MGCP_NODE, &cfg_mgcp_sdp_payload_number_cmd);
+ install_element(MGCP_NODE, &cfg_mgcp_sdp_payload_name_cmd);
+ install_element(MGCP_NODE, &cfg_mgcp_loop_cmd);
+ install_element(MGCP_NODE, &cfg_mgcp_number_endp_cmd);
+ install_element(MGCP_NODE, &cfg_mgcp_forward_ip_cmd);
+ install_element(MGCP_NODE, &cfg_mgcp_forward_port_cmd);
+ return 0;
+}
+
+int mgcp_parse_config(const char *config_file, struct gsm_network *dummy_network)
+{
+ int i, rc;
+
+ rc = vty_read_config_file(config_file);
+ if (rc < 0) {
+ fprintf(stderr, "Failed to parse the config file: '%s'\n", config_file);
+ return rc;
+ }
+
+
+ if (!bts_ip)
+ fprintf(stderr, "No BTS ip address specified. This will allow everyone to connect.\n");
+
+ endpoints = _talloc_zero_array(tall_bsc_ctx,
+ sizeof(struct mgcp_endpoint),
+ number_endpoints, "endpoints");
+ if (!endpoints) {
+ fprintf(stderr, "Failed to allocate endpoints: %d. Quitting.\n", number_endpoints);
+ return -1;
+ }
+
+ /* Initialize all endpoints */
+ for (i = 0; i < number_endpoints; ++i) {
+ endpoints[i].local_rtp.fd = -1;
+ endpoints[i].local_rtcp.fd = -1;
+ endpoints[i].ci = CI_UNUSED;
+ }
+
+ /*
+ * This application supports two modes.
+ * 1.) a true MGCP gateway with support for AUEP, CRCX, MDCX, DLCX
+ * 2.) plain forwarding of RTP packets on the endpoints.
+ * both modes are mutual exclusive
+ */
+ if (forward_ip) {
+ int port = rtp_base_port;
+ if (forward_port != 0)
+ port = forward_port;
+
+ if (!early_bind) {
+ LOGP(DMGCP, LOGL_NOTICE, "Forwarding requires early bind.\n");
+ return -1;
+ }
+
+ /*
+ * Store the forward IP and assign a ci. For early bind
+ * the sockets will be created after this.
+ */
+ for (i = 1; i < number_endpoints; ++i) {
+ struct mgcp_endpoint *endp = &endpoints[i];
+ inet_aton(forward_ip, &endp->remote);
+ endp->ci = CI_UNUSED + 23;
+ endp->net_rtp = htons(rtp_calculate_port(ENDPOINT_NUMBER(endp), port));
+ endp->net_rtcp = htons(rtp_calculate_port(ENDPOINT_NUMBER(endp), port) + 1);
+ }
+
+ LOGP(DMGCP, LOGL_NOTICE, "Configured for Audio Forwarding.\n");
+ }
+
+ /* early bind */
+ if (early_bind) {
+ for (i = 1; i < number_endpoints; ++i) {
+ struct mgcp_endpoint *endp = &endpoints[i];
+ endp->rtp_port = rtp_calculate_port(ENDPOINT_NUMBER(endp), rtp_base_port);
+ if (bind_rtp(endp) != 0) {
+ LOGP(DMGCP, LOGL_FATAL, "Failed to bind: %d\n", endp->rtp_port);
+ return -1;
+ }
+ }
+ }
+
+ return !!forward_ip;
+}
+
+void mgcp_set_change_cb(mgcp_change cb, void *data)
+{
+ change_cb = cb;
+ change_cb_data = data;
+}
diff --git a/openbsc/src/mncc.c b/openbsc/src/mncc.c
index f62541c05..15e2978e6 100644
--- a/openbsc/src/mncc.c
+++ b/openbsc/src/mncc.c
@@ -22,6 +22,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <errno.h>
#include <sys/types.h>
#include <openbsc/gsm_04_08.h>
@@ -29,6 +30,8 @@
#include <openbsc/mncc.h>
#include <openbsc/talloc.h>
#include <openbsc/gsm_data.h>
+#include <openbsc/transaction.h>
+#include <openbsc/rtp_proxy.h>
void *tall_call_ctx;
@@ -82,10 +85,9 @@ static struct mncc_names {
{"MNCC_FRAME_DROP", 0x0202},
{"MNCC_LCHAN_MODIFY", 0x0203},
- {"GSM_TRAU_FRAME", 0x0300},
+ {"GSM_TCH_FRAME", 0x0300},
- {NULL, 0}
-};
+ {NULL, 0} };
static LLIST_HEAD(call_list);
@@ -136,19 +138,36 @@ static int mncc_setup_ind(struct gsm_call *call, int msg_type,
struct gsm_mncc mncc;
struct gsm_call *remote;
+ memset(&mncc, 0, sizeof(struct gsm_mncc));
+ mncc.callref = call->callref;
+
/* already have remote call */
if (call->remote_ref)
return 0;
+ /* transfer mode 1 would be packet mode, which was never specified */
+ if (setup->bearer_cap.mode != 0) {
+ LOGP(DMNCC, LOGL_NOTICE, "(call %x) We don't support "
+ "packet mode\n", call->callref);
+ mncc_set_cause(&mncc, GSM48_CAUSE_LOC_PRN_S_LU,
+ GSM48_CC_CAUSE_BEARER_CA_UNAVAIL);
+ goto out_reject;
+ }
+
+ /* we currently only do speech */
+ if (setup->bearer_cap.transfer != GSM_MNCC_BCAP_SPEECH) {
+ LOGP(DMNCC, LOGL_NOTICE, "(call %x) We only support "
+ "voice calls\n", call->callref);
+ mncc_set_cause(&mncc, GSM48_CAUSE_LOC_PRN_S_LU,
+ GSM48_CC_CAUSE_BEARER_CA_UNAVAIL);
+ goto out_reject;
+ }
+
/* create remote call */
if (!(remote = talloc(tall_call_ctx, struct gsm_call))) {
- memset(&mncc, 0, sizeof(struct gsm_mncc));
- mncc.callref = call->callref;
mncc_set_cause(&mncc, GSM48_CAUSE_LOC_PRN_S_LU,
GSM48_CC_CAUSE_RESOURCE_UNAVAIL);
- mncc_send(call->net, MNCC_REJ_REQ, &mncc);
- free_call(call);
- return 0;
+ goto out_reject;
}
llist_add_tail(&remote->entry, &call_list);
remote->net = call->net;
@@ -179,6 +198,11 @@ static int mncc_setup_ind(struct gsm_call *call, int msg_type,
setup->callref = remote->callref;
DEBUGP(DMNCC, "(call %x) Forwarding SETUP to remote.\n", call->callref);
return mncc_send(remote->net, MNCC_SETUP_REQ, setup);
+
+out_reject:
+ mncc_send(call->net, MNCC_REJ_REQ, &mncc);
+ free_call(call);
+ return 0;
}
static int mncc_alert_ind(struct gsm_call *call, int msg_type,
@@ -210,7 +234,8 @@ static int mncc_notify_ind(struct gsm_call *call, int msg_type,
static int mncc_setup_cnf(struct gsm_call *call, int msg_type,
struct gsm_mncc *connect)
{
- struct gsm_mncc connect_ack;
+ struct gsm_mncc connect_ack, frame_recv;
+ struct gsm_network *net = call->net;
struct gsm_call *remote;
u_int32_t refs[2];
@@ -231,7 +256,26 @@ static int mncc_setup_cnf(struct gsm_call *call, int msg_type,
refs[0] = call->callref;
refs[1] = call->remote_ref;
DEBUGP(DMNCC, "(call %x) Bridging with remote.\n", call->callref);
- return mncc_send(call->net, MNCC_BRIDGE, refs);
+
+ /* in direct mode, we always have to bridge the channels */
+ if (ipacc_rtp_direct)
+ return mncc_send(call->net, MNCC_BRIDGE, refs);
+
+ /* proxy mode */
+ if (!net->handover.active) {
+ /* in the no-handover case, we can bridge, i.e. use
+ * the old RTP proxy code */
+ return mncc_send(call->net, MNCC_BRIDGE, refs);
+ } else {
+ /* in case of handover, we need to re-write the RTP
+ * SSRC, sequence and timestamp values and thus
+ * need to enable RTP receive for both directions */
+ memset(&frame_recv, 0, sizeof(struct gsm_mncc));
+ frame_recv.callref = call->callref;
+ mncc_send(call->net, MNCC_FRAME_RECV, &frame_recv);
+ frame_recv.callref = call->remote_ref;
+ return mncc_send(call->net, MNCC_FRAME_RECV, &frame_recv);
+ }
}
static int mncc_disc_ind(struct gsm_call *call, int msg_type,
@@ -279,6 +323,28 @@ static int mncc_rel_cnf(struct gsm_call *call, int msg_type, struct gsm_mncc *re
return 0;
}
+/* receiving a TCH/F frame from the BSC code */
+static int mncc_rcv_tchf(struct gsm_call *call, int msg_type,
+ struct gsm_data_frame *dfr)
+{
+ struct gsm_trans *remote_trans;
+
+ remote_trans = trans_find_by_callref(call->net, call->remote_ref);
+
+ /* this shouldn't really happen */
+ if (!remote_trans || !remote_trans->lchan) {
+ LOGP(DMNCC, LOGL_ERROR, "No transaction or transaction without lchan?!?\n");
+ return -EIO;
+ }
+
+ /* RTP socket of remote end has meanwhile died */
+ if (!remote_trans->lchan->abis_ip.rtp_socket)
+ return -EIO;
+
+ return rtp_send_frame(remote_trans->lchan->abis_ip.rtp_socket, dfr);
+}
+
+
int mncc_recv(struct gsm_network *net, int msg_type, void *arg)
{
struct gsm_mncc *data = arg;
@@ -320,8 +386,15 @@ int mncc_recv(struct gsm_network *net, int msg_type, void *arg)
DEBUGP(DMNCC, "(call %x) Call created.\n", call->callref);
}
- DEBUGP(DMNCC, "(call %x) Received message %s\n", call->callref,
- get_mncc_name(msg_type));
+ switch (msg_type) {
+ case GSM_TCHF_FRAME:
+ case GSM_TCHF_FRAME_EFR:
+ break;
+ default:
+ DEBUGP(DMNCC, "(call %x) Received message %s\n", call->callref,
+ get_mncc_name(msg_type));
+ break;
+ }
switch(msg_type) {
case MNCC_SETUP_IND:
@@ -382,8 +455,12 @@ int mncc_recv(struct gsm_network *net, int msg_type, void *arg)
call->callref, data->cause.value);
rc = mncc_send(net, MNCC_RETRIEVE_REJ, data);
break;
+ case GSM_TCHF_FRAME:
+ case GSM_TCHF_FRAME_EFR:
+ rc = mncc_rcv_tchf(call, msg_type, arg);
+ break;
default:
- DEBUGP(DMNCC, "(call %x) Message unhandled\n", callref);
+ LOGP(DMNCC, LOGL_NOTICE, "(call %x) Message unhandled\n", callref);
break;
}
diff --git a/openbsc/src/msgb.c b/openbsc/src/msgb.c
index edeb975a9..9f2fcfa96 100644
--- a/openbsc/src/msgb.c
+++ b/openbsc/src/msgb.c
@@ -26,8 +26,9 @@
#include <openbsc/msgb.h>
#include <openbsc/gsm_data.h>
#include <openbsc/talloc.h>
+#include <openbsc/debug.h>
-static void *tall_msgb_ctx;
+void *tall_msgb_ctx;
struct msgb *msgb_alloc(u_int16_t size, const char *name)
{
@@ -35,8 +36,10 @@ struct msgb *msgb_alloc(u_int16_t size, const char *name)
msg = _talloc_zero(tall_msgb_ctx, sizeof(*msg) + size, name);
- if (!msg)
+ if (!msg) {
+ LOGP(DRSL, LOGL_FATAL, "unable to allocate msgb\n");
return NULL;
+ }
msg->data_len = size;
msg->len = 0;
@@ -93,8 +96,3 @@ void msgb_reset(struct msgb *msg)
msg->l3h = NULL;
msg->smsh = NULL;
}
-
-static __attribute__((constructor)) void on_dso_load_trau_msgb(void)
-{
- tall_msgb_ctx = talloc_named_const(tall_bsc_ctx, 1, "msgb");
-}
diff --git a/openbsc/src/openbsc.cfg.1-1 b/openbsc/src/openbsc.cfg.1-1
index a8331ddbd..a25804f63 100644
--- a/openbsc/src/openbsc.cfg.1-1
+++ b/openbsc/src/openbsc.cfg.1-1
@@ -11,6 +11,8 @@ network
mobile network code 1
short name OpenBSC
long name OpenBSC
+ timer t3101 10
+ timer t3113 60
bts 0
type bs11
band GSM900
@@ -29,7 +31,7 @@ network
phys_chan_config CCCH+SDCCH4
e1 line 0 timeslot 1 sub-slot full
timeslot 1
- phys_chan_config SDCCH8
+ phys_chan_config TCH/F
e1 line 0 timeslot 2 sub-slot 1
timeslot 2
phys_chan_config TCH/F
diff --git a/openbsc/src/openbsc.cfg.1-2 b/openbsc/src/openbsc.cfg.1-2
index 10aa7b48b..84d50c75c 100644
--- a/openbsc/src/openbsc.cfg.1-2
+++ b/openbsc/src/openbsc.cfg.1-2
@@ -11,6 +11,8 @@ network
mobile network code 1
short name OpenBSC
long name OpenBSC
+ timer t3101 10
+ timer t3113 60
bts 0
type bs11
band GSM900
diff --git a/openbsc/src/openbsc.cfg.2-2 b/openbsc/src/openbsc.cfg.2-2
index 0dd9d9b5d..9ae800342 100644
--- a/openbsc/src/openbsc.cfg.2-2
+++ b/openbsc/src/openbsc.cfg.2-2
@@ -11,6 +11,8 @@ network
mobile network code 1
short name OpenBSC
long name OpenBSC
+ timer t3101 10
+ timer t3113 60
bts 0
type bs11
band GSM900
@@ -29,7 +31,7 @@ network
phys_chan_config CCCH+SDCCH4
e1 line 0 timeslot 1 sub-slot full
timeslot 1
- phys_chan_config SDCCH8
+ phys_chan_config TCH/F
e1 line 0 timeslot 2 sub-slot 1
timeslot 2
phys_chan_config TCH/F
diff --git a/openbsc/src/openbsc.cfg.nanobts b/openbsc/src/openbsc.cfg.nanobts
index a12794ffd..a1ceaec79 100644
--- a/openbsc/src/openbsc.cfg.nanobts
+++ b/openbsc/src/openbsc.cfg.nanobts
@@ -11,6 +11,8 @@ network
mobile network code 1
short name OpenBSC
long name OpenBSC
+ timer t3101 10
+ timer t3113 60
bts 0
type nanobts
ip.access unit_id 1801 0
diff --git a/openbsc/src/paging.c b/openbsc/src/paging.c
index 69902e8b1..91de70241 100644
--- a/openbsc/src/paging.c
+++ b/openbsc/src/paging.c
@@ -55,7 +55,7 @@ static unsigned int calculate_group(struct gsm_bts *bts, struct gsm_subscriber *
int blocks;
unsigned int group;
- ccch_conf = bts->chan_desc.ccch_conf;
+ ccch_conf = bts->si_common.chan_desc.ccch_conf;
bs_cc_chans = rsl_ccch_conf_to_bs_cc_chans(ccch_conf);
/* code word + 2, as 2 channels equals 0x0 */
blocks = rsl_number_of_paging_subchannels(bts);
@@ -212,7 +212,9 @@ static void paging_T3113_expired(void *data)
cbfn = req->cbfn;
paging_remove_request(&req->bts->paging, req);
- dispatch_signal(SS_PAGING, S_PAGING_COMPLETED, &sig_data);
+ counter_inc(req->bts->network->stats.paging.expired);
+
+ dispatch_signal(SS_PAGING, S_PAGING_EXPIRED, &sig_data);
if (cbfn)
cbfn(GSM_HOOK_RR_PAGING, GSM_PAGING_EXPIRED, NULL, NULL,
cbfn_param);
@@ -239,7 +241,7 @@ static int _paging_request(struct gsm_bts *bts, struct gsm_subscriber *subscr,
req->cbfn_param = data;
req->T3113.cb = paging_T3113_expired;
req->T3113.data = req;
- bsc_schedule_timer(&req->T3113, T3113_VALUE);
+ bsc_schedule_timer(&req->T3113, bts->network->T3113, 0);
llist_add_tail(&req->entry, &bts_entry->pending_requests);
if (!bsc_timer_pending(&bts_entry->work_timer))
@@ -254,6 +256,8 @@ int paging_request(struct gsm_network *network, struct gsm_subscriber *subscr,
struct gsm_bts *bts = NULL;
int num_pages = 0;
+ counter_inc(network->stats.paging.attempted);
+
/* start paging subscriber on all BTS within Location Area */
do {
int rc;
@@ -261,6 +265,11 @@ int paging_request(struct gsm_network *network, struct gsm_subscriber *subscr,
bts = gsm_bts_by_lac(network, subscr->lac, bts);
if (!bts)
break;
+
+ /* skip all currently inactive TRX */
+ if (!trx_is_usable(bts->c0))
+ continue;
+
num_pages++;
/* Trigger paging, pass any error to caller */
@@ -269,6 +278,9 @@ int paging_request(struct gsm_network *network, struct gsm_subscriber *subscr,
return rc;
} while (1);
+ if (num_pages == 0)
+ counter_inc(network->stats.paging.detached);
+
return num_pages;
}
diff --git a/openbsc/src/rest_octets.c b/openbsc/src/rest_octets.c
new file mode 100644
index 000000000..74874bd9a
--- /dev/null
+++ b/openbsc/src/rest_octets.c
@@ -0,0 +1,396 @@
+/* GSM Mobile Radio Interface Layer 3 messages on the A-bis interface,
+ * rest octet handling according to
+ * 3GPP TS 04.08 version 7.21.0 Release 1998 / ETSI TS 100 940 V7.21.0 */
+
+/* (C) 2009 by Harald Welte <laforge@gnumonks.org>
+ *
+ * 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 <string.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include <openbsc/gsm_data.h>
+#include <openbsc/bitvec.h>
+#include <openbsc/rest_octets.h>
+
+/* generate SI1 rest octets */
+int rest_octets_si1(u_int8_t *data, u_int8_t *nch_pos)
+{
+ struct bitvec bv;
+
+ memset(&bv, 0, sizeof(bv));
+ bv.data = data;
+ bv.data_len = 1;
+
+ if (nch_pos) {
+ bitvec_set_bit(&bv, H);
+ bitvec_set_uint(&bv, *nch_pos, 5);
+ } else
+ bitvec_set_bit(&bv, L);
+
+ bitvec_spare_padding(&bv, 7);
+ return bv.data_len;
+}
+
+/* Append selection parameters to bitvec */
+static void append_selection_params(struct bitvec *bv,
+ const struct gsm48_si_selection_params *sp)
+{
+ if (sp->present) {
+ bitvec_set_bit(bv, H);
+ bitvec_set_bit(bv, sp->cbq);
+ bitvec_set_uint(bv, sp->cell_resel_off, 6);
+ bitvec_set_uint(bv, sp->temp_offs, 3);
+ bitvec_set_uint(bv, sp->penalty_time, 5);
+ } else
+ bitvec_set_bit(bv, L);
+}
+
+/* Append power offset to bitvec */
+static void append_power_offset(struct bitvec *bv,
+ const struct gsm48_si_power_offset *po)
+{
+ if (po->present) {
+ bitvec_set_bit(bv, H);
+ bitvec_set_uint(bv, po->power_offset, 2);
+ } else
+ bitvec_set_bit(bv, L);
+}
+
+/* Append GPRS indicator to bitvec */
+static void append_gprs_ind(struct bitvec *bv,
+ const struct gsm48_si3_gprs_ind *gi)
+{
+ if (gi->present) {
+ bitvec_set_bit(bv, H);
+ bitvec_set_uint(bv, gi->ra_colour, 3);
+ /* 0 == SI13 in BCCH Norm, 1 == SI13 sent on BCCH Ext */
+ bitvec_set_bit(bv, gi->si13_position);
+ } else
+ bitvec_set_bit(bv, L);
+}
+
+
+/* Generate SI3 Rest Octests (Chapter 10.5.2.34 / Table 10.4.72) */
+int rest_octets_si3(u_int8_t *data, const struct gsm48_si_ro_info *si3)
+{
+ struct bitvec bv;
+
+ memset(&bv, 0, sizeof(bv));
+ bv.data = data;
+ bv.data_len = 4;
+
+ /* Optional Selection Parameters */
+ append_selection_params(&bv, &si3->selection_params);
+
+ /* Optional Power Offset */
+ append_power_offset(&bv, &si3->power_offset);
+
+ /* Do we have a SI2ter on the BCCH? */
+ if (si3->si2ter_indicator)
+ bitvec_set_bit(&bv, H);
+ else
+ bitvec_set_bit(&bv, L);
+
+ /* Early Classmark Sending Control */
+ if (si3->early_cm_ctrl)
+ bitvec_set_bit(&bv, H);
+ else
+ bitvec_set_bit(&bv, L);
+
+ /* Do we have a SI Type 9 on the BCCH? */
+ if (si3->scheduling.present) {
+ bitvec_set_bit(&bv, H);
+ bitvec_set_uint(&bv, si3->scheduling.where, 3);
+ } else
+ bitvec_set_bit(&bv, L);
+
+ /* GPRS Indicator */
+ append_gprs_ind(&bv, &si3->gprs_ind);
+
+ bitvec_spare_padding(&bv, (bv.data_len*8)-1);
+ return bv.data_len;
+}
+
+static int append_lsa_params(struct bitvec *bv,
+ const struct gsm48_lsa_params *lsa_params)
+{
+ /* FIXME */
+}
+
+/* Generate SI4 Rest Octets (Chapter 10.5.2.35) */
+int rest_octets_si4(u_int8_t *data, const struct gsm48_si_ro_info *si4)
+{
+ struct bitvec bv;
+
+ memset(&bv, 0, sizeof(bv));
+ bv.data = data;
+ bv.data_len = 10; /* FIXME: up to ? */
+
+ /* SI4 Rest Octets O */
+ append_selection_params(&bv, &si4->selection_params);
+ append_power_offset(&bv, &si4->power_offset);
+ append_gprs_ind(&bv, &si4->gprs_ind);
+
+ if (0 /* FIXME */) {
+ /* H and SI4 Rest Octets S */
+ bitvec_set_bit(&bv, H);
+
+ /* LSA Parameters */
+ if (si4->lsa_params.present) {
+ bitvec_set_bit(&bv, H);
+ append_lsa_params(&bv, &si4->lsa_params);
+ } else
+ bitvec_set_bit(&bv, L);
+
+ /* Cell Identity */
+ if (1) {
+ bitvec_set_bit(&bv, H);
+ bitvec_set_uint(&bv, si4->cell_id, 16);
+ } else
+ bitvec_set_bit(&bv, L);
+
+ /* LSA ID Information */
+ if (0) {
+ bitvec_set_bit(&bv, H);
+ /* FIXME */
+ } else
+ bitvec_set_bit(&bv, L);
+ } else {
+ /* L and break indicator */
+ bitvec_set_bit(&bv, L);
+ bitvec_set_bit(&bv, si4->break_ind ? H : L);
+ }
+
+ return bv.data_len;
+}
+
+/* GPRS Mobile Allocation as per TS 04.60 Chapter 12.10a:
+ < GPRS Mobile Allocation IE > ::=
+ < HSN : bit (6) >
+ { 0 | 1 < RFL number list : < RFL number list struct > > }
+ { 0 < MA_LENGTH : bit (6) >
+ < MA_BITMAP: bit (val(MA_LENGTH) + 1) >
+ | 1 { 0 | 1 <ARFCN index list : < ARFCN index list struct > > } } ;
+
+ < RFL number list struct > :: =
+ < RFL_NUMBER : bit (4) >
+ { 0 | 1 < RFL number list struct > } ;
+ < ARFCN index list struct > ::=
+ < ARFCN_INDEX : bit(6) >
+ { 0 | 1 < ARFCN index list struct > } ;
+ */
+static int append_gprs_mobile_alloc(struct bitvec *bv)
+{
+ /* Hopping Sequence Number */
+ bitvec_set_uint(bv, 0, 6);
+
+ if (0) {
+ /* We want to use a RFL number list */
+ bitvec_set_bit(bv, 1);
+ /* FIXME: RFL number list */
+ } else
+ bitvec_set_bit(bv, 0);
+
+ if (0) {
+ /* We want to use a MA_BITMAP */
+ bitvec_set_bit(bv, 0);
+ /* FIXME: MA_LENGTH, MA_BITMAP, ... */
+ } else {
+ bitvec_set_bit(bv, 1);
+ if (0) {
+ /* We want to provide an ARFCN index list */
+ bitvec_set_bit(bv, 1);
+ /* FIXME */
+ } else
+ bitvec_set_bit(bv, 0);
+ }
+ return 0;
+}
+
+static int encode_t3192(unsigned int t3192)
+{
+ if (t3192 == 0)
+ return 3;
+ else if (t3192 <= 80)
+ return 4;
+ else if (t3192 <= 120)
+ return 5;
+ else if (t3192 <= 160)
+ return 6;
+ else if (t3192 <= 200)
+ return 7;
+ else if (t3192 <= 500)
+ return 0;
+ else if (t3192 <= 1000)
+ return 1;
+ else if (t3192 <= 1500)
+ return 2;
+ else
+ return -EINVAL;
+}
+
+static int encode_drx_timer(unsigned int drx)
+{
+ if (drx == 0)
+ return 0;
+ else if (drx == 1)
+ return 1;
+ else if (drx == 2)
+ return 2;
+ else if (drx <= 4)
+ return 3;
+ else if (drx <= 8)
+ return 4;
+ else if (drx <= 16)
+ return 5;
+ else if (drx <= 32)
+ return 6;
+ else if (drx <= 64)
+ return 7;
+ else
+ return -EINVAL;
+}
+
+/* GPRS Cell Options as per TS 04.60 Chapter 12.24
+ < GPRS Cell Options IE > ::=
+ < NMO : bit(2) >
+ < T3168 : bit(3) >
+ < T3192 : bit(3) >
+ < DRX_TIMER_MAX: bit(3) >
+ < ACCESS_BURST_TYPE: bit >
+ < CONTROL_ACK_TYPE : bit >
+ < BS_CV_MAX: bit(4) >
+ { 0 | 1 < PAN_DEC : bit(3) >
+ < PAN_INC : bit(3) >
+ < PAN_MAX : bit(3) >
+ { 0 | 1 < Extension Length : bit(6) >
+ < bit (val(Extension Length) + 1
+ & { < Extension Information > ! { bit ** = <no string> } } ;
+ < Extension Information > ::=
+ { 0 | 1 < EGPRS_PACKET_CHANNEL_REQUEST : bit >
+ < BEP_PERIOD : bit(4) > }
+ < PFC_FEATURE_MODE : bit >
+ < DTM_SUPPORT : bit >
+ <BSS_PAGING_COORDINATION: bit >
+ <spare bit > ** ;
+ */
+static int append_gprs_cell_opt(struct bitvec *bv,
+ const struct gprs_cell_options *gco)
+{
+ int t3192, drx_timer_max;
+
+ t3192 = encode_t3192(gco->t3192);
+ if (t3192 < 0)
+ return t3192;
+
+ drx_timer_max = encode_drx_timer(gco->drx_timer_max);
+ if (drx_timer_max < 0)
+ return drx_timer_max;
+
+ bitvec_set_uint(bv, gco->nmo, 2);
+ bitvec_set_uint(bv, gco->t3168 / 500, 3);
+ bitvec_set_uint(bv, t3192, 3);
+ bitvec_set_uint(bv, drx_timer_max, 3);
+ /* ACCESS_BURST_TYPE: Hard-code 8bit */
+ bitvec_set_bit(bv, 0);
+ /* CONTROL_ACK_TYPE: Hard-code to RLC/MAC control block */
+ bitvec_set_bit(bv, 1);
+ bitvec_set_uint(bv, gco->bs_cv_max, 4);
+
+ /* hard-code no PAN_{DEC,INC,MAX} */
+ bitvec_set_bit(bv, 0);
+
+ /* no extension information (EDGE) */
+ bitvec_set_bit(bv, 0);
+
+ return 0;
+}
+
+static void append_gprs_pwr_ctrl_pars(struct bitvec *bv,
+ const struct gprs_power_ctrl_pars *pcp)
+{
+ bitvec_set_uint(bv, pcp->alpha, 4);
+ bitvec_set_uint(bv, pcp->t_avg_w, 5);
+ bitvec_set_uint(bv, pcp->t_avg_t, 5);
+ bitvec_set_uint(bv, pcp->pc_meas_chan, 1);
+ bitvec_set_uint(bv, pcp->n_avg_i, 4);
+}
+
+/* Generate SI13 Rest Octests (Chapter 10.5.2.37b) */
+int rest_octets_si13(u_int8_t *data, const struct gsm48_si13_info *si13)
+{
+ struct bitvec bv;
+
+ memset(&bv, 0, sizeof(bv));
+ bv.data = data;
+ bv.data_len = 20;
+
+ if (0) {
+ /* No rest octets */
+ bitvec_set_bit(&bv, L);
+ } else {
+ bitvec_set_bit(&bv, H);
+ bitvec_set_uint(&bv, si13->bcch_change_mark, 3);
+ bitvec_set_uint(&bv, si13->si_change_field, 4);
+ if (0) {
+ bitvec_set_bit(&bv, 0);
+ } else {
+ bitvec_set_bit(&bv, 1);
+ bitvec_set_uint(&bv, si13->bcch_change_mark, 2);
+ append_gprs_mobile_alloc(&bv);
+ }
+ if (!si13->pbcch_present) {
+ /* PBCCH not present in cell */
+ bitvec_set_bit(&bv, 0);
+ bitvec_set_uint(&bv, si13->no_pbcch.rac, 8);
+ bitvec_set_bit(&bv, si13->no_pbcch.spgc_ccch_sup);
+ bitvec_set_uint(&bv, si13->no_pbcch.prio_acc_thr, 3);
+ bitvec_set_uint(&bv, si13->no_pbcch.net_ctrl_ord, 2);
+ append_gprs_cell_opt(&bv, &si13->cell_opts);
+ append_gprs_pwr_ctrl_pars(&bv, &si13->pwr_ctrl_pars);
+ } else {
+ /* PBCCH present in cell */
+ bitvec_set_bit(&bv, 1);
+ bitvec_set_uint(&bv, si13->pbcch.psi1_rep_per, 4);
+ /* PBCCH Descripiton */
+ bitvec_set_uint(&bv, si13->pbcch.pb, 4);
+ bitvec_set_uint(&bv, si13->pbcch.tsc, 3);
+ bitvec_set_uint(&bv, si13->pbcch.tn, 3);
+ switch (si13->pbcch.carrier_type) {
+ case PBCCH_BCCH:
+ bitvec_set_bit(&bv, 0);
+ bitvec_set_bit(&bv, 0);
+ break;
+ case PBCCH_ARFCN:
+ bitvec_set_bit(&bv, 0);
+ bitvec_set_bit(&bv, 1);
+ bitvec_set_uint(&bv, si13->pbcch.arfcn, 10);
+ break;
+ case PBCCH_MAIO:
+ bitvec_set_bit(&bv, 1);
+ bitvec_set_uint(&bv, si13->pbcch.maio, 6);
+ break;
+ }
+ }
+ }
+ bitvec_spare_padding(&bv, (bv.data_len*8)-1);
+ return bv.data_len;
+}
diff --git a/openbsc/src/rrlp.c b/openbsc/src/rrlp.c
index 523b53f0b..35044518c 100644
--- a/openbsc/src/rrlp.c
+++ b/openbsc/src/rrlp.c
@@ -1,4 +1,4 @@
-
+/* Radio Resource LCS (Location) Protocol, GMS TS 04.31 */
/* (C) 2009 by Harald Welte <laforge@gnumonks.org>
*
@@ -28,9 +28,42 @@
#include <openbsc/gsm_subscriber.h>
#include <openbsc/chan_alloc.h>
-/* RRLP MS based position request */
+/* RRLP msPositionReq, nsBased,
+ * Accuracy=60, Method=gps, ResponseTime=2, oneSet */
static const u_int8_t ms_based_pos_req[] = { 0x40, 0x01, 0x78, 0xa8 };
+/* RRLP msPositionReq, msBasedPref,
+ Accuracy=60, Method=gpsOrEOTD, ResponseTime=5, multipleSets */
+static const u_int8_t ms_pref_pos_req[] = { 0x40, 0x02, 0x79, 0x50 };
+
+/* RRLP msPositionReq, msAssistedPref,
+ Accuracy=60, Method=gpsOrEOTD, ResponseTime=5, multipleSets */
+static const u_int8_t ass_pref_pos_req[] = { 0x40, 0x03, 0x79, 0x50 };
+
+static int send_rrlp_req(struct gsm_lchan *lchan)
+{
+ struct gsm_network *net = lchan->ts->trx->bts->network;
+ const u_int8_t *req;
+
+ switch (net->rrlp.mode) {
+ case RRLP_MODE_MS_BASED:
+ req = ms_based_pos_req;
+ break;
+ case RRLP_MODE_MS_PREF:
+ req = ms_pref_pos_req;
+ break;
+ case RRLP_MODE_ASS_PREF:
+ req = ass_pref_pos_req;
+ break;
+ case RRLP_MODE_NONE:
+ default:
+ return 0;
+ }
+
+ return gsm48_send_rr_app_info(lchan, 0x00,
+ sizeof(ms_based_pos_req), req);
+}
+
static int subscr_sig_cb(unsigned int subsys, unsigned int signal,
void *handler_data, void *signal_data)
{
@@ -44,8 +77,7 @@ static int subscr_sig_cb(unsigned int subsys, unsigned int signal,
lchan = lchan_for_subscr(subscr);
if (!lchan)
break;
- gsm48_send_rr_app_info(lchan, 0x00, sizeof(ms_based_pos_req),
- ms_based_pos_req);
+ send_rrlp_req(lchan);
break;
}
return 0;
@@ -57,11 +89,11 @@ static int paging_sig_cb(unsigned int subsys, unsigned int signal,
struct paging_signal_data *psig_data = signal_data;
switch (signal) {
- case S_PAGING_COMPLETED:
+ case S_PAGING_SUCCEEDED:
/* A subscriber has attached. */
- gsm48_send_rr_app_info(psig_data->lchan, 0x00,
- sizeof(ms_based_pos_req),
- ms_based_pos_req);
+ send_rrlp_req(psig_data->lchan);
+ break;
+ case S_PAGING_EXPIRED:
break;
}
return 0;
diff --git a/openbsc/src/rtp_proxy.c b/openbsc/src/rtp_proxy.c
index 59c0735a5..83b774f97 100644
--- a/openbsc/src/rtp_proxy.c
+++ b/openbsc/src/rtp_proxy.c
@@ -24,6 +24,10 @@
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
+#include <sys/time.h> /* gettimeofday() */
+#include <unistd.h> /* get..() */
+#include <time.h> /* clock() */
+#include <sys/utsname.h> /* uname() */
#include <openbsc/talloc.h>
#include <openbsc/gsm_data.h>
@@ -57,6 +61,214 @@ struct rtcp_hdr {
#define RTCP_IE_CNAME 1
+/* according to RFC 3550 */
+struct rtp_hdr {
+ u_int8_t csrc_count:4,
+ extension:1,
+ padding:1,
+ version:2;
+ u_int8_t payload_type:7,
+ marker:1;
+ u_int16_t sequence;
+ u_int32_t timestamp;
+ u_int32_t ssrc;
+} __attribute__((packed));
+
+struct rtp_x_hdr {
+ u_int16_t by_profile;
+ u_int16_t length;
+} __attribute__((packed));
+
+#define RTP_VERSION 2
+
+#define RTP_PT_GSM_FULL 3
+#define RTP_PT_GSM_EFR 97
+
+/* decode an rtp frame and create a new buffer with payload */
+static int rtp_decode(struct msgb *msg, u_int32_t callref, struct msgb **data)
+{
+ struct msgb *new_msg;
+ struct gsm_data_frame *frame;
+ struct rtp_hdr *rtph = (struct rtp_hdr *)msg->data;
+ struct rtp_x_hdr *rtpxh;
+ u_int8_t *payload;
+ int payload_len;
+ int msg_type;
+ int x_len;
+
+ if (msg->len < 12) {
+ DEBUGPC(DMUX, "received RTP frame too short (len = %d)\n",
+ msg->len);
+ return -EINVAL;
+ }
+ if (rtph->version != RTP_VERSION) {
+ DEBUGPC(DMUX, "received RTP version %d not supported.\n",
+ rtph->version);
+ return -EINVAL;
+ }
+ payload = msg->data + sizeof(struct rtp_hdr) + (rtph->csrc_count << 2);
+ payload_len = msg->len - sizeof(struct rtp_hdr) - (rtph->csrc_count << 2);
+ if (payload_len < 0) {
+ DEBUGPC(DMUX, "received RTP frame too short (len = %d, "
+ "csrc count = %d)\n", msg->len, rtph->csrc_count);
+ return -EINVAL;
+ }
+ if (rtph->extension) {
+ if (payload_len < sizeof(struct rtp_x_hdr)) {
+ DEBUGPC(DMUX, "received RTP frame too short for "
+ "extension header\n");
+ return -EINVAL;
+ }
+ rtpxh = (struct rtp_x_hdr *)payload;
+ x_len = ntohs(rtpxh->length) * 4 + sizeof(struct rtp_x_hdr);
+ payload += x_len;
+ payload_len -= x_len;
+ if (payload_len < 0) {
+ DEBUGPC(DMUX, "received RTP frame too short, "
+ "extension header exceeds frame length\n");
+ return -EINVAL;
+ }
+ }
+ if (rtph->padding) {
+ if (payload_len < 0) {
+ DEBUGPC(DMUX, "received RTP frame too short for "
+ "padding length\n");
+ return -EINVAL;
+ }
+ payload_len -= payload[payload_len - 1];
+ if (payload_len < 0) {
+ DEBUGPC(DMUX, "received RTP frame with padding "
+ "greater than payload\n");
+ return -EINVAL;
+ }
+ }
+
+ switch (rtph->payload_type) {
+ case RTP_PT_GSM_FULL:
+ msg_type = GSM_TCHF_FRAME;
+ if (payload_len != 33) {
+ DEBUGPC(DMUX, "received RTP full rate frame with "
+ "payload length != 32 (len = %d)\n",
+ payload_len);
+ return -EINVAL;
+ }
+ break;
+ case RTP_PT_GSM_EFR:
+ msg_type = GSM_TCHF_FRAME_EFR;
+ break;
+ default:
+ DEBUGPC(DMUX, "received RTP frame with unknown payload "
+ "type %d\n", rtph->payload_type);
+ return -EINVAL;
+ }
+
+ new_msg = msgb_alloc(sizeof(struct gsm_data_frame) + payload_len,
+ "GSM-DATA");
+ if (!new_msg)
+ return -ENOMEM;
+ frame = (struct gsm_data_frame *)(new_msg->data);
+ frame->msg_type = msg_type;
+ frame->callref = callref;
+ memcpy(frame->data, payload, payload_len);
+ msgb_put(new_msg, sizeof(struct gsm_data_frame) + payload_len);
+
+ *data = new_msg;
+ return 0;
+}
+
+/* "to - from" */
+static void tv_difference(struct timeval *diff, const struct timeval *from,
+ const struct timeval *__to)
+{
+ struct timeval _to = *__to, *to = &_to;
+
+ if (to->tv_usec < from->tv_usec) {
+ to->tv_sec -= 1;
+ to->tv_usec += 1000000;
+ }
+
+ diff->tv_usec = to->tv_usec - from->tv_usec;
+ diff->tv_sec = to->tv_sec - from->tv_sec;
+}
+
+/* encode and send a rtp frame */
+int rtp_send_frame(struct rtp_socket *rs, struct gsm_data_frame *frame)
+{
+ struct rtp_sub_socket *rss = &rs->rtp;
+ struct msgb *msg;
+ struct rtp_hdr *rtph;
+ int payload_type;
+ int payload_len;
+ int duration; /* in samples */
+
+ if (rs->tx_action != RTP_SEND_DOWNSTREAM) {
+ /* initialize sequences */
+ rs->tx_action = RTP_SEND_DOWNSTREAM;
+ rs->transmit.ssrc = rand();
+ rs->transmit.sequence = random();
+ rs->transmit.timestamp = random();
+ }
+
+ switch (frame->msg_type) {
+ case GSM_TCHF_FRAME:
+ payload_type = RTP_PT_GSM_FULL;
+ payload_len = 33;
+ duration = 160;
+ break;
+ case GSM_TCHF_FRAME_EFR:
+ payload_type = RTP_PT_GSM_EFR;
+ payload_len = 31;
+ duration = 160;
+ break;
+ default:
+ DEBUGPC(DMUX, "unsupported message type %d\n",
+ frame->msg_type);
+ return -EINVAL;
+ }
+
+ {
+ struct timeval tv, tv_diff;
+ long int usec_diff, frame_diff;
+
+ gettimeofday(&tv, NULL);
+ tv_difference(&tv_diff, &rs->transmit.last_tv, &tv);
+ rs->transmit.last_tv = tv;
+
+ usec_diff = tv_diff.tv_sec * 1000000 + tv_diff.tv_usec;
+ frame_diff = (usec_diff / 20000);
+
+ if (abs(frame_diff) > 1) {
+ long int frame_diff_excess = frame_diff - 1;
+
+ LOGP(DMUX, LOGL_NOTICE,
+ "Correcting frame difference of %ld frames\n", frame_diff_excess);
+ rs->transmit.sequence += frame_diff_excess;
+ rs->transmit.timestamp += frame_diff_excess * duration;
+ }
+ }
+
+ msg = msgb_alloc(sizeof(struct rtp_hdr) + payload_len, "RTP-GSM-FULL");
+ if (!msg)
+ return -ENOMEM;
+ rtph = (struct rtp_hdr *)msg->data;
+ rtph->version = RTP_VERSION;
+ rtph->padding = 0;
+ rtph->extension = 0;
+ rtph->csrc_count = 0;
+ rtph->marker = 0;
+ rtph->payload_type = payload_type;
+ rtph->sequence = htons(rs->transmit.sequence++);
+ rtph->timestamp = htonl(rs->transmit.timestamp);
+ rs->transmit.timestamp += duration;
+ rtph->ssrc = htonl(rs->transmit.ssrc);
+ memcpy(msg->data + sizeof(struct rtp_hdr), frame->data, payload_len);
+ msgb_put(msg, sizeof(struct rtp_hdr) + payload_len);
+ msgb_enqueue(&rss->tx_queue, msg);
+ rss->bfd.when |= BSC_FD_WRITE;
+
+ return 0;
+}
+
/* iterate over all chunks in one RTCP message, look for CNAME IEs and
* replace all of those with 'new_cname' */
static int rtcp_sdes_cname_mangle(struct msgb *msg, struct rtcp_hdr *rh,
@@ -123,10 +335,16 @@ static int rtcp_mangle(struct msgb *msg, struct rtp_socket *rs)
if (!mangle_rtcp_cname)
return 0;
+ printf("RTCP\n");
/* iterate over list of RTCP messages */
rtph = (struct rtcp_hdr *)msg->data;
- while ((void *)rtph + sizeof(*rtph) < (void *)msg->data + msg->len) {
+ while ((void *)rtph + sizeof(*rtph) <= (void *)msg->data + msg->len) {
old_len = (ntohs(rtph->length) + 1) * 4;
+ if ((void *)rtph + old_len > (void *)msg->data + msg->len) {
+ DEBUGPC(DMUX, "received RTCP packet too short for "
+ "length element\n");
+ return -EINVAL;
+ }
if (rtph->type == RTCP_TYPE_SDES) {
char new_cname[255];
strncpy(new_cname, inet_ntoa(rss->sin_local.sin_addr),
@@ -148,6 +366,7 @@ static int rtp_socket_read(struct rtp_socket *rs, struct rtp_sub_socket *rss)
{
int rc;
struct msgb *msg = msgb_alloc(RTP_ALLOC_SIZE, "RTP/RTCP");
+ struct msgb *new_msg;
struct rtp_sub_socket *other_rss;
if (!msg)
@@ -184,13 +403,40 @@ static int rtp_socket_read(struct rtp_socket *rs, struct rtp_sub_socket *rss)
break;
case RTP_RECV_UPSTREAM:
- case RTP_NONE:
- /* FIXME: other cases */
- DEBUGP(DMUX, "unhandled action: %d\n", rs->rx_action);
+ if (!rs->receive.callref || !rs->receive.net) {
+ rc = -EIO;
+ goto out_free;
+ }
+ if (rss->bfd.priv_nr == RTP_PRIV_RTCP) {
+ if (!mangle_rtcp_cname) {
+ msgb_free(msg);
+ break;
+ }
+ /* modify RTCP SDES CNAME */
+ rc = rtcp_mangle(msg, rs);
+ if (rc < 0)
+ goto out_free;
+ msgb_enqueue(&rss->tx_queue, msg);
+ rss->bfd.when |= BSC_FD_WRITE;
+ break;
+ }
+ if (rss->bfd.priv_nr != RTP_PRIV_RTP) {
+ rc = -EINVAL;
+ goto out_free;
+ }
+ rc = rtp_decode(msg, rs->receive.callref, &new_msg);
+ if (rc < 0)
+ goto out_free;
+ msgb_free(msg);
+ msgb_enqueue(&rs->receive.net->upqueue, new_msg);
+ break;
+
+ case RTP_NONE: /* if socket exists, but disabled by app */
+ msgb_free(msg);
break;
}
- return rc;
+ return 0;
out_free:
msgb_free(msg);
@@ -211,7 +457,7 @@ static int rtp_socket_write(struct rtp_socket *rs, struct rtp_sub_socket *rss)
written = write(rss->bfd.fd, msg->data, msg->len);
if (written < msg->len) {
- perror("short write");
+ LOGP(DMIB, LOGL_ERROR, "short write");
msgb_free(msg);
return -EIO;
}
@@ -420,6 +666,23 @@ int rtp_socket_proxy(struct rtp_socket *this, struct rtp_socket *other)
return 0;
}
+/* bind RTP/RTCP socket to application */
+int rtp_socket_upstream(struct rtp_socket *this, struct gsm_network *net,
+ u_int32_t callref)
+{
+ DEBUGP(DMUX, "rtp_socket_proxy(this=%p, callref=%u)\n",
+ this, callref);
+
+ if (callref) {
+ this->rx_action = RTP_RECV_UPSTREAM;
+ this->receive.net = net;
+ this->receive.callref = callref;
+ } else
+ this->rx_action = RTP_NONE;
+
+ return 0;
+}
+
static void free_tx_queue(struct rtp_sub_socket *rss)
{
struct msgb *msg;
diff --git a/openbsc/src/select.c b/openbsc/src/select.c
index c11f3a511..bed96498c 100644
--- a/openbsc/src/select.c
+++ b/openbsc/src/select.c
@@ -95,14 +95,20 @@ restart:
llist_for_each_entry_safe(ufd, tmp, &bsc_fds, list) {
int flags = 0;
- if (FD_ISSET(ufd->fd, &readset))
+ if (FD_ISSET(ufd->fd, &readset)) {
flags |= BSC_FD_READ;
+ FD_CLR(ufd->fd, &readset);
+ }
- if (FD_ISSET(ufd->fd, &writeset))
+ if (FD_ISSET(ufd->fd, &writeset)) {
flags |= BSC_FD_WRITE;
+ FD_CLR(ufd->fd, &writeset);
+ }
- if (FD_ISSET(ufd->fd, &exceptset))
+ if (FD_ISSET(ufd->fd, &exceptset)) {
flags |= BSC_FD_EXCEPT;
+ FD_CLR(ufd->fd, &exceptset);
+ }
if (flags) {
work = 1;
diff --git a/openbsc/src/silent_call.c b/openbsc/src/silent_call.c
index 82b656327..a0c166e0a 100644
--- a/openbsc/src/silent_call.c
+++ b/openbsc/src/silent_call.c
@@ -34,6 +34,7 @@
#include <openbsc/abis_rsl.h>
#include <openbsc/chan_alloc.h>
+/* paging of the requested subscriber has completed */
static int paging_cb_silent(unsigned int hooknum, unsigned int event,
struct msgb *msg, void *_lchan, void *_data)
{
@@ -53,6 +54,7 @@ static int paging_cb_silent(unsigned int hooknum, unsigned int event,
case GSM_PAGING_SUCCEEDED:
DEBUGPC(DSMS, "success, using Timeslot %u on ARFCN %u\n",
lchan->ts->nr, lchan->ts->trx->arfcn);
+ lchan->silent_call = 1;
/* increment lchan reference count */
dispatch_signal(SS_SCALL, S_SCALL_SUCCESS, &sigdata);
use_lchan(lchan);
@@ -69,15 +71,58 @@ static int paging_cb_silent(unsigned int hooknum, unsigned int event,
return rc;
}
-int gsm_silent_call_start(struct gsm_subscriber *subscr, void *data)
+/* receive a layer 3 message from a silent call */
+int silent_call_rx(struct msgb *msg)
+{
+ /* FIXME: do something like sending it through a UDP port */
+ return 0;
+}
+
+struct msg_match {
+ u_int8_t pdisc;
+ u_int8_t msg_type;
+};
+
+/* list of messages that are handled inside OpenBSC, even in a silent call */
+static const struct msg_match silent_call_accept[] = {
+ { GSM48_PDISC_MM, GSM48_MT_MM_LOC_UPD_REQUEST },
+ { GSM48_PDISC_MM, GSM48_MT_MM_CM_SERV_REQ },
+};
+
+/* decide if we need to reroute a message as part of a silent call */
+int silent_call_reroute(struct msgb *msg)
+{
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ u_int8_t pdisc = gh->proto_discr & 0x0f;
+ int i;
+
+ /* if we're not part of a silent call, never reroute */
+ if (!msg->lchan->silent_call)
+ return 0;
+
+ /* check if we are a special message that is handled in openbsc */
+ for (i = 0; i < ARRAY_SIZE(silent_call_accept); i++) {
+ if (silent_call_accept[i].pdisc == pdisc &&
+ silent_call_accept[i].msg_type == gh->msg_type)
+ return 0;
+ }
+
+ /* otherwise, reroute */
+ return 1;
+}
+
+
+/* initiate a silent call with a given subscriber */
+int gsm_silent_call_start(struct gsm_subscriber *subscr, void *data, int type)
{
int rc;
- rc = paging_request(subscr->net, subscr, RSL_CHANNEED_TCH_F,
+ rc = paging_request(subscr->net, subscr, type,
paging_cb_silent, data);
return rc;
}
+/* end a silent call with a given subscriber */
int gsm_silent_call_stop(struct gsm_subscriber *subscr)
{
struct gsm_lchan *lchan;
@@ -86,7 +131,10 @@ int gsm_silent_call_stop(struct gsm_subscriber *subscr)
if (!lchan)
return -EINVAL;
- /* FIXME: did we actually establish a silent call for this guy? */
+ /* did we actually establish a silent call for this guy? */
+ if (!lchan->silent_call)
+ return -EINVAL;
+
put_lchan(lchan);
return 0;
diff --git a/openbsc/src/statistics.c b/openbsc/src/statistics.c
new file mode 100644
index 000000000..9452b16e1
--- /dev/null
+++ b/openbsc/src/statistics.c
@@ -0,0 +1,70 @@
+/* utility routines for keeping some statistics */
+
+/* (C) 2009 by Harald Welte <laforge@gnumonks.org>
+ *
+ * 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 <sys/types.h>
+
+#include <openbsc/gsm_data.h>
+#include <openbsc/signal.h>
+#include <openbsc/linuxlist.h>
+#include <openbsc/talloc.h>
+#include <openbsc/statistics.h>
+#include <openbsc/db.h>
+#include <openbsc/timer.h>
+
+static LLIST_HEAD(counters);
+
+void *tall_ctr_ctx;
+
+struct counter *counter_alloc(const char *name)
+{
+ struct counter *ctr = talloc_zero(tall_ctr_ctx, struct counter);
+
+ if (!ctr)
+ return NULL;
+
+ ctr->name = name;
+ llist_add_tail(&ctr->list, &counters);
+
+ return ctr;
+}
+
+void counter_free(struct counter *ctr)
+{
+ llist_del(&ctr->list);
+ talloc_free(ctr);
+}
+
+int counters_for_each(int (*handle_counter)(struct counter *, void *), void *data)
+{
+ struct counter *ctr;
+ int rc = 0;
+
+ llist_for_each_entry(ctr, &counters, list) {
+ rc = handle_counter(ctr, data);
+ if (rc < 0)
+ return rc;
+ }
+
+ return rc;
+}
+
diff --git a/openbsc/src/system_information.c b/openbsc/src/system_information.c
new file mode 100644
index 000000000..9bdf2c139
--- /dev/null
+++ b/openbsc/src/system_information.c
@@ -0,0 +1,469 @@
+/* GSM 04.08 System Information (SI) encoding and decoding
+ * 3GPP TS 04.08 version 7.21.0 Release 1998 / ETSI TS 100 940 V7.21.0 */
+
+/* (C) 2008-2009 by Harald Welte <laforge@gnumonks.org>
+ *
+ * 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 <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+
+#include <openbsc/gsm_04_08.h>
+#include <openbsc/gsm_data.h>
+#include <openbsc/abis_rsl.h>
+#include <openbsc/rest_octets.h>
+#include <openbsc/bitvec.h>
+#include <openbsc/debug.h>
+
+#define GSM48_CELL_CHAN_DESC_SIZE 16
+#define GSM_MACBLOCK_LEN 23
+#define GSM_MACBLOCK_PADDING 0x2b
+
+/* verify the sizes of the system information type structs */
+
+/* rest octets are not part of the struct */
+static_assert(sizeof(struct gsm48_system_information_type_header) == 3, _si_header_size);
+static_assert(sizeof(struct gsm48_rach_control) == 3, _si_rach_control);
+static_assert(sizeof(struct gsm48_system_information_type_1) == 22, _si1_size);
+static_assert(sizeof(struct gsm48_system_information_type_2) == 23, _si2_size);
+static_assert(sizeof(struct gsm48_system_information_type_3) == 19, _si3_size);
+static_assert(sizeof(struct gsm48_system_information_type_4) == 13, _si4_size);
+
+/* bs11 forgot the l2 len, 0-6 rest octets */
+static_assert(sizeof(struct gsm48_system_information_type_5) == 18, _si5_size);
+static_assert(sizeof(struct gsm48_system_information_type_6) == 11, _si6_size);
+
+static_assert(sizeof(struct gsm48_system_information_type_13) == 3, _si13_size);
+
+/* Frequency Lists as per TS 04.08 10.5.2.13 */
+
+/* 10.5.2.13.2: Bit map 0 format */
+static int freq_list_bm0_set_arfcn(u_int8_t *chan_list, unsigned int arfcn)
+{
+ unsigned int byte, bit;
+
+ if (arfcn > 124 || arfcn < 1) {
+ LOGP(DRR, LOGL_ERROR, "Bitmap 0 only supports ARFCN 1...124\n");
+ return -EINVAL;
+ }
+
+ /* the bitmask is from 1..124, not from 0..123 */
+ arfcn--;
+
+ byte = arfcn / 8;
+ bit = arfcn % 8;
+
+ chan_list[GSM48_CELL_CHAN_DESC_SIZE-1-byte] |= (1 << bit);
+
+ return 0;
+}
+
+/* 10.5.2.13.7: Variable bit map format */
+static int freq_list_bmrel_set_arfcn(u_int8_t *chan_list, unsigned int arfcn)
+{
+ unsigned int byte, bit;
+ unsigned int min_arfcn;
+ unsigned int bitno;
+
+ min_arfcn = (chan_list[0] & 1) << 9;
+ min_arfcn |= chan_list[1] << 1;
+ min_arfcn |= (chan_list[2] >> 7) & 1;
+
+ /* The lower end of our bitmaks is always implicitly included */
+ if (arfcn == min_arfcn)
+ return 0;
+
+ if (arfcn < min_arfcn) {
+ LOGP(DRR, LOGL_ERROR, "arfcn(%u) < min(%u)\n", arfcn, min_arfcn);
+ return -EINVAL;
+ }
+ if (arfcn > min_arfcn + 111) {
+ LOGP(DRR, LOGL_ERROR, "arfcn(%u) > min(%u) + 111\n", arfcn, min_arfcn);
+ return -EINVAL;
+ }
+
+ bitno = (arfcn - min_arfcn);
+ byte = bitno / 8;
+ bit = bitno % 8;
+
+ chan_list[2 + byte] |= 1 << (7 - bit);
+
+ return 0;
+}
+
+/* generate a cell channel list as per Section 10.5.2.1b of 04.08 */
+static int bitvec2freq_list(u_int8_t *chan_list, struct bitvec *bv,
+ const struct gsm_bts *bts)
+{
+ int i, rc, min = 1024, max = -1;
+
+ memset(chan_list, 0, 16);
+
+ /* GSM900-only handsets only support 'bit map 0 format' */
+ if (bts->band == GSM_BAND_900) {
+ chan_list[0] = 0;
+
+ for (i = 0; i < bv->data_len*8; i++) {
+ if (bitvec_get_bit_pos(bv, i)) {
+ rc = freq_list_bm0_set_arfcn(chan_list, i);
+ if (rc < 0)
+ return rc;
+ }
+ }
+ return 0;
+ }
+
+ /* We currently only support the 'Variable bitmap format' */
+ chan_list[0] = 0x8e;
+
+ for (i = 0; i < bv->data_len*8; i++) {
+ if (bitvec_get_bit_pos(bv, i)) {
+ if (i < min)
+ min = i;
+ if (i > max)
+ max = i;
+ }
+ }
+
+ if (max == -1) {
+ /* Empty set, use 'bit map 0 format' */
+ chan_list[0] = 0;
+ return 0;
+ }
+
+ if ((max - min) > 111) {
+ LOGP(DRR, LOGL_ERROR, "min_arfcn=%u, max_arfcn=%u, "
+ "distance > 111\n", min, max);
+ return -EINVAL;
+ }
+
+ chan_list[0] |= (min >> 9) & 1;
+ chan_list[1] = (min >> 1);
+ chan_list[2] = (min & 1) << 7;
+
+ for (i = 0; i < bv->data_len*8; i++) {
+ if (bitvec_get_bit_pos(bv, i)) {
+ rc = freq_list_bmrel_set_arfcn(chan_list, i);
+ if (rc < 0)
+ return rc;
+ }
+ }
+
+ return 0;
+}
+
+/* generate a cell channel list as per Section 10.5.2.1b of 04.08 */
+static int generate_cell_chan_list(u_int8_t *chan_list, struct gsm_bts *bts)
+{
+ struct gsm_bts_trx *trx;
+ struct bitvec *bv = &bts->si_common.cell_alloc;
+
+ /* first we generate a bitvec of all TRX ARFCN's in our BTS */
+ llist_for_each_entry(trx, &bts->trx_list, list)
+ bitvec_set_bit_pos(bv, trx->arfcn, 1);
+
+ /* then we generate a GSM 04.08 frequency list from the bitvec */
+ return bitvec2freq_list(chan_list, bv, bts);
+}
+
+/* generate a cell channel list as per Section 10.5.2.1b of 04.08 */
+static int generate_bcch_chan_list(u_int8_t *chan_list, struct gsm_bts *bts)
+{
+ struct gsm_bts *cur_bts;
+ struct bitvec *bv = &bts->si_common.neigh_list;
+
+ /* first we generate a bitvec of the BCCH ARFCN's in our BSC */
+ llist_for_each_entry(cur_bts, &bts->network->bts_list, list) {
+ if (cur_bts == bts)
+ continue;
+ bitvec_set_bit_pos(bv, cur_bts->c0->arfcn, 1);
+ }
+
+ /* then we generate a GSM 04.08 frequency list from the bitvec */
+ return bitvec2freq_list(chan_list, bv, bts);
+}
+
+static int generate_si1(u_int8_t *output, struct gsm_bts *bts)
+{
+ int rc;
+ struct gsm48_system_information_type_1 *si1 =
+ (struct gsm48_system_information_type_1 *) output;
+
+ memset(si1, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN);
+
+ si1->header.l2_plen = (21 << 2) | 1;
+ si1->header.rr_protocol_discriminator = GSM48_PDISC_RR;
+ si1->header.skip_indicator = 0;
+ si1->header.system_information = GSM48_MT_RR_SYSINFO_1;
+
+ rc = generate_cell_chan_list(si1->cell_channel_description, bts);
+ if (rc < 0)
+ return rc;
+
+ si1->rach_control = bts->si_common.rach_control;
+
+ /* SI1 Rest Octets (10.5.2.32), contains NCH position */
+ rc = rest_octets_si1(si1->rest_octets, NULL);
+ return sizeof(*si1) + rc;
+}
+
+static int generate_si2(u_int8_t *output, struct gsm_bts *bts)
+{
+ int rc;
+ struct gsm48_system_information_type_2 *si2 =
+ (struct gsm48_system_information_type_2 *) output;
+
+ memset(si2, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN);
+
+ si2->header.l2_plen = (22 << 2) | 1;
+ si2->header.rr_protocol_discriminator = GSM48_PDISC_RR;
+ si2->header.skip_indicator = 0;
+ si2->header.system_information = GSM48_MT_RR_SYSINFO_2;
+
+ rc = generate_bcch_chan_list(si2->bcch_frequency_list, bts);
+ if (rc < 0)
+ return rc;
+
+ si2->ncc_permitted = bts->si_common.ncc_permitted;
+ si2->rach_control = bts->si_common.rach_control;
+
+ return sizeof(*si2);
+}
+
+struct gsm48_si_ro_info si_info = {
+ .selection_params = {
+ .present = 0,
+ },
+ .power_offset = {
+ .present = 0,
+ },
+ .si2ter_indicator = 0,
+ .early_cm_ctrl = 1,
+ .scheduling = {
+ .present = 0,
+ },
+ .gprs_ind = {
+ .si13_position = 0,
+ .ra_colour = 0,
+ .present = 0,
+ },
+ .lsa_params = {
+ .present = 0,
+ },
+ .cell_id = 0, /* FIXME: doesn't the bts have this? */
+ .break_ind = 0,
+};
+
+static int generate_si3(u_int8_t *output, struct gsm_bts *bts)
+{
+ int rc;
+ struct gsm48_system_information_type_3 *si3 =
+ (struct gsm48_system_information_type_3 *) output;
+
+ memset(si3, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN);
+
+ si3->header.l2_plen = (18 << 2) | 1;
+ si3->header.rr_protocol_discriminator = GSM48_PDISC_RR;
+ si3->header.skip_indicator = 0;
+ si3->header.system_information = GSM48_MT_RR_SYSINFO_3;
+
+ si3->cell_identity = htons(bts->cell_identity);
+ gsm0408_generate_lai(&si3->lai, bts->network->country_code,
+ bts->network->network_code,
+ bts->location_area_code);
+ si3->control_channel_desc = bts->si_common.chan_desc;
+ si3->cell_options = bts->si_common.cell_options;
+ si3->cell_sel_par = bts->si_common.cell_sel_par;
+ si3->rach_control = bts->si_common.rach_control;
+
+ /* SI3 Rest Octets (10.5.2.34), containing
+ CBQ, CELL_RESELECT_OFFSET, TEMPORARY_OFFSET, PENALTY_TIME
+ Power Offset, 2ter Indicator, Early Classmark Sending,
+ Scheduling if and WHERE, GPRS Indicator, SI13 position */
+ rc = rest_octets_si3(si3->rest_octets, &si_info);
+
+ return sizeof(*si3) + rc;
+}
+
+static int generate_si4(u_int8_t *output, struct gsm_bts *bts)
+{
+ int rc;
+ struct gsm48_system_information_type_4 *si4 =
+ (struct gsm48_system_information_type_4 *) output;
+
+ /* length of all IEs present except SI4 rest octets and l2_plen */
+ int l2_plen = sizeof(*si4) - 1;
+
+ memset(si4, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN);
+
+ si4->header.rr_protocol_discriminator = GSM48_PDISC_RR;
+ si4->header.skip_indicator = 0;
+ si4->header.system_information = GSM48_MT_RR_SYSINFO_4;
+
+ gsm0408_generate_lai(&si4->lai, bts->network->country_code,
+ bts->network->network_code,
+ bts->location_area_code);
+ si4->cell_sel_par = bts->si_common.cell_sel_par;
+ si4->rach_control = bts->si_common.rach_control;
+
+ /* Optional: CBCH Channel Description + CBCH Mobile Allocation */
+
+ si4->header.l2_plen = (l2_plen << 2) | 1;
+
+ /* SI4 Rest Octets (10.5.2.35), containing
+ Optional Power offset, GPRS Indicator,
+ Cell Identity, LSA ID, Selection Parameter */
+ rc = rest_octets_si4(si4->data, &si_info);
+
+ return sizeof(*si4) + rc;
+}
+
+static int generate_si5(u_int8_t *output, struct gsm_bts *bts)
+{
+ struct gsm48_system_information_type_5 *si5;
+ int rc, l2_plen = 18;
+
+ memset(output, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN);
+
+ /* ip.access nanoBTS needs l2_plen!! */
+ if (is_ipaccess_bts(bts)) {
+ *output++ = (l2_plen << 2) | 1;
+ l2_plen++;
+ }
+
+ si5 = (struct gsm48_system_information_type_5 *) output;
+
+ /* l2 pseudo length, not part of msg: 18 */
+ si5->rr_protocol_discriminator = GSM48_PDISC_RR;
+ si5->skip_indicator = 0;
+ si5->system_information = GSM48_MT_RR_SYSINFO_5;
+ rc = generate_bcch_chan_list(si5->bcch_frequency_list, bts);
+ if (rc < 0)
+ return rc;
+
+ /* 04.08 9.1.37: L2 Pseudo Length of 18 */
+ return l2_plen;
+}
+
+static int generate_si6(u_int8_t *output, struct gsm_bts *bts)
+{
+ struct gsm48_system_information_type_6 *si6;
+ int l2_plen = 11;
+
+ memset(output, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN);
+
+ /* ip.access nanoBTS needs l2_plen!! */
+ if (is_ipaccess_bts(bts)) {
+ *output++ = (l2_plen << 2) | 1;
+ l2_plen++;
+ }
+
+ si6 = (struct gsm48_system_information_type_6 *) output;
+
+ /* l2 pseudo length, not part of msg: 11 */
+ si6->rr_protocol_discriminator = GSM48_PDISC_RR;
+ si6->skip_indicator = 0;
+ si6->system_information = GSM48_MT_RR_SYSINFO_6;
+ si6->cell_identity = htons(bts->cell_identity);
+ gsm0408_generate_lai(&si6->lai, bts->network->country_code,
+ bts->network->network_code,
+ bts->location_area_code);
+ si6->cell_options = bts->si_common.cell_options;
+ si6->ncc_permitted = bts->si_common.ncc_permitted;
+
+ /* SI6 Rest Octets: 10.5.2.35a: PCH / NCH info, VBS/VGCS options */
+
+ return l2_plen;
+}
+
+static struct gsm48_si13_info si13_default = {
+ .cell_opts = {
+ .nmo = GPRS_NMO_III,
+ .t3168 = 1000,
+ .t3192 = 1000,
+ .drx_timer_max = 1,
+ .bs_cv_max = 15,
+ },
+ .pwr_ctrl_pars = {
+ .alpha = 10, /* a = 1.0 */
+ .t_avg_w = 25,
+ .t_avg_t = 25,
+ .pc_meas_chan = 0, /* downling measured on CCCH */
+ .n_avg_i = 15,
+ },
+ .bcch_change_mark = 0,
+ .si_change_field = 0,
+ .pbcch_present = 0,
+ {
+ .no_pbcch = {
+ .rac = 0,
+ .spgc_ccch_sup = 0,
+ .net_ctrl_ord = 0,
+ .prio_acc_thr = 0,
+ },
+ },
+};
+
+static int generate_si13(u_int8_t *output, struct gsm_bts *bts)
+{
+ struct gsm48_system_information_type_13 *si13 =
+ (struct gsm48_system_information_type_13 *) output;
+ int ret;
+
+ memset(si13, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN);
+
+ si13->header.rr_protocol_discriminator = GSM48_PDISC_RR;
+ si13->header.skip_indicator = 0;
+ si13->header.system_information = GSM48_MT_RR_SYSINFO_13;
+
+ ret = rest_octets_si13(si13->rest_octets, &si13_default);
+ if (ret < 0)
+ return ret;
+
+ si13->header.l2_plen = ret & 0xff;
+
+ return sizeof (*si13) + ret;
+}
+
+int gsm_generate_si(u_int8_t *output, struct gsm_bts *bts, int type)
+{
+ switch (type) {
+ case RSL_SYSTEM_INFO_1:
+ return generate_si1(output, bts);
+ case RSL_SYSTEM_INFO_2:
+ return generate_si2(output, bts);
+ case RSL_SYSTEM_INFO_3:
+ return generate_si3(output, bts);
+ case RSL_SYSTEM_INFO_4:
+ return generate_si4(output, bts);
+ case RSL_SYSTEM_INFO_5:
+ return generate_si5(output, bts);
+ case RSL_SYSTEM_INFO_6:
+ return generate_si6(output, bts);
+ case RSL_SYSTEM_INFO_13:
+ return generate_si13(output, bts);
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
diff --git a/openbsc/src/talloc.c b/openbsc/src/talloc.c
index bd5e1b0e0..d8213238e 100644
--- a/openbsc/src/talloc.c
+++ b/openbsc/src/talloc.c
@@ -105,6 +105,15 @@
#endif
#endif
+#ifdef __APPLE__
+/* taken from http://insanecoding.blogspot.com/2007/03/methods-for-safe-string-handling.html */
+size_t strnlen(const char *s, size_t n)
+{
+ const char *p = (const char *)memchr(s, 0, n);
+ return(p ? p-s : n);
+}
+#endif
+
/* this null_context is only used if talloc_enable_leak_report() or
talloc_enable_leak_report_full() is called, otherwise it remains
NULL
diff --git a/openbsc/src/talloc_ctx.c b/openbsc/src/talloc_ctx.c
index aa9f0b4d7..5f0ee4de8 100644
--- a/openbsc/src/talloc_ctx.c
+++ b/openbsc/src/talloc_ctx.c
@@ -1,6 +1,7 @@
#include <openbsc/talloc.h>
#include <openbsc/gsm_data.h>
+extern void *tall_msgb_ctx;
extern void *tall_fle_ctx;
extern void *tall_locop_ctx;
extern void *tall_gsms_ctx;
@@ -13,9 +14,11 @@ extern void *tall_tqe_ctx;
extern void *tall_trans_ctx;
extern void *tall_map_ctx;
extern void *tall_upq_ctx;
+extern void *tall_ctr_ctx;
void talloc_ctx_init(void)
{
+ tall_msgb_ctx = talloc_named_const(tall_bsc_ctx, 0, "msgb");
tall_fle_ctx = talloc_named_const(tall_bsc_ctx, 0,
"bs11_file_list_entry");
tall_locop_ctx = talloc_named_const(tall_bsc_ctx, 0, "loc_updating_oper");
@@ -29,4 +32,5 @@ void talloc_ctx_init(void)
tall_trans_ctx = talloc_named_const(tall_bsc_ctx, 0, "transaction");
tall_map_ctx = talloc_named_const(tall_bsc_ctx, 0, "trau_map_entry");
tall_upq_ctx = talloc_named_const(tall_bsc_ctx, 0, "trau_upq_entry");
+ tall_ctr_ctx = talloc_named_const(tall_bsc_ctx, 0, "counter");
}
diff --git a/openbsc/src/telnet_interface.c b/openbsc/src/telnet_interface.c
index 128c34e94..6f452a55b 100644
--- a/openbsc/src/telnet_interface.c
+++ b/openbsc/src/telnet_interface.c
@@ -35,6 +35,7 @@
#include <openbsc/paging.h>
#include <openbsc/signal.h>
#include <openbsc/talloc.h>
+#include <openbsc/debug.h>
#include <vty/buffer.h>
@@ -71,7 +72,7 @@ void telnet_init(struct gsm_network *network, int port) {
fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
if (fd < 0) {
- perror("Telnet interface socket creation failed");
+ LOGP(DNM, LOGL_ERROR, "Telnet interface socket creation failed\n");
return;
}
@@ -83,12 +84,12 @@ void telnet_init(struct gsm_network *network, int port) {
sock_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
if (bind(fd, (struct sockaddr*)&sock_addr, sizeof(sock_addr)) < 0) {
- perror("Telnet interface failed to bind");
+ LOGP(DNM, LOGL_ERROR, "Telnet interface failed to bind\n");
return;
}
if (listen(fd, 0) < 0) {
- perror("Telnet interface failed to listen");
+ LOGP(DNM, LOGL_ERROR, "Telnet interface failed to listen\n");
return;
}
@@ -101,9 +102,9 @@ static void print_welcome(int fd) {
int ret;
static char *msg =
"Welcome to the OpenBSC Control interface\n"
- "Copyright (C) 2008, 2009 Harald Welte\n"
+ "Copyright (C) 2008-2010 Harald Welte\n"
"Contributions by Daniel Willmann, Jan Lübbe, "
- "Stefan Schmidt, Holger Freyther\n\n"
+ "Stefan Schmidt, Holger Freyther, Andreas Eversberg\n\n"
"License GPLv2+: GNU GPL version 2 or later "
"<http://gnu.org/licenses/gpl.html>\n"
"This is free software: you are free to change "
@@ -119,6 +120,12 @@ int telnet_close_client(struct bsc_fd *fd) {
close(fd->fd);
bsc_unregister_fd(fd);
+
+ if (conn->dbg) {
+ debug_del_target(conn->dbg);
+ talloc_free(conn->dbg);
+ }
+
llist_del(&conn->entry);
talloc_free(conn);
return 0;
@@ -154,7 +161,7 @@ static int telnet_new_connection(struct bsc_fd *fd, unsigned int what) {
int new_connection = accept(fd->fd, (struct sockaddr*)&sockaddr, &len);
if (new_connection < 0) {
- perror("telnet accept failed");
+ LOGP(DNM, LOGL_ERROR, "telnet accept failed\n");
return -1;
}
@@ -171,8 +178,10 @@ static int telnet_new_connection(struct bsc_fd *fd, unsigned int what) {
print_welcome(new_connection);
connection->vty = vty_create(new_connection, connection);
- if (!connection->vty)
+ if (!connection->vty) {
+ LOGP(DNM, LOGL_ERROR, "couldn't create VTY\n");
return -1;
+ }
return 0;
}
diff --git a/openbsc/src/tlv_parser.c b/openbsc/src/tlv_parser.c
index fd0045f97..13ca7b14b 100644
--- a/openbsc/src/tlv_parser.c
+++ b/openbsc/src/tlv_parser.c
@@ -149,6 +149,19 @@ int tlv_parse(struct tlv_parsed *dec, const struct tlv_definition *def,
return num_parsed;
}
+/* take a master (src) tlvdev and fill up all empty slots in 'dst' */
+void tlv_def_patch(struct tlv_definition *dst, const struct tlv_definition *src)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(dst->def); i++) {
+ if (src->def[i].type == TLV_TYPE_NONE)
+ continue;
+ if (dst->def[i].type == TLV_TYPE_NONE)
+ dst->def[i] = src->def[i];
+ }
+}
+
static __attribute__((constructor)) void on_dso_load_tlv(void)
{
int i;
diff --git a/openbsc/src/token_auth.c b/openbsc/src/token_auth.c
index 0931007ef..fd81f035a 100644
--- a/openbsc/src/token_auth.c
+++ b/openbsc/src/token_auth.c
@@ -60,10 +60,10 @@ static int token_subscr_cb(unsigned int subsys, unsigned int signal,
struct gsm_sms *sms;
int rc = 0;
- if (subscr->net->auth_policy != GSM_AUTH_POLICY_TOKEN)
+ if (signal != S_SUBSCR_ATTACHED)
return 0;
- if (signal != S_SUBSCR_ATTACHED)
+ if (subscr->net->auth_policy != GSM_AUTH_POLICY_TOKEN)
return 0;
if (subscr->flags & GSM_SUBSCRIBER_FIRST_CONTACT) {
@@ -103,7 +103,7 @@ unauth:
if (lchan) {
u_int8_t auth_rand[16];
/* kick the subscriber off the network */
- gsm48_tx_mm_auth_req(lchan, auth_rand);
+ gsm48_tx_mm_auth_req(lchan, auth_rand, 0);
gsm48_tx_mm_auth_rej(lchan);
/* FIXME: close the channel early ?*/
//gsm48_send_rr_Release(lchan);
@@ -139,7 +139,7 @@ static int token_sms_cb(unsigned int subsys, unsigned int signal,
lchan = lchan_for_subscr(sms->receiver);
if (lchan) {
/* kick the subscriber off the network */
- gsm48_tx_mm_auth_req(lchan, auth_rand);
+ gsm48_tx_mm_auth_req(lchan, auth_rand, 0);
gsm48_tx_mm_auth_rej(lchan);
/* FIXME: close the channel early ?*/
//gsm48_send_rr_Release(lchan);
diff --git a/openbsc/src/transaction.c b/openbsc/src/transaction.c
index 04eaa3c99..c972037be 100644
--- a/openbsc/src/transaction.c
+++ b/openbsc/src/transaction.c
@@ -119,7 +119,7 @@ int trans_assign_trans_id(struct gsm_subscriber *subscr,
struct gsm_network *net = subscr->net;
struct gsm_trans *trans;
unsigned int used_tid_bitmask = 0;
- int i;
+ int i, j, h;
if (ti_flag)
ti_flag = 0x8;
@@ -133,10 +133,39 @@ int trans_assign_trans_id(struct gsm_subscriber *subscr,
used_tid_bitmask |= (1 << trans->transaction_id);
}
- for (i = 0; i <= 7; i++) {
- if ((used_tid_bitmask & (1 << (i | ti_flag))) == 0)
- return i | ti_flag;
+ /* find a new one, trying to go in a 'circular' pattern */
+ for (h = 6; h > 0; h--)
+ if (used_tid_bitmask & (1 << (h | ti_flag)))
+ break;
+ for (i = 0; i < 7; i++) {
+ j = ((h + i) % 7) | ti_flag;
+ if ((used_tid_bitmask & (1 << j)) == 0)
+ return j;
}
return -1;
}
+
+/* update all transactions to use a different LCHAN, e.g.
+ * after handover has succeeded */
+int trans_lchan_change(struct gsm_lchan *lchan_old,
+ struct gsm_lchan *lchan_new)
+{
+ struct gsm_network *net = lchan_old->ts->trx->bts->network;
+ struct gsm_trans *trans;
+ int num = 0;
+
+ llist_for_each_entry(trans, &net->trans_list, entry) {
+ if (trans->lchan == lchan_old) {
+ /* drop old channel use cound */
+ put_lchan(trans->lchan);
+ /* assign new channel */
+ trans->lchan = lchan_new;
+ /* bump new channel use count */
+ use_lchan(trans->lchan);
+ num++;
+ }
+ }
+
+ return num;
+}
diff --git a/openbsc/src/trau_frame.c b/openbsc/src/trau_frame.c
index aa039574b..2bc61a513 100644
--- a/openbsc/src/trau_frame.c
+++ b/openbsc/src/trau_frame.c
@@ -107,11 +107,13 @@ int decode_trau_frame(struct decoded_trau_frame *fr, const u_int8_t *trau_bits)
case TRAU_FT_DATA_DOWN:
case TRAU_FT_D145_SYNC:
case TRAU_FT_EDATA:
- DEBUGP(DMUX, "can't decode unimplemented TRAU Frame Type 0x%02x\n", cbits5);
+ LOGP(DMUX, LOGL_NOTICE, "can't decode unimplemented TRAU "
+ "Frame Type 0x%02x\n", cbits5);
return -1;
break;
default:
- DEBUGP(DMUX, "can't decode unknown TRAU Frame Type 0x%02x\n", cbits5);
+ LOGP(DMUX, LOGL_NOTICE, "can't decode unknown TRAU "
+ "Frame Type 0x%02x\n", cbits5);
return -1;
break;
}
@@ -162,11 +164,13 @@ int trau_frame_up2down(struct decoded_trau_frame *fr)
case TRAU_FT_DATA_UP:
case TRAU_FT_D145_SYNC:
case TRAU_FT_EDATA:
- DEBUGP(DMUX, "unimplemented TRAU Frame Type 0x%02x\n", cbits5);
+ LOGP(DMUX, LOGL_NOTICE, "unimplemented TRAU Frame Type "
+ "0x%02x\n", cbits5);
return -1;
break;
default:
- DEBUGP(DMUX, "unknown TRAU Frame Type 0x%02x\n", cbits5);
+ LOGP(DMUX, LOGL_NOTICE, "unknown TRAU Frame Type "
+ "0x%02x\n", cbits5);
return -1;
break;
}
@@ -224,11 +228,13 @@ int encode_trau_frame(u_int8_t *trau_bits, const struct decoded_trau_frame *fr)
case TRAU_FT_DATA_DOWN:
case TRAU_FT_D145_SYNC:
case TRAU_FT_EDATA:
- DEBUGP(DMUX, "unimplemented TRAU Frame Type 0x%02x\n", cbits5);
+ LOGP(DMUX, LOGL_NOTICE, "unimplemented TRAU Frame Type "
+ "0x%02x\n", cbits5);
return -1;
break;
default:
- DEBUGP(DMUX, "unknown TRAU Frame Type 0x%02x\n", cbits5);
+ LOGP(DMUX, LOGL_NOTICE, "unknown TRAU Frame Type "
+ "0x%02x\n", cbits5);
return -1;
break;
}
diff --git a/openbsc/src/trau_mux.c b/openbsc/src/trau_mux.c
index 6a19f0c99..9930751a5 100644
--- a/openbsc/src/trau_mux.c
+++ b/openbsc/src/trau_mux.c
@@ -32,6 +32,19 @@
#include <openbsc/debug.h>
#include <openbsc/talloc.h>
+u_int8_t gsm_fr_map[] = {
+ 6, 6, 5, 5, 4, 4, 3, 3,
+ 7, 2, 2, 6, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 7, 2, 2, 6, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 7, 2, 2, 6, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 7, 2, 2, 6, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3
+};
+
struct map_entry {
struct llist_head list;
struct gsm_e1_subslot src, dst;
@@ -56,8 +69,10 @@ int trau_mux_map(const struct gsm_e1_subslot *src,
struct map_entry *me;
me = talloc(tall_map_ctx, struct map_entry);
- if (!me)
+ if (!me) {
+ LOGP(DMIB, LOGL_FATAL, "Out of memory\n");
return -ENOMEM;
+ }
DEBUGP(DCC, "Setting up TRAU mux map between (e1=%u,ts=%u,ss=%u) "
"and (e1=%u,ts=%u,ss=%u)\n",
@@ -142,6 +157,8 @@ lookup_trau_upqueue(const struct gsm_e1_subslot *src)
return NULL;
}
+static const u_int8_t c_bits_check[] = { 0, 0, 0, 1, 0 };
+
/* we get called by subchan_demux */
int trau_mux_input(struct gsm_e1_subslot *src_e1_ss,
const u_int8_t *trau_bits, int num_bits)
@@ -151,8 +168,6 @@ int trau_mux_input(struct gsm_e1_subslot *src_e1_ss,
struct gsm_e1_subslot *dst_e1_ss = lookup_trau_mux_map(src_e1_ss);
struct subch_mux *mx;
struct upqueue_entry *ue;
- struct msgb *msg;
- struct gsm_trau_frame *frame;
int rc;
/* decode TRAU, change it to downlink, re-encode */
@@ -161,19 +176,44 @@ int trau_mux_input(struct gsm_e1_subslot *src_e1_ss,
return rc;
if (!dst_e1_ss) {
+ struct msgb *msg;
+ struct gsm_data_frame *frame;
+ unsigned char *data;
+ int i, j, k, l, o;
/* frame shall be sent to upqueue */
if (!(ue = lookup_trau_upqueue(src_e1_ss)))
return -EINVAL;
if (!ue->callref)
return -EINVAL;
- msg = msgb_alloc(sizeof(struct gsm_trau_frame) + sizeof(tf),
- "TRAU");
+ if (memcmp(tf.c_bits, c_bits_check, sizeof(c_bits_check)))
+ DEBUGPC(DMUX, "illegal trau (C1-C5) %s\n",
+ hexdump(tf.c_bits, sizeof(c_bits_check)));
+ msg = msgb_alloc(sizeof(struct gsm_data_frame) + 33,
+ "GSM-DATA");
if (!msg)
return -ENOMEM;
- frame = (struct gsm_trau_frame *)msg->data;
- frame->msg_type = GSM_TRAU_FRAME;
+
+ frame = (struct gsm_data_frame *)msg->data;
+ memset(frame, 0, sizeof(struct gsm_data_frame));
+ data = frame->data;
+ data[0] = 0xd << 4;
+ /* reassemble d-bits */
+ i = 0; /* counts bits */
+ j = 4; /* counts output bits */
+ k = gsm_fr_map[0]-1; /* current number bit in element */
+ l = 0; /* counts element bits */
+ o = 0; /* offset input bits */
+ while (i < 260) {
+ data[j/8] |= (tf.d_bits[k+o] << (7-(j%8)));
+ if (--k < 0) {
+ o += gsm_fr_map[l];
+ k = gsm_fr_map[++l]-1;
+ }
+ i++;
+ j++;
+ }
+ frame->msg_type = GSM_TCHF_FRAME;
frame->callref = ue->callref;
- memcpy(frame->data, &tf, sizeof(tf));
msgb_enqueue(&ue->net->upqueue, msg);
return 0;
@@ -219,17 +259,53 @@ int trau_recv_lchan(struct gsm_lchan *lchan, u_int32_t callref)
return 0;
}
-int trau_send_lchan(struct gsm_lchan *lchan, struct decoded_trau_frame *tf)
+int trau_send_frame(struct gsm_lchan *lchan, struct gsm_data_frame *frame)
{
u_int8_t trau_bits_out[TRAU_FRAME_BITS];
struct gsm_e1_subslot *dst_e1_ss = &lchan->ts->e1_link;
struct subch_mux *mx;
+ int i, j, k, l, o;
+ unsigned char *data = frame->data;
+ struct decoded_trau_frame tf;
mx = e1inp_get_mux(dst_e1_ss->e1_nr, dst_e1_ss->e1_ts);
if (!mx)
return -EINVAL;
- encode_trau_frame(trau_bits_out, tf);
+ switch (frame->msg_type) {
+ case GSM_TCHF_FRAME:
+ /* set c-bits and t-bits */
+ tf.c_bits[0] = 1;
+ tf.c_bits[1] = 1;
+ tf.c_bits[2] = 1;
+ tf.c_bits[3] = 0;
+ tf.c_bits[4] = 0;
+ memset(&tf.c_bits[5], 0, 6);
+ memset(&tf.c_bits[11], 1, 10);
+ memset(&tf.t_bits[0], 1, 4);
+ /* reassemble d-bits */
+ i = 0; /* counts bits */
+ j = 4; /* counts input bits */
+ k = gsm_fr_map[0]-1; /* current number bit in element */
+ l = 0; /* counts element bits */
+ o = 0; /* offset output bits */
+ while (i < 260) {
+ tf.d_bits[k+o] = (data[j/8] >> (7-(j%8))) & 1;
+ if (--k < 0) {
+ o += gsm_fr_map[l];
+ k = gsm_fr_map[++l]-1;
+ }
+ i++;
+ j++;
+ }
+ break;
+ default:
+ DEBUGPC(DMUX, "unsupported message type %d\n",
+ frame->msg_type);
+ return -EINVAL;
+ }
+
+ encode_trau_frame(trau_bits_out, &tf);
/* and send it to the muxer */
return subchan_mux_enqueue(mx, dst_e1_ss->e1_ts_ss, trau_bits_out,
diff --git a/openbsc/src/vty/command.c b/openbsc/src/vty/command.c
index 6372fb13b..5b1dcb977 100644
--- a/openbsc/src/vty/command.c
+++ b/openbsc/src/vty/command.c
@@ -49,6 +49,8 @@ Boston, MA 02111-1307, USA. */
#include <openbsc/gsm_subscriber.h>
#include <openbsc/talloc.h>
+void *tall_vty_cmd_ctx;
+
/* Command vector which includes some level of command lists. Normally
each daemon maintains each own cmdvec. */
vector cmdvec;
@@ -173,7 +175,7 @@ char *argv_concat(const char **argv, int argc, int shift)
len += strlen(argv[i]) + 1;
if (!len)
return NULL;
- p = str = _talloc_zero(tall_vty_ctx, len, "arvg_concat");
+ p = str = _talloc_zero(tall_vty_cmd_ctx, len, "arvg_concat");
for (i = shift; i < argc; i++) {
size_t arglen;
memcpy(p, argv[i], (arglen = strlen(argv[i])));
@@ -275,7 +277,7 @@ vector cmd_make_strvec(const char *string)
*cp != '\0')
cp++;
strlen = cp - start;
- token = _talloc_zero(tall_vty_ctx, strlen + 1, "make_strvec");
+ token = _talloc_zero(tall_vty_cmd_ctx, strlen + 1, "make_strvec");
memcpy(token, start, strlen);
*(token + strlen) = '\0';
vector_set(strvec, token);
@@ -331,7 +333,7 @@ static char *cmd_desc_str(const char **string)
cp++;
strlen = cp - start;
- token = _talloc_zero(tall_vty_ctx, strlen + 1, "cmd_desc_str");
+ token = _talloc_zero(tall_vty_cmd_ctx, strlen + 1, "cmd_desc_str");
memcpy(token, start, strlen);
*(token + strlen) = '\0';
@@ -402,11 +404,11 @@ static vector cmd_make_descvec(const char *string, const char *descstr)
len = cp - sp;
- token = _talloc_zero(tall_vty_ctx, len + 1, "cmd_make_descvec");
+ token = _talloc_zero(tall_vty_cmd_ctx, len + 1, "cmd_make_descvec");
memcpy(token, sp, len);
*(token + len) = '\0';
- desc = talloc_zero(tall_vty_ctx, struct desc);
+ desc = talloc_zero(tall_vty_cmd_ctx, struct desc);
desc->cmd = token;
desc->str = cmd_desc_str(&dp);
@@ -1804,7 +1806,7 @@ static char **cmd_complete_command_real(vector vline, struct vty *vty,
if ((desc = vector_slot(descvec, j))) {
if ((string = cmd_entry_function(vector_slot(vline, index), desc->cmd)))
if (cmd_unique_string (matchvec, string))
- vector_set (matchvec, talloc_strdup(tall_vty_ctx, string));
+ vector_set (matchvec, talloc_strdup(tall_vty_cmd_ctx, string));
}
}
}
@@ -1845,7 +1847,7 @@ static char **cmd_complete_command_real(vector vline, struct vty *vty,
if (len < lcd) {
char *lcdstr;
- lcdstr = _talloc_zero(tall_vty_ctx, lcd + 1,
+ lcdstr = _talloc_zero(tall_vty_cmd_ctx, lcd + 1,
"complete-lcdstr");
memcpy(lcdstr, matchvec->index[0], lcd);
lcdstr[lcd] = '\0';
@@ -2463,13 +2465,13 @@ DEFUN(config_write_file,
config_file = host.config;
config_file_sav =
- _talloc_zero(tall_vty_ctx,
+ _talloc_zero(tall_vty_cmd_ctx,
strlen(config_file) + strlen(CONF_BACKUP_EXT) + 1,
"config_file_sav");
strcpy(config_file_sav, config_file);
strcat(config_file_sav, CONF_BACKUP_EXT);
- config_file_tmp = _talloc_zero(tall_vty_ctx, strlen(config_file) + 8,
+ config_file_tmp = _talloc_zero(tall_vty_cmd_ctx, strlen(config_file) + 8,
"config_file_tmp");
sprintf(config_file_tmp, "%s.XXXXXX", config_file);
@@ -2650,7 +2652,7 @@ DEFUN(config_hostname,
if (host.name)
talloc_free(host.name);
- host.name = talloc_strdup(tall_vty_ctx, argv[0]);
+ host.name = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
return CMD_SUCCESS;
}
@@ -2685,7 +2687,7 @@ DEFUN(config_password, password_cmd,
host.password = NULL;
if (host.password_encrypt)
talloc_free(host.password_encrypt);
- host.password_encrypt = talloc_strdup(tall_vty_ctx, argv[1]);
+ host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, argv[1]);
return CMD_SUCCESS;
} else {
vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE);
@@ -2708,10 +2710,10 @@ DEFUN(config_password, password_cmd,
if (host.encrypt) {
if (host.password_encrypt)
talloc_free(host.password_encrypt);
- host.password_encrypt = talloc_strdup(tall_vty_ctx, zencrypt(argv[0]));
+ host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0]));
} else
#endif
- host.password = talloc_strdup(tall_vty_ctx, argv[0]);
+ host.password = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
return CMD_SUCCESS;
}
@@ -2744,7 +2746,7 @@ ALIAS(config_password, password_text_cmd,
if (host.enable_encrypt)
talloc_free(host.enable_encrypt);
- host.enable_encrypt = talloc_strdup(tall_vty_ctx, argv[1]);
+ host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, argv[1]);
return CMD_SUCCESS;
} else {
@@ -2769,10 +2771,10 @@ ALIAS(config_password, password_text_cmd,
if (host.encrypt) {
if (host.enable_encrypt)
talloc_free(host.enable_encrypt);
- host.enable_encrypt = talloc_strdup(tall_vty_ctx, zencrypt(argv[0]));
+ host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0]));
} else
#endif
- host.enable = talloc_strdup(tall_vty_ctx, argv[0]);
+ host.enable = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
return CMD_SUCCESS;
}
@@ -2816,12 +2818,12 @@ DEFUN(service_password_encrypt,
if (host.password) {
if (host.password_encrypt)
talloc_free(host.password_encrypt);
- host.password_encrypt = talloc_strdup(tall_vty_ctx, zencrypt(host.password));
+ host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(host.password));
}
if (host.enable) {
if (host.enable_encrypt)
talloc_free(host.enable_encrypt);
- host.enable_encrypt = talloc_strdup(tall_vty_ctx, zencrypt(host.enable));
+ host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(host.enable));
}
return CMD_SUCCESS;
@@ -3095,7 +3097,7 @@ static int set_log_file(struct vty *vty, const char *fname, int loglevel)
if (host.logfile)
talloc_free(host.logfile);
- host.logfile = talloc_strdup(tall_vty_ctx, fname);
+ host.logfile = talloc_strdup(tall_vty_cmd_ctx, fname);
return CMD_SUCCESS;
}
@@ -3285,7 +3287,7 @@ DEFUN(banner_motd_file,
{
if (host.motdfile)
talloc_free(host.motdfile);
- host.motdfile = talloc_strdup(tall_vty_ctx, argv[0]);
+ host.motdfile = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
return CMD_SUCCESS;
}
@@ -3313,7 +3315,7 @@ DEFUN(no_banner_motd,
/* Set config filename. Called from vty.c */
void host_config_set(const char *filename)
{
- host.config = talloc_strdup(tall_vty_ctx, filename);
+ host.config = talloc_strdup(tall_vty_cmd_ctx, filename);
}
void install_default(enum node_type node)
diff --git a/openbsc/src/vty/vector.c b/openbsc/src/vty/vector.c
index 371f71d95..06e1aaa8e 100644
--- a/openbsc/src/vty/vector.c
+++ b/openbsc/src/vty/vector.c
@@ -27,10 +27,12 @@
#include <openbsc/talloc.h>
#include <memory.h>
+void *tall_vty_vec_ctx;
+
/* Initialize vector : allocate memory and return vector. */
vector vector_init(unsigned int size)
{
- vector v = talloc_zero(tall_vty_ctx, struct _vector);
+ vector v = talloc_zero(tall_vty_vec_ctx, struct _vector);
if (!v)
return NULL;
@@ -40,7 +42,7 @@ vector vector_init(unsigned int size)
v->alloced = size;
v->active = 0;
- v->index = _talloc_zero(tall_vty_ctx, sizeof(void *) * size,
+ v->index = _talloc_zero(tall_vty_vec_ctx, sizeof(void *) * size,
"vector_init:index");
if (!v->index) {
talloc_free(v);
@@ -68,7 +70,7 @@ void vector_free(vector v)
vector vector_copy(vector v)
{
unsigned int size;
- vector new = talloc_zero(tall_vty_ctx, struct _vector);
+ vector new = talloc_zero(tall_vty_vec_ctx, struct _vector);
if (!new)
return NULL;
@@ -76,7 +78,7 @@ vector vector_copy(vector v)
new->alloced = v->alloced;
size = sizeof(void *) * (v->alloced);
- new->index = _talloc_zero(tall_vty_ctx, size, "vector_copy:index");
+ new->index = _talloc_zero(tall_vty_vec_ctx, size, "vector_copy:index");
if (!new->index) {
talloc_free(new);
return NULL;
@@ -92,7 +94,7 @@ void vector_ensure(vector v, unsigned int num)
if (v->alloced > num)
return;
- v->index = talloc_realloc_size(tall_vty_ctx, v->index,
+ v->index = talloc_realloc_size(tall_vty_vec_ctx, v->index,
sizeof(void *) * (v->alloced * 2));
memset(&v->index[v->alloced], 0, sizeof(void *) * v->alloced);
v->alloced *= 2;
diff --git a/openbsc/src/vty/vty.c b/openbsc/src/vty/vty.c
index affe28d8d..2339bbd4b 100644
--- a/openbsc/src/vty/vty.c
+++ b/openbsc/src/vty/vty.c
@@ -236,6 +236,8 @@ int vty_out(struct vty *vty, const char *format, ...)
talloc_free(p);
}
+ vty_event(VTY_WRITE, vty->fd, vty);
+
return len;
}
@@ -1631,6 +1633,8 @@ extern void *tall_bsc_ctx;
void vty_init()
{
tall_vty_ctx = talloc_named_const(NULL, 0, "vty");
+ tall_vty_vec_ctx = talloc_named_const(tall_vty_ctx, 0, "vty_vector");
+ tall_vty_cmd_ctx = talloc_named_const(tall_vty_ctx, 0, "vty_command");
/* For further configuration read, preserve current directory. */
vty_save_cwd();
diff --git a/openbsc/src/vty_interface.c b/openbsc/src/vty_interface.c
index 5712ca1c7..76598d377 100644
--- a/openbsc/src/vty_interface.c
+++ b/openbsc/src/vty_interface.c
@@ -33,8 +33,11 @@
#include <openbsc/e1_input.h>
#include <openbsc/abis_nm.h>
#include <openbsc/gsm_utils.h>
+#include <openbsc/chan_alloc.h>
+#include <openbsc/meas_rep.h>
#include <openbsc/db.h>
#include <openbsc/talloc.h>
+#include <openbsc/telnet_interface.h>
static struct gsm_network *gsmnet;
@@ -74,8 +77,30 @@ static void net_dump_nmstate(struct vty *vty, struct gsm_nm_state *nms)
nm_avail_name(nms->availability), VTY_NEWLINE);
}
+static void dump_pchan_load_vty(struct vty *vty, char *prefix,
+ const struct pchan_load *pl)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(pl->pchan); i++) {
+ const struct load_counter *lc = &pl->pchan[i];
+ unsigned int percent;
+
+ if (lc->total == 0)
+ continue;
+
+ percent = (lc->used * 100) / lc->total;
+
+ vty_out(vty, "%s%20s: %3u%% (%u/%u)%s", prefix,
+ gsm_pchan_name(i), percent, lc->used, lc->total,
+ VTY_NEWLINE);
+ }
+}
+
static void net_dump_vty(struct vty *vty, struct gsm_network *net)
{
+ struct pchan_load pl;
+
vty_out(vty, "BSC is on Country Code %u, Network Code %u "
"and has %u BTS%s", net->country_code, net->network_code,
net->num_bts, VTY_NEWLINE);
@@ -85,10 +110,21 @@ static void net_dump_vty(struct vty *vty, struct gsm_network *net)
net->name_short, VTY_NEWLINE);
vty_out(vty, " Authentication policy: %s%s",
gsm_auth_policy_name(net->auth_policy), VTY_NEWLINE);
+ vty_out(vty, " Location updating reject cause: %u%s",
+ net->reject_cause, VTY_NEWLINE);
vty_out(vty, " Encryption: A5/%u%s", net->a5_encryption,
VTY_NEWLINE);
vty_out(vty, " NECI (TCH/H): %u%s", net->neci,
VTY_NEWLINE);
+ vty_out(vty, " RRLP Mode: %s%s", rrlp_mode_name(net->rrlp.mode),
+ VTY_NEWLINE);
+ vty_out(vty, " MM Info: %s%s", net->send_mm_info ? "On" : "Off",
+ VTY_NEWLINE);
+ vty_out(vty, " Handover: %s%s", net->handover.active ? "On" : "Off",
+ VTY_NEWLINE);
+ network_chan_load(&pl, net);
+ vty_out(vty, " Current Channel Load:%s", VTY_NEWLINE);
+ dump_pchan_load_vty(vty, " ", &pl);
}
DEFUN(show_net, show_net_cmd, "show network",
@@ -120,13 +156,26 @@ static void e1isl_dump_vty(struct vty *vty, struct e1inp_sign_link *e1l)
static void bts_dump_vty(struct vty *vty, struct gsm_bts *bts)
{
+ struct pchan_load pl;
+
vty_out(vty, "BTS %u is of %s type in band %s, has CI %u LAC %u, "
"BSIC %u, TSC %u and %u TRX%s",
bts->nr, btstype2str(bts->type), gsm_band_name(bts->band),
bts->cell_identity,
bts->location_area_code, bts->bsic, bts->tsc,
bts->num_trx, VTY_NEWLINE);
- if (bts->cell_barred)
+ vty_out(vty, "MS Max power: %u dBm%s", bts->ms_max_power, VTY_NEWLINE);
+ vty_out(vty, "Minimum Rx Level for Access: %i dBm%s",
+ rxlev2dbm(bts->si_common.cell_sel_par.rxlev_acc_min),
+ VTY_NEWLINE);
+ vty_out(vty, "Cell Reselection Hysteresis: %u dBm%s",
+ bts->si_common.cell_sel_par.cell_resel_hyst*2, VTY_NEWLINE);
+ vty_out(vty, "RACH TX-Integer: %u%s", bts->si_common.rach_control.tx_integer,
+ VTY_NEWLINE);
+ vty_out(vty, "RACH Max transmissions: %u%s",
+ rach_max_trans_raw2val(bts->si_common.rach_control.max_trans),
+ VTY_NEWLINE);
+ if (bts->si_common.rach_control.cell_bar)
vty_out(vty, " CELL IS BARRED%s", VTY_NEWLINE);
if (is_ipaccess_bts(bts))
vty_out(vty, " Unit ID: %u/%u/0, OML Stream ID 0x%02x%s",
@@ -143,6 +192,10 @@ static void bts_dump_vty(struct vty *vty, struct gsm_bts *bts)
e1isl_dump_vty(vty, bts->oml_link);
}
/* FIXME: oml_link, chan_desc */
+ memset(&pl, 0, sizeof(pl));
+ bts_chan_load(&pl, bts);
+ vty_out(vty, " Current Channel Load:%s", VTY_NEWLINE);
+ dump_pchan_load_vty(vty, " ", &pl);
}
DEFUN(show_bts, show_bts_cmd, "show bts [number]",
@@ -213,6 +266,7 @@ static void config_write_trx_single(struct vty *vty, struct gsm_bts_trx *trx)
vty_out(vty, " trx %u%s", trx->nr, VTY_NEWLINE);
vty_out(vty, " arfcn %u%s", trx->arfcn, VTY_NEWLINE);
+ vty_out(vty, " nominal power %u%s", trx->nominal_power, VTY_NEWLINE);
vty_out(vty, " max_power_red %u%s", trx->max_power_red, VTY_NEWLINE);
config_write_e1_link(vty, &trx->rsl_e1_link, " rsl ");
vty_out(vty, " rsl e1 tei %u%s", trx->rsl_tei, VTY_NEWLINE);
@@ -234,13 +288,22 @@ static void config_write_bts_single(struct vty *vty, struct gsm_bts *bts)
vty_out(vty, " training_sequence_code %u%s", bts->tsc, VTY_NEWLINE);
vty_out(vty, " base_station_id_code %u%s", bts->bsic, VTY_NEWLINE);
vty_out(vty, " ms max power %u%s", bts->ms_max_power, VTY_NEWLINE);
- if (bts->chan_desc.t3212)
+ vty_out(vty, " cell reselection hysteresis %u%s",
+ bts->si_common.cell_sel_par.cell_resel_hyst*2, VTY_NEWLINE);
+ vty_out(vty, " rxlev access min %u%s",
+ bts->si_common.cell_sel_par.rxlev_acc_min, VTY_NEWLINE);
+ if (bts->si_common.chan_desc.t3212)
vty_out(vty, " periodic location update %u%s",
- bts->chan_desc.t3212 * 10, VTY_NEWLINE);
+ bts->si_common.chan_desc.t3212 * 10, VTY_NEWLINE);
vty_out(vty, " channel allocator %s%s",
bts->chan_alloc_reverse ? "descending" : "ascending",
VTY_NEWLINE);
- if (bts->cell_barred)
+ vty_out(vty, " rach tx integer %u%s",
+ bts->si_common.rach_control.tx_integer, VTY_NEWLINE);
+ vty_out(vty, " rach max transmission %u%s",
+ rach_max_trans_raw2val(bts->si_common.rach_control.max_trans),
+ VTY_NEWLINE);
+ if (bts->si_common.rach_control.cell_bar)
vty_out(vty, " cell barred 1%s", VTY_NEWLINE);
if (is_ipaccess_bts(bts)) {
vty_out(vty, " ip.access unit_id %u %u%s",
@@ -273,8 +336,37 @@ static int config_write_net(struct vty *vty)
vty_out(vty, " short name %s%s", gsmnet->name_short, VTY_NEWLINE);
vty_out(vty, " long name %s%s", gsmnet->name_long, VTY_NEWLINE);
vty_out(vty, " auth policy %s%s", gsm_auth_policy_name(gsmnet->auth_policy), VTY_NEWLINE);
+ vty_out(vty, " location updating reject cause %u%s",
+ gsmnet->reject_cause, VTY_NEWLINE);
vty_out(vty, " encryption a5 %u%s", gsmnet->a5_encryption, VTY_NEWLINE);
vty_out(vty, " neci %u%s", gsmnet->neci, VTY_NEWLINE);
+ vty_out(vty, " rrlp mode %s%s", rrlp_mode_name(gsmnet->rrlp.mode),
+ VTY_NEWLINE);
+ vty_out(vty, " mm info %u%s", gsmnet->send_mm_info, VTY_NEWLINE);
+ vty_out(vty, " handover %u%s", gsmnet->handover.active, VTY_NEWLINE);
+ vty_out(vty, " handover window rxlev averaging %u%s",
+ gsmnet->handover.win_rxlev_avg, VTY_NEWLINE);
+ vty_out(vty, " handover window rxqual averaging %u%s",
+ gsmnet->handover.win_rxqual_avg, VTY_NEWLINE);
+ vty_out(vty, " handover window rxlev neighbor averaging %u%s",
+ gsmnet->handover.win_rxlev_avg_neigh, VTY_NEWLINE);
+ vty_out(vty, " handover power budget interval %u%s",
+ gsmnet->handover.pwr_interval, VTY_NEWLINE);
+ vty_out(vty, " handover power budget hysteresis %u%s",
+ gsmnet->handover.pwr_hysteresis, VTY_NEWLINE);
+ vty_out(vty, " handover maximum distance %u%s",
+ gsmnet->handover.max_distance, VTY_NEWLINE);
+ vty_out(vty, " timer t3101 %u%s", gsmnet->T3101, VTY_NEWLINE);
+ vty_out(vty, " timer t3103 %u%s", gsmnet->T3103, VTY_NEWLINE);
+ vty_out(vty, " timer t3105 %u%s", gsmnet->T3105, VTY_NEWLINE);
+ vty_out(vty, " timer t3107 %u%s", gsmnet->T3107, VTY_NEWLINE);
+ vty_out(vty, " timer t3109 %u%s", gsmnet->T3109, VTY_NEWLINE);
+ vty_out(vty, " timer t3111 %u%s", gsmnet->T3111, VTY_NEWLINE);
+ vty_out(vty, " timer t3113 %u%s", gsmnet->T3113, VTY_NEWLINE);
+ vty_out(vty, " timer t3115 %u%s", gsmnet->T3115, VTY_NEWLINE);
+ vty_out(vty, " timer t3117 %u%s", gsmnet->T3117, VTY_NEWLINE);
+ vty_out(vty, " timer t3119 %u%s", gsmnet->T3119, VTY_NEWLINE);
+ vty_out(vty, " timer t3141 %u%s", gsmnet->T3141, VTY_NEWLINE);
return CMD_SUCCESS;
}
@@ -354,24 +446,15 @@ DEFUN(show_trx,
static void ts_dump_vty(struct vty *vty, struct gsm_bts_trx_ts *ts)
{
- struct in_addr ia;
-
vty_out(vty, "Timeslot %u of TRX %u in BTS %u, phys cfg %s%s",
ts->nr, ts->trx->nr, ts->trx->bts->nr,
gsm_pchan_name(ts->pchan), VTY_NEWLINE);
vty_out(vty, " NM State: ");
net_dump_nmstate(vty, &ts->nm_state);
- if (is_ipaccess_bts(ts->trx->bts)) {
- ia.s_addr = ts->abis_ip.bound_ip;
- vty_out(vty, " Bound IP: %s Port %u RTP_TYPE2=%u CONN_ID=%u%s",
- inet_ntoa(ia), ts->abis_ip.bound_port,
- ts->abis_ip.rtp_payload2, ts->abis_ip.conn_id,
- VTY_NEWLINE);
- } else {
+ if (!is_ipaccess_bts(ts->trx->bts))
vty_out(vty, " E1 Line %u, Timeslot %u, Subslot %u%s",
ts->e1_link.e1_nr, ts->e1_link.e1_ts,
ts->e1_link.e1_ts_ss, VTY_NEWLINE);
- }
}
DEFUN(show_ts,
@@ -429,8 +512,12 @@ DEFUN(show_ts,
return CMD_SUCCESS;
}
-void subscr_dump_vty(struct vty *vty, struct gsm_subscriber *subscr)
+static void subscr_dump_vty(struct vty *vty, struct gsm_subscriber *subscr)
{
+ int rc;
+ struct gsm_auth_info ainfo;
+ struct gsm_auth_tuple atuple;
+
vty_out(vty, " ID: %llu, Authorized: %d%s", subscr->id,
subscr->authorized, VTY_NEWLINE);
if (subscr->name)
@@ -443,23 +530,77 @@ void subscr_dump_vty(struct vty *vty, struct gsm_subscriber *subscr)
if (subscr->tmsi != GSM_RESERVED_TMSI)
vty_out(vty, " TMSI: %08X%s", subscr->tmsi,
VTY_NEWLINE);
+
vty_out(vty, " Use count: %u%s", subscr->use_count, VTY_NEWLINE);
}
+static void meas_rep_dump_uni_vty(struct vty *vty,
+ struct gsm_meas_rep_unidir *mru,
+ const char *prefix,
+ const char *dir)
+{
+ vty_out(vty, "%s RXL-FULL-%s: %4d dBm, RXL-SUB-%s: %4d dBm ",
+ prefix, dir, rxlev2dbm(mru->full.rx_lev),
+ dir, rxlev2dbm(mru->sub.rx_lev));
+ vty_out(vty, "RXQ-FULL-%s: %d, RXQ-SUB-%s: %d%s",
+ dir, mru->full.rx_qual, dir, mru->sub.rx_qual,
+ VTY_NEWLINE);
+}
+
+static void meas_rep_dump_vty(struct vty *vty, struct gsm_meas_rep *mr,
+ const char *prefix)
+{
+ vty_out(vty, "%sMeasurement Report:%s", prefix, VTY_NEWLINE);
+ vty_out(vty, "%s Flags: %s%s%s%s%s", prefix,
+ mr->flags & MEAS_REP_F_UL_DTX ? "DTXu " : "",
+ mr->flags & MEAS_REP_F_DL_DTX ? "DTXd " : "",
+ mr->flags & MEAS_REP_F_FPC ? "FPC " : "",
+ mr->flags & MEAS_REP_F_DL_VALID ? " " : "DLinval ",
+ VTY_NEWLINE);
+ if (mr->flags & MEAS_REP_F_MS_TO)
+ vty_out(vty, "%s MS Timing Offset: %u%s", prefix,
+ mr->ms_timing_offset, VTY_NEWLINE);
+ if (mr->flags & MEAS_REP_F_MS_L1)
+ vty_out(vty, "%s L1 MS Power: %u dBm, Timing Advance: %u%s",
+ prefix, mr->ms_l1.pwr, mr->ms_l1.ta, VTY_NEWLINE);
+ if (mr->flags & MEAS_REP_F_DL_VALID)
+ meas_rep_dump_uni_vty(vty, &mr->dl, prefix, "dl");
+ meas_rep_dump_uni_vty(vty, &mr->ul, prefix, "ul");
+}
+
static void lchan_dump_vty(struct vty *vty, struct gsm_lchan *lchan)
{
+ int idx;
+
vty_out(vty, "Lchan %u in Timeslot %u of TRX %u in BTS %u, Type %s%s",
lchan->nr, lchan->ts->nr, lchan->ts->trx->nr,
- lchan->ts->trx->bts->nr, gsm_lchan_name(lchan->type),
+ lchan->ts->trx->bts->nr, gsm_lchant_name(lchan->type),
+ VTY_NEWLINE);
+ vty_out(vty, " Use Count: %u, State: %s%s", lchan->use_count,
+ gsm_lchans_name(lchan->state), VTY_NEWLINE);
+ vty_out(vty, " BS Power: %u dBm, MS Power: %u dBm%s",
+ lchan->ts->trx->nominal_power - lchan->ts->trx->max_power_red
+ - lchan->bs_power*2,
+ ms_pwr_dbm(lchan->ts->trx->bts->band, lchan->ms_power),
VTY_NEWLINE);
- vty_out(vty, " Use Count: %u%s", lchan->use_count, VTY_NEWLINE);
- vty_out(vty, " BS Power %u, MS Power %u%s", lchan->bs_power,
- lchan->ms_power, VTY_NEWLINE);
if (lchan->subscr) {
vty_out(vty, " Subscriber:%s", VTY_NEWLINE);
subscr_dump_vty(vty, lchan->subscr);
} else
vty_out(vty, " No Subscriber%s", VTY_NEWLINE);
+ if (is_ipaccess_bts(lchan->ts->trx->bts)) {
+ struct in_addr ia;
+ ia.s_addr = lchan->abis_ip.bound_ip;
+ vty_out(vty, " Bound IP: %s Port %u RTP_TYPE2=%u CONN_ID=%u%s",
+ inet_ntoa(ia), lchan->abis_ip.bound_port,
+ lchan->abis_ip.rtp_payload2, lchan->abis_ip.conn_id,
+ VTY_NEWLINE);
+ }
+
+ /* we want to report the last measurement report */
+ idx = calc_initial_idx(ARRAY_SIZE(lchan->meas_rep),
+ lchan->meas_rep_idx, 1);
+ meas_rep_dump_vty(vty, &lchan->meas_rep[idx], " ");
}
#if 0
@@ -712,6 +853,244 @@ DEFUN(show_paging,
return CMD_SUCCESS;
}
+static void _vty_output(struct debug_target *tgt, const char *line)
+{
+ struct vty *vty = tgt->tgt_vty.vty;
+ vty_out(vty, "%s", line);
+ /* This is an ugly hack, but there is no easy way... */
+ if (strchr(line, '\n'))
+ vty_out(vty, "\r");
+}
+
+struct debug_target *debug_target_create_vty(struct vty *vty)
+{
+ struct debug_target *target;
+
+ target = debug_target_create();
+ if (!target)
+ return NULL;
+
+ target->tgt_vty.vty = vty;
+ target->output = _vty_output;
+ return target;
+}
+
+DEFUN(enable_logging,
+ enable_logging_cmd,
+ "logging enable",
+ "Enables logging to this vty\n")
+{
+ struct telnet_connection *conn;
+
+ conn = (struct telnet_connection *) vty->priv;
+ if (conn->dbg) {
+ vty_out(vty, "Logging already enabled.%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ conn->dbg = debug_target_create_vty(vty);
+ if (!conn->dbg)
+ return CMD_WARNING;
+
+ debug_add_target(conn->dbg);
+ return CMD_SUCCESS;
+}
+
+DEFUN(logging_fltr_imsi,
+ logging_fltr_imsi_cmd,
+ "logging filter imsi IMSI",
+ "Print all messages related to a IMSI\n")
+{
+ struct telnet_connection *conn;
+
+ conn = (struct telnet_connection *) vty->priv;
+ if (!conn->dbg) {
+ vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ debug_set_imsi_filter(conn->dbg, argv[0]);
+ return CMD_SUCCESS;
+}
+
+DEFUN(logging_fltr_all,
+ logging_fltr_all_cmd,
+ "logging filter all <0-1>",
+ "Print all messages to the console\n")
+{
+ struct telnet_connection *conn;
+
+ conn = (struct telnet_connection *) vty->priv;
+ if (!conn->dbg) {
+ vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ debug_set_all_filter(conn->dbg, atoi(argv[0]));
+ return CMD_SUCCESS;
+}
+
+DEFUN(logging_use_clr,
+ logging_use_clr_cmd,
+ "logging color <0-1>",
+ "Use color for printing messages\n")
+{
+ struct telnet_connection *conn;
+
+ conn = (struct telnet_connection *) vty->priv;
+ if (!conn->dbg) {
+ vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ debug_set_use_color(conn->dbg, atoi(argv[0]));
+ return CMD_SUCCESS;
+}
+
+DEFUN(logging_prnt_timestamp,
+ logging_prnt_timestamp_cmd,
+ "logging timestamp <0-1>",
+ "Print the timestamp of each message\n")
+{
+ struct telnet_connection *conn;
+
+ conn = (struct telnet_connection *) vty->priv;
+ if (!conn->dbg) {
+ vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ debug_set_print_timestamp(conn->dbg, atoi(argv[0]));
+ return CMD_SUCCESS;
+}
+
+/* FIXME: those have to be kept in sync with the log levels and categories */
+#define VTY_DEBUG_CATEGORIES "(rll|cc|mm|rr|rsl|nm|sms|pag|mncc|inp|mi|mib|mux|meas|sccp|msc|mgcp|ho|db|ref)"
+#define VTY_DEBUG_LEVELS "(everything|debug|info|notice|error|fatal)"
+DEFUN(logging_level,
+ logging_level_cmd,
+ "logging level " VTY_DEBUG_CATEGORIES " " VTY_DEBUG_LEVELS,
+ "Set the log level for a specified category\n")
+{
+ struct telnet_connection *conn;
+ int category = debug_parse_category(argv[0]);
+ int level = debug_parse_level(argv[1]);
+
+ conn = (struct telnet_connection *) vty->priv;
+ if (!conn->dbg) {
+ vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (category < 0) {
+ vty_out(vty, "Invalid category `%s'%s", argv[0], VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (level < 0) {
+ vty_out(vty, "Invalid level `%s'%s", argv[1], VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ conn->dbg->categories[category].enabled = 1;
+ conn->dbg->categories[category].loglevel = level;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(logging_set_category_mask,
+ logging_set_category_mask_cmd,
+ "logging set debug mask MASK",
+ "Decide which categories to output.\n")
+{
+ struct telnet_connection *conn;
+
+ conn = (struct telnet_connection *) vty->priv;
+ if (!conn->dbg) {
+ vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ debug_parse_category_mask(conn->dbg, argv[0]);
+ return CMD_SUCCESS;
+}
+
+DEFUN(logging_set_log_level,
+ logging_set_log_level_cmd,
+ "logging set log level <0-8>",
+ "Set the global log level. The value 0 implies no filtering.\n")
+{
+ struct telnet_connection *conn;
+
+ conn = (struct telnet_connection *) vty->priv;
+ if (!conn->dbg) {
+ vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ debug_set_log_level(conn->dbg, atoi(argv[0]));
+ return CMD_SUCCESS;
+}
+
+DEFUN(diable_logging,
+ disable_logging_cmd,
+ "logging disable",
+ "Disables logging to this vty\n")
+{
+ struct telnet_connection *conn;
+
+ conn = (struct telnet_connection *) vty->priv;
+ if (!conn->dbg) {
+ vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ debug_del_target(conn->dbg);
+ talloc_free(conn->dbg);
+ conn->dbg = NULL;
+ return CMD_SUCCESS;
+}
+
+DEFUN(show_stats,
+ show_stats_cmd,
+ "show statistics",
+ SHOW_STR "Display network statistics\n")
+{
+ struct gsm_network *net = gsmnet;
+
+ vty_out(vty, "Channel Requests : %lu total, %lu no channel%s",
+ counter_get(net->stats.chreq.total),
+ counter_get(net->stats.chreq.no_channel), VTY_NEWLINE);
+ vty_out(vty, "Location Update : %lu attach, %lu normal, %lu periodic%s",
+ counter_get(net->stats.loc_upd_type.attach),
+ counter_get(net->stats.loc_upd_type.normal),
+ counter_get(net->stats.loc_upd_type.periodic), VTY_NEWLINE);
+ vty_out(vty, "IMSI Detach Indications : %lu%s",
+ counter_get(net->stats.loc_upd_type.detach), VTY_NEWLINE);
+ vty_out(vty, "Location Update Response: %lu accept, %lu reject%s",
+ counter_get(net->stats.loc_upd_resp.accept),
+ counter_get(net->stats.loc_upd_resp.reject), VTY_NEWLINE);
+ vty_out(vty, "Paging : %lu attempted, %lu complete, %lu expired%s",
+ counter_get(net->stats.paging.attempted),
+ counter_get(net->stats.paging.completed),
+ counter_get(net->stats.paging.expired), VTY_NEWLINE);
+ vty_out(vty, "Handover : %lu attempted, %lu no_channel, %lu timeout, "
+ "%lu completed, %lu failed%s",
+ counter_get(net->stats.handover.attempted),
+ counter_get(net->stats.handover.no_channel),
+ counter_get(net->stats.handover.timeout),
+ counter_get(net->stats.handover.completed),
+ counter_get(net->stats.handover.failed), VTY_NEWLINE);
+ vty_out(vty, "SMS MO : %lu submitted, %lu no receiver%s",
+ counter_get(net->stats.sms.submitted),
+ counter_get(net->stats.sms.no_receiver), VTY_NEWLINE);
+ vty_out(vty, "SMS MT : %lu delivered, %lu no memory, %lu other error%s",
+ counter_get(net->stats.sms.delivered),
+ counter_get(net->stats.sms.rp_err_mem),
+ counter_get(net->stats.sms.rp_err_other), VTY_NEWLINE);
+ return CMD_SUCCESS;
+}
+
DEFUN(cfg_net,
cfg_net_cmd,
"network",
@@ -782,6 +1161,16 @@ DEFUN(cfg_net_auth_policy,
return CMD_SUCCESS;
}
+DEFUN(cfg_net_reject_cause,
+ cfg_net_reject_cause_cmd,
+ "location updating reject cause <2-111>",
+ "Set the reject cause of location updating reject\n")
+{
+ gsmnet->reject_cause = atoi(argv[0]);
+
+ return CMD_SUCCESS;
+}
+
DEFUN(cfg_net_encryption,
cfg_net_encryption_cmd,
"encryption a5 (0|1|2)",
@@ -801,6 +1190,120 @@ DEFUN(cfg_net_neci,
return CMD_SUCCESS;
}
+DEFUN(cfg_net_rrlp_mode, cfg_net_rrlp_mode_cmd,
+ "rrlp mode (none|ms-based|ms-preferred|ass-preferred)",
+ "Set the Radio Resource Location Protocol Mode")
+{
+ gsmnet->rrlp.mode = rrlp_mode_parse(argv[0]);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_net_mm_info, cfg_net_mm_info_cmd,
+ "mm info (0|1)",
+ "Whether to send MM INFO after LOC UPD ACCEPT")
+{
+ gsmnet->send_mm_info = atoi(argv[0]);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_net_handover, cfg_net_handover_cmd,
+ "handover (0|1)",
+ "Whether or not to use in-call handover")
+{
+ int enable = atoi(argv[0]);
+
+ if (enable && ipacc_rtp_direct) {
+ vty_out(vty, "%% Cannot enable handover unless RTP Proxy mode "
+ "is enabled by using the -P command line option%s",
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ gsmnet->handover.active = enable;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_net_ho_win_rxlev_avg, cfg_net_ho_win_rxlev_avg_cmd,
+ "handover window rxlev averaging <1-10>",
+ "How many RxLev measurements are used for averaging")
+{
+ gsmnet->handover.win_rxlev_avg = atoi(argv[0]);
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_net_ho_win_rxqual_avg, cfg_net_ho_win_rxqual_avg_cmd,
+ "handover window rxqual averaging <1-10>",
+ "How many RxQual measurements are used for averaging")
+{
+ gsmnet->handover.win_rxqual_avg = atoi(argv[0]);
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_net_ho_win_rxlev_neigh_avg, cfg_net_ho_win_rxlev_avg_neigh_cmd,
+ "handover window rxlev neighbor averaging <1-10>",
+ "How many RxQual measurements are used for averaging")
+{
+ gsmnet->handover.win_rxlev_avg_neigh = atoi(argv[0]);
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_net_ho_pwr_interval, cfg_net_ho_pwr_interval_cmd,
+ "handover power budget interval <1-99>",
+ "How often to check if we have a better cell (SACCH frames)")
+{
+ gsmnet->handover.pwr_interval = atoi(argv[0]);
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_net_ho_pwr_hysteresis, cfg_net_ho_pwr_hysteresis_cmd,
+ "handover power budget hysteresis <0-999>",
+ "How many dB does a neighbor to be stronger to become a HO candidate")
+{
+ gsmnet->handover.pwr_hysteresis = atoi(argv[0]);
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_net_ho_max_distance, cfg_net_ho_max_distance_cmd,
+ "handover maximum distance <0-9999>",
+ "How big is the maximum timing advance before HO is forced")
+{
+ gsmnet->handover.max_distance = atoi(argv[0]);
+ return CMD_SUCCESS;
+}
+
+#define DECLARE_TIMER(number, doc) \
+ DEFUN(cfg_net_T##number, \
+ cfg_net_T##number##_cmd, \
+ "timer t" #number " <0-65535>", \
+ doc) \
+{ \
+ int value = atoi(argv[0]); \
+ \
+ if (value < 0 || value > 65535) { \
+ vty_out(vty, "Timer value %s out of range.%s", \
+ argv[0], VTY_NEWLINE); \
+ return CMD_WARNING; \
+ } \
+ \
+ gsmnet->T##number = value; \
+ return CMD_SUCCESS; \
+}
+
+DECLARE_TIMER(3101, "Set the timeout value for IMMEDIATE ASSIGNMENT.")
+DECLARE_TIMER(3103, "Set the timeout value for HANDOVER.")
+DECLARE_TIMER(3105, "Currently not used.")
+DECLARE_TIMER(3107, "Currently not used.")
+DECLARE_TIMER(3109, "Currently not used.")
+DECLARE_TIMER(3111, "Currently not used.")
+DECLARE_TIMER(3113, "Set the time to try paging a subscriber.")
+DECLARE_TIMER(3115, "Currently not used.")
+DECLARE_TIMER(3117, "Currently not used.")
+DECLARE_TIMER(3119, "Currently not used.")
+DECLARE_TIMER(3141, "Currently not used.")
+
+
/* per-BTS configuration */
DEFUN(cfg_bts,
cfg_bts_cmd,
@@ -821,8 +1324,11 @@ DEFUN(cfg_bts,
} else
bts = gsm_bts_num(gsmnet, bts_nr);
- if (!bts)
+ if (!bts) {
+ vty_out(vty, "%% Unable to allocate BTS %u%s",
+ gsmnet->num_bts, VTY_NEWLINE);
return CMD_WARNING;
+ }
vty->index = bts;
vty->node = BTS_NODE;
@@ -836,13 +1342,11 @@ DEFUN(cfg_bts_type,
"Set the BTS type\n")
{
struct gsm_bts *bts = vty->index;
+ int rc;
- bts->type = parse_btstype(argv[0]);
-
- if (is_ipaccess_bts(bts)) {
- /* Set the default OML Stream ID to 0xff */
- bts->oml_tei = 0xff;
- }
+ rc = gsm_set_bts_type(bts, parse_btstype(argv[0]));
+ if (rc < 0)
+ return CMD_WARNING;
return CMD_SUCCESS;
}
@@ -909,6 +1413,7 @@ DEFUN(cfg_bts_lac,
return CMD_SUCCESS;
}
+
DEFUN(cfg_bts_tsc,
cfg_bts_tsc_cmd,
"training_sequence_code <0-255>",
@@ -1024,13 +1529,33 @@ DEFUN(cfg_bts_challoc, cfg_bts_challoc_cmd,
return CMD_SUCCESS;
}
+DEFUN(cfg_bts_rach_tx_integer,
+ cfg_bts_rach_tx_integer_cmd,
+ "rach tx integer <0-15>",
+ "Set the raw tx integer value in RACH Control parameters IE")
+{
+ struct gsm_bts *bts = vty->index;
+ bts->si_common.rach_control.tx_integer = atoi(argv[0]) & 0xf;
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_bts_rach_max_trans,
+ cfg_bts_rach_max_trans_cmd,
+ "rach max transmission (1|2|4|7)",
+ "Set the maximum number of RACH burst transmissions")
+{
+ struct gsm_bts *bts = vty->index;
+ bts->si_common.rach_control.max_trans = rach_max_trans_val2raw(atoi(argv[0]));
+ return CMD_SUCCESS;
+}
+
DEFUN(cfg_bts_cell_barred, cfg_bts_cell_barred_cmd,
"cell barred (0|1)",
"Should this cell be barred from access?")
{
struct gsm_bts *bts = vty->index;
- bts->cell_barred = atoi(argv[0]);
+ bts->si_common.rach_control.cell_bar = atoi(argv[0]);
return CMD_SUCCESS;
}
@@ -1046,13 +1571,35 @@ DEFUN(cfg_bts_ms_max_power, cfg_bts_ms_max_power_cmd,
return CMD_SUCCESS;
}
+DEFUN(cfg_bts_cell_resel_hyst, cfg_bts_cell_resel_hyst_cmd,
+ "cell reselection hysteresis <0-14>",
+ "Cell Re-Selection Hysteresis in dB")
+{
+ struct gsm_bts *bts = vty->index;
+
+ bts->si_common.cell_sel_par.cell_resel_hyst = atoi(argv[0])/2;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_bts_rxlev_acc_min, cfg_bts_rxlev_acc_min_cmd,
+ "rxlev access min <0-63>",
+ "Minimum RxLev needed for cell access (better than -110dBm)")
+{
+ struct gsm_bts *bts = vty->index;
+
+ bts->si_common.cell_sel_par.rxlev_acc_min = atoi(argv[0]);
+
+ return CMD_SUCCESS;
+}
+
DEFUN(cfg_bts_per_loc_upd, cfg_bts_per_loc_upd_cmd,
"periodic location update <0-1530>",
"Periodic Location Updating Interval in Minutes")
{
struct gsm_bts *bts = vty->index;
- bts->chan_desc.t3212 = atoi(argv[0]) / 10;
+ bts->si_common.chan_desc.t3212 = atoi(argv[0]) / 10;
return CMD_SUCCESS;
}
@@ -1106,6 +1653,18 @@ DEFUN(cfg_trx_arfcn,
return CMD_SUCCESS;
}
+DEFUN(cfg_trx_nominal_power,
+ cfg_trx_nominal_power_cmd,
+ "nominal power <0-100>",
+ "Nominal TRX RF Power in dB\n")
+{
+ struct gsm_bts_trx *trx = vty->index;
+
+ trx->nominal_power = atoi(argv[0]);
+
+ return CMD_SUCCESS;
+}
+
DEFUN(cfg_trx_max_power_red,
cfg_trx_max_power_red_cmd,
"max_power_red <0-100>",
@@ -1158,6 +1717,17 @@ DEFUN(cfg_trx_rsl_e1_tei,
return CMD_SUCCESS;
}
+DEFUN(cfg_trx_rf_locked,
+ cfg_trx_rf_locked_cmd,
+ "rf_locked (0|1)",
+ "Turn off RF of the TRX.\n")
+{
+ int locked = atoi(argv[0]);
+ struct gsm_bts_trx *trx = vty->index;
+
+ gsm_trx_lock_rf(trx, locked);
+ return CMD_SUCCESS;
+}
/* per TS configuration */
DEFUN(cfg_ts,
@@ -1230,6 +1800,17 @@ int bsc_vty_init(struct gsm_network *net)
install_element(VIEW_NODE, &show_e1ts_cmd);
install_element(VIEW_NODE, &show_paging_cmd);
+ install_element(VIEW_NODE, &show_stats_cmd);
+
+ install_element(VIEW_NODE, &enable_logging_cmd);
+ install_element(VIEW_NODE, &disable_logging_cmd);
+ install_element(VIEW_NODE, &logging_fltr_imsi_cmd);
+ install_element(VIEW_NODE, &logging_fltr_all_cmd);
+ install_element(VIEW_NODE, &logging_use_clr_cmd);
+ install_element(VIEW_NODE, &logging_prnt_timestamp_cmd);
+ install_element(VIEW_NODE, &logging_set_category_mask_cmd);
+ install_element(VIEW_NODE, &logging_level_cmd);
+ install_element(VIEW_NODE, &logging_set_log_level_cmd);
install_element(CONFIG_NODE, &cfg_net_cmd);
install_node(&net_node, config_write_net);
@@ -1239,8 +1820,29 @@ int bsc_vty_init(struct gsm_network *net)
install_element(GSMNET_NODE, &cfg_net_name_short_cmd);
install_element(GSMNET_NODE, &cfg_net_name_long_cmd);
install_element(GSMNET_NODE, &cfg_net_auth_policy_cmd);
+ install_element(GSMNET_NODE, &cfg_net_reject_cause_cmd);
install_element(GSMNET_NODE, &cfg_net_encryption_cmd);
install_element(GSMNET_NODE, &cfg_net_neci_cmd);
+ install_element(GSMNET_NODE, &cfg_net_rrlp_mode_cmd);
+ install_element(GSMNET_NODE, &cfg_net_mm_info_cmd);
+ install_element(GSMNET_NODE, &cfg_net_handover_cmd);
+ install_element(GSMNET_NODE, &cfg_net_ho_win_rxlev_avg_cmd);
+ install_element(GSMNET_NODE, &cfg_net_ho_win_rxqual_avg_cmd);
+ install_element(GSMNET_NODE, &cfg_net_ho_win_rxlev_avg_neigh_cmd);
+ install_element(GSMNET_NODE, &cfg_net_ho_pwr_interval_cmd);
+ install_element(GSMNET_NODE, &cfg_net_ho_pwr_hysteresis_cmd);
+ install_element(GSMNET_NODE, &cfg_net_ho_max_distance_cmd);
+ install_element(GSMNET_NODE, &cfg_net_T3101_cmd);
+ install_element(GSMNET_NODE, &cfg_net_T3103_cmd);
+ install_element(GSMNET_NODE, &cfg_net_T3105_cmd);
+ install_element(GSMNET_NODE, &cfg_net_T3107_cmd);
+ install_element(GSMNET_NODE, &cfg_net_T3109_cmd);
+ install_element(GSMNET_NODE, &cfg_net_T3111_cmd);
+ install_element(GSMNET_NODE, &cfg_net_T3113_cmd);
+ install_element(GSMNET_NODE, &cfg_net_T3115_cmd);
+ install_element(GSMNET_NODE, &cfg_net_T3117_cmd);
+ install_element(GSMNET_NODE, &cfg_net_T3119_cmd);
+ install_element(GSMNET_NODE, &cfg_net_T3141_cmd);
install_element(GSMNET_NODE, &cfg_bts_cmd);
install_node(&bts_node, config_write_bts);
@@ -1256,18 +1858,24 @@ int bsc_vty_init(struct gsm_network *net)
install_element(BTS_NODE, &cfg_bts_oml_e1_cmd);
install_element(BTS_NODE, &cfg_bts_oml_e1_tei_cmd);
install_element(BTS_NODE, &cfg_bts_challoc_cmd);
+ install_element(BTS_NODE, &cfg_bts_rach_tx_integer_cmd);
+ install_element(BTS_NODE, &cfg_bts_rach_max_trans_cmd);
install_element(BTS_NODE, &cfg_bts_cell_barred_cmd);
install_element(BTS_NODE, &cfg_bts_ms_max_power_cmd);
install_element(BTS_NODE, &cfg_bts_per_loc_upd_cmd);
+ install_element(BTS_NODE, &cfg_bts_cell_resel_hyst_cmd);
+ install_element(BTS_NODE, &cfg_bts_rxlev_acc_min_cmd);
install_element(BTS_NODE, &cfg_trx_cmd);
install_node(&trx_node, dummy_config_write);
install_default(TRX_NODE);
install_element(TRX_NODE, &cfg_trx_arfcn_cmd);
+ install_element(TRX_NODE, &cfg_trx_nominal_power_cmd);
install_element(TRX_NODE, &cfg_trx_max_power_red_cmd);
install_element(TRX_NODE, &cfg_trx_rsl_e1_cmd);
install_element(TRX_NODE, &cfg_trx_rsl_e1_tei_cmd);
+ install_element(TRX_NODE, &cfg_trx_rf_locked_cmd);
install_element(TRX_NODE, &cfg_ts_cmd);
install_node(&ts_node, dummy_config_write);
diff --git a/openbsc/src/vty_interface_layer3.c b/openbsc/src/vty_interface_layer3.c
index 4cc08c2da..f2b572895 100644
--- a/openbsc/src/vty_interface_layer3.c
+++ b/openbsc/src/vty_interface_layer3.c
@@ -40,9 +40,7 @@
#include <openbsc/db.h>
#include <openbsc/talloc.h>
#include <openbsc/signal.h>
-
-/* forward declarations */
-void subscr_dump_vty(struct vty *vty, struct gsm_subscriber *subscr);
+#include <openbsc/debug.h>
static struct gsm_network *gsmnet;
@@ -74,6 +72,33 @@ static struct buffer *argv_to_buffer(int argc, const char *argv[], int base)
return b;
}
+static int hexparse(const char *str, u_int8_t *b, int max_len)
+
+{
+ int i, l, v;
+
+ l = strlen(str);
+ if ((l&1) || ((l>>1) > max_len))
+ return -1;
+
+ memset(b, 0x00, max_len);
+
+ for (i=0; i<l; i++) {
+ char c = str[i];
+ if (c >= '0' && c <= '9')
+ v = c - '0';
+ else if (c >= 'a' && c <= 'f')
+ v = 10 + (c - 'a');
+ else if (c >= 'A' && c <= 'F')
+ v = 10 + (c - 'a');
+ else
+ return -1;
+ b[i>>1] |= v << (i&1 ? 0 : 4);
+ }
+
+ return i>>1;
+}
+
/* per-subscriber configuration */
DEFUN(cfg_subscr,
cfg_subscr_cmd,
@@ -97,6 +122,55 @@ DEFUN(cfg_subscr,
return CMD_SUCCESS;
}
+static void subscr_dump_full_vty(struct vty *vty, struct gsm_subscriber *subscr)
+{
+ int rc;
+ struct gsm_auth_info ainfo;
+ struct gsm_auth_tuple atuple;
+
+ vty_out(vty, " ID: %llu, Authorized: %d%s", subscr->id,
+ subscr->authorized, VTY_NEWLINE);
+ if (subscr->name)
+ vty_out(vty, " Name: '%s'%s", subscr->name, VTY_NEWLINE);
+ if (subscr->extension)
+ vty_out(vty, " Extension: %s%s", subscr->extension,
+ VTY_NEWLINE);
+ if (subscr->imsi)
+ vty_out(vty, " IMSI: %s%s", subscr->imsi, VTY_NEWLINE);
+ if (subscr->tmsi != GSM_RESERVED_TMSI)
+ vty_out(vty, " TMSI: %08X%s", subscr->tmsi,
+ VTY_NEWLINE);
+
+ rc = get_authinfo_by_subscr(&ainfo, subscr);
+ if (!rc) {
+ vty_out(vty, " A3A8 algorithm id: %d%s",
+ ainfo.auth_algo, VTY_NEWLINE);
+ vty_out(vty, " A3A8 Ki: %s%s",
+ hexdump(ainfo.a3a8_ki, ainfo.a3a8_ki_len),
+ VTY_NEWLINE);
+ }
+
+ rc = get_authtuple_by_subscr(&atuple, subscr);
+ if (!rc) {
+ vty_out(vty, " A3A8 last tuple (used %d times):%s",
+ atuple.use_count, VTY_NEWLINE);
+ vty_out(vty, " seq # : %d%s",
+ atuple.key_seq, VTY_NEWLINE);
+ vty_out(vty, " RAND : %s%s",
+ hexdump(atuple.rand, sizeof(atuple.rand)),
+ VTY_NEWLINE);
+ vty_out(vty, " SRES : %s%s",
+ hexdump(atuple.sres, sizeof(atuple.sres)),
+ VTY_NEWLINE);
+ vty_out(vty, " Kc : %s%s",
+ hexdump(atuple.kc, sizeof(atuple.kc)),
+ VTY_NEWLINE);
+ }
+
+ vty_out(vty, " Use count: %u%s", subscr->use_count, VTY_NEWLINE);
+}
+
+
/* Subscriber */
DEFUN(show_subscr,
show_subscr_cmd,
@@ -114,7 +188,7 @@ DEFUN(show_subscr,
VTY_NEWLINE);
return CMD_WARNING;
}
- subscr_dump_vty(vty, subscr);
+ subscr_dump_full_vty(vty, subscr);
subscr_put(subscr);
return CMD_SUCCESS;
@@ -135,7 +209,7 @@ DEFUN(show_subscr_cache,
llist_for_each_entry(subscr, &active_subscribers, entry) {
vty_out(vty, " Subscriber:%s", VTY_NEWLINE);
- subscr_dump_vty(vty, subscr);
+ subscr_dump_full_vty(vty, subscr);
}
return CMD_SUCCESS;
@@ -143,23 +217,20 @@ DEFUN(show_subscr_cache,
DEFUN(sms_send_pend,
sms_send_pend_cmd,
- "sms send pending MIN_ID",
- "Send all pending SMS starting from MIN_ID")
+ "sms send pending",
+ "Send all pending SMS")
{
struct gsm_sms *sms;
- int id = atoi(argv[0]);
+ int id = 0;
while (1) {
- sms = db_sms_get_unsent(gsmnet, id++);
+ sms = db_sms_get_unsent_by_subscr(gsmnet, id);
if (!sms)
- return CMD_WARNING;
-
- if (!sms->receiver) {
- sms_free(sms);
- continue;
- }
+ break;
gsm411_send_sms_subscr(sms->receiver, sms);
+
+ id = sms->receiver->id + 1;
}
return CMD_SUCCESS;
@@ -269,10 +340,46 @@ DEFUN(subscriber_silent_sms,
return rc;
}
-DEFUN(subscriber_silent_call,
- subscriber_silent_call_cmd,
- "subscriber " SUBSCR_TYPES " EXTEN silent call (start|stop)",
- "Send a silent call to a subscriber")
+DEFUN(subscriber_silent_call_start,
+ subscriber_silent_call_start_cmd,
+ "subscriber " SUBSCR_TYPES " EXTEN silent call start (any|tch/f|tch/any|sdcch)",
+ "Start a silent call to a subscriber")
+{
+ struct gsm_subscriber *subscr = get_subscr_by_argv(argv[0], argv[1]);
+ int rc, type;
+
+ if (!subscr) {
+ vty_out(vty, "%% No subscriber found for %s %s%s",
+ argv[0], argv[1], VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (!strcmp(argv[2], "tch/f"))
+ type = RSL_CHANNEED_TCH_F;
+ else if (!strcmp(argv[2], "tch/any"))
+ type = RSL_CHANNEED_TCH_ForH;
+ else if (!strcmp(argv[2], "sdcch"))
+ type = RSL_CHANNEED_SDCCH;
+ else
+ type = RSL_CHANNEED_ANY; /* Defaults to ANY */
+
+ rc = gsm_silent_call_start(subscr, vty, type);
+ if (rc <= 0) {
+ vty_out(vty, "%% Subscriber not attached%s",
+ VTY_NEWLINE);
+ subscr_put(subscr);
+ return CMD_WARNING;
+ }
+
+ subscr_put(subscr);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(subscriber_silent_call_stop,
+ subscriber_silent_call_stop_cmd,
+ "subscriber " SUBSCR_TYPES " EXTEN silent call stop",
+ "Stop a silent call to a subscriber")
{
struct gsm_subscriber *subscr = get_subscr_by_argv(argv[0], argv[1]);
int rc;
@@ -283,17 +390,10 @@ DEFUN(subscriber_silent_call,
return CMD_WARNING;
}
- if (!strcmp(argv[2], "start")) {
- rc = gsm_silent_call_start(subscr, vty);
- if (rc <= 0) {
- vty_out(vty, "%% Subscriber not attached%s",
- VTY_NEWLINE);
- return CMD_WARNING;
- }
- } else {
- rc = gsm_silent_call_stop(subscr);
- if (rc < 0)
- return CMD_WARNING;
+ rc = gsm_silent_call_stop(subscr);
+ if (rc < 0) {
+ subscr_put(subscr);
+ return CMD_WARNING;
}
subscr_put(subscr);
@@ -349,6 +449,40 @@ DEFUN(cfg_subscr_authorized,
return CMD_SUCCESS;
}
+#define A3A8_ALG_TYPES "(none|comp128v1)"
+
+DEFUN(cfg_subscr_a3a8,
+ cfg_subscr_a3a8_cmd,
+ "a3a8 " A3A8_ALG_TYPES " [KI]",
+ "Set a3a8 parameters for the subscriber")
+{
+ struct gsm_subscriber *subscr = vty->index;
+ const char *alg_str = argv[0];
+ const char *ki_str = argv[1];
+ struct gsm_auth_info ainfo;
+ int rc;
+
+ if (!strcasecmp(alg_str, "none")) {
+ /* Just erase */
+ rc = set_authinfo_for_subscr(NULL, subscr);
+ } else if (!strcasecmp(alg_str, "comp128v1")) {
+ /* Parse hex string Ki */
+ rc = hexparse(ki_str, ainfo.a3a8_ki, sizeof(ainfo.a3a8_ki));
+ if (rc != 16)
+ return CMD_WARNING;
+
+ /* Set the infos */
+ ainfo.auth_algo = AUTH_ALGO_COMP128v1;
+ ainfo.a3a8_ki_len = rc;
+ rc = set_authinfo_for_subscr(&ainfo, subscr);
+ } else {
+ /* Unknown method */
+ return CMD_WARNING;
+ }
+
+ return rc ? CMD_WARNING : CMD_SUCCESS;
+}
+
static int scall_cbfn(unsigned int subsys, unsigned int signal,
void *handler_data, void *signal_data)
{
@@ -381,7 +515,8 @@ int bsc_vty_init_extra(struct gsm_network *net)
install_element(VIEW_NODE, &subscriber_send_sms_cmd);
install_element(VIEW_NODE, &subscriber_silent_sms_cmd);
- install_element(VIEW_NODE, &subscriber_silent_call_cmd);
+ install_element(VIEW_NODE, &subscriber_silent_call_start_cmd);
+ install_element(VIEW_NODE, &subscriber_silent_call_stop_cmd);
install_element(CONFIG_NODE, &cfg_subscr_cmd);
install_node(&subscr_node, dummy_config_write);
@@ -390,6 +525,7 @@ int bsc_vty_init_extra(struct gsm_network *net)
install_element(SUBSCR_NODE, &cfg_subscr_name_cmd);
install_element(SUBSCR_NODE, &cfg_subscr_extension_cmd);
install_element(SUBSCR_NODE, &cfg_subscr_authorized_cmd);
+ install_element(SUBSCR_NODE, &cfg_subscr_a3a8_cmd);
return 0;
}