diff options
Diffstat (limited to 'openbsc/src')
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); |