summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--openbsc/configure.in2
-rw-r--r--openbsc/contrib/README2
-rwxr-xr-xopenbsc/contrib/send_handshake.py30
-rw-r--r--openbsc/include/openbsc/Makefile.am2
-rw-r--r--openbsc/include/openbsc/bssap.h333
-rw-r--r--openbsc/include/openbsc/chan_alloc.h26
-rw-r--r--openbsc/include/openbsc/gsm_data.h80
-rw-r--r--openbsc/include/openbsc/gsm_subscriber.h2
-rw-r--r--openbsc/include/openbsc/mgcp.h2
-rw-r--r--openbsc/include/openbsc/msgb.h2
-rw-r--r--openbsc/src/Makefile.am9
-rwxr-xr-xopenbsc/src/abis_nm.c10
-rw-r--r--openbsc/src/abis_rsl.c18
-rw-r--r--openbsc/src/bsc_init.c71
-rw-r--r--openbsc/src/bsc_mgcp.c1
-rw-r--r--openbsc/src/bsc_msc_ip.c813
-rw-r--r--openbsc/src/bssap.c1134
-rw-r--r--openbsc/src/chan_alloc.c30
-rw-r--r--openbsc/src/gsm_04_08.c5
-rw-r--r--openbsc/src/gsm_04_08_utils.c4
-rw-r--r--openbsc/src/gsm_data.c1
-rw-r--r--openbsc/src/gsm_subscriber_base.c23
-rw-r--r--openbsc/src/msgb.c1
-rw-r--r--openbsc/src/transaction.c7
-rw-r--r--openbsc/src/vty_interface.c124
-rw-r--r--openbsc/src/vty_interface_bsc.c48
-rw-r--r--openbsc/tests/channel/channel_test.c1
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() {}