aboutsummaryrefslogtreecommitdiffstats
path: root/openbsc/src
diff options
context:
space:
mode:
Diffstat (limited to 'openbsc/src')
-rw-r--r--openbsc/src/Makefile.am12
-rwxr-xr-xopenbsc/src/abis_nm.c264
-rw-r--r--openbsc/src/abis_rsl.c104
-rw-r--r--openbsc/src/bsc_init.c170
-rw-r--r--openbsc/src/bsc_mgcp.c1172
-rw-r--r--openbsc/src/chan_alloc.c10
-rw-r--r--openbsc/src/db.c10
-rw-r--r--openbsc/src/debug.c3
-rw-r--r--openbsc/src/e1_config.c2
-rw-r--r--openbsc/src/gsm_04_08.c81
-rw-r--r--openbsc/src/gsm_04_08_utils.c200
-rw-r--r--openbsc/src/gsm_04_11.c226
-rw-r--r--openbsc/src/gsm_04_80.c330
-rw-r--r--openbsc/src/gsm_data.c33
-rw-r--r--openbsc/src/gsm_subscriber_base.c3
-rw-r--r--openbsc/src/gsm_utils.c19
-rw-r--r--openbsc/src/input/ipaccess.c53
-rw-r--r--openbsc/src/input/misdn.c2
-rw-r--r--openbsc/src/ipaccess-config.c57
-rw-r--r--openbsc/src/mgcp.cfg19
-rw-r--r--openbsc/src/msgb.c20
-rw-r--r--openbsc/src/openbsc.cfg.1-12
-rw-r--r--openbsc/src/openbsc.cfg.1-22
-rw-r--r--openbsc/src/openbsc.cfg.2-22
-rw-r--r--openbsc/src/openbsc.cfg.nanobts40
-rw-r--r--openbsc/src/paging.c16
-rw-r--r--openbsc/src/rrlp.c1
-rw-r--r--openbsc/src/sccp/sccp.c1171
-rw-r--r--openbsc/src/silent_call.c93
-rw-r--r--openbsc/src/talloc.c9
-rw-r--r--openbsc/src/telnet_interface.c1
-rw-r--r--openbsc/src/timer.c2
-rw-r--r--openbsc/src/tlv_parser.c168
-rw-r--r--openbsc/src/token_auth.c3
-rw-r--r--openbsc/src/transaction.c2
-rw-r--r--openbsc/src/ussd.c71
-rw-r--r--openbsc/src/vty_interface.c135
-rw-r--r--openbsc/src/vty_interface_layer3.c128
38 files changed, 4202 insertions, 434 deletions
diff --git a/openbsc/src/Makefile.am b/openbsc/src/Makefile.am
index 726c71278..100641e63 100644
--- a/openbsc/src/Makefile.am
+++ b/openbsc/src/Makefile.am
@@ -1,8 +1,9 @@
INCLUDES = $(all_includes) -I$(top_srcdir)/include
AM_CFLAGS=-Wall
-sbin_PROGRAMS = bsc_hack bs11_config ipaccess-find ipaccess-config isdnsync
-noinst_LIBRARIES = libbsc.a libmsc.a libvty.a
+sbin_PROGRAMS = bsc_hack bs11_config ipaccess-find ipaccess-config \
+ isdnsync bsc_mgcp
+noinst_LIBRARIES = libbsc.a libmsc.a libvty.a libsccp.a
noinst_HEADERS = vty/cardshell.h
libbsc_a_SOURCES = abis_rsl.c abis_nm.c gsm_data.c gsm_04_08_utils.c \
@@ -14,10 +15,12 @@ libbsc_a_SOURCES = abis_rsl.c abis_nm.c gsm_data.c gsm_04_08_utils.c \
libmsc_a_SOURCES = gsm_subscriber.c db.c telnet_interface.c \
mncc.c rtp_proxy.c md5c.c gsm_04_08.c gsm_04_11.c transaction.c \
- token_auth.c rrlp.c
+ token_auth.c rrlp.c gsm_04_80.c ussd.c silent_call.c
libvty_a_SOURCES = vty/buffer.c vty/command.c vty/vector.c vty/vty.c
+libsccp_a_SOURCES = sccp/sccp.c
+
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)
@@ -30,3 +33,6 @@ ipaccess_config_SOURCES = ipaccess-config.c
ipaccess_config_LDADD = libbsc.a libmsc.a libbsc.a libvty.a -ldl -ldbi $(LIBCRYPT)
isdnsync_SOURCES = isdnsync.c
+
+bsc_mgcp_SOURCES = bsc_mgcp.c msgb.c talloc.c debug.c select.c timer.c telnet_interface.c
+bsc_mgcp_LDADD = libvty.a
diff --git a/openbsc/src/abis_nm.c b/openbsc/src/abis_nm.c
index f9a7252a7..b1fe97ddf 100755
--- a/openbsc/src/abis_nm.c
+++ b/openbsc/src/abis_nm.c
@@ -328,7 +328,6 @@ static const struct tlv_definition nm_att_tlvdef = {
[NM_ATT_GET_ARI] = { TLV_TYPE_TL16V },
[NM_ATT_HW_CONF_CHG] = { TLV_TYPE_TL16V },
[NM_ATT_OUTST_ALARM] = { TLV_TYPE_TV },
- [NM_ATT_FILE_DATA] = { TLV_TYPE_TL16V },
[NM_ATT_MEAS_RES] = { TLV_TYPE_TL16V },
/* BS11 specifics */
[NM_ATT_BS11_ESN_FW_CODE_NO] = { TLV_TYPE_TLV },
@@ -410,6 +409,8 @@ static const enum abis_nm_chan_comb chcomb4pchan[] = {
[GSM_PCHAN_TCH_F] = NM_CHANC_TCHFull,
[GSM_PCHAN_TCH_H] = NM_CHANC_TCHHalf,
[GSM_PCHAN_SDCCH8_SACCH8C] = NM_CHANC_SDCCH,
+ [GSM_PCHAN_PDCH] = NM_CHANC_IPAC_PDCH,
+ [GSM_PCHAN_TCH_F_PDCH] = NM_CHANC_IPAC_TCHFull_PDCH,
/* FIXME: bounds check */
};
@@ -571,6 +572,18 @@ const char *nm_avail_name(u_int8_t avail)
return avail_names[avail];
}
+static struct value_string test_names[] = {
+ /* FIXME: standard test names */
+ { NM_IPACC_TESTNO_CHAN_USAGE, "Channel Usage" },
+ { NM_IPACC_TESTNO_BCCH_CHAN_USAGE, "BCCH Channel Usage" },
+ { NM_IPACC_TESTNO_FREQ_SYNC, "Frequency Synchronization" },
+ { NM_IPACC_TESTNO_BCCH_INFO, "BCCH Info" },
+ { NM_IPACC_TESTNO_TX_BEACON, "Transmit Beacon" },
+ { NM_IPACC_TESTNO_SYSINFO_MONITOR, "System Info Monitor" },
+ { NM_IPACC_TESTNO_BCCCH_MONITOR, "BCCH Monitor" },
+ { 0, NULL }
+};
+
const char *nm_adm_name(u_int8_t adm)
{
switch (adm) {
@@ -585,6 +598,14 @@ const char *nm_adm_name(u_int8_t adm)
}
}
+static void debugp_foh(struct abis_om_fom_hdr *foh)
+{
+ DEBUGP(DNM, "OC=%s(%02x) INST=(%02x,%02x,%02x) ",
+ obj_class_name(foh->obj_class), foh->obj_class,
+ foh->obj_inst.bts_nr, foh->obj_inst.trx_nr,
+ foh->obj_inst.ts_nr);
+}
+
/* obtain the gsm_nm_state data structure for a given object instance */
static struct gsm_nm_state *
objclass2nmstate(struct gsm_bts *bts, u_int8_t obj_class,
@@ -598,20 +619,26 @@ objclass2nmstate(struct gsm_bts *bts, u_int8_t obj_class,
nm_state = &bts->nm_state;
break;
case NM_OC_RADIO_CARRIER:
- 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;
+ }
trx = gsm_bts_trx_num(bts, obj_inst->trx_nr);
nm_state = &trx->nm_state;
break;
case NM_OC_BASEB_TRANSC:
- 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;
+ }
trx = gsm_bts_trx_num(bts, obj_inst->trx_nr);
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;
+ }
trx = gsm_bts_trx_num(bts, obj_inst->trx_nr);
if (obj_inst->ts_nr >= TRX_NR_TS)
return NULL;
@@ -648,6 +675,17 @@ objclass2nmstate(struct gsm_bts *bts, u_int8_t obj_class,
return NULL;
nm_state = &bts->bs11.envabtse[obj_inst->trx_nr].nm_state;
break;
+ case NM_OC_GPRS_NSE:
+ nm_state = &bts->gprs.nse.nm_state;
+ break;
+ case NM_OC_GPRS_CELL:
+ nm_state = &bts->gprs.cell.nm_state;
+ break;
+ case NM_OC_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;
}
return nm_state;
}
@@ -665,20 +703,26 @@ objclass2obj(struct gsm_bts *bts, u_int8_t obj_class,
obj = bts;
break;
case NM_OC_RADIO_CARRIER:
- 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;
+ }
trx = gsm_bts_trx_num(bts, obj_inst->trx_nr);
obj = trx;
break;
case NM_OC_BASEB_TRANSC:
- 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;
+ }
trx = gsm_bts_trx_num(bts, obj_inst->trx_nr);
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;
+ }
trx = gsm_bts_trx_num(bts, obj_inst->trx_nr);
if (obj_inst->ts_nr >= TRX_NR_TS)
return NULL;
@@ -687,6 +731,17 @@ objclass2obj(struct gsm_bts *bts, u_int8_t obj_class,
case NM_OC_SITE_MANAGER:
obj = &bts->site_mgr;
break;
+ case NM_OC_GPRS_NSE:
+ obj = &bts->gprs.nse;
+ break;
+ case NM_OC_GPRS_CELL:
+ obj = &bts->gprs.cell;
+ break;
+ case NM_OC_GPRS_NSVC:
+ if (obj_inst->trx_nr > ARRAY_SIZE(bts->gprs.nsvc))
+ return NULL;
+ obj = &bts->gprs.nsvc[obj_inst->trx_nr];
+ break;
}
return obj;
}
@@ -701,6 +756,8 @@ static int update_admstate(struct gsm_bts *bts, u_int8_t obj_class,
int rc;
obj = objclass2obj(bts, obj_class, obj_inst);
+ if (!obj)
+ return -EINVAL;
nm_state = objclass2nmstate(bts, obj_class, obj_inst);
if (!nm_state)
return -1;
@@ -730,7 +787,7 @@ static int abis_nm_rx_statechg_rep(struct msgb *mb)
nm_state = objclass2nmstate(bts, foh->obj_class, &foh->obj_inst);
if (!nm_state) {
- DEBUGPC(DNM, "\n");
+ DEBUGPC(DNM, "unknown object class\n");
return -EINVAL;
}
@@ -751,7 +808,7 @@ static int abis_nm_rx_statechg_rep(struct msgb *mb)
}
if (TLVP_PRESENT(&tp, NM_ATT_ADM_STATE)) {
new_state.administrative = *TLVP_VAL(&tp, NM_ATT_ADM_STATE);
- DEBUGPC(DNM, "ADM=%02s ", nm_adm_name(new_state.administrative));
+ DEBUGPC(DNM, "ADM=%2s ", nm_adm_name(new_state.administrative));
}
DEBUGPC(DNM, "\n");
@@ -799,10 +856,7 @@ static int abis_nm_rcvmsg_report(struct msgb *mb)
struct abis_om_fom_hdr *foh = msgb_l3(mb);
u_int8_t mt = foh->msg_type;
- DEBUGP(DNM, "OC=%s(%02x) INST=(%02x,%02x,%02x) ",
- obj_class_name(foh->obj_class), foh->obj_class,
- foh->obj_inst.bts_nr, foh->obj_inst.trx_nr,
- foh->obj_inst.ts_nr);
+ debugp_foh(foh);
//nmh->cfg->report_cb(mb, foh);
@@ -860,7 +914,9 @@ static int abis_nm_rx_sw_act_req(struct msgb *mb)
int nack = 0;
int ret;
- DEBUGP(DNM, "Software Activate Request ");
+ debugp_foh(foh);
+
+ DEBUGPC(DNM, "SW Activate Request: ");
if (foh->obj_class >= 0xf0 && foh->obj_class <= 0xf3) {
DEBUGPC(DNM, "NACKing for GPRS obj_class 0x%02x\n", foh->obj_class);
@@ -965,10 +1021,7 @@ static int abis_nm_rcvmsg_fom(struct msgb *mb)
if (is_in_arr(mt, nacks, ARRAY_SIZE(nacks))) {
struct tlv_parsed tp;
- DEBUGP(DNM, "OC=%s(%02x) INST=(%02x,%02x,%02x) ",
- obj_class_name(foh->obj_class), foh->obj_class,
- foh->obj_inst.bts_nr, foh->obj_inst.trx_nr,
- foh->obj_inst.ts_nr);
+ debugp_foh(foh);
if (nack_names[mt])
DEBUGPC(DNM, "%s NACK ", nack_names[mt]);
@@ -983,7 +1036,7 @@ static int abis_nm_rcvmsg_fom(struct msgb *mb)
else
DEBUGPC(DNM, "\n");
- dispatch_signal(SS_NM, S_NM_NACK, (void*) ((long)mt));
+ dispatch_signal(SS_NM, S_NM_NACK, (void*) &mt);
return 0;
}
#if 0
@@ -1851,11 +1904,12 @@ int abis_nm_opstart(struct gsm_bts *bts, u_int8_t obj_class, u_int8_t i0, u_int8
struct abis_om_hdr *oh;
struct msgb *msg = nm_msgb_alloc();
- DEBUGP(DNM, "Sending OPSTART obj_class=0x%02x obj_inst=(0x%02x, 0x%02x, 0x%02x)\n",
- obj_class, i0, i1, i2);
oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
fill_om_fom_hdr(oh, 0, NM_MT_OPSTART, obj_class, i0, i1, i2);
+ debugp_foh((struct abis_om_fom_hdr *) oh->data);
+ DEBUGPC(DNM, "Sending OPSTART\n");
+
return abis_nm_sendmsg(bts, msg);
}
@@ -2474,7 +2528,9 @@ static int abis_nm_rx_ipacc(struct msgb *msg)
foh = (struct abis_om_fom_hdr *) (oh->data + 1 + idstrlen);
abis_nm_tlv_parse(&tp, foh->data, oh->length-sizeof(*foh));
- DEBUGP(DNM, "IPACCESS(0x%02x): ", foh->msg_type);
+ debugp_foh(foh);
+
+ DEBUGPC(DNM, "IPACCESS(0x%02x): ", foh->msg_type);
switch (foh->msg_type) {
case NM_MT_IPACC_RSL_CONNECT_ACK:
@@ -2487,6 +2543,9 @@ static int abis_nm_rx_ipacc(struct msgb *msg)
DEBUGPC(DNM, "PORT=%u ",
ntohs(*((u_int16_t *)
TLVP_VAL(&tp, NM_ATT_IPACC_DST_IP_PORT))));
+ if (TLVP_PRESENT(&tp, NM_ATT_IPACC_STREAM_ID))
+ DEBUGPC(DNM, "STREAM=0x%02x ",
+ *TLVP_VAL(&tp, NM_ATT_IPACC_STREAM_ID));
DEBUGPC(DNM, "\n");
break;
case NM_MT_IPACC_RSL_CONNECT_NACK:
@@ -2542,7 +2601,7 @@ 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, (void*) ((long)foh->msg_type));
+ dispatch_signal(SS_NM, S_NM_IPACC_NACK, &foh->msg_type);
break;
default:
break;
@@ -2597,8 +2656,167 @@ int abis_nm_ipaccess_set_nvattr(struct gsm_bts *bts, u_int8_t *attr,
attr_len);
}
+int abis_nm_ipaccess_rsl_connect(struct gsm_bts_trx *trx,
+ u_int32_t ip, u_int16_t port, u_int8_t stream)
+{
+ struct in_addr ia;
+ u_int8_t attr[] = { NM_ATT_IPACC_STREAM_ID, 0,
+ NM_ATT_IPACC_DST_IP_PORT, 0, 0,
+ NM_ATT_IPACC_DST_IP, 0, 0, 0, 0 };
+
+ int attr_len = sizeof(attr);
+
+ ia.s_addr = htonl(ip);
+ attr[1] = stream;
+ attr[3] = port >> 8;
+ attr[4] = port & 0xff;
+ *(u_int32_t *)(attr+6) = ia.s_addr;
+
+ /* if ip == 0, we use the default IP */
+ if (ip == 0)
+ attr_len -= 5;
+
+ DEBUGP(DNM, "ip.access RSL CONNECT IP=%s PORT=%u STREAM=0x%02x\n",
+ inet_ntoa(ia), port, stream);
+
+ return abis_nm_ipaccess_msg(trx->bts, NM_MT_IPACC_RSL_CONNECT,
+ NM_OC_BASEB_TRANSC, trx->bts->bts_nr,
+ trx->nr, 0xff, attr, attr_len);
+}
+
/* restart / reboot an ip.access nanoBTS */
int abis_nm_ipaccess_restart(struct gsm_bts *bts)
{
return __simple_cmd(bts, NM_MT_IPACC_RESTART);
}
+
+int abis_nm_ipaccess_set_attr(struct gsm_bts *bts, u_int8_t obj_class,
+ u_int8_t bts_nr, u_int8_t trx_nr, u_int8_t ts_nr,
+ u_int8_t *attr, u_int8_t attr_len)
+{
+ return abis_nm_ipaccess_msg(bts, NM_MT_IPACC_SET_ATTR,
+ obj_class, bts_nr, trx_nr, ts_nr,
+ 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->rf_locked = locked;
+ 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",
+ [NM_IPACC_TESTRES_NO_CHANS] = "NO CHANNELS",
+ [NM_IPACC_TESTRES_PARTIAL] = "PARTIAL",
+ [NM_IPACC_TESTRES_STOPPED] = "STOPPED",
+};
+
+const char *ipacc_testres_name(u_int8_t res)
+{
+ if (res < ARRAY_SIZE(ipacc_testres_names) &&
+ ipacc_testres_names[res])
+ return ipacc_testres_names[res];
+
+ return "unknown";
+}
+
+void ipac_parse_cgi(struct cell_global_id *cid, const u_int8_t *buf)
+{
+ cid->mcc = (buf[0] & 0xf) * 100;
+ cid->mcc += (buf[0] >> 4) * 10;
+ cid->mcc += (buf[1] & 0xf) * 1;
+
+ if (buf[1] >> 4 == 0xf) {
+ cid->mnc = (buf[2] & 0xf) * 10;
+ cid->mnc += (buf[2] >> 4) * 1;
+ } else {
+ cid->mnc = (buf[2] & 0xf) * 100;
+ cid->mnc += (buf[2] >> 4) * 10;
+ cid->mnc += (buf[1] >> 4) * 1;
+ }
+
+ cid->lac = ntohs(*((u_int16_t *)&buf[3]));
+ cid->ci = ntohs(*((u_int16_t *)&buf[5]));
+}
+
+/* parse BCCH information IEI from wire format to struct ipac_bcch_info */
+int ipac_parse_bcch_info(struct ipac_bcch_info *binf, u_int8_t *buf)
+{
+ u_int8_t *cur = buf;
+ u_int16_t len;
+
+ memset(binf, 0, sizeof(binf));
+
+ if (cur[0] != NM_IPAC_EIE_BCCH_INFO)
+ return -EINVAL;
+ cur++;
+
+ len = ntohs(*(u_int16_t *)cur);
+ cur += 2;
+
+ binf->info_type = ntohs(*(u_int16_t *)cur);
+ cur += 2;
+
+ if (binf->info_type & IPAC_BINF_FREQ_ERR_QUAL)
+ binf->freq_qual = *cur >> 2;
+
+ binf->arfcn = *cur++ & 3 << 8;
+ binf->arfcn |= *cur++;
+
+ if (binf->info_type & IPAC_BINF_RXLEV)
+ binf->rx_lev = *cur & 0x3f;
+ cur++;
+
+ if (binf->info_type & IPAC_BINF_RXQUAL)
+ binf->rx_qual = *cur & 0x7;
+ cur++;
+
+ if (binf->info_type & IPAC_BINF_FREQ_ERR_QUAL)
+ binf->freq_err = ntohs(*(u_int16_t *)cur);
+ cur += 2;
+
+ if (binf->info_type & IPAC_BINF_FRAME_OFFSET)
+ binf->frame_offset = ntohs(*(u_int16_t *)cur);
+ cur += 2;
+
+ if (binf->info_type & IPAC_BINF_FRAME_NR_OFFSET)
+ binf->frame_nr_offset = ntohl(*(u_int32_t *)cur);
+ cur += 4;
+
+ if (binf->info_type & IPAC_BINF_BSIC)
+ binf->bsic = *cur & 0x3f;
+ cur++;
+
+ ipac_parse_cgi(&binf->cgi, cur);
+ cur += 7;
+
+ if (binf->info_type & IPAC_BINF_NEIGH_BA_SI2) {
+ memcpy(binf->ba_list_si2, cur, sizeof(binf->ba_list_si2));
+ cur += sizeof(binf->ba_list_si2);
+ }
+
+ if (binf->info_type & IPAC_BINF_NEIGH_BA_SI2bis) {
+ memcpy(binf->ba_list_si2bis, cur,
+ sizeof(binf->ba_list_si2bis));
+ cur += sizeof(binf->ba_list_si2bis);
+ }
+
+ if (binf->info_type & IPAC_BINF_NEIGH_BA_SI2ter) {
+ memcpy(binf->ba_list_si2ter, cur,
+ sizeof(binf->ba_list_si2ter));
+ cur += sizeof(binf->ba_list_si2ter);
+ }
+
+ return 0;
+}
+
+
diff --git a/openbsc/src/abis_rsl.c b/openbsc/src/abis_rsl.c
index 800f2027f..219b01aa0 100644
--- a/openbsc/src/abis_rsl.c
+++ b/openbsc/src/abis_rsl.c
@@ -202,7 +202,9 @@ struct gsm_lchan *lchan_lookup(struct gsm_bts_trx *trx, u_int8_t chan_nr)
if (cbits == 0x01) {
lch_idx = 0; /* TCH/F */
- if (ts->pchan != GSM_PCHAN_TCH_F)
+ 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",
chan_nr, ts->pchan);
} else if ((cbits & 0x1e) == 0x02) {
@@ -237,6 +239,7 @@ struct gsm_lchan *lchan_lookup(struct gsm_bts_trx *trx, u_int8_t chan_nr)
return lchan;
}
+/* See Table 10.5.25 of GSM04.08 */
u_int8_t lchan2chan_nr(struct gsm_lchan *lchan)
{
struct gsm_bts_trx_ts *ts = lchan->ts;
@@ -244,6 +247,8 @@ u_int8_t lchan2chan_nr(struct gsm_lchan *lchan)
switch (ts->pchan) {
case GSM_PCHAN_TCH_F:
+ case GSM_PCHAN_PDCH:
+ case GSM_PCHAN_TCH_F_PDCH:
cbits = 0x01;
break;
case GSM_PCHAN_TCH_H:
@@ -483,6 +488,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)
+ DEBUGP(DRSL, "unsupported: rsl_mode == signalling, "
+ "but tch_mode != signalling\n");
+
switch (lchan->type) {
case GSM_LCHAN_SDCCH:
cm->chan_rt = RSL_CMOD_CRT_SDCCH;
@@ -643,6 +653,11 @@ int rsl_chan_mode_modify_req(struct gsm_lchan *lchan)
msgb_tlv_put(msg, RSL_IE_ENCR_INFO, rc, encr_info);
}
+ 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);
@@ -971,7 +986,7 @@ static int rsl_rx_meas_res(struct msgb *msg)
}
if (TLVP_PRESENT(&tp, RSL_IE_L3_INFO)) {
DEBUGPC(DMEAS, "L3\n");
- msg->l3h = TLVP_VAL(&tp, RSL_IE_L3_INFO);
+ msg->l3h = (u_int8_t *) TLVP_VAL(&tp, RSL_IE_L3_INFO);
return gsm0408_rcvmsg(msg, 0);
} else
DEBUGPC(DMEAS, "\n");
@@ -1016,6 +1031,18 @@ static int abis_rsl_rx_dchan(struct msgb *msg)
case RSL_MT_MODE_MODIFY_NACK:
DEBUGPC(DRSL, "CHANNEL MODE MODIFY NACK\n");
break;
+ case RSL_MT_IPAC_PDCH_ACT_ACK:
+ DEBUGPC(DRSL, "IPAC PDCH ACT ACK\n");
+ break;
+ case RSL_MT_IPAC_PDCH_ACT_NACK:
+ DEBUGPC(DRSL, "IPAC PDCH ACT NACK\n");
+ break;
+ case RSL_MT_IPAC_PDCH_DEACT_ACK:
+ DEBUGPC(DRSL, "IPAC PDCH DEACT ACK\n");
+ break;
+ case RSL_MT_IPAC_PDCH_DEACT_NACK:
+ DEBUGPC(DRSL, "IPAC PDCH DEACT NACK\n");
+ break;
case RSL_MT_PHY_CONTEXT_CONF:
case RSL_MT_PREPROC_MEAS_RES:
case RSL_MT_TALKER_DET:
@@ -1116,13 +1143,14 @@ static int rsl_rx_chan_rqd(struct msgb *msg)
/* determine channel type (SDCCH/TCH_F/TCH_H) based on
* request reference RA */
- lctype = get_ctype_by_chreq(bts, rqd_ref->ra);
- chreq_reason = get_reason_by_chreq(bts, rqd_ref->ra);
+ 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);
/* check availability / allocate channel */
lchan = lchan_alloc(bts, lctype);
if (!lchan) {
- fprintf(stderr, "CHAN RQD: no resources\n");
+ DEBUGP(DRSL, "CHAN RQD: no resources for %u 0x%x\n",
+ lctype, rqd_ref->ra);
/* FIXME: send some kind of reject ?!? */
return -ENOMEM;
}
@@ -1163,7 +1191,7 @@ static int rsl_rx_chan_rqd(struct msgb *msg)
/* 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);
@@ -1277,6 +1305,7 @@ static int abis_rsl_rx_rll(struct msgb *msg)
case RSL_MT_EST_IND:
DEBUGPC(DRLL, "ESTABLISH INDICATION\n");
/* lchan is established, stop T3101 */
+ msg->lchan->sapis[rllh->link_id & 0x7] = LCHAN_SAPI_MS;
bsc_del_timer(&msg->lchan->T3101);
if (msgb_l2len(msg) >
sizeof(struct abis_rsl_common_hdr) + sizeof(*rllh) &&
@@ -1287,12 +1316,14 @@ static int abis_rsl_rx_rll(struct msgb *msg)
break;
case RSL_MT_EST_CONF:
DEBUGPC(DRLL, "ESTABLISH CONFIRM\n");
+ msg->lchan->sapis[rllh->link_id & 0x7] = LCHAN_SAPI_NET;
rll_indication(msg->lchan, rllh->link_id,
BSC_RLLR_IND_EST_CONF);
break;
case RSL_MT_REL_IND:
/* BTS informs us of having received DISC from MS */
DEBUGPC(DRLL, "RELEASE INDICATION\n");
+ msg->lchan->sapis[rllh->link_id & 0x7] = LCHAN_SAPI_UNUSED;
rll_indication(msg->lchan, rllh->link_id,
BSC_RLLR_IND_REL_IND);
/* we can now releae the channel on the BTS/Abis side */
@@ -1304,6 +1335,7 @@ static int abis_rsl_rx_rll(struct msgb *msg)
/* BTS informs us of having received UA from MS,
* in response to DISC that we've sent earlier */
DEBUGPC(DRLL, "RELEASE CONFIRMATION\n");
+ msg->lchan->sapis[rllh->link_id & 0x7] = LCHAN_SAPI_UNUSED;
/* we can now releae the channel on the BTS/Abis side */
/* FIXME: officially we need to start T3111 and wait for
* some grace period */
@@ -1340,14 +1372,14 @@ static u_int8_t ipa_smod_s_for_tch_mode(u_int8_t tch_mode)
}
/* ip.access specific RSL extensions */
-int rsl_ipacc_bind(struct gsm_lchan *lchan)
+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_BIND);
+ init_dchan_hdr(dh, RSL_MT_IPAC_CRCX);
dh->c.msg_discr = ABIS_RSL_MDISC_IPACCESS;
dh->chan_nr = lchan2chan_nr(lchan);
@@ -1364,7 +1396,7 @@ int rsl_ipacc_bind(struct gsm_lchan *lchan)
return abis_rsl_sendmsg(msg);
}
-int rsl_ipacc_connect(struct gsm_lchan *lchan, u_int32_t ip, u_int16_t port,
+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)
{
struct msgb *msg = rsl_msgb_alloc();
@@ -1374,7 +1406,7 @@ int rsl_ipacc_connect(struct gsm_lchan *lchan, u_int32_t ip, u_int16_t port,
struct in_addr ia;
dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh));
- init_dchan_hdr(dh, RSL_MT_IPAC_CONNECT);
+ init_dchan_hdr(dh, RSL_MT_IPAC_MDCX);
dh->c.msg_discr = ABIS_RSL_MDISC_IPACCESS;
dh->chan_nr = lchan2chan_nr(lchan);
@@ -1382,7 +1414,7 @@ int rsl_ipacc_connect(struct gsm_lchan *lchan, u_int32_t ip, u_int16_t port,
speech_mode = 0x00 | ipa_smod_s_for_tch_mode(lchan->tch_mode);
ia.s_addr = htonl(ip);
- DEBUGP(DRSL, "channel=%s chan_nr=0x%02x IPAC_CONNECT "
+ 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);
@@ -1414,7 +1446,25 @@ int rsl_ipacc_connect(struct gsm_lchan *lchan, u_int32_t ip, u_int16_t port,
return abis_rsl_sendmsg(msg);
}
-static int abis_rsl_rx_ipacc_bindack(struct msgb *msg)
+int rsl_ipacc_pdch_activate(struct gsm_lchan *lchan)
+{
+ struct msgb *msg = rsl_msgb_alloc();
+ struct abis_rsl_dchan_hdr *dh;
+
+ dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh));
+ init_dchan_hdr(dh, RSL_MT_IPAC_PDCH_ACT);
+ 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);
+
+ msg->trx = lchan->ts->trx;
+
+ return abis_rsl_sendmsg(msg);
+}
+
+static int abis_rsl_rx_ipacc_crcx_ack(struct msgb *msg)
{
struct abis_rsl_dchan_hdr *dh = msgb_l2(msg);
struct tlv_parsed tv;
@@ -1452,12 +1502,12 @@ static int abis_rsl_rx_ipacc_bindack(struct msgb *msg)
ts->abis_ip.bound_port = ntohs(port);
ts->abis_ip.conn_id = ntohs(attr_f8);
- dispatch_signal(SS_ABISIP, S_ABISIP_BIND_ACK, msg->lchan);
+ dispatch_signal(SS_ABISIP, S_ABISIP_CRCX_ACK, msg->lchan);
return 0;
}
-static int abis_rsl_rx_ipacc_disc_ind(struct msgb *msg)
+static int abis_rsl_rx_ipacc_dlcx_ind(struct msgb *msg)
{
struct abis_rsl_dchan_hdr *dh = msgb_l2(msg);
struct tlv_parsed tv;
@@ -1468,7 +1518,7 @@ static int abis_rsl_rx_ipacc_disc_ind(struct msgb *msg)
print_rsl_cause(TLVP_VAL(&tv, RSL_IE_CAUSE),
TLVP_LEN(&tv, RSL_IE_CAUSE));
- dispatch_signal(SS_ABISIP, S_ABISIP_DISC_IND, msg->lchan);
+ dispatch_signal(SS_ABISIP, S_ABISIP_DLCX_IND, msg->lchan);
return 0;
}
@@ -1483,27 +1533,27 @@ static int abis_rsl_rx_ipacc(struct msgb *msg)
gsm_ts_name(msg->lchan->ts), rllh->chan_nr);
switch (rllh->c.msg_type) {
- case RSL_MT_IPAC_BIND_ACK:
- DEBUGPC(DRSL, "IPAC_BIND_ACK ");
- rc = abis_rsl_rx_ipacc_bindack(msg);
+ case RSL_MT_IPAC_CRCX_ACK:
+ DEBUGPC(DRSL, "IPAC_CRCX_ACK ");
+ rc = abis_rsl_rx_ipacc_crcx_ack(msg);
break;
- case RSL_MT_IPAC_BIND_NACK:
+ case RSL_MT_IPAC_CRCX_NACK:
/* somehow the BTS was unable to bind the lchan to its local
* port?!? */
- DEBUGPC(DRSL, "IPAC_BIND_NACK ");
+ DEBUGPC(DRSL, "IPAC_CRCX_NACK ");
break;
- case RSL_MT_IPAC_CONNECT_ACK:
+ case RSL_MT_IPAC_MDCX_ACK:
/* the BTS tells us that a connect operation was successful */
- DEBUGPC(DRSL, "IPAC_CONNECT_ACK ");
+ DEBUGPC(DRSL, "IPAC_MDCX_ACK ");
break;
- case RSL_MT_IPAC_CONNECT_NACK:
+ case RSL_MT_IPAC_MDCX_NACK:
/* somehow the BTS was unable to connect the lchan to a remote
* port */
- DEBUGPC(DRSL, "IPAC_CONNECT_NACK ");
+ DEBUGPC(DRSL, "IPAC_MDCX_NACK ");
break;
- case RSL_MT_IPAC_DISCONNECT_IND:
- DEBUGPC(DRSL, "IPAC_DISCONNECT_IND ");
- rc = abis_rsl_rx_ipacc_disc_ind(msg);
+ case RSL_MT_IPAC_DLCX_IND:
+ DEBUGPC(DRSL, "IPAC_DLCX_IND ");
+ rc = abis_rsl_rx_ipacc_dlcx_ind(msg);
break;
default:
DEBUGPC(DRSL, "Unknown ip.access msg_type 0x%02x", rllh->c.msg_type);
diff --git a/openbsc/src/bsc_init.c b/openbsc/src/bsc_init.c
index 3d8b1b158..153e024e4 100644
--- a/openbsc/src/bsc_init.c
+++ b/openbsc/src/bsc_init.c
@@ -338,11 +338,6 @@ static unsigned char nanobts_attr_radio[] = {
NM_ATT_ARFCN_LIST, 0x00, 0x02, HARDCODED_ARFCN >> 8, HARDCODED_ARFCN & 0xff,
};
-static unsigned char nanobts_attr_e0[] = {
- NM_ATT_IPACC_STREAM_ID, 0x00,
- NM_ATT_IPACC_DST_IP_PORT, 0x0b, 0xbb, /* TCP PORT for RSL */
-};
-
/* Callback function to be called whenever we get a GSM 12.21 state change event */
int nm_state_event(enum nm_evt evt, u_int8_t obj_class, void *obj,
struct gsm_nm_state *old_state, struct gsm_nm_state *new_state)
@@ -351,51 +346,57 @@ int nm_state_event(enum nm_evt evt, u_int8_t obj_class, void *obj,
struct gsm_bts_trx *trx;
struct gsm_bts_trx_ts *ts;
- /* This is currently only required on nanoBTS */
+ /* This event-driven BTS setup is currently only required on nanoBTS */
- switch (evt) {
- case EVT_STATECHG_OPER:
- switch (obj_class) {
- case NM_OC_SITE_MANAGER:
- bts = container_of(obj, struct gsm_bts, site_mgr);
- if (old_state->operational != 2 && new_state->operational == 2) {
- abis_nm_opstart(bts, NM_OC_SITE_MANAGER, 0xff, 0xff, 0xff);
- }
- break;
- case NM_OC_BTS:
- bts = obj;
- if (new_state->availability == NM_AVSTATE_DEPENDENCY) {
- patch_nm_tables(bts);
- abis_nm_set_bts_attr(bts, nanobts_attr_bts,
- sizeof(nanobts_attr_bts));
- abis_nm_opstart(bts, NM_OC_BTS,
- bts->bts_nr, 0xff, 0xff);
- abis_nm_chg_adm_state(bts, NM_OC_BTS,
- bts->bts_nr, 0xff, 0xff,
- NM_STATE_UNLOCKED);
- }
- break;
- case NM_OC_CHANNEL:
- ts = obj;
- trx = ts->trx;
- if (new_state->availability == NM_AVSTATE_DEPENDENCY) {
- if (ts->nr == 0 && trx == trx->bts->c0)
- abis_nm_set_channel_attr(ts, NM_CHANC_BCCHComb);
- else
- abis_nm_set_channel_attr(ts, NM_CHANC_TCHFull);
- abis_nm_opstart(trx->bts, NM_OC_CHANNEL,
- trx->bts->bts_nr, trx->nr, ts->nr);
- abis_nm_chg_adm_state(trx->bts, NM_OC_CHANNEL,
- trx->bts->bts_nr, trx->nr, ts->nr,
- NM_STATE_UNLOCKED);
- }
- break;
- default:
- break;
+ /* EVT_STATECHG_ADM is called after we call chg_adm_state() and would create
+ * endless loop */
+ if (evt != EVT_STATECHG_OPER)
+ return 0;
+
+ 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)
+ abis_nm_opstart(bts, obj_class, 0xff, 0xff, 0xff);
+ break;
+ case NM_OC_BTS:
+ bts = obj;
+ if (new_state->availability == NM_AVSTATE_DEPENDENCY) {
+ patch_nm_tables(bts);
+ abis_nm_set_bts_attr(bts, nanobts_attr_bts,
+ sizeof(nanobts_attr_bts));
+ abis_nm_chg_adm_state(bts, obj_class,
+ bts->bts_nr, 0xff, 0xff,
+ NM_STATE_UNLOCKED);
+ abis_nm_opstart(bts, obj_class,
+ bts->bts_nr, 0xff, 0xff);
+ }
+ break;
+ case NM_OC_CHANNEL:
+ ts = obj;
+ trx = ts->trx;
+ if (new_state->operational == 1 &&
+ new_state->availability == NM_AVSTATE_DEPENDENCY) {
+ patch_nm_tables(trx->bts);
+ enum abis_nm_chan_comb ccomb =
+ abis_nm_chcomb4pchan(ts->pchan);
+ abis_nm_set_channel_attr(ts, ccomb);
+ abis_nm_chg_adm_state(trx->bts, obj_class,
+ trx->bts->bts_nr, trx->nr, ts->nr,
+ NM_STATE_UNLOCKED);
+ abis_nm_opstart(trx->bts, obj_class,
+ trx->bts->bts_nr, trx->nr, ts->nr);
}
break;
+ case NM_OC_RADIO_CARRIER:
+ trx = obj;
+ 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;
default:
- //DEBUGP(DMM, "Unhandled state change in %s:%d\n", __func__, __LINE__);
break;
}
return 0;
@@ -405,36 +406,51 @@ int nm_state_event(enum nm_evt evt, u_int8_t obj_class, void *obj,
static int sw_activ_rep(struct msgb *mb)
{
struct abis_om_fom_hdr *foh = msgb_l3(mb);
- struct gsm_bts_trx *trx = mb->trx;
+ struct gsm_bts *bts = mb->trx->bts;
+ struct gsm_bts_trx *trx = gsm_bts_trx_num(bts, foh->obj_inst.trx_nr);
+
switch (foh->obj_class) {
case NM_OC_BASEB_TRANSC:
- /* TRX software is active, tell it to initiate RSL Link */
- abis_nm_ipaccess_msg(trx->bts, 0xe0, NM_OC_BASEB_TRANSC,
- trx->bts->bts_nr, trx->nr, 0xff,
- nanobts_attr_e0, sizeof(nanobts_attr_e0));
- abis_nm_opstart(trx->bts, 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);
- abis_nm_chg_adm_state(trx->bts, NM_OC_BASEB_TRANSC,
- trx->bts->bts_nr, trx->nr, 0xff,
- NM_STATE_UNLOCKED);
+ /* 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:
- patch_nm_tables(trx->bts);
+ 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->rf_locked ?
+ NM_STATE_LOCKED : NM_STATE_UNLOCKED;
+ /* 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_opstart(trx->bts, NM_OC_RADIO_CARRIER,
- trx->bts->bts_nr, trx->nr, 0xff);
- abis_nm_chg_adm_state(trx->bts, NM_OC_RADIO_CARRIER,
+ sizeof(nanobts_attr_radio));
+ abis_nm_chg_adm_state(trx->bts, foh->obj_class,
trx->bts->bts_nr, trx->nr, 0xff,
- NM_STATE_UNLOCKED);
+ rc_state);
+ abis_nm_opstart(trx->bts, foh->obj_class, trx->bts->bts_nr,
+ trx->nr, 0xff);
break;
+ }
}
return 0;
}
/* Callback function for NACK on the OML NM */
-static int oml_msg_nack(int mt)
+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. "
@@ -449,11 +465,14 @@ static int oml_msg_nack(int mt)
static int nm_sig_cb(unsigned int subsys, unsigned int signal,
void *handler_data, void *signal_data)
{
+ u_int8_t *msg_type;
+
switch (signal) {
case S_NM_SW_ACTIV_REP:
return sw_activ_rep(signal_data);
case S_NM_NACK:
- return oml_msg_nack((int)signal_data);
+ msg_type = signal_data;
+ return oml_msg_nack(*msg_type);
default:
break;
}
@@ -527,7 +546,21 @@ static void nm_reconfig_trx(struct gsm_bts_trx *trx)
}
break;
case GSM_BTS_TYPE_NANOBTS:
- trx->nominal_power = 20;
+ switch (trx->bts->band) {
+ case GSM_BAND_850:
+ case GSM_BAND_900:
+ trx->nominal_power = 20;
+ break;
+ case GSM_BAND_1800:
+ case GSM_BAND_1900:
+ trx->nominal_power = 23;
+ break;
+ default:
+ fprintf(stderr, "Unsupported nanoBTS GSM band %s\n",
+ gsm_band_name(trx->bts->band));
+ break;
+ }
+ break;
default:
break;
}
@@ -870,9 +903,6 @@ static void patch_nm_tables(struct gsm_bts *bts)
bs11_attr_radio[2] &= 0xf0;
bs11_attr_radio[2] |= arfcn_high;
bs11_attr_radio[3] = arfcn_low;
- nanobts_attr_radio[5] &= 0xf0;
- nanobts_attr_radio[5] |= arfcn_high;
- nanobts_attr_radio[6] = arfcn_low;
/* patch BSIC */
bs11_attr_bts[1] = bts->bsic;
@@ -933,6 +963,10 @@ static void patch_si_tables(struct gsm_bts *bts)
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;
diff --git a/openbsc/src/bsc_mgcp.c b/openbsc/src/bsc_mgcp.c
new file mode 100644
index 000000000..6d5e6b154
--- /dev/null
+++ b/openbsc/src/bsc_mgcp.c
@@ -0,0 +1,1172 @@
+/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
+
+/*
+ * (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * (C) 2009 by on-waves.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.
+ *
+ */
+
+#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 *local_ip = NULL;
+static const char *source_addr = "0.0.0.0";
+static struct bsc_fd bfd;
+static unsigned int number_endpoints = 0;
+static const char *bts_ip = NULL;
+static struct in_addr bts_in;
+static int first_request = 1;
+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 *config_file = "mgcp.cfg";
+
+/* used by msgb and mgcp */
+void *tall_bsc_ctx = NULL;
+
+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 */
+ 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;
+
+ /* in network byte order */
+ int rtp, 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;
+ void (*handle_request) (struct msgb *msg, struct sockaddr_in *source);
+ char *debug_name;
+};
+
+#define MGCP_REQUEST(NAME, REQ, DEBUG_NAME) \
+ { .name = NAME, .handle_request = REQ, .debug_name = DEBUG_NAME },
+
+static void handle_audit_endpoint(struct msgb *msg, struct sockaddr_in *source);
+static void handle_create_con(struct msgb *msg, struct sockaddr_in *source);
+static void handle_delete_con(struct msgb *msg, struct sockaddr_in *source);
+static void handle_modify_con(struct msgb *msg, struct sockaddr_in *source);
+
+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 _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) {
+ DEBUGP(DMGCP, "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)
+ 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
+ ? 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) {
+ /* it was the BTS... */
+ if (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;
+ }
+
+ DEBUGP(DMGCP, "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 _send(fd->fd, &endp->remote,
+ proto == PROTO_RTP ? endp->rtp : endp->rtcp,
+ buf, rc);
+ } else {
+ return _send(fd->fd, &bts_in,
+ 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)
+ 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)
+{
+ /* set to zero until we get the info */
+ memset(&endp->remote, 0, sizeof(endp->remote));
+
+ if (create_bind(&endp->local_rtp, endp->rtp_port) != 0) {
+ DEBUGP(DMGCP, "Failed to create RTP port: %d on 0x%x\n",
+ endp->rtp_port, ENDPOINT_NUMBER(endp));
+ goto cleanup0;
+ }
+
+ if (create_bind(&endp->local_rtcp, endp->rtp_port + 1) != 0) {
+ DEBUGP(DMGCP, "Failed to create RTCP port: %d on 0x%x\n",
+ 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) {
+ DEBUGP(DMGCP, "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) {
+ DEBUGP(DMGCP, "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 void send_response_with_data(int code, const char *msg, const char *trans,
+ const char *data, struct sockaddr_in *source)
+{
+ char buf[4096];
+ int len;
+
+ if (data) {
+ len = snprintf(buf, sizeof(buf), "%d %s\n%s", code, trans, data);
+ } else {
+ len = snprintf(buf, sizeof(buf), "%d %s\n", code, trans);
+ }
+ DEBUGP(DMGCP, "Sending response: code: %d for '%s'\n", code, msg);
+
+ sendto(bfd.fd, buf, len, 0, (struct sockaddr *)source, sizeof(*source));
+}
+
+static void send_response(int code, const char *msg, const char *trans, struct sockaddr_in *source)
+{
+ send_response_with_data(code, msg, trans, NULL, source);
+}
+
+static void send_with_sdp(struct mgcp_endpoint *endp, const char *msg, const char *trans_id, struct sockaddr_in *source)
+{
+ 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, source);
+}
+
+/* send a static record */
+static void send_rsip(struct sockaddr_in *source)
+{
+ char reset[4096];
+ int len, rc;
+
+ len = snprintf(reset, sizeof(reset) - 1,
+ "RSIP %u *@mgw MGCP 1.0\n"
+ "RM: restart\n", generate_transaction_id());
+ rc = sendto(bfd.fd, reset, len, 0, (struct sockaddr *) source, sizeof(*source));
+ if (rc < 0) {
+ DEBUGP(DMGCP, "Failed to send RSIP: %d\n", rc);
+ }
+}
+
+/*
+ * handle incoming messages:
+ * - this can be a command (four letters, space, transaction id)
+ * - or a response (three numbers, space, transaction id)
+ */
+static void handle_message(struct msgb *msg, struct sockaddr_in *source)
+{
+ int code;
+
+ if (msg->len < 4) {
+ DEBUGP(DMGCP, "mgs too short: %d\n", msg->len);
+ return;
+ }
+
+ /* attempt to treat it as a response */
+ if (sscanf((const char *)&msg->data[0], "%3d %*s", &code) == 1) {
+ DEBUGP(DMGCP, "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;
+ mgcp_requests[i].handle_request(msg, source);
+ }
+ if (!handled) {
+ DEBUGP(DMGCP, "MSG with type: '%.4s' not handled\n", &msg->data[0]);
+ }
+ }
+}
+
+/* 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) {
+ DEBUGP(DMGCP, "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) {
+ DEBUGP(DMGCP, "Not enough space in ptr\n");
+ return -1;
+ }
+
+ found = find_msg_pointers(msg, ptr, size);
+
+ if (found < 3) {
+ DEBUGP(DMGCP, "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) {
+ DEBUGP(DMGCP, "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) {
+ DEBUGP(DMGCP, "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) {
+ DEBUGP(DMGCP, "ConnectionIdentifiers do not match on 0x%x. %d != %s\n",
+ ENDPOINT_NUMBER(endp), endp->ci, ci);
+ return -1;
+ }
+
+ return 0;
+}
+
+static void handle_audit_endpoint(struct msgb *msg, struct sockaddr_in *source)
+{
+ 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, source);
+}
+
+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 {
+ DEBUGP(DMGCP, "Unknown connection mode: '%s'\n", msg);
+ ret = -1;
+ }
+
+ return ret;
+}
+
+static void handle_create_con(struct msgb *msg, struct sockaddr_in *source)
+{
+ 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, source);
+
+ if (endp->ci != CI_UNUSED) {
+ DEBUGP(DMGCP, "Endpoint is already used. 0x%x\n", ENDPOINT_NUMBER(endp));
+ return send_response(500, "CRCX", trans_id, source);
+ }
+
+ /* 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:
+ DEBUGP(DMGCP, "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->rtp = endp->rtcp = endp->bts_rtp = endp->bts_rtcp = 0;
+
+ /* 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;
+
+ DEBUGP(DMGCP, "Creating endpoint on: 0x%x CI: %u port: %u\n",
+ ENDPOINT_NUMBER(endp), endp->ci, endp->rtp_port);
+ return send_with_sdp(endp, "CRCX", trans_id, source);
+error:
+ DEBUGP(DMGCP, "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, source);
+
+error2:
+ DEBUGP(DMGCP, "Resource error on 0x%x\n", ENDPOINT_NUMBER(endp));
+ return send_response(error_code, "CRCX", trans_id, source);
+}
+
+static void handle_modify_con(struct msgb *msg, struct sockaddr_in *source)
+{
+ 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, source);
+
+ if (endp->ci == CI_UNUSED) {
+ DEBUGP(DMGCP, "Endpoint is not holding a connection. 0x%x\n", ENDPOINT_NUMBER(endp));
+ return send_response(error_code, "MDCX", trans_id, source);
+ }
+
+ 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->rtp = htons(port);
+ endp->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:
+ DEBUGP(DMGCP, "Unhandled option: '%c'/%d on 0x%x\n",
+ msg->l3h[line_start], msg->l3h[line_start],
+ ENDPOINT_NUMBER(endp));
+ break;
+ }
+ MSG_TOKENIZE_END
+
+ /* modify */
+ DEBUGP(DMGCP, "Modified endpoint on: 0x%x Server: %s:%u\n",
+ ENDPOINT_NUMBER(endp), inet_ntoa(endp->remote), endp->rtp);
+ return send_with_sdp(endp, "MDCX", trans_id, source);
+
+error:
+ DEBUGP(DMGCP, "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, source);
+
+error3:
+ return send_response(error_code, "MDCX", trans_id, source);
+}
+
+static void handle_delete_con(struct msgb *msg, struct sockaddr_in *source)
+{
+ 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, source);
+
+ if (endp->ci == CI_UNUSED) {
+ DEBUGP(DMGCP, "Endpoint is not used. 0x%x\n", ENDPOINT_NUMBER(endp));
+ return send_response(error_code, "DLCX", trans_id, source);
+ }
+
+ 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:
+ DEBUGP(DMGCP, "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 */
+ DEBUGP(DMGCP, "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->rtp = endp->rtcp = endp->bts_rtp = endp->bts_rtcp = 0;
+
+ return send_response(250, "DLCX", trans_id, source);
+
+error:
+ DEBUGP(DMGCP, "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, source);
+
+error3:
+ return send_response(error_code, "DLCX", trans_id, source);
+}
+
+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;
+
+ 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;
+ send_rsip(&addr);
+ return 0;
+ }
+
+ /* handle message now */
+ msg->l2h = msgb_put(msg, rc);
+ handle_message(msg, &addr);
+ msgb_reset(msg);
+ return 0;
+}
+
+/*
+ * 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);
+ 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);
+
+ 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->rtp), ntohs(endp->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;
+}
+
+int bsc_vty_init(struct gsm_network *dummy)
+{
+ cmd_init(1);
+ vty_init();
+
+ 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);
+ return 0;
+}
+
+int main(int argc, char** argv)
+{
+ struct gsm_network dummy_network;
+ struct sockaddr_in addr;
+ int on = 1, i, rc;
+
+ tall_bsc_ctx = talloc_named_const(NULL, 1, "mgcp-callagent");
+ handle_options(argc, argv);
+
+ telnet_init(&dummy_network, 4243);
+ 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, "Need to specify the BTS ip address for RTP handling.\n");
+ return -1;
+ }
+
+ 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;
+ }
+
+ /* initialize the socket */
+ 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;
+ }
+
+ /* initialisation */
+ srand(time(NULL));
+
+ /* 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)
+ return -1;
+ }
+ }
+
+ /* main loop */
+ while (1) {
+ bsc_select_main(0);
+ }
+
+
+ return 0;
+}
diff --git a/openbsc/src/chan_alloc.c b/openbsc/src/chan_alloc.c
index 48c728c09..7ba679c87 100644
--- a/openbsc/src/chan_alloc.c
+++ b/openbsc/src/chan_alloc.c
@@ -212,6 +212,12 @@ struct gsm_lchan *lchan_alloc(struct gsm_bts *bts, enum gsm_chan_t type)
lchan->type = type;
lchan->use_count = 0;
+ /* clear sapis */
+ memset(lchan->sapis, 0, ARRAY_SIZE(lchan->sapis));
+
+ /* clear multi rate config */
+ memset(&lchan->mr_conf, 0, sizeof(lchan->mr_conf));
+
/* Configure the time and start it so it will be closed */
lchan->release_timer.cb = auto_release_channel;
lchan->release_timer.data = lchan;
@@ -227,7 +233,7 @@ void lchan_free(struct gsm_lchan *lchan)
lchan->type = GSM_LCHAN_NONE;
if (lchan->subscr) {
subscr_put(lchan->subscr);
- lchan->subscr = 0;
+ lchan->subscr = NULL;
}
/* We might kill an active channel... */
@@ -304,5 +310,5 @@ struct gsm_lchan *lchan_for_subscr(struct gsm_subscriber *subscr)
return lchan;
}
- return 0;
+ return NULL;
}
diff --git a/openbsc/src/db.c b/openbsc/src/db.c
index 270d4d90b..369505a2c 100644
--- a/openbsc/src/db.c
+++ b/openbsc/src/db.c
@@ -439,9 +439,11 @@ int db_sync_equipment(struct gsm_equipment *equip)
{
dbi_result result;
unsigned char *cm2, *cm3;
+ u_int8_t classmark1;
+ memcpy(&classmark1, &equip->classmark1, sizeof(classmark1));
printf("DB: Sync Equipment IMEI=%s, classmark1=%02x",
- equip->imei, equip->classmark1);
+ equip->imei, classmark1);
if (equip->classmark2_len)
printf(", classmark2=%s",
hexdump(equip->classmark2, equip->classmark2_len));
@@ -462,7 +464,7 @@ int db_sync_equipment(struct gsm_equipment *equip)
"classmark2 = %s, "
"classmark3 = %s "
"WHERE imei = '%s' ",
- equip->classmark1, cm2, cm3, equip->imei);
+ classmark1, cm2, cm3, equip->imei);
free(cm2);
free(cm3);
@@ -590,7 +592,7 @@ int db_subscriber_alloc_token(struct gsm_subscriber* subscriber, u_int32_t* toke
}
int db_subscriber_assoc_imei(struct gsm_subscriber* subscriber, char imei[GSM_IMEI_LENGTH]) {
- u_int64_t equipment_id, watch_id;
+ unsigned long long equipment_id, watch_id;
dbi_result result;
strncpy(subscriber->equipment.imei, imei,
@@ -853,7 +855,7 @@ int db_apdu_blob_store(struct gsm_subscriber *subscr,
u_int8_t *apdu)
{
dbi_result result;
- char *q_apdu;
+ unsigned char *q_apdu;
dbi_conn_quote_binary_copy(conn, apdu, len, &q_apdu);
diff --git a/openbsc/src/debug.c b/openbsc/src/debug.c
index fa903af98..5dc2e0ff7 100644
--- a/openbsc/src/debug.c
+++ b/openbsc/src/debug.c
@@ -57,6 +57,9 @@ static const struct debug_info debug_info[] = {
DEBUG_CATEGORY(DMIB, "DMIB", "", "")
DEBUG_CATEGORY(DMUX, "DMUX", "", "")
DEBUG_CATEGORY(DMEAS, "DMEAS", "", "")
+ DEBUG_CATEGORY(DSCCP, "DSCCP", "", "")
+ DEBUG_CATEGORY(DMSC, "DMSC", "", "")
+ DEBUG_CATEGORY(DMGCP, "DMGCP", "", "")
};
static int use_color = 1;
diff --git a/openbsc/src/e1_config.c b/openbsc/src/e1_config.c
index 16fd0d999..62bacf2ca 100644
--- a/openbsc/src/e1_config.c
+++ b/openbsc/src/e1_config.c
@@ -219,7 +219,7 @@ int ia_config_connect(struct gsm_bts *bts, struct sockaddr_in *sin)
sign_ts = &line->ts[1-1];
rsl_ts = &line->ts[2-1];
oml_link = e1inp_sign_link_create(sign_ts, E1INP_SIGN_OML,
- bts->c0, 0, 0xff);
+ bts->c0, 0xff, 0);
rsl_link = e1inp_sign_link_create(rsl_ts, E1INP_SIGN_RSL,
bts->c0, 0, 0);
diff --git a/openbsc/src/gsm_04_08.c b/openbsc/src/gsm_04_08.c
index ba4d07e2b..9eff60f8e 100644
--- a/openbsc/src/gsm_04_08.c
+++ b/openbsc/src/gsm_04_08.c
@@ -48,6 +48,7 @@
#include <openbsc/rtp_proxy.h>
#include <openbsc/talloc.h>
#include <openbsc/transaction.h>
+#include <openbsc/ussd.h>
#define GSM_MAX_FACILITY 128
#define GSM_MAX_SSVERSION 128
@@ -1086,34 +1087,6 @@ static int mm_rx_loc_upd_req(struct msgb *msg)
return gsm0408_authorize(lchan, msg);
}
-/* 9.1.5 Channel mode modify: Modify the mode on the MS side */
-int gsm48_tx_chan_mode_modify(struct gsm_lchan *lchan, u_int8_t mode)
-{
- struct msgb *msg = gsm48_msgb_alloc();
- struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
- struct gsm48_chan_mode_modify *cmm =
- (struct gsm48_chan_mode_modify *) msgb_put(msg, sizeof(*cmm));
- u_int16_t arfcn = lchan->ts->trx->arfcn & 0x3ff;
-
- DEBUGP(DRR, "-> CHANNEL MODE MODIFY mode=0x%02x\n", mode);
-
- lchan->tch_mode = mode;
- msg->lchan = lchan;
- gh->proto_discr = GSM48_PDISC_RR;
- gh->msg_type = GSM48_MT_RR_CHAN_MODE_MODIF;
-
- /* fill the channel information element, this code
- * should probably be shared with rsl_rx_chan_rqd() */
- cmm->chan_desc.chan_nr = lchan2chan_nr(lchan);
- cmm->chan_desc.h0.tsc = lchan->ts->trx->bts->tsc;
- cmm->chan_desc.h0.h = 0;
- cmm->chan_desc.h0.arfcn_high = arfcn >> 8;
- cmm->chan_desc.h0.arfcn_low = arfcn & 0xff;
- cmm->mode = mode;
-
- return gsm48_sendmsg(msg, NULL);
-}
-
#if 0
static u_int8_t to_bcd8(u_int8_t val)
{
@@ -1128,9 +1101,7 @@ int gsm48_tx_mm_info(struct gsm_lchan *lchan)
struct gsm48_hdr *gh;
struct gsm_network *net = lchan->ts->trx->bts->network;
u_int8_t *ptr8;
- u_int16_t *ptr16;
int name_len, name_pad;
- int i;
#if 0
time_t cur_t;
struct tm* cur_time;
@@ -1458,7 +1429,7 @@ static int gsm0408_rcv_mm(struct msgb *msg)
}
/* Receive a PAGING RESPONSE message from the MS */
-static int gsm48_rr_rx_pag_resp(struct msgb *msg)
+static int gsm48_rx_rr_pag_resp(struct msgb *msg)
{
struct gsm_bts *bts = msg->lchan->ts->trx->bts;
struct gsm48_hdr *gh = msgb_l3(msg);
@@ -1613,14 +1584,10 @@ static int gsm0408_rcv_rr(struct msgb *msg)
DEBUGP(DRR, "GRPS SUSPEND REQUEST\n");
break;
case GSM48_MT_RR_PAG_RESP:
- rc = gsm48_rr_rx_pag_resp(msg);
+ rc = gsm48_rx_rr_pag_resp(msg);
break;
case GSM48_MT_RR_CHAN_MODE_MODIF_ACK:
- DEBUGP(DRR, "CHANNEL MODE MODIFY ACK\n");
- /* We've successfully modified the MS side of the channel,
- * now go on to modify the BTS side of the channel */
- msg->lchan->rsl_cmode = RSL_CMOD_SPD_SPEECH;
- rc = rsl_chan_mode_modify_req(msg->lchan);
+ rc = gsm48_rx_rr_modif_ack(msg);
break;
case GSM48_MT_RR_STATUS:
rc = gsm48_rx_rr_status(msg);
@@ -1645,7 +1612,7 @@ static int gsm0408_rcv_rr(struct msgb *msg)
}
int gsm48_send_rr_app_info(struct gsm_lchan *lchan, u_int8_t apdu_id,
- u_int8_t apdu_len, u_int8_t *apdu)
+ u_int8_t apdu_len, const u_int8_t *apdu)
{
struct msgb *msg = gsm48_msgb_alloc();
struct gsm48_hdr *gh;
@@ -1867,7 +1834,7 @@ static int handle_abisip_signal(unsigned int subsys, unsigned int signal,
ts = lchan->ts;
switch (signal) {
- case S_ABISIP_BIND_ACK:
+ 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) {
@@ -1893,7 +1860,7 @@ static int handle_abisip_signal(unsigned int subsys, unsigned int signal,
}
}
break;
- case S_ABISIP_DISC_IND:
+ 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);
@@ -1915,7 +1882,7 @@ static int ipacc_connect_proxy_bind(struct gsm_lchan *lchan)
struct rtp_socket *rs = ts->abis_ip.rtp_socket;
int rc;
- rc = rsl_ipacc_connect(lchan, ntohl(rs->rtp.sin_local.sin_addr.s_addr),
+ 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*/
@@ -1958,14 +1925,14 @@ static int tch_map(struct gsm_lchan *lchan, struct gsm_lchan *remote_lchan)
} else {
/* directly connect TCH RTP streams to each other */
ts = remote_lchan->ts;
- rc = rsl_ipacc_connect(lchan, ts->abis_ip.bound_ip,
+ 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);
if (rc < 0)
return rc;
ts = lchan->ts;
- rc = rsl_ipacc_connect(remote_lchan, ts->abis_ip.bound_ip,
+ 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);
@@ -3196,22 +3163,11 @@ static int gsm48_cc_rx_userinfo(struct gsm_trans *trans, struct msgb *msg)
return mncc_recvmsg(trans->subscr->net, trans, MNCC_USERINFO_IND, &user);
}
-static int gsm48_lchan_modify(struct gsm_trans *trans, void *arg)
+static int _gsm48_lchan_modify(struct gsm_trans *trans, void *arg)
{
struct gsm_mncc *mode = arg;
- int rc;
-
- rc = gsm48_tx_chan_mode_modify(trans->lchan, mode->lchan_mode);
- if (rc < 0)
- return rc;
-
- /* FIXME: we not only need to do this after mode modify, but
- * also after channel activation */
- if (is_ipaccess_bts(trans->lchan->ts->trx->bts) &&
- mode->lchan_mode != GSM48_CMODE_SIGN)
- rc = rsl_ipacc_bind(trans->lchan);
- return rc;
+ return gsm48_lchan_modify(trans->lchan, mode->lchan_mode);
}
static struct downstate {
@@ -3269,7 +3225,7 @@ static struct downstate {
MNCC_REL_REQ, gsm48_cc_tx_release},
/* special */
{ALL_STATES,
- MNCC_LCHAN_MODIFY, gsm48_lchan_modify},
+ MNCC_LCHAN_MODIFY, _gsm48_lchan_modify},
};
#define DOWNSLLEN \
@@ -3280,7 +3236,6 @@ int mncc_send(struct gsm_network *net, int msg_type, void *arg)
{
int i, rc = 0;
struct gsm_trans *trans = NULL, *transt;
- struct gsm_subscriber *subscr;
struct gsm_lchan *lchan = NULL;
struct gsm_bts *bts = NULL;
struct gsm_mncc *data = arg, rel;
@@ -3324,6 +3279,8 @@ int mncc_send(struct gsm_network *net, int msg_type, void *arg)
/* Callref unknown */
if (!trans) {
+ struct gsm_subscriber *subscr;
+
if (msg_type != MNCC_SETUP_REQ) {
DEBUGP(DCC, "(bts - trx - ts - ti -- sub %s) "
"Received '%s' from MNCC with "
@@ -3399,6 +3356,8 @@ int mncc_send(struct gsm_network *net, int msg_type, void *arg)
"started.\n", bts->nr,
data->called.number,
get_mncc_name(msg_type));
+ subscr_put(subscr);
+ trans_free(trans);
return 0;
}
/* store setup informations until paging was successfull */
@@ -3406,11 +3365,13 @@ int mncc_send(struct gsm_network *net, int msg_type, void *arg)
/* Trigger paging */
paging_request(net, subscr, RSL_CHANNEED_TCH_F,
setup_trig_pag_evt, subscr);
+ subscr_put(subscr);
return 0;
}
/* Assign lchan */
trans->lchan = lchan;
use_lchan(lchan);
+ subscr_put(subscr);
}
lchan = trans->lchan;
@@ -3589,10 +3550,12 @@ int gsm0408_rcvmsg(struct msgb *msg, u_int8_t link_id)
break;
case GSM48_PDISC_MM_GPRS:
case GSM48_PDISC_SM_GPRS:
- case GSM48_PDISC_NC_SS: /* mobile-originated USSD */
fprintf(stderr, "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);
diff --git a/openbsc/src/gsm_04_08_utils.c b/openbsc/src/gsm_04_08_utils.c
index ef9e59c77..ad038fba6 100644
--- a/openbsc/src/gsm_04_08_utils.c
+++ b/openbsc/src/gsm_04_08_utils.c
@@ -255,9 +255,14 @@ static const struct chreq chreq_type_neci1[] = {
{ 0x50, 0xf0, CHREQ_T_DATA_CALL_TCH_H },
{ 0x00, 0xf0, CHREQ_T_LOCATION_UPD },
{ 0x10, 0xf0, CHREQ_T_SDCCH },
- { 0x80, 0xe0, CHREQ_T_PAG_R_ANY },
+ { 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 */
@@ -267,9 +272,14 @@ static const struct chreq chreq_type_neci0[] = {
{ 0xe0, 0xe0, CHREQ_T_TCH_F },
{ 0x50, 0xf0, CHREQ_T_DATA_CALL_TCH_H },
{ 0x00, 0xe0, CHREQ_T_LOCATION_UPD },
- { 0x80, 0xe0, CHREQ_T_PAG_R_ANY },
+ { 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[] = {
@@ -282,9 +292,13 @@ static const enum gsm_chan_t ctype_by_chreq[] = {
[CHREQ_T_VOICE_CALL_TCH_H] = GSM_LCHAN_TCH_H,
[CHREQ_T_DATA_CALL_TCH_H] = GSM_LCHAN_TCH_H,
[CHREQ_T_LOCATION_UPD] = GSM_LCHAN_SDCCH,
- [CHREQ_T_PAG_R_ANY] = GSM_LCHAN_SDCCH,
+ [CHREQ_T_PAG_R_ANY_NECI1] = GSM_LCHAN_SDCCH,
+ [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[] = {
@@ -297,18 +311,32 @@ static const enum gsm_chreq_reason_t reason_by_chreq[] = {
[CHREQ_T_VOICE_CALL_TCH_H] = GSM_CHREQ_REASON_OTHER,
[CHREQ_T_DATA_CALL_TCH_H] = GSM_CHREQ_REASON_OTHER,
[CHREQ_T_LOCATION_UPD] = GSM_CHREQ_REASON_LOCATION_UPD,
- [CHREQ_T_PAG_R_ANY] = GSM_CHREQ_REASON_PAG,
+ [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)
+enum gsm_chan_t get_ctype_by_chreq(struct gsm_bts *bts, u_int8_t ra, int neci)
{
int i;
- /* FIXME: determine if we set NECI = 0 in the BTS SI4 */
+ int length;
+ const struct chreq *chreq;
- for (i = 0; i < ARRAY_SIZE(chreq_type_neci0); i++) {
- const struct chreq *chr = &chreq_type_neci0[i];
+ if (neci) {
+ chreq = chreq_type_neci1;
+ length = ARRAY_SIZE(chreq_type_neci1);
+ } else {
+ chreq = chreq_type_neci0;
+ length = ARRAY_SIZE(chreq_type_neci0);
+ }
+
+
+ for (i = 0; i < length; i++) {
+ const struct chreq *chr = &chreq[i];
if ((ra & chr->mask) == chr->val)
return ctype_by_chreq[chr->type];
}
@@ -316,13 +344,22 @@ enum gsm_chan_t get_ctype_by_chreq(struct gsm_bts *bts, u_int8_t ra)
return GSM_LCHAN_SDCCH;
}
-enum gsm_chreq_reason_t get_reason_by_chreq(struct gsm_bts *bts, u_int8_t ra)
+enum gsm_chreq_reason_t get_reason_by_chreq(struct gsm_bts *bts, u_int8_t ra, int neci)
{
int i;
- /* FIXME: determine if we set NECI = 0 in the BTS SI4 */
+ int length;
+ const struct chreq *chreq;
+
+ if (neci) {
+ chreq = chreq_type_neci1;
+ length = ARRAY_SIZE(chreq_type_neci1);
+ } else {
+ chreq = chreq_type_neci0;
+ length = ARRAY_SIZE(chreq_type_neci0);
+ }
- for (i = 0; i < ARRAY_SIZE(chreq_type_neci0); i++) {
- const struct chreq *chr = &chreq_type_neci0[i];
+ for (i = 0; i < length; i++) {
+ const struct chreq *chr = &chreq[i];
if ((ra & chr->mask) == chr->val)
return reason_by_chreq[chr->type];
}
@@ -482,3 +519,142 @@ int gsm48_send_rr_ciph_mode(struct gsm_lchan *lchan, int want_imeisv)
return rsl_encryption_cmd(msg);
}
+/* Chapter 9.1.2: Assignment Command */
+int gsm48_send_rr_ass_cmd(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;
+ gh->proto_discr = GSM48_PDISC_RR;
+ gh->msg_type = GSM48_MT_RR_ASS_CMD;
+
+ /*
+ * fill the channel information element, this code
+ * should probably be shared with rsl_rx_chan_rqd(),
+ * gsm48_tx_chan_mode_modify. But beware that 10.5.2.5
+ * 10.5.2.5.a have slightly different semantic for
+ * 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;
+ ass->power_command = power_command;
+
+ /* 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");
+ } else {
+ u_int8_t *data = msgb_put(msg, 4);
+ data[0] = GSM48_IE_MUL_RATE_CFG;
+ data[1] = 0x2;
+ memcpy(&data[2], &lchan->mr_conf, 2);
+ }
+ }
+
+ return gsm48_sendmsg(msg, NULL);
+}
+
+/* 9.1.5 Channel mode modify: Modify the mode on the MS side */
+int gsm48_tx_chan_mode_modify(struct gsm_lchan *lchan, u_int8_t mode)
+{
+ struct msgb *msg = gsm48_msgb_alloc();
+ struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
+ struct gsm48_chan_mode_modify *cmm =
+ (struct gsm48_chan_mode_modify *) msgb_put(msg, sizeof(*cmm));
+ u_int16_t arfcn = lchan->ts->trx->arfcn & 0x3ff;
+
+ DEBUGP(DRR, "-> CHANNEL MODE MODIFY mode=0x%02x\n", mode);
+
+ lchan->tch_mode = mode;
+ msg->lchan = lchan;
+ gh->proto_discr = GSM48_PDISC_RR;
+ gh->msg_type = GSM48_MT_RR_CHAN_MODE_MODIF;
+
+ /* fill the channel information element, this code
+ * should probably be shared with rsl_rx_chan_rqd() */
+ cmm->chan_desc.chan_nr = lchan2chan_nr(lchan);
+ cmm->chan_desc.h0.tsc = lchan->ts->trx->bts->tsc;
+ cmm->chan_desc.h0.h = 0;
+ cmm->chan_desc.h0.arfcn_high = arfcn >> 8;
+ cmm->chan_desc.h0.arfcn_low = arfcn & 0xff;
+ cmm->mode = 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");
+ } else {
+ u_int8_t *data = msgb_put(msg, 4);
+ data[0] = GSM48_IE_MUL_RATE_CFG;
+ data[1] = 0x2;
+ memcpy(&data[2], &lchan->mr_conf, 2);
+ }
+ }
+
+ return gsm48_sendmsg(msg, NULL);
+}
+
+int gsm48_lchan_modify(struct gsm_lchan *lchan, u_int8_t lchan_mode)
+{
+ int rc;
+
+ rc = gsm48_tx_chan_mode_modify(lchan, lchan_mode);
+ if (rc < 0)
+ return rc;
+
+ return rc;
+}
+
+int gsm48_rx_rr_modif_ack(struct msgb *msg)
+{
+ int rc;
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ struct gsm48_chan_mode_modify *mod =
+ (struct gsm48_chan_mode_modify *) gh->data;
+
+ 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",
+ msg->lchan->tch_mode, mod->mode);
+ return -1;
+ }
+
+ /* update the channel type */
+ switch (mod->mode) {
+ case GSM48_CMODE_SIGN:
+ msg->lchan->rsl_cmode = RSL_CMOD_SPD_SIGN;
+ break;
+ case GSM48_CMODE_SPEECH_V1:
+ case GSM48_CMODE_SPEECH_EFR:
+ case GSM48_CMODE_SPEECH_AMR:
+ msg->lchan->rsl_cmode = RSL_CMOD_SPD_SPEECH;
+ break;
+ case GSM48_CMODE_DATA_14k5:
+ case GSM48_CMODE_DATA_12k0:
+ case GSM48_CMODE_DATA_6k0:
+ case GSM48_CMODE_DATA_3k6:
+ msg->lchan->rsl_cmode = RSL_CMOD_SPD_DATA;
+ break;
+ }
+
+ /* We've successfully modified the MS side of the channel,
+ * now go on to modify the BTS side of the channel */
+ rc = rsl_chan_mode_modify_req(msg->lchan);
+
+ /* FIXME: we not only need to do this after mode modify, but
+ * also after channel activation */
+ if (is_ipaccess_bts(msg->lchan->ts->trx->bts) && mod->mode != GSM48_CMODE_SIGN)
+ rsl_ipacc_crcx(msg->lchan);
+ return rc;
+}
diff --git a/openbsc/src/gsm_04_11.c b/openbsc/src/gsm_04_11.c
index 59020746f..8e3c64974 100644
--- a/openbsc/src/gsm_04_11.c
+++ b/openbsc/src/gsm_04_11.c
@@ -56,11 +56,6 @@
void *tall_gsms_ctx;
static u_int32_t new_callref = 0x40000001;
-struct value_string {
- u_int32_t value;
- const char *str;
-};
-
static const struct value_string cp_cause_strs[] = {
{ GSM411_CP_CAUSE_NET_FAIL, "Network Failure" },
{ GSM411_CP_CAUSE_CONGESTION, "Congestion" },
@@ -104,19 +99,6 @@ static const struct value_string rp_cause_strs[] = {
{ 0, NULL }
};
-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";
-}
-
struct gsm_sms *sms_alloc(void)
{
return talloc_zero(tall_gsms_ctx, struct gsm_sms);
@@ -216,40 +198,172 @@ static int gsm411_rp_sendmsg(struct msgb *msg, struct gsm_trans *trans,
return gsm411_cp_sendmsg(msg, trans, GSM411_MT_CP_DATA);
}
-static unsigned long gsm340_validity_period(u_int8_t sms_vpf, u_int8_t *sms_vp)
+/* 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);
+}
+
+/* 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;
+ 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) {
+ DEBUGP(DSMS, "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 */
- /* FIXME: like service center time stamp */
- DEBUGP(DSMS, "VPI absolute not implemented yet\n");
- 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 */
+ DEBUGP(DSMS, "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 */
@@ -317,30 +431,6 @@ static int gsm340_gen_oa(u_int8_t *oa, unsigned int oa_len,
return len_in_bytes;
}
-static u_int8_t bcdify(u_int8_t value)
-{
- u_int8_t ret;
-
- ret = value / 10;
- ret |= (value % 10) << 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++ = 0; /* FIXME: timezone */
-}
-
/* 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)
@@ -470,6 +560,8 @@ 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 (*smsp & (1<<7)) smsp++;
smsp += 7;
break;
case GSM340_TP_VPF_NONE:
@@ -674,7 +766,7 @@ 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 (%s)\n", cause,
+ DEBUGP(DSMS, "RX SMS RP-ERROR, cause %d:%d (%s)\n", cause_len, cause,
get_value_string(rp_cause_strs, cause));
if (!trans->sms.is_mt) {
@@ -1068,8 +1160,6 @@ static int subscr_sig_cb(unsigned int subsys, unsigned int signal,
struct gsm_lchan *lchan;
struct gsm_sms *sms;
- u_int32_t token;
-
switch (signal) {
case S_SUBSCR_ATTACHED:
/* A subscriber has attached. Check if there are
diff --git a/openbsc/src/gsm_04_80.c b/openbsc/src/gsm_04_80.c
new file mode 100644
index 000000000..d3b472f30
--- /dev/null
+++ b/openbsc/src/gsm_04_80.c
@@ -0,0 +1,330 @@
+/* GSM Mobile Radio Interface Layer 3 messages on the A-bis interface
+ * 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>
+ * (C) 2008, 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * (C) 2009 by Mike Haben <michael.haben@btinternet.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.
+ *
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <openbsc/msgb.h>
+#include <openbsc/tlv.h>
+#include <openbsc/debug.h>
+#include <openbsc/gsm_data.h>
+#include <openbsc/gsm_utils.h>
+#include <openbsc/gsm_04_08.h>
+#include <openbsc/gsm_04_80.h>
+
+/* Forward declarations */
+static int parse_ussd(u_int8_t *ussd, struct ussd_request *req);
+static int parse_ussd_info_elements(u_int8_t *ussd_ie,
+ struct ussd_request *req);
+static int parse_facility_ie(u_int8_t *facility_ie, u_int8_t length,
+ struct ussd_request *req);
+static int parse_ss_invoke(u_int8_t *invoke_data, u_int8_t length,
+ struct ussd_request *req);
+static int parse_process_uss_req(u_int8_t *uss_req_data, u_int8_t length,
+ struct ussd_request *req);
+
+static inline unsigned char *msgb_wrap_with_TL(struct msgb *msgb, u_int8_t tag)
+{
+ msgb->data -= 2;
+ msgb->data[0] = tag;
+ msgb->data[1] = msgb->len;
+ msgb->len += 2;
+ return msgb->data;
+}
+
+static inline unsigned char *msgb_push_TLV1(struct msgb *msgb, u_int8_t tag,
+ u_int8_t value)
+{
+ msgb->data -= 3;
+ msgb->len += 3;
+ msgb->data[0] = tag;
+ msgb->data[1] = 1;
+ msgb->data[2] = value;
+ return msgb->data;
+}
+
+
+/* Decode a mobile-originated USSD-request message */
+int gsm0480_decode_ussd_request(const struct msgb *msg, struct ussd_request *req)
+{
+ int rc = 0;
+ u_int8_t *parse_ptr = msgb_l3(msg);
+
+ if ((*parse_ptr & 0x0F) == GSM48_PDISC_NC_SS) {
+ req->transaction_id = *parse_ptr & 0x70;
+ rc = parse_ussd(parse_ptr+1, req);
+ }
+
+ if (!rc)
+ DEBUGP(DMM, "Error occurred while parsing received USSD!\n");
+
+ return rc;
+}
+
+static int parse_ussd(u_int8_t *ussd, struct ussd_request *req)
+{
+ int rc = 1;
+ u_int8_t msg_type = ussd[0] & 0xBF; /* message-type - section 3.4 */
+
+ switch (msg_type) {
+ case GSM0480_MTYPE_RELEASE_COMPLETE:
+ DEBUGP(DMM, "USS Release Complete\n");
+ /* could also parse out the optional Cause/Facility data */
+ req->text[0] = 0xFF;
+ break;
+ case GSM0480_MTYPE_REGISTER:
+ case GSM0480_MTYPE_FACILITY:
+ rc &= parse_ussd_info_elements(ussd+1, req);
+ break;
+ default:
+ fprintf(stderr, "Unknown GSM 04.80 message-type field 0x%02x\n",
+ ussd[0]);
+ rc = 0;
+ break;
+ }
+
+ return rc;
+}
+
+static int parse_ussd_info_elements(u_int8_t *ussd_ie, struct ussd_request *req)
+{
+ int rc;
+ /* Information Element Identifier - table 3.2 & GSM 04.08 section 10.5 */
+ u_int8_t iei = ussd_ie[0];
+ u_int8_t iei_length = ussd_ie[1];
+
+ switch (iei) {
+ case GSM48_IE_CAUSE:
+ break;
+ case GSM0480_IE_FACILITY:
+ rc = parse_facility_ie(ussd_ie+2, iei_length, req);
+ break;
+ case GSM0480_IE_SS_VERSION:
+ break;
+ default:
+ fprintf(stderr, "Unhandled GSM 04.08 or 04.80 IEI 0x%02x\n",
+ iei);
+ rc = 0;
+ break;
+ }
+
+ return rc;
+}
+
+static int parse_facility_ie(u_int8_t *facility_ie, u_int8_t length,
+ struct ussd_request *req)
+{
+ int rc = 1;
+ u_int8_t offset = 0;
+
+ do {
+ /* Component Type tag - table 3.7 */
+ u_int8_t component_type = facility_ie[offset];
+ u_int8_t component_length = facility_ie[offset+1];
+
+ switch (component_type) {
+ case GSM0480_CTYPE_INVOKE:
+ rc &= parse_ss_invoke(facility_ie+2,
+ component_length,
+ req);
+ break;
+ case GSM0480_CTYPE_RETURN_RESULT:
+ break;
+ case GSM0480_CTYPE_RETURN_ERROR:
+ break;
+ case GSM0480_CTYPE_REJECT:
+ break;
+ default:
+ fprintf(stderr, "Unknown GSM 04.80 Facility "
+ "Component Type 0x%02x\n", component_type);
+ rc = 0;
+ break;
+ }
+ offset += (component_length+2);
+ } while (offset < length);
+
+ return rc;
+}
+
+/* Parse an Invoke component - see table 3.3 */
+static int parse_ss_invoke(u_int8_t *invoke_data, u_int8_t length,
+ struct ussd_request *req)
+{
+ int rc = 1;
+ u_int8_t offset;
+
+ /* mandatory part */
+ if (invoke_data[0] != GSM0480_COMPIDTAG_INVOKE_ID) {
+ fprintf(stderr, "Unexpected GSM 04.80 Component-ID tag "
+ "0x%02x (expecting Invoke ID tag)\n", invoke_data[0]);
+ }
+
+ offset = invoke_data[1] + 2;
+ req->invoke_id = invoke_data[2];
+
+ /* optional part */
+ if (invoke_data[offset] == GSM0480_COMPIDTAG_LINKED_ID)
+ offset += invoke_data[offset+1] + 2; /* skip over it */
+
+ /* mandatory part */
+ if (invoke_data[offset] == GSM0480_OPERATION_CODE) {
+ u_int8_t operation_code = invoke_data[offset+2];
+ switch (operation_code) {
+ case GSM0480_OP_CODE_PROCESS_USS_REQ:
+ rc = parse_process_uss_req(invoke_data + offset + 3,
+ length - offset - 3,
+ req);
+ break;
+ default:
+ fprintf(stderr, "GSM 04.80 operation code 0x%02x "
+ "is not yet handled\n", operation_code);
+ rc = 0;
+ break;
+ }
+ } else {
+ fprintf(stderr, "Unexpected GSM 04.80 Component-ID tag 0x%02x "
+ "(expecting Operation Code tag)\n",
+ invoke_data[0]);
+ rc = 0;
+ }
+
+ return rc;
+}
+
+/* Parse the parameters of a Process UnstructuredSS Request */
+static int parse_process_uss_req(u_int8_t *uss_req_data, u_int8_t length,
+ struct ussd_request *req)
+{
+ int rc = 0;
+ int num_chars;
+ u_int8_t dcs;
+
+ if (uss_req_data[0] == GSM_0480_SEQUENCE_TAG) {
+ if (uss_req_data[2] == ASN1_OCTET_STRING_TAG) {
+ dcs = uss_req_data[4];
+ if ((dcs == 0x0F) &&
+ (uss_req_data[5] == ASN1_OCTET_STRING_TAG)) {
+ num_chars = (uss_req_data[6] * 8) / 7;
+ /* Prevent a mobile-originated buffer-overrun! */
+ if (num_chars > MAX_LEN_USSD_STRING)
+ num_chars = MAX_LEN_USSD_STRING;
+ gsm_7bit_decode(req->text,
+ &(uss_req_data[7]), num_chars);
+ /* append null-terminator */
+ req->text[num_chars+1] = 0;
+ rc = 1;
+ }
+ }
+ }
+ return rc;
+}
+
+/* Send response to a mobile-originated ProcessUnstructuredSS-Request */
+int gsm0480_send_ussd_response(const struct msgb *in_msg, const char *response_text,
+ const struct ussd_request *req)
+{
+ struct msgb *msg = gsm48_msgb_alloc();
+ struct gsm48_hdr *gh;
+ u_int8_t *ptr8;
+ int response_len;
+
+ response_len = (strlen(response_text) * 7) / 8;
+ if (((strlen(response_text) * 7) % 8) != 0)
+ response_len += 1;
+
+ msg->bts_link = in_msg->bts_link;
+ msg->lchan = in_msg->lchan;
+
+ /* First put the payload text into the message */
+ ptr8 = msgb_put(msg, response_len);
+ gsm_7bit_encode(ptr8, response_text);
+
+ /* Then wrap it as an Octet String */
+ msgb_wrap_with_TL(msg, ASN1_OCTET_STRING_TAG);
+
+ /* Pre-pend the DCS octet string */
+ msgb_push_TLV1(msg, ASN1_OCTET_STRING_TAG, 0x0F);
+
+ /* Then wrap these as a Sequence */
+ msgb_wrap_with_TL(msg, GSM_0480_SEQUENCE_TAG);
+
+ /* Pre-pend the operation code */
+ msgb_push_TLV1(msg, GSM0480_OPERATION_CODE,
+ GSM0480_OP_CODE_PROCESS_USS_REQ);
+
+ /* Wrap the operation code and IA5 string as a sequence */
+ msgb_wrap_with_TL(msg, GSM_0480_SEQUENCE_TAG);
+
+ /* Pre-pend the invoke ID */
+ msgb_push_TLV1(msg, GSM0480_COMPIDTAG_INVOKE_ID, req->invoke_id);
+
+ /* Wrap this up as a Return Result component */
+ msgb_wrap_with_TL(msg, GSM0480_CTYPE_RETURN_RESULT);
+
+ /* Wrap the component in a Facility message */
+ msgb_wrap_with_TL(msg, GSM0480_IE_FACILITY);
+
+ /* And finally pre-pend the L3 header */
+ gh = (struct gsm48_hdr *) msgb_push(msg, sizeof(*gh));
+ gh->proto_discr = GSM48_PDISC_NC_SS | req->transaction_id
+ | (1<<7); /* TI direction = 1 */
+ gh->msg_type = GSM0480_MTYPE_RELEASE_COMPLETE;
+
+ return gsm48_sendmsg(msg, NULL);
+}
+
+int gsm0480_send_ussd_reject(const struct msgb *in_msg,
+ const struct ussd_request *req)
+{
+ struct msgb *msg = gsm48_msgb_alloc();
+ struct gsm48_hdr *gh;
+
+ msg->bts_link = in_msg->bts_link;
+ msg->lchan = in_msg->lchan;
+
+ /* First insert the problem code */
+ msgb_push_TLV1(msg, GSM_0480_PROBLEM_CODE_TAG_GENERAL,
+ GSM_0480_GEN_PROB_CODE_UNRECOGNISED);
+
+ /* Before it insert the invoke ID */
+ msgb_push_TLV1(msg, GSM0480_COMPIDTAG_INVOKE_ID, req->invoke_id);
+
+ /* Wrap this up as a Reject component */
+ msgb_wrap_with_TL(msg, GSM0480_CTYPE_REJECT);
+
+ /* Wrap the component in a Facility message */
+ msgb_wrap_with_TL(msg, GSM0480_IE_FACILITY);
+
+ /* And finally pre-pend the L3 header */
+ gh = (struct gsm48_hdr *) msgb_push(msg, sizeof(*gh));
+ gh->proto_discr = GSM48_PDISC_NC_SS;
+ gh->proto_discr |= req->transaction_id | (1<<7); /* TI direction = 1 */
+ gh->msg_type = GSM0480_MTYPE_RELEASE_COMPLETE;
+
+ return gsm48_sendmsg(msg, NULL);
+}
diff --git a/openbsc/src/gsm_data.c b/openbsc/src/gsm_data.c
index 6767c3fd5..8212346ec 100644
--- a/openbsc/src/gsm_data.c
+++ b/openbsc/src/gsm_data.c
@@ -27,9 +27,23 @@
#include <openbsc/gsm_data.h>
#include <openbsc/talloc.h>
+#include <openbsc/abis_nm.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";
+}
+
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)
{
@@ -45,6 +59,8 @@ static const char *pchan_names[] = {
[GSM_PCHAN_TCH_F] = "TCH/F",
[GSM_PCHAN_TCH_H] = "TCH/H",
[GSM_PCHAN_SDCCH8_SACCH8C] = "SDCCH8",
+ [GSM_PCHAN_PDCH] = "PDCH",
+ [GSM_PCHAN_TCH_F_PDCH] = "TCH/F_PDCH",
[GSM_PCHAN_UNKNOWN] = "UNKNOWN",
};
@@ -102,13 +118,12 @@ const char *gsm_chreq_name(enum gsm_chreq_reason_t c)
struct gsm_bts_trx *gsm_bts_trx_alloc(struct gsm_bts *bts)
{
- struct gsm_bts_trx *trx = talloc(bts, struct gsm_bts_trx);
+ struct gsm_bts_trx *trx = talloc_zero(bts, struct gsm_bts_trx);
int k;
if (!trx)
return NULL;
- memset(trx, 0, sizeof(*trx));
trx->bts = bts;
trx->nr = bts->num_trx++;
@@ -138,12 +153,12 @@ struct gsm_bts_trx *gsm_bts_trx_alloc(struct gsm_bts *bts)
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(net, struct gsm_bts);
+ struct gsm_bts *bts = talloc_zero(net, struct gsm_bts);
+ int i;
if (!bts)
return NULL;
- memset(bts, 0, sizeof(*bts));
bts->network = net;
bts->nr = net->num_bts++;
bts->type = type;
@@ -153,6 +168,11 @@ struct gsm_bts *gsm_bts_alloc(struct gsm_network *net, enum gsm_bts_type type,
INIT_LLIST_HEAD(&bts->trx_list);
bts->ms_max_power = 15; /* dBm */
+ for (i = 0; i < ARRAY_SIZE(bts->gprs.nsvc); i++) {
+ bts->gprs.nsvc[i].bts = bts;
+ bts->gprs.nsvc[i].id = i;
+ }
+
/* create our primary TRX */
bts->c0 = gsm_bts_trx_alloc(bts);
if (!bts->c0) {
@@ -171,10 +191,9 @@ struct gsm_network *gsm_network_init(u_int16_t country_code, u_int16_t network_c
{
struct gsm_network *net;
- net = talloc(tall_bsc_ctx, struct gsm_network);
+ net = talloc_zero(tall_bsc_ctx, struct gsm_network);
if (!net)
return NULL;
- memset(net, 0, sizeof(*net));
net->country_code = country_code;
net->network_code = network_code;
@@ -224,7 +243,7 @@ static char ts2str[255];
char *gsm_ts_name(struct gsm_bts_trx_ts *ts)
{
snprintf(ts2str, sizeof(ts2str), "(bts=%d,trx=%d,ts=%d)",
- ts->trx->bts->bts_nr, ts->trx->nr, ts->nr);
+ ts->trx->bts->nr, ts->trx->nr, ts->nr);
return ts2str;
}
diff --git a/openbsc/src/gsm_subscriber_base.c b/openbsc/src/gsm_subscriber_base.c
index 868b35599..48374eae5 100644
--- a/openbsc/src/gsm_subscriber_base.c
+++ b/openbsc/src/gsm_subscriber_base.c
@@ -115,11 +115,10 @@ struct gsm_subscriber *subscr_alloc(void)
{
struct gsm_subscriber *s;
- s = talloc(tall_subscr_ctx, struct gsm_subscriber);
+ s = talloc_zero(tall_subscr_ctx, struct gsm_subscriber);
if (!s)
return NULL;
- memset(s, 0, sizeof(*s));
llist_add_tail(&s->entry, &active_subscribers);
s->use_count = 1;
s->tmsi = GSM_RESERVED_TMSI;
diff --git a/openbsc/src/gsm_utils.c b/openbsc/src/gsm_utils.c
index de18dba26..ddfd7f3de 100644
--- a/openbsc/src/gsm_utils.c
+++ b/openbsc/src/gsm_utils.c
@@ -23,8 +23,10 @@
#include <openbsc/gsm_data.h>
#include <openbsc/gsm_utils.h>
+#include <execinfo.h>
#include <stdlib.h>
#include <string.h>
+#include <stdio.h>
#include <errno.h>
/* GSM 03.38 6.2.1 Charachter packing */
@@ -148,4 +150,21 @@ int ms_pwr_dbm(enum gsm_band band, u_int8_t lvl)
return -EINVAL;
}
+void generate_backtrace()
+{
+ int i, nptrs;
+ void *buffer[100];
+ char **strings;
+
+ nptrs = backtrace(buffer, ARRAY_SIZE(buffer));
+ printf("backtrace() returned %d addresses\n", nptrs);
+
+ strings = backtrace_symbols(buffer, nptrs);
+ if (!strings)
+ return;
+ for (i = 1; i < nptrs; i++)
+ printf("%s\n", strings[i]);
+
+ free(strings);
+}
diff --git a/openbsc/src/input/ipaccess.c b/openbsc/src/input/ipaccess.c
index 2239cf152..2d9f51ef9 100644
--- a/openbsc/src/input/ipaccess.c
+++ b/openbsc/src/input/ipaccess.c
@@ -222,20 +222,20 @@ static int ipaccess_rcvmsg(struct e1inp_line *line, struct msgb *msg,
if (bfd->priv_nr == 1) {
bts->oml_link = e1inp_sign_link_create(&line->ts[1-1],
E1INP_SIGN_OML, bts->c0,
- 0, 0xff);
+ bts->oml_tei, 0);
} else if (bfd->priv_nr == 2) {
struct e1inp_ts *e1i_ts;
struct bsc_fd *newbfd;
+ struct gsm_bts_trx *trx = gsm_bts_trx_num(bts, trx_id);
- /* FIXME: implement this for non-0 TRX */
bfd->data = line = bts->oml_link->ts->line;
- e1i_ts = &line->ts[2-1];
+ e1i_ts = &line->ts[2+trx_id - 1];
newbfd = &e1i_ts->driver.ipaccess.fd;
+ e1inp_ts_config(e1i_ts, line, E1INP_TS_TYPE_SIGN);
- bts->c0->rsl_link =
- e1inp_sign_link_create(e1i_ts,
- E1INP_SIGN_RSL, bts->c0,
- 0, 0);
+ trx->rsl_link = e1inp_sign_link_create(e1i_ts,
+ E1INP_SIGN_RSL, trx,
+ trx->rsl_tei, 0);
/* get rid of our old temporary bfd */
memcpy(newbfd, bfd, sizeof(*newbfd));
bsc_unregister_fd(bfd);
@@ -259,7 +259,7 @@ struct msgb *ipaccess_read_msg(struct bsc_fd *bfd, int *error)
{
struct msgb *msg = msgb_alloc(TS1_ALLOC_SIZE, "Abis/IP");
struct ipaccess_head *hh;
- int ret = 0;
+ int len, ret = 0;
if (!msg) {
*error = -ENOMEM;
@@ -284,8 +284,9 @@ struct msgb *ipaccess_read_msg(struct bsc_fd *bfd, int *error)
/* then read te length as specified in header */
msg->l2h = msg->data + sizeof(*hh);
- ret = recv(bfd->fd, msg->l2h, hh->len, 0);
- if (ret < hh->len) {
+ len = ntohs(hh->len);
+ ret = recv(bfd->fd, msg->l2h, len, 0);
+ if (ret < len) {
fprintf(stderr, "short read!\n");
msgb_free(msg);
*error = -EIO;
@@ -304,7 +305,7 @@ static int handle_ts1_read(struct bsc_fd *bfd)
struct e1inp_sign_link *link;
struct msgb *msg;
struct ipaccess_head *hh;
- int ret, error;
+ int ret = 0, error;
msg = ipaccess_read_msg(bfd, &error);
if (!msg) {
@@ -337,7 +338,7 @@ static int handle_ts1_read(struct bsc_fd *bfd)
/* BIG FAT WARNING: bfd might no longer exist here, since ipaccess_rcvmsg()
* might have free'd it !!! */
- link = e1inp_lookup_sign_link(e1i_ts, 0, hh->proto);
+ 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);
msgb_free(msg);
@@ -345,17 +346,17 @@ static int handle_ts1_read(struct bsc_fd *bfd)
}
msg->trx = link->trx;
- switch (hh->proto) {
- case IPAC_PROTO_RSL:
+ switch (link->type) {
+ case E1INP_SIGN_RSL:
if (!rsl_up) {
- e1inp_event(e1i_ts, EVT_E1_TEI_UP, 0, IPAC_PROTO_RSL);
+ e1inp_event(e1i_ts, EVT_E1_TEI_UP, link->tei, link->sapi);
rsl_up = 1;
}
ret = abis_rsl_rcvmsg(msg);
break;
- case IPAC_PROTO_OML:
+ case E1INP_SIGN_OML:
if (!oml_up) {
- e1inp_event(e1i_ts, EVT_E1_TEI_UP, 0, IPAC_PROTO_OML);
+ e1inp_event(e1i_ts, EVT_E1_TEI_UP, link->tei, link->sapi);
oml_up = 1;
}
ret = abis_nm_rcvmsg(msg);
@@ -374,8 +375,7 @@ void ipaccess_prepend_header(struct msgb *msg, int proto)
/* prepend the ip.access header */
hh = (struct ipaccess_head *) msgb_push(msg, sizeof(*hh));
- hh->zero = 0;
- hh->len = msg->len - sizeof(*hh);
+ hh->len = htons(msg->len - sizeof(*hh));
hh->proto = proto;
}
@@ -426,9 +426,8 @@ static int handle_ts1_write(struct bsc_fd *bfd)
return -EINVAL;
}
-
msg->l2h = msg->data;
- ipaccess_prepend_header(msg, proto);
+ ipaccess_prepend_header(msg, sign_link->tei);
DEBUGP(DMI, "TX %u: %s\n", ts_nr, hexdump(msg->l2h, msgb_l2len(msg)));
@@ -495,17 +494,15 @@ static int listen_fd_cb(struct bsc_fd *listen_bfd, unsigned int what)
}
DEBUGP(DINP, "accept()ed new OML link from %s\n", inet_ntoa(sa.sin_addr));
- line = talloc(tall_bsc_ctx, struct e1inp_line);
+ line = talloc_zero(tall_bsc_ctx, struct e1inp_line);
if (!line) {
close(ret);
return -ENOMEM;
}
- memset(line, 0, sizeof(*line));
line->driver = &ipaccess_driver;
//line->driver_data = e1h;
/* create virrtual E1 timeslots for signalling */
e1inp_ts_config(&line->ts[1-1], line, E1INP_TS_TYPE_SIGN);
- e1inp_ts_config(&line->ts[2-1], line, E1INP_TS_TYPE_SIGN);
e1i_ts = &line->ts[idx];
@@ -540,14 +537,13 @@ static int rsl_listen_fd_cb(struct bsc_fd *listen_bfd, unsigned int what)
if (!(what & BSC_FD_READ))
return 0;
- bfd = talloc(tall_bsc_ctx, struct bsc_fd);
+ bfd = talloc_zero(tall_bsc_ctx, struct bsc_fd);
if (!bfd)
return -ENOMEM;
- memset(bfd, 0, sizeof(*bfd));
/* Some BTS has connected to us, but we don't know yet which line
* (as created by the OML link) to associate it with. Thus, we
- * aloocate a temporary bfd until we have received ID from BTS */
+ * allocate a temporary bfd until we have received ID from BTS */
bfd->fd = accept(listen_bfd->fd, (struct sockaddr *) &sa, &sa_len);
if (bfd->fd < 0) {
@@ -654,10 +650,9 @@ int ipaccess_setup(struct gsm_network *gsmnet)
if (ret)
return ret;
- e1h = talloc(tall_bsc_ctx, struct ia_e1_handle);
+ e1h = talloc_zero(tall_bsc_ctx, struct ia_e1_handle);
if (!e1h)
return -ENOMEM;
- memset(e1h, 0, sizeof(*e1h));
e1h->gsmnet = gsmnet;
diff --git a/openbsc/src/input/misdn.c b/openbsc/src/input/misdn.c
index 82268e811..135cfad48 100644
--- a/openbsc/src/input/misdn.c
+++ b/openbsc/src/input/misdn.c
@@ -262,7 +262,7 @@ static int handle_tsX_write(struct bsc_fd *bfd)
ret = send(bfd->fd, tx_buf, sizeof(*hh) + BCHAN_TX_GRAN, 0);
if (ret < sizeof(*hh) + BCHAN_TX_GRAN)
- DEBUGP(DMIB, "send returns %d instead of %u\n", ret,
+ DEBUGP(DMIB, "send returns %d instead of %lu\n", ret,
sizeof(*hh) + BCHAN_TX_GRAN);
return ret;
diff --git a/openbsc/src/ipaccess-config.c b/openbsc/src/ipaccess-config.c
index 46043d571..c50a46581 100644
--- a/openbsc/src/ipaccess-config.c
+++ b/openbsc/src/ipaccess-config.c
@@ -61,7 +61,7 @@ static u_int8_t unit_id_attr[] = { 0x91, 0x00, 9, '2', '3', '4', '2', '/' , '0',
* result. The nanoBTS will send us a NACK when we did something the
* BTS didn't like.
*/
-static int ipacc_msg_nack(int mt)
+static int ipacc_msg_nack(u_int8_t mt)
{
fprintf(stderr, "Failure to set attribute. This seems fatal\n");
exit(-1);
@@ -79,30 +79,14 @@ struct ipacc_cusage_elem {
rxlev:6;
} __attribute__ ((packed));
-static const char *ipacc_testres_names[] = {
- [NM_IPACC_TESTRES_SUCCESS] = "SUCCESS",
- [NM_IPACC_TESTRES_TIMEOUT] = "TIMEOUT",
- [NM_IPACC_TESTRES_NO_CHANS] = "NO CHANNELS",
- [NM_IPACC_TESTRES_PARTIAL] = "PARTIAL",
- [NM_IPACC_TESTRES_STOPPED] = "STOPPED",
-};
-
-const char *ipacc_testres_name(u_int8_t res)
-{
- if (res < ARRAY_SIZE(ipacc_testres_names) &&
- ipacc_testres_names[res])
- return ipacc_testres_names[res];
-
- return "unknown";
-}
-
static int test_rep(void *_msg)
{
struct msgb *msg = _msg;
struct abis_om_fom_hdr *foh = msgb_l3(msg);
u_int16_t test_rep_len, ferr_list_len;
struct ipacc_ferr_elem *ife;
- int i;
+ struct ipac_bcch_info binfo;
+ int i, rc;
DEBUGP(DNM, "TEST REPORT: ");
@@ -119,7 +103,7 @@ static int test_rep(void *_msg)
/* data[6]: ip.access nested IE. 3 == freq_err_list */
switch (foh->data[6]) {
- case 3:
+ case NM_IPAC_EIE_FREQ_ERR_LIST:
/* data[7..8]: length of ferr_list */
ferr_list_len = ntohs(*(u_int16_t *) &foh->data[7]);
@@ -130,7 +114,7 @@ static int test_rep(void *_msg)
ife->arfcn, ntohs(ife->freq_err));
}
break;
- case 4:
+ case NM_IPAC_EIE_CHAN_USE_LIST:
/* data[7..8]: length of ferr_list */
ferr_list_len = ntohs(*(u_int16_t *) &foh->data[7]);
@@ -142,6 +126,19 @@ static int test_rep(void *_msg)
cu & 0x3ff, cu >> 10);
}
break;
+ case NM_IPAC_EIE_BCCH_INFO_TYPE:
+ break;
+ case NM_IPAC_EIE_BCCH_INFO:
+ rc = ipac_parse_bcch_info(&binfo, foh->data+6);
+ if (rc < 0) {
+ DEBUGP(DNM, "BCCH Info parsing failed\n");
+ break;
+ }
+ DEBUGP(DNM, "==> ARFCN %u, RxLev %2u, RxQual %2u: %3d-%d, LAC %d CI %d\n",
+ binfo.arfcn, binfo.rx_lev, binfo.rx_qual,
+ binfo.cgi.mcc, binfo.cgi.mnc,
+ binfo.cgi.lac, binfo.cgi.ci);
+ break;
default:
break;
}
@@ -152,9 +149,12 @@ 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;
+
switch (signal) {
case S_NM_IPACC_NACK:
- return ipacc_msg_nack((int)signal_data);
+ msg_type = signal_data;
+ return ipacc_msg_nack(*msg_type);
case S_NM_TEST_REP:
return test_rep(signal_data);
default:
@@ -279,15 +279,16 @@ static void print_help(void)
printf(" -o --oml-ip ip\n");
printf(" -r --restart\n");
printf(" -n flags/mask\tSet NVRAM attributes.\n");
- printf(" -l --listen testnr \tPerform speciified test number\n");
+ printf(" -l --listen testnr \tPerform specified test number\n");
printf(" -h --help this text\n");
+ printf(" -s --stream-id ID\n");
}
int main(int argc, char **argv)
{
struct gsm_bts *bts;
struct sockaddr_in sin;
- int rc, option_index = 0;
+ int rc, option_index = 0, stream_id = 0xff;
printf("ipaccess-config (C) 2009 by Harald Welte\n");
printf("This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY\n\n");
@@ -302,9 +303,10 @@ int main(int argc, char **argv)
{ "restart", 0, 0, 'r' },
{ "help", 0, 0, 'h' },
{ "listen", 1, 0, 'l' },
+ { "stream-id", 1, 0, 's' },
};
- c = getopt_long(argc, argv, "u:o:rn:l:h", long_options,
+ c = getopt_long(argc, argv, "u:o:rn:l:hs:", long_options,
&option_index);
if (c == -1)
@@ -332,6 +334,10 @@ int main(int argc, char **argv)
case 'l':
net_listen_testnr = atoi(optarg);
break;
+ case 's':
+ stream_id = atoi(optarg);
+ printf("foo: %d\n", stream_id);
+ break;
case 'h':
print_usage();
print_help();
@@ -350,6 +356,7 @@ int main(int argc, char **argv)
bts = gsm_bts_alloc(gsmnet, GSM_BTS_TYPE_NANOBTS, HARDCODED_TSC,
HARDCODED_BSIC);
+ bts->oml_tei = stream_id;
register_signal_handler(SS_NM, nm_sig_cb, NULL);
printf("Trying to connect to ip.access BTS ...\n");
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/msgb.c b/openbsc/src/msgb.c
index 52edf2dcd..edeb975a9 100644
--- a/openbsc/src/msgb.c
+++ b/openbsc/src/msgb.c
@@ -74,6 +74,26 @@ struct msgb *msgb_dequeue(struct llist_head *queue)
return llist_entry(lh, struct msgb, list);
}
+void msgb_reset(struct msgb *msg)
+{
+ msg->len = 0;
+ msg->len = 0;
+ msg->data = msg->_data;
+
+ msg->head = msg->data;
+ msg->data = msg->data;
+ /* reset tail pointer */
+ msg->tail = msg->data;
+
+ /* reset pointers */
+ msg->bts_link = NULL;
+ msg->trx = NULL;
+ msg->lchan = NULL;
+ msg->l2h = NULL;
+ 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..d312843b0 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
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..c1468a647 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
diff --git a/openbsc/src/openbsc.cfg.nanobts b/openbsc/src/openbsc.cfg.nanobts
new file mode 100644
index 000000000..a1ceaec79
--- /dev/null
+++ b/openbsc/src/openbsc.cfg.nanobts
@@ -0,0 +1,40 @@
+!
+! OpenBSC configuration saved from vty
+!
+password foo
+!
+line vty
+ no login
+!
+network
+ network country code 1
+ 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
+ band GSM1800
+ location_area_code 1
+ training_sequence_code 7
+ base_station_id_code 63
+ trx 0
+ arfcn 514
+ timeslot 0
+ phys_chan_config CCCH+SDCCH4
+ timeslot 1
+ phys_chan_config SDCCH8
+ timeslot 2
+ phys_chan_config TCH/F
+ timeslot 3
+ phys_chan_config TCH/F
+ timeslot 4
+ phys_chan_config TCH/F
+ timeslot 5
+ phys_chan_config TCH/F
+ timeslot 6
+ phys_chan_config TCH/F
+ timeslot 7
+ phys_chan_config TCH/F
diff --git a/openbsc/src/paging.c b/openbsc/src/paging.c
index 87c7e7d38..fe6ea52d1 100644
--- a/openbsc/src/paging.c
+++ b/openbsc/src/paging.c
@@ -197,6 +197,8 @@ static void paging_T3113_expired(void *data)
{
struct gsm_paging_request *req = (struct gsm_paging_request *)data;
struct paging_signal_data sig_data;
+ void *cbfn_param;
+ gsm_cbfn *cbfn;
DEBUGP(DPAG, "T3113 expired for request %p (%s)\n",
req, req->subscr->imsi);
@@ -205,11 +207,15 @@ static void paging_T3113_expired(void *data)
sig_data.bts = req->bts;
sig_data.lchan = NULL;
- dispatch_signal(SS_PAGING, S_PAGING_COMPLETED, &sig_data);
- if (req->cbfn)
- req->cbfn(GSM_HOOK_RR_PAGING, GSM_PAGING_EXPIRED, NULL, NULL,
- req->cbfn_param);
+ /* must be destroyed before calling cbfn, to prevent double free */
+ cbfn_param = req->cbfn_param;
+ cbfn = req->cbfn;
paging_remove_request(&req->bts->paging, req);
+
+ dispatch_signal(SS_PAGING, S_PAGING_COMPLETED, &sig_data);
+ if (cbfn)
+ cbfn(GSM_HOOK_RR_PAGING, GSM_PAGING_EXPIRED, NULL, NULL,
+ cbfn_param);
}
static int _paging_request(struct gsm_bts *bts, struct gsm_subscriber *subscr,
@@ -233,7 +239,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))
diff --git a/openbsc/src/rrlp.c b/openbsc/src/rrlp.c
index 61bb20244..523b53f0b 100644
--- a/openbsc/src/rrlp.c
+++ b/openbsc/src/rrlp.c
@@ -26,6 +26,7 @@
#include <openbsc/gsm_04_08.h>
#include <openbsc/signal.h>
#include <openbsc/gsm_subscriber.h>
+#include <openbsc/chan_alloc.h>
/* RRLP MS based position request */
static const u_int8_t ms_based_pos_req[] = { 0x40, 0x01, 0x78, 0xa8 };
diff --git a/openbsc/src/sccp/sccp.c b/openbsc/src/sccp/sccp.c
new file mode 100644
index 000000000..522afcf7a
--- /dev/null
+++ b/openbsc/src/sccp/sccp.c
@@ -0,0 +1,1171 @@
+/*
+ * SCCP management code
+ *
+ * (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * (C) 2009 by on-waves.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.
+ *
+ */
+
+#include <string.h>
+
+#include <sccp/sccp.h>
+
+#include <openbsc/debug.h>
+#include <openbsc/talloc.h>
+#include <openbsc/linuxlist.h>
+
+static void *tall_sccp_ctx;
+static LLIST_HEAD(sccp_connections);
+
+#define SCCP_MSG_SIZE 4096
+#define SCCP_MSG_HEADROOM 128
+
+/* global data */
+const struct sockaddr_sccp sccp_ssn_bssap = {
+ .sccp_family = 0,
+ .sccp_ssn = SCCP_SSN_BSSAP,
+};
+
+struct sccp_system {
+ /* layer3 -> layer2 */
+ int (*write_data)(struct msgb *data, void *context);
+ void *write_context;
+};
+
+
+static struct sccp_system sccp_system = {
+ .write_data = NULL,
+};
+
+struct sccp_data_callback {
+ /* connection based */
+ int (*accept_cb)(struct sccp_connection *, void *);
+ void *accept_context;
+
+ /* connection less */
+ int (*read_cb)(struct msgb *, unsigned int, void *);
+ void *read_context;
+
+ u_int8_t ssn;
+ struct llist_head callback;
+};
+
+static LLIST_HEAD(sccp_callbacks);
+
+static struct sccp_data_callback *_find_ssn(u_int8_t ssn)
+{
+ struct sccp_data_callback *cb;
+
+ llist_for_each_entry(cb, &sccp_callbacks, callback) {
+ if (cb->ssn == ssn)
+ return cb;
+ }
+
+ /* need to add one */
+ cb = talloc_zero(tall_sccp_ctx, struct sccp_data_callback);
+ if (!cb) {
+ DEBUGP(DSCCP, "Failed to allocate sccp callback.\n");
+ return NULL;
+ }
+
+ cb->ssn = ssn;
+ llist_add_tail(&cb->callback, &sccp_callbacks);
+ return cb;
+}
+
+
+static int _send_msg(struct msgb *msg)
+{
+ return sccp_system.write_data(msg, sccp_system.write_context);
+}
+
+/*
+ * parsing routines
+ */
+static int copy_address(struct sccp_address *addr, u_int8_t offset, struct msgb *msgb)
+{
+ struct sccp_called_party_address *party;
+
+ int room = msgb_l2len(msgb) - offset;
+ u_int8_t read = 0;
+ u_int8_t length;
+
+ if (room <= 0) {
+ DEBUGP(DSCCP, "Not enough room for an address: %u\n", room);
+ return -1;
+ }
+
+ length = msgb->l2h[offset];
+ if (room <= length) {
+ DEBUGP(DSCCP, "Not enough room for optional data %u %u\n", room, length);
+ return -1;
+ }
+
+
+ party = (struct sccp_called_party_address *)(msgb->l2h + offset + 1);
+ if (party->point_code_indicator) {
+ if (length <= read + 2) {
+ DEBUGP(DSCCP, "POI does not fit %u\n", length);
+ return -1;
+ }
+
+
+ memcpy(&addr->poi, &party->data[read], 2);
+ read += 2;
+ }
+
+ if (party->ssn_indicator) {
+ if (length <= read + 1) {
+ DEBUGP(DSCCP, "SSN does not fit %u\n", length);
+ return -1;
+ }
+
+ addr->ssn = party->data[read];
+ read += 1;
+ }
+
+ if (party->global_title_indicator) {
+ DEBUGP(DSCCP, "GTI not supported %u\n", *(u_int8_t *)party);
+ return -1;
+ }
+
+ addr->address = *party;
+ return 0;
+}
+
+static int check_address(struct sccp_address *addr)
+{
+ /* ignore point_code_indicator... it should be zero... but */
+ if (addr->address.ssn_indicator != 1
+ || addr->address.global_title_indicator == 1
+ || addr->address.routing_indicator != 1) {
+ DEBUGP(DSCCP, "Invalid called address according to 08.06: 0x%x 0x%x\n",
+ *(u_int8_t *)&addr->address, addr->ssn);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int _sccp_parse_optional_data(const int offset,
+ struct msgb *msgb, struct sccp_optional_data *data)
+{
+ u_int16_t room = msgb_l2len(msgb) - offset;
+ u_int16_t read = 0;
+
+ while (room > read) {
+ u_int8_t type = msgb->l2h[offset + read];
+ if (type == SCCP_PNC_END_OF_OPTIONAL)
+ return 0;
+
+ if (read + 1 >= room) {
+ DEBUGP(DSCCP, "no place for length\n");
+ return 0;
+ }
+
+ u_int8_t length = msgb->l2h[offset + read + 1];
+ read += 2 + length;
+
+
+ if (room <= read) {
+ DEBUGP(DSCCP, "no space for the data: type: %d read: %d room: %d l2: %d\n",
+ type, read, room, msgb_l2len(msgb));
+ return 0;
+ }
+
+ if (type == SCCP_PNC_DATA) {
+ data->data_len = length;
+ data->data_start = offset + read - length;
+ }
+
+ }
+
+ return -1;
+}
+
+/*
+ * Send UDT. Currently we have a fixed address...
+ */
+static int _sccp_send_data(int class, const struct sockaddr_sccp *in,
+ const struct sockaddr_sccp *out, struct msgb *payload)
+{
+ struct sccp_data_unitdata *udt;
+ u_int8_t *data;
+ int ret;
+
+ if (msgb_l3len(payload) > 256) {
+ DEBUGP(DSCCP, "The payload is too big for one udt\n");
+ return -1;
+ }
+
+ struct msgb *msg = msgb_alloc_headroom(SCCP_MSG_SIZE,
+ SCCP_MSG_HEADROOM, "sccp: udt");
+ msg->l2h = &msg->data[0];
+ udt = (struct sccp_data_unitdata *)msgb_put(msg, sizeof(*udt));
+
+ udt->type = SCCP_MSG_TYPE_UDT;
+ udt->proto_class = class;
+ udt->variable_called = 3;
+ udt->variable_calling = 5;
+ udt->variable_data = 7;
+
+ /* for variable data we start with a size and the data */
+ data = msgb_put(msg, 1 + 2);
+ data[0] = 2;
+ data[1] = 0x42;
+ data[2] = out->sccp_ssn;
+
+ data = msgb_put(msg, 1 + 2);
+ data[0] = 2;
+ data[1] = 0x42;
+ data[2] = in->sccp_ssn;
+
+ /* copy the payload */
+ data = msgb_put(msg, 1 + msgb_l3len(payload));
+ data[0] = msgb_l3len(payload);
+ memcpy(&data[1], payload->l3h, msgb_l3len(payload));
+
+ ret = _send_msg(msg);
+ msgb_free(msg);
+
+ return ret;
+}
+
+static int _sccp_handle_read(struct msgb *msgb)
+{
+ static const u_int32_t header_size = sizeof(struct sccp_data_unitdata);
+ static const u_int32_t called_offset = offsetof(struct sccp_data_unitdata, variable_called);
+ static const u_int32_t calling_offset = offsetof(struct sccp_data_unitdata, variable_calling);
+ static const u_int32_t data_offset = offsetof(struct sccp_data_unitdata, variable_data);
+
+ struct sccp_data_callback *cb;
+ struct sccp_data_unitdata *udt = (struct sccp_data_unitdata *)msgb->l2h;
+ struct sccp_address called, calling;
+
+ /* we don't have enough size for the struct */
+ if (msgb_l2len(msgb) < header_size) {
+ DEBUGP(DSCCP, "msgb < header_size %u %u\n",
+ msgb_l2len(msgb), header_size);
+ return -1;
+ }
+
+ /* copy out the calling and called address. Add the off */
+ if (copy_address(&called, called_offset + udt->variable_called, msgb) != 0)
+ return -1;
+
+ if (check_address(&called) != 0) {
+ DEBUGP(DSCCP, "Invalid called address according to 08.06: 0x%x 0x%x\n",
+ *(u_int8_t *)&called.address, called.ssn);
+ return -1;
+ }
+
+ cb = _find_ssn(called.ssn);
+ if (!cb || !cb->read_cb) {
+ DEBUGP(DSCCP, "No routing for UDT for called SSN: %u\n", called.ssn);
+ return -1;
+ }
+
+ if (copy_address(&calling, calling_offset + udt->variable_calling, msgb) != 0)
+ return -1;
+
+ if (check_address(&calling) != 0) {
+ DEBUGP(DSCCP, "Invalid called address according to 08.06: 0x%x 0x%x\n",
+ *(u_int8_t *)&called.address, called.ssn);
+ }
+
+ /* we don't have enough size for the data */
+ if (msgb_l2len(msgb) < data_offset + udt->variable_data + 1) {
+ DEBUGP(DSCCP, "msgb < header + offset %u %u %u\n",
+ msgb_l2len(msgb), header_size, udt->variable_data);
+ return -1;
+ }
+
+
+ msgb->l3h = &udt->data[udt->variable_data];
+
+ if (msgb_l3len(msgb) != msgb->l3h[-1]) {
+ DEBUGP(DSCCP, "msgb is truncated %u %u\n",
+ msgb_l3len(msgb), msgb->l3h[-1]);
+ return -1;
+ }
+
+ /* sanity check */
+ return cb->read_cb(msgb, msgb_l3len(msgb), cb->read_context);
+}
+
+/*
+ * handle connection orientated methods
+ */
+static int source_local_reference_is_free(struct sccp_source_reference *reference)
+{
+ struct sccp_connection *connection;
+
+ llist_for_each_entry(connection, &sccp_connections, list) {
+ if (memcmp(reference, &connection->source_local_reference, sizeof(*reference)) == 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+static int destination_local_reference_is_free(struct sccp_source_reference *reference)
+{
+ struct sccp_connection *connection;
+
+ llist_for_each_entry(connection, &sccp_connections, list) {
+ if (memcmp(reference, &connection->destination_local_reference, sizeof(*reference)) == 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+static int assign_source_local_reference(struct sccp_connection *connection)
+{
+ static u_int32_t last_ref = 0x30000;
+ int wrapped = 0;
+
+ do {
+ struct sccp_source_reference reference;
+ reference.octet1 = (last_ref >> 0) & 0xff;
+ reference.octet2 = (last_ref >> 8) & 0xff;
+ reference.octet3 = (last_ref >> 16) & 0xff;
+
+ ++last_ref;
+ /* do not use the reversed word and wrap around */
+ if ((last_ref & 0x00FFFFFF) == 0x00FFFFFF) {
+ DEBUGP(DSCCP, "Wrapped searching for a free code\n");
+ last_ref = 0;
+ ++wrapped;
+ }
+
+ if (source_local_reference_is_free(&reference) == 0) {
+ connection->source_local_reference = reference;
+ return 0;
+ }
+ } while (wrapped != 2);
+
+ DEBUGP(DSCCP, "Finding a free reference failed\n");
+ return -1;
+}
+
+static void _sccp_set_connection_state(struct sccp_connection *connection, int new_state)
+{
+ int old_state = connection->connection_state;
+
+ connection->connection_state = new_state;
+ if (connection->state_cb)
+ connection->state_cb(connection, old_state);
+}
+
+static int _sccp_send_refuse(struct sccp_connection_request *req, int cause)
+{
+ struct msgb *msgb;
+ struct sccp_connection_refused *ref;
+ u_int8_t *data;
+ int ret;
+
+ msgb = msgb_alloc_headroom(SCCP_MSG_SIZE,
+ SCCP_MSG_HEADROOM, "sccp ref");
+ msgb->l2h = &msgb->data[0];
+
+ ref = (struct sccp_connection_refused *) msgb_put(msgb, sizeof(*ref));
+ ref->type = SCCP_MSG_TYPE_CREF;
+ memcpy(&ref->destination_local_reference, &req->source_local_reference,
+ sizeof(struct sccp_source_reference));
+ ref->cause = cause;
+ ref->optional_start = 1;
+
+ data = msgb_put(msgb, 1);
+ data[0] = SCCP_PNC_END_OF_OPTIONAL;
+
+ ret = _send_msg(msgb);
+ msgb_free(msgb);
+ return ret;
+}
+
+static int _sccp_send_connection_confirm(struct sccp_connection *connection)
+{
+ struct msgb *response;
+ struct sccp_connection_confirm *confirm;
+ u_int8_t *optional_data;
+ int ret;
+
+ if (assign_source_local_reference(connection) != 0)
+ return -1;
+
+ response = msgb_alloc_headroom(SCCP_MSG_SIZE,
+ SCCP_MSG_HEADROOM, "sccp confirm");
+ response->l2h = &response->data[0];
+
+ confirm = (struct sccp_connection_confirm *) msgb_put(response, sizeof(*confirm));
+
+ confirm->type = SCCP_MSG_TYPE_CC;
+ memcpy(&confirm->destination_local_reference,
+ &connection->destination_local_reference,
+ sizeof(connection->destination_local_reference));
+ memcpy(&confirm->source_local_reference,
+ &connection->source_local_reference,
+ sizeof(connection->source_local_reference));
+ confirm->proto_class = 2;
+ confirm->optional_start = 1;
+
+ optional_data = (u_int8_t *) msgb_put(response, 1);
+ optional_data[0] = SCCP_PNC_END_OF_OPTIONAL;
+
+ ret = _send_msg(response);
+ msgb_free(response);
+
+ _sccp_set_connection_state(connection, SCCP_CONNECTION_STATE_ESTABLISHED);
+ return ret;
+}
+
+static int _sccp_send_connection_request(struct sccp_connection *connection,
+ const struct sockaddr_sccp *called, struct msgb *msg)
+{
+ struct msgb *request;
+ struct sccp_connection_request *req;
+ u_int8_t *data;
+ u_int8_t extra_size = 3 + 1;
+ int ret;
+
+
+ if (msg && (msgb_l3len(msg) < 3 || msgb_l3len(msg) > 130)) {
+ DEBUGP(DSCCP, "Invalid amount of data... %d\n", msgb_l3len(msg));
+ return -1;
+ }
+
+ /* try to find a id */
+ if (assign_source_local_reference(connection) != 0) {
+ DEBUGP(DSCCP, "Assigning a local reference failed.\n");
+ _sccp_set_connection_state(connection, SCCP_CONNECTION_STATE_SETUP_ERROR);
+ return -1;
+ }
+
+
+ if (msg)
+ extra_size += 2 + msgb_l3len(msg);
+ request = msgb_alloc_headroom(SCCP_MSG_SIZE,
+ SCCP_MSG_HEADROOM, "sccp connection request");
+ request->l2h = &request->data[0];
+ req = (struct sccp_connection_request *) msgb_put(request, sizeof(*req));
+
+ req->type = SCCP_MSG_TYPE_CR;
+ memcpy(&req->source_local_reference, &connection->source_local_reference,
+ sizeof(connection->source_local_reference));
+ req->proto_class = 2;
+ req->variable_called = 2;
+ req->optional_start = 4;
+
+ /* write the called party address */
+ data = msgb_put(request, 1 + 2);
+ data[0] = 2;
+ data[1] = 0x42;
+ data[2] = called->sccp_ssn;
+
+ /* write the payload */
+ if (msg) {
+ data = msgb_put(request, 2 + msgb_l3len(msg));
+ data[0] = SCCP_PNC_DATA;
+ data[1] = msgb_l3len(msg);
+ memcpy(&data[2], msg->l3h, msgb_l3len(msg));
+ }
+
+ data = msgb_put(request, 1);
+ data[0] = SCCP_PNC_END_OF_OPTIONAL;
+
+ llist_add_tail(&connection->list, &sccp_connections);
+ _sccp_set_connection_state(connection, SCCP_CONNECTION_STATE_REQUEST);
+
+ ret = _send_msg(request);
+ msgb_free(request);
+
+ return ret;
+}
+
+static int _sccp_send_connection_data(struct sccp_connection *conn, struct msgb *_data)
+{
+ struct msgb *msgb;
+ struct sccp_data_form1 *dt1;
+ u_int8_t *data;
+ int extra_size;
+ int ret;
+
+ if (msgb_l3len(_data) < 2 || msgb_l3len(_data) > 256) {
+ DEBUGP(DSCCP, "data size too big, segmenting unimplemented.\n");
+ return -1;
+ }
+
+ extra_size = 1 + msgb_l3len(_data);
+ msgb = msgb_alloc_headroom(SCCP_MSG_SIZE,
+ SCCP_MSG_HEADROOM, "sccp dt1");
+ msgb->l2h = &msgb->data[0];
+
+ dt1 = (struct sccp_data_form1 *) msgb_put(msgb, sizeof(*dt1));
+ dt1->type = SCCP_MSG_TYPE_DT1;
+ memcpy(&dt1->destination_local_reference, &conn->destination_local_reference,
+ sizeof(struct sccp_source_reference));
+ dt1->segmenting = 0;
+
+ /* copy the data */
+ dt1->variable_start = 1;
+ data = msgb_put(msgb, extra_size);
+ data[0] = extra_size - 1;
+ memcpy(&data[1], _data->l3h, extra_size - 1);
+
+ ret = _send_msg(msgb);
+ msgb_free(msgb);
+
+ return ret;
+}
+
+static int _sccp_send_connection_it(struct sccp_connection *conn)
+{
+ struct msgb *msgb;
+ struct sccp_data_it *it;
+ int ret;
+
+ msgb = msgb_alloc_headroom(SCCP_MSG_SIZE,
+ SCCP_MSG_HEADROOM, "sccp it");
+ msgb->l2h = &msgb->data[0];
+ it = (struct sccp_data_it *) msgb_put(msgb, sizeof(*it));
+ it->type = SCCP_MSG_TYPE_IT;
+ memcpy(&it->destination_local_reference, &conn->destination_local_reference,
+ sizeof(struct sccp_source_reference));
+ memcpy(&it->source_local_reference, &conn->source_local_reference,
+ sizeof(struct sccp_source_reference));
+
+ it->proto_class = 0x2;
+ it->sequencing[0] = it->sequencing[1] = 0;
+ it->credit = 0;
+
+ ret = _send_msg(msgb);
+ msgb_free(msgb);
+ return ret;
+}
+
+static int _sccp_send_connection_released(struct sccp_connection *conn, int cause)
+{
+ struct msgb *msg;
+ struct sccp_connection_released *rel;
+ u_int8_t *data;
+ int ret;
+
+ msg = msgb_alloc_headroom(SCCP_MSG_SIZE, SCCP_MSG_HEADROOM,
+ "sccp: connection released");
+ msg->l2h = &msg->data[0];
+ rel = (struct sccp_connection_released *) msgb_put(msg, sizeof(*rel));
+ rel->type = SCCP_MSG_TYPE_RLSD;
+ rel->release_cause = cause;
+
+ /* copy the source references */
+ memcpy(&rel->destination_local_reference, &conn->destination_local_reference,
+ sizeof(struct sccp_source_reference));
+ memcpy(&rel->source_local_reference, &conn->source_local_reference,
+ sizeof(struct sccp_source_reference));
+
+ data = msgb_put(msg, 1);
+ data[0] = SCCP_PNC_END_OF_OPTIONAL;
+
+ _sccp_set_connection_state(conn, SCCP_CONNECTION_STATE_RELEASE);
+ ret = _send_msg(msg);
+ msgb_free(msg);
+
+ return ret;
+}
+
+/*
+ * Open a connection. The following is going to happen:
+ *
+ * - Verify the packet, e.g. that we have no other connection
+ * that id.
+ * - Ask the user if he wants to accept the connection
+ * - Try to open the connection by assigning a source local reference
+ * and sending the packet
+ */
+static int _sccp_handle_connection_request(struct msgb *msgb)
+{
+ static const u_int32_t header_size =
+ sizeof(struct sccp_connection_request);
+ static const u_int32_t optional_offset =
+ offsetof(struct sccp_connection_request, optional_start);
+ static const u_int32_t called_offset =
+ offsetof(struct sccp_connection_request, variable_called);
+
+ struct sccp_data_callback *cb;
+ struct sccp_connection_request *req = (struct sccp_connection_request *)msgb->data;
+ struct sccp_address called;
+ struct sccp_connection *connection;
+ struct sccp_optional_data optional_data;
+
+ /* header check */
+ if (msgb_l2len(msgb) < header_size) {
+ DEBUGP(DSCCP, "msgb < header_size %u %u\n",
+ msgb_l2len(msgb), header_size);
+ return -1;
+ }
+
+ /* copy out the calling and called address. Add the offset */
+ if (copy_address(&called, called_offset + req->variable_called, msgb) != 0)
+ return -1;
+
+ if (check_address(&called) != 0) {
+ DEBUGP(DSCCP, "Invalid called address according to 08.06: 0x%x 0x%x\n",
+ *(u_int8_t *)&called.address, called.ssn);
+ return -1;
+ }
+
+ cb = _find_ssn(called.ssn);
+ if (!cb || !cb->accept_cb) {
+ DEBUGP(DSCCP, "No routing for CR for called SSN: %u\n", called.ssn);
+ return -1;
+ }
+
+ /* check if the system wants this connection */
+ connection = talloc_zero(tall_sccp_ctx, struct sccp_connection);
+ if (!connection) {
+ DEBUGP(DSCCP, "Allocation failed\n");
+ return -1;
+ }
+
+ /*
+ * sanity checks:
+ * - Is the source_local_reference in any other connection?
+ * then will call accept, assign a "destination" local reference
+ * and send a connection confirm, otherwise we will send a refuseed
+ * one....
+ */
+ if (destination_local_reference_is_free(&req->source_local_reference) != 0) {
+ DEBUGP(DSCCP, "Need to reject connection with existing reference\n");
+ _sccp_send_refuse(req, SCCP_REFUSAL_SCCP_FAILURE);
+ talloc_free(connection);
+ return -1;
+ }
+
+ connection->incoming = 1;
+ connection->destination_local_reference = req->source_local_reference;
+
+ /*
+ * parse optional data.
+ */
+ memset(&optional_data, 0, sizeof(optional_data));
+ if (_sccp_parse_optional_data(optional_offset + req->optional_start, msgb, &optional_data) != 0) {
+ DEBUGP(DSCCP, "parsing of optional data failed.\n");
+ talloc_free(connection);
+ return -1;
+ }
+
+ if (cb->accept_cb(connection, cb->accept_context) != 0) {
+ _sccp_send_refuse(req, SCCP_REFUSAL_END_USER_ORIGINATED);
+ _sccp_set_connection_state(connection, SCCP_CONNECTION_STATE_REFUSED);
+ talloc_free(connection);
+ return 0;
+ }
+
+
+ llist_add_tail(&connection->list, &sccp_connections);
+
+ if (_sccp_send_connection_confirm(connection) != 0) {
+ DEBUGP(DSCCP, "Sending confirm failed... no available source reference?\n");
+
+ _sccp_send_refuse(req, SCCP_REFUSAL_SCCP_FAILURE);
+ _sccp_set_connection_state(connection, SCCP_CONNECTION_STATE_REFUSED);
+ llist_del(&connection->list);
+ talloc_free(connection);
+
+ return -1;
+ }
+
+ /*
+ * If we have data let us forward things.
+ */
+ if (optional_data.data_len != 0 && connection->data_cb) {
+ msgb->l3h = &msgb->l2h[optional_data.data_start];
+ connection->data_cb(connection, msgb, optional_data.data_len);
+ }
+
+ return 0;
+}
+
+/* Handle the release confirmed */
+static int _sccp_handle_connection_release_complete(struct msgb *data)
+{
+ static int header_size = sizeof(struct sccp_connection_release_complete);
+
+ struct sccp_connection_release_complete *cmpl;
+ struct sccp_connection *conn;
+
+ /* header check */
+ if (msgb_l2len(data) < header_size) {
+ DEBUGP(DSCCP, "msgb < header_size %u %u\n",
+ msgb_l2len(data), header_size);
+ return -1;
+ }
+
+ cmpl = (struct sccp_connection_release_complete *) data->l2h;
+
+ /* find the connection */
+ llist_for_each_entry(conn, &sccp_connections, list) {
+ if (conn->data_cb
+ && memcmp(&conn->source_local_reference,
+ &cmpl->destination_local_reference,
+ sizeof(conn->source_local_reference)) == 0
+ && memcmp(&conn->destination_local_reference,
+ &cmpl->source_local_reference,
+ sizeof(conn->destination_local_reference)) == 0) {
+ goto found;
+ }
+ }
+
+
+ DEBUGP(DSCCP, "Release complete of unknown connection\n");
+ return -1;
+
+found:
+ llist_del(&conn->list);
+ _sccp_set_connection_state(conn, SCCP_CONNECTION_STATE_RELEASE_COMPLETE);
+ return 0;
+}
+
+/* Handle the Data Form 1 message */
+static int _sccp_handle_connection_dt1(struct msgb *data)
+{
+ static int variable_offset = offsetof(struct sccp_data_form1, variable_start);
+ static int header_size = sizeof(struct sccp_data_form1);
+
+ struct sccp_data_form1 *dt1 = (struct sccp_data_form1 *)data->l2h;
+ struct sccp_connection *conn;
+ int size;
+
+ /* we don't have enough size for the struct */
+ if (msgb_l2len(data) < header_size) {
+ DEBUGP(DSCCP, "msgb > header_size %u %u\n",
+ msgb_l2len(data), header_size);
+ return -1;
+ }
+
+ if (dt1->segmenting != 0) {
+ DEBUGP(DSCCP, "This packet has segmenting, not supported: %d\n", dt1->segmenting);
+ return -1;
+ }
+
+ /* lookup if we have a connection with the given reference */
+ llist_for_each_entry(conn, &sccp_connections, list) {
+ if (conn->data_cb
+ && memcmp(&conn->source_local_reference,
+ &dt1->destination_local_reference,
+ sizeof(conn->source_local_reference)) == 0) {
+
+ /* some more size checks in here */
+ if (msgb_l2len(data) < variable_offset + dt1->variable_start + 1) {
+ DEBUGP(DSCCP, "Not enough space for variable start: %u %u\n",
+ msgb_l2len(data), dt1->variable_start);
+ return -1;
+ }
+
+ size = data->l2h[variable_offset + dt1->variable_start];
+ data->l3h = &data->l2h[dt1->variable_start + variable_offset + 1];
+
+ if (msgb_l3len(data) < size) {
+ DEBUGP(DSCCP, "Not enough room for the payload: %u %u\n",
+ msgb_l3len(data), size);
+ return -1;
+ }
+
+ conn->data_cb(conn, data, size);
+ return 0;
+ }
+ }
+
+ DEBUGP(DSCCP, "No connection found for dt1 data\n");
+ return -1;
+}
+
+/* confirm a connection release */
+static int _sccp_send_connection_release_complete(struct sccp_connection *connection)
+{
+ struct msgb *msgb;
+ struct sccp_connection_release_complete *rlc;
+ int ret;
+
+ msgb = msgb_alloc_headroom(SCCP_MSG_SIZE,
+ SCCP_MSG_HEADROOM, "sccp rlc");
+ msgb->l2h = &msgb->data[0];
+
+ rlc = (struct sccp_connection_release_complete *) msgb_put(msgb, sizeof(*rlc));
+ rlc->type = SCCP_MSG_TYPE_RLC;
+ memcpy(&rlc->destination_local_reference,
+ &connection->destination_local_reference, sizeof(struct sccp_source_reference));
+ memcpy(&rlc->source_local_reference,
+ &connection->source_local_reference, sizeof(struct sccp_source_reference));
+
+ ret = _send_msg(msgb);
+ msgb_free(msgb);
+
+ /*
+ * Remove from the list of active connections and set the state. User code
+ * should now free the entry.
+ */
+ llist_del(&connection->list);
+ _sccp_set_connection_state(connection, SCCP_CONNECTION_STATE_RELEASE_COMPLETE);
+
+ return ret;
+}
+
+/* connection released, send a released confirm */
+static int _sccp_handle_connection_released(struct msgb *data)
+{
+ static int header_size = sizeof(struct sccp_connection_released);
+ static int optional_offset = offsetof(struct sccp_connection_released, optional_start);
+
+ struct sccp_optional_data optional_data;
+ struct sccp_connection_released *rls = (struct sccp_connection_released *)data->l2h;
+ struct sccp_connection *conn;
+
+ /* we don't have enough size for the struct */
+ if (msgb_l2len(data) < header_size) {
+ DEBUGP(DSCCP, "msgb > header_size %u %u\n",
+ msgb_l2len(data), header_size);
+ return -1;
+ }
+
+ /* lookup if we have a connection with the given reference */
+ llist_for_each_entry(conn, &sccp_connections, list) {
+ if (conn->data_cb
+ && memcmp(&conn->source_local_reference,
+ &rls->destination_local_reference,
+ sizeof(conn->source_local_reference)) == 0
+ && memcmp(&conn->destination_local_reference,
+ &rls->source_local_reference,
+ sizeof(conn->destination_local_reference)) == 0) {
+ goto found;
+ }
+ }
+
+
+ DEBUGP(DSCCP, "Unknown connection was released.\n");
+ return -1;
+
+ /* we have found a connection */
+found:
+ memset(&optional_data, 0, sizeof(optional_data));
+ if (_sccp_parse_optional_data(optional_offset + rls->optional_start, data, &optional_data) != 0) {
+ DEBUGP(DSCCP, "parsing of optional data failed.\n");
+ return -1;
+ }
+
+ /* optional data */
+ if (optional_data.data_len != 0 && conn->data_cb) {
+ data->l3h = &data->l2h[optional_data.data_start];
+ conn->data_cb(conn, data, optional_data.data_len);
+ }
+
+ /* generate a response */
+ if (_sccp_send_connection_release_complete(conn) != 0) {
+ DEBUGP(DSCCP, "Sending release confirmed failed\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int _sccp_handle_connection_refused(struct msgb *msgb)
+{
+ static const u_int32_t header_size =
+ sizeof(struct sccp_connection_refused);
+ static int optional_offset = offsetof(struct sccp_connection_refused, optional_start);
+
+ struct sccp_optional_data optional_data;
+ struct sccp_connection *conn;
+ struct sccp_connection_refused *ref;
+
+ /* header check */
+ if (msgb_l2len(msgb) < header_size) {
+ DEBUGP(DSCCP, "msgb < header_size %u %u\n",
+ msgb_l2len(msgb), header_size);
+ return -1;
+ }
+
+ ref = (struct sccp_connection_refused *) msgb->l2h;
+
+ /* lookup if we have a connection with the given reference */
+ llist_for_each_entry(conn, &sccp_connections, list) {
+ if (conn->incoming == 0 && conn->data_cb
+ && memcmp(&conn->source_local_reference,
+ &ref->destination_local_reference,
+ sizeof(conn->source_local_reference)) == 0) {
+ goto found;
+ }
+ }
+
+ DEBUGP(DSCCP, "Refused but no connection found\n");
+ return -1;
+
+found:
+ memset(&optional_data, 0, sizeof(optional_data));
+ if (_sccp_parse_optional_data(optional_offset + ref->optional_start, msgb, &optional_data) != 0) {
+ DEBUGP(DSCCP, "parsing of optional data failed.\n");
+ return -1;
+ }
+
+ /* optional data */
+ if (optional_data.data_len != 0 && conn->data_cb) {
+ msgb->l3h = &msgb->l2h[optional_data.data_start];
+ conn->data_cb(conn, msgb, optional_data.data_len);
+ }
+
+
+ llist_del(&conn->list);
+ _sccp_set_connection_state(conn, SCCP_CONNECTION_STATE_REFUSED);
+ return 0;
+}
+
+static int _sccp_handle_connection_confirm(struct msgb *msgb)
+{
+ static u_int32_t header_size =
+ sizeof(struct sccp_connection_confirm);
+ static const u_int32_t optional_offset =
+ offsetof(struct sccp_connection_confirm, optional_start);
+
+ struct sccp_optional_data optional_data;
+ struct sccp_connection *conn;
+ struct sccp_connection_confirm *con;
+
+ /* header check */
+ if (msgb_l2len(msgb) < header_size) {
+ DEBUGP(DSCCP, "msgb < header_size %u %u\n",
+ msgb_l2len(msgb), header_size);
+ return -1;
+ }
+
+ con = (struct sccp_connection_confirm *) msgb->l2h;
+
+ /* lookup if we have a connection with the given reference */
+ llist_for_each_entry(conn, &sccp_connections, list) {
+ if (conn->incoming == 0 && conn->data_cb
+ && memcmp(&conn->source_local_reference,
+ &con->destination_local_reference,
+ sizeof(conn->source_local_reference)) == 0) {
+ goto found;
+ }
+ }
+
+ DEBUGP(DSCCP, "Confirmed but no connection found\n");
+ return -1;
+
+found:
+ /* copy the addresses of the connection */
+ conn->destination_local_reference = con->source_local_reference;
+ _sccp_set_connection_state(conn, SCCP_CONNECTION_STATE_ESTABLISHED);
+
+ memset(&optional_data, 0, sizeof(optional_data));
+ if (_sccp_parse_optional_data(optional_offset + con->optional_start, msgb, &optional_data) != 0) {
+ DEBUGP(DSCCP, "parsing of optional data failed.\n");
+ return -1;
+ }
+
+ /* optional data */
+ if (optional_data.data_len != 0 && conn->data_cb) {
+ msgb->l3h = &msgb->l2h[optional_data.data_start];
+ conn->data_cb(conn, msgb, optional_data.data_len);
+ }
+
+ return 0;
+}
+
+
+int sccp_system_init(int (*outgoing)(struct msgb *data, void *ctx), void *ctx)
+{
+ sccp_system.write_data = outgoing;
+ sccp_system.write_context = ctx;
+
+ return 0;
+}
+
+/* oh my god a real SCCP packet. need to dispatch it now */
+int sccp_system_incoming(struct msgb *msgb)
+{
+ if (msgb_l2len(msgb) < 1 ) {
+ DEBUGP(DSCCP, "Too short packet\n");
+ return -1;
+ }
+
+ int type = msgb->l2h[0];
+
+ switch(type) {
+ case SCCP_MSG_TYPE_CR:
+ return _sccp_handle_connection_request(msgb);
+ break;
+ case SCCP_MSG_TYPE_RLSD:
+ return _sccp_handle_connection_released(msgb);
+ break;
+ case SCCP_MSG_TYPE_CREF:
+ return _sccp_handle_connection_refused(msgb);
+ break;
+ case SCCP_MSG_TYPE_CC:
+ return _sccp_handle_connection_confirm(msgb);
+ break;
+ case SCCP_MSG_TYPE_RLC:
+ return _sccp_handle_connection_release_complete(msgb);
+ break;
+ case SCCP_MSG_TYPE_DT1:
+ return _sccp_handle_connection_dt1(msgb);
+ break;
+ case SCCP_MSG_TYPE_UDT:
+ return _sccp_handle_read(msgb);
+ break;
+ default:
+ DEBUGP(DSCCP, "unimplemented msg type: %d\n", type);
+ };
+
+ return -1;
+}
+
+/* create a packet from the data */
+int sccp_connection_write(struct sccp_connection *connection, struct msgb *data)
+{
+ if (connection->connection_state < SCCP_CONNECTION_STATE_CONFIRM
+ || connection->connection_state > SCCP_CONNECTION_STATE_ESTABLISHED) {
+ DEBUGP(DSCCP, "sccp_connection_write: Wrong connection state: %p %d\n",
+ connection, connection->connection_state);
+ return -1;
+ }
+
+ return _sccp_send_connection_data(connection, data);
+}
+
+/*
+ * Send a Inactivity Test message. The owner of the connection
+ * should start a timer and call this method regularily. Calling
+ * this every 60 seconds should be good enough.
+ */
+int sccp_connection_send_it(struct sccp_connection *connection)
+{
+ if (connection->connection_state < SCCP_CONNECTION_STATE_CONFIRM
+ || connection->connection_state > SCCP_CONNECTION_STATE_ESTABLISHED) {
+ DEBUGP(DSCCP, "sccp_connection_write: Wrong connection state: %p %d\n",
+ connection, connection->connection_state);
+ return -1;
+ }
+
+ return _sccp_send_connection_it(connection);
+}
+
+/* send a connection release and wait for the connection released */
+int sccp_connection_close(struct sccp_connection *connection, int cause)
+{
+ if (connection->connection_state < SCCP_CONNECTION_STATE_CONFIRM
+ || connection->connection_state > SCCP_CONNECTION_STATE_ESTABLISHED) {
+ DEBUGPC(DSCCP, "Can not close the connection. It was never opened: %p %d\n",
+ connection, connection->connection_state);
+ return -1;
+ }
+
+ return _sccp_send_connection_released(connection, cause);
+}
+
+int sccp_connection_free(struct sccp_connection *connection)
+{
+ if (connection->connection_state > SCCP_CONNECTION_STATE_NONE
+ && connection->connection_state < SCCP_CONNECTION_STATE_RELEASE_COMPLETE) {
+ DEBUGP(DSCCP, "The connection needs to be released before it is freed");
+ return -1;
+ }
+
+ talloc_free(connection);
+ return 0;
+}
+
+struct sccp_connection *sccp_connection_socket(void)
+{
+ return talloc_zero(tall_sccp_ctx, struct sccp_connection);
+}
+
+int sccp_connection_connect(struct sccp_connection *conn,
+ const struct sockaddr_sccp *local,
+ struct msgb *data)
+{
+ return _sccp_send_connection_request(conn, local, data);
+}
+
+int sccp_connection_set_incoming(const struct sockaddr_sccp *sock,
+ int (*accept_cb)(struct sccp_connection *, void *), void *context)
+{
+ struct sccp_data_callback *cb;
+
+ if (!sock)
+ return -2;
+
+ cb = _find_ssn(sock->sccp_ssn);
+ if (!cb)
+ return -1;
+
+ cb->accept_cb = accept_cb;
+ cb->accept_context = context;
+ return 0;
+}
+
+int sccp_write(struct msgb *data, const struct sockaddr_sccp *in,
+ const struct sockaddr_sccp *out, int class)
+{
+ return _sccp_send_data(class, in, out, data);
+}
+
+int sccp_set_read(const struct sockaddr_sccp *sock,
+ int (*read_cb)(struct msgb *, unsigned int, void *), void *context)
+{
+ struct sccp_data_callback *cb;
+
+ if (!sock)
+ return -2;
+
+ cb = _find_ssn(sock->sccp_ssn);
+ if (!cb)
+ return -1;
+
+ cb->read_cb = read_cb;
+ cb->read_context = context;
+ return 0;
+}
+
+static_assert(sizeof(struct sccp_source_reference) <= sizeof(u_int32_t), enough_space);
+
+u_int32_t sccp_src_ref_to_int(struct sccp_source_reference *ref)
+{
+ u_int32_t src_ref = 0;
+ memcpy(&src_ref, ref, sizeof(*ref));
+ return src_ref;
+}
+
+struct sccp_source_reference sccp_src_ref_from_int(u_int32_t int_ref)
+{
+ struct sccp_source_reference ref;
+ memcpy(&ref, &int_ref, sizeof(ref));
+ return ref;
+}
+
+static __attribute__((constructor)) void on_dso_load(void)
+{
+ tall_sccp_ctx = talloc_named_const(NULL, 1, "sccp");
+}
+
+static __attribute__((destructor)) void on_dso_unload(void)
+{
+ talloc_report_full(tall_sccp_ctx, stderr);
+}
diff --git a/openbsc/src/silent_call.c b/openbsc/src/silent_call.c
new file mode 100644
index 000000000..82b656327
--- /dev/null
+++ b/openbsc/src/silent_call.c
@@ -0,0 +1,93 @@
+/* GSM silent call feature */
+
+/*
+ * (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 <unistd.h>
+#include <errno.h>
+
+#include <openbsc/msgb.h>
+#include <openbsc/signal.h>
+#include <openbsc/debug.h>
+#include <openbsc/paging.h>
+#include <openbsc/gsm_data.h>
+#include <openbsc/gsm_subscriber.h>
+#include <openbsc/abis_rsl.h>
+#include <openbsc/chan_alloc.h>
+
+static int paging_cb_silent(unsigned int hooknum, unsigned int event,
+ struct msgb *msg, void *_lchan, void *_data)
+{
+ struct gsm_lchan *lchan = _lchan;
+ struct scall_signal_data sigdata;
+ int rc;
+
+ if (hooknum != GSM_HOOK_RR_PAGING)
+ return -EINVAL;
+
+ DEBUGP(DSMS, "paging_cb_silent: ");
+
+ sigdata.lchan = lchan;
+ sigdata.data = _data;
+
+ switch (event) {
+ case GSM_PAGING_SUCCEEDED:
+ DEBUGPC(DSMS, "success, using Timeslot %u on ARFCN %u\n",
+ lchan->ts->nr, lchan->ts->trx->arfcn);
+ /* increment lchan reference count */
+ dispatch_signal(SS_SCALL, S_SCALL_SUCCESS, &sigdata);
+ use_lchan(lchan);
+ break;
+ case GSM_PAGING_EXPIRED:
+ DEBUGP(DSMS, "expired\n");
+ dispatch_signal(SS_SCALL, S_SCALL_EXPIRED, &sigdata);
+ break;
+ default:
+ rc = -EINVAL;
+ break;
+ }
+
+ return rc;
+}
+
+int gsm_silent_call_start(struct gsm_subscriber *subscr, void *data)
+{
+ int rc;
+
+ rc = paging_request(subscr->net, subscr, RSL_CHANNEED_TCH_F,
+ paging_cb_silent, data);
+ return rc;
+}
+
+int gsm_silent_call_stop(struct gsm_subscriber *subscr)
+{
+ struct gsm_lchan *lchan;
+
+ lchan = lchan_for_subscr(subscr);
+ if (!lchan)
+ return -EINVAL;
+
+ /* FIXME: did we actually establish a silent call for this guy? */
+ put_lchan(lchan);
+
+ 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/telnet_interface.c b/openbsc/src/telnet_interface.c
index d7c905518..128c34e94 100644
--- a/openbsc/src/telnet_interface.c
+++ b/openbsc/src/telnet_interface.c
@@ -165,7 +165,6 @@ static int telnet_new_connection(struct bsc_fd *fd, unsigned int what) {
connection->fd.fd = new_connection;
connection->fd.when = BSC_FD_READ;
connection->fd.cb = client_data;
- connection->bts = 0;
bsc_register_fd(&connection->fd);
llist_add_tail(&connection->entry, &active_connections);
diff --git a/openbsc/src/timer.c b/openbsc/src/timer.c
index 6f974a2c3..ffeb4aba3 100644
--- a/openbsc/src/timer.c
+++ b/openbsc/src/timer.c
@@ -176,7 +176,7 @@ restart:
int bsc_timer_check(void)
{
struct timer_list *timer;
- int i;
+ int i = 0;
llist_for_each_entry(timer, &timer_list, entry) {
i++;
diff --git a/openbsc/src/tlv_parser.c b/openbsc/src/tlv_parser.c
index e835f951f..fd0045f97 100644
--- a/openbsc/src/tlv_parser.c
+++ b/openbsc/src/tlv_parser.c
@@ -1,5 +1,8 @@
#include <stdio.h>
#include <openbsc/tlv.h>
+#include <openbsc/gsm_data.h>
+
+struct tlv_definition tvlv_att_def;
int tlv_dump(struct tlv_parsed *dec)
{
@@ -13,6 +16,82 @@ int tlv_dump(struct tlv_parsed *dec)
return 0;
}
+/* o_tag: output: tag found
+ * o_len: output: length of the data
+ * o_val: output: pointer to the data
+ * def: input: a structure defining the valid TLV tags / configurations
+ * buf: input: the input data buffer to be parsed
+ * buf_len: input: the length of the input data buffer
+ *
+ * Also, returns the number of bytes consumed by the TLV entry
+ */
+int tlv_parse_one(u_int8_t *o_tag, u_int16_t *o_len, const u_int8_t **o_val,
+ const struct tlv_definition *def,
+ const u_int8_t *buf, int buf_len)
+{
+ u_int8_t tag;
+ int len;
+
+ tag = *buf;
+ *o_tag = tag;
+
+ /* FIXME: use tables for knwon IEI */
+ switch (def->def[tag].type) {
+ case TLV_TYPE_T:
+ /* GSM TS 04.07 11.2.4: Type 1 TV or Type 2 T */
+ *o_val = buf;
+ *o_len = 0;
+ len = 1;
+ break;
+ case TLV_TYPE_TV:
+ *o_val = buf+1;
+ *o_len = 1;
+ len = 2;
+ break;
+ case TLV_TYPE_FIXED:
+ *o_val = buf+1;
+ *o_len = def->def[tag].fixed_len;
+ len = def->def[tag].fixed_len + 1;
+ break;
+ case TLV_TYPE_TLV:
+ /* GSM TS 04.07 11.2.4: Type 4 TLV */
+ if (buf + 1 > buf + buf_len)
+ return -1;
+ *o_val = buf+2;
+ *o_len = *(buf+1);
+ len = *o_len + 2;
+ if (len > buf_len)
+ return -2;
+ break;
+ case TLV_TYPE_TvLV:
+ if (*(buf+1) & 0x80) {
+ /* like TLV, but without highest bit of len */
+ if (buf + 1 > buf + buf_len)
+ return -1;
+ *o_val = buf+2;
+ *o_len = *(buf+1) & 0x7f;
+ len = *o_len + 2;
+ if (len > buf_len)
+ return -2;
+ break;
+ }
+ /* like TL16V, fallthrough */
+ case TLV_TYPE_TL16V:
+ if (2 > buf_len)
+ return -1;
+ *o_val = buf+3;
+ *o_len = *(buf+1) << 8 | *(buf+2);
+ len = *o_len + 3;
+ if (len > buf_len)
+ return -2;
+ break;
+ default:
+ return -3;
+ }
+
+ return len;
+}
+
/* dec: output: a caller-allocated pointer to a struct tlv_parsed,
* def: input: a structure defining the valid TLV tags / configurations
* buf: input: the input data buffer to be parsed
@@ -24,82 +103,55 @@ int tlv_parse(struct tlv_parsed *dec, const struct tlv_definition *def,
const u_int8_t *buf, int buf_len, u_int8_t lv_tag,
u_int8_t lv_tag2)
{
- u_int8_t tag, len = 1;
- const u_int8_t *pos = buf;
- int num_parsed = 0;
+ int ofs = 0, num_parsed = 0;
+ u_int16_t len;
memset(dec, 0, sizeof(*dec));
if (lv_tag) {
- if (pos > buf + buf_len)
+ if (ofs > buf_len)
return -1;
- dec->lv[lv_tag].val = pos+1;
- dec->lv[lv_tag].len = *pos;
+ dec->lv[lv_tag].val = &buf[ofs+1];
+ dec->lv[lv_tag].len = buf[ofs];
len = dec->lv[lv_tag].len + 1;
- if (pos + len > buf + buf_len)
+ if (ofs + len > buf_len)
return -2;
num_parsed++;
- pos += len;
+ ofs += len;
}
if (lv_tag2) {
- if (pos > buf + buf_len)
+ if (ofs > buf_len)
return -1;
- dec->lv[lv_tag2].val = pos+1;
- dec->lv[lv_tag2].len = *pos;
+ dec->lv[lv_tag2].val = &buf[ofs+1];
+ dec->lv[lv_tag2].len = buf[ofs];
len = dec->lv[lv_tag2].len + 1;
- if (pos + len > buf + buf_len)
+ if (ofs + len > buf_len)
return -2;
num_parsed++;
- pos += len;
+ ofs += len;
}
- for (; pos < buf+buf_len; pos += len) {
- tag = *pos;
- /* FIXME: use tables for knwon IEI */
- switch (def->def[tag].type) {
- case TLV_TYPE_T:
- /* GSM TS 04.07 11.2.4: Type 1 TV or Type 2 T */
- dec->lv[tag].val = pos;
- dec->lv[tag].len = 0;
- len = 1;
- num_parsed++;
- break;
- case TLV_TYPE_TV:
- dec->lv[tag].val = pos+1;
- dec->lv[tag].len = 1;
- len = 2;
- num_parsed++;
- break;
- case TLV_TYPE_FIXED:
- dec->lv[tag].val = pos+1;
- dec->lv[tag].len = def->def[tag].fixed_len;
- len = def->def[tag].fixed_len + 1;
- num_parsed++;
- break;
- case TLV_TYPE_TLV:
- /* GSM TS 04.07 11.2.4: Type 4 TLV */
- if (pos + 1 > buf + buf_len)
- return -1;
- dec->lv[tag].val = pos+2;
- dec->lv[tag].len = *(pos+1);
- len = dec->lv[tag].len + 2;
- if (pos + len > buf + buf_len)
- return -2;
- num_parsed++;
- break;
- case TLV_TYPE_TL16V:
- if (pos + 2 > buf + buf_len)
- return -1;
- dec->lv[tag].val = pos+3;
- dec->lv[tag].len = *(pos+1) << 8 | *(pos+2);
- len = dec->lv[tag].len + 3;
- if (pos + len > buf + buf_len)
- return -2;
- num_parsed++;
- break;
- }
+ while (ofs < buf_len) {
+ int rv;
+ u_int8_t tag;
+ const u_int8_t *val;
+
+ rv = tlv_parse_one(&tag, &len, &val, def,
+ &buf[ofs], buf_len-ofs);
+ if (rv < 0)
+ return rv;
+ dec->lv[tag].val = val;
+ dec->lv[tag].len = len;
+ ofs += rv;
+ num_parsed++;
}
//tlv_dump(dec);
return num_parsed;
}
+static __attribute__((constructor)) void on_dso_load_tlv(void)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(tvlv_att_def.def); i++)
+ tvlv_att_def.def[i].type = TLV_TYPE_TvLV;
+}
diff --git a/openbsc/src/token_auth.c b/openbsc/src/token_auth.c
index 695b55243..0931007ef 100644
--- a/openbsc/src/token_auth.c
+++ b/openbsc/src/token_auth.c
@@ -33,6 +33,9 @@
#define TOKEN_SMS_TEXT "HAR 2009 GSM. Register at http://har2009.gnumonks.org/ " \
"Your IMSI is %s, auth token is %08X, phone no is %s."
+extern struct gsm_sms *sms_from_text(struct gsm_subscriber *receiver,
+ const char *text);
+
static char *build_sms_string(struct gsm_subscriber *subscr, u_int32_t token)
{
char *sms_str;
diff --git a/openbsc/src/transaction.c b/openbsc/src/transaction.c
index 950faa2f1..04eaa3c99 100644
--- a/openbsc/src/transaction.c
+++ b/openbsc/src/transaction.c
@@ -86,8 +86,6 @@ struct gsm_trans *trans_alloc(struct gsm_subscriber *subscr,
void trans_free(struct gsm_trans *trans)
{
- struct gsm_bts *bts;
-
switch (trans->protocol) {
case GSM48_PDISC_CC:
_gsm48_cc_trans_free(trans);
diff --git a/openbsc/src/ussd.c b/openbsc/src/ussd.c
new file mode 100644
index 000000000..a3d11f080
--- /dev/null
+++ b/openbsc/src/ussd.c
@@ -0,0 +1,71 @@
+/* Network-specific handling of mobile-originated USSDs. */
+
+/* (C) 2008-2009 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2008, 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * (C) 2009 by Mike Haben <michael.haben@btinternet.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.
+ *
+ */
+
+/* This module defines the network-specific handling of mobile-originated
+ USSD messages. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <openbsc/gsm_04_80.h>
+#include <openbsc/gsm_subscriber.h>
+#include <openbsc/debug.h>
+
+/* Declarations of USSD strings to be recognised */
+const char USSD_TEXT_OWN_NUMBER[] = "*#100#";
+
+/* Forward declarations of network-specific handler functions */
+static int send_own_number(const struct msgb *msg, const struct ussd_request *req);
+
+
+/* Entrypoint - handler function common to all mobile-originated USSDs */
+int handle_rcv_ussd(struct msgb *msg)
+{
+ struct ussd_request req;
+
+ gsm0480_decode_ussd_request(msg, &req);
+ if (req.text[0] == 0xFF) /* Release-Complete */
+ return 0;
+
+ if (strstr(USSD_TEXT_OWN_NUMBER, req.text) != NULL) {
+ DEBUGP(DMM, "USSD: Own number requested\n");
+ return send_own_number(msg, &req);
+ } else {
+ DEBUGP(DMM, "Unhandled USSD %s\n", req.text);
+ return gsm0480_send_ussd_reject(msg, &req);
+ }
+}
+
+/* A network-specific handler function */
+static int send_own_number(const struct msgb *msg, const struct ussd_request *req)
+{
+ char *own_number = msg->lchan->subscr->extension;
+ char response_string[GSM_EXTENSION_LENGTH + 20];
+
+ /* Need trailing CR as EOT character */
+ snprintf(response_string, sizeof(response_string), "Your extension is %s\r", own_number);
+ return gsm0480_send_ussd_response(msg, response_string, req);
+}
diff --git a/openbsc/src/vty_interface.c b/openbsc/src/vty_interface.c
index d6f1bb54e..066dfd5a9 100644
--- a/openbsc/src/vty_interface.c
+++ b/openbsc/src/vty_interface.c
@@ -87,6 +87,8 @@ static void net_dump_vty(struct vty *vty, struct gsm_network *net)
gsm_auth_policy_name(net->auth_policy), 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);
}
DEFUN(show_net, show_net_cmd, "show network",
@@ -127,17 +129,19 @@ static void bts_dump_vty(struct vty *vty, struct gsm_bts *bts)
if (bts->cell_barred)
vty_out(vty, " CELL IS BARRED%s", VTY_NEWLINE);
if (is_ipaccess_bts(bts))
- vty_out(vty, " Unit ID: %u/%u/0%s",
+ vty_out(vty, " Unit ID: %u/%u/0, OML Stream ID 0x%02x%s",
bts->ip_access.site_id, bts->ip_access.bts_id,
- VTY_NEWLINE);
+ bts->oml_tei, VTY_NEWLINE);
vty_out(vty, " NM State: ");
net_dump_nmstate(vty, &bts->nm_state);
vty_out(vty, " Site Mgr NM State: ");
net_dump_nmstate(vty, &bts->site_mgr.nm_state);
vty_out(vty, " Paging: FIXME pending requests, %u free slots%s",
bts->paging.available_slots, VTY_NEWLINE);
- vty_out(vty, " E1 Signalling Link:%s", VTY_NEWLINE);
- e1isl_dump_vty(vty, bts->oml_link);
+ if (!is_ipaccess_bts(bts)) {
+ vty_out(vty, " E1 Signalling Link:%s", VTY_NEWLINE);
+ e1isl_dump_vty(vty, bts->oml_link);
+ }
/* FIXME: oml_link, chan_desc */
}
@@ -224,7 +228,7 @@ static void config_write_bts_single(struct vty *vty, struct gsm_bts *bts)
vty_out(vty, " bts %u%s", bts->nr, VTY_NEWLINE);
vty_out(vty, " type %s%s", btstype2str(bts->type), VTY_NEWLINE);
vty_out(vty, " band %s%s", gsm_band_name(bts->band), VTY_NEWLINE);
- vty_out(vty, " cell_identity %u%s", bts->cell_identity, VTY_NEWLINE);
+ vty_out(vty, " cell_identity %u%s", bts->cell_identity, VTY_NEWLINE);
vty_out(vty, " location_area_code %u%s", bts->location_area_code,
VTY_NEWLINE);
vty_out(vty, " training_sequence_code %u%s", bts->tsc, VTY_NEWLINE);
@@ -238,10 +242,11 @@ static void config_write_bts_single(struct vty *vty, struct gsm_bts *bts)
VTY_NEWLINE);
if (bts->cell_barred)
vty_out(vty, " cell barred 1%s", VTY_NEWLINE);
- if (is_ipaccess_bts(bts))
+ if (is_ipaccess_bts(bts)) {
vty_out(vty, " ip.access unit_id %u %u%s",
bts->ip_access.site_id, bts->ip_access.bts_id, VTY_NEWLINE);
- else {
+ vty_out(vty, " oml ip.access stream_id %u%s", bts->oml_tei, VTY_NEWLINE);
+ } else {
config_write_e1_link(vty, &bts->oml_e1_link, " oml ");
vty_out(vty, " oml e1 tei %u%s", bts->oml_tei, VTY_NEWLINE);
}
@@ -269,6 +274,18 @@ static int config_write_net(struct vty *vty)
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, " encryption a5 %u%s", gsmnet->a5_encryption, VTY_NEWLINE);
+ vty_out(vty, " neci %u%s", gsmnet->neci, 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;
}
@@ -285,8 +302,13 @@ static void trx_dump_vty(struct vty *vty, struct gsm_bts_trx *trx)
net_dump_nmstate(vty, &trx->nm_state);
vty_out(vty, " Baseband Transceiver NM State: ");
net_dump_nmstate(vty, &trx->bb_transc.nm_state);
- vty_out(vty, " E1 Signalling Link:%s", VTY_NEWLINE);
- e1isl_dump_vty(vty, trx->rsl_link);
+ if (is_ipaccess_bts(trx->bts)) {
+ vty_out(vty, " ip.access stream ID: 0x%02x%s",
+ trx->rsl_tei, VTY_NEWLINE);
+ } else {
+ vty_out(vty, " E1 Signalling Link:%s", VTY_NEWLINE);
+ e1isl_dump_vty(vty, trx->rsl_link);
+ }
}
DEFUN(show_trx,
@@ -612,7 +634,7 @@ DEFUN(show_e1ts,
"show e1_timeslot [line_nr] [ts_nr]",
SHOW_STR "Display information about a E1 timeslot\n")
{
- struct e1inp_line *line;
+ struct e1inp_line *line = NULL;
struct e1inp_ts *ts;
int ts_nr;
@@ -776,11 +798,51 @@ DEFUN(cfg_net_encryption,
"encryption a5 (0|1|2)",
"Enable or disable encryption (A5) for this network\n")
{
- gsmnet->auth_policy = atoi(argv[0]);
+ gsmnet->a5_encryption= atoi(argv[0]);
return CMD_SUCCESS;
}
+DEFUN(cfg_net_neci,
+ cfg_net_neci_cmd,
+ "neci (0|1)",
+ "Set if NECI of cell selection is to be set")
+{
+ gsmnet->neci = atoi(argv[0]);
+ return CMD_SUCCESS;
+}
+
+#define DECLARE_TIMER(number) \
+ DEFUN(cfg_net_T##number, \
+ cfg_net_T##number##_cmd, \
+ "timer t" #number " <0-65535>", \
+ "Set the T" #number " value.") \
+{ \
+ 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)
+DECLARE_TIMER(3103)
+DECLARE_TIMER(3105)
+DECLARE_TIMER(3107)
+DECLARE_TIMER(3109)
+DECLARE_TIMER(3111)
+DECLARE_TIMER(3113)
+DECLARE_TIMER(3115)
+DECLARE_TIMER(3117)
+DECLARE_TIMER(3119)
+DECLARE_TIMER(3141)
+
+
/* per-BTS configuration */
DEFUN(cfg_bts,
cfg_bts_cmd,
@@ -819,6 +881,11 @@ DEFUN(cfg_bts_type,
bts->type = parse_btstype(argv[0]);
+ if (is_ipaccess_bts(bts)) {
+ /* Set the default OML Stream ID to 0xff */
+ bts->oml_tei = 0xff;
+ }
+
return CMD_SUCCESS;
}
@@ -941,6 +1008,25 @@ DEFUN(cfg_bts_unit_id,
return CMD_SUCCESS;
}
+DEFUN(cfg_bts_stream_id,
+ cfg_bts_stream_id_cmd,
+ "oml ip.access stream_id <0-255>",
+ "Set the ip.access Stream ID of the OML link of this BTS\n")
+{
+ struct gsm_bts *bts = vty->index;
+ int stream_id = atoi(argv[0]);
+
+ if (!is_ipaccess_bts(bts)) {
+ vty_out(vty, "%% BTS is not of ip.access type%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ bts->oml_tei = stream_id;
+
+ return CMD_SUCCESS;
+}
+
+
DEFUN(cfg_bts_oml_e1,
cfg_bts_oml_e1_cmd,
"oml e1 line E1_LINE timeslot <1-31> sub-slot (0|1|2|3|full)",
@@ -1069,7 +1155,7 @@ DEFUN(cfg_trx_max_power_red,
{
int maxpwr_r = atoi(argv[0]);
struct gsm_bts_trx *trx = vty->index;
- int upper_limit = 12; /* default 12.21 max power red. */
+ int upper_limit = 24; /* default 12.21 max power red. */
/* FIXME: check if our BTS type supports more than 12 */
if (maxpwr_r < 0 || maxpwr_r > upper_limit) {
@@ -1114,6 +1200,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,
@@ -1196,6 +1293,18 @@ int bsc_vty_init(struct gsm_network *net)
install_element(GSMNET_NODE, &cfg_net_name_long_cmd);
install_element(GSMNET_NODE, &cfg_net_auth_policy_cmd);
install_element(GSMNET_NODE, &cfg_net_encryption_cmd);
+ install_element(GSMNET_NODE, &cfg_net_neci_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);
@@ -1207,6 +1316,7 @@ int bsc_vty_init(struct gsm_network *net)
install_element(BTS_NODE, &cfg_bts_tsc_cmd);
install_element(BTS_NODE, &cfg_bts_bsic_cmd);
install_element(BTS_NODE, &cfg_bts_unit_id_cmd);
+ install_element(BTS_NODE, &cfg_bts_stream_id_cmd);
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);
@@ -1222,6 +1332,7 @@ int bsc_vty_init(struct gsm_network *net)
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 032e16fc4..4cc08c2da 100644
--- a/openbsc/src/vty_interface_layer3.c
+++ b/openbsc/src/vty_interface_layer3.c
@@ -1,5 +1,6 @@
/* OpenBSC interface to quagga VTY */
/* (C) 2009 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2009 by Holger Hans Peter Freyther
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
@@ -31,12 +32,14 @@
#include <openbsc/linuxlist.h>
#include <openbsc/gsm_data.h>
#include <openbsc/gsm_subscriber.h>
+#include <openbsc/silent_call.h>
#include <openbsc/gsm_04_11.h>
#include <openbsc/e1_input.h>
#include <openbsc/abis_nm.h>
#include <openbsc/gsm_utils.h>
#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);
@@ -54,7 +57,6 @@ static int dummy_config_write(struct vty *v)
return CMD_SUCCESS;
}
-
static struct buffer *argv_to_buffer(int argc, const char *argv[], int base)
{
struct buffer *b = buffer_new(1024);
@@ -88,6 +90,7 @@ DEFUN(cfg_subscr,
return CMD_WARNING;
}
+ /* vty_go_parent should put this subscriber */
vty->index = subscr;
vty->node = SUBSCR_NODE;
@@ -112,6 +115,7 @@ DEFUN(show_subscr,
return CMD_WARNING;
}
subscr_dump_vty(vty, subscr);
+ subscr_put(subscr);
return CMD_SUCCESS;
}
@@ -191,57 +195,111 @@ struct gsm_sms *sms_from_text(struct gsm_subscriber *receiver, const char *text)
}
static int _send_sms_buffer(struct gsm_subscriber *receiver,
- struct buffer *b)
+ struct buffer *b, u_int8_t tp_pid)
{
struct gsm_sms *sms;
sms = sms_from_text(receiver, buffer_getstr(b));
-
+ sms->protocol_id = tp_pid;
gsm411_send_sms_subscr(receiver, sms);
return CMD_SUCCESS;
}
-DEFUN(sms_send_ext,
- sms_send_ext_cmd,
- "sms send extension EXTEN .LINE",
- "Send a message to a subscriber identified by EXTEN")
+static struct gsm_subscriber *get_subscr_by_argv(const char *type,
+ const char *id)
+{
+ if (!strcmp(type, "extension"))
+ return subscr_get_by_extension(gsmnet, id);
+ else if (!strcmp(type, "imsi"))
+ return subscr_get_by_imsi(gsmnet, id);
+ else if (!strcmp(type, "tmsi"))
+ return subscr_get_by_tmsi(gsmnet, atoi(id));
+ else if (!strcmp(type, "id"))
+ return subscr_get_by_id(gsmnet, atoi(id));
+
+ return NULL;
+}
+#define SUBSCR_TYPES "(extension|imsi|tmsi|id)"
+
+DEFUN(subscriber_send_sms,
+ subscriber_send_sms_cmd,
+ "subscriber " SUBSCR_TYPES " EXTEN sms send .LINE",
+ "Select subscriber based on extension")
{
- struct gsm_subscriber *receiver;
+ struct gsm_subscriber *subscr = get_subscr_by_argv(argv[0], argv[1]);
struct buffer *b;
int rc;
- receiver = subscr_get_by_extension(gsmnet, argv[0]);
- if (!receiver)
+ if (!subscr) {
+ vty_out(vty, "%% No subscriber found for %s %s%s",
+ argv[0], argv[1], VTY_NEWLINE);
return CMD_WARNING;
-
- b = argv_to_buffer(argc, argv, 1);
- rc = _send_sms_buffer(receiver, b);
+ }
+ b = argv_to_buffer(argc, argv, 2);
+ rc = _send_sms_buffer(subscr, b, 0);
buffer_free(b);
+ subscr_put(subscr);
+
return rc;
}
-DEFUN(sms_send_imsi,
- sms_send_imsi_cmd,
- "sms send imsi IMSI .LINE",
- "Send a message to a subscriber identified by IMSI")
+DEFUN(subscriber_silent_sms,
+ subscriber_silent_sms_cmd,
+ "subscriber " SUBSCR_TYPES " EXTEN silent sms send .LINE",
+ "Select subscriber based on extension")
{
- struct gsm_subscriber *receiver;
+ struct gsm_subscriber *subscr = get_subscr_by_argv(argv[0], argv[1]);
struct buffer *b;
int rc;
- receiver = subscr_get_by_imsi(gsmnet, argv[0]);
- if (!receiver)
+ if (!subscr) {
+ vty_out(vty, "%% No subscriber found for %s %s%s",
+ argv[0], argv[1], VTY_NEWLINE);
return CMD_WARNING;
+ }
- b = argv_to_buffer(argc, argv, 1);
- rc = _send_sms_buffer(receiver, b);
+ b = argv_to_buffer(argc, argv, 2);
+ rc = _send_sms_buffer(subscr, b, 64);
buffer_free(b);
+ subscr_put(subscr);
+
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")
+{
+ struct gsm_subscriber *subscr = get_subscr_by_argv(argv[0], argv[1]);
+ int rc;
+
+ 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], "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;
+ }
+
+ subscr_put(subscr);
+
+ return CMD_SUCCESS;
+}
DEFUN(cfg_subscr_name,
cfg_subscr_name_cmd,
@@ -291,17 +349,39 @@ DEFUN(cfg_subscr_authorized,
return CMD_SUCCESS;
}
+static int scall_cbfn(unsigned int subsys, unsigned int signal,
+ void *handler_data, void *signal_data)
+{
+ struct scall_signal_data *sigdata = signal_data;
+ struct vty *vty = sigdata->data;
+
+ switch (signal) {
+ case S_SCALL_SUCCESS:
+ vty_out(vty, "%% silent call on ARFCN %u timeslot %u%s",
+ sigdata->lchan->ts->trx->arfcn, sigdata->lchan->ts->nr,
+ VTY_NEWLINE);
+ break;
+ case S_SCALL_EXPIRED:
+ vty_out(vty, "%% silent call expired paging%s", VTY_NEWLINE);
+ break;
+ }
+ return 0;
+}
int bsc_vty_init_extra(struct gsm_network *net)
{
gsmnet = net;
+ register_signal_handler(SS_SCALL, scall_cbfn, NULL);
+
install_element(VIEW_NODE, &show_subscr_cmd);
install_element(VIEW_NODE, &show_subscr_cache_cmd);
install_element(VIEW_NODE, &sms_send_pend_cmd);
- install_element(VIEW_NODE, &sms_send_ext_cmd);
- install_element(VIEW_NODE, &sms_send_imsi_cmd);
+
+ 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(CONFIG_NODE, &cfg_subscr_cmd);
install_node(&subscr_node, dummy_config_write);