From 097bdeb77de0aad435dc99a89ca1b586e8872b55 Mon Sep 17 00:00:00 2001 From: Holger Hans Peter Freyther Date: Thu, 22 Oct 2009 14:30:42 +0200 Subject: [bssap] First go at implementing ASSIGNMENT REQUEST --- openbsc/include/openbsc/bssap.h | 15 ++- openbsc/include/openbsc/gsm_data.h | 10 ++ openbsc/src/bsc_msc_ip.c | 119 ++++++++++++++++- openbsc/src/bssap.c | 263 +++++++++++++++++++++++++++++++++++++ 4 files changed, 401 insertions(+), 6 deletions(-) (limited to 'openbsc') diff --git a/openbsc/include/openbsc/bssap.h b/openbsc/include/openbsc/bssap.h index d6553409a..809cbfd7d 100644 --- a/openbsc/include/openbsc/bssap.h +++ b/openbsc/include/openbsc/bssap.h @@ -157,9 +157,9 @@ enum GSM0808_IE_CODING { GSM0808_IE_RESERVED_1 = 15, GSM0808_IE_RESERVED_2 = 16, GSM0808_IE_RESERVED_3 = 17, - GSM0808_IE_CLASSMARK_INFORMATION_TYPE_2 = 18, - GSM0808_IE_CLASSMARK_INFORMATION_TYPE_3 = 19, - GSM0808_IE_INTERFERENCE_BAND_TO_BE_USED = 20, + GSM0808_IE_CLASSMARK_INFORMATION_T2 = 18, + GSM0808_IE_CLASSMARK_INFORMATION_T3 = 19, + GSM0808_IE_INTERFERENCE_BAND_TO_USE = 20, GSM0808_IE_RR_CAUSE = 21, GSM0808_IE_RESERVED_4 = 22, GSM0808_IE_LAYER_3_INFORMATION = 23, @@ -196,13 +196,13 @@ enum GSM0808_IE_CODING { GSM0808_IE_CONNECTION_RELEASE_RQSTED = 54, GSM0808_IE_GROUP_CALL_REFERENCE = 55, GSM0808_IE_EMLPP_PRIORITY = 56, - GSM0808_IE_CONFIGURATION_EVOLUTION_INDICATION = 57, + GSM0808_IE_CONFIG_EVO_INDI = 57, GSM0808_IE_OLD_BSS_TO_NEW_BSS_INFORMATION = 58, GSM0808_IE_LSA_IDENTIFIER = 59, GSM0808_IE_LSA_IDENTIFIER_LIST = 60, GSM0808_IE_LSA_INFORMATION = 61, GSM0808_IE_LCS_QOS = 62, - GSM0808_IE_LSA_ACCESS_CONTROL_SUPPRESSION = 63, + GSM0808_IE_LSA_ACCESS_CTRL_SUPPR = 63, GSM0808_IE_LCS_PRIORITY = 67, GSM0808_IE_LOCATION_TYPE = 68, GSM0808_IE_LOCATION_ESTIMATE = 69, @@ -274,6 +274,11 @@ struct msgb *bssmap_create_clear_complete(void); struct msgb *bssmap_create_cipher_complete(struct msgb *layer3, int bsc_enc_algo); struct msgb *bssmap_create_cipher_reject(u_int8_t cause); struct msgb *bssmap_create_sapi_reject(u_int8_t link_id); +struct msgb *bssmap_create_assignment_completed(struct gsm_lchan *lchan, u_int8_t rr_cause); +struct msgb *bssmap_create_assignment_failure(u_int8_t cause, u_int8_t *rr_cause); + +void gsm0808_send_assignment_failure(struct gsm_lchan *l, u_int8_t cause, u_int8_t *rr_value); +void gsm0808_send_assignment_compl(struct gsm_lchan *l, u_int8_t rr_value); int dtap_rcvmsg(struct gsm_lchan *lchan, struct msgb *msg, unsigned int length); struct msgb *dtap_create_msg(struct msgb *msg_l3, u_int8_t link_id); diff --git a/openbsc/include/openbsc/gsm_data.h b/openbsc/include/openbsc/gsm_data.h index d01973e4b..b79722102 100644 --- a/openbsc/include/openbsc/gsm_data.h +++ b/openbsc/include/openbsc/gsm_data.h @@ -122,6 +122,15 @@ struct bss_sccp_connection_data { struct sccp_connection *sccp; int ciphering_handled : 1; + /* Timers... */ + + /* for assginment command */ + struct timer_list T10; + + /* audio handling */ + int rtp_port; + int rtp_payload2; + /* Queue SCCP and GSM0408 messages */ struct llist_head gsm_queue; unsigned int gsm_queue_size; @@ -130,6 +139,7 @@ struct bss_sccp_connection_data { unsigned int sccp_queue_size; }; +#define GSM0808_T10_VALUE 6, 0 #define sccp_get_lchan(data_ctx) ((struct bss_sccp_connection_data *)data_ctx)->lchan #define lchan_get_sccp(lchan) lchan->msc_data->sccp struct bss_sccp_connection_data *bss_sccp_create_data(); diff --git a/openbsc/src/bsc_msc_ip.c b/openbsc/src/bsc_msc_ip.c index f3c85904e..27603a7eb 100644 --- a/openbsc/src/bsc_msc_ip.c +++ b/openbsc/src/bsc_msc_ip.c @@ -52,6 +52,7 @@ struct gsm_network *bsc_gsmnet = 0; static const char *config_file = "openbsc.cfg"; static char *msc_address = "127.0.0.1"; static struct bsc_fd msc_connection; +static struct in_addr local_addr; extern int ipacc_rtp_direct; extern int bsc_bootstrap_network(int (*layer4)(struct gsm_network *, int, void *), const char *cfg_file); @@ -74,6 +75,7 @@ struct bss_sccp_connection_data *bss_sccp_create_data() void bss_sccp_free_data(struct bss_sccp_connection_data *data) { + bsc_del_timer(&data->T10); bsc_free_queued(data->sccp); bts_free_queued(data); talloc_free(data); @@ -267,6 +269,67 @@ static int handle_cipher_m_complete(struct msgb *msg) return 1; } +/* Receive a ASSIGNMENT COMPLETE */ +static int handle_ass_compl(struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + + DEBUGP(DMSC, "ASSIGNMENT COMPLETE from MS, forwarding to MSC\n"); + + if (!msg->lchan->msc_data) { + DEBUGP(DMSC, "No MSC data\n"); + return -1; + } + + if (msgb_l3len(msg) - sizeof(*gh) != 1) { + DEBUGP(DMSC, "assignment failure invalid: %d\n", + msgb_l3len(msg) - sizeof(*gh)); + return -1; + } + gsm0808_send_assignment_compl(msg->lchan, gh->data[0]); + return 1; +} + +/* + * Receive a ASSIGNMENT FAILURE. If the message is failed + * to be parsed the T10 timer will send the failure. + */ +static int handle_ass_fail(struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + + DEBUGP(DMSC, "ASSIGNMENT FAILURE from MS, forwarding to MSC\n"); + if (!msg->lchan->msc_data) { + DEBUGP(DMSC, "No MSC data\n"); + return -1; + } + + if (msgb_l3len(msg) - sizeof(*gh) != 1) { + DEBUGP(DMSC, "assignment failure invalid: %d\n", + msgb_l3len(msg) - sizeof(*gh)); + return -1; + } + + gsm0808_send_assignment_failure(msg->lchan, + GSM0808_CAUSE_RADIO_INTERFACE_MESSAGE_FAILURE, &gh->data[0]); + return 1; +} + +/* + * Receive a GSM04.08 MODIFY ACK. Actually we have to check + * the content to see if this was a success or not. + */ +static int handle_modify_ack(struct msgb *msg) +{ + int rc; + + /* modify RSL */ + rc = gsm48_rx_rr_modif_ack(msg); + + gsm0808_send_assignment_compl(msg->lchan, 0); + return 1; +} + /* Receive a GSM 04.08 Radio Resource (RR) message */ static int gsm0408_rcv_rr(struct msgb *msg) { @@ -284,6 +347,15 @@ static int gsm0408_rcv_rr(struct msgb *msg) case GSM48_MT_RR_CIPH_M_COMPL: rc = handle_cipher_m_complete(msg); break; + case GSM48_MT_RR_ASS_COMPL: + rc = handle_ass_compl(msg); + break; + case GSM48_MT_RR_ASS_FAIL: + rc = handle_ass_fail(msg); + break; + case GSM48_MT_RR_CHAN_MODE_MODIF_ACK: + rc = handle_modify_ack(msg); + break; default: break; } @@ -345,6 +417,43 @@ int gsm0408_rcvmsg(struct msgb *msg, u_int8_t link_id) return rc; } +/* handle ipaccess signals */ +static int handle_abisip_signal(unsigned int subsys, unsigned int signal, + void *handler_data, void *signal_data) +{ + struct gsm_lchan *lchan = signal_data; + struct gsm_bts_trx_ts *ts; + int rc; + + if (subsys != SS_ABISIP) + return 0; + + ts = lchan->ts; + + switch (signal) { + case S_ABISIP_CRCX_ACK: + /* we can ask it to connect now */ + if (lchan->msc_data) { + DEBUGP(DMSC, "Connecting BTS to port: %d conn: %d\n", + lchan->msc_data->rtp_port, ts->abis_ip.conn_id); + + rc = rsl_ipacc_mdcx(lchan, ntohl(local_addr.s_addr), + lchan->msc_data->rtp_port, + ts->abis_ip.conn_id, + lchan->msc_data->rtp_payload2); + if (rc < 0) { + DEBUGP(DMSC, "Failed to send connect: %d\n", rc); + return rc; + } + } + break; + case S_ABISIP_DLCX_IND: + break; + } + + return 0; +} + static void print_usage() { printf("Usage: bsc_hack\n"); @@ -522,6 +631,7 @@ static void print_help() printf(" -s --disable-color\n"); printf(" -c --config-file filename The config file to use.\n"); printf(" -m --msc=IP. The address of the MSC.\n"); + printf(" -l --local=IP. The local address of the MGCP.\n"); } static void handle_options(int argc, char** argv) @@ -536,10 +646,11 @@ static void handle_options(int argc, char** argv) {"timestamp", 0, 0, 'T'}, {"rtp-proxy", 0, 0, 'P'}, {"msc", 1, 0, 'm'}, + {"local", 1, 0, 'l'}, {0, 0, 0, 0} }; - c = getopt_long(argc, argv, "hd:sTPc:m:", + c = getopt_long(argc, argv, "hd:sTPc:m:l:", long_options, &option_index); if (c == -1) break; @@ -567,6 +678,9 @@ static void handle_options(int argc, char** argv) case 'm': msc_address = strdup(optarg); break; + case 'l': + inet_aton(optarg, &local_addr); + break; default: /* ignore */ break; @@ -612,6 +726,9 @@ int main(int argc, char **argv) sccp_connection_set_incoming(&sccp_ssn_bssap, msc_sccp_accept, NULL); sccp_set_read(&sccp_ssn_bssap, msc_sccp_read, NULL); + /* initialize ipaccess handling */ + register_signal_handler(SS_ABISIP, handle_abisip_signal, NULL); + rc = connect_to_msc(msc_address, 5000); if (rc < 0) { fprintf(stderr, "Opening the MSC connection failed.\n"); diff --git a/openbsc/src/bssap.c b/openbsc/src/bssap.c index adf2cabcd..63bcc956b 100644 --- a/openbsc/src/bssap.c +++ b/openbsc/src/bssap.c @@ -44,6 +44,17 @@ static const struct tlv_definition bss_att_tlvdef = { [GSM0808_IE_CELL_IDENTIFIER_LIST] = { TLV_TYPE_TLV }, [GSM0808_IE_CHANNEL_NEEDED] = { TLV_TYPE_TV }, [GSM0808_IE_EMLPP_PRIORITY] = { TLV_TYPE_TV }, + [GSM0808_IE_CHANNEL_TYPE] = { TLV_TYPE_TLV }, + [GSM0808_IE_PRIORITY] = { TLV_TYPE_TLV }, + [GSM0808_IE_CIRCUIT_IDENTITY_CODE] = { TLV_TYPE_TV }, + [GSM0808_IE_DOWNLINK_DTX_FLAG] = { TLV_TYPE_TV }, + [GSM0808_IE_INTERFERENCE_BAND_TO_USE] = { TLV_TYPE_TV }, + [GSM0808_IE_CLASSMARK_INFORMATION_T2] = { TLV_TYPE_TLV }, + [GSM0808_IE_GROUP_CALL_REFERENCE] = { TLV_TYPE_TLV }, + [GSM0808_IE_TALKER_FLAG] = { TLV_TYPE_T }, + [GSM0808_IE_CONFIG_EVO_INDI] = { TLV_TYPE_TV }, + [GSM0808_IE_LSA_ACCESS_CTRL_SUPPR] = { TLV_TYPE_TV }, + [GSM0808_IE_SERVICE_HANDOVER] = { TLV_TYPE_TV}, }, }; @@ -151,6 +162,7 @@ static int bssmap_handle_clear_command(struct sccp_connection *conn, if (msg->lchan) { DEBUGP(DMSC, "Releasing all transactions on %p\n", conn); + bsc_del_timer(&msg->lchan->msc_data->T10); msg->lchan->msc_data->lchan = NULL; msg->lchan->msc_data = NULL; put_lchan(msg->lchan); @@ -210,6 +222,75 @@ reject: return -1; } +/* + * Handle the network configurable T10 parameter + */ +static void bssmap_t10_fired(void *_conn) +{ + struct sccp_connection *conn = (struct sccp_connection *) _conn; + struct msgb *resp; + + DEBUGP(DMSC, "T10 fired, assignment failed: %p\n", conn); + resp = bssmap_create_assignment_failure( + GSM0808_CAUSE_NO_RADIO_RESOURCE_AVAILABLE, NULL); + if (!resp) { + DEBUGP(DMSC, "Allocation failure: %p\n", conn); + return; + } + + bsc_queue_connection_write(conn, resp); +} + +/* + * Handle the assignment request message. + * + * See ยง3.2.1.1 for the message type + */ +static int bssmap_handle_assignm_req(struct sccp_connection *conn, + struct msgb *msg, unsigned int length) +{ + struct tlv_parsed tp; + struct bss_sccp_connection_data *msc_data; + int ret = 0; + + if (!msg->lchan || !msg->lchan->msc_data) { + DEBUGP(DMSC, "No lchan/msc_data in cipher mode command.\n"); + goto reject; + } + + msc_data = msg->lchan->msc_data; + tlv_parse(&tp, &bss_att_tlvdef, msg->l4h + 1, length - 1, 0, 0); + + if (!TLVP_PRESENT(&tp, GSM0808_IE_CHANNEL_TYPE)) { + DEBUGP(DMSC, "Mandantory channel type not present.\n"); + goto reject; + } + + if (!TLVP_PRESENT(&tp, GSM0808_IE_CIRCUIT_IDENTITY_CODE)) { + DEBUGP(DMSC, "Identity code missing. Audio routing will not work.\n"); + goto reject; + } + + /* + * currently only activation of speach on the + * existing channel is supported. This means we + * will send a Channel Modify message and take it + * from there. + * Setup channel type, timer + */ + //gsm48_lchan_modify(); + + msc_data->T10.cb = bssmap_t10_fired; + msc_data->T10.data = conn; + bsc_schedule_timer(&msc_data->T10, GSM0808_T10_VALUE); + return ret; + +reject: + gsm0808_send_assignment_failure(msg->lchan, + GSM0808_CAUSE_NO_RADIO_RESOURCE_AVAILABLE, NULL); + return -1; +} + int bssmap_rcvmsg_udt(struct gsm_network *net, struct msgb *msg, unsigned int length) { int ret = 0; @@ -247,6 +328,9 @@ int bssmap_rcvmsg_dt1(struct sccp_connection *conn, struct msgb *msg, unsigned i case BSS_MAP_MSG_CIPHER_MODE_CMD: ret = bssmap_handle_cipher_mode(conn, msg, length); break; + case BSS_MAP_MSG_ASSIGMENT_RQST: + ret = bssmap_handle_assignm_req(conn, msg, length); + break; default: DEBUGP(DMSC, "Unimplemented msg type: %d\n", msg->l4h[0]); break; @@ -442,6 +526,156 @@ struct msgb *bssmap_create_sapi_reject(u_int8_t link_id) return msg; } +static u_int8_t chan_mode_to_speech(enum gsm48_chan_mode mode) +{ + switch (mode) { + case GSM48_CMODE_SPEECH_V1: + return 1; + break; + case GSM48_CMODE_SPEECH_EFR: + return 0x11; + break; + case GSM48_CMODE_SPEECH_AMR: + return 0x21; + break; + case GSM48_CMODE_SIGN: + case GSM48_CMODE_DATA_14k5: + case GSM48_CMODE_DATA_12k0: + case GSM48_CMODE_DATA_6k0: + case GSM48_CMODE_DATA_3k6: + default: + DEBUGP(DMSC, "Using non speech mode: %d\n", mode); + return 0; + break; + } +} + +/* 3.2.2.33 */ +static u_int8_t lchan_to_chosen_channel(struct gsm_lchan *lchan) +{ + u_int8_t channel_mode = 0, channel = 0; + + switch (lchan->tch_mode) { + case GSM48_CMODE_SPEECH_V1: + case GSM48_CMODE_SPEECH_EFR: + case GSM48_CMODE_SPEECH_AMR: + channel_mode = 0x9; + break; + case GSM48_CMODE_SIGN: + channel_mode = 0x8; + break; + case GSM48_CMODE_DATA_14k5: + channel_mode = 0xe; + break; + case GSM48_CMODE_DATA_12k0: + channel_mode = 0xb; + break; + case GSM48_CMODE_DATA_6k0: + channel_mode = 0xc; + break; + case GSM48_CMODE_DATA_3k6: + channel_mode = 0xd; + break; + } + + switch (lchan->type) { + case GSM_LCHAN_NONE: + channel = 0x0; + break; + case GSM_LCHAN_SDCCH: + channel = 0x1; + break; + case GSM_LCHAN_TCH_F: + channel = 0x8; + break; + case GSM_LCHAN_TCH_H: + channel = 0x9; + break; + case GSM_LCHAN_UNKNOWN: + DEBUGP(DMSC, "Unknown lchan type: %p\n", lchan); + break; + } + + return channel_mode << 4 | channel; +} + +struct msgb *bssmap_create_assignment_completed(struct gsm_lchan *lchan, u_int8_t rr_cause) +{ + u_int8_t *data; + u_int8_t speech_mode; + + struct msgb *msg = msgb_alloc(35, "bssmap: ass compl"); + if (!msg) + return NULL; + + msg->l3h = msgb_put(msg, 3); + msg->l3h[0] = BSSAP_MSG_BSS_MANAGEMENT; + msg->l3h[1] = 0xff; + msg->l3h[2] = BSS_MAP_MSG_ASSIGMENT_COMPLETE; + + /* write 3.2.2.22 */ + data = msgb_put(msg, 2); + data[0] = GSM0808_IE_RR_CAUSE; + data[1] = rr_cause; + + /* write cirtcuit identity code 3.2.2.2 */ + /* write cell identifier 3.2.2.17 */ + /* write chosen channel 3.2.2.33 when BTS picked it */ + data = msgb_put(msg, 2); + data[0] = GSM0808_IE_CHOSEN_CHANNEL; + data[1] = lchan_to_chosen_channel(lchan); + + /* write chosen encryption algorithm 3.2.2.44 */ + data = msgb_put(msg, 2); + data[0] = GSM0808_IE_CHOSEN_ENCR_ALG; + data[1] = RSL_ENC_ALG_A5(lchan->encr.alg_id); + + /* write circuit pool 3.2.2.45 */ + /* write speech version chosen: 3.2.2.51 when BTS picked it */ + speech_mode = chan_mode_to_speech(lchan->tch_mode); + if (speech_mode != 0) { + data = msgb_put(msg, 2); + data[0] = GSM0808_IE_SPEECH_VERSION; + data[1] = speech_mode; + } + + /* write LSA identifier 3.2.2.15 */ + + + /* update the size */ + msg->l3h[1] = msgb_l3len(msg) - 2; + return msg; +} + +struct msgb *bssmap_create_assignment_failure(u_int8_t cause, u_int8_t *rr_cause) +{ + u_int8_t *data; + struct msgb *msg = msgb_alloc(35, "bssmap: ass fail"); + if (!msg) + return NULL; + + msg->l3h = msgb_put(msg, 5); + msg->l3h[0] = BSSAP_MSG_BSS_MANAGEMENT; + msg->l3h[1] = 0xff; + msg->l3h[2] = BSS_MAP_MSG_ASSIGMENT_FAILURE; + msg->l3h[3] = GSM0808_IE_CAUSE; + msg->l3h[4] = cause; + + /* RR cause 3.2.2.22 */ + if (rr_cause) { + data = msgb_put(msg, 2); + data[0] = GSM0808_IE_RR_CAUSE; + data[1] = *rr_cause; + } + + /* Circuit pool 3.22.45 */ + /* Circuit pool list 3.2.2.46 */ + + /* update the size */ + msg->l3h[1] = msgb_l3len(msg) - 2; + return msg; +} + struct msgb *dtap_create_msg(struct msgb *msg_l3, u_int8_t link_id) { struct dtap_header *header; @@ -484,6 +718,7 @@ static int bssap_handle_lchan_signal(unsigned int subsys, unsigned int signal, if (!lchan || !lchan->msc_data) return 0; + bsc_del_timer(&lchan->msc_data->T10); conn = lchan->msc_data->sccp; lchan->msc_data->lchan = NULL; lchan->msc_data = NULL; @@ -667,6 +902,34 @@ void bts_send_queued(struct bss_sccp_connection_data *data) data->gsm_queue_size = 0; } +void gsm0808_send_assignment_failure(struct gsm_lchan *lchan, u_int8_t cause, u_int8_t *rr_value) +{ + struct msgb *resp; + + bsc_del_timer(&lchan->msc_data->T10); + resp = bssmap_create_assignment_failure(cause, rr_value); + if (!resp) { + DEBUGP(DMSC, "Allocation failure: %p\n", lchan_get_sccp(lchan)); + return; + } + + bsc_queue_connection_write(lchan_get_sccp(lchan), resp); +} + +void gsm0808_send_assignment_compl(struct gsm_lchan *lchan, u_int8_t rr_cause) +{ + struct msgb *resp; + + bsc_del_timer(&lchan->msc_data->T10); + resp = bssmap_create_assignment_completed(lchan, rr_cause); + if (!resp) { + DEBUGP(DMSC, "Creating MSC response failed: %p\n", lchan_get_sccp(lchan)); + return; + } + + bsc_queue_connection_write(lchan_get_sccp(lchan), resp); +} + static __attribute__((constructor)) void on_dso_load_bssap(void) { register_signal_handler(SS_LCHAN, bssap_handle_lchan_signal, NULL); -- cgit v1.2.3