diff options
27 files changed, 2690 insertions, 91 deletions
diff --git a/openbsc/configure.in b/openbsc/configure.in index cba6c6cd9..520fb75ef 100644 --- a/openbsc/configure.in +++ b/openbsc/configure.in @@ -1,7 +1,7 @@ dnl Process this file with autoconf to produce a configure script AC_INIT -AM_INIT_AUTOMAKE(openbsc, 0.0alpha1) +AM_INIT_AUTOMAKE(openbsc, 0.1onwaves) dnl kernel style compile messages m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) diff --git a/openbsc/contrib/README b/openbsc/contrib/README new file mode 100644 index 000000000..43c8d623f --- /dev/null +++ b/openbsc/contrib/README @@ -0,0 +1,2 @@ +This contains a set of scripts used for the development of the +MSC functionality. diff --git a/openbsc/contrib/send_handshake.py b/openbsc/contrib/send_handshake.py new file mode 100755 index 000000000..2d0dcec42 --- /dev/null +++ b/openbsc/contrib/send_handshake.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python + +import sys + +# packages +ACK ="\x00\x01\xfe\x06" +RESET_ACK = "\x00\x13\xfd\x09\x00\x03\x07\x0b\x04\x43\x01\x00\xfe\x04\x43\x5c\x00\xfe\x03\x00\x01\x31" +PAGE = "\x00\x20\xfd\x09\x00\x03\x07\x0b\x04\x43\x01\x00\xfe\x04\x43\x5c\x00\xfe\x10\x00\x0e\x52\x08\x08\x29\x42\x08\x05\x03\x12\x23\x42\x1a\x01\x06" + + +# simple handshake... +sys.stdout.write(ACK) +sys.stdout.flush() +sys.stdin.read(4) + +# wait for some data and send reset ack +sys.stdin.read(21) +sys.stdout.write(RESET_ACK) +sys.stdout.flush() + +sys.stdout.write(RESET_ACK) +sys.stdout.flush() + +# page a subscriber +sys.stdout.write(PAGE) +sys.stdout.flush() + +while True: + sys.stdin.read(1) + diff --git a/openbsc/include/openbsc/Makefile.am b/openbsc/include/openbsc/Makefile.am index 8bb64eb91..9f9928c90 100644 --- a/openbsc/include/openbsc/Makefile.am +++ b/openbsc/include/openbsc/Makefile.am @@ -4,4 +4,4 @@ noinst_HEADERS = abis_nm.h abis_rsl.h debug.h db.h gsm_04_08.h gsm_data.h \ subchan_demux.h trau_frame.h e1_input.h trau_mux.h signal.h \ gsm_utils.h ipaccess.h rs232.h openbscdefines.h rtp_proxy.h \ bsc_rll.h mncc.h talloc.h transaction.h ussd.h gsm_04_80.h \ - silent_call.h mgcp.h + silent_call.h mgcp.h bssap.h diff --git a/openbsc/include/openbsc/bssap.h b/openbsc/include/openbsc/bssap.h new file mode 100644 index 000000000..2ca2cac72 --- /dev/null +++ b/openbsc/include/openbsc/bssap.h @@ -0,0 +1,333 @@ +/* From GSM08.08 */ + +#ifndef BSSAP_H +#define BSSAP_H + +#include <stdlib.h> + +#include <openbsc/msgb.h> +#include <openbsc/gsm_data.h> + +/* + * this is from GSM 03.03 CGI but is copied in GSM 08.08 + * in § 3.2.2.27 for Cell Identifier List + */ +enum CELL_IDENT { + CELL_IDENT_WHOLE_GLOBAL = 0, + CELL_IDENT_LAC_AND_CI = 1, + CELL_IDENT_CI = 2, + CELL_IDENT_NO_CELL = 3, + CELL_IDENT_LAI_AND_LAC = 4, + CELL_IDENT_LAC = 5, + CELL_IDENT_BSS = 6, + CELL_IDENT_UTRAN_PLMN_LAC_RNC = 8, + CELL_IDENT_UTRAN_RNC = 9, + CELL_IDENT_UTRAN_LAC_RNC = 10, +}; + + +/* GSM 08.06 § 6.3 */ +enum BSSAP_MSG_TYPE { + BSSAP_MSG_BSS_MANAGEMENT = 0x0, + BSSAP_MSG_DTAP = 0x1, +}; + +struct bssmap_header { + u_int8_t type; + u_int8_t length; +} __attribute__((packed)); + +struct dtap_header { + u_int8_t type; + u_int8_t link_id; + u_int8_t length; +} __attribute__((packed)); + + +enum BSS_MAP_MSG_TYPE { + BSS_MAP_MSG_RESERVED_0 = 0, + + /* ASSIGNMENT MESSAGES */ + BSS_MAP_MSG_ASSIGMENT_RQST = 1, + BSS_MAP_MSG_ASSIGMENT_COMPLETE = 2, + BSS_MAP_MSG_ASSIGMENT_FAILURE = 3, + + /* HANDOVER MESSAGES */ + BSS_MAP_MSG_HANDOVER_RQST = 16, + BSS_MAP_MSG_HANDOVER_REQUIRED = 17, + BSS_MAP_MSG_HANDOVER_RQST_ACKNOWLEDGE= 18, + BSS_MAP_MSG_HANDOVER_CMD = 19, + BSS_MAP_MSG_HANDOVER_COMPLETE = 20, + BSS_MAP_MSG_HANDOVER_SUCCEEDED = 21, + BSS_MAP_MSG_HANDOVER_FAILURE = 22, + BSS_MAP_MSG_HANDOVER_PERFORMED = 23, + BSS_MAP_MSG_HANDOVER_CANDIDATE_ENQUIRE = 24, + BSS_MAP_MSG_HANDOVER_CANDIDATE_RESPONSE = 25, + BSS_MAP_MSG_HANDOVER_REQUIRED_REJECT = 26, + BSS_MAP_MSG_HANDOVER_DETECT = 27, + + /* RELEASE MESSAGES */ + BSS_MAP_MSG_CLEAR_CMD = 32, + BSS_MAP_MSG_CLEAR_COMPLETE = 33, + BSS_MAP_MSG_CLEAR_RQST = 34, + BSS_MAP_MSG_RESERVED_1 = 35, + BSS_MAP_MSG_RESERVED_2 = 36, + BSS_MAP_MSG_SAPI_N_REJECT = 37, + BSS_MAP_MSG_CONFUSION = 38, + + /* OTHER CONNECTION RELATED MESSAGES */ + BSS_MAP_MSG_SUSPEND = 40, + BSS_MAP_MSG_RESUME = 41, + BSS_MAP_MSG_CONNECTION_ORIENTED_INFORMATION = 42, + BSS_MAP_MSG_PERFORM_LOCATION_RQST = 43, + BSS_MAP_MSG_LSA_INFORMATION = 44, + BSS_MAP_MSG_PERFORM_LOCATION_RESPONSE = 45, + BSS_MAP_MSG_PERFORM_LOCATION_ABORT = 46, + BSS_MAP_MSG_COMMON_ID = 47, + + /* GENERAL MESSAGES */ + BSS_MAP_MSG_RESET = 48, + BSS_MAP_MSG_RESET_ACKNOWLEDGE = 49, + BSS_MAP_MSG_OVERLOAD = 50, + BSS_MAP_MSG_RESERVED_3 = 51, + BSS_MAP_MSG_RESET_CIRCUIT = 52, + BSS_MAP_MSG_RESET_CIRCUIT_ACKNOWLEDGE = 53, + BSS_MAP_MSG_MSC_INVOKE_TRACE = 54, + BSS_MAP_MSG_BSS_INVOKE_TRACE = 55, + BSS_MAP_MSG_CONNECTIONLESS_INFORMATION = 58, + + /* TERRESTRIAL RESOURCE MESSAGES */ + BSS_MAP_MSG_BLOCK = 64, + BSS_MAP_MSG_BLOCKING_ACKNOWLEDGE = 65, + BSS_MAP_MSG_UNBLOCK = 66, + BSS_MAP_MSG_UNBLOCKING_ACKNOWLEDGE = 67, + BSS_MAP_MSG_CIRCUIT_GROUP_BLOCK = 68, + BSS_MAP_MSG_CIRCUIT_GROUP_BLOCKING_ACKNOWLEDGE = 69, + BSS_MAP_MSG_CIRCUIT_GROUP_UNBLOCK = 70, + BSS_MAP_MSG_CIRCUIT_GROUP_UNBLOCKING_ACKNOWLEDGE = 71, + BSS_MAP_MSG_UNEQUIPPED_CIRCUIT = 72, + BSS_MAP_MSG_CHANGE_CIRCUIT = 78, + BSS_MAP_MSG_CHANGE_CIRCUIT_ACKNOWLEDGE = 79, + + /* RADIO RESOURCE MESSAGES */ + BSS_MAP_MSG_RESOURCE_RQST = 80, + BSS_MAP_MSG_RESOURCE_INDICATION = 81, + BSS_MAP_MSG_PAGING = 82, + BSS_MAP_MSG_CIPHER_MODE_CMD = 83, + BSS_MAP_MSG_CLASSMARK_UPDATE = 84, + BSS_MAP_MSG_CIPHER_MODE_COMPLETE = 85, + BSS_MAP_MSG_QUEUING_INDICATION = 86, + BSS_MAP_MSG_COMPLETE_LAYER_3 = 87, + BSS_MAP_MSG_CLASSMARK_RQST = 88, + BSS_MAP_MSG_CIPHER_MODE_REJECT = 89, + BSS_MAP_MSG_LOAD_INDICATION = 90, + + /* VGCS/VBS */ + BSS_MAP_MSG_VGCS_VBS_SETUP = 4, + BSS_MAP_MSG_VGCS_VBS_SETUP_ACK = 5, + BSS_MAP_MSG_VGCS_VBS_SETUP_REFUSE = 6, + BSS_MAP_MSG_VGCS_VBS_ASSIGNMENT_RQST = 7, + BSS_MAP_MSG_VGCS_VBS_ASSIGNMENT_RESULT = 28, + BSS_MAP_MSG_VGCS_VBS_ASSIGNMENT_FAILURE = 29, + BSS_MAP_MSG_VGCS_VBS_QUEUING_INDICATION = 30, + BSS_MAP_MSG_UPLINK_RQST = 31, + BSS_MAP_MSG_UPLINK_RQST_ACKNOWLEDGE = 39, + BSS_MAP_MSG_UPLINK_RQST_CONFIRMATION = 73, + BSS_MAP_MSG_UPLINK_RELEASE_INDICATION = 74, + BSS_MAP_MSG_UPLINK_REJECT_CMD = 75, + BSS_MAP_MSG_UPLINK_RELEASE_CMD = 76, + BSS_MAP_MSG_UPLINK_SEIZED_CMD = 77, +}; + +enum GSM0808_IE_CODING { + GSM0808_IE_CIRCUIT_IDENTITY_CODE = 1, + GSM0808_IE_RESERVED_0 = 2, + GSM0808_IE_RESOURCE_AVAILABLE = 3, + GSM0808_IE_CAUSE = 4, + GSM0808_IE_CELL_IDENTIFIER = 5, + GSM0808_IE_PRIORITY = 6, + GSM0808_IE_LAYER_3_HEADER_INFORMATION = 7, + GSM0808_IE_IMSI = 8, + GSM0808_IE_TMSI = 9, + GSM0808_IE_ENCRYPTION_INFORMATION = 10, + GSM0808_IE_CHANNEL_TYPE = 11, + GSM0808_IE_PERIODICITY = 12, + GSM0808_IE_EXTENDED_RESOURCE_INDICATOR = 13, + GSM0808_IE_NUMBER_OF_MSS = 14, + GSM0808_IE_RESERVED_1 = 15, + GSM0808_IE_RESERVED_2 = 16, + GSM0808_IE_RESERVED_3 = 17, + 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, + GSM0808_IE_DLCI = 24, + GSM0808_IE_DOWNLINK_DTX_FLAG = 25, + GSM0808_IE_CELL_IDENTIFIER_LIST = 26, + GSM0808_IE_RESPONSE_RQST = 27, + GSM0808_IE_RESOURCE_INDICATION_METHOD = 28, + GSM0808_IE_CLASSMARK_INFORMATION_TYPE_1 = 29, + GSM0808_IE_CIRCUIT_IDENTITY_CODE_LIST = 30, + GSM0808_IE_DIAGNOSTIC = 31, + GSM0808_IE_LAYER_3_MESSAGE_CONTENTS = 32, + GSM0808_IE_CHOSEN_CHANNEL = 33, + GSM0808_IE_TOTAL_RESOURCE_ACCESSIBLE = 34, + GSM0808_IE_CIPHER_RESPONSE_MODE = 35, + GSM0808_IE_CHANNEL_NEEDED = 36, + GSM0808_IE_TRACE_TYPE = 37, + GSM0808_IE_TRIGGERID = 38, + GSM0808_IE_TRACE_REFERENCE = 39, + GSM0808_IE_TRANSACTIONID = 40, + GSM0808_IE_MOBILE_IDENTITY = 41, + GSM0808_IE_OMCID = 42, + GSM0808_IE_FORWARD_INDICATOR = 43, + GSM0808_IE_CHOSEN_ENCR_ALG = 44, + GSM0808_IE_CIRCUIT_POOL = 45, + GSM0808_IE_CIRCUIT_POOL_LIST = 46, + GSM0808_IE_TIME_INDICATION = 47, + GSM0808_IE_RESOURCE_SITUATION = 48, + GSM0808_IE_CURRENT_CHANNEL_TYPE_1 = 49, + GSM0808_IE_QUEUEING_INDICATOR = 50, + GSM0808_IE_SPEECH_VERSION = 64, + GSM0808_IE_ASSIGNMENT_REQUIREMENT = 51, + GSM0808_IE_TALKER_FLAG = 53, + GSM0808_IE_CONNECTION_RELEASE_RQSTED = 54, + GSM0808_IE_GROUP_CALL_REFERENCE = 55, + GSM0808_IE_EMLPP_PRIORITY = 56, + 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_CTRL_SUPPR = 63, + GSM0808_IE_LCS_PRIORITY = 67, + GSM0808_IE_LOCATION_TYPE = 68, + GSM0808_IE_LOCATION_ESTIMATE = 69, + GSM0808_IE_POSITIONING_DATA = 70, + GSM0808_IE_LCS_CAUSE = 71, + GSM0808_IE_LCS_CLIENT_TYPE = 72, + GSM0808_IE_APDU = 73, + GSM0808_IE_NETWORK_ELEMENT_IDENTITY = 74, + GSM0808_IE_GPS_ASSISTANCE_DATA = 75, + GSM0808_IE_DECIPHERING_KEYS = 76, + GSM0808_IE_RETURN_ERROR_RQST = 77, + GSM0808_IE_RETURN_ERROR_CAUSE = 78, + GSM0808_IE_SEGMENTATION = 79, + GSM0808_IE_SERVICE_HANDOVER = 80, + GSM0808_IE_SOURCE_RNC_TO_TARGET_RNC_TRANSPARENT_UMTS = 81, + GSM0808_IE_SOURCE_RNC_TO_TARGET_RNC_TRANSPARENT_CDMA2000= 82, + GSM0808_IE_RESERVED_5 = 65, + GSM0808_IE_RESERVED_6 = 66, +}; + +enum gsm0808_cause { + GSM0808_CAUSE_RADIO_INTERFACE_MESSAGE_FAILURE = 0, + GSM0808_CAUSE_RADIO_INTERFACE_FAILURE = 1, + GSM0808_CAUSE_UPLINK_QUALITY = 2, + GSM0808_CAUSE_UPLINK_STRENGTH = 3, + GSM0808_CAUSE_DOWNLINK_QUALITY = 4, + GSM0808_CAUSE_DOWNLINK_STRENGTH = 5, + GSM0808_CAUSE_DISTANCE = 6, + GSM0808_CAUSE_O_AND_M_INTERVENTION = 7, + GSM0808_CAUSE_RESPONSE_TO_MSC_INVOCATION = 8, + GSM0808_CAUSE_CALL_CONTROL = 9, + GSM0808_CAUSE_RADIO_INTERFACE_FAILURE_REVERSION = 10, + GSM0808_CAUSE_HANDOVER_SUCCESSFUL = 11, + GSM0808_CAUSE_BETTER_CELL = 12, + GSM0808_CAUSE_DIRECTED_RETRY = 13, + GSM0808_CAUSE_JOINED_GROUP_CALL_CHANNEL = 14, + GSM0808_CAUSE_TRAFFIC = 15, + GSM0808_CAUSE_EQUIPMENT_FAILURE = 32, + GSM0808_CAUSE_NO_RADIO_RESOURCE_AVAILABLE = 33, + GSM0808_CAUSE_RQSTED_TERRESTRIAL_RESOURCE_UNAVAILABLE = 34, + GSM0808_CAUSE_CCCH_OVERLOAD = 35, + GSM0808_CAUSE_PROCESSOR_OVERLOAD = 36, + GSM0808_CAUSE_BSS_NOT_EQUIPPED = 37, + GSM0808_CAUSE_MS_NOT_EQUIPPED = 38, + GSM0808_CAUSE_INVALID_CELL = 39, + GSM0808_CAUSE_TRAFFIC_LOAD = 40, + GSM0808_CAUSE_PREEMPTION = 41, + GSM0808_CAUSE_RQSTED_TRANSCODING_RATE_ADAPTION_UNAVAILABLE = 48, + GSM0808_CAUSE_CIRCUIT_POOL_MISMATCH = 49, + GSM0808_CAUSE_SWITCH_CIRCUIT_POOL = 50, + GSM0808_CAUSE_RQSTED_SPEECH_VERSION_UNAVAILABLE = 51, + GSM0808_CAUSE_LSA_NOT_ALLOWED = 52, + GSM0808_CAUSE_CIPHERING_ALGORITHM_NOT_SUPPORTED = 64, + GSM0808_CAUSE_TERRESTRIAL_CIRCUIT_ALREADY_ALLOCATED = 80, + GSM0808_CAUSE_INVALID_MESSAGE_CONTENTS = 81, + GSM0808_CAUSE_INFORMATION_ELEMENT_OR_FIELD_MISSING = 82, + GSM0808_CAUSE_INCORRECT_VALUE = 83, + GSM0808_CAUSE_UNKNOWN_MESSAGE_TYPE = 84, + GSM0808_CAUSE_UNKNOWN_INFORMATION_ELEMENT = 85, + GSM0808_CAUSE_PROTOCOL_ERROR_BETWEEN_BSS_AND_MSC = 96, +}; + +/* GSM 08.08 3.2.2.11 Channel Type */ +enum gsm0808_chan_indicator { + GSM0808_CHAN_SPEECH = 1, + GSM0808_CHAN_DATA = 2, + GSM0808_CHAN_SIGN = 3, +}; + +enum gsm0808_chan_rate_type_data { + GSM0808_DATA_FULL_BM = 0x8, + GSM0808_DATA_HALF_LM = 0x9, + GSM0808_DATA_FULL_RPREF = 0xa, + GSM0808_DATA_HALF_PREF = 0xb, + GSM0808_DATA_FULL_PREF_NO_CHANGE = 0x1a, + GSM0808_DATA_HALF_PREF_NO_CHANGE = 0x1b, + GSM0808_DATA_MULTI_MASK = 0x20, + GSM0808_DATA_MULTI_MASK_NO_CHANGE = 0x30, +}; + +enum gsm0808_chan_rate_type_speech { + GSM0808_SPEECH_FULL_BM = 0x8, + GSM0808_SPEECH_HALF_LM = 0x9, + GSM0808_SPEECH_FULL_PREF= 0xa, + GSM0808_SPEECH_HALF_PREF= 0xb, + GSM0808_SPEECH_FULL_PREF_NO_CHANGE = 0x1a, + GSM0808_SPEECH_HALF_PREF_NO_CHANGE = 0x1b, + GSM0808_SPEECH_PERM = 0xf, + GSM0808_SPEECH_PERM_NO_CHANGE = 0x1f, +}; + +enum gsm0808_permitted_speech { + GSM0808_PERM_FR1 = 0x01, + GSM0808_PERM_FR2 = 0x11, + GSM0808_PERM_FR3 = 0x21, + GSM0808_PERM_HR1 = GSM0808_PERM_FR1 | 0x4, + GSM0808_PERM_HR2 = GSM0808_PERM_FR2 | 0x4, + GSM0808_PERM_HR3 = GSM0808_PERM_FR3 | 0x4, +}; + +int bssmap_rcvmsg_dt1(struct sccp_connection *conn, struct msgb *msg, unsigned int length); +int bssmap_rcvmsg_udt(struct gsm_network *net, struct msgb *msg, unsigned int length); + +struct msgb *bssmap_create_layer3(struct msgb *msg); +struct msgb *bssmap_create_reset(void); +struct msgb *bssmap_create_clear_complete(void); +struct msgb *bssmap_create_cipher_complete(struct msgb *layer3); +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); +struct msgb *bssmap_create_classmark_update(const u_int8_t *classmark, u_int8_t length); + +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); + +void bsc_queue_connection_write(struct sccp_connection *conn, struct msgb *msg); +void bsc_free_queued(struct sccp_connection *conn); +void bsc_send_queued(struct sccp_connection *conn); + +void bts_queue_send(struct msgb *msg, int link_id); +void bts_send_queued(struct bss_sccp_connection_data*); +void bts_free_queued(struct bss_sccp_connection_data*); + +#endif diff --git a/openbsc/include/openbsc/chan_alloc.h b/openbsc/include/openbsc/chan_alloc.h index 38855d1ee..ef42fe1c5 100644 --- a/openbsc/include/openbsc/chan_alloc.h +++ b/openbsc/include/openbsc/chan_alloc.h @@ -23,6 +23,28 @@ #include "gsm_subscriber.h" +/* + * Refcounting for the lchan. If the refcount drops to zero + * the channel will send a RSL release request. + */ +#define use_lchan(lchan) \ + do { lchan->use_count++; \ + DEBUGP(DCC, "lchan (bts=%d,trx=%d,ts=%d,ch=%d) increases usage to: %d\n", \ + lchan->ts->trx->bts->nr, lchan->ts->trx->nr, lchan->ts->nr, \ + lchan->nr, lchan->use_count); \ + } while(0); + +#define put_lchan(lchan) \ + do { lchan->use_count--; \ + DEBUGP(DCC, "lchan (bts=%d,trx=%d,ts=%d,ch=%d) decreases usage to: %d\n", \ + lchan->ts->trx->bts->nr, lchan->ts->trx->nr, lchan->ts->nr, \ + lchan->nr, lchan->use_count); \ + if (lchan->use_count <= 0) \ + _lchan_release(lchan); \ + } while(0); + + + /* Special allocator for C0 of BTS */ struct gsm_bts_trx_ts *ts_c0_alloc(struct gsm_bts *bts, enum gsm_phys_chan_config pchan); @@ -46,7 +68,7 @@ struct gsm_lchan *lchan_alloc(struct gsm_bts *bts, enum gsm_chan_t type); /* Free a logical channel (SDCCH, TCH, ...) */ void lchan_free(struct gsm_lchan *lchan); -/* Consider releasing the channel */ -int lchan_auto_release(struct gsm_lchan *lchan); +/* internal.. do not use */ +int _lchan_release(struct gsm_lchan *lchan); #endif /* _CHAN_ALLOC_H */ diff --git a/openbsc/include/openbsc/gsm_data.h b/openbsc/include/openbsc/gsm_data.h index 0ac8674fd..7fb6a8448 100644 --- a/openbsc/include/openbsc/gsm_data.h +++ b/openbsc/include/openbsc/gsm_data.h @@ -85,36 +85,49 @@ typedef int gsm_cbfn(unsigned int hooknum, struct msgb *msg, void *data, void *param); -/* - * Use the channel. As side effect the lchannel recycle timer - * will be started. - */ -#define LCHAN_RELEASE_TIMEOUT 20, 0 -#define use_lchan(lchan) \ - do { lchan->use_count++; \ - DEBUGP(DCC, "lchan (bts=%d,trx=%d,ts=%d,ch=%d) increases usage to: %d\n", \ - lchan->ts->trx->bts->nr, lchan->ts->trx->nr, lchan->ts->nr, \ - lchan->nr, lchan->use_count); \ - bsc_schedule_timer(&lchan->release_timer, LCHAN_RELEASE_TIMEOUT); } while(0); - -#define put_lchan(lchan) \ - do { lchan->use_count--; \ - DEBUGP(DCC, "lchan (bts=%d,trx=%d,ts=%d,ch=%d) decreases usage to: %d\n", \ - lchan->ts->trx->bts->nr, lchan->ts->trx->nr, lchan->ts->nr, \ - lchan->nr, lchan->use_count); \ - } while(0); - - /* communications link with a BTS */ struct gsm_bts_link { struct gsm_bts *bts; }; +struct sccp_connection; struct gsm_lchan; struct gsm_subscriber; struct gsm_mncc; struct rtp_socket; +/* BSC/MSC data holding them together */ +struct bss_sccp_connection_data { + struct gsm_lchan *lchan; + struct sccp_connection *sccp; + int ciphering_handled : 1; + + /* Timers... */ + + /* for assginment command */ + struct timer_list T10; + + /* for SCCP ... */ + struct timer_list sccp_it; + + /* audio handling */ + int rtp_port; + + /* Queue SCCP and GSM0408 messages */ + struct llist_head gsm_queue; + unsigned int gsm_queue_size; + + struct llist_head sccp_queue; + 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(); +void bss_sccp_free_data(struct bss_sccp_connection_data *); + + /* Network Management State */ struct gsm_nm_state { u_int8_t operational; @@ -170,9 +183,6 @@ struct gsm_lchan { /* To whom we are allocated at the moment */ struct gsm_subscriber *subscr; - /* Timer started to release the channel */ - struct timer_list release_timer; - struct timer_list T3101; /* Established data link layer services */ @@ -183,6 +193,12 @@ struct gsm_lchan { */ struct gsm_loc_updating_operation *loc_operation; + /* + * MSC handling... + */ + struct bss_sccp_connection_data *msc_data; + + /* use count. how many users use this channel */ unsigned int use_count; }; @@ -257,6 +273,9 @@ struct gsm_bts_trx { } bs11; }; struct gsm_bts_trx_ts ts[TRX_NR_TS]; + + /* NM state */ + int rf_locked; }; enum gsm_bts_type { @@ -405,6 +424,14 @@ enum gsm_auth_policy { GSM_AUTH_POLICY_TOKEN, /* accept first, send token per sms, then revoke authorization */ }; +/* + * internal data for audio management + */ +struct gsm_audio_support { + u_int8_t hr : 1, + ver : 7; +}; + struct gsm_network { /* global parameters */ u_int16_t country_code; @@ -415,6 +442,11 @@ struct gsm_network { int a5_encryption; int neci; + struct gsm_audio_support **audio_support; + int audio_length; + int rtp_payload; + int rtp_base_port; + /* layer 4 */ int (*mncc_recv) (struct gsm_network *net, int msg_type, void *arg); struct llist_head upqueue; @@ -506,4 +538,6 @@ static inline int is_siemens_bts(struct gsm_bts *bts) enum gsm_auth_policy gsm_auth_policy_parse(const char *arg); const char *gsm_auth_policy_name(enum gsm_auth_policy policy); +void gsm_trx_lock_rf(struct gsm_bts_trx *trx, int locked); + #endif diff --git a/openbsc/include/openbsc/gsm_subscriber.h b/openbsc/include/openbsc/gsm_subscriber.h index d612ed566..eac0ae2a9 100644 --- a/openbsc/include/openbsc/gsm_subscriber.h +++ b/openbsc/include/openbsc/gsm_subscriber.h @@ -81,6 +81,8 @@ struct gsm_subscriber *subscr_get_by_extension(struct gsm_network *net, const char *ext); struct gsm_subscriber *subscr_get_by_id(struct gsm_network *net, unsigned long long id); +struct gsm_subscriber *subscr_get_or_create(struct gsm_network *net, + const char *imsi); int subscr_update(struct gsm_subscriber *s, struct gsm_bts *bts, int reason); void subscr_put_channel(struct gsm_lchan *lchan); void subscr_get_channel(struct gsm_subscriber *subscr, diff --git a/openbsc/include/openbsc/mgcp.h b/openbsc/include/openbsc/mgcp.h index fa6224c12..3cba203de 100644 --- a/openbsc/include/openbsc/mgcp.h +++ b/openbsc/include/openbsc/mgcp.h @@ -21,8 +21,6 @@ * */ -unsigned int rtp_base_port = 4000; - /** * Calculate the RTP audio port for the given multiplex * and the direction. This allows a semi static endpoint diff --git a/openbsc/include/openbsc/msgb.h b/openbsc/include/openbsc/msgb.h index ab3c03396..1a0688f57 100644 --- a/openbsc/include/openbsc/msgb.h +++ b/openbsc/include/openbsc/msgb.h @@ -37,6 +37,7 @@ struct msgb { unsigned char *l2h; unsigned char *l3h; unsigned char *smsh; + unsigned char *l4h; u_int16_t data_len; u_int16_t len; @@ -55,6 +56,7 @@ extern void msgb_reset(struct msgb *m); #define msgb_l2(m) ((void *)(m->l2h)) #define msgb_l3(m) ((void *)(m->l3h)) +#define msgb_l4(m) ((void *)(m->l4h)) #define msgb_sms(m) ((void *)(m->smsh)) static inline unsigned int msgb_l2len(const struct msgb *msgb) diff --git a/openbsc/src/Makefile.am b/openbsc/src/Makefile.am index 9c8fa7b4d..fef7ee001 100644 --- a/openbsc/src/Makefile.am +++ b/openbsc/src/Makefile.am @@ -2,7 +2,7 @@ INCLUDES = $(all_includes) -I$(top_srcdir)/include AM_CFLAGS=-Wall sbin_PROGRAMS = bsc_hack bs11_config ipaccess-find ipaccess-config \ - isdnsync bsc_mgcp + isdnsync bsc_mgcp bsc_msc_ip noinst_LIBRARIES = libbsc.a libmsc.a libvty.a libsccp.a noinst_HEADERS = vty/cardshell.h @@ -11,9 +11,9 @@ libbsc_a_SOURCES = abis_rsl.c abis_nm.c gsm_data.c gsm_04_08_utils.c \ gsm_subscriber_base.c subchan_demux.c bsc_rll.c transaction.c \ trau_frame.c trau_mux.c paging.c e1_config.c e1_input.c tlv_parser.c \ input/misdn.c input/ipaccess.c signal.c gsm_utils.c talloc.c \ - talloc_ctx.c + talloc_ctx.c telnet_interface.c -libmsc_a_SOURCES = gsm_subscriber.c db.c telnet_interface.c \ +libmsc_a_SOURCES = gsm_subscriber.c db.c \ mncc.c rtp_proxy.c gsm_04_08.c gsm_04_11.c transaction.c \ token_auth.c rrlp.c gsm_04_80.c ussd.c silent_call.c @@ -24,6 +24,9 @@ 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) +bsc_msc_ip_SOURCES = bssap.c bsc_msc_ip.c bsc_init.c vty_interface.c vty_interface_bsc.c +bsc_msc_ip_LDADD = libbsc.a libvty.a libsccp.a + bs11_config_SOURCES = bs11_config.c abis_nm.c gsm_data.c msgb.c debug.c \ select.c timer.c rs232.c tlv_parser.c signal.c talloc.c diff --git a/openbsc/src/abis_nm.c b/openbsc/src/abis_nm.c index 4d4cec0a3..288903d97 100755 --- a/openbsc/src/abis_nm.c +++ b/openbsc/src/abis_nm.c @@ -2699,6 +2699,16 @@ int abis_nm_ipaccess_set_attr(struct gsm_bts *bts, u_int8_t obj_class, attr, attr_len); } +void gsm_trx_lock_rf(struct gsm_bts_trx *trx, int locked) +{ + int new_state = locked ? NM_STATE_LOCKED : NM_STATE_UNLOCKED; + + trx->rf_locked = locked; + abis_nm_chg_adm_state(trx->bts, NM_OC_RADIO_CARRIER, + trx->bts->bts_nr, trx->nr, 0xff, + new_state); +} + static const char *ipacc_testres_names[] = { [NM_IPACC_TESTRES_SUCCESS] = "SUCCESS", [NM_IPACC_TESTRES_TIMEOUT] = "TIMEOUT", diff --git a/openbsc/src/abis_rsl.c b/openbsc/src/abis_rsl.c index 0dee79b17..4df2ce1aa 100644 --- a/openbsc/src/abis_rsl.c +++ b/openbsc/src/abis_rsl.c @@ -837,6 +837,10 @@ int rsl_data_request(struct msgb *msg, u_int8_t link_id) return -EINVAL; } + if (msg->lchan->use_count <= 0) { + DEBUGP(DRSL, "BUG: Trying to send data on unused lchan\n"); + } + /* First push the L3 IE tag and length */ msgb_tv16_push(msg, RSL_IE_L3_INFO, l3_len); @@ -1563,9 +1567,21 @@ static int abis_rsl_rx_ipacc(struct msgb *msg) /* Entry-point where L2 RSL from BTS enters */ int abis_rsl_rcvmsg(struct msgb *msg) { - struct abis_rsl_common_hdr *rslh = msgb_l2(msg) ; + struct abis_rsl_common_hdr *rslh; int rc = 0; + if (!msg) { + DEBUGP(DRSL, "Empty RSL msg?..\n"); + return -1; + } + + if (msgb_l2len(msg) < sizeof(*rslh)) { + DEBUGP(DRSL, "Truncated RSL message with l2len: %u\n", msgb_l2len(msg)); + return -1; + } + + rslh = msgb_l2(msg); + switch (rslh->msg_discr & 0xfe) { case ABIS_RSL_MDISC_RLL: rc = abis_rsl_rx_rll(msg); diff --git a/openbsc/src/bsc_init.c b/openbsc/src/bsc_init.c index d11cde578..83d30f116 100644 --- a/openbsc/src/bsc_init.c +++ b/openbsc/src/bsc_init.c @@ -357,12 +357,15 @@ int nm_state_event(enum nm_evt evt, u_int8_t obj_class, void *obj, 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) + new_state->availability == NM_AVSTATE_OK) { + printf("STARTING SITE MANAGER\n"); abis_nm_opstart(bts, obj_class, 0xff, 0xff, 0xff); + } break; case NM_OC_BTS: bts = obj; if (new_state->availability == NM_AVSTATE_DEPENDENCY) { + printf("STARTING BTS...\n"); patch_nm_tables(bts); abis_nm_set_bts_attr(bts, nanobts_attr_bts, sizeof(nanobts_attr_bts)); @@ -378,6 +381,7 @@ int nm_state_event(enum nm_evt evt, u_int8_t obj_class, void *obj, trx = ts->trx; if (new_state->operational == 1 && new_state->availability == NM_AVSTATE_DEPENDENCY) { + printf("STARTING OC Channel...\n"); patch_nm_tables(trx->bts); enum abis_nm_chan_comb ccomb = abis_nm_chcomb4pchan(ts->pchan); @@ -392,36 +396,11 @@ int nm_state_event(enum nm_evt evt, u_int8_t obj_class, void *obj, case NM_OC_RADIO_CARRIER: trx = obj; if (new_state->operational == 1 && - new_state->availability == NM_AVSTATE_OFF_LINE) { - /* Patch ARFCN into radio attribute */ - nanobts_attr_radio[5] &= 0xf0; - nanobts_attr_radio[5] |= trx->arfcn >> 8; - nanobts_attr_radio[6] = trx->arfcn & 0xff; - abis_nm_set_radio_attr(trx, nanobts_attr_radio, - sizeof(nanobts_attr_radio)); - abis_nm_chg_adm_state(trx->bts, obj_class, - trx->bts->bts_nr, trx->nr, 0xff, - NM_STATE_UNLOCKED); + new_state->availability == NM_AVSTATE_OK) { + printf("STARTING NM Radio Carrier...\n"); abis_nm_opstart(trx->bts, obj_class, trx->bts->bts_nr, trx->nr, 0xff); } - if (new_state->operational == 1 && - new_state->availability == NM_AVSTATE_OK) - abis_nm_opstart(trx->bts, obj_class, trx->bts->bts_nr, - trx->nr, 0xff); - break; - case NM_OC_BASEB_TRANSC: - trx = container_of(obj, struct gsm_bts_trx, bb_transc); - if (new_state->operational == 1 && - new_state->availability == NM_AVSTATE_DEPENDENCY) { - abis_nm_chg_adm_state(trx->bts, obj_class, - trx->bts->bts_nr, trx->nr, 0xff, - NM_STATE_UNLOCKED); - abis_nm_opstart(trx->bts, obj_class, - trx->bts->bts_nr, trx->nr, 0xff); - /* TRX software is active, tell it to initiate RSL Link */ - abis_nm_ipaccess_rsl_connect(trx, 0, 3003, trx->rsl_tei); - } break; default: break; @@ -438,6 +417,42 @@ static int sw_activ_rep(struct msgb *mb) switch (foh->obj_class) { + case NM_OC_BASEB_TRANSC: + printf("Starting baseband\n"); + abis_nm_chg_adm_state(trx->bts, foh->obj_class, + trx->bts->bts_nr, trx->nr, 0xff, + NM_STATE_UNLOCKED); + abis_nm_opstart(trx->bts, foh->obj_class, + trx->bts->bts_nr, trx->nr, 0xff); + /* TRX software is active, tell it to initiate RSL Link */ + abis_nm_ipaccess_rsl_connect(trx, 0, 3003, trx->rsl_tei); + break; + case NM_OC_RADIO_CARRIER: { + /* + * Locking the radio carrier will make it go + * offline again and we would come here. The + * framework should determine that there was + * no change and avoid recursion. + * + * This code is here to make sure that on start + * a TRX remains locked. + */ + int rc_state = trx->rf_locked ? + NM_STATE_LOCKED : NM_STATE_UNLOCKED; + printf("Starting radio: %d %d\n", rc_state, trx->rf_locked); + /* Patch ARFCN into radio attribute */ + nanobts_attr_radio[5] &= 0xf0; + nanobts_attr_radio[5] |= trx->arfcn >> 8; + nanobts_attr_radio[6] = trx->arfcn & 0xff; + abis_nm_set_radio_attr(trx, nanobts_attr_radio, + sizeof(nanobts_attr_radio)); + abis_nm_chg_adm_state(trx->bts, foh->obj_class, + trx->bts->bts_nr, trx->nr, 0xff, + rc_state); + abis_nm_opstart(trx->bts, foh->obj_class, trx->bts->bts_nr, + trx->nr, 0xff); + break; + } } return 0; } diff --git a/openbsc/src/bsc_mgcp.c b/openbsc/src/bsc_mgcp.c index 6d5e6b154..045dff29c 100644 --- a/openbsc/src/bsc_mgcp.c +++ b/openbsc/src/bsc_mgcp.c @@ -63,6 +63,7 @@ static const char *audio_name = "GSM-EFR/8000"; static int audio_payload = 97; static int audio_loop = 0; static int early_bind = 0; +static int rtp_base_port = 0; static char *config_file = "mgcp.cfg"; diff --git a/openbsc/src/bsc_msc_ip.c b/openbsc/src/bsc_msc_ip.c new file mode 100644 index 000000000..0d441ebbc --- /dev/null +++ b/openbsc/src/bsc_msc_ip.c @@ -0,0 +1,813 @@ +/* A hackish minimal BSC (+MSC +HLR) implementation */ + +/* (C) 2008-2009 by Harald Welte <laforge@gnumonks.org> + * (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 <unistd.h> +#include <time.h> +#include <errno.h> +#include <signal.h> +#include <fcntl.h> +#include <sys/stat.h> + +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#define _GNU_SOURCE +#include <getopt.h> + +#include <openbsc/select.h> +#include <openbsc/debug.h> +#include <openbsc/e1_input.h> +#include <openbsc/talloc.h> +#include <openbsc/select.h> +#include <openbsc/ipaccess.h> +#include <openbsc/bssap.h> +#include <openbsc/paging.h> +#include <openbsc/signal.h> +#include <openbsc/chan_alloc.h> + +#include <sccp/sccp.h> + +/* SCCP helper */ +#define SCCP_IT_TIMER 60 + +/* MCC and MNC for the Location Area Identifier */ +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); +extern int bsc_shutdown_net(struct gsm_network *net); + +struct bss_sccp_connection_data *bss_sccp_create_data() +{ + struct bss_sccp_connection_data *data; + + data = _talloc_zero(tall_bsc_ctx, + sizeof(struct bss_sccp_connection_data), + "bsc<->msc"); + if (!data) + return NULL; + + INIT_LLIST_HEAD(&data->sccp_queue); + INIT_LLIST_HEAD(&data->gsm_queue); + return data; +} + +void bss_sccp_free_data(struct bss_sccp_connection_data *data) +{ + bsc_del_timer(&data->T10); + bsc_del_timer(&data->sccp_it); + bsc_free_queued(data->sccp); + bts_free_queued(data); + talloc_free(data); +} + +static void sccp_it_fired(void *_data) +{ + struct bss_sccp_connection_data *data = + (struct bss_sccp_connection_data *) _data; + + sccp_connection_send_it(data->sccp); + bsc_schedule_timer(&data->sccp_it, SCCP_IT_TIMER, 0); +} + + +/* GSM subscriber drop-ins */ +extern struct llist_head *subscr_bsc_active_subscriber(void); +struct gsm_subscriber *find_subscriber(u_int8_t type, const char *mi_string) +{ + struct gsm_subscriber *subscr; + u_int32_t tmsi = GSM_RESERVED_TMSI; + if (type == GSM_MI_TYPE_TMSI) { + tmsi = tmsi_from_string(mi_string); + if (tmsi == GSM_RESERVED_TMSI) { + DEBUGP(DMSC, "The TMSI is the reserved one.\n"); + return NULL; + } + } + + llist_for_each_entry(subscr, subscr_bsc_active_subscriber(), entry) { + if (type == GSM_MI_TYPE_TMSI && tmsi == subscr->tmsi) { + return subscr_get(subscr); + } else if (type == GSM_MI_TYPE_IMSI && strcmp(mi_string, subscr->imsi) == 0) { + return subscr_get(subscr); + } + } + + DEBUGP(DMSC, "No subscriber has been found.\n"); + return NULL; +} + + + +/* SCCP handling */ +void msc_outgoing_sccp_data(struct sccp_connection *conn, struct msgb *msg, unsigned int len) +{ + struct bssmap_header *bs; + + if (len < 1) { + DEBUGP(DMSC, "The header is too short.\n"); + return; + } + + switch (msg->l3h[0]) { + case BSSAP_MSG_BSS_MANAGEMENT: + msg->l4h = &msg->l3h[sizeof(*bs)]; + msg->lchan = sccp_get_lchan(conn->data_ctx); + bssmap_rcvmsg_dt1(conn, msg, len - sizeof(*bs)); + break; + case BSSAP_MSG_DTAP: + dtap_rcvmsg(sccp_get_lchan(conn->data_ctx), msg, len); + break; + default: + DEBUGPC(DMSC, "Unimplemented msg type: %d\n", msg->l3h[0]); + } +} + +void msc_outgoing_sccp_state(struct sccp_connection *conn, int old_state) +{ + if (conn->connection_state >= SCCP_CONNECTION_STATE_RELEASE_COMPLETE) { + DEBUGP(DMSC, "Freeing sccp conn: %p state: %d\n", conn, conn->connection_state); + if (sccp_get_lchan(conn->data_ctx) != NULL) { + struct gsm_lchan *lchan = sccp_get_lchan(conn->data_ctx); + + DEBUGP(DMSC, "ERROR: The lchan is still associated\n."); + + lchan->msc_data = NULL; + put_lchan(lchan); + } + + bss_sccp_free_data((struct bss_sccp_connection_data *)conn->data_ctx); + sccp_connection_free(conn); + return; + } else if (conn->connection_state == SCCP_CONNECTION_STATE_ESTABLISHED) { + DEBUGP(DMSC, "Connection established: %p\n", conn); + bsc_send_queued(conn); + } +} + +/* + * General COMPLETE LAYER3 INFORMATION handling for + * PAGING RESPONSE, LOCATION UPDATING REQUEST, CM REESTABLISHMENT REQUEST, + * CM SERVICE REQUEST, IMSI DETACH, IMMEDIATE SETUP. + * + * IMMEDIATE SETUP is coming from GROUP CC that is not yet + * supported... + */ +int open_sccp_connection(struct msgb *layer3) +{ + struct bss_sccp_connection_data *con_data; + struct sccp_connection *sccp_connection; + struct msgb *data; + + DEBUGP(DMSC, "Opening new layer3 connection\n"); + sccp_connection = sccp_connection_socket(); + if (!sccp_connection) { + DEBUGP(DMSC, "Failed to allocate memory.\n"); + return -ENOMEM; + } + + data = bssmap_create_layer3(layer3); + if (!data) { + DEBUGP(DMSC, "Failed to allocate complete layer3.\n"); + sccp_connection_free(sccp_connection); + return -ENOMEM; + } + + con_data = bss_sccp_create_data(); + if (!con_data) { + DEBUGP(DMSC, "Failed to allocate bss<->msc data.\n"); + sccp_connection_free(sccp_connection); + msgb_free(data); + return -ENOMEM; + } + + /* initialize the bridge */ + con_data->lchan = layer3->lchan; + con_data->sccp = sccp_connection; + + sccp_connection->state_cb = msc_outgoing_sccp_state; + sccp_connection->data_cb = msc_outgoing_sccp_data; + sccp_connection->data_ctx = con_data; + layer3->lchan->msc_data = con_data; + + /* start the inactivity test timer */ + con_data->sccp_it.cb = sccp_it_fired; + con_data->sccp_it.data = con_data; + bsc_schedule_timer(&con_data->sccp_it, SCCP_IT_TIMER, 0); + + /* FIXME: Use transaction for this */ + use_lchan(layer3->lchan); + sccp_connection_connect(sccp_connection, &sccp_ssn_bssap, data); + msgb_free(data); + + return 1; +} + +/* figure out if this is the inial layer3 message */ +static int send_dtap_or_open_connection(struct msgb *msg) +{ + if (msg->lchan->msc_data) { + struct msgb *dtap = dtap_create_msg(msg, 0); + if (!dtap) { + DEBUGP(DMSC, "Creating a DTAP message failed.\n"); + return -1; + } + + bsc_queue_connection_write(lchan_get_sccp(msg->lchan), dtap); + return 1; + } else { + return open_sccp_connection(msg); + } +} + +/* Receive a PAGING RESPONSE message from the MS */ +static int handle_paging_response(struct msgb *msg) +{ + struct gsm_subscriber *subscr; + char mi_string[GSM48_MI_SIZE]; + u_int8_t mi_type; + + gsm48_paging_extract_mi(msg, mi_string, &mi_type); + DEBUGP(DMSC, "PAGING RESPONSE: mi_type=0x%02x MI(%s)\n", + mi_type, mi_string); + + subscr = find_subscriber(mi_type, mi_string); + if (!subscr) + return -EINVAL; + + /* force the paging to stop at every bts */ + subscr->lac = GSM_LAC_RESERVED_ALL_BTS; + if (gsm48_handle_paging_resp(msg, subscr) != 0) { + DEBUGP(DMSC, "Paging failed.\n"); + return -1; + } + + /* open a new transaction and SCCP connection */ + return send_dtap_or_open_connection(msg); +} + +/* Receive a CIPHER MODE COMPLETE from the MS */ +static int handle_cipher_m_complete(struct msgb *msg) +{ + struct msgb *resp; + + DEBUGP(DMSC, "CIPHER MODE COMPLETE from MS, forwarding to MSC\n"); + resp = bssmap_create_cipher_complete(msg); + if (!resp) { + DEBUGP(DMSC, "Creating MSC response failed.\n"); + return -1; + } + + + /* handled this message */ + bsc_queue_connection_write(lchan_get_sccp(msg->lchan), resp); + 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); + if (rc < 0) + gsm0808_send_assignment_failure(msg->lchan, + GSM0808_CAUSE_NO_RADIO_RESOURCE_AVAILABLE, NULL); + else + 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) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + int rc = 0; + + switch (gh->msg_type) { + case GSM48_MT_RR_PAG_RESP: + rc = handle_paging_response(msg); + break; + case GSM48_MT_RR_MEAS_REP: + /* ignore measurement for now */ + rc = -1; + break; + 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; + } + + return rc; +} + +/* Receive a GSM 04.08 Mobility Management (MM) message */ +static int gsm0408_rcv_mm(struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + int rc = 0; + + switch (gh->msg_type & 0xbf) { + case GSM48_MT_MM_LOC_UPD_REQUEST: + case GSM48_MT_MM_CM_REEST_REQ: + case GSM48_MT_MM_CM_SERV_REQ: + case GSM48_MT_MM_IMSI_DETACH_IND: + rc = send_dtap_or_open_connection(msg); + break; + default: + break; + } + + return rc; +} + +int gsm0408_rcvmsg(struct msgb *msg, u_int8_t link_id) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + u_int8_t pdisc = gh->proto_discr & 0x0f; + int rc = 0; + + switch (pdisc) { + case GSM48_PDISC_RR: + rc = gsm0408_rcv_rr(msg); + break; + case GSM48_PDISC_MM: + rc = gsm0408_rcv_mm(msg); + break; + default: + break; + } + + /* + * if we have a sccp connection and didn't handle the message + * forward it to the MSC using DTAP + */ + if (rc == 0 && msg->lchan->msc_data && lchan_get_sccp(msg->lchan)) { + struct msgb *dtap = dtap_create_msg(msg, link_id); + if (!dtap) { + DEBUGP(DMSC, "Creating a DTAP message failed.\n"); + return -1; + } + + bsc_queue_connection_write(lchan_get_sccp(msg->lchan), dtap); + } + + 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); + + int rtp_payload = ts->trx->bts->network->rtp_payload; + if (rtp_payload == 0) + rtp_payload = ts->abis_ip.rtp_payload2; + + rc = rsl_ipacc_mdcx(lchan, ntohl(local_addr.s_addr), + lchan->msc_data->rtp_port, + ts->abis_ip.conn_id, + rtp_payload); + 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"); +} + +/* + * SCCP handling + */ +static int msc_sccp_write_ipa(struct msgb *msg, void *data) +{ + int ret; + + DEBUGP(DMSC, "Sending SCCP to MSC: %u\n", msgb_l2len(msg)); + ipaccess_prepend_header(msg, IPAC_PROTO_SCCP); + + + DEBUGP(DMI, "MSC TX %s\n", hexdump(msg->l2h, msgb_l2len(msg))); + ret = write(msc_connection.fd, msg->data, msg->len); + + if (ret <= 0) { + perror("MSC: Failed to send SCCP"); + return -1; + } + + return 0; +} + +static int msc_sccp_accept(struct sccp_connection *connection, void *data) +{ + DEBUGP(DMSC, "Rejecting incoming SCCP connection.\n"); + return -1; +} + +static int msc_sccp_read(struct msgb *msgb, unsigned int length, void *data) +{ + struct bssmap_header *bs; + + DEBUGP(DMSC, "Incoming SCCP message ftom MSC: %s\n", hexdump(msgb->l3h, length)); + + if (length < sizeof(*bs)) { + DEBUGP(DMSC, "The header is too short.\n"); + return -1; + } + + bs = (struct bssmap_header *) msgb->l3h; + if (bs->length < length - sizeof(*bs)) + return -1; + + switch (bs->type) { + case BSSAP_MSG_BSS_MANAGEMENT: + msgb->l4h = &msgb->l3h[sizeof(*bs)]; + bssmap_rcvmsg_udt(bsc_gsmnet, msgb, length - sizeof(*bs)); + break; + default: + DEBUGPC(DMSC, "Unimplemented msg type: %d\n", bs->type); + } + + return 0; +} + + +/* + * network initialisation + */ +static void initialize_if_needed(void) +{ + if (!bsc_gsmnet) { + int rc; + struct msgb *msg; + + fprintf(stderr, "Bootstraping the network. Sending GSM08.08 reset.\n"); + rc = bsc_bootstrap_network(NULL, config_file); + if (rc < 0) { + fprintf(stderr, "Bootstrapping the network failed. exiting.\n"); + exit(1); + } + + + /* send a gsm 08.08 reset message from here */ + msg = bssmap_create_reset(); + if (!msg) { + DEBUGP(DMSC, "Failed to create the reset message.\n"); + return; + } + + sccp_write(msg, &sccp_ssn_bssap, &sccp_ssn_bssap, 0); + msgb_free(msg); + } +} + +/* + * callback with IP access data + */ +static int ipaccess_a_fd_cb(struct bsc_fd *bfd, unsigned int what) +{ + int error; + struct msgb *msg = ipaccess_read_msg(bfd, &error); + struct ipaccess_head *hh; + + if (!msg) { + if (error == 0) { + fprintf(stderr, "The connection to the MSC was lost, exiting\n"); + exit(-2); + } + + fprintf(stderr, "Failed to parse ip access message: %d\n", error); + return -1; + } + + DEBUGP(DMSC, "From MSC: %s proto: %d\n", hexdump(msg->data, msg->len), msg->l2h[0]); + + /* handle base message handling */ + hh = (struct ipaccess_head *) msg->data; + ipaccess_rcvmsg_base(msg, bfd); + + /* initialize the networking. This includes sending a GSM08.08 message */ + if (hh->proto == IPAC_PROTO_IPACCESS && msg->l2h[0] == IPAC_MSGT_ID_ACK) + initialize_if_needed(); + else if (hh->proto == IPAC_PROTO_SCCP) + sccp_system_incoming(msg); + + return 0; +} + +/* + * Connect to the MSC + */ +static int connect_to_msc(const char *ip, int port) +{ + struct sockaddr_in sin; + int on = 1, ret; + + printf("Attempting to connect MSC at %s:%d\n", ip, port); + + msc_connection.fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + msc_connection.cb = ipaccess_a_fd_cb; + msc_connection.when = BSC_FD_READ; + msc_connection.data = NULL; + msc_connection.priv_nr = 1; + + if (msc_connection.fd < 0) { + perror("Creating TCP socket failed"); + return msc_connection.fd; + } + + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = htons(port); + inet_aton(ip, &sin.sin_addr); + + setsockopt(msc_connection.fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + ret = connect(msc_connection.fd, (struct sockaddr *) &sin, sizeof(sin)); + + if (ret < 0) { + perror("Connection failed"); + return ret; + } + + ret = bsc_register_fd(&msc_connection); + if (ret < 0) { + perror("Registering the fd failed"); + close(msc_connection.fd); + return ret; + } + + return ret; +} + +static void print_help() +{ + printf(" Some useful help...\n"); + printf(" -h --help this text\n"); + printf(" -d option --debug=DRLL:DCC:DMM:DRR:DRSL:DNM enable debugging\n"); + 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) +{ + while (1) { + int option_index = 0, c; + static struct option long_options[] = { + {"help", 0, 0, 'h'}, + {"debug", 1, 0, 'd'}, + {"config-file", 1, 0, 'c'}, + {"disable-color", 0, 0, 's'}, + {"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:l:", + long_options, &option_index); + if (c == -1) + break; + + switch (c) { + case 'h': + print_usage(); + print_help(); + exit(0); + case 's': + debug_use_color(0); + break; + case 'd': + debug_parse_category_mask(optarg); + break; + case 'c': + config_file = strdup(optarg); + break; + case 'T': + debug_timestamp(1); + break; + case 'P': + ipacc_rtp_direct = 0; + break; + case 'm': + msc_address = strdup(optarg); + break; + case 'l': + inet_aton(optarg, &local_addr); + break; + default: + /* ignore */ + break; + } + } +} + +static void signal_handler(int signal) +{ + fprintf(stdout, "signal %u received\n", signal); + + switch (signal) { + case SIGINT: + bsc_shutdown_net(bsc_gsmnet); + sleep(3); + exit(0); + break; + case SIGABRT: + /* in case of abort, we want to obtain a talloc report + * and then return to the caller, who will abort the process */ + case SIGUSR1: + talloc_report_full(tall_bsc_ctx, stderr); + break; + default: + break; + } +} + +static void test_mode() +{ + static const u_int8_t assignment_req[] = { 0x01, 0x0b, 0x03, 0x01, 0x0b, 0x25, 0x01, 0x00, 0x01 }; + struct gsm_lchan lchan; + struct sccp_connection conn; + struct bss_sccp_connection_data data; + + struct gsm_bts_trx_ts trx_ts; + struct gsm_bts_trx trx; + struct gsm_bts bts; + int rc; + + /* initialize */ + fprintf(stderr, "Bootstraping the network. Sending GSM08.08 reset.\n"); + rc = bsc_bootstrap_network(NULL, config_file); + if (rc < 0) { + fprintf(stderr, "Bootstrapping the network failed. exiting.\n"); + exit(1); + } + + bts.network = bsc_gsmnet; + trx.bts = &bts; + trx_ts.trx = &trx; + lchan.ts = &trx_ts; + + /* create fake data connection */ + data.lchan = &lchan; + data.sccp = &conn; + lchan.msc_data = &data; + conn.data_ctx = &data; + + + struct msgb *msg = msgb_alloc(400, "test-msg"); + msg->lchan = &lchan; + + msg->l4h = msgb_put(msg, ARRAY_SIZE(assignment_req)); + memcpy(msg->l4h, assignment_req, ARRAY_SIZE(assignment_req)); + bssmap_rcvmsg_dt1(&conn, msg, ARRAY_SIZE(assignment_req)); +} + +int main(int argc, char **argv) +{ + int rc; + + tall_bsc_ctx = talloc_named_const(NULL, 1, "openbsc"); + + /* parse options */ + handle_options(argc, argv); + + /* seed the PRNG */ + srand(time(NULL)); + + /* initialize sccp */ + sccp_system_init(msc_sccp_write_ipa, NULL); + 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"); + exit(1); + } + + signal(SIGINT, &signal_handler); + signal(SIGABRT, &signal_handler); + signal(SIGUSR1, &signal_handler); + + while (1) { + bsc_select_main(0); + } +} diff --git a/openbsc/src/bssap.c b/openbsc/src/bssap.c new file mode 100644 index 000000000..48f4ae119 --- /dev/null +++ b/openbsc/src/bssap.c @@ -0,0 +1,1134 @@ +/* GSM 08.08 BSSMAP handling */ +/* (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 <openbsc/bssap.h> +#include <openbsc/bsc_rll.h> +#include <openbsc/gsm_04_08.h> +#include <openbsc/gsm_subscriber.h> +#include <openbsc/debug.h> +#include <openbsc/mgcp.h> +#include <openbsc/signal.h> +#include <openbsc/tlv.h> +#include <openbsc/paging.h> +#include <openbsc/chan_alloc.h> + +#include <sccp/sccp.h> + +#include <arpa/inet.h> +#include <assert.h> + + +#define BSSMAP_MSG_SIZE 512 +#define BSSMAP_MSG_HEADROOM 128 + + +static const struct tlv_definition bss_att_tlvdef = { + .def = { + [GSM0808_IE_IMSI] = { TLV_TYPE_TLV }, + [GSM0808_IE_TMSI] = { TLV_TYPE_TLV }, + [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}, + [GSM0808_IE_ENCRYPTION_INFORMATION] = { TLV_TYPE_TLV }, + [GSM0808_IE_CIPHER_RESPONSE_MODE] = { TLV_TYPE_TV }, + }, +}; + + +static int bssmap_paging_cb(unsigned int hooknum, unsigned int event, struct msgb *msg, void *data, void *param) +{ + DEBUGP(DMSC, "Paging is complete.\n"); + return 0; +} + +static int bssmap_handle_reset_ack(struct gsm_network *net, struct msgb *msg, unsigned int length) +{ + DEBUGP(DMSC, "Reset ACK from MSC\n"); + + return 0; +} + +/* GSM 08.08 § 3.2.1.19 */ +static int bssmap_handle_paging(struct gsm_network *net, struct msgb *msg, unsigned int payload_length) +{ + struct tlv_parsed tp; + char mi_string[GSM48_MI_SIZE]; + u_int32_t tmsi = GSM_RESERVED_TMSI; + unsigned int lac = GSM_LAC_RESERVED_ALL_BTS; + u_int8_t data_length; + const u_int8_t *data; + struct gsm_subscriber *subscr; + u_int8_t chan_needed = RSL_CHANNEED_ANY; + int paged; + + tlv_parse(&tp, &bss_att_tlvdef, msg->l4h + 1, payload_length - 1, 0, 0); + + if (!TLVP_PRESENT(&tp, GSM0808_IE_IMSI)) { + DEBUGP(DMSC, "Mandantory IMSI not present.\n"); + return -1; + } else if ((TLVP_VAL(&tp, GSM0808_IE_IMSI)[0] & GSM_MI_TYPE_MASK) != GSM_MI_TYPE_IMSI) { + DEBUGP(DMSC, "Wrong content in the IMSI\n"); + return -1; + } + + if (!TLVP_PRESENT(&tp, GSM0808_IE_CELL_IDENTIFIER_LIST)) { + DEBUGP(DMSC, "Mandantory CELL IDENTIFIER LIST not present.\n"); + return -1; + } + + if (TLVP_PRESENT(&tp, GSM0808_IE_TMSI)) { + gsm48_mi_to_string(mi_string, sizeof(mi_string), + TLVP_VAL(&tp, GSM0808_IE_TMSI), TLVP_LEN(&tp, GSM0808_IE_TMSI)); + tmsi = strtoul(mi_string, NULL, 10); + } + + + /* + * parse the IMSI + */ + gsm48_mi_to_string(mi_string, sizeof(mi_string), + TLVP_VAL(&tp, GSM0808_IE_IMSI), TLVP_LEN(&tp, GSM0808_IE_IMSI)); + + /* + * parse the cell identifier list + */ + data_length = TLVP_LEN(&tp, GSM0808_IE_CELL_IDENTIFIER_LIST); + data = TLVP_VAL(&tp, GSM0808_IE_CELL_IDENTIFIER_LIST); + + /* + * Support paging to all network or one BTS at one LAC + */ + if (data_length == 3 && data[0] == CELL_IDENT_LAC) { + unsigned int *_lac = (unsigned int *)&data[1]; + lac = ntohs(*_lac); + } else if (data_length > 1 || (data[0] & 0x0f) != CELL_IDENT_BSS) { + DEBUGPC(DMSC, "Unsupported Cell Identifier List: %s\n", hexdump(data, data_length)); + return -1; + } + + if (TLVP_PRESENT(&tp, GSM0808_IE_CHANNEL_NEEDED) && TLVP_LEN(&tp, GSM0808_IE_CHANNEL_NEEDED) == 1) + chan_needed = TLVP_VAL(&tp, GSM0808_IE_CHANNEL_NEEDED)[0] & 0x03; + + if (TLVP_PRESENT(&tp, GSM0808_IE_EMLPP_PRIORITY)) { + DEBUGP(DMSC, "eMLPP is not handled\n"); + } + + DEBUGP(DMSC, "Paging request from MSC IMSI: '%s' TMSI: '0x%x/%u' LAC: 0x%x\n", mi_string, tmsi, tmsi, lac); + subscr = subscr_get_or_create(net, mi_string); + if (!subscr) + return -1; + + /* reassign the tmsi, trust the net over our internal state */ + subscr->tmsi = tmsi; + subscr->lac = lac; + paged = paging_request(net, subscr, chan_needed, bssmap_paging_cb, subscr); + DEBUGP(DMSC, "Paged IMSI: '%s' TMSI: '0x%x/%u' LAC: 0x%x on #bts: %d\n", mi_string, tmsi, tmsi, lac, paged); + + subscr_put(subscr); + return -1; +} + +/* GSM 08.08 § 3.1.9.1 and 3.2.1.21... release our gsm_lchan and send message */ +static int bssmap_handle_clear_command(struct sccp_connection *conn, + struct msgb *msg, unsigned int payload_length) +{ + struct msgb *resp; + + /* TODO: handle the cause of this package */ + + 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); + } + + /* send the clear complete message */ + resp = bssmap_create_clear_complete(); + if (!resp) { + DEBUGP(DMSC, "Sending clear complete failed.\n"); + return -1; + } + + bsc_queue_connection_write(conn, resp); + return 0; +} + +/* + * GSM 08.08 § 3.4.7 cipher mode handling. We will have to pick + * the cipher to be used for this. In case we are already using + * a cipher we will have to send cipher mode reject to the MSC, + * otherwise we will have to pick something that we and the MS + * is supporting. Currently we are doing it in a rather static + * way by picking one ecnryption or no encrytpion. + */ +static int bssmap_handle_cipher_mode(struct sccp_connection *conn, + struct msgb *msg, unsigned int payload_length) +{ + u_int16_t len; + struct gsm_network *network = NULL; + const u_int8_t *data; + struct tlv_parsed tp; + struct msgb *resp; + int reject_cause = -1; + int include_imeisv = 1; + + /* HACK: Sending A5/0 to the MS */ + if (!msg->lchan || !msg->lchan->msc_data) { + DEBUGP(DMSC, "No lchan/msc_data in cipher mode command.\n"); + goto reject; + } + + if (msg->lchan->msc_data->ciphering_handled) { + DEBUGP(DMSC, "Already seen ciphering command. Protocol Error.\n"); + goto reject; + } + + msg->lchan->msc_data->ciphering_handled = 1; + + tlv_parse(&tp, &bss_att_tlvdef, msg->l4h + 1, payload_length - 1, 0, 0); + if (!TLVP_PRESENT(&tp, GSM0808_IE_ENCRYPTION_INFORMATION)) { + DEBUGP(DMSC, "IE Encryption Information missing.\n"); + goto reject; + } + + /* + * check if our global setting is allowed + * - Currently we check for A5/0 and A5/1 + * - Copy the key if that is necessary + * - Otherwise reject + */ + len = TLVP_LEN(&tp, GSM0808_IE_ENCRYPTION_INFORMATION); + if (len < 1) { + DEBUGP(DMSC, "IE Encryption Information is too short.\n"); + goto reject; + } + + network = msg->lchan->ts->trx->bts->network; + data = TLVP_VAL(&tp, GSM0808_IE_ENCRYPTION_INFORMATION); + + if (network->a5_encryption == 0 && (data[0] & 0x1) == 0x1) { + msg->lchan->encr.alg_id = RSL_ENC_ALG_A5(0); + } else if (network->a5_encryption != 0 && (data[0] & 0x2) == 0x2) { + msg->lchan->encr.alg_id = RSL_ENC_ALG_A5(1); + msg->lchan->encr.key_len = len - 1; + memcpy(msg->lchan->encr.key, &data[1], len - 1); + } else { + DEBUGP(DMSC, "Can not select encryption...\n"); + goto reject; + } + + if (TLVP_PRESENT(&tp, GSM0808_IE_CIPHER_RESPONSE_MODE)) { + include_imeisv = TLVP_VAL(&tp, GSM0808_IE_CIPHER_RESPONSE_MODE)[0] & 0x1; + } + + return gsm48_send_rr_ciph_mode(msg->lchan, include_imeisv); + +reject: + resp = bssmap_create_cipher_reject(reject_cause); + if (!resp) { + DEBUGP(DMSC, "Sending the cipher reject failed.\n"); + return -1; + } + + bsc_queue_connection_write(conn, resp); + 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); +} + +/* + * helpers for the assignment command + */ +enum gsm0808_permitted_speech audio_support_to_gsm88(struct gsm_audio_support *audio) +{ + if (audio->hr) { + switch (audio->ver) { + case 1: + return GSM0808_PERM_HR1; + break; + case 2: + return GSM0808_PERM_HR2; + break; + case 3: + return GSM0808_PERM_HR3; + break; + default: + DEBUGP(DMSC, "Wrong speech mode: %d\n", audio->ver); + return GSM0808_PERM_FR1; + } + } else { + switch (audio->ver) { + case 1: + return GSM0808_PERM_FR1; + break; + case 2: + return GSM0808_PERM_FR2; + break; + case 3: + return GSM0808_PERM_FR3; + break; + default: + DEBUGP(DMSC, "Wrong speech mode: %d\n", audio->ver); + return GSM0808_PERM_HR1; + } + } +} + +enum gsm48_chan_mode gsm88_to_chan_mode(enum gsm0808_permitted_speech speech) +{ + switch (speech) { + case GSM0808_PERM_HR1: + case GSM0808_PERM_FR1: + return GSM48_CMODE_SPEECH_V1; + break; + case GSM0808_PERM_HR2: + case GSM0808_PERM_FR2: + return GSM48_CMODE_SPEECH_EFR; + break; + case GSM0808_PERM_HR3: + case GSM0808_PERM_FR3: + return GSM48_CMODE_SPEECH_AMR; + break; + } + + assert(0); +} + +/* + * 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 gsm_network *network; + struct tlv_parsed tp; + struct bss_sccp_connection_data *msc_data; + u_int8_t *data; + u_int16_t cic; + u_int8_t timeslot; + u_int8_t multiplex; + enum gsm48_chan_mode chan_mode = GSM48_CMODE_SIGN; + int i, supported, port; + + 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; + network = msg->lchan->ts->trx->bts->network; + 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; + } + + cic = ntohs(*(u_int16_t *)TLVP_VAL(&tp, GSM0808_IE_CIRCUIT_IDENTITY_CODE)); + timeslot = cic & 0x1f; + multiplex = (cic & ~0x1f) >> 5; + + /* + * Currently we only support a limited subset of all + * possible channel types. The limitation ends by not using + * multi-slot, limiting the channel coding, speech... + */ + if (TLVP_LEN(&tp, GSM0808_IE_CHANNEL_TYPE) < 3) { + DEBUGP(DMSC, "ChannelType len !=3 not supported: %d\n", + TLVP_LEN(&tp, GSM0808_IE_CHANNEL_TYPE)); + goto reject; + } + + /* + * Try to figure out if we support the proposed speech codecs. For + * now we will always pick the full rate codecs. + */ + + data = (u_int8_t *) TLVP_VAL(&tp, GSM0808_IE_CHANNEL_TYPE); + if ((data[0] & 0xf) != 0x1) { + DEBUGP(DMSC, "ChannelType != speech: %d\n", data[0]); + goto reject; + } + + if (data[1] != GSM0808_SPEECH_FULL_PREF && data[1] != GSM0808_SPEECH_HALF_PREF) { + DEBUGP(DMSC, "ChannelType full not allowed: %d\n", data[1]); + goto reject; + } + + /* + * go through the list of preferred codecs of our gsm network + * and try to find it among the permitted codecs. If we found + * it we will send chan_mode to the right mode and break the + * inner loop. The outer loop will exit due chan_mode having + * the correct value. + */ + for (supported = 0; + chan_mode == GSM48_CMODE_SIGN && supported < network->audio_length; + ++supported) { + + int perm_val = audio_support_to_gsm88(network->audio_support[supported]); + for (i = 2; i < TLVP_LEN(&tp, GSM0808_IE_CHANNEL_TYPE); ++i) { + if ((data[i] & 0x7f) == perm_val) { + chan_mode = gsm88_to_chan_mode(perm_val); + break; + } else if ((data[i] & 0x80) == 0x00) { + break; + } + } + } + + if (chan_mode == GSM48_CMODE_SIGN) { + DEBUGP(DMSC, "No supported audio type found.\n"); + goto reject; + } + + /* modify the channel now */ + msc_data->T10.cb = bssmap_t10_fired; + msc_data->T10.data = conn; + bsc_schedule_timer(&msc_data->T10, GSM0808_T10_VALUE); + + /* the mgcp call agent starts counting at one. a bit of a weird mapping */ + if (timeslot == 0) + timeslot = 1; + port = timeslot + (31 * multiplex); + msc_data->rtp_port = rtp_calculate_port(port, + network->rtp_base_port); + DEBUGP(DMSC, "Sending ChanModify for speech on: sccp: %p mode: 0x%x on port %d %d/0x%x port: %u\n", + conn, chan_mode, port, multiplex, timeslot, msc_data->rtp_port); + if (chan_mode == GSM48_CMODE_SPEECH_AMR) { + msg->lchan->mr_conf.ver = 1; + msg->lchan->mr_conf.icmi = 1; + msg->lchan->mr_conf.m5_90 = 1; + } + + return gsm48_lchan_modify(msg->lchan, chan_mode); + +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; + + if (length < 1) { + DEBUGP(DMSC, "Not enough room: %d\n", length); + return -1; + } + + switch (msg->l4h[0]) { + case BSS_MAP_MSG_RESET_ACKNOWLEDGE: + ret = bssmap_handle_reset_ack(net, msg, length); + break; + case BSS_MAP_MSG_PAGING: + ret = bssmap_handle_paging(net, msg, length); + break; + } + + return ret; +} + +int bssmap_rcvmsg_dt1(struct sccp_connection *conn, struct msgb *msg, unsigned int length) +{ + int ret = 0; + + if (length < 1) { + DEBUGP(DMSC, "Not enough room: %d\n", length); + return -1; + } + + switch (msg->l4h[0]) { + case BSS_MAP_MSG_CLEAR_CMD: + ret = bssmap_handle_clear_command(conn, msg, length); + break; + case BSS_MAP_MSG_CIPHER_MODE_CMD: + ret = bssmap_handle_cipher_mode(conn, msg, length); + break; + case BSS_MAP_MSG_ASSIGMENT_RQST: + ret = bssmap_handle_assignm_req(conn, msg, length); + break; + default: + DEBUGP(DMSC, "Unimplemented msg type: %d\n", msg->l4h[0]); + break; + } + + return ret; +} + +int dtap_rcvmsg(struct gsm_lchan *lchan, struct msgb *msg, unsigned int length) +{ + struct dtap_header *header; + struct msgb *gsm48; + u_int8_t *data; + + if (!lchan) { + DEBUGP(DMSC, "No lchan available\n"); + return -1; + } + + header = (struct dtap_header *) msg->l3h; + if (sizeof(*header) >= length) { + DEBUGP(DMSC, "The DTAP header does not fit. Wanted: %u got: %u\n", sizeof(*header), length); + DEBUGP(DMSC, "hex: %s\n", hexdump(msg->l3h, length)); + return -1; + } + + if (header->length > length - sizeof(*header)) { + DEBUGP(DMSC, "The DTAP l4 information does not fit: header: %u length: %u\n", header->length, length); + DEBUGP(DMSC, "hex: %s\n", hexdump(msg->l3h, length)); + return -1; + } + + DEBUGP(DMSC, "DTAP message: SAPI: %u CHAN: %u\n", header->link_id & 0x07, header->link_id & 0xC0); + + /* forward the data */ + gsm48 = gsm48_msgb_alloc(); + if (!gsm48) { + DEBUGP(DMSC, "Allocation of the message failed.\n"); + return -1; + } + + gsm48->lchan = lchan; + gsm48->trx = gsm48->lchan->ts->trx; + gsm48->l3h = gsm48->data; + data = msgb_put(gsm48, length - sizeof(*header)); + memcpy(data, msg->l3h + sizeof(*header), length - sizeof(*header)); + + /* + * patch LAI entries... + */ + struct gsm48_hdr *gh = (struct gsm48_hdr *)gsm48->l3h; + if (gh->msg_type == GSM48_MT_MM_LOC_UPD_ACCEPT) { + if (gh->data[2] == 0x80) + gh->data[2] = 0x08; + } + + bts_queue_send(gsm48, header->link_id); + return 0; +} + +/* Create messages */ +struct msgb *bssmap_create_layer3(struct msgb *msg_l3) +{ + u_int8_t *data; + u_int16_t *ci; + struct msgb* msg; + struct gsm48_loc_area_id *lai; + struct gsm_bts *bts = msg_l3->lchan->ts->trx->bts; + + msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, + "bssmap cmpl l3"); + if (!msg) + return NULL; + + + /* create the bssmap header */ + msg->l3h = msgb_put(msg, 2); + msg->l3h[0] = 0x0; + + /* create layer 3 header */ + data = msgb_put(msg, 1); + data[0] = BSS_MAP_MSG_COMPLETE_LAYER_3; + + /* create the cell header */ + data = msgb_put(msg, 3); + data[0] = GSM0808_IE_CELL_IDENTIFIER; + data[1] = 1 + sizeof(*lai) + 2; + data[2] = CELL_IDENT_WHOLE_GLOBAL; + + lai = (struct gsm48_loc_area_id *) msgb_put(msg, sizeof(*lai)); + gsm0408_generate_lai(lai, bts->network->country_code, + /*bts->network->network_code - 1*/ 8, bts->location_area_code); + + ci = (u_int16_t *) msgb_put(msg, 2); + *ci = htons(bts->cell_identity); + + /* copy the layer3 data */ + data = msgb_put(msg, msgb_l3len(msg_l3) + 2); + data[0] = GSM0808_IE_LAYER_3_INFORMATION; + data[1] = msgb_l3len(msg_l3); + memcpy(&data[2], msg_l3->l3h, data[1]); + + /* update the size */ + msg->l3h[1] = msgb_l3len(msg) - 2; + + return msg; +} + +struct msgb *bssmap_create_reset(void) +{ + struct msgb *msg = msgb_alloc(30, "bssmap: reset"); + if (!msg) + return NULL; + + msg->l3h = msgb_put(msg, 6); + msg->l3h[0] = BSSAP_MSG_BSS_MANAGEMENT; + msg->l3h[1] = 0x04; + msg->l3h[2] = 0x30; + msg->l3h[3] = 0x04; + msg->l3h[4] = 0x01; + msg->l3h[5] = 0x20; + return msg; +} + +struct msgb *bssmap_create_clear_complete(void) +{ + struct msgb *msg = msgb_alloc(30, "bssmap: clear complete"); + if (!msg) + return NULL; + + msg->l3h = msgb_put(msg, 3); + msg->l3h[0] = BSSAP_MSG_BSS_MANAGEMENT; + msg->l3h[1] = 1; + msg->l3h[2] = BSS_MAP_MSG_CLEAR_COMPLETE; + + return msg; +} + +struct msgb *bssmap_create_cipher_complete(struct msgb *layer3) +{ + struct msgb *msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, + "cipher-complete"); + if (!msg) + return NULL; + + /* send response with BSS override for A5/1... cheating */ + msg->l3h = msgb_put(msg, 3); + msg->l3h[0] = BSSAP_MSG_BSS_MANAGEMENT; + msg->l3h[1] = 0xff; + msg->l3h[2] = BSS_MAP_MSG_CIPHER_MODE_COMPLETE; + + /* include layer3 in case we have at least two octets */ + if (layer3 && msgb_l3len(layer3) > 2) { + msg->l4h = msgb_put(msg, msgb_l3len(layer3) + 2); + msg->l4h[0] = GSM0808_IE_LAYER_3_MESSAGE_CONTENTS; + msg->l4h[1] = msgb_l3len(layer3); + memcpy(&msg->l4h[2], layer3->l3h, msgb_l3len(layer3)); + } + + /* and the optional BSS message */ + msg->l4h = msgb_put(msg, 2); + msg->l4h[0] = GSM0808_IE_CHOSEN_ENCR_ALG; + msg->l4h[1] = layer3->lchan->encr.alg_id; + + /* update the size */ + msg->l3h[1] = msgb_l3len(msg) - 2; + return msg; +} + +struct msgb *bssmap_create_cipher_reject(u_int8_t cause) +{ + struct msgb *msg = msgb_alloc(30, "bssmap: clear complete"); + if (!msg) + return NULL; + + msg->l3h = msgb_put(msg, 3); + msg->l3h[0] = BSSAP_MSG_BSS_MANAGEMENT; + msg->l3h[1] = 2; + msg->l3h[2] = BSS_MAP_MSG_CIPHER_MODE_REJECT; + msg->l3h[3] = cause; + + return msg; +} + +struct msgb *bssmap_create_classmark_update(const u_int8_t *classmark_data, u_int8_t length) +{ + struct msgb *msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, + "classmark-update"); + 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_CLASSMARK_UPDATE; + + msg->l4h = msgb_put(msg, length); + memcpy(msg->l4h, classmark_data, length); + + /* update the size */ + msg->l3h[1] = msgb_l3len(msg) - 2; + return msg; +} + +struct msgb *bssmap_create_sapi_reject(u_int8_t link_id) +{ + struct msgb *msg = msgb_alloc(30, "bssmap: sapi 'n' reject"); + if (!msg) + return NULL; + + msg->l3h = msgb_put(msg, 5); + msg->l3h[0] = BSSAP_MSG_BSS_MANAGEMENT; + msg->l3h[1] = 3; + msg->l3h[2] = BSS_MAP_MSG_SAPI_N_REJECT; + msg->l3h[3] = link_id; + msg->l3h[4] = GSM0808_CAUSE_BSS_NOT_EQUIPPED; + + 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] = 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, 6); + 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] = 1; + msg->l3h[5] = 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; + u_int8_t *data; + struct msgb *msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, + "dtap"); + if (!msg) + return NULL; + + /* DTAP header */ + msg->l3h = msgb_put(msg, sizeof(*header)); + header = (struct dtap_header *) &msg->l3h[0]; + header->type = BSSAP_MSG_DTAP; + header->link_id = link_id; + header->length = msgb_l3len(msg_l3); + + /* Payload */ + data = msgb_put(msg, header->length); + memcpy(data, msg_l3->l3h, header->length); + + return msg; +} + +static int bssap_handle_lchan_signal(unsigned int subsys, unsigned int signal, + void *handler_data, void *signal_data) +{ + struct msgb *msg; + struct gsm_lchan *lchan; + struct sccp_connection *conn; + + if (subsys != SS_LCHAN || signal != S_LCHAN_UNEXPECTED_RELEASE) + return 0; + + /* + * If we have a SCCP Connection we need to inform the MSC about + * the resource error and then drop the lchan<->sccp association. + */ + lchan = (struct gsm_lchan *)signal_data; + + 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; + + msg = msgb_alloc(30, "sccp: clear request"); + if (!msg) { + DEBUGP(DMSC, "Failed to allocate clear request.\n"); + return 0; + } + + msg->l3h = msgb_put(msg, 2 + 4); + msg->l3h[0] = BSSAP_MSG_BSS_MANAGEMENT; + msg->l3h[1] = 4; + + msg->l3h[2] = BSS_MAP_MSG_CLEAR_RQST; + msg->l3h[3] = GSM0808_IE_CAUSE; + msg->l3h[4] = 1; + msg->l3h[5] = GSM0808_CAUSE_RADIO_INTERFACE_FAILURE; + + DEBUGP(DMSC, "Sending clear request on unexpected channel release.\n"); + bsc_queue_connection_write(conn, msg); + + return 0; +} + +/* + * queue handling for BSS AP + */ +void bsc_queue_connection_write(struct sccp_connection *conn, struct msgb *msg) +{ + struct bss_sccp_connection_data *data; + + data = (struct bss_sccp_connection_data *)conn->data_ctx; + + if (conn->connection_state > SCCP_CONNECTION_STATE_ESTABLISHED) { + DEBUGP(DMSC, "Connection closing, dropping packet on: %p\n", conn); + msgb_free(msg); + } else if (conn->connection_state == SCCP_CONNECTION_STATE_ESTABLISHED + && data->sccp_queue_size == 0) { + sccp_connection_write(conn, msg); + msgb_free(msg); + } else if (data->sccp_queue_size > 10) { + DEBUGP(DMSC, "Dropping packet on %p due queue overflow\n", conn); + msgb_free(msg); + } else { + DEBUGP(DMSC, "Queuing packet on %p. Queue size: %d\n", conn, data->sccp_queue_size); + ++data->sccp_queue_size; + msgb_enqueue(&data->sccp_queue, msg); + } +} + +void bsc_free_queued(struct sccp_connection *conn) +{ + struct bss_sccp_connection_data *data; + struct msgb *msg; + + data = (struct bss_sccp_connection_data *)conn->data_ctx; + while (!llist_empty(&data->sccp_queue)) { + /* this is not allowed to fail */ + msg = msgb_dequeue(&data->sccp_queue); + msgb_free(msg); + } + + data->sccp_queue_size = 0; +} + +void bsc_send_queued(struct sccp_connection *conn) +{ + struct bss_sccp_connection_data *data; + struct msgb *msg; + + data = (struct bss_sccp_connection_data *)conn->data_ctx; + + while (!llist_empty(&data->sccp_queue)) { + /* this is not allowed to fail */ + msg = msgb_dequeue(&data->sccp_queue); + sccp_connection_write(conn, msg); + msgb_free(msg); + --data->sccp_queue_size; + } +} + +/* RLL callback */ +static void rll_ind_cb(struct gsm_lchan *lchan, u_int8_t link_id, + void *_data, enum bsc_rllr_ind rllr_ind) +{ + struct sccp_source_reference ref = sccp_src_ref_from_int((u_int32_t) _data); + struct bss_sccp_connection_data *data = lchan->msc_data; + + if (!data || !data->sccp) { + DEBUGP(DMSC, "Time-out/Establish after sccp release? Ind: %d lchan: %p\n", + rllr_ind, lchan); + return; + } + + if (memcmp(&data->sccp->source_local_reference, &ref, sizeof(ref)) != 0) { + DEBUGP(DMSC, "Wrong SCCP connection. Not handling RLL callback: %u %u\n", + sccp_src_ref_to_int(&ref), + sccp_src_ref_to_int(&data->sccp->source_local_reference)); + return; + } + + switch (rllr_ind) { + case BSC_RLLR_IND_EST_CONF: + /* nothing to do */ + bts_send_queued(data); + break; + case BSC_RLLR_IND_REL_IND: + case BSC_RLLR_IND_ERR_IND: + case BSC_RLLR_IND_TIMEOUT: { + /* reject queued messages */ + struct msgb *sapi_reject; + + bts_free_queued(data); + sapi_reject = bssmap_create_sapi_reject(link_id); + if (!sapi_reject){ + DEBUGP(DMSC, "Failed to create SAPI reject\n"); + return; + } + + bsc_queue_connection_write(data->sccp, sapi_reject); + break; + } + } +} + +/* decide if we need to queue because of SAPI != 0 */ +void bts_queue_send(struct msgb *msg, int link_id) +{ + struct bss_sccp_connection_data *data = msg->lchan->msc_data; + + if (data->gsm_queue_size == 0) { + if (msg->lchan->sapis[link_id & 0x7] != LCHAN_SAPI_UNUSED) { + rsl_data_request(msg, link_id); + } else { + msg->smsh = (unsigned char*) link_id; + msgb_enqueue(&data->gsm_queue, msg); + ++data->gsm_queue_size; + + /* establish link */ + rll_establish(msg->lchan, link_id & 0x7, + rll_ind_cb, + (void *)sccp_src_ref_to_int(&data->sccp->source_local_reference)); + } + } else if (data->gsm_queue_size == 10) { + DEBUGP(DMSC, "Queue full on %p. Dropping GSM0408.\n", data->sccp); + } else { + DEBUGP(DMSC, "Queueing GSM0408 message on %p. Queue size: %d\n", + data->sccp, data->gsm_queue_size + 1); + + msg->smsh = (unsigned char*) link_id; + msgb_enqueue(&data->gsm_queue, msg); + ++data->gsm_queue_size; + } +} + +void bts_free_queued(struct bss_sccp_connection_data *data) +{ + struct msgb *msg; + + while (!llist_empty(&data->gsm_queue)) { + /* this is not allowed to fail */ + msg = msgb_dequeue(&data->gsm_queue); + msgb_free(msg); + } + + data->gsm_queue_size = 0; +} + +void bts_send_queued(struct bss_sccp_connection_data *data) +{ + struct msgb *msg; + + while (!llist_empty(&data->gsm_queue)) { + /* this is not allowed to fail */ + msg = msgb_dequeue(&data->gsm_queue); + rsl_data_request(msg, (int) msg->smsh); + } + + 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); +} diff --git a/openbsc/src/chan_alloc.c b/openbsc/src/chan_alloc.c index 7ba679c87..b4f080585 100644 --- a/openbsc/src/chan_alloc.c +++ b/openbsc/src/chan_alloc.c @@ -33,8 +33,6 @@ #include <openbsc/debug.h> #include <openbsc/signal.h> -static void auto_release_channel(void *_lchan); - struct gsm_bts_trx_ts *ts_c0_alloc(struct gsm_bts *bts, enum gsm_phys_chan_config pchan) { @@ -218,10 +216,9 @@ struct gsm_lchan *lchan_alloc(struct gsm_bts *bts, enum gsm_chan_t type) /* clear multi rate config */ memset(&lchan->mr_conf, 0, sizeof(lchan->mr_conf)); - /* Configure the time and start it so it will be closed */ - lchan->release_timer.cb = auto_release_channel; - lchan->release_timer.data = lchan; - bsc_schedule_timer(&lchan->release_timer, LCHAN_RELEASE_TIMEOUT); + /* clear any msc reference */ + lchan->msc_data = NULL; + } return lchan; @@ -242,44 +239,35 @@ void lchan_free(struct gsm_lchan *lchan) lchan->use_count = 0; } - /* stop the timer */ - bsc_del_timer(&lchan->release_timer); - /* FIXME: ts_free() the timeslot, if we're the last logical * channel using it */ } /* Consider releasing the channel now */ -int lchan_auto_release(struct gsm_lchan *lchan) +int _lchan_release(struct gsm_lchan *lchan) { if (lchan->use_count > 0) { + DEBUGP(DRLL, "BUG: _lchan_release called without zero use_count.\n"); return 0; } /* Assume we have GSM04.08 running and send a release */ if (lchan->subscr) { + ++lchan->use_count; gsm48_send_rr_release(lchan); + --lchan->use_count; } /* spoofed? message */ if (lchan->use_count < 0) { - DEBUGP(DRLL, "Channel count is negative: %d\n", lchan->use_count); + DEBUGP(DRLL, "BUG: channel count is negative: %d\n", lchan->use_count); } - DEBUGP(DRLL, "Recycling the channel with: %d (%x)\n", lchan->nr, lchan->nr); + DEBUGP(DRLL, "Releasing the channel with: %d (%x)\n", lchan->nr, lchan->nr); rsl_release_request(lchan, 0); return 1; } -/* Auto release the channel when the use count is zero */ -static void auto_release_channel(void *_lchan) -{ - struct gsm_lchan *lchan = _lchan; - - if (!lchan_auto_release(lchan)) - bsc_schedule_timer(&lchan->release_timer, LCHAN_RELEASE_TIMEOUT); -} - struct gsm_lchan* lchan_find(struct gsm_bts *bts, struct gsm_subscriber *subscr) { struct gsm_bts_trx *trx; int ts_no, lchan_no; diff --git a/openbsc/src/gsm_04_08.c b/openbsc/src/gsm_04_08.c index 1f8235411..6be83da6c 100644 --- a/openbsc/src/gsm_04_08.c +++ b/openbsc/src/gsm_04_08.c @@ -296,13 +296,13 @@ static int gsm0408_authorize(struct gsm_lchan *lchan, struct msgb *msg) int rc; db_subscriber_alloc_tmsi(lchan->subscr); - release_loc_updating_req(lchan); rc = gsm0408_loc_upd_acc(msg->lchan, lchan->subscr->tmsi); /* call subscr_update after putting the loc_upd_acc * in the transmit queue, since S_SUBSCR_ATTACHED might * trigger further action like SMS delivery */ subscr_update(lchan->subscr, msg->trx->bts, GSM_SUBSCRIBER_UPDATE_ATTACHED); + release_loc_updating_req(lchan); return rc; } @@ -972,9 +972,8 @@ static void loc_upd_rej_cb(void *data) { struct gsm_lchan *lchan = data; - release_loc_updating_req(lchan); gsm0408_loc_upd_rej(lchan, reject_cause); - lchan_auto_release(lchan); + release_loc_updating_req(lchan); } static void schedule_reject(struct gsm_lchan *lchan) diff --git a/openbsc/src/gsm_04_08_utils.c b/openbsc/src/gsm_04_08_utils.c index b2fbdc2c3..c81413987 100644 --- a/openbsc/src/gsm_04_08_utils.c +++ b/openbsc/src/gsm_04_08_utils.c @@ -282,8 +282,8 @@ 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_NECI1] = GSM_LCHAN_SDCCH, - [CHREQ_T_PAG_R_ANY_NECI0] = GSM_LCHAN_SDCCH, + [CHREQ_T_PAG_R_ANY_NECI1] = GSM_LCHAN_TCH_H, + [CHREQ_T_PAG_R_ANY_NECI0] = GSM_LCHAN_TCH_F, [CHREQ_T_PAG_R_TCH_F] = GSM_LCHAN_TCH_F, [CHREQ_T_PAG_R_TCH_FH] = GSM_LCHAN_TCH_F, }; diff --git a/openbsc/src/gsm_data.c b/openbsc/src/gsm_data.c index 69a9096ca..8212346ec 100644 --- a/openbsc/src/gsm_data.c +++ b/openbsc/src/gsm_data.c @@ -27,6 +27,7 @@ #include <openbsc/gsm_data.h> #include <openbsc/talloc.h> +#include <openbsc/abis_nm.h> void *tall_bsc_ctx; diff --git a/openbsc/src/gsm_subscriber_base.c b/openbsc/src/gsm_subscriber_base.c index 48374eae5..929834e42 100644 --- a/openbsc/src/gsm_subscriber_base.c +++ b/openbsc/src/gsm_subscriber_base.c @@ -32,6 +32,7 @@ #include <openbsc/paging.h> #include <openbsc/debug.h> #include <openbsc/paging.h> +#include <openbsc/chan_alloc.h> LLIST_HEAD(active_subscribers); void *tall_subscr_ctx; @@ -89,6 +90,7 @@ static int subscr_paging_cb(unsigned int hooknum, unsigned int event, request->cbfn(hooknum, event, msg, data, request->param); subscr->in_callback = 0; + subscr_put(request->subscr); talloc_free(request); return 0; } @@ -166,7 +168,7 @@ void subscr_get_channel(struct gsm_subscriber *subscr, } memset(request, 0, sizeof(*request)); - request->subscr = subscr; + request->subscr = subscr_get(subscr); request->channel_type = type; request->cbfn = cbfn; request->param = param; @@ -212,3 +214,22 @@ void subscr_put_channel(struct gsm_lchan *lchan) subscr_send_paging_request(lchan->subscr); } +struct gsm_subscriber *subscr_get_or_create(struct gsm_network *net, + const char *imsi) +{ + struct gsm_subscriber *subscr; + + llist_for_each_entry(subscr, subscr_bsc_active_subscriber(), entry) { + if (strcmp(subscr->imsi, imsi) == 0 && subscr->net == net) + return subscr_get(subscr); + } + + subscr = subscr_alloc(); + if (!subscr) + return NULL; + + strcpy(subscr->imsi, imsi); + subscr->net = net; + return subscr; +} + diff --git a/openbsc/src/msgb.c b/openbsc/src/msgb.c index edeb975a9..0f8627a27 100644 --- a/openbsc/src/msgb.c +++ b/openbsc/src/msgb.c @@ -92,6 +92,7 @@ void msgb_reset(struct msgb *msg) msg->l2h = NULL; msg->l3h = NULL; msg->smsh = NULL; + msg->l4h = NULL; } static __attribute__((constructor)) void on_dso_load_trau_msgb(void) diff --git a/openbsc/src/transaction.c b/openbsc/src/transaction.c index 04eaa3c99..03735d4a4 100644 --- a/openbsc/src/transaction.c +++ b/openbsc/src/transaction.c @@ -28,6 +28,7 @@ #include <openbsc/gsm_04_08.h> #include <openbsc/mncc.h> #include <openbsc/paging.h> +#include <openbsc/chan_alloc.h> void *tall_trans_ctx; @@ -95,14 +96,14 @@ void trans_free(struct gsm_trans *trans) break; } - if (trans->lchan) - put_lchan(trans->lchan); - if (!trans->lchan && trans->subscr && trans->subscr->net) { /* Stop paging on all bts' */ paging_request_stop(NULL, trans->subscr, NULL); } + if (trans->lchan) + put_lchan(trans->lchan); + if (trans->subscr) subscr_put(trans->subscr); diff --git a/openbsc/src/vty_interface.c b/openbsc/src/vty_interface.c index 5712ca1c7..abfbbcb68 100644 --- a/openbsc/src/vty_interface.c +++ b/openbsc/src/vty_interface.c @@ -76,6 +76,8 @@ static void net_dump_nmstate(struct vty *vty, struct gsm_nm_state *nms) static void net_dump_vty(struct vty *vty, struct gsm_network *net) { + int i; + vty_out(vty, "BSC is on Country Code %u, Network Code %u " "and has %u BTS%s", net->country_code, net->network_code, net->num_bts, VTY_NEWLINE); @@ -89,6 +91,11 @@ static void net_dump_vty(struct vty *vty, struct gsm_network *net) VTY_NEWLINE); vty_out(vty, " NECI (TCH/H): %u%s", net->neci, VTY_NEWLINE); + vty_out(vty, " Allowed Audio Codecs: "); + for (i = 0; i < net->audio_length; ++i) + vty_out(vty, "hr: %d ver: %d, ", + net->audio_support[i]->hr, net->audio_support[i]->ver); + vty_out(vty, "%s", VTY_NEWLINE); } DEFUN(show_net, show_net_cmd, "show network", @@ -275,6 +282,24 @@ static int config_write_net(struct vty *vty) 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, " ipacc rtp_payload %u%s", gsmnet->rtp_payload, VTY_NEWLINE); + + if (gsmnet->audio_length != 0) { + int i; + + vty_out(vty, " codec_list "); + for (i = 0; i < gsmnet->audio_length; ++i) { + printf("I... %d %d\n", i, gsmnet->audio_length); + if (i != 0) + vty_out(vty, ", "); + + if (gsmnet->audio_support[i]->hr) + vty_out(vty, "hr%.1u", gsmnet->audio_support[i]->ver); + else + vty_out(vty, "fr%.1u", gsmnet->audio_support[i]->ver); + } + vty_out(vty, "%s", VTY_NEWLINE); + } return CMD_SUCCESS; } @@ -801,6 +826,90 @@ DEFUN(cfg_net_neci, return CMD_SUCCESS; } +DEFUN(cfg_net_supported_codecs, + cfg_net_supported_codecs_cmd, + "codec_list .LIST", + "Set the three preferred audio codecs.\n" + "Codec List") +{ + int saw_fr, saw_hr; + int i; + + saw_fr = saw_hr = 0; + + /* free the old list... if it exists */ + if (gsmnet->audio_support) { + talloc_free(gsmnet->audio_support); + gsmnet->audio_support = NULL; + gsmnet->audio_length = 0; + } + + /* create a new array */ + gsmnet->audio_support = + talloc_zero_array(gsmnet, struct gsm_audio_support *, argc); + gsmnet->audio_length = argc; + + for (i = 0; i < argc; ++i) { + /* check for hrX or frX */ + if (strlen(argv[i]) != 3 + || argv[i][1] != 'r' + || (argv[i][0] != 'h' && argv[i][0] != 'f') + || argv[i][2] < 0x30 + || argv[i][2] > 0x39) + goto error; + + gsmnet->audio_support[i] = talloc_zero(gsmnet->audio_support, + struct gsm_audio_support); + gsmnet->audio_support[i]->ver = atoi(argv[i] + 2); + + if (strncmp("hr", argv[i], 2) == 0) { + gsmnet->audio_support[i]->hr = 1; + saw_hr = 1; + } else if (strncmp("fr", argv[i], 2) == 0) { + gsmnet->audio_support[i]->hr = 0; + saw_fr = 1; + } + + if (saw_hr && saw_fr) { + vty_out(vty, "Can not have full-rate and half-rate codec.%s", + VTY_NEWLINE); + return CMD_ERR_INCOMPLETE; + } + } + + return CMD_SUCCESS; + +error: + vty_out(vty, "Codec name must be hrX or frX. Was '%s'%s", + argv[i], VTY_NEWLINE); + return CMD_ERR_INCOMPLETE; +} + +DEFUN(cfg_net_ipacc_rtp_payload, + cfg_net_ipacc_rtp_payload_cmd, + "ipacc rtp_payload <0-256>", + "Override the RTP payload to use") +{ + gsmnet->rtp_payload = atoi(argv[0]) & 0xff; + + return CMD_SUCCESS; +} + +DEFUN(cfg_net_rtp_base_port, + cfg_net_rtp_base_port_cmd, + "rtp base <0-65534>", + "Base port to use for MGCP RTP") +{ + unsigned int port = atoi(argv[0]); + if (port > 65534) { + vty_out(vty, "%% wrong base port '%s'%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + + gsmnet->rtp_base_port = port; + return CMD_SUCCESS; +} + /* per-BTS configuration */ DEFUN(cfg_bts, cfg_bts_cmd, @@ -1158,6 +1267,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, @@ -1241,6 +1361,9 @@ int bsc_vty_init(struct gsm_network *net) 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_supported_codecs_cmd); + install_element(GSMNET_NODE, &cfg_net_ipacc_rtp_payload_cmd); + install_element(GSMNET_NODE, &cfg_net_rtp_base_port_cmd); install_element(GSMNET_NODE, &cfg_bts_cmd); install_node(&bts_node, config_write_bts); @@ -1268,6 +1391,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_bsc.c b/openbsc/src/vty_interface_bsc.c new file mode 100644 index 000000000..346ed007f --- /dev/null +++ b/openbsc/src/vty_interface_bsc.c @@ -0,0 +1,48 @@ +/* OpenBSC interface to quagga VTY - BSC options */ +/* (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 <sys/types.h> + +#include <vty/command.h> +#include <vty/buffer.h> +#include <vty/vty.h> + +#include <openbsc/gsm_data.h> + +static struct gsmnet *gsmnet = NULL; + +DEFUN(show_bsc, show_bsc_cmd, "show bsc", + SHOW_STR "Display information about the BSC\n") +{ + vty_out(vty, "BSC... not implemented yet%s", VTY_NEWLINE); + return CMD_SUCCESS; +} + +int bsc_vty_init_extra(struct gsm_network *net) +{ + gsmnet = net; + + /* get runtime information */ + install_element(VIEW_NODE, &show_bsc_cmd); + + return 0; +} diff --git a/openbsc/tests/channel/channel_test.c b/openbsc/tests/channel/channel_test.c index 1b01878b5..ed0f3ad57 100644 --- a/openbsc/tests/channel/channel_test.c +++ b/openbsc/tests/channel/channel_test.c @@ -76,4 +76,5 @@ int main(int argc, char** argv) void nm_state_event() {} void input_event() {} void sms_alloc() {} +void _lchan_release() {} |