mirror of
https://gitea.osmocom.org/cellular-infrastructure/osmo-mgw.git
synced 2025-10-23 08:12:01 +00:00
Compare commits
100 Commits
1.9.0
...
on-waves/0
Author | SHA1 | Date | |
---|---|---|---|
|
2727909dba | ||
|
ad9856ec15 | ||
|
9d53a8ad2a | ||
|
058956e8ee | ||
|
a34bb9167f | ||
|
b8ac7ffd7c | ||
|
b13cf829da | ||
|
fdc64f6806 | ||
|
ab46372e2a | ||
|
ff0a562f9a | ||
|
556008d724 | ||
|
0094f84f30 | ||
|
86069143ff | ||
|
44f0be88a3 | ||
|
5d88b372d7 | ||
|
71c7bf5907 | ||
|
869033148c | ||
|
bc0f7c0988 | ||
|
7d06063cfb | ||
|
4e42b637fd | ||
|
f44de9942b | ||
|
3a110ae60b | ||
|
bb84adc465 | ||
|
8d123ea3c0 | ||
|
88ca894df7 | ||
|
42b0d6b494 | ||
|
82d8b0457b | ||
|
433d6ee1a2 | ||
|
203a6eddf8 | ||
|
56ef6249e3 | ||
|
b2a96b1be7 | ||
|
d4c29c1574 | ||
|
3d947e6d67 | ||
|
b62c9a19cf | ||
|
ff5957568f | ||
|
7d2e1ca4be | ||
|
7ce2e0c8b0 | ||
|
78d442420b | ||
|
8cd2709ebf | ||
|
41a1780102 | ||
|
2f84715984 | ||
|
7253154fc5 | ||
|
6c1c76683f | ||
|
a92fe9a4ca | ||
|
e83a3f584e | ||
|
118ddebc36 | ||
|
bb53004d47 | ||
|
6af20842cb | ||
|
cc41cb07e7 | ||
|
d6fb23523a | ||
|
2aa0b45cc0 | ||
|
619df61ad2 | ||
|
893ea65f38 | ||
|
64b811f113 | ||
|
91fc9bf862 | ||
|
111a58dd37 | ||
|
d1a2563a74 | ||
|
7d3ef919ce | ||
|
cba98d87d6 | ||
|
5c18ad0829 | ||
|
0d9ed87d5c | ||
|
ec7be0c969 | ||
|
9be3347601 | ||
|
3eef7b7d81 | ||
|
9de4a6daa9 | ||
|
851ace9f33 | ||
|
d1dd069b48 | ||
|
401db32ca2 | ||
|
17e03d21d2 | ||
|
26a9bff201 | ||
|
80fb260a60 | ||
|
55a0716da7 | ||
|
c88fb75616 | ||
|
d55a4dc326 | ||
|
a4e6f2e6e1 | ||
|
7f71d99cc3 | ||
|
b92167cf80 | ||
|
4b6a6dbe7e | ||
|
763e8c7766 | ||
|
823ff16088 | ||
|
6f93c6a1e0 | ||
|
f97e48b0de | ||
|
761600b0fd | ||
|
8549462bc6 | ||
|
436e5c6308 | ||
|
f8b9d844c1 | ||
|
58ec07d580 | ||
|
71465c21f4 | ||
|
16d0a833f8 | ||
|
ea72b62cac | ||
|
49a84ec6e9 | ||
|
42c636b6c8 | ||
|
a0a55f555e | ||
|
23ed00e410 | ||
|
3fe910b9f1 | ||
|
097bdeb77d | ||
|
1b85de02e0 | ||
|
2281d1835f | ||
|
fb4433a129 | ||
|
d954dcf9e1 |
@@ -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.3.4onwaves)
|
||||
|
||||
dnl kernel style compile messages
|
||||
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
|
||||
|
2
openbsc/contrib/README
Normal file
2
openbsc/contrib/README
Normal file
@@ -0,0 +1,2 @@
|
||||
This contains a set of scripts used for the development of the
|
||||
MSC functionality.
|
30
openbsc/contrib/send_handshake.py
Executable file
30
openbsc/contrib/send_handshake.py
Executable file
@@ -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)
|
||||
|
@@ -5,4 +5,4 @@ noinst_HEADERS = abis_nm.h abis_rsl.h debug.h db.h gsm_04_08.h gsm_data.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 meas_rep.h bitvec.h rest_octets.h \
|
||||
system_information.h handover.h
|
||||
system_information.h handover.h bssap.h bsc_msc.h bsc_nat.h
|
||||
|
30
openbsc/include/openbsc/bsc_msc.h
Normal file
30
openbsc/include/openbsc/bsc_msc.h
Normal file
@@ -0,0 +1,30 @@
|
||||
/* Routines to talk to the MSC using the IPA Protocol */
|
||||
/*
|
||||
* (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
|
||||
* (C) 2010 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef BSC_MSC_H
|
||||
#define BSC_MSC_H
|
||||
|
||||
#include "select.h"
|
||||
|
||||
int connect_to_msc(struct bsc_fd *fd, const char *ip, int port);
|
||||
|
||||
#endif
|
33
openbsc/include/openbsc/bsc_nat.h
Normal file
33
openbsc/include/openbsc/bsc_nat.h
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
|
||||
* (C) 2010 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef BSC_NAT_H
|
||||
#define BSC_NAT_H
|
||||
|
||||
#include <sys/types.h>
|
||||
#include "msgb.h"
|
||||
|
||||
/**
|
||||
* filter based on IP Access header in both directions
|
||||
*/
|
||||
int bsc_nat_filter_ipa(struct msgb *msg);
|
||||
|
||||
#endif
|
334
openbsc/include/openbsc/bssap.h
Normal file
334
openbsc/include/openbsc/bssap.h
Normal file
@@ -0,0 +1,334 @@
|
||||
/* 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*);
|
||||
void bts_unblock_queue(struct bss_sccp_connection_data*);
|
||||
|
||||
#endif
|
@@ -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 */
|
||||
|
@@ -768,7 +768,7 @@ int gsm48_send_rr_release(struct gsm_lchan *lchan);
|
||||
int gsm48_send_rr_ciph_mode(struct gsm_lchan *lchan, int want_imeisv);
|
||||
int gsm48_send_rr_app_info(struct gsm_lchan *lchan, u_int8_t apdu_id,
|
||||
u_int8_t apdu_len, const u_int8_t *apdu);
|
||||
int gsm48_send_rr_ass_cmd(struct gsm_lchan *lchan, u_int8_t power_class);
|
||||
int gsm48_send_rr_ass_cmd(struct gsm_lchan *dest_lchan, struct gsm_lchan *lchan, u_int8_t power_class);
|
||||
int gsm48_send_ho_cmd(struct gsm_lchan *old_lchan, struct gsm_lchan *new_lchan,
|
||||
u_int8_t power_command, u_int8_t ho_ref);
|
||||
|
||||
|
@@ -93,36 +93,51 @@ 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 gsm_lchan *secondary_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 */
|
||||
int block_gsm;
|
||||
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;
|
||||
@@ -201,9 +216,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 */
|
||||
@@ -214,6 +226,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;
|
||||
|
||||
@@ -509,6 +527,14 @@ enum gsm_auth_policy {
|
||||
#define GSM_T3101_DEFAULT 10
|
||||
#define GSM_T3113_DEFAULT 60
|
||||
|
||||
/*
|
||||
* 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;
|
||||
@@ -539,6 +565,11 @@ struct gsm_network {
|
||||
|
||||
struct gsmnet_stats stats;
|
||||
|
||||
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;
|
||||
@@ -564,6 +595,10 @@ struct gsm_network {
|
||||
struct {
|
||||
enum rrlp_mode mode;
|
||||
} rrlp;
|
||||
|
||||
/* a hack for On Waves. It must be signed */
|
||||
int32_t core_country_code;
|
||||
int32_t core_network_code;
|
||||
};
|
||||
|
||||
#define SMS_HDR_SIZE 128
|
||||
|
@@ -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,
|
||||
|
@@ -44,5 +44,6 @@ int ipaccess_connect(struct e1inp_line *line, struct sockaddr_in *sa);
|
||||
int ipaccess_rcvmsg_base(struct msgb *msg, struct bsc_fd *bfd);
|
||||
struct msgb *ipaccess_read_msg(struct bsc_fd *bfd, int *error);
|
||||
void ipaccess_prepend_header(struct msgb *msg, int proto);
|
||||
int ipaccess_send_id_ack(int fd);
|
||||
|
||||
#endif /* _IPACCESS_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
|
||||
|
@@ -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)
|
||||
|
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* SCCP management code
|
||||
*
|
||||
* (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
|
||||
* (C) 2009, 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
@@ -143,4 +143,25 @@ extern const struct sockaddr_sccp sccp_ssn_bssap;
|
||||
u_int32_t sccp_src_ref_to_int(struct sccp_source_reference *ref);
|
||||
struct sccp_source_reference sccp_src_ref_from_int(u_int32_t);
|
||||
|
||||
/**
|
||||
* Below this are helper functions and structs for parsing SCCP messages
|
||||
*/
|
||||
struct sccp_parse_result {
|
||||
struct sccp_address called;
|
||||
struct sccp_address calling;
|
||||
|
||||
/* point to the msg packet */
|
||||
struct sccp_source_reference *source_local_reference;
|
||||
struct sccp_source_reference *destination_local_reference;
|
||||
|
||||
/* data pointer */
|
||||
int data_len;
|
||||
};
|
||||
|
||||
/*
|
||||
* helper functions for the nat code
|
||||
*/
|
||||
int sccp_determine_msg_type(struct msgb *msg);
|
||||
int sccp_parse_header(struct msgb *msg, struct sccp_parse_result *result);
|
||||
|
||||
#endif
|
||||
|
@@ -2,21 +2,22 @@ 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 bsc_nat
|
||||
noinst_LIBRARIES = libbsc.a libmsc.a libvty.a libsccp.a
|
||||
noinst_HEADERS = vty/cardshell.h
|
||||
|
||||
libbsc_a_SOURCES = abis_rsl.c abis_nm.c gsm_data.c gsm_04_08_utils.c \
|
||||
msgb.c select.c chan_alloc.c timer.c debug.c handover_logic.c \
|
||||
msgb.c select.c chan_alloc.c timer.c debug.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 system_information.c bitvec.c rest_octets.c \
|
||||
handover_decision.c meas_rep.c rtp_proxy.c
|
||||
rtp_proxy.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 gsm_04_08.c gsm_04_11.c transaction.c \
|
||||
token_auth.c rrlp.c gsm_04_80.c ussd.c silent_call.c
|
||||
token_auth.c rrlp.c gsm_04_80.c ussd.c silent_call.c \
|
||||
handover_logic.c handover_decision.c meas_rep.c
|
||||
|
||||
libvty_a_SOURCES = vty/buffer.c vty/command.c vty/vector.c vty/vty.c
|
||||
|
||||
@@ -25,6 +26,10 @@ 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.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
|
||||
|
||||
@@ -37,3 +42,6 @@ isdnsync_SOURCES = isdnsync.c
|
||||
|
||||
bsc_mgcp_SOURCES = bsc_mgcp.c msgb.c talloc.c debug.c select.c timer.c telnet_interface.c
|
||||
bsc_mgcp_LDADD = libvty.a
|
||||
|
||||
bsc_nat_SOURCES = nat/bsc_nat.c nat/bsc_filter.c bsc_msc.c
|
||||
bsc_nat_LDADD = libbsc.a libsccp.a
|
||||
|
@@ -630,6 +630,10 @@ int rsl_chan_activate_lchan(struct gsm_lchan *lchan, u_int8_t act_type,
|
||||
msgb_tv_put(msg, RSL_IE_MS_POWER, lchan->ms_power);
|
||||
msgb_tv_put(msg, RSL_IE_TIMING_ADVANCE, ta);
|
||||
|
||||
if (lchan->tch_mode == GSM48_CMODE_SPEECH_AMR)
|
||||
msgb_tlv_put(msg, RSL_IE_MR_CONFIG, sizeof(lchan->mr_conf),
|
||||
(u_int8_t *) &lchan->mr_conf);
|
||||
|
||||
msg->trx = lchan->ts->trx;
|
||||
|
||||
return abis_rsl_sendmsg(msg);
|
||||
@@ -853,6 +857,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);
|
||||
|
||||
@@ -1477,31 +1485,11 @@ static u_int8_t ipa_smod_s_for_lchan(struct gsm_lchan *lchan)
|
||||
{
|
||||
switch (lchan->tch_mode) {
|
||||
case GSM48_CMODE_SPEECH_V1:
|
||||
switch (lchan->type) {
|
||||
case GSM_LCHAN_TCH_F:
|
||||
return 0x00;
|
||||
case GSM_LCHAN_TCH_H:
|
||||
return 0x03;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0x00;
|
||||
case GSM48_CMODE_SPEECH_EFR:
|
||||
switch (lchan->type) {
|
||||
case GSM_LCHAN_TCH_F:
|
||||
return 0x01;
|
||||
/* there's no half-rate EFR */
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0x01;
|
||||
case GSM48_CMODE_SPEECH_AMR:
|
||||
switch (lchan->type) {
|
||||
case GSM_LCHAN_TCH_F:
|
||||
return 0x02;
|
||||
case GSM_LCHAN_TCH_H:
|
||||
return 0x05;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0x02;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -1795,9 +1783,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);
|
||||
|
@@ -356,12 +356,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));
|
||||
@@ -377,6 +380,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);
|
||||
|
@@ -63,7 +63,10 @@ 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 = 4000;
|
||||
|
||||
static char *forward_ip = NULL;
|
||||
static int forward_port = 0;
|
||||
static char *config_file = "mgcp.cfg";
|
||||
|
||||
/* used by msgb and mgcp */
|
||||
@@ -95,7 +98,7 @@ struct mgcp_endpoint {
|
||||
char *local_options;
|
||||
int conn_mode;
|
||||
|
||||
/* the local rtp port */
|
||||
/* the local rtp port we are binding to */
|
||||
int rtp_port;
|
||||
|
||||
/*
|
||||
@@ -107,9 +110,10 @@ struct mgcp_endpoint {
|
||||
struct bsc_fd local_rtcp;
|
||||
|
||||
struct in_addr remote;
|
||||
struct in_addr bts;
|
||||
|
||||
/* in network byte order */
|
||||
int rtp, rtcp;
|
||||
int net_rtp, net_rtcp;
|
||||
int bts_rtp, bts_rtcp;
|
||||
};
|
||||
|
||||
@@ -235,8 +239,10 @@ static int rtp_data_cb(struct bsc_fd *fd, unsigned int what)
|
||||
}
|
||||
|
||||
/* do not forward aynthing... maybe there is a packet from the bts */
|
||||
if (endp->ci == CI_UNUSED)
|
||||
if (endp->ci == CI_UNUSED) {
|
||||
DEBUGP(DMGCP, "Unknown message on endpoint: 0x%x\n", ENDPOINT_NUMBER(endp));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Figure out where to forward it to. This code assumes that we
|
||||
@@ -246,20 +252,22 @@ static int rtp_data_cb(struct bsc_fd *fd, unsigned int what)
|
||||
* able to tell if this is legitimate.
|
||||
*/
|
||||
#warning "Slight spec violation. With connection mode recvonly we should attempt to forward."
|
||||
dest = memcmp(&addr.sin_addr, &endp->remote, sizeof(addr.sin_addr)) == 0
|
||||
dest = memcmp(&addr.sin_addr, &endp->remote, sizeof(addr.sin_addr)) == 0 &&
|
||||
(endp->net_rtp == addr.sin_port || endp->net_rtcp == addr.sin_port)
|
||||
? DEST_BTS : DEST_NETWORK;
|
||||
proto = fd == &endp->local_rtp ? PROTO_RTP : PROTO_RTCP;
|
||||
|
||||
/* We have no idea who called us, maybe it is the BTS. */
|
||||
if (dest == DEST_NETWORK && endp->bts_rtp == 0) {
|
||||
if (dest == DEST_NETWORK && (endp->bts_rtp == 0 || forward_ip)) {
|
||||
/* it was the BTS... */
|
||||
if (memcmp(&addr.sin_addr, &bts_in, sizeof(bts_in)) == 0) {
|
||||
if (!bts_ip || memcmp(&addr.sin_addr, &bts_in, sizeof(bts_in)) == 0) {
|
||||
if (fd == &endp->local_rtp) {
|
||||
endp->bts_rtp = addr.sin_port;
|
||||
} else {
|
||||
endp->bts_rtcp = addr.sin_port;
|
||||
}
|
||||
|
||||
endp->bts = addr.sin_addr;
|
||||
DEBUGP(DMGCP, "Found BTS for endpoint: 0x%x on port: %d/%d\n",
|
||||
ENDPOINT_NUMBER(endp), ntohs(endp->bts_rtp), ntohs(endp->bts_rtcp));
|
||||
}
|
||||
@@ -271,10 +279,10 @@ static int rtp_data_cb(struct bsc_fd *fd, unsigned int what)
|
||||
|
||||
if (dest == DEST_NETWORK) {
|
||||
return _send(fd->fd, &endp->remote,
|
||||
proto == PROTO_RTP ? endp->rtp : endp->rtcp,
|
||||
proto == PROTO_RTP ? endp->net_rtp : endp->net_rtcp,
|
||||
buf, rc);
|
||||
} else {
|
||||
return _send(fd->fd, &bts_in,
|
||||
return _send(fd->fd, &endp->bts,
|
||||
proto == PROTO_RTP ? endp->bts_rtp : endp->bts_rtcp,
|
||||
buf, rc);
|
||||
}
|
||||
@@ -303,9 +311,6 @@ static int create_bind(struct bsc_fd *fd, int port)
|
||||
|
||||
static int bind_rtp(struct mgcp_endpoint *endp)
|
||||
{
|
||||
/* set to zero until we get the info */
|
||||
memset(&endp->remote, 0, sizeof(endp->remote));
|
||||
|
||||
if (create_bind(&endp->local_rtp, endp->rtp_port) != 0) {
|
||||
DEBUGP(DMGCP, "Failed to create RTP port: %d on 0x%x\n",
|
||||
endp->rtp_port, ENDPOINT_NUMBER(endp));
|
||||
@@ -636,7 +641,10 @@ static void handle_create_con(struct msgb *msg, struct sockaddr_in *source)
|
||||
MSG_TOKENIZE_END
|
||||
|
||||
/* initialize */
|
||||
endp->rtp = endp->rtcp = endp->bts_rtp = endp->bts_rtcp = 0;
|
||||
endp->net_rtp = endp->net_rtcp = endp->bts_rtp = endp->bts_rtcp = 0;
|
||||
|
||||
/* set to zero until we get the info */
|
||||
memset(&endp->remote, 0, sizeof(endp->remote));
|
||||
|
||||
/* bind to the port now */
|
||||
endp->rtp_port = rtp_calculate_port(ENDPOINT_NUMBER(endp), rtp_base_port);
|
||||
@@ -716,8 +724,8 @@ static void handle_modify_con(struct msgb *msg, struct sockaddr_in *source)
|
||||
const char *param = (const char *)&msg->l3h[line_start];
|
||||
|
||||
if (sscanf(param, "m=audio %d RTP/AVP %*d", &port) == 1) {
|
||||
endp->rtp = htons(port);
|
||||
endp->rtcp = htons(port + 1);
|
||||
endp->net_rtp = htons(port);
|
||||
endp->net_rtcp = htons(port + 1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -740,7 +748,7 @@ static void handle_modify_con(struct msgb *msg, struct sockaddr_in *source)
|
||||
|
||||
/* modify */
|
||||
DEBUGP(DMGCP, "Modified endpoint on: 0x%x Server: %s:%u\n",
|
||||
ENDPOINT_NUMBER(endp), inet_ntoa(endp->remote), endp->rtp);
|
||||
ENDPOINT_NUMBER(endp), inet_ntoa(endp->remote), endp->net_rtp);
|
||||
return send_with_sdp(endp, "MDCX", trans_id, source);
|
||||
|
||||
error:
|
||||
@@ -802,7 +810,7 @@ static void handle_delete_con(struct msgb *msg, struct sockaddr_in *source)
|
||||
bsc_unregister_fd(&endp->local_rtcp);
|
||||
}
|
||||
|
||||
endp->rtp = endp->rtcp = endp->bts_rtp = endp->bts_rtcp = 0;
|
||||
endp->net_rtp = endp->net_rtcp = endp->bts_rtp = endp->bts_rtcp = 0;
|
||||
|
||||
return send_response(250, "DLCX", trans_id, source);
|
||||
|
||||
@@ -900,7 +908,8 @@ static int config_write_mgcp(struct vty *vty)
|
||||
vty_out(vty, "mgcp%s", VTY_NEWLINE);
|
||||
if (local_ip)
|
||||
vty_out(vty, " local ip %s%s", local_ip, VTY_NEWLINE);
|
||||
vty_out(vty, " bts ip %s%s", bts_ip, VTY_NEWLINE);
|
||||
if (bts_ip)
|
||||
vty_out(vty, " bts ip %s%s", bts_ip, VTY_NEWLINE);
|
||||
vty_out(vty, " bind ip %s%s", source_addr, VTY_NEWLINE);
|
||||
vty_out(vty, " bind port %u%s", source_port, VTY_NEWLINE);
|
||||
vty_out(vty, " bind early %u%s", !!early_bind, VTY_NEWLINE);
|
||||
@@ -909,6 +918,10 @@ static int config_write_mgcp(struct vty *vty)
|
||||
vty_out(vty, " sdp audio payload name %s%s", audio_name, VTY_NEWLINE);
|
||||
vty_out(vty, " loop %u%s", !!audio_loop, VTY_NEWLINE);
|
||||
vty_out(vty, " endpoints %u%s", number_endpoints, VTY_NEWLINE);
|
||||
if (forward_ip)
|
||||
vty_out(vty, " forward audio ip %s%s", forward_ip, VTY_NEWLINE);
|
||||
if (forward_port != 0)
|
||||
vty_out(vty, " forward audio port %d%s", forward_port, VTY_NEWLINE);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
@@ -923,7 +936,7 @@ DEFUN(show_mcgp, show_mgcp_cmd, "show mgcp",
|
||||
struct mgcp_endpoint *endp = &endpoints[i];
|
||||
vty_out(vty, " Endpoint 0x%.2x: CI: %d net: %u/%u bts: %u/%u%s",
|
||||
i, endp->ci,
|
||||
ntohs(endp->rtp), ntohs(endp->rtcp),
|
||||
ntohs(endp->net_rtp), ntohs(endp->net_rtcp),
|
||||
ntohs(endp->bts_rtp), ntohs(endp->bts_rtcp), VTY_NEWLINE);
|
||||
}
|
||||
|
||||
@@ -1055,6 +1068,26 @@ DEFUN(cfg_mgcp_number_endp,
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_mgcp_forward_ip,
|
||||
cfg_mgcp_forward_ip_cmd,
|
||||
"forward audio ip IP",
|
||||
"Forward packets from and to the IP. This disables most of the MGCP feature.")
|
||||
{
|
||||
if (forward_ip)
|
||||
talloc_free(forward_ip);
|
||||
forward_ip = talloc_strdup(tall_bsc_ctx, argv[0]);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_mgcp_forward_port,
|
||||
cfg_mgcp_forward_port_cmd,
|
||||
"forward audio port <1-15000>",
|
||||
"Forward packets from and to the port. This disables most of the MGCP feature.")
|
||||
{
|
||||
forward_port = atoi(argv[0]);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
int bsc_vty_init(struct gsm_network *dummy)
|
||||
{
|
||||
cmd_init(1);
|
||||
@@ -1076,6 +1109,8 @@ int bsc_vty_init(struct gsm_network *dummy)
|
||||
install_element(MGCP_NODE, &cfg_mgcp_sdp_payload_name_cmd);
|
||||
install_element(MGCP_NODE, &cfg_mgcp_loop_cmd);
|
||||
install_element(MGCP_NODE, &cfg_mgcp_number_endp_cmd);
|
||||
install_element(MGCP_NODE, &cfg_mgcp_forward_ip_cmd);
|
||||
install_element(MGCP_NODE, &cfg_mgcp_forward_port_cmd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1096,10 +1131,8 @@ int main(int argc, char** argv)
|
||||
}
|
||||
|
||||
|
||||
if (!bts_ip) {
|
||||
fprintf(stderr, "Need to specify the BTS ip address for RTP handling.\n");
|
||||
return -1;
|
||||
}
|
||||
if (!bts_ip)
|
||||
fprintf(stderr, "No BTS ip address specified. This will allow everyone to connect.\n");
|
||||
|
||||
endpoints = _talloc_zero_array(tall_bsc_ctx,
|
||||
sizeof(struct mgcp_endpoint),
|
||||
@@ -1116,37 +1149,69 @@ int main(int argc, char** argv)
|
||||
endpoints[i].ci = CI_UNUSED;
|
||||
}
|
||||
|
||||
/* initialize the socket */
|
||||
bfd.when = BSC_FD_READ;
|
||||
bfd.cb = read_call_agent;
|
||||
bfd.fd = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if (bfd.fd < 0) {
|
||||
perror("Gateway failed to listen");
|
||||
return -1;
|
||||
}
|
||||
/*
|
||||
* This application supports two modes.
|
||||
* 1.) a true MGCP gateway with support for AUEP, CRCX, MDCX, DLCX
|
||||
* 2.) plain forwarding of RTP packets on the endpoints.
|
||||
* both modes are mutual exclusive
|
||||
*/
|
||||
if (forward_ip) {
|
||||
int port = rtp_base_port;
|
||||
if (forward_port != 0)
|
||||
port = forward_port;
|
||||
|
||||
setsockopt(bfd.fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
|
||||
if (!early_bind) {
|
||||
DEBUGP(DMGCP, "Forwarding requires early bind.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(source_port);
|
||||
inet_aton(source_addr, &addr.sin_addr);
|
||||
/*
|
||||
* Store the forward IP and assign a ci. For early bind
|
||||
* the sockets will be created after this.
|
||||
*/
|
||||
for (i = 1; i < number_endpoints; ++i) {
|
||||
struct mgcp_endpoint *endp = &endpoints[i];
|
||||
inet_aton(forward_ip, &endp->remote);
|
||||
endp->ci = CI_UNUSED + 23;
|
||||
endp->net_rtp = htons(rtp_calculate_port(ENDPOINT_NUMBER(endp), port));
|
||||
endp->net_rtcp = htons(rtp_calculate_port(ENDPOINT_NUMBER(endp), port) + 1);
|
||||
}
|
||||
|
||||
if (bind(bfd.fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
|
||||
perror("Gateway failed to bind");
|
||||
return -1;
|
||||
}
|
||||
DEBUGP(DMGCP, "Configured for Audio Forwarding.\n");
|
||||
} else {
|
||||
bfd.when = BSC_FD_READ;
|
||||
bfd.cb = read_call_agent;
|
||||
bfd.fd = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if (bfd.fd < 0) {
|
||||
perror("Gateway failed to listen");
|
||||
return -1;
|
||||
}
|
||||
|
||||
bfd.data = msgb_alloc(4096, "mgcp-msg");
|
||||
if (!bfd.data) {
|
||||
fprintf(stderr, "Gateway memory error.\n");
|
||||
return -1;
|
||||
}
|
||||
setsockopt(bfd.fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
|
||||
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(source_port);
|
||||
inet_aton(source_addr, &addr.sin_addr);
|
||||
|
||||
if (bind(bfd.fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
|
||||
perror("Gateway failed to bind");
|
||||
return -1;
|
||||
}
|
||||
|
||||
bfd.data = msgb_alloc(4096, "mgcp-msg");
|
||||
if (!bfd.data) {
|
||||
fprintf(stderr, "Gateway memory error.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
if (bsc_register_fd(&bfd) != 0) {
|
||||
DEBUGP(DMGCP, "Failed to register the fd\n");
|
||||
return -1;
|
||||
if (bsc_register_fd(&bfd) != 0) {
|
||||
DEBUGP(DMGCP, "Failed to register the fd\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
DEBUGP(DMGCP, "Configured for MGCP.\n");
|
||||
}
|
||||
|
||||
/* initialisation */
|
||||
|
72
openbsc/src/bsc_msc.c
Normal file
72
openbsc/src/bsc_msc.c
Normal file
@@ -0,0 +1,72 @@
|
||||
/* Routines to talk to the MSC using the IPA Protocol */
|
||||
/*
|
||||
* (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
|
||||
* (C) 2010 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/bsc_msc.h>
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/socket.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int connect_to_msc(struct bsc_fd *fd, const char *ip, int port)
|
||||
{
|
||||
struct sockaddr_in sin;
|
||||
int on = 1, ret;
|
||||
|
||||
printf("Attempting to connect MSC at %s:%d\n", ip, port);
|
||||
|
||||
fd->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||
fd->when = BSC_FD_READ;
|
||||
fd->data = NULL;
|
||||
fd->priv_nr = 1;
|
||||
|
||||
if (fd->fd < 0) {
|
||||
perror("Creating TCP socket failed");
|
||||
return fd->fd;
|
||||
}
|
||||
|
||||
|
||||
memset(&sin, 0, sizeof(sin));
|
||||
sin.sin_family = AF_INET;
|
||||
sin.sin_port = htons(port);
|
||||
inet_aton(ip, &sin.sin_addr);
|
||||
|
||||
setsockopt(fd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
|
||||
ret = connect(fd->fd, (struct sockaddr *) &sin, sizeof(sin));
|
||||
|
||||
if (ret < 0) {
|
||||
perror("Connection failed");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = bsc_register_fd(fd);
|
||||
if (ret < 0) {
|
||||
perror("Registering the fd failed");
|
||||
close(fd->fd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
809
openbsc/src/bsc_msc_ip.c
Normal file
809
openbsc/src/bsc_msc_ip.c
Normal file
@@ -0,0 +1,809 @@
|
||||
/* 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 <openbsc/bsc_msc.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) {
|
||||
struct bss_sccp_connection_data *con_data;
|
||||
|
||||
DEBUGP(DMSC, "Connection established: %p\n", conn);
|
||||
|
||||
/* start the inactivity test timer */
|
||||
con_data = (struct bss_sccp_connection_data *) conn->data_ctx;
|
||||
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);
|
||||
|
||||
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;
|
||||
|
||||
/* 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 */
|
||||
bts_unblock_queue(msg->lchan->msc_data);
|
||||
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 gsm_lchan *old_chan;
|
||||
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");
|
||||
put_lchan(msg->lchan);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (msg->lchan->msc_data->secondary_lchan != msg->lchan) {
|
||||
LOGP(DMSC, LOGL_NOTICE, "Wrong assignment complete.\n");
|
||||
put_lchan(msg->lchan);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (msgb_l3len(msg) - sizeof(*gh) != 1) {
|
||||
DEBUGP(DMSC, "assignment failure invalid: %d\n",
|
||||
msgb_l3len(msg) - sizeof(*gh));
|
||||
put_lchan(msg->lchan);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* swap the channels and release the old */
|
||||
old_chan = msg->lchan->msc_data->lchan;
|
||||
msg->lchan->msc_data->lchan = msg->lchan;
|
||||
msg->lchan->msc_data->secondary_lchan = NULL;
|
||||
old_chan->msc_data = NULL;
|
||||
|
||||
/* give up the old channel to not do a SACCH deactivate */
|
||||
subscr_put(old_chan->subscr);
|
||||
old_chan->subscr = NULL;
|
||||
put_lchan(old_chan);
|
||||
|
||||
/* activate audio on it... */
|
||||
if (is_ipaccess_bts(msg->lchan->ts->trx->bts) && msg->lchan->tch_mode != GSM48_CMODE_SIGN)
|
||||
rsl_ipacc_crcx(msg->lchan);
|
||||
|
||||
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");
|
||||
put_lchan(msg->lchan);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (msg->lchan->msc_data->secondary_lchan != msg->lchan) {
|
||||
LOGP(DMSC, LOGL_NOTICE, "Wrong assignment complete.\n");
|
||||
put_lchan(msg->lchan);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (msgb_l3len(msg) - sizeof(*gh) != 1) {
|
||||
DEBUGP(DMSC, "assignment failure invalid: %d\n",
|
||||
msgb_l3len(msg) - sizeof(*gh));
|
||||
put_lchan(msg->lchan);
|
||||
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, lchan->abis_ip.conn_id);
|
||||
|
||||
int rtp_payload = ts->trx->bts->network->rtp_payload;
|
||||
if (rtp_payload == 0)
|
||||
rtp_payload = lchan->abis_ip.rtp_payload2;
|
||||
|
||||
rc = rsl_ipacc_mdcx(lchan, ntohl(local_addr.s_addr),
|
||||
lchan->msc_data->rtp_port,
|
||||
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);
|
||||
|
||||
msgb_free(msg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
msc_connection.cb = ipaccess_a_fd_cb;
|
||||
rc = connect_to_msc(&msc_connection, 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);
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
|
||||
while (1) {
|
||||
bsc_select_main(0);
|
||||
}
|
||||
}
|
1299
openbsc/src/bssap.c
Normal file
1299
openbsc/src/bssap.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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)
|
||||
{
|
||||
@@ -223,10 +221,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;
|
||||
@@ -249,8 +246,6 @@ void lchan_free(struct gsm_lchan *lchan)
|
||||
lchan->use_count = 0;
|
||||
}
|
||||
|
||||
/* stop the timer */
|
||||
bsc_del_timer(&lchan->release_timer);
|
||||
bsc_del_timer(&lchan->T3101);
|
||||
|
||||
/* clear cached measuement reports */
|
||||
@@ -261,21 +256,23 @@ void lchan_free(struct gsm_lchan *lchan)
|
||||
}
|
||||
for (i = 0; i < ARRAY_SIZE(lchan->neigh_meas); i++)
|
||||
lchan->neigh_meas[i].arfcn = 0;
|
||||
|
||||
/* 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 */
|
||||
@@ -288,15 +285,6 @@ int lchan_auto_release(struct gsm_lchan *lchan)
|
||||
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;
|
||||
|
@@ -232,7 +232,6 @@ 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);
|
||||
if (lchan->ts->trx->bts->network->send_mm_info) {
|
||||
/* send MM INFO with network name */
|
||||
@@ -244,8 +243,7 @@ static int gsm0408_authorize(struct gsm_lchan *lchan, struct msgb *msg)
|
||||
* trigger further action like SMS delivery */
|
||||
subscr_update(lchan->subscr, msg->trx->bts,
|
||||
GSM_SUBSCRIBER_UPDATE_ATTACHED);
|
||||
/* try to close channel ASAP */
|
||||
lchan_auto_release(lchan);
|
||||
release_loc_updating_req(lchan);
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -931,9 +929,8 @@ static void loc_upd_rej_cb(void *data)
|
||||
struct gsm_lchan *lchan = data;
|
||||
struct gsm_bts *bts = lchan->ts->trx->bts;
|
||||
|
||||
release_loc_updating_req(lchan);
|
||||
gsm0408_loc_upd_rej(lchan, bts->network->reject_cause);
|
||||
lchan_auto_release(lchan);
|
||||
release_loc_updating_req(lchan);
|
||||
}
|
||||
|
||||
static void schedule_reject(struct gsm_lchan *lchan)
|
||||
@@ -1355,8 +1352,6 @@ static int gsm48_rx_mm_imsi_detach_ind(struct msgb *msg)
|
||||
* imagine an IMSI DETACH happening during an active call! */
|
||||
|
||||
/* subscriber is detached: should we release lchan? */
|
||||
lchan_auto_release(msg->lchan);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -2698,7 +2693,6 @@ static int gsm48_cc_rx_release_compl(struct gsm_trans *trans, struct msgb *msg)
|
||||
MNCC_REL_CNF, &rel);
|
||||
/* FIXME: in case of multiple calls, we can't simply
|
||||
* hang up here ! */
|
||||
lchan_auto_release(msg->lchan);
|
||||
break;
|
||||
default:
|
||||
rc = mncc_recvmsg(trans->subscr->net, trans,
|
||||
|
@@ -567,7 +567,7 @@ int gsm48_send_ho_cmd(struct gsm_lchan *old_lchan, struct gsm_lchan *new_lchan,
|
||||
}
|
||||
|
||||
/* Chapter 9.1.2: Assignment Command */
|
||||
int gsm48_send_rr_ass_cmd(struct gsm_lchan *lchan, u_int8_t power_command)
|
||||
int gsm48_send_rr_ass_cmd(struct gsm_lchan *dest_lchan, struct gsm_lchan *lchan, u_int8_t power_command)
|
||||
{
|
||||
struct msgb *msg = gsm48_msgb_alloc();
|
||||
struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
|
||||
@@ -576,7 +576,7 @@ int gsm48_send_rr_ass_cmd(struct gsm_lchan *lchan, u_int8_t power_command)
|
||||
|
||||
DEBUGP(DRR, "-> ASSIGNMENT COMMAND tch_mode=0x%02x\n", lchan->tch_mode);
|
||||
|
||||
msg->lchan = lchan;
|
||||
msg->lchan = dest_lchan;
|
||||
gh->proto_discr = GSM48_PDISC_RR;
|
||||
gh->msg_type = GSM48_MT_RR_ASS_CMD;
|
||||
|
||||
@@ -591,6 +591,8 @@ int gsm48_send_rr_ass_cmd(struct gsm_lchan *lchan, u_int8_t power_command)
|
||||
gsm48_chan_desc(&ass->chan_desc, lchan);
|
||||
ass->power_command = power_command;
|
||||
|
||||
msgb_tv_put(msg, GSM48_IE_CHANMODE_1, lchan->tch_mode);
|
||||
|
||||
/* in case of multi rate we need to attach a config */
|
||||
if (lchan->tch_mode == GSM48_CMODE_SPEECH_AMR) {
|
||||
if (lchan->mr_conf.ver == 0) {
|
||||
|
@@ -224,6 +224,10 @@ struct gsm_network *gsm_network_init(u_int16_t country_code, u_int16_t network_c
|
||||
|
||||
net->mncc_recv = mncc_recv;
|
||||
|
||||
net->core_country_code = -1;
|
||||
net->core_network_code = -1;
|
||||
net->rtp_base_port = 4000;
|
||||
|
||||
return net;
|
||||
}
|
||||
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -223,7 +223,6 @@ static int ho_gsm48_ho_compl(struct gsm_lchan *new_lchan)
|
||||
trans_lchan_change(ho->old_lchan, new_lchan);
|
||||
|
||||
ho->old_lchan->state = LCHAN_S_INACTIVE;
|
||||
lchan_auto_release(ho->old_lchan);
|
||||
|
||||
/* do something to re-route the actual speech frames ! */
|
||||
|
||||
|
@@ -164,6 +164,12 @@ static int parse_unitid(const char *str, u_int16_t *site_id, u_int16_t *bts_id,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* send the id ack */
|
||||
int ipaccess_send_id_ack(int fd)
|
||||
{
|
||||
return write(fd, id_ack, sizeof(id_ack));
|
||||
}
|
||||
|
||||
/* base handling of the ip.access protocol */
|
||||
int ipaccess_rcvmsg_base(struct msgb *msg,
|
||||
struct bsc_fd *bfd)
|
||||
@@ -180,7 +186,7 @@ int ipaccess_rcvmsg_base(struct msgb *msg,
|
||||
break;
|
||||
case IPAC_MSGT_ID_ACK:
|
||||
DEBUGP(DMI, "ID_ACK? -> ACK!\n");
|
||||
ret = write(bfd->fd, id_ack, sizeof(id_ack));
|
||||
ret = ipaccess_send_id_ack(bfd->fd);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
@@ -236,6 +242,14 @@ static int ipaccess_rcvmsg(struct e1inp_line *line, struct msgb *msg,
|
||||
trx->rsl_link = e1inp_sign_link_create(e1i_ts,
|
||||
E1INP_SIGN_RSL, trx,
|
||||
trx->rsl_tei, 0);
|
||||
|
||||
if (newbfd->fd >= 0) {
|
||||
LOGP(DINP, LOGL_ERROR, "BTS is still registered. Closing old connection.\n");
|
||||
bsc_unregister_fd(newbfd);
|
||||
close(newbfd->fd);
|
||||
newbfd->fd = -1;
|
||||
}
|
||||
|
||||
/* get rid of our old temporary bfd */
|
||||
memcpy(newbfd, bfd, sizeof(*newbfd));
|
||||
newbfd->priv_nr = 2+trx_id;
|
||||
@@ -478,6 +492,7 @@ static int listen_fd_cb(struct bsc_fd *listen_bfd, unsigned int what)
|
||||
{
|
||||
int ret;
|
||||
int idx = 0;
|
||||
int i;
|
||||
struct e1inp_line *line;
|
||||
struct e1inp_ts *e1i_ts;
|
||||
struct bsc_fd *bfd;
|
||||
@@ -504,6 +519,10 @@ static int listen_fd_cb(struct bsc_fd *listen_bfd, unsigned int what)
|
||||
/* create virrtual E1 timeslots for signalling */
|
||||
e1inp_ts_config(&line->ts[1-1], line, E1INP_TS_TYPE_SIGN);
|
||||
|
||||
/* initialize the fds */
|
||||
for (i = 0; i < ARRAY_SIZE(line->ts); ++i)
|
||||
line->ts[i].driver.ipaccess.fd.fd = -1;
|
||||
|
||||
e1i_ts = &line->ts[idx];
|
||||
|
||||
bfd = &e1i_ts->driver.ipaccess.fd;
|
||||
|
@@ -95,6 +95,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)
|
||||
|
34
openbsc/src/nat/bsc_filter.c
Normal file
34
openbsc/src/nat/bsc_filter.c
Normal file
@@ -0,0 +1,34 @@
|
||||
/* BSC Multiplexer/NAT */
|
||||
|
||||
/*
|
||||
* (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
|
||||
* (C) 2010 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/bsc_nat.h>
|
||||
#include <openbsc/ipaccess.h>
|
||||
|
||||
int bsc_nat_filter_ipa(struct msgb *msg)
|
||||
{
|
||||
struct ipaccess_head *hh;
|
||||
|
||||
/* handle base message handling */
|
||||
hh = (struct ipaccess_head *) msg->data;
|
||||
return hh->proto == IPAC_PROTO_IPACCESS;
|
||||
}
|
412
openbsc/src/nat/bsc_nat.c
Normal file
412
openbsc/src/nat/bsc_nat.c
Normal file
@@ -0,0 +1,412 @@
|
||||
/* BSC Multiplexer/NAT */
|
||||
|
||||
/*
|
||||
* (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
|
||||
* (C) 2010 by on-waves.com
|
||||
* (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 <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <getopt.h>
|
||||
|
||||
#include <openbsc/debug.h>
|
||||
#include <openbsc/msgb.h>
|
||||
#include <openbsc/bsc_msc.h>
|
||||
#include <openbsc/bsc_nat.h>
|
||||
#include <openbsc/ipaccess.h>
|
||||
#include <openbsc/abis_nm.h>
|
||||
#include <openbsc/talloc.h>
|
||||
#include <openbsc/linuxlist.h>
|
||||
|
||||
#include <sccp/sccp.h>
|
||||
|
||||
static const char *config_file = "openbsc.cfg";
|
||||
static char *msc_address = "127.0.0.1";
|
||||
static struct in_addr local_addr;
|
||||
static struct bsc_fd msc_connection;
|
||||
static struct bsc_fd bsc_connection;
|
||||
|
||||
|
||||
/*
|
||||
* Per BSC data structure
|
||||
*/
|
||||
struct bsc_connection {
|
||||
struct llist_head list_entry;
|
||||
|
||||
/* do we know anything about this BSC? */
|
||||
int authenticated;
|
||||
|
||||
/* the fd we use to communicate */
|
||||
struct bsc_fd bsc_fd;
|
||||
};
|
||||
|
||||
static LLIST_HEAD(bsc_connections);
|
||||
|
||||
|
||||
/*
|
||||
* below are stubs we need to link
|
||||
*/
|
||||
int nm_state_event(enum nm_evt evt, u_int8_t obj_class, void *obj,
|
||||
struct gsm_nm_state *old_state, struct gsm_nm_state *new_state)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
void input_event(int event, enum e1inp_sign_type type, struct gsm_bts_trx *trx)
|
||||
{}
|
||||
|
||||
int gsm0408_rcvmsg(struct msgb *msg, u_int8_t link_id)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Below is the handling of messages coming
|
||||
* from the MSC and need to be forwarded to
|
||||
* a real BSC.
|
||||
*/
|
||||
static void initialize_msc_if_needed()
|
||||
{
|
||||
static int init = 0;
|
||||
init = 1;
|
||||
|
||||
/* do we need to send a GSM 08.08 message here? */
|
||||
}
|
||||
|
||||
static void forward_sccp_to_bts(struct msgb *msg)
|
||||
{
|
||||
struct bsc_connection *bsc;
|
||||
int rc;
|
||||
|
||||
/* filter, drop, patch the message? */
|
||||
|
||||
/* drop packets with the wrong IPA header */
|
||||
if (bsc_nat_filter_ipa(msg))
|
||||
return;
|
||||
|
||||
/* currently send this to every BSC connected */
|
||||
llist_for_each_entry(bsc, &bsc_connections, list_entry) {
|
||||
rc = write(bsc->bsc_fd.fd, msg->data, msg->len);
|
||||
|
||||
/* try the next one */
|
||||
if (rc < msg->len)
|
||||
fprintf(stderr, "Failed to write message to BTS.\n");
|
||||
}
|
||||
}
|
||||
|
||||
static int ipaccess_msc_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, "MSG 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_msc_if_needed();
|
||||
else if (hh->proto == IPAC_PROTO_SCCP)
|
||||
forward_sccp_to_bts(msg);
|
||||
|
||||
msgb_free(msg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Below is the handling of messages coming
|
||||
* from the BSC and need to be forwarded to
|
||||
* a real BSC.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Remove the connection from the connections list,
|
||||
* remove it from the patching of SCCP header lists
|
||||
* as well. Maybe in the future even close connection..
|
||||
*/
|
||||
static void remove_bsc_connection(struct bsc_connection *connection)
|
||||
{
|
||||
bsc_unregister_fd(&connection->bsc_fd);
|
||||
llist_del(&connection->list_entry);
|
||||
talloc_free(connection);
|
||||
}
|
||||
|
||||
static int forward_sccp_to_msc(struct msgb *msg)
|
||||
{
|
||||
/* FIXME: We need to filter out certain messages */
|
||||
|
||||
/* drop packets with the wrong IPA header */
|
||||
if (bsc_nat_filter_ipa(msg))
|
||||
return 0;
|
||||
|
||||
/* send the non-filtered but maybe modified msg */
|
||||
return write(msc_connection.fd, msg->data, msg->len);
|
||||
}
|
||||
|
||||
static int ipaccess_bsc_cb(struct bsc_fd *bfd, unsigned int what)
|
||||
{
|
||||
int error;
|
||||
struct msgb *msg = ipaccess_read_msg(bfd, &error);
|
||||
|
||||
if (!msg) {
|
||||
if (error == 0) {
|
||||
fprintf(stderr, "The connection to the BSC was lost. Cleaning it\n");
|
||||
remove_bsc_connection((struct bsc_connection *) bfd->data);
|
||||
}
|
||||
|
||||
fprintf(stderr, "Failed to parse ip access message: %d\n", error);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
DEBUGP(DMSC, "MSG from BSC: %s proto: %d\n", hexdump(msg->data, msg->len), msg->l2h[0]);
|
||||
|
||||
/* Handle messages from the BSC */
|
||||
/* FIXME: Currently no PONG is sent to the BSC */
|
||||
/* FIXME: Currently no ID ACK is sent to the BSC */
|
||||
forward_sccp_to_msc(msg);
|
||||
msgb_free(msg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ipaccess_listen_bsc_cb(struct bsc_fd *bfd, unsigned int what)
|
||||
{
|
||||
struct bsc_connection *bsc;
|
||||
int ret;
|
||||
struct sockaddr_in sa;
|
||||
socklen_t sa_len = sizeof(sa);
|
||||
|
||||
if (!(what & BSC_FD_READ))
|
||||
return 0;
|
||||
|
||||
ret = accept(bfd->fd, (struct sockaddr *) &sa, &sa_len);
|
||||
if (ret < 0) {
|
||||
perror("accept");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* todo... do something with the connection */
|
||||
/* todo... use GNUtls to see if we want to trust this as a BTS */
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
bsc = talloc_zero(tall_bsc_ctx, struct bsc_connection);
|
||||
if (!bsc) {
|
||||
DEBUGP(DMSC, "Failed to allocate BSC struct.\n");
|
||||
close(ret);
|
||||
return -1;
|
||||
}
|
||||
|
||||
bsc->bsc_fd.data = bsc;
|
||||
bsc->bsc_fd.fd = ret;
|
||||
bsc->bsc_fd.cb = ipaccess_bsc_cb;
|
||||
bsc->bsc_fd.when = BSC_FD_READ;
|
||||
if (bsc_register_fd(&bsc->bsc_fd) < 0) {
|
||||
DEBUGP(DMSC, "Failed to register BSC fd.\n");
|
||||
close(ret);
|
||||
talloc_free(bsc);
|
||||
return -2;
|
||||
}
|
||||
|
||||
DEBUGP(DMSC, "Registered new BSC\n");
|
||||
llist_add(&bsc->list_entry, &bsc_connections);
|
||||
ipaccess_send_id_ack(ret);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int listen_for_bsc(struct bsc_fd *bfd, struct in_addr *in_addr, int port)
|
||||
{
|
||||
struct sockaddr_in addr;
|
||||
int ret, on = 1;
|
||||
|
||||
bfd->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||
bfd->cb = ipaccess_listen_bsc_cb;
|
||||
bfd->when = BSC_FD_READ;
|
||||
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(port);
|
||||
addr.sin_addr.s_addr = in_addr->s_addr;
|
||||
|
||||
setsockopt(bfd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
|
||||
|
||||
ret = bind(bfd->fd, (struct sockaddr *) &addr, sizeof(addr));
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "Could not bind the BSC socket %s\n",
|
||||
strerror(errno));
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
ret = listen(bfd->fd, 1);
|
||||
if (ret < 0) {
|
||||
perror("listen");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = bsc_register_fd(bfd);
|
||||
if (ret < 0) {
|
||||
perror("register_listen_fd");
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void print_usage()
|
||||
{
|
||||
printf("Usage: bsc_nat\n");
|
||||
}
|
||||
|
||||
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 this BSC.\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'},
|
||||
{"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 '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 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;
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
int rc;
|
||||
|
||||
/* parse options */
|
||||
local_addr.s_addr = INADDR_ANY;
|
||||
handle_options(argc, argv);
|
||||
|
||||
/* seed the PRNG */
|
||||
srand(time(NULL));
|
||||
|
||||
/* connect to the MSC */
|
||||
msc_connection.cb = ipaccess_msc_cb;
|
||||
rc = connect_to_msc(&msc_connection, msc_address, 5000);
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "Opening the MSC connection failed.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* wait for the BSC */
|
||||
if (listen_for_bsc(&bsc_connection, &local_addr, 5000) < 0) {
|
||||
fprintf(stderr, "Failed to listen for BSC.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
signal(SIGINT, &signal_handler);
|
||||
signal(SIGABRT, &signal_handler);
|
||||
signal(SIGUSR1, &signal_handler);
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
|
||||
while (1) {
|
||||
bsc_select_main(0);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
@@ -160,7 +160,7 @@ static void paging_handle_pending_requests(struct gsm_bts_paging_state *paging_b
|
||||
} while (paging_bts->available_slots > 0
|
||||
&& initial_request != current_request);
|
||||
|
||||
bsc_schedule_timer(&paging_bts->work_timer, 1, 0);
|
||||
bsc_schedule_timer(&paging_bts->work_timer, 2, 0);
|
||||
}
|
||||
|
||||
static void paging_worker(void *data)
|
||||
@@ -245,7 +245,7 @@ static int _paging_request(struct gsm_bts *bts, struct gsm_subscriber *subscr,
|
||||
llist_add_tail(&req->entry, &bts_entry->pending_requests);
|
||||
|
||||
if (!bsc_timer_pending(&bts_entry->work_timer))
|
||||
bsc_schedule_timer(&bts_entry->work_timer, 1, 0);
|
||||
bsc_schedule_timer(&bts_entry->work_timer, 2, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@@ -37,7 +37,7 @@ int rest_octets_si1(u_int8_t *data, u_int8_t *nch_pos)
|
||||
|
||||
memset(&bv, 0, sizeof(bv));
|
||||
bv.data = data;
|
||||
bv.data_len = 2;
|
||||
bv.data_len = 1;
|
||||
|
||||
if (nch_pos) {
|
||||
bitvec_set_bit(&bv, H);
|
||||
@@ -45,7 +45,7 @@ int rest_octets_si1(u_int8_t *data, u_int8_t *nch_pos)
|
||||
} else
|
||||
bitvec_set_bit(&bv, L);
|
||||
|
||||
bitvec_spare_padding(&bv, 15);
|
||||
bitvec_spare_padding(&bv, 7);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -95,7 +95,7 @@ int rest_octets_si3(u_int8_t *data, const struct gsm48_si_ro_info *si3)
|
||||
|
||||
memset(&bv, 0, sizeof(bv));
|
||||
bv.data = data;
|
||||
bv.data_len = 5;
|
||||
bv.data_len = 4;
|
||||
|
||||
/* Optional Selection Parameters */
|
||||
append_selection_params(&bv, &si3->selection_params);
|
||||
@@ -141,7 +141,7 @@ int rest_octets_si4(u_int8_t *data, const struct gsm48_si_ro_info *si4)
|
||||
|
||||
memset(&bv, 0, sizeof(bv));
|
||||
bv.data = data;
|
||||
bv.data_len = 11; /* FIXME: up to ? */
|
||||
bv.data_len = 10; /* FIXME: up to ? */
|
||||
|
||||
/* SI4 Rest Octets O */
|
||||
append_selection_params(&bv, &si4->selection_params);
|
||||
@@ -340,7 +340,7 @@ int rest_octets_si13(u_int8_t *data, const struct gsm48_si13_info *si13)
|
||||
|
||||
memset(&bv, 0, sizeof(bv));
|
||||
bv.data = data;
|
||||
bv.data_len = 21;
|
||||
bv.data_len = 20;
|
||||
|
||||
if (0) {
|
||||
/* No rest octets */
|
||||
|
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* SCCP management code
|
||||
*
|
||||
* (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
|
||||
* (C) 2009 by on-waves.com
|
||||
* (C) 2009, 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
|
||||
* (C) 2009, 2010 by on-waves.com
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
@@ -199,6 +199,134 @@ static int _sccp_parse_optional_data(const int offset,
|
||||
return -1;
|
||||
}
|
||||
|
||||
int _sccp_parse_connection_request(struct msgb *msgb, struct sccp_parse_result *result)
|
||||
{
|
||||
static const u_int32_t header_size =
|
||||
sizeof(struct sccp_connection_request);
|
||||
static const u_int32_t optional_offset =
|
||||
offsetof(struct sccp_connection_request, optional_start);
|
||||
static const u_int32_t called_offset =
|
||||
offsetof(struct sccp_connection_request, variable_called);
|
||||
|
||||
struct sccp_connection_request *req = (struct sccp_connection_request *)msgb->data;
|
||||
struct sccp_optional_data optional_data;
|
||||
|
||||
/* header check */
|
||||
if (msgb_l2len(msgb) < header_size) {
|
||||
DEBUGP(DSCCP, "msgb < header_size %u %u\n",
|
||||
msgb_l2len(msgb), header_size);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* copy out the calling and called address. Add the offset */
|
||||
if (copy_address(&result->called, called_offset + req->variable_called, msgb) != 0)
|
||||
return -1;
|
||||
|
||||
if (check_address(&result->called) != 0) {
|
||||
DEBUGP(DSCCP, "Invalid called address according to 08.06: 0x%x 0x%x\n",
|
||||
*(u_int8_t *)&result->called.address, result->called.ssn);
|
||||
return -1;
|
||||
}
|
||||
|
||||
result->source_local_reference = &req->source_local_reference;
|
||||
|
||||
/*
|
||||
* parse optional data.
|
||||
*/
|
||||
memset(&optional_data, 0, sizeof(optional_data));
|
||||
if (_sccp_parse_optional_data(optional_offset + req->optional_start, msgb, &optional_data) != 0) {
|
||||
DEBUGP(DSCCP, "parsing of optional data failed.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (optional_data.data_len != 0) {
|
||||
msgb->l3h = &msgb->l2h[optional_data.data_start];
|
||||
result->data_len = optional_data.data_len;
|
||||
} else {
|
||||
result->data_len = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int _sccp_parse_connection_released(struct msgb *msg, struct sccp_parse_result *result)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int _sccp_parse_connection_refused(struct msgb *msg, struct sccp_parse_result *result)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int _sccp_parse_connection_confirm(struct msgb *msg, struct sccp_parse_result *result)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int _sccp_parse_connection_release_complete(struct msgb *msg, struct sccp_parse_result *result)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int _sccp_parse_connection_dt1(struct msgb *msg, struct sccp_parse_result *result)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int _sccp_parse_udt(struct msgb *msgb, struct sccp_parse_result *result)
|
||||
{
|
||||
static const u_int32_t header_size = sizeof(struct sccp_data_unitdata);
|
||||
static const u_int32_t called_offset = offsetof(struct sccp_data_unitdata, variable_called);
|
||||
static const u_int32_t calling_offset = offsetof(struct sccp_data_unitdata, variable_calling);
|
||||
static const u_int32_t data_offset = offsetof(struct sccp_data_unitdata, variable_data);
|
||||
|
||||
struct sccp_data_unitdata *udt = (struct sccp_data_unitdata *)msgb->l2h;
|
||||
|
||||
if (msgb_l2len(msgb) < header_size) {
|
||||
DEBUGP(DSCCP, "msgb < header_size %u %u\n",
|
||||
msgb_l2len(msgb), header_size);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* copy out the calling and called address. Add the off */
|
||||
if (copy_address(&result->called, called_offset + udt->variable_called, msgb) != 0)
|
||||
return -1;
|
||||
|
||||
if (check_address(&result->called) != 0) {
|
||||
DEBUGP(DSCCP, "Invalid called address according to 08.06: 0x%x 0x%x\n",
|
||||
*(u_int8_t *)&result->called.address, result->called.ssn);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (copy_address(&result->calling, calling_offset + udt->variable_calling, msgb) != 0)
|
||||
return -1;
|
||||
|
||||
if (check_address(&result->calling) != 0) {
|
||||
DEBUGP(DSCCP, "Invalid called address according to 08.06: 0x%x 0x%x\n",
|
||||
*(u_int8_t *)&result->called.address, result->called.ssn);
|
||||
}
|
||||
|
||||
/* we don't have enough size for the data */
|
||||
if (msgb_l2len(msgb) < data_offset + udt->variable_data + 1) {
|
||||
DEBUGP(DSCCP, "msgb < header + offset %u %u %u\n",
|
||||
msgb_l2len(msgb), header_size, udt->variable_data);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
msgb->l3h = &udt->data[udt->variable_data];
|
||||
|
||||
if (msgb_l3len(msgb) != msgb->l3h[-1]) {
|
||||
DEBUGP(DSCCP, "msgb is truncated %u %u\n",
|
||||
msgb_l3len(msgb), msgb->l3h[-1]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Send UDT. Currently we have a fixed address...
|
||||
*/
|
||||
@@ -249,59 +377,15 @@ static int _sccp_send_data(int class, const struct sockaddr_sccp *in,
|
||||
|
||||
static int _sccp_handle_read(struct msgb *msgb)
|
||||
{
|
||||
static const u_int32_t header_size = sizeof(struct sccp_data_unitdata);
|
||||
static const u_int32_t called_offset = offsetof(struct sccp_data_unitdata, variable_called);
|
||||
static const u_int32_t calling_offset = offsetof(struct sccp_data_unitdata, variable_calling);
|
||||
static const u_int32_t data_offset = offsetof(struct sccp_data_unitdata, variable_data);
|
||||
|
||||
struct sccp_data_callback *cb;
|
||||
struct sccp_data_unitdata *udt = (struct sccp_data_unitdata *)msgb->l2h;
|
||||
struct sccp_address called, calling;
|
||||
struct sccp_parse_result result;
|
||||
|
||||
/* we don't have enough size for the struct */
|
||||
if (msgb_l2len(msgb) < header_size) {
|
||||
DEBUGP(DSCCP, "msgb < header_size %u %u\n",
|
||||
msgb_l2len(msgb), header_size);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* copy out the calling and called address. Add the off */
|
||||
if (copy_address(&called, called_offset + udt->variable_called, msgb) != 0)
|
||||
if (_sccp_parse_udt(msgb, &result) != 0)
|
||||
return -1;
|
||||
|
||||
if (check_address(&called) != 0) {
|
||||
DEBUGP(DSCCP, "Invalid called address according to 08.06: 0x%x 0x%x\n",
|
||||
*(u_int8_t *)&called.address, called.ssn);
|
||||
return -1;
|
||||
}
|
||||
|
||||
cb = _find_ssn(called.ssn);
|
||||
cb = _find_ssn(result.called.ssn);
|
||||
if (!cb || !cb->read_cb) {
|
||||
DEBUGP(DSCCP, "No routing for UDT for called SSN: %u\n", called.ssn);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (copy_address(&calling, calling_offset + udt->variable_calling, msgb) != 0)
|
||||
return -1;
|
||||
|
||||
if (check_address(&calling) != 0) {
|
||||
DEBUGP(DSCCP, "Invalid called address according to 08.06: 0x%x 0x%x\n",
|
||||
*(u_int8_t *)&called.address, called.ssn);
|
||||
}
|
||||
|
||||
/* we don't have enough size for the data */
|
||||
if (msgb_l2len(msgb) < data_offset + udt->variable_data + 1) {
|
||||
DEBUGP(DSCCP, "msgb < header + offset %u %u %u\n",
|
||||
msgb_l2len(msgb), header_size, udt->variable_data);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
msgb->l3h = &udt->data[udt->variable_data];
|
||||
|
||||
if (msgb_l3len(msgb) != msgb->l3h[-1]) {
|
||||
DEBUGP(DSCCP, "msgb is truncated %u %u\n",
|
||||
msgb_l3len(msgb), msgb->l3h[-1]);
|
||||
DEBUGP(DSCCP, "No routing for UDT for called SSN: %u\n", result.called.ssn);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -374,7 +458,7 @@ static void _sccp_set_connection_state(struct sccp_connection *connection, int n
|
||||
connection->state_cb(connection, old_state);
|
||||
}
|
||||
|
||||
static int _sccp_send_refuse(struct sccp_connection_request *req, int cause)
|
||||
static int _sccp_send_refuse(struct sccp_source_reference *src_ref, int cause)
|
||||
{
|
||||
struct msgb *msgb;
|
||||
struct sccp_connection_refused *ref;
|
||||
@@ -387,7 +471,7 @@ static int _sccp_send_refuse(struct sccp_connection_request *req, int cause)
|
||||
|
||||
ref = (struct sccp_connection_refused *) msgb_put(msgb, sizeof(*ref));
|
||||
ref->type = SCCP_MSG_TYPE_CREF;
|
||||
memcpy(&ref->destination_local_reference, &req->source_local_reference,
|
||||
memcpy(&ref->destination_local_reference, src_ref,
|
||||
sizeof(struct sccp_source_reference));
|
||||
ref->cause = cause;
|
||||
ref->optional_start = 1;
|
||||
@@ -601,39 +685,17 @@ static int _sccp_send_connection_released(struct sccp_connection *conn, int caus
|
||||
*/
|
||||
static int _sccp_handle_connection_request(struct msgb *msgb)
|
||||
{
|
||||
static const u_int32_t header_size =
|
||||
sizeof(struct sccp_connection_request);
|
||||
static const u_int32_t optional_offset =
|
||||
offsetof(struct sccp_connection_request, optional_start);
|
||||
static const u_int32_t called_offset =
|
||||
offsetof(struct sccp_connection_request, variable_called);
|
||||
struct sccp_parse_result result;
|
||||
|
||||
struct sccp_data_callback *cb;
|
||||
struct sccp_connection_request *req = (struct sccp_connection_request *)msgb->data;
|
||||
struct sccp_address called;
|
||||
struct sccp_connection *connection;
|
||||
struct sccp_optional_data optional_data;
|
||||
|
||||
/* header check */
|
||||
if (msgb_l2len(msgb) < header_size) {
|
||||
DEBUGP(DSCCP, "msgb < header_size %u %u\n",
|
||||
msgb_l2len(msgb), header_size);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* copy out the calling and called address. Add the offset */
|
||||
if (copy_address(&called, called_offset + req->variable_called, msgb) != 0)
|
||||
if (_sccp_parse_connection_request(msgb, &result) != 0)
|
||||
return -1;
|
||||
|
||||
if (check_address(&called) != 0) {
|
||||
DEBUGP(DSCCP, "Invalid called address according to 08.06: 0x%x 0x%x\n",
|
||||
*(u_int8_t *)&called.address, called.ssn);
|
||||
return -1;
|
||||
}
|
||||
|
||||
cb = _find_ssn(called.ssn);
|
||||
cb = _find_ssn(result.called.ssn);
|
||||
if (!cb || !cb->accept_cb) {
|
||||
DEBUGP(DSCCP, "No routing for CR for called SSN: %u\n", called.ssn);
|
||||
DEBUGP(DSCCP, "No routing for CR for called SSN: %u\n", result.called.ssn);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -651,28 +713,18 @@ static int _sccp_handle_connection_request(struct msgb *msgb)
|
||||
* and send a connection confirm, otherwise we will send a refuseed
|
||||
* one....
|
||||
*/
|
||||
if (destination_local_reference_is_free(&req->source_local_reference) != 0) {
|
||||
if (destination_local_reference_is_free(result.source_local_reference) != 0) {
|
||||
DEBUGP(DSCCP, "Need to reject connection with existing reference\n");
|
||||
_sccp_send_refuse(req, SCCP_REFUSAL_SCCP_FAILURE);
|
||||
_sccp_send_refuse(result.source_local_reference, SCCP_REFUSAL_SCCP_FAILURE);
|
||||
talloc_free(connection);
|
||||
return -1;
|
||||
}
|
||||
|
||||
connection->incoming = 1;
|
||||
connection->destination_local_reference = req->source_local_reference;
|
||||
|
||||
/*
|
||||
* parse optional data.
|
||||
*/
|
||||
memset(&optional_data, 0, sizeof(optional_data));
|
||||
if (_sccp_parse_optional_data(optional_offset + req->optional_start, msgb, &optional_data) != 0) {
|
||||
DEBUGP(DSCCP, "parsing of optional data failed.\n");
|
||||
talloc_free(connection);
|
||||
return -1;
|
||||
}
|
||||
connection->destination_local_reference = *result.source_local_reference;
|
||||
|
||||
if (cb->accept_cb(connection, cb->accept_context) != 0) {
|
||||
_sccp_send_refuse(req, SCCP_REFUSAL_END_USER_ORIGINATED);
|
||||
_sccp_send_refuse(result.source_local_reference, SCCP_REFUSAL_END_USER_ORIGINATED);
|
||||
_sccp_set_connection_state(connection, SCCP_CONNECTION_STATE_REFUSED);
|
||||
talloc_free(connection);
|
||||
return 0;
|
||||
@@ -684,7 +736,7 @@ static int _sccp_handle_connection_request(struct msgb *msgb)
|
||||
if (_sccp_send_connection_confirm(connection) != 0) {
|
||||
DEBUGP(DSCCP, "Sending confirm failed... no available source reference?\n");
|
||||
|
||||
_sccp_send_refuse(req, SCCP_REFUSAL_SCCP_FAILURE);
|
||||
_sccp_send_refuse(result.source_local_reference, SCCP_REFUSAL_SCCP_FAILURE);
|
||||
_sccp_set_connection_state(connection, SCCP_CONNECTION_STATE_REFUSED);
|
||||
llist_del(&connection->list);
|
||||
talloc_free(connection);
|
||||
@@ -695,9 +747,8 @@ static int _sccp_handle_connection_request(struct msgb *msgb)
|
||||
/*
|
||||
* If we have data let us forward things.
|
||||
*/
|
||||
if (optional_data.data_len != 0 && connection->data_cb) {
|
||||
msgb->l3h = &msgb->l2h[optional_data.data_start];
|
||||
connection->data_cb(connection, msgb, optional_data.data_len);
|
||||
if (result.data_len != 0 && connection->data_cb) {
|
||||
connection->data_cb(connection, msgb, result.data_len);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -1160,6 +1211,14 @@ struct sccp_source_reference sccp_src_ref_from_int(u_int32_t int_ref)
|
||||
return ref;
|
||||
}
|
||||
|
||||
int sccp_determine_msg_type(struct msgb *msg)
|
||||
{
|
||||
if (msgb_l2len(msg) < 1)
|
||||
return -1;
|
||||
|
||||
return msg->l2h[0];
|
||||
}
|
||||
|
||||
static __attribute__((constructor)) void on_dso_load(void)
|
||||
{
|
||||
tall_sccp_ctx = talloc_named_const(NULL, 1, "sccp");
|
||||
|
@@ -319,6 +319,8 @@ static int generate_si5(u_int8_t *output, struct gsm_bts *bts)
|
||||
struct gsm48_system_information_type_5 *si5;
|
||||
int rc, l2_plen = 18;
|
||||
|
||||
memset(output, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN);
|
||||
|
||||
/* ip.access nanoBTS needs l2_plen!! */
|
||||
if (is_ipaccess_bts(bts)) {
|
||||
*output++ = (l2_plen << 2) | 1;
|
||||
@@ -326,7 +328,6 @@ static int generate_si5(u_int8_t *output, struct gsm_bts *bts)
|
||||
}
|
||||
|
||||
si5 = (struct gsm48_system_information_type_5 *) output;
|
||||
memset(si5, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN);
|
||||
|
||||
/* l2 pseudo length, not part of msg: 18 */
|
||||
si5->rr_protocol_discriminator = GSM48_PDISC_RR;
|
||||
@@ -345,6 +346,8 @@ static int generate_si6(u_int8_t *output, struct gsm_bts *bts)
|
||||
struct gsm48_system_information_type_6 *si6;
|
||||
int l2_plen = 11;
|
||||
|
||||
memset(output, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN);
|
||||
|
||||
/* ip.access nanoBTS needs l2_plen!! */
|
||||
if (is_ipaccess_bts(bts)) {
|
||||
*output++ = (l2_plen << 2) | 1;
|
||||
@@ -352,7 +355,6 @@ static int generate_si6(u_int8_t *output, struct gsm_bts *bts)
|
||||
}
|
||||
|
||||
si6 = (struct gsm48_system_information_type_6 *) output;
|
||||
memset(si6, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN);
|
||||
|
||||
/* l2 pseudo length, not part of msg: 11 */
|
||||
si6->rr_protocol_discriminator = GSM48_PDISC_RR;
|
||||
|
@@ -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);
|
||||
|
||||
|
@@ -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);
|
||||
@@ -97,6 +99,11 @@ static void net_dump_vty(struct vty *vty, struct gsm_network *net)
|
||||
VTY_NEWLINE);
|
||||
vty_out(vty, " Handover: %s%s", net->handover.active ? "On" : "Off",
|
||||
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",
|
||||
@@ -287,7 +294,11 @@ static int config_write_net(struct vty *vty)
|
||||
{
|
||||
vty_out(vty, "network%s", VTY_NEWLINE);
|
||||
vty_out(vty, " network country code %u%s", gsmnet->country_code, VTY_NEWLINE);
|
||||
if (gsmnet->core_country_code > 0)
|
||||
vty_out(vty, " core network country code %u%s", gsmnet->core_country_code, VTY_NEWLINE);
|
||||
vty_out(vty, " mobile network code %u%s", gsmnet->network_code, VTY_NEWLINE);
|
||||
if (gsmnet->core_network_code > 0)
|
||||
vty_out(vty, " core mobile network code %u%s", gsmnet->core_network_code, VTY_NEWLINE);
|
||||
vty_out(vty, " short name %s%s", gsmnet->name_short, VTY_NEWLINE);
|
||||
vty_out(vty, " long name %s%s", gsmnet->name_long, VTY_NEWLINE);
|
||||
vty_out(vty, " auth policy %s%s", gsm_auth_policy_name(gsmnet->auth_policy), VTY_NEWLINE);
|
||||
@@ -322,6 +333,25 @@ static int config_write_net(struct vty *vty)
|
||||
vty_out(vty, " timer t3117 %u%s", gsmnet->T3117, VTY_NEWLINE);
|
||||
vty_out(vty, " timer t3119 %u%s", gsmnet->T3119, VTY_NEWLINE);
|
||||
vty_out(vty, " timer t3141 %u%s", gsmnet->T3141, VTY_NEWLINE);
|
||||
vty_out(vty, " ipacc rtp_payload %u%s", gsmnet->rtp_payload, VTY_NEWLINE);
|
||||
vty_out(vty, " rtp base %u%s", gsmnet->rtp_base_port, 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;
|
||||
}
|
||||
@@ -818,6 +848,16 @@ DEFUN(cfg_net_ncc,
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_core_net_ncc,
|
||||
cfg_core_net_ncc_cmd,
|
||||
"core network country code <1-999>",
|
||||
"Set the GSM country code to be used in the MSC connection")
|
||||
{
|
||||
gsmnet->core_country_code = atoi(argv[0]);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_net_mnc,
|
||||
cfg_net_mnc_cmd,
|
||||
"mobile network code <1-999>",
|
||||
@@ -828,6 +868,16 @@ DEFUN(cfg_net_mnc,
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_core_net_mnc,
|
||||
cfg_core_net_mnc_cmd,
|
||||
"core mobile network code <1-999>",
|
||||
"Set the GSM mobile network code to be used in the MSC connection")
|
||||
{
|
||||
gsmnet->core_network_code = atoi(argv[0]);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_net_name_short,
|
||||
cfg_net_name_short_cmd,
|
||||
"short name NAME",
|
||||
@@ -976,6 +1026,90 @@ DEFUN(cfg_net_ho_max_distance, cfg_net_ho_max_distance_cmd,
|
||||
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;
|
||||
}
|
||||
|
||||
#define DECLARE_TIMER(number) \
|
||||
DEFUN(cfg_net_T##number, \
|
||||
cfg_net_T##number##_cmd, \
|
||||
@@ -986,7 +1120,7 @@ DEFUN(cfg_net_ho_max_distance, cfg_net_ho_max_distance_cmd,
|
||||
\
|
||||
if (value < 0 || value > 65535) { \
|
||||
vty_out(vty, "Timer value %s out of range.%s", \
|
||||
argv[0], VTY_NEWLINE); \
|
||||
argv[0], VTY_NEWLINE); \
|
||||
return CMD_WARNING; \
|
||||
} \
|
||||
\
|
||||
@@ -1006,7 +1140,6 @@ DECLARE_TIMER(3117)
|
||||
DECLARE_TIMER(3119)
|
||||
DECLARE_TIMER(3141)
|
||||
|
||||
|
||||
/* per-BTS configuration */
|
||||
DEFUN(cfg_bts,
|
||||
cfg_bts_cmd,
|
||||
@@ -1476,7 +1609,9 @@ int bsc_vty_init(struct gsm_network *net)
|
||||
install_node(&net_node, config_write_net);
|
||||
install_default(GSMNET_NODE);
|
||||
install_element(GSMNET_NODE, &cfg_net_ncc_cmd);
|
||||
install_element(GSMNET_NODE, &cfg_core_net_ncc_cmd);
|
||||
install_element(GSMNET_NODE, &cfg_net_mnc_cmd);
|
||||
install_element(GSMNET_NODE, &cfg_core_net_mnc_cmd);
|
||||
install_element(GSMNET_NODE, &cfg_net_name_short_cmd);
|
||||
install_element(GSMNET_NODE, &cfg_net_name_long_cmd);
|
||||
install_element(GSMNET_NODE, &cfg_net_auth_policy_cmd);
|
||||
@@ -1492,6 +1627,9 @@ int bsc_vty_init(struct gsm_network *net)
|
||||
install_element(GSMNET_NODE, &cfg_net_ho_pwr_interval_cmd);
|
||||
install_element(GSMNET_NODE, &cfg_net_ho_pwr_hysteresis_cmd);
|
||||
install_element(GSMNET_NODE, &cfg_net_ho_max_distance_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_net_T3101_cmd);
|
||||
install_element(GSMNET_NODE, &cfg_net_T3103_cmd);
|
||||
install_element(GSMNET_NODE, &cfg_net_T3105_cmd);
|
||||
|
48
openbsc/src/vty_interface_bsc.c
Normal file
48
openbsc/src/vty_interface_bsc.c
Normal file
@@ -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;
|
||||
}
|
@@ -76,4 +76,5 @@ int main(int argc, char** argv)
|
||||
void nm_state_event() {}
|
||||
void input_event() {}
|
||||
void sms_alloc() {}
|
||||
void _lchan_release() {}
|
||||
|
||||
|
Reference in New Issue
Block a user