diff options
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; + |