diff options
author | Pablo Neira Ayuso <pablo@netfilter.org> | 2011-05-23 19:01:34 +0200 |
---|---|---|
committer | Pablo Neira Ayuso <pablo@netfilter.org> | 2011-05-23 19:01:34 +0200 |
commit | 3056d012d3d5da853535488619f12beeea59a6a4 (patch) | |
tree | 4b5a511154e0241b9ca4724534995d443d9baac3 /src | |
parent | dd93bf46ed0c3c75c1e330db2197703c0b6f8cda (diff) | |
parent | f7a1bcce0cea50650517e305d59e674475f913d0 (diff) |
Merge branch 'master' of git.osmocom.org:libosmocore
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am | 4 | ||||
-rw-r--r-- | src/gsm/Makefile.am | 2 | ||||
-rw-r--r-- | src/gsm/abis_nm.c | 406 | ||||
-rw-r--r-- | src/gsmtap_util.c | 204 | ||||
-rw-r--r-- | src/socket.c | 179 | ||||
-rw-r--r-- | src/vty/telnet_interface.c | 12 |
6 files changed, 713 insertions, 94 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index e58bc286..1ae3cff8 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -2,7 +2,7 @@ SUBDIRS=. vty codec gsm # This is _NOT_ the library release version, it's an API version. # Please read Chapter 6 "Library interface versions" of the libtool documentation before making any modification -LIBVERSION=1:0:1 +LIBVERSION=2:0:0 INCLUDES = $(all_includes) -I$(top_srcdir)/include AM_CFLAGS = -fPIC -Wall @@ -11,7 +11,7 @@ lib_LTLIBRARIES = libosmocore.la libosmocore_la_SOURCES = timer.c select.c signal.c msgb.c bits.c \ bitvec.c statistics.c \ - write_queue.c utils.c \ + write_queue.c utils.c socket.c \ logging.c logging_syslog.c rate_ctr.c \ gsmtap_util.c crc16.c panic.c backtrace.c \ process.c conv.c application.c diff --git a/src/gsm/Makefile.am b/src/gsm/Makefile.am index fb4a8cb2..94f137e2 100644 --- a/src/gsm/Makefile.am +++ b/src/gsm/Makefile.am @@ -9,6 +9,6 @@ lib_LTLIBRARIES = libosmogsm.la libosmogsm_la_SOURCES = a5.c rxlev_stat.c tlv_parser.c comp128.c gsm_utils.c \ rsl.c gsm48.c gsm48_ie.c gsm0808.c \ - gprs_cipher_core.c gsm0480.c + gprs_cipher_core.c gsm0480.c abis_nm.c libosmogsm_la_LDFLAGS = -version-info $(LIBVERSION) libosmogsm_la_LIBADD = $(top_builddir)/src/libosmocore.la diff --git a/src/gsm/abis_nm.c b/src/gsm/abis_nm.c new file mode 100644 index 00000000..acea4ed5 --- /dev/null +++ b/src/gsm/abis_nm.c @@ -0,0 +1,406 @@ +/* GSM Network Management (OML) messages on the A-bis interface + * 3GPP TS 12.21 version 8.0.0 Release 1999 / ETSI TS 100 623 V8.0.0 */ + +/* (C) 2008-2011 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, see <http://www.gnu.org/licenses/>. + * + */ + +#include <stdint.h> +#include <osmocom/core/utils.h> +#include <osmocom/core/logging.h> +#include <osmocom/gsm/tlv.h> +#include <osmocom/gsm/protocol/gsm_12_21.h> +#include <osmocom/gsm/abis_nm.h> + +/* unidirectional messages from BTS to BSC */ +const enum abis_nm_msgtype abis_nm_reports[4] = { + NM_MT_SW_ACTIVATED_REP, + NM_MT_TEST_REP, + NM_MT_STATECHG_EVENT_REP, + NM_MT_FAILURE_EVENT_REP, +}; + +/* messages without ACK/NACK */ +const enum abis_nm_msgtype abis_nm_no_ack_nack[3] = { + NM_MT_MEAS_RES_REQ, + NM_MT_STOP_MEAS, + NM_MT_START_MEAS, +}; + +/* Messages related to software load */ +const enum abis_nm_msgtype abis_nm_sw_load_msgs[9] = { + NM_MT_LOAD_INIT_ACK, + NM_MT_LOAD_INIT_NACK, + NM_MT_LOAD_SEG_ACK, + NM_MT_LOAD_ABORT, + NM_MT_LOAD_END_ACK, + NM_MT_LOAD_END_NACK, + //NM_MT_SW_ACT_REQ, + NM_MT_ACTIVATE_SW_ACK, + NM_MT_ACTIVATE_SW_NACK, + NM_MT_SW_ACTIVATED_REP, +}; + +const enum abis_nm_msgtype abis_nm_nacks[33] = { + NM_MT_LOAD_INIT_NACK, + NM_MT_LOAD_END_NACK, + NM_MT_SW_ACT_REQ_NACK, + NM_MT_ACTIVATE_SW_NACK, + NM_MT_ESTABLISH_TEI_NACK, + NM_MT_CONN_TERR_SIGN_NACK, + NM_MT_DISC_TERR_SIGN_NACK, + NM_MT_CONN_TERR_TRAF_NACK, + NM_MT_DISC_TERR_TRAF_NACK, + NM_MT_CONN_MDROP_LINK_NACK, + NM_MT_DISC_MDROP_LINK_NACK, + NM_MT_SET_BTS_ATTR_NACK, + NM_MT_SET_RADIO_ATTR_NACK, + NM_MT_SET_CHAN_ATTR_NACK, + NM_MT_PERF_TEST_NACK, + NM_MT_SEND_TEST_REP_NACK, + NM_MT_STOP_TEST_NACK, + NM_MT_STOP_EVENT_REP_NACK, + NM_MT_REST_EVENT_REP_NACK, + NM_MT_CHG_ADM_STATE_NACK, + NM_MT_CHG_ADM_STATE_REQ_NACK, + NM_MT_REP_OUTST_ALARMS_NACK, + NM_MT_CHANGEOVER_NACK, + NM_MT_OPSTART_NACK, + NM_MT_REINIT_NACK, + NM_MT_SET_SITE_OUT_NACK, + NM_MT_CHG_HW_CONF_NACK, + NM_MT_GET_ATTR_NACK, + NM_MT_SET_ALARM_THRES_NACK, + NM_MT_BS11_BEGIN_DB_TX_NACK, + NM_MT_BS11_END_DB_TX_NACK, + NM_MT_BS11_CREATE_OBJ_NACK, + NM_MT_BS11_DELETE_OBJ_NACK, +}; + +static const struct value_string nack_names[] = { + { NM_MT_LOAD_INIT_NACK, "SOFTWARE LOAD INIT" }, + { NM_MT_LOAD_END_NACK, "SOFTWARE LOAD END" }, + { NM_MT_SW_ACT_REQ_NACK, "SOFTWARE ACTIVATE REQUEST" }, + { NM_MT_ACTIVATE_SW_NACK, "ACTIVATE SOFTWARE" }, + { NM_MT_ESTABLISH_TEI_NACK, "ESTABLISH TEI" }, + { NM_MT_CONN_TERR_SIGN_NACK, "CONNECT TERRESTRIAL SIGNALLING" }, + { NM_MT_DISC_TERR_SIGN_NACK, "DISCONNECT TERRESTRIAL SIGNALLING" }, + { NM_MT_CONN_TERR_TRAF_NACK, "CONNECT TERRESTRIAL TRAFFIC" }, + { NM_MT_DISC_TERR_TRAF_NACK, "DISCONNECT TERRESTRIAL TRAFFIC" }, + { NM_MT_CONN_MDROP_LINK_NACK, "CONNECT MULTI-DROP LINK" }, + { NM_MT_DISC_MDROP_LINK_NACK, "DISCONNECT MULTI-DROP LINK" }, + { NM_MT_SET_BTS_ATTR_NACK, "SET BTS ATTRIBUTE" }, + { NM_MT_SET_RADIO_ATTR_NACK, "SET RADIO ATTRIBUTE" }, + { NM_MT_SET_CHAN_ATTR_NACK, "SET CHANNEL ATTRIBUTE" }, + { NM_MT_PERF_TEST_NACK, "PERFORM TEST" }, + { NM_MT_SEND_TEST_REP_NACK, "SEND TEST REPORT" }, + { NM_MT_STOP_TEST_NACK, "STOP TEST" }, + { NM_MT_STOP_EVENT_REP_NACK, "STOP EVENT REPORT" }, + { NM_MT_REST_EVENT_REP_NACK, "RESET EVENT REPORT" }, + { NM_MT_CHG_ADM_STATE_NACK, "CHANGE ADMINISTRATIVE STATE" }, + { NM_MT_CHG_ADM_STATE_REQ_NACK, + "CHANGE ADMINISTRATIVE STATE REQUEST" }, + { NM_MT_REP_OUTST_ALARMS_NACK, "REPORT OUTSTANDING ALARMS" }, + { NM_MT_CHANGEOVER_NACK, "CHANGEOVER" }, + { NM_MT_OPSTART_NACK, "OPSTART" }, + { NM_MT_REINIT_NACK, "REINIT" }, + { NM_MT_SET_SITE_OUT_NACK, "SET SITE OUTPUT" }, + { NM_MT_CHG_HW_CONF_NACK, "CHANGE HARDWARE CONFIGURATION" }, + { NM_MT_GET_ATTR_NACK, "GET ATTRIBUTE" }, + { NM_MT_SET_ALARM_THRES_NACK, "SET ALARM THRESHOLD" }, + { NM_MT_BS11_BEGIN_DB_TX_NACK, "BS11 BEGIN DATABASE TRANSMISSION" }, + { NM_MT_BS11_END_DB_TX_NACK, "BS11 END DATABASE TRANSMISSION" }, + { NM_MT_BS11_CREATE_OBJ_NACK, "BS11 CREATE OBJECT" }, + { NM_MT_BS11_DELETE_OBJ_NACK, "BS11 DELETE OBJECT" }, + { 0, NULL } +}; + +const char *abis_nm_nack_name(uint8_t nack) +{ + return get_value_string(nack_names, nack); +} + +/* Chapter 9.4.36 */ +static const struct value_string nack_cause_names[] = { + /* General Nack Causes */ + { NM_NACK_INCORR_STRUCT, "Incorrect message structure" }, + { NM_NACK_MSGTYPE_INVAL, "Invalid message type value" }, + { NM_NACK_OBJCLASS_INVAL, "Invalid Object class value" }, + { NM_NACK_OBJCLASS_NOTSUPP, "Object class not supported" }, + { NM_NACK_BTSNR_UNKN, "BTS no. unknown" }, + { NM_NACK_TRXNR_UNKN, "Baseband Transceiver no. unknown" }, + { NM_NACK_OBJINST_UNKN, "Object Instance unknown" }, + { NM_NACK_ATTRID_INVAL, "Invalid attribute identifier value" }, + { NM_NACK_ATTRID_NOTSUPP, "Attribute identifier not supported" }, + { NM_NACK_PARAM_RANGE, "Parameter value outside permitted range" }, + { NM_NACK_ATTRLIST_INCONSISTENT,"Inconsistency in attribute list" }, + { NM_NACK_SPEC_IMPL_NOTSUPP, "Specified implementation not supported" }, + { NM_NACK_CANT_PERFORM, "Message cannot be performed" }, + /* Specific Nack Causes */ + { NM_NACK_RES_NOTIMPL, "Resource not implemented" }, + { NM_NACK_RES_NOTAVAIL, "Resource not available" }, + { NM_NACK_FREQ_NOTAVAIL, "Frequency not available" }, + { NM_NACK_TEST_NOTSUPP, "Test not supported" }, + { NM_NACK_CAPACITY_RESTR, "Capacity restrictions" }, + { NM_NACK_PHYSCFG_NOTPERFORM, "Physical configuration cannot be performed" }, + { NM_NACK_TEST_NOTINIT, "Test not initiated" }, + { NM_NACK_PHYSCFG_NOTRESTORE, "Physical configuration cannot be restored" }, + { NM_NACK_TEST_NOSUCH, "No such test" }, + { NM_NACK_TEST_NOSTOP, "Test cannot be stopped" }, + { NM_NACK_MSGINCONSIST_PHYSCFG, "Message inconsistent with physical configuration" }, + { NM_NACK_FILE_INCOMPLETE, "Complete file notreceived" }, + { NM_NACK_FILE_NOTAVAIL, "File not available at destination" }, + { NM_NACK_FILE_NOTACTIVATE, "File cannot be activate" }, + { NM_NACK_REQ_NOT_GRANT, "Request not granted" }, + { NM_NACK_WAIT, "Wait" }, + { NM_NACK_NOTH_REPORT_EXIST, "Nothing reportable existing" }, + { NM_NACK_MEAS_NOTSUPP, "Measurement not supported" }, + { NM_NACK_MEAS_NOTSTART, "Measurement not started" }, + { 0, NULL } +}; + +const char *abis_nm_nack_cause_name(uint8_t cause) +{ + return get_value_string(nack_cause_names, cause); +} + +/* Chapter 9.4.16: Event Type */ +static const struct value_string event_type_names[] = { + { NM_EVT_COMM_FAIL, "communication failure" }, + { NM_EVT_QOS_FAIL, "quality of service failure" }, + { NM_EVT_PROC_FAIL, "processing failure" }, + { NM_EVT_EQUIP_FAIL, "equipment failure" }, + { NM_EVT_ENV_FAIL, "environment failure" }, + { 0, NULL } +}; + +const char *abis_nm_event_type_name(uint8_t cause) +{ + return get_value_string(event_type_names, cause); +} + +/* Chapter 9.4.63: Perceived Severity */ +static const struct value_string severity_names[] = { + { NM_SEVER_CEASED, "failure ceased" }, + { NM_SEVER_CRITICAL, "critical failure" }, + { NM_SEVER_MAJOR, "major failure" }, + { NM_SEVER_MINOR, "minor failure" }, + { NM_SEVER_WARNING, "warning level failure" }, + { NM_SEVER_INDETERMINATE, "indeterminate failure" }, + { 0, NULL } +}; + +const char *abis_nm_severity_name(uint8_t cause) +{ + return get_value_string(severity_names, cause); +} + +/* Attributes that the BSC can set, not only get, according to Section 9.4 */ +const enum abis_nm_attr abis_nm_att_settable[] = { + NM_ATT_ADD_INFO, + NM_ATT_ADD_TEXT, + NM_ATT_DEST, + NM_ATT_EVENT_TYPE, + NM_ATT_FILE_DATA, + NM_ATT_GET_ARI, + NM_ATT_HW_CONF_CHG, + NM_ATT_LIST_REQ_ATTR, + NM_ATT_MDROP_LINK, + NM_ATT_MDROP_NEXT, + NM_ATT_NACK_CAUSES, + NM_ATT_OUTST_ALARM, + NM_ATT_PHYS_CONF, + NM_ATT_PROB_CAUSE, + NM_ATT_RAD_SUBC, + NM_ATT_SOURCE, + NM_ATT_SPEC_PROB, + NM_ATT_START_TIME, + NM_ATT_TEST_DUR, + NM_ATT_TEST_NO, + NM_ATT_TEST_REPORT, + NM_ATT_WINDOW_SIZE, + NM_ATT_SEVERITY, + NM_ATT_MEAS_RES, + NM_ATT_MEAS_TYPE, +}; + +const struct tlv_definition abis_nm_att_tlvdef = { + .def = { + [NM_ATT_ABIS_CHANNEL] = { TLV_TYPE_FIXED, 3 }, + [NM_ATT_ADD_INFO] = { TLV_TYPE_TL16V }, + [NM_ATT_ADD_TEXT] = { TLV_TYPE_TL16V }, + [NM_ATT_ADM_STATE] = { TLV_TYPE_TV }, + [NM_ATT_ARFCN_LIST]= { TLV_TYPE_TL16V }, + [NM_ATT_AUTON_REPORT] = { TLV_TYPE_TV }, + [NM_ATT_AVAIL_STATUS] = { TLV_TYPE_TL16V }, + [NM_ATT_BCCH_ARFCN] = { TLV_TYPE_FIXED, 2 }, + [NM_ATT_BSIC] = { TLV_TYPE_TV }, + [NM_ATT_BTS_AIR_TIMER] = { TLV_TYPE_TV }, + [NM_ATT_CCCH_L_I_P] = { TLV_TYPE_TV }, + [NM_ATT_CCCH_L_T] = { TLV_TYPE_TV }, + [NM_ATT_CHAN_COMB] = { TLV_TYPE_TV }, + [NM_ATT_CONN_FAIL_CRIT] = { TLV_TYPE_TL16V }, + [NM_ATT_DEST] = { TLV_TYPE_TL16V }, + [NM_ATT_EVENT_TYPE] = { TLV_TYPE_TV }, + [NM_ATT_FILE_DATA] = { TLV_TYPE_TL16V }, + [NM_ATT_FILE_ID] = { TLV_TYPE_TL16V }, + [NM_ATT_FILE_VERSION] = { TLV_TYPE_TL16V }, + [NM_ATT_GSM_TIME] = { TLV_TYPE_FIXED, 2 }, + [NM_ATT_HSN] = { TLV_TYPE_TV }, + [NM_ATT_HW_CONFIG] = { TLV_TYPE_TL16V }, + [NM_ATT_HW_DESC] = { TLV_TYPE_TL16V }, + [NM_ATT_INTAVE_PARAM] = { TLV_TYPE_TV }, + [NM_ATT_INTERF_BOUND] = { TLV_TYPE_FIXED, 6 }, + [NM_ATT_LIST_REQ_ATTR] = { TLV_TYPE_TL16V }, + [NM_ATT_MAIO] = { TLV_TYPE_TV }, + [NM_ATT_MANUF_STATE] = { TLV_TYPE_TV }, + [NM_ATT_MANUF_THRESH] = { TLV_TYPE_TL16V }, + [NM_ATT_MANUF_ID] = { TLV_TYPE_TL16V }, + [NM_ATT_MAX_TA] = { TLV_TYPE_TV }, + [NM_ATT_MDROP_LINK] = { TLV_TYPE_FIXED, 2 }, + [NM_ATT_MDROP_NEXT] = { TLV_TYPE_FIXED, 2 }, + [NM_ATT_NACK_CAUSES] = { TLV_TYPE_TV }, + [NM_ATT_NY1] = { TLV_TYPE_TV }, + [NM_ATT_OPER_STATE] = { TLV_TYPE_TV }, + [NM_ATT_OVERL_PERIOD] = { TLV_TYPE_TL16V }, + [NM_ATT_PHYS_CONF] = { TLV_TYPE_TL16V }, + [NM_ATT_POWER_CLASS] = { TLV_TYPE_TV }, + [NM_ATT_POWER_THRESH] = { TLV_TYPE_FIXED, 3 }, + [NM_ATT_PROB_CAUSE] = { TLV_TYPE_FIXED, 3 }, + [NM_ATT_RACH_B_THRESH] = { TLV_TYPE_TV }, + [NM_ATT_LDAVG_SLOTS] = { TLV_TYPE_FIXED, 2 }, + [NM_ATT_RAD_SUBC] = { TLV_TYPE_TV }, + [NM_ATT_RF_MAXPOWR_R] = { TLV_TYPE_TV }, + [NM_ATT_SITE_INPUTS] = { TLV_TYPE_TL16V }, + [NM_ATT_SITE_OUTPUTS] = { TLV_TYPE_TL16V }, + [NM_ATT_SOURCE] = { TLV_TYPE_TL16V }, + [NM_ATT_SPEC_PROB] = { TLV_TYPE_TV }, + [NM_ATT_START_TIME] = { TLV_TYPE_FIXED, 2 }, + [NM_ATT_T200] = { TLV_TYPE_FIXED, 7 }, + [NM_ATT_TEI] = { TLV_TYPE_TV }, + [NM_ATT_TEST_DUR] = { TLV_TYPE_FIXED, 2 }, + [NM_ATT_TEST_NO] = { TLV_TYPE_TV }, + [NM_ATT_TEST_REPORT] = { TLV_TYPE_TL16V }, + [NM_ATT_VSWR_THRESH] = { TLV_TYPE_FIXED, 2 }, + [NM_ATT_WINDOW_SIZE] = { TLV_TYPE_TV }, + [NM_ATT_TSC] = { TLV_TYPE_TV }, + [NM_ATT_SW_CONFIG] = { TLV_TYPE_TL16V }, + [NM_ATT_SEVERITY] = { TLV_TYPE_TV }, + [NM_ATT_GET_ARI] = { TLV_TYPE_TL16V }, + [NM_ATT_HW_CONF_CHG] = { TLV_TYPE_TL16V }, + [NM_ATT_OUTST_ALARM] = { TLV_TYPE_TV }, + [NM_ATT_MEAS_RES] = { TLV_TYPE_TL16V }, + }, +}; + +static const struct value_string abis_nm_obj_class_names[] = { + { NM_OC_SITE_MANAGER, "SITE-MANAGER" }, + { NM_OC_BTS, "BTS" }, + { NM_OC_RADIO_CARRIER, "RADIO-CARRIER" }, + { NM_OC_BASEB_TRANSC, "BASEBAND-TRANSCEIVER" }, + { NM_OC_CHANNEL, "CHANNEL" }, + { NM_OC_BS11_ADJC, "ADJC" }, + { NM_OC_BS11_HANDOVER, "HANDOVER" }, + { NM_OC_BS11_PWR_CTRL, "POWER-CONTROL" }, + { NM_OC_BS11_BTSE, "BTSE" }, + { NM_OC_BS11_RACK, "RACK" }, + { NM_OC_BS11_TEST, "TEST" }, + { NM_OC_BS11_ENVABTSE, "ENVABTSE" }, + { NM_OC_BS11_BPORT, "BPORT" }, + { NM_OC_GPRS_NSE, "GPRS-NSE" }, + { NM_OC_GPRS_CELL, "GPRS-CELL" }, + { NM_OC_GPRS_NSVC, "GPRS-NSVC" }, + { NM_OC_BS11, "SIEMENSHW" }, + { 0, NULL } +}; + +const char *abis_nm_obj_class_name(uint8_t oc) +{ + return get_value_string(abis_nm_obj_class_names, oc); +} + +const char *abis_nm_opstate_name(uint8_t os) +{ + switch (os) { + case NM_OPSTATE_DISABLED: + return "Disabled"; + case NM_OPSTATE_ENABLED: + return "Enabled"; + case NM_OPSTATE_NULL: + return "NULL"; + default: + return "RFU"; + } +} + +/* Chapter 9.4.7 */ +static const struct value_string avail_names[] = { + { 0, "In test" }, + { 1, "Failed" }, + { 2, "Power off" }, + { 3, "Off line" }, + /* Not used */ + { 5, "Dependency" }, + { 6, "Degraded" }, + { 7, "Not installed" }, + { 0xff, "OK" }, + { 0, NULL } +}; + +const char *abis_nm_avail_name(uint8_t avail) +{ + return get_value_string(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 *abis_nm_test_name(uint8_t test) +{ + return get_value_string(test_names, test); +} + +static const struct value_string abis_nm_adm_state_names[] = { + { NM_STATE_LOCKED, "Locked" }, + { NM_STATE_UNLOCKED, "Unlocked" }, + { NM_STATE_SHUTDOWN, "Shutdown" }, + { NM_STATE_NULL, "NULL" }, + { 0, NULL } +}; + +const char *abis_nm_adm_name(uint8_t adm) +{ + return get_value_string(abis_nm_adm_state_names, adm); +} + +void abis_nm_debugp_foh(int ss, struct abis_om_fom_hdr *foh) +{ + DEBUGP(ss, "OC=%s(%02x) INST=(%02x,%02x,%02x) ", + abis_nm_obj_class_name(foh->obj_class), foh->obj_class, + foh->obj_inst.bts_nr, foh->obj_inst.trx_nr, + foh->obj_inst.ts_nr); +} diff --git a/src/gsmtap_util.c b/src/gsmtap_util.c index e0bc848c..3d20bfc2 100644 --- a/src/gsmtap_util.c +++ b/src/gsmtap_util.c @@ -1,6 +1,6 @@ -/* GSMTAP output for Osmocom Layer2 (will only work on the host PC) */ +/* GSMTAP support code in libmsomcore */ /* - * (C) 2010 by Harald Welte <laforge@gnumonks.org> + * (C) 2010-2011 by Harald Welte <laforge@gnumonks.org> * * All Rights Reserved * @@ -22,19 +22,19 @@ #include "../config.h" -#ifdef HAVE_SYS_SELECT_H - #include <osmocom/core/gsmtap_util.h> #include <osmocom/core/logging.h> #include <osmocom/core/gsmtap.h> #include <osmocom/core/msgb.h> +#include <osmocom/core/talloc.h> #include <osmocom/core/select.h> +#include <osmocom/core/socket.h> #include <osmocom/gsm/protocol/gsm_04_08.h> #include <osmocom/gsm/rsl.h> +#include <sys/types.h> + #include <arpa/inet.h> -#include <sys/socket.h> -#include <netinet/in.h> #include <stdio.h> #include <unistd.h> @@ -42,10 +42,6 @@ #include <string.h> #include <errno.h> -static struct osmo_fd gsmtap_bfd = { .fd = -1 }; -static struct osmo_fd gsmtap_sink_bfd = { .fd = -1 }; -static LLIST_HEAD(gsmtap_txqueue); - uint8_t chantype_rsl2gsmtap(uint8_t rsl_chantype, uint8_t link_id) { uint8_t ret = GSMTAP_CHANNEL_UNKNOWN; @@ -114,44 +110,91 @@ struct msgb *gsmtap_makemsg(uint16_t arfcn, uint8_t ts, uint8_t chan_type, return msg; } +#ifdef HAVE_SYS_SOCKET_H + +#include <sys/socket.h> +#include <netinet/in.h> + +/* Open a GSMTAP source (sending) socket, conncet it to host/port and + * return resulting fd */ +int gsmtap_source_init_fd(const char *host, uint16_t port) +{ + if (port == 0) + port = GSMTAP_UDP_PORT; + if (host == NULL) + host = "localhost"; + + return osmo_sock_init(AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, host, port, 0); +} + +int gsmtap_source_add_sink_fd(int gsmtap_fd) +{ + struct sockaddr_storage ss; + socklen_t ss_len = sizeof(ss); + int rc; + + rc = getpeername(gsmtap_fd, (struct sockaddr *)&ss, &ss_len); + if (rc < 0) + return rc; + + if (osmo_sockaddr_is_local((struct sockaddr *)&ss, ss_len) == 1) { + rc = osmo_sock_init_sa((struct sockaddr *)&ss, SOCK_DGRAM, IPPROTO_UDP, 1); + if (rc >= 0) + return rc; + } + + return -ENODEV; +} + +int gsmtap_sendmsg(struct gsmtap_inst *gti, struct msgb *msg) +{ + if (!gti) + return -ENODEV; + + if (gti->ofd_wq_mode) + return osmo_wqueue_enqueue(>i->wq, msg); + else { + /* try immediate send and return error if any */ + int rc; + + rc = write(gsmtap_inst_fd(gti), msg->data, msg->len); + if (rc <= 0) { + return rc; + } else if (rc >= msg->len) { + msgb_free(msg); + return 0; + } else { + /* short write */ + return -EIO; + } + } +} + /* receive a message from L1/L2 and put it in GSMTAP */ -int gsmtap_sendmsg(uint16_t arfcn, uint8_t ts, uint8_t chan_type, uint8_t ss, - uint32_t fn, int8_t signal_dbm, uint8_t snr, - const uint8_t *data, unsigned int len) +int gsmtap_send(struct gsmtap_inst *gti, uint16_t arfcn, uint8_t ts, + uint8_t chan_type, uint8_t ss, uint32_t fn, + int8_t signal_dbm, uint8_t snr, const uint8_t *data, + unsigned int len) { struct msgb *msg; - /* gsmtap was never initialized, so don't try to send anything */ - if (gsmtap_bfd.fd == -1) - return 0; + if (!gti) + return -ENODEV; msg = gsmtap_makemsg(arfcn, ts, chan_type, ss, fn, signal_dbm, snr, data, len); if (!msg) return -ENOMEM; - msgb_enqueue(&gsmtap_txqueue, msg); - gsmtap_bfd.when |= BSC_FD_WRITE; - - return 0; + return gsmtap_sendmsg(gti, msg); } /* Callback from select layer if we can write to the socket */ -static int gsmtap_fd_cb(struct osmo_fd *fd, unsigned int flags) +static int gsmtap_wq_w_cb(struct osmo_fd *ofd, struct msgb *msg) { - struct msgb *msg; int rc; - if (!(flags & BSC_FD_WRITE)) - return 0; - - msg = msgb_dequeue(&gsmtap_txqueue); - if (!msg) { - /* no more messages in the queue, disable READ cb */ - gsmtap_bfd.when = 0; - return 0; - } - rc = write(gsmtap_bfd.fd, msg->data, msg->len); + rc = write(ofd->fd, msg->data, msg->len); if (rc < 0) { perror("writing msgb to gsmtap fd"); msgb_free(msg); @@ -167,37 +210,6 @@ static int gsmtap_fd_cb(struct osmo_fd *fd, unsigned int flags) return 0; } -int gsmtap_init(uint32_t dst_ip) -{ - int rc; - struct sockaddr_in sin; - - sin.sin_family = AF_INET; - sin.sin_port = htons(GSMTAP_UDP_PORT); - sin.sin_addr.s_addr = htonl(dst_ip); - - /* create socket */ - rc = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); - if (rc < 0) { - perror("creating UDP socket"); - return rc; - } - gsmtap_bfd.fd = rc; - rc = connect(rc, (struct sockaddr *)&sin, sizeof(sin)); - if (rc < 0) { - perror("connecting UDP socket"); - close(gsmtap_bfd.fd); - gsmtap_bfd.fd = -1; - return rc; - } - - gsmtap_bfd.when = BSC_FD_WRITE; - gsmtap_bfd.cb = gsmtap_fd_cb; - gsmtap_bfd.data = NULL; - - return osmo_fd_register(&gsmtap_bfd); -} - /* Callback from select layer if we can read from the sink socket */ static int gsmtap_sink_fd_cb(struct osmo_fd *fd, unsigned int flags) { @@ -217,37 +229,53 @@ static int gsmtap_sink_fd_cb(struct osmo_fd *fd, unsigned int flags) return 0; } -/* Create a local 'gsmtap sink' avoiding the UDP packets being rejected - * with ICMP reject messages */ -int gsmtap_sink_init(uint32_t bind_ip) +/* Add a local sink to an existing GSMTAP source instance */ +int gsmtap_source_add_sink(struct gsmtap_inst *gti) { - int rc; - struct sockaddr_in sin; + int fd; - sin.sin_family = AF_INET; - sin.sin_port = htons(GSMTAP_UDP_PORT); - sin.sin_addr.s_addr = htonl(bind_ip); + fd = gsmtap_source_add_sink_fd(gsmtap_inst_fd(gti)); + if (fd < 0) + return fd; - rc = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); - if (rc < 0) { - perror("creating UDP socket"); - return rc; - } - gsmtap_sink_bfd.fd = rc; - rc = bind(rc, (struct sockaddr *)&sin, sizeof(sin)); - if (rc < 0) { - perror("binding UDP socket"); - close(gsmtap_sink_bfd.fd); - gsmtap_sink_bfd.fd = -1; - return rc; + if (gti->ofd_wq_mode) { + struct osmo_fd *sink_ofd; + + sink_ofd = >i->sink_ofd; + sink_ofd->fd = fd; + sink_ofd->when = BSC_FD_READ; + sink_ofd->cb = gsmtap_sink_fd_cb; + + osmo_fd_register(sink_ofd); } - gsmtap_sink_bfd.when = BSC_FD_READ; - gsmtap_sink_bfd.cb = gsmtap_sink_fd_cb; - gsmtap_sink_bfd.data = NULL; + return fd; +} + +/* like gsmtap_init2() but integrated with libosmocore select.c */ +struct gsmtap_inst *gsmtap_source_init(const char *host, uint16_t port, + int ofd_wq_mode) +{ + struct gsmtap_inst *gti; + int fd; + + fd = gsmtap_source_init_fd(host, port); + if (fd < 0) + return NULL; + + gti = talloc_zero(NULL, struct gsmtap_inst); + gti->ofd_wq_mode = ofd_wq_mode; + gti->wq.bfd.fd = fd; + gti->sink_ofd.fd = -1; - return osmo_fd_register(&gsmtap_sink_bfd); + if (ofd_wq_mode) { + osmo_wqueue_init(>i->wq, 64); + gti->wq.write_cb = &gsmtap_wq_w_cb; + + osmo_fd_register(>i->wq.bfd); + } + return gti; } -#endif /* HAVE_SYS_SELECT_H */ +#endif /* HAVE_SYS_SOCKET_H */ diff --git a/src/socket.c b/src/socket.c new file mode 100644 index 00000000..66907c8c --- /dev/null +++ b/src/socket.c @@ -0,0 +1,179 @@ +#include "../config.h" + +#ifdef HAVE_SYS_SOCKET_H + +#include <osmocom/core/logging.h> +#include <osmocom/core/select.h> +#include <osmocom/core/socket.h> + +#include <arpa/inet.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <netinet/in.h> + +#include <stdio.h> +#include <unistd.h> +#include <stdint.h> +#include <string.h> +#include <errno.h> +#include <netdb.h> +#include <ifaddrs.h> + +int osmo_sock_init(uint16_t family, uint16_t type, uint8_t proto, + const char *host, uint16_t port, int connect0_bind1) +{ + struct addrinfo hints, *result, *rp; + int sfd, rc, on = 1; + char portbuf[16]; + + sprintf(portbuf, "%u", port); + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = family; + hints.ai_socktype = type; + hints.ai_flags = 0; + hints.ai_protocol = proto; + + rc = getaddrinfo(host, portbuf, &hints, &result); + if (rc != 0) { + perror("getaddrinfo returned NULL"); + return -EINVAL; + } + + for (rp = result; rp != NULL; rp = rp->ai_next) { + sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + if (sfd == -1) + continue; + if (connect0_bind1 == 0) { + if (connect(sfd, rp->ai_addr, rp->ai_addrlen) != -1) + break; + } else { + if (bind(sfd, rp->ai_addr, rp->ai_addrlen) != -1) + break; + } + close(sfd); + } + freeaddrinfo(result); + + if (rp == NULL) { + perror("unable to connect/bind socket"); + return -ENODEV; + } + + setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + + /* Make sure to call 'listen' on a bound, connection-oriented sock */ + if (connect0_bind1 == 1) { + switch (type) { + case SOCK_STREAM: + case SOCK_SEQPACKET: + listen(sfd, 10); + break; + } + } + return sfd; +} + +int osmo_sock_init_ofd(struct osmo_fd *ofd, int family, int type, int proto, + const char *host, uint16_t port, int connect0_bind1) +{ + int sfd, rc; + + sfd = osmo_sock_init(family, type, proto, host, port, connect0_bind1); + if (sfd < 0) + return sfd; + + ofd->fd = sfd; + ofd->when = BSC_FD_READ; + + rc = osmo_fd_register(ofd); + if (rc < 0) { + close(sfd); + return rc; + } + + return sfd; +} + +int osmo_sock_init_sa(struct sockaddr *ss, uint16_t type, + uint8_t proto, int connect0_bind1) +{ + char host[NI_MAXHOST]; + uint16_t port; + struct sockaddr_in *sin; + struct sockaddr_in6 *sin6; + int s, sa_len; + + /* determine port and host from ss */ + switch (ss->sa_family) { + case AF_INET: + sin = (struct sockaddr_in *) ss; + sa_len = sizeof(struct sockaddr_in); + port = ntohs(sin->sin_port); + break; + case AF_INET6: + sin6 = (struct sockaddr_in6 *) ss; + sa_len = sizeof(struct sockaddr_in6); + port = ntohs(sin6->sin6_port); + break; + default: + return -EINVAL; + } + + s = getnameinfo(ss, sa_len, host, NI_MAXHOST, + NULL, 0, NI_NUMERICHOST); + if (s != 0) { + perror("getnameinfo failed"); + return s; + } + + return osmo_sock_init(ss->sa_family, type, proto, host, + port, connect0_bind1); +} + +static int sockaddr_equal(const struct sockaddr *a, + const struct sockaddr *b, unsigned int len) +{ + struct sockaddr_in *sin_a, *sin_b; + struct sockaddr_in6 *sin6_a, *sin6_b; + + if (a->sa_family != b->sa_family) + return 0; + + switch (a->sa_family) { + case AF_INET: + sin_a = (struct sockaddr_in *)a; + sin_b = (struct sockaddr_in *)b; + if (!memcmp(&sin_a->sin_addr, &sin_b->sin_addr, + sizeof(struct in_addr))) + return 1; + break; + case AF_INET6: + sin6_a = (struct sockaddr_in6 *)a; + sin6_b = (struct sockaddr_in6 *)b; + if (!memcmp(&sin6_a->sin6_addr, &sin6_b->sin6_addr, + sizeof(struct in6_addr))) + return 1; + break; + } + return 0; +} + +/* determine if the given address is a local address */ +int osmo_sockaddr_is_local(struct sockaddr *addr, socklen_t addrlen) +{ + struct ifaddrs *ifaddr, *ifa; + + if (getifaddrs(&ifaddr) == -1) { + perror("getifaddrs"); + return -EIO; + } + + for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) { + if (sockaddr_equal(ifa->ifa_addr, addr, addrlen)) + return 1; + } + + return 0; +} + +#endif /* HAVE_SYS_SOCKET_H */ diff --git a/src/vty/telnet_interface.c b/src/vty/telnet_interface.c index 78459944..c08a256a 100644 --- a/src/vty/telnet_interface.c +++ b/src/vty/telnet_interface.c @@ -95,10 +95,16 @@ extern struct host host; static void print_welcome(int fd) { int ret; - static char *msg = - "Welcome to the OpenBSC Control interface\r\n"; + static const char *msg1 = "Welcome to the "; + static const char *msg2 = " control interface\r\n"; + char *app_name = "<unnamed>"; - ret = write(fd, msg, strlen(msg)); + if (host.app_info->name) + app_name = host.app_info->name; + + ret = write(fd, msg1, strlen(msg1)); + ret = write(fd, app_name, strlen(app_name)); + ret = write(fd, msg2, strlen(msg2)); if (host.app_info->copyright) ret = write(fd, host.app_info->copyright, strlen(host.app_info->copyright)); |