aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHarald Welte <laforge@gnumonks.org>2009-11-27 09:19:21 +0100
committerHarald Welte <laforge@gnumonks.org>2009-11-27 09:19:21 +0100
commit39a4f047c89ceb111c1756596c712a57048154e3 (patch)
tree9c237408c22b7635903884af2456f9eec5cdcc4a
parent260b7ddb239d0d497ee7bb7c8c93d52a1c0fe88c (diff)
parent1a79d364401dfad2a71f1e61ff13a3861e3da46e (diff)
Merge remote branch 'origin/master' into the lcr_rtp brancheversberg/lcr_rtp
Conflicts: openbsc/src/Makefile.am
-rw-r--r--openbsc/configure.in5
-rwxr-xr-xopenbsc/contrib/bt.py33
-rwxr-xr-xopenbsc/contrib/convert_to_enum.py37
-rwxr-xr-xopenbsc/contrib/mgcp_server.py54
-rw-r--r--openbsc/doc/gsm-hopping.txt54
-rw-r--r--openbsc/doc/oml-interface.txt21
-rw-r--r--openbsc/include/Makefile.am2
-rw-r--r--openbsc/include/openbsc/Makefile.am3
-rw-r--r--openbsc/include/openbsc/abis_nm.h60
-rw-r--r--openbsc/include/openbsc/abis_rsl.h29
-rw-r--r--openbsc/include/openbsc/debug.h7
-rw-r--r--openbsc/include/openbsc/gsm_04_08.h49
-rw-r--r--openbsc/include/openbsc/gsm_04_11.h3
-rw-r--r--openbsc/include/openbsc/gsm_04_80.h143
-rw-r--r--openbsc/include/openbsc/gsm_data.h63
-rw-r--r--openbsc/include/openbsc/gsm_subscriber.h7
-rw-r--r--openbsc/include/openbsc/gsm_utils.h2
-rw-r--r--openbsc/include/openbsc/ipaccess.h5
-rw-r--r--openbsc/include/openbsc/mgcp.h48
-rw-r--r--openbsc/include/openbsc/msgb.h1
-rw-r--r--openbsc/include/openbsc/signal.h18
-rw-r--r--openbsc/include/openbsc/silent_call.h7
-rw-r--r--openbsc/include/openbsc/telnet_interface.h7
-rw-r--r--openbsc/include/openbsc/timer.h7
-rw-r--r--openbsc/include/openbsc/tlv.h49
-rw-r--r--openbsc/include/openbsc/ussd.h10
-rw-r--r--openbsc/include/sccp/Makefile.am1
-rw-r--r--openbsc/include/sccp/sccp.h146
-rw-r--r--openbsc/include/sccp/sccp_types.h394
-rw-r--r--openbsc/include/vty/command.h1
-rw-r--r--openbsc/src/Makefile.am12
-rwxr-xr-xopenbsc/src/abis_nm.c264
-rw-r--r--openbsc/src/abis_rsl.c104
-rw-r--r--openbsc/src/bsc_init.c170
-rw-r--r--openbsc/src/bsc_mgcp.c1172
-rw-r--r--openbsc/src/chan_alloc.c10
-rw-r--r--openbsc/src/db.c10
-rw-r--r--openbsc/src/debug.c3
-rw-r--r--openbsc/src/e1_config.c2
-rw-r--r--openbsc/src/gsm_04_08.c81
-rw-r--r--openbsc/src/gsm_04_08_utils.c200
-rw-r--r--openbsc/src/gsm_04_11.c226
-rw-r--r--openbsc/src/gsm_04_80.c330
-rw-r--r--openbsc/src/gsm_data.c33
-rw-r--r--openbsc/src/gsm_subscriber_base.c3
-rw-r--r--openbsc/src/gsm_utils.c19
-rw-r--r--openbsc/src/input/ipaccess.c53
-rw-r--r--openbsc/src/input/misdn.c2
-rw-r--r--openbsc/src/ipaccess-config.c57
-rw-r--r--openbsc/src/mgcp.cfg19
-rw-r--r--openbsc/src/msgb.c20
-rw-r--r--openbsc/src/openbsc.cfg.1-12
-rw-r--r--openbsc/src/openbsc.cfg.1-22
-rw-r--r--openbsc/src/openbsc.cfg.2-22
-rw-r--r--openbsc/src/openbsc.cfg.nanobts40
-rw-r--r--openbsc/src/paging.c16
-rw-r--r--openbsc/src/rrlp.c1
-rw-r--r--openbsc/src/sccp/sccp.c1171
-rw-r--r--openbsc/src/silent_call.c93
-rw-r--r--openbsc/src/talloc.c9
-rw-r--r--openbsc/src/telnet_interface.c1
-rw-r--r--openbsc/src/timer.c2
-rw-r--r--openbsc/src/tlv_parser.c168
-rw-r--r--openbsc/src/token_auth.c3
-rw-r--r--openbsc/src/transaction.c2
-rw-r--r--openbsc/src/ussd.c71
-rw-r--r--openbsc/src/vty_interface.c135
-rw-r--r--openbsc/src/vty_interface_layer3.c128
-rw-r--r--openbsc/tests/Makefile.am2
-rw-r--r--openbsc/tests/sccp/Makefile.am8
-rw-r--r--openbsc/tests/sccp/sccp_test.c724
-rw-r--r--openbsc/tests/sms/sms_test.c1
-rw-r--r--wireshark/abis_oml.patch24
-rw-r--r--wireshark/abisip.patch313
-rw-r--r--wireshark/gsm_a_rr-rrlp.patch179
-rw-r--r--wireshark/rsl-ipaccess.patch467
-rw-r--r--wireshark/rsl-system_info.patch13
77 files changed, 6345 insertions, 1288 deletions
diff --git a/openbsc/configure.in b/openbsc/configure.in
index 94fb15f4d..cba6c6cd9 100644
--- a/openbsc/configure.in
+++ b/openbsc/configure.in
@@ -3,6 +3,9 @@ AC_INIT
AM_INIT_AUTOMAKE(openbsc, 0.0alpha1)
+dnl kernel style compile messages
+m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
+
dnl checks for programs
AC_PROG_MAKE_SET
AC_PROG_CC
@@ -37,6 +40,7 @@ AC_OUTPUT(
openbsc.pc
include/openbsc/Makefile
include/vty/Makefile
+ include/sccp/Makefile
include/Makefile
src/Makefile
tests/Makefile
@@ -46,4 +50,5 @@ AC_OUTPUT(
tests/gsm0408/Makefile
tests/db/Makefile
tests/channel/Makefile
+ tests/sccp/Makefile
Makefile)
diff --git a/openbsc/contrib/bt.py b/openbsc/contrib/bt.py
new file mode 100755
index 000000000..1b111efc8
--- /dev/null
+++ b/openbsc/contrib/bt.py
@@ -0,0 +1,33 @@
+#!/usr/bin/env python
+
+import os
+
+f = open("unbalanced")
+lines = []
+for line in f:
+ lines.append(line)
+
+filenames = {}
+
+output = []
+for line in lines:
+ if "[0x" in line:
+ start = line.find("[")
+ end = line.find("]")
+ addr = line[start+1:end]
+ try:
+ file = filenames[addr]
+ except KeyError:
+ r = os.popen("addr2line -fs -e ./bsc_hack %s" % addr)
+ all = r.read().replace("\n", ",")
+ file = all
+ filenames[addr] = file
+
+ line = line.replace(addr, file)
+ output.append(line)
+
+g = open("unbalanced.2", "w")
+g.write("".join(output))
+
+
+
diff --git a/openbsc/contrib/convert_to_enum.py b/openbsc/contrib/convert_to_enum.py
new file mode 100755
index 000000000..bcd6f2cee
--- /dev/null
+++ b/openbsc/contrib/convert_to_enum.py
@@ -0,0 +1,37 @@
+#!/usr/bin/env python
+
+#
+# Convert ETSI documents to an enum
+#
+
+import re, sys
+
+def convert(string):
+ string = string.strip().replace(" ", "").rjust(8, "0")
+ var = 0
+ offset = 7
+ for char in string:
+ assert offset >= 0
+ var = var | (int(char) << offset)
+ offset = offset - 1
+
+ return var
+
+def string(name):
+ name = name.replace(" ", "_")
+ name = name.replace('"', "")
+ name = name.replace('/', '_')
+ name = name.replace('(', '_')
+ name = name.replace(')', '_')
+ return "%s_%s" % (sys.argv[2], name.upper())
+
+file = open(sys.argv[1])
+
+
+for line in file:
+ m = re.match(r"[ \t]*(?P<value>[01 ]+)[ ]+(?P<name>[a-zA-Z /0-9()]+)", line[:-1])
+
+ if m:
+ print "\t%s\t\t= %d," % (string(m.groupdict()["name"]), convert(m.groupdict()["value"]))
+ else:
+ print line[:-1]
diff --git a/openbsc/contrib/mgcp_server.py b/openbsc/contrib/mgcp_server.py
new file mode 100755
index 000000000..cf3ef3845
--- /dev/null
+++ b/openbsc/contrib/mgcp_server.py
@@ -0,0 +1,54 @@
+#!/usr/bin/env python
+# Simple server for mgcp... send audit, receive response..
+
+import socket, time
+
+MGCP_GATEWAY_PORT = 2427
+MGCP_CALLAGENT_PORT = 2727
+
+rsip_resp = """200 321321332\r\n"""
+audit_packet = """AUEP %d 13@mgw MGCP 1.0\r\n"""
+crcx_packet = """CRCX %d 14@mgw MGCP 1.0\r\nC: 4a84ad5d25f\r\nL: p:20, a:GSM-EFR, nt:IN\r\nM: recvonly\r\n"""
+dlcx_packet = """DLCX %d 14@mgw MGCP 1.0\r\nC: 4a84ad5d25f\r\nI: %d\r\n"""
+mdcx_packet = """MDCX %d 14@mgw MGCP 1.0\r\nC: 4a84ad5d25f\r\nI: %d\r\nL: p:20, a:GSM-EFR, nt:IN\r\nM: recvonly\r\n\r\nv=0\r\no=- 258696477 0 IN IP4 172.16.1.107\r\ns=-\r\nc=IN IP4 172.16.1.107\r\nt=0 0\r\nm=audio 4400 RTP/AVP 127\r\na=rtpmap:127 GSM-EFR/8000/1\r\na=ptime:20\r\na=recvonly\r\nm=image 4402 udptl t38\r\na=T38FaxVersion:0\r\na=T38MaxBitRate:14400\r\n"""
+
+def hexdump(src, length=8):
+ """Recipe is from http://code.activestate.com/recipes/142812/"""
+ result = []
+ digits = 4 if isinstance(src, unicode) else 2
+ for i in xrange(0, len(src), length):
+ s = src[i:i+length]
+ hexa = b' '.join(["%0*X" % (digits, ord(x)) for x in s])
+ text = b''.join([x if 0x20 <= ord(x) < 0x7F else b'.' for x in s])
+ result.append( b"%04X %-*s %s" % (i, length*(digits + 1), hexa, text) )
+ return b'\n'.join(result)
+
+server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+server_socket.bind(("127.0.0.1", MGCP_CALLAGENT_PORT))
+server_socket.setblocking(0)
+
+
+def send_receive(packet):
+ server_socket.sendto(packet, ("127.0.0.1", MGCP_GATEWAY_PORT))
+ try:
+ data, addr = server_socket.recvfrom(4096)
+ print hexdump(data), addr
+ except socket.error:
+ pass
+
+def generate_tid():
+ import random
+ return random.randint(0, 65123)
+
+
+
+i = 1
+while True:
+ send_receive(rsip_resp)
+ send_receive(audit_packet)
+ send_receive(crcx_packet % generate_tid() )
+ send_receive(mdcx_packet % (generate_tid(), i))
+ send_receive(dlcx_packet % (generate_tid(), i))
+ i = i + 1
+
+ time.sleep(3)
diff --git a/openbsc/doc/gsm-hopping.txt b/openbsc/doc/gsm-hopping.txt
new file mode 100644
index 000000000..591093999
--- /dev/null
+++ b/openbsc/doc/gsm-hopping.txt
@@ -0,0 +1,54 @@
+according to GSM 05.02:
+
+general parameters from CCCH:
+* CA cell allocation of ARFCN's (System Information / BCCH)
+* FN: TDMA frame number (t1,t2,t3') in SCH
+
+specific parameters from channel assignment:
+* MA: mobile allocation, defines set of ARFCN's, up to 64
+* MAIO: index
+* HSN: hopping sequence generator number (0..64)
+
+
+hopping sequence generation (6.2.3):
+
+u_int8_t rntable[114] = {
+ 48, 98, 63, 1, 36, 95, 78, 102, 94, 73,
+ 0, 64, 25, 81, 76, 59, 124, 23, 104, 100,
+ 101, 47, 118, 85, 18, 56, 96, 86, 54, 2,
+ 80, 34, 127, 13, 6, 89, 57, 103, 12, 74,
+ 55, 111, 75, 38, 109, 71, 112, 29, 11, 88,
+ 87, 19, 3, 68, 110, 26, 33, 31, 8, 45,
+ 82, 58, 40, 107, 32, 5, 106, 92, 62, 67,
+ 77, 108, 122, 37, 60, 66, 121, 42, 51, 126,
+ 117, 114, 4, 90, 43, 52, 53, 113, 120, 72,
+ 16, 49, 7, 79, 119, 61, 22, 84, 9, 97,
+ 125, 99, 17, 123
+};
+
+/* mai=0 represents lowest ARFCN in the MA */
+
+
+u_int8_t hopping_mai(u_int8_t hsn, u_int32_t fn, u_int8_t maio,
+ u_int8_t t1, u_int8_t t2, u_int8_t t3_)
+{
+ u_int8_t mai;
+
+ if (hsn == 0) /* cyclic hopping */
+ mai = (fn + maio) % n;
+ else {
+ u_int32_t m, m_, t_, s;
+
+ m = t2 + rntable[(hsn xor (t1 % 64)) + t3];
+ m_ = m % (2^NBIN);
+ t_ = t3 % (2^NBIN);
+ if (m_ < n then)
+ s = m_;
+ else
+ s = (m_ + t_) % n;
+ mai = (s + maio) % n;
+ }
+
+ return mai;
+}
+
diff --git a/openbsc/doc/oml-interface.txt b/openbsc/doc/oml-interface.txt
new file mode 100644
index 000000000..8ddcfea5c
--- /dev/null
+++ b/openbsc/doc/oml-interface.txt
@@ -0,0 +1,21 @@
+oml interface design notes
+
+problems:
+
+* there is no way how to tag a command sent to the BTS, with the response
+ having the same tag to identify the originator of the command
+* therefore, we can have e.g. both the BSC and the OML interface send a
+ SET ATTRIBUTE message, where the responses would end up at the wrong
+ query.
+
+the only possible solutions i can imagine:
+* have some kind of exclusive locking, where the OML interface gets blocked
+ from the BSC and is exclusively assigned to the OML console until all commands
+ of the OML console have terminated. This can either be done explicitly
+ dynamically or on demand
+
+* use the OML interface synchronously, i.e. always wait for the response from
+ the BTS before
+
+* unilateral / unsolicited messages need to be broadcasted to both the BSC and
+ the OML console
diff --git a/openbsc/include/Makefile.am b/openbsc/include/Makefile.am
index a95129fa9..56b2a338a 100644
--- a/openbsc/include/Makefile.am
+++ b/openbsc/include/Makefile.am
@@ -1,3 +1,3 @@
-SUBDIRS = openbsc vty
+SUBDIRS = openbsc vty sccp
noinst_HEADERS = mISDNif.h compat_af_isdn.h
diff --git a/openbsc/include/openbsc/Makefile.am b/openbsc/include/openbsc/Makefile.am
index 8d454bb00..8bb64eb91 100644
--- a/openbsc/include/openbsc/Makefile.am
+++ b/openbsc/include/openbsc/Makefile.am
@@ -3,4 +3,5 @@ noinst_HEADERS = abis_nm.h abis_rsl.h debug.h db.h gsm_04_08.h gsm_data.h \
timer.h misdn.h chan_alloc.h telnet_interface.h paging.h \
subchan_demux.h trau_frame.h e1_input.h trau_mux.h signal.h \
gsm_utils.h ipaccess.h rs232.h openbscdefines.h rtp_proxy.h \
- bsc_rll.h mncc.h talloc.h transaction.h
+ bsc_rll.h mncc.h talloc.h transaction.h ussd.h gsm_04_80.h \
+ silent_call.h mgcp.h
diff --git a/openbsc/include/openbsc/abis_nm.h b/openbsc/include/openbsc/abis_nm.h
index 3e72321c5..00697a384 100644
--- a/openbsc/include/openbsc/abis_nm.h
+++ b/openbsc/include/openbsc/abis_nm.h
@@ -482,6 +482,7 @@ enum abis_nm_avail_state {
NM_AVSTATE_DEPENDENCY = 5,
NM_AVSTATE_DEGRADED = 6,
NM_AVSTATE_NOT_INSTALLED= 7,
+ NM_AVSTATE_OK = 0xff,
};
/* Section 9.4.13: Channel Combination */
@@ -659,6 +660,58 @@ enum abis_nm_ipacc_testres_ie {
NM_IPACC_TR_IE_FREQ_ERR = 18,
};
+enum ipac_eie {
+ NM_IPAC_EIE_ARFCN_WHITE = 0x01,
+ NM_IPAC_EIE_ARFCH_BLACK = 0x02,
+ NM_IPAC_EIE_FREQ_ERR_LIST = 0x03,
+ NM_IPAC_EIE_CHAN_USE_LIST = 0x04,
+ NM_IPAC_EIE_BCCH_INFO_TYPE = 0x05,
+ NM_IPAC_EIE_BCCH_INFO = 0x06,
+ /* FIXME */
+};
+
+enum ipac_bcch_info_type {
+ IPAC_BINF_RXLEV = (1 << 8),
+ IPAC_BINF_RXQUAL = (1 << 9),
+ IPAC_BINF_FREQ_ERR_QUAL = (1 << 10),
+ IPAC_BINF_FRAME_OFFSET = (1 << 11),
+ IPAC_BINF_FRAME_NR_OFFSET = (1 << 12),
+ IPAC_BINF_BSIC = (1 << 13),
+ IPAC_BINF_CGI = (1 << 14),
+ IPAC_BINF_NEIGH_BA_SI2 = (1 << 15),
+ IPAC_BINF_NEIGH_BA_SI2bis = (1 << 0),
+ IPAC_BINF_NEIGH_BA_SI2ter = (1 << 1),
+ IPAC_BINF_CELL_ALLOC = (1 << 2),
+};
+
+struct cell_global_id {
+ u_int16_t mcc;
+ u_int16_t mnc;
+ u_int16_t lac;
+ u_int16_t ci;
+};
+
+/* The BCCH info from an ip.access test, in host byte order
+ * and already parsed... */
+struct ipac_bcch_info {
+ struct llist_head list;
+
+ u_int16_t info_type;
+ u_int8_t freq_qual;
+ u_int16_t arfcn;
+ u_int8_t rx_lev;
+ u_int8_t rx_qual;
+ int16_t freq_err;
+ u_int16_t frame_offset;
+ u_int32_t frame_nr_offset;
+ u_int8_t bsic;
+ struct cell_global_id cgi;
+ u_int8_t ba_list_si2[16];
+ u_int8_t ba_list_si2bis[16];
+ u_int8_t ba_list_si2ter[16];
+ u_int8_t ca_list_si1[16];
+};
+
/* PUBLIC */
struct msgb;
@@ -748,6 +801,13 @@ int abis_nm_ipaccess_msg(struct gsm_bts *bts, u_int8_t msg_type,
int abis_nm_ipaccess_set_nvattr(struct gsm_bts *bts, u_int8_t *attr,
int attr_len);
int abis_nm_ipaccess_restart(struct gsm_bts *bts);
+int abis_nm_ipaccess_set_attr(struct gsm_bts *bts, u_int8_t obj_class,
+ u_int8_t bts_nr, u_int8_t trx_nr, u_int8_t ts_nr,
+ u_int8_t *attr, u_int8_t attr_len);
+int abis_nm_ipaccess_rsl_connect(struct gsm_bts_trx *trx,
+ u_int32_t ip, u_int16_t port, u_int8_t stream);
+int ipac_parse_bcch_info(struct ipac_bcch_info *binf, u_int8_t *buf);
+const char *ipacc_testres_name(u_int8_t res);
/* Functions calling into other code parts */
enum nm_evt {
diff --git a/openbsc/include/openbsc/abis_rsl.h b/openbsc/include/openbsc/abis_rsl.h
index 8c5547d1d..a911be355 100644
--- a/openbsc/include/openbsc/abis_rsl.h
+++ b/openbsc/include/openbsc/abis_rsl.h
@@ -142,16 +142,16 @@ enum abis_rsl_msgtype {
RSL_MT_IPAC_DISC_MUX = 0x56,
RSL_MT_IPAC_DISC_MUX_ACK,
RSL_MT_IPAC_DISC_MUX_NACK,
- RSL_MT_IPAC_BIND = 0x70, /* Bind to local BTS RTP port */
- RSL_MT_IPAC_BIND_ACK,
- RSL_MT_IPAC_BIND_NACK,
- RSL_MT_IPAC_CONNECT = 0x73,
- RSL_MT_IPAC_CONNECT_ACK,
- RSL_MT_IPAC_CONNECT_NACK,
- RSL_MT_IPAC_DISCONNECT_IND = 0x76,
- RSL_MT_IPAC_DISCONNECT = 0x77,
- RSL_MT_IPAC_DISCONNECT_ACK,
- RSL_MT_IPAC_DISCONNECT_NACK,
+ RSL_MT_IPAC_CRCX = 0x70, /* Bind to local BTS RTP port */
+ RSL_MT_IPAC_CRCX_ACK,
+ RSL_MT_IPAC_CRCX_NACK,
+ RSL_MT_IPAC_MDCX = 0x73,
+ RSL_MT_IPAC_MDCX_ACK,
+ RSL_MT_IPAC_MDCX_NACK,
+ RSL_MT_IPAC_DLCX_IND = 0x76,
+ RSL_MT_IPAC_DLCX = 0x77,
+ RSL_MT_IPAC_DLCX_ACK,
+ RSL_MT_IPAC_DLCX_NACK,
};
/* Siemens vendor-specific */
@@ -535,10 +535,11 @@ enum rsl_mrpci_phase {
int rsl_siemens_mrpci(struct gsm_lchan *lchan, struct rsl_mrpci *mrpci);
/* ip.access specfic RSL extensions */
-int rsl_ipacc_bind(struct gsm_lchan *lchan);
-int rsl_ipacc_connect(struct gsm_lchan *lchan, u_int32_t ip,
- u_int16_t port, u_int16_t conn_id,
- u_int8_t rtp_payload2);
+int rsl_ipacc_crcx(struct gsm_lchan *lchan);
+int rsl_ipacc_mdcx(struct gsm_lchan *lchan, u_int32_t ip,
+ u_int16_t port, u_int16_t conn_id,
+ u_int8_t rtp_payload2);
+int rsl_ipacc_pdch_activate(struct gsm_lchan *lchan);
int abis_rsl_rcvmsg(struct msgb *msg);
diff --git a/openbsc/include/openbsc/debug.h b/openbsc/include/openbsc/debug.h
index 089132ea9..447c3584f 100644
--- a/openbsc/include/openbsc/debug.h
+++ b/openbsc/include/openbsc/debug.h
@@ -20,6 +20,11 @@
#define DMUX 0x4000
#define DINP 0x8000
+#define DSCCP 0x10000
+#define DMSC 0x20000
+
+#define DMGCP 0x40000
+
#ifdef DEBUG
#define DEBUGP(ss, fmt, args...) debugp(ss, __FILE__, __LINE__, 0, fmt, ## args)
#define DEBUGPC(ss, fmt, args...) debugp(ss, __FILE__, __LINE__, 1, fmt, ## args)
@@ -31,7 +36,7 @@
#define static_assert(exp, name) typedef int dummy##name [(exp) ? 1 : -1];
char *hexdump(const unsigned char *buf, int len);
-void debugp(unsigned int subsys, char *file, int line, int cont, const char *format, ...);
+void debugp(unsigned int subsys, char *file, int line, int cont, const char *format, ...) __attribute__ ((format (printf, 5, 6)));
void debug_parse_category_mask(const char* mask);
void debug_use_color(int use_color);
void debug_timestamp(int enable);
diff --git a/openbsc/include/openbsc/gsm_04_08.h b/openbsc/include/openbsc/gsm_04_08.h
index 485e240cd..b7c8a2662 100644
--- a/openbsc/include/openbsc/gsm_04_08.h
+++ b/openbsc/include/openbsc/gsm_04_08.h
@@ -33,6 +33,23 @@ struct gsm48_chan_desc {
};
} __attribute__ ((packed));
+/* Chapter 10.5.2.21aa */
+struct gsm48_multi_rate_conf {
+ u_int8_t smod : 2,
+ spare: 1,
+ icmi : 1,
+ nscb : 1,
+ ver : 3;
+ u_int8_t m4_75 : 1,
+ m5_15 : 1,
+ m5_90 : 1,
+ m6_70 : 1,
+ m7_40 : 1,
+ m7_95 : 1,
+ m10_2 : 1,
+ m12_2 : 1;
+} __attribute__((packed));
+
/* Chapter 10.5.2.30 */
struct gsm48_req_ref {
u_int8_t ra;
@@ -42,7 +59,11 @@ struct gsm48_req_ref {
t3_low:3;
} __attribute__ ((packed));
-/* Chapter 9.1.5 */
+/*
+ * Chapter 9.1.5/9.1.6
+ *
+ * For 9.1.6 the chan_desc has the meaning of 10.5.2.5a
+ */
struct gsm48_chan_mode_modify {
struct gsm48_chan_desc chan_desc;
u_int8_t mode;
@@ -59,6 +80,15 @@ enum gsm48_chan_mode {
GSM48_CMODE_DATA_3k6 = 0x23,
};
+/* Chapter 9.1.2 */
+struct gsm48_ass_cmd {
+ /* Semantic is from 10.5.2.5a */
+ struct gsm48_chan_desc chan_desc;
+ u_int8_t power_command;
+ u_int8_t data[0];
+} __attribute__((packed));
+
+
/* Chapter 9.1.18 */
struct gsm48_imm_ass {
u_int8_t l2_plen;
@@ -398,6 +428,7 @@ struct gsm48_imsi_detach_ind {
#define GSM_MI_TYPE_TMSI 0x04
#define GSM_MI_ODD 0x08
+#define GSM48_IE_MUL_RATE_CFG 0x03 /* 10.5.2.21aa */
#define GSM48_IE_MOBILE_ID 0x17
#define GSM48_IE_NAME_LONG 0x43 /* 10.5.3.5a */
#define GSM48_IE_NAME_SHORT 0x45 /* 10.5.3.5a */
@@ -621,9 +652,13 @@ enum chreq_type {
CHREQ_T_VOICE_CALL_TCH_H,
CHREQ_T_DATA_CALL_TCH_H,
CHREQ_T_LOCATION_UPD,
- CHREQ_T_PAG_R_ANY,
+ CHREQ_T_PAG_R_ANY_NECI0,
+ CHREQ_T_PAG_R_ANY_NECI1,
CHREQ_T_PAG_R_TCH_F,
CHREQ_T_PAG_R_TCH_FH,
+ CHREQ_T_LMU,
+ CHREQ_T_RESERVED_SDCCH,
+ CHREQ_T_RESERVED_IGNORE,
};
/* Chapter 11.3 */
@@ -711,8 +746,8 @@ void gsm0408_set_reject_cause(int cause);
int gsm0408_rcvmsg(struct msgb *msg, u_int8_t link_id);
void gsm0408_generate_lai(struct gsm48_loc_area_id *lai48, u_int16_t mcc,
u_int16_t mnc, u_int16_t lac);
-enum gsm_chan_t get_ctype_by_chreq(struct gsm_bts *bts, u_int8_t ra);
-enum gsm_chreq_reason_t get_reason_by_chreq(struct gsm_bts *bts, u_int8_t ra);
+enum gsm_chan_t get_ctype_by_chreq(struct gsm_bts *bts, u_int8_t ra, int neci);
+enum gsm_chreq_reason_t get_reason_by_chreq(struct gsm_bts *bts, u_int8_t ra, int neci);
int gsm48_tx_mm_info(struct gsm_lchan *lchan);
int gsm48_tx_mm_auth_req(struct gsm_lchan *lchan, u_int8_t *rand);
@@ -726,7 +761,8 @@ int gsm48_mi_to_string(char *string, const int str_len, const u_int8_t *mi, cons
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, u_int8_t *apdu);
+ 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 bsc_upqueue(struct gsm_network *net);
@@ -744,4 +780,7 @@ int send_siemens_mrpci(struct gsm_lchan *lchan, u_int8_t *classmark2_lv);
int gsm48_paging_extract_mi(struct msgb *msg, char *mi_string, u_int8_t *mi_type);
int gsm48_handle_paging_resp(struct msgb *msg, struct gsm_subscriber *subscr);
+int gsm48_lchan_modify(struct gsm_lchan *lchan, u_int8_t lchan_mode);
+int gsm48_rx_rr_modif_ack(struct msgb *msg);
+
#endif
diff --git a/openbsc/include/openbsc/gsm_04_11.h b/openbsc/include/openbsc/gsm_04_11.h
index bfa90b522..1851bba53 100644
--- a/openbsc/include/openbsc/gsm_04_11.h
+++ b/openbsc/include/openbsc/gsm_04_11.h
@@ -210,4 +210,7 @@ int gsm411_send_sms_lchan(struct gsm_lchan *lchan, struct gsm_sms *sms);
struct gsm_sms *sms_alloc(void);
void sms_free(struct gsm_sms *sms);
+void _gsm411_sms_trans_free(struct gsm_trans *trans);
+int gsm411_send_sms_subscr(struct gsm_subscriber *subscr,
+ struct gsm_sms *sms);
#endif
diff --git a/openbsc/include/openbsc/gsm_04_80.h b/openbsc/include/openbsc/gsm_04_80.h
new file mode 100644
index 000000000..c240bbe94
--- /dev/null
+++ b/openbsc/include/openbsc/gsm_04_80.h
@@ -0,0 +1,143 @@
+#ifndef _GSM_04_80_H
+#define _GSM_04_80_H
+
+/* GSM TS 04.80 definitions (Supplementary Services Specification, Formats and Coding) */
+
+/* Section 3.4 */
+#define GSM0480_MTYPE_RELEASE_COMPLETE 0x2A
+#define GSM0480_MTYPE_FACILITY 0x3A
+#define GSM0480_MTYPE_REGISTER 0x3B
+
+/* Section 3.5 */
+#define GSM0480_IE_FACILITY 0x1C
+#define GSM0480_IE_SS_VERSION 0x7F
+
+/* Section 3.6.2 */
+#define GSM0480_CTYPE_INVOKE 0xA1
+#define GSM0480_CTYPE_RETURN_RESULT 0xA2
+#define GSM0480_CTYPE_RETURN_ERROR 0xA3
+#define GSM0480_CTYPE_REJECT 0xA4
+
+/* Section 3.6.3 */
+#define GSM0480_COMPIDTAG_INVOKE_ID 0x02
+#define GSM0480_COMPIDTAG_LINKED_ID 0x80
+
+/* Section 3.6.4 */
+#define GSM0480_OPERATION_CODE 0x02
+
+/* Section 3.6.5 */
+#define GSM_0480_SEQUENCE_TAG 0x30
+#define GSM_0480_SET_TAG 0x31
+
+/* Section 3.6.6 */
+#define GSM_0480_ERROR_CODE_TAG 0x02
+
+/* Section 3.6.7 */
+/* Table 3.13 */
+#define GSM_0480_PROBLEM_CODE_TAG_GENERAL 0x80
+#define GSM_0480_PROBLEM_CODE_TAG_INVOKE 0x81
+#define GSM_0480_PROBLEM_CODE_TAG_RETURN_RESULT 0x82
+#define GSM_0480_PROBLEM_CODE_TAG_RETURN_ERROR 0x83
+
+/* Table 3.14 */
+#define GSM_0480_GEN_PROB_CODE_UNRECOGNISED 0x00
+#define GSM_0480_GEN_PROB_CODE_MISTYPED 0x01
+#define GSM_0480_GEN_PROB_CODE_BAD_STRUCTURE 0x02
+
+/* Table 3.15 */
+#define GSM_0480_INVOKE_PROB_CODE_DUPLICATE_INVOKE_ID 0x00
+#define GSM_0480_INVOKE_PROB_CODE_UNRECOGNISED_OPERATION 0x01
+#define GSM_0480_INVOKE_PROB_CODE_MISTYPED_PARAMETER 0x02
+#define GSM_0480_INVOKE_PROB_CODE_RESOURCE_LIMITATION 0x03
+#define GSM_0480_INVOKE_PROB_CODE_INITIATING_RELEASE 0x04
+#define GSM_0480_INVOKE_PROB_CODE_UNRECOGNISED_LINKED_ID 0x05
+#define GSM_0480_INVOKE_PROB_CODE_UNEXPECTED_LINKED_RESPONSE 0x06
+#define GSM_0480_INVOKE_PROB_CODE_UNEXPECTED_LINKED_OPERATION 0x07
+
+/* Table 3.16 */
+#define GSM_0480_RESULT_PROB_CODE_UNRECOGNISED_INVOKE_ID 0x00
+#define GSM_0480_RESULT_PROB_CODE_RETURN_RESULT_UNEXPECTED 0x01
+#define GSM_0480_RESULT_PROB_CODE_MISTYPED_PARAMETER 0x02
+
+/* Table 3.17 */
+#define GSM_0480_ERROR_PROB_CODE_UNRECOGNISED_INVOKE_ID 0x00
+#define GSM_0480_ERROR_PROB_CODE_RETURN_ERROR_UNEXPECTED 0x01
+#define GSM_0480_ERROR_PROB_CODE_UNRECOGNISED_ERROR 0x02
+#define GSM_0480_ERROR_PROB_CODE_UNEXPECTED_ERROR 0x03
+#define GSM_0480_ERROR_PROB_CODE_MISTYPED_PARAMETER 0x04
+
+/* Section 4.5 */
+#define GSM0480_OP_CODE_REGISTER_SS 0x0A
+#define GSM0480_OP_CODE_ERASE_SS 0x0B
+#define GSM0480_OP_CODE_ACTIVATE_SS 0x0C
+#define GSM0480_OP_CODE_DEACTIVATE_SS 0x0D
+#define GSM0480_OP_CODE_INTERROGATE_SS 0x0E
+#define GSM0480_OP_CODE_NOTIFY_SS 0x10
+#define GSM0480_OP_CODE_REGISTER_PASSWORD 0x11
+#define GSM0480_OP_CODE_GET_PASSWORD 0x12
+#define GSM0480_OP_CODE_PROCESS_USS_DATA 0x13
+#define GSM0480_OP_CODE_FORWARD_CHECK_SS_IND 0x26
+#define GSM0480_OP_CODE_PROCESS_USS_REQ 0x3B
+#define GSM0480_OP_CODE_USS_REQUEST 0x3C
+#define GSM0480_OP_CODE_USS_NOTIFY 0x3D
+#define GSM0480_OP_CODE_FORWARD_CUG_INFO 0x78
+#define GSM0480_OP_CODE_SPLIT_MPTY 0x79
+#define GSM0480_OP_CODE_RETRIEVE_MPTY 0x7A
+#define GSM0480_OP_CODE_HOLD_MPTY 0x7B
+#define GSM0480_OP_CODE_BUILD_MPTY 0x7C
+#define GSM0480_OP_CODE_FORWARD_CHARGE_ADVICE 0x7D
+
+#define GSM0480_ERR_CODE_UNKNOWN_SUBSCRIBER 0x01
+#define GSM0480_ERR_CODE_ILLEGAL_SUBSCRIBER 0x09
+#define GSM0480_ERR_CODE_BEARER_SERVICE_NOT_PROVISIONED 0x0A
+#define GSM0480_ERR_CODE_TELESERVICE_NOT_PROVISIONED 0x0B
+#define GSM0480_ERR_CODE_ILLEGAL_EQUIPMENT 0x0C
+#define GSM0480_ERR_CODE_CALL_BARRED 0x0D
+#define GSM0480_ERR_CODE_ILLEGAL_SS_OPERATION 0x10
+#define GSM0480_ERR_CODE_SS_ERROR_STATUS 0x11
+#define GSM0480_ERR_CODE_SS_NOT_AVAILABLE 0x12
+#define GSM0480_ERR_CODE_SS_SUBSCRIPTION_VIOLATION 0x13
+#define GSM0480_ERR_CODE_SS_INCOMPATIBILITY 0x14
+#define GSM0480_ERR_CODE_FACILITY_NOT_SUPPORTED 0x15
+#define GSM0480_ERR_CODE_ABSENT_SUBSCRIBER 0x1B
+#define GSM0480_ERR_CODE_SYSTEM_FAILURE 0x22
+#define GSM0480_ERR_CODE_DATA_MISSING 0x23
+#define GSM0480_ERR_CODE_UNEXPECTED_DATA_VALUE 0x24
+#define GSM0480_ERR_CODE_PW_REGISTRATION_FAILURE 0x25
+#define GSM0480_ERR_CODE_NEGATIVE_PW_CHECK 0x26
+#define GSM0480_ERR_CODE_NUM_PW_ATTEMPTS_VIOLATION 0x2B
+#define GSM0480_ERR_CODE_UNKNOWN_ALPHABET 0x47
+#define GSM0480_ERR_CODE_USSD_BUSY 0x48
+#define GSM0480_ERR_CODE_MAX_MPTY_PARTICIPANTS 0x7E
+#define GSM0480_ERR_CODE_RESOURCES_NOT_AVAILABLE 0x7F
+
+/* ASN.1 type-tags */
+#define ASN1_BOOLEAN_TAG 0x01
+#define ASN1_INTEGER_TAG 0x02
+#define ASN1_BIT_STRING_TAG 0x03
+#define ASN1_OCTET_STRING_TAG 0x04
+#define ASN1_NULL_TYPE_TAG 0x05
+#define ASN1_OBJECT_ID_TAG 0x06
+#define ASN1_UTF8_STRING_TAG 0x0C
+#define ASN1_PRINTABLE_STRING_TAG 0x13
+#define ASN1_IA5_STRING_TAG 0x16
+#define ASN1_UNICODE_STRING_TAG 0x1E
+
+#include <openbsc/msgb.h>
+
+#define MAX_LEN_USSD_STRING 31
+
+struct ussd_request {
+ char text[MAX_LEN_USSD_STRING + 1];
+ u_int8_t transaction_id;
+ u_int8_t invoke_id;
+};
+
+int gsm0480_decode_ussd_request(const struct msgb *msg,
+ struct ussd_request *request);
+int gsm0480_send_ussd_response(const struct msgb *in_msg, const char* response_text,
+ const struct ussd_request *req);
+int gsm0480_send_ussd_reject(const struct msgb *msg,
+ const struct ussd_request *request);
+
+#endif
diff --git a/openbsc/include/openbsc/gsm_data.h b/openbsc/include/openbsc/gsm_data.h
index a493a69d9..638b03506 100644
--- a/openbsc/include/openbsc/gsm_data.h
+++ b/openbsc/include/openbsc/gsm_data.h
@@ -3,6 +3,13 @@
#include <sys/types.h>
+struct value_string {
+ unsigned int value;
+ const char *str;
+};
+
+const char *get_value_string(const struct value_string *vs, u_int32_t val);
+
enum gsm_band {
GSM_BAND_400,
GSM_BAND_850,
@@ -18,6 +25,8 @@ enum gsm_phys_chan_config {
GSM_PCHAN_TCH_F,
GSM_PCHAN_TCH_H,
GSM_PCHAN_SDCCH8_SACCH8C,
+ GSM_PCHAN_PDCH, /* GPRS PDCH */
+ GSM_PCHAN_TCH_F_PDCH, /* TCH/F if used, PDCH otherwise */
GSM_PCHAN_UNKNOWN,
};
@@ -122,13 +131,18 @@ struct gsm_nm_state {
*/
struct gsm_loc_updating_operation {
struct timer_list updating_timer;
- int waiting_for_imsi : 1;
- int waiting_for_imei : 1;
+ unsigned int waiting_for_imsi : 1;
+ unsigned int waiting_for_imei : 1;
};
#define MAX_A5_KEY_LEN (128/8)
#define RSL_ENC_ALG_A5(x) (x+1)
+/* is the data link established? who established it? */
+#define LCHAN_SAPI_UNUSED 0
+#define LCHAN_SAPI_MS 1
+#define LCHAN_SAPI_NET 2
+
struct gsm_lchan {
/* The TS that we're part of */
struct gsm_bts_trx_ts *ts;
@@ -149,6 +163,9 @@ struct gsm_lchan {
u_int8_t key_len;
u_int8_t key[MAX_A5_KEY_LEN];
} encr;
+
+ /* AMR bits */
+ struct gsm48_multi_rate_conf mr_conf;
/* To whom we are allocated at the moment */
struct gsm_subscriber *subscr;
@@ -158,6 +175,9 @@ struct gsm_lchan {
struct timer_list T3101;
+ /* Established data link layer services */
+ u_int8_t sapis[8];
+
/*
* Operations that have a state and might be pending
*/
@@ -237,6 +257,9 @@ struct gsm_bts_trx {
} bs11;
};
struct gsm_bts_trx_ts ts[TRX_NR_TS];
+
+ /* NM state */
+ int rf_locked;
};
enum gsm_bts_type {
@@ -266,7 +289,6 @@ struct gsm_paging_request {
gsm_cbfn *cbfn;
void *cbfn_param;
};
-#define T3113_VALUE 60, 0
/*
* This keeps track of the paging status of one BTS. It
@@ -289,6 +311,12 @@ struct gsm_envabtse {
struct gsm_nm_state nm_state;
};
+struct gsm_bts_gprs_nsvc {
+ struct gsm_bts *bts;
+ int id;
+ struct gsm_nm_state nm_state;
+};
+
/* One BTS */
struct gsm_bts {
/* list header in net->bts_list */
@@ -356,6 +384,17 @@ struct gsm_bts {
struct gsm_envabtse envabtse[4];
} bs11;
};
+
+ /* Not entirely sure how ip.access specific this is */
+ struct {
+ struct {
+ struct gsm_nm_state nm_state;
+ } nse;
+ struct {
+ struct gsm_nm_state nm_state;
+ } cell;
+ struct gsm_bts_gprs_nsvc nsvc[2];
+ } gprs;
/* transceivers */
int num_trx;
@@ -376,6 +415,7 @@ struct gsm_network {
char *name_short;
enum gsm_auth_policy auth_policy;
int a5_encryption;
+ int neci;
/* layer 4 */
int (*mncc_recv) (struct gsm_network *net, int msg_type, void *arg);
@@ -384,12 +424,25 @@ struct gsm_network {
unsigned int num_bts;
struct llist_head bts_list;
+
+ /* timer values */
+ int T3101;
+ int T3103;
+ int T3105;
+ int T3107;
+ int T3109;
+ int T3111;
+ int T3113;
+ int T3115;
+ int T3117;
+ int T3119;
+ int T3141;
};
#define SMS_HDR_SIZE 128
#define SMS_TEXT_SIZE 256
struct gsm_sms {
- u_int64_t id;
+ unsigned long long id;
struct gsm_subscriber *sender;
struct gsm_subscriber *receiver;
@@ -468,4 +521,6 @@ static inline int is_siemens_bts(struct gsm_bts *bts)
enum gsm_auth_policy gsm_auth_policy_parse(const char *arg);
const char *gsm_auth_policy_name(enum gsm_auth_policy policy);
+void gsm_trx_lock_rf(struct gsm_bts_trx *trx, int locked);
+
#endif
diff --git a/openbsc/include/openbsc/gsm_subscriber.h b/openbsc/include/openbsc/gsm_subscriber.h
index ea70c3aa2..d612ed566 100644
--- a/openbsc/include/openbsc/gsm_subscriber.h
+++ b/openbsc/include/openbsc/gsm_subscriber.h
@@ -8,13 +8,14 @@
#define GSM_IMEI_LENGTH 17
#define GSM_IMSI_LENGTH 17
#define GSM_NAME_LENGTH 128
-#define GSM_EXTENSION_LENGTH 128
+
+#define GSM_EXTENSION_LENGTH 15 /* MSISDN can only be 15 digits length */
+#define GSM_MIN_EXTEN 20000
+#define GSM_MAX_EXTEN 49999
/* reserved according to GSM 03.03 § 2.4 */
#define GSM_RESERVED_TMSI 0xFFFFFFFF
-#define GSM_MIN_EXTEN 20000
-#define GSM_MAX_EXTEN 49999
#define GSM_SUBSCRIBER_FIRST_CONTACT 0x00000001
#define tmsi_from_string(str) strtoul(str, NULL, 10)
diff --git a/openbsc/include/openbsc/gsm_utils.h b/openbsc/include/openbsc/gsm_utils.h
index 7cd0578ad..1bd1bc55a 100644
--- a/openbsc/include/openbsc/gsm_utils.h
+++ b/openbsc/include/openbsc/gsm_utils.h
@@ -32,4 +32,6 @@ int gsm_7bit_encode(u_int8_t *result, const char *data);
int ms_pwr_ctl_lvl(enum gsm_band band, unsigned int dbm);
int ms_pwr_dbm(enum gsm_band band, u_int8_t lvl);
+
+void generate_backtrace();
#endif
diff --git a/openbsc/include/openbsc/ipaccess.h b/openbsc/include/openbsc/ipaccess.h
index 87942f270..395687764 100644
--- a/openbsc/include/openbsc/ipaccess.h
+++ b/openbsc/include/openbsc/ipaccess.h
@@ -1,9 +1,10 @@
#ifndef _IPACCESS_H
#define _IPACCESS_H
+#include "e1_input.h"
+
struct ipaccess_head {
- u_int8_t zero;
- u_int8_t len;
+ u_int16_t len; /* network byte order */
u_int8_t proto;
u_int8_t data[0];
} __attribute__ ((packed));
diff --git a/openbsc/include/openbsc/mgcp.h b/openbsc/include/openbsc/mgcp.h
new file mode 100644
index 000000000..fa6224c12
--- /dev/null
+++ b/openbsc/include/openbsc/mgcp.h
@@ -0,0 +1,48 @@
+/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
+
+/*
+ * (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * (C) 2009 by on-waves.com
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+unsigned int rtp_base_port = 4000;
+
+/**
+ * Calculate the RTP audio port for the given multiplex
+ * and the direction. This allows a semi static endpoint
+ * to port calculation removing the need for the BSC
+ * and the MediaGateway to communicate.
+ *
+ * Port usage explained:
+ * base + (multiplex * 2) + 0 == local port to wait for network packets
+ * base + (multiplex * 2) + 1 == local port for rtcp
+ *
+ * The above port will receive packets from the BTS that need
+ * to be patched and forwarded to the network.
+ * The above port will receive packets from the network that
+ * need to be patched and forwarded to the BTS.
+ *
+ * We assume to have a static BTS IP address so we can differentiate
+ * network and BTS.
+ *
+ */
+int rtp_calculate_port(int multiplex, int base)
+{
+ return base + (multiplex * 2);
+}
diff --git a/openbsc/include/openbsc/msgb.h b/openbsc/include/openbsc/msgb.h
index 5ecac459b..ab3c03396 100644
--- a/openbsc/include/openbsc/msgb.h
+++ b/openbsc/include/openbsc/msgb.h
@@ -51,6 +51,7 @@ extern struct msgb *msgb_alloc(u_int16_t size, const char *name);
extern void msgb_free(struct msgb *m);
extern void msgb_enqueue(struct llist_head *queue, struct msgb *msg);
extern struct msgb *msgb_dequeue(struct llist_head *queue);
+extern void msgb_reset(struct msgb *m);
#define msgb_l2(m) ((void *)(m->l2h))
#define msgb_l3(m) ((void *)(m->l3h))
diff --git a/openbsc/include/openbsc/signal.h b/openbsc/include/openbsc/signal.h
index 1af849684..fee9d5bfd 100644
--- a/openbsc/include/openbsc/signal.h
+++ b/openbsc/include/openbsc/signal.h
@@ -39,6 +39,7 @@ enum signal_subsystems {
SS_NM,
SS_LCHAN,
SS_SUBSCR,
+ SS_SCALL,
};
/* SS_PAGING signals */
@@ -56,8 +57,8 @@ enum signal_sms {
/* SS_ABISIP signals */
enum signal_abisip {
- S_ABISIP_BIND_ACK,
- S_ABISIP_DISC_IND,
+ S_ABISIP_CRCX_ACK,
+ S_ABISIP_DLCX_IND,
};
/* SS_NM signals */
@@ -85,6 +86,13 @@ enum signal_subscr {
S_SUBSCR_DETACHED,
};
+/* SS_SCALL signals */
+enum signal_scall {
+ S_SCALL_SUCCESS,
+ S_SCALL_EXPIRED,
+ S_SCALL_DETACHED,
+};
+
typedef int signal_cbfn(unsigned int subsys, unsigned int signal,
void *handler_data, void *signal_data);
@@ -96,6 +104,12 @@ struct paging_signal_data {
struct gsm_lchan *lchan;
};
+struct scall_signal_data {
+ struct gsm_subscriber *subscr;
+ struct gsm_lchan *lchan;
+ void *data;
+};
+
/* Management */
int register_signal_handler(unsigned int subsys, signal_cbfn *cbfn, void *data);
void unregister_signal_handler(unsigned int subsys, signal_cbfn *cbfn, void *data);
diff --git a/openbsc/include/openbsc/silent_call.h b/openbsc/include/openbsc/silent_call.h
new file mode 100644
index 000000000..53ed4e248
--- /dev/null
+++ b/openbsc/include/openbsc/silent_call.h
@@ -0,0 +1,7 @@
+#ifndef _SILENT_CALL_H
+#define _SILENT_CALL_H
+
+extern int gsm_silent_call_start(struct gsm_subscriber *subscr, void *data);
+extern int gsm_silent_call_stop(struct gsm_subscriber *subscr);
+
+#endif /* _SILENT_CALL_H */
diff --git a/openbsc/include/openbsc/telnet_interface.h b/openbsc/include/openbsc/telnet_interface.h
index 97357d794..d3381e0a4 100644
--- a/openbsc/include/openbsc/telnet_interface.h
+++ b/openbsc/include/openbsc/telnet_interface.h
@@ -35,13 +35,6 @@ struct telnet_connection {
struct gsm_network *network;
struct bsc_fd fd;
struct vty *vty;
-
- int bts;
-
- int command;
- char *imsi;
- char commands[1024];
- int read;
};
diff --git a/openbsc/include/openbsc/timer.h b/openbsc/include/openbsc/timer.h
index ae67a5a1a..fee888bfd 100644
--- a/openbsc/include/openbsc/timer.h
+++ b/openbsc/include/openbsc/timer.h
@@ -44,9 +44,9 @@
struct timer_list {
struct llist_head entry;
struct timeval timeout;
- int active : 1;
- int handled : 1;
- int in_list : 1;
+ unsigned int active : 1;
+ unsigned int handled : 1;
+ unsigned int in_list : 1;
void (*cb)(void*);
void *data;
@@ -67,5 +67,6 @@ int bsc_timer_pending(struct timer_list *timer);
struct timeval *bsc_nearest_timer();
void bsc_prepare_timers();
int bsc_update_timers();
+int bsc_timer_check(void);
#endif
diff --git a/openbsc/include/openbsc/tlv.h b/openbsc/include/openbsc/tlv.h
index ae88e6ed3..0cf4388d8 100644
--- a/openbsc/include/openbsc/tlv.h
+++ b/openbsc/include/openbsc/tlv.h
@@ -6,11 +6,33 @@
#include <openbsc/msgb.h>
+/* Terminology / wording
+ tag length value (in bits)
+
+ V - - 8
+ LV - 8 N * 8
+ TLV 8 8 N * 8
+ TL16V 8 16 N * 8
+ TLV16 8 8 N * 16
+ TvLV 8 8/16 N * 8
+
+*/
+
#define LV_GROSS_LEN(x) (x+1)
#define TLV_GROSS_LEN(x) (x+2)
#define TLV16_GROSS_LEN(x) ((2*x)+2)
#define TL16V_GROSS_LEN(x) (x+3)
+#define TVLV_MAX_ONEBYTE 0x7f
+
+static inline u_int16_t TVLV_GROSS_LEN(u_int16_t len)
+{
+ if (len <= TVLV_MAX_ONEBYTE)
+ return TLV_GROSS_LEN(len);
+ else
+ return TL16V_GROSS_LEN(len);
+}
+
/* TLV generation */
static inline u_int8_t *lv_put(u_int8_t *buf, u_int8_t len,
@@ -49,6 +71,20 @@ static inline u_int8_t *tl16v_put(u_int8_t *buf, u_int8_t tag, u_int16_t len,
return buf + len*2;
}
+static inline u_int8_t *tvlv_put(u_int8_t *buf, u_int8_t tag, u_int16_t len,
+ const u_int8_t *val)
+{
+ u_int8_t *ret;
+
+ if (len <= TVLV_MAX_ONEBYTE) {
+ ret = tlv_put(buf, tag, len, val);
+ buf[1] |= 0x80;
+ } else
+ ret = tl16v_put(buf, tag, len, val);
+
+ return ret;
+}
+
static inline u_int8_t *msgb_tlv16_put(struct msgb *msg, u_int8_t tag, u_int8_t len, const u_int16_t *val)
{
u_int8_t *buf = msgb_put(msg, TLV16_GROSS_LEN(len));
@@ -62,6 +98,13 @@ static inline u_int8_t *msgb_tl16v_put(struct msgb *msg, u_int8_t tag, u_int16_t
return tl16v_put(buf, tag, len, val);
}
+static inline u_int8_t *msgb_tvlv_put(struct msgb *msg, u_int8_t tag, u_int16_t len,
+ const u_int8_t *val)
+{
+ u_int8_t *buf = msgb_put(msg, TVLV_GROSS_LEN(len));
+ return tvlv_put(buf, tag, len, val);
+}
+
static inline u_int8_t *v_put(u_int8_t *buf, u_int8_t val)
{
*buf++ = val;
@@ -146,6 +189,7 @@ enum tlv_type {
TLV_TYPE_TV,
TLV_TYPE_TLV,
TLV_TYPE_TL16V,
+ TLV_TYPE_TvLV,
};
struct tlv_def {
@@ -161,6 +205,11 @@ struct tlv_parsed {
struct tlv_p_entry lv[0xff];
};
+extern struct tlv_definition tvlv_att_def;
+
+int tlv_parse_one(u_int8_t *o_tag, u_int16_t *o_len, const u_int8_t **o_val,
+ const struct tlv_definition *def,
+ const u_int8_t *buf, int buf_len);
int tlv_parse(struct tlv_parsed *dec, const struct tlv_definition *def,
const u_int8_t *buf, int buf_len, u_int8_t lv_tag, u_int8_t lv_tag2);
diff --git a/openbsc/include/openbsc/ussd.h b/openbsc/include/openbsc/ussd.h
new file mode 100644
index 000000000..e7bd6d69d
--- /dev/null
+++ b/openbsc/include/openbsc/ussd.h
@@ -0,0 +1,10 @@
+#ifndef _USSD_H
+#define _USSD_H
+
+/* Handler function for mobile-originated USSD messages */
+
+#include <openbsc/msgb.h>
+
+int handle_rcv_ussd(struct msgb *msg);
+
+#endif
diff --git a/openbsc/include/sccp/Makefile.am b/openbsc/include/sccp/Makefile.am
new file mode 100644
index 000000000..42fd31047
--- /dev/null
+++ b/openbsc/include/sccp/Makefile.am
@@ -0,0 +1 @@
+noinst_HEADERS = sccp_types.h sccp.h
diff --git a/openbsc/include/sccp/sccp.h b/openbsc/include/sccp/sccp.h
new file mode 100644
index 000000000..3ad568c0b
--- /dev/null
+++ b/openbsc/include/sccp/sccp.h
@@ -0,0 +1,146 @@
+/*
+ * SCCP management code
+ *
+ * (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.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.
+ *
+ */
+
+#ifndef SCCP_H
+#define SCCP_H
+
+#include <stdlib.h>
+
+#include <sys/socket.h>
+
+#include <openbsc/msgb.h>
+
+#include "sccp_types.h"
+
+struct sccp_system;
+
+enum {
+ SCCP_CONNECTION_STATE_NONE,
+ SCCP_CONNECTION_STATE_REQUEST,
+ SCCP_CONNECTION_STATE_CONFIRM,
+ SCCP_CONNECTION_STATE_ESTABLISHED,
+ SCCP_CONNECTION_STATE_RELEASE,
+ SCCP_CONNECTION_STATE_RELEASE_COMPLETE,
+ SCCP_CONNECTION_STATE_REFUSED,
+ SCCP_CONNECTION_STATE_SETUP_ERROR,
+};
+
+struct sockaddr_sccp {
+ sa_family_t sccp_family; /* AF_SCCP in the future??? */
+ u_int8_t sccp_ssn; /* subssystem number for routing */
+
+ /* TODO fill in address indicator... if that is ever needed */
+
+ /* not sure about these */
+ /* u_int8_t sccp_class; */
+};
+
+/*
+ * parsed structure of an address
+ */
+struct sccp_address {
+ struct sccp_called_party_address address;
+ u_int8_t ssn;
+ u_int8_t poi[2];
+};
+
+struct sccp_optional_data {
+ u_int8_t data_len;
+ u_int8_t data_start;
+};
+
+struct sccp_connection {
+ /* public */
+ void *data_ctx;
+ void (*data_cb)(struct sccp_connection *conn, struct msgb *msg, unsigned int len);
+
+ void *state_ctx;
+ void (*state_cb)(struct sccp_connection *, int old_state);
+
+ struct sccp_source_reference source_local_reference;
+ struct sccp_source_reference destination_local_reference;
+
+ int connection_state;
+
+ /* private */
+ /* list of active connections */
+ struct llist_head list;
+ struct sccp_system *system;
+ int incoming;
+};
+
+/**
+ * system functionality to implement on top of any other transport layer:
+ * call sccp_system_incoming for incoming data (from the network)
+ * sccp will call outgoing whenever outgoing data exists
+ */
+int sccp_system_init(int (*outgoing)(struct msgb *data, void *ctx), void *context);
+int sccp_system_incoming(struct msgb *data);
+
+/**
+ * Send data on an existing connection
+ */
+int sccp_connection_write(struct sccp_connection *connection, struct msgb *data);
+int sccp_connection_send_it(struct sccp_connection *connection);
+int sccp_connection_close(struct sccp_connection *connection, int cause);
+int sccp_connection_free(struct sccp_connection *connection);
+
+/**
+ * Create a new socket. Set your callbacks and then call bind to open
+ * the connection.
+ */
+struct sccp_connection *sccp_connection_socket(void);
+
+/**
+ * Open the connection and send additional data
+ */
+int sccp_connection_connect(struct sccp_connection *conn,
+ const struct sockaddr_sccp *sccp_called,
+ struct msgb *data);
+
+/**
+ * mostly for testing purposes only. Set the accept callback.
+ * TODO: add true routing information... in analogy to socket, bind, accept
+ */
+int sccp_connection_set_incoming(const struct sockaddr_sccp *sock,
+ int (*accept_cb)(struct sccp_connection *connection, void *data),
+ void *user_data);
+
+/**
+ * Send data in terms of unit data. A fixed address indicator will be used.
+ */
+int sccp_write(struct msgb *data,
+ const struct sockaddr_sccp *sock_sender,
+ const struct sockaddr_sccp *sock_target, int class);
+int sccp_set_read(const struct sockaddr_sccp *sock,
+ int (*read_cb)(struct msgb *msgb, unsigned int, void *user_data),
+ void *user_data);
+
+/* generic sock addresses */
+extern const struct sockaddr_sccp sccp_ssn_bssap;
+
+/* helpers */
+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);
+
+#endif
diff --git a/openbsc/include/sccp/sccp_types.h b/openbsc/include/sccp/sccp_types.h
new file mode 100644
index 000000000..9310a6bf0
--- /dev/null
+++ b/openbsc/include/sccp/sccp_types.h
@@ -0,0 +1,394 @@
+/*
+ * ITU Q.713 defined types for SCCP
+ *
+ * (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.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.
+ *
+ */
+
+#ifndef SCCP_TYPES_H
+#define SCCP_TYPES_H
+
+/* Table 1/Q.713 - SCCP message types */
+enum sccp_message_types {
+ SCCP_MSG_TYPE_CR = 1,
+ SCCP_MSG_TYPE_CC = 2,
+ SCCP_MSG_TYPE_CREF = 3,
+ SCCP_MSG_TYPE_RLSD = 4,
+ SCCP_MSG_TYPE_RLC = 5,
+ SCCP_MSG_TYPE_DT1 = 6,
+ SCCP_MSG_TYPE_DT2 = 7,
+ SCCP_MSG_TYPE_AK = 8,
+ SCCP_MSG_TYPE_UDT = 9,
+ SCCP_MSG_TYPE_UDTS = 10,
+ SCCP_MSG_TYPE_ED = 11,
+ SCCP_MSG_TYPE_EA = 12,
+ SCCP_MSG_TYPE_RSR = 13,
+ SCCP_MSG_TYPE_RSC = 14,
+ SCCP_MSG_TYPE_ERR = 15,
+ SCCP_MSG_TYPE_IT = 16,
+ SCCP_MSG_TYPE_XUDT = 17,
+ SCCP_MSG_TYPE_XUDTS = 18,
+ SCCP_MSG_TYPE_LUDT = 19,
+ SCCP_MSG_TYPE_LUDTS = 20
+};
+
+/* Table 2/Q.713 - SCCP parameter name codes */
+enum sccp_parameter_name_codes {
+ SCCP_PNC_END_OF_OPTIONAL = 0,
+ SCCP_PNC_DESTINATION_LOCAL_REFERENCE = 1,
+ SCCP_PNC_SOURCE_LOCAL_REFERENCE = 2,
+ SCCP_PNC_CALLED_PARTY_ADDRESS = 3,
+ SCCP_PNC_CALLING_PARTY_ADDRESS = 4,
+ SCCP_PNC_PROTOCOL_CLASS = 5,
+ SCCP_PNC_SEGMENTING = 6,
+ SCCP_PNC_RECEIVE_SEQ_NUMBER = 7,
+ SCCP_PNC_SEQUENCING = 8,
+ SCCP_PNC_CREDIT = 9,
+ SCCP_PNC_RELEASE_CAUSE = 10,
+ SCCP_PNC_RETURN_CAUSE = 11,
+ SCCP_PNC_RESET_CAUSE = 12,
+ SCCP_PNC_ERROR_CAUSE = 13,
+ SCCP_PNC_REFUSAL_CAUSE = 14,
+ SCCP_PNC_DATA = 15,
+ SCCP_PNC_SEGMENTATION = 16,
+ SCCP_PNC_HOP_COUNTER = 17,
+ SCCP_PNC_IMPORTANCE = 18,
+ SCCP_PNC_LONG_DATA = 19,
+};
+
+/* Figure 3/Q.713 Called/calling party address */
+enum {
+ SCCP_TITLE_IND_NONE = 0,
+ SCCP_TITLE_IND_NATURE_ONLY = 1,
+ SCCP_TITLE_IND_TRANSLATION_ONLY = 2,
+ SCCP_TITLE_IND_TRANS_NUM_ENC = 3,
+ SCCP_TITLE_IND_TRANS_NUM_ENC_NATURE = 4,
+};
+
+enum {
+ SCCP_CALL_ROUTE_ON_SSN = 1,
+ SCCP_CALL_ROUTE_ON_GT = 0,
+};
+
+struct sccp_called_party_address {
+ u_int8_t point_code_indicator : 1,
+ ssn_indicator : 1,
+ global_title_indicator : 4,
+ routing_indicator : 1,
+ reserved : 1;
+ u_int8_t data[0];
+} __attribute__((packed));
+
+/* indicator indicates presence in the above order */
+
+/* Figure 6/Q.713 */
+struct sccp_signalling_point_code {
+ u_int8_t lsb;
+ u_int8_t msb : 6,
+ reserved : 2;
+} __attribute__((packed));
+
+/* SSN == subsystem number */
+enum sccp_subsystem_number {
+ SCCP_SSN_NOT_KNOWN_OR_USED = 0,
+ SCCP_SSN_MANAGEMENT = 1,
+ SCCP_SSN_RESERVED_ITU = 2,
+ SCCP_SSN_ISDN_USER_PART = 3,
+ SCCP_SSN_OMAP = 4, /* operation, maint and administration part */
+ SCCP_SSN_MAP = 5, /* mobile application part */
+ SCCP_SSN_HLR = 6,
+ SCCP_SSN_VLR = 7,
+ SCCP_SSN_MSC = 8,
+ SCCP_SSN_EIC = 9, /* equipent identifier centre */
+ SCCP_SSN_AUC = 10, /* authentication centre */
+ SCCP_SSN_ISDN_SUPPL_SERVICES = 11,
+ SCCP_SSN_RESERVED_INTL = 12,
+ SCCP_SSN_ISDN_EDGE_TO_EDGE = 13,
+ SCCP_SSN_TC_TEST_RESPONDER = 14,
+
+ /* From GSM 03.03 8.2 */
+ SCCP_SSN_BSSAP = 254,
+ SCCP_SSN_BSSOM = 253,
+};
+
+/* Q.713, 3.4.2.3 */
+enum {
+ SCCP_NAI_UNKNOWN = 0,
+ SCCP_NAI_SUBSCRIBER_NUMBER = 1,
+ SCCP_NAI_RESERVED_NATIONAL = 2,
+ SCCP_NAI_NATIONAL_SIGNIFICANT = 3,
+ SCCP_NAI_INTERNATIONAL = 4,
+};
+
+struct sccp_global_title {
+ u_int8_t nature_of_addr_ind : 7,
+ odd_even : 1;
+ u_int8_t data[0];
+} __attribute__((packed));
+
+/* Q.713, 3.3 */
+struct sccp_source_reference {
+ u_int8_t octet1;
+ u_int8_t octet2;
+ u_int8_t octet3;
+} __attribute__((packed));
+
+/* Q.714, 3.6 */
+enum sccp_protocol_class {
+ SCCP_PROTOCOL_CLASS_0 = 0,
+ SCCP_PROTOCOL_CLASS_1 = 1,
+ SCCP_PROTOCOL_CLASS_2 = 2,
+ SCCP_PROTOCOL_CLASS_3 = 3,
+};
+
+/* bits 5-8 when class0, class1 is used */
+enum sccp_protocol_options {
+ SCCP_PROTOCOL_NO_SPECIAL = 0,
+ SCCP_PROTOCOL_RETURN_MESSAGE = 8,
+};
+
+enum sccp_release_cause {
+ SCCP_RELEASE_CAUSE_END_USER_ORIGINATED = 0,
+ SCCP_RELEASE_CAUSE_END_USER_CONGESTION = 1,
+ SCCP_RELEASE_CAUSE_END_USER_FAILURE = 2,
+ SCCP_RELEASE_CAUSE_SCCP_USER_ORIGINATED = 3,
+ SCCP_RELEASE_CAUSE_REMOTE_PROCEDURE_ERROR = 4,
+ SCCP_RELEASE_CAUSE_INCONSISTENT_CONN_DATA = 5,
+ SCCP_RELEASE_CAUSE_ACCESS_FAILURE = 6,
+ SCCP_RELEASE_CAUSE_ACCESS_CONGESTION = 7,
+ SCCP_RELEASE_CAUSE_SUBSYSTEM_FAILURE = 8,
+ SCCP_RELEASE_CAUSE_SUBSYSTEM_CONGESTION = 9,
+ SCCP_RELEASE_CAUSE_MTP_FAILURE = 10,
+ SCCP_RELEASE_CAUSE_NETWORK_CONGESTION = 11,
+ SCCP_RELEASE_CAUSE_EXPIRATION_RESET = 12,
+ SCCP_RELEASE_CAUSE_EXPIRATION_INACTIVE = 13,
+ SCCP_RELEASE_CAUSE_RESERVED = 14,
+ SCCP_RELEASE_CAUSE_UNQUALIFIED = 15,
+ SCCP_RELEASE_CAUSE_SCCP_FAILURE = 16,
+};
+
+enum sccp_return_cause {
+ SCCP_RETURN_CAUSE_NO_TRANSLATION_NATURE = 0,
+ SCCP_RETURN_CAUSE_NO_TRANSLATION = 1,
+ SCCP_RETURN_CAUSE_SUBSYSTEM_CONGESTION = 2,
+ SCCP_RETURN_CAUSE_SUBSYSTEM_FAILURE = 3,
+ SCCP_RETURN_CAUSE_UNEQUIPPED_USER = 4,
+ SCCP_RETURN_CAUSE_MTP_FAILURE = 5,
+ SCCP_RETURN_CAUSE_NETWORK_CONGESTION = 6,
+ SCCP_RETURN_CAUSE_UNQUALIFIED = 7,
+ SCCP_RETURN_CAUSE_ERROR_IN_MSG_TRANSPORT = 8,
+ SCCP_RETURN_CAUSE_ERROR_IN_LOCAL_PROCESSING = 9,
+ SCCP_RETURN_CAUSE_DEST_CANNOT_PERFORM_REASSEMBLY = 10,
+ SCCP_RETURN_CAUSE_SCCP_FAILURE = 11,
+ SCCP_RETURN_CAUSE_HOP_COUNTER_VIOLATION = 12,
+ SCCP_RETURN_CAUSE_SEGMENTATION_NOT_SUPPORTED= 13,
+ SCCP_RETURN_CAUSE_SEGMENTATION_FAOLURE = 14
+};
+
+enum sccp_reset_cause {
+ SCCP_RESET_CAUSE_END_USER_ORIGINATED = 0,
+ SCCP_RESET_CAUSE_SCCP_USER_ORIGINATED = 1,
+ SCCP_RESET_CAUSE_MSG_OUT_OF_ORDER_PS = 2,
+ SCCP_RESET_CAUSE_MSG_OUT_OF_ORDER_PR = 3,
+ SCCP_RESET_CAUSE_RPC_OUT_OF_WINDOW = 4,
+ SCCP_RESET_CAUSE_RPC_INCORRECT_PS = 5,
+ SCCP_RESET_CAUSE_RPC_GENERAL = 6,
+ SCCP_RESET_CAUSE_REMOTE_END_USER_OPERATIONAL= 7,
+ SCCP_RESET_CAUSE_NETWORK_OPERATIONAL = 8,
+ SCCP_RESET_CAUSE_ACCESS_OPERATIONAL = 9,
+ SCCP_RESET_CAUSE_NETWORK_CONGESTION = 10,
+ SCCP_RESET_CAUSE_RESERVED = 11,
+};
+
+enum sccp_error_cause {
+ SCCP_ERROR_LRN_MISMATCH_UNASSIGNED = 0, /* local reference number */
+ SCCP_ERROR_LRN_MISMATCH_INCONSISTENT = 1,
+ SCCP_ERROR_POINT_CODE_MISMATCH = 2,
+ SCCP_ERROR_SERVICE_CLASS_MISMATCH = 3,
+ SCCP_ERROR_UNQUALIFIED = 4,
+};
+
+enum sccp_refusal_cause {
+ SCCP_REFUSAL_END_USER_ORIGINATED = 0,
+ SCCP_REFUSAL_END_USER_CONGESTION = 1,
+ SCCP_REFUSAL_END_USER_FAILURE = 2,
+ SCCP_REFUSAL_SCCP_USER_ORIGINATED = 3,
+ SCCP_REFUSAL_DESTINATION_ADDRESS_UKNOWN = 4,
+ SCCP_REFUSAL_DESTINATION_INACCESSIBLE = 5,
+ SCCP_REFUSAL_NET_QOS_NON_TRANSIENT = 6,
+ SCCP_REFUSAL_NET_QOS_TRANSIENT = 7,
+ SCCP_REFUSAL_ACCESS_FAILURE = 8,
+ SCCP_REFUSAL_ACCESS_CONGESTION = 9,
+ SCCP_REFUSAL_SUBSYSTEM_FAILURE = 10,
+ SCCP_REFUSAL_SUBSYTEM_CONGESTION = 11,
+ SCCP_REFUSAL_EXPIRATION = 12,
+ SCCP_REFUSAL_INCOMPATIBLE_USER_DATA = 13,
+ SCCP_REFUSAL_RESERVED = 14,
+ SCCP_REFUSAL_UNQUALIFIED = 15,
+ SCCP_REFUSAL_HOP_COUNTER_VIOLATION = 16,
+ SCCP_REFUSAL_SCCP_FAILURE = 17,
+ SCCP_REFUSAL_UNEQUIPPED_USER = 18,
+};
+
+/*
+ * messages... as of Q.713 Chapter 4
+ */
+struct sccp_connection_request {
+ /* mandantory */
+ u_int8_t type;
+ struct sccp_source_reference source_local_reference;
+ u_int8_t proto_class;
+
+
+ /* variable */
+ u_int8_t variable_called;
+#if VARIABLE
+ called_party_address
+#endif
+
+ /* optional */
+ u_int8_t optional_start;
+
+#if OPTIONAL
+ credit 3
+ callingparty var 4-n
+ data 3-130
+ hop_counter 3
+ importance 3
+ end_of_optional 1
+#endif
+
+ u_int8_t data[0];
+} __attribute__((packed));
+
+struct sccp_connection_confirm {
+ /* mandantory */
+ u_int8_t type;
+ struct sccp_source_reference destination_local_reference;
+ struct sccp_source_reference source_local_reference;
+ u_int8_t proto_class;
+
+ /* optional */
+ u_int8_t optional_start;
+
+ /* optional */
+#if OPTIONAL
+ credit 3
+ called party 4
+ data 3-130
+ importance 3
+ end_of_optional 1
+#endif
+
+ u_int8_t data[0];
+} __attribute__((packed));
+
+struct sccp_connection_refused {
+ /* mandantory */
+ u_int8_t type;
+ struct sccp_source_reference destination_local_reference;
+ u_int8_t cause;
+
+ /* optional */
+ u_int8_t optional_start;
+
+ /* optional */
+#if OPTIONAL
+ called party 4
+ data 3-130
+ importance 3
+ end_of_optional 1
+#endif
+
+ u_int8_t data[0];
+} __attribute__((packed));
+
+struct sccp_connection_released {
+ /* mandantory */
+ u_int8_t type;
+ struct sccp_source_reference destination_local_reference;
+ struct sccp_source_reference source_local_reference;
+ u_int8_t release_cause;
+
+
+ /* optional */
+ u_int8_t optional_start;
+
+#if OPTIONAL
+ data 3-130
+ importance 3
+ end_of_optional 1
+#endif
+ u_int8_t data[0];
+} __attribute__((packed));
+
+struct sccp_connection_release_complete {
+ u_int8_t type;
+ struct sccp_source_reference destination_local_reference;
+ struct sccp_source_reference source_local_reference;
+} __attribute__((packed));
+
+struct sccp_data_form1 {
+ /* mandantory */
+ u_int8_t type;
+ struct sccp_source_reference destination_local_reference;
+ u_int8_t segmenting;
+
+ /* variable */
+ u_int8_t variable_start;
+
+#if VARIABLE
+ data 2-256;
+#endif
+
+ u_int8_t data[0];
+} __attribute__((packed));
+
+
+struct sccp_data_unitdata {
+ /* mandantory */
+ u_int8_t type;
+ u_int8_t proto_class;
+
+
+ /* variable */
+ u_int8_t variable_called;
+ u_int8_t variable_calling;
+ u_int8_t variable_data;
+
+#if VARIABLE
+ called party address
+ calling party address
+#endif
+
+ u_int8_t data[0];
+} __attribute__((packed));
+
+struct sccp_data_it {
+ /* mandantory */
+ u_int8_t type;
+ struct sccp_source_reference destination_local_reference;
+ struct sccp_source_reference source_local_reference;
+ u_int8_t proto_class;
+
+ u_int8_t sequencing[2];
+ u_int8_t credit;
+} __attribute__((packed));
+
+#endif
diff --git a/openbsc/include/vty/command.h b/openbsc/include/vty/command.h
index 9daa2d62d..10a60add5 100644
--- a/openbsc/include/vty/command.h
+++ b/openbsc/include/vty/command.h
@@ -106,6 +106,7 @@ enum node_type {
TRX_NODE,
TS_NODE,
SUBSCR_NODE,
+ MGCP_NODE,
};
/* Node which has some commands and prompt string and configuration
diff --git a/openbsc/src/Makefile.am b/openbsc/src/Makefile.am
index 726c71278..100641e63 100644
--- a/openbsc/src/Makefile.am
+++ b/openbsc/src/Makefile.am
@@ -1,8 +1,9 @@
INCLUDES = $(all_includes) -I$(top_srcdir)/include
AM_CFLAGS=-Wall
-sbin_PROGRAMS = bsc_hack bs11_config ipaccess-find ipaccess-config isdnsync
-noinst_LIBRARIES = libbsc.a libmsc.a libvty.a
+sbin_PROGRAMS = bsc_hack bs11_config ipaccess-find ipaccess-config \
+ isdnsync bsc_mgcp
+noinst_LIBRARIES = libbsc.a libmsc.a libvty.a libsccp.a
noinst_HEADERS = vty/cardshell.h
libbsc_a_SOURCES = abis_rsl.c abis_nm.c gsm_data.c gsm_04_08_utils.c \
@@ -14,10 +15,12 @@ libbsc_a_SOURCES = abis_rsl.c abis_nm.c gsm_data.c gsm_04_08_utils.c \
libmsc_a_SOURCES = gsm_subscriber.c db.c telnet_interface.c \
mncc.c rtp_proxy.c md5c.c gsm_04_08.c gsm_04_11.c transaction.c \
- token_auth.c rrlp.c
+ token_auth.c rrlp.c gsm_04_80.c ussd.c silent_call.c
libvty_a_SOURCES = vty/buffer.c vty/command.c vty/vector.c vty/vty.c
+libsccp_a_SOURCES = sccp/sccp.c
+
bsc_hack_SOURCES = bsc_hack.c bsc_init.c vty_interface.c vty_interface_layer3.c
bsc_hack_LDADD = libmsc.a libbsc.a libmsc.a libvty.a -ldl -ldbi $(LIBCRYPT)
@@ -30,3 +33,6 @@ ipaccess_config_SOURCES = ipaccess-config.c
ipaccess_config_LDADD = libbsc.a libmsc.a libbsc.a libvty.a -ldl -ldbi $(LIBCRYPT)
isdnsync_SOURCES = isdnsync.c
+
+bsc_mgcp_SOURCES = bsc_mgcp.c msgb.c talloc.c debug.c select.c timer.c telnet_interface.c
+bsc_mgcp_LDADD = libvty.a
diff --git a/openbsc/src/abis_nm.c b/openbsc/src/abis_nm.c
index f9a7252a7..b1fe97ddf 100755
--- a/openbsc/src/abis_nm.c
+++ b/openbsc/src/abis_nm.c
@@ -328,7 +328,6 @@ static const struct tlv_definition nm_att_tlvdef = {
[NM_ATT_GET_ARI] = { TLV_TYPE_TL16V },
[NM_ATT_HW_CONF_CHG] = { TLV_TYPE_TL16V },
[NM_ATT_OUTST_ALARM] = { TLV_TYPE_TV },
- [NM_ATT_FILE_DATA] = { TLV_TYPE_TL16V },
[NM_ATT_MEAS_RES] = { TLV_TYPE_TL16V },
/* BS11 specifics */
[NM_ATT_BS11_ESN_FW_CODE_NO] = { TLV_TYPE_TLV },
@@ -410,6 +409,8 @@ static const enum abis_nm_chan_comb chcomb4pchan[] = {
[GSM_PCHAN_TCH_F] = NM_CHANC_TCHFull,
[GSM_PCHAN_TCH_H] = NM_CHANC_TCHHalf,
[GSM_PCHAN_SDCCH8_SACCH8C] = NM_CHANC_SDCCH,
+ [GSM_PCHAN_PDCH] = NM_CHANC_IPAC_PDCH,
+ [GSM_PCHAN_TCH_F_PDCH] = NM_CHANC_IPAC_TCHFull_PDCH,
/* FIXME: bounds check */
};
@@ -571,6 +572,18 @@ const char *nm_avail_name(u_int8_t avail)
return avail_names[avail];
}
+static struct value_string test_names[] = {
+ /* FIXME: standard test names */
+ { NM_IPACC_TESTNO_CHAN_USAGE, "Channel Usage" },
+ { NM_IPACC_TESTNO_BCCH_CHAN_USAGE, "BCCH Channel Usage" },
+ { NM_IPACC_TESTNO_FREQ_SYNC, "Frequency Synchronization" },
+ { NM_IPACC_TESTNO_BCCH_INFO, "BCCH Info" },
+ { NM_IPACC_TESTNO_TX_BEACON, "Transmit Beacon" },
+ { NM_IPACC_TESTNO_SYSINFO_MONITOR, "System Info Monitor" },
+ { NM_IPACC_TESTNO_BCCCH_MONITOR, "BCCH Monitor" },
+ { 0, NULL }
+};
+
const char *nm_adm_name(u_int8_t adm)
{
switch (adm) {
@@ -585,6 +598,14 @@ const char *nm_adm_name(u_int8_t adm)
}
}
+static void debugp_foh(struct abis_om_fom_hdr *foh)
+{
+ DEBUGP(DNM, "OC=%s(%02x) INST=(%02x,%02x,%02x) ",
+ obj_class_name(foh->obj_class), foh->obj_class,
+ foh->obj_inst.bts_nr, foh->obj_inst.trx_nr,
+ foh->obj_inst.ts_nr);
+}
+
/* obtain the gsm_nm_state data structure for a given object instance */
static struct gsm_nm_state *
objclass2nmstate(struct gsm_bts *bts, u_int8_t obj_class,
@@ -598,20 +619,26 @@ objclass2nmstate(struct gsm_bts *bts, u_int8_t obj_class,
nm_state = &bts->nm_state;
break;
case NM_OC_RADIO_CARRIER:
- if (obj_inst->trx_nr >= bts->num_trx)
+ if (obj_inst->trx_nr >= bts->num_trx) {
+ DEBUGPC(DNM, "TRX %u does not exist ", obj_inst->trx_nr);
return NULL;
+ }
trx = gsm_bts_trx_num(bts, obj_inst->trx_nr);
nm_state = &trx->nm_state;
break;
case NM_OC_BASEB_TRANSC:
- if (obj_inst->trx_nr >= bts->num_trx)
+ if (obj_inst->trx_nr >= bts->num_trx) {
+ DEBUGPC(DNM, "TRX %u does not exist ", obj_inst->trx_nr);
return NULL;
+ }
trx = gsm_bts_trx_num(bts, obj_inst->trx_nr);
nm_state = &trx->bb_transc.nm_state;
break;
case NM_OC_CHANNEL:
- if (obj_inst->trx_nr > bts->num_trx)
+ if (obj_inst->trx_nr > bts->num_trx) {
+ DEBUGPC(DNM, "TRX %u does not exist ", obj_inst->trx_nr);
return NULL;
+ }
trx = gsm_bts_trx_num(bts, obj_inst->trx_nr);
if (obj_inst->ts_nr >= TRX_NR_TS)
return NULL;
@@ -648,6 +675,17 @@ objclass2nmstate(struct gsm_bts *bts, u_int8_t obj_class,
return NULL;
nm_state = &bts->bs11.envabtse[obj_inst->trx_nr].nm_state;
break;
+ case NM_OC_GPRS_NSE:
+ nm_state = &bts->gprs.nse.nm_state;
+ break;
+ case NM_OC_GPRS_CELL:
+ nm_state = &bts->gprs.cell.nm_state;
+ break;
+ case NM_OC_GPRS_NSVC:
+ if (obj_inst->trx_nr > ARRAY_SIZE(bts->gprs.nsvc))
+ return NULL;
+ nm_state = &bts->gprs.nsvc[obj_inst->trx_nr].nm_state;
+ break;
}
return nm_state;
}
@@ -665,20 +703,26 @@ objclass2obj(struct gsm_bts *bts, u_int8_t obj_class,
obj = bts;
break;
case NM_OC_RADIO_CARRIER:
- if (obj_inst->trx_nr >= bts->num_trx)
+ if (obj_inst->trx_nr >= bts->num_trx) {
+ DEBUGPC(DNM, "TRX %u does not exist ", obj_inst->trx_nr);
return NULL;
+ }
trx = gsm_bts_trx_num(bts, obj_inst->trx_nr);
obj = trx;
break;
case NM_OC_BASEB_TRANSC:
- if (obj_inst->trx_nr >= bts->num_trx)
+ if (obj_inst->trx_nr >= bts->num_trx) {
+ DEBUGPC(DNM, "TRX %u does not exist ", obj_inst->trx_nr);
return NULL;
+ }
trx = gsm_bts_trx_num(bts, obj_inst->trx_nr);
obj = &trx->bb_transc;
break;
case NM_OC_CHANNEL:
- if (obj_inst->trx_nr > bts->num_trx)
+ if (obj_inst->trx_nr > bts->num_trx) {
+ DEBUGPC(DNM, "TRX %u does not exist ", obj_inst->trx_nr);
return NULL;
+ }
trx = gsm_bts_trx_num(bts, obj_inst->trx_nr);
if (obj_inst->ts_nr >= TRX_NR_TS)
return NULL;
@@ -687,6 +731,17 @@ objclass2obj(struct gsm_bts *bts, u_int8_t obj_class,
case NM_OC_SITE_MANAGER:
obj = &bts->site_mgr;
break;
+ case NM_OC_GPRS_NSE:
+ obj = &bts->gprs.nse;
+ break;
+ case NM_OC_GPRS_CELL:
+ obj = &bts->gprs.cell;
+ break;
+ case NM_OC_GPRS_NSVC:
+ if (obj_inst->trx_nr > ARRAY_SIZE(bts->gprs.nsvc))
+ return NULL;
+ obj = &bts->gprs.nsvc[obj_inst->trx_nr];
+ break;
}
return obj;
}
@@ -701,6 +756,8 @@ static int update_admstate(struct gsm_bts *bts, u_int8_t obj_class,
int rc;
obj = objclass2obj(bts, obj_class, obj_inst);
+ if (!obj)
+ return -EINVAL;
nm_state = objclass2nmstate(bts, obj_class, obj_inst);
if (!nm_state)
return -1;
@@ -730,7 +787,7 @@ static int abis_nm_rx_statechg_rep(struct msgb *mb)
nm_state = objclass2nmstate(bts, foh->obj_class, &foh->obj_inst);
if (!nm_state) {
- DEBUGPC(DNM, "\n");
+ DEBUGPC(DNM, "unknown object class\n");
return -EINVAL;
}
@@ -751,7 +808,7 @@ static int abis_nm_rx_statechg_rep(struct msgb *mb)
}
if (TLVP_PRESENT(&tp, NM_ATT_ADM_STATE)) {
new_state.administrative = *TLVP_VAL(&tp, NM_ATT_ADM_STATE);
- DEBUGPC(DNM, "ADM=%02s ", nm_adm_name(new_state.administrative));
+ DEBUGPC(DNM, "ADM=%2s ", nm_adm_name(new_state.administrative));
}
DEBUGPC(DNM, "\n");
@@ -799,10 +856,7 @@ static int abis_nm_rcvmsg_report(struct msgb *mb)
struct abis_om_fom_hdr *foh = msgb_l3(mb);
u_int8_t mt = foh->msg_type;
- DEBUGP(DNM, "OC=%s(%02x) INST=(%02x,%02x,%02x) ",
- obj_class_name(foh->obj_class), foh->obj_class,
- foh->obj_inst.bts_nr, foh->obj_inst.trx_nr,
- foh->obj_inst.ts_nr);
+ debugp_foh(foh);
//nmh->cfg->report_cb(mb, foh);
@@ -860,7 +914,9 @@ static int abis_nm_rx_sw_act_req(struct msgb *mb)
int nack = 0;
int ret;
- DEBUGP(DNM, "Software Activate Request ");
+ debugp_foh(foh);
+
+ DEBUGPC(DNM, "SW Activate Request: ");
if (foh->obj_class >= 0xf0 && foh->obj_class <= 0xf3) {
DEBUGPC(DNM, "NACKing for GPRS obj_class 0x%02x\n", foh->obj_class);
@@ -965,10 +1021,7 @@ static int abis_nm_rcvmsg_fom(struct msgb *mb)
if (is_in_arr(mt, nacks, ARRAY_SIZE(nacks))) {
struct tlv_parsed tp;
- DEBUGP(DNM, "OC=%s(%02x) INST=(%02x,%02x,%02x) ",
- obj_class_name(foh->obj_class), foh->obj_class,
- foh->obj_inst.bts_nr, foh->obj_inst.trx_nr,
- foh->obj_inst.ts_nr);
+ debugp_foh(foh);
if (nack_names[mt])
DEBUGPC(DNM, "%s NACK ", nack_names[mt]);
@@ -983,7 +1036,7 @@ static int abis_nm_rcvmsg_fom(struct msgb *mb)
else
DEBUGPC(DNM, "\n");
- dispatch_signal(SS_NM, S_NM_NACK, (void*) ((long)mt));
+ dispatch_signal(SS_NM, S_NM_NACK, (void*) &mt);
return 0;
}
#if 0
@@ -1851,11 +1904,12 @@ int abis_nm_opstart(struct gsm_bts *bts, u_int8_t obj_class, u_int8_t i0, u_int8
struct abis_om_hdr *oh;
struct msgb *msg = nm_msgb_alloc();
- DEBUGP(DNM, "Sending OPSTART obj_class=0x%02x obj_inst=(0x%02x, 0x%02x, 0x%02x)\n",
- obj_class, i0, i1, i2);
oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
fill_om_fom_hdr(oh, 0, NM_MT_OPSTART, obj_class, i0, i1, i2);
+ debugp_foh((struct abis_om_fom_hdr *) oh->data);
+ DEBUGPC(DNM, "Sending OPSTART\n");
+
return abis_nm_sendmsg(bts, msg);
}
@@ -2474,7 +2528,9 @@ static int abis_nm_rx_ipacc(struct msgb *msg)
foh = (struct abis_om_fom_hdr *) (oh->data + 1 + idstrlen);
abis_nm_tlv_parse(&tp, foh->data, oh->length-sizeof(*foh));
- DEBUGP(DNM, "IPACCESS(0x%02x): ", foh->msg_type);
+ debugp_foh(foh);
+
+ DEBUGPC(DNM, "IPACCESS(0x%02x): ", foh->msg_type);
switch (foh->msg_type) {
case NM_MT_IPACC_RSL_CONNECT_ACK:
@@ -2487,6 +2543,9 @@ static int abis_nm_rx_ipacc(struct msgb *msg)
DEBUGPC(DNM, "PORT=%u ",
ntohs(*((u_int16_t *)
TLVP_VAL(&tp, NM_ATT_IPACC_DST_IP_PORT))));
+ if (TLVP_PRESENT(&tp, NM_ATT_IPACC_STREAM_ID))
+ DEBUGPC(DNM, "STREAM=0x%02x ",
+ *TLVP_VAL(&tp, NM_ATT_IPACC_STREAM_ID));
DEBUGPC(DNM, "\n");
break;
case NM_MT_IPACC_RSL_CONNECT_NACK:
@@ -2542,7 +2601,7 @@ static int abis_nm_rx_ipacc(struct msgb *msg)
case NM_MT_IPACC_RSL_CONNECT_NACK:
case NM_MT_IPACC_SET_NVATTR_NACK:
case NM_MT_IPACC_GET_NVATTR_NACK:
- dispatch_signal(SS_NM, S_NM_IPACC_NACK, (void*) ((long)foh->msg_type));
+ dispatch_signal(SS_NM, S_NM_IPACC_NACK, &foh->msg_type);
break;
default:
break;
@@ -2597,8 +2656,167 @@ int abis_nm_ipaccess_set_nvattr(struct gsm_bts *bts, u_int8_t *attr,
attr_len);
}
+int abis_nm_ipaccess_rsl_connect(struct gsm_bts_trx *trx,
+ u_int32_t ip, u_int16_t port, u_int8_t stream)
+{
+ struct in_addr ia;
+ u_int8_t attr[] = { NM_ATT_IPACC_STREAM_ID, 0,
+ NM_ATT_IPACC_DST_IP_PORT, 0, 0,
+ NM_ATT_IPACC_DST_IP, 0, 0, 0, 0 };
+
+ int attr_len = sizeof(attr);
+
+ ia.s_addr = htonl(ip);
+ attr[1] = stream;
+ attr[3] = port >> 8;
+ attr[4] = port & 0xff;
+ *(u_int32_t *)(attr+6) = ia.s_addr;
+
+ /* if ip == 0, we use the default IP */
+ if (ip == 0)
+ attr_len -= 5;
+
+ DEBUGP(DNM, "ip.access RSL CONNECT IP=%s PORT=%u STREAM=0x%02x\n",
+ inet_ntoa(ia), port, stream);
+
+ return abis_nm_ipaccess_msg(trx->bts, NM_MT_IPACC_RSL_CONNECT,
+ NM_OC_BASEB_TRANSC, trx->bts->bts_nr,
+ trx->nr, 0xff, attr, attr_len);
+}
+
/* restart / reboot an ip.access nanoBTS */
int abis_nm_ipaccess_restart(struct gsm_bts *bts)
{
return __simple_cmd(bts, NM_MT_IPACC_RESTART);
}
+
+int abis_nm_ipaccess_set_attr(struct gsm_bts *bts, u_int8_t obj_class,
+ u_int8_t bts_nr, u_int8_t trx_nr, u_int8_t ts_nr,
+ u_int8_t *attr, u_int8_t attr_len)
+{
+ return abis_nm_ipaccess_msg(bts, NM_MT_IPACC_SET_ATTR,
+ obj_class, bts_nr, trx_nr, ts_nr,
+ attr, attr_len);
+}
+
+void gsm_trx_lock_rf(struct gsm_bts_trx *trx, int locked)
+{
+ int new_state = locked ? NM_STATE_LOCKED : NM_STATE_UNLOCKED;
+
+ trx->rf_locked = locked;
+ if (!trx->bts || !trx->bts->oml_link)
+ return;
+
+ abis_nm_chg_adm_state(trx->bts, NM_OC_RADIO_CARRIER,
+ trx->bts->bts_nr, trx->nr, 0xff,
+ new_state);
+}
+
+static const char *ipacc_testres_names[] = {
+ [NM_IPACC_TESTRES_SUCCESS] = "SUCCESS",
+ [NM_IPACC_TESTRES_TIMEOUT] = "TIMEOUT",
+ [NM_IPACC_TESTRES_NO_CHANS] = "NO CHANNELS",
+ [NM_IPACC_TESTRES_PARTIAL] = "PARTIAL",
+ [NM_IPACC_TESTRES_STOPPED] = "STOPPED",
+};
+
+const char *ipacc_testres_name(u_int8_t res)
+{
+ if (res < ARRAY_SIZE(ipacc_testres_names) &&
+ ipacc_testres_names[res])
+ return ipacc_testres_names[res];
+
+ return "unknown";
+}
+
+void ipac_parse_cgi(struct cell_global_id *cid, const u_int8_t *buf)
+{
+ cid->mcc = (buf[0] & 0xf) * 100;
+ cid->mcc += (buf[0] >> 4) * 10;
+ cid->mcc += (buf[1] & 0xf) * 1;
+
+ if (buf[1] >> 4 == 0xf) {
+ cid->mnc = (buf[2] & 0xf) * 10;
+ cid->mnc += (buf[2] >> 4) * 1;
+ } else {
+ cid->mnc = (buf[2] & 0xf) * 100;
+ cid->mnc += (buf[2] >> 4) * 10;
+ cid->mnc += (buf[1] >> 4) * 1;
+ }
+
+ cid->lac = ntohs(*((u_int16_t *)&buf[3]));
+ cid->ci = ntohs(*((u_int16_t *)&buf[5]));
+}
+
+/* parse BCCH information IEI from wire format to struct ipac_bcch_info */
+int ipac_parse_bcch_info(struct ipac_bcch_info *binf, u_int8_t *buf)
+{
+ u_int8_t *cur = buf;
+ u_int16_t len;
+
+ memset(binf, 0, sizeof(binf));
+
+ if (cur[0] != NM_IPAC_EIE_BCCH_INFO)
+ return -EINVAL;
+ cur++;
+
+ len = ntohs(*(u_int16_t *)cur);
+ cur += 2;
+
+ binf->info_type = ntohs(*(u_int16_t *)cur);
+ cur += 2;
+
+ if (binf->info_type & IPAC_BINF_FREQ_ERR_QUAL)
+ binf->freq_qual = *cur >> 2;
+
+ binf->arfcn = *cur++ & 3 << 8;
+ binf->arfcn |= *cur++;
+
+ if (binf->info_type & IPAC_BINF_RXLEV)
+ binf->rx_lev = *cur & 0x3f;
+ cur++;
+
+ if (binf->info_type & IPAC_BINF_RXQUAL)
+ binf->rx_qual = *cur & 0x7;
+ cur++;
+
+ if (binf->info_type & IPAC_BINF_FREQ_ERR_QUAL)
+ binf->freq_err = ntohs(*(u_int16_t *)cur);
+ cur += 2;
+
+ if (binf->info_type & IPAC_BINF_FRAME_OFFSET)
+ binf->frame_offset = ntohs(*(u_int16_t *)cur);
+ cur += 2;
+
+ if (binf->info_type & IPAC_BINF_FRAME_NR_OFFSET)
+ binf->frame_nr_offset = ntohl(*(u_int32_t *)cur);
+ cur += 4;
+
+ if (binf->info_type & IPAC_BINF_BSIC)
+ binf->bsic = *cur & 0x3f;
+ cur++;
+
+ ipac_parse_cgi(&binf->cgi, cur);
+ cur += 7;
+
+ if (binf->info_type & IPAC_BINF_NEIGH_BA_SI2) {
+ memcpy(binf->ba_list_si2, cur, sizeof(binf->ba_list_si2));
+ cur += sizeof(binf->ba_list_si2);
+ }
+
+ if (binf->info_type & IPAC_BINF_NEIGH_BA_SI2bis) {
+ memcpy(binf->ba_list_si2bis, cur,
+ sizeof(binf->ba_list_si2bis));
+ cur += sizeof(binf->ba_list_si2bis);
+ }
+
+ if (binf->info_type & IPAC_BINF_NEIGH_BA_SI2ter) {
+ memcpy(binf->ba_list_si2ter, cur,
+ sizeof(binf->ba_list_si2ter));
+ cur += sizeof(binf->ba_list_si2ter);
+ }
+
+ return 0;
+}
+
+
diff --git a/openbsc/src/abis_rsl.c b/openbsc/src/abis_rsl.c
index 800f2027f..219b01aa0 100644
--- a/openbsc/src/abis_rsl.c
+++ b/openbsc/src/abis_rsl.c
@@ -202,7 +202,9 @@ struct gsm_lchan *lchan_lookup(struct gsm_bts_trx *trx, u_int8_t chan_nr)
if (cbits == 0x01) {
lch_idx = 0; /* TCH/F */
- if (ts->pchan != GSM_PCHAN_TCH_F)
+ if (ts->pchan != GSM_PCHAN_TCH_F &&
+ ts->pchan != GSM_PCHAN_PDCH &&
+ ts->pchan != GSM_PCHAN_TCH_F_PDCH)
fprintf(stderr, "chan_nr=0x%02x but pchan=%u\n",
chan_nr, ts->pchan);
} else if ((cbits & 0x1e) == 0x02) {
@@ -237,6 +239,7 @@ struct gsm_lchan *lchan_lookup(struct gsm_bts_trx *trx, u_int8_t chan_nr)
return lchan;
}
+/* See Table 10.5.25 of GSM04.08 */
u_int8_t lchan2chan_nr(struct gsm_lchan *lchan)
{
struct gsm_bts_trx_ts *ts = lchan->ts;
@@ -244,6 +247,8 @@ u_int8_t lchan2chan_nr(struct gsm_lchan *lchan)
switch (ts->pchan) {
case GSM_PCHAN_TCH_F:
+ case GSM_PCHAN_PDCH:
+ case GSM_PCHAN_TCH_F_PDCH:
cbits = 0x01;
break;
case GSM_PCHAN_TCH_H:
@@ -483,6 +488,11 @@ static int channel_mode_from_lchan(struct rsl_ie_chan_mode *cm,
/* set TCH Speech/Data */
cm->spd_ind = lchan->rsl_cmode;
+ if (lchan->rsl_cmode == RSL_CMOD_SPD_SIGN &&
+ lchan->tch_mode != GSM48_CMODE_SIGN)
+ DEBUGP(DRSL, "unsupported: rsl_mode == signalling, "
+ "but tch_mode != signalling\n");
+
switch (lchan->type) {
case GSM_LCHAN_SDCCH:
cm->chan_rt = RSL_CMOD_CRT_SDCCH;
@@ -643,6 +653,11 @@ int rsl_chan_mode_modify_req(struct gsm_lchan *lchan)
msgb_tlv_put(msg, RSL_IE_ENCR_INFO, rc, encr_info);
}
+ if (lchan->tch_mode == GSM48_CMODE_SPEECH_AMR) {
+ msgb_tlv_put(msg, RSL_IE_MR_CONFIG, sizeof(lchan->mr_conf),
+ (u_int8_t *) &lchan->mr_conf);
+ }
+
msg->trx = lchan->ts->trx;
return abis_rsl_sendmsg(msg);
@@ -971,7 +986,7 @@ static int rsl_rx_meas_res(struct msgb *msg)
}
if (TLVP_PRESENT(&tp, RSL_IE_L3_INFO)) {
DEBUGPC(DMEAS, "L3\n");
- msg->l3h = TLVP_VAL(&tp, RSL_IE_L3_INFO);
+ msg->l3h = (u_int8_t *) TLVP_VAL(&tp, RSL_IE_L3_INFO);
return gsm0408_rcvmsg(msg, 0);
} else
DEBUGPC(DMEAS, "\n");
@@ -1016,6 +1031,18 @@ static int abis_rsl_rx_dchan(struct msgb *msg)
case RSL_MT_MODE_MODIFY_NACK:
DEBUGPC(DRSL, "CHANNEL MODE MODIFY NACK\n");
break;
+ case RSL_MT_IPAC_PDCH_ACT_ACK:
+ DEBUGPC(DRSL, "IPAC PDCH ACT ACK\n");
+ break;
+ case RSL_MT_IPAC_PDCH_ACT_NACK:
+ DEBUGPC(DRSL, "IPAC PDCH ACT NACK\n");
+ break;
+ case RSL_MT_IPAC_PDCH_DEACT_ACK:
+ DEBUGPC(DRSL, "IPAC PDCH DEACT ACK\n");
+ break;
+ case RSL_MT_IPAC_PDCH_DEACT_NACK:
+ DEBUGPC(DRSL, "IPAC PDCH DEACT NACK\n");
+ break;
case RSL_MT_PHY_CONTEXT_CONF:
case RSL_MT_PREPROC_MEAS_RES:
case RSL_MT_TALKER_DET:
@@ -1116,13 +1143,14 @@ static int rsl_rx_chan_rqd(struct msgb *msg)
/* determine channel type (SDCCH/TCH_F/TCH_H) based on
* request reference RA */
- lctype = get_ctype_by_chreq(bts, rqd_ref->ra);
- chreq_reason = get_reason_by_chreq(bts, rqd_ref->ra);
+ lctype = get_ctype_by_chreq(bts, rqd_ref->ra, bts->network->neci);
+ chreq_reason = get_reason_by_chreq(bts, rqd_ref->ra, bts->network->neci);
/* check availability / allocate channel */
lchan = lchan_alloc(bts, lctype);
if (!lchan) {
- fprintf(stderr, "CHAN RQD: no resources\n");
+ DEBUGP(DRSL, "CHAN RQD: no resources for %u 0x%x\n",
+ lctype, rqd_ref->ra);
/* FIXME: send some kind of reject ?!? */
return -ENOMEM;
}
@@ -1163,7 +1191,7 @@ static int rsl_rx_chan_rqd(struct msgb *msg)
/* Start timer T3101 to wait for GSM48_MT_RR_PAG_RESP */
lchan->T3101.cb = t3101_expired;
lchan->T3101.data = lchan;
- bsc_schedule_timer(&lchan->T3101, 10, 0);
+ bsc_schedule_timer(&lchan->T3101, bts->network->T3101, 0);
/* send IMMEDIATE ASSIGN CMD on RSL to BTS (to send on CCCH to MS) */
ret = rsl_imm_assign_cmd(bts, sizeof(ia), (u_int8_t *) &ia);
@@ -1277,6 +1305,7 @@ static int abis_rsl_rx_rll(struct msgb *msg)
case RSL_MT_EST_IND:
DEBUGPC(DRLL, "ESTABLISH INDICATION\n");
/* lchan is established, stop T3101 */
+ msg->lchan->sapis[rllh->link_id & 0x7] = LCHAN_SAPI_MS;
bsc_del_timer(&msg->lchan->T3101);
if (msgb_l2len(msg) >
sizeof(struct abis_rsl_common_hdr) + sizeof(*rllh) &&
@@ -1287,12 +1316,14 @@ static int abis_rsl_rx_rll(struct msgb *msg)
break;
case RSL_MT_EST_CONF:
DEBUGPC(DRLL, "ESTABLISH CONFIRM\n");
+ msg->lchan->sapis[rllh->link_id & 0x7] = LCHAN_SAPI_NET;
rll_indication(msg->lchan, rllh->link_id,
BSC_RLLR_IND_EST_CONF);
break;
case RSL_MT_REL_IND:
/* BTS informs us of having received DISC from MS */
DEBUGPC(DRLL, "RELEASE INDICATION\n");
+ msg->lchan->sapis[rllh->link_id & 0x7] = LCHAN_SAPI_UNUSED;
rll_indication(msg->lchan, rllh->link_id,
BSC_RLLR_IND_REL_IND);
/* we can now releae the channel on the BTS/Abis side */
@@ -1304,6 +1335,7 @@ static int abis_rsl_rx_rll(struct msgb *msg)
/* BTS informs us of having received UA from MS,
* in response to DISC that we've sent earlier */
DEBUGPC(DRLL, "RELEASE CONFIRMATION\n");
+ msg->lchan->sapis[rllh->link_id & 0x7] = LCHAN_SAPI_UNUSED;
/* we can now releae the channel on the BTS/Abis side */
/* FIXME: officially we need to start T3111 and wait for
* some grace period */
@@ -1340,14 +1372,14 @@ static u_int8_t ipa_smod_s_for_tch_mode(u_int8_t tch_mode)
}
/* ip.access specific RSL extensions */
-int rsl_ipacc_bind(struct gsm_lchan *lchan)
+int rsl_ipacc_crcx(struct gsm_lchan *lchan)
{
struct msgb *msg = rsl_msgb_alloc();
struct abis_rsl_dchan_hdr *dh;
u_int8_t speech_mode;
dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh));
- init_dchan_hdr(dh, RSL_MT_IPAC_BIND);
+ init_dchan_hdr(dh, RSL_MT_IPAC_CRCX);
dh->c.msg_discr = ABIS_RSL_MDISC_IPACCESS;
dh->chan_nr = lchan2chan_nr(lchan);
@@ -1364,7 +1396,7 @@ int rsl_ipacc_bind(struct gsm_lchan *lchan)
return abis_rsl_sendmsg(msg);
}
-int rsl_ipacc_connect(struct gsm_lchan *lchan, u_int32_t ip, u_int16_t port,
+int rsl_ipacc_mdcx(struct gsm_lchan *lchan, u_int32_t ip, u_int16_t port,
u_int16_t conn_id, u_int8_t rtp_payload2)
{
struct msgb *msg = rsl_msgb_alloc();
@@ -1374,7 +1406,7 @@ int rsl_ipacc_connect(struct gsm_lchan *lchan, u_int32_t ip, u_int16_t port,
struct in_addr ia;
dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh));
- init_dchan_hdr(dh, RSL_MT_IPAC_CONNECT);
+ init_dchan_hdr(dh, RSL_MT_IPAC_MDCX);
dh->c.msg_discr = ABIS_RSL_MDISC_IPACCESS;
dh->chan_nr = lchan2chan_nr(lchan);
@@ -1382,7 +1414,7 @@ int rsl_ipacc_connect(struct gsm_lchan *lchan, u_int32_t ip, u_int16_t port,
speech_mode = 0x00 | ipa_smod_s_for_tch_mode(lchan->tch_mode);
ia.s_addr = htonl(ip);
- DEBUGP(DRSL, "channel=%s chan_nr=0x%02x IPAC_CONNECT "
+ DEBUGP(DRSL, "channel=%s chan_nr=0x%02x IPAC_MDCX "
"IP=%s PORT=%d RTP_PAYLOAD2=%d CONN_ID=%d speech_mode=0x%02x\n",
gsm_ts_name(lchan->ts), dh->chan_nr,
inet_ntoa(ia), port, rtp_payload2, conn_id, speech_mode);
@@ -1414,7 +1446,25 @@ int rsl_ipacc_connect(struct gsm_lchan *lchan, u_int32_t ip, u_int16_t port,
return abis_rsl_sendmsg(msg);
}
-static int abis_rsl_rx_ipacc_bindack(struct msgb *msg)
+int rsl_ipacc_pdch_activate(struct gsm_lchan *lchan)
+{
+ struct msgb *msg = rsl_msgb_alloc();
+ struct abis_rsl_dchan_hdr *dh;
+
+ dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh));
+ init_dchan_hdr(dh, RSL_MT_IPAC_PDCH_ACT);
+ dh->c.msg_discr = ABIS_RSL_MDISC_DED_CHAN;
+ dh->chan_nr = lchan2chan_nr(lchan);
+
+ DEBUGP(DRSL, "channel=%s chan_nr=0x%02x IPAC_PDCH_ACT\n",
+ gsm_ts_name(lchan->ts), dh->chan_nr);
+
+ msg->trx = lchan->ts->trx;
+
+ return abis_rsl_sendmsg(msg);
+}
+
+static int abis_rsl_rx_ipacc_crcx_ack(struct msgb *msg)
{
struct abis_rsl_dchan_hdr *dh = msgb_l2(msg);
struct tlv_parsed tv;
@@ -1452,12 +1502,12 @@ static int abis_rsl_rx_ipacc_bindack(struct msgb *msg)
ts->abis_ip.bound_port = ntohs(port);
ts->abis_ip.conn_id = ntohs(attr_f8);
- dispatch_signal(SS_ABISIP, S_ABISIP_BIND_ACK, msg->lchan);
+ dispatch_signal(SS_ABISIP, S_ABISIP_CRCX_ACK, msg->lchan);
return 0;
}
-static int abis_rsl_rx_ipacc_disc_ind(struct msgb *msg)
+static int abis_rsl_rx_ipacc_dlcx_ind(struct msgb *msg)
{
struct abis_rsl_dchan_hdr *dh = msgb_l2(msg);
struct tlv_parsed tv;
@@ -1468,7 +1518,7 @@ static int abis_rsl_rx_ipacc_disc_ind(struct msgb *msg)
print_rsl_cause(TLVP_VAL(&tv, RSL_IE_CAUSE),
TLVP_LEN(&tv, RSL_IE_CAUSE));
- dispatch_signal(SS_ABISIP, S_ABISIP_DISC_IND, msg->lchan);
+ dispatch_signal(SS_ABISIP, S_ABISIP_DLCX_IND, msg->lchan);
return 0;
}
@@ -1483,27 +1533,27 @@ static int abis_rsl_rx_ipacc(struct msgb *msg)
gsm_ts_name(msg->lchan->ts), rllh->chan_nr);
switch (rllh->c.msg_type) {
- case RSL_MT_IPAC_BIND_ACK:
- DEBUGPC(DRSL, "IPAC_BIND_ACK ");
- rc = abis_rsl_rx_ipacc_bindack(msg);
+ case RSL_MT_IPAC_CRCX_ACK:
+ DEBUGPC(DRSL, "IPAC_CRCX_ACK ");
+ rc = abis_rsl_rx_ipacc_crcx_ack(msg);
break;
- case RSL_MT_IPAC_BIND_NACK:
+ case RSL_MT_IPAC_CRCX_NACK:
/* somehow the BTS was unable to bind the lchan to its local
* port?!? */
- DEBUGPC(DRSL, "IPAC_BIND_NACK ");
+ DEBUGPC(DRSL, "IPAC_CRCX_NACK ");
break;
- case RSL_MT_IPAC_CONNECT_ACK:
+ case RSL_MT_IPAC_MDCX_ACK:
/* the BTS tells us that a connect operation was successful */
- DEBUGPC(DRSL, "IPAC_CONNECT_ACK ");
+ DEBUGPC(DRSL, "IPAC_MDCX_ACK ");
break;
- case RSL_MT_IPAC_CONNECT_NACK:
+ case RSL_MT_IPAC_MDCX_NACK:
/* somehow the BTS was unable to connect the lchan to a remote
* port */
- DEBUGPC(DRSL, "IPAC_CONNECT_NACK ");
+ DEBUGPC(DRSL, "IPAC_MDCX_NACK ");
break;
- case RSL_MT_IPAC_DISCONNECT_IND:
- DEBUGPC(DRSL, "IPAC_DISCONNECT_IND ");
- rc = abis_rsl_rx_ipacc_disc_ind(msg);
+ case RSL_MT_IPAC_DLCX_IND:
+ DEBUGPC(DRSL, "IPAC_DLCX_IND ");
+ rc = abis_rsl_rx_ipacc_dlcx_ind(msg);
break;
default:
DEBUGPC(DRSL, "Unknown ip.access msg_type 0x%02x", rllh->c.msg_type);
diff --git a/openbsc/src/bsc_init.c b/openbsc/src/bsc_init.c
index 3d8b1b158..153e024e4 100644
--- a/openbsc/src/bsc_init.c
+++ b/openbsc/src/bsc_init.c
@@ -338,11 +338,6 @@ static unsigned char nanobts_attr_radio[] = {
NM_ATT_ARFCN_LIST, 0x00, 0x02, HARDCODED_ARFCN >> 8, HARDCODED_ARFCN & 0xff,
};
-static unsigned char nanobts_attr_e0[] = {
- NM_ATT_IPACC_STREAM_ID, 0x00,
- NM_ATT_IPACC_DST_IP_PORT, 0x0b, 0xbb, /* TCP PORT for RSL */
-};
-
/* Callback function to be called whenever we get a GSM 12.21 state change event */
int nm_state_event(enum nm_evt evt, u_int8_t obj_class, void *obj,
struct gsm_nm_state *old_state, struct gsm_nm_state *new_state)
@@ -351,51 +346,57 @@ int nm_state_event(enum nm_evt evt, u_int8_t obj_class, void *obj,
struct gsm_bts_trx *trx;
struct gsm_bts_trx_ts *ts;
- /* This is currently only required on nanoBTS */
+ /* This event-driven BTS setup is currently only required on nanoBTS */
- switch (evt) {
- case EVT_STATECHG_OPER:
- switch (obj_class) {
- case NM_OC_SITE_MANAGER:
- bts = container_of(obj, struct gsm_bts, site_mgr);
- if (old_state->operational != 2 && new_state->operational == 2) {
- abis_nm_opstart(bts, NM_OC_SITE_MANAGER, 0xff, 0xff, 0xff);
- }
- break;
- case NM_OC_BTS:
- bts = obj;
- if (new_state->availability == NM_AVSTATE_DEPENDENCY) {
- patch_nm_tables(bts);
- abis_nm_set_bts_attr(bts, nanobts_attr_bts,
- sizeof(nanobts_attr_bts));
- abis_nm_opstart(bts, NM_OC_BTS,
- bts->bts_nr, 0xff, 0xff);
- abis_nm_chg_adm_state(bts, NM_OC_BTS,
- bts->bts_nr, 0xff, 0xff,
- NM_STATE_UNLOCKED);
- }
- break;
- case NM_OC_CHANNEL:
- ts = obj;
- trx = ts->trx;
- if (new_state->availability == NM_AVSTATE_DEPENDENCY) {
- if (ts->nr == 0 && trx == trx->bts->c0)
- abis_nm_set_channel_attr(ts, NM_CHANC_BCCHComb);
- else
- abis_nm_set_channel_attr(ts, NM_CHANC_TCHFull);
- abis_nm_opstart(trx->bts, NM_OC_CHANNEL,
- trx->bts->bts_nr, trx->nr, ts->nr);
- abis_nm_chg_adm_state(trx->bts, NM_OC_CHANNEL,
- trx->bts->bts_nr, trx->nr, ts->nr,
- NM_STATE_UNLOCKED);
- }
- break;
- default:
- break;
+ /* EVT_STATECHG_ADM is called after we call chg_adm_state() and would create
+ * endless loop */
+ if (evt != EVT_STATECHG_OPER)
+ return 0;
+
+ switch (obj_class) {
+ case NM_OC_SITE_MANAGER:
+ bts = container_of(obj, struct gsm_bts, site_mgr);
+ if (new_state->operational == 2 &&
+ new_state->availability == NM_AVSTATE_OK)
+ abis_nm_opstart(bts, obj_class, 0xff, 0xff, 0xff);
+ break;
+ case NM_OC_BTS:
+ bts = obj;
+ if (new_state->availability == NM_AVSTATE_DEPENDENCY) {
+ patch_nm_tables(bts);
+ abis_nm_set_bts_attr(bts, nanobts_attr_bts,
+ sizeof(nanobts_attr_bts));
+ abis_nm_chg_adm_state(bts, obj_class,
+ bts->bts_nr, 0xff, 0xff,
+ NM_STATE_UNLOCKED);
+ abis_nm_opstart(bts, obj_class,
+ bts->bts_nr, 0xff, 0xff);
+ }
+ break;
+ case NM_OC_CHANNEL:
+ ts = obj;
+ trx = ts->trx;
+ if (new_state->operational == 1 &&
+ new_state->availability == NM_AVSTATE_DEPENDENCY) {
+ patch_nm_tables(trx->bts);
+ enum abis_nm_chan_comb ccomb =
+ abis_nm_chcomb4pchan(ts->pchan);
+ abis_nm_set_channel_attr(ts, ccomb);
+ abis_nm_chg_adm_state(trx->bts, obj_class,
+ trx->bts->bts_nr, trx->nr, ts->nr,
+ NM_STATE_UNLOCKED);
+ abis_nm_opstart(trx->bts, obj_class,
+ trx->bts->bts_nr, trx->nr, ts->nr);
}
break;
+ case NM_OC_RADIO_CARRIER:
+ trx = obj;
+ if (new_state->operational == 1 &&
+ new_state->availability == NM_AVSTATE_OK)
+ abis_nm_opstart(trx->bts, obj_class, trx->bts->bts_nr,
+ trx->nr, 0xff);
+ break;
default:
- //DEBUGP(DMM, "Unhandled state change in %s:%d\n", __func__, __LINE__);
break;
}
return 0;
@@ -405,36 +406,51 @@ int nm_state_event(enum nm_evt evt, u_int8_t obj_class, void *obj,
static int sw_activ_rep(struct msgb *mb)
{
struct abis_om_fom_hdr *foh = msgb_l3(mb);
- struct gsm_bts_trx *trx = mb->trx;
+ struct gsm_bts *bts = mb->trx->bts;
+ struct gsm_bts_trx *trx = gsm_bts_trx_num(bts, foh->obj_inst.trx_nr);
+
switch (foh->obj_class) {
case NM_OC_BASEB_TRANSC:
- /* TRX software is active, tell it to initiate RSL Link */
- abis_nm_ipaccess_msg(trx->bts, 0xe0, NM_OC_BASEB_TRANSC,
- trx->bts->bts_nr, trx->nr, 0xff,
- nanobts_attr_e0, sizeof(nanobts_attr_e0));
- abis_nm_opstart(trx->bts, NM_OC_BASEB_TRANSC,
+ abis_nm_chg_adm_state(trx->bts, foh->obj_class,
+ trx->bts->bts_nr, trx->nr, 0xff,
+ NM_STATE_UNLOCKED);
+ abis_nm_opstart(trx->bts, foh->obj_class,
trx->bts->bts_nr, trx->nr, 0xff);
- abis_nm_chg_adm_state(trx->bts, NM_OC_BASEB_TRANSC,
- trx->bts->bts_nr, trx->nr, 0xff,
- NM_STATE_UNLOCKED);
+ /* TRX software is active, tell it to initiate RSL Link */
+ abis_nm_ipaccess_rsl_connect(trx, 0, 3003, trx->rsl_tei);
break;
- case NM_OC_RADIO_CARRIER:
- patch_nm_tables(trx->bts);
+ case NM_OC_RADIO_CARRIER: {
+ /*
+ * Locking the radio carrier will make it go
+ * offline again and we would come here. The
+ * framework should determine that there was
+ * no change and avoid recursion.
+ *
+ * This code is here to make sure that on start
+ * a TRX remains locked.
+ */
+ int rc_state = trx->rf_locked ?
+ NM_STATE_LOCKED : NM_STATE_UNLOCKED;
+ /* Patch ARFCN into radio attribute */
+ nanobts_attr_radio[5] &= 0xf0;
+ nanobts_attr_radio[5] |= trx->arfcn >> 8;
+ nanobts_attr_radio[6] = trx->arfcn & 0xff;
abis_nm_set_radio_attr(trx, nanobts_attr_radio,
- sizeof(nanobts_attr_radio));
- abis_nm_opstart(trx->bts, NM_OC_RADIO_CARRIER,
- trx->bts->bts_nr, trx->nr, 0xff);
- abis_nm_chg_adm_state(trx->bts, NM_OC_RADIO_CARRIER,
+ sizeof(nanobts_attr_radio));
+ abis_nm_chg_adm_state(trx->bts, foh->obj_class,
trx->bts->bts_nr, trx->nr, 0xff,
- NM_STATE_UNLOCKED);
+ rc_state);
+ abis_nm_opstart(trx->bts, foh->obj_class, trx->bts->bts_nr,
+ trx->nr, 0xff);
break;
+ }
}
return 0;
}
/* Callback function for NACK on the OML NM */
-static int oml_msg_nack(int mt)
+static int oml_msg_nack(u_int8_t mt)
{
if (mt == NM_MT_SET_BTS_ATTR_NACK) {
fprintf(stderr, "Failed to set BTS attributes. That is fatal. "
@@ -449,11 +465,14 @@ static int oml_msg_nack(int mt)
static int nm_sig_cb(unsigned int subsys, unsigned int signal,
void *handler_data, void *signal_data)
{
+ u_int8_t *msg_type;
+
switch (signal) {
case S_NM_SW_ACTIV_REP:
return sw_activ_rep(signal_data);
case S_NM_NACK:
- return oml_msg_nack((int)signal_data);
+ msg_type = signal_data;
+ return oml_msg_nack(*msg_type);
default:
break;
}
@@ -527,7 +546,21 @@ static void nm_reconfig_trx(struct gsm_bts_trx *trx)
}
break;
case GSM_BTS_TYPE_NANOBTS:
- trx->nominal_power = 20;
+ switch (trx->bts->band) {
+ case GSM_BAND_850:
+ case GSM_BAND_900:
+ trx->nominal_power = 20;
+ break;
+ case GSM_BAND_1800:
+ case GSM_BAND_1900:
+ trx->nominal_power = 23;
+ break;
+ default:
+ fprintf(stderr, "Unsupported nanoBTS GSM band %s\n",
+ gsm_band_name(trx->bts->band));
+ break;
+ }
+ break;
default:
break;
}
@@ -870,9 +903,6 @@ static void patch_nm_tables(struct gsm_bts *bts)
bs11_attr_radio[2] &= 0xf0;
bs11_attr_radio[2] |= arfcn_high;
bs11_attr_radio[3] = arfcn_low;
- nanobts_attr_radio[5] &= 0xf0;
- nanobts_attr_radio[5] |= arfcn_high;
- nanobts_attr_radio[6] = arfcn_low;
/* patch BSIC */
bs11_attr_bts[1] = bts->bsic;
@@ -933,6 +963,10 @@ static void patch_si_tables(struct gsm_bts *bts)
type_4->cell_sel_par.ms_txpwr_max_ccch =
ms_pwr_ctl_lvl(bts->band, bts->ms_max_power);
+ /* Set NECI to influence channel request */
+ type_3->cell_sel_par.neci = bts->network->neci;
+ type_4->cell_sel_par.neci = bts->network->neci;
+
if (bts->cell_barred) {
type_1->rach_control.cell_bar = 1;
type_2->rach_control.cell_bar = 1;
diff --git a/openbsc/src/bsc_mgcp.c b/openbsc/src/bsc_mgcp.c
new file mode 100644
index 000000000..6d5e6b154
--- /dev/null
+++ b/openbsc/src/bsc_mgcp.c
@@ -0,0 +1,1172 @@
+/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
+
+/*
+ * (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * (C) 2009 by on-waves.com
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <limits.h>
+#include <unistd.h>
+
+#include <sys/socket.h>
+#include <arpa/inet.h>
+
+#include <openbsc/debug.h>
+#include <openbsc/msgb.h>
+#include <openbsc/talloc.h>
+#include <openbsc/gsm_data.h>
+#include <openbsc/select.h>
+#include <openbsc/mgcp.h>
+#include <openbsc/telnet_interface.h>
+
+#include <vty/command.h>
+#include <vty/vty.h>
+
+/* this is here for the vty... it will never be called */
+void subscr_put() { abort(); }
+
+#define _GNU_SOURCE
+#include <getopt.h>
+
+#warning "Make use of the rtp proxy code"
+
+static int source_port = 2427;
+static const char *local_ip = NULL;
+static const char *source_addr = "0.0.0.0";
+static struct bsc_fd bfd;
+static unsigned int number_endpoints = 0;
+static const char *bts_ip = NULL;
+static struct in_addr bts_in;
+static int first_request = 1;
+static const char *audio_name = "GSM-EFR/8000";
+static int audio_payload = 97;
+static int audio_loop = 0;
+static int early_bind = 0;
+
+static char *config_file = "mgcp.cfg";
+
+/* used by msgb and mgcp */
+void *tall_bsc_ctx = NULL;
+
+enum mgcp_connection_mode {
+ MGCP_CONN_NONE = 0,
+ MGCP_CONN_RECV_ONLY = 1,
+ MGCP_CONN_SEND_ONLY = 2,
+ MGCP_CONN_RECV_SEND = MGCP_CONN_RECV_ONLY | MGCP_CONN_SEND_ONLY,
+};
+
+enum {
+ DEST_NETWORK = 0,
+ DEST_BTS = 1,
+};
+
+enum {
+ PROTO_RTP,
+ PROTO_RTCP,
+};
+
+#define CI_UNUSED 0
+static unsigned int last_call_id = 0;
+
+struct mgcp_endpoint {
+ int ci;
+ char *callid;
+ char *local_options;
+ int conn_mode;
+
+ /* the local rtp port */
+ int rtp_port;
+
+ /*
+ * RTP mangling:
+ * - we get RTP and RTCP to us and need to forward to the BTS
+ * - we get RTP and RTCP from the BTS and forward to the network
+ */
+ struct bsc_fd local_rtp;
+ struct bsc_fd local_rtcp;
+
+ struct in_addr remote;
+
+ /* in network byte order */
+ int rtp, rtcp;
+ int bts_rtp, bts_rtcp;
+};
+
+static struct mgcp_endpoint *endpoints = NULL;
+#define ENDPOINT_NUMBER(endp) abs(endp - endpoints)
+
+/**
+ * Macro for tokenizing MGCP messages and SDP in one go.
+ *
+ */
+#define MSG_TOKENIZE_START \
+ line_start = 0; \
+ for (i = 0; i < msgb_l3len(msg); ++i) { \
+ /* we have a line end */ \
+ if (msg->l3h[i] == '\n') { \
+ /* skip the first line */ \
+ if (line_start == 0) { \
+ line_start = i + 1; \
+ continue; \
+ } \
+ \
+ /* check if we have a proper param */ \
+ if (i - line_start == 1 && msg->l3h[line_start] == '\r') { \
+ } else if (i - line_start > 2 \
+ && islower(msg->l3h[line_start]) \
+ && msg->l3h[line_start + 1] == '=') { \
+ } else if (i - line_start < 3 \
+ || msg->l3h[line_start + 1] != ':' \
+ || msg->l3h[line_start + 2] != ' ') \
+ goto error; \
+ \
+ msg->l3h[i] = '\0'; \
+ if (msg->l3h[i-1] == '\r') \
+ msg->l3h[i-1] = '\0';
+
+#define MSG_TOKENIZE_END \
+ line_start = i + 1; \
+ } \
+ }
+
+
+struct mgcp_msg_ptr {
+ unsigned int start;
+ unsigned int length;
+};
+
+struct mgcp_request {
+ char *name;
+ void (*handle_request) (struct msgb *msg, struct sockaddr_in *source);
+ char *debug_name;
+};
+
+#define MGCP_REQUEST(NAME, REQ, DEBUG_NAME) \
+ { .name = NAME, .handle_request = REQ, .debug_name = DEBUG_NAME },
+
+static void handle_audit_endpoint(struct msgb *msg, struct sockaddr_in *source);
+static void handle_create_con(struct msgb *msg, struct sockaddr_in *source);
+static void handle_delete_con(struct msgb *msg, struct sockaddr_in *source);
+static void handle_modify_con(struct msgb *msg, struct sockaddr_in *source);
+
+static int generate_call_id()
+{
+ int i;
+
+ /* use the call id */
+ ++last_call_id;
+
+ /* handle wrap around */
+ if (last_call_id == CI_UNUSED)
+ ++last_call_id;
+
+ /* callstack can only be of size number_of_endpoints */
+ /* verify that the call id is free, e.g. in case of overrun */
+ for (i = 1; i < number_endpoints; ++i)
+ if (endpoints[i].ci == last_call_id)
+ return generate_call_id();
+
+ return last_call_id;
+}
+
+/* FIXIME/TODO: need to have a list of pending transactions and check that */
+static unsigned int generate_transaction_id()
+{
+ return abs(rand());
+}
+
+static int _send(int fd, struct in_addr *addr, int port, char *buf, int len)
+{
+ struct sockaddr_in out;
+ out.sin_family = AF_INET;
+ out.sin_port = port;
+ memcpy(&out.sin_addr, addr, sizeof(*addr));
+
+ return sendto(fd, buf, len, 0, (struct sockaddr *)&out, sizeof(out));
+}
+
+/*
+ * There is data coming. We will have to figure out if it
+ * came from the BTS or the MediaGateway of the MSC. On top
+ * of that we need to figure out if it was RTP or RTCP.
+ *
+ * Currently we do not communicate with the BSC so we have
+ * no idea where the BTS is listening for RTP and need to
+ * do the classic routing trick. Wait for the first packet
+ * from the BTS and then go ahead.
+ */
+static int rtp_data_cb(struct bsc_fd *fd, unsigned int what)
+{
+ char buf[4096];
+ struct sockaddr_in addr;
+ socklen_t slen = sizeof(addr);
+ struct mgcp_endpoint *endp;
+ int rc, dest, proto;
+
+ endp = (struct mgcp_endpoint *) fd->data;
+
+ rc = recvfrom(fd->fd, &buf, sizeof(buf), 0,
+ (struct sockaddr *) &addr, &slen);
+ if (rc < 0) {
+ DEBUGP(DMGCP, "Failed to receive message on: 0x%x\n",
+ ENDPOINT_NUMBER(endp));
+ return -1;
+ }
+
+ /* do not forward aynthing... maybe there is a packet from the bts */
+ if (endp->ci == CI_UNUSED)
+ return -1;
+
+ /*
+ * Figure out where to forward it to. This code assumes that we
+ * have received the Connection Modify and know who is a legitimate
+ * partner. According to the spec we could attempt to forward even
+ * after the Create Connection but we will not as we are not really
+ * able to tell if this is legitimate.
+ */
+ #warning "Slight spec violation. With connection mode recvonly we should attempt to forward."
+ dest = memcmp(&addr.sin_addr, &endp->remote, sizeof(addr.sin_addr)) == 0
+ ? DEST_BTS : DEST_NETWORK;
+ proto = fd == &endp->local_rtp ? PROTO_RTP : PROTO_RTCP;
+
+ /* We have no idea who called us, maybe it is the BTS. */
+ if (dest == DEST_NETWORK && endp->bts_rtp == 0) {
+ /* it was the BTS... */
+ if (memcmp(&addr.sin_addr, &bts_in, sizeof(bts_in)) == 0) {
+ if (fd == &endp->local_rtp) {
+ endp->bts_rtp = addr.sin_port;
+ } else {
+ endp->bts_rtcp = addr.sin_port;
+ }
+
+ DEBUGP(DMGCP, "Found BTS for endpoint: 0x%x on port: %d/%d\n",
+ ENDPOINT_NUMBER(endp), ntohs(endp->bts_rtp), ntohs(endp->bts_rtcp));
+ }
+ }
+
+ /* dispatch */
+ if (audio_loop)
+ dest = !dest;
+
+ if (dest == DEST_NETWORK) {
+ return _send(fd->fd, &endp->remote,
+ proto == PROTO_RTP ? endp->rtp : endp->rtcp,
+ buf, rc);
+ } else {
+ return _send(fd->fd, &bts_in,
+ proto == PROTO_RTP ? endp->bts_rtp : endp->bts_rtcp,
+ buf, rc);
+ }
+}
+
+static int create_bind(struct bsc_fd *fd, int port)
+{
+ struct sockaddr_in addr;
+ int on = 1;
+
+ fd->fd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (fd->fd < 0)
+ return -1;
+
+ setsockopt(fd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(port);
+ inet_aton(source_addr, &addr.sin_addr);
+
+ if (bind(fd->fd, (struct sockaddr *) &addr, sizeof(addr)) < 0)
+ return -1;
+
+ return 0;
+}
+
+static int bind_rtp(struct mgcp_endpoint *endp)
+{
+ /* set to zero until we get the info */
+ memset(&endp->remote, 0, sizeof(endp->remote));
+
+ if (create_bind(&endp->local_rtp, endp->rtp_port) != 0) {
+ DEBUGP(DMGCP, "Failed to create RTP port: %d on 0x%x\n",
+ endp->rtp_port, ENDPOINT_NUMBER(endp));
+ goto cleanup0;
+ }
+
+ if (create_bind(&endp->local_rtcp, endp->rtp_port + 1) != 0) {
+ DEBUGP(DMGCP, "Failed to create RTCP port: %d on 0x%x\n",
+ endp->rtp_port + 1, ENDPOINT_NUMBER(endp));
+ goto cleanup1;
+ }
+
+ endp->local_rtp.cb = rtp_data_cb;
+ endp->local_rtp.data = endp;
+ endp->local_rtp.when = BSC_FD_READ;
+ if (bsc_register_fd(&endp->local_rtp) != 0) {
+ DEBUGP(DMGCP, "Failed to register RTP port %d on 0x%x\n",
+ endp->rtp_port, ENDPOINT_NUMBER(endp));
+ goto cleanup2;
+ }
+
+ endp->local_rtcp.cb = rtp_data_cb;
+ endp->local_rtcp.data = endp;
+ endp->local_rtcp.when = BSC_FD_READ;
+ if (bsc_register_fd(&endp->local_rtcp) != 0) {
+ DEBUGP(DMGCP, "Failed to register RTCP port %d on 0x%x\n",
+ endp->rtp_port + 1, ENDPOINT_NUMBER(endp));
+ goto cleanup3;
+ }
+
+ return 0;
+
+cleanup3:
+ bsc_unregister_fd(&endp->local_rtp);
+cleanup2:
+ close(endp->local_rtcp.fd);
+ endp->local_rtcp.fd = -1;
+cleanup1:
+ close(endp->local_rtp.fd);
+ endp->local_rtp.fd = -1;
+cleanup0:
+ return -1;
+}
+
+/*
+ * array of function pointers for handling various
+ * messages. In the future this might be binary sorted
+ * for performance reasons.
+ */
+static const struct mgcp_request mgcp_requests [] = {
+ MGCP_REQUEST("AUEP", handle_audit_endpoint, "AuditEndpoint")
+ MGCP_REQUEST("CRCX", handle_create_con, "CreateConnection")
+ MGCP_REQUEST("DLCX", handle_delete_con, "DeleteConnection")
+ MGCP_REQUEST("MDCX", handle_modify_con, "ModifiyConnection")
+};
+
+static void send_response_with_data(int code, const char *msg, const char *trans,
+ const char *data, struct sockaddr_in *source)
+{
+ char buf[4096];
+ int len;
+
+ if (data) {
+ len = snprintf(buf, sizeof(buf), "%d %s\n%s", code, trans, data);
+ } else {
+ len = snprintf(buf, sizeof(buf), "%d %s\n", code, trans);
+ }
+ DEBUGP(DMGCP, "Sending response: code: %d for '%s'\n", code, msg);
+
+ sendto(bfd.fd, buf, len, 0, (struct sockaddr *)source, sizeof(*source));
+}
+
+static void send_response(int code, const char *msg, const char *trans, struct sockaddr_in *source)
+{
+ send_response_with_data(code, msg, trans, NULL, source);
+}
+
+static void send_with_sdp(struct mgcp_endpoint *endp, const char *msg, const char *trans_id, struct sockaddr_in *source)
+{
+ const char *addr = local_ip;
+ char sdp_record[4096];
+
+ if (!addr)
+ addr = source_addr;
+
+ snprintf(sdp_record, sizeof(sdp_record) - 1,
+ "I: %d\n\n"
+ "v=0\r\n"
+ "c=IN IP4 %s\r\n"
+ "m=audio %d RTP/AVP %d\r\n"
+ "a=rtpmap:%d %s\r\n",
+ endp->ci, addr, endp->rtp_port,
+ audio_payload, audio_payload, audio_name);
+ return send_response_with_data(200, msg, trans_id, sdp_record, source);
+}
+
+/* send a static record */
+static void send_rsip(struct sockaddr_in *source)
+{
+ char reset[4096];
+ int len, rc;
+
+ len = snprintf(reset, sizeof(reset) - 1,
+ "RSIP %u *@mgw MGCP 1.0\n"
+ "RM: restart\n", generate_transaction_id());
+ rc = sendto(bfd.fd, reset, len, 0, (struct sockaddr *) source, sizeof(*source));
+ if (rc < 0) {
+ DEBUGP(DMGCP, "Failed to send RSIP: %d\n", rc);
+ }
+}
+
+/*
+ * handle incoming messages:
+ * - this can be a command (four letters, space, transaction id)
+ * - or a response (three numbers, space, transaction id)
+ */
+static void handle_message(struct msgb *msg, struct sockaddr_in *source)
+{
+ int code;
+
+ if (msg->len < 4) {
+ DEBUGP(DMGCP, "mgs too short: %d\n", msg->len);
+ return;
+ }
+
+ /* attempt to treat it as a response */
+ if (sscanf((const char *)&msg->data[0], "%3d %*s", &code) == 1) {
+ DEBUGP(DMGCP, "Response: Code: %d\n", code);
+ } else {
+ int i, handled = 0;
+ msg->l3h = &msg->l2h[4];
+ for (i = 0; i < ARRAY_SIZE(mgcp_requests); ++i)
+ if (strncmp(mgcp_requests[i].name, (const char *) &msg->data[0], 4) == 0) {
+ handled = 1;
+ mgcp_requests[i].handle_request(msg, source);
+ }
+ if (!handled) {
+ DEBUGP(DMGCP, "MSG with type: '%.4s' not handled\n", &msg->data[0]);
+ }
+ }
+}
+
+/* string tokenizer for the poor */
+static int find_msg_pointers(struct msgb *msg, struct mgcp_msg_ptr *ptrs, int ptrs_length)
+{
+ int i, found = 0;
+
+ int whitespace = 1;
+ for (i = 0; i < msgb_l3len(msg) && ptrs_length > 0; ++i) {
+ /* if we have a space we found an end */
+ if (msg->l3h[i] == ' ' || msg->l3h[i] == '\r' || msg->l3h[i] == '\n') {
+ if (!whitespace) {
+ ++found;
+ whitespace = 1;
+ ptrs->length = i - ptrs->start - 1;
+ ++ptrs;
+ --ptrs_length;
+ } else {
+ /* skip any number of whitespace */
+ }
+
+ /* line end... stop */
+ if (msg->l3h[i] == '\r' || msg->l3h[i] == '\n')
+ break;
+ } else if (msg->l3h[i] == '\r' || msg->l3h[i] == '\n') {
+ /* line end, be done */
+ break;
+ } else if (whitespace) {
+ whitespace = 0;
+ ptrs->start = i;
+ }
+ }
+
+ if (ptrs_length == 0)
+ return -1;
+ return found;
+}
+
+static struct mgcp_endpoint *find_endpoint(const char *mgcp)
+{
+ char *endptr = NULL;
+ unsigned int gw = INT_MAX;
+
+ gw = strtoul(mgcp, &endptr, 16);
+ if (gw == 0 || gw >= number_endpoints || strcmp(endptr, "@mgw") != 0) {
+ DEBUGP(DMGCP, "Not able to find endpoint: '%s'\n", mgcp);
+ return NULL;
+ }
+
+ return &endpoints[gw];
+}
+
+static int analyze_header(struct msgb *msg, struct mgcp_msg_ptr *ptr, int size,
+ const char **transaction_id, struct mgcp_endpoint **endp)
+{
+ int found;
+
+ if (size < 3) {
+ DEBUGP(DMGCP, "Not enough space in ptr\n");
+ return -1;
+ }
+
+ found = find_msg_pointers(msg, ptr, size);
+
+ if (found < 3) {
+ DEBUGP(DMGCP, "Gateway: Not enough params. Found: %d\n", found);
+ return -1;
+ }
+
+ /*
+ * replace the space with \0. the main method gurantess that
+ * we still have + 1 for null termination
+ */
+ msg->l3h[ptr[3].start + ptr[3].length + 1] = '\0';
+ msg->l3h[ptr[2].start + ptr[2].length + 1] = '\0';
+ msg->l3h[ptr[1].start + ptr[1].length + 1] = '\0';
+ msg->l3h[ptr[0].start + ptr[0].length + 1] = '\0';
+
+ if (strncmp("1.0", (const char *)&msg->l3h[ptr[3].start], 3) != 0
+ || strncmp("MGCP", (const char *)&msg->l3h[ptr[2].start], 4) != 0) {
+ DEBUGP(DMGCP, "Wrong MGCP version. Not handling: '%s' '%s'\n",
+ (const char *)&msg->l3h[ptr[3].start],
+ (const char *)&msg->l3h[ptr[2].start]);
+ return -1;
+ }
+
+ *transaction_id = (const char *)&msg->l3h[ptr[0].start];
+ *endp = find_endpoint((const char *)&msg->l3h[ptr[1].start]);
+ return *endp == NULL;
+}
+
+static int verify_call_id(const struct mgcp_endpoint *endp,
+ const char *callid)
+{
+ if (strcmp(endp->callid, callid) != 0) {
+ DEBUGP(DMGCP, "CallIDs does not match on 0x%x. '%s' != '%s'\n",
+ ENDPOINT_NUMBER(endp), endp->callid, callid);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int verify_ci(const struct mgcp_endpoint *endp,
+ const char *ci)
+{
+ if (atoi(ci) != endp->ci) {
+ DEBUGP(DMGCP, "ConnectionIdentifiers do not match on 0x%x. %d != %s\n",
+ ENDPOINT_NUMBER(endp), endp->ci, ci);
+ return -1;
+ }
+
+ return 0;
+}
+
+static void handle_audit_endpoint(struct msgb *msg, struct sockaddr_in *source)
+{
+ struct mgcp_msg_ptr data_ptrs[6];
+ int found, response;
+ const char *trans_id;
+ struct mgcp_endpoint *endp;
+
+ found = analyze_header(msg, data_ptrs, ARRAY_SIZE(data_ptrs), &trans_id, &endp);
+ if (found != 0)
+ response = 500;
+ else
+ response = 200;
+
+ return send_response(response, "AUEP", trans_id, source);
+}
+
+static int parse_conn_mode(const char* msg, int *conn_mode)
+{
+ int ret = 0;
+ if (strcmp(msg, "recvonly") == 0)
+ *conn_mode = MGCP_CONN_RECV_ONLY;
+ else if (strcmp(msg, "sendrecv") == 0)
+ *conn_mode = MGCP_CONN_RECV_SEND;
+ else {
+ DEBUGP(DMGCP, "Unknown connection mode: '%s'\n", msg);
+ ret = -1;
+ }
+
+ return ret;
+}
+
+static void handle_create_con(struct msgb *msg, struct sockaddr_in *source)
+{
+ struct mgcp_msg_ptr data_ptrs[6];
+ int found, i, line_start;
+ const char *trans_id;
+ struct mgcp_endpoint *endp;
+ int error_code = 500;
+
+ found = analyze_header(msg, data_ptrs, ARRAY_SIZE(data_ptrs), &trans_id, &endp);
+ if (found != 0)
+ return send_response(500, "CRCX", trans_id, source);
+
+ if (endp->ci != CI_UNUSED) {
+ DEBUGP(DMGCP, "Endpoint is already used. 0x%x\n", ENDPOINT_NUMBER(endp));
+ return send_response(500, "CRCX", trans_id, source);
+ }
+
+ /* parse CallID C: and LocalParameters L: */
+ MSG_TOKENIZE_START
+ switch (msg->l3h[line_start]) {
+ case 'L':
+ endp->local_options = talloc_strdup(endpoints,
+ (const char *)&msg->l3h[line_start + 3]);
+ break;
+ case 'C':
+ endp->callid = talloc_strdup(endpoints,
+ (const char *)&msg->l3h[line_start + 3]);
+ break;
+ case 'M':
+ if (parse_conn_mode((const char *)&msg->l3h[line_start + 3],
+ &endp->conn_mode) != 0) {
+ error_code = 517;
+ goto error2;
+ }
+ break;
+ default:
+ DEBUGP(DMGCP, "Unhandled option: '%c'/%d on 0x%x\n",
+ msg->l3h[line_start], msg->l3h[line_start],
+ ENDPOINT_NUMBER(endp));
+ break;
+ }
+ MSG_TOKENIZE_END
+
+ /* initialize */
+ endp->rtp = endp->rtcp = endp->bts_rtp = endp->bts_rtcp = 0;
+
+ /* bind to the port now */
+ endp->rtp_port = rtp_calculate_port(ENDPOINT_NUMBER(endp), rtp_base_port);
+ if (!early_bind && bind_rtp(endp) != 0)
+ goto error2;
+
+ /* assign a local call identifier or fail */
+ endp->ci = generate_call_id();
+ if (endp->ci == CI_UNUSED)
+ goto error2;
+
+ DEBUGP(DMGCP, "Creating endpoint on: 0x%x CI: %u port: %u\n",
+ ENDPOINT_NUMBER(endp), endp->ci, endp->rtp_port);
+ return send_with_sdp(endp, "CRCX", trans_id, source);
+error:
+ DEBUGP(DMGCP, "Malformed line: %s on 0x%x with: line_start: %d %d\n",
+ hexdump(msg->l3h, msgb_l3len(msg)),
+ ENDPOINT_NUMBER(endp), line_start, i);
+ return send_response(error_code, "CRCX", trans_id, source);
+
+error2:
+ DEBUGP(DMGCP, "Resource error on 0x%x\n", ENDPOINT_NUMBER(endp));
+ return send_response(error_code, "CRCX", trans_id, source);
+}
+
+static void handle_modify_con(struct msgb *msg, struct sockaddr_in *source)
+{
+ struct mgcp_msg_ptr data_ptrs[6];
+ int found, i, line_start;
+ const char *trans_id;
+ struct mgcp_endpoint *endp;
+ int error_code = 500;
+
+ found = analyze_header(msg, data_ptrs, ARRAY_SIZE(data_ptrs), &trans_id, &endp);
+ if (found != 0)
+ return send_response(error_code, "MDCX", trans_id, source);
+
+ if (endp->ci == CI_UNUSED) {
+ DEBUGP(DMGCP, "Endpoint is not holding a connection. 0x%x\n", ENDPOINT_NUMBER(endp));
+ return send_response(error_code, "MDCX", trans_id, source);
+ }
+
+ MSG_TOKENIZE_START
+ switch (msg->l3h[line_start]) {
+ case 'C': {
+ if (verify_call_id(endp, (const char *)&msg->l3h[line_start + 3]) != 0)
+ goto error3;
+ break;
+ }
+ case 'I': {
+ if (verify_ci(endp, (const char *)&msg->l3h[line_start + 3]) != 0)
+ goto error3;
+ break;
+ }
+ case 'L':
+ /* skip */
+ break;
+ case 'M':
+ if (parse_conn_mode((const char *)&msg->l3h[line_start + 3],
+ &endp->conn_mode) != 0) {
+ error_code = 517;
+ goto error3;
+ }
+ break;
+ case '\0':
+ /* SDP file begins */
+ break;
+ case 'a':
+ case 'o':
+ case 's':
+ case 't':
+ case 'v':
+ /* skip these SDP attributes */
+ break;
+ case 'm': {
+ int port;
+ const char *param = (const char *)&msg->l3h[line_start];
+
+ if (sscanf(param, "m=audio %d RTP/AVP %*d", &port) == 1) {
+ endp->rtp = htons(port);
+ endp->rtcp = htons(port + 1);
+ }
+ break;
+ }
+ case 'c': {
+ char ipv4[16];
+ const char *param = (const char *)&msg->l3h[line_start];
+
+ if (sscanf(param, "c=IN IP4 %15s", ipv4) == 1) {
+ inet_aton(ipv4, &endp->remote);
+ }
+ break;
+ }
+ default:
+ DEBUGP(DMGCP, "Unhandled option: '%c'/%d on 0x%x\n",
+ msg->l3h[line_start], msg->l3h[line_start],
+ ENDPOINT_NUMBER(endp));
+ break;
+ }
+ MSG_TOKENIZE_END
+
+ /* modify */
+ DEBUGP(DMGCP, "Modified endpoint on: 0x%x Server: %s:%u\n",
+ ENDPOINT_NUMBER(endp), inet_ntoa(endp->remote), endp->rtp);
+ return send_with_sdp(endp, "MDCX", trans_id, source);
+
+error:
+ DEBUGP(DMGCP, "Malformed line: %s on 0x%x with: line_start: %d %d %d\n",
+ hexdump(msg->l3h, msgb_l3len(msg)),
+ ENDPOINT_NUMBER(endp), line_start, i, msg->l3h[line_start]);
+ return send_response(error_code, "MDCX", trans_id, source);
+
+error3:
+ return send_response(error_code, "MDCX", trans_id, source);
+}
+
+static void handle_delete_con(struct msgb *msg, struct sockaddr_in *source)
+{
+ struct mgcp_msg_ptr data_ptrs[6];
+ int found, i, line_start;
+ const char *trans_id;
+ struct mgcp_endpoint *endp;
+ int error_code = 500;
+
+ found = analyze_header(msg, data_ptrs, ARRAY_SIZE(data_ptrs), &trans_id, &endp);
+ if (found != 0)
+ return send_response(error_code, "DLCX", trans_id, source);
+
+ if (endp->ci == CI_UNUSED) {
+ DEBUGP(DMGCP, "Endpoint is not used. 0x%x\n", ENDPOINT_NUMBER(endp));
+ return send_response(error_code, "DLCX", trans_id, source);
+ }
+
+ MSG_TOKENIZE_START
+ switch (msg->l3h[line_start]) {
+ case 'C': {
+ if (verify_call_id(endp, (const char *)&msg->l3h[line_start + 3]) != 0)
+ goto error3;
+ break;
+ }
+ case 'I': {
+ if (verify_ci(endp, (const char *)&msg->l3h[line_start + 3]) != 0)
+ goto error3;
+ break;
+ }
+ default:
+ DEBUGP(DMGCP, "Unhandled option: '%c'/%d on 0x%x\n",
+ msg->l3h[line_start], msg->l3h[line_start],
+ ENDPOINT_NUMBER(endp));
+ break;
+ }
+ MSG_TOKENIZE_END
+
+
+ /* free the connection */
+ DEBUGP(DMGCP, "Deleting endpoint on: 0x%x\n", ENDPOINT_NUMBER(endp));
+ endp->ci= CI_UNUSED;
+ talloc_free(endp->callid);
+ talloc_free(endp->local_options);
+
+ if (!early_bind) {
+ bsc_unregister_fd(&endp->local_rtp);
+ bsc_unregister_fd(&endp->local_rtcp);
+ }
+
+ endp->rtp = endp->rtcp = endp->bts_rtp = endp->bts_rtcp = 0;
+
+ return send_response(250, "DLCX", trans_id, source);
+
+error:
+ DEBUGP(DMGCP, "Malformed line: %s on 0x%x with: line_start: %d %d\n",
+ hexdump(msg->l3h, msgb_l3len(msg)),
+ ENDPOINT_NUMBER(endp), line_start, i);
+ return send_response(error_code, "DLCX", trans_id, source);
+
+error3:
+ return send_response(error_code, "DLCX", trans_id, source);
+}
+
+static void print_help()
+{
+ printf("Some useful help...\n");
+ printf(" -h --help is printing this text.\n");
+ printf(" -c --config-file filename The config file to use.\n");
+}
+
+static void handle_options(int argc, char** argv)
+{
+ while (1) {
+ int option_index = 0, c;
+ static struct option long_options[] = {
+ {"help", 0, 0, 'h'},
+ {"config-file", 1, 0, 'c'},
+ {0, 0, 0, 0},
+ };
+
+ c = getopt_long(argc, argv, "hc:", long_options, &option_index);
+
+ if (c == -1)
+ break;
+
+ switch(c) {
+ case 'h':
+ print_help();
+ exit(0);
+ break;
+ case 'c':
+ config_file = talloc_strdup(tall_bsc_ctx, optarg);
+ break;
+ default:
+ /* ignore */
+ break;
+ };
+ }
+}
+
+static int read_call_agent(struct bsc_fd *fd, unsigned int what)
+{
+ struct sockaddr_in addr;
+ socklen_t slen = sizeof(addr);
+ struct msgb *msg;
+
+ msg = (struct msgb *) fd->data;
+
+ /* read one less so we can use it as a \0 */
+ int rc = recvfrom(bfd.fd, msg->data, msg->data_len - 1, 0,
+ (struct sockaddr *) &addr, &slen);
+ if (rc < 0) {
+ perror("Gateway failed to read");
+ return -1;
+ } else if (slen > sizeof(addr)) {
+ fprintf(stderr, "Gateway received message from outerspace: %d %d\n",
+ slen, sizeof(addr));
+ return -1;
+ }
+
+ if (first_request) {
+ first_request = 0;
+ send_rsip(&addr);
+ return 0;
+ }
+
+ /* handle message now */
+ msg->l2h = msgb_put(msg, rc);
+ handle_message(msg, &addr);
+ msgb_reset(msg);
+ return 0;
+}
+
+/*
+ * vty code for mgcp below
+ */
+struct cmd_node mgcp_node = {
+ MGCP_NODE,
+ "%s(mgcp)#",
+ 1,
+};
+
+static int config_write_mgcp(struct vty *vty)
+{
+ vty_out(vty, "mgcp%s", VTY_NEWLINE);
+ if (local_ip)
+ vty_out(vty, " local ip %s%s", local_ip, VTY_NEWLINE);
+ vty_out(vty, " bts ip %s%s", bts_ip, VTY_NEWLINE);
+ vty_out(vty, " bind ip %s%s", source_addr, VTY_NEWLINE);
+ vty_out(vty, " bind port %u%s", source_port, VTY_NEWLINE);
+ vty_out(vty, " bind early %u%s", !!early_bind, VTY_NEWLINE);
+ vty_out(vty, " rtp base %u%s", rtp_base_port, VTY_NEWLINE);
+ vty_out(vty, " sdp audio payload number %u%s", audio_payload, VTY_NEWLINE);
+ vty_out(vty, " sdp audio payload name %s%s", audio_name, VTY_NEWLINE);
+ vty_out(vty, " loop %u%s", !!audio_loop, VTY_NEWLINE);
+ vty_out(vty, " endpoints %u%s", number_endpoints, VTY_NEWLINE);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(show_mcgp, show_mgcp_cmd, "show mgcp",
+ SHOW_STR "Display information about the MGCP Media Gateway")
+{
+ int i;
+
+ vty_out(vty, "MGCP is up and running with %u endpoints:%s", number_endpoints - 1, VTY_NEWLINE);
+ for (i = 1; i < number_endpoints; ++i) {
+ struct mgcp_endpoint *endp = &endpoints[i];
+ vty_out(vty, " Endpoint 0x%.2x: CI: %d net: %u/%u bts: %u/%u%s",
+ i, endp->ci,
+ ntohs(endp->rtp), ntohs(endp->rtcp),
+ ntohs(endp->bts_rtp), ntohs(endp->bts_rtcp), VTY_NEWLINE);
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_mgcp,
+ cfg_mgcp_cmd,
+ "mgcp",
+ "Configure the MGCP")
+{
+ vty->node = MGCP_NODE;
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_mgcp_local_ip,
+ cfg_mgcp_local_ip_cmd,
+ "local ip IP",
+ "Set the IP to be used in SDP records")
+{
+ local_ip = talloc_strdup(tall_bsc_ctx, argv[0]);
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_mgcp_bts_ip,
+ cfg_mgcp_bts_ip_cmd,
+ "bts ip IP",
+ "Set the IP of the BTS for RTP forwarding")
+{
+ bts_ip = talloc_strdup(tall_bsc_ctx, argv[0]);
+ inet_aton(bts_ip, &bts_in);
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_mgcp_bind_ip,
+ cfg_mgcp_bind_ip_cmd,
+ "bind ip IP",
+ "Bind the MGCP to this local addr")
+{
+ source_addr = talloc_strdup(tall_bsc_ctx, argv[0]);
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_mgcp_bind_port,
+ cfg_mgcp_bind_port_cmd,
+ "bind port <0-65534>",
+ "Bind the MGCP to this port")
+{
+ unsigned int port = atoi(argv[0]);
+ if (port > 65534) {
+ vty_out(vty, "%% wrong bind port '%s'%s", argv[0], VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ source_port = port;
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_mgcp_bind_early,
+ cfg_mgcp_bind_early_cmd,
+ "bind early (0|1)",
+ "Bind all RTP ports early")
+{
+ unsigned int bind = atoi(argv[0]);
+ if (bind != 0 && bind != 1) {
+ vty_out(vty, "%% param must be 0 or 1.%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ early_bind = bind == 1;
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_mgcp_rtp_base_port,
+ cfg_mgcp_rtp_base_port_cmd,
+ "rtp base <0-65534>",
+ "Base port to use")
+{
+ unsigned int port = atoi(argv[0]);
+ if (port > 65534) {
+ vty_out(vty, "%% wrong base port '%s'%s", argv[0], VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ rtp_base_port = port;
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_mgcp_sdp_payload_number,
+ cfg_mgcp_sdp_payload_number_cmd,
+ "sdp audio payload number <1-255>",
+ "Set the audio codec to use")
+{
+ unsigned int payload = atoi(argv[0]);
+ if (payload > 255) {
+ vty_out(vty, "%% wrong payload number '%s'%s", argv[0], VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ audio_payload = payload;
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_mgcp_sdp_payload_name,
+ cfg_mgcp_sdp_payload_name_cmd,
+ "sdp audio payload name NAME",
+ "Set the audio name to use")
+{
+ audio_name = talloc_strdup(tall_bsc_ctx, argv[0]);
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_mgcp_loop,
+ cfg_mgcp_loop_cmd,
+ "loop (0|1)",
+ "Loop the audio")
+{
+ audio_loop = atoi(argv[0]);
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_mgcp_number_endp,
+ cfg_mgcp_number_endp_cmd,
+ "number endpoints <0-65534>",
+ "The number of endpoints to allocate. This is not dynamic.")
+{
+ /* + 1 as we start counting at one */
+ number_endpoints = atoi(argv[0]) + 1;
+ return CMD_SUCCESS;
+}
+
+int bsc_vty_init(struct gsm_network *dummy)
+{
+ cmd_init(1);
+ vty_init();
+
+ install_element(VIEW_NODE, &show_mgcp_cmd);
+
+
+ install_element(CONFIG_NODE, &cfg_mgcp_cmd);
+ install_node(&mgcp_node, config_write_mgcp);
+ install_default(MGCP_NODE);
+ install_element(MGCP_NODE, &cfg_mgcp_local_ip_cmd);
+ install_element(MGCP_NODE, &cfg_mgcp_bts_ip_cmd);
+ install_element(MGCP_NODE, &cfg_mgcp_bind_ip_cmd);
+ install_element(MGCP_NODE, &cfg_mgcp_bind_port_cmd);
+ install_element(MGCP_NODE, &cfg_mgcp_bind_early_cmd);
+ install_element(MGCP_NODE, &cfg_mgcp_rtp_base_port_cmd);
+ install_element(MGCP_NODE, &cfg_mgcp_sdp_payload_number_cmd);
+ install_element(MGCP_NODE, &cfg_mgcp_sdp_payload_name_cmd);
+ install_element(MGCP_NODE, &cfg_mgcp_loop_cmd);
+ install_element(MGCP_NODE, &cfg_mgcp_number_endp_cmd);
+ return 0;
+}
+
+int main(int argc, char** argv)
+{
+ struct gsm_network dummy_network;
+ struct sockaddr_in addr;
+ int on = 1, i, rc;
+
+ tall_bsc_ctx = talloc_named_const(NULL, 1, "mgcp-callagent");
+ handle_options(argc, argv);
+
+ telnet_init(&dummy_network, 4243);
+ rc = vty_read_config_file(config_file);
+ if (rc < 0) {
+ fprintf(stderr, "Failed to parse the config file: '%s'\n", config_file);
+ return rc;
+ }
+
+
+ if (!bts_ip) {
+ fprintf(stderr, "Need to specify the BTS ip address for RTP handling.\n");
+ return -1;
+ }
+
+ endpoints = _talloc_zero_array(tall_bsc_ctx,
+ sizeof(struct mgcp_endpoint),
+ number_endpoints, "endpoints");
+ if (!endpoints) {
+ fprintf(stderr, "Failed to allocate endpoints: %d. Quitting.\n", number_endpoints);
+ return -1;
+ }
+
+ /* Initialize all endpoints */
+ for (i = 0; i < number_endpoints; ++i) {
+ endpoints[i].local_rtp.fd = -1;
+ endpoints[i].local_rtcp.fd = -1;
+ endpoints[i].ci = CI_UNUSED;
+ }
+
+ /* initialize the socket */
+ bfd.when = BSC_FD_READ;
+ bfd.cb = read_call_agent;
+ bfd.fd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (bfd.fd < 0) {
+ perror("Gateway failed to listen");
+ return -1;
+ }
+
+ setsockopt(bfd.fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(source_port);
+ inet_aton(source_addr, &addr.sin_addr);
+
+ if (bind(bfd.fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ perror("Gateway failed to bind");
+ return -1;
+ }
+
+ bfd.data = msgb_alloc(4096, "mgcp-msg");
+ if (!bfd.data) {
+ fprintf(stderr, "Gateway memory error.\n");
+ return -1;
+ }
+
+
+ if (bsc_register_fd(&bfd) != 0) {
+ DEBUGP(DMGCP, "Failed to register the fd\n");
+ return -1;
+ }
+
+ /* initialisation */
+ srand(time(NULL));
+
+ /* early bind */
+ if (early_bind) {
+ for (i = 1; i < number_endpoints; ++i) {
+ struct mgcp_endpoint *endp = &endpoints[i];
+ endp->rtp_port = rtp_calculate_port(ENDPOINT_NUMBER(endp), rtp_base_port);
+ if (bind_rtp(endp) != 0)
+ return -1;
+ }
+ }
+
+ /* main loop */
+ while (1) {
+ bsc_select_main(0);
+ }
+
+
+ return 0;
+}
diff --git a/openbsc/src/chan_alloc.c b/openbsc/src/chan_alloc.c
index 48c728c09..7ba679c87 100644
--- a/openbsc/src/chan_alloc.c
+++ b/openbsc/src/chan_alloc.c
@@ -212,6 +212,12 @@ struct gsm_lchan *lchan_alloc(struct gsm_bts *bts, enum gsm_chan_t type)
lchan->type = type;
lchan->use_count = 0;
+ /* clear sapis */
+ memset(lchan->sapis, 0, ARRAY_SIZE(lchan->sapis));
+
+ /* clear multi rate config */
+ memset(&lchan->mr_conf, 0, sizeof(lchan->mr_conf));
+
/* Configure the time and start it so it will be closed */
lchan->release_timer.cb = auto_release_channel;
lchan->release_timer.data = lchan;
@@ -227,7 +233,7 @@ void lchan_free(struct gsm_lchan *lchan)
lchan->type = GSM_LCHAN_NONE;
if (lchan->subscr) {
subscr_put(lchan->subscr);
- lchan->subscr = 0;
+ lchan->subscr = NULL;
}
/* We might kill an active channel... */
@@ -304,5 +310,5 @@ struct gsm_lchan *lchan_for_subscr(struct gsm_subscriber *subscr)
return lchan;
}
- return 0;
+ return NULL;
}
diff --git a/openbsc/src/db.c b/openbsc/src/db.c
index 270d4d90b..369505a2c 100644
--- a/openbsc/src/db.c
+++ b/openbsc/src/db.c
@@ -439,9 +439,11 @@ int db_sync_equipment(struct gsm_equipment *equip)
{
dbi_result result;
unsigned char *cm2, *cm3;
+ u_int8_t classmark1;
+ memcpy(&classmark1, &equip->classmark1, sizeof(classmark1));
printf("DB: Sync Equipment IMEI=%s, classmark1=%02x",
- equip->imei, equip->classmark1);
+ equip->imei, classmark1);
if (equip->classmark2_len)
printf(", classmark2=%s",
hexdump(equip->classmark2, equip->classmark2_len));
@@ -462,7 +464,7 @@ int db_sync_equipment(struct gsm_equipment *equip)
"classmark2 = %s, "
"classmark3 = %s "
"WHERE imei = '%s' ",
- equip->classmark1, cm2, cm3, equip->imei);
+ classmark1, cm2, cm3, equip->imei);
free(cm2);
free(cm3);
@@ -590,7 +592,7 @@ int db_subscriber_alloc_token(struct gsm_subscriber* subscriber, u_int32_t* toke
}
int db_subscriber_assoc_imei(struct gsm_subscriber* subscriber, char imei[GSM_IMEI_LENGTH]) {
- u_int64_t equipment_id, watch_id;
+ unsigned long long equipment_id, watch_id;
dbi_result result;
strncpy(subscriber->equipment.imei, imei,
@@ -853,7 +855,7 @@ int db_apdu_blob_store(struct gsm_subscriber *subscr,
u_int8_t *apdu)
{
dbi_result result;
- char *q_apdu;
+ unsigned char *q_apdu;
dbi_conn_quote_binary_copy(conn, apdu, len, &q_apdu);
diff --git a/openbsc/src/debug.c b/openbsc/src/debug.c
index fa903af98..5dc2e0ff7 100644
--- a/openbsc/src/debug.c
+++ b/openbsc/src/debug.c
@@ -57,6 +57,9 @@ static const struct debug_info debug_info[] = {
DEBUG_CATEGORY(DMIB, "DMIB", "", "")
DEBUG_CATEGORY(DMUX, "DMUX", "", "")
DEBUG_CATEGORY(DMEAS, "DMEAS", "", "")
+ DEBUG_CATEGORY(DSCCP, "DSCCP", "", "")
+ DEBUG_CATEGORY(DMSC, "DMSC", "", "")
+ DEBUG_CATEGORY(DMGCP, "DMGCP", "", "")
};
static int use_color = 1;
diff --git a/openbsc/src/e1_config.c b/openbsc/src/e1_config.c
index 16fd0d999..62bacf2ca 100644
--- a/openbsc/src/e1_config.c
+++ b/openbsc/src/e1_config.c
@@ -219,7 +219,7 @@ int ia_config_connect(struct gsm_bts *bts, struct sockaddr_in *sin)
sign_ts = &line->ts[1-1];
rsl_ts = &line->ts[2-1];
oml_link = e1inp_sign_link_create(sign_ts, E1INP_SIGN_OML,
- bts->c0, 0, 0xff);
+ bts->c0, 0xff, 0);
rsl_link = e1inp_sign_link_create(rsl_ts, E1INP_SIGN_RSL,
bts->c0, 0, 0);
diff --git a/openbsc/src/gsm_04_08.c b/openbsc/src/gsm_04_08.c
index ba4d07e2b..9eff60f8e 100644
--- a/openbsc/src/gsm_04_08.c
+++ b/openbsc/src/gsm_04_08.c
@@ -48,6 +48,7 @@
#include <openbsc/rtp_proxy.h>
#include <openbsc/talloc.h>
#include <openbsc/transaction.h>
+#include <openbsc/ussd.h>
#define GSM_MAX_FACILITY 128
#define GSM_MAX_SSVERSION 128
@@ -1086,34 +1087,6 @@ static int mm_rx_loc_upd_req(struct msgb *msg)
return gsm0408_authorize(lchan, msg);
}
-/* 9.1.5 Channel mode modify: Modify the mode on the MS side */
-int gsm48_tx_chan_mode_modify(struct gsm_lchan *lchan, u_int8_t mode)
-{
- struct msgb *msg = gsm48_msgb_alloc();
- struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
- struct gsm48_chan_mode_modify *cmm =
- (struct gsm48_chan_mode_modify *) msgb_put(msg, sizeof(*cmm));
- u_int16_t arfcn = lchan->ts->trx->arfcn & 0x3ff;
-
- DEBUGP(DRR, "-> CHANNEL MODE MODIFY mode=0x%02x\n", mode);
-
- lchan->tch_mode = mode;
- msg->lchan = lchan;
- gh->proto_discr = GSM48_PDISC_RR;
- gh->msg_type = GSM48_MT_RR_CHAN_MODE_MODIF;
-
- /* fill the channel information element, this code
- * should probably be shared with rsl_rx_chan_rqd() */
- cmm->chan_desc.chan_nr = lchan2chan_nr(lchan);
- cmm->chan_desc.h0.tsc = lchan->ts->trx->bts->tsc;
- cmm->chan_desc.h0.h = 0;
- cmm->chan_desc.h0.arfcn_high = arfcn >> 8;
- cmm->chan_desc.h0.arfcn_low = arfcn & 0xff;
- cmm->mode = mode;
-
- return gsm48_sendmsg(msg, NULL);
-}
-
#if 0
static u_int8_t to_bcd8(u_int8_t val)
{
@@ -1128,9 +1101,7 @@ int gsm48_tx_mm_info(struct gsm_lchan *lchan)
struct gsm48_hdr *gh;
struct gsm_network *net = lchan->ts->trx->bts->network;
u_int8_t *ptr8;
- u_int16_t *ptr16;
int name_len, name_pad;
- int i;
#if 0
time_t cur_t;
struct tm* cur_time;
@@ -1458,7 +1429,7 @@ static int gsm0408_rcv_mm(struct msgb *msg)
}
/* Receive a PAGING RESPONSE message from the MS */
-static int gsm48_rr_rx_pag_resp(struct msgb *msg)
+static int gsm48_rx_rr_pag_resp(struct msgb *msg)
{
struct gsm_bts *bts = msg->lchan->ts->trx->bts;
struct gsm48_hdr *gh = msgb_l3(msg);
@@ -1613,14 +1584,10 @@ static int gsm0408_rcv_rr(struct msgb *msg)
DEBUGP(DRR, "GRPS SUSPEND REQUEST\n");
break;
case GSM48_MT_RR_PAG_RESP:
- rc = gsm48_rr_rx_pag_resp(msg);
+ rc = gsm48_rx_rr_pag_resp(msg);
break;
case GSM48_MT_RR_CHAN_MODE_MODIF_ACK:
- DEBUGP(DRR, "CHANNEL MODE MODIFY ACK\n");
- /* We've successfully modified the MS side of the channel,
- * now go on to modify the BTS side of the channel */
- msg->lchan->rsl_cmode = RSL_CMOD_SPD_SPEECH;
- rc = rsl_chan_mode_modify_req(msg->lchan);
+ rc = gsm48_rx_rr_modif_ack(msg);
break;
case GSM48_MT_RR_STATUS:
rc = gsm48_rx_rr_status(msg);
@@ -1645,7 +1612,7 @@ static int gsm0408_rcv_rr(struct msgb *msg)
}
int gsm48_send_rr_app_info(struct gsm_lchan *lchan, u_int8_t apdu_id,
- u_int8_t apdu_len, u_int8_t *apdu)
+ u_int8_t apdu_len, const u_int8_t *apdu)
{
struct msgb *msg = gsm48_msgb_alloc();
struct gsm48_hdr *gh;
@@ -1867,7 +1834,7 @@ static int handle_abisip_signal(unsigned int subsys, unsigned int signal,
ts = lchan->ts;
switch (signal) {
- case S_ABISIP_BIND_ACK:
+ case S_ABISIP_CRCX_ACK:
/* the BTS has successfully bound a TCH to a local ip/port,
* which means we can connect our UDP socket to it */
if (ts->abis_ip.rtp_socket) {
@@ -1893,7 +1860,7 @@ static int handle_abisip_signal(unsigned int subsys, unsigned int signal,
}
}
break;
- case S_ABISIP_DISC_IND:
+ case S_ABISIP_DLCX_IND:
/* the BTS tells us a RTP stream has been disconnected */
if (ts->abis_ip.rtp_socket) {
rtp_socket_free(ts->abis_ip.rtp_socket);
@@ -1915,7 +1882,7 @@ static int ipacc_connect_proxy_bind(struct gsm_lchan *lchan)
struct rtp_socket *rs = ts->abis_ip.rtp_socket;
int rc;
- rc = rsl_ipacc_connect(lchan, ntohl(rs->rtp.sin_local.sin_addr.s_addr),
+ rc = rsl_ipacc_mdcx(lchan, ntohl(rs->rtp.sin_local.sin_addr.s_addr),
ntohs(rs->rtp.sin_local.sin_port),
ts->abis_ip.conn_id,
/* FIXME: use RTP payload of bound socket, not BTS*/
@@ -1958,14 +1925,14 @@ static int tch_map(struct gsm_lchan *lchan, struct gsm_lchan *remote_lchan)
} else {
/* directly connect TCH RTP streams to each other */
ts = remote_lchan->ts;
- rc = rsl_ipacc_connect(lchan, ts->abis_ip.bound_ip,
+ rc = rsl_ipacc_mdcx(lchan, ts->abis_ip.bound_ip,
ts->abis_ip.bound_port,
lchan->ts->abis_ip.conn_id,
ts->abis_ip.rtp_payload2);
if (rc < 0)
return rc;
ts = lchan->ts;
- rc = rsl_ipacc_connect(remote_lchan, ts->abis_ip.bound_ip,
+ rc = rsl_ipacc_mdcx(remote_lchan, ts->abis_ip.bound_ip,
ts->abis_ip.bound_port,
remote_lchan->ts->abis_ip.conn_id,
ts->abis_ip.rtp_payload2);
@@ -3196,22 +3163,11 @@ static int gsm48_cc_rx_userinfo(struct gsm_trans *trans, struct msgb *msg)
return mncc_recvmsg(trans->subscr->net, trans, MNCC_USERINFO_IND, &user);
}
-static int gsm48_lchan_modify(struct gsm_trans *trans, void *arg)
+static int _gsm48_lchan_modify(struct gsm_trans *trans, void *arg)
{
struct gsm_mncc *mode = arg;
- int rc;
-
- rc = gsm48_tx_chan_mode_modify(trans->lchan, mode->lchan_mode);
- if (rc < 0)
- return rc;
-
- /* FIXME: we not only need to do this after mode modify, but
- * also after channel activation */
- if (is_ipaccess_bts(trans->lchan->ts->trx->bts) &&
- mode->lchan_mode != GSM48_CMODE_SIGN)
- rc = rsl_ipacc_bind(trans->lchan);
- return rc;
+ return gsm48_lchan_modify(trans->lchan, mode->lchan_mode);
}
static struct downstate {
@@ -3269,7 +3225,7 @@ static struct downstate {
MNCC_REL_REQ, gsm48_cc_tx_release},
/* special */
{ALL_STATES,
- MNCC_LCHAN_MODIFY, gsm48_lchan_modify},
+ MNCC_LCHAN_MODIFY, _gsm48_lchan_modify},
};
#define DOWNSLLEN \
@@ -3280,7 +3236,6 @@ int mncc_send(struct gsm_network *net, int msg_type, void *arg)
{
int i, rc = 0;
struct gsm_trans *trans = NULL, *transt;
- struct gsm_subscriber *subscr;
struct gsm_lchan *lchan = NULL;
struct gsm_bts *bts = NULL;
struct gsm_mncc *data = arg, rel;
@@ -3324,6 +3279,8 @@ int mncc_send(struct gsm_network *net, int msg_type, void *arg)
/* Callref unknown */
if (!trans) {
+ struct gsm_subscriber *subscr;
+
if (msg_type != MNCC_SETUP_REQ) {
DEBUGP(DCC, "(bts - trx - ts - ti -- sub %s) "
"Received '%s' from MNCC with "
@@ -3399,6 +3356,8 @@ int mncc_send(struct gsm_network *net, int msg_type, void *arg)
"started.\n", bts->nr,
data->called.number,
get_mncc_name(msg_type));
+ subscr_put(subscr);
+ trans_free(trans);
return 0;
}
/* store setup informations until paging was successfull */
@@ -3406,11 +3365,13 @@ int mncc_send(struct gsm_network *net, int msg_type, void *arg)
/* Trigger paging */
paging_request(net, subscr, RSL_CHANNEED_TCH_F,
setup_trig_pag_evt, subscr);
+ subscr_put(subscr);
return 0;
}
/* Assign lchan */
trans->lchan = lchan;
use_lchan(lchan);
+ subscr_put(subscr);
}
lchan = trans->lchan;
@@ -3589,10 +3550,12 @@ int gsm0408_rcvmsg(struct msgb *msg, u_int8_t link_id)
break;
case GSM48_PDISC_MM_GPRS:
case GSM48_PDISC_SM_GPRS:
- case GSM48_PDISC_NC_SS: /* mobile-originated USSD */
fprintf(stderr, "Unimplemented GSM 04.08 discriminator 0x%02x\n",
pdisc);
break;
+ case GSM48_PDISC_NC_SS:
+ rc = handle_rcv_ussd(msg);
+ break;
default:
fprintf(stderr, "Unknown GSM 04.08 discriminator 0x%02x\n",
pdisc);
diff --git a/openbsc/src/gsm_04_08_utils.c b/openbsc/src/gsm_04_08_utils.c
index ef9e59c77..ad038fba6 100644
--- a/openbsc/src/gsm_04_08_utils.c
+++ b/openbsc/src/gsm_04_08_utils.c
@@ -255,9 +255,14 @@ static const struct chreq chreq_type_neci1[] = {
{ 0x50, 0xf0, CHREQ_T_DATA_CALL_TCH_H },
{ 0x00, 0xf0, CHREQ_T_LOCATION_UPD },
{ 0x10, 0xf0, CHREQ_T_SDCCH },
- { 0x80, 0xe0, CHREQ_T_PAG_R_ANY },
+ { 0x80, 0xe0, CHREQ_T_PAG_R_ANY_NECI1 },
{ 0x20, 0xf0, CHREQ_T_PAG_R_TCH_F },
{ 0x30, 0xf0, CHREQ_T_PAG_R_TCH_FH },
+ { 0x67, 0xff, CHREQ_T_LMU },
+ { 0x60, 0xf9, CHREQ_T_RESERVED_SDCCH },
+ { 0x61, 0xfb, CHREQ_T_RESERVED_SDCCH },
+ { 0x63, 0xff, CHREQ_T_RESERVED_SDCCH },
+ { 0x7f, 0xff, CHREQ_T_RESERVED_IGNORE },
};
/* If SYSTEM INFORMATION TYPE 4 NECI bit == 0 */
@@ -267,9 +272,14 @@ static const struct chreq chreq_type_neci0[] = {
{ 0xe0, 0xe0, CHREQ_T_TCH_F },
{ 0x50, 0xf0, CHREQ_T_DATA_CALL_TCH_H },
{ 0x00, 0xe0, CHREQ_T_LOCATION_UPD },
- { 0x80, 0xe0, CHREQ_T_PAG_R_ANY },
+ { 0x80, 0xe0, CHREQ_T_PAG_R_ANY_NECI0 },
{ 0x20, 0xf0, CHREQ_T_PAG_R_TCH_F },
{ 0x30, 0xf0, CHREQ_T_PAG_R_TCH_FH },
+ { 0x67, 0xff, CHREQ_T_LMU },
+ { 0x60, 0xf9, CHREQ_T_RESERVED_SDCCH },
+ { 0x61, 0xfb, CHREQ_T_RESERVED_SDCCH },
+ { 0x63, 0xff, CHREQ_T_RESERVED_SDCCH },
+ { 0x7f, 0xff, CHREQ_T_RESERVED_IGNORE },
};
static const enum gsm_chan_t ctype_by_chreq[] = {
@@ -282,9 +292,13 @@ static const enum gsm_chan_t ctype_by_chreq[] = {
[CHREQ_T_VOICE_CALL_TCH_H] = GSM_LCHAN_TCH_H,
[CHREQ_T_DATA_CALL_TCH_H] = GSM_LCHAN_TCH_H,
[CHREQ_T_LOCATION_UPD] = GSM_LCHAN_SDCCH,
- [CHREQ_T_PAG_R_ANY] = GSM_LCHAN_SDCCH,
+ [CHREQ_T_PAG_R_ANY_NECI1] = GSM_LCHAN_SDCCH,
+ [CHREQ_T_PAG_R_ANY_NECI0] = GSM_LCHAN_SDCCH,
[CHREQ_T_PAG_R_TCH_F] = GSM_LCHAN_TCH_F,
[CHREQ_T_PAG_R_TCH_FH] = GSM_LCHAN_TCH_F,
+ [CHREQ_T_LMU] = GSM_LCHAN_SDCCH,
+ [CHREQ_T_RESERVED_SDCCH] = GSM_LCHAN_SDCCH,
+ [CHREQ_T_RESERVED_IGNORE] = GSM_LCHAN_UNKNOWN,
};
static const enum gsm_chreq_reason_t reason_by_chreq[] = {
@@ -297,18 +311,32 @@ static const enum gsm_chreq_reason_t reason_by_chreq[] = {
[CHREQ_T_VOICE_CALL_TCH_H] = GSM_CHREQ_REASON_OTHER,
[CHREQ_T_DATA_CALL_TCH_H] = GSM_CHREQ_REASON_OTHER,
[CHREQ_T_LOCATION_UPD] = GSM_CHREQ_REASON_LOCATION_UPD,
- [CHREQ_T_PAG_R_ANY] = GSM_CHREQ_REASON_PAG,
+ [CHREQ_T_PAG_R_ANY_NECI1] = GSM_CHREQ_REASON_PAG,
+ [CHREQ_T_PAG_R_ANY_NECI0] = GSM_CHREQ_REASON_PAG,
[CHREQ_T_PAG_R_TCH_F] = GSM_CHREQ_REASON_PAG,
[CHREQ_T_PAG_R_TCH_FH] = GSM_CHREQ_REASON_PAG,
+ [CHREQ_T_LMU] = GSM_CHREQ_REASON_OTHER,
+ [CHREQ_T_RESERVED_SDCCH] = GSM_CHREQ_REASON_OTHER,
+ [CHREQ_T_RESERVED_IGNORE] = GSM_CHREQ_REASON_OTHER,
};
-enum gsm_chan_t get_ctype_by_chreq(struct gsm_bts *bts, u_int8_t ra)
+enum gsm_chan_t get_ctype_by_chreq(struct gsm_bts *bts, u_int8_t ra, int neci)
{
int i;
- /* FIXME: determine if we set NECI = 0 in the BTS SI4 */
+ int length;
+ const struct chreq *chreq;
- for (i = 0; i < ARRAY_SIZE(chreq_type_neci0); i++) {
- const struct chreq *chr = &chreq_type_neci0[i];
+ if (neci) {
+ chreq = chreq_type_neci1;
+ length = ARRAY_SIZE(chreq_type_neci1);
+ } else {
+ chreq = chreq_type_neci0;
+ length = ARRAY_SIZE(chreq_type_neci0);
+ }
+
+
+ for (i = 0; i < length; i++) {
+ const struct chreq *chr = &chreq[i];
if ((ra & chr->mask) == chr->val)
return ctype_by_chreq[chr->type];
}
@@ -316,13 +344,22 @@ enum gsm_chan_t get_ctype_by_chreq(struct gsm_bts *bts, u_int8_t ra)
return GSM_LCHAN_SDCCH;
}
-enum gsm_chreq_reason_t get_reason_by_chreq(struct gsm_bts *bts, u_int8_t ra)
+enum gsm_chreq_reason_t get_reason_by_chreq(struct gsm_bts *bts, u_int8_t ra, int neci)
{
int i;
- /* FIXME: determine if we set NECI = 0 in the BTS SI4 */
+ int length;
+ const struct chreq *chreq;
+
+ if (neci) {
+ chreq = chreq_type_neci1;
+ length = ARRAY_SIZE(chreq_type_neci1);
+ } else {
+ chreq = chreq_type_neci0;
+ length = ARRAY_SIZE(chreq_type_neci0);
+ }
- for (i = 0; i < ARRAY_SIZE(chreq_type_neci0); i++) {
- const struct chreq *chr = &chreq_type_neci0[i];
+ for (i = 0; i < length; i++) {
+ const struct chreq *chr = &chreq[i];
if ((ra & chr->mask) == chr->val)
return reason_by_chreq[chr->type];
}
@@ -482,3 +519,142 @@ int gsm48_send_rr_ciph_mode(struct gsm_lchan *lchan, int want_imeisv)
return rsl_encryption_cmd(msg);
}
+/* Chapter 9.1.2: Assignment Command */
+int gsm48_send_rr_ass_cmd(struct gsm_lchan *lchan, u_int8_t power_command)
+{
+ struct msgb *msg = gsm48_msgb_alloc();
+ struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
+ struct gsm48_ass_cmd *ass =
+ (struct gsm48_ass_cmd *) msgb_put(msg, sizeof(*ass));
+ u_int16_t arfcn = lchan->ts->trx->arfcn & 0x3ff;
+
+ DEBUGP(DRR, "-> ASSIGNMENT COMMAND tch_mode=0x%02x\n", lchan->tch_mode);
+
+ msg->lchan = lchan;
+ gh->proto_discr = GSM48_PDISC_RR;
+ gh->msg_type = GSM48_MT_RR_ASS_CMD;
+
+ /*
+ * fill the channel information element, this code
+ * should probably be shared with rsl_rx_chan_rqd(),
+ * gsm48_tx_chan_mode_modify. But beware that 10.5.2.5
+ * 10.5.2.5.a have slightly different semantic for
+ * the chan_desc. But as long as multi-slot configurations
+ * are not used we seem to be fine.
+ */
+ ass->chan_desc.chan_nr = lchan2chan_nr(lchan);
+ ass->chan_desc.h0.tsc = lchan->ts->trx->bts->tsc;
+ ass->chan_desc.h0.h = 0;
+ ass->chan_desc.h0.arfcn_high = arfcn >> 8;
+ ass->chan_desc.h0.arfcn_low = arfcn & 0xff;
+ ass->power_command = power_command;
+
+ /* in case of multi rate we need to attach a config */
+ if (lchan->tch_mode == GSM48_CMODE_SPEECH_AMR) {
+ if (lchan->mr_conf.ver == 0) {
+ DEBUGP(DRR, "BUG: Using multirate codec without multirate config.\n");
+ } else {
+ u_int8_t *data = msgb_put(msg, 4);
+ data[0] = GSM48_IE_MUL_RATE_CFG;
+ data[1] = 0x2;
+ memcpy(&data[2], &lchan->mr_conf, 2);
+ }
+ }
+
+ return gsm48_sendmsg(msg, NULL);
+}
+
+/* 9.1.5 Channel mode modify: Modify the mode on the MS side */
+int gsm48_tx_chan_mode_modify(struct gsm_lchan *lchan, u_int8_t mode)
+{
+ struct msgb *msg = gsm48_msgb_alloc();
+ struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
+ struct gsm48_chan_mode_modify *cmm =
+ (struct gsm48_chan_mode_modify *) msgb_put(msg, sizeof(*cmm));
+ u_int16_t arfcn = lchan->ts->trx->arfcn & 0x3ff;
+
+ DEBUGP(DRR, "-> CHANNEL MODE MODIFY mode=0x%02x\n", mode);
+
+ lchan->tch_mode = mode;
+ msg->lchan = lchan;
+ gh->proto_discr = GSM48_PDISC_RR;
+ gh->msg_type = GSM48_MT_RR_CHAN_MODE_MODIF;
+
+ /* fill the channel information element, this code
+ * should probably be shared with rsl_rx_chan_rqd() */
+ cmm->chan_desc.chan_nr = lchan2chan_nr(lchan);
+ cmm->chan_desc.h0.tsc = lchan->ts->trx->bts->tsc;
+ cmm->chan_desc.h0.h = 0;
+ cmm->chan_desc.h0.arfcn_high = arfcn >> 8;
+ cmm->chan_desc.h0.arfcn_low = arfcn & 0xff;
+ cmm->mode = mode;
+
+ /* in case of multi rate we need to attach a config */
+ if (lchan->tch_mode == GSM48_CMODE_SPEECH_AMR) {
+ if (lchan->mr_conf.ver == 0) {
+ DEBUGP(DRR, "BUG: Using multirate codec without multirate config.\n");
+ } else {
+ u_int8_t *data = msgb_put(msg, 4);
+ data[0] = GSM48_IE_MUL_RATE_CFG;
+ data[1] = 0x2;
+ memcpy(&data[2], &lchan->mr_conf, 2);
+ }
+ }
+
+ return gsm48_sendmsg(msg, NULL);
+}
+
+int gsm48_lchan_modify(struct gsm_lchan *lchan, u_int8_t lchan_mode)
+{
+ int rc;
+
+ rc = gsm48_tx_chan_mode_modify(lchan, lchan_mode);
+ if (rc < 0)
+ return rc;
+
+ return rc;
+}
+
+int gsm48_rx_rr_modif_ack(struct msgb *msg)
+{
+ int rc;
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ struct gsm48_chan_mode_modify *mod =
+ (struct gsm48_chan_mode_modify *) gh->data;
+
+ DEBUGP(DRR, "CHANNEL MODE MODIFY ACK\n");
+
+ if (mod->mode != msg->lchan->tch_mode) {
+ DEBUGP(DRR, "CHANNEL MODE change failed. Wanted: %d Got: %d\n",
+ msg->lchan->tch_mode, mod->mode);
+ return -1;
+ }
+
+ /* update the channel type */
+ switch (mod->mode) {
+ case GSM48_CMODE_SIGN:
+ msg->lchan->rsl_cmode = RSL_CMOD_SPD_SIGN;
+ break;
+ case GSM48_CMODE_SPEECH_V1:
+ case GSM48_CMODE_SPEECH_EFR:
+ case GSM48_CMODE_SPEECH_AMR:
+ msg->lchan->rsl_cmode = RSL_CMOD_SPD_SPEECH;
+ break;
+ case GSM48_CMODE_DATA_14k5:
+ case GSM48_CMODE_DATA_12k0:
+ case GSM48_CMODE_DATA_6k0:
+ case GSM48_CMODE_DATA_3k6:
+ msg->lchan->rsl_cmode = RSL_CMOD_SPD_DATA;
+ break;
+ }
+
+ /* We've successfully modified the MS side of the channel,
+ * now go on to modify the BTS side of the channel */
+ rc = rsl_chan_mode_modify_req(msg->lchan);
+
+ /* FIXME: we not only need to do this after mode modify, but
+ * also after channel activation */
+ if (is_ipaccess_bts(msg->lchan->ts->trx->bts) && mod->mode != GSM48_CMODE_SIGN)
+ rsl_ipacc_crcx(msg->lchan);
+ return rc;
+}
diff --git a/openbsc/src/gsm_04_11.c b/openbsc/src/gsm_04_11.c
index 59020746f..8e3c64974 100644
--- a/openbsc/src/gsm_04_11.c
+++ b/openbsc/src/gsm_04_11.c
@@ -56,11 +56,6 @@
void *tall_gsms_ctx;
static u_int32_t new_callref = 0x40000001;
-struct value_string {
- u_int32_t value;
- const char *str;
-};
-
static const struct value_string cp_cause_strs[] = {
{ GSM411_CP_CAUSE_NET_FAIL, "Network Failure" },
{ GSM411_CP_CAUSE_CONGESTION, "Congestion" },
@@ -104,19 +99,6 @@ static const struct value_string rp_cause_strs[] = {
{ 0, NULL }
};
-const char *get_value_string(const struct value_string *vs, u_int32_t val)
-{
- int i;
-
- for (i = 0;; i++) {
- if (vs[i].value == 0 && vs[i].str == NULL)
- break;
- if (vs[i].value == val)
- return vs[i].str;
- }
- return "unknown";
-}
-
struct gsm_sms *sms_alloc(void)
{
return talloc_zero(tall_gsms_ctx, struct gsm_sms);
@@ -216,40 +198,172 @@ static int gsm411_rp_sendmsg(struct msgb *msg, struct gsm_trans *trans,
return gsm411_cp_sendmsg(msg, trans, GSM411_MT_CP_DATA);
}
-static unsigned long gsm340_validity_period(u_int8_t sms_vpf, u_int8_t *sms_vp)
+/* Turn int into semi-octet representation: 98 => 0x89 */
+static u_int8_t bcdify(u_int8_t value)
+{
+ u_int8_t ret;
+
+ ret = value / 10;
+ ret |= (value % 10) << 4;
+
+ return ret;
+}
+
+/* Turn semi-octet representation into int: 0x89 => 98 */
+static u_int8_t unbcdify(u_int8_t value)
{
+ u_int8_t ret;
+
+ if ((value & 0x0F) > 9 || (value >> 4) > 9)
+ DEBUGP(DSMS, "unbcdify got too big nibble: 0x%02X\n", value);
+
+ ret = (value&0x0F)*10;
+ if (ret > 90)
+ ret += value>>4;
+
+ return ret;
+}
+
+/* Generate 03.40 TP-SCTS */
+static void gsm340_gen_scts(u_int8_t *scts, time_t time)
+{
+ struct tm *tm = localtime(&time);
+
+ *scts++ = bcdify(tm->tm_year % 100);
+ *scts++ = bcdify(tm->tm_mon + 1);
+ *scts++ = bcdify(tm->tm_mday);
+ *scts++ = bcdify(tm->tm_hour);
+ *scts++ = bcdify(tm->tm_min);
+ *scts++ = bcdify(tm->tm_sec);
+ *scts++ = bcdify(tm->tm_gmtoff/(60*15));
+}
+
+/* Decode 03.40 TP-SCTS (into utc/gmt timestamp) */
+static time_t gsm340_scts(u_int8_t *scts)
+{
+ struct tm tm;
+
+ u_int8_t yr = unbcdify(*scts++);
+
+ if (yr <= 80)
+ tm.tm_year = 100 + yr;
+ else
+ tm.tm_year = yr;
+ tm.tm_mon = unbcdify(*scts++) - 1;
+ tm.tm_mday = unbcdify(*scts++);
+ tm.tm_hour = unbcdify(*scts++);
+ tm.tm_min = unbcdify(*scts++);
+ tm.tm_sec = unbcdify(*scts++);
+ /* according to gsm 03.40 time zone is
+ "expressed in quarters of an hour" */
+ tm.tm_gmtoff = unbcdify(*scts++) * 15*60;
+
+ return mktime(&tm);
+}
+
+/* Return the default validity period in minutes */
+static unsigned long gsm340_vp_default(void)
+{
+ unsigned long minutes;
+ /* Default validity: two days */
+ minutes = 24 * 60 * 2;
+ return minutes;
+}
+
+/* Decode validity period format 'relative' */
+static unsigned long gsm340_vp_relative(u_int8_t *sms_vp)
+{
+ /* Chapter 9.2.3.12.1 */
u_int8_t vp;
unsigned long minutes;
+ vp = *(sms_vp);
+ if (vp <= 143)
+ minutes = vp + 1 * 5;
+ else if (vp <= 167)
+ minutes = 12*60 + (vp-143) * 30;
+ else if (vp <= 196)
+ minutes = vp-166 * 60 * 24;
+ else
+ minutes = vp-192 * 60 * 24 * 7;
+ return minutes;
+}
+
+/* Decode validity period format 'absolute' */
+static unsigned long gsm340_vp_absolute(u_int8_t *sms_vp)
+{
+ /* Chapter 9.2.3.12.2 */
+ time_t expires, now;
+ unsigned long minutes;
+
+ expires = gsm340_scts(sms_vp);
+ now = mktime(gmtime(NULL));
+ if (expires <= now)
+ minutes = 0;
+ else
+ minutes = (expires-now)/60;
+ return minutes;
+}
+
+/* Decode validity period format 'relative in integer representation' */
+static unsigned long gsm340_vp_relative_integer(u_int8_t *sms_vp)
+{
+ u_int8_t vp;
+ unsigned long minutes;
+ vp = *(sms_vp);
+ if (vp == 0) {
+ DEBUGP(DSMS, "reserved relative_integer validity period\n");
+ return gsm340_vp_default();
+ }
+ minutes = vp/60;
+ return minutes;
+}
+
+/* Decode validity period format 'relative in semi-octet representation' */
+static unsigned long gsm340_vp_relative_semioctet(u_int8_t *sms_vp)
+{
+ unsigned long minutes;
+ minutes = unbcdify(*sms_vp++)*60; /* hours */
+ minutes += unbcdify(*sms_vp++); /* minutes */
+ minutes += unbcdify(*sms_vp++)/60; /* seconds */
+ return minutes;
+}
+
+/* decode validity period. return minutes */
+static unsigned long gsm340_validity_period(u_int8_t sms_vpf, u_int8_t *sms_vp)
+{
+ u_int8_t fi; /* functionality indicator */
+
switch (sms_vpf) {
case GSM340_TP_VPF_RELATIVE:
- /* Chapter 9.2.3.12.1 */
- vp = *(sms_vp);
- if (vp <= 143)
- minutes = vp + 1 * 5;
- else if (vp <= 167)
- minutes = 12*60 + (vp-143) * 30;
- else if (vp <= 196)
- minutes = vp-166 * 60 * 24;
- else
- minutes = vp-192 * 60 * 24 * 7;
- break;
+ return gsm340_vp_relative(sms_vp);
case GSM340_TP_VPF_ABSOLUTE:
- /* Chapter 9.2.3.12.2 */
- /* FIXME: like service center time stamp */
- DEBUGP(DSMS, "VPI absolute not implemented yet\n");
- break;
+ return gsm340_vp_absolute(sms_vp);
case GSM340_TP_VPF_ENHANCED:
/* Chapter 9.2.3.12.3 */
- /* FIXME: implementation */
- DEBUGP(DSMS, "VPI enhanced not implemented yet\n");
- break;
+ fi = *sms_vp++;
+ /* ignore additional fi */
+ if (fi & (1<<7)) sms_vp++;
+ /* read validity period format */
+ switch (fi & 0b111) {
+ case 0b000:
+ return gsm340_vp_default(); /* no vpf specified */
+ case 0b001:
+ return gsm340_vp_relative(sms_vp);
+ case 0b010:
+ return gsm340_vp_relative_integer(sms_vp);
+ case 0b011:
+ return gsm340_vp_relative_semioctet(sms_vp);
+ default:
+ /* The GSM spec says that the SC should reject any
+ unsupported and/or undefined values. FIXME */
+ DEBUGP(DSMS, "Reserved enhanced validity period format\n");
+ return gsm340_vp_default();
+ }
case GSM340_TP_VPF_NONE:
- /* Default validity: two days */
- minutes = 24 * 60 * 2;
- break;
+ default:
+ return gsm340_vp_default();
}
- return minutes;
}
/* determine coding alphabet dependent on GSM 03.38 Section 4 DCS */
@@ -317,30 +431,6 @@ static int gsm340_gen_oa(u_int8_t *oa, unsigned int oa_len,
return len_in_bytes;
}
-static u_int8_t bcdify(u_int8_t value)
-{
- u_int8_t ret;
-
- ret = value / 10;
- ret |= (value % 10) << 4;
-
- return ret;
-}
-
-/* Generate 03.40 TP-SCTS */
-static void gsm340_gen_scts(u_int8_t *scts, time_t time)
-{
- struct tm *tm = localtime(&time);
-
- *scts++ = bcdify(tm->tm_year % 100);
- *scts++ = bcdify(tm->tm_mon + 1);
- *scts++ = bcdify(tm->tm_mday);
- *scts++ = bcdify(tm->tm_hour);
- *scts++ = bcdify(tm->tm_min);
- *scts++ = bcdify(tm->tm_sec);
- *scts++ = 0; /* FIXME: timezone */
-}
-
/* generate a msgb containing a TPDU derived from struct gsm_sms,
* returns total size of TPDU */
static int gsm340_gen_tpdu(struct msgb *msg, struct gsm_sms *sms)
@@ -470,6 +560,8 @@ static int gsm340_rx_tpdu(struct msgb *msg)
case GSM340_TP_VPF_ABSOLUTE:
case GSM340_TP_VPF_ENHANCED:
sms_vp = smsp;
+ /* the additional functionality indicator... */
+ if (*smsp & (1<<7)) smsp++;
smsp += 7;
break;
case GSM340_TP_VPF_NONE:
@@ -674,7 +766,7 @@ static int gsm411_rx_rp_error(struct msgb *msg, struct gsm_trans *trans,
* successfully receive the SMS. We need to investigate
* the cause and take action depending on it */
- DEBUGP(DSMS, "RX SMS RP-ERROR, cause %d (%s)\n", cause,
+ DEBUGP(DSMS, "RX SMS RP-ERROR, cause %d:%d (%s)\n", cause_len, cause,
get_value_string(rp_cause_strs, cause));
if (!trans->sms.is_mt) {
@@ -1068,8 +1160,6 @@ static int subscr_sig_cb(unsigned int subsys, unsigned int signal,
struct gsm_lchan *lchan;
struct gsm_sms *sms;
- u_int32_t token;
-
switch (signal) {
case S_SUBSCR_ATTACHED:
/* A subscriber has attached. Check if there are
diff --git a/openbsc/src/gsm_04_80.c b/openbsc/src/gsm_04_80.c
new file mode 100644
index 000000000..d3b472f30
--- /dev/null
+++ b/openbsc/src/gsm_04_80.c
@@ -0,0 +1,330 @@
+/* GSM Mobile Radio Interface Layer 3 messages on the A-bis interface
+ * 3GPP TS 04.08 version 7.21.0 Release 1998 / ETSI TS 100 940 V7.21.0 */
+
+/* (C) 2008-2009 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2008, 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * (C) 2009 by Mike Haben <michael.haben@btinternet.com>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <openbsc/msgb.h>
+#include <openbsc/tlv.h>
+#include <openbsc/debug.h>
+#include <openbsc/gsm_data.h>
+#include <openbsc/gsm_utils.h>
+#include <openbsc/gsm_04_08.h>
+#include <openbsc/gsm_04_80.h>
+
+/* Forward declarations */
+static int parse_ussd(u_int8_t *ussd, struct ussd_request *req);
+static int parse_ussd_info_elements(u_int8_t *ussd_ie,
+ struct ussd_request *req);
+static int parse_facility_ie(u_int8_t *facility_ie, u_int8_t length,
+ struct ussd_request *req);
+static int parse_ss_invoke(u_int8_t *invoke_data, u_int8_t length,
+ struct ussd_request *req);
+static int parse_process_uss_req(u_int8_t *uss_req_data, u_int8_t length,
+ struct ussd_request *req);
+
+static inline unsigned char *msgb_wrap_with_TL(struct msgb *msgb, u_int8_t tag)
+{
+ msgb->data -= 2;
+ msgb->data[0] = tag;
+ msgb->data[1] = msgb->len;
+ msgb->len += 2;
+ return msgb->data;
+}
+
+static inline unsigned char *msgb_push_TLV1(struct msgb *msgb, u_int8_t tag,
+ u_int8_t value)
+{
+ msgb->data -= 3;
+ msgb->len += 3;
+ msgb->data[0] = tag;
+ msgb->data[1] = 1;
+ msgb->data[2] = value;
+ return msgb->data;
+}
+
+
+/* Decode a mobile-originated USSD-request message */
+int gsm0480_decode_ussd_request(const struct msgb *msg, struct ussd_request *req)
+{
+ int rc = 0;
+ u_int8_t *parse_ptr = msgb_l3(msg);
+
+ if ((*parse_ptr & 0x0F) == GSM48_PDISC_NC_SS) {
+ req->transaction_id = *parse_ptr & 0x70;
+ rc = parse_ussd(parse_ptr+1, req);
+ }
+
+ if (!rc)
+ DEBUGP(DMM, "Error occurred while parsing received USSD!\n");
+
+ return rc;
+}
+
+static int parse_ussd(u_int8_t *ussd, struct ussd_request *req)
+{
+ int rc = 1;
+ u_int8_t msg_type = ussd[0] & 0xBF; /* message-type - section 3.4 */
+
+ switch (msg_type) {
+ case GSM0480_MTYPE_RELEASE_COMPLETE:
+ DEBUGP(DMM, "USS Release Complete\n");
+ /* could also parse out the optional Cause/Facility data */
+ req->text[0] = 0xFF;
+ break;
+ case GSM0480_MTYPE_REGISTER:
+ case GSM0480_MTYPE_FACILITY:
+ rc &= parse_ussd_info_elements(ussd+1, req);
+ break;
+ default:
+ fprintf(stderr, "Unknown GSM 04.80 message-type field 0x%02x\n",
+ ussd[0]);
+ rc = 0;
+ break;
+ }
+
+ return rc;
+}
+
+static int parse_ussd_info_elements(u_int8_t *ussd_ie, struct ussd_request *req)
+{
+ int rc;
+ /* Information Element Identifier - table 3.2 & GSM 04.08 section 10.5 */
+ u_int8_t iei = ussd_ie[0];
+ u_int8_t iei_length = ussd_ie[1];
+
+ switch (iei) {
+ case GSM48_IE_CAUSE:
+ break;
+ case GSM0480_IE_FACILITY:
+ rc = parse_facility_ie(ussd_ie+2, iei_length, req);
+ break;
+ case GSM0480_IE_SS_VERSION:
+ break;
+ default:
+ fprintf(stderr, "Unhandled GSM 04.08 or 04.80 IEI 0x%02x\n",
+ iei);
+ rc = 0;
+ break;
+ }
+
+ return rc;
+}
+
+static int parse_facility_ie(u_int8_t *facility_ie, u_int8_t length,
+ struct ussd_request *req)
+{
+ int rc = 1;
+ u_int8_t offset = 0;
+
+ do {
+ /* Component Type tag - table 3.7 */
+ u_int8_t component_type = facility_ie[offset];
+ u_int8_t component_length = facility_ie[offset+1];
+
+ switch (component_type) {
+ case GSM0480_CTYPE_INVOKE:
+ rc &= parse_ss_invoke(facility_ie+2,
+ component_length,
+ req);
+ break;
+ case GSM0480_CTYPE_RETURN_RESULT:
+ break;
+ case GSM0480_CTYPE_RETURN_ERROR:
+ break;
+ case GSM0480_CTYPE_REJECT:
+ break;
+ default:
+ fprintf(stderr, "Unknown GSM 04.80 Facility "
+ "Component Type 0x%02x\n", component_type);
+ rc = 0;
+ break;
+ }
+ offset += (component_length+2);
+ } while (offset < length);
+
+ return rc;
+}
+
+/* Parse an Invoke component - see table 3.3 */
+static int parse_ss_invoke(u_int8_t *invoke_data, u_int8_t length,
+ struct ussd_request *req)
+{
+ int rc = 1;
+ u_int8_t offset;
+
+ /* mandatory part */
+ if (invoke_data[0] != GSM0480_COMPIDTAG_INVOKE_ID) {
+ fprintf(stderr, "Unexpected GSM 04.80 Component-ID tag "
+ "0x%02x (expecting Invoke ID tag)\n", invoke_data[0]);
+ }
+
+ offset = invoke_data[1] + 2;
+ req->invoke_id = invoke_data[2];
+
+ /* optional part */
+ if (invoke_data[offset] == GSM0480_COMPIDTAG_LINKED_ID)
+ offset += invoke_data[offset+1] + 2; /* skip over it */
+
+ /* mandatory part */
+ if (invoke_data[offset] == GSM0480_OPERATION_CODE) {
+ u_int8_t operation_code = invoke_data[offset+2];
+ switch (operation_code) {
+ case GSM0480_OP_CODE_PROCESS_USS_REQ:
+ rc = parse_process_uss_req(invoke_data + offset + 3,
+ length - offset - 3,
+ req);
+ break;
+ default:
+ fprintf(stderr, "GSM 04.80 operation code 0x%02x "
+ "is not yet handled\n", operation_code);
+ rc = 0;
+ break;
+ }
+ } else {
+ fprintf(stderr, "Unexpected GSM 04.80 Component-ID tag 0x%02x "
+ "(expecting Operation Code tag)\n",
+ invoke_data[0]);
+ rc = 0;
+ }
+
+ return rc;
+}
+
+/* Parse the parameters of a Process UnstructuredSS Request */
+static int parse_process_uss_req(u_int8_t *uss_req_data, u_int8_t length,
+ struct ussd_request *req)
+{
+ int rc = 0;
+ int num_chars;
+ u_int8_t dcs;
+
+ if (uss_req_data[0] == GSM_0480_SEQUENCE_TAG) {
+ if (uss_req_data[2] == ASN1_OCTET_STRING_TAG) {
+ dcs = uss_req_data[4];
+ if ((dcs == 0x0F) &&
+ (uss_req_data[5] == ASN1_OCTET_STRING_TAG)) {
+ num_chars = (uss_req_data[6] * 8) / 7;
+ /* Prevent a mobile-originated buffer-overrun! */
+ if (num_chars > MAX_LEN_USSD_STRING)
+ num_chars = MAX_LEN_USSD_STRING;
+ gsm_7bit_decode(req->text,
+ &(uss_req_data[7]), num_chars);
+ /* append null-terminator */
+ req->text[num_chars+1] = 0;
+ rc = 1;
+ }
+ }
+ }
+ return rc;
+}
+
+/* Send response to a mobile-originated ProcessUnstructuredSS-Request */
+int gsm0480_send_ussd_response(const struct msgb *in_msg, const char *response_text,
+ const struct ussd_request *req)
+{
+ struct msgb *msg = gsm48_msgb_alloc();
+ struct gsm48_hdr *gh;
+ u_int8_t *ptr8;
+ int response_len;
+
+ response_len = (strlen(response_text) * 7) / 8;
+ if (((strlen(response_text) * 7) % 8) != 0)
+ response_len += 1;
+
+ msg->bts_link = in_msg->bts_link;
+ msg->lchan = in_msg->lchan;
+
+ /* First put the payload text into the message */
+ ptr8 = msgb_put(msg, response_len);
+ gsm_7bit_encode(ptr8, response_text);
+
+ /* Then wrap it as an Octet String */
+ msgb_wrap_with_TL(msg, ASN1_OCTET_STRING_TAG);
+
+ /* Pre-pend the DCS octet string */
+ msgb_push_TLV1(msg, ASN1_OCTET_STRING_TAG, 0x0F);
+
+ /* Then wrap these as a Sequence */
+ msgb_wrap_with_TL(msg, GSM_0480_SEQUENCE_TAG);
+
+ /* Pre-pend the operation code */
+ msgb_push_TLV1(msg, GSM0480_OPERATION_CODE,
+ GSM0480_OP_CODE_PROCESS_USS_REQ);
+
+ /* Wrap the operation code and IA5 string as a sequence */
+ msgb_wrap_with_TL(msg, GSM_0480_SEQUENCE_TAG);
+
+ /* Pre-pend the invoke ID */
+ msgb_push_TLV1(msg, GSM0480_COMPIDTAG_INVOKE_ID, req->invoke_id);
+
+ /* Wrap this up as a Return Result component */
+ msgb_wrap_with_TL(msg, GSM0480_CTYPE_RETURN_RESULT);
+
+ /* Wrap the component in a Facility message */
+ msgb_wrap_with_TL(msg, GSM0480_IE_FACILITY);
+
+ /* And finally pre-pend the L3 header */
+ gh = (struct gsm48_hdr *) msgb_push(msg, sizeof(*gh));
+ gh->proto_discr = GSM48_PDISC_NC_SS | req->transaction_id
+ | (1<<7); /* TI direction = 1 */
+ gh->msg_type = GSM0480_MTYPE_RELEASE_COMPLETE;
+
+ return gsm48_sendmsg(msg, NULL);
+}
+
+int gsm0480_send_ussd_reject(const struct msgb *in_msg,
+ const struct ussd_request *req)
+{
+ struct msgb *msg = gsm48_msgb_alloc();
+ struct gsm48_hdr *gh;
+
+ msg->bts_link = in_msg->bts_link;
+ msg->lchan = in_msg->lchan;
+
+ /* First insert the problem code */
+ msgb_push_TLV1(msg, GSM_0480_PROBLEM_CODE_TAG_GENERAL,
+ GSM_0480_GEN_PROB_CODE_UNRECOGNISED);
+
+ /* Before it insert the invoke ID */
+ msgb_push_TLV1(msg, GSM0480_COMPIDTAG_INVOKE_ID, req->invoke_id);
+
+ /* Wrap this up as a Reject component */
+ msgb_wrap_with_TL(msg, GSM0480_CTYPE_REJECT);
+
+ /* Wrap the component in a Facility message */
+ msgb_wrap_with_TL(msg, GSM0480_IE_FACILITY);
+
+ /* And finally pre-pend the L3 header */
+ gh = (struct gsm48_hdr *) msgb_push(msg, sizeof(*gh));
+ gh->proto_discr = GSM48_PDISC_NC_SS;
+ gh->proto_discr |= req->transaction_id | (1<<7); /* TI direction = 1 */
+ gh->msg_type = GSM0480_MTYPE_RELEASE_COMPLETE;
+
+ return gsm48_sendmsg(msg, NULL);
+}
diff --git a/openbsc/src/gsm_data.c b/openbsc/src/gsm_data.c
index 6767c3fd5..8212346ec 100644
--- a/openbsc/src/gsm_data.c
+++ b/openbsc/src/gsm_data.c
@@ -27,9 +27,23 @@
#include <openbsc/gsm_data.h>
#include <openbsc/talloc.h>
+#include <openbsc/abis_nm.h>
void *tall_bsc_ctx;
+const char *get_value_string(const struct value_string *vs, u_int32_t val)
+{
+ int i;
+
+ for (i = 0;; i++) {
+ if (vs[i].value == 0 && vs[i].str == NULL)
+ break;
+ if (vs[i].value == val)
+ return vs[i].str;
+ }
+ return "unknown";
+}
+
void set_ts_e1link(struct gsm_bts_trx_ts *ts, u_int8_t e1_nr,
u_int8_t e1_ts, u_int8_t e1_ts_ss)
{
@@ -45,6 +59,8 @@ static const char *pchan_names[] = {
[GSM_PCHAN_TCH_F] = "TCH/F",
[GSM_PCHAN_TCH_H] = "TCH/H",
[GSM_PCHAN_SDCCH8_SACCH8C] = "SDCCH8",
+ [GSM_PCHAN_PDCH] = "PDCH",
+ [GSM_PCHAN_TCH_F_PDCH] = "TCH/F_PDCH",
[GSM_PCHAN_UNKNOWN] = "UNKNOWN",
};
@@ -102,13 +118,12 @@ const char *gsm_chreq_name(enum gsm_chreq_reason_t c)
struct gsm_bts_trx *gsm_bts_trx_alloc(struct gsm_bts *bts)
{
- struct gsm_bts_trx *trx = talloc(bts, struct gsm_bts_trx);
+ struct gsm_bts_trx *trx = talloc_zero(bts, struct gsm_bts_trx);
int k;
if (!trx)
return NULL;
- memset(trx, 0, sizeof(*trx));
trx->bts = bts;
trx->nr = bts->num_trx++;
@@ -138,12 +153,12 @@ struct gsm_bts_trx *gsm_bts_trx_alloc(struct gsm_bts *bts)
struct gsm_bts *gsm_bts_alloc(struct gsm_network *net, enum gsm_bts_type type,
u_int8_t tsc, u_int8_t bsic)
{
- struct gsm_bts *bts = talloc(net, struct gsm_bts);
+ struct gsm_bts *bts = talloc_zero(net, struct gsm_bts);
+ int i;
if (!bts)
return NULL;
- memset(bts, 0, sizeof(*bts));
bts->network = net;
bts->nr = net->num_bts++;
bts->type = type;
@@ -153,6 +168,11 @@ struct gsm_bts *gsm_bts_alloc(struct gsm_network *net, enum gsm_bts_type type,
INIT_LLIST_HEAD(&bts->trx_list);
bts->ms_max_power = 15; /* dBm */
+ for (i = 0; i < ARRAY_SIZE(bts->gprs.nsvc); i++) {
+ bts->gprs.nsvc[i].bts = bts;
+ bts->gprs.nsvc[i].id = i;
+ }
+
/* create our primary TRX */
bts->c0 = gsm_bts_trx_alloc(bts);
if (!bts->c0) {
@@ -171,10 +191,9 @@ struct gsm_network *gsm_network_init(u_int16_t country_code, u_int16_t network_c
{
struct gsm_network *net;
- net = talloc(tall_bsc_ctx, struct gsm_network);
+ net = talloc_zero(tall_bsc_ctx, struct gsm_network);
if (!net)
return NULL;
- memset(net, 0, sizeof(*net));
net->country_code = country_code;
net->network_code = network_code;
@@ -224,7 +243,7 @@ static char ts2str[255];
char *gsm_ts_name(struct gsm_bts_trx_ts *ts)
{
snprintf(ts2str, sizeof(ts2str), "(bts=%d,trx=%d,ts=%d)",
- ts->trx->bts->bts_nr, ts->trx->nr, ts->nr);
+ ts->trx->bts->nr, ts->trx->nr, ts->nr);
return ts2str;
}
diff --git a/openbsc/src/gsm_subscriber_base.c b/openbsc/src/gsm_subscriber_base.c
index 868b35599..48374eae5 100644
--- a/openbsc/src/gsm_subscriber_base.c
+++ b/openbsc/src/gsm_subscriber_base.c
@@ -115,11 +115,10 @@ struct gsm_subscriber *subscr_alloc(void)
{
struct gsm_subscriber *s;
- s = talloc(tall_subscr_ctx, struct gsm_subscriber);
+ s = talloc_zero(tall_subscr_ctx, struct gsm_subscriber);
if (!s)
return NULL;
- memset(s, 0, sizeof(*s));
llist_add_tail(&s->entry, &active_subscribers);
s->use_count = 1;
s->tmsi = GSM_RESERVED_TMSI;
diff --git a/openbsc/src/gsm_utils.c b/openbsc/src/gsm_utils.c
index de18dba26..ddfd7f3de 100644
--- a/openbsc/src/gsm_utils.c
+++ b/openbsc/src/gsm_utils.c
@@ -23,8 +23,10 @@
#include <openbsc/gsm_data.h>
#include <openbsc/gsm_utils.h>
+#include <execinfo.h>
#include <stdlib.h>
#include <string.h>
+#include <stdio.h>
#include <errno.h>
/* GSM 03.38 6.2.1 Charachter packing */
@@ -148,4 +150,21 @@ int ms_pwr_dbm(enum gsm_band band, u_int8_t lvl)
return -EINVAL;
}
+void generate_backtrace()
+{
+ int i, nptrs;
+ void *buffer[100];
+ char **strings;
+
+ nptrs = backtrace(buffer, ARRAY_SIZE(buffer));
+ printf("backtrace() returned %d addresses\n", nptrs);
+
+ strings = backtrace_symbols(buffer, nptrs);
+ if (!strings)
+ return;
+ for (i = 1; i < nptrs; i++)
+ printf("%s\n", strings[i]);
+
+ free(strings);
+}
diff --git a/openbsc/src/input/ipaccess.c b/openbsc/src/input/ipaccess.c
index 2239cf152..2d9f51ef9 100644
--- a/openbsc/src/input/ipaccess.c
+++ b/openbsc/src/input/ipaccess.c
@@ -222,20 +222,20 @@ static int ipaccess_rcvmsg(struct e1inp_line *line, struct msgb *msg,
if (bfd->priv_nr == 1) {
bts->oml_link = e1inp_sign_link_create(&line->ts[1-1],
E1INP_SIGN_OML, bts->c0,
- 0, 0xff);
+ bts->oml_tei, 0);
} else if (bfd->priv_nr == 2) {
struct e1inp_ts *e1i_ts;
struct bsc_fd *newbfd;
+ struct gsm_bts_trx *trx = gsm_bts_trx_num(bts, trx_id);
- /* FIXME: implement this for non-0 TRX */
bfd->data = line = bts->oml_link->ts->line;
- e1i_ts = &line->ts[2-1];
+ e1i_ts = &line->ts[2+trx_id - 1];
newbfd = &e1i_ts->driver.ipaccess.fd;
+ e1inp_ts_config(e1i_ts, line, E1INP_TS_TYPE_SIGN);
- bts->c0->rsl_link =
- e1inp_sign_link_create(e1i_ts,
- E1INP_SIGN_RSL, bts->c0,
- 0, 0);
+ trx->rsl_link = e1inp_sign_link_create(e1i_ts,
+ E1INP_SIGN_RSL, trx,
+ trx->rsl_tei, 0);
/* get rid of our old temporary bfd */
memcpy(newbfd, bfd, sizeof(*newbfd));
bsc_unregister_fd(bfd);
@@ -259,7 +259,7 @@ struct msgb *ipaccess_read_msg(struct bsc_fd *bfd, int *error)
{
struct msgb *msg = msgb_alloc(TS1_ALLOC_SIZE, "Abis/IP");
struct ipaccess_head *hh;
- int ret = 0;
+ int len, ret = 0;
if (!msg) {
*error = -ENOMEM;
@@ -284,8 +284,9 @@ struct msgb *ipaccess_read_msg(struct bsc_fd *bfd, int *error)
/* then read te length as specified in header */
msg->l2h = msg->data + sizeof(*hh);
- ret = recv(bfd->fd, msg->l2h, hh->len, 0);
- if (ret < hh->len) {
+ len = ntohs(hh->len);
+ ret = recv(bfd->fd, msg->l2h, len, 0);
+ if (ret < len) {
fprintf(stderr, "short read!\n");
msgb_free(msg);
*error = -EIO;
@@ -304,7 +305,7 @@ static int handle_ts1_read(struct bsc_fd *bfd)
struct e1inp_sign_link *link;
struct msgb *msg;
struct ipaccess_head *hh;
- int ret, error;
+ int ret = 0, error;
msg = ipaccess_read_msg(bfd, &error);
if (!msg) {
@@ -337,7 +338,7 @@ static int handle_ts1_read(struct bsc_fd *bfd)
/* BIG FAT WARNING: bfd might no longer exist here, since ipaccess_rcvmsg()
* might have free'd it !!! */
- link = e1inp_lookup_sign_link(e1i_ts, 0, hh->proto);
+ link = e1inp_lookup_sign_link(e1i_ts, hh->proto, 0);
if (!link) {
printf("no matching signalling link for hh->proto=0x%02x\n", hh->proto);
msgb_free(msg);
@@ -345,17 +346,17 @@ static int handle_ts1_read(struct bsc_fd *bfd)
}
msg->trx = link->trx;
- switch (hh->proto) {
- case IPAC_PROTO_RSL:
+ switch (link->type) {
+ case E1INP_SIGN_RSL:
if (!rsl_up) {
- e1inp_event(e1i_ts, EVT_E1_TEI_UP, 0, IPAC_PROTO_RSL);
+ e1inp_event(e1i_ts, EVT_E1_TEI_UP, link->tei, link->sapi);
rsl_up = 1;
}
ret = abis_rsl_rcvmsg(msg);
break;
- case IPAC_PROTO_OML:
+ case E1INP_SIGN_OML:
if (!oml_up) {
- e1inp_event(e1i_ts, EVT_E1_TEI_UP, 0, IPAC_PROTO_OML);
+ e1inp_event(e1i_ts, EVT_E1_TEI_UP, link->tei, link->sapi);
oml_up = 1;
}
ret = abis_nm_rcvmsg(msg);
@@ -374,8 +375,7 @@ void ipaccess_prepend_header(struct msgb *msg, int proto)
/* prepend the ip.access header */
hh = (struct ipaccess_head *) msgb_push(msg, sizeof(*hh));
- hh->zero = 0;
- hh->len = msg->len - sizeof(*hh);
+ hh->len = htons(msg->len - sizeof(*hh));
hh->proto = proto;
}
@@ -426,9 +426,8 @@ static int handle_ts1_write(struct bsc_fd *bfd)
return -EINVAL;
}
-
msg->l2h = msg->data;
- ipaccess_prepend_header(msg, proto);
+ ipaccess_prepend_header(msg, sign_link->tei);
DEBUGP(DMI, "TX %u: %s\n", ts_nr, hexdump(msg->l2h, msgb_l2len(msg)));
@@ -495,17 +494,15 @@ static int listen_fd_cb(struct bsc_fd *listen_bfd, unsigned int what)
}
DEBUGP(DINP, "accept()ed new OML link from %s\n", inet_ntoa(sa.sin_addr));
- line = talloc(tall_bsc_ctx, struct e1inp_line);
+ line = talloc_zero(tall_bsc_ctx, struct e1inp_line);
if (!line) {
close(ret);
return -ENOMEM;
}
- memset(line, 0, sizeof(*line));
line->driver = &ipaccess_driver;
//line->driver_data = e1h;
/* create virrtual E1 timeslots for signalling */
e1inp_ts_config(&line->ts[1-1], line, E1INP_TS_TYPE_SIGN);
- e1inp_ts_config(&line->ts[2-1], line, E1INP_TS_TYPE_SIGN);
e1i_ts = &line->ts[idx];
@@ -540,14 +537,13 @@ static int rsl_listen_fd_cb(struct bsc_fd *listen_bfd, unsigned int what)
if (!(what & BSC_FD_READ))
return 0;
- bfd = talloc(tall_bsc_ctx, struct bsc_fd);
+ bfd = talloc_zero(tall_bsc_ctx, struct bsc_fd);
if (!bfd)
return -ENOMEM;
- memset(bfd, 0, sizeof(*bfd));
/* Some BTS has connected to us, but we don't know yet which line
* (as created by the OML link) to associate it with. Thus, we
- * aloocate a temporary bfd until we have received ID from BTS */
+ * allocate a temporary bfd until we have received ID from BTS */
bfd->fd = accept(listen_bfd->fd, (struct sockaddr *) &sa, &sa_len);
if (bfd->fd < 0) {
@@ -654,10 +650,9 @@ int ipaccess_setup(struct gsm_network *gsmnet)
if (ret)
return ret;
- e1h = talloc(tall_bsc_ctx, struct ia_e1_handle);
+ e1h = talloc_zero(tall_bsc_ctx, struct ia_e1_handle);
if (!e1h)
return -ENOMEM;
- memset(e1h, 0, sizeof(*e1h));
e1h->gsmnet = gsmnet;
diff --git a/openbsc/src/input/misdn.c b/openbsc/src/input/misdn.c
index 82268e811..135cfad48 100644
--- a/openbsc/src/input/misdn.c
+++ b/openbsc/src/input/misdn.c
@@ -262,7 +262,7 @@ static int handle_tsX_write(struct bsc_fd *bfd)
ret = send(bfd->fd, tx_buf, sizeof(*hh) + BCHAN_TX_GRAN, 0);
if (ret < sizeof(*hh) + BCHAN_TX_GRAN)
- DEBUGP(DMIB, "send returns %d instead of %u\n", ret,
+ DEBUGP(DMIB, "send returns %d instead of %lu\n", ret,
sizeof(*hh) + BCHAN_TX_GRAN);
return ret;
diff --git a/openbsc/src/ipaccess-config.c b/openbsc/src/ipaccess-config.c
index 46043d571..c50a46581 100644
--- a/openbsc/src/ipaccess-config.c
+++ b/openbsc/src/ipaccess-config.c
@@ -61,7 +61,7 @@ static u_int8_t unit_id_attr[] = { 0x91, 0x00, 9, '2', '3', '4', '2', '/' , '0',
* result. The nanoBTS will send us a NACK when we did something the
* BTS didn't like.
*/
-static int ipacc_msg_nack(int mt)
+static int ipacc_msg_nack(u_int8_t mt)
{
fprintf(stderr, "Failure to set attribute. This seems fatal\n");
exit(-1);
@@ -79,30 +79,14 @@ struct ipacc_cusage_elem {
rxlev:6;
} __attribute__ ((packed));
-static const char *ipacc_testres_names[] = {
- [NM_IPACC_TESTRES_SUCCESS] = "SUCCESS",
- [NM_IPACC_TESTRES_TIMEOUT] = "TIMEOUT",
- [NM_IPACC_TESTRES_NO_CHANS] = "NO CHANNELS",
- [NM_IPACC_TESTRES_PARTIAL] = "PARTIAL",
- [NM_IPACC_TESTRES_STOPPED] = "STOPPED",
-};
-
-const char *ipacc_testres_name(u_int8_t res)
-{
- if (res < ARRAY_SIZE(ipacc_testres_names) &&
- ipacc_testres_names[res])
- return ipacc_testres_names[res];
-
- return "unknown";
-}
-
static int test_rep(void *_msg)
{
struct msgb *msg = _msg;
struct abis_om_fom_hdr *foh = msgb_l3(msg);
u_int16_t test_rep_len, ferr_list_len;
struct ipacc_ferr_elem *ife;
- int i;
+ struct ipac_bcch_info binfo;
+ int i, rc;
DEBUGP(DNM, "TEST REPORT: ");
@@ -119,7 +103,7 @@ static int test_rep(void *_msg)
/* data[6]: ip.access nested IE. 3 == freq_err_list */
switch (foh->data[6]) {
- case 3:
+ case NM_IPAC_EIE_FREQ_ERR_LIST:
/* data[7..8]: length of ferr_list */
ferr_list_len = ntohs(*(u_int16_t *) &foh->data[7]);
@@ -130,7 +114,7 @@ static int test_rep(void *_msg)
ife->arfcn, ntohs(ife->freq_err));
}
break;
- case 4:
+ case NM_IPAC_EIE_CHAN_USE_LIST:
/* data[7..8]: length of ferr_list */
ferr_list_len = ntohs(*(u_int16_t *) &foh->data[7]);
@@ -142,6 +126,19 @@ static int test_rep(void *_msg)
cu & 0x3ff, cu >> 10);
}
break;
+ case NM_IPAC_EIE_BCCH_INFO_TYPE:
+ break;
+ case NM_IPAC_EIE_BCCH_INFO:
+ rc = ipac_parse_bcch_info(&binfo, foh->data+6);
+ if (rc < 0) {
+ DEBUGP(DNM, "BCCH Info parsing failed\n");
+ break;
+ }
+ DEBUGP(DNM, "==> ARFCN %u, RxLev %2u, RxQual %2u: %3d-%d, LAC %d CI %d\n",
+ binfo.arfcn, binfo.rx_lev, binfo.rx_qual,
+ binfo.cgi.mcc, binfo.cgi.mnc,
+ binfo.cgi.lac, binfo.cgi.ci);
+ break;
default:
break;
}
@@ -152,9 +149,12 @@ static int test_rep(void *_msg)
static int nm_sig_cb(unsigned int subsys, unsigned int signal,
void *handler_data, void *signal_data)
{
+ u_int8_t *msg_type;
+
switch (signal) {
case S_NM_IPACC_NACK:
- return ipacc_msg_nack((int)signal_data);
+ msg_type = signal_data;
+ return ipacc_msg_nack(*msg_type);
case S_NM_TEST_REP:
return test_rep(signal_data);
default:
@@ -279,15 +279,16 @@ static void print_help(void)
printf(" -o --oml-ip ip\n");
printf(" -r --restart\n");
printf(" -n flags/mask\tSet NVRAM attributes.\n");
- printf(" -l --listen testnr \tPerform speciified test number\n");
+ printf(" -l --listen testnr \tPerform specified test number\n");
printf(" -h --help this text\n");
+ printf(" -s --stream-id ID\n");
}
int main(int argc, char **argv)
{
struct gsm_bts *bts;
struct sockaddr_in sin;
- int rc, option_index = 0;
+ int rc, option_index = 0, stream_id = 0xff;
printf("ipaccess-config (C) 2009 by Harald Welte\n");
printf("This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY\n\n");
@@ -302,9 +303,10 @@ int main(int argc, char **argv)
{ "restart", 0, 0, 'r' },
{ "help", 0, 0, 'h' },
{ "listen", 1, 0, 'l' },
+ { "stream-id", 1, 0, 's' },
};
- c = getopt_long(argc, argv, "u:o:rn:l:h", long_options,
+ c = getopt_long(argc, argv, "u:o:rn:l:hs:", long_options,
&option_index);
if (c == -1)
@@ -332,6 +334,10 @@ int main(int argc, char **argv)
case 'l':
net_listen_testnr = atoi(optarg);
break;
+ case 's':
+ stream_id = atoi(optarg);
+ printf("foo: %d\n", stream_id);
+ break;
case 'h':
print_usage();
print_help();
@@ -350,6 +356,7 @@ int main(int argc, char **argv)
bts = gsm_bts_alloc(gsmnet, GSM_BTS_TYPE_NANOBTS, HARDCODED_TSC,
HARDCODED_BSIC);
+ bts->oml_tei = stream_id;
register_signal_handler(SS_NM, nm_sig_cb, NULL);
printf("Trying to connect to ip.access BTS ...\n");
diff --git a/openbsc/src/mgcp.cfg b/openbsc/src/mgcp.cfg
new file mode 100644
index 000000000..678f54637
--- /dev/null
+++ b/openbsc/src/mgcp.cfg
@@ -0,0 +1,19 @@
+!
+! MGCP configuration hand edited
+! !
+password foo
+!
+line vty
+ no login
+!
+mgcp
+! local ip 213.167.134.14
+ bts ip 172.16.252.43
+ bind ip 213.167.134.141
+ bind port 2427
+ bind early 1
+ rtp base 4000
+ sdp audio payload number 98
+ sdp audio payload name AMR/8000
+ number endpoints 31
+ loop 1
diff --git a/openbsc/src/msgb.c b/openbsc/src/msgb.c
index 52edf2dcd..edeb975a9 100644
--- a/openbsc/src/msgb.c
+++ b/openbsc/src/msgb.c
@@ -74,6 +74,26 @@ struct msgb *msgb_dequeue(struct llist_head *queue)
return llist_entry(lh, struct msgb, list);
}
+void msgb_reset(struct msgb *msg)
+{
+ msg->len = 0;
+ msg->len = 0;
+ msg->data = msg->_data;
+
+ msg->head = msg->data;
+ msg->data = msg->data;
+ /* reset tail pointer */
+ msg->tail = msg->data;
+
+ /* reset pointers */
+ msg->bts_link = NULL;
+ msg->trx = NULL;
+ msg->lchan = NULL;
+ msg->l2h = NULL;
+ msg->l3h = NULL;
+ msg->smsh = NULL;
+}
+
static __attribute__((constructor)) void on_dso_load_trau_msgb(void)
{
tall_msgb_ctx = talloc_named_const(tall_bsc_ctx, 1, "msgb");
diff --git a/openbsc/src/openbsc.cfg.1-1 b/openbsc/src/openbsc.cfg.1-1
index a8331ddbd..d312843b0 100644
--- a/openbsc/src/openbsc.cfg.1-1
+++ b/openbsc/src/openbsc.cfg.1-1
@@ -11,6 +11,8 @@ network
mobile network code 1
short name OpenBSC
long name OpenBSC
+ timer t3101 10
+ timer t3113 60
bts 0
type bs11
band GSM900
diff --git a/openbsc/src/openbsc.cfg.1-2 b/openbsc/src/openbsc.cfg.1-2
index 10aa7b48b..84d50c75c 100644
--- a/openbsc/src/openbsc.cfg.1-2
+++ b/openbsc/src/openbsc.cfg.1-2
@@ -11,6 +11,8 @@ network
mobile network code 1
short name OpenBSC
long name OpenBSC
+ timer t3101 10
+ timer t3113 60
bts 0
type bs11
band GSM900
diff --git a/openbsc/src/openbsc.cfg.2-2 b/openbsc/src/openbsc.cfg.2-2
index 0dd9d9b5d..c1468a647 100644
--- a/openbsc/src/openbsc.cfg.2-2
+++ b/openbsc/src/openbsc.cfg.2-2
@@ -11,6 +11,8 @@ network
mobile network code 1
short name OpenBSC
long name OpenBSC
+ timer t3101 10
+ timer t3113 60
bts 0
type bs11
band GSM900
diff --git a/openbsc/src/openbsc.cfg.nanobts b/openbsc/src/openbsc.cfg.nanobts
new file mode 100644
index 000000000..a1ceaec79
--- /dev/null
+++ b/openbsc/src/openbsc.cfg.nanobts
@@ -0,0 +1,40 @@
+!
+! OpenBSC configuration saved from vty
+!
+password foo
+!
+line vty
+ no login
+!
+network
+ network country code 1
+ mobile network code 1
+ short name OpenBSC
+ long name OpenBSC
+ timer t3101 10
+ timer t3113 60
+ bts 0
+ type nanobts
+ ip.access unit_id 1801 0
+ band GSM1800
+ location_area_code 1
+ training_sequence_code 7
+ base_station_id_code 63
+ trx 0
+ arfcn 514
+ timeslot 0
+ phys_chan_config CCCH+SDCCH4
+ timeslot 1
+ phys_chan_config SDCCH8
+ timeslot 2
+ phys_chan_config TCH/F
+ timeslot 3
+ phys_chan_config TCH/F
+ timeslot 4
+ phys_chan_config TCH/F
+ timeslot 5
+ phys_chan_config TCH/F
+ timeslot 6
+ phys_chan_config TCH/F
+ timeslot 7
+ phys_chan_config TCH/F
diff --git a/openbsc/src/paging.c b/openbsc/src/paging.c
index 87c7e7d38..fe6ea52d1 100644
--- a/openbsc/src/paging.c
+++ b/openbsc/src/paging.c
@@ -197,6 +197,8 @@ static void paging_T3113_expired(void *data)
{
struct gsm_paging_request *req = (struct gsm_paging_request *)data;
struct paging_signal_data sig_data;
+ void *cbfn_param;
+ gsm_cbfn *cbfn;
DEBUGP(DPAG, "T3113 expired for request %p (%s)\n",
req, req->subscr->imsi);
@@ -205,11 +207,15 @@ static void paging_T3113_expired(void *data)
sig_data.bts = req->bts;
sig_data.lchan = NULL;
- dispatch_signal(SS_PAGING, S_PAGING_COMPLETED, &sig_data);
- if (req->cbfn)
- req->cbfn(GSM_HOOK_RR_PAGING, GSM_PAGING_EXPIRED, NULL, NULL,
- req->cbfn_param);
+ /* must be destroyed before calling cbfn, to prevent double free */
+ cbfn_param = req->cbfn_param;
+ cbfn = req->cbfn;
paging_remove_request(&req->bts->paging, req);
+
+ dispatch_signal(SS_PAGING, S_PAGING_COMPLETED, &sig_data);
+ if (cbfn)
+ cbfn(GSM_HOOK_RR_PAGING, GSM_PAGING_EXPIRED, NULL, NULL,
+ cbfn_param);
}
static int _paging_request(struct gsm_bts *bts, struct gsm_subscriber *subscr,
@@ -233,7 +239,7 @@ static int _paging_request(struct gsm_bts *bts, struct gsm_subscriber *subscr,
req->cbfn_param = data;
req->T3113.cb = paging_T3113_expired;
req->T3113.data = req;
- bsc_schedule_timer(&req->T3113, T3113_VALUE);
+ bsc_schedule_timer(&req->T3113, bts->network->T3113, 0);
llist_add_tail(&req->entry, &bts_entry->pending_requests);
if (!bsc_timer_pending(&bts_entry->work_timer))
diff --git a/openbsc/src/rrlp.c b/openbsc/src/rrlp.c
index 61bb20244..523b53f0b 100644
--- a/openbsc/src/rrlp.c
+++ b/openbsc/src/rrlp.c
@@ -26,6 +26,7 @@
#include <openbsc/gsm_04_08.h>
#include <openbsc/signal.h>
#include <openbsc/gsm_subscriber.h>
+#include <openbsc/chan_alloc.h>
/* RRLP MS based position request */
static const u_int8_t ms_based_pos_req[] = { 0x40, 0x01, 0x78, 0xa8 };
diff --git a/openbsc/src/sccp/sccp.c b/openbsc/src/sccp/sccp.c
new file mode 100644
index 000000000..522afcf7a
--- /dev/null
+++ b/openbsc/src/sccp/sccp.c
@@ -0,0 +1,1171 @@
+/*
+ * SCCP management code
+ *
+ * (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * (C) 2009 by on-waves.com
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <string.h>
+
+#include <sccp/sccp.h>
+
+#include <openbsc/debug.h>
+#include <openbsc/talloc.h>
+#include <openbsc/linuxlist.h>
+
+static void *tall_sccp_ctx;
+static LLIST_HEAD(sccp_connections);
+
+#define SCCP_MSG_SIZE 4096
+#define SCCP_MSG_HEADROOM 128
+
+/* global data */
+const struct sockaddr_sccp sccp_ssn_bssap = {
+ .sccp_family = 0,
+ .sccp_ssn = SCCP_SSN_BSSAP,
+};
+
+struct sccp_system {
+ /* layer3 -> layer2 */
+ int (*write_data)(struct msgb *data, void *context);
+ void *write_context;
+};
+
+
+static struct sccp_system sccp_system = {
+ .write_data = NULL,
+};
+
+struct sccp_data_callback {
+ /* connection based */
+ int (*accept_cb)(struct sccp_connection *, void *);
+ void *accept_context;
+
+ /* connection less */
+ int (*read_cb)(struct msgb *, unsigned int, void *);
+ void *read_context;
+
+ u_int8_t ssn;
+ struct llist_head callback;
+};
+
+static LLIST_HEAD(sccp_callbacks);
+
+static struct sccp_data_callback *_find_ssn(u_int8_t ssn)
+{
+ struct sccp_data_callback *cb;
+
+ llist_for_each_entry(cb, &sccp_callbacks, callback) {
+ if (cb->ssn == ssn)
+ return cb;
+ }
+
+ /* need to add one */
+ cb = talloc_zero(tall_sccp_ctx, struct sccp_data_callback);
+ if (!cb) {
+ DEBUGP(DSCCP, "Failed to allocate sccp callback.\n");
+ return NULL;
+ }
+
+ cb->ssn = ssn;
+ llist_add_tail(&cb->callback, &sccp_callbacks);
+ return cb;
+}
+
+
+static int _send_msg(struct msgb *msg)
+{
+ return sccp_system.write_data(msg, sccp_system.write_context);
+}
+
+/*
+ * parsing routines
+ */
+static int copy_address(struct sccp_address *addr, u_int8_t offset, struct msgb *msgb)
+{
+ struct sccp_called_party_address *party;
+
+ int room = msgb_l2len(msgb) - offset;
+ u_int8_t read = 0;
+ u_int8_t length;
+
+ if (room <= 0) {
+ DEBUGP(DSCCP, "Not enough room for an address: %u\n", room);
+ return -1;
+ }
+
+ length = msgb->l2h[offset];
+ if (room <= length) {
+ DEBUGP(DSCCP, "Not enough room for optional data %u %u\n", room, length);
+ return -1;
+ }
+
+
+ party = (struct sccp_called_party_address *)(msgb->l2h + offset + 1);
+ if (party->point_code_indicator) {
+ if (length <= read + 2) {
+ DEBUGP(DSCCP, "POI does not fit %u\n", length);
+ return -1;
+ }
+
+
+ memcpy(&addr->poi, &party->data[read], 2);
+ read += 2;
+ }
+
+ if (party->ssn_indicator) {
+ if (length <= read + 1) {
+ DEBUGP(DSCCP, "SSN does not fit %u\n", length);
+ return -1;
+ }
+
+ addr->ssn = party->data[read];
+ read += 1;
+ }
+
+ if (party->global_title_indicator) {
+ DEBUGP(DSCCP, "GTI not supported %u\n", *(u_int8_t *)party);
+ return -1;
+ }
+
+ addr->address = *party;
+ return 0;
+}
+
+static int check_address(struct sccp_address *addr)
+{
+ /* ignore point_code_indicator... it should be zero... but */
+ if (addr->address.ssn_indicator != 1
+ || addr->address.global_title_indicator == 1
+ || addr->address.routing_indicator != 1) {
+ DEBUGP(DSCCP, "Invalid called address according to 08.06: 0x%x 0x%x\n",
+ *(u_int8_t *)&addr->address, addr->ssn);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int _sccp_parse_optional_data(const int offset,
+ struct msgb *msgb, struct sccp_optional_data *data)
+{
+ u_int16_t room = msgb_l2len(msgb) - offset;
+ u_int16_t read = 0;
+
+ while (room > read) {
+ u_int8_t type = msgb->l2h[offset + read];
+ if (type == SCCP_PNC_END_OF_OPTIONAL)
+ return 0;
+
+ if (read + 1 >= room) {
+ DEBUGP(DSCCP, "no place for length\n");
+ return 0;
+ }
+
+ u_int8_t length = msgb->l2h[offset + read + 1];
+ read += 2 + length;
+
+
+ if (room <= read) {
+ DEBUGP(DSCCP, "no space for the data: type: %d read: %d room: %d l2: %d\n",
+ type, read, room, msgb_l2len(msgb));
+ return 0;
+ }
+
+ if (type == SCCP_PNC_DATA) {
+ data->data_len = length;
+ data->data_start = offset + read - length;
+ }
+
+ }
+
+ return -1;
+}
+
+/*
+ * Send UDT. Currently we have a fixed address...
+ */
+static int _sccp_send_data(int class, const struct sockaddr_sccp *in,
+ const struct sockaddr_sccp *out, struct msgb *payload)
+{
+ struct sccp_data_unitdata *udt;
+ u_int8_t *data;
+ int ret;
+
+ if (msgb_l3len(payload) > 256) {
+ DEBUGP(DSCCP, "The payload is too big for one udt\n");
+ return -1;
+ }
+
+ struct msgb *msg = msgb_alloc_headroom(SCCP_MSG_SIZE,
+ SCCP_MSG_HEADROOM, "sccp: udt");
+ msg->l2h = &msg->data[0];
+ udt = (struct sccp_data_unitdata *)msgb_put(msg, sizeof(*udt));
+
+ udt->type = SCCP_MSG_TYPE_UDT;
+ udt->proto_class = class;
+ udt->variable_called = 3;
+ udt->variable_calling = 5;
+ udt->variable_data = 7;
+
+ /* for variable data we start with a size and the data */
+ data = msgb_put(msg, 1 + 2);
+ data[0] = 2;
+ data[1] = 0x42;
+ data[2] = out->sccp_ssn;
+
+ data = msgb_put(msg, 1 + 2);
+ data[0] = 2;
+ data[1] = 0x42;
+ data[2] = in->sccp_ssn;
+
+ /* copy the payload */
+ data = msgb_put(msg, 1 + msgb_l3len(payload));
+ data[0] = msgb_l3len(payload);
+ memcpy(&data[1], payload->l3h, msgb_l3len(payload));
+
+ ret = _send_msg(msg);
+ msgb_free(msg);
+
+ return ret;
+}
+
+static int _sccp_handle_read(struct msgb *msgb)
+{
+ static const u_int32_t header_size = sizeof(struct sccp_data_unitdata);
+ static const u_int32_t called_offset = offsetof(struct sccp_data_unitdata, variable_called);
+ static const u_int32_t calling_offset = offsetof(struct sccp_data_unitdata, variable_calling);
+ static const u_int32_t data_offset = offsetof(struct sccp_data_unitdata, variable_data);
+
+ struct sccp_data_callback *cb;
+ struct sccp_data_unitdata *udt = (struct sccp_data_unitdata *)msgb->l2h;
+ struct sccp_address called, calling;
+
+ /* we don't have enough size for the struct */
+ if (msgb_l2len(msgb) < header_size) {
+ DEBUGP(DSCCP, "msgb < header_size %u %u\n",
+ msgb_l2len(msgb), header_size);
+ return -1;
+ }
+
+ /* copy out the calling and called address. Add the off */
+ if (copy_address(&called, called_offset + udt->variable_called, msgb) != 0)
+ return -1;
+
+ if (check_address(&called) != 0) {
+ DEBUGP(DSCCP, "Invalid called address according to 08.06: 0x%x 0x%x\n",
+ *(u_int8_t *)&called.address, called.ssn);
+ return -1;
+ }
+
+ cb = _find_ssn(called.ssn);
+ if (!cb || !cb->read_cb) {
+ DEBUGP(DSCCP, "No routing for UDT for called SSN: %u\n", called.ssn);
+ return -1;
+ }
+
+ if (copy_address(&calling, calling_offset + udt->variable_calling, msgb) != 0)
+ return -1;
+
+ if (check_address(&calling) != 0) {
+ DEBUGP(DSCCP, "Invalid called address according to 08.06: 0x%x 0x%x\n",
+ *(u_int8_t *)&called.address, called.ssn);
+ }
+
+ /* we don't have enough size for the data */
+ if (msgb_l2len(msgb) < data_offset + udt->variable_data + 1) {
+ DEBUGP(DSCCP, "msgb < header + offset %u %u %u\n",
+ msgb_l2len(msgb), header_size, udt->variable_data);
+ return -1;
+ }
+
+
+ msgb->l3h = &udt->data[udt->variable_data];
+
+ if (msgb_l3len(msgb) != msgb->l3h[-1]) {
+ DEBUGP(DSCCP, "msgb is truncated %u %u\n",
+ msgb_l3len(msgb), msgb->l3h[-1]);
+ return -1;
+ }
+
+ /* sanity check */
+ return cb->read_cb(msgb, msgb_l3len(msgb), cb->read_context);
+}
+
+/*
+ * handle connection orientated methods
+ */
+static int source_local_reference_is_free(struct sccp_source_reference *reference)
+{
+ struct sccp_connection *connection;
+
+ llist_for_each_entry(connection, &sccp_connections, list) {
+ if (memcmp(reference, &connection->source_local_reference, sizeof(*reference)) == 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+static int destination_local_reference_is_free(struct sccp_source_reference *reference)
+{
+ struct sccp_connection *connection;
+
+ llist_for_each_entry(connection, &sccp_connections, list) {
+ if (memcmp(reference, &connection->destination_local_reference, sizeof(*reference)) == 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+static int assign_source_local_reference(struct sccp_connection *connection)
+{
+ static u_int32_t last_ref = 0x30000;
+ int wrapped = 0;
+
+ do {
+ struct sccp_source_reference reference;
+ reference.octet1 = (last_ref >> 0) & 0xff;
+ reference.octet2 = (last_ref >> 8) & 0xff;
+ reference.octet3 = (last_ref >> 16) & 0xff;
+
+ ++last_ref;
+ /* do not use the reversed word and wrap around */
+ if ((last_ref & 0x00FFFFFF) == 0x00FFFFFF) {
+ DEBUGP(DSCCP, "Wrapped searching for a free code\n");
+ last_ref = 0;
+ ++wrapped;
+ }
+
+ if (source_local_reference_is_free(&reference) == 0) {
+ connection->source_local_reference = reference;
+ return 0;
+ }
+ } while (wrapped != 2);
+
+ DEBUGP(DSCCP, "Finding a free reference failed\n");
+ return -1;
+}
+
+static void _sccp_set_connection_state(struct sccp_connection *connection, int new_state)
+{
+ int old_state = connection->connection_state;
+
+ connection->connection_state = new_state;
+ if (connection->state_cb)
+ connection->state_cb(connection, old_state);
+}
+
+static int _sccp_send_refuse(struct sccp_connection_request *req, int cause)
+{
+ struct msgb *msgb;
+ struct sccp_connection_refused *ref;
+ u_int8_t *data;
+ int ret;
+
+ msgb = msgb_alloc_headroom(SCCP_MSG_SIZE,
+ SCCP_MSG_HEADROOM, "sccp ref");
+ msgb->l2h = &msgb->data[0];
+
+ ref = (struct sccp_connection_refused *) msgb_put(msgb, sizeof(*ref));
+ ref->type = SCCP_MSG_TYPE_CREF;
+ memcpy(&ref->destination_local_reference, &req->source_local_reference,
+ sizeof(struct sccp_source_reference));
+ ref->cause = cause;
+ ref->optional_start = 1;
+
+ data = msgb_put(msgb, 1);
+ data[0] = SCCP_PNC_END_OF_OPTIONAL;
+
+ ret = _send_msg(msgb);
+ msgb_free(msgb);
+ return ret;
+}
+
+static int _sccp_send_connection_confirm(struct sccp_connection *connection)
+{
+ struct msgb *response;
+ struct sccp_connection_confirm *confirm;
+ u_int8_t *optional_data;
+ int ret;
+
+ if (assign_source_local_reference(connection) != 0)
+ return -1;
+
+ response = msgb_alloc_headroom(SCCP_MSG_SIZE,
+ SCCP_MSG_HEADROOM, "sccp confirm");
+ response->l2h = &response->data[0];
+
+ confirm = (struct sccp_connection_confirm *) msgb_put(response, sizeof(*confirm));
+
+ confirm->type = SCCP_MSG_TYPE_CC;
+ memcpy(&confirm->destination_local_reference,
+ &connection->destination_local_reference,
+ sizeof(connection->destination_local_reference));
+ memcpy(&confirm->source_local_reference,
+ &connection->source_local_reference,
+ sizeof(connection->source_local_reference));
+ confirm->proto_class = 2;
+ confirm->optional_start = 1;
+
+ optional_data = (u_int8_t *) msgb_put(response, 1);
+ optional_data[0] = SCCP_PNC_END_OF_OPTIONAL;
+
+ ret = _send_msg(response);
+ msgb_free(response);
+
+ _sccp_set_connection_state(connection, SCCP_CONNECTION_STATE_ESTABLISHED);
+ return ret;
+}
+
+static int _sccp_send_connection_request(struct sccp_connection *connection,
+ const struct sockaddr_sccp *called, struct msgb *msg)
+{
+ struct msgb *request;
+ struct sccp_connection_request *req;
+ u_int8_t *data;
+ u_int8_t extra_size = 3 + 1;
+ int ret;
+
+
+ if (msg && (msgb_l3len(msg) < 3 || msgb_l3len(msg) > 130)) {
+ DEBUGP(DSCCP, "Invalid amount of data... %d\n", msgb_l3len(msg));
+ return -1;
+ }
+
+ /* try to find a id */
+ if (assign_source_local_reference(connection) != 0) {
+ DEBUGP(DSCCP, "Assigning a local reference failed.\n");
+ _sccp_set_connection_state(connection, SCCP_CONNECTION_STATE_SETUP_ERROR);
+ return -1;
+ }
+
+
+ if (msg)
+ extra_size += 2 + msgb_l3len(msg);
+ request = msgb_alloc_headroom(SCCP_MSG_SIZE,
+ SCCP_MSG_HEADROOM, "sccp connection request");
+ request->l2h = &request->data[0];
+ req = (struct sccp_connection_request *) msgb_put(request, sizeof(*req));
+
+ req->type = SCCP_MSG_TYPE_CR;
+ memcpy(&req->source_local_reference, &connection->source_local_reference,
+ sizeof(connection->source_local_reference));
+ req->proto_class = 2;
+ req->variable_called = 2;
+ req->optional_start = 4;
+
+ /* write the called party address */
+ data = msgb_put(request, 1 + 2);
+ data[0] = 2;
+ data[1] = 0x42;
+ data[2] = called->sccp_ssn;
+
+ /* write the payload */
+ if (msg) {
+ data = msgb_put(request, 2 + msgb_l3len(msg));
+ data[0] = SCCP_PNC_DATA;
+ data[1] = msgb_l3len(msg);
+ memcpy(&data[2], msg->l3h, msgb_l3len(msg));
+ }
+
+ data = msgb_put(request, 1);
+ data[0] = SCCP_PNC_END_OF_OPTIONAL;
+
+ llist_add_tail(&connection->list, &sccp_connections);
+ _sccp_set_connection_state(connection, SCCP_CONNECTION_STATE_REQUEST);
+
+ ret = _send_msg(request);
+ msgb_free(request);
+
+ return ret;
+}
+
+static int _sccp_send_connection_data(struct sccp_connection *conn, struct msgb *_data)
+{
+ struct msgb *msgb;
+ struct sccp_data_form1 *dt1;
+ u_int8_t *data;
+ int extra_size;
+ int ret;
+
+ if (msgb_l3len(_data) < 2 || msgb_l3len(_data) > 256) {
+ DEBUGP(DSCCP, "data size too big, segmenting unimplemented.\n");
+ return -1;
+ }
+
+ extra_size = 1 + msgb_l3len(_data);
+ msgb = msgb_alloc_headroom(SCCP_MSG_SIZE,
+ SCCP_MSG_HEADROOM, "sccp dt1");
+ msgb->l2h = &msgb->data[0];
+
+ dt1 = (struct sccp_data_form1 *) msgb_put(msgb, sizeof(*dt1));
+ dt1->type = SCCP_MSG_TYPE_DT1;
+ memcpy(&dt1->destination_local_reference, &conn->destination_local_reference,
+ sizeof(struct sccp_source_reference));
+ dt1->segmenting = 0;
+
+ /* copy the data */
+ dt1->variable_start = 1;
+ data = msgb_put(msgb, extra_size);
+ data[0] = extra_size - 1;
+ memcpy(&data[1], _data->l3h, extra_size - 1);
+
+ ret = _send_msg(msgb);
+ msgb_free(msgb);
+
+ return ret;
+}
+
+static int _sccp_send_connection_it(struct sccp_connection *conn)
+{
+ struct msgb *msgb;
+ struct sccp_data_it *it;
+ int ret;
+
+ msgb = msgb_alloc_headroom(SCCP_MSG_SIZE,
+ SCCP_MSG_HEADROOM, "sccp it");
+ msgb->l2h = &msgb->data[0];
+ it = (struct sccp_data_it *) msgb_put(msgb, sizeof(*it));
+ it->type = SCCP_MSG_TYPE_IT;
+ memcpy(&it->destination_local_reference, &conn->destination_local_reference,
+ sizeof(struct sccp_source_reference));
+ memcpy(&it->source_local_reference, &conn->source_local_reference,
+ sizeof(struct sccp_source_reference));
+
+ it->proto_class = 0x2;
+ it->sequencing[0] = it->sequencing[1] = 0;
+ it->credit = 0;
+
+ ret = _send_msg(msgb);
+ msgb_free(msgb);
+ return ret;
+}
+
+static int _sccp_send_connection_released(struct sccp_connection *conn, int cause)
+{
+ struct msgb *msg;
+ struct sccp_connection_released *rel;
+ u_int8_t *data;
+ int ret;
+
+ msg = msgb_alloc_headroom(SCCP_MSG_SIZE, SCCP_MSG_HEADROOM,
+ "sccp: connection released");
+ msg->l2h = &msg->data[0];
+ rel = (struct sccp_connection_released *) msgb_put(msg, sizeof(*rel));
+ rel->type = SCCP_MSG_TYPE_RLSD;
+ rel->release_cause = cause;
+
+ /* copy the source references */
+ memcpy(&rel->destination_local_reference, &conn->destination_local_reference,
+ sizeof(struct sccp_source_reference));
+ memcpy(&rel->source_local_reference, &conn->source_local_reference,
+ sizeof(struct sccp_source_reference));
+
+ data = msgb_put(msg, 1);
+ data[0] = SCCP_PNC_END_OF_OPTIONAL;
+
+ _sccp_set_connection_state(conn, SCCP_CONNECTION_STATE_RELEASE);
+ ret = _send_msg(msg);
+ msgb_free(msg);
+
+ return ret;
+}
+
+/*
+ * Open a connection. The following is going to happen:
+ *
+ * - Verify the packet, e.g. that we have no other connection
+ * that id.
+ * - Ask the user if he wants to accept the connection
+ * - Try to open the connection by assigning a source local reference
+ * and sending the packet
+ */
+static int _sccp_handle_connection_request(struct msgb *msgb)
+{
+ static const u_int32_t header_size =
+ sizeof(struct sccp_connection_request);
+ static const u_int32_t optional_offset =
+ offsetof(struct sccp_connection_request, optional_start);
+ static const u_int32_t called_offset =
+ offsetof(struct sccp_connection_request, variable_called);
+
+ struct sccp_data_callback *cb;
+ struct sccp_connection_request *req = (struct sccp_connection_request *)msgb->data;
+ struct sccp_address called;
+ struct sccp_connection *connection;
+ struct sccp_optional_data optional_data;
+
+ /* header check */
+ if (msgb_l2len(msgb) < header_size) {
+ DEBUGP(DSCCP, "msgb < header_size %u %u\n",
+ msgb_l2len(msgb), header_size);
+ return -1;
+ }
+
+ /* copy out the calling and called address. Add the offset */
+ if (copy_address(&called, called_offset + req->variable_called, msgb) != 0)
+ return -1;
+
+ if (check_address(&called) != 0) {
+ DEBUGP(DSCCP, "Invalid called address according to 08.06: 0x%x 0x%x\n",
+ *(u_int8_t *)&called.address, called.ssn);
+ return -1;
+ }
+
+ cb = _find_ssn(called.ssn);
+ if (!cb || !cb->accept_cb) {
+ DEBUGP(DSCCP, "No routing for CR for called SSN: %u\n", called.ssn);
+ return -1;
+ }
+
+ /* check if the system wants this connection */
+ connection = talloc_zero(tall_sccp_ctx, struct sccp_connection);
+ if (!connection) {
+ DEBUGP(DSCCP, "Allocation failed\n");
+ return -1;
+ }
+
+ /*
+ * sanity checks:
+ * - Is the source_local_reference in any other connection?
+ * then will call accept, assign a "destination" local reference
+ * and send a connection confirm, otherwise we will send a refuseed
+ * one....
+ */
+ if (destination_local_reference_is_free(&req->source_local_reference) != 0) {
+ DEBUGP(DSCCP, "Need to reject connection with existing reference\n");
+ _sccp_send_refuse(req, SCCP_REFUSAL_SCCP_FAILURE);
+ talloc_free(connection);
+ return -1;
+ }
+
+ connection->incoming = 1;
+ connection->destination_local_reference = req->source_local_reference;
+
+ /*
+ * parse optional data.
+ */
+ memset(&optional_data, 0, sizeof(optional_data));
+ if (_sccp_parse_optional_data(optional_offset + req->optional_start, msgb, &optional_data) != 0) {
+ DEBUGP(DSCCP, "parsing of optional data failed.\n");
+ talloc_free(connection);
+ return -1;
+ }
+
+ if (cb->accept_cb(connection, cb->accept_context) != 0) {
+ _sccp_send_refuse(req, SCCP_REFUSAL_END_USER_ORIGINATED);
+ _sccp_set_connection_state(connection, SCCP_CONNECTION_STATE_REFUSED);
+ talloc_free(connection);
+ return 0;
+ }
+
+
+ llist_add_tail(&connection->list, &sccp_connections);
+
+ if (_sccp_send_connection_confirm(connection) != 0) {
+ DEBUGP(DSCCP, "Sending confirm failed... no available source reference?\n");
+
+ _sccp_send_refuse(req, SCCP_REFUSAL_SCCP_FAILURE);
+ _sccp_set_connection_state(connection, SCCP_CONNECTION_STATE_REFUSED);
+ llist_del(&connection->list);
+ talloc_free(connection);
+
+ return -1;
+ }
+
+ /*
+ * If we have data let us forward things.
+ */
+ if (optional_data.data_len != 0 && connection->data_cb) {
+ msgb->l3h = &msgb->l2h[optional_data.data_start];
+ connection->data_cb(connection, msgb, optional_data.data_len);
+ }
+
+ return 0;
+}
+
+/* Handle the release confirmed */
+static int _sccp_handle_connection_release_complete(struct msgb *data)
+{
+ static int header_size = sizeof(struct sccp_connection_release_complete);
+
+ struct sccp_connection_release_complete *cmpl;
+ struct sccp_connection *conn;
+
+ /* header check */
+ if (msgb_l2len(data) < header_size) {
+ DEBUGP(DSCCP, "msgb < header_size %u %u\n",
+ msgb_l2len(data), header_size);
+ return -1;
+ }
+
+ cmpl = (struct sccp_connection_release_complete *) data->l2h;
+
+ /* find the connection */
+ llist_for_each_entry(conn, &sccp_connections, list) {
+ if (conn->data_cb
+ && memcmp(&conn->source_local_reference,
+ &cmpl->destination_local_reference,
+ sizeof(conn->source_local_reference)) == 0
+ && memcmp(&conn->destination_local_reference,
+ &cmpl->source_local_reference,
+ sizeof(conn->destination_local_reference)) == 0) {
+ goto found;
+ }
+ }
+
+
+ DEBUGP(DSCCP, "Release complete of unknown connection\n");
+ return -1;
+
+found:
+ llist_del(&conn->list);
+ _sccp_set_connection_state(conn, SCCP_CONNECTION_STATE_RELEASE_COMPLETE);
+ return 0;
+}
+
+/* Handle the Data Form 1 message */
+static int _sccp_handle_connection_dt1(struct msgb *data)
+{
+ static int variable_offset = offsetof(struct sccp_data_form1, variable_start);
+ static int header_size = sizeof(struct sccp_data_form1);
+
+ struct sccp_data_form1 *dt1 = (struct sccp_data_form1 *)data->l2h;
+ struct sccp_connection *conn;
+ int size;
+
+ /* we don't have enough size for the struct */
+ if (msgb_l2len(data) < header_size) {
+ DEBUGP(DSCCP, "msgb > header_size %u %u\n",
+ msgb_l2len(data), header_size);
+ return -1;
+ }
+
+ if (dt1->segmenting != 0) {
+ DEBUGP(DSCCP, "This packet has segmenting, not supported: %d\n", dt1->segmenting);
+ return -1;
+ }
+
+ /* lookup if we have a connection with the given reference */
+ llist_for_each_entry(conn, &sccp_connections, list) {
+ if (conn->data_cb
+ && memcmp(&conn->source_local_reference,
+ &dt1->destination_local_reference,
+ sizeof(conn->source_local_reference)) == 0) {
+
+ /* some more size checks in here */
+ if (msgb_l2len(data) < variable_offset + dt1->variable_start + 1) {
+ DEBUGP(DSCCP, "Not enough space for variable start: %u %u\n",
+ msgb_l2len(data), dt1->variable_start);
+ return -1;
+ }
+
+ size = data->l2h[variable_offset + dt1->variable_start];
+ data->l3h = &data->l2h[dt1->variable_start + variable_offset + 1];
+
+ if (msgb_l3len(data) < size) {
+ DEBUGP(DSCCP, "Not enough room for the payload: %u %u\n",
+ msgb_l3len(data), size);
+ return -1;
+ }
+
+ conn->data_cb(conn, data, size);
+ return 0;
+ }
+ }
+
+ DEBUGP(DSCCP, "No connection found for dt1 data\n");
+ return -1;
+}
+
+/* confirm a connection release */
+static int _sccp_send_connection_release_complete(struct sccp_connection *connection)
+{
+ struct msgb *msgb;
+ struct sccp_connection_release_complete *rlc;
+ int ret;
+
+ msgb = msgb_alloc_headroom(SCCP_MSG_SIZE,
+ SCCP_MSG_HEADROOM, "sccp rlc");
+ msgb->l2h = &msgb->data[0];
+
+ rlc = (struct sccp_connection_release_complete *) msgb_put(msgb, sizeof(*rlc));
+ rlc->type = SCCP_MSG_TYPE_RLC;
+ memcpy(&rlc->destination_local_reference,
+ &connection->destination_local_reference, sizeof(struct sccp_source_reference));
+ memcpy(&rlc->source_local_reference,
+ &connection->source_local_reference, sizeof(struct sccp_source_reference));
+
+ ret = _send_msg(msgb);
+ msgb_free(msgb);
+
+ /*
+ * Remove from the list of active connections and set the state. User code
+ * should now free the entry.
+ */
+ llist_del(&connection->list);
+ _sccp_set_connection_state(connection, SCCP_CONNECTION_STATE_RELEASE_COMPLETE);
+
+ return ret;
+}
+
+/* connection released, send a released confirm */
+static int _sccp_handle_connection_released(struct msgb *data)
+{
+ static int header_size = sizeof(struct sccp_connection_released);
+ static int optional_offset = offsetof(struct sccp_connection_released, optional_start);
+
+ struct sccp_optional_data optional_data;
+ struct sccp_connection_released *rls = (struct sccp_connection_released *)data->l2h;
+ struct sccp_connection *conn;
+
+ /* we don't have enough size for the struct */
+ if (msgb_l2len(data) < header_size) {
+ DEBUGP(DSCCP, "msgb > header_size %u %u\n",
+ msgb_l2len(data), header_size);
+ return -1;
+ }
+
+ /* lookup if we have a connection with the given reference */
+ llist_for_each_entry(conn, &sccp_connections, list) {
+ if (conn->data_cb
+ && memcmp(&conn->source_local_reference,
+ &rls->destination_local_reference,
+ sizeof(conn->source_local_reference)) == 0
+ && memcmp(&conn->destination_local_reference,
+ &rls->source_local_reference,
+ sizeof(conn->destination_local_reference)) == 0) {
+ goto found;
+ }
+ }
+
+
+ DEBUGP(DSCCP, "Unknown connection was released.\n");
+ return -1;
+
+ /* we have found a connection */
+found:
+ memset(&optional_data, 0, sizeof(optional_data));
+ if (_sccp_parse_optional_data(optional_offset + rls->optional_start, data, &optional_data) != 0) {
+ DEBUGP(DSCCP, "parsing of optional data failed.\n");
+ return -1;
+ }
+
+ /* optional data */
+ if (optional_data.data_len != 0 && conn->data_cb) {
+ data->l3h = &data->l2h[optional_data.data_start];
+ conn->data_cb(conn, data, optional_data.data_len);
+ }
+
+ /* generate a response */
+ if (_sccp_send_connection_release_complete(conn) != 0) {
+ DEBUGP(DSCCP, "Sending release confirmed failed\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int _sccp_handle_connection_refused(struct msgb *msgb)
+{
+ static const u_int32_t header_size =
+ sizeof(struct sccp_connection_refused);
+ static int optional_offset = offsetof(struct sccp_connection_refused, optional_start);
+
+ struct sccp_optional_data optional_data;
+ struct sccp_connection *conn;
+ struct sccp_connection_refused *ref;
+
+ /* header check */
+ if (msgb_l2len(msgb) < header_size) {
+ DEBUGP(DSCCP, "msgb < header_size %u %u\n",
+ msgb_l2len(msgb), header_size);
+ return -1;
+ }
+
+ ref = (struct sccp_connection_refused *) msgb->l2h;
+
+ /* lookup if we have a connection with the given reference */
+ llist_for_each_entry(conn, &sccp_connections, list) {
+ if (conn->incoming == 0 && conn->data_cb
+ && memcmp(&conn->source_local_reference,
+ &ref->destination_local_reference,
+ sizeof(conn->source_local_reference)) == 0) {
+ goto found;
+ }
+ }
+
+ DEBUGP(DSCCP, "Refused but no connection found\n");
+ return -1;
+
+found:
+ memset(&optional_data, 0, sizeof(optional_data));
+ if (_sccp_parse_optional_data(optional_offset + ref->optional_start, msgb, &optional_data) != 0) {
+ DEBUGP(DSCCP, "parsing of optional data failed.\n");
+ return -1;
+ }
+
+ /* optional data */
+ if (optional_data.data_len != 0 && conn->data_cb) {
+ msgb->l3h = &msgb->l2h[optional_data.data_start];
+ conn->data_cb(conn, msgb, optional_data.data_len);
+ }
+
+
+ llist_del(&conn->list);
+ _sccp_set_connection_state(conn, SCCP_CONNECTION_STATE_REFUSED);
+ return 0;
+}
+
+static int _sccp_handle_connection_confirm(struct msgb *msgb)
+{
+ static u_int32_t header_size =
+ sizeof(struct sccp_connection_confirm);
+ static const u_int32_t optional_offset =
+ offsetof(struct sccp_connection_confirm, optional_start);
+
+ struct sccp_optional_data optional_data;
+ struct sccp_connection *conn;
+ struct sccp_connection_confirm *con;
+
+ /* header check */
+ if (msgb_l2len(msgb) < header_size) {
+ DEBUGP(DSCCP, "msgb < header_size %u %u\n",
+ msgb_l2len(msgb), header_size);
+ return -1;
+ }
+
+ con = (struct sccp_connection_confirm *) msgb->l2h;
+
+ /* lookup if we have a connection with the given reference */
+ llist_for_each_entry(conn, &sccp_connections, list) {
+ if (conn->incoming == 0 && conn->data_cb
+ && memcmp(&conn->source_local_reference,
+ &con->destination_local_reference,
+ sizeof(conn->source_local_reference)) == 0) {
+ goto found;
+ }
+ }
+
+ DEBUGP(DSCCP, "Confirmed but no connection found\n");
+ return -1;
+
+found:
+ /* copy the addresses of the connection */
+ conn->destination_local_reference = con->source_local_reference;
+ _sccp_set_connection_state(conn, SCCP_CONNECTION_STATE_ESTABLISHED);
+
+ memset(&optional_data, 0, sizeof(optional_data));
+ if (_sccp_parse_optional_data(optional_offset + con->optional_start, msgb, &optional_data) != 0) {
+ DEBUGP(DSCCP, "parsing of optional data failed.\n");
+ return -1;
+ }
+
+ /* optional data */
+ if (optional_data.data_len != 0 && conn->data_cb) {
+ msgb->l3h = &msgb->l2h[optional_data.data_start];
+ conn->data_cb(conn, msgb, optional_data.data_len);
+ }
+
+ return 0;
+}
+
+
+int sccp_system_init(int (*outgoing)(struct msgb *data, void *ctx), void *ctx)
+{
+ sccp_system.write_data = outgoing;
+ sccp_system.write_context = ctx;
+
+ return 0;
+}
+
+/* oh my god a real SCCP packet. need to dispatch it now */
+int sccp_system_incoming(struct msgb *msgb)
+{
+ if (msgb_l2len(msgb) < 1 ) {
+ DEBUGP(DSCCP, "Too short packet\n");
+ return -1;
+ }
+
+ int type = msgb->l2h[0];
+
+ switch(type) {
+ case SCCP_MSG_TYPE_CR:
+ return _sccp_handle_connection_request(msgb);
+ break;
+ case SCCP_MSG_TYPE_RLSD:
+ return _sccp_handle_connection_released(msgb);
+ break;
+ case SCCP_MSG_TYPE_CREF:
+ return _sccp_handle_connection_refused(msgb);
+ break;
+ case SCCP_MSG_TYPE_CC:
+ return _sccp_handle_connection_confirm(msgb);
+ break;
+ case SCCP_MSG_TYPE_RLC:
+ return _sccp_handle_connection_release_complete(msgb);
+ break;
+ case SCCP_MSG_TYPE_DT1:
+ return _sccp_handle_connection_dt1(msgb);
+ break;
+ case SCCP_MSG_TYPE_UDT:
+ return _sccp_handle_read(msgb);
+ break;
+ default:
+ DEBUGP(DSCCP, "unimplemented msg type: %d\n", type);
+ };
+
+ return -1;
+}
+
+/* create a packet from the data */
+int sccp_connection_write(struct sccp_connection *connection, struct msgb *data)
+{
+ if (connection->connection_state < SCCP_CONNECTION_STATE_CONFIRM
+ || connection->connection_state > SCCP_CONNECTION_STATE_ESTABLISHED) {
+ DEBUGP(DSCCP, "sccp_connection_write: Wrong connection state: %p %d\n",
+ connection, connection->connection_state);
+ return -1;
+ }
+
+ return _sccp_send_connection_data(connection, data);
+}
+
+/*
+ * Send a Inactivity Test message. The owner of the connection
+ * should start a timer and call this method regularily. Calling
+ * this every 60 seconds should be good enough.
+ */
+int sccp_connection_send_it(struct sccp_connection *connection)
+{
+ if (connection->connection_state < SCCP_CONNECTION_STATE_CONFIRM
+ || connection->connection_state > SCCP_CONNECTION_STATE_ESTABLISHED) {
+ DEBUGP(DSCCP, "sccp_connection_write: Wrong connection state: %p %d\n",
+ connection, connection->connection_state);
+ return -1;
+ }
+
+ return _sccp_send_connection_it(connection);
+}
+
+/* send a connection release and wait for the connection released */
+int sccp_connection_close(struct sccp_connection *connection, int cause)
+{
+ if (connection->connection_state < SCCP_CONNECTION_STATE_CONFIRM
+ || connection->connection_state > SCCP_CONNECTION_STATE_ESTABLISHED) {
+ DEBUGPC(DSCCP, "Can not close the connection. It was never opened: %p %d\n",
+ connection, connection->connection_state);
+ return -1;
+ }
+
+ return _sccp_send_connection_released(connection, cause);
+}
+
+int sccp_connection_free(struct sccp_connection *connection)
+{
+ if (connection->connection_state > SCCP_CONNECTION_STATE_NONE
+ && connection->connection_state < SCCP_CONNECTION_STATE_RELEASE_COMPLETE) {
+ DEBUGP(DSCCP, "The connection needs to be released before it is freed");
+ return -1;
+ }
+
+ talloc_free(connection);
+ return 0;
+}
+
+struct sccp_connection *sccp_connection_socket(void)
+{
+ return talloc_zero(tall_sccp_ctx, struct sccp_connection);
+}
+
+int sccp_connection_connect(struct sccp_connection *conn,
+ const struct sockaddr_sccp *local,
+ struct msgb *data)
+{
+ return _sccp_send_connection_request(conn, local, data);
+}
+
+int sccp_connection_set_incoming(const struct sockaddr_sccp *sock,
+ int (*accept_cb)(struct sccp_connection *, void *), void *context)
+{
+ struct sccp_data_callback *cb;
+
+ if (!sock)
+ return -2;
+
+ cb = _find_ssn(sock->sccp_ssn);
+ if (!cb)
+ return -1;
+
+ cb->accept_cb = accept_cb;
+ cb->accept_context = context;
+ return 0;
+}
+
+int sccp_write(struct msgb *data, const struct sockaddr_sccp *in,
+ const struct sockaddr_sccp *out, int class)
+{
+ return _sccp_send_data(class, in, out, data);
+}
+
+int sccp_set_read(const struct sockaddr_sccp *sock,
+ int (*read_cb)(struct msgb *, unsigned int, void *), void *context)
+{
+ struct sccp_data_callback *cb;
+
+ if (!sock)
+ return -2;
+
+ cb = _find_ssn(sock->sccp_ssn);
+ if (!cb)
+ return -1;
+
+ cb->read_cb = read_cb;
+ cb->read_context = context;
+ return 0;
+}
+
+static_assert(sizeof(struct sccp_source_reference) <= sizeof(u_int32_t), enough_space);
+
+u_int32_t sccp_src_ref_to_int(struct sccp_source_reference *ref)
+{
+ u_int32_t src_ref = 0;
+ memcpy(&src_ref, ref, sizeof(*ref));
+ return src_ref;
+}
+
+struct sccp_source_reference sccp_src_ref_from_int(u_int32_t int_ref)
+{
+ struct sccp_source_reference ref;
+ memcpy(&ref, &int_ref, sizeof(ref));
+ return ref;
+}
+
+static __attribute__((constructor)) void on_dso_load(void)
+{
+ tall_sccp_ctx = talloc_named_const(NULL, 1, "sccp");
+}
+
+static __attribute__((destructor)) void on_dso_unload(void)
+{
+ talloc_report_full(tall_sccp_ctx, stderr);
+}
diff --git a/openbsc/src/silent_call.c b/openbsc/src/silent_call.c
new file mode 100644
index 000000000..82b656327
--- /dev/null
+++ b/openbsc/src/silent_call.c
@@ -0,0 +1,93 @@
+/* GSM silent call feature */
+
+/*
+ * (C) 2009 by Harald Welte <laforge@gnumonks.org>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <openbsc/msgb.h>
+#include <openbsc/signal.h>
+#include <openbsc/debug.h>
+#include <openbsc/paging.h>
+#include <openbsc/gsm_data.h>
+#include <openbsc/gsm_subscriber.h>
+#include <openbsc/abis_rsl.h>
+#include <openbsc/chan_alloc.h>
+
+static int paging_cb_silent(unsigned int hooknum, unsigned int event,
+ struct msgb *msg, void *_lchan, void *_data)
+{
+ struct gsm_lchan *lchan = _lchan;
+ struct scall_signal_data sigdata;
+ int rc;
+
+ if (hooknum != GSM_HOOK_RR_PAGING)
+ return -EINVAL;
+
+ DEBUGP(DSMS, "paging_cb_silent: ");
+
+ sigdata.lchan = lchan;
+ sigdata.data = _data;
+
+ switch (event) {
+ case GSM_PAGING_SUCCEEDED:
+ DEBUGPC(DSMS, "success, using Timeslot %u on ARFCN %u\n",
+ lchan->ts->nr, lchan->ts->trx->arfcn);
+ /* increment lchan reference count */
+ dispatch_signal(SS_SCALL, S_SCALL_SUCCESS, &sigdata);
+ use_lchan(lchan);
+ break;
+ case GSM_PAGING_EXPIRED:
+ DEBUGP(DSMS, "expired\n");
+ dispatch_signal(SS_SCALL, S_SCALL_EXPIRED, &sigdata);
+ break;
+ default:
+ rc = -EINVAL;
+ break;
+ }
+
+ return rc;
+}
+
+int gsm_silent_call_start(struct gsm_subscriber *subscr, void *data)
+{
+ int rc;
+
+ rc = paging_request(subscr->net, subscr, RSL_CHANNEED_TCH_F,
+ paging_cb_silent, data);
+ return rc;
+}
+
+int gsm_silent_call_stop(struct gsm_subscriber *subscr)
+{
+ struct gsm_lchan *lchan;
+
+ lchan = lchan_for_subscr(subscr);
+ if (!lchan)
+ return -EINVAL;
+
+ /* FIXME: did we actually establish a silent call for this guy? */
+ put_lchan(lchan);
+
+ return 0;
+}
diff --git a/openbsc/src/talloc.c b/openbsc/src/talloc.c
index bd5e1b0e0..d8213238e 100644
--- a/openbsc/src/talloc.c
+++ b/openbsc/src/talloc.c
@@ -105,6 +105,15 @@
#endif
#endif
+#ifdef __APPLE__
+/* taken from http://insanecoding.blogspot.com/2007/03/methods-for-safe-string-handling.html */
+size_t strnlen(const char *s, size_t n)
+{
+ const char *p = (const char *)memchr(s, 0, n);
+ return(p ? p-s : n);
+}
+#endif
+
/* this null_context is only used if talloc_enable_leak_report() or
talloc_enable_leak_report_full() is called, otherwise it remains
NULL
diff --git a/openbsc/src/telnet_interface.c b/openbsc/src/telnet_interface.c
index d7c905518..128c34e94 100644
--- a/openbsc/src/telnet_interface.c
+++ b/openbsc/src/telnet_interface.c
@@ -165,7 +165,6 @@ static int telnet_new_connection(struct bsc_fd *fd, unsigned int what) {
connection->fd.fd = new_connection;
connection->fd.when = BSC_FD_READ;
connection->fd.cb = client_data;
- connection->bts = 0;
bsc_register_fd(&connection->fd);
llist_add_tail(&connection->entry, &active_connections);
diff --git a/openbsc/src/timer.c b/openbsc/src/timer.c
index 6f974a2c3..ffeb4aba3 100644
--- a/openbsc/src/timer.c
+++ b/openbsc/src/timer.c
@@ -176,7 +176,7 @@ restart:
int bsc_timer_check(void)
{
struct timer_list *timer;
- int i;
+ int i = 0;
llist_for_each_entry(timer, &timer_list, entry) {
i++;
diff --git a/openbsc/src/tlv_parser.c b/openbsc/src/tlv_parser.c
index e835f951f..fd0045f97 100644
--- a/openbsc/src/tlv_parser.c
+++ b/openbsc/src/tlv_parser.c
@@ -1,5 +1,8 @@
#include <stdio.h>
#include <openbsc/tlv.h>
+#include <openbsc/gsm_data.h>
+
+struct tlv_definition tvlv_att_def;
int tlv_dump(struct tlv_parsed *dec)
{
@@ -13,6 +16,82 @@ int tlv_dump(struct tlv_parsed *dec)
return 0;
}
+/* o_tag: output: tag found
+ * o_len: output: length of the data
+ * o_val: output: pointer to the data
+ * def: input: a structure defining the valid TLV tags / configurations
+ * buf: input: the input data buffer to be parsed
+ * buf_len: input: the length of the input data buffer
+ *
+ * Also, returns the number of bytes consumed by the TLV entry
+ */
+int tlv_parse_one(u_int8_t *o_tag, u_int16_t *o_len, const u_int8_t **o_val,
+ const struct tlv_definition *def,
+ const u_int8_t *buf, int buf_len)
+{
+ u_int8_t tag;
+ int len;
+
+ tag = *buf;
+ *o_tag = tag;
+
+ /* FIXME: use tables for knwon IEI */
+ switch (def->def[tag].type) {
+ case TLV_TYPE_T:
+ /* GSM TS 04.07 11.2.4: Type 1 TV or Type 2 T */
+ *o_val = buf;
+ *o_len = 0;
+ len = 1;
+ break;
+ case TLV_TYPE_TV:
+ *o_val = buf+1;
+ *o_len = 1;
+ len = 2;
+ break;
+ case TLV_TYPE_FIXED:
+ *o_val = buf+1;
+ *o_len = def->def[tag].fixed_len;
+ len = def->def[tag].fixed_len + 1;
+ break;
+ case TLV_TYPE_TLV:
+ /* GSM TS 04.07 11.2.4: Type 4 TLV */
+ if (buf + 1 > buf + buf_len)
+ return -1;
+ *o_val = buf+2;
+ *o_len = *(buf+1);
+ len = *o_len + 2;
+ if (len > buf_len)
+ return -2;
+ break;
+ case TLV_TYPE_TvLV:
+ if (*(buf+1) & 0x80) {
+ /* like TLV, but without highest bit of len */
+ if (buf + 1 > buf + buf_len)
+ return -1;
+ *o_val = buf+2;
+ *o_len = *(buf+1) & 0x7f;
+ len = *o_len + 2;
+ if (len > buf_len)
+ return -2;
+ break;
+ }
+ /* like TL16V, fallthrough */
+ case TLV_TYPE_TL16V:
+ if (2 > buf_len)
+ return -1;
+ *o_val = buf+3;
+ *o_len = *(buf+1) << 8 | *(buf+2);
+ len = *o_len + 3;
+ if (len > buf_len)
+ return -2;
+ break;
+ default:
+ return -3;
+ }
+
+ return len;
+}
+
/* dec: output: a caller-allocated pointer to a struct tlv_parsed,
* def: input: a structure defining the valid TLV tags / configurations
* buf: input: the input data buffer to be parsed
@@ -24,82 +103,55 @@ int tlv_parse(struct tlv_parsed *dec, const struct tlv_definition *def,
const u_int8_t *buf, int buf_len, u_int8_t lv_tag,
u_int8_t lv_tag2)
{
- u_int8_t tag, len = 1;
- const u_int8_t *pos = buf;
- int num_parsed = 0;
+ int ofs = 0, num_parsed = 0;
+ u_int16_t len;
memset(dec, 0, sizeof(*dec));
if (lv_tag) {
- if (pos > buf + buf_len)
+ if (ofs > buf_len)
return -1;
- dec->lv[lv_tag].val = pos+1;
- dec->lv[lv_tag].len = *pos;
+ dec->lv[lv_tag].val = &buf[ofs+1];
+ dec->lv[lv_tag].len = buf[ofs];
len = dec->lv[lv_tag].len + 1;
- if (pos + len > buf + buf_len)
+ if (ofs + len > buf_len)
return -2;
num_parsed++;
- pos += len;
+ ofs += len;
}
if (lv_tag2) {
- if (pos > buf + buf_len)
+ if (ofs > buf_len)
return -1;
- dec->lv[lv_tag2].val = pos+1;
- dec->lv[lv_tag2].len = *pos;
+ dec->lv[lv_tag2].val = &buf[ofs+1];
+ dec->lv[lv_tag2].len = buf[ofs];
len = dec->lv[lv_tag2].len + 1;
- if (pos + len > buf + buf_len)
+ if (ofs + len > buf_len)
return -2;
num_parsed++;
- pos += len;
+ ofs += len;
}
- for (; pos < buf+buf_len; pos += len) {
- tag = *pos;
- /* FIXME: use tables for knwon IEI */
- switch (def->def[tag].type) {
- case TLV_TYPE_T:
- /* GSM TS 04.07 11.2.4: Type 1 TV or Type 2 T */
- dec->lv[tag].val = pos;
- dec->lv[tag].len = 0;
- len = 1;
- num_parsed++;
- break;
- case TLV_TYPE_TV:
- dec->lv[tag].val = pos+1;
- dec->lv[tag].len = 1;
- len = 2;
- num_parsed++;
- break;
- case TLV_TYPE_FIXED:
- dec->lv[tag].val = pos+1;
- dec->lv[tag].len = def->def[tag].fixed_len;
- len = def->def[tag].fixed_len + 1;
- num_parsed++;
- break;
- case TLV_TYPE_TLV:
- /* GSM TS 04.07 11.2.4: Type 4 TLV */
- if (pos + 1 > buf + buf_len)
- return -1;
- dec->lv[tag].val = pos+2;
- dec->lv[tag].len = *(pos+1);
- len = dec->lv[tag].len + 2;
- if (pos + len > buf + buf_len)
- return -2;
- num_parsed++;
- break;
- case TLV_TYPE_TL16V:
- if (pos + 2 > buf + buf_len)
- return -1;
- dec->lv[tag].val = pos+3;
- dec->lv[tag].len = *(pos+1) << 8 | *(pos+2);
- len = dec->lv[tag].len + 3;
- if (pos + len > buf + buf_len)
- return -2;
- num_parsed++;
- break;
- }
+ while (ofs < buf_len) {
+ int rv;
+ u_int8_t tag;
+ const u_int8_t *val;
+
+ rv = tlv_parse_one(&tag, &len, &val, def,
+ &buf[ofs], buf_len-ofs);
+ if (rv < 0)
+ return rv;
+ dec->lv[tag].val = val;
+ dec->lv[tag].len = len;
+ ofs += rv;
+ num_parsed++;
}
//tlv_dump(dec);
return num_parsed;
}
+static __attribute__((constructor)) void on_dso_load_tlv(void)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(tvlv_att_def.def); i++)
+ tvlv_att_def.def[i].type = TLV_TYPE_TvLV;
+}
diff --git a/openbsc/src/token_auth.c b/openbsc/src/token_auth.c
index 695b55243..0931007ef 100644
--- a/openbsc/src/token_auth.c
+++ b/openbsc/src/token_auth.c
@@ -33,6 +33,9 @@
#define TOKEN_SMS_TEXT "HAR 2009 GSM. Register at http://har2009.gnumonks.org/ " \
"Your IMSI is %s, auth token is %08X, phone no is %s."
+extern struct gsm_sms *sms_from_text(struct gsm_subscriber *receiver,
+ const char *text);
+
static char *build_sms_string(struct gsm_subscriber *subscr, u_int32_t token)
{
char *sms_str;
diff --git a/openbsc/src/transaction.c b/openbsc/src/transaction.c
index 950faa2f1..04eaa3c99 100644
--- a/openbsc/src/transaction.c
+++ b/openbsc/src/transaction.c
@@ -86,8 +86,6 @@ struct gsm_trans *trans_alloc(struct gsm_subscriber *subscr,
void trans_free(struct gsm_trans *trans)
{
- struct gsm_bts *bts;
-
switch (trans->protocol) {
case GSM48_PDISC_CC:
_gsm48_cc_trans_free(trans);
diff --git a/openbsc/src/ussd.c b/openbsc/src/ussd.c
new file mode 100644
index 000000000..a3d11f080
--- /dev/null
+++ b/openbsc/src/ussd.c
@@ -0,0 +1,71 @@
+/* Network-specific handling of mobile-originated USSDs. */
+
+/* (C) 2008-2009 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2008, 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * (C) 2009 by Mike Haben <michael.haben@btinternet.com>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+/* This module defines the network-specific handling of mobile-originated
+ USSD messages. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <openbsc/gsm_04_80.h>
+#include <openbsc/gsm_subscriber.h>
+#include <openbsc/debug.h>
+
+/* Declarations of USSD strings to be recognised */
+const char USSD_TEXT_OWN_NUMBER[] = "*#100#";
+
+/* Forward declarations of network-specific handler functions */
+static int send_own_number(const struct msgb *msg, const struct ussd_request *req);
+
+
+/* Entrypoint - handler function common to all mobile-originated USSDs */
+int handle_rcv_ussd(struct msgb *msg)
+{
+ struct ussd_request req;
+
+ gsm0480_decode_ussd_request(msg, &req);
+ if (req.text[0] == 0xFF) /* Release-Complete */
+ return 0;
+
+ if (strstr(USSD_TEXT_OWN_NUMBER, req.text) != NULL) {
+ DEBUGP(DMM, "USSD: Own number requested\n");
+ return send_own_number(msg, &req);
+ } else {
+ DEBUGP(DMM, "Unhandled USSD %s\n", req.text);
+ return gsm0480_send_ussd_reject(msg, &req);
+ }
+}
+
+/* A network-specific handler function */
+static int send_own_number(const struct msgb *msg, const struct ussd_request *req)
+{
+ char *own_number = msg->lchan->subscr->extension;
+ char response_string[GSM_EXTENSION_LENGTH + 20];
+
+ /* Need trailing CR as EOT character */
+ snprintf(response_string, sizeof(response_string), "Your extension is %s\r", own_number);
+ return gsm0480_send_ussd_response(msg, response_string, req);
+}
diff --git a/openbsc/src/vty_interface.c b/openbsc/src/vty_interface.c
index d6f1bb54e..066dfd5a9 100644
--- a/openbsc/src/vty_interface.c
+++ b/openbsc/src/vty_interface.c
@@ -87,6 +87,8 @@ static void net_dump_vty(struct vty *vty, struct gsm_network *net)
gsm_auth_policy_name(net->auth_policy), VTY_NEWLINE);
vty_out(vty, " Encryption: A5/%u%s", net->a5_encryption,
VTY_NEWLINE);
+ vty_out(vty, " NECI (TCH/H): %u%s", net->neci,
+ VTY_NEWLINE);
}
DEFUN(show_net, show_net_cmd, "show network",
@@ -127,17 +129,19 @@ static void bts_dump_vty(struct vty *vty, struct gsm_bts *bts)
if (bts->cell_barred)
vty_out(vty, " CELL IS BARRED%s", VTY_NEWLINE);
if (is_ipaccess_bts(bts))
- vty_out(vty, " Unit ID: %u/%u/0%s",
+ vty_out(vty, " Unit ID: %u/%u/0, OML Stream ID 0x%02x%s",
bts->ip_access.site_id, bts->ip_access.bts_id,
- VTY_NEWLINE);
+ bts->oml_tei, VTY_NEWLINE);
vty_out(vty, " NM State: ");
net_dump_nmstate(vty, &bts->nm_state);
vty_out(vty, " Site Mgr NM State: ");
net_dump_nmstate(vty, &bts->site_mgr.nm_state);
vty_out(vty, " Paging: FIXME pending requests, %u free slots%s",
bts->paging.available_slots, VTY_NEWLINE);
- vty_out(vty, " E1 Signalling Link:%s", VTY_NEWLINE);
- e1isl_dump_vty(vty, bts->oml_link);
+ if (!is_ipaccess_bts(bts)) {
+ vty_out(vty, " E1 Signalling Link:%s", VTY_NEWLINE);
+ e1isl_dump_vty(vty, bts->oml_link);
+ }
/* FIXME: oml_link, chan_desc */
}
@@ -224,7 +228,7 @@ static void config_write_bts_single(struct vty *vty, struct gsm_bts *bts)
vty_out(vty, " bts %u%s", bts->nr, VTY_NEWLINE);
vty_out(vty, " type %s%s", btstype2str(bts->type), VTY_NEWLINE);
vty_out(vty, " band %s%s", gsm_band_name(bts->band), VTY_NEWLINE);
- vty_out(vty, " cell_identity %u%s", bts->cell_identity, VTY_NEWLINE);
+ vty_out(vty, " cell_identity %u%s", bts->cell_identity, VTY_NEWLINE);
vty_out(vty, " location_area_code %u%s", bts->location_area_code,
VTY_NEWLINE);
vty_out(vty, " training_sequence_code %u%s", bts->tsc, VTY_NEWLINE);
@@ -238,10 +242,11 @@ static void config_write_bts_single(struct vty *vty, struct gsm_bts *bts)
VTY_NEWLINE);
if (bts->cell_barred)
vty_out(vty, " cell barred 1%s", VTY_NEWLINE);
- if (is_ipaccess_bts(bts))
+ if (is_ipaccess_bts(bts)) {
vty_out(vty, " ip.access unit_id %u %u%s",
bts->ip_access.site_id, bts->ip_access.bts_id, VTY_NEWLINE);
- else {
+ vty_out(vty, " oml ip.access stream_id %u%s", bts->oml_tei, VTY_NEWLINE);
+ } else {
config_write_e1_link(vty, &bts->oml_e1_link, " oml ");
vty_out(vty, " oml e1 tei %u%s", bts->oml_tei, VTY_NEWLINE);
}
@@ -269,6 +274,18 @@ static int config_write_net(struct vty *vty)
vty_out(vty, " long name %s%s", gsmnet->name_long, VTY_NEWLINE);
vty_out(vty, " auth policy %s%s", gsm_auth_policy_name(gsmnet->auth_policy), VTY_NEWLINE);
vty_out(vty, " encryption a5 %u%s", gsmnet->a5_encryption, VTY_NEWLINE);
+ vty_out(vty, " neci %u%s", gsmnet->neci, VTY_NEWLINE);
+ vty_out(vty, " timer t3101 %u%s", gsmnet->T3101, VTY_NEWLINE);
+ vty_out(vty, " timer t3103 %u%s", gsmnet->T3103, VTY_NEWLINE);
+ vty_out(vty, " timer t3105 %u%s", gsmnet->T3105, VTY_NEWLINE);
+ vty_out(vty, " timer t3107 %u%s", gsmnet->T3107, VTY_NEWLINE);
+ vty_out(vty, " timer t3109 %u%s", gsmnet->T3109, VTY_NEWLINE);
+ vty_out(vty, " timer t3111 %u%s", gsmnet->T3111, VTY_NEWLINE);
+ vty_out(vty, " timer t3113 %u%s", gsmnet->T3113, VTY_NEWLINE);
+ vty_out(vty, " timer t3115 %u%s", gsmnet->T3115, VTY_NEWLINE);
+ vty_out(vty, " timer t3117 %u%s", gsmnet->T3117, VTY_NEWLINE);
+ vty_out(vty, " timer t3119 %u%s", gsmnet->T3119, VTY_NEWLINE);
+ vty_out(vty, " timer t3141 %u%s", gsmnet->T3141, VTY_NEWLINE);
return CMD_SUCCESS;
}
@@ -285,8 +302,13 @@ static void trx_dump_vty(struct vty *vty, struct gsm_bts_trx *trx)
net_dump_nmstate(vty, &trx->nm_state);
vty_out(vty, " Baseband Transceiver NM State: ");
net_dump_nmstate(vty, &trx->bb_transc.nm_state);
- vty_out(vty, " E1 Signalling Link:%s", VTY_NEWLINE);
- e1isl_dump_vty(vty, trx->rsl_link);
+ if (is_ipaccess_bts(trx->bts)) {
+ vty_out(vty, " ip.access stream ID: 0x%02x%s",
+ trx->rsl_tei, VTY_NEWLINE);
+ } else {
+ vty_out(vty, " E1 Signalling Link:%s", VTY_NEWLINE);
+ e1isl_dump_vty(vty, trx->rsl_link);
+ }
}
DEFUN(show_trx,
@@ -612,7 +634,7 @@ DEFUN(show_e1ts,
"show e1_timeslot [line_nr] [ts_nr]",
SHOW_STR "Display information about a E1 timeslot\n")
{
- struct e1inp_line *line;
+ struct e1inp_line *line = NULL;
struct e1inp_ts *ts;
int ts_nr;
@@ -776,11 +798,51 @@ DEFUN(cfg_net_encryption,
"encryption a5 (0|1|2)",
"Enable or disable encryption (A5) for this network\n")
{
- gsmnet->auth_policy = atoi(argv[0]);
+ gsmnet->a5_encryption= atoi(argv[0]);
return CMD_SUCCESS;
}
+DEFUN(cfg_net_neci,
+ cfg_net_neci_cmd,
+ "neci (0|1)",
+ "Set if NECI of cell selection is to be set")
+{
+ gsmnet->neci = atoi(argv[0]);
+ return CMD_SUCCESS;
+}
+
+#define DECLARE_TIMER(number) \
+ DEFUN(cfg_net_T##number, \
+ cfg_net_T##number##_cmd, \
+ "timer t" #number " <0-65535>", \
+ "Set the T" #number " value.") \
+{ \
+ int value = atoi(argv[0]); \
+ \
+ if (value < 0 || value > 65535) { \
+ vty_out(vty, "Timer value %s out of range.%s", \
+ argv[0], VTY_NEWLINE); \
+ return CMD_WARNING; \
+ } \
+ \
+ gsmnet->T##number = value; \
+ return CMD_SUCCESS; \
+}
+
+DECLARE_TIMER(3101)
+DECLARE_TIMER(3103)
+DECLARE_TIMER(3105)
+DECLARE_TIMER(3107)
+DECLARE_TIMER(3109)
+DECLARE_TIMER(3111)
+DECLARE_TIMER(3113)
+DECLARE_TIMER(3115)
+DECLARE_TIMER(3117)
+DECLARE_TIMER(3119)
+DECLARE_TIMER(3141)
+
+
/* per-BTS configuration */
DEFUN(cfg_bts,
cfg_bts_cmd,
@@ -819,6 +881,11 @@ DEFUN(cfg_bts_type,
bts->type = parse_btstype(argv[0]);
+ if (is_ipaccess_bts(bts)) {
+ /* Set the default OML Stream ID to 0xff */
+ bts->oml_tei = 0xff;
+ }
+
return CMD_SUCCESS;
}
@@ -941,6 +1008,25 @@ DEFUN(cfg_bts_unit_id,
return CMD_SUCCESS;
}
+DEFUN(cfg_bts_stream_id,
+ cfg_bts_stream_id_cmd,
+ "oml ip.access stream_id <0-255>",
+ "Set the ip.access Stream ID of the OML link of this BTS\n")
+{
+ struct gsm_bts *bts = vty->index;
+ int stream_id = atoi(argv[0]);
+
+ if (!is_ipaccess_bts(bts)) {
+ vty_out(vty, "%% BTS is not of ip.access type%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ bts->oml_tei = stream_id;
+
+ return CMD_SUCCESS;
+}
+
+
DEFUN(cfg_bts_oml_e1,
cfg_bts_oml_e1_cmd,
"oml e1 line E1_LINE timeslot <1-31> sub-slot (0|1|2|3|full)",
@@ -1069,7 +1155,7 @@ DEFUN(cfg_trx_max_power_red,
{
int maxpwr_r = atoi(argv[0]);
struct gsm_bts_trx *trx = vty->index;
- int upper_limit = 12; /* default 12.21 max power red. */
+ int upper_limit = 24; /* default 12.21 max power red. */
/* FIXME: check if our BTS type supports more than 12 */
if (maxpwr_r < 0 || maxpwr_r > upper_limit) {
@@ -1114,6 +1200,17 @@ DEFUN(cfg_trx_rsl_e1_tei,
return CMD_SUCCESS;
}
+DEFUN(cfg_trx_rf_locked,
+ cfg_trx_rf_locked_cmd,
+ "rf_locked (0|1)",
+ "Turn off RF of the TRX.\n")
+{
+ int locked = atoi(argv[0]);
+ struct gsm_bts_trx *trx = vty->index;
+
+ gsm_trx_lock_rf(trx, locked);
+ return CMD_SUCCESS;
+}
/* per TS configuration */
DEFUN(cfg_ts,
@@ -1196,6 +1293,18 @@ int bsc_vty_init(struct gsm_network *net)
install_element(GSMNET_NODE, &cfg_net_name_long_cmd);
install_element(GSMNET_NODE, &cfg_net_auth_policy_cmd);
install_element(GSMNET_NODE, &cfg_net_encryption_cmd);
+ install_element(GSMNET_NODE, &cfg_net_neci_cmd);
+ install_element(GSMNET_NODE, &cfg_net_T3101_cmd);
+ install_element(GSMNET_NODE, &cfg_net_T3103_cmd);
+ install_element(GSMNET_NODE, &cfg_net_T3105_cmd);
+ install_element(GSMNET_NODE, &cfg_net_T3107_cmd);
+ install_element(GSMNET_NODE, &cfg_net_T3109_cmd);
+ install_element(GSMNET_NODE, &cfg_net_T3111_cmd);
+ install_element(GSMNET_NODE, &cfg_net_T3113_cmd);
+ install_element(GSMNET_NODE, &cfg_net_T3115_cmd);
+ install_element(GSMNET_NODE, &cfg_net_T3117_cmd);
+ install_element(GSMNET_NODE, &cfg_net_T3119_cmd);
+ install_element(GSMNET_NODE, &cfg_net_T3141_cmd);
install_element(GSMNET_NODE, &cfg_bts_cmd);
install_node(&bts_node, config_write_bts);
@@ -1207,6 +1316,7 @@ int bsc_vty_init(struct gsm_network *net)
install_element(BTS_NODE, &cfg_bts_tsc_cmd);
install_element(BTS_NODE, &cfg_bts_bsic_cmd);
install_element(BTS_NODE, &cfg_bts_unit_id_cmd);
+ install_element(BTS_NODE, &cfg_bts_stream_id_cmd);
install_element(BTS_NODE, &cfg_bts_oml_e1_cmd);
install_element(BTS_NODE, &cfg_bts_oml_e1_tei_cmd);
install_element(BTS_NODE, &cfg_bts_challoc_cmd);
@@ -1222,6 +1332,7 @@ int bsc_vty_init(struct gsm_network *net)
install_element(TRX_NODE, &cfg_trx_max_power_red_cmd);
install_element(TRX_NODE, &cfg_trx_rsl_e1_cmd);
install_element(TRX_NODE, &cfg_trx_rsl_e1_tei_cmd);
+ install_element(TRX_NODE, &cfg_trx_rf_locked_cmd);
install_element(TRX_NODE, &cfg_ts_cmd);
install_node(&ts_node, dummy_config_write);
diff --git a/openbsc/src/vty_interface_layer3.c b/openbsc/src/vty_interface_layer3.c
index 032e16fc4..4cc08c2da 100644
--- a/openbsc/src/vty_interface_layer3.c
+++ b/openbsc/src/vty_interface_layer3.c
@@ -1,5 +1,6 @@
/* OpenBSC interface to quagga VTY */
/* (C) 2009 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2009 by Holger Hans Peter Freyther
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
@@ -31,12 +32,14 @@
#include <openbsc/linuxlist.h>
#include <openbsc/gsm_data.h>
#include <openbsc/gsm_subscriber.h>
+#include <openbsc/silent_call.h>
#include <openbsc/gsm_04_11.h>
#include <openbsc/e1_input.h>
#include <openbsc/abis_nm.h>
#include <openbsc/gsm_utils.h>
#include <openbsc/db.h>
#include <openbsc/talloc.h>
+#include <openbsc/signal.h>
/* forward declarations */
void subscr_dump_vty(struct vty *vty, struct gsm_subscriber *subscr);
@@ -54,7 +57,6 @@ static int dummy_config_write(struct vty *v)
return CMD_SUCCESS;
}
-
static struct buffer *argv_to_buffer(int argc, const char *argv[], int base)
{
struct buffer *b = buffer_new(1024);
@@ -88,6 +90,7 @@ DEFUN(cfg_subscr,
return CMD_WARNING;
}
+ /* vty_go_parent should put this subscriber */
vty->index = subscr;
vty->node = SUBSCR_NODE;
@@ -112,6 +115,7 @@ DEFUN(show_subscr,
return CMD_WARNING;
}
subscr_dump_vty(vty, subscr);
+ subscr_put(subscr);
return CMD_SUCCESS;
}
@@ -191,57 +195,111 @@ struct gsm_sms *sms_from_text(struct gsm_subscriber *receiver, const char *text)
}
static int _send_sms_buffer(struct gsm_subscriber *receiver,
- struct buffer *b)
+ struct buffer *b, u_int8_t tp_pid)
{
struct gsm_sms *sms;
sms = sms_from_text(receiver, buffer_getstr(b));
-
+ sms->protocol_id = tp_pid;
gsm411_send_sms_subscr(receiver, sms);
return CMD_SUCCESS;
}
-DEFUN(sms_send_ext,
- sms_send_ext_cmd,
- "sms send extension EXTEN .LINE",
- "Send a message to a subscriber identified by EXTEN")
+static struct gsm_subscriber *get_subscr_by_argv(const char *type,
+ const char *id)
+{
+ if (!strcmp(type, "extension"))
+ return subscr_get_by_extension(gsmnet, id);
+ else if (!strcmp(type, "imsi"))
+ return subscr_get_by_imsi(gsmnet, id);
+ else if (!strcmp(type, "tmsi"))
+ return subscr_get_by_tmsi(gsmnet, atoi(id));
+ else if (!strcmp(type, "id"))
+ return subscr_get_by_id(gsmnet, atoi(id));
+
+ return NULL;
+}
+#define SUBSCR_TYPES "(extension|imsi|tmsi|id)"
+
+DEFUN(subscriber_send_sms,
+ subscriber_send_sms_cmd,
+ "subscriber " SUBSCR_TYPES " EXTEN sms send .LINE",
+ "Select subscriber based on extension")
{
- struct gsm_subscriber *receiver;
+ struct gsm_subscriber *subscr = get_subscr_by_argv(argv[0], argv[1]);
struct buffer *b;
int rc;
- receiver = subscr_get_by_extension(gsmnet, argv[0]);
- if (!receiver)
+ if (!subscr) {
+ vty_out(vty, "%% No subscriber found for %s %s%s",
+ argv[0], argv[1], VTY_NEWLINE);
return CMD_WARNING;
-
- b = argv_to_buffer(argc, argv, 1);
- rc = _send_sms_buffer(receiver, b);
+ }
+ b = argv_to_buffer(argc, argv, 2);
+ rc = _send_sms_buffer(subscr, b, 0);
buffer_free(b);
+ subscr_put(subscr);
+
return rc;
}
-DEFUN(sms_send_imsi,
- sms_send_imsi_cmd,
- "sms send imsi IMSI .LINE",
- "Send a message to a subscriber identified by IMSI")
+DEFUN(subscriber_silent_sms,
+ subscriber_silent_sms_cmd,
+ "subscriber " SUBSCR_TYPES " EXTEN silent sms send .LINE",
+ "Select subscriber based on extension")
{
- struct gsm_subscriber *receiver;
+ struct gsm_subscriber *subscr = get_subscr_by_argv(argv[0], argv[1]);
struct buffer *b;
int rc;
- receiver = subscr_get_by_imsi(gsmnet, argv[0]);
- if (!receiver)
+ if (!subscr) {
+ vty_out(vty, "%% No subscriber found for %s %s%s",
+ argv[0], argv[1], VTY_NEWLINE);
return CMD_WARNING;
+ }
- b = argv_to_buffer(argc, argv, 1);
- rc = _send_sms_buffer(receiver, b);
+ b = argv_to_buffer(argc, argv, 2);
+ rc = _send_sms_buffer(subscr, b, 64);
buffer_free(b);
+ subscr_put(subscr);
+
return rc;
}
+DEFUN(subscriber_silent_call,
+ subscriber_silent_call_cmd,
+ "subscriber " SUBSCR_TYPES " EXTEN silent call (start|stop)",
+ "Send a silent call to a subscriber")
+{
+ struct gsm_subscriber *subscr = get_subscr_by_argv(argv[0], argv[1]);
+ int rc;
+
+ if (!subscr) {
+ vty_out(vty, "%% No subscriber found for %s %s%s",
+ argv[0], argv[1], VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (!strcmp(argv[2], "start")) {
+ rc = gsm_silent_call_start(subscr, vty);
+ if (rc <= 0) {
+ vty_out(vty, "%% Subscriber not attached%s",
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ } else {
+ rc = gsm_silent_call_stop(subscr);
+ if (rc < 0)
+ return CMD_WARNING;
+ }
+
+ subscr_put(subscr);
+
+ return CMD_SUCCESS;
+}
DEFUN(cfg_subscr_name,
cfg_subscr_name_cmd,
@@ -291,17 +349,39 @@ DEFUN(cfg_subscr_authorized,
return CMD_SUCCESS;
}
+static int scall_cbfn(unsigned int subsys, unsigned int signal,
+ void *handler_data, void *signal_data)
+{
+ struct scall_signal_data *sigdata = signal_data;
+ struct vty *vty = sigdata->data;
+
+ switch (signal) {
+ case S_SCALL_SUCCESS:
+ vty_out(vty, "%% silent call on ARFCN %u timeslot %u%s",
+ sigdata->lchan->ts->trx->arfcn, sigdata->lchan->ts->nr,
+ VTY_NEWLINE);
+ break;
+ case S_SCALL_EXPIRED:
+ vty_out(vty, "%% silent call expired paging%s", VTY_NEWLINE);
+ break;
+ }
+ return 0;
+}
int bsc_vty_init_extra(struct gsm_network *net)
{
gsmnet = net;
+ register_signal_handler(SS_SCALL, scall_cbfn, NULL);
+
install_element(VIEW_NODE, &show_subscr_cmd);
install_element(VIEW_NODE, &show_subscr_cache_cmd);
install_element(VIEW_NODE, &sms_send_pend_cmd);
- install_element(VIEW_NODE, &sms_send_ext_cmd);
- install_element(VIEW_NODE, &sms_send_imsi_cmd);
+
+ install_element(VIEW_NODE, &subscriber_send_sms_cmd);
+ install_element(VIEW_NODE, &subscriber_silent_sms_cmd);
+ install_element(VIEW_NODE, &subscriber_silent_call_cmd);
install_element(CONFIG_NODE, &cfg_subscr_cmd);
install_node(&subscr_node, dummy_config_write);
diff --git a/openbsc/tests/Makefile.am b/openbsc/tests/Makefile.am
index 2d4e81c75..f40105fbf 100644
--- a/openbsc/tests/Makefile.am
+++ b/openbsc/tests/Makefile.am
@@ -1 +1 @@
-SUBDIRS = debug timer sms gsm0408 db channel
+SUBDIRS = debug timer sms gsm0408 db channel sccp
diff --git a/openbsc/tests/sccp/Makefile.am b/openbsc/tests/sccp/Makefile.am
new file mode 100644
index 000000000..5a275fc2b
--- /dev/null
+++ b/openbsc/tests/sccp/Makefile.am
@@ -0,0 +1,8 @@
+INCLUDES = $(all_includes) -I$(top_srcdir)/include
+AM_CFLAGS=-Wall -ggdb3
+
+noinst_PROGRAMS = sccp_test
+
+sccp_test_SOURCES = sccp_test.c
+sccp_test_LDADD = $(top_builddir)/src/libsccp.a $(top_builddir)/src/libbsc.a
+
diff --git a/openbsc/tests/sccp/sccp_test.c b/openbsc/tests/sccp/sccp_test.c
new file mode 100644
index 000000000..bd28ed179
--- /dev/null
+++ b/openbsc/tests/sccp/sccp_test.c
@@ -0,0 +1,724 @@
+/*
+ * SCCP testing code
+ *
+ * (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * (C) 2009 by on-waves.com
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <stdio.h>
+
+#include <arpa/inet.h>
+
+#include <sccp/sccp.h>
+#include <openbsc/gsm_data.h>
+#include <openbsc/debug.h>
+
+#define MIN(x, y) ((x) < (y) ? (x) : (y))
+
+/* BSC -> MSC */
+static const u_int8_t bssmap_reset[] = {
+ 0x09, 0x00, 0x03, 0x05, 0x07, 0x02, 0x42, 0xfe,
+ 0x02, 0x42, 0xfe, 0x06, 0x00, 0x04, 0x30, 0x04,
+ 0x01, 0x20,
+};
+
+/* MSC -> BSC reset ack */
+static const u_int8_t bssmap_reset_ack[] = {
+ 0x09, 0x00, 0x03, 0x07, 0x0b, 0x04, 0x43, 0x01,
+ 0x00, 0xfe, 0x04, 0x43, 0x5c, 0x00, 0xfe, 0x03,
+ 0x00, 0x01, 0x31,
+};
+
+/* MSC -> BSC paging, connection less */
+static const u_int8_t bssmap_paging[] = {
+ 0x09, 0x00, 0x03, 0x07, 0x0b, 0x04, 0x43, 0x01,
+ 0x00, 0xfe, 0x04, 0x43, 0x5c, 0x00, 0xfe, 0x10,
+ 0x00, 0x0e, 0x52, 0x08, 0x08, 0x29, 0x47, 0x10,
+ 0x02, 0x01, 0x31, 0x97, 0x61, 0x1a, 0x01, 0x06,
+};
+
+/* MSC -> BSC paging, UDT without PC */
+static const u_int8_t bssmap_udt[] = {
+ 0x09, 0x00, 0x03, 0x05, 0x07, 0x02, 0x42, 0xfe,
+ 0x02, 0x42, 0xfe, 0x10, 0x00, 0x0e, 0x52, 0x08,
+ 0x08, 0x29, 0x47, 0x10, 0x02, 0x01, 0x31, 0x97,
+ 0x61, 0x1a, 0x01, 0x06,
+};
+
+/* BSC -> MSC connection open */
+static const u_int8_t bssmap_cr[] = {
+ 0x01, 0x01, 0x02, 0x03, 0x02, 0x02, 0x04, 0x02,
+ 0x42, 0xfe, 0x0f, 0x1f, 0x00, 0x1d, 0x57, 0x05,
+ 0x08, 0x00, 0x72, 0xf4, 0x80, 0x20, 0x12, 0xc3,
+ 0x50, 0x17, 0x10, 0x05, 0x24, 0x11, 0x03, 0x33,
+ 0x19, 0xa2, 0x08, 0x29, 0x47, 0x10, 0x02, 0x01,
+ 0x31, 0x97, 0x61, 0x00
+};
+
+/* MSC -> BSC connection confirm */
+static const u_int8_t bssmap_cc[] = {
+ 0x02, 0x01, 0x02, 0x03, 0x00, 0x00, 0x03, 0x02, 0x01, 0x00,
+};
+
+/* MSC -> BSC DTAP
+ *
+ * we fake a bit and make it BSC -> MSC... so the
+ * payload does not make any sense..
+ */
+static const u_int8_t bssmap_dtap[] = {
+ 0x06, 0x00, 0x00, 0x03, 0x00, 0x01, 0x0f, 0x01, 0x00, 0x0c,
+ 0x03, 0x05, 0x5c, 0x08, 0x11, 0x81, 0x33, 0x66, 0x02, 0x13,
+ 0x45, 0xf4,
+};
+
+/* MSC -> BSC clear command */
+static const u_int8_t bssmap_clear[] = {
+ 0x06, 0x00, 0x00, 0x03, 0x00, 0x01, 0x06, 0x00, 0x04, 0x20,
+ 0x04, 0x01, 0x09,
+};
+
+/* MSC -> BSC released */
+static const u_int8_t bssmap_released[] = {
+ 0x04, 0x00, 0x00, 0x03, 0x01, 0x02, 0x03, 0x00, 0x01, 0x0f,
+ 0x02, 0x23, 0x42, 0x00,
+};
+
+/* BSC -> MSC released */
+static const u_int8_t bssmap_release_complete[] = {
+ 0x05, 0x01, 0x02, 0x03, 0x00, 0x00, 0x03
+};
+
+struct test_data {
+ int length;
+ const u_int8_t *data;
+ int payload_start;
+ int payload_length;
+ u_int8_t first_byte;
+
+ /* in case it should trigger a sccp response */
+ int write;
+ const u_int8_t *response;
+ int response_length;
+};
+
+static const struct test_data test_data[] = {
+ {
+ .length = ARRAY_SIZE(bssmap_reset),
+ .data = &bssmap_reset[0],
+ .payload_start = 12,
+ .payload_length = ARRAY_SIZE(bssmap_reset) - 12,
+ .first_byte = 0x0,
+ },
+ {
+ .length = ARRAY_SIZE(bssmap_reset_ack),
+ .data = &bssmap_reset_ack[0],
+ .payload_start = 16,
+ .payload_length = ARRAY_SIZE(bssmap_reset_ack) - 16,
+ .first_byte = 0x0,
+ },
+ {
+ .length = ARRAY_SIZE(bssmap_paging),
+ .data = &bssmap_paging[0],
+ .payload_start = 16,
+ .payload_length = ARRAY_SIZE(bssmap_paging) - 16,
+ .first_byte = 0x0,
+ },
+ {
+ .length = ARRAY_SIZE(bssmap_cr),
+ .data = &bssmap_cr[0],
+ .payload_start = 12,
+ /* 0x00 is end of optional data, subtract this byte */
+ .payload_length = 31,
+ .first_byte = 0x0,
+
+ /* the connection request should trigger a connection confirm */
+ .write = 1,
+ .response = &bssmap_cc[0],
+ .response_length= ARRAY_SIZE(bssmap_cc),
+ },
+ {
+ .length = ARRAY_SIZE(bssmap_dtap),
+ .data = &bssmap_dtap[0],
+ .payload_start = 7,
+ .payload_length = 15,
+ .first_byte = 0x01,
+ },
+ {
+ .length = ARRAY_SIZE(bssmap_clear),
+ .data = &bssmap_clear[0],
+ .payload_start = 7,
+ .payload_length = 6,
+ .first_byte = 0x00,
+ },
+ {
+ .length = ARRAY_SIZE(bssmap_released),
+ .data = &bssmap_released[0],
+ .payload_length = 2,
+ .payload_start = 11,
+ .first_byte = 0x23,
+
+ .write = 1,
+ .response = &bssmap_release_complete[0],
+ .response_length= ARRAY_SIZE(bssmap_release_complete),
+ },
+};
+
+/* we will send UDTs and verify they look like this */
+static const struct test_data send_data[] = {
+ {
+ .length = ARRAY_SIZE(bssmap_udt),
+ .data = &bssmap_udt[0],
+ .payload_start = 12,
+ .payload_length = ARRAY_SIZE(bssmap_udt) - 12,
+ .first_byte = 0x0,
+ },
+ {
+ .length = ARRAY_SIZE(bssmap_reset),
+ .data = &bssmap_reset[0],
+ .payload_start = 12,
+ .payload_length = ARRAY_SIZE(bssmap_reset) - 12,
+ .first_byte = 0x0,
+ },
+};
+
+struct connection_test {
+ /* should the connection be refused? */
+ int refuse;
+
+ int with_data;
+
+ /* on which side to close the connection? */
+ int close_side;
+ int close_cause;
+};
+
+/* sccp connection handling we want to test */
+static const struct connection_test connection_tests[] = {
+ {
+ .refuse = 1,
+ },
+ {
+ .refuse = 1,
+ .with_data = 1,
+ },
+ {
+ .refuse = 0,
+ .close_side = 0,
+ .close_cause = 5,
+ },
+ {
+ .refuse = 0,
+ .close_side = 0,
+ .close_cause = 5,
+ .with_data = 1,
+ },
+ {
+ .refuse = 0,
+ .close_side = 1,
+ .close_cause = 5,
+ },
+ {
+ .refuse = 0,
+ .close_side = 1,
+ .close_cause = 5,
+ .with_data = 1,
+ },
+};
+
+/* testing procedure:
+ * - we will use sccp_write and see what will be set in the
+ * outgoing callback
+ * - we will call sccp_system_incoming and see which calls
+ * are made. And then compare it to the ones we expect. We
+ * want the payload to arrive, or callbacks to be called.
+ * - we will use sccp_connection_socket and sccp_connection_write
+ * and verify state handling of connections
+ */
+
+static int current_test;
+
+/*
+ * test state...
+ */
+static int called = 0;
+static int matched = 0;
+static int write_called = 0;
+
+#define FAIL(x, args...) printf("FAILURE in %s:%d: " x, __FILE__, __LINE__, ## args)
+
+/*
+ * writing these packets and expecting a result
+ */
+int sccp_read_cb(struct msgb *data, unsigned len, void *context)
+{
+ u_int16_t payload_length = test_data[current_test].payload_length;
+ const u_int8_t *got, *wanted;
+ int i;
+
+ called = 1;
+
+ if (msgb_l3len(data) < len) {
+ /* this should never be reached */
+ FAIL("Something horrible happened.. invalid packet..\n");
+ exit(-1);
+ }
+
+ if (len == 0 || len != payload_length) {
+ FAIL("length mismatch: got: %d wanted: %d\n", msgb_l3len(data), payload_length);
+ return -1;
+ }
+
+ if (data->l3h[0] != test_data[current_test].first_byte) {
+ FAIL("The first bytes of l3 do not match: 0x%x 0x%x\n",
+ data->l3h[0], test_data[current_test].first_byte);
+ return -1;
+ }
+
+ got = &data->l3h[0];
+ wanted = test_data[current_test].data + test_data[current_test].payload_start;
+
+ for (i = 0; i < len; ++i) {
+ if (got[i] != wanted[i]) {
+ FAIL("Failed to compare byte. Got: 0x%x Wanted: 0x%x at %d\n",
+ got[i], wanted[i], i);
+ return -1;
+ }
+ }
+
+ matched = 1;
+ return 0;
+}
+
+int sccp_write_cb(struct msgb *data, void *ctx)
+{
+ int i = 0;
+ const u_int8_t *got, *wanted;
+
+ if (test_data[current_test].response == NULL) {
+ FAIL("Didn't expect write callback\n");
+ return -1;
+ } else if (test_data[current_test].response_length != msgb_l2len(data)) {
+ FAIL("Size does not match. Got: %d Wanted: %d\n",
+ msgb_l2len(data), test_data[current_test].response_length);
+ }
+
+ got = &data->l2h[0];
+ wanted = test_data[current_test].response;
+
+ for (i = 0; i < msgb_l2len(data); ++i) {
+ if (got[i] != wanted[i]) {
+ FAIL("Failed to compare byte. Got: 0x%x Wanted: 0x%x at %d\n",
+ got[i], wanted[i], i);
+ return -1;
+ }
+ }
+
+ write_called = 1;
+ return 0;
+}
+
+void sccp_c_read(struct sccp_connection *connection, struct msgb *msgb, unsigned int len)
+{
+ sccp_read_cb(msgb, len, connection->data_ctx);
+}
+
+void sccp_c_state(struct sccp_connection *connection, int old_state)
+{
+ if (connection->connection_state >= SCCP_CONNECTION_STATE_RELEASE_COMPLETE)
+ sccp_connection_free(connection);
+}
+
+int sccp_accept_cb(struct sccp_connection *connection, void *user_data)
+{
+ called = 1;
+ unsigned int ref = 0;
+ ref |= connection->destination_local_reference.octet1 << 24;
+ ref |= connection->destination_local_reference.octet2 << 16;
+ ref |= connection->destination_local_reference.octet3 << 8;
+ ref = ntohl(ref);
+
+ connection->data_cb = sccp_c_read;
+ connection->state_cb = sccp_c_state;
+
+ /* accept this */
+ return 0;
+}
+
+static int sccp_udt_write_cb(struct msgb *data, void *context)
+{
+ const u_int8_t *got, *wanted;
+ int i;
+
+ write_called = 1;
+
+ if (send_data[current_test].length != msgb_l2len(data)) {
+ FAIL("Size does not match. Got: %d Wanted: %d\n",
+ msgb_l2len(data), send_data[current_test].length);
+ return -1;
+ }
+
+ got = &data->l2h[0];
+ wanted = send_data[current_test].data;
+
+ for (i = 0; i < msgb_l2len(data); ++i) {
+ if (got[i] != wanted[i]) {
+ FAIL("Failed to compare byte. Got: 0x%x Wanted: 0x%x at %d\n",
+ got[i], wanted[i], i);
+ return -1;
+ }
+ }
+
+ matched = 1;
+ return 0;
+}
+
+static void test_sccp_system(void)
+{
+ sccp_system_init(sccp_write_cb, NULL);
+ sccp_set_read(&sccp_ssn_bssap, sccp_read_cb, NULL);
+ sccp_connection_set_incoming(&sccp_ssn_bssap, sccp_accept_cb, NULL);
+
+ for (current_test = 0; current_test < ARRAY_SIZE(test_data); ++current_test) {
+ unsigned int length = test_data[current_test].length;
+ struct msgb *msg = msgb_alloc_headroom(length + 2, 2, __func__);
+ msg->l2h = msgb_put(msg, length);
+ memcpy(msg->l2h, test_data[current_test].data, length);
+
+ called = matched = write_called = 0;
+ printf("Testing packet: %d\n", current_test);
+ sccp_system_incoming(msg);
+
+ if (!called || !matched || (test_data[current_test].write != write_called))
+ FAIL("current test: %d called: %d matched: %d write: %d\n",
+ current_test, called, matched, write_called);
+
+ msgb_free(msg);
+ }
+}
+
+/* test sending of udt */
+static void test_sccp_send_udt(void)
+{
+ sccp_system_init(sccp_udt_write_cb, NULL);
+ sccp_set_read(NULL, NULL, NULL);
+ sccp_connection_set_incoming(NULL, NULL, NULL);
+
+ for (current_test = 0; current_test < ARRAY_SIZE(send_data); ++current_test) {
+ const struct test_data *test = &send_data[current_test];
+
+ struct msgb *msg = msgb_alloc(test->payload_length, __func__);
+ msg->l3h = msgb_put(msg, test->payload_length);
+ memcpy(msg->l3h, test->data + test->payload_start, test->payload_length);
+
+ matched = write_called = 0;
+ printf("Testing packet: %d\n", current_test);
+ sccp_write(msg, &sccp_ssn_bssap, &sccp_ssn_bssap, 0);
+
+ if (!matched || !write_called)
+ FAIL("current test: %d matched: %d write: %d\n",
+ current_test, matched, write_called);
+
+ msgb_free(msg);
+ }
+}
+
+/* send udt from one end to another */
+static unsigned int test_value = 0x2442;
+static int sccp_udt_read(struct msgb *data, unsigned int len, void *context)
+{
+ unsigned int *val;
+
+ if (len != 4) {
+ FAIL("Wrong size: %d\n", msgb_l3len(data));
+ return -1;
+ }
+
+ val = (unsigned int*)data->l3h;
+ matched = test_value == *val;
+
+ return 0;
+}
+
+static int sccp_write_loop(struct msgb *data, void *context)
+{
+ /* send it back to us */
+ sccp_system_incoming(data);
+ return 0;
+}
+
+static void test_sccp_udt_communication(void)
+{
+ struct msgb *data;
+ unsigned int *val;
+
+ sccp_system_init(sccp_write_loop, NULL);
+ sccp_set_read(&sccp_ssn_bssap, sccp_udt_read, NULL);
+ sccp_connection_set_incoming(NULL, NULL, NULL);
+
+
+ data = msgb_alloc(4, "test data");
+ data->l3h = &data->data[0];
+ val = (unsigned int *)msgb_put(data, 4);
+ *val = test_value;
+
+ matched = 0;
+ sccp_write(data, &sccp_ssn_bssap, &sccp_ssn_bssap, 0);
+
+ if (!matched)
+ FAIL("Talking with us didn't work\n");
+
+ msgb_free(data);
+}
+
+
+/* connection testing... open, send, close */
+static const struct connection_test *current_con_test;
+static struct sccp_connection *outgoing_con;
+static struct sccp_connection *incoming_con;
+static int outgoing_data, incoming_data, incoming_state, outgoing_state;
+
+static struct msgb *test_data1, *test_data2, *test_data3;
+
+static void sccp_conn_in_state(struct sccp_connection *conn, int old_state)
+{
+ printf("\tincome: %d -> %d\n", old_state, conn->connection_state);
+ if (conn->connection_state >= SCCP_CONNECTION_STATE_RELEASE_COMPLETE) {
+ if (conn == incoming_con) {
+ sccp_connection_free(conn);
+ incoming_con = NULL;
+ }
+ }
+}
+
+static void sccp_conn_in_data(struct sccp_connection *conn, struct msgb *msg, unsigned int len)
+{
+ /* compare the data */
+ ++incoming_data;
+ printf("\tincoming data: %d\n", len);
+
+ /* compare the data */
+ if (len != 4) {
+ FAIL("Length of packet is wrong: %u %u\n", msgb_l3len(msg), len);
+ return;
+ }
+
+ if (incoming_data == 1) {
+ if (memcmp(msg->l3h, test_data1->l3h, len) != 0) {
+ FAIL("Comparing the data failed: %d\n", incoming_data);
+ incoming_state = 0;
+ printf("Got: %s\n", hexdump(msg->l3h, len));
+ printf("Wanted: %s\n", hexdump(test_data1->l3h, len));
+
+ }
+ } else if (incoming_data == 2) {
+ if (memcmp(msg->l3h, test_data2->l3h, len) != 0) {
+ FAIL("Comparing the data failed: %d\n", incoming_data);
+ incoming_state = 0;
+ printf("Got: %s\n", hexdump(msg->l3h, len));
+ printf("Wanted: %s\n", hexdump(test_data2->l3h, len));
+ }
+ }
+
+ /* sending out data */
+ if (incoming_data == 2) {
+ printf("\tReturning data3\n");
+ sccp_connection_write(conn, test_data3);
+ }
+}
+
+static int sccp_conn_accept(struct sccp_connection *conn, void *ctx)
+{
+ printf("\taccept: %p\n", conn);
+ conn->state_cb = sccp_conn_in_state;
+ conn->data_cb = sccp_conn_in_data;
+
+ if (current_con_test->refuse)
+ return -1;
+
+ incoming_con = conn;
+ return 0;
+}
+
+/* callbacks for the outgoing side */
+static void sccp_conn_out_state(struct sccp_connection *conn, int old_state)
+{
+ printf("\toutgoing: %p %d -> %d\n", conn, old_state, conn->connection_state);
+
+ if (conn->connection_state >= SCCP_CONNECTION_STATE_RELEASE_COMPLETE) {
+ if (conn == outgoing_con) {
+ sccp_connection_free(conn);
+ outgoing_con = NULL;
+ }
+ }
+}
+
+static void sccp_conn_out_data(struct sccp_connection *conn, struct msgb *msg, unsigned int len)
+{
+ ++outgoing_data;
+ printf("\toutgoing data: %p %d\n", conn, len);
+
+ if (len != 4)
+ FAIL("Length of packet is wrong: %u %u\n", msgb_l3len(msg), len);
+
+ if (outgoing_data == 1) {
+ if (memcmp(msg->l3h, test_data3->l3h, len) != 0) {
+ FAIL("Comparing the data failed\n");
+ outgoing_state = 0;
+ }
+ }
+}
+
+static void do_test_sccp_connection(const struct connection_test *test)
+{
+ int ret;
+
+ current_con_test = test;
+ outgoing_con = incoming_con = 0;
+
+ outgoing_con = sccp_connection_socket();
+ if (!outgoing_con) {
+ FAIL("Connection is NULL\n");
+ return;
+ }
+
+ outgoing_con->state_cb = sccp_conn_out_state;
+ outgoing_con->data_cb = sccp_conn_out_data;
+ outgoing_data = incoming_data = 0;
+ incoming_state = outgoing_state = 1;
+
+ /* start testing */
+ if (test->with_data) {
+ if (sccp_connection_connect(outgoing_con, &sccp_ssn_bssap, test_data1) != 0)
+ FAIL("Binding failed\n");
+ } else {
+ ++incoming_data;
+ if (sccp_connection_connect(outgoing_con, &sccp_ssn_bssap, NULL) != 0)
+ FAIL("Binding failed\n");
+ }
+
+ if (test->refuse) {
+ if (outgoing_con)
+ FAIL("Outgoing connection should have been refused.\n");
+ } else {
+ if (!incoming_con)
+ FAIL("Creating incoming didn't work.\n");
+
+ printf("\tWriting test data2\n");
+ sccp_connection_write(outgoing_con, test_data2);
+ sccp_connection_send_it(outgoing_con);
+
+ /* closing connection */
+ if (test->close_side == 0)
+ ret = sccp_connection_close(outgoing_con, 0);
+ else
+ ret = sccp_connection_close(incoming_con, 0);
+
+ if (ret != 0)
+ FAIL("Closing the connection failed\n");
+ }
+
+ /* outgoing should be gone now */
+ if (outgoing_con)
+ FAIL("Outgoing connection was not properly closed\n");
+
+ if (incoming_con)
+ FAIL("Incoming connection was not propery closed.\n");
+
+ if (test->refuse == 0) {
+ if (outgoing_data != 1 || incoming_data != 2) {
+ FAIL("Data sending failed: %d/%d %d/%d\n",
+ outgoing_data, 1,
+ incoming_data, 2);
+ }
+ }
+
+ if (!incoming_state || !outgoing_state)
+ FAIL("Failure with the state transition. %d %d\n",
+ outgoing_state, incoming_state);
+}
+
+static void test_sccp_connection(void)
+{
+ sccp_system_init(sccp_write_loop, NULL);
+ sccp_set_read(NULL, NULL, NULL);
+ sccp_connection_set_incoming(&sccp_ssn_bssap, sccp_conn_accept, NULL);
+
+ test_data1 = msgb_alloc(4, "data1");
+ test_data1->l3h = msgb_put(test_data1, 4);
+ *((unsigned int*)test_data1->l3h) = 0x23421122;
+
+ test_data2 = msgb_alloc(4, "data2");
+ test_data2->l3h = msgb_put(test_data2, 4);
+ *((unsigned int*)test_data2->l3h) = 0x42232211;
+
+ test_data3 = msgb_alloc(4, "data3");
+ test_data3->l3h = msgb_put(test_data3, 4);
+ *((unsigned int*)test_data3->l3h) = 0x2323ff55;
+
+
+ for (current_test = 0; current_test < ARRAY_SIZE(connection_tests); ++current_test) {
+ printf("Testing %d refuse: %d with_data: %d\n",
+ current_test, connection_tests[current_test].refuse,
+ connection_tests[current_test].with_data);
+ do_test_sccp_connection(&connection_tests[current_test]);
+ }
+
+ msgb_free(test_data1);
+ msgb_free(test_data2);
+ msgb_free(test_data3);
+}
+
+/* invalid input */
+static void test_sccp_system_crash(void)
+{
+ printf("trying to provoke a crash with invalid input\n");
+ sccp_set_read(&sccp_ssn_bssap, sccp_read_cb, NULL);
+ sccp_connection_set_incoming(&sccp_ssn_bssap, sccp_accept_cb, NULL);
+
+ for (current_test = 0; current_test < ARRAY_SIZE(test_data); ++current_test) {
+ int original_length = test_data[current_test].length;
+ int length = original_length + 2;
+ int i;
+
+ printf("Testing packet: %d\n", current_test);
+
+ for (i = length; i >= 0; --i) {
+ unsigned int length = MIN(test_data[current_test].length, i);
+ struct msgb *msg = msgb_alloc_headroom(length + 2, 2, __func__);
+ msg->l2h = msgb_put(msg, length);
+ memcpy(msg->l2h, test_data[current_test].data, length);
+ sccp_system_incoming(msg);
+ msgb_free(msg);
+ }
+ }
+
+ printf("survived\n");
+}
+
+
+int main(int argc, char **argv)
+{
+ test_sccp_system();
+ test_sccp_send_udt();
+ test_sccp_udt_communication();
+ test_sccp_connection();
+ test_sccp_system_crash();
+ return 0;
+}
diff --git a/openbsc/tests/sms/sms_test.c b/openbsc/tests/sms/sms_test.c
index fa0963644..2ce2cc6c4 100644
--- a/openbsc/tests/sms/sms_test.c
+++ b/openbsc/tests/sms/sms_test.c
@@ -24,6 +24,7 @@
#include <sys/types.h>
#include <openbsc/debug.h>
#include <openbsc/msgb.h>
+#include <openbsc/gsm_data.h>
#include <openbsc/gsm_utils.h>
int main(int argc, char** argv)
diff --git a/wireshark/abis_oml.patch b/wireshark/abis_oml.patch
index 2de1610a3..dc51f76c1 100644
--- a/wireshark/abis_oml.patch
+++ b/wireshark/abis_oml.patch
@@ -1,8 +1,8 @@
Index: wireshark/epan/dissectors/Makefile.common
===================================================================
---- wireshark.orig/epan/dissectors/Makefile.common
-+++ wireshark/epan/dissectors/Makefile.common
-@@ -473,6 +473,7 @@
+--- wireshark.orig/epan/dissectors/Makefile.common 2009-10-21 23:03:44.000000000 +0200
++++ wireshark/epan/dissectors/Makefile.common 2009-10-21 23:03:57.000000000 +0200
+@@ -472,6 +472,7 @@
packet-gsm_a_gm.c \
packet-gsm_a_rp.c \
packet-gsm_a_rr.c \
@@ -12,8 +12,8 @@ Index: wireshark/epan/dissectors/Makefile.common
packet-gsm_bssmap_le.c \
Index: wireshark/epan/dissectors/packet-gsm_abis_oml.c
===================================================================
---- /dev/null
-+++ wireshark/epan/dissectors/packet-gsm_abis_oml.c
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ wireshark/epan/dissectors/packet-gsm_abis_oml.c 2009-10-22 10:06:18.000000000 +0200
@@ -0,0 +1,1365 @@
+/* packet-abis_oml.c
+ * Routines for packet dissection of GSM A-bis over IP (3GPP TS 12.21)
@@ -1113,7 +1113,7 @@ Index: wireshark/epan/dissectors/packet-gsm_abis_oml.c
+ },
+ { &hf_oml_fom_attr_val,
+ { "FOM Attribute Value", "oml.fom.attr_val",
-+ FT_BYTES, BASE_HEX, NULL, 0,
++ FT_BYTES, BASE_NONE, NULL, 0,
+ NULL, HFILL }
+ },
+
@@ -1308,20 +1308,20 @@ Index: wireshark/epan/dissectors/packet-gsm_abis_oml.c
+ },
+ { &hf_attr_ipa_tr_si2,
+ { "System Information 2", "oml.fom.attr.ipa.si2",
-+ FT_BYTES, BASE_HEX, NULL, 0, NULL, HFILL }
++ FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL }
+ },
+ { &hf_attr_ipa_tr_si2bis,
+ { "System Information 2bis", "oml.fom.attr.ipa.si2bis",
-+ FT_BYTES, BASE_HEX, NULL, 0, NULL, HFILL }
++ FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL }
+ },
+ { &hf_attr_ipa_tr_si2ter,
+ { "System Information 2ter", "oml.fom.attr.ipa.si2ter",
-+ FT_BYTES, BASE_HEX, NULL, 0, NULL, HFILL }
++ FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL }
+ },
+ { &hf_attr_ipa_tr_chan_desc,
+ { "Cell Channel Description",
+ "oml.fom.attr.ipa.chan_desc",
-+ FT_BYTES, BASE_HEX, NULL, 0, NULL, HFILL }
++ FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL }
+ },
+ { &hf_attr_ipa_nsl_sport,
+ { "NS Link IP Source Port",
@@ -1382,8 +1382,8 @@ Index: wireshark/epan/dissectors/packet-gsm_abis_oml.c
+}
Index: wireshark/epan/dissectors/packet-gsm_abis_oml.h
===================================================================
---- /dev/null
-+++ wireshark/epan/dissectors/packet-gsm_abis_oml.h
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ wireshark/epan/dissectors/packet-gsm_abis_oml.h 2009-10-21 23:03:57.000000000 +0200
@@ -0,0 +1,786 @@
+/* GSM Network Management messages on the A-bis interface
+ * 3GPP TS 12.21 version 8.0.0 Release 1999 / ETSI TS 100 623 V8.0.0 */
diff --git a/wireshark/abisip.patch b/wireshark/abisip.patch
deleted file mode 100644
index 44ca3ee7f..000000000
--- a/wireshark/abisip.patch
+++ /dev/null
@@ -1,313 +0,0 @@
-Index: epan/dissectors/Makefile.common
-===================================================================
---- epan/dissectors/Makefile.common.orig
-+++ epan/dissectors/Makefile.common
-@@ -471,6 +471,7 @@
- packet-gsm_a_gm.c \
- packet-gsm_a_rp.c \
- packet-gsm_a_rr.c \
-+ packet-gsm_abis_ip.c \
- packet-gsm_bsslap.c \
- packet-gsm_bssmap_le.c \
- packet-gsm_sms.c \
-Index: epan/dissectors/packet-rsl.c
-===================================================================
---- epan/dissectors/packet-rsl.c.orig
-+++ epan/dissectors/packet-rsl.c
-@@ -3950,6 +3950,7 @@
- proto_register_field_array(proto_rsl, hf, array_length(hf));
- proto_register_subtree_array(ett, array_length(ett));
-
-+ register_dissector("gsm_abis_rsl", dissect_rsl, proto_rsl);
-
- }
-
-Index: epan/dissectors/packet-gsm_abis_ip.c
-===================================================================
---- /dev/null
-+++ epan/dissectors/packet-gsm_abis_ip.c
-@@ -0,0 +1,284 @@
-+/* packet-gsm_abis_ip.c
-+ * Routines for packet dissection of ip.access A-bis over IP
-+ * Copyright 2009 by Harald Welte <laforge@gnumonks.org>
-+ *
-+ * $Id$
-+ *
-+ * Wireshark - Network traffic analyzer
-+ * By Gerald Combs <gerald@wireshark.org>
-+ * Copyright 1998 Gerald Combs
-+ *
-+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-+ */
-+
-+#ifdef HAVE_CONFIG_H
-+# include "config.h"
-+#endif
-+
-+#include <glib.h>
-+
-+#include <epan/packet.h>
-+#include <epan/emem.h>
-+
-+/* Initialize the protocol and registered fields */
-+static int proto_abisip = -1;
-+static int proto_ipaccess = -1;
-+
-+static int hf_abisip_data_len = -1;
-+static int hf_abisip_protocol = -1;
-+
-+static int hf_ipaccess_msgtype = -1;
-+static int hf_ipaccess_attr_tag = -1;
-+static int hf_ipaccess_attr_string = -1;
-+
-+/* Initialize the subtree pointers */
-+static gint ett_abisip = -1;
-+static gint ett_ipaccess = -1;
-+
-+enum {
-+ SUB_OML,
-+ SUB_RSL,
-+ SUB_IPACCESS,
-+
-+ SUB_MAX
-+};
-+
-+static dissector_handle_t sub_handles[SUB_MAX];
-+
-+#define TCP_PORT_ABISIP_PRIM 3002
-+#define TCP_PORT_ABISIP_SEC 3003
-+#define TCP_PORT_ABISIP_INST 3006
-+
-+#define ABISIP_RSL 0x00
-+#define ABISIP_IPACCESS 0xfe
-+#define ABISIP_OML 0xff
-+
-+static const value_string abisip_protocol_vals[] = {
-+ { 0x00, "RSL" },
-+ { 0xfe, "IPA" },
-+ { 0xff, "OML" },
-+ { 0, NULL }
-+};
-+
-+static const value_string ipaccess_msgtype_vals[] = {
-+ { 0x00, "PING?" },
-+ { 0x01, "PONG!" },
-+ { 0x04, "IDENTITY REQUEST" },
-+ { 0x05, "IDENTITY RESPONSE" },
-+ { 0x06, "IDENTITY CONF" },
-+ { 0, NULL }
-+};
-+
-+static const value_string ipaccess_idtag_vals[] = {
-+ { 0x00, "Serial Number" },
-+ { 0x01, "Unit Name" },
-+ { 0x02, "Location" },
-+ { 0x04, "Equipment Version" },
-+ { 0x05, "Software Version" },
-+ { 0x06, "IP Address" },
-+ { 0x07, "MAC Address" },
-+ { 0x08, "Unit ID" },
-+};
-+
-+static gint
-+dissect_ipa_attr(tvbuff_t *tvb, int base_offs, proto_tree *tree)
-+{
-+ guint8 len, attr_type;
-+
-+ int offset = base_offs;
-+
-+ while (tvb_reported_length_remaining(tvb, offset) != 0) {
-+ attr_type = tvb_get_guint8(tvb, offset);
-+
-+ switch (attr_type) {
-+ case 0x00: /* a string prefixed by its length */
-+ len = tvb_get_guint8(tvb, offset+1);
-+ proto_tree_add_item(tree, hf_ipaccess_attr_tag,
-+ tvb, offset+2, 1, FALSE);
-+ proto_tree_add_item(tree, hf_ipaccess_attr_string,
-+ tvb, offset+3, len-1, FALSE);
-+ break;
-+ case 0x01: /* a single-byte reqest for a certain attr */
-+ len = 0;
-+ proto_tree_add_item(tree, hf_ipaccess_attr_tag,
-+ tvb, offset+1, 1, FALSE);
-+ break;
-+ default:
-+ len = 0;
-+ proto_tree_add_text(tree, tvb, offset+1, 1,
-+ "unknonw attribute type 0x%02x",
-+ attr_type);
-+ break;
-+ };
-+ offset += len + 2;
-+ };
-+ return offset;
-+}
-+
-+/* Dissect an ip.access specific message */
-+static gint
-+dissect_ipaccess(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
-+{
-+ proto_item *ti;
-+ proto_tree *ipaccess_tree;
-+ guint8 msg_type;
-+
-+ msg_type = tvb_get_guint8(tvb, 0);
-+
-+ if (check_col(pinfo->cinfo, COL_INFO))
-+ col_append_fstr(pinfo->cinfo, COL_INFO, "%s ",
-+ val_to_str(msg_type, ipaccess_msgtype_vals,
-+ "unknown 0x%02x"));
-+ if (tree) {
-+ ti = proto_tree_add_item(tree, proto_ipaccess, tvb, 0, -1, FALSE);
-+ ipaccess_tree = proto_item_add_subtree(ti, ett_ipaccess);
-+ proto_tree_add_item(ipaccess_tree, hf_ipaccess_msgtype,
-+ tvb, 0, 1, FALSE);
-+ switch (msg_type) {
-+ case 4:
-+ case 5:
-+ dissect_ipa_attr(tvb, 1, ipaccess_tree);
-+ break;
-+ }
-+ }
-+
-+ return 1;
-+}
-+
-+
-+/* Code to actually dissect the packets */
-+static void
-+dissect_abisip(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
-+{
-+
-+ int offset = 0;
-+
-+ if (check_col(pinfo->cinfo, COL_PROTOCOL))
-+ col_set_str(pinfo->cinfo, COL_PROTOCOL, "Abis/IP");
-+ if (check_col(pinfo->cinfo, COL_INFO))
-+ col_clear(pinfo->cinfo, COL_INFO);
-+
-+ while (tvb_reported_length_remaining(tvb, offset) != 0) {
-+ proto_item *ti;
-+ proto_tree *abisip_tree;
-+ guint8 len, msg_type;
-+ tvbuff_t *next_tvb;
-+
-+ len = tvb_get_guint8(tvb, offset+1);
-+ msg_type = tvb_get_guint8(tvb, offset+2);
-+
-+ if (check_col(pinfo->cinfo, COL_INFO))
-+ col_append_fstr(pinfo->cinfo, COL_INFO, "%s ",
-+ val_to_str(msg_type, abisip_protocol_vals,
-+ "unknown 0x%02x"));
-+
-+ if (tree) {
-+ ti = proto_tree_add_protocol_format(tree, proto_abisip,
-+ tvb, offset, len+3,
-+ "A-bis/IP protocol ip.access, type: %s",
-+ val_to_str(msg_type, abisip_protocol_vals,
-+ "unknown 0x%02x"));
-+ abisip_tree = proto_item_add_subtree(ti, ett_abisip);
-+ proto_tree_add_item(abisip_tree, hf_abisip_data_len,
-+ tvb, offset+1, 1, FALSE);
-+ proto_tree_add_item(abisip_tree, hf_abisip_protocol,
-+ tvb, offset+2, 1, FALSE);
-+ }
-+
-+ next_tvb = tvb_new_subset(tvb, offset+3, len, len);
-+
-+ switch (msg_type) {
-+ case ABISIP_RSL:
-+ /* hand this off to the standard A-bis RSL dissector */
-+ call_dissector(sub_handles[SUB_RSL], next_tvb, pinfo, tree);
-+ break;
-+ case ABISIP_OML:
-+ /* hand this off to the standard A-bis OML dissector */
-+ if (sub_handles[SUB_OML])
-+ call_dissector(sub_handles[SUB_OML], next_tvb,
-+ pinfo, tree);
-+ break;
-+ case ABISIP_IPACCESS:
-+ dissect_ipaccess(next_tvb, pinfo, tree);
-+ break;
-+ }
-+ offset += len + 3;
-+ }
-+}
-+
-+void proto_register_abis_ip(void)
-+{
-+ static hf_register_info hf[] = {
-+ {&hf_abisip_data_len,
-+ {"DataLen", "abisip.data_len",
-+ FT_UINT8, BASE_DEC, NULL, 0x0,
-+ "The length of the data (in bytes)", HFILL}
-+ },
-+ {&hf_abisip_protocol,
-+ {"Protocol", "abisip.protocol",
-+ FT_UINT8, BASE_HEX, VALS(abisip_protocol_vals), 0x0,
-+ "The A-bis/IP Sub-Protocol", HFILL}
-+ },
-+ };
-+ static hf_register_info hf_ipa[] = {
-+ {&hf_ipaccess_msgtype,
-+ {"MessageType", "ipaccess.msg_type",
-+ FT_UINT8, BASE_HEX, VALS(ipaccess_msgtype_vals), 0x0,
-+ "Type of ip.access messsage", HFILL}
-+ },
-+ {&hf_ipaccess_attr_tag,
-+ {"Tag", "ipaccess.attr_tag",
-+ FT_UINT8, BASE_HEX, VALS(ipaccess_idtag_vals), 0x0,
-+ "Attribute Tag", HFILL}
-+ },
-+ {&hf_ipaccess_attr_string,
-+ {"String", "ipaccess.attr_string",
-+ FT_STRING, BASE_NONE, NULL, 0x0,
-+ "String attribute", HFILL}
-+ },
-+ };
-+
-+ static gint *ett[] = {
-+ &ett_abisip,
-+ &ett_ipaccess,
-+ };
-+
-+ proto_abisip =
-+ proto_register_protocol("GSM A-bis/IP protocol as used by ip.access",
-+ "GSM A-bis/IP", "gsm_abis_ip");
-+ proto_ipaccess =
-+ proto_register_protocol("GSM A-bis/IP ip.access CCM sub-protocol",
-+ "IPA", "ipaccess");
-+
-+ proto_register_field_array(proto_abisip, hf, array_length(hf));
-+ proto_register_field_array(proto_ipaccess, hf_ipa, array_length(hf_ipa));
-+ proto_register_subtree_array(ett, array_length(ett));
-+
-+ register_dissector("gsm_abis_ip", dissect_abisip, proto_abisip);
-+}
-+
-+void proto_reg_handoff_gsm_abis_ip(void)
-+{
-+ dissector_handle_t abisip_handle;
-+
-+ sub_handles[SUB_RSL] = find_dissector("gsm_abis_rsl");
-+ sub_handles[SUB_OML] = find_dissector("gsm_abis_oml");
-+
-+ abisip_handle = create_dissector_handle(dissect_abisip, proto_abisip);
-+ dissector_add("tcp.port", TCP_PORT_ABISIP_PRIM, abisip_handle);
-+ dissector_add("tcp.port", TCP_PORT_ABISIP_SEC, abisip_handle);
-+ dissector_add("tcp.port", TCP_PORT_ABISIP_INST, abisip_handle);
-+ dissector_add("udp.port", TCP_PORT_ABISIP_INST, abisip_handle);
-+}
diff --git a/wireshark/gsm_a_rr-rrlp.patch b/wireshark/gsm_a_rr-rrlp.patch
deleted file mode 100644
index e49e92e8b..000000000
--- a/wireshark/gsm_a_rr-rrlp.patch
+++ /dev/null
@@ -1,179 +0,0 @@
-Index: wireshark/epan/dissectors/packet-gsm_a_rr.c
-===================================================================
---- wireshark.orig/epan/dissectors/packet-gsm_a_rr.c
-+++ wireshark/epan/dissectors/packet-gsm_a_rr.c
-@@ -301,9 +302,9 @@
- { 0x00, "Extended Measurement Results" }, /* [3] 10.5.2.45 Extended Measurement Results */
- { 0x00, "Extended Measurement Frequency List" }, /* [3] 10.5.2.46 Extended Measurement Frequency List */
- { 0x00, "Suspension Cause" }, /* [3] 10.5.2.47 */
--/* [3] 10.5.2.48 APDU ID
-- * [3] 10.5.2.49 APDU Flags
-- * [3] 10.5.2.50 APDU Data */
-+ { 0x00, "APDU ID" }, /* [3] 10.5.2.48 APDU ID */
-+ { 0x00, "APDU Flags" }, /* [3] 10.5.2.49 APDU Flags */
-+ { 0x00, "APDU Data" }, /* [3] 10.5.2.50 APDU Data */
- { 0x00, "Handover to UTRAN Command" }, /* [3] 10.5.2.51 Handover To UTRAN Command */
- /* [3] 10.5.2.52 Handover To cdma2000 Command
- * [3] 10.5.2.53 (void)
-@@ -497,6 +498,9 @@
- static int hf_gsm_a_rr_chnl_needed_ch3 = -1;
- static int hf_gsm_a_rr_chnl_needed_ch4 = -1;
- static int hf_gsm_a_rr_suspension_cause = -1;
-+static int hf_gsm_a_rr_apdu_id = -1;
-+static int hf_gsm_a_rr_apdu_flags = -1;
-+static int hf_gsm_a_rr_apdu_data = -1;
- static int hf_gsm_a_rr_set_of_amr_codec_modes_v1_b8 = -1;
- static int hf_gsm_a_rr_set_of_amr_codec_modes_v1_b7 = -1;
- static int hf_gsm_a_rr_set_of_amr_codec_modes_v1_b6 = -1;
-@@ -691,7 +695,7 @@
- static char a_bigbuf[1024];
-
- static dissector_handle_t data_handle;
--
-+static dissector_handle_t rrlp_dissector;
-
-
- #define NUM_GSM_RR_ELEM (sizeof(gsm_rr_elem_strings)/sizeof(value_string))
-@@ -6224,9 +6228,50 @@
- }
- /*
- * [3] 10.5.2.48 APDU ID
-+ */
-+static const value_string gsm_a_rr_apdu_id_vals[] = {
-+ { 0, "RRLP (GSM 04.31) LCS" },
-+ { 0, NULL },
-+};
-+static guint16
-+de_rr_apdu_id(tvbuff_t *tvb, proto_tree *tree, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_)
-+{
-+ proto_tree_add_item(tree, hf_gsm_a_rr_apdu_id, tvb, offset, 1, FALSE);
-+
-+ return 0;
-+}
-+
-+/*
- * [3] 10.5.2.49 APDU Flags
-+ */
-+static const value_string gsm_a_rr_apdu_flags_vals[] = {
-+ { 1, "Last or only segment" },
-+ { 2, "First or only segment" },
-+ { 0, NULL },
-+};
-+static guint16
-+de_rr_apdu_flags(tvbuff_t *tvb, proto_tree *tree, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_)
-+{
-+ proto_tree_add_item(tree, hf_gsm_a_rr_apdu_flags, tvb, offset, 1, FALSE);
-+
-+ return 1;
-+}
-+
-+/*
- * [3] 10.5.2.50 APDU Data
- */
-+static guint16
-+de_rr_apdu_data(tvbuff_t *tvb, proto_tree *tree, guint32 offset, guint len, gchar *add_string _U_, int string_len _U_)
-+{
-+ tvbuff_t *sub_tvb;
-+ static packet_info p_info;
-+
-+ sub_tvb = tvb_new_subset(tvb, offset, len, len);
-+
-+ call_dissector(rrlp_dissector, sub_tvb, &p_info, tree);
-+
-+ return len;
-+}
-
- /*
- * [3] 10.5.2.51 Handover To UTRAN Command
-@@ -6466,9 +6511,9 @@
- de_rr_ext_meas_result, /* [3] 10.5.2.45 Extended Measurement Results */
- de_rr_ext_meas_freq_list, /* [3] 10.5.2.46 Extended Measurement Frequency List */
- de_rr_sus_cau, /* [3] 10.5.2.47 Suspension Cause */
--/* [3] 10.5.2.48 APDU ID
-- * [3] 10.5.2.49 APDU Flags
-- * [3] 10.5.2.50 APDU Data */
-+ de_rr_apdu_id, /* [3] 10.5.2.48 APDU ID */
-+ de_rr_apdu_flags, /* [3] 10.5.2.49 APDU Flags */
-+ de_rr_apdu_data, /* [3] 10.5.2.50 APDU Data */
- de_rr_ho_to_utran_cmd, /* [3] 10.5.2.51 Handover To UTRAN Command */
- /* [3] 10.5.2.52 Handover To cdma2000 Command
- * [3] 10.5.2.53 (void)
-@@ -7864,6 +7909,24 @@
- }
-
- /*
-+ * 9.1.53 Application Information
-+ */
-+static void
-+dtap_rr_app_inf(tvbuff_t *tvb, proto_tree *tree, guint32 offset, guint len)
-+{
-+ guint32 curr_offset;
-+ guint32 consumed;
-+ guint curr_len;
-+
-+ curr_offset = offset;
-+ curr_len = len;
-+
-+ ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_APDU_ID);
-+ ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_APDU_FLAGS);
-+ ELEM_MAND_LV(GSM_A_PDU_TYPE_RR, DE_RR_APDU_DATA, NULL);
-+}
-+
-+/*
- * [4] 9.1.54 Measurement Information
- */
- static const value_string gsm_a_rr_3g_wait_vals[] = {
-@@ -8386,7 +8449,7 @@
-
- NULL, /* UTRAN Classmark Change/Handover To UTRAN Command */ /* spec conflict */
-
-- NULL, /* Application Information */
-+ dtap_rr_app_inf, /* Application Information */
-
- NULL, /* NONE */
- };
-@@ -9155,6 +9219,21 @@
- FT_UINT8,BASE_DEC, VALS(gsm_a_rr_suspension_cause_vals), 0x0,
- NULL, HFILL }
- },
-+ { &hf_gsm_a_rr_apdu_id,
-+ { "APDU ID","gsm_a.rr.apdu_id",
-+ FT_UINT8,BASE_HEX, VALS(gsm_a_rr_apdu_id_vals), 0x0f,
-+ NULL, HFILL }
-+ },
-+ { &hf_gsm_a_rr_apdu_flags,
-+ { "APDU Flags","gsm_a.rr.apdu_flags",
-+ FT_UINT8,BASE_HEX, VALS(gsm_a_rr_apdu_flags_vals), 0xf0,
-+ NULL, HFILL }
-+ },
-+ { &hf_gsm_a_rr_apdu_data,
-+ { "APDU Data","gsm_a.rr.apdu_data",
-+ FT_BYTES,BASE_HEX, NULL, 0x00,
-+ NULL, HFILL }
-+ },
- { &hf_gsm_a_rr_set_of_amr_codec_modes_v1_b8,
- { "12,2 kbit/s codec rate", "gsm_a.rr.set_of_amr_codec_modes_v1b8",
- FT_BOOLEAN,8, TFS(&gsm_a_rr_set_of_amr_codec_modes), 0x80,
-@@ -10157,4 +10236,5 @@
- data_handle = find_dissector("data");
- rrc_irat_ho_info_handle = find_dissector("rrc.irat.irat_ho_info");
- rrc_irat_ho_to_utran_cmd_handle = find_dissector("rrc.irat.ho_to_utran_cmd");
-+ rrlp_dissector = find_dissector("rrlp");
- }
-Index: wireshark/epan/dissectors/packet-gsm_a_common.h
-===================================================================
---- wireshark.orig/epan/dissectors/packet-gsm_a_common.h
-+++ wireshark/epan/dissectors/packet-gsm_a_common.h
-@@ -1101,9 +1101,9 @@
- DE_RR_EXT_MEAS_RESULT, /* [3] 10.5.2.45 Extended Measurement Results */
- DE_RR_EXT_MEAS_FREQ_LIST, /* [3] 10.5.2.46 Extended Measurement Frequency List */
- DE_RR_SUS_CAU, /* [3] 10.5.2.47 Suspension Cause */
--/* [3] 10.5.2.48 APDU ID
-- * [3] 10.5.2.49 APDU Flags
-- * [3] 10.5.2.50 APDU Data */
-+ DE_RR_APDU_ID, /* [3] 10.5.2.48 APDU ID */
-+ DE_RR_APDU_FLAGS, /* [3] 10.5.2.49 APDU Flags */
-+ DE_RR_APDU_DATA, /* [3] 10.5.2.50 APDU Data */
- DE_RR_HO_TO_UTRAN_CMD, /* [3] 10.5.2.51 Handover To UTRAN Command */
- /* [3] 10.5.2.52 Handover To cdma2000 Command
- * [3] 10.5.2.53 (void)
diff --git a/wireshark/rsl-ipaccess.patch b/wireshark/rsl-ipaccess.patch
index 365983b57..36c09c57b 100644
--- a/wireshark/rsl-ipaccess.patch
+++ b/wireshark/rsl-ipaccess.patch
@@ -1,14 +1,14 @@
Index: wireshark/epan/dissectors/packet-rsl.c
===================================================================
---- wireshark.orig/epan/dissectors/packet-rsl.c
-+++ wireshark/epan/dissectors/packet-rsl.c
+--- wireshark.orig/epan/dissectors/packet-rsl.c 2009-10-21 23:03:41.000000000 +0200
++++ wireshark/epan/dissectors/packet-rsl.c 2009-10-22 10:02:51.000000000 +0200
@@ -2,6 +2,7 @@
* Routines for Radio Signalling Link (RSL) dissection.
*
* Copyright 2007, Anders Broman <anders.broman@ericsson.com>
+ * Copyright 2009, Harald Welte <laforge@gnumonks.org>
*
- * $Id: packet-rsl.c 29344 2009-08-09 07:36:13Z krj $
+ * $Id: packet-rsl.c 29944 2009-09-16 13:39:37Z morriss $
*
@@ -44,6 +45,8 @@
#include <epan/lapd_sapi.h>
@@ -19,22 +19,32 @@ Index: wireshark/epan/dissectors/packet-rsl.c
/* Initialize the protocol and registered fields */
static int proto_rsl = -1;
-@@ -116,6 +119,14 @@
+@@ -117,6 +120,24 @@
static int hf_rsl_rtd = -1;
static int hf_rsl_delay_ind = -1;
static int hf_rsl_tfo = -1;
-+static int hf_rsl_speech_mode = -1;
++static int hf_rsl_speech_mode_s = -1;
++static int hf_rsl_speech_mode_m = -1;
+static int hf_rsl_conn_stat = -1;
+static int hf_rsl_conn_id = -1;
+static int hf_rsl_rtp_payload = -1;
++static int hf_rsl_rtp_csd_fmt_d = -1;
++static int hf_rsl_rtp_csd_fmt_ir = -1;
+static int hf_rsl_local_port = -1;
+static int hf_rsl_remote_port = -1;
+static int hf_rsl_local_ip = -1;
+static int hf_rsl_remote_ip = -1;
++static int hf_rsl_cstat_tx_pkts = -1;
++static int hf_rsl_cstat_tx_octs = -1;
++static int hf_rsl_cstat_rx_pkts = -1;
++static int hf_rsl_cstat_rx_octs = -1;
++static int hf_rsl_cstat_lost_pkts = -1;
++static int hf_rsl_cstat_ia_jitter = -1;
++static int hf_rsl_cstat_avg_tx_dly = -1;
/* Initialize the subtree pointers */
static int ett_rsl = -1;
-@@ -173,6 +184,15 @@
+@@ -174,6 +195,15 @@
static int ett_ie_meas_res_no = -1;
static int ett_ie_message_id = -1;
static int ett_ie_sys_info_type = -1;
@@ -50,7 +60,7 @@ Index: wireshark/epan/dissectors/packet-rsl.c
proto_tree *top_tree;
dissector_handle_t gsm_a_ccch_handle;
-@@ -208,8 +228,11 @@
+@@ -209,8 +239,11 @@
{ 0x06, "Common Channel Management messages" },
{ 0x08, "TRX Management messages" },
{ 0x16, "Location Services messages" },
@@ -62,7 +72,7 @@ Index: wireshark/epan/dissectors/packet-rsl.c
/*
* 9.2 MESSAGE TYPE
*/
-@@ -276,6 +299,49 @@
+@@ -277,6 +310,49 @@
/* 0 1 - - - - - - Location Services messages: */
#define RSL_MSG_LOC_INF 65 /* 8.7.1 */
@@ -112,7 +122,7 @@ Index: wireshark/epan/dissectors/packet-rsl.c
static const value_string rsl_msg_type_vals[] = {
/* 0 0 0 0 - - - - Radio Link Layer Management messages: */
-@@ -338,6 +404,26 @@
+@@ -339,6 +415,26 @@
{ 0x3f, "TFO MODification REQuest" }, /* 8.4.31 */
/* 0 1 - - - - - - Location Services messages: */
{ 0x41, "Location Information" }, /* 8.7.1 */
@@ -126,20 +136,20 @@ Index: wireshark/epan/dissectors/packet-rsl.c
+ { 0x60, "ip.access MEASurement PREPROCessing DeFauLT" },
+ { 0x61, "ip.access HANDOover CANDidate ENQuiry" },
+ { 0x62, "ip.access HANDOover CANDidate RESPonse" },
-+ { 0x70, "ip.access BIND" },
-+ { 0x71, "ip.access BIND ACK" },
-+ { 0x72, "ip.access BIND NACK" },
-+ { 0x73, "ip.access CONNECT" },
-+ { 0x74, "ip.access CONNECT ACK" },
-+ { 0x75, "ip.access CONNECT NACK" },
-+ { 0x76, "ip.access DISCONNECT INDication" },
-+ { 0x77, "ip.access DISCONNECT" },
-+ { 0x78, "ip.access DISCONNECT ACK" },
-+ { 0x79, "ip.access DISCONNECT NACK" },
++ { 0x70, "ip.access CRCX" },
++ { 0x71, "ip.access CRCX ACK" },
++ { 0x72, "ip.access CRCX NACK" },
++ { 0x73, "ip.access MDCX" },
++ { 0x74, "ip.access MDCX ACK" },
++ { 0x75, "ip.access MDCX NACK" },
++ { 0x76, "ip.access DLCX INDication" },
++ { 0x77, "ip.access DLCX" },
++ { 0x78, "ip.access DLCX ACK" },
++ { 0x79, "ip.access DLCX NACK" },
{ 0, NULL }
};
-@@ -371,10 +457,10 @@
+@@ -372,10 +468,10 @@ static const value_string rsl_msg_type_vals[] = {
#define RSL_IE_MESSAGE_ID 28
#define RSL_IE_SYS_INFO_TYPE 30
@@ -154,7 +164,7 @@ Index: wireshark/epan/dissectors/packet-rsl.c
#define RSL_IE_FULL_IMM_ASS_INF 35
#define RSL_IE_SMSCB_INF 36
#define RSL_IE_FULL_MS_TIMING_OFFSET 37
-@@ -477,6 +563,24 @@
+@@ -478,6 +574,24 @@
Not used
*/
@@ -179,7 +189,7 @@ Index: wireshark/epan/dissectors/packet-rsl.c
{ 0, NULL }
};
-@@ -513,6 +617,95 @@
+@@ -514,6 +628,96 @@
{ 0, NULL }
};
@@ -265,6 +275,7 @@ Index: wireshark/epan/dissectors/packet-rsl.c
+ [RSL_IE_IPAC_REMOTE_IP] = { TLV_TYPE_FIXED, 4 },
+ [RSL_IE_IPAC_REMOTE_PORT] = { TLV_TYPE_FIXED, 2 },
+ [RSL_IE_IPAC_LOCAL_IP] = { TLV_TYPE_FIXED, 4 },
++ [RSL_IE_IPAC_CONN_STAT] = { TLV_TYPE_TLV, 0 },
+ [RSL_IE_IPAC_LOCAL_PORT] = { TLV_TYPE_FIXED, 2 },
+ [RSL_IE_IPAC_SPEECH_MODE] = { TLV_TYPE_TV, 0 },
+ [RSL_IE_IPAC_CONN_ID] = { TLV_TYPE_FIXED, 2 },
@@ -275,7 +286,7 @@ Index: wireshark/epan/dissectors/packet-rsl.c
/* 9.3.1 Channel number 9.3.1 M TV 2 */
static int
dissect_rsl_ie_ch_no(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, int offset, gboolean is_mandatory)
-@@ -2043,7 +2236,6 @@
+@@ -2044,7 +2248,6 @@
proto_item_set_len(ti, length+2);
proto_tree_add_item(ie_tree, hf_rsl_ie_length, tvb, offset, 1, FALSE);
@@ -283,244 +294,10 @@ Index: wireshark/epan/dissectors/packet-rsl.c
/* Received Message */
offset = dissct_rsl_msg(tvb, pinfo, ie_tree, offset);
-@@ -2907,13 +3099,425 @@
- return ie_offset + length;
+@@ -2909,12 +3112,183 @@
}
-+#if 0
-+static int
-+dissect_rsl_ipac_ie_f8(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, int offset, gboolean is_mandatory)
-+{
-+ proto_item *ti;
-+ proto_tree *ie_tree;
-+ guint8 ie_id;
-+
-+ if (is_mandatory == FALSE) {
-+ ie_id = tvb_get_guint8(tvb, offset);
-+ if (ie_id != 0xf8)
-+ return offset;
-+ }
-+
-+ ti = proto_tree_add_text(tree, tvb, offset, 0, "Unknown 0xf8 IE");
-+ ie_tree = proto_item_add_subtree(ti, ett_ie_f8);
-+
-+ /* Element identifier */
-+ proto_tree_add_item(ie_tree, hf_rsl_ie_id, tvb, offset, 1, FALSE);
-+ offset++;
-+ /* Fixed Length */
-+ proto_item_set_len(ti, 3);
-+
-+ proto_tree_add_item(ie_tree, hf_rsl_f8, tvb, offset, 2, FALSE);
-+ offset += 2;
-+
-+ return offset;
-+}
-+
-+static int
-+dissect_rsl_ipac_ie_local_port(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, int offset, gboolean is_mandatory)
-+{
-+ proto_item *ti;
-+ proto_tree *ie_tree;
-+ guint8 ie_id;
-+
-+ if (is_mandatory == FALSE) {
-+ ie_id = tvb_get_guint8(tvb, offset);
-+ if (ie_id != RSL_IE_IPAC_LOCAL_PORT)
-+ return offset;
-+ }
-+
-+ ti = proto_tree_add_text(tree, tvb, offset, 0, "Local RTP Port IE");
-+ ie_tree = proto_item_add_subtree(ti, ett_ie_local_port);
-+
-+ /* Element identifier */
-+ proto_tree_add_item(ie_tree, hf_rsl_ie_id, tvb, offset, 1, FALSE);
-+ offset++;
-+ /* Fixed Length */
-+ proto_item_set_len(ti, 3);
-+
-+ proto_tree_add_item(ie_tree, hf_rsl_local_port, tvb, offset, 2, FALSE);
-+ offset += 2;
-+
-+ return offset;
-+}
-+
-+static int
-+dissect_rsl_ipac_ie_remote_port(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, int offset, gboolean is_mandatory)
-+{
-+ proto_item *ti;
-+ proto_tree *ie_tree;
-+ guint8 ie_id;
-+
-+ if (is_mandatory == FALSE) {
-+ ie_id = tvb_get_guint8(tvb, offset);
-+ if (ie_id != RSL_IE_IPAC_REMOTE_PORT)
-+ return offset;
-+ }
-+
-+ ti = proto_tree_add_text(tree, tvb, offset, 0, "Remote RTP Port IE");
-+ ie_tree = proto_item_add_subtree(ti, ett_ie_remote_port);
-+
-+ /* Element identifier */
-+ proto_tree_add_item(ie_tree, hf_rsl_ie_id, tvb, offset, 1, FALSE);
-+ offset++;
-+ /* Fixed Length */
-+ proto_item_set_len(ti, 3);
-+
-+ proto_tree_add_uint(ie_tree, hf_rsl_remote_port, tvb, offset, 2, FALSE);
-+ offset += 2;
-+
-+ return offset;
-+}
-+
-+static int
-+dissect_rsl_ipac_ie_local_ip(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, int offset, gboolean is_mandatory)
-+{
-+ proto_item *ti;
-+ proto_tree *ie_tree;
-+ guint8 ie_id;
-+ guint32 ip;
-+
-+ if (is_mandatory == FALSE) {
-+ ie_id = tvb_get_guint8(tvb, offset);
-+ if (ie_id != RSL_IE_IPAC_LOCAL_IP)
-+ return offset;
-+ }
-+
-+ ti = proto_tree_add_text(tree, tvb, offset, 0, "Local IP Address IE");
-+ ie_tree = proto_item_add_subtree(ti, ett_ie_local_ip);
-+
-+ /* Element identifier */
-+ proto_tree_add_item(ie_tree, hf_rsl_ie_id, tvb, offset, 1, FALSE);
-+ offset++;
-+ /* Fixed Length */
-+ proto_item_set_len(ti, 5);
-+
-+ ip = tvb_get_ipv4(tvb, offset);
-+ proto_tree_add_ipv4(ie_tree, hf_rsl_local_ip, tvb, offset, 4, ip);
-+ offset += 4;
-+
-+ return offset;
-+}
-+
-+static int
-+dissect_rsl_ipac_ie_remote_ip(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, int offset, gboolean is_mandatory)
-+{
-+ proto_item *ti;
-+ proto_tree *ie_tree;
-+ guint8 ie_id;
-+ guint32 ip;
-+
-+ if (is_mandatory == FALSE) {
-+ ie_id = tvb_get_guint8(tvb, offset);
-+ if (ie_id != RSL_IE_IPAC_REMOTE_IP)
-+ return offset;
-+ }
-+
-+ ti = proto_tree_add_text(tree, tvb, offset, 0, "Remote IP Address IE");
-+ ie_tree = proto_item_add_subtree(ti, ett_ie_remote_ip);
-+
-+ /* Element identifier */
-+ proto_tree_add_item(ie_tree, hf_rsl_ie_id, tvb, offset, 1, FALSE);
-+ offset++;
-+ /* Fixed Length */
-+ proto_item_set_len(ti, 5);
-+
-+ ip = tvb_get_ipv4(tvb, offset);
-+ proto_tree_add_ipv4(ie_tree, hf_rsl_remote_ip, tvb, offset, 4, ip);
-+ offset += 4;
-+
-+ return offset;
-+}
-+
-+static int
-+dissect_rsl_ipac_ie_f6(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, int offset, gboolean is_mandatory)
-+{
-+ proto_item *ti;
-+ proto_tree *ie_tree;
-+ guint8 length;
-+ guint8 ie_id;
-+
-+ if (is_mandatory == FALSE) {
-+ ie_id = tvb_get_guint8(tvb, offset);
-+ if (ie_id != 0xf6)
-+ return offset;
-+ }
-+
-+ ti = proto_tree_add_text(tree, tvb, offset, 0, "Unknown 0xf6 IE");
-+ ie_tree = proto_item_add_subtree(ti, ett_ie_f6);
-+
-+ /* Element identifier */
-+ proto_tree_add_item(ie_tree, hf_rsl_ie_id, tvb, offset, 1, FALSE);
-+ offset++;
-+
-+ /* Length */
-+ length = tvb_get_guint8(tvb, offset);
-+ offset++;
-+ proto_item_set_len(ti, length+2);
-+
-+ proto_tree_add_bytes(ie_tree, hf_rsl_f6, tvb, offset, length,
-+ tvb_get_ptr(tvb, offset, length));
-+ offset += length;
-+
-+ return offset;
-+}
-+
-+static int
-+dissect_rsl_ipac_ie_f4(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, int offset, gboolean is_mandatory)
-+{
-+ proto_item *ti;
-+ proto_tree *ie_tree;
-+ guint8 ie_id;
-+
-+ if (is_mandatory == FALSE) {
-+ ie_id = tvb_get_guint8(tvb, offset);
-+ if (ie_id != 0xf4)
-+ return offset;
-+ }
-+
-+ ti = proto_tree_add_text(tree, tvb, offset, 0, "Unknown 0xf4 IE");
-+ ie_tree = proto_item_add_subtree(ti, ett_ie_f4);
-+
-+ /* Element identifier */
-+ proto_tree_add_item(ie_tree, hf_rsl_ie_id, tvb, offset, 1, FALSE);
-+ offset++;
-+ /* Fixed Length */
-+ proto_item_set_len(ti, 2);
-+ proto_tree_add_item(ie_tree, hf_rsl_f4, tvb, offset, 1, FALSE);
-+ offset++;
-+
-+ return offset;
-+}
-+
-+static int
-+dissect_rsl_ipac_ie_fc(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, int offset, gboolean is_mandatory)
-+{
-+ proto_item *ti;
-+ proto_tree *ie_tree;
-+ guint8 ie_id;
-+
-+ if (is_mandatory == FALSE) {
-+ ie_id = tvb_get_guint8(tvb, offset);
-+ if (ie_id != 0xfc)
-+ return offset;
-+ }
-+
-+ ti = proto_tree_add_text(tree, tvb, offset, 0, "Unknown 0xfc IE");
-+ ie_tree = proto_item_add_subtree(ti, ett_ie_fc);
-+
-+ /* Element identifier */
-+ proto_tree_add_item(ie_tree, hf_rsl_ie_id, tvb, offset, 1, FALSE);
-+ offset++;
-+ /* Fixed Length */
-+ proto_item_set_len(ti, 2);
-+ proto_tree_add_item(ie_tree, hf_rsl_fc, tvb, offset, 1, FALSE);
-+ offset++;
-+
-+ return offset;
-+}
-+#endif
-+
-+static int
+ static int
+dissct_rsl_ipaccess_msg(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset)
+{
+ guint8 msg_type;
@@ -531,6 +308,7 @@ Index: wireshark/epan/dissectors/packet-rsl.c
+ msg_type = tvb_get_guint8(tvb, offset)&0x7f;
+ offset++;
+
++#if 0
+ switch (msg_type) {
+ case RSL_MSG_TYPE_IPAC_BIND:
+ case RSL_MSG_TYPE_IPAC_BIND_ACK:
@@ -551,41 +329,9 @@ Index: wireshark/epan/dissectors/packet-rsl.c
+ /* Channel number 9.3.1 M TV 2 */
+ offset = dissect_rsl_ie_ch_no(tvb, pinfo, tree, offset, TRUE);
+ break;
-+#if 0
-+ /* Channel number 9.3.1 M TV 2 */
-+ offset = dissect_rsl_ie_ch_no(tvb, pinfo, tree, offset, TRUE);
-+ offset = dissect_rsl_ipac_ie_f8(tvb, pinfo, tree, offset, TRUE);
-+ offset = dissect_rsl_ipac_ie_local_port(tvb, pinfo, tree, offset, TRUE);
-+ offset = dissect_rsl_ipac_ie_local_ip(tvb, pinfo, tree, offset, TRUE);
-+ offset = dissect_rsl_ipac_ie_fc(tvb, pinfo, tree, offset, TRUE);
-+ break;
-+ /* Channel number 9.3.1 M TV 2 */
-+ offset = dissect_rsl_ie_ch_no(tvb, pinfo, tree, offset, TRUE);
-+ break;
-+ /* Channel number 9.3.1 M TV 2 */
-+ offset = dissect_rsl_ie_ch_no(tvb, pinfo, tree, offset, TRUE);
-+ offset = dissect_rsl_ipac_ie_remote_ip(tvb, pinfo, tree, offset, TRUE);
-+ offset = dissect_rsl_ipac_ie_remote_port(tvb, pinfo, tree, offset, TRUE);
-+ offset = dissect_rsl_ipac_ie_f4(tvb, pinfo, tree, offset, TRUE);
-+ offset = dissect_rsl_ipac_ie_fc(tvb, pinfo, tree, offset, TRUE);
-+ break;
-+ /* Channel number 9.3.1 M TV 2 */
-+ offset = dissect_rsl_ie_ch_no(tvb, pinfo, tree, offset, TRUE);
-+ offset = dissect_rsl_ipac_ie_f8(tvb, pinfo, tree, offset, TRUE);
-+ break;
-+ /* Channel number 9.3.1 M TV 2 */
-+ offset = dissect_rsl_ie_ch_no(tvb, pinfo, tree, offset, TRUE);
-+ break;
-+ /* Channel number 9.3.1 M TV 2 */
-+ offset = dissect_rsl_ie_ch_no(tvb, pinfo, tree, offset, TRUE);
-+ offset = dissect_rsl_ipac_ie_f8(tvb, pinfo, tree, offset, TRUE);
-+ offset = dissect_rsl_ipac_ie_f6(tvb, pinfo, tree, offset, TRUE);
-+ /* Cause 9.3.26 M TLV >=3 */
-+ offset = dissect_rsl_ie_cause(tvb, pinfo, tree, offset, TRUE);
-+ break;
-+#endif
+ }
-+ /* parse remaining TLV attributes */
++#endif
++ /* parse TLV attributes */
+ while (tvb_reported_length_remaining(tvb, offset) != 0) {
+ guint8 tag;
+ unsigned int len, hlen, len_len;
@@ -635,13 +381,13 @@ Index: wireshark/epan/dissectors/packet-rsl.c
+
+ switch (tag) {
+ case RSL_IE_CH_NO:
-+ dissect_rsl_ie_ch_no(tvb, pinfo, tree, offset, TRUE);
++ dissect_rsl_ie_ch_no(tvb, pinfo, ie_tree, offset, FALSE);
+ break;
+ case RSL_IE_FRAME_NO:
-+ dissect_rsl_ie_frame_no(tvb, pinfo, ie_tree, offset, TRUE);
++ dissect_rsl_ie_frame_no(tvb, pinfo, ie_tree, offset, FALSE);
+ break;
+ case RSL_IE_MS_POW:
-+ dissect_rsl_ie_ms_pow(tvb, pinfo, ie_tree, offset, TRUE);
++ dissect_rsl_ie_ms_pow(tvb, pinfo, ie_tree, offset, FALSE);
+ break;
+ case RSL_IE_IPAC_REMOTE_IP:
+ proto_tree_add_item(ie_tree, hf_rsl_remote_ip, tvb,
@@ -662,7 +408,9 @@ Index: wireshark/epan/dissectors/packet-rsl.c
+ local_port = tvb_get_ntohs(tvb, offset);
+ break;
+ case RSL_IE_IPAC_SPEECH_MODE:
-+ proto_tree_add_item(ie_tree, hf_rsl_speech_mode, tvb,
++ proto_tree_add_item(ie_tree, hf_rsl_speech_mode_s, tvb,
++ offset, len, FALSE);
++ proto_tree_add_item(ie_tree, hf_rsl_speech_mode_m, tvb,
+ offset, len, FALSE);
+ break;
+ case RSL_IE_IPAC_RTP_PAYLOAD:
@@ -670,10 +418,32 @@ Index: wireshark/epan/dissectors/packet-rsl.c
+ proto_tree_add_item(ie_tree, hf_rsl_rtp_payload, tvb,
+ offset, len, FALSE);
+ break;
++ case RSL_IE_IPAC_RTP_CSD_FMT:
++ proto_tree_add_item(ie_tree, hf_rsl_rtp_csd_fmt_d, tvb,
++ offset, len, FALSE);
++ proto_tree_add_item(ie_tree, hf_rsl_rtp_csd_fmt_ir, tvb,
++ offset, len, FALSE);
++ break;
+ case RSL_IE_IPAC_CONN_ID:
+ proto_tree_add_item(ie_tree, hf_rsl_conn_id, tvb,
+ offset, len, FALSE);
+ break;
++ case RSL_IE_IPAC_CONN_STAT:
++ proto_tree_add_item(ie_tree, hf_rsl_cstat_tx_pkts, tvb,
++ offset, 4, FALSE);
++ proto_tree_add_item(ie_tree, hf_rsl_cstat_tx_octs, tvb,
++ offset+4, 4, FALSE);
++ proto_tree_add_item(ie_tree, hf_rsl_cstat_rx_pkts, tvb,
++ offset+8, 4, FALSE);
++ proto_tree_add_item(ie_tree, hf_rsl_cstat_rx_octs, tvb,
++ offset+12, 4, FALSE);
++ proto_tree_add_item(ie_tree, hf_rsl_cstat_lost_pkts, tvb,
++ offset+16, 4, FALSE);
++ proto_tree_add_item(ie_tree, hf_rsl_cstat_ia_jitter, tvb,
++ offset+20, 4, FALSE);
++ proto_tree_add_item(ie_tree, hf_rsl_cstat_avg_tx_dly, tvb,
++ offset+24, 4, FALSE);
++ break;
+ }
+ offset += len;
+ }
@@ -693,7 +463,7 @@ Index: wireshark/epan/dissectors/packet-rsl.c
+ return offset;
+}
+
- static int
++static int
dissct_rsl_msg(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset)
{
- guint8 msg_type;
@@ -710,7 +480,7 @@ Index: wireshark/epan/dissectors/packet-rsl.c
offset++;
switch (msg_type){
-@@ -3481,6 +4085,18 @@
+@@ -3482,6 +3856,18 @@
/* LLP APDU 9.3.58 M LV 2-N */
offset = dissect_rsl_ie_llp_apdu(tvb, pinfo, tree, offset, TRUE);
break;
@@ -729,7 +499,48 @@ Index: wireshark/epan/dissectors/packet-rsl.c
default:
break;
}
-@@ -3515,7 +4131,6 @@
+@@ -3489,6 +3875,40 @@
+ return offset;
+
+ }
++
++static const value_string rsl_ipacc_spm_s_vals[] = {
++ { 0, "GSM FR codec (GSM type 1, FS)" },
++ { 1, "GSM EFR codec (GSM type 2, FS)" },
++ { 2, "GSM AMR/FR codec (GSM type 3, FS)" },
++ { 3, "GSM HR codec (GSM type 1, HS)" },
++ { 5, "GSM AMR/HR codec (GSM type 3, HS)" },
++ { 0xf, "As specified by RTP Payload Type IE" },
++ { 0, NULL }
++};
++
++static const value_string rsl_ipacc_spm_m_vals[] = {
++ { 0, "Send and Receive" },
++ { 1, "Receive Only" },
++ { 2, "Send Only" },
++ { 0, NULL }
++};
++
++static const value_string rsl_ipacc_rtp_csd_fmt_d_vals[] = {
++ { 0, "External TRAU format" },
++ { 1, "Non-TRAU Packed format" },
++ { 2, "TRAU within the BTS" },
++ { 3, "IWF-Free BTS-BTS Data" },
++ { 0, NULL }
++};
++
++static const value_string rsl_ipacc_rtp_csd_fmt_ir_vals[] = {
++ { 0, "8kb/s" },
++ { 1, "16kb/s" },
++ { 2, "32kb/s" },
++ { 3, "64kb/s" },
++ { 0, NULL }
++};
++
+ static void
+ dissect_rsl(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+ {
+@@ -3516,7 +3936,6 @@
/* 9.1 Message discriminator */
proto_tree_add_item(rsl_tree, hf_rsl_msg_dsc, tvb, offset, 1, FALSE);
proto_tree_add_item(rsl_tree, hf_rsl_T_bit, tvb, offset, 1, FALSE);
@@ -737,17 +548,23 @@ Index: wireshark/epan/dissectors/packet-rsl.c
offset = dissct_rsl_msg(tvb, pinfo, rsl_tree, offset);
-@@ -3881,6 +4496,42 @@
+@@ -3886,6 +4305,86 @@
FT_UINT8, BASE_DEC, VALS(rsl_emlpp_prio_vals), 0x03,
NULL, HFILL }
},
-+ { &hf_rsl_speech_mode,
-+ { "ip.access Speech Mode", "rsl.ipacc.speech_mode",
-+ FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }
++ { &hf_rsl_speech_mode_s,
++ { "ip.access Speech Mode S", "rsl.ipacc.speech_mode_s",
++ FT_UINT8, BASE_HEX, VALS(rsl_ipacc_spm_s_vals),
++ 0xf, NULL, HFILL }
++ },
++ { &hf_rsl_speech_mode_m,
++ { "ip.access Speech Mode M", "rsl.ipacc.speech_mode_m",
++ FT_UINT8, BASE_HEX, VALS(rsl_ipacc_spm_m_vals),
++ 0xf0, NULL, HFILL }
+ },
+ { &hf_rsl_conn_stat,
+ { "ip.access Connection Statistics","rsl.ipacc.conn_stat",
-+ FT_BYTES, BASE_HEX, NULL, 0x0, NULL, HFILL }
++ FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }
+ },
+ { &hf_rsl_conn_id,
+ { "ip.access Connection ID", "rsl.ipacc.conn_id",
@@ -757,6 +574,16 @@ Index: wireshark/epan/dissectors/packet-rsl.c
+ { "ip.access RTP Payload Type", "rsl.ipacc.rtp_payload",
+ FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }
+ },
++ { &hf_rsl_rtp_csd_fmt_d,
++ { "ip.access RTP CSD Format D", "rsl.ipacc.rtp_csd_fmt_d",
++ FT_UINT8, BASE_HEX, VALS(rsl_ipacc_rtp_csd_fmt_d_vals),
++ 0x0f, NULL, HFILL },
++ },
++ { &hf_rsl_rtp_csd_fmt_ir,
++ { "ip.access RTP CSD Format IR", "rsl.ipacc.rtp_csd_fmt_ir",
++ FT_UINT8, BASE_HEX, VALS(rsl_ipacc_rtp_csd_fmt_ir_vals),
++ 0xf0, NULL, HFILL },
++ },
+ { &hf_rsl_local_port,
+ { "ip.access Local RTP Port", "rsl.ipacc.local_port",
+ FT_UINT16, BASE_DEC, NULL, 0x0,
@@ -777,10 +604,38 @@ Index: wireshark/epan/dissectors/packet-rsl.c
+ FT_IPv4, BASE_NONE, NULL, 0x0,
+ "ip.access Remote IP Address", HFILL },
+ },
++ { &hf_rsl_cstat_tx_pkts,
++ { "Packets Sent", "rsl.ipacc.cstat.tx_pkts",
++ FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL }
++ },
++ { &hf_rsl_cstat_tx_octs,
++ { "Octets Sent", "rsl.ipacc.cstat.tx_octets",
++ FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL }
++ },
++ { &hf_rsl_cstat_rx_pkts,
++ { "Packets Received", "rsl.ipacc.cstat.rx_pkts",
++ FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL }
++ },
++ { &hf_rsl_cstat_rx_octs,
++ { "Octets Received", "rsl.ipacc.cstat.rx_octets",
++ FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL }
++ },
++ { &hf_rsl_cstat_lost_pkts,
++ { "Packets Lost", "rsl.ipacc.cstat.lost_pkts",
++ FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL }
++ },
++ { &hf_rsl_cstat_ia_jitter,
++ { "Inter-arrival Jitter", "rsl.ipacc.cstat.ia_jitter",
++ FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL }
++ },
++ { &hf_rsl_cstat_avg_tx_dly,
++ { "Average Tx Delay", "rsl.ipacc.cstat.avg_tx_delay",
++ FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL }
++ },
};
static gint *ett[] = {
&ett_rsl,
-@@ -3938,6 +4589,14 @@
+@@ -3943,6 +4442,14 @@
&ett_ie_meas_res_no,
&ett_ie_message_id,
&ett_ie_sys_info_type,
diff --git a/wireshark/rsl-system_info.patch b/wireshark/rsl-system_info.patch
new file mode 100644
index 000000000..2945c6540
--- /dev/null
+++ b/wireshark/rsl-system_info.patch
@@ -0,0 +1,13 @@
+Index: wireshark/epan/dissectors/packet-rsl.c
+===================================================================
+--- wireshark.orig/epan/dissectors/packet-rsl.c
++++ wireshark/epan/dissectors/packet-rsl.c
+@@ -2291,7 +2291,7 @@
+
+ proto_tree_add_text(ie_tree, tvb,offset,length,"Layer 3 message");
+ next_tvb = tvb_new_subset(tvb, offset, length, length);
+- /* call_dissector(gsm_a_dtap_handle, next_tvb, pinfo, top_tree);*/
++ call_dissector(gsm_a_ccch_handle, next_tvb, pinfo, top_tree);
+
+ offset = offset + length;
+