diff options
Diffstat (limited to 'src')
37 files changed, 0 insertions, 20105 deletions
diff --git a/src/Makefile.am b/src/Makefile.am deleted file mode 100644 index fbbbfdc94..000000000 --- a/src/Makefile.am +++ /dev/null @@ -1,27 +0,0 @@ -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 libvty.a -noinst_HEADERS = vty/cardshell.h - -libbsc_a_SOURCES = abis_rsl.c abis_nm.c gsm_04_08.c gsm_data.c \ - gsm_subscriber.c msgb.c select.c chan_alloc.c timer.c debug.c db.c \ - gsm_04_11.c telnet_interface.c subchan_demux.c \ - trau_frame.c trau_mux.c paging.c e1_config.c e1_input.c tlv_parser.c \ - input/misdn.c input/ipaccess.c signal.c gsm_utils.c - -libvty_a_SOURCES = vty/buffer.c vty/command.c vty/vector.c vty/vty.c - -bsc_hack_SOURCES = bsc_hack.c vty_interface.c -bsc_hack_LDADD = libbsc.a libvty.a -ldl -ldbi $(LIBCRYPT) - -bs11_config_SOURCES = bs11_config.c abis_nm.c gsm_data.c msgb.c debug.c \ - select.c timer.c rs232.c tlv_parser.c signal.c - -ipaccess_find_SOURCES = ipaccess-find.c select.c timer.c - -ipaccess_config_SOURCES = ipaccess-config.c -ipaccess_config_LDADD = libbsc.a libvty.a -ldl -ldbi $(LIBCRYPT) - -isdnsync_SOURCES = isdnsync.c diff --git a/src/abis_nm.c b/src/abis_nm.c deleted file mode 100644 index 74dba2377..000000000 --- a/src/abis_nm.c +++ /dev/null @@ -1,2332 +0,0 @@ -/* 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-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 <errno.h> -#include <unistd.h> -#include <stdio.h> -#include <fcntl.h> -#include <stdlib.h> -#include <libgen.h> -#include <time.h> -#include <limits.h> - -#include <sys/types.h> -#include <sys/stat.h> -#include <netinet/in.h> -#include <arpa/inet.h> - -#include <openbsc/gsm_data.h> -#include <openbsc/debug.h> -#include <openbsc/msgb.h> -#include <openbsc/tlv.h> -#include <openbsc/abis_nm.h> -#include <openbsc/misdn.h> -#include <openbsc/signal.h> - -#define OM_ALLOC_SIZE 1024 -#define OM_HEADROOM_SIZE 128 - -/* unidirectional messages from BTS to BSC */ -static const enum abis_nm_msgtype reports[] = { - NM_MT_SW_ACTIVATED_REP, - NM_MT_TEST_REP, - NM_MT_STATECHG_EVENT_REP, - NM_MT_FAILURE_EVENT_REP, -}; - -/* messages without ACK/NACK */ -static const enum abis_nm_msgtype no_ack_nack[] = { - NM_MT_MEAS_RES_REQ, - NM_MT_STOP_MEAS, - NM_MT_START_MEAS, -}; - -/* Messages related to software load */ -static const enum abis_nm_msgtype sw_load_msgs[] = { - 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, -}; - -static const enum abis_nm_msgtype nacks[] = { - 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 char *nack_names[0xff] = { - [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", -}; - -/* Chapter 9.4.36 */ -static const char *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", - [MN_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", -}; - -static char namebuf[255]; -static const char *nack_cause_name(u_int8_t cause) -{ - if (cause < ARRAY_SIZE(nack_cause_names) && nack_cause_names[cause]) - return nack_cause_names[cause]; - - snprintf(namebuf, sizeof(namebuf), "0x%02x\n", cause); - return namebuf; -} - -/* Chapter 9.4.16: Event Type */ -static const char *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", -}; - -static const char *event_type_name(u_int8_t cause) -{ - if (cause < ARRAY_SIZE(event_type_names) && event_type_names[cause]) - return event_type_names[cause]; - - snprintf(namebuf, sizeof(namebuf), "0x%02x\n", cause); - return namebuf; -} - -/* Chapter 9.4.63: Perceived Severity */ -static const char *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", -}; - -static const char *severity_name(u_int8_t cause) -{ - if (cause < ARRAY_SIZE(severity_names) && severity_names[cause]) - return severity_names[cause]; - - snprintf(namebuf, sizeof(namebuf), "0x%02x\n", cause); - return namebuf; -} - -/* Attributes that the BSC can set, not only get, according to Section 9.4 */ -static const enum abis_nm_attr 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, -}; - -static const struct tlv_definition 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_FILE_DATA] = { TLV_TYPE_TL16V }, - [NM_ATT_MEAS_RES] = { TLV_TYPE_TL16V }, - /* BS11 specifics */ - [NM_ATT_BS11_ESN_FW_CODE_NO] = { TLV_TYPE_TLV }, - [NM_ATT_BS11_ESN_HW_CODE_NO] = { TLV_TYPE_TLV }, - [NM_ATT_BS11_ESN_PCB_SERIAL] = { TLV_TYPE_TLV }, - [NM_ATT_BS11_BOOT_SW_VERS] = { TLV_TYPE_TLV }, - [0xd5] = { TLV_TYPE_TLV }, - [0xa8] = { TLV_TYPE_TLV }, - [NM_ATT_BS11_PASSWORD] = { TLV_TYPE_TLV }, - [NM_ATT_BS11_TXPWR] = { TLV_TYPE_TLV }, - [NM_ATT_BS11_RSSI_OFFS] = { TLV_TYPE_TLV }, - [NM_ATT_BS11_LINE_CFG] = { TLV_TYPE_TV }, - [NM_ATT_BS11_L1_PROT_TYPE] = { TLV_TYPE_TV }, - [NM_ATT_BS11_BIT_ERR_THESH] = { TLV_TYPE_FIXED, 2 }, - [NM_ATT_BS11_DIVERSITY] = { TLV_TYPE_TLV }, - [NM_ATT_BS11_LMT_LOGON_SESSION]={ TLV_TYPE_TLV }, - [NM_ATT_BS11_LMT_LOGIN_TIME] = { TLV_TYPE_TLV }, - [NM_ATT_BS11_LMT_USER_ACC_LEV] ={ TLV_TYPE_TLV }, - [NM_ATT_BS11_LMT_USER_NAME] = { TLV_TYPE_TLV }, - [NM_ATT_BS11_BTS_STATE] = { TLV_TYPE_TLV }, - [NM_ATT_BS11_E1_STATE] = { TLV_TYPE_TLV }, - [NM_ATT_BS11_PLL_MODE] = { TLV_TYPE_TLV }, - [NM_ATT_BS11_PLL] = { TLV_TYPE_TLV }, - [NM_ATT_BS11_CCLK_ACCURACY] = { TLV_TYPE_TV }, - [NM_ATT_BS11_CCLK_TYPE] = { TLV_TYPE_TV }, - /* ip.access specifics */ - [NM_ATT_IPACC_RSL_BSC_IP] = { TLV_TYPE_FIXED, 4 }, - [NM_ATT_IPACC_RSL_BSC_PORT] = { TLV_TYPE_FIXED, 2 }, - [NM_ATT_IPACC_PRIM_OML_IP] = { TLV_TYPE_FIXED, 6 }, - [0x95] = { TLV_TYPE_FIXED, 2 }, - [0x85] = { TLV_TYPE_TV }, - - }, -}; - -int abis_nm_tlv_parse(struct tlv_parsed *tp, const u_int8_t *buf, int len) -{ - return tlv_parse(tp, &nm_att_tlvdef, buf, len, 0, 0); -} - -static int is_in_arr(enum abis_nm_msgtype mt, const enum abis_nm_msgtype *arr, int size) -{ - int i; - - for (i = 0; i < size; i++) { - if (arr[i] == mt) - return 1; - } - - return 0; -} - -#if 0 -/* is this msgtype the usual ACK/NACK type ? */ -static int is_ack_nack(enum abis_nm_msgtype mt) -{ - return !is_in_arr(mt, no_ack_nack, ARRAY_SIZE(no_ack_nack)); -} -#endif - -/* is this msgtype a report ? */ -static int is_report(enum abis_nm_msgtype mt) -{ - return is_in_arr(mt, reports, ARRAY_SIZE(reports)); -} - -#define MT_ACK(x) (x+1) -#define MT_NACK(x) (x+2) - -static void fill_om_hdr(struct abis_om_hdr *oh, u_int8_t len) -{ - oh->mdisc = ABIS_OM_MDISC_FOM; - oh->placement = ABIS_OM_PLACEMENT_ONLY; - oh->sequence = 0; - oh->length = len; -} - -static void fill_om_fom_hdr(struct abis_om_hdr *oh, u_int8_t len, - u_int8_t msg_type, u_int8_t obj_class, - u_int8_t bts_nr, u_int8_t trx_nr, u_int8_t ts_nr) -{ - struct abis_om_fom_hdr *foh = - (struct abis_om_fom_hdr *) oh->data; - - fill_om_hdr(oh, len+sizeof(*foh)); - foh->msg_type = msg_type; - foh->obj_class = obj_class; - foh->obj_inst.bts_nr = bts_nr; - foh->obj_inst.trx_nr = trx_nr; - foh->obj_inst.ts_nr = ts_nr; -} - -static struct msgb *nm_msgb_alloc(void) -{ - return msgb_alloc_headroom(OM_ALLOC_SIZE, OM_HEADROOM_SIZE); -} - -/* Send a OML NM Message from BSC to BTS */ -int abis_nm_sendmsg(struct gsm_bts *bts, struct msgb *msg) -{ - msg->trx = bts->c0; - - return _abis_nm_sendmsg(msg); -} - -static int abis_nm_rcvmsg_sw(struct msgb *mb); - -static const char *obj_class_name(u_int8_t oc) -{ - switch (oc) { - case NM_OC_SITE_MANAGER: - return "SITE MANAGER"; - case NM_OC_BTS: - return "BTS"; - case NM_OC_RADIO_CARRIER: - return "RADIO CARRIER"; - case NM_OC_BASEB_TRANSC: - return "BASEBAND TRANSCEIVER"; - case NM_OC_CHANNEL: - return "CHANNEL"; - case NM_OC_BS11_ADJC: - return "ADJC"; - case NM_OC_BS11_HANDOVER: - return "HANDOVER"; - case NM_OC_BS11_PWR_CTRL: - return "POWER CONTROL"; - case NM_OC_BS11_BTSE: - return "BTSE"; - case NM_OC_BS11_RACK: - return "RACK"; - case NM_OC_BS11_TEST: - return "TEST"; - case NM_OC_BS11_ENVABTSE: - return "ENVABTSE"; - case NM_OC_BS11_BPORT: - return "BPORT"; - case NM_OC_GPRS_NSE: - return "GPRS NSE"; - case NM_OC_GPRS_CELL: - return "GPRS CELL"; - case NM_OC_GPRS_NSVC0: - return "GPRS NSVC0"; - case NM_OC_GPRS_NSVC1: - return "GPRS NSVC1"; - case NM_OC_BS11: - return "SIEMENSHW"; - } - - return "UNKNOWN"; -} - -const char *nm_opstate_name(u_int8_t os) -{ - switch (os) { - case 1: - return "Disabled"; - case 2: - return "Enabled"; - case 0xff: - return "NULL"; - default: - return "RFU"; - } -} - -/* Chapter 9.4.7 */ -static const char *avail_names[] = { - "In test", - "Failed", - "Power off", - "Off line", - "<not used>", - "Dependency", - "Degraded", - "Not installed", -}; - -const char *nm_avail_name(u_int8_t avail) -{ - if (avail == 0xff) - return "OK"; - if (avail >= ARRAY_SIZE(avail_names)) - return "UNKNOWN"; - return avail_names[avail]; -} - -const char *nm_adm_name(u_int8_t adm) -{ - switch (adm) { - case 1: - return "Locked"; - case 2: - return "Unlocked"; - case 3: - return "Shutdown"; - default: - return "<not used>"; - } -} - -/* 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, - struct abis_om_obj_inst *obj_inst) -{ - struct gsm_bts_trx *trx; - struct gsm_nm_state *nm_state = NULL; - - switch (obj_class) { - case NM_OC_BTS: - nm_state = &bts->nm_state; - break; - case NM_OC_RADIO_CARRIER: - if (obj_inst->trx_nr >= bts->num_trx) - return NULL; - trx = &bts->trx[obj_inst->trx_nr]; - nm_state = &trx->nm_state; - break; - case NM_OC_BASEB_TRANSC: - if (obj_inst->trx_nr >= bts->num_trx) - return NULL; - trx = &bts->trx[obj_inst->trx_nr]; - nm_state = &trx->bb_transc.nm_state; - break; - case NM_OC_CHANNEL: - if (obj_inst->trx_nr > bts->num_trx) - return NULL; - trx = &bts->trx[obj_inst->trx_nr]; - if (obj_inst->ts_nr >= TRX_NR_TS) - return NULL; - nm_state = &trx->ts[obj_inst->ts_nr].nm_state; - break; - case NM_OC_SITE_MANAGER: - nm_state = &bts->site_mgr.nm_state; - break; - case NM_OC_BS11: - switch (obj_inst->bts_nr) { - case BS11_OBJ_CCLK: - nm_state = &bts->bs11.cclk.nm_state; - break; - case BS11_OBJ_BBSIG: - if (obj_inst->ts_nr > bts->num_trx) - return NULL; - trx = &bts->trx[obj_inst->ts_nr]; - nm_state = &trx->bs11.bbsig.nm_state; - break; - case BS11_OBJ_PA: - if (obj_inst->ts_nr > bts->num_trx) - return NULL; - trx = &bts->trx[obj_inst->ts_nr]; - nm_state = &trx->bs11.pa.nm_state; - break; - default: - return NULL; - } - case NM_OC_BS11_RACK: - nm_state = &bts->bs11.rack.nm_state; - break; - case NM_OC_BS11_ENVABTSE: - if (obj_inst->trx_nr > ARRAY_SIZE(bts->bs11.envabtse)) - return NULL; - nm_state = &bts->bs11.envabtse[obj_inst->trx_nr].nm_state; - break; - } - return nm_state; -} - -/* obtain the in-memory data structure of a given object instance */ -static void * -objclass2obj(struct gsm_bts *bts, u_int8_t obj_class, - struct abis_om_obj_inst *obj_inst) -{ - struct gsm_bts_trx *trx; - void *obj = NULL; - - switch (obj_class) { - case NM_OC_BTS: - obj = bts; - break; - case NM_OC_RADIO_CARRIER: - if (obj_inst->trx_nr >= bts->num_trx) - return NULL; - trx = &bts->trx[obj_inst->trx_nr]; - obj = trx; - break; - case NM_OC_BASEB_TRANSC: - if (obj_inst->trx_nr >= bts->num_trx) - return NULL; - trx = &bts->trx[obj_inst->trx_nr]; - obj = &trx->bb_transc; - break; - case NM_OC_CHANNEL: - if (obj_inst->trx_nr > bts->num_trx) - return NULL; - trx = &bts->trx[obj_inst->trx_nr]; - if (obj_inst->ts_nr >= TRX_NR_TS) - return NULL; - obj = &trx->ts[obj_inst->ts_nr]; - break; - case NM_OC_SITE_MANAGER: - obj = &bts->site_mgr; - break; - } - return obj; -} - -/* Update the administrative state of a given object in our in-memory data - * structures and send an event to the higher layer */ -static int update_admstate(struct gsm_bts *bts, u_int8_t obj_class, - struct abis_om_obj_inst *obj_inst, u_int8_t adm_state) -{ - struct gsm_nm_state *nm_state, new_state; - void *obj; - int rc; - - obj = objclass2obj(bts, obj_class, obj_inst); - nm_state = objclass2nmstate(bts, obj_class, obj_inst); - if (!nm_state) - return -1; - - new_state = *nm_state; - new_state.administrative = adm_state; - - rc = nm_state_event(EVT_STATECHG_ADM, obj_class, obj, nm_state, &new_state); - - nm_state->administrative = adm_state; - - return rc; -} - -static int abis_nm_rx_statechg_rep(struct msgb *mb) -{ - struct abis_om_hdr *oh = msgb_l2(mb); - struct abis_om_fom_hdr *foh = msgb_l3(mb); - struct gsm_bts *bts = mb->trx->bts; - struct tlv_parsed tp; - struct gsm_nm_state *nm_state, new_state; - int rc; - - DEBUGPC(DNM, "STATE CHG: "); - - memset(&new_state, 0, sizeof(new_state)); - - nm_state = objclass2nmstate(bts, foh->obj_class, &foh->obj_inst); - if (!nm_state) { - DEBUGPC(DNM, "\n"); - return -EINVAL; - } - - new_state = *nm_state; - - abis_nm_tlv_parse(&tp, foh->data, oh->length-sizeof(*foh)); - if (TLVP_PRESENT(&tp, NM_ATT_OPER_STATE)) { - new_state.operational = *TLVP_VAL(&tp, NM_ATT_OPER_STATE); - DEBUGPC(DNM, "OP_STATE=%s ", nm_opstate_name(new_state.operational)); - } - if (TLVP_PRESENT(&tp, NM_ATT_AVAIL_STATUS)) { - if (TLVP_LEN(&tp, NM_ATT_AVAIL_STATUS) == 0) - new_state.availability = 0xff; - else - new_state.availability = *TLVP_VAL(&tp, NM_ATT_AVAIL_STATUS); - DEBUGPC(DNM, "AVAIL=%s(%02x) ", nm_avail_name(new_state.availability), - new_state.availability); - } - if (TLVP_PRESENT(&tp, NM_ATT_ADM_STATE)) { - new_state.administrative = *TLVP_VAL(&tp, NM_ATT_ADM_STATE); - DEBUGPC(DNM, "ADM=%02x ", nm_adm_name(new_state.administrative)); - } - DEBUGPC(DNM, "\n"); - - if (memcmp(&new_state, nm_state, sizeof(new_state))) { - /* Update the operational state of a given object in our in-memory data - * structures and send an event to the higher layer */ - void *obj = objclass2obj(bts, foh->obj_class, &foh->obj_inst); - rc = nm_state_event(EVT_STATECHG_OPER, foh->obj_class, obj, nm_state, &new_state); - *nm_state = new_state; - } -#if 0 - if (op_state == 1) { - /* try to enable objects that are disabled */ - abis_nm_opstart(bts, foh->obj_class, - foh->obj_inst.bts_nr, - foh->obj_inst.trx_nr, - foh->obj_inst.ts_nr); - } -#endif - return 0; -} - -static int rx_fail_evt_rep(struct msgb *mb) -{ - struct abis_om_hdr *oh = msgb_l2(mb); - struct abis_om_fom_hdr *foh = msgb_l3(mb); - struct tlv_parsed tp; - - DEBUGPC(DNM, "Failure Event Report "); - - abis_nm_tlv_parse(&tp, foh->data, oh->length-sizeof(*foh)); - - if (TLVP_PRESENT(&tp, NM_ATT_EVENT_TYPE)) - DEBUGPC(DNM, "Type=%s ", event_type_name(*TLVP_VAL(&tp, NM_ATT_EVENT_TYPE))); - if (TLVP_PRESENT(&tp, NM_ATT_SEVERITY)) - DEBUGPC(DNM, "Severity=%s ", severity_name(*TLVP_VAL(&tp, NM_ATT_SEVERITY))); - - DEBUGPC(DNM, "\n"); - - return 0; -} - -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); - - //nmh->cfg->report_cb(mb, foh); - - switch (mt) { - case NM_MT_STATECHG_EVENT_REP: - return abis_nm_rx_statechg_rep(mb); - break; - case NM_MT_SW_ACTIVATED_REP: - DEBUGPC(DNM, "Software Activated Report\n"); - dispatch_signal(SS_NM, S_NM_SW_ACTIV_REP, mb); - break; - case NM_MT_FAILURE_EVENT_REP: - rx_fail_evt_rep(mb); - dispatch_signal(SS_NM, S_NM_FAIL_REP, mb); - break; - default: - DEBUGPC(DNM, "reporting NM MT 0x%02x\n", mt); - break; - - }; - - return 0; -} - -/* Activate the specified software into the BTS */ -static int ipacc_sw_activate(struct gsm_bts *bts, u_int8_t obj_class, u_int8_t i0, u_int8_t i1, - u_int8_t i2, u_int8_t *sw_desc, u_int8_t swdesc_len) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - u_int8_t len = swdesc_len; - u_int8_t *trailer; - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, len, NM_MT_ACTIVATE_SW, obj_class, i0, i1, i2); - - trailer = msgb_put(msg, swdesc_len); - memcpy(trailer, sw_desc, swdesc_len); - - return abis_nm_sendmsg(bts, msg); -} - -static int abis_nm_rx_sw_act_req(struct msgb *mb) -{ - struct abis_om_hdr *oh = msgb_l2(mb); - struct abis_om_fom_hdr *foh = msgb_l3(mb); - int nack = 0; - int ret; - - DEBUGP(DNM, "Software Activate Request "); - - if (foh->obj_class >= 0xf0 && foh->obj_class <= 0xf3) { - DEBUGPC(DNM, "NACKing for GPRS obj_class 0x%02x\n", foh->obj_class); - nack = 1; - } else - DEBUGPC(DNM, "ACKing and Activating\n"); - - ret = abis_nm_sw_act_req_ack(mb->trx->bts, foh->obj_class, - foh->obj_inst.bts_nr, - foh->obj_inst.trx_nr, - foh->obj_inst.ts_nr, nack, - foh->data, oh->length-sizeof(*foh)); - - if (nack) - return ret; - - /* FIXME: properly parse attributes */ - return ipacc_sw_activate(mb->trx->bts, foh->obj_class, - foh->obj_inst.bts_nr, - foh->obj_inst.trx_nr, - foh->obj_inst.ts_nr, - foh->data + oh->length-sizeof(*foh)-22, 22); -} - -/* Receive a CHANGE_ADM_STATE_ACK, parse the TLV and update local state */ -static int abis_nm_rx_chg_adm_state_ack(struct msgb *mb) -{ - struct abis_om_hdr *oh = msgb_l2(mb); - struct abis_om_fom_hdr *foh = msgb_l3(mb); - struct tlv_parsed tp; - u_int8_t adm_state; - - abis_nm_tlv_parse(&tp, foh->data, oh->length-sizeof(*foh)); - if (!TLVP_PRESENT(&tp, NM_ATT_ADM_STATE)) - return -EINVAL; - - adm_state = *TLVP_VAL(&tp, NM_ATT_ADM_STATE); - - return update_admstate(mb->trx->bts, foh->obj_class, &foh->obj_inst, adm_state); -} - -static int abis_nm_rx_lmt_event(struct msgb *mb) -{ - struct abis_om_hdr *oh = msgb_l2(mb); - struct abis_om_fom_hdr *foh = msgb_l3(mb); - struct tlv_parsed tp; - - DEBUGP(DNM, "LMT Event "); - abis_nm_tlv_parse(&tp, foh->data, oh->length-sizeof(*foh)); - if (TLVP_PRESENT(&tp, NM_ATT_BS11_LMT_LOGON_SESSION) && - TLVP_LEN(&tp, NM_ATT_BS11_LMT_LOGON_SESSION) >= 1) { - u_int8_t onoff = *TLVP_VAL(&tp, NM_ATT_BS11_LMT_LOGON_SESSION); - DEBUGPC(DNM, "LOG%s ", onoff ? "ON" : "OFF"); - } - if (TLVP_PRESENT(&tp, NM_ATT_BS11_LMT_USER_ACC_LEV) && - TLVP_LEN(&tp, NM_ATT_BS11_LMT_USER_ACC_LEV) >= 1) { - u_int8_t level = *TLVP_VAL(&tp, NM_ATT_BS11_LMT_USER_ACC_LEV); - DEBUGPC(DNM, "Level=%u ", level); - } - if (TLVP_PRESENT(&tp, NM_ATT_BS11_LMT_USER_NAME) && - TLVP_LEN(&tp, NM_ATT_BS11_LMT_USER_NAME) >= 1) { - char *name = (char *) TLVP_VAL(&tp, NM_ATT_BS11_LMT_USER_NAME); - DEBUGPC(DNM, "Username=%s ", name); - } - DEBUGPC(DNM, "\n"); - /* FIXME: parse LMT LOGON TIME */ - return 0; -} - -/* Receive a OML NM Message from BTS */ -static int abis_nm_rcvmsg_fom(struct msgb *mb) -{ - struct abis_om_hdr *oh = msgb_l2(mb); - struct abis_om_fom_hdr *foh = msgb_l3(mb); - u_int8_t mt = foh->msg_type; - - /* check for unsolicited message */ - if (is_report(mt)) - return abis_nm_rcvmsg_report(mb); - - if (is_in_arr(mt, sw_load_msgs, ARRAY_SIZE(sw_load_msgs))) - return abis_nm_rcvmsg_sw(mb); - - if (is_in_arr(mt, nacks, ARRAY_SIZE(nacks))) { - struct tlv_parsed tp; - if (nack_names[mt]) - DEBUGP(DNM, "%s NACK ", nack_names[mt]); - /* FIXME: NACK cause */ - else - DEBUGP(DNM, "NACK 0x%02x ", mt); - - abis_nm_tlv_parse(&tp, foh->data, oh->length-sizeof(*foh)); - if (TLVP_PRESENT(&tp, NM_ATT_NACK_CAUSES)) - DEBUGPC(DNM, "CAUSE=%s\n", - nack_cause_name(*TLVP_VAL(&tp, NM_ATT_NACK_CAUSES))); - else - DEBUGPC(DNM, "\n"); - } -#if 0 - /* check if last message is to be acked */ - if (is_ack_nack(nmh->last_msgtype)) { - if (mt == MT_ACK(nmh->last_msgtype)) { - fprintf(stderr, "received ACK (0x%x)\n", - foh->msg_type); - /* we got our ACK, continue sending the next msg */ - } else if (mt == MT_NACK(nmh->last_msgtype)) { - /* we got a NACK, signal this to the caller */ - fprintf(stderr, "received NACK (0x%x)\n", - foh->msg_type); - /* FIXME: somehow signal this to the caller */ - } else { - /* really strange things happen */ - return -EINVAL; - } - } -#endif - - switch (mt) { - case NM_MT_CHG_ADM_STATE_ACK: - return abis_nm_rx_chg_adm_state_ack(mb); - break; - case NM_MT_SW_ACT_REQ: - return abis_nm_rx_sw_act_req(mb); - break; - case NM_MT_BS11_LMT_SESSION: - return abis_nm_rx_lmt_event(mb); - break; - } - - return 0; -} - -static int abis_nm_rx_ipacc(struct msgb *mb); - -static int abis_nm_rcvmsg_manuf(struct msgb *mb) -{ - int rc; - int bts_type = mb->trx->bts->type; - - switch (bts_type) { - case GSM_BTS_TYPE_NANOBTS_900: - case GSM_BTS_TYPE_NANOBTS_1800: - rc = abis_nm_rx_ipacc(mb); - break; - default: - fprintf(stderr, "don't know how to parse OML for this " - "BTS type (%u)\n", bts_type); - rc = 0; - break; - } - - return rc; -} - -/* High-Level API */ -/* Entry-point where L2 OML from BTS enters the NM code */ -int abis_nm_rcvmsg(struct msgb *msg) -{ - struct abis_om_hdr *oh = msgb_l2(msg); - int rc = 0; - - /* Various consistency checks */ - if (oh->placement != ABIS_OM_PLACEMENT_ONLY) { - fprintf(stderr, "ABIS OML placement 0x%x not supported\n", - oh->placement); - return -EINVAL; - } - if (oh->sequence != 0) { - fprintf(stderr, "ABIS OML sequence 0x%x != 0x00\n", - oh->sequence); - return -EINVAL; - } -#if 0 - unsigned int l2_len = msg->tail - (u_int8_t *)msgb_l2(msg); - unsigned int hlen = sizeof(*oh) + sizeof(struct abis_om_fom_hdr); - if (oh->length + hlen > l2_len) { - fprintf(stderr, "ABIS OML truncated message (%u > %u)\n", - oh->length + sizeof(*oh), l2_len); - return -EINVAL; - } - if (oh->length + hlen < l2_len) - fprintf(stderr, "ABIS OML message with extra trailer?!? (oh->len=%d, sizeof_oh=%d l2_len=%d\n", oh->length, sizeof(*oh), l2_len); -#endif - msg->l3h = (unsigned char *)oh + sizeof(*oh); - - switch (oh->mdisc) { - case ABIS_OM_MDISC_FOM: - rc = abis_nm_rcvmsg_fom(msg); - break; - case ABIS_OM_MDISC_MANUF: - rc = abis_nm_rcvmsg_manuf(msg); - break; - case ABIS_OM_MDISC_MMI: - case ABIS_OM_MDISC_TRAU: - fprintf(stderr, "unimplemented ABIS OML message discriminator 0x%x\n", - oh->mdisc); - break; - default: - fprintf(stderr, "unknown ABIS OML message discriminator 0x%x\n", - oh->mdisc); - return -EINVAL; - } - - msgb_free(msg); - return rc; -} - -#if 0 -/* initialized all resources */ -struct abis_nm_h *abis_nm_init(struct abis_nm_cfg *cfg) -{ - struct abis_nm_h *nmh; - - nmh = malloc(sizeof(*nmh)); - if (!nmh) - return NULL; - - nmh->cfg = cfg; - - return nmh; -} - -/* free all resources */ -void abis_nm_fini(struct abis_nm_h *nmh) -{ - free(nmh); -} -#endif - -/* Here we are trying to define a high-level API that can be used by - * the actual BSC implementation. However, the architecture is currently - * still under design. Ideally the calls to this API would be synchronous, - * while the underlying stack behind the APi runs in a traditional select - * based state machine. - */ - -/* 6.2 Software Load: */ -enum sw_state { - SW_STATE_NONE, - SW_STATE_WAIT_INITACK, - SW_STATE_WAIT_SEGACK, - SW_STATE_WAIT_ENDACK, - SW_STATE_WAIT_ACTACK, - SW_STATE_ERROR, -}; - -struct abis_nm_sw { - struct gsm_bts *bts; - gsm_cbfn *cbfn; - void *cb_data; - int forced; - - /* this will become part of the SW LOAD INITIATE */ - u_int8_t obj_class; - u_int8_t obj_instance[3]; - - u_int8_t file_id[255]; - u_int8_t file_id_len; - - u_int8_t file_version[255]; - u_int8_t file_version_len; - - u_int8_t window_size; - u_int8_t seg_in_window; - - int fd; - FILE *stream; - enum sw_state state; - int last_seg; -}; - -static struct abis_nm_sw g_sw; - -/* 6.2.1 / 8.3.1: Load Data Initiate */ -static int sw_load_init(struct abis_nm_sw *sw) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - u_int8_t len = 3*2 + sw->file_id_len + sw->file_version_len; - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, len, NM_MT_LOAD_INIT, sw->obj_class, - sw->obj_instance[0], sw->obj_instance[1], - sw->obj_instance[2]); - - /* FIXME: this is BS11 specific format */ - msgb_tlv_put(msg, NM_ATT_FILE_ID, sw->file_id_len, sw->file_id); - msgb_tlv_put(msg, NM_ATT_FILE_VERSION, sw->file_version_len, - sw->file_version); - msgb_tv_put(msg, NM_ATT_WINDOW_SIZE, sw->window_size); - - return abis_nm_sendmsg(sw->bts, msg); -} - -static int is_last_line(FILE *stream) -{ - char next_seg_buf[256]; - long pos; - - /* check if we're sending the last line */ - pos = ftell(stream); - if (!fgets(next_seg_buf, sizeof(next_seg_buf)-2, stream)) { - fseek(stream, pos, SEEK_SET); - return 1; - } - - fseek(stream, pos, SEEK_SET); - return 0; -} - -/* 6.2.2 / 8.3.2 Load Data Segment */ -static int sw_load_segment(struct abis_nm_sw *sw) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - char seg_buf[256]; - char *line_buf = seg_buf+2; - unsigned char *tlv; - u_int8_t len; - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - - switch (sw->bts->type) { - case GSM_BTS_TYPE_BS11: - if (fgets(line_buf, sizeof(seg_buf)-2, sw->stream) == NULL) { - perror("fgets reading segment"); - return -EINVAL; - } - seg_buf[0] = 0x00; - - /* check if we're sending the last line */ - sw->last_seg = is_last_line(sw->stream); - if (sw->last_seg) - seg_buf[1] = 0; - else - seg_buf[1] = 1 + sw->seg_in_window++; - - len = strlen(line_buf) + 2; - tlv = msgb_put(msg, TLV_GROSS_LEN(len)); - tlv_put(tlv, NM_ATT_BS11_FILE_DATA, len, (u_int8_t *)seg_buf); - /* BS11 wants CR + LF in excess of the TLV length !?! */ - tlv[1] -= 2; - - /* we only now know the exact length for the OM hdr */ - len = strlen(line_buf)+2; - break; - default: - /* FIXME: Other BTS types */ - return -1; - } - - fill_om_fom_hdr(oh, len, NM_MT_LOAD_SEG, sw->obj_class, - sw->obj_instance[0], sw->obj_instance[1], - sw->obj_instance[2]); - - return abis_nm_sendmsg(sw->bts, msg); -} - -/* 6.2.4 / 8.3.4 Load Data End */ -static int sw_load_end(struct abis_nm_sw *sw) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - u_int8_t len = 2*2 + sw->file_id_len + sw->file_version_len; - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, len, NM_MT_LOAD_END, sw->obj_class, - sw->obj_instance[0], sw->obj_instance[1], - sw->obj_instance[2]); - - /* FIXME: this is BS11 specific format */ - msgb_tlv_put(msg, NM_ATT_FILE_ID, sw->file_id_len, sw->file_id); - msgb_tlv_put(msg, NM_ATT_FILE_VERSION, sw->file_version_len, - sw->file_version); - - return abis_nm_sendmsg(sw->bts, msg); -} - -/* Activate the specified software into the BTS */ -static int sw_activate(struct abis_nm_sw *sw) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - u_int8_t len = 2*2 + sw->file_id_len + sw->file_version_len; - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, len, NM_MT_ACTIVATE_SW, sw->obj_class, - sw->obj_instance[0], sw->obj_instance[1], - sw->obj_instance[2]); - - /* FIXME: this is BS11 specific format */ - msgb_tlv_put(msg, NM_ATT_FILE_ID, sw->file_id_len, sw->file_id); - msgb_tlv_put(msg, NM_ATT_FILE_VERSION, sw->file_version_len, - sw->file_version); - - return abis_nm_sendmsg(sw->bts, msg); -} - -static int sw_open_file(struct abis_nm_sw *sw, const char *fname) -{ - char file_id[12+1]; - char file_version[80+1]; - int rc; - - sw->fd = open(fname, O_RDONLY); - if (sw->fd < 0) - return sw->fd; - - switch (sw->bts->type) { - case GSM_BTS_TYPE_BS11: - sw->stream = fdopen(sw->fd, "r"); - if (!sw->stream) { - perror("fdopen"); - return -1; - } - /* read first line and parse file ID and VERSION */ - rc = fscanf(sw->stream, "@(#)%12s:%80s\r\n", - file_id, file_version); - if (rc != 2) { - perror("parsing header line of software file"); - return -1; - } - strcpy((char *)sw->file_id, file_id); - sw->file_id_len = strlen(file_id); - strcpy((char *)sw->file_version, file_version); - sw->file_version_len = strlen(file_version); - /* rewind to start of file */ - rewind(sw->stream); - break; - default: - /* We don't know how to treat them yet */ - close(sw->fd); - return -EINVAL; - } - - return 0; -} - -static void sw_close_file(struct abis_nm_sw *sw) -{ - switch (sw->bts->type) { - case GSM_BTS_TYPE_BS11: - fclose(sw->stream); - break; - default: - close(sw->fd); - break; - } -} - -/* Fill the window */ -static int sw_fill_window(struct abis_nm_sw *sw) -{ - int rc; - - while (sw->seg_in_window < sw->window_size) { - rc = sw_load_segment(sw); - if (rc < 0) - return rc; - if (sw->last_seg) - break; - } - return 0; -} - -/* callback function from abis_nm_rcvmsg() handler */ -static int abis_nm_rcvmsg_sw(struct msgb *mb) -{ - struct abis_om_fom_hdr *foh = msgb_l3(mb); - int rc = -1; - struct abis_nm_sw *sw = &g_sw; - enum sw_state old_state = sw->state; - - //DEBUGP(DNM, "state %u, NM MT 0x%02x\n", sw->state, foh->msg_type); - - switch (sw->state) { - case SW_STATE_WAIT_INITACK: - switch (foh->msg_type) { - case NM_MT_LOAD_INIT_ACK: - /* fill window with segments */ - if (sw->cbfn) - sw->cbfn(GSM_HOOK_NM_SWLOAD, - NM_MT_LOAD_INIT_ACK, mb, - sw->cb_data, NULL); - rc = sw_fill_window(sw); - sw->state = SW_STATE_WAIT_SEGACK; - break; - case NM_MT_LOAD_INIT_NACK: - if (sw->forced) { - DEBUGP(DNM, "FORCED: Ignoring Software Load " - "Init NACK\n"); - if (sw->cbfn) - sw->cbfn(GSM_HOOK_NM_SWLOAD, - NM_MT_LOAD_INIT_ACK, mb, - sw->cb_data, NULL); - rc = sw_fill_window(sw); - sw->state = SW_STATE_WAIT_SEGACK; - } else { - DEBUGP(DNM, "Software Load Init NACK\n"); - /* FIXME: cause */ - if (sw->cbfn) - sw->cbfn(GSM_HOOK_NM_SWLOAD, - NM_MT_LOAD_INIT_NACK, mb, - sw->cb_data, NULL); - sw->state = SW_STATE_ERROR; - } - break; - } - break; - case SW_STATE_WAIT_SEGACK: - switch (foh->msg_type) { - case NM_MT_LOAD_SEG_ACK: - if (sw->cbfn) - sw->cbfn(GSM_HOOK_NM_SWLOAD, - NM_MT_LOAD_SEG_ACK, mb, - sw->cb_data, NULL); - sw->seg_in_window = 0; - if (!sw->last_seg) { - /* fill window with more segments */ - rc = sw_fill_window(sw); - sw->state = SW_STATE_WAIT_SEGACK; - } else { - /* end the transfer */ - sw->state = SW_STATE_WAIT_ENDACK; - rc = sw_load_end(sw); - } - break; - } - break; - case SW_STATE_WAIT_ENDACK: - switch (foh->msg_type) { - case NM_MT_LOAD_END_ACK: - sw_close_file(sw); - DEBUGP(DNM, "Software Load End (BTS %u)\n", - sw->bts->nr); - sw->state = SW_STATE_NONE; - if (sw->cbfn) - sw->cbfn(GSM_HOOK_NM_SWLOAD, - NM_MT_LOAD_END_ACK, mb, - sw->cb_data, NULL); - break; - case NM_MT_LOAD_END_NACK: - if (sw->forced) { - DEBUGP(DNM, "FORCED: Ignoring Software Load" - "End NACK\n"); - sw->state = SW_STATE_NONE; - if (sw->cbfn) - sw->cbfn(GSM_HOOK_NM_SWLOAD, - NM_MT_LOAD_END_ACK, mb, - sw->cb_data, NULL); - } else { - DEBUGP(DNM, "Software Load End NACK\n"); - /* FIXME: cause */ - sw->state = SW_STATE_ERROR; - if (sw->cbfn) - sw->cbfn(GSM_HOOK_NM_SWLOAD, - NM_MT_LOAD_END_NACK, mb, - sw->cb_data, NULL); - } - break; - } - case SW_STATE_WAIT_ACTACK: - switch (foh->msg_type) { - case NM_MT_ACTIVATE_SW_ACK: - /* we're done */ - DEBUGP(DNM, "Activate Software DONE!\n"); - sw->state = SW_STATE_NONE; - rc = 0; - if (sw->cbfn) - sw->cbfn(GSM_HOOK_NM_SWLOAD, - NM_MT_ACTIVATE_SW_ACK, mb, - sw->cb_data, NULL); - break; - case NM_MT_ACTIVATE_SW_NACK: - DEBUGP(DNM, "Activate Software NACK\n"); - /* FIXME: cause */ - sw->state = SW_STATE_ERROR; - if (sw->cbfn) - sw->cbfn(GSM_HOOK_NM_SWLOAD, - NM_MT_ACTIVATE_SW_NACK, mb, - sw->cb_data, NULL); - break; - } - case SW_STATE_NONE: - switch (foh->msg_type) { - case NM_MT_ACTIVATE_SW_ACK: - rc = 0; - break; - } - break; - case SW_STATE_ERROR: - break; - } - - if (rc) - DEBUGP(DNM, "unexpected NM MT 0x%02x in state %u -> %u\n", - foh->msg_type, old_state, sw->state); - - return rc; -} - -/* Load the specified software into the BTS */ -int abis_nm_software_load(struct gsm_bts *bts, const char *fname, - u_int8_t win_size, int forced, - gsm_cbfn *cbfn, void *cb_data) -{ - struct abis_nm_sw *sw = &g_sw; - int rc; - - DEBUGP(DNM, "Software Load (BTS %u, File \"%s\")\n", - bts->nr, fname); - - if (sw->state != SW_STATE_NONE) - return -EBUSY; - - sw->bts = bts; - sw->obj_class = NM_OC_SITE_MANAGER; - sw->obj_instance[0] = 0xff; - sw->obj_instance[1] = 0xff; - sw->obj_instance[2] = 0xff; - sw->window_size = win_size; - sw->state = SW_STATE_WAIT_INITACK; - sw->cbfn = cbfn; - sw->cb_data = cb_data; - sw->forced = forced; - - rc = sw_open_file(sw, fname); - if (rc < 0) { - sw->state = SW_STATE_NONE; - return rc; - } - - return sw_load_init(sw); -} - -int abis_nm_software_load_status(struct gsm_bts *bts) -{ - struct abis_nm_sw *sw = &g_sw; - struct stat st; - int rc, percent; - - rc = fstat(sw->fd, &st); - if (rc < 0) { - perror("ERROR during stat"); - return rc; - } - - percent = (ftell(sw->stream) * 100) / st.st_size; - return percent; -} - -/* Activate the specified software into the BTS */ -int abis_nm_software_activate(struct gsm_bts *bts, const char *fname, - gsm_cbfn *cbfn, void *cb_data) -{ - struct abis_nm_sw *sw = &g_sw; - int rc; - - DEBUGP(DNM, "Activating Software (BTS %u, File \"%s\")\n", - bts->nr, fname); - - if (sw->state != SW_STATE_NONE) - return -EBUSY; - - sw->bts = bts; - sw->obj_class = NM_OC_SITE_MANAGER; - sw->obj_instance[0] = 0xff; - sw->obj_instance[1] = 0xff; - sw->obj_instance[2] = 0xff; - sw->state = SW_STATE_WAIT_ACTACK; - sw->cbfn = cbfn; - sw->cb_data = cb_data; - - /* Open the file in order to fill some sw struct members */ - rc = sw_open_file(sw, fname); - if (rc < 0) { - sw->state = SW_STATE_NONE; - return rc; - } - sw_close_file(sw); - - return sw_activate(sw); -} - -static void fill_nm_channel(struct abis_nm_channel *ch, u_int8_t bts_port, - u_int8_t ts_nr, u_int8_t subslot_nr) -{ - ch->attrib = NM_ATT_ABIS_CHANNEL; - ch->bts_port = bts_port; - ch->timeslot = ts_nr; - ch->subslot = subslot_nr; -} - -int abis_nm_establish_tei(struct gsm_bts *bts, u_int8_t trx_nr, - u_int8_t e1_port, u_int8_t e1_timeslot, u_int8_t e1_subslot, - u_int8_t tei) -{ - struct abis_om_hdr *oh; - struct abis_nm_channel *ch; - u_int8_t len = sizeof(*ch) + 2; - struct msgb *msg = nm_msgb_alloc(); - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, len, NM_MT_ESTABLISH_TEI, NM_OC_RADIO_CARRIER, - bts->bts_nr, trx_nr, 0xff); - - msgb_tv_put(msg, NM_ATT_TEI, tei); - - ch = (struct abis_nm_channel *) msgb_put(msg, sizeof(*ch)); - fill_nm_channel(ch, e1_port, e1_timeslot, e1_subslot); - - return abis_nm_sendmsg(bts, msg); -} - -/* connect signalling of one (BTS,TRX) to a particular timeslot on the E1 */ -int abis_nm_conn_terr_sign(struct gsm_bts_trx *trx, - u_int8_t e1_port, u_int8_t e1_timeslot, u_int8_t e1_subslot) -{ - struct gsm_bts *bts = trx->bts; - struct abis_om_hdr *oh; - struct abis_nm_channel *ch; - struct msgb *msg = nm_msgb_alloc(); - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, sizeof(*ch), NM_MT_CONN_TERR_SIGN, - NM_OC_RADIO_CARRIER, bts->bts_nr, trx->nr, 0xff); - - ch = (struct abis_nm_channel *) msgb_put(msg, sizeof(*ch)); - fill_nm_channel(ch, e1_port, e1_timeslot, e1_subslot); - - return abis_nm_sendmsg(bts, msg); -} - -#if 0 -int abis_nm_disc_terr_sign(struct abis_nm_h *h, struct abis_om_obj_inst *inst, - struct abis_nm_abis_channel *chan) -{ -} -#endif - -int abis_nm_conn_terr_traf(struct gsm_bts_trx_ts *ts, - u_int8_t e1_port, u_int8_t e1_timeslot, - u_int8_t e1_subslot) -{ - struct gsm_bts *bts = ts->trx->bts; - struct abis_om_hdr *oh; - struct abis_nm_channel *ch; - struct msgb *msg = nm_msgb_alloc(); - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, sizeof(*ch), NM_MT_CONN_TERR_TRAF, - NM_OC_CHANNEL, bts->bts_nr, ts->trx->nr, ts->nr); - - ch = (struct abis_nm_channel *) msgb_put(msg, sizeof(*ch)); - fill_nm_channel(ch, e1_port, e1_timeslot, e1_subslot); - - DEBUGP(DNM, "CONNECT TERR TRAF Um=%s E1=(%u,%u,%u)\n", - gsm_ts_name(ts), - e1_port, e1_timeslot, e1_subslot); - - return abis_nm_sendmsg(bts, msg); -} - -#if 0 -int abis_nm_disc_terr_traf(struct abis_nm_h *h, struct abis_om_obj_inst *inst, - struct abis_nm_abis_channel *chan, - u_int8_t subchan) -{ -} -#endif - -/* Chapter 8.6.1 */ -int abis_nm_set_bts_attr(struct gsm_bts *bts, u_int8_t *attr, int attr_len) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - u_int8_t *cur; - - DEBUGP(DNM, "Set BTS Attr (bts=%d)\n", bts->nr); - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, attr_len, NM_MT_SET_BTS_ATTR, NM_OC_BTS, bts->bts_nr, 0xff, 0xff); - cur = msgb_put(msg, attr_len); - memcpy(cur, attr, attr_len); - - return abis_nm_sendmsg(bts, msg); -} - -/* Chapter 8.6.2 */ -int abis_nm_set_radio_attr(struct gsm_bts_trx *trx, u_int8_t *attr, int attr_len) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - u_int8_t *cur; - - DEBUGP(DNM, "Set TRX Attr (bts=%d,trx=%d)\n", trx->bts->nr, trx->nr); - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, attr_len, NM_MT_SET_RADIO_ATTR, NM_OC_RADIO_CARRIER, - trx->bts->bts_nr, trx->nr, 0xff); - cur = msgb_put(msg, attr_len); - memcpy(cur, attr, attr_len); - - return abis_nm_sendmsg(trx->bts, msg); -} - -/* Chapter 8.6.3 */ -int abis_nm_set_channel_attr(struct gsm_bts_trx_ts *ts, u_int8_t chan_comb) -{ - struct gsm_bts *bts = ts->trx->bts; - struct abis_om_hdr *oh; - u_int16_t arfcn = htons(ts->trx->arfcn); - u_int8_t zero = 0x00; - struct msgb *msg = nm_msgb_alloc(); - u_int8_t len = 2 + 2; - - if (bts->type == GSM_BTS_TYPE_BS11) - len += 4 + 2 + 2 + 3; - - DEBUGP(DNM, "Set Chan Attr %s\n", gsm_ts_name(ts)); - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, len, NM_MT_SET_CHAN_ATTR, - NM_OC_CHANNEL, bts->bts_nr, - ts->trx->nr, ts->nr); - /* FIXME: don't send ARFCN list, hopping sequence, mAIO, ...*/ - if (bts->type == GSM_BTS_TYPE_BS11) - msgb_tlv16_put(msg, NM_ATT_ARFCN_LIST, 1, &arfcn); - msgb_tv_put(msg, NM_ATT_CHAN_COMB, chan_comb); - if (bts->type == GSM_BTS_TYPE_BS11) { - msgb_tv_put(msg, NM_ATT_HSN, 0x00); - msgb_tv_put(msg, NM_ATT_MAIO, 0x00); - } - msgb_tv_put(msg, NM_ATT_TSC, 0x07); /* training sequence */ - if (bts->type == GSM_BTS_TYPE_BS11) - msgb_tlv_put(msg, 0x59, 1, &zero); - - return abis_nm_sendmsg(bts, msg); -} - -int abis_nm_sw_act_req_ack(struct gsm_bts *bts, u_int8_t obj_class, u_int8_t i1, - u_int8_t i2, u_int8_t i3, int nack, u_int8_t *attr, int att_len) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - u_int8_t msgtype = NM_MT_SW_ACT_REQ_ACK; - u_int8_t len = att_len; - - if (nack) { - len += 2; - msgtype = NM_MT_SW_ACT_REQ_NACK; - } - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, att_len, msgtype, obj_class, i1, i2, i3); - - if (attr) { - u_int8_t *ptr = msgb_put(msg, att_len); - memcpy(ptr, attr, att_len); - } - if (nack) - msgb_tv_put(msg, NM_ATT_NACK_CAUSES, NM_NACK_OBJCLASS_NOTSUPP); - - return abis_nm_sendmsg(bts, msg); -} - -int abis_nm_raw_msg(struct gsm_bts *bts, int len, u_int8_t *rawmsg) -{ - struct msgb *msg = nm_msgb_alloc(); - struct abis_om_hdr *oh; - u_int8_t *data; - - oh = (struct abis_om_hdr *) msgb_put(msg, sizeof(*oh)); - fill_om_hdr(oh, len); - data = msgb_put(msg, len); - memcpy(data, rawmsg, len); - - return abis_nm_sendmsg(bts, msg); -} - -/* Siemens specific commands */ -static int __simple_cmd(struct gsm_bts *bts, u_int8_t msg_type) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, 0, msg_type, NM_OC_SITE_MANAGER, - 0xff, 0xff, 0xff); - - return abis_nm_sendmsg(bts, msg); -} - -/* Chapter 8.9.2 */ -int abis_nm_opstart(struct gsm_bts *bts, u_int8_t obj_class, u_int8_t i0, u_int8_t i1, u_int8_t i2) -{ - 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); - - return abis_nm_sendmsg(bts, msg); -} - -/* Chapter 8.8.5 */ -int abis_nm_chg_adm_state(struct gsm_bts *bts, u_int8_t obj_class, u_int8_t i0, - u_int8_t i1, u_int8_t i2, u_int8_t adm_state) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, 2, NM_MT_CHG_ADM_STATE, obj_class, i0, i1, i2); - msgb_tv_put(msg, NM_ATT_ADM_STATE, adm_state); - - return abis_nm_sendmsg(bts, msg); -} - - -int abis_nm_event_reports(struct gsm_bts *bts, int on) -{ - if (on == 0) - return __simple_cmd(bts, NM_MT_STOP_EVENT_REP); - else - return __simple_cmd(bts, NM_MT_REST_EVENT_REP); -} - -/* Siemens (or BS-11) specific commands */ - -int abis_nm_bs11_bsc_disconnect(struct gsm_bts *bts, int reconnect) -{ - if (reconnect == 0) - return __simple_cmd(bts, NM_MT_BS11_DISCONNECT); - else - return __simple_cmd(bts, NM_MT_BS11_RECONNECT); -} - -int abis_nm_bs11_restart(struct gsm_bts *bts) -{ - return __simple_cmd(bts, NM_MT_BS11_RESTART); -} - - -struct bs11_date_time { - u_int16_t year; - u_int8_t month; - u_int8_t day; - u_int8_t hour; - u_int8_t min; - u_int8_t sec; -} __attribute__((packed)); - - -void get_bs11_date_time(struct bs11_date_time *aet) -{ - time_t t; - struct tm *tm; - - t = time(NULL); - tm = localtime(&t); - aet->sec = tm->tm_sec; - aet->min = tm->tm_min; - aet->hour = tm->tm_hour; - aet->day = tm->tm_mday; - aet->month = tm->tm_mon; - aet->year = htons(1900 + tm->tm_year); -} - -int abis_nm_bs11_reset_resource(struct gsm_bts *bts) -{ - return __simple_cmd(bts, NM_MT_BS11_RESET_RESOURCE); -} - -int abis_nm_bs11_db_transmission(struct gsm_bts *bts, int begin) -{ - if (begin) - return __simple_cmd(bts, NM_MT_BS11_BEGIN_DB_TX); - else - return __simple_cmd(bts, NM_MT_BS11_END_DB_TX); -} - -int abis_nm_bs11_create_object(struct gsm_bts *bts, - enum abis_bs11_objtype type, u_int8_t idx, - u_int8_t attr_len, const u_int8_t *attr) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - u_int8_t *cur; - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, attr_len, NM_MT_BS11_CREATE_OBJ, - NM_OC_BS11, type, 0, idx); - cur = msgb_put(msg, attr_len); - memcpy(cur, attr, attr_len); - - return abis_nm_sendmsg(bts, msg); -} - -int abis_nm_bs11_delete_object(struct gsm_bts *bts, - enum abis_bs11_objtype type, u_int8_t idx) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, 0, NM_MT_BS11_DELETE_OBJ, - NM_OC_BS11, type, 0, idx); - - return abis_nm_sendmsg(bts, msg); -} - -int abis_nm_bs11_create_envaBTSE(struct gsm_bts *bts, u_int8_t idx) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - u_int8_t zero = 0x00; - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, 3, NM_MT_BS11_CREATE_OBJ, - NM_OC_BS11_ENVABTSE, 0, idx, 0xff); - msgb_tlv_put(msg, 0x99, 1, &zero); - - return abis_nm_sendmsg(bts, msg); -} - -int abis_nm_bs11_create_bport(struct gsm_bts *bts, u_int8_t idx) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, 0, NM_MT_BS11_CREATE_OBJ, NM_OC_BS11_BPORT, - idx, 0, 0); - - return abis_nm_sendmsg(bts, msg); -} - -static const u_int8_t sm_attr[] = { NM_ATT_TEI, NM_ATT_ABIS_CHANNEL }; -int abis_nm_bs11_get_oml_tei_ts(struct gsm_bts *bts) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, 2+sizeof(sm_attr), NM_MT_GET_ATTR, NM_OC_SITE_MANAGER, - 0xff, 0xff, 0xff); - msgb_tlv_put(msg, NM_ATT_LIST_REQ_ATTR, sizeof(sm_attr), sm_attr); - - return abis_nm_sendmsg(bts, msg); -} - -/* like abis_nm_conn_terr_traf + set_tei */ -int abis_nm_bs11_conn_oml_tei(struct gsm_bts *bts, u_int8_t e1_port, - u_int8_t e1_timeslot, u_int8_t e1_subslot, - u_int8_t tei) -{ - struct abis_om_hdr *oh; - struct abis_nm_channel *ch; - struct msgb *msg = nm_msgb_alloc(); - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, sizeof(*ch)+2, NM_MT_BS11_SET_ATTR, - NM_OC_SITE_MANAGER, 0xff, 0xff, 0xff); - - ch = (struct abis_nm_channel *) msgb_put(msg, sizeof(*ch)); - fill_nm_channel(ch, e1_port, e1_timeslot, e1_subslot); - msgb_tv_put(msg, NM_ATT_TEI, tei); - - return abis_nm_sendmsg(bts, msg); -} - -int abis_nm_bs11_set_trx_power(struct gsm_bts_trx *trx, u_int8_t level) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, 3, NM_MT_BS11_SET_ATTR, - NM_OC_BS11, BS11_OBJ_PA, 0x00, trx->nr); - msgb_tlv_put(msg, NM_ATT_BS11_TXPWR, 1, &level); - - return abis_nm_sendmsg(trx->bts, msg); -} - -int abis_nm_bs11_get_trx_power(struct gsm_bts_trx *trx) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - u_int8_t attr = NM_ATT_BS11_TXPWR; - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, 2+sizeof(attr), NM_MT_GET_ATTR, - NM_OC_BS11, BS11_OBJ_PA, 0x00, trx->nr); - msgb_tlv_put(msg, NM_ATT_LIST_REQ_ATTR, sizeof(attr), &attr); - - return abis_nm_sendmsg(trx->bts, msg); -} - -int abis_nm_bs11_get_pll_mode(struct gsm_bts *bts) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - u_int8_t attr[] = { NM_ATT_BS11_PLL_MODE }; - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, 2+sizeof(attr), NM_MT_GET_ATTR, - NM_OC_BS11, BS11_OBJ_LI, 0x00, 0x00); - msgb_tlv_put(msg, NM_ATT_LIST_REQ_ATTR, sizeof(attr), attr); - - return abis_nm_sendmsg(bts, msg); -} - -int abis_nm_bs11_get_cclk(struct gsm_bts *bts) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - u_int8_t attr[] = { NM_ATT_BS11_CCLK_ACCURACY, - NM_ATT_BS11_CCLK_TYPE }; - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, 2+sizeof(attr), NM_MT_GET_ATTR, - NM_OC_BS11, BS11_OBJ_CCLK, 0x00, 0x00); - msgb_tlv_put(msg, NM_ATT_LIST_REQ_ATTR, sizeof(attr), attr); - - return abis_nm_sendmsg(bts, msg); - -} - -//static const u_int8_t bs11_logon_c7[] = { 0x07, 0xd9, 0x01, 0x11, 0x0d, 0x10, 0x20 }; -static const u_int8_t bs11_logon_c8[] = { 0x02 }; -static const u_int8_t bs11_logon_c9[] = "FACTORY"; - -int abis_nm_bs11_factory_logon(struct gsm_bts *bts, int on) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - struct bs11_date_time bdt; - - get_bs11_date_time(&bdt); - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - if (on) { - u_int8_t len = 3*2 + sizeof(bdt) - + sizeof(bs11_logon_c8) + sizeof(bs11_logon_c9); - fill_om_fom_hdr(oh, len, NM_MT_BS11_LMT_LOGON, - NM_OC_BS11_BTSE, 0xff, 0xff, 0xff); - msgb_tlv_put(msg, NM_ATT_BS11_LMT_LOGIN_TIME, - sizeof(bdt), (u_int8_t *) &bdt); - msgb_tlv_put(msg, NM_ATT_BS11_LMT_USER_ACC_LEV, - sizeof(bs11_logon_c8), bs11_logon_c8); - msgb_tlv_put(msg, NM_ATT_BS11_LMT_USER_NAME, - sizeof(bs11_logon_c9), bs11_logon_c9); - } else { - fill_om_fom_hdr(oh, 0, NM_MT_BS11_LMT_LOGOFF, - NM_OC_BS11_BTSE, 0xff, 0xff, 0xff); - } - - return abis_nm_sendmsg(bts, msg); -} - -int abis_nm_bs11_set_trx1_pw(struct gsm_bts *bts, const char *password) -{ - struct abis_om_hdr *oh; - struct msgb *msg; - - if (strlen(password) != 10) - return -EINVAL; - - msg = nm_msgb_alloc(); - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, 2+strlen(password), NM_MT_BS11_SET_ATTR, - NM_OC_BS11, BS11_OBJ_TRX1, 0x00, 0x00); - msgb_tlv_put(msg, NM_ATT_BS11_PASSWORD, 10, (const u_int8_t *)password); - - return abis_nm_sendmsg(bts, msg); -} - -/* change the BS-11 PLL Mode to either locked (E1 derived) or standalone */ -int abis_nm_bs11_set_pll_locked(struct gsm_bts *bts, int locked) -{ - struct abis_om_hdr *oh; - struct msgb *msg; - u_int8_t tlv_value; - - msg = nm_msgb_alloc(); - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, 3, NM_MT_BS11_SET_ATTR, NM_OC_BS11, - BS11_OBJ_LI, 0x00, 0x00); - - if (locked) - tlv_value = BS11_LI_PLL_LOCKED; - else - tlv_value = BS11_LI_PLL_STANDALONE; - - msgb_tlv_put(msg, NM_ATT_BS11_PLL_MODE, 1, &tlv_value); - - return abis_nm_sendmsg(bts, msg); -} - -int abis_nm_bs11_get_state(struct gsm_bts *bts) -{ - return __simple_cmd(bts, NM_MT_BS11_GET_STATE); -} - -/* BS11 SWL */ - -struct abis_nm_bs11_sw { - struct gsm_bts *bts; - char swl_fname[PATH_MAX]; - u_int8_t win_size; - int forced; - struct llist_head file_list; - gsm_cbfn *user_cb; /* specified by the user */ -}; -static struct abis_nm_bs11_sw _g_bs11_sw, *g_bs11_sw = &_g_bs11_sw; - -struct file_list_entry { - struct llist_head list; - char fname[PATH_MAX]; -}; - -struct file_list_entry *fl_dequeue(struct llist_head *queue) -{ - struct llist_head *lh; - - if (llist_empty(queue)) - return NULL; - - lh = queue->next; - llist_del(lh); - - return llist_entry(lh, struct file_list_entry, list); -} - -static int bs11_read_swl_file(struct abis_nm_bs11_sw *bs11_sw) -{ - char linebuf[255]; - struct llist_head *lh, *lh2; - FILE *swl; - int rc = 0; - - swl = fopen(bs11_sw->swl_fname, "r"); - if (!swl) - return -ENODEV; - - /* zero the stale file list, if any */ - llist_for_each_safe(lh, lh2, &bs11_sw->file_list) { - llist_del(lh); - free(lh); - } - - while (fgets(linebuf, sizeof(linebuf), swl)) { - char file_id[12+1]; - char file_version[80+1]; - struct file_list_entry *fle; - static char dir[PATH_MAX]; - - if (strlen(linebuf) < 4) - continue; - - rc = sscanf(linebuf+4, "%12s:%80s\r\n", file_id, file_version); - if (rc < 0) { - perror("ERR parsing SWL file"); - rc = -EINVAL; - goto out; - } - if (rc < 2) - continue; - - fle = malloc(sizeof(*fle)); - if (!fle) { - rc = -ENOMEM; - goto out; - } - memset(fle, 0, sizeof(*fle)); - - /* construct new filename */ - strncpy(dir, bs11_sw->swl_fname, sizeof(dir)); - strncat(fle->fname, dirname(dir), sizeof(fle->fname) - 1); - strcat(fle->fname, "/"); - strncat(fle->fname, file_id, sizeof(fle->fname) - 1 -strlen(fle->fname)); - - llist_add_tail(&fle->list, &bs11_sw->file_list); - } - -out: - fclose(swl); - return rc; -} - -/* bs11 swload specific callback, passed to abis_nm core swload */ -static int bs11_swload_cbfn(unsigned int hook, unsigned int event, - struct msgb *msg, void *data, void *param) -{ - struct abis_nm_bs11_sw *bs11_sw = data; - struct file_list_entry *fle; - int rc = 0; - - switch (event) { - case NM_MT_LOAD_END_ACK: - fle = fl_dequeue(&bs11_sw->file_list); - if (fle) { - /* start download the next file of our file list */ - rc = abis_nm_software_load(bs11_sw->bts, fle->fname, - bs11_sw->win_size, - bs11_sw->forced, - &bs11_swload_cbfn, bs11_sw); - free(fle); - } else { - /* activate the SWL */ - rc = abis_nm_software_activate(bs11_sw->bts, - bs11_sw->swl_fname, - bs11_swload_cbfn, - bs11_sw); - } - break; - case NM_MT_LOAD_SEG_ACK: - case NM_MT_LOAD_END_NACK: - case NM_MT_LOAD_INIT_ACK: - case NM_MT_LOAD_INIT_NACK: - case NM_MT_ACTIVATE_SW_NACK: - case NM_MT_ACTIVATE_SW_ACK: - default: - /* fallthrough to the user callback */ - if (bs11_sw->user_cb) - rc = bs11_sw->user_cb(hook, event, msg, NULL, NULL); - break; - } - - return rc; -} - -/* Siemens provides a SWL file that is a mere listing of all the other - * files that are part of a software release. We need to upload first - * the list file, and then each file that is listed in the list file */ -int abis_nm_bs11_load_swl(struct gsm_bts *bts, const char *fname, - u_int8_t win_size, int forced, gsm_cbfn *cbfn) -{ - struct abis_nm_bs11_sw *bs11_sw = g_bs11_sw; - struct file_list_entry *fle; - int rc = 0; - - INIT_LLIST_HEAD(&bs11_sw->file_list); - bs11_sw->bts = bts; - bs11_sw->win_size = win_size; - bs11_sw->user_cb = cbfn; - bs11_sw->forced = forced; - - strncpy(bs11_sw->swl_fname, fname, sizeof(bs11_sw->swl_fname)); - rc = bs11_read_swl_file(bs11_sw); - if (rc < 0) - return rc; - - /* dequeue next item in file list */ - fle = fl_dequeue(&bs11_sw->file_list); - if (!fle) - return -EINVAL; - - /* start download the next file of our file list */ - rc = abis_nm_software_load(bts, fle->fname, win_size, forced, - bs11_swload_cbfn, bs11_sw); - free(fle); - return rc; -} - -#if 0 -static u_int8_t req_attr_btse[] = { - NM_ATT_ADM_STATE, NM_ATT_BS11_LMT_LOGON_SESSION, - NM_ATT_BS11_LMT_LOGIN_TIME, NM_ATT_BS11_LMT_USER_ACC_LEV, - NM_ATT_BS11_LMT_USER_NAME, - - 0xaf, NM_ATT_BS11_RX_OFFSET, NM_ATT_BS11_VENDOR_NAME, - - NM_ATT_BS11_SW_LOAD_INTENDED, NM_ATT_BS11_SW_LOAD_SAFETY, - - NM_ATT_BS11_SW_LOAD_STORED }; - -static u_int8_t req_attr_btsm[] = { - NM_ATT_ABIS_CHANNEL, NM_ATT_TEI, NM_ATT_BS11_ABIS_EXT_TIME, - NM_ATT_ADM_STATE, NM_ATT_AVAIL_STATUS, 0xce, NM_ATT_FILE_ID, - NM_ATT_FILE_VERSION, NM_ATT_OPER_STATE, 0xe8, NM_ATT_BS11_ALL_TEST_CATG, - NM_ATT_SW_DESCR, NM_ATT_GET_ARI }; -#endif - -static u_int8_t req_attr[] = { - NM_ATT_ADM_STATE, NM_ATT_AVAIL_STATUS, 0xa8, NM_ATT_OPER_STATE, - 0xd5, 0xa1, NM_ATT_BS11_ESN_FW_CODE_NO, NM_ATT_BS11_ESN_HW_CODE_NO, - 0x42, NM_ATT_BS11_ESN_PCB_SERIAL, NM_ATT_BS11_PLL }; - -int abis_nm_bs11_get_serno(struct gsm_bts *bts) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - /* SiemensHW CCTRL object */ - fill_om_fom_hdr(oh, 2+sizeof(req_attr), NM_MT_GET_ATTR, NM_OC_BS11, - 0x03, 0x00, 0x00); - msgb_tlv_put(msg, NM_ATT_LIST_REQ_ATTR, sizeof(req_attr), req_attr); - - return abis_nm_sendmsg(bts, msg); -} - -int abis_nm_bs11_set_ext_time(struct gsm_bts *bts) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - struct bs11_date_time aet; - - get_bs11_date_time(&aet); - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - /* SiemensHW CCTRL object */ - fill_om_fom_hdr(oh, 2+sizeof(aet), NM_MT_BS11_SET_ATTR, NM_OC_SITE_MANAGER, - 0xff, 0xff, 0xff); - msgb_tlv_put(msg, NM_ATT_BS11_ABIS_EXT_TIME, sizeof(aet), (u_int8_t *) &aet); - - return abis_nm_sendmsg(bts, msg); -} - -/* ip.access nanoBTS specific commands */ -static const char ipaccess_magic[] = "com.ipaccess"; - - -static int abis_nm_rx_ipacc(struct msgb *msg) -{ - struct abis_om_hdr *oh = msgb_l2(msg); - struct abis_om_fom_hdr *foh; - u_int8_t idstrlen = oh->data[0]; - struct tlv_parsed tp; - - if (strncmp((char *)&oh->data[1], ipaccess_magic, idstrlen)) { - DEBUGP(DNM, "id string is not com.ipaccess !?!\n"); - return -EINVAL; - } - - 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); - - switch (foh->msg_type) { - case NM_MT_IPACC_RSL_CONNECT_ACK: - DEBUGPC(DNM, "RSL CONNECT ACK "); - if (TLVP_PRESENT(&tp, NM_ATT_IPACC_RSL_BSC_IP)) - DEBUGPC(DNM, "IP=%s ", - inet_ntoa(*((struct in_addr *) - TLVP_VAL(&tp, NM_ATT_IPACC_RSL_BSC_IP)))); - if (TLVP_PRESENT(&tp, NM_ATT_IPACC_RSL_BSC_PORT)) - DEBUGPC(DNM, "PORT=%u ", - ntohs(*((u_int16_t *) - TLVP_VAL(&tp, NM_ATT_IPACC_RSL_BSC_PORT)))); - DEBUGPC(DNM, "\n"); - break; - case NM_MT_IPACC_RSL_CONNECT_NACK: - DEBUGPC(DNM, "RSL CONNECT NACK "); - if (TLVP_PRESENT(&tp, NM_ATT_NACK_CAUSES)) - DEBUGPC(DNM, " CAUSE=%s\n", - nack_cause_name(*TLVP_VAL(&tp, NM_ATT_NACK_CAUSES))); - else - DEBUGPC(DNM, "\n"); - break; - case NM_MT_IPACC_SET_NVATTR_ACK: - DEBUGPC(DNM, "SET NVATTR ACK\n"); - /* FIXME: decode and show the actual attributes */ - break; - case NM_MT_IPACC_SET_NVATTR_NACK: - DEBUGPC(DNM, "SET NVATTR NACK "); - if (TLVP_PRESENT(&tp, NM_ATT_NACK_CAUSES)) - DEBUGPC(DNM, " CAUSE=%s\n", - nack_cause_name(*TLVP_VAL(&tp, NM_ATT_NACK_CAUSES))); - else - DEBUGPC(DNM, "\n"); - break; - default: - DEBUGPC(DNM, "unknown\n"); - break; - } - return 0; -} - -/* send an ip-access manufacturer specific message */ -int abis_nm_ipaccess_msg(struct gsm_bts *bts, u_int8_t msg_type, - u_int8_t obj_class, u_int8_t bts_nr, - u_int8_t trx_nr, u_int8_t ts_nr, - u_int8_t *attr, int attr_len) -{ - struct msgb *msg = nm_msgb_alloc(); - struct abis_om_hdr *oh; - struct abis_om_fom_hdr *foh; - u_int8_t *data; - - /* construct the 12.21 OM header, observe the erroneous length */ - oh = (struct abis_om_hdr *) msgb_put(msg, sizeof(*oh)); - fill_om_hdr(oh, sizeof(*foh) + attr_len); - oh->mdisc = ABIS_OM_MDISC_MANUF; - - /* add the ip.access magic */ - data = msgb_put(msg, sizeof(ipaccess_magic)+1); - *data++ = sizeof(ipaccess_magic); - memcpy(data, ipaccess_magic, sizeof(ipaccess_magic)); - - /* fill the 12.21 FOM header */ - foh = (struct abis_om_fom_hdr *) msgb_put(msg, sizeof(*foh)); - foh->msg_type = msg_type; - foh->obj_class = obj_class; - foh->obj_inst.bts_nr = bts_nr; - foh->obj_inst.trx_nr = trx_nr; - foh->obj_inst.ts_nr = ts_nr; - - if (attr && attr_len) { - data = msgb_put(msg, attr_len); - memcpy(data, attr, attr_len); - } - - return abis_nm_sendmsg(bts, msg); -} - -/* set some attributes in NVRAM */ -int abis_nm_ipaccess_set_nvattr(struct gsm_bts *bts, u_int8_t *attr, - int attr_len) -{ - return abis_nm_ipaccess_msg(bts, NM_MT_IPACC_SET_NVATTR, - NM_OC_BASEB_TRANSC, 0, 0, 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); -} diff --git a/src/abis_rsl.c b/src/abis_rsl.c deleted file mode 100644 index 4b2a7fccf..000000000 --- a/src/abis_rsl.c +++ /dev/null @@ -1,1263 +0,0 @@ -/* GSM Radio Signalling Link messages on the A-bis interface - * 3GPP TS 08.58 version 8.6.0 Release 1999 / ETSI TS 100 596 V8.6.0 */ - -/* (C) 2008-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 <stdio.h> -#include <stdlib.h> -#include <errno.h> -#include <sys/types.h> -#include <netinet/in.h> -#include <arpa/inet.h> - -#include <openbsc/gsm_data.h> -#include <openbsc/gsm_04_08.h> -#include <openbsc/abis_rsl.h> -#include <openbsc/chan_alloc.h> -#include <openbsc/debug.h> -#include <openbsc/tlv.h> -#include <openbsc/paging.h> -#include <openbsc/signal.h> - -#define RSL_ALLOC_SIZE 1024 -#define RSL_ALLOC_HEADROOM 128 - -#define MAX(a, b) (a) >= (b) ? (a) : (b) - -static const struct tlv_definition rsl_att_tlvdef = { - .def = { - [RSL_IE_CHAN_NR] = { TLV_TYPE_TV }, - [RSL_IE_LINK_IDENT] = { TLV_TYPE_TV }, - [RSL_IE_ACT_TYPE] = { TLV_TYPE_TV }, - [RSL_IE_BS_POWER] = { TLV_TYPE_TV }, - [RSL_IE_CHAN_IDENT] = { TLV_TYPE_TLV }, - [RSL_IE_CHAN_MODE] = { TLV_TYPE_TLV }, - [RSL_IE_ENCR_INFO] = { TLV_TYPE_TLV }, - [RSL_IE_FRAME_NUMBER] = { TLV_TYPE_FIXED, 2 }, - [RSL_IE_HANDO_REF] = { TLV_TYPE_TV }, - [RSL_IE_L1_INFO] = { TLV_TYPE_FIXED, 2 }, - [RSL_IE_L3_INFO] = { TLV_TYPE_TL16V }, - [RSL_IE_MS_IDENTITY] = { TLV_TYPE_TLV }, - [RSL_IE_MS_POWER] = { TLV_TYPE_TV }, - [RSL_IE_PAGING_GROUP] = { TLV_TYPE_TV }, - [RSL_IE_PAGING_LOAD] = { TLV_TYPE_FIXED, 2 }, - [RSL_IE_PYHS_CONTEXT] = { TLV_TYPE_TLV }, - [RSL_IE_ACCESS_DELAY] = { TLV_TYPE_TV }, - [RSL_IE_RACH_LOAD] = { TLV_TYPE_TLV }, - [RSL_IE_REQ_REFERENCE] = { TLV_TYPE_FIXED, 3 }, - [RSL_IE_RELEASE_MODE] = { TLV_TYPE_TV }, - [RSL_IE_RESOURCE_INFO] = { TLV_TYPE_TLV }, - [RSL_IE_RLM_CAUSE] = { TLV_TYPE_TLV }, - [RSL_IE_STARTNG_TIME] = { TLV_TYPE_FIXED, 2 }, - [RSL_IE_TIMING_ADVANCE] = { TLV_TYPE_TV }, - [RSL_IE_UPLINK_MEAS] = { TLV_TYPE_TLV }, - [RSL_IE_CAUSE] = { TLV_TYPE_TLV }, - [RSL_IE_MEAS_RES_NR] = { TLV_TYPE_TV }, - [RSL_IE_MSG_ID] = { TLV_TYPE_TV }, - [RSL_IE_SYSINFO_TYPE] = { TLV_TYPE_TV }, - [RSL_IE_MS_POWER_PARAM] = { TLV_TYPE_TLV }, - [RSL_IE_BS_POWER_PARAM] = { TLV_TYPE_TLV }, - [RSL_IE_PREPROC_PARAM] = { TLV_TYPE_TLV }, - [RSL_IE_PREPROC_MEAS] = { TLV_TYPE_TLV }, - [RSL_IE_IMM_ASS_INFO] = { TLV_TYPE_TLV }, - [RSL_IE_SMSCB_INFO] = { TLV_TYPE_FIXED, 23 }, - [RSL_IE_MS_TIMING_OFFSET] = { TLV_TYPE_TV }, - [RSL_IE_ERR_MSG] = { TLV_TYPE_TLV }, - [RSL_IE_FULL_BCCH_INFO] = { TLV_TYPE_TLV }, - [RSL_IE_CHAN_NEEDED] = { TLV_TYPE_TV }, - [RSL_IE_CB_CMD_TYPE] = { TLV_TYPE_TV }, - [RSL_IE_SMSCB_MSG] = { TLV_TYPE_TLV }, - [RSL_IE_FULL_IMM_ASS_INFO] = { TLV_TYPE_TLV }, - [RSL_IE_SACCH_INFO] = { TLV_TYPE_TLV }, - [RSL_IE_CBCH_LOAD_INFO] = { TLV_TYPE_TV }, - [RSL_IE_SMSCB_CHAN_INDICATOR] = { TLV_TYPE_TV }, - [RSL_IE_GROUP_CALL_REF] = { TLV_TYPE_TLV }, - [RSL_IE_CHAN_DESC] = { TLV_TYPE_TLV }, - [RSL_IE_NCH_DRX_INFO] = { TLV_TYPE_TLV }, - [RSL_IE_CMD_INDICATOR] = { TLV_TYPE_TLV }, - [RSL_IE_EMLPP_PRIO] = { TLV_TYPE_TV }, - [RSL_IE_UIC] = { TLV_TYPE_TLV }, - [RSL_IE_MAIN_CHAN_REF] = { TLV_TYPE_TV }, - [RSL_IE_MR_CONFIG] = { TLV_TYPE_TLV }, - [RSL_IE_MR_CONTROL] = { TLV_TYPE_TV }, - [RSL_IE_SUP_CODEC_TYPES] = { TLV_TYPE_TLV }, - [RSL_IE_CODEC_CONFIG] = { TLV_TYPE_TLV }, - [RSL_IE_RTD] = { TLV_TYPE_TV }, - [RSL_IE_TFO_STATUS] = { TLV_TYPE_TV }, - [RSL_IE_LLP_APDU] = { TLV_TYPE_TLV }, - [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_LOCAL_PORT] = { TLV_TYPE_FIXED, 2 }, - [0xf4] = { TLV_TYPE_TV }, - [0xf8] = { TLV_TYPE_FIXED, 2 }, - [0xfc] = { TLV_TYPE_TV }, - }, -}; -#define rsl_tlv_parse(dec, buf, len) \ - tlv_parse(dec, &rsl_att_tlvdef, buf, len, 0, 0) - -static u_int8_t mdisc_by_msgtype(u_int8_t msg_type) -{ - /* mask off the transparent bit ? */ - msg_type &= 0xfe; - - if ((msg_type & 0xf0) == 0x00) - return ABIS_RSL_MDISC_RLL; - if ((msg_type & 0xf0) == 0x10) { - if (msg_type >= 0x19 && msg_type <= 0x22) - return ABIS_RSL_MDISC_TRX; - else - return ABIS_RSL_MDISC_COM_CHAN; - } - if ((msg_type & 0xe0) == 0x20) - return ABIS_RSL_MDISC_DED_CHAN; - - return ABIS_RSL_MDISC_LOC; -} - -static inline void init_dchan_hdr(struct abis_rsl_dchan_hdr *dh, - u_int8_t msg_type) -{ - dh->c.msg_discr = mdisc_by_msgtype(msg_type); - dh->c.msg_type = msg_type; - dh->ie_chan = RSL_IE_CHAN_NR; -} - -static inline void init_llm_hdr(struct abis_rsl_rll_hdr *dh, - u_int8_t msg_type) -{ - /* dh->c.msg_discr = mdisc_by_msgtype(msg_type); */ - dh->c.msg_discr = ABIS_RSL_MDISC_RLL; - dh->c.msg_type = msg_type; - dh->ie_chan = RSL_IE_CHAN_NR; - dh->ie_link_id = RSL_IE_LINK_IDENT; -} - - -/* encode channel number as per Section 9.3.1 */ -u_int8_t rsl_enc_chan_nr(u_int8_t type, u_int8_t subch, u_int8_t timeslot) -{ - u_int8_t ret; - - ret = (timeslot & 0x07) | type; - - switch (type) { - case RSL_CHAN_Lm_ACCHs: - subch &= 0x01; - break; - case RSL_CHAN_SDCCH4_ACCH: - subch &= 0x07; - break; - case RSL_CHAN_SDCCH8_ACCH: - subch &= 0x07; - break; - default: - /* no subchannels allowed */ - subch = 0x00; - break; - } - ret |= (subch << 3); - - return ret; -} - -/* determine logical channel based on TRX and channel number IE */ -struct gsm_lchan *lchan_lookup(struct gsm_bts_trx *trx, u_int8_t chan_nr) -{ - struct gsm_lchan *lchan; - u_int8_t ts_nr = chan_nr & 0x07; - u_int8_t cbits = chan_nr >> 3; - u_int8_t lch_idx; - struct gsm_bts_trx_ts *ts = &trx->ts[ts_nr]; - - if (cbits == 0x01) { - lch_idx = 0; /* TCH/F */ - if (ts->pchan != GSM_PCHAN_TCH_F) - fprintf(stderr, "chan_nr=0x%02x but pchan=%u\n", - chan_nr, ts->pchan); - } else if ((cbits & 0x1e) == 0x02) { - lch_idx = cbits & 0x1; /* TCH/H */ - if (ts->pchan != GSM_PCHAN_TCH_H) - fprintf(stderr, "chan_nr=0x%02x but pchan=%u\n", - chan_nr, ts->pchan); - } else if ((cbits & 0x1c) == 0x04) { - lch_idx = cbits & 0x3; /* SDCCH/4 */ - if (ts->pchan != GSM_PCHAN_CCCH_SDCCH4) - fprintf(stderr, "chan_nr=0x%02x but pchan=%u\n", - chan_nr, ts->pchan); - } else if ((cbits & 0x18) == 0x08) { - lch_idx = cbits & 0x7; /* SDCCH/8 */ - if (ts->pchan != GSM_PCHAN_SDCCH8_SACCH8C) - fprintf(stderr, "chan_nr=0x%02x but pchan=%u\n", - chan_nr, ts->pchan); - } else if (cbits == 0x10 || cbits == 0x11 || cbits == 0x12) { - lch_idx = 0; - if (ts->pchan != GSM_PCHAN_CCCH && - ts->pchan != GSM_PCHAN_CCCH_SDCCH4) - fprintf(stderr, "chan_nr=0x%02x but pchan=%u\n", - chan_nr, ts->pchan); - /* FIXME: we should not return first sdcch4 !!! */ - } else { - fprintf(stderr, "unknown chan_nr=0x%02x\n", chan_nr); - return NULL; - } - - lchan = &ts->lchan[lch_idx]; - - return lchan; -} - -u_int8_t lchan2chan_nr(struct gsm_lchan *lchan) -{ - struct gsm_bts_trx_ts *ts = lchan->ts; - u_int8_t cbits, chan_nr; - - switch (ts->pchan) { - case GSM_PCHAN_TCH_F: - cbits = 0x01; - break; - case GSM_PCHAN_TCH_H: - cbits = 0x02; - cbits += lchan->nr; - break; - case GSM_PCHAN_CCCH_SDCCH4: - cbits = 0x04; - cbits += lchan->nr; - break; - case GSM_PCHAN_SDCCH8_SACCH8C: - cbits = 0x08; - cbits += lchan->nr; - break; - default: - case GSM_PCHAN_CCCH: - cbits = 0x10; - break; - } - - chan_nr = (cbits << 3) | (ts->nr & 0x7); - - return chan_nr; -} - -/* As per TS 03.03 Section 2.2, the IMSI has 'not more than 15 digits' */ -u_int64_t str_to_imsi(const char *imsi_str) -{ - u_int64_t ret; - - ret = strtoull(imsi_str, NULL, 10); - - return ret; -} - -/* Table 5 Clause 7 TS 05.02 */ -unsigned int n_pag_blocks(int bs_ccch_sdcch_comb, unsigned int bs_ag_blks_res) -{ - if (!bs_ccch_sdcch_comb) - return 9 - bs_ag_blks_res; - else - return 3 - bs_ag_blks_res; -} - -/* Chapter 6.5.2 of TS 05.02 */ -unsigned int get_ccch_group(u_int64_t imsi, unsigned int bs_cc_chans, - unsigned int n_pag_blocks) -{ - return (imsi % 1000) % (bs_cc_chans * n_pag_blocks) / n_pag_blocks; -} - -/* Chapter 6.5.2 of TS 05.02 */ -unsigned int get_paging_group(u_int64_t imsi, unsigned int bs_cc_chans, - int n_pag_blocks) -{ - return (imsi % 1000) % (bs_cc_chans * n_pag_blocks) % n_pag_blocks; -} - -static struct msgb *rsl_msgb_alloc(void) -{ - return msgb_alloc_headroom(RSL_ALLOC_SIZE, RSL_ALLOC_HEADROOM); -} - -#define MACBLOCK_SIZE 23 -static void pad_macblock(u_int8_t *out, const u_int8_t *in, int len) -{ - memcpy(out, in, len); - - if (len < MACBLOCK_SIZE) - memset(out+len, 0x2b, MACBLOCK_SIZE-len); -} - -static void print_rsl_cause(u_int8_t *cause_tlv) -{ - u_int8_t cause_len; - int i; - - if (cause_tlv[0] != RSL_IE_CAUSE) - return; - - cause_len = cause_tlv[1]; - DEBUGPC(DRSL, "CAUSE: "); - for (i = 0; i < cause_len; i++) - DEBUGPC(DRSL, "%02x ", cause_tlv[2+i]); -} - -/* Send a BCCH_INFO message as per Chapter 8.5.1 */ -int rsl_bcch_info(struct gsm_bts_trx *trx, u_int8_t type, - const u_int8_t *data, int len) -{ - struct abis_rsl_dchan_hdr *dh; - struct msgb *msg = rsl_msgb_alloc(); - - dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof*dh); - init_dchan_hdr(dh, RSL_MT_BCCH_INFO); - dh->chan_nr = RSL_CHAN_BCCH; - - msgb_tv_put(msg, RSL_IE_SYSINFO_TYPE, type); - msgb_tlv_put(msg, RSL_IE_FULL_BCCH_INFO, len, data); - - msg->trx = trx; - - return abis_rsl_sendmsg(msg); -} - -int rsl_sacch_filling(struct gsm_bts_trx *trx, u_int8_t type, - const u_int8_t *data, int len) -{ - struct abis_rsl_common_hdr *ch; - struct msgb *msg = rsl_msgb_alloc(); - - ch = (struct abis_rsl_common_hdr *) msgb_put(msg, sizeof(*ch)); - ch->msg_discr = ABIS_RSL_MDISC_TRX; - ch->msg_type = RSL_MT_SACCH_FILL; - - msgb_tv_put(msg, RSL_IE_SYSINFO_TYPE, type); - msgb_tl16v_put(msg, RSL_IE_L3_INFO, len, data); - - msg->trx = trx; - - return abis_rsl_sendmsg(msg); -} - -/* Chapter 8.4.1 */ -#if 0 -int rsl_chan_activate(struct gsm_bts_trx *trx, u_int8_t chan_nr, - u_int8_t act_type, - struct rsl_ie_chan_mode *chan_mode, - struct rsl_ie_chan_ident *chan_ident, - u_int8_t bs_power, u_int8_t ms_power, - u_int8_t ta) -{ - struct abis_rsl_dchan_hdr *dh; - struct msgb *msg = rsl_msgb_alloc(); - - dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); - init_dchan_hdr(dh, RSL_MT_CHAN_ACTIV); - dh->chan_nr = chan_nr; - - msgb_tv_put(msg, RSL_IE_ACT_TYPE, act_type); - /* For compatibility with Phase 1 */ - msgb_tlv_put(msg, RSL_IE_CHAN_MODE, sizeof(*chan_mode), - (u_int8_t *) chan_mode); - msgb_tlv_put(msg, RSL_IE_CHAN_IDENT, 4, - (u_int8_t *) chan_ident); -#if 0 - msgb_tlv_put(msg, RSL_IE_ENCR_INFO, 1, - (u_int8_t *) &encr_info); -#endif - msgb_tv_put(msg, RSL_IE_BS_POWER, bs_power); - msgb_tv_put(msg, RSL_IE_MS_POWER, ms_power); - msgb_tv_put(msg, RSL_IE_TIMING_ADVANCE, ta); - - msg->trx = trx; - - return abis_rsl_sendmsg(msg); -} -#endif - -int rsl_chan_activate_lchan(struct gsm_lchan *lchan, u_int8_t act_type, - u_int8_t ta, u_int8_t mode) -{ - struct abis_rsl_dchan_hdr *dh; - struct msgb *msg = rsl_msgb_alloc(); - - u_int8_t chan_nr = lchan2chan_nr(lchan); - u_int16_t arfcn = lchan->ts->trx->arfcn; - struct rsl_ie_chan_mode cm; - struct rsl_ie_chan_ident ci; - - memset(&cm, 0, sizeof(cm)); - /* FIXME: what to do with data calls ? */ - cm.dtx_dtu = 0x00; - switch (lchan->type) { - case GSM_LCHAN_SDCCH: - cm.spd_ind = RSL_CMOD_SPD_SIGN; - cm.chan_rt = RSL_CMOD_CRT_SDCCH; - cm.chan_rate = 0x00; - break; - case GSM_LCHAN_TCH_F: - cm.chan_rt = RSL_CMOD_CRT_TCH_Bm; - switch (mode) { - case RSL_CMOD_SPD_SIGN: - cm.spd_ind = RSL_CMOD_SPD_SIGN; - cm.chan_rate = 0x00; - break; - case RSL_CMOD_SPD_SPEECH: - cm.spd_ind = RSL_CMOD_SPD_SPEECH; - cm.chan_rate = RSL_CMOD_SP_GSM2; - break; - } - break; - case GSM_LCHAN_TCH_H: - DEBUGP(DRSL, "Unimplemented TCH_H activation\n"); - return -1; - case GSM_LCHAN_UNKNOWN: - case GSM_LCHAN_NONE: - return -1; - } - - memset(&ci, 0, sizeof(ci)); - ci.chan_desc.iei = 0x64; - ci.chan_desc.chan_nr = chan_nr; - ci.chan_desc.oct3 = (lchan->ts->trx->bts->tsc << 5) | ((arfcn & 0x3ff) >> 8); - ci.chan_desc.oct4 = arfcn & 0xff; - - dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); - init_dchan_hdr(dh, RSL_MT_CHAN_ACTIV); - dh->chan_nr = chan_nr; - - msgb_tv_put(msg, RSL_IE_ACT_TYPE, act_type); - /* For compatibility with Phase 1 */ - msgb_tlv_put(msg, RSL_IE_CHAN_MODE, sizeof(cm), - (u_int8_t *) &cm); - msgb_tlv_put(msg, RSL_IE_CHAN_IDENT, 4, - (u_int8_t *) &ci); -#if 0 - msgb_tlv_put(msg, RSL_IE_ENCR_INFO, 1, - (u_int8_t *) &encr_info); -#endif - msgb_tv_put(msg, RSL_IE_BS_POWER, lchan->bs_power); - msgb_tv_put(msg, RSL_IE_MS_POWER, lchan->ms_power); - msgb_tv_put(msg, RSL_IE_TIMING_ADVANCE, ta); - - msg->trx = lchan->ts->trx; - - return abis_rsl_sendmsg(msg); -} - -/* Chapter 8.4.9 */ -int rsl_chan_mode_modify_req(struct gsm_lchan *lchan) -{ - struct abis_rsl_dchan_hdr *dh; - struct msgb *msg = rsl_msgb_alloc(); - - u_int8_t chan_nr = lchan2chan_nr(lchan); - struct rsl_ie_chan_mode cm; - - memset(&cm, 0, sizeof(cm)); - - /* FIXME: what to do with data calls ? */ - cm.dtx_dtu = 0x00; - switch (lchan->type) { - /* todo more modes */ - case GSM_LCHAN_TCH_F: - cm.spd_ind = RSL_CMOD_SPD_SPEECH; - cm.chan_rt = RSL_CMOD_CRT_TCH_Bm; - switch(lchan->tch_mode) { - case GSM48_CMODE_SPEECH_V1: - cm.chan_rate = RSL_CMOD_SP_GSM1; - break; - case GSM48_CMODE_SPEECH_EFR: - cm.chan_rate = RSL_CMOD_SP_GSM2; - break; - default: - DEBUGP(DRSL, "Unimplemented channel modification\n"); - return -1; - } - break; - default: - DEBUGP(DRSL, "Unimplemented channel modification\n"); - return -1; - } - - dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); - init_dchan_hdr(dh, RSL_MT_MODE_MODIFY_REQ); - dh->chan_nr = chan_nr; - - msgb_tlv_put(msg, RSL_IE_CHAN_MODE, sizeof(cm), - (u_int8_t *) &cm); -#if 0 - msgb_tlv_put(msg, RSL_IE_ENCR_INFO, 1, - (u_int8_t *) &encr_info); -#endif - - msg->trx = lchan->ts->trx; - - return abis_rsl_sendmsg(msg); -} - -/* Chapter 9.1.7 of 04.08 */ -int rsl_chan_release(struct gsm_lchan *lchan) -{ - struct abis_rsl_dchan_hdr *dh; - struct msgb *msg = rsl_msgb_alloc(); - - dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); - init_dchan_hdr(dh, RSL_MT_RF_CHAN_REL); - dh->chan_nr = lchan2chan_nr(lchan); - - msg->lchan = lchan; - msg->trx = lchan->ts->trx; - - DEBUGP(DRSL, "Channel Release CMD channel=%s chan_nr=0x%02x\n", - gsm_ts_name(lchan->ts), dh->chan_nr); - - return abis_rsl_sendmsg(msg); -} - -int rsl_paging_cmd(struct gsm_bts *bts, u_int8_t paging_group, u_int8_t len, - u_int8_t *ms_ident, u_int8_t chan_needed) -{ - struct abis_rsl_dchan_hdr *dh; - struct msgb *msg = rsl_msgb_alloc(); - - dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); - init_dchan_hdr(dh, RSL_MT_PAGING_CMD); - dh->chan_nr = RSL_CHAN_PCH_AGCH; - - msgb_tv_put(msg, RSL_IE_PAGING_GROUP, paging_group); - msgb_tlv_put(msg, RSL_IE_MS_IDENTITY, len-2, ms_ident+2); - msgb_tv_put(msg, RSL_IE_CHAN_NEEDED, chan_needed); - - msg->trx = bts->c0; - - return abis_rsl_sendmsg(msg); -} - -int rsl_paging_cmd_subscr(struct gsm_bts *bts, u_int8_t chan_need, - struct gsm_subscriber *subscr) -{ -#if 0 - u_int8_t mi[128]; - unsigned int mi_len; - u_int8_t paging_group; -#endif - - return -1; -} - -int imsi_str2bcd(u_int8_t *bcd_out, const char *str_in) -{ - int i, len = strlen(str_in); - - for (i = 0; i < len; i++) { - int num = str_in[i] - 0x30; - if (num < 0 || num > 9) - return -1; - if (i % 2 == 0) - bcd_out[i/2] = num; - else - bcd_out[i/2] |= (num << 4); - } - - return 0; -} - -/* Chapter 8.5.6 */ -int rsl_imm_assign_cmd(struct gsm_bts *bts, u_int8_t len, u_int8_t *val) -{ - struct msgb *msg = rsl_msgb_alloc(); - struct abis_rsl_dchan_hdr *dh; - u_int8_t buf[MACBLOCK_SIZE]; - - dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); - init_dchan_hdr(dh, RSL_MT_IMMEDIATE_ASSIGN_CMD); - dh->chan_nr = RSL_CHAN_PCH_AGCH; - - switch (bts->type) { - case GSM_BTS_TYPE_BS11: - msgb_tlv_put(msg, RSL_IE_IMM_ASS_INFO, len, val); - break; - default: - /* If phase 2, construct a FULL_IMM_ASS_INFO */ - pad_macblock(buf, val, len); - msgb_tlv_put(msg, RSL_IE_FULL_IMM_ASS_INFO, MACBLOCK_SIZE, buf); - break; - } - - msg->trx = bts->c0; - - return abis_rsl_sendmsg(msg); -} - -/* Send "DATA REQUEST" message with given L3 Info payload */ -/* Chapter 8.3.1 */ -int rsl_data_request(struct msgb *msg, u_int8_t link_id) -{ - u_int8_t l3_len = msg->tail - (u_int8_t *)msgb_l3(msg); - struct abis_rsl_rll_hdr *rh; - - if (msg->lchan == NULL) { - fprintf(stderr, "cannot send DATA REQUEST to unknown lchan\n"); - return -EINVAL; - } - - /* First push the L3 IE tag and length */ - msgb_tv16_push(msg, RSL_IE_L3_INFO, l3_len); - - /* Then push the RSL header */ - rh = (struct abis_rsl_rll_hdr *) msgb_push(msg, sizeof(*rh)); - init_llm_hdr(rh, RSL_MT_DATA_REQ); - rh->c.msg_discr |= ABIS_RSL_MDISC_TRANSP; - rh->chan_nr = lchan2chan_nr(msg->lchan); - rh->link_id = link_id; - - msg->trx = msg->lchan->ts->trx; - - return abis_rsl_sendmsg(msg); -} - -/* Chapter 8.4.2: Channel Activate Acknowledge */ -static int rsl_rx_chan_act_ack(struct msgb *msg) -{ - struct abis_rsl_dchan_hdr *rslh = msgb_l2(msg); - - /* BTS has confirmed channel activation, we now need - * to assign the activated channel to the MS */ - if (rslh->ie_chan != RSL_IE_CHAN_NR) - return -EINVAL; - - return 0; -} - -/* Chapter 8.4.3: Channel Activate NACK */ -static int rsl_rx_chan_act_nack(struct msgb *msg) -{ - struct abis_rsl_dchan_hdr *dh = msgb_l2(msg); - struct tlv_parsed tp; - - /* BTS has rejected channel activation ?!? */ - if (dh->ie_chan != RSL_IE_CHAN_NR) - return -EINVAL; - - rsl_tlv_parse(&tp, dh->data, msgb_l2len(msg)-sizeof(*dh)); - if (TLVP_PRESENT(&tp, RSL_IE_CAUSE)) - DEBUGPC(DRSL, "CAUSE=0x%02x ", *TLVP_VAL(&tp, RSL_IE_CAUSE)); - - return 0; -} - -/* Chapter 8.4.4: Connection Failure Indication */ -static int rsl_rx_conn_fail(struct msgb *msg) -{ - struct abis_rsl_dchan_hdr *dh = msgb_l2(msg); - struct tlv_parsed tp; - - DEBUGPC(DRSL, "CONNECTION FAIL: "); - print_rsl_cause(dh->data); - - rsl_tlv_parse(&tp, dh->data, msgb_l2len(msg)-sizeof(*dh)); - - if (msg->trx->bts->type == GSM_BTS_TYPE_BS11) { - /* FIXME: we have no idea what cause 0x18 is !!! */ - if (TLVP_PRESENT(&tp, RSL_IE_CAUSE) && - TLVP_LEN(&tp, RSL_IE_CAUSE) >= 1 && - *TLVP_VAL(&tp, RSL_IE_CAUSE) == 0x18) { - if (msg->lchan->use_count > 0) { - DEBUGPC(DRSL, "Cause 0x18 IGNORING, lchan in use! (%d times)\n", msg->lchan->use_count); - return 0; - } - } - } - - DEBUGPC(DRSL, "RELEASING.\n"); - - /* FIXME: only free it after channel release ACK */ - return rsl_chan_release(msg->lchan); -} - -static int rsl_rx_meas_res(struct msgb *msg) -{ - struct abis_rsl_dchan_hdr *dh = msgb_l2(msg); - struct tlv_parsed tp; - - DEBUGPC(DRSL, "MEASUREMENT RESULT "); - rsl_tlv_parse(&tp, dh->data, msgb_l2len(msg)-sizeof(*dh)); - - if (TLVP_PRESENT(&tp, RSL_IE_MEAS_RES_NR)) - DEBUGPC(DRSL, "NR=%d ", *TLVP_VAL(&tp, RSL_IE_MEAS_RES_NR)); - if (TLVP_PRESENT(&tp, RSL_IE_UPLINK_MEAS)) { - u_int8_t len = TLVP_LEN(&tp, RSL_IE_UPLINK_MEAS); - const u_int8_t *val = TLVP_VAL(&tp, RSL_IE_UPLINK_MEAS); - if (len >= 3) { - if (val[0] & 0x40) - DEBUGPC(DRSL, "DTXd "); - DEBUGPC(DRSL, "RXL-FULL-up=%d RXL-SUB-up=%d ", - val[0] & 0x3f, val[1] & 0x3f); - DEBUGPC(DRSL, "RXQ-FULL-up=%d RXQ-SUB-up=%d ", - val[2]>>3 & 0x7, val[2] & 0x7); - } - } - if (TLVP_PRESENT(&tp, RSL_IE_BS_POWER)) - DEBUGPC(DRSL, "BS_POWER=%d ", *TLVP_VAL(&tp, RSL_IE_BS_POWER)); - if (TLVP_PRESENT(&tp, RSL_IE_MS_TIMING_OFFSET)) - DEBUGPC(DRSL, "MS_TO=%d ", - *TLVP_VAL(&tp, RSL_IE_MS_TIMING_OFFSET)); - if (TLVP_PRESENT(&tp, RSL_IE_L1_INFO)) - DEBUGPC(DRSL, "L1 "); - if (TLVP_PRESENT(&tp, RSL_IE_L3_INFO)) { - DEBUGPC(DRSL, "L3\n"); - msg->l3h = TLVP_VAL(&tp, RSL_IE_L3_INFO); - return gsm0408_rcvmsg(msg); - } else - DEBUGPC(DRSL, "\n"); - - return 0; -} - -static int abis_rsl_rx_dchan(struct msgb *msg) -{ - struct abis_rsl_dchan_hdr *rslh = msgb_l2(msg); - int rc = 0; - char *ts_name; - - msg->lchan = lchan_lookup(msg->trx, rslh->chan_nr); - ts_name = gsm_ts_name(msg->lchan->ts); - - DEBUGP(DRSL, "channel=%s chan_nr=0x%02x ", ts_name, rslh->chan_nr); - - switch (rslh->c.msg_type) { - case RSL_MT_CHAN_ACTIV_ACK: - DEBUGPC(DRSL, "CHANNEL ACTIVATE ACK\n"); - rc = rsl_rx_chan_act_ack(msg); - break; - case RSL_MT_CHAN_ACTIV_NACK: - DEBUGPC(DRSL, "CHANNEL ACTIVATE NACK\n"); - rc = rsl_rx_chan_act_nack(msg); - break; - case RSL_MT_CONN_FAIL: - rc = rsl_rx_conn_fail(msg); - break; - case RSL_MT_MEAS_RES: - rc = rsl_rx_meas_res(msg); - break; - case RSL_MT_RF_CHAN_REL_ACK: - DEBUGPC(DRSL, "RF CHANNEL RELEASE ACK\n"); - lchan_free(msg->lchan); - break; - case RSL_MT_MODE_MODIFY_ACK: - DEBUGPC(DRSL, "CHANNEL MODE MODIFY ACK\n"); - break; - case RSL_MT_MODE_MODIFY_NACK: - DEBUGPC(DRSL, "CHANNEL MODE MODIFY NACK\n"); - break; - case RSL_MT_PHY_CONTEXT_CONF: - case RSL_MT_PREPROC_MEAS_RES: - case RSL_MT_TALKER_DET: - case RSL_MT_LISTENER_DET: - case RSL_MT_REMOTE_CODEC_CONF_REP: - case RSL_MT_MR_CODEC_MOD_ACK: - case RSL_MT_MR_CODEC_MOD_NACK: - case RSL_MT_MR_CODEC_MOD_PER: - DEBUGPC(DRSL, "Unimplemented Abis RSL DChan msg 0x%02x\n", - rslh->c.msg_type); - break; - default: - DEBUGPC(DRSL, "unknown Abis RSL DChan msg 0x%02x\n", - rslh->c.msg_type); - return -EINVAL; - } - - return rc; -} - -static int rsl_rx_error_rep(struct msgb *msg) -{ - struct abis_rsl_common_hdr *rslh = msgb_l2(msg); - - DEBUGP(DRSL, "ERROR REPORT "); - print_rsl_cause(rslh->data); - DEBUGPC(DRSL, "\n"); - - return 0; -} - -static int abis_rsl_rx_trx(struct msgb *msg) -{ - struct abis_rsl_common_hdr *rslh = msgb_l2(msg); - int rc = 0; - - switch (rslh->msg_type) { - case RSL_MT_ERROR_REPORT: - rc = rsl_rx_error_rep(msg); - break; - case RSL_MT_RF_RES_IND: - /* interference on idle channels of TRX */ - //DEBUGP(DRSL, "TRX: RF Interference Indication\n"); - break; - case RSL_MT_OVERLOAD: - /* indicate CCCH / ACCH / processor overload */ - DEBUGP(DRSL, "TRX: CCCH/ACCH/CPU Overload\n"); - break; - default: - DEBUGP(DRSL, "Unknown Abis RSL TRX message type 0x%02x\n", - rslh->msg_type); - return -EINVAL; - } - return rc; -} - -/* MS has requested a channel on the RACH */ -static int rsl_rx_chan_rqd(struct msgb *msg) -{ - struct gsm_bts *bts = msg->trx->bts; - struct abis_rsl_dchan_hdr *rqd_hdr = msgb_l2(msg); - struct gsm48_req_ref *rqd_ref; - struct gsm48_imm_ass ia; - enum gsm_chan_t lctype; - enum gsm_chreq_reason_t chreq_reason; - struct gsm_lchan *lchan; - u_int8_t rqd_ta; - int ret; - - u_int16_t arfcn; - u_int8_t ts_number, subch; - - /* parse request reference to be used in immediate assign */ - if (rqd_hdr->data[0] != RSL_IE_REQ_REFERENCE) - return -EINVAL; - - rqd_ref = (struct gsm48_req_ref *) &rqd_hdr->data[1]; - - /* parse access delay and use as TA */ - if (rqd_hdr->data[sizeof(struct gsm48_req_ref)+1] != RSL_IE_ACCESS_DELAY) - return -EINVAL; - rqd_ta = rqd_hdr->data[sizeof(struct gsm48_req_ref)+2]; - - /* 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); - - /* check availability / allocate channel */ - lchan = lchan_alloc(bts, lctype); - if (!lchan) { - fprintf(stderr, "CHAN RQD: no resources\n"); - /* FIXME: send some kind of reject ?!? */ - return -ENOMEM; - } - - ts_number = lchan->ts->nr; - arfcn = lchan->ts->trx->arfcn; - subch = lchan->nr; - - lchan->ms_power = lchan->bs_power = 0x0f; /* 30dB reduction */ - rsl_chan_activate_lchan(lchan, 0x00, rqd_ta, RSL_CMOD_SPD_SIGN); - - /* create IMMEDIATE ASSIGN 04.08 messge */ - memset(&ia, 0, sizeof(ia)); - ia.l2_plen = 0x2d; - ia.proto_discr = GSM48_PDISC_RR; - ia.msg_type = GSM48_MT_RR_IMM_ASS; - ia.page_mode = GSM48_PM_SAME; - ia.chan_desc.chan_nr = lchan2chan_nr(lchan); - ia.chan_desc.h0.h = 0; - ia.chan_desc.h0.arfcn_high = arfcn >> 8; - ia.chan_desc.h0.arfcn_low = arfcn & 0xff; - ia.chan_desc.h0.tsc = 7; - /* use request reference extracted from CHAN_RQD */ - memcpy(&ia.req_ref, rqd_ref, sizeof(ia.req_ref)); - ia.timing_advance = rqd_ta; - ia.mob_alloc_len = 0; - - DEBUGP(DRSL, "Activating ARFCN(%u) TS(%u) SS(%u) lctype %s " - "chan_nr=0x%02x r=%s ra=0x%02x\n", - arfcn, ts_number, subch, gsm_lchan_name(lchan->type), - ia.chan_desc.chan_nr, gsm_chreq_name(chreq_reason), - rqd_ref->ra); - - /* FIXME: Start timer T3101 to wait for GSM48_MT_RR_PAG_RESP */ - - /* 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); - - return ret; -} - -/* MS has requested a channel on the RACH */ -static int rsl_rx_ccch_load(struct msgb *msg) -{ - struct abis_rsl_dchan_hdr *rslh = msgb_l2(msg); - u_int16_t pg_buf_space; - u_int16_t rach_slot_count = -1; - u_int16_t rach_busy_count = -1; - u_int16_t rach_access_count = -1; - - switch (rslh->data[0]) { - case RSL_IE_PAGING_LOAD: - pg_buf_space = rslh->data[1] << 8 | rslh->data[2]; - paging_update_buffer_space(msg->trx->bts, pg_buf_space); - break; - case RSL_IE_RACH_LOAD: - if (msg->data_len >= 7) { - rach_slot_count = rslh->data[2] << 8 | rslh->data[3]; - rach_busy_count = rslh->data[4] << 8 | rslh->data[5]; - rach_access_count = rslh->data[6] << 8 | rslh->data[7]; - } - break; - default: - break; - } - - return 0; -} - -static int abis_rsl_rx_cchan(struct msgb *msg) -{ - struct abis_rsl_dchan_hdr *rslh = msgb_l2(msg); - int rc = 0; - - msg->lchan = lchan_lookup(msg->trx, rslh->chan_nr); - - switch (rslh->c.msg_type) { - case RSL_MT_CHAN_RQD: - /* MS has requested a channel on the RACH */ - rc = rsl_rx_chan_rqd(msg); - break; - case RSL_MT_CCCH_LOAD_IND: - /* current load on the CCCH */ - rc = rsl_rx_ccch_load(msg); - break; - case RSL_MT_DELETE_IND: - /* CCCH overloaded, IMM_ASSIGN was dropped */ - case RSL_MT_CBCH_LOAD_IND: - /* current load on the CBCH */ - fprintf(stderr, "Unimplemented Abis RSL TRX message type " - "0x%02x\n", rslh->c.msg_type); - break; - default: - fprintf(stderr, "Unknown Abis RSL TRX message type 0x%02x\n", - rslh->c.msg_type); - return -EINVAL; - } - - return rc; -} - -static int rsl_rx_rll_err_ind(struct msgb *msg) -{ - struct abis_rsl_rll_hdr *rllh = msgb_l2(msg); - u_int8_t *rlm_cause = rllh->data; - - DEBUGPC(DRLL, "cause=0x%02x", rlm_cause[1]); - - return 0; -} - -/* ESTABLISH INDICATION, LOCATION AREA UPDATE REQUEST - 0x02, 0x06, - 0x01, 0x20, - 0x02, 0x00, - 0x0b, 0x00, 0x0f, 0x05, 0x08, ... */ - -static int abis_rsl_rx_rll(struct msgb *msg) -{ - struct abis_rsl_rll_hdr *rllh = msgb_l2(msg); - int rc = 0; - char *ts_name; - - msg->lchan = lchan_lookup(msg->trx, rllh->chan_nr); - ts_name = gsm_ts_name(msg->lchan->ts); - DEBUGP(DRLL, "channel=%s chan_nr=0x%02x ", ts_name, rllh->chan_nr); - - switch (rllh->c.msg_type) { - case RSL_MT_DATA_IND: - DEBUGPC(DRLL, "DATA INDICATION\n"); - if (msgb_l2len(msg) > - sizeof(struct abis_rsl_common_hdr) + sizeof(*rllh) && - rllh->data[0] == RSL_IE_L3_INFO) { - msg->l3h = &rllh->data[3]; - return gsm0408_rcvmsg(msg); - } - break; - case RSL_MT_EST_IND: - DEBUGPC(DRLL, "ESTABLISH INDICATION\n"); - if (msgb_l2len(msg) > - sizeof(struct abis_rsl_common_hdr) + sizeof(*rllh) && - rllh->data[0] == RSL_IE_L3_INFO) { - msg->l3h = &rllh->data[3]; - return gsm0408_rcvmsg(msg); - } - break; - case RSL_MT_REL_IND: - DEBUGPC(DRLL, "RELEASE INDICATION "); - break; - case RSL_MT_REL_CONF: - DEBUGPC(DRLL, "RELEASE CONFIRMATION "); - break; - case RSL_MT_ERROR_IND: - DEBUGPC(DRLL, "ERROR INDICATION "); - rc = rsl_rx_rll_err_ind(msg); - break; - case RSL_MT_UNIT_DATA_IND: - DEBUGPC(DRLL, "unimplemented Abis RLL message type 0x%02x ", - rllh->c.msg_type); - break; - default: - DEBUGPC(DRLL, "unknown Abis RLL message type 0x%02x ", - rllh->c.msg_type); - } - DEBUGPC(DRLL, "\n"); - return rc; -} - -/* ip.access specific RSL extensions */ -int rsl_ipacc_bind(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_BIND); - dh->c.msg_discr = ABIS_RSL_MDISC_IPACCESS; - dh->chan_nr = lchan2chan_nr(lchan); - - msg->trx = lchan->ts->trx; - - return abis_rsl_sendmsg(msg); -} - -int rsl_ipacc_connect(struct gsm_lchan *lchan, u_int32_t ip, u_int16_t port, u_int16_t f8, u_int8_t fc) -{ - struct msgb *msg = rsl_msgb_alloc(); - struct abis_rsl_dchan_hdr *dh; - u_int8_t *att_f8, *att_ip, *att_port; - - dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); - init_dchan_hdr(dh, RSL_MT_IPAC_CONNECT); - dh->c.msg_discr = ABIS_RSL_MDISC_IPACCESS; - dh->chan_nr = lchan2chan_nr(lchan); - - att_f8 = msgb_put(msg, sizeof(f8)+1); - att_f8[0] = 0xf8; - att_f8[1] = f8 >> 8; - att_f8[2] = f8 & 0xff; - - att_ip = msgb_put(msg, sizeof(ip)+1); - att_ip[0] = RSL_IE_IPAC_REMOTE_IP; - att_ip[1] = ip >> 24; - att_ip[2] = ip >> 16; - att_ip[3] = ip >> 8; - att_ip[4] = ip & 0xff; - //att_ip[4] = 11; - - att_port = msgb_put(msg, sizeof(port)+1); - att_port[0] = RSL_IE_IPAC_REMOTE_PORT; - att_port[1] = port >> 8; - att_port[2] = port & 0xff; - - msgb_tv_put(msg, 0xf4, 1); /* F4 01 */ - msgb_tv_put(msg, 0xfc, fc); /* FC 7F */ - msg->trx = lchan->ts->trx; - - return abis_rsl_sendmsg(msg); -} - -static int abis_rsl_rx_ipacc_bindack(struct msgb *msg) -{ - struct abis_rsl_dchan_hdr *dh = msgb_l2(msg); - struct tlv_parsed tv; - struct gsm_bts_trx_ts *ts = msg->lchan->ts; - struct in_addr ip; - u_int16_t port, attr_f8; - - /* the BTS has acknowledged a local bind, it now tells us the IP - * address and port number to which it has bound the given logical - * channel */ - - rsl_tlv_parse(&tv, dh->data, msgb_l2len(msg)-sizeof(*dh)); - if (!TLVP_PRESENT(&tv, RSL_IE_IPAC_LOCAL_PORT) || - !TLVP_PRESENT(&tv, RSL_IE_IPAC_LOCAL_IP) || - !TLVP_PRESENT(&tv, 0xfc) || - !TLVP_PRESENT(&tv, 0xf8)) { - DEBUGPC(DRSL, "mandatory IE missing"); - return -EINVAL; - } - ip.s_addr = *((u_int32_t *) TLVP_VAL(&tv, RSL_IE_IPAC_LOCAL_IP)); - port = *((u_int16_t *) TLVP_VAL(&tv, RSL_IE_IPAC_LOCAL_PORT)); - attr_f8 = *((u_int16_t *) TLVP_VAL(&tv, 0xf8)); - - DEBUGPC(DRSL, "IP=%s PORT=%d FC=%d F8=%d", - inet_ntoa(ip), ntohs(port), *TLVP_VAL(&tv, 0xfc), - ntohs(attr_f8)); - - /* update our local information about this TS */ - ts->abis_ip.bound_ip = ntohl(ip.s_addr); - ts->abis_ip.bound_port = ntohs(port); - ts->abis_ip.attr_f8 = ntohs(attr_f8); - ts->abis_ip.attr_fc = *TLVP_VAL(&tv, 0xfc); - - dispatch_signal(SS_ABISIP, S_ABISIP_BIND_ACK, msg->lchan); - - return 0; -} - -static int abis_rsl_rx_ipacc_disc_ind(struct msgb *msg) -{ - struct abis_rsl_dchan_hdr *dh = msgb_l2(msg); - struct tlv_parsed tv; - - rsl_tlv_parse(&tv, dh->data, msgb_l2len(msg)-sizeof(*dh)); - if (!TLVP_PRESENT(&tv, RSL_IE_CAUSE)) { - DEBUGPC(DRSL, "mandatory IE missing! "); - return -EINVAL; - } - - DEBUGPC(DRSL, "cause=0x%02x ", *TLVP_VAL(&tv, RSL_IE_CAUSE)); - - return 0; -} - -static int abis_rsl_rx_ipacc(struct msgb *msg) -{ - struct abis_rsl_rll_hdr *rllh = msgb_l2(msg); - int rc = 0; - - msg->lchan = lchan_lookup(msg->trx, rllh->chan_nr); - DEBUGP(DRSL, "channel=%s chan_nr=0x%02x ", - 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); - break; - case RSL_MT_IPAC_BIND_NACK: - /* somehow the BTS was unable to bind the lchan to its local - * port?!? */ - DEBUGPC(DRSL, "IPAC_BIND_NACK "); - break; - case RSL_MT_IPAC_CONNECT_ACK: - /* the BTS tells us that a connect operation was successful */ - DEBUGPC(DRSL, "IPAC_CONNECT_ACK "); - break; - case RSL_MT_IPAC_CONNECT_NACK: - /* somehow the BTS was unable to connect the lchan to a remote - * port */ - DEBUGPC(DRSL, "IPAC_CONNECT_NACK "); - break; - case RSL_MT_IPAC_DISCONNECT_IND: - DEBUGPC(DRSL, "IPAC_DISCONNECT_IND "); - rc = abis_rsl_rx_ipacc_disc_ind(msg); - break; - default: - DEBUGPC(DRSL, "Unknown ip.access msg_type 0x%02x", rllh->c.msg_type); - break; - } - DEBUGPC(DRSL, "\n"); - - return rc; -} - - -/* Entry-point where L2 RSL from BTS enters */ -int abis_rsl_rcvmsg(struct msgb *msg) -{ - struct abis_rsl_common_hdr *rslh = msgb_l2(msg) ; - int rc = 0; - - switch (rslh->msg_discr & 0xfe) { - case ABIS_RSL_MDISC_RLL: - rc = abis_rsl_rx_rll(msg); - break; - case ABIS_RSL_MDISC_DED_CHAN: - rc = abis_rsl_rx_dchan(msg); - break; - case ABIS_RSL_MDISC_COM_CHAN: - rc = abis_rsl_rx_cchan(msg); - break; - case ABIS_RSL_MDISC_TRX: - rc = abis_rsl_rx_trx(msg); - break; - case ABIS_RSL_MDISC_LOC: - fprintf(stderr, "unimplemented RSL msg disc 0x%02x\n", - rslh->msg_discr); - break; - case ABIS_RSL_MDISC_IPACCESS: - rc = abis_rsl_rx_ipacc(msg); - break; - default: - fprintf(stderr, "unknown RSL message discriminator 0x%02x\n", - rslh->msg_discr); - return -EINVAL; - } - msgb_free(msg); - return rc; -} - - -/* Section 3.3.2.3 . I think this looks like a table */ -int rsl_ccch_conf_to_bs_cc_chans(int ccch_conf) -{ - switch (ccch_conf) { - case RSL_BCCH_CCCH_CONF_1_NC: - return 1; - case RSL_BCCH_CCCH_CONF_1_C: - return 1; - case RSL_BCCH_CCCH_CONF_2_NC: - return 2; - case RSL_BCCH_CCCH_CONF_3_NC: - return 3; - case RSL_BCCH_CCCH_CONF_4_NC: - return 4; - default: - return -1; - } -} - -int rsl_ccch_conf_to_bs_ccch_sdcch_comb(int ccch_conf) -{ - switch (ccch_conf) { - case RSL_BCCH_CCCH_CONF_1_NC: - return 0; - case RSL_BCCH_CCCH_CONF_1_C: - return 1; - case RSL_BCCH_CCCH_CONF_2_NC: - return 0; - case RSL_BCCH_CCCH_CONF_3_NC: - return 0; - case RSL_BCCH_CCCH_CONF_4_NC: - return 0; - default: - return -1; - } -} - -/* From Table 10.5.33 of GSM 04.08 */ -int rsl_number_of_paging_subchannels(struct gsm_bts *bts) -{ - if (bts->chan_desc.ccch_conf == RSL_BCCH_CCCH_CONF_1_C) { - return MAX(1, (3 - bts->chan_desc.bs_ag_blks_res)) - * (bts->chan_desc.bs_pa_mfrms + 2); - } else { - return (9 - bts->chan_desc.bs_ag_blks_res) - * (bts->chan_desc.bs_pa_mfrms + 2); - } -} diff --git a/src/bs11_config.c b/src/bs11_config.c deleted file mode 100644 index 656feb453..000000000 --- a/src/bs11_config.c +++ /dev/null @@ -1,805 +0,0 @@ -/* Siemens BS-11 microBTS configuration tool */ - -/* (C) 2009 by Harald Welte <laforge@gnumonks.org> - * All Rights Reserved - * - * This software is based on ideas (but not code) of BS11Config - * (C) 2009 by Dieter Spaar <spaar@mirider.augusta.de> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - */ - -#include <unistd.h> -#include <stdlib.h> -#include <stdio.h> -#include <errno.h> -#include <string.h> -#include <getopt.h> -#include <fcntl.h> -#include <signal.h> - -#include <sys/types.h> -#include <sys/stat.h> - -#include <openbsc/gsm_data.h> -#include <openbsc/abis_nm.h> -#include <openbsc/msgb.h> -#include <openbsc/tlv.h> -#include <openbsc/debug.h> -#include <openbsc/select.h> -#include <openbsc/rs232.h> - -/* state of our bs11_config application */ -enum bs11cfg_state { - STATE_NONE, - STATE_LOGON_WAIT, - STATE_LOGON_ACK, - STATE_SWLOAD, - STATE_QUERY, -}; -static enum bs11cfg_state bs11cfg_state = STATE_NONE; -static char *command; -struct timer_list status_timer; - -static const u_int8_t obj_li_attr[] = { - NM_ATT_BS11_BIT_ERR_THESH, 0x09, 0x00, - NM_ATT_BS11_L1_PROT_TYPE, 0x00, - NM_ATT_BS11_LINE_CFG, 0x00, -}; -static const u_int8_t obj_bbsig0_attr[] = { - NM_ATT_BS11_RSSI_OFFS, 0x02, 0x00, 0x00, - NM_ATT_BS11_DIVERSITY, 0x01, 0x00, -}; -static const u_int8_t obj_pa0_attr[] = { - NM_ATT_BS11_TXPWR, 0x01, BS11_TRX_POWER_GSM_30mW, -}; -static const char *trx1_password = "1111111111"; -#define TEI_OML 25 - -static const u_int8_t too_fast[] = { 0x12, 0x80, 0x00, 0x00, 0x02, 0x02 }; - - -int handle_serial_msg(struct msgb *rx_msg); - -/* create all objects for an initial configuration */ -static int create_objects(struct gsm_bts *bts) -{ - fprintf(stdout, "Crating Objects for minimal config\n"); - abis_nm_bs11_create_object(bts, BS11_OBJ_LI, 0, sizeof(obj_li_attr), - obj_li_attr); - abis_nm_bs11_create_object(bts, BS11_OBJ_GPSU, 0, 0, NULL); - abis_nm_bs11_create_object(bts, BS11_OBJ_ALCO, 0, 0, NULL); - abis_nm_bs11_create_object(bts, BS11_OBJ_CCLK, 0, 0, NULL); - abis_nm_bs11_create_object(bts, BS11_OBJ_BBSIG, 0, - sizeof(obj_bbsig0_attr), obj_bbsig0_attr); - abis_nm_bs11_create_object(bts, BS11_OBJ_PA, 0, - sizeof(obj_pa0_attr), obj_pa0_attr); - abis_nm_bs11_create_envaBTSE(bts, 0); - abis_nm_bs11_create_envaBTSE(bts, 1); - abis_nm_bs11_create_envaBTSE(bts, 2); - abis_nm_bs11_create_envaBTSE(bts, 3); - - abis_nm_bs11_conn_oml_tei(bts, 0, 1, 0xff, TEI_OML); - - abis_nm_bs11_set_trx_power(&bts->trx[0], BS11_TRX_POWER_GSM_30mW); - - sleep(1); - - abis_nm_bs11_set_trx1_pw(bts, trx1_password); - - sleep(1); - - return 0; -} - -static int create_trx1(struct gsm_bts *bts) -{ - u_int8_t bbsig1_attr[sizeof(obj_bbsig0_attr)+12]; - u_int8_t *cur = bbsig1_attr; - - fprintf(stdout, "Crating Objects for TRX1\n"); - - abis_nm_bs11_set_trx1_pw(bts, trx1_password); - - sleep(1); - - cur = tlv_put(cur, NM_ATT_BS11_PASSWORD, 10, - (u_int8_t *)trx1_password); - memcpy(cur, obj_bbsig0_attr, sizeof(obj_bbsig0_attr)); - abis_nm_bs11_create_object(bts, BS11_OBJ_BBSIG, 1, - sizeof(bbsig1_attr), bbsig1_attr); - abis_nm_bs11_create_object(bts, BS11_OBJ_PA, 1, - sizeof(obj_pa0_attr), obj_pa0_attr); - abis_nm_bs11_set_trx_power(&bts->trx[1], BS11_TRX_POWER_GSM_30mW); - - return 0; -} - -static char *serial_port = "/dev/ttyUSB0"; -static char *fname_safety = "BTSBMC76.SWI"; -static char *fname_software = "HS011106.SWL"; -static int delay_ms = 0; -static int win_size = 8; -static int param_disconnect = 0; -static int param_restart = 0; -static int param_forced = 0; -static struct gsm_bts *g_bts; - -static int file_is_readable(const char *fname) -{ - int rc; - struct stat st; - - rc = stat(fname, &st); - if (rc < 0) - return 0; - - if (S_ISREG(st.st_mode) && (st.st_mode & S_IRUSR)) - return 1; - - return 0; -} - -static int percent; -static int percent_old; - -/* callback function passed to the ABIS OML code */ -static int swload_cbfn(unsigned int hook, unsigned int event, struct msgb *msg, - void *data, void *param) -{ - if (hook != GSM_HOOK_NM_SWLOAD) - return 0; - - switch (event) { - case NM_MT_LOAD_INIT_ACK: - fprintf(stdout, "Software Load Initiate ACK\n"); - break; - case NM_MT_LOAD_INIT_NACK: - fprintf(stderr, "ERROR: Software Load Initiate NACK\n"); - exit(5); - break; - case NM_MT_LOAD_END_ACK: - if (data) { - /* we did a safety load and must activate it */ - abis_nm_software_activate(g_bts, fname_safety, - swload_cbfn, g_bts); - sleep(5); - } - break; - case NM_MT_LOAD_END_NACK: - fprintf(stderr, "ERROR: Software Load End NACK\n"); - exit(3); - break; - case NM_MT_ACTIVATE_SW_NACK: - fprintf(stderr, "ERROR: Activate Software NACK\n"); - exit(4); - break; - case NM_MT_ACTIVATE_SW_ACK: - bs11cfg_state = STATE_NONE; - - break; - case NM_MT_LOAD_SEG_ACK: - percent = abis_nm_software_load_status(g_bts); - if (percent > percent_old) - printf("Software Download Progress: %d%%\n", percent); - percent_old = percent; - break; - } - return 0; -} - -static const char *bs11_link_state[] = { - [0x00] = "Down", - [0x01] = "Up", - [0x02] = "Restoring", -}; - -static const char *linkstate_name(u_int8_t linkstate) -{ - if (linkstate > ARRAY_SIZE(bs11_link_state)) - return "Unknown"; - - return bs11_link_state[linkstate]; -} - -static const char *mbccu_load[] = { - [0] = "No Load", - [1] = "Load BTSCAC", - [2] = "Load BTSDRX", - [3] = "Load BTSBBX", - [4] = "Load BTSARC", - [5] = "Load", -}; - -static const char *mbccu_load_name(u_int8_t linkstate) -{ - if (linkstate > ARRAY_SIZE(mbccu_load)) - return "Unknown"; - - return mbccu_load[linkstate]; -} - -static const char *bts_phase_name(u_int8_t phase) -{ - switch (phase) { - case BS11_STATE_WARM_UP: - case BS11_STATE_WARM_UP_2: - return "Warm Up"; - break; - case BS11_STATE_LOAD_SMU_SAFETY: - return "Load SMU Safety"; - break; - case BS11_STATE_LOAD_SMU_INTENDED: - return "Load SMU Intended"; - break; - case BS11_STATE_LOAD_MBCCU: - return "Load MBCCU"; - break; - case BS11_STATE_SOFTWARE_RQD: - return "Software required"; - break; - case BS11_STATE_WAIT_MIN_CFG: - case BS11_STATE_WAIT_MIN_CFG_2: - return "Wait minimal config"; - break; - case BS11_STATE_MAINTENANCE: - return "Maintenance"; - break; - case BS11_STATE_NORMAL: - return "Normal"; - break; - case BS11_STATE_ABIS_LOAD: - return "Abis load"; - break; - default: - return "Unknown"; - break; - } -} - -static const char *trx_power_name(u_int8_t pwr) -{ - switch (pwr) { - case BS11_TRX_POWER_GSM_2W: - return "2W (GSM)"; - case BS11_TRX_POWER_GSM_250mW: - return "250mW (GSM)"; - case BS11_TRX_POWER_GSM_80mW: - return "80mW (GSM)"; - case BS11_TRX_POWER_GSM_30mW: - return "30mW (GSM)"; - case BS11_TRX_POWER_DCS_3W: - return "3W (DCS)"; - case BS11_TRX_POWER_DCS_1W6: - return "1.6W (DCS)"; - case BS11_TRX_POWER_DCS_500mW: - return "500mW (DCS)"; - case BS11_TRX_POWER_DCS_160mW: - return "160mW (DCS)"; - default: - return "unknown value"; - } -} - -static const char *pll_mode_name(u_int8_t mode) -{ - switch (mode) { - case BS11_LI_PLL_LOCKED: - return "E1 Locked"; - case BS11_LI_PLL_STANDALONE: - return "Standalone"; - default: - return "unknown"; - } -} - -static const char *cclk_acc_name(u_int8_t acc) -{ - switch (acc) { - case 0: - /* Out of the demanded +/- 0.05ppm */ - return "Medium"; - case 1: - /* Synchronized with Abis, within demanded tolerance +/- 0.05ppm */ - return "High"; - default: - return "unknown"; - } -} - -static const char *obj_name(struct abis_om_fom_hdr *foh) -{ - static char retbuf[256]; - - retbuf[0] = 0; - - switch (foh->obj_class) { - case NM_OC_BS11: - strcat(retbuf, "BS11 "); - switch (foh->obj_inst.bts_nr) { - case BS11_OBJ_PA: - sprintf(retbuf+strlen(retbuf), "Power Amplifier %d ", - foh->obj_inst.ts_nr); - break; - case BS11_OBJ_LI: - sprintf(retbuf+strlen(retbuf), "Line Interface "); - break; - case BS11_OBJ_CCLK: - sprintf(retbuf+strlen(retbuf), "CCLK "); - break; - } - break; - case NM_OC_SITE_MANAGER: - strcat(retbuf, "SITE MANAGER "); - break; - } - return retbuf; -} - -static void print_state(struct tlv_parsed *tp) -{ - if (TLVP_PRESENT(tp, NM_ATT_BS11_BTS_STATE)) { - u_int8_t phase, mbccu; - if (TLVP_LEN(tp, NM_ATT_BS11_BTS_STATE) >= 1) { - phase = *TLVP_VAL(tp, NM_ATT_BS11_BTS_STATE); - printf("PHASE: %u %-20s ", phase & 0xf, - bts_phase_name(phase)); - } - if (TLVP_LEN(tp, NM_ATT_BS11_BTS_STATE) >= 2) { - mbccu = *(TLVP_VAL(tp, NM_ATT_BS11_BTS_STATE)+1); - printf("MBCCU0: %-11s MBCCU1: %-11s ", - mbccu_load_name(mbccu & 0xf), mbccu_load_name(mbccu >> 4)); - } - } - if (TLVP_PRESENT(tp, NM_ATT_BS11_E1_STATE) && - TLVP_LEN(tp, NM_ATT_BS11_E1_STATE) >= 1) { - u_int8_t e1_state = *TLVP_VAL(tp, NM_ATT_BS11_E1_STATE); - printf("Abis-link: %-9s ", linkstate_name(e1_state & 0xf)); - } - printf("\n"); -} - -static int print_attr(struct tlv_parsed *tp) -{ - if (TLVP_PRESENT(tp, NM_ATT_BS11_ESN_PCB_SERIAL)) { - printf("\tBS-11 ESN PCB Serial Number: %s\n", - TLVP_VAL(tp, NM_ATT_BS11_ESN_PCB_SERIAL)); - } - if (TLVP_PRESENT(tp, NM_ATT_BS11_ESN_HW_CODE_NO)) { - printf("\tBS-11 ESN Hardware Code Number: %s\n", - TLVP_VAL(tp, NM_ATT_BS11_ESN_HW_CODE_NO)+6); - } - if (TLVP_PRESENT(tp, NM_ATT_BS11_ESN_FW_CODE_NO)) { - printf("\tBS-11 ESN Firmware Code Number: %s\n", - TLVP_VAL(tp, NM_ATT_BS11_ESN_FW_CODE_NO)+6); - } -#if 0 - if (TLVP_PRESENT(tp, NM_ATT_BS11_BOOT_SW_VERS)) { - printf("BS-11 Boot Software Version: %s\n", - TLVP_VAL(tp, NM_ATT_BS11_BOOT_SW_VERS)+6); - } -#endif - if (TLVP_PRESENT(tp, NM_ATT_ABIS_CHANNEL) && - TLVP_LEN(tp, NM_ATT_ABIS_CHANNEL) >= 3) { - const u_int8_t *chan = TLVP_VAL(tp, NM_ATT_ABIS_CHANNEL); - printf("\tE1 Channel: Port=%u Timeslot=%u ", - chan[0], chan[1]); - if (chan[2] == 0xff) - printf("(Full Slot)\n"); - else - printf("Subslot=%u\n", chan[2]); - } - if (TLVP_PRESENT(tp, NM_ATT_TEI)) - printf("\tTEI: %d\n", *TLVP_VAL(tp, NM_ATT_TEI)); - if (TLVP_PRESENT(tp, NM_ATT_BS11_TXPWR) && - TLVP_LEN(tp, NM_ATT_BS11_TXPWR) >= 1) { - printf("\tTRX Power: %s\n", - trx_power_name(*TLVP_VAL(tp, NM_ATT_BS11_TXPWR))); - } - if (TLVP_PRESENT(tp, NM_ATT_BS11_PLL_MODE) && - TLVP_LEN(tp, NM_ATT_BS11_PLL_MODE) >= 1) { - printf("\tPLL Mode: %s\n", - pll_mode_name(*TLVP_VAL(tp, NM_ATT_BS11_PLL_MODE))); - } - if (TLVP_PRESENT(tp, NM_ATT_BS11_PLL) && - TLVP_LEN(tp, NM_ATT_BS11_PLL) >= 4) { - const u_int8_t *vp = TLVP_VAL(tp, NM_ATT_BS11_PLL); - printf("\tPLL Set Value=%d, Work Value=%d\n", - vp[0] << 8 | vp[1], vp[2] << 8 | vp[3]); - } - if (TLVP_PRESENT(tp, NM_ATT_BS11_CCLK_ACCURACY) && - TLVP_LEN(tp, NM_ATT_BS11_CCLK_ACCURACY) >= 1) { - const u_int8_t *acc = TLVP_VAL(tp, NM_ATT_BS11_CCLK_ACCURACY); - printf("\tCCLK Accuracy: %s (%d)\n", cclk_acc_name(*acc), *acc); - } - if (TLVP_PRESENT(tp, NM_ATT_BS11_CCLK_TYPE) && - TLVP_LEN(tp, NM_ATT_BS11_CCLK_TYPE) >= 1) { - const u_int8_t *acc = TLVP_VAL(tp, NM_ATT_BS11_CCLK_TYPE); - printf("\tCCLK Type=%d\n", *acc); - } - - - return 0; -} - -static void cmd_query(void) -{ - bs11cfg_state = STATE_QUERY; - abis_nm_bs11_get_serno(g_bts); - abis_nm_bs11_get_oml_tei_ts(g_bts); - abis_nm_bs11_get_pll_mode(g_bts); - abis_nm_bs11_get_cclk(g_bts); - abis_nm_bs11_get_trx_power(&g_bts->trx[0]); - abis_nm_bs11_get_trx_power(&g_bts->trx[1]); - sleep(1); - abis_nm_bs11_factory_logon(g_bts, 0); - command = NULL; -} - -/* handle a response from the BTS to a GET STATE command */ -static int handle_state_resp(enum abis_bs11_phase state) -{ - int rc = 0; - - switch (state) { - case BS11_STATE_WARM_UP: - case BS11_STATE_LOAD_SMU_SAFETY: - case BS11_STATE_LOAD_SMU_INTENDED: - case BS11_STATE_LOAD_MBCCU: - break; - case BS11_STATE_SOFTWARE_RQD: - bs11cfg_state = STATE_SWLOAD; - /* send safety load. Use g_bts as private 'param' - * argument, so our swload_cbfn can distinguish - * a safety load from a regular software */ - if (file_is_readable(fname_safety)) - rc = abis_nm_software_load(g_bts, fname_safety, - win_size, param_forced, - swload_cbfn, g_bts); - else - fprintf(stderr, "No valid Safety Load file \"%s\"\n", - fname_safety); - break; - case BS11_STATE_WAIT_MIN_CFG: - case BS11_STATE_WAIT_MIN_CFG_2: - bs11cfg_state = STATE_SWLOAD; - rc = create_objects(g_bts); - break; - case BS11_STATE_MAINTENANCE: - if (command) { - if (!strcmp(command, "disconnect")) - abis_nm_bs11_factory_logon(g_bts, 0); - else if (!strcmp(command, "reconnect")) - rc = abis_nm_bs11_bsc_disconnect(g_bts, 1); - else if (!strcmp(command, "software") - && bs11cfg_state != STATE_SWLOAD) { - bs11cfg_state = STATE_SWLOAD; - /* send software (FIXME: over A-bis?) */ - if (file_is_readable(fname_software)) - rc = abis_nm_bs11_load_swl(g_bts, fname_software, - win_size, param_forced, - swload_cbfn); - else - fprintf(stderr, "No valid Software file \"%s\"\n", - fname_software); - } else if (!strcmp(command, "delete-trx1")) { - printf("Locing BBSIG and PA objects of TRX1\n"); - abis_nm_chg_adm_state(g_bts, NM_OC_BS11, - BS11_OBJ_BBSIG, 0, 1, - NM_STATE_LOCKED); - abis_nm_chg_adm_state(g_bts, NM_OC_BS11, - BS11_OBJ_PA, 0, 1, - NM_STATE_LOCKED); - sleep(1); - printf("Deleting BBSIG and PA objects of TRX1\n"); - abis_nm_bs11_delete_object(g_bts, BS11_OBJ_BBSIG, 1); - abis_nm_bs11_delete_object(g_bts, BS11_OBJ_PA, 1); - sleep(1); - abis_nm_bs11_factory_logon(g_bts, 0); - command = NULL; - } else if (!strcmp(command, "create-trx1")) { - create_trx1(g_bts); - sleep(1); - abis_nm_bs11_factory_logon(g_bts, 0); - command = NULL; - } else if (!strcmp(command, "pll-e1-locked")) { - abis_nm_bs11_set_pll_locked(g_bts, 1); - sleep(1); - abis_nm_bs11_factory_logon(g_bts, 0); - command = NULL; - } else if (!strcmp(command, "pll-standalone")) { - abis_nm_bs11_set_pll_locked(g_bts, 0); - sleep(1); - abis_nm_bs11_factory_logon(g_bts, 0); - command = NULL; - } else if (!strcmp(command, "oml-tei")) { - abis_nm_bs11_conn_oml_tei(g_bts, 0, 1, 0xff, TEI_OML); - command = NULL; - } else if (!strcmp(command, "restart")) { - abis_nm_bs11_restart(g_bts); - command = NULL; - } else if (!strcmp(command, "query")) { - cmd_query(); - } - } - break; - case BS11_STATE_NORMAL: - if (command) { - if (!strcmp(command, "reconnect")) - abis_nm_bs11_factory_logon(g_bts, 0); - else if (!strcmp(command, "disconnect")) - abis_nm_bs11_bsc_disconnect(g_bts, 0); - else if (!strcmp(command, "query")) { - cmd_query(); - } - } else if (param_disconnect) { - param_disconnect = 0; - abis_nm_bs11_bsc_disconnect(g_bts, 0); - if (param_restart) { - param_restart = 0; - abis_nm_bs11_restart(g_bts); - } - } - break; - default: - break; - } - return rc; -} - -/* handle a fully-received message/packet from the RS232 port */ -int handle_serial_msg(struct msgb *rx_msg) -{ - struct abis_om_hdr *oh; - struct abis_om_fom_hdr *foh; - struct tlv_parsed tp; - int rc = -1; - -#if 0 - if (rx_msg->len < LAPD_HDR_LEN - + sizeof(struct abis_om_fom_hdr) - + sizeof(struct abis_om_hdr)) { - if (!memcmp(rx_msg->data + 2, too_fast, - sizeof(too_fast))) { - fprintf(stderr, "BS11 tells us we're too " - "fast, try --delay bigger than %u\n", - delay_ms); - return -E2BIG; - } else - fprintf(stderr, "unknown BS11 message\n"); - } -#endif - - oh = (struct abis_om_hdr *) msgb_l2(rx_msg); - foh = (struct abis_om_fom_hdr *) oh->data; - switch (foh->msg_type) { - case NM_MT_BS11_LMT_LOGON_ACK: - printf("LMT LOGON: ACK\n\n"); - if (bs11cfg_state == STATE_NONE) - bs11cfg_state = STATE_LOGON_ACK; - rc = abis_nm_bs11_get_state(g_bts); - break; - case NM_MT_BS11_LMT_LOGOFF_ACK: - printf("LMT LOGOFF: ACK\n"); - exit(0); - break; - case NM_MT_BS11_GET_STATE_ACK: - rc = abis_nm_tlv_parse(&tp, foh->data, oh->length-sizeof(*foh)); - print_state(&tp); - if (TLVP_PRESENT(&tp, NM_ATT_BS11_BTS_STATE) && - TLVP_LEN(&tp, NM_ATT_BS11_BTS_STATE) >= 1) - rc = handle_state_resp(*TLVP_VAL(&tp, NM_ATT_BS11_BTS_STATE)); - break; - case NM_MT_GET_ATTR_RESP: - printf("\n%sATTRIBUTES:\n", obj_name(foh)); - abis_nm_tlv_parse(&tp, foh->data, oh->length-sizeof(*foh)); - rc = print_attr(&tp); - //hexdump(foh->data, oh->length-sizeof(*foh)); - break; - case NM_MT_BS11_SET_ATTR_ACK: - printf("SET ATTRIBUTE ObjClass=0x%02x ObjInst=(%d,%d,%d) ACK\n", - foh->obj_class, foh->obj_inst.bts_nr, - foh->obj_inst.trx_nr, foh->obj_inst.ts_nr); - rc = 0; - break; - case NM_MT_BS11_SET_ATTR_NACK: - printf("SET ATTRIBUTE ObjClass=0x%02x ObjInst=(%d,%d,%d) NACK\n", - foh->obj_class, foh->obj_inst.bts_nr, - foh->obj_inst.trx_nr, foh->obj_inst.ts_nr); - break; - default: - rc = abis_nm_rcvmsg(rx_msg); - } - if (rc < 0) { - perror("ERROR in main loop"); - //break; - } - if (rc == 1) - return rc; - - switch (bs11cfg_state) { - case STATE_NONE: - abis_nm_bs11_factory_logon(g_bts, 1); - break; - case STATE_LOGON_ACK: - bsc_schedule_timer(&status_timer, 5, 0); - break; - default: - break; - } - - return rc; -} - -int nm_state_event(enum nm_evt evt, u_int8_t obj_class, void *obj, - struct gsm_nm_state *old_state, struct gsm_nm_state *new_state) -{ - return 0; -} - -void status_timer_cb(void *data) -{ - abis_nm_bs11_get_state(g_bts); -} - -static void print_banner(void) -{ - printf("bs11_config (C) 2009 by Harald Welte and Dieter Spaar\n"); - printf("This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY\n\n"); -} - -static void print_help(void) -{ - printf("bs11_config [options] [command]\n"); - printf("\nSupported options:\n"); - printf("\t-h --help\t\t\tPrint this help text\n"); - printf("\t-p --port </dev/ttyXXX>\t\tSpecify serial port\n"); - printf("\t-s --software <file>\t\tSpecify Software file\n"); - printf("\t-S --safety <file>\t\tSpecify Safety Load file\n"); - printf("\t-d --delay <ms>\t\tSpecify delay in milliseconds\n"); - printf("\t-D --disconnect\t\t\tDisconnect BTS from BSC\n"); - printf("\t-w --win-size <num>\t\tSpecify Window Size\n"); - printf("\t-f --forced\t\t\tForce Software Load\n"); - printf("\nSupported commands:\n"); - printf("\tquery\t\tQuery the BS-11 about serial number and configuration\n"); - printf("\tdisconnect\tDisconnect A-bis link (go into administrative state)\n"); - printf("\tresconnect\tReconnect A-bis link (go into normal state)\n"); - printf("\trestart\t\tRestart the BTS\n"); - printf("\tsoftware\tDownload Software (only in administrative state)\n"); - printf("\tcreate-trx1\tCreate objects for TRX1 (Danger: Your BS-11 might overheat)\n"); - printf("\tdelete-trx1\tDelete objects for TRX1\n"); - printf("\tpll-e1-locked\tSet the PLL to be locked to E1 clock\n"); - printf("\tpll-standalone\tSet the PLL to be in standalone mode\n"); - printf("\toml-tei\tSet OML E1 TS and TEI\n"); -} - -static void handle_options(int argc, char **argv) -{ - int option_index = 0; - print_banner(); - - while (1) { - int c; - static struct option long_options[] = { - { "help", 0, 0, 'h' }, - { "port", 1, 0, 'p' }, - { "software", 1, 0, 's' }, - { "safety", 1, 0, 'S' }, - { "delay", 1, 0, 'd' }, - { "disconnect", 0, 0, 'D' }, - { "win-size", 1, 0, 'w' }, - { "forced", 0, 0, 'f' }, - { "restart", 0, 0, 'r' }, - { "debug", 1, 0, 'b'}, - }; - - c = getopt_long(argc, argv, "hp:s:S:td:Dw:fra:", - long_options, &option_index); - - if (c == -1) - break; - - switch (c) { - case 'h': - print_help(); - exit(0); - case 'p': - serial_port = optarg; - break; - case 'b': - debug_parse_category_mask(optarg); - break; - case 's': - fname_software = optarg; - break; - case 'S': - fname_safety = optarg; - break; - case 'd': - delay_ms = atoi(optarg); - break; - case 'w': - win_size = atoi(optarg); - break; - case 'D': - param_disconnect = 1; - break; - case 'f': - param_forced = 1; - break; - case 'r': - param_disconnect = 1; - param_restart = 1; - break; - default: - break; - } - } - if (optind < argc) - command = argv[optind]; -} - -static int num_sigint; - -static void signal_handler(int signal) -{ - fprintf(stdout, "\nsignal %u received\n", signal); - - switch (signal) { - case SIGINT: - num_sigint++; - abis_nm_bs11_factory_logon(g_bts, 0); - if (num_sigint >= 3) - exit(0); - break; - } -} - -int main(int argc, char **argv) -{ - struct gsm_network *gsmnet; - int rc; - - handle_options(argc, argv); - - gsmnet = gsm_network_init(1, 1, 1, GSM_BTS_TYPE_BS11); - if (!gsmnet) { - fprintf(stderr, "Unable to allocate gsm network\n"); - exit(1); - } - g_bts = &gsmnet->bts[0]; - - rc = rs232_setup(serial_port, delay_ms, g_bts); - if (rc < 0) { - fprintf(stderr, "Problem setting up serial port\n"); - exit(1); - } - - signal(SIGINT, &signal_handler); - - abis_nm_bs11_factory_logon(g_bts, 1); - //abis_nm_bs11_get_serno(g_bts); - - status_timer.cb = status_timer_cb; - - while (1) { - bsc_select_main(0); - } - - abis_nm_bs11_factory_logon(g_bts, 0); - - exit(0); -} diff --git a/src/bsc_hack.c b/src/bsc_hack.c deleted file mode 100644 index 7aa8b9aef..000000000 --- a/src/bsc_hack.c +++ /dev/null @@ -1,1159 +0,0 @@ -/* A hackish minimal BSC (+MSC +HLR) implementation */ - -/* (C) 2008-2009 by Harald Welte <laforge@gnumonks.org> - * (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org> - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - */ - -#include <unistd.h> -#include <stdlib.h> -#include <stdio.h> -#include <stdarg.h> -#include <time.h> -#include <string.h> -#include <errno.h> -#include <signal.h> -#include <fcntl.h> -#include <sys/stat.h> - -#define _GNU_SOURCE -#include <getopt.h> - -#include <openbsc/db.h> -#include <openbsc/timer.h> -#include <openbsc/gsm_data.h> -#include <openbsc/gsm_04_08.h> -#include <openbsc/select.h> -#include <openbsc/abis_rsl.h> -#include <openbsc/abis_nm.h> -#include <openbsc/debug.h> -#include <openbsc/misdn.h> -#include <openbsc/telnet_interface.h> -#include <openbsc/paging.h> -#include <openbsc/e1_input.h> -#include <openbsc/signal.h> - -/* global pointer to the gsm network data structure */ -static struct gsm_network *gsmnet; - -/* MCC and MNC for the Location Area Identifier */ -static int MCC = 1; -static int MNC = 1; -static int LAC = 1; -static int ARFCN = HARDCODED_ARFCN; -static int cardnr = 0; -static int release_l2 = 0; -static enum gsm_bts_type BTS_TYPE = GSM_BTS_TYPE_BS11; -static const char *database_name = "hlr.sqlite3"; - -/* The following definitions are for OM and NM packets that we cannot yet - * generate by code but we just pass on */ - -// BTS Site Manager, SET ATTRIBUTES - -/* - Object Class: BTS Site Manager - Instance 1: FF - Instance 2: FF - Instance 3: FF -SET ATTRIBUTES - sAbisExternalTime: 2007/09/08 14:36:11 - omLAPDRelTimer: 30sec - shortLAPDIntTimer: 5sec - emergencyTimer1: 10 minutes - emergencyTimer2: 0 minutes -*/ - -unsigned char msg_1[] = -{ - 0xD0, 0x00, 0xFF, 0xFF, 0xFF, - NM_ATT_BS11_ABIS_EXT_TIME, 0x07, - 0xD7, 0x09, 0x08, 0x0E, 0x24, 0x0B, 0xCE, - 0x02, - 0x00, 0x1E, - NM_ATT_BS11_SH_LAPD_INT_TIMER, - 0x01, 0x05, - 0x42, 0x02, 0x00, 0x0A, - 0x44, 0x02, 0x00, 0x00 -}; - -// BTS, SET BTS ATTRIBUTES - -/* - Object Class: BTS - BTS relat. Number: 0 - Instance 2: FF - Instance 3: FF -SET BTS ATTRIBUTES - bsIdentityCode / BSIC: - PLMN_colour_code: 7h - BS_colour_code: 7h - BTS Air Timer T3105: 4 ,unit 10 ms - btsIsHopping: FALSE - periodCCCHLoadIndication: 1sec - thresholdCCCHLoadIndication: 0% - cellAllocationNumber: 00h = GSM 900 - enableInterferenceClass: 00h = Disabled - fACCHQual: 6 (FACCH stealing flags minus 1) - intaveParameter: 31 SACCH multiframes - interferenceLevelBoundaries: - Interference Boundary 1: 0Ah - Interference Boundary 2: 0Fh - Interference Boundary 3: 14h - Interference Boundary 4: 19h - Interference Boundary 5: 1Eh - mSTxPwrMax: 11 - GSM range: 2=39dBm, 15=13dBm, stepsize 2 dBm - DCS1800 range: 0=30dBm, 15=0dBm, stepsize 2 dBm - PCS1900 range: 0=30dBm, 15=0dBm, stepsize 2 dBm - 30=33dBm, 31=32dBm - ny1: - Maximum number of repetitions for PHYSICAL INFORMATION message (GSM 04.08): 20 - powerOutputThresholds: - Out Power Fault Threshold: -10 dB - Red Out Power Threshold: - 6 dB - Excessive Out Power Threshold: 5 dB - rACHBusyThreshold: -127 dBm - rACHLoadAveragingSlots: 250 ,number of RACH burst periods - rfResourceIndicationPeriod: 125 SACCH multiframes - T200: - SDCCH: 044 in 5 ms - FACCH/Full rate: 031 in 5 ms - FACCH/Half rate: 041 in 5 ms - SACCH with TCH SAPI0: 090 in 10 ms - SACCH with SDCCH: 090 in 10 ms - SDCCH with SAPI3: 090 in 5 ms - SACCH with TCH SAPI3: 135 in 10 ms - tSync: 9000 units of 10 msec - tTrau: 9000 units of 10 msec - enableUmLoopTest: 00h = disabled - enableExcessiveDistance: 00h = Disabled - excessiveDistance: 64km - hoppingMode: 00h = baseband hopping - cellType: 00h = Standard Cell - BCCH ARFCN / bCCHFrequency: 1 -*/ - -unsigned char msg_2[] = -{ - 0x41, NM_OC_BTS, 0x00, 0xFF, 0xFF, - NM_ATT_BSIC, HARDCODED_BSIC, - NM_ATT_BTS_AIR_TIMER, 0x04, - NM_ATT_BS11_BTSLS_HOPPING, 0x00, - NM_ATT_CCCH_L_I_P, 0x01, - NM_ATT_CCCH_L_T, 0x00, - NM_ATT_BS11_CELL_ALLOC_NR, NM_BS11_CANR_GSM, - NM_ATT_BS11_ENA_INTERF_CLASS, 0x01, - NM_ATT_BS11_FACCH_QUAL, 0x06, - /* interference avg. period in numbers of SACCH multifr */ - NM_ATT_INTAVE_PARAM, 0x1F, - NM_ATT_INTERF_BOUND, 0x0A, 0x0F, 0x14, 0x19, 0x1E, 0x7B, - NM_ATT_CCCH_L_T, 0x23, - NM_ATT_GSM_TIME, 0x28, 0x00, - NM_ATT_ADM_STATE, 0x03, - NM_ATT_RACH_B_THRESH, 0x7F, - NM_ATT_LDAVG_SLOTS, 0x00, 0xFA, - NM_ATT_BS11_RF_RES_IND_PER, 0x7D, - NM_ATT_T200, 0x2C, 0x1F, 0x29, 0x5A, 0x5A, 0x5A, 0x87, - NM_ATT_BS11_TSYNC, 0x23, 0x28, - NM_ATT_BS11_TTRAU, 0x23, 0x28, - NM_ATT_TEST_DUR, 0x01, 0x00, - NM_ATT_OUTST_ALARM, 0x01, 0x00, - NM_ATT_BS11_EXCESSIVE_DISTANCE, 0x01, 0x40, - NM_ATT_BS11_HOPPING_MODE, 0x01, 0x00, - NM_ATT_BS11_PLL, 0x01, 0x00, - NM_ATT_BCCH_ARFCN, 0x00, HARDCODED_ARFCN/*0x01*/, -}; - -// Handover Recognition, SET ATTRIBUTES - -/* -Illegal Contents GSM Formatted O&M Msg - Object Class: Handover Recognition - BTS relat. Number: 0 - Instance 2: FF - Instance 3: FF -SET ATTRIBUTES - enableDelayPowerBudgetHO: 00h = Disabled - enableDistanceHO: 00h = Disabled - enableInternalInterCellHandover: 00h = Disabled - enableInternalIntraCellHandover: 00h = Disabled - enablePowerBudgetHO: 00h = Disabled - enableRXLEVHO: 00h = Disabled - enableRXQUALHO: 00h = Disabled - hoAveragingDistance: 8 SACCH multiframes - hoAveragingLev: - A_LEV_HO: 8 SACCH multiframes - W_LEV_HO: 1 SACCH multiframes - hoAveragingPowerBudget: 16 SACCH multiframes - hoAveragingQual: - A_QUAL_HO: 8 SACCH multiframes - W_QUAL_HO: 2 SACCH multiframes - hoLowerThresholdLevDL: (10 - 110) dBm - hoLowerThresholdLevUL: (5 - 110) dBm - hoLowerThresholdQualDL: 06h = 6.4% < BER < 12.8% - hoLowerThresholdQualUL: 06h = 6.4% < BER < 12.8% - hoThresholdLevDLintra : (20 - 110) dBm - hoThresholdLevULintra: (20 - 110) dBm - hoThresholdMsRangeMax: 20 km - nCell: 06h - timerHORequest: 3 ,unit 2 SACCH multiframes -*/ - -unsigned char msg_3[] = -{ - 0xD0, NM_OC_BS11_HANDOVER, 0x00, 0xFF, 0xFF, - 0xD0, 0x00, - 0x64, 0x00, - 0x67, 0x00, - 0x68, 0x00, - 0x6A, 0x00, - 0x6C, 0x00, - 0x6D, 0x00, - 0x6F, 0x08, - 0x70, 0x08, 0x01, - 0x71, 0x10, 0x10, 0x10, - 0x72, 0x08, 0x02, - 0x73, 0x0A, - 0x74, 0x05, - 0x75, 0x06, - 0x76, 0x06, - 0x78, 0x14, - 0x79, 0x14, - 0x7A, 0x14, - 0x7D, 0x06, - 0x92, 0x03, 0x20, 0x01, 0x00, - 0x45, 0x01, 0x00, - 0x48, 0x01, 0x00, - 0x5A, 0x01, 0x00, - 0x5B, 0x01, 0x05, - 0x5E, 0x01, 0x1A, - 0x5F, 0x01, 0x20, - 0x9D, 0x01, 0x00, - 0x47, 0x01, 0x00, - 0x5C, 0x01, 0x64, - 0x5D, 0x01, 0x1E, - 0x97, 0x01, 0x20, - 0xF7, 0x01, 0x3C, -}; - -// Power Control, SET ATTRIBUTES - -/* - Object Class: Power Control - BTS relat. Number: 0 - Instance 2: FF - Instance 3: FF -SET ATTRIBUTES - enableMsPowerControl: 00h = Disabled - enablePowerControlRLFW: 00h = Disabled - pcAveragingLev: - A_LEV_PC: 4 SACCH multiframes - W_LEV_PC: 1 SACCH multiframes - pcAveragingQual: - A_QUAL_PC: 4 SACCH multiframes - W_QUAL_PC: 2 SACCH multiframes - pcLowerThresholdLevDL: 0Fh - pcLowerThresholdLevUL: 0Ah - pcLowerThresholdQualDL: 05h = 3.2% < BER < 6.4% - pcLowerThresholdQualUL: 05h = 3.2% < BER < 6.4% - pcRLFThreshold: 0Ch - pcUpperThresholdLevDL: 14h - pcUpperThresholdLevUL: 0Fh - pcUpperThresholdQualDL: 04h = 1.6% < BER < 3.2% - pcUpperThresholdQualUL: 04h = 1.6% < BER < 3.2% - powerConfirm: 2 ,unit 2 SACCH multiframes - powerControlInterval: 2 ,unit 2 SACCH multiframes - powerIncrStepSize: 02h = 4 dB - powerRedStepSize: 01h = 2 dB - radioLinkTimeoutBs: 64 SACCH multiframes - enableBSPowerControl: 00h = disabled -*/ - -unsigned char msg_4[] = -{ - 0xD0, NM_OC_BS11_PWR_CTRL, 0x00, 0xFF, 0xFF, - NM_ATT_BS11_ENA_MS_PWR_CTRL, 0x00, - NM_ATT_BS11_ENA_PWR_CTRL_RLFW, 0x00, - 0x7E, 0x04, 0x01, - 0x7F, 0x04, 0x02, - 0x80, 0x0F, - 0x81, 0x0A, - 0x82, 0x05, - 0x83, 0x05, - 0x84, 0x0C, - 0x85, 0x14, - 0x86, 0x0F, - 0x87, 0x04, - 0x88, 0x04, - 0x89, 0x02, - 0x8A, 0x02, - 0x8B, 0x02, - 0x8C, 0x01, - 0x8D, 0x40, - 0x65, 0x01, 0x00 // set to 0x01 to enable BSPowerControl -}; - - -// Transceiver, SET TRX ATTRIBUTES (TRX 0) - -/* - Object Class: Transceiver - BTS relat. Number: 0 - Tranceiver number: 0 - Instance 3: FF -SET TRX ATTRIBUTES - aRFCNList (HEX): 0001 - txPwrMaxReduction: 00h = 30dB - radioMeasGran: 254 SACCH multiframes - radioMeasRep: 01h = enabled - memberOfEmergencyConfig: 01h = TRUE - trxArea: 00h = TRX doesn't belong to a concentric cell -*/ - -unsigned char msg_6[] = -{ - 0x44, NM_OC_RADIO_CARRIER, 0x00, 0x00, 0xFF, - NM_ATT_ARFCN_LIST, 0x01, 0x00, HARDCODED_ARFCN /*0x01*/, - NM_ATT_RF_MAXPOWR_R, 0x00, - NM_ATT_BS11_RADIO_MEAS_GRAN, 0x01, 0xFE, - NM_ATT_BS11_RADIO_MEAS_REP, 0x01, 0x01, - NM_ATT_BS11_EMRG_CFG_MEMBER, 0x01, 0x01, - NM_ATT_BS11_TRX_AREA, 0x01, 0x00, -}; - -static unsigned char nanobts_attr_bts[] = { - NM_ATT_INTERF_BOUND, 0x55, 0x5b, 0x61, 0x67, 0x6d, 0x73, - /* interference avg. period in numbers of SACCH multifr */ - NM_ATT_INTAVE_PARAM, 0x06, - /* conn fail based on SACCH error rate */ - NM_ATT_CONN_FAIL_CRIT, 0x00, 0x02, 0x01, 0x10, - NM_ATT_T200, 0x1e, 0x24, 0x24, 0xa8, 0x34, 0x21, 0xa8, - NM_ATT_MAX_TA, 0x3f, - NM_ATT_OVERL_PERIOD, 0x00, 0x01, 10, /* seconds */ - NM_ATT_CCCH_L_T, 10, /* percent */ - NM_ATT_CCCH_L_I_P, 1, /* seconds */ - NM_ATT_RACH_B_THRESH, 10, /* busy threshold in - dBm */ - NM_ATT_LDAVG_SLOTS, 0x03, 0xe8, /* rach load averaging 1000 slots */ - NM_ATT_BTS_AIR_TIMER, 128, /* miliseconds */ - NM_ATT_NY1, 10, /* 10 retransmissions of physical config */ - NM_ATT_BCCH_ARFCN, HARDCODED_ARFCN >> 8, HARDCODED_ARFCN & 0xff, - NM_ATT_BSIC, HARDCODED_BSIC, -}; - -static unsigned char nanobts_attr_radio[] = { - NM_ATT_RF_MAXPOWR_R, 0x0c, /* number of -2dB reduction steps / Pn */ - NM_ATT_ARFCN_LIST, 0x00, 0x02, HARDCODED_ARFCN >> 8, HARDCODED_ARFCN & 0xff, -}; - -static unsigned char nanobts_attr_e0[] = { - 0x85, 0x00, - 0x81, 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) -{ - struct gsm_bts *bts; - struct gsm_bts_trx *trx; - struct gsm_bts_trx_ts *ts; - - /* This 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 == 5) { - 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 == 5) { - if (ts->nr == 0 && trx == trx->bts->c0) - abis_nm_set_channel_attr(ts, NM_CHANC_BCCH_CBCH); - 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; - } - break; - default: - //DEBUGP(DMM, "Unhandled state change in %s:%d\n", __func__, __LINE__); - break; - } - return 0; -} - -/* Callback function to be called every time we receive a 12.21 SW activated report */ -static int sw_activ_rep(struct msgb *mb) -{ - struct abis_om_fom_hdr *foh = msgb_l3(mb); - struct gsm_bts_trx *trx = mb->trx; - - 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, - 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); - break; - case NM_OC_RADIO_CARRIER: - 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, - trx->bts->bts_nr, trx->nr, 0xff, - NM_STATE_UNLOCKED); - break; - } - return 0; -} - -/* Callback function to be called every time we receive a signal from NM */ -static int nm_sig_cb(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data) -{ - switch (signal) { - case S_NM_SW_ACTIV_REP: - return sw_activ_rep(signal_data); - default: - break; - } - return 0; -} - -static void bootstrap_om_nanobts(struct gsm_bts *bts) -{ - /* We don't do callback based bootstrapping, but event driven (see above) */ -} - -static void bootstrap_om_bs11(struct gsm_bts *bts) -{ - struct gsm_bts_trx *trx = &bts->trx[0]; - - /* stop sending event reports */ - abis_nm_event_reports(bts, 0); - - /* begin DB transmission */ - abis_nm_bs11_db_transmission(bts, 1); - - /* end DB transmission */ - abis_nm_bs11_db_transmission(bts, 0); - - /* Reset BTS Site manager resource */ - abis_nm_bs11_reset_resource(bts); - - /* begin DB transmission */ - abis_nm_bs11_db_transmission(bts, 1); - - abis_nm_raw_msg(bts, sizeof(msg_1), msg_1); /* set BTS SiteMgr attr*/ - abis_nm_raw_msg(bts, sizeof(msg_2), msg_2); /* set BTS attr */ - abis_nm_raw_msg(bts, sizeof(msg_3), msg_3); /* set BTS handover attr */ - abis_nm_raw_msg(bts, sizeof(msg_4), msg_4); /* set BTS power control attr */ - - /* Connect signalling of bts0/trx0 to e1_0/ts1/64kbps */ - abis_nm_conn_terr_sign(trx, 0, 1, 0xff); - abis_nm_raw_msg(bts, sizeof(msg_6), msg_6); /* SET TRX ATTRIBUTES */ - - /* Use TEI 1 for signalling */ - abis_nm_establish_tei(bts, 0, 0, 1, 0xff, 0x01); - abis_nm_set_channel_attr(&trx->ts[0], NM_CHANC_SDCCH_CBCH); - -#ifdef HAVE_TRX1 - /* TRX 1 */ - abis_nm_conn_terr_sign(&bts->trx[1], 0, 1, 0xff); - /* FIXME: TRX ATTRIBUTE */ - abis_nm_establish_tei(bts, 0, 0, 1, 0xff, 0x02); -#endif - - /* SET CHANNEL ATTRIBUTE TS1 */ - abis_nm_set_channel_attr(&trx->ts[1], NM_CHANC_TCHFull); - /* Connect traffic of bts0/trx0/ts1 to e1_0/ts2/b */ - abis_nm_conn_terr_traf(&trx->ts[1], 0, 2, 1); - - /* SET CHANNEL ATTRIBUTE TS2 */ - abis_nm_set_channel_attr(&trx->ts[2], NM_CHANC_TCHFull); - /* Connect traffic of bts0/trx0/ts2 to e1_0/ts2/c */ - abis_nm_conn_terr_traf(&trx->ts[2], 0, 2, 2); - - /* SET CHANNEL ATTRIBUTE TS3 */ - abis_nm_set_channel_attr(&trx->ts[3], NM_CHANC_TCHFull); - /* Connect traffic of bts0/trx0/ts3 to e1_0/ts2/d */ - abis_nm_conn_terr_traf(&trx->ts[3], 0, 2, 3); - - /* SET CHANNEL ATTRIBUTE TS4 */ - abis_nm_set_channel_attr(&trx->ts[4], NM_CHANC_TCHFull); - /* Connect traffic of bts0/trx0/ts4 to e1_0/ts3/a */ - abis_nm_conn_terr_traf(&trx->ts[4], 0, 3, 0); - - /* SET CHANNEL ATTRIBUTE TS5 */ - abis_nm_set_channel_attr(&trx->ts[5], NM_CHANC_TCHFull); - /* Connect traffic of bts0/trx0/ts5 to e1_0/ts3/b */ - abis_nm_conn_terr_traf(&trx->ts[5], 0, 3, 1); - - /* SET CHANNEL ATTRIBUTE TS6 */ - abis_nm_set_channel_attr(&trx->ts[6], NM_CHANC_TCHFull); - /* Connect traffic of bts0/trx0/ts6 to e1_0/ts3/c */ - abis_nm_conn_terr_traf(&trx->ts[6], 0, 3, 2); - - /* SET CHANNEL ATTRIBUTE TS7 */ - abis_nm_set_channel_attr(&trx->ts[7], NM_CHANC_TCHFull); - /* Connect traffic of bts0/trx0/ts7 to e1_0/ts3/d */ - abis_nm_conn_terr_traf(&trx->ts[7], 0, 3, 3); - - /* end DB transmission */ - abis_nm_bs11_db_transmission(bts, 0); - - /* Reset BTS Site manager resource */ - abis_nm_bs11_reset_resource(bts); - - /* restart sending event reports */ - abis_nm_event_reports(bts, 1); -} - -static void bootstrap_om(struct gsm_bts *bts) -{ - fprintf(stdout, "bootstrapping OML for BTS %u\n", bts->nr); - - switch (bts->type) { - case GSM_BTS_TYPE_BS11: - bootstrap_om_bs11(bts); - break; - case GSM_BTS_TYPE_NANOBTS_900: - case GSM_BTS_TYPE_NANOBTS_1800: - bootstrap_om_nanobts(bts); - break; - default: - fprintf(stderr, "Unable to bootstrap OML: Unknown BTS type %d\n", bts->type); - } -} - -static int shutdown_om(struct gsm_bts *bts) -{ - /* stop sending event reports */ - abis_nm_event_reports(bts, 0); - - /* begin DB transmission */ - abis_nm_bs11_db_transmission(bts, 1); - - /* end DB transmission */ - abis_nm_bs11_db_transmission(bts, 0); - - /* Reset BTS Site manager resource */ - abis_nm_bs11_reset_resource(bts); - - return 0; -} - -static int shutdown_net(struct gsm_network *net) -{ - int i; - for (i = 0; i < net->num_bts; i++) { - int rc; - rc = shutdown_om(&net->bts[i]); - if (rc < 0) - return rc; - } - - return 0; -} - -struct bcch_info { - u_int8_t type; - u_int8_t len; - const u_int8_t *data; -}; - -/* -SYSTEM INFORMATION TYPE 1 - Cell channel description - Format-ID bit map 0 - CA-ARFCN Bit 124...001 (Hex): 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 - RACH Control Parameters - maximum 7 retransmissions - 8 slots used to spread transmission - cell not barred for access - call reestablishment not allowed - Access Control Class = 0000 -*/ -static u_int8_t si1[] = { - /* header */0x55, 0x06, 0x19, - /* ccdesc */0x04 /*0x00*/, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 /*0x01*/, - /* rach */0xD5, 0x00, 0x00, - /* s1 reset*/0x2B -}; - -/* - SYSTEM INFORMATION TYPE 2 - Neighbour Cells Description - EXT-IND: Carries the complete BA - BA-IND = 0 - Format-ID bit map 0 - CA-ARFCN Bit 124...001 (Hex): 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - NCC permitted (NCC) = FF - RACH Control Parameters - maximum 7 retransmissions - 8 slots used to spread transmission - cell not barred for access - call reestablishment not allowed - Access Control Class = 0000 -*/ -static u_int8_t si2[] = { - /* header */0x59, 0x06, 0x1A, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* ncc */0xFF, - /* rach*/0xD5, 0x00, 0x00 -}; - -/* -SYSTEM INFORMATION TYPE 3 - Cell identity = 00001 (1h) - Location area identification - Mobile Country Code (MCC): 001 - Mobile Network Code (MNC): 01 - Location Area Code (LAC): 00001 (1h) - Control Channel Description - Attach-detach: MSs in the cell are not allowed to apply IMSI attach /detach - 0 blocks reserved for access grant - 1 channel used for CCCH, with SDCCH - 5 multiframes period for PAGING REQUEST - Time-out T3212 = 0 - Cell Options BCCH - Power control indicator: not set - MSs shall not use uplink DTX - Radio link timeout = 36 - Cell Selection Parameters - Cell reselect hysteresis = 6 dB RXLEV hysteresis for LA re-selection - max.TX power level MS may use for CCH = 2 <- according to GSM05.05 39dBm (max) - Additional Reselect Parameter Indication (ACS) = only SYSTEM INFO 4: The SI rest octets, if present, shall be used to derive the value of PI and possibly C2 parameters - Half rate support (NECI): New establishment causes are not supported - min.RX signal level for MS = 0 - RACH Control Parameters - maximum 7 retransmissions - 8 slots used to spread transmission - cell not barred for access - call reestablishment not allowed - Access Control Class = 0000 - SI 3 Rest Octets - Cell Bar Qualify (CBQ): 0 - Cell Reselect Offset = 0 dB - Temporary Offset = 0 dB - Penalty Time = 20 s - System Information 2ter Indicator (2TI): 0 = not available - Early Classmark Sending Control (ECSC): 0 = forbidden - Scheduling Information is not sent in SYSTEM INFORMATION TYPE 9 on the BCCH -*/ -static u_int8_t si3[] = { - /* header */0x49, 0x06, 0x1B, - /* cell */0x00, 0x01, - /* lai */0x00, 0xF1, 0x10, 0x00, 0x01, - /* desc */0x01, 0x03, 0x00, - /* option*/0x28, - /* selection*/0x62, 0x00, - /* rach */0xD5, 0x00, 0x00, - /* reset*/0x80, 0x00, 0x00, 0x2B -}; - -/* -SYSTEM INFORMATION TYPE 4 - Location area identification - Mobile Country Code (MCC): 001 - Mobile Network Code (MNC): 01 - Location Area Code (LAC): 00001 (1h) - Cell Selection Parameters - Cell reselect hysteresis = 6 dB RXLEV hysteresis for LA re-selection - max.TX power level MS may use for CCH = 2 - Additional Reselect Parameter Indication (ACS) = only SYSTEM INFO 4: The SI rest octets, if present, shall be used to derive the value of PI and possibly C2 parameters - Half rate support (NECI): New establishment causes are not supported - min.RX signal level for MS = 0 - RACH Control Parameters - maximum 7 retransmissions - 8 slots used to spread transmission - cell not barred for access - call reestablishment not allowed - Access Control Class = 0000 - Channel Description - Type = SDCCH/4[2] - Timeslot Number: 0 - Training Sequence Code: 7h - ARFCN: 1 - SI Rest Octets - Cell Bar Qualify (CBQ): 0 - Cell Reselect Offset = 0 dB - Temporary Offset = 0 dB - Penalty Time = 20 s -*/ -static u_int8_t si4[] = { - /* header */0x41, 0x06, 0x1C, - /* lai */0x00, 0xF1, 0x10, 0x00, 0x01, - /* sel */0x62, 0x00, - /* rach*/0xD5, 0x00, 0x00, - /* var */0x64, 0x30, 0xE0, HARDCODED_ARFCN/*0x01*/, 0x80, 0x00, 0x00, - 0x2B, 0x2B, 0x2B -}; - -/* - SYSTEM INFORMATION TYPE 5 - Neighbour Cells Description - EXT-IND: Carries the complete BA - BA-IND = 0 - Format-ID bit map 0 - CA-ARFCN Bit 124...001 (Hex): 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -*/ - -static u_int8_t si5[] = { - /* header without l2 len*/0x06, 0x1D, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -}; - -// SYSTEM INFORMATION TYPE 6 - -/* -SACCH FILLING - System Info Type: SYSTEM INFORMATION 6 - L3 Information (Hex): 06 1E 00 01 xx xx 10 00 01 28 FF - -SYSTEM INFORMATION TYPE 6 - Cell identity = 00001 (1h) - Location area identification - Mobile Country Code (MCC): 001 - Mobile Network Code (MNC): 01 - Location Area Code (LAC): 00001 (1h) - Cell Options SACCH - Power control indicator: not set - MSs shall not use uplink DTX on a TCH-F. MS shall not use uplink DTX on TCH-H. - Radio link timeout = 36 - NCC permitted (NCC) = FF -*/ - -static u_int8_t si6[] = { - /* header */0x06, 0x1E, - /* cell id*/ 0x00, 0x01, - /* lai */ 0x00, 0xF1, 0x10, 0x00, 0x01, - /* options */ 0x28, - /* ncc */ 0xFF, -}; - - - -static const struct bcch_info bcch_infos[] = { - { - .type = RSL_SYSTEM_INFO_1, - .len = sizeof(si1), - .data = si1, - }, { - .type = RSL_SYSTEM_INFO_2, - .len = sizeof(si2), - .data = si2, - }, { - .type = RSL_SYSTEM_INFO_3, - .len = sizeof(si3), - .data = si3, - }, { - .type = RSL_SYSTEM_INFO_4, - .len = sizeof(si4), - .data = si4, - }, -}; - -static_assert(sizeof(si1) == sizeof(struct gsm48_system_information_type_1), type1) -static_assert(sizeof(si2) == sizeof(struct gsm48_system_information_type_2), type2) -static_assert(sizeof(si3) == sizeof(struct gsm48_system_information_type_3), type3) -static_assert(sizeof(si4) >= sizeof(struct gsm48_system_information_type_4), type4) -static_assert(sizeof(si5) == sizeof(struct gsm48_system_information_type_5), type5) -static_assert(sizeof(si6) >= sizeof(struct gsm48_system_information_type_6), type6) - -/* set all system information types */ -static int set_system_infos(struct gsm_bts_trx *trx) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(bcch_infos); i++) { - rsl_bcch_info(trx, bcch_infos[i].type, - bcch_infos[i].data, - bcch_infos[i].len); - } - rsl_sacch_filling(trx, RSL_SYSTEM_INFO_5, si5, sizeof(si5)); - rsl_sacch_filling(trx, RSL_SYSTEM_INFO_6, si6, sizeof(si6)); - - return 0; -} - -/* - * Patch the various SYSTEM INFORMATION tables to update - * the LAI - */ -static void patch_tables(struct gsm_bts *bts) -{ - u_int8_t arfcn_low = bts->trx[0].arfcn & 0xff; - u_int8_t arfcn_high = (bts->trx[0].arfcn >> 8) & 0x0f; - /* covert the raw packet to the struct */ - struct gsm48_system_information_type_3 *type_3 = - (struct gsm48_system_information_type_3*)&si3; - struct gsm48_system_information_type_4 *type_4 = - (struct gsm48_system_information_type_4*)&si4; - struct gsm48_system_information_type_6 *type_6 = - (struct gsm48_system_information_type_6*)&si6; - struct gsm48_loc_area_id lai; - - gsm0408_generate_lai(&lai, bts->network->country_code, - bts->network->network_code, - bts->location_area_code); - - /* assign the MCC and MNC */ - type_3->lai = lai; - type_4->lai = lai; - type_6->lai = lai; - - /* patch ARFCN into BTS Attributes */ - msg_2[74] &= 0xf0; - msg_2[74] |= arfcn_high; - msg_2[75] = arfcn_low; - nanobts_attr_bts[42] &= 0xf0; - nanobts_attr_bts[42] |= arfcn_high; - nanobts_attr_bts[43] = arfcn_low; - - /* patch ARFCN into TRX Attributes */ - msg_6[7] &= 0xf0; - msg_6[7] |= arfcn_high; - msg_6[8] = arfcn_low; - nanobts_attr_radio[5] &= 0xf0; - nanobts_attr_radio[5] |= arfcn_high; - nanobts_attr_radio[6] = arfcn_low; - - type_4->data[2] &= 0xf0; - type_4->data[2] |= arfcn_high; - type_4->data[3] = arfcn_low; - - /* patch Control Channel Description 10.5.2.11 */ - type_3->control_channel_desc = bts->chan_desc; - - /* patch BSIC */ - msg_2[6] = bts->bsic; - nanobts_attr_bts[sizeof(nanobts_attr_bts)-1] = bts->bsic; -} - - -static void bootstrap_rsl(struct gsm_bts_trx *trx) -{ - fprintf(stdout, "bootstrapping RSL for BTS/TRX (%u/%u) " - "using MCC=%u MNC=%u\n", trx->nr, trx->bts->nr, MCC, MNC); - set_system_infos(trx); -} - -void input_event(int event, enum e1inp_sign_type type, struct gsm_bts_trx *trx) -{ - switch (event) { - case EVT_E1_TEI_UP: - switch (type) { - case E1INP_SIGN_OML: - bootstrap_om(trx->bts); - break; - case E1INP_SIGN_RSL: - bootstrap_rsl(trx); - break; - default: - break; - } - break; - case EVT_E1_TEI_DN: - fprintf(stderr, "Lost some E1 TEI link\n"); - /* FIXME: deal with TEI or L1 link loss */ - break; - default: - break; - } -} - -static int bootstrap_bts(struct gsm_bts *bts) -{ - bts->location_area_code = LAC; - bts->trx[0].arfcn = ARFCN; - - /* Control Channel Description */ - memset(&bts->chan_desc, 0, sizeof(struct gsm48_control_channel_descr)); - bts->chan_desc.att = 1; - bts->chan_desc.ccch_conf = RSL_BCCH_CCCH_CONF_1_C; - bts->chan_desc.bs_pa_mfrms = RSL_BS_PA_MFRMS_5; - bts->chan_desc.t3212 = 0; - - patch_tables(bts); - - paging_init(bts); - - if (bts->type == GSM_BTS_TYPE_BS11) { - struct gsm_bts_trx *trx = &bts->trx[0]; - set_ts_e1link(&trx->ts[0], 0, 1, 0xff); - set_ts_e1link(&trx->ts[1], 0, 2, 1); - set_ts_e1link(&trx->ts[2], 0, 2, 2); - set_ts_e1link(&trx->ts[3], 0, 2, 3); - set_ts_e1link(&trx->ts[4], 0, 3, 0); - set_ts_e1link(&trx->ts[5], 0, 3, 1); - set_ts_e1link(&trx->ts[6], 0, 3, 2); - set_ts_e1link(&trx->ts[7], 0, 3, 3); -#ifdef HAVE_TRX1 - /* TRX 1 */ - trx = &bts->trx[1]; - set_ts_e1link(&trx->ts[0], 0, 1, 0xff); - set_ts_e1link(&trx->ts[1], 0, 2, 1); - set_ts_e1link(&trx->ts[2], 0, 2, 2); - set_ts_e1link(&trx->ts[3], 0, 2, 3); - set_ts_e1link(&trx->ts[4], 0, 3, 0); - set_ts_e1link(&trx->ts[5], 0, 3, 1); - set_ts_e1link(&trx->ts[6], 0, 3, 2); - set_ts_e1link(&trx->ts[7], 0, 3, 3); -#endif - } - - return 0; -} - -static int bootstrap_network(void) -{ - struct gsm_bts *bts; - - /* initialize our data structures */ - gsmnet = gsm_network_init(2, BTS_TYPE, MCC, MNC); - if (!gsmnet) - return -ENOMEM; - - gsmnet->name_long = "OpenBSC"; - gsmnet->name_short = "OpenBSC"; - - bts = &gsmnet->bts[0]; - bootstrap_bts(bts); - - if (db_init(database_name)) { - printf("DB: Failed to init database. Please check the option settings.\n"); - return -1; - } - printf("DB: Database initialized.\n"); - - if (db_prepare()) { - printf("DB: Failed to prepare database.\n"); - return -1; - } - printf("DB: Database prepared.\n"); - - telnet_init(gsmnet, 4242); - - register_signal_handler(SS_NM, nm_sig_cb, NULL); - - /* E1 mISDN input setup */ - if (BTS_TYPE == GSM_BTS_TYPE_BS11) { - gsmnet->num_bts = 1; - return e1_config(bts, cardnr, release_l2); - } else { - /* FIXME: do this dynamic */ - bts->ip_access.site_id = 1801; - bts->ip_access.bts_id = 0; - bts = &gsmnet->bts[1]; - bootstrap_bts(bts); - bts->ip_access.site_id = 1800; - bts->ip_access.bts_id = 0; - return ipaccess_setup(gsmnet); - } -} - -static void create_pcap_file(char *file) -{ - mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; - int fd = open(file, O_WRONLY|O_TRUNC|O_CREAT, mode); - - if (fd < 0) { - perror("Failed to open file for pcap"); - return; - } - - e1_set_pcap_fd(fd); -} - -static void print_usage() -{ - printf("Usage: bsc_hack\n"); -} - -static void print_help() -{ - printf(" Some useful help...\n"); - printf(" -d option --debug=DRLL:DCC:DMM:DRR:DRSL:DNM enable debugging\n"); - printf(" -s --disable-color\n"); - printf(" -n --network-code number(MNC) \n"); - printf(" -c --country-code number (MCC) \n"); - printf(" -L --location-area-code number (LAC) \n"); - printf(" -f --arfcn number The frequency ARFCN\n"); - printf(" -l --database db-name The database to use\n"); - printf(" -a --authorize-everyone Allow everyone into the network.\n"); - printf(" -r --reject-cause number The reject cause for LOCATION UPDATING REJECT.\n"); - printf(" -p --pcap file The filename of the pcap file\n"); - printf(" -t --bts-type type The BTS type (bs11, nanobts900, nanobts1800)\n"); - printf(" -C --cardnr number For bs11 select E1 card number other than 0\n"); - printf(" -R --release-l2 Releases mISDN layer 2 after exit, to unload driver.\n"); - printf(" -h --help this text\n"); -} - -static void handle_options(int argc, char** argv) -{ - while (1) { - int option_index = 0, c; - static struct option long_options[] = { - {"help", 0, 0, 'h'}, - {"debug", 1, 0, 'd'}, - {"disable-color", 0, 0, 's'}, - {"network-code", 1, 0, 'n'}, - {"country-code", 1, 0, 'c'}, - {"location-area-code", 1, 0, 'L'}, - {"database", 1, 0, 'l'}, - {"authorize-everyone", 0, 0, 'a'}, - {"reject-cause", 1, 0, 'r'}, - {"pcap", 1, 0, 'p'}, - {"arfcn", 1, 0, 'f'}, - {"bts-type", 1, 0, 't'}, - {"cardnr", 1, 0, 'C'}, - {"release-l2", 0, 0, 'R'}, - {"timestamp", 0, 0, 'T'}, - {0, 0, 0, 0} - }; - - c = getopt_long(argc, argv, "hc:n:d:sar:p:f:t:C:RL:l:T", - long_options, &option_index); - if (c == -1) - break; - - switch (c) { - case 'h': - print_usage(); - print_help(); - exit(0); - case 's': - debug_use_color(0); - break; - case 'd': - debug_parse_category_mask(optarg); - break; - case 'n': - MNC = atoi(optarg); - break; - case 'c': - MCC = atoi(optarg); - break; - case 'L': - LAC = atoi(optarg); - break; - case 'f': - ARFCN = atoi(optarg); - break; - case 'l': - database_name = strdup(optarg); - break; - case 'a': - gsm0408_allow_everyone(1); - break; - case 'r': - gsm0408_set_reject_cause(atoi(optarg)); - break; - case 'p': - create_pcap_file(optarg); - break; - case 't': - BTS_TYPE = parse_btstype(optarg); - break; - case 'C': - cardnr = atoi(optarg); - break; - case 'R': - release_l2 = 1; - break; - case 'T': - debug_timestamp(1); - break; - default: - /* ignore */ - break; - } - } -} - -static void signal_handler(int signal) -{ - fprintf(stdout, "signal %u received\n", signal); - - switch (signal) { - case SIGHUP: - case SIGABRT: - shutdown_net(gsmnet); - break; - default: - break; - } -} - -int main(int argc, char **argv) -{ - int rc; - - /* parse options */ - handle_options(argc, argv); - - /* seed the PRNG */ - srand(time(NULL)); - - rc = bootstrap_network(); - if (rc < 0) - exit(1); - - signal(SIGHUP, &signal_handler); - signal(SIGABRT, &signal_handler); - - while (1) { - bsc_select_main(0); - } -} diff --git a/src/chan_alloc.c b/src/chan_alloc.c deleted file mode 100644 index 77a4f57bc..000000000 --- a/src/chan_alloc.c +++ /dev/null @@ -1,256 +0,0 @@ -/* GSM Channel allocation routines - * - * (C) 2008 by Harald Welte <laforge@gnumonks.org> - * (C) 2008, 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. - * - */ - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <errno.h> - -#include <openbsc/gsm_data.h> -#include <openbsc/chan_alloc.h> -#include <openbsc/abis_nm.h> -#include <openbsc/abis_rsl.h> -#include <openbsc/debug.h> -#include <openbsc/signal.h> - -static void auto_release_channel(void *_lchan); - -struct gsm_bts_trx_ts *ts_c0_alloc(struct gsm_bts *bts, - enum gsm_phys_chan_config pchan) -{ - struct gsm_bts_trx *trx = &bts->trx[0]; - struct gsm_bts_trx_ts *ts = &trx->ts[0]; - - if (pchan != GSM_PCHAN_CCCH && - pchan != GSM_PCHAN_CCCH_SDCCH4) - return NULL; - - if (ts->pchan != GSM_PCHAN_NONE) - return NULL; - - ts->pchan = pchan; - - return ts; -} - -static const enum abis_nm_chan_comb chcomb4pchan[] = { - [GSM_PCHAN_CCCH] = NM_CHANC_mainBCCH, - [GSM_PCHAN_CCCH_SDCCH4] = NM_CHANC_BCCCHComb, - [GSM_PCHAN_TCH_F] = NM_CHANC_TCHFull, - [GSM_PCHAN_TCH_H] = NM_CHANC_TCHHalf, - [GSM_PCHAN_SDCCH8_SACCH8C] = NM_CHANC_SDCCH, - /* FIXME: bounds check */ -}; - -/* Allocate a logical channel (TS) */ -struct gsm_bts_trx_ts *ts_alloc(struct gsm_bts *bts, - enum gsm_phys_chan_config pchan) -{ - int i, j; - for (i = 0; i < bts->num_trx; i++) { - struct gsm_bts_trx *trx = &bts->trx[i]; - int from, to; - - /* the following constraints are pure policy, - * no requirement to put this restriction in place */ - switch (pchan) { - case GSM_PCHAN_CCCH: - case GSM_PCHAN_CCCH_SDCCH4: - from = 0; to = 0; - break; - case GSM_PCHAN_SDCCH8_SACCH8C: - from = 1; to = 1; - break; - case GSM_PCHAN_TCH_F: - case GSM_PCHAN_TCH_H: - from = 2; to = 7; - break; - default: - return NULL; - } - - for (j = from; j <= to; j++) { - struct gsm_bts_trx_ts *ts = &trx->ts[j]; - if (ts->pchan == GSM_PCHAN_NONE) { - ts->pchan = pchan; - /* set channel attribute on OML */ - abis_nm_set_channel_attr(ts, chcomb4pchan[pchan]); - return ts; - } - } - } - return NULL; -} - -/* Free a physical channel (TS) */ -void ts_free(struct gsm_bts_trx_ts *ts) -{ - ts->pchan = GSM_PCHAN_NONE; -} - -static const u_int8_t subslots_per_pchan[] = { - [GSM_PCHAN_NONE] = 0, - [GSM_PCHAN_CCCH] = 0, - [GSM_PCHAN_CCCH_SDCCH4] = 4, - [GSM_PCHAN_TCH_F] = 1, - [GSM_PCHAN_TCH_H] = 2, - [GSM_PCHAN_SDCCH8_SACCH8C] = 8. -}; - -static struct gsm_lchan * -_lc_find(struct gsm_bts *bts, enum gsm_phys_chan_config pchan) -{ - struct gsm_bts_trx *trx; - struct gsm_bts_trx_ts *ts; - int i, j, ss; - for (i = 0; i < bts->num_trx; i++) { - trx = &bts->trx[i]; - for (j = 0; j < 8; j++) { - ts = &trx->ts[j]; - if (ts->pchan != pchan) - continue; - /* check if all sub-slots are allocated yet */ - for (ss = 0; ss < subslots_per_pchan[pchan]; ss++) { - struct gsm_lchan *lc = &ts->lchan[ss]; - if (lc->type == GSM_LCHAN_NONE) - return lc; - } - } - } - /* we cannot allocate more of these */ - if (pchan == GSM_PCHAN_CCCH_SDCCH4) - return NULL; - - /* if we've reached here, we need to allocate a new physical - * channel for the logical channel type requested */ - ts = ts_alloc(bts, pchan); - if (!ts) { - /* no more radio resources */ - return NULL; - } - return &ts->lchan[0]; -} - -/* Allocate a logical channel */ -struct gsm_lchan *lchan_alloc(struct gsm_bts *bts, enum gsm_chan_t type) -{ - struct gsm_lchan *lchan = NULL; - - switch (type) { - case GSM_LCHAN_SDCCH: - lchan = _lc_find(bts, GSM_PCHAN_CCCH_SDCCH4); - if (lchan == NULL) - lchan = _lc_find(bts, GSM_PCHAN_SDCCH8_SACCH8C); - break; - case GSM_LCHAN_TCH_F: - lchan = _lc_find(bts, GSM_PCHAN_TCH_F); - break; - case GSM_LCHAN_TCH_H: - lchan =_lc_find(bts, GSM_PCHAN_TCH_H); - break; - default: - fprintf(stderr, "Unknown gsm_chan_t %u\n", type); - } - - if (lchan) { - lchan->type = type; - lchan->use_count = 0; - - /* Configure the time and start it so it will be closed */ - lchan->release_timer.cb = auto_release_channel; - lchan->release_timer.data = lchan; - bsc_schedule_timer(&lchan->release_timer, LCHAN_RELEASE_TIMEOUT); - } - - return lchan; -} - -/* Free a logical channel */ -void lchan_free(struct gsm_lchan *lchan) -{ - lchan->type = GSM_LCHAN_NONE; - if (lchan->subscr) { - subscr_put(lchan->subscr); - lchan->subscr = 0; - } - - /* We might kill an active channel... */ - if (lchan->use_count != 0) { - dispatch_signal(SS_LCHAN, S_LCHAN_UNEXPECTED_RELEASE, lchan); - lchan->use_count = 0; - } - - /* stop the timer */ - bsc_del_timer(&lchan->release_timer); - - /* FIXME: ts_free() the timeslot, if we're the last logical - * channel using it */ -} - -/* Consider releasing the channel now */ -int lchan_auto_release(struct gsm_lchan *lchan) -{ - if (lchan->use_count > 0) { - return 0; - } - - /* Assume we have GSM04.08 running and send a release */ - if (lchan->subscr) { - gsm48_send_rr_release(lchan); - } - - /* spoofed? message */ - if (lchan->use_count < 0) { - DEBUGP(DRLL, "Channel count is negative: %d\n", lchan->use_count); - } - - DEBUGP(DRLL, "Recycling the channel with: %d (%x)\n", lchan->nr, lchan->nr); - rsl_chan_release(lchan); - return 1; -} - -/* Auto release the channel when the use count is zero */ -static void auto_release_channel(void *_lchan) -{ - struct gsm_lchan *lchan = _lchan; - - if (!lchan_auto_release(lchan)) - bsc_schedule_timer(&lchan->release_timer, LCHAN_RELEASE_TIMEOUT); -} - -struct gsm_lchan* lchan_find(struct gsm_bts *bts, struct gsm_subscriber *subscr) { - int trx, ts_no, lchan_no; - - for (trx = 0; trx < bts->num_trx; ++trx) { - for (ts_no = 0; ts_no < 8; ++ts_no) { - for (lchan_no = 0; lchan_no < TS_MAX_LCHAN; ++lchan_no) { - struct gsm_lchan *lchan = - &bts->trx[trx].ts[ts_no].lchan[lchan_no]; - if (subscr == lchan->subscr) - return lchan; - } - } - } - - return NULL; -} diff --git a/src/db.c b/src/db.c deleted file mode 100644 index 600699ae7..000000000 --- a/src/db.c +++ /dev/null @@ -1,464 +0,0 @@ -/* Simple HLR/VLR database backend using dbi */ -/* (C) 2008 by Jan Luebbe <jluebbe@debian.org> - * (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. - * - */ - -#include <openbsc/gsm_data.h> -#include <openbsc/db.h> - -#include <libgen.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <errno.h> -#include <dbi/dbi.h> - -static char *db_basename = NULL; -static char *db_dirname = NULL; -static dbi_conn conn; - -static char *create_stmts[] = { - "CREATE TABLE IF NOT EXISTS Meta (" - "id INTEGER PRIMARY KEY AUTOINCREMENT, " - "key TEXT UNIQUE NOT NULL, " - "value TEXT NOT NULL" - ")", - "INSERT OR IGNORE INTO Meta " - "(key, value) " - "VALUES " - "('revision', '1')", - "CREATE TABLE IF NOT EXISTS Subscriber (" - "id INTEGER PRIMARY KEY AUTOINCREMENT, " - "created TIMESTAMP NOT NULL, " - "updated TIMESTAMP NOT NULL, " - "imsi NUMERIC UNIQUE NOT NULL, " - "name TEXT, " - "extension TEXT UNIQUE, " - "authorized INTEGER NOT NULL DEFAULT 0, " - "tmsi TEXT UNIQUE, " - "lac INTEGER NOT NULL DEFAULT 0" - ")", - "CREATE TABLE IF NOT EXISTS Equipment (" - "id INTEGER PRIMARY KEY AUTOINCREMENT, " - "created TIMESTAMP NOT NULL, " - "updated TIMESTAMP NOT NULL, " - "name TEXT, " - "imei NUMERIC UNIQUE NOT NULL" - ")", - "CREATE TABLE IF NOT EXISTS EquipmentWatch (" - "id INTEGER PRIMARY KEY AUTOINCREMENT, " - "created TIMESTAMP NOT NULL, " - "updated TIMESTAMP NOT NULL, " - "subscriber_id NUMERIC NOT NULL, " - "equipment_id NUMERIC NOT NULL, " - "UNIQUE (subscriber_id, equipment_id) " - ")", - "CREATE TABLE IF NOT EXISTS SMS (" - "id INTEGER PRIMARY KEY AUTOINCREMENT, " - "created TIMESTAMP NOT NULL, " - "sent TIMESTAMP, " - "sender_id NUMERIC NOT NULL, " - "receiver_id NUMERIC NOT NULL, " - "header NUMERIC, " - "text TEXT NOT NULL " - ")", - "CREATE TABLE IF NOT EXISTS VLR (" - "id INTEGER PRIMARY KEY AUTOINCREMENT, " - "created TIMESTAMP NOT NULL, " - "updated TIMESTAMP NOT NULL, " - "subscriber_id NUMERIC UNIQUE NOT NULL, " - "last_bts NUMERIC NOT NULL " - ")", -}; - -void db_error_func(dbi_conn conn, void* data) { - const char* msg; - dbi_conn_error(conn, &msg); - printf("DBI: %s\n", msg); -} - -int db_init(const char *name) { - dbi_initialize(NULL); - conn = dbi_conn_new("sqlite3"); - if (conn==NULL) { - printf("DB: Failed to create connection.\n"); - return 1; - } - - dbi_conn_error_handler( conn, db_error_func, NULL ); - - /* MySQL - dbi_conn_set_option(conn, "host", "localhost"); - dbi_conn_set_option(conn, "username", "your_name"); - dbi_conn_set_option(conn, "password", "your_password"); - dbi_conn_set_option(conn, "dbname", "your_dbname"); - dbi_conn_set_option(conn, "encoding", "UTF-8"); - */ - - /* SqLite 3 */ - db_basename = strdup(name); - db_dirname = strdup(name); - dbi_conn_set_option(conn, "sqlite3_dbdir", dirname(db_dirname)); - dbi_conn_set_option(conn, "dbname", basename(db_basename)); - - if (dbi_conn_connect(conn) < 0) { - free(db_dirname); - free(db_basename); - db_dirname = db_basename = NULL; - return 1; - } - - return 0; -} - -int db_prepare() { - dbi_result result; - int i; - - for (i = 0; i < ARRAY_SIZE(create_stmts); i++) { - result = dbi_conn_query(conn, create_stmts[i]); - if (result==NULL) { - printf("DB: Failed to create some table.\n"); - return 1; - } - dbi_result_free(result); - } - - return 0; -} - -int db_fini() { - dbi_conn_close(conn); - dbi_shutdown(); - - if (db_dirname) - free(db_dirname); - if (db_basename) - free(db_basename); - return 0; -} - -struct gsm_subscriber* db_create_subscriber(char *imsi) { - dbi_result result; - struct gsm_subscriber* subscr; - - /* Is this subscriber known in the db? */ - subscr = db_get_subscriber(GSM_SUBSCRIBER_IMSI, imsi); - if (subscr) { - result = dbi_conn_queryf(conn, - "UPDATE Subscriber set updated = datetime('now') " - "WHERE imsi = %s " , imsi); - if (result==NULL) { - printf("DB: failed to update timestamp\n"); - } else { - dbi_result_free(result); - } - return subscr; - } - - subscr = subscr_alloc(); - if (!subscr) - return NULL; - result = dbi_conn_queryf(conn, - "INSERT INTO Subscriber " - "(imsi, created, updated) " - "VALUES " - "(%s, datetime('now'), datetime('now')) ", - imsi - ); - if (result==NULL) { - printf("DB: Failed to create Subscriber by IMSI.\n"); - } - subscr->id = dbi_conn_sequence_last(conn, NULL); - strncpy(subscr->imsi, imsi, GSM_IMSI_LENGTH-1); - dbi_result_free(result); - printf("DB: New Subscriber: ID %llu, IMSI %s\n", subscr->id, subscr->imsi); - return subscr; -} - -struct gsm_subscriber *db_get_subscriber(enum gsm_subscriber_field field, const char *id) { - dbi_result result; - const char *string; - char *quoted; - struct gsm_subscriber *subscr; - - switch (field) { - case GSM_SUBSCRIBER_IMSI: - dbi_conn_quote_string_copy(conn, id, "ed); - result = dbi_conn_queryf(conn, - "SELECT * FROM Subscriber " - "WHERE imsi = %s ", - quoted - ); - free(quoted); - break; - case GSM_SUBSCRIBER_TMSI: - dbi_conn_quote_string_copy(conn, id, "ed); - result = dbi_conn_queryf(conn, - "SELECT * FROM Subscriber " - "WHERE tmsi = %s ", - quoted - ); - free(quoted); - break; - case GSM_SUBSCRIBER_EXTENSION: - dbi_conn_quote_string_copy(conn, id, "ed); - result = dbi_conn_queryf(conn, - "SELECT * FROM Subscriber " - "WHERE extension = %s ", - quoted - ); - free(quoted); - break; - default: - printf("DB: Unknown query selector for Subscriber.\n"); - return NULL; - } - if (result==NULL) { - printf("DB: Failed to query Subscriber.\n"); - return NULL; - } - if (!dbi_result_next_row(result)) { - printf("DB: Failed to find the Subscriber. '%u' '%s'\n", - field, id); - dbi_result_free(result); - return NULL; - } - - subscr = subscr_alloc(); - subscr->id = dbi_result_get_ulonglong(result, "id"); - string = dbi_result_get_string(result, "imsi"); - if (string) - strncpy(subscr->imsi, string, GSM_IMSI_LENGTH); - - string = dbi_result_get_string(result, "tmsi"); - if (string) - strncpy(subscr->tmsi, string, GSM_TMSI_LENGTH); - - string = dbi_result_get_string(result, "name"); - if (string) - strncpy(subscr->name, string, GSM_NAME_LENGTH); - - string = dbi_result_get_string(result, "extension"); - if (string) - strncpy(subscr->extension, string, GSM_EXTENSION_LENGTH); - - subscr->lac = dbi_result_get_uint(result, "lac"); - subscr->authorized = dbi_result_get_uint(result, "authorized"); - printf("DB: Found Subscriber: ID %llu, IMSI %s, NAME '%s', TMSI %s, EXTEN '%s', LAC %hu, AUTH %u\n", - subscr->id, subscr->imsi, subscr->name, subscr->tmsi, subscr->extension, - subscr->lac, subscr->authorized); - dbi_result_free(result); - return subscr; -} - -int db_sync_subscriber(struct gsm_subscriber* subscriber) { - dbi_result result; - result = dbi_conn_queryf(conn, - "UPDATE Subscriber " - "SET updated = datetime('now'), " - "tmsi = '%s', " - "lac = %i, " - "authorized = %i " - "WHERE imsi = %s ", - subscriber->tmsi, subscriber->lac, subscriber->authorized, subscriber->imsi - ); - - if (result==NULL) { - printf("DB: Failed to update Subscriber (by IMSI).\n"); - return 1; - } - dbi_result_free(result); - return 0; -} - -int db_subscriber_alloc_tmsi(struct gsm_subscriber* subscriber) { - dbi_result result=NULL; - char* tmsi_quoted; - for (;;) { - sprintf(subscriber->tmsi, "%i", rand()); - dbi_conn_quote_string_copy(conn, subscriber->tmsi, &tmsi_quoted); - result = dbi_conn_queryf(conn, - "SELECT * FROM Subscriber " - "WHERE tmsi = %s ", - tmsi_quoted - ); - free(tmsi_quoted); - if (result==NULL) { - printf("DB: Failed to query Subscriber while allocating new TMSI.\n"); - return 1; - } - if (dbi_result_get_numrows(result)){ - dbi_result_free(result); - continue; - } - if (!dbi_result_next_row(result)) { - dbi_result_free(result); - printf("DB: Allocated TMSI %s for IMSI %s.\n", subscriber->tmsi, subscriber->imsi); - return db_sync_subscriber(subscriber); - } - dbi_result_free(result); - } - return 0; -} - -int db_subscriber_assoc_imei(struct gsm_subscriber* subscriber, char imei[GSM_IMEI_LENGTH]) { - u_int64_t equipment_id, watch_id; - dbi_result result; - - result = dbi_conn_queryf(conn, - "INSERT OR IGNORE INTO Equipment " - "(imei, created, updated) " - "VALUES " - "(%s, datetime('now'), datetime('now')) ", - imei - ); - if (result==NULL) { - printf("DB: Failed to create Equipment by IMEI.\n"); - return 1; - } - equipment_id = 0; - if (dbi_result_get_numrows_affected(result)) { - equipment_id = dbi_conn_sequence_last(conn, NULL); - } - dbi_result_free(result); - if (equipment_id) { - printf("DB: New Equipment: ID %llu, IMEI %s\n", equipment_id, imei); - } - else { - result = dbi_conn_queryf(conn, - "SELECT id FROM Equipment " - "WHERE imei = %s ", - imei - ); - if (result==NULL) { - printf("DB: Failed to query Equipment by IMEI.\n"); - return 1; - } - if (!dbi_result_next_row(result)) { - printf("DB: Failed to find the Equipment.\n"); - dbi_result_free(result); - return 1; - } - equipment_id = dbi_result_get_ulonglong(result, "id"); - dbi_result_free(result); - } - - result = dbi_conn_queryf(conn, - "INSERT OR IGNORE INTO EquipmentWatch " - "(subscriber_id, equipment_id, created, updated) " - "VALUES " - "(%llu, %llu, datetime('now'), datetime('now')) ", - subscriber->id, equipment_id - ); - if (result==NULL) { - printf("DB: Failed to create EquipmentWatch.\n"); - return 1; - } - watch_id = 0; - if (dbi_result_get_numrows_affected(result)) { - watch_id = dbi_conn_sequence_last(conn, NULL); - } - dbi_result_free(result); - if (watch_id) { - printf("DB: New EquipmentWatch: ID %llu, IMSI %s, IMEI %s\n", equipment_id, subscriber->imsi, imei); - } - else { - result = dbi_conn_queryf(conn, - "UPDATE EquipmentWatch " - "SET updated = datetime('now') " - "WHERE subscriber_id = %llu AND equipment_id = %llu ", - subscriber->id, equipment_id - ); - if (result==NULL) { - printf("DB: Failed to update EquipmentWatch.\n"); - return 1; - } - dbi_result_free(result); - printf("DB: Updated EquipmentWatch: ID %llu, IMSI %s, IMEI %s\n", equipment_id, subscriber->imsi, imei); - } - - return 0; -} - -/* store an [unsent] SMS to the database */ -int db_sms_store(struct gsm_sms *sms) -{ - dbi_result result; - char *q_text; - - dbi_conn_quote_string_copy(conn, (char *)sms->text, &q_text); - result = dbi_conn_queryf(conn, - "INSERT INTO SMS " - "(created,sender_id,receiver_id,header,text) VALUES " - "(datetime('now'),%llu,%llu,%s,%s)", - sms->sender->id, - sms->receiver ? sms->receiver->id : 0, - NULL, q_text); - free(q_text); - - if (!result) - return -EIO; - - dbi_result_free(result); - return 0; -} - -/* retrieve the next unsent SMS with ID >= min_id */ -struct gsm_sms *db_sms_get_unsent(int min_id) -{ - dbi_result result; - struct gsm_sms *sms = malloc(sizeof(*sms)); - - if (!sms) { - free(sms); - return NULL; - } - - result = dbi_conn_queryf(conn, - "SELECT * FROM SMS " - "WHERE id >= %llu ORDER BY id", min_id); - if (!result) { - free(sms); - return NULL; - } - - /* FIXME: fill gsm_sms from database */ - - dbi_result_free(result); - return sms; -} - -/* mark a given SMS as read */ -int db_sms_mark_sent(struct gsm_sms *sms) -{ - dbi_result result; - - result = dbi_conn_queryf(conn, - "UPDATE SMS " - "SET sent = datetime('now') " - "WHERE id = %llu", sms->id); - if (!result) { - printf("DB: Failed to mark SMS %llu as sent.\n", sms->id); - return 1; - } - - dbi_result_free(result); - return 0; -} diff --git a/src/debug.c b/src/debug.c deleted file mode 100644 index aeb993097..000000000 --- a/src/debug.c +++ /dev/null @@ -1,161 +0,0 @@ -/* Debugging/Logging support code */ -/* (C) 2008 by Harald Welte <laforge@gnumonks.org> - * (C) 2008 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. - * - */ - -#include <stdarg.h> -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <strings.h> -#include <time.h> - -#include <openbsc/debug.h> - -unsigned int debug_mask = 0xffffffff & ~(DMI|DMIB); - -struct debug_info { - const char *name; - const char *color; - const char *description; - int number; -}; - -#define DEBUG_CATEGORY(NUMBER, NAME, COLOR, DESCRIPTION) \ - { .name = NAME, .color = COLOR, .description = DESCRIPTION, .number = NUMBER }, - -#define ARRAY_SIZE(array) (sizeof(array)/sizeof(array[0])) - -static const struct debug_info debug_info[] = { - DEBUG_CATEGORY(DRLL, "DRLL", "\033[1;31m", "") - DEBUG_CATEGORY(DCC, "DCC", "\033[1;32m", "") - DEBUG_CATEGORY(DMM, "DMM", "\033[1;33m", "") - DEBUG_CATEGORY(DRR, "DRR", "\033[1;34m", "") - DEBUG_CATEGORY(DRSL, "DRSL", "\033[1;35m", "") - DEBUG_CATEGORY(DNM, "DNM", "\033[1;36m", "") - DEBUG_CATEGORY(DSMS, "DSMS", "\033[1;37m", "") - DEBUG_CATEGORY(DPAG, "DPAG", "\033[1;38m", "") - DEBUG_CATEGORY(DMNCC, "DMNCC","\033[1;39m", "") - DEBUG_CATEGORY(DINP, "DINP", "", "") - DEBUG_CATEGORY(DMI, "DMI", "", "") - DEBUG_CATEGORY(DMIB, "DMIB", "", "") - DEBUG_CATEGORY(DMUX, "DMUX", "", "") -}; - -static int use_color = 1; - -void debug_use_color(int color) -{ - use_color = color; -} - -static int print_timestamp = 0; - -void debug_timestamp(int enable) -{ - print_timestamp = enable; -} - - -/* - * Parse the category mask. - * category1:category2:category3 - */ -void debug_parse_category_mask(const char *_mask) -{ - unsigned int new_mask = 0; - int i = 0; - char *mask = strdup(_mask); - char *category_token = NULL; - - category_token = strtok(mask, ":"); - do { - for (i = 0; i < ARRAY_SIZE(debug_info); ++i) { - if (strcasecmp(debug_info[i].name, category_token) == 0) - new_mask |= debug_info[i].number; - } - } while ((category_token = strtok(NULL, ":"))); - - - free(mask); - debug_mask = new_mask; -} - -const char* color(int subsys) -{ - int i = 0; - - for (i = 0; use_color && i < ARRAY_SIZE(debug_info); ++i) { - if (debug_info[i].number == subsys) - return debug_info[i].color; - } - - return ""; -} - -void debugp(unsigned int subsys, char *file, int line, int cont, const char *format, ...) -{ - va_list ap; - FILE *outfd = stderr; - - if (!(debug_mask & subsys)) - return; - - va_start(ap, format); - - fprintf(outfd, "%s", color(subsys)); - - if (!cont) { - if (print_timestamp) { - char *timestr; - time_t tm; - tm = time(NULL); - timestr = ctime(&tm); - timestr[strlen(timestr)-1] = '\0'; - fprintf(outfd, "%s ", timestr); - } - fprintf(outfd, "<%4.4x> %s:%d ", subsys, file, line); - } - vfprintf(outfd, format, ap); - fprintf(outfd, "\033[0;m"); - - va_end(ap); - - fflush(outfd); -} - -static char hexd_buff[4096]; - -char *hexdump(unsigned char *buf, int len) -{ - int i; - char *cur = hexd_buff; - - hexd_buff[0] = 0; - for (i = 0; i < len; i++) { - int len_remain = sizeof(hexd_buff) - (cur - hexd_buff); - int rc = snprintf(cur, len_remain, "%02x ", buf[i]); - if (rc <= 0) - break; - cur += rc; - } - hexd_buff[sizeof(hexd_buff)-1] = 0; - return hexd_buff; -} - diff --git a/src/e1_config.c b/src/e1_config.c deleted file mode 100644 index fc23b55e1..000000000 --- a/src/e1_config.c +++ /dev/null @@ -1,108 +0,0 @@ -#include <string.h> -#include <errno.h> - -#include <netinet/in.h> - -#include <openbsc/gsm_data.h> -#include <openbsc/e1_input.h> -#include <openbsc/trau_frame.h> -#include <openbsc/trau_mux.h> -#include <openbsc/misdn.h> - -#define SAPI_L2ML 0 -#define SAPI_OML 62 -#define SAPI_RSL 0 /* 63 ? */ - -#define TEI_L2ML 127 -#define TEI_OML 25 -#define TEI_RSL 1 - -/* do some compiled-in configuration for our BTS/E1 setup */ -int e1_config(struct gsm_bts *bts, int cardnr, int release_l2) -{ - struct e1inp_line *line; - struct e1inp_ts *sign_ts; - struct e1inp_sign_link *oml_link, *rsl_link; - - line = malloc(sizeof(*line)); - if (!line) - return -ENOMEM; - memset(line, 0, sizeof(*line)); - - /* create E1 timeslots for signalling and TRAU frames */ - e1inp_ts_config(&line->ts[1-1], line, E1INP_TS_TYPE_SIGN); - e1inp_ts_config(&line->ts[2-1], line, E1INP_TS_TYPE_TRAU); - e1inp_ts_config(&line->ts[3-1], line, E1INP_TS_TYPE_TRAU); - - /* create signalling links for TS1 */ - sign_ts = &line->ts[1-1]; - oml_link = e1inp_sign_link_create(sign_ts, E1INP_SIGN_OML, - bts->c0, TEI_OML, SAPI_OML); - rsl_link = e1inp_sign_link_create(sign_ts, E1INP_SIGN_RSL, - bts->c0, TEI_RSL, SAPI_RSL); - - /* create back-links from bts/trx */ - bts->oml_link = oml_link; - bts->c0->rsl_link = rsl_link; - - /* enable subchannel demuxer on TS2 */ - subch_demux_activate(&line->ts[2-1].trau.demux, 1); - subch_demux_activate(&line->ts[2-1].trau.demux, 2); - subch_demux_activate(&line->ts[2-1].trau.demux, 3); - - /* enable subchannel demuxer on TS3 */ - subch_demux_activate(&line->ts[3-1].trau.demux, 0); - subch_demux_activate(&line->ts[3-1].trau.demux, 1); - subch_demux_activate(&line->ts[3-1].trau.demux, 2); - subch_demux_activate(&line->ts[3-1].trau.demux, 3); - -#ifdef HAVE_TRX1 - /* create E1 timeslots for TRAU frames of TRX1 */ - e1inp_ts_config(&line->ts[4-1], line, E1INP_TS_TYPE_TRAU); - e1inp_ts_config(&line->ts[5-1], line, E1INP_TS_TYPE_TRAU); - - /* create RSL signalling link for TRX1 */ - sign_ts = &line->ts[1-1]; - rsl_link = e1inp_sign_link_create(sign_ts, E1INP_SIGN_RSL, - &bts->trx[1], TEI_RSL+1, SAPI_RSL); - /* create back-links from trx */ - bts->trx[1].rsl_link = rsl_link; -#endif - - return mi_setup(cardnr, line, release_l2); -} - -/* configure pseudo E1 line in ip.access style and connect to BTS */ -int ia_config_connect(struct gsm_bts *bts, struct sockaddr_in *sin) -{ - struct e1inp_line *line; - struct e1inp_ts *sign_ts, *rsl_ts; - struct e1inp_sign_link *oml_link, *rsl_link; - - line = malloc(sizeof(*line)); - if (!line) - return NULL; - memset(line, 0, sizeof(*line)); - - /* create E1 timeslots for signalling and TRAU frames */ - e1inp_ts_config(&line->ts[1-1], line, E1INP_TS_TYPE_SIGN); - e1inp_ts_config(&line->ts[2-1], line, E1INP_TS_TYPE_SIGN); - - /* create signalling links for TS1 */ - 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); - rsl_link = e1inp_sign_link_create(rsl_ts, E1INP_SIGN_RSL, - bts->c0, 0, 0); - - /* create back-links from bts/trx */ - bts->oml_link = oml_link; - bts->c0->rsl_link = rsl_link; - - /* default port at BTS for incoming connections is 3006 */ - if (sin->sin_port == 0) - sin->sin_port = htons(3006); - - return ipaccess_connect(line, sin); -} diff --git a/src/e1_input.c b/src/e1_input.c deleted file mode 100644 index c3c7c7597..000000000 --- a/src/e1_input.c +++ /dev/null @@ -1,498 +0,0 @@ -/* OpenBSC Abis interface to E1 */ - -/* (C) 2008-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 <stdio.h> -#include <unistd.h> -#include <stdlib.h> -#include <errno.h> -#include <string.h> -#include <time.h> -#include <sys/fcntl.h> -#include <sys/types.h> -#include <sys/socket.h> -#include <sys/ioctl.h> -#include <arpa/inet.h> -#include <mISDNif.h> - -//#define AF_COMPATIBILITY_FUNC -//#include <compat_af_isdn.h> -#ifndef AF_ISDN -#define AF_ISDN 34 -#define PF_ISDN AF_ISDN -#endif - -#include <openbsc/select.h> -#include <openbsc/msgb.h> -#include <openbsc/debug.h> -#include <openbsc/gsm_data.h> -#include <openbsc/e1_input.h> -#include <openbsc/abis_nm.h> -#include <openbsc/abis_rsl.h> -#include <openbsc/linuxlist.h> -#include <openbsc/subchan_demux.h> -#include <openbsc/trau_frame.h> -#include <openbsc/trau_mux.h> - -#define NUM_E1_TS 32 - -/* list of all E1 drivers */ -LLIST_HEAD(e1inp_driver_list); - -/* list of all E1 lines */ -LLIST_HEAD(e1inp_line_list); - -/* to be implemented, e.g. by bsc_hack.c */ -void input_event(int event, enum e1inp_sign_type type, struct gsm_bts_trx *trx); - -/* - * pcap writing of the misdn load - * pcap format is from http://wiki.wireshark.org/Development/LibpcapFileFormat - */ -#define DLT_LINUX_LAPD 177 -#define PCAP_INPUT 0 -#define PCAP_OUTPUT 1 - -struct pcap_hdr { - u_int32_t magic_number; - u_int16_t version_major; - u_int16_t version_minor; - int32_t thiszone; - u_int32_t sigfigs; - u_int32_t snaplen; - u_int32_t network; -} __attribute__((packed)); - -struct pcaprec_hdr { - u_int32_t ts_sec; - u_int32_t ts_usec; - u_int32_t incl_len; - u_int32_t orig_len; -} __attribute__((packed)); - -struct fake_linux_lapd_header { - u_int16_t pkttype; - u_int16_t hatype; - u_int16_t halen; - u_int64_t addr; - int16_t protocol; -} __attribute__((packed)); - -struct lapd_header { - u_int8_t ea1 : 1; - u_int8_t cr : 1; - u_int8_t sapi : 6; - u_int8_t ea2 : 1; - u_int8_t tei : 7; - u_int8_t control_foo; /* fake UM's ... */ -} __attribute__((packed)); - -static_assert((int)&((struct fake_linux_lapd_header*)NULL)->hatype == 2, hatype_offset); -static_assert((int)&((struct fake_linux_lapd_header*)NULL)->halen == 4, halen_offset); -static_assert((int)&((struct fake_linux_lapd_header*)NULL)->addr == 6, addr_offset); -static_assert((int)&((struct fake_linux_lapd_header*)NULL)->protocol == 14, proto_offset); -static_assert(sizeof(struct fake_linux_lapd_header) == 16, lapd_header_size); - - -static int pcap_fd = -1; - -void e1_set_pcap_fd(int fd) -{ - int ret; - struct pcap_hdr header = { - .magic_number = 0xa1b2c3d4, - .version_major = 2, - .version_minor = 4, - .thiszone = 0, - .sigfigs = 0, - .snaplen = 65535, - .network = DLT_LINUX_LAPD, - }; - - pcap_fd = fd; - ret = write(pcap_fd, &header, sizeof(header)); -} - -/* This currently only works for the D-Channel */ -static void write_pcap_packet(int direction, int sapi, int tei, - struct msgb *msg) { - if (pcap_fd < 0) - return; - - int ret; - time_t cur_time; - struct tm *tm; - - struct fake_linux_lapd_header header = { - .pkttype = 4, - .hatype = 0, - .halen = 0, - .addr = direction == PCAP_OUTPUT ? 0x0 : 0x1, - .protocol = ntohs(48), - }; - - struct lapd_header lapd_header = { - .ea1 = 0, - .cr = direction == PCAP_OUTPUT ? 1 : 0, - .sapi = sapi & 0x3F, - .ea2 = 1, - .tei = tei & 0x7F, - .control_foo = 0x03 /* UI */, - }; - - struct pcaprec_hdr payload_header = { - .ts_sec = 0, - .ts_usec = 0, - .incl_len = msg->len + sizeof(struct fake_linux_lapd_header) - + sizeof(struct lapd_header) - - MISDN_HEADER_LEN, - .orig_len = msg->len + sizeof(struct fake_linux_lapd_header) - + sizeof(struct lapd_header) - - MISDN_HEADER_LEN, - }; - - - cur_time = time(NULL); - tm = localtime(&cur_time); - payload_header.ts_sec = mktime(tm); - - ret = write(pcap_fd, &payload_header, sizeof(payload_header)); - ret = write(pcap_fd, &header, sizeof(header)); - ret = write(pcap_fd, &lapd_header, sizeof(lapd_header)); - ret = write(pcap_fd, msg->data + MISDN_HEADER_LEN, - msg->len - MISDN_HEADER_LEN); -} - -static const char *sign_types[] = { - [E1INP_SIGN_NONE] = "None", - [E1INP_SIGN_OML] = "OML", - [E1INP_SIGN_RSL] = "RSL", -}; -const char *e1inp_signtype_name(enum e1inp_sign_type tp) -{ - if (tp >= ARRAY_SIZE(sign_types)) - return "undefined"; - return sign_types[tp]; -} - -static const char *ts_types[] = { - [E1INP_TS_TYPE_NONE] = "None", - [E1INP_TS_TYPE_SIGN] = "Signalling", - [E1INP_TS_TYPE_TRAU] = "TRAU", -}; - -const char *e1inp_tstype_name(enum e1inp_ts_type tp) -{ - if (tp >= ARRAY_SIZE(ts_types)) - return "undefined"; - return ts_types[tp]; -} - -/* callback when a TRAU frame was received */ -static int subch_cb(struct subch_demux *dmx, int ch, u_int8_t *data, int len, - void *_priv) -{ - struct e1inp_ts *e1i_ts = _priv; - struct gsm_e1_subslot src_ss; - - src_ss.e1_nr = e1i_ts->line->num; - src_ss.e1_ts = e1i_ts->num; - src_ss.e1_ts_ss = ch; - - return trau_mux_input(&src_ss, data, len); -} - -int abis_rsl_sendmsg(struct msgb *msg) -{ - struct e1inp_sign_link *sign_link; - struct e1inp_driver *e1inp_driver; - struct e1inp_ts *e1i_ts; - - msg->l2h = msg->data; - - if (!msg->trx || !msg->trx->rsl_link) { - fprintf(stderr, "rsl_sendmsg: msg->trx == NULL\n"); - return -EINVAL; - } - - sign_link = msg->trx->rsl_link; - e1i_ts = sign_link->ts; - if (!bsc_timer_pending(&e1i_ts->sign.tx_timer)) { - /* notify the driver we have something to write */ - e1inp_driver = sign_link->ts->line->driver; - e1inp_driver->want_write(e1i_ts); - } - msgb_enqueue(&sign_link->tx_list, msg); - - /* dump it */ - write_pcap_packet(PCAP_OUTPUT, sign_link->sapi, sign_link->tei, msg); - - return 0; -} - -int _abis_nm_sendmsg(struct msgb *msg) -{ - struct e1inp_sign_link *sign_link; - struct e1inp_driver *e1inp_driver; - struct e1inp_ts *e1i_ts; - - msg->l2h = msg->data; - - if (!msg->trx || !msg->trx->bts || !msg->trx->bts->oml_link) { - fprintf(stderr, "nm_sendmsg: msg->trx == NULL\n"); - return -EINVAL; - } - - sign_link = msg->trx->bts->oml_link; - e1i_ts = sign_link->ts; - if (!bsc_timer_pending(&e1i_ts->sign.tx_timer)) { - /* notify the driver we have something to write */ - e1inp_driver = sign_link->ts->line->driver; - e1inp_driver->want_write(e1i_ts); - } - msgb_enqueue(&sign_link->tx_list, msg); - - /* dump it */ - write_pcap_packet(PCAP_OUTPUT, sign_link->sapi, sign_link->tei, msg); - - return 0; -} - -/* Timeslot */ - -/* configure and initialize one e1inp_ts */ -int e1inp_ts_config(struct e1inp_ts *ts, struct e1inp_line *line, - enum e1inp_ts_type type) -{ - ts->type = type; - ts->line = line; - - switch (type) { - case E1INP_TS_TYPE_SIGN: - INIT_LLIST_HEAD(&ts->sign.sign_links); - break; - case E1INP_TS_TYPE_TRAU: - subchan_mux_init(&ts->trau.mux); - ts->trau.demux.out_cb = subch_cb; - ts->trau.demux.data = ts; - subch_demux_init(&ts->trau.demux); - break; - default: - fprintf(stderr, "unsupported E1 timeslot type %u\n", - ts->type); - return -EINVAL; - } - return 0; -} - -static struct e1inp_line *e1inp_line_get(u_int8_t e1_nr) -{ - struct e1inp_line *e1i_line; - - /* iterate over global list of e1 lines */ - llist_for_each_entry(e1i_line, &e1inp_line_list, list) { - if (e1i_line->num == e1_nr) - return e1i_line; - } - return NULL; -} - -static struct e1inp_ts *e1inp_ts_get(u_int8_t e1_nr, u_int8_t ts_nr) -{ - struct e1inp_line *e1i_line; - - e1i_line = e1inp_line_get(e1_nr); - if (!e1i_line) - return NULL; - - return &e1i_line->ts[ts_nr-1]; -} - -struct subch_mux *e1inp_get_mux(u_int8_t e1_nr, u_int8_t ts_nr) -{ - struct e1inp_ts *e1i_ts = e1inp_ts_get(e1_nr, ts_nr); - - if (!e1i_ts) - return NULL; - - return &e1i_ts->trau.mux; -} - -/* Signalling Link */ - -struct e1inp_sign_link *e1inp_lookup_sign_link(struct e1inp_ts *e1i, - u_int8_t tei, u_int8_t sapi) -{ - struct e1inp_sign_link *link; - - llist_for_each_entry(link, &e1i->sign.sign_links, list) { - if (link->sapi == sapi && link->tei == tei) - return link; - } - - return NULL; -} - -/* create a new signalling link in a E1 timeslot */ - -struct e1inp_sign_link * -e1inp_sign_link_create(struct e1inp_ts *ts, enum e1inp_sign_type type, - struct gsm_bts_trx *trx, u_int8_t tei, - u_int8_t sapi) -{ - struct e1inp_sign_link *link; - - if (ts->type != E1INP_TS_TYPE_SIGN) - return NULL; - - link = malloc(sizeof(*link)); - if (!link) - return NULL; - - memset(link, 0, sizeof(*link)); - - link->ts = ts; - link->type = type; - INIT_LLIST_HEAD(&link->tx_list); - link->trx = trx; - link->tei = tei; - link->sapi = sapi; - - llist_add_tail(&link->list, &ts->sign.sign_links); - - return link; -} - -/* the E1 driver tells us he has received something on a TS */ -int e1inp_rx_ts(struct e1inp_ts *ts, struct msgb *msg, - u_int8_t tei, u_int8_t sapi) -{ - struct e1inp_sign_link *link; - int ret; - - switch (ts->type) { - case E1INP_TS_TYPE_SIGN: - /* consult the list of signalling links */ - write_pcap_packet(PCAP_INPUT, sapi, tei, msg); - link = e1inp_lookup_sign_link(ts, tei, sapi); - if (!link) { - fprintf(stderr, "didn't find singalling link for " - "tei %d, sapi %d\n", tei, sapi); - return -EINVAL; - } - switch (link->type) { - case E1INP_SIGN_OML: - msg->trx = link->trx; - ret = abis_nm_rcvmsg(msg); - break; - case E1INP_SIGN_RSL: - msg->trx = link->trx; - ret = abis_rsl_rcvmsg(msg); - break; - default: - ret = -EINVAL; - fprintf(stderr, "unknown link type %u\n", link->type); - break; - } - break; - case E1INP_TS_TYPE_TRAU: - ret = subch_demux_in(&ts->trau.demux, msg->l2h, msgb_l2len(msg)); - break; - default: - ret = -EINVAL; - fprintf(stderr, "unknown TS type %u\n", ts->type); - break; - } - - return ret; -} - -#define TSX_ALLOC_SIZE 4096 - -/* called by driver if it wants to transmit on a given TS */ -struct msgb *e1inp_tx_ts(struct e1inp_ts *e1i_ts, - struct e1inp_sign_link **sign_link) -{ - struct e1inp_sign_link *link; - struct msgb *msg = NULL; - int len; - - switch (e1i_ts->type) { - case E1INP_TS_TYPE_SIGN: - /* FIXME: implement this round robin */ - llist_for_each_entry(link, &e1i_ts->sign.sign_links, list) { - msg = msgb_dequeue(&link->tx_list); - if (msg) { - if (sign_link) - *sign_link = link; - break; - } - } - break; - case E1INP_TS_TYPE_TRAU: - msg = msgb_alloc(TSX_ALLOC_SIZE); - if (!msg) - return NULL; - len = subchan_mux_out(&e1i_ts->trau.mux, msg->data, 40); - msgb_put(msg, 40); - break; - default: - fprintf(stderr, "unsupported E1 TS type %u\n", e1i_ts->type); - return NULL; - } - return msg; -} - -/* called by driver in case some kind of link state event */ -int e1inp_event(struct e1inp_ts *ts, int evt, u_int8_t tei, u_int8_t sapi) -{ - struct e1inp_sign_link *link; - - link = e1inp_lookup_sign_link(ts, tei, sapi); - if (!link) - return -EINVAL; - - /* FIXME: report further upwards */ - input_event(evt, link->type, link->trx); - return 0; -} - -/* register a driver with the E1 core */ -int e1inp_driver_register(struct e1inp_driver *drv) -{ - llist_add_tail(&drv->list, &e1inp_driver_list); - return 0; -} - -/* register a line with the E1 core */ -int e1inp_line_register(struct e1inp_line *line) -{ - int i; - - for (i = 0; i < NUM_E1_TS; i++) { - line->ts[i].num = i+1; - line->ts[i].line = line; - } - - llist_add_tail(&line->list, &e1inp_line_list); - - return 0; -} diff --git a/src/gsm_04_08.c b/src/gsm_04_08.c deleted file mode 100644 index 274d3b6bf..000000000 --- a/src/gsm_04_08.c +++ /dev/null @@ -1,1723 +0,0 @@ -/* 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> - * - * 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 <time.h> -#include <netinet/in.h> - -#include <openbsc/db.h> -#include <openbsc/msgb.h> -#include <openbsc/tlv.h> -#include <openbsc/debug.h> -#include <openbsc/gsm_data.h> -#include <openbsc/gsm_subscriber.h> -#include <openbsc/gsm_04_11.h> -#include <openbsc/gsm_04_08.h> -#include <openbsc/abis_rsl.h> -#include <openbsc/chan_alloc.h> -#include <openbsc/paging.h> -#include <openbsc/signal.h> -#include <openbsc/trau_frame.h> -#include <openbsc/trau_mux.h> - -#define GSM48_ALLOC_SIZE 1024 -#define GSM48_ALLOC_HEADROOM 128 - -static const struct tlv_definition rsl_att_tlvdef = { - .def = { - [GSM48_IE_MOBILE_ID] = { TLV_TYPE_TLV }, - [GSM48_IE_NAME_LONG] = { TLV_TYPE_TLV }, - [GSM48_IE_NAME_SHORT] = { TLV_TYPE_TLV }, - [GSM48_IE_UTC] = { TLV_TYPE_TV }, - [GSM48_IE_NET_TIME_TZ] = { TLV_TYPE_FIXED, 7 }, - [GSM48_IE_LSA_IDENT] = { TLV_TYPE_TLV }, - - [GSM48_IE_BEARER_CAP] = { TLV_TYPE_TLV }, - [GSM48_IE_CAUSE] = { TLV_TYPE_TLV }, - [GSM48_IE_CC_CAP] = { TLV_TYPE_TLV }, - [GSM48_IE_ALERT] = { TLV_TYPE_TLV }, - [GSM48_IE_FACILITY] = { TLV_TYPE_TLV }, - [GSM48_IE_PROGR_IND] = { TLV_TYPE_TLV }, - [GSM48_IE_AUX_STATUS] = { TLV_TYPE_TLV }, - [GSM48_IE_KPD_FACILITY] = { TLV_TYPE_TV }, - [GSM48_IE_SIGNAL] = { TLV_TYPE_TV }, - [GSM48_IE_CONN_NUM] = { TLV_TYPE_TLV }, - [GSM48_IE_CONN_SUBADDR] = { TLV_TYPE_TLV }, - [GSM48_IE_CALLING_BCD] = { TLV_TYPE_TLV }, - [GSM48_IE_CALLING_SUB] = { TLV_TYPE_TLV }, - [GSM48_IE_CALLED_BCD] = { TLV_TYPE_TLV }, - [GSM48_IE_CALLED_SUB] = { TLV_TYPE_TLV }, - [GSM48_IE_REDIR_BCD] = { TLV_TYPE_TLV }, - [GSM48_IE_REDIR_SUB] = { TLV_TYPE_TLV }, - [GSM48_IE_LOWL_COMPAT] = { TLV_TYPE_TLV }, - [GSM48_IE_HIGHL_COMPAT] = { TLV_TYPE_TLV }, - [GSM48_IE_USER_USER] = { TLV_TYPE_TLV }, - [GSM48_IE_SS_VERS] = { TLV_TYPE_TLV }, - [GSM48_IE_MORE_DATA] = { TLV_TYPE_T }, - [GSM48_IE_CLIR_SUPP] = { TLV_TYPE_T }, - [GSM48_IE_CLIR_INVOC] = { TLV_TYPE_T }, - [GSM48_IE_REV_C_SETUP] = { TLV_TYPE_T }, - /* FIXME: more elements */ - }, -}; - -static const char *rr_cause_names[] = { - [GSM48_RR_CAUSE_NORMAL] = "Normal event", - [GSM48_RR_CAUSE_ABNORMAL_UNSPEC] = "Abnormal release, unspecified", - [GSM48_RR_CAUSE_ABNORMAL_UNACCT] = "Abnormal release, channel unacceptable", - [GSM48_RR_CAUSE_ABNORMAL_TIMER] = "Abnormal release, timer expired", - [GSM48_RR_CAUSE_ABNORMAL_NOACT] = "Abnormal release, no activity on radio path", - [GSM48_RR_CAUSE_PREMPTIVE_REL] = "Preemptive release", - [GSM48_RR_CAUSE_HNDOVER_IMP] = "Handover impossible, timing advance out of range", - [GSM48_RR_CAUSE_CHAN_MODE_UNACCT] = "Channel mode unacceptable", - [GSM48_RR_CAUSE_FREQ_NOT_IMPL] = "Frequency not implemented", - [GSM48_RR_CAUSE_CALL_CLEARED] = "Call already cleared", - [GSM48_RR_CAUSE_SEMANT_INCORR] = "Semantically incorrect message", - [GSM48_RR_CAUSE_INVALID_MAND_INF] = "Invalid mandatory information", - [GSM48_RR_CAUSE_MSG_TYPE_N] = "Message type non-existant or not implemented", - [GSM48_RR_CAUSE_MSG_TYPE_N_COMPAT] = "Message type not compatible with protocol state", - [GSM48_RR_CAUSE_COND_IE_ERROR] = "Conditional IE error", - [GSM48_RR_CAUSE_NO_CELL_ALLOC_A] = "No cell allocation available", - [GSM48_RR_CAUSE_PROT_ERROR_UNSPC] = "Protocol error unspecified", -}; - -static char strbuf[64]; - -static const char *rr_cause_name(u_int8_t cause) -{ - if (cause < ARRAY_SIZE(rr_cause_names) && - rr_cause_names[cause]) - return rr_cause_names[cause]; - - snprintf(strbuf, sizeof(strbuf), "0x%02x", cause); - return strbuf; -} - -static void parse_meas_rep(struct gsm_meas_rep *rep, const u_int8_t *data, - int len) -{ - memset(rep, 0, sizeof(*rep)); - - if (data[0] & 0x80) - rep->flags |= MEAS_REP_F_BA1; - if (data[0] & 0x40) - rep->flags |= MEAS_REP_F_DTX; - if (data[1] & 0x40) - rep->flags |= MEAS_REP_F_VALID; - - rep->rxlev_full = data[0] & 0x3f; - rep->rxlev_sub = data[1] & 0x3f; - rep->rxqual_full = (data[3] >> 4) & 0x7; - rep->rxqual_sub = (data[3] >> 1) & 0x7; - rep->num_cell = data[4] >> 6 | ((data[3] & 0x01) << 2); - if (rep->num_cell < 1) - return; - - /* an encoding nightmare in perfection */ - - rep->cell[0].rxlev = data[4] & 0x3f; - rep->cell[0].bcch_freq = data[5] >> 2; - rep->cell[0].bsic = ((data[5] & 0x03) << 3) | (data[6] >> 5); - if (rep->num_cell < 2) - return; - - rep->cell[1].rxlev = ((data[6] & 0x1f) << 1) | (data[7] >> 7); - rep->cell[1].bcch_freq = (data[7] >> 2) & 0x1f; - rep->cell[1].bsic = ((data[7] & 0x03) << 4) | (data[8] >> 4); - if (rep->num_cell < 3) - return; - - rep->cell[2].rxlev = ((data[8] & 0x0f) << 2) | (data[9] >> 6); - rep->cell[2].bcch_freq = (data[9] >> 1) & 0x1f; - rep->cell[2].bsic = ((data[9] & 0x01) << 6) | (data[10] >> 3); - if (rep->num_cell < 4) - return; - - rep->cell[3].rxlev = ((data[10] & 0x07) << 3) | (data[11] >> 5); - rep->cell[3].bcch_freq = data[11] & 0x1f; - rep->cell[3].bsic = data[12] >> 2; - if (rep->num_cell < 5) - return; - - rep->cell[4].rxlev = ((data[12] & 0x03) << 4) | (data[13] >> 4); - rep->cell[4].bcch_freq = ((data[13] & 0xf) << 1) | (data[14] >> 7); - rep->cell[4].bsic = (data[14] >> 1) & 0x3f; - if (rep->num_cell < 6) - return; - - rep->cell[5].rxlev = ((data[14] & 0x01) << 5) | (data[15] >> 3); - rep->cell[5].bcch_freq = ((data[15] & 0x07) << 2) | (data[16] >> 6); - rep->cell[5].bsic = data[16] & 0x3f; -} - -int gsm0408_loc_upd_acc(struct gsm_lchan *lchan, u_int32_t tmsi); -static int gsm48_tx_simple(struct gsm_lchan *lchan, - u_int8_t pdisc, u_int8_t msg_type); -static void schedule_reject(struct gsm_lchan *lchan); - -struct gsm_lai { - u_int16_t mcc; - u_int16_t mnc; - u_int16_t lac; -}; - -static int authorize_everonye = 0; -void gsm0408_allow_everyone(int everyone) -{ - printf("Allowing everyone?\n"); - authorize_everonye = everyone; -} - -static int reject_cause = 0; -void gsm0408_set_reject_cause(int cause) -{ - reject_cause = cause; -} - -static int authorize_subscriber(struct gsm_loc_updating_operation *loc, - struct gsm_subscriber *subscriber) -{ - if (!subscriber) - return 0; - - /* - * Do not send accept yet as more information should arrive. Some - * phones will not send us the information and we will have to check - * what we want to do with that. - */ - if (loc && (loc->waiting_for_imsi || loc->waiting_for_imei)) - return 0; - - if (authorize_everonye) - return 1; - - return subscriber->authorized; -} - -static void release_loc_updating_req(struct gsm_lchan *lchan) -{ - if (!lchan->loc_operation) - return; - - bsc_del_timer(&lchan->loc_operation->updating_timer); - free(lchan->loc_operation); - lchan->loc_operation = 0; - put_lchan(lchan); -} - -static void allocate_loc_updating_req(struct gsm_lchan *lchan) -{ - use_lchan(lchan); - release_loc_updating_req(lchan); - - lchan->loc_operation = (struct gsm_loc_updating_operation *) - malloc(sizeof(*lchan->loc_operation)); - memset(lchan->loc_operation, 0, sizeof(*lchan->loc_operation)); -} - -static int gsm0408_authorize(struct gsm_lchan *lchan, struct msgb *msg) -{ - u_int32_t tmsi; - - if (authorize_subscriber(lchan->loc_operation, lchan->subscr)) { - db_subscriber_alloc_tmsi(lchan->subscr); - subscr_update(lchan->subscr, msg->trx->bts, GSM_SUBSCRIBER_UPDATE_ATTACHED); - tmsi = strtoul(lchan->subscr->tmsi, NULL, 10); - release_loc_updating_req(lchan); - return gsm0408_loc_upd_acc(msg->lchan, tmsi); - } - - return 0; -} - -static int gsm0408_handle_lchan_signal(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data) -{ - if (subsys != SS_LCHAN || signal != S_LCHAN_UNEXPECTED_RELEASE) - return 0; - - /* - * Cancel any outstanding location updating request - * operation taking place on the lchan. - */ - struct gsm_lchan *lchan = (struct gsm_lchan *)handler_data; - release_loc_updating_req(lchan); - - return 0; -} - -/* - * This will be ran by the linker when loading the DSO. We use it to - * do system initialization, e.g. registration of signal handlers. - */ -static __attribute__((constructor)) void on_dso_load_0408(void) -{ - register_signal_handler(SS_LCHAN, gsm0408_handle_lchan_signal, NULL); -} - -static void to_bcd(u_int8_t *bcd, u_int16_t val) -{ - bcd[2] = val % 10; - val = val / 10; - bcd[1] = val % 10; - val = val / 10; - bcd[0] = val % 10; - val = val / 10; -} - -void gsm0408_generate_lai(struct gsm48_loc_area_id *lai48, u_int16_t mcc, - u_int16_t mnc, u_int16_t lac) -{ - u_int8_t bcd[3]; - - to_bcd(bcd, mcc); - lai48->digits[0] = bcd[0] | (bcd[1] << 4); - lai48->digits[1] = bcd[2]; - - to_bcd(bcd, mnc); - /* FIXME: do we need three-digit MNC? See Table 10.5.3 */ -#if 0 - lai48->digits[1] |= bcd[2] << 4; - lai48->digits[2] = bcd[0] | (bcd[1] << 4); -#else - lai48->digits[1] |= 0xf << 4; - lai48->digits[2] = bcd[1] | (bcd[2] << 4); -#endif - - lai48->lac = htons(lac); -} - -#define TMSI_LEN 5 -#define MID_TMSI_LEN (TMSI_LEN + 2) - -int generate_mid_from_tmsi(u_int8_t *buf, u_int32_t tmsi) -{ - u_int32_t *tptr = (u_int32_t *) &buf[3]; - - buf[0] = GSM48_IE_MOBILE_ID; - buf[1] = TMSI_LEN; - buf[2] = 0xf0 | GSM_MI_TYPE_TMSI; - *tptr = htonl(tmsi); - - return 7; -} - -static const char bcd_num_digits[] = { - '0', '1', '2', '3', '4', '5', '6', '7', - '8', '9', '*', '#', 'a', 'b', 'c', '\0' -}; - -/* decode a 'called party BCD number' as in 10.5.4.7 */ -u_int8_t decode_bcd_number(char *output, int output_len, const u_int8_t *bcd_lv) -{ - u_int8_t in_len = bcd_lv[0]; - int i; - - if (in_len < 1) - return 0; - - for (i = 2; i <= in_len; i++) { - /* lower nibble */ - output_len--; - if (output_len <= 1) - break; - *output++ = bcd_num_digits[bcd_lv[i] & 0xf]; - - /* higher nibble */ - output_len--; - if (output_len <= 1) - break; - *output++ = bcd_num_digits[bcd_lv[i] >> 4]; - } - if (output_len >= 1) - *output++ = '\0'; - - /* return number type / calling plan */ - return bcd_lv[1] & 0x3f; -} - -/* convert a single ASCII character to call-control BCD */ -static int asc_to_bcd(const char asc) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(bcd_num_digits); i++) { - if (bcd_num_digits[i] == asc) - return i; - } - return -EINVAL; -} - -/* convert a ASCII phone number to 'called party BCD number' */ -int encode_bcd_number(u_int8_t *bcd_lv, u_int8_t max_len, - u_int8_t type, const char *input) -{ - int in_len = strlen(input); - int i; - u_int8_t *bcd_cur = bcd_lv + 2; - - if (in_len/2 + 1 > max_len) - return -EIO; - - /* two digits per byte, plus type byte */ - bcd_lv[0] = in_len/2 + 1; - if (in_len % 2) - bcd_lv[0]++; - - /* if the caller wants to create a valid 'calling party BCD - * number', then the extension bit MUST NOT be set. For the - * 'called party BCD number' it MUST be set. *sigh */ - bcd_lv[1] = type; - - for (i = 0; i < in_len; i++) { - int rc = asc_to_bcd(input[i]); - if (rc < 0) - return rc; - if (i % 2 == 0) - *bcd_cur = rc; - else - *bcd_cur++ |= (rc << 4); - } - /* append padding nibble in case of odd length */ - if (i % 2) - *bcd_cur++ |= 0xf0; - - /* return how many bytes we used */ - return (bcd_cur - bcd_lv); -} - -struct msgb *gsm48_msgb_alloc(void) -{ - return msgb_alloc_headroom(GSM48_ALLOC_SIZE, GSM48_ALLOC_HEADROOM); -} - -int gsm48_sendmsg(struct msgb *msg) -{ - struct gsm48_hdr *gh = (struct gsm48_hdr *) msg->data; - - if (msg->lchan) { - msg->trx = msg->lchan->ts->trx; - - if ((gh->proto_discr & GSM48_PDISC_MASK) == GSM48_PDISC_CC) { - /* Send a 04.08 call control message, add transaction - * ID and TI flag */ - gh->proto_discr |= msg->lchan->call.transaction_id; - - /* GSM 04.07 Section 11.2.3.1.3 */ - switch (msg->lchan->call.type) { - case GSM_CT_MO: - gh->proto_discr |= 0x80; - break; - case GSM_CT_MT: - break; - case GSM_CT_NONE: - break; - } - } - } - - msg->l3h = msg->data; - - return rsl_data_request(msg, 0); -} - - -/* Chapter 9.2.14 : Send LOCATION UPDATING REJECT */ -int gsm0408_loc_upd_rej(struct gsm_lchan *lchan, u_int8_t cause) -{ - struct msgb *msg = gsm48_msgb_alloc(); - struct gsm48_hdr *gh; - - msg->lchan = lchan; - - gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1); - gh->proto_discr = GSM48_PDISC_MM; - gh->msg_type = GSM48_MT_MM_LOC_UPD_REJECT; - gh->data[0] = cause; - - DEBUGP(DMM, "-> LOCATION UPDATING REJECT on channel: %d\n", lchan->nr); - - return gsm48_sendmsg(msg); -} - -/* Chapter 9.2.13 : Send LOCATION UPDATE ACCEPT */ -int gsm0408_loc_upd_acc(struct gsm_lchan *lchan, u_int32_t tmsi) -{ - struct gsm_bts *bts = lchan->ts->trx->bts; - struct msgb *msg = gsm48_msgb_alloc(); - struct gsm48_hdr *gh; - struct gsm48_loc_area_id *lai; - u_int8_t *mid; - int ret; - - msg->lchan = lchan; - - gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - gh->proto_discr = GSM48_PDISC_MM; - gh->msg_type = GSM48_MT_MM_LOC_UPD_ACCEPT; - - lai = (struct gsm48_loc_area_id *) msgb_put(msg, sizeof(*lai)); - gsm0408_generate_lai(lai, bts->network->country_code, - bts->network->network_code, bts->location_area_code); - - mid = msgb_put(msg, MID_TMSI_LEN); - generate_mid_from_tmsi(mid, tmsi); - - DEBUGP(DMM, "-> LOCATION UPDATE ACCEPT\n"); - - ret = gsm48_sendmsg(msg); - - ret = gsm48_tx_mm_info(lchan); - - return ret; -} - -static char bcd2char(u_int8_t bcd) -{ - if (bcd < 0xa) - return '0' + bcd; - else - return 'A' + (bcd - 0xa); -} - -/* Convert Mobile Identity (10.5.1.4) to string */ -static int mi_to_string(char *string, int str_len, u_int8_t *mi, int mi_len) -{ - int i; - u_int8_t mi_type; - char *str_cur = string; - u_int32_t tmsi; - - mi_type = mi[0] & GSM_MI_TYPE_MASK; - - switch (mi_type) { - case GSM_MI_TYPE_NONE: - break; - case GSM_MI_TYPE_TMSI: - /* Table 10.5.4.3, reverse generate_mid_from_tmsi */ - if (mi_len == TMSI_LEN && mi[0] == (0xf0 | GSM_MI_TYPE_TMSI)) { - memcpy(&tmsi, &mi[1], 4); - tmsi = ntohl(tmsi); - return snprintf(string, str_len, "%u", tmsi); - } - break; - case GSM_MI_TYPE_IMSI: - case GSM_MI_TYPE_IMEI: - case GSM_MI_TYPE_IMEISV: - *str_cur++ = bcd2char(mi[0] >> 4); - - for (i = 1; i < mi_len; i++) { - if (str_cur + 2 >= string + str_len) - return str_cur - string; - *str_cur++ = bcd2char(mi[i] & 0xf); - /* skip last nibble in last input byte when GSM_EVEN */ - if( (i != mi_len-1) || (mi[0] & GSM_MI_ODD)) - *str_cur++ = bcd2char(mi[i] >> 4); - } - break; - default: - break; - } - *str_cur++ = '\0'; - - return str_cur - string; -} - -/* Transmit Chapter 9.2.10 Identity Request */ -static int mm_tx_identity_req(struct gsm_lchan *lchan, u_int8_t id_type) -{ - struct msgb *msg = gsm48_msgb_alloc(); - struct gsm48_hdr *gh; - - msg->lchan = lchan; - - gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1); - gh->proto_discr = GSM48_PDISC_MM; - gh->msg_type = GSM48_MT_MM_ID_REQ; - gh->data[0] = id_type; - - return gsm48_sendmsg(msg); -} - -#define MI_SIZE 32 - -/* Parse Chapter 9.2.11 Identity Response */ -static int mm_rx_id_resp(struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - struct gsm_lchan *lchan = msg->lchan; - u_int8_t mi_type = gh->data[1] & GSM_MI_TYPE_MASK; - char mi_string[MI_SIZE]; - - mi_to_string(mi_string, sizeof(mi_string), &gh->data[1], gh->data[0]); - DEBUGP(DMM, "IDENTITY RESPONSE: mi_type=0x%02x MI(%s)\n", - mi_type, mi_string); - - /* - * Rogue messages could trick us but so is life - */ - put_lchan(lchan); - - switch (mi_type) { - case GSM_MI_TYPE_IMSI: - if (!lchan->subscr) - lchan->subscr = db_create_subscriber(mi_string); - if (lchan->loc_operation) - lchan->loc_operation->waiting_for_imsi = 0; - break; - case GSM_MI_TYPE_IMEI: - case GSM_MI_TYPE_IMEISV: - /* update subscribe <-> IMEI mapping */ - if (lchan->subscr) - db_subscriber_assoc_imei(lchan->subscr, mi_string); - if (lchan->loc_operation) - lchan->loc_operation->waiting_for_imei = 0; - break; - } - - /* Check if we can let the mobile station enter */ - return gsm0408_authorize(lchan, msg); -} - - -static void loc_upd_rej_cb(void *data) -{ - struct gsm_lchan *lchan = data; - - release_loc_updating_req(lchan); - gsm0408_loc_upd_rej(lchan, reject_cause); - lchan_auto_release(lchan); -} - -static void schedule_reject(struct gsm_lchan *lchan) -{ - lchan->loc_operation->updating_timer.cb = loc_upd_rej_cb; - lchan->loc_operation->updating_timer.data = lchan; - bsc_schedule_timer(&lchan->loc_operation->updating_timer, 5, 0); -} - -static const char *lupd_name(u_int8_t type) -{ - switch (type) { - case GSM48_LUPD_NORMAL: - return "NORMAL"; - case GSM48_LUPD_PERIODIC: - return "PEROIDOC"; - case GSM48_LUPD_IMSI_ATT: - return "IMSI ATTACH"; - default: - return "UNKNOWN"; - } -} - -#define MI_SIZE 32 -/* Chapter 9.2.15: Receive Location Updating Request */ -static int mm_rx_loc_upd_req(struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - struct gsm48_loc_upd_req *lu; - struct gsm_subscriber *subscr; - struct gsm_lchan *lchan = msg->lchan; - u_int8_t mi_type; - char mi_string[MI_SIZE]; - int rc; - - lu = (struct gsm48_loc_upd_req *) gh->data; - - mi_type = lu->mi[0] & GSM_MI_TYPE_MASK; - - mi_to_string(mi_string, sizeof(mi_string), lu->mi, lu->mi_len); - - DEBUGP(DMM, "LUPDREQ: mi_type=0x%02x MI(%s) type=%s\n", mi_type, mi_string, - lupd_name(lu->type)); - - /* - * Pseudo Spoof detection: Just drop a second/concurrent - * location updating request. - */ - if (lchan->loc_operation) { - DEBUGP(DMM, "LUPDREQ: ignoring request due an existing one: %p.\n", - lchan->loc_operation); - gsm0408_loc_upd_rej(lchan, GSM48_REJECT_PROTOCOL_ERROR); - return 0; - } - - allocate_loc_updating_req(lchan); - - switch (mi_type) { - case GSM_MI_TYPE_IMSI: - /* we always want the IMEI, too */ - use_lchan(lchan); - rc = mm_tx_identity_req(lchan, GSM_MI_TYPE_IMEI); - lchan->loc_operation->waiting_for_imei = 1; - - /* look up subscriber based on IMSI */ - subscr = db_create_subscriber(mi_string); - break; - case GSM_MI_TYPE_TMSI: - /* we always want the IMEI, too */ - use_lchan(lchan); - rc = mm_tx_identity_req(lchan, GSM_MI_TYPE_IMEI); - lchan->loc_operation->waiting_for_imei = 1; - - /* look up the subscriber based on TMSI, request IMSI if it fails */ - subscr = subscr_get_by_tmsi(mi_string); - if (!subscr) { - /* send IDENTITY REQUEST message to get IMSI */ - use_lchan(lchan); - rc = mm_tx_identity_req(lchan, GSM_MI_TYPE_IMSI); - lchan->loc_operation->waiting_for_imsi = 1; - } - break; - case GSM_MI_TYPE_IMEI: - case GSM_MI_TYPE_IMEISV: - /* no sim card... FIXME: what to do ? */ - DEBUGP(DMM, "unimplemented mobile identity type\n"); - break; - default: - DEBUGP(DMM, "unknown mobile identity type\n"); - break; - } - - lchan->subscr = subscr; - - /* - * Schedule the reject timer and check if we can let the - * subscriber into our network immediately or if we need to wait - * for identity responses. - */ - schedule_reject(lchan); - return gsm0408_authorize(lchan, msg); -} - -/* 9.1.5 Channel mode modify */ -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); -} - -/* Section 9.2.15a */ -int gsm48_tx_mm_info(struct gsm_lchan *lchan) -{ - struct msgb *msg = gsm48_msgb_alloc(); - struct gsm48_hdr *gh; - struct gsm_network *net = lchan->ts->trx->bts->network; - u_int8_t *ptr8; - u_int16_t *ptr16; - int name_len; - int i; - - msg->lchan = lchan; - - gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - gh->proto_discr = GSM48_PDISC_MM; - gh->msg_type = GSM48_MT_MM_INFO; - - if (net->name_long) { - name_len = strlen(net->name_long); - /* 10.5.3.5a */ - ptr8 = msgb_put(msg, 3); - ptr8[0] = GSM48_IE_NAME_LONG; - ptr8[1] = name_len*2 +1; - ptr8[2] = 0x90; /* UCS2, no spare bits, no CI */ - - ptr16 = (u_int16_t *) msgb_put(msg, name_len*2); - for (i = 0; i < name_len; i++) - ptr16[i] = htons(net->name_long[i]); - - /* FIXME: Use Cell Broadcast, not UCS-2, since - * UCS-2 is only supported by later revisions of the spec */ - } - - if (net->name_short) { - name_len = strlen(net->name_short); - /* 10.5.3.5a */ - ptr8 = (u_int8_t *) msgb_put(msg, 3); - ptr8[0] = GSM48_IE_NAME_LONG; - ptr8[1] = name_len*2 + 1; - ptr8[2] = 0x90; /* UCS2, no spare bits, no CI */ - - ptr16 = (u_int16_t *) msgb_put(msg, name_len*2); - for (i = 0; i < name_len; i++) - ptr16[i] = htons(net->name_short[i]); - } - -#if 0 - /* move back to the top */ - time_t cur_t; - struct tm* cur_time; - int tz15min; - /* Section 10.5.3.9 */ - cur_t = time(NULL); - cur_time = gmtime(cur_t); - ptr8 = msgb_put(msg, 8); - ptr8[0] = GSM48_IE_NET_TIME_TZ; - ptr8[1] = to_bcd8(cur_time->tm_year % 100); - ptr8[2] = to_bcd8(cur_time->tm_mon); - ptr8[3] = to_bcd8(cur_time->tm_mday); - ptr8[4] = to_bcd8(cur_time->tm_hour); - ptr8[5] = to_bcd8(cur_time->tm_min); - ptr8[6] = to_bcd8(cur_time->tm_sec); - /* 02.42: coded as BCD encoded signed value in units of 15 minutes */ - tz15min = (cur_time->tm_gmtoff)/(60*15); - ptr8[6] = to_bcd8(tz15min); - if (tz15min < 0) - ptr8[6] |= 0x80; -#endif - - return gsm48_sendmsg(msg); -} - -static int gsm48_tx_mm_serv_ack(struct gsm_lchan *lchan) -{ - DEBUGP(DMM, "-> CM SERVICE ACK\n"); - return gsm48_tx_simple(lchan, GSM48_PDISC_MM, GSM48_MT_MM_CM_SERV_ACC); -} - -/* 9.2.6 CM service reject */ -static int gsm48_tx_mm_serv_rej(struct gsm_lchan *lchan, - enum gsm48_reject_value value) -{ - struct msgb *msg = gsm48_msgb_alloc(); - struct gsm48_hdr *gh; - - gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1); - - msg->lchan = lchan; - use_lchan(lchan); - - gh->proto_discr = GSM48_PDISC_MM; - gh->msg_type = GSM48_MT_MM_CM_SERV_REJ; - gh->data[0] = value; - DEBUGP(DMM, "-> CM SERVICE Reject cause: %d\n", value); - - return gsm48_sendmsg(msg); -} - - -/* - * Handle CM Service Requests - * a) Verify that the packet is long enough to contain the information - * we require otherwsie reject with INCORRECT_MESSAGE - * b) Try to parse the TMSI. If we do not have one reject - * c) Check that we know the subscriber with the TMSI otherwise reject - * with a HLR cause - * d) Set the subscriber on the gsm_lchan and accept - */ -static int gsm48_rx_mm_serv_req(struct msgb *msg) -{ - u_int8_t mi_type; - char mi_string[MI_SIZE]; - - struct gsm_subscriber *subscr; - struct gsm48_hdr *gh = msgb_l3(msg); - struct gsm48_service_request *req = - (struct gsm48_service_request *)gh->data; - /* unfortunately in Phase1 the classmar2 length is variable */ - u_int8_t classmark2_len = gh->data[1]; - u_int8_t *classmark2 = gh->data+2; - u_int8_t mi_len = *(classmark2 + classmark2_len); - u_int8_t *mi = (classmark2 + classmark2_len + 1); - - DEBUGP(DMM, "<- CM SERVICE REQUEST "); - if (msg->data_len < sizeof(struct gsm48_service_request*)) { - DEBUGPC(DMM, "wrong sized message\n"); - return gsm48_tx_mm_serv_rej(msg->lchan, - GSM48_REJECT_INCORRECT_MESSAGE); - } - - if (msg->data_len < req->mi_len + 6) { - DEBUGPC(DMM, "does not fit in packet\n"); - return gsm48_tx_mm_serv_rej(msg->lchan, - GSM48_REJECT_INCORRECT_MESSAGE); - } - - mi_type = mi[0] & GSM_MI_TYPE_MASK; - if (mi_type != GSM_MI_TYPE_TMSI) { - DEBUGPC(DMM, "mi_type is not TMSI: %d\n", mi_type); - return gsm48_tx_mm_serv_rej(msg->lchan, - GSM48_REJECT_INCORRECT_MESSAGE); - } - - mi_to_string(mi_string, sizeof(mi_string), mi, mi_len); - DEBUGPC(DMM, "serv_type=0x%02x mi_type=0x%02x M(%s)\n", - req->cm_service_type, mi_type, mi_string); - - subscr = subscr_get_by_tmsi(mi_string); - - /* FIXME: if we don't know the TMSI, inquire abit IMSI and allocate new TMSI */ - if (!subscr) - return gsm48_tx_mm_serv_rej(msg->lchan, - GSM48_REJECT_IMSI_UNKNOWN_IN_HLR); - - if (!msg->lchan->subscr) - msg->lchan->subscr = subscr; - else if (msg->lchan->subscr != subscr) { - DEBUGP(DMM, "<- CM Channel already owned by someone else?\n"); - subscr_put(subscr); - } - - subscr->classmark2_len = classmark2_len; - memcpy(subscr->classmark2, classmark2, classmark2_len); - - return gsm48_tx_mm_serv_ack(msg->lchan); -} - -static int gsm48_rx_mm_imsi_detach_ind(struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - struct gsm48_imsi_detach_ind *idi = - (struct gsm48_imsi_detach_ind *) gh->data; - u_int8_t mi_type = idi->mi[0] & GSM_MI_TYPE_MASK; - char mi_string[MI_SIZE]; - struct gsm_subscriber *subscr; - - mi_to_string(mi_string, sizeof(mi_string), idi->mi, idi->mi_len); - DEBUGP(DMM, "IMSI DETACH INDICATION: mi_type=0x%02x MI(%s): ", - mi_type, mi_string); - - switch (mi_type) { - case GSM_MI_TYPE_TMSI: - subscr = subscr_get_by_tmsi(mi_string); - break; - case GSM_MI_TYPE_IMSI: - subscr = subscr_get_by_imsi(mi_string); - break; - case GSM_MI_TYPE_IMEI: - case GSM_MI_TYPE_IMEISV: - /* no sim card... FIXME: what to do ? */ - DEBUGPC(DMM, "unimplemented mobile identity type\n"); - break; - default: - DEBUGPC(DMM, "unknown mobile identity type\n"); - break; - } - - if (subscr) { - subscr_update(subscr, msg->trx->bts, - GSM_SUBSCRIBER_UPDATE_DETACHED); - DEBUGP(DMM, "Subscriber: %s\n", - subscr->name ? subscr->name : subscr->imsi); - subscr_put(subscr); - } else - DEBUGP(DMM, "Unknown Subscriber ?!?\n"); - - put_lchan(msg->lchan); - - return 0; -} - -static int gsm48_rx_mm_status(struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - - DEBUGP(DMM, "MM STATUS (reject cause 0x%02x)\n", gh->data[0]); - - return 0; -} - -/* Receive a GSM 04.08 Mobility Management (MM) message */ -static int gsm0408_rcv_mm(struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - int rc; - - switch (gh->msg_type & 0xbf) { - case GSM48_MT_MM_LOC_UPD_REQUEST: - DEBUGP(DMM, "LOCATION UPDATING REQUEST\n"); - rc = mm_rx_loc_upd_req(msg); - break; - case GSM48_MT_MM_ID_RESP: - rc = mm_rx_id_resp(msg); - break; - case GSM48_MT_MM_CM_SERV_REQ: - rc = gsm48_rx_mm_serv_req(msg); - break; - case GSM48_MT_MM_STATUS: - rc = gsm48_rx_mm_status(msg); - break; - case GSM48_MT_MM_TMSI_REALL_COMPL: - DEBUGP(DMM, "TMSI Reallocation Completed. Subscriber: %s\n", - msg->lchan->subscr ? - msg->lchan->subscr->imsi : - "unknown subscriber"); - break; - case GSM48_MT_MM_IMSI_DETACH_IND: - rc = gsm48_rx_mm_imsi_detach_ind(msg); - break; - case GSM48_MT_MM_CM_REEST_REQ: - DEBUGP(DMM, "CM REESTABLISH REQUEST: Not implemented\n"); - break; - case GSM48_MT_MM_AUTH_RESP: - DEBUGP(DMM, "AUTHENTICATION RESPONSE: Not implemented\n"); - break; - default: - fprintf(stderr, "Unknown GSM 04.08 MM msg type 0x%02x\n", - gh->msg_type); - break; - } - - return rc; -} - -/* Receive a PAGING RESPONSE message from the MS */ -static int gsm48_rr_rx_pag_resp(struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - u_int8_t *classmark2_lv = gh->data + 1; - u_int8_t *mi_lv = gh->data + 2 + *classmark2_lv; - u_int8_t mi_type = mi_lv[1] & GSM_MI_TYPE_MASK; - char mi_string[MI_SIZE]; - struct gsm_subscriber *subscr; - struct paging_signal_data sig_data; - int rc = 0; - - mi_to_string(mi_string, sizeof(mi_string), mi_lv+1, *mi_lv); - DEBUGP(DRR, "PAGING RESPONSE: mi_type=0x%02x MI(%s)\n", - mi_type, mi_string); - switch (mi_type) { - case GSM_MI_TYPE_TMSI: - subscr = subscr_get_by_tmsi(mi_string); - break; - case GSM_MI_TYPE_IMSI: - subscr = subscr_get_by_imsi(mi_string); - break; - } - - if (!subscr) { - DEBUGP(DRR, "<- Can't find any subscriber for this ID\n"); - /* FIXME: request id? close channel? */ - return -EINVAL; - } - DEBUGP(DRR, "<- Channel was requested by %s\n", - subscr->name ? subscr->name : subscr->imsi); - - subscr->classmark2_len = *classmark2_lv; - memcpy(subscr->classmark2, classmark2_lv+1, *classmark2_lv); - - if (!msg->lchan->subscr) { - msg->lchan->subscr = subscr; - } else if (msg->lchan->subscr != subscr) { - DEBUGP(DRR, "<- Channel already owned by someone else?\n"); - subscr_put(subscr); - return -EINVAL; - } else { - DEBUGP(DRR, "<- Channel already owned by us\n"); - subscr_put(subscr); - subscr = msg->lchan->subscr; - } - - sig_data.subscr = subscr; - sig_data.bts = msg->lchan->ts->trx->bts; - sig_data.lchan = msg->lchan; - - dispatch_signal(SS_PAGING, S_PAGING_COMPLETED, &sig_data); - paging_request_stop(msg->trx->bts, subscr, msg->lchan); - - /* FIXME: somehow signal the completion of the PAGING to - * the entity that requested the paging */ - - return rc; -} - -static int gsm48_rx_rr_classmark(struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - struct gsm_subscriber *subscr = msg->lchan->subscr; - unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); - u_int8_t cm2_len, cm3_len = 0; - u_int8_t *cm2, *cm3 = NULL; - - DEBUGP(DRR, "CLASSMARK CHANGE "); - - /* classmark 2 */ - cm2_len = gh->data[0]; - cm2 = &gh->data[1]; - DEBUGPC(DRR, "CM2(len=%u) ", cm2_len); - - if (payload_len > cm2_len + 1) { - /* we must have a classmark3 */ - if (gh->data[cm2_len+1] != 0x20) { - DEBUGPC(DRR, "ERR CM3 TAG\n"); - return -EINVAL; - } - if (cm2_len > 3) { - DEBUGPC(DRR, "CM2 too long!\n"); - return -EINVAL; - } - - cm3_len = gh->data[cm2_len+2]; - cm3 = &gh->data[cm2_len+3]; - if (cm3_len > 14) { - DEBUGPC(DRR, "CM3 len %u too long!\n", cm3_len); - return -EINVAL; - } - DEBUGPC(DRR, "CM3(len=%u)\n", cm3_len); - } - if (subscr) { - subscr->classmark2_len = cm2_len; - memcpy(subscr->classmark2, cm2, cm2_len); - if (cm3) { - subscr->classmark3_len = cm3_len; - memcpy(subscr->classmark3, cm3, cm3_len); - } - } - - /* FIXME: store the classmark2/3 values with the equipment register */ - - return 0; -} - -static int gsm48_rx_rr_status(struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - - DEBUGP(DRR, "STATUS rr_cause = %s\n", - rr_cause_name(gh->data[0])); - - return 0; -} - -static int gsm48_rx_rr_meas_rep(struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); - static struct gsm_meas_rep meas_rep; - - DEBUGP(DRR, "MEASUREMENT REPORT "); - parse_meas_rep(&meas_rep, gh->data, payload_len); - if (meas_rep.flags & MEAS_REP_F_DTX) - DEBUGPC(DRR, "DTX "); - if (meas_rep.flags & MEAS_REP_F_BA1) - DEBUGPC(DRR, "BA1 "); - if (!(meas_rep.flags & MEAS_REP_F_VALID)) - DEBUGPC(DRR, "NOT VALID "); - else - DEBUGPC(DRR, "FULL(lev=%u, qual=%u) SUB(lev=%u, qual=%u) ", - meas_rep.rxlev_full, meas_rep.rxqual_full, meas_rep.rxlev_sub, - meas_rep.rxqual_sub); - - DEBUGPC(DRR, "NUM_NEIGH=%u\n", meas_rep.num_cell); - - /* FIXME: put the results somwhere */ - - return 0; -} - -/* Receive a GSM 04.08 Radio Resource (RR) message */ -static int gsm0408_rcv_rr(struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - int rc = 0; - - switch (gh->msg_type) { - case GSM48_MT_RR_CLSM_CHG: - rc = gsm48_rx_rr_classmark(msg); - break; - case GSM48_MT_RR_GPRS_SUSP_REQ: - DEBUGP(DRR, "GRPS SUSPEND REQUEST\n"); - break; - case GSM48_MT_RR_PAG_RESP: - rc = gsm48_rr_rx_pag_resp(msg); - break; - case GSM48_MT_RR_CHAN_MODE_MODIF_ACK: - DEBUGP(DRR, "CHANNEL MODE MODIFY ACK\n"); - rc = rsl_chan_mode_modify_req(msg->lchan); - break; - case GSM48_MT_RR_STATUS: - rc = gsm48_rx_rr_status(msg); - break; - case GSM48_MT_RR_MEAS_REP: - rc = gsm48_rx_rr_meas_rep(msg); - break; - default: - fprintf(stderr, "Unimplemented GSM 04.08 RR msg type 0x%02x\n", - gh->msg_type); - break; - } - - return rc; -} - -/* 7.1.7 and 9.1.7 Channel release*/ -int gsm48_send_rr_release(struct gsm_lchan *lchan) -{ - struct msgb *msg = gsm48_msgb_alloc(); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - u_int8_t *cause; - - msg->lchan = lchan; - gh->proto_discr = GSM48_PDISC_RR; - gh->msg_type = GSM48_MT_RR_CHAN_REL; - - cause = msgb_put(msg, 1); - cause[0] = GSM48_RR_CAUSE_NORMAL; - - DEBUGP(DRR, "Sending Channel Release: Chan: Number: %d Type: %d\n", - lchan->nr, lchan->type); - - return gsm48_sendmsg(msg); -} - -/* Call Control */ - -/* The entire call control code is written in accordance with Figure 7.10c - * for 'very early assignment', i.e. we allocate a TCH/F during IMMEDIATE - * ASSIGN, then first use that TCH/F for signalling and later MODE MODIFY - * it for voice */ - -static int gsm48_cc_tx_status(struct gsm_lchan *lchan) -{ - struct msgb *msg = gsm48_msgb_alloc(); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - u_int8_t *cause, *call_state; - - gh->proto_discr = GSM48_PDISC_CC; - - msg->lchan = lchan; - - gh->msg_type = GSM48_MT_CC_STATUS; - - cause = msgb_put(msg, 3); - cause[0] = 2; - cause[1] = GSM48_CAUSE_CS_GSM | GSM48_CAUSE_LOC_USER; - cause[2] = 0x80 | 30; /* response to status inquiry */ - - call_state = msgb_put(msg, 1); - call_state[0] = 0xc0 | 0x00; - - return gsm48_sendmsg(msg); -} - -static int gsm48_tx_simple(struct gsm_lchan *lchan, - u_int8_t pdisc, u_int8_t msg_type) -{ - struct msgb *msg = gsm48_msgb_alloc(); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - - msg->lchan = lchan; - - gh->proto_discr = pdisc; - gh->msg_type = msg_type; - - return gsm48_sendmsg(msg); -} - -/* call-back from paging the B-end of the connection */ -static int setup_trig_pag_evt(unsigned int hooknum, unsigned int event, - struct msgb *msg, void *_lchan, void *param) -{ - struct gsm_lchan *lchan = _lchan; - struct gsm_call *remote_call = param; - struct gsm_call *call = &lchan->call; - int rc = 0; - - if (hooknum != GSM_HOOK_RR_PAGING) - return -EINVAL; - - switch (event) { - case GSM_PAGING_SUCCEEDED: - DEBUGP(DCC, "paging succeeded!\n"); - remote_call->remote_lchan = lchan; - call->remote_lchan = remote_call->local_lchan; - /* send SETUP request to called party */ - rc = gsm48_cc_tx_setup(lchan, call->remote_lchan->subscr); - if (is_ipaccess_bts(lchan->ts->trx->bts)) - rsl_ipacc_bind(lchan); - break; - case GSM_PAGING_EXPIRED: - DEBUGP(DCC, "paging expired!\n"); - /* notify caller that we cannot reach called party */ - /* FIXME: correct cause, etc */ - rc = gsm48_tx_simple(remote_call->local_lchan, GSM48_PDISC_CC, - GSM48_MT_CC_RELEASE_COMPL); - break; - } - return rc; -} - -static int gsm48_cc_rx_status_enq(struct msgb *msg) -{ - DEBUGP(DCC, "-> STATUS ENQ\n"); - return gsm48_cc_tx_status(msg->lchan); -} - -static int gsm48_cc_rx_setup(struct msgb *msg) -{ - struct gsm_call *call = &msg->lchan->call; - struct gsm48_hdr *gh = msgb_l3(msg); - unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); - struct gsm_subscriber *called_subscr; - char called_number[(43-2)*2 + 1] = "\0"; - struct tlv_parsed tp; - u_int8_t num_type; - int ret; - - if (call->state == GSM_CSTATE_NULL || - call->state == GSM_CSTATE_RELEASE_REQ) - use_lchan(msg->lchan); - - call->type = GSM_CT_MO; - call->state = GSM_CSTATE_INITIATED; - call->local_lchan = msg->lchan; - call->transaction_id = gh->proto_discr & 0xf0; - - tlv_parse(&tp, &rsl_att_tlvdef, gh->data, payload_len, 0, 0); - if (!TLVP_PRESENT(&tp, GSM48_IE_CALLED_BCD)) - goto err; - - /* Parse the number that was dialed and lookup subscriber */ - num_type = decode_bcd_number(called_number, sizeof(called_number), - TLVP_VAL(&tp, GSM48_IE_CALLED_BCD)-1); - - DEBUGP(DCC, "A -> SETUP(tid=0x%02x,number='%s')\n", call->transaction_id, - called_number); - - called_subscr = subscr_get_by_extension(called_number); - if (!called_subscr) { - DEBUGP(DCC, "could not find subscriber, RELEASE\n"); - put_lchan(msg->lchan); - return gsm48_tx_simple(msg->lchan, GSM48_PDISC_CC, - GSM48_MT_CC_RELEASE_COMPL); - } - if (called_subscr == msg->lchan->subscr) { - DEBUGP(DCC, "subscriber calling himself ?!?\n"); - put_lchan(msg->lchan); - subscr_put(called_subscr); - return gsm48_tx_simple(msg->lchan, GSM48_PDISC_CC, - GSM48_MT_CC_RELEASE_COMPL); - } - - subscr_get(msg->lchan->subscr); - call->called_subscr = called_subscr; - - /* start paging of the receiving end of the call */ - /* FIXME: we're assuming that the receiver is at the same BTS - * than we are, which is obviously a wrong assumption in multi-BTS - * case */ - paging_request(msg->trx->bts, called_subscr, RSL_CHANNEED_TCH_F, - setup_trig_pag_evt, call); - - /* send a CALL PROCEEDING message to the MO */ - ret = gsm48_tx_simple(msg->lchan, GSM48_PDISC_CC, - GSM48_MT_CC_CALL_PROC); - - if (is_ipaccess_bts(msg->trx->bts)) - rsl_ipacc_bind(msg->lchan); - - /* change TCH/F mode to voice */ - return gsm48_tx_chan_mode_modify(msg->lchan, GSM48_CMODE_SPEECH_EFR); - -err: - /* FIXME: send some kind of RELEASE */ - return 0; -} - -static int gsm48_cc_rx_alerting(struct msgb *msg) -{ - struct gsm_call *call = &msg->lchan->call; - - DEBUGP(DCC, "A -> ALERTING\n"); - - /* forward ALERTING to other party */ - if (!call->remote_lchan) - return -EIO; - - DEBUGP(DCC, "B <- ALERTING\n"); - return gsm48_tx_simple(call->remote_lchan, GSM48_PDISC_CC, - GSM48_MT_CC_ALERTING); -} - -/* map two ipaccess RTP streams onto each other */ -static int tch_map(struct gsm_lchan *lchan, struct gsm_lchan *remote_lchan) -{ - struct gsm_bts *bts = lchan->ts->trx->bts; - struct gsm_bts *remote_bts = remote_lchan->ts->trx->bts; - struct gsm_bts_trx_ts *ts; - - DEBUGP(DCC, "Setting up TCH map between (bts=%u,trx=%u,ts=%u) and (bts=%u,trx=%u,ts=%u)\n", - bts->nr, lchan->ts->trx->nr, lchan->ts->nr, - remote_bts->nr, remote_lchan->ts->trx->nr, remote_lchan->ts->nr); - - if (bts->type != remote_bts->type) { - DEBUGP(DCC, "Cannot switch calls between different BTS types yet\n"); - return -EINVAL; - } - - switch (bts->type) { - case GSM_BTS_TYPE_NANOBTS_900: - case GSM_BTS_TYPE_NANOBTS_1800: - ts = remote_lchan->ts; - rsl_ipacc_connect(lchan, ts->abis_ip.bound_ip, ts->abis_ip.bound_port, - lchan->ts->abis_ip.attr_f8, ts->abis_ip.attr_fc); - - ts = lchan->ts; - rsl_ipacc_connect(remote_lchan, ts->abis_ip.bound_ip, ts->abis_ip.bound_port, - remote_lchan->ts->abis_ip.attr_f8, ts->abis_ip.attr_fc); - break; - case GSM_BTS_TYPE_BS11: - trau_mux_map_lchan(lchan, remote_lchan); - break; - default: - DEBUGP(DCC, "Unknown BTS type %u\n", bts->type); - break; - } - - return 0; -} - -static int gsm48_cc_rx_connect(struct msgb *msg) -{ - struct gsm_call *call = &msg->lchan->call; - int rc; - - DEBUGP(DCC, "A -> CONNECT\n"); - - rc = tch_map(msg->lchan, call->remote_lchan); - if (rc) - return -EIO; - - if (!call->remote_lchan) - return -EIO; - - DEBUGP(DCC, "A <- CONNECT ACK\n"); - /* MT+MO: need to respond with CONNECT_ACK and pass on */ - rc = gsm48_tx_simple(msg->lchan, GSM48_PDISC_CC, - GSM48_MT_CC_CONNECT_ACK); - - - /* forward CONNECT to other party */ - DEBUGP(DCC, "B <- CONNECT\n"); - return gsm48_tx_simple(call->remote_lchan, GSM48_PDISC_CC, - GSM48_MT_CC_CONNECT); -} - -static int gsm48_cc_rx_disconnect(struct msgb *msg) -{ - struct gsm_call *call = &msg->lchan->call; - int rc; - - - /* Section 5.4.3.2 */ - DEBUGP(DCC, "A -> DISCONNECT (state->RELEASE_REQ)\n"); - call->state = GSM_CSTATE_RELEASE_REQ; - /* FIXME: clear the network connection */ - DEBUGP(DCC, "A <- RELEASE\n"); - rc = gsm48_tx_simple(msg->lchan, GSM48_PDISC_CC, - GSM48_MT_CC_RELEASE); - - /* - * FIXME: This looks wrong! We should have a single - * place to do MMCC-REL-CNF/-REQ/-IND and then switch - * to the NULL state and relase the call - */ - subscr_put_channel(msg->lchan); - - /* forward DISCONNECT to other party */ - if (!call->remote_lchan) - return -EIO; - - DEBUGP(DCC, "B <- DISCONNECT\n"); - return gsm48_tx_simple(call->remote_lchan, GSM48_PDISC_CC, - GSM48_MT_CC_DISCONNECT); -} - -static const u_int8_t calling_bcd[] = { 0xb9, 0x32, 0x24 }; - -int gsm48_cc_tx_setup(struct gsm_lchan *lchan, - struct gsm_subscriber *calling_subscr) -{ - struct msgb *msg = gsm48_msgb_alloc(); - struct gsm48_hdr *gh; - struct gsm_call *call = &lchan->call; - u_int8_t bcd_lv[19]; - - gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - - call->type = GSM_CT_MT; - - call->local_lchan = msg->lchan = lchan; - use_lchan(lchan); - - gh->proto_discr = GSM48_PDISC_CC; - gh->msg_type = GSM48_MT_CC_SETUP; - - msgb_tv_put(msg, GSM48_IE_SIGNAL, GSM48_SIGNAL_DIALTONE); - if (calling_subscr) { - encode_bcd_number(bcd_lv, sizeof(bcd_lv), 0xb9, - calling_subscr->extension); - msgb_tlv_put(msg, GSM48_IE_CALLING_BCD, - bcd_lv[0], bcd_lv+1); - } - if (lchan->subscr) { - encode_bcd_number(bcd_lv, sizeof(bcd_lv), 0xb9, - lchan->subscr->extension); - msgb_tlv_put(msg, GSM48_IE_CALLED_BCD, - bcd_lv[0], bcd_lv+1); - } - - DEBUGP(DCC, "B <- SETUP\n"); - - return gsm48_sendmsg(msg); -} - -static int gsm0408_rcv_cc(struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - u_int8_t msg_type = gh->msg_type & 0xbf; - struct gsm_call *call = &msg->lchan->call; - int rc = 0; - - switch (msg_type) { - case GSM48_MT_CC_CALL_CONF: - /* Response to SETUP */ - DEBUGP(DCC, "-> CALL CONFIRM\n"); - /* we now need to MODIFY the channel */ - rc = gsm48_tx_chan_mode_modify(msg->lchan, GSM48_CMODE_SPEECH_EFR); - break; - case GSM48_MT_CC_RELEASE_COMPL: - /* Answer from MS to RELEASE */ - DEBUGP(DCC, "-> RELEASE COMPLETE (state->NULL)\n"); - call->state = GSM_CSTATE_NULL; - break; - case GSM48_MT_CC_ALERTING: - rc = gsm48_cc_rx_alerting(msg); - break; - case GSM48_MT_CC_CONNECT: - rc = gsm48_cc_rx_connect(msg); - break; - case GSM48_MT_CC_CONNECT_ACK: - /* MO: Answer to CONNECT */ - call->state = GSM_CSTATE_ACTIVE; - DEBUGP(DCC, "-> CONNECT_ACK (state->ACTIVE)\n"); - break; - case GSM48_MT_CC_RELEASE: - DEBUGP(DCC, "-> RELEASE\n"); - DEBUGP(DCC, "<- RELEASE_COMPLETE\n"); - /* need to respond with RELEASE_COMPLETE */ - rc = gsm48_tx_simple(msg->lchan, GSM48_PDISC_CC, - GSM48_MT_CC_RELEASE_COMPL); - subscr_put_channel(msg->lchan); - call->state = GSM_CSTATE_NULL; - break; - case GSM48_MT_CC_STATUS_ENQ: - rc = gsm48_cc_rx_status_enq(msg); - break; - case GSM48_MT_CC_DISCONNECT: - rc = gsm48_cc_rx_disconnect(msg); - break; - case GSM48_MT_CC_SETUP: - /* MO: wants to establish a call */ - rc = gsm48_cc_rx_setup(msg); - break; - case GSM48_MT_CC_EMERG_SETUP: - DEBUGP(DCC, "-> EMERGENCY SETUP\n"); - /* FIXME: continue with CALL_PROCEEDING, ALERTING, CONNECT, RELEASE_COMPLETE */ - break; - case GSM48_MT_CC_HOLD: - DEBUGP(DCC, "-> HOLD (rejecting)\n"); - rc = gsm48_tx_simple(msg->lchan, GSM48_PDISC_CC, - GSM48_MT_CC_HOLD_REJ); - break; - case GSM48_MT_CC_RETR: - DEBUGP(DCC, "-> RETR (rejecting)\n"); - rc = gsm48_tx_simple(msg->lchan, GSM48_PDISC_CC, - GSM48_MT_CC_RETR_REJ); - break; - default: - fprintf(stderr, "Unimplemented GSM 04.08 CC msg type 0x%02x\n", - msg_type); - break; - } - - return rc; -} - -/* here we pass in a msgb from the RSL->RLL. We expect the l3 pointer to be set */ -int gsm0408_rcvmsg(struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - u_int8_t pdisc = gh->proto_discr & 0x0f; - int rc = 0; - - switch (pdisc) { - case GSM48_PDISC_CC: - rc = gsm0408_rcv_cc(msg); - break; - case GSM48_PDISC_MM: - rc = gsm0408_rcv_mm(msg); - break; - case GSM48_PDISC_RR: - rc = gsm0408_rcv_rr(msg); - break; - case GSM48_PDISC_SMS: - rc = gsm0411_rcv_sms(msg); - break; - case GSM48_PDISC_MM_GPRS: - case GSM48_PDISC_SM_GPRS: - fprintf(stderr, "Unimplemented GSM 04.08 discriminator 0x%02d\n", - pdisc); - break; - default: - fprintf(stderr, "Unknown GSM 04.08 discriminator 0x%02d\n", - pdisc); - break; - } - - return rc; -} - -enum chreq_type { - CHREQ_T_EMERG_CALL, - CHREQ_T_CALL_REEST_TCH_F, - CHREQ_T_CALL_REEST_TCH_H, - CHREQ_T_CALL_REEST_TCH_H_DBL, - CHREQ_T_SDCCH, - CHREQ_T_TCH_F, - 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_TCH_F, - CHREQ_T_PAG_R_TCH_FH, -}; - -/* Section 9.1.8 / Table 9.9 */ -struct chreq { - u_int8_t val; - u_int8_t mask; - enum chreq_type type; -}; - -/* If SYSTEM INFORMATION TYPE 4 NECI bit == 1 */ -static const struct chreq chreq_type_neci1[] = { - { 0xa0, 0xe0, CHREQ_T_EMERG_CALL }, - { 0xc0, 0xe0, CHREQ_T_CALL_REEST_TCH_F }, - { 0x68, 0xfc, CHREQ_T_CALL_REEST_TCH_H }, - { 0x6c, 0xfc, CHREQ_T_CALL_REEST_TCH_H_DBL }, - { 0xe0, 0xe0, CHREQ_T_SDCCH }, - { 0x40, 0xf0, CHREQ_T_VOICE_CALL_TCH_H }, - { 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 }, - { 0x20, 0xf0, CHREQ_T_PAG_R_TCH_F }, - { 0x30, 0xf0, CHREQ_T_PAG_R_TCH_FH }, -}; - -/* If SYSTEM INFORMATION TYPE 4 NECI bit == 0 */ -static const struct chreq chreq_type_neci0[] = { - { 0xa0, 0xe0, CHREQ_T_EMERG_CALL }, - { 0xc0, 0xe0, CHREQ_T_CALL_REEST_TCH_H }, - { 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 }, - { 0x20, 0xf0, CHREQ_T_PAG_R_TCH_F }, - { 0x30, 0xf0, CHREQ_T_PAG_R_TCH_FH }, -}; - -static const enum gsm_chan_t ctype_by_chreq[] = { - [CHREQ_T_EMERG_CALL] = GSM_LCHAN_TCH_F, - [CHREQ_T_CALL_REEST_TCH_F] = GSM_LCHAN_TCH_F, - [CHREQ_T_CALL_REEST_TCH_H] = GSM_LCHAN_TCH_H, - [CHREQ_T_CALL_REEST_TCH_H_DBL] = GSM_LCHAN_TCH_H, - [CHREQ_T_SDCCH] = GSM_LCHAN_SDCCH, - [CHREQ_T_TCH_F] = GSM_LCHAN_TCH_F, - [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_TCH_F] = GSM_LCHAN_TCH_F, - [CHREQ_T_PAG_R_TCH_FH] = GSM_LCHAN_TCH_F, -}; - -static const enum gsm_chreq_reason_t reason_by_chreq[] = { - [CHREQ_T_EMERG_CALL] = GSM_CHREQ_REASON_EMERG, - [CHREQ_T_CALL_REEST_TCH_F] = GSM_CHREQ_REASON_CALL, - [CHREQ_T_CALL_REEST_TCH_H] = GSM_CHREQ_REASON_CALL, - [CHREQ_T_CALL_REEST_TCH_H_DBL] = GSM_CHREQ_REASON_CALL, - [CHREQ_T_SDCCH] = GSM_CHREQ_REASON_OTHER, - [CHREQ_T_TCH_F] = GSM_CHREQ_REASON_OTHER, - [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_TCH_F] = GSM_CHREQ_REASON_PAG, - [CHREQ_T_PAG_R_TCH_FH] = GSM_CHREQ_REASON_PAG, -}; - -enum gsm_chan_t get_ctype_by_chreq(struct gsm_bts *bts, u_int8_t ra) -{ - int i; - /* FIXME: determine if we set NECI = 0 in the BTS SI4 */ - - for (i = 0; i < ARRAY_SIZE(chreq_type_neci0); i++) { - const struct chreq *chr = &chreq_type_neci0[i]; - if ((ra & chr->mask) == chr->val) - return ctype_by_chreq[chr->type]; - } - fprintf(stderr, "Unknown CHANNEL REQUEST RQD 0x%02x\n", ra); - return GSM_LCHAN_SDCCH; -} - -enum gsm_chreq_reason_t get_reason_by_chreq(struct gsm_bts *bts, u_int8_t ra) -{ - int i; - /* FIXME: determine if we set NECI = 0 in the BTS SI4 */ - - for (i = 0; i < ARRAY_SIZE(chreq_type_neci0); i++) { - const struct chreq *chr = &chreq_type_neci0[i]; - if ((ra & chr->mask) == chr->val) - return reason_by_chreq[chr->type]; - } - fprintf(stderr, "Unknown CHANNEL REQUEST REASON 0x%02x\n", ra); - return GSM_CHREQ_REASON_OTHER; -} diff --git a/src/gsm_04_11.c b/src/gsm_04_11.c deleted file mode 100644 index c29953806..000000000 --- a/src/gsm_04_11.c +++ /dev/null @@ -1,500 +0,0 @@ -/* Point-to-Point (PP) Short Message Service (SMS) - * Support on Mobile Radio Interface - * 3GPP TS 04.11 version 7.1.0 Release 1998 / ETSI TS 100 942 V7.1.0 */ - -/* (C) 2008 by Daniel Willmann <daniel@totalueberwachung.de> - * (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 <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <errno.h> -#include <netinet/in.h> - -#include <openbsc/msgb.h> -#include <openbsc/tlv.h> -#include <openbsc/debug.h> -#include <openbsc/gsm_data.h> -#include <openbsc/gsm_subscriber.h> -#include <openbsc/gsm_04_11.h> -#include <openbsc/gsm_04_08.h> -#include <openbsc/gsm_utils.h> -#include <openbsc/abis_rsl.h> -#include <openbsc/signal.h> -#include <openbsc/db.h> - -#define GSM411_ALLOC_SIZE 1024 -#define GSM411_ALLOC_HEADROOM 128 - -struct msgb *gsm411_msgb_alloc(void) -{ - return msgb_alloc_headroom(GSM411_ALLOC_SIZE, GSM411_ALLOC_HEADROOM); -} - -int gsm0411_sendmsg(struct msgb *msg) -{ - if (msg->lchan) - msg->trx = msg->lchan->ts->trx; - - msg->l3h = msg->data; - - return rsl_data_request(msg, 0); -} - - -#if 0 -static u_int8_t gsm0411_tpdu_from_sms(u_int8_t *tpdu, struct sms_deliver *sms) -{ -} -#endif - -static unsigned long gsm340_validity_period(struct sms_submit *sms) -{ - u_int8_t vp; - unsigned long minutes; - - 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; - 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; - case GSM340_TP_VPF_ENHANCED: - /* Chapter 9.2.3.12.3 */ - /* FIXME: implementation */ - DEBUGP(DSMS, "VPI enhanced not implemented yet\n"); - break; - } - return minutes; -} - -/* determine coding alphabet dependent on GSM 03.38 Section 4 DCS */ -enum sms_alphabet gsm338_get_sms_alphabet(u_int8_t dcs) -{ - u_int8_t cgbits = dcs >> 4; - enum sms_alphabet alpha = DCS_NONE; - - if ((cgbits & 0xc) == 0) { - if (cgbits & 2) - DEBUGP(DSMS, "Compressed SMS not supported yet\n"); - - switch (dcs & 3) { - case 0: - alpha = DCS_7BIT_DEFAULT; - break; - case 1: - alpha = DCS_8BIT_DATA; - break; - case 2: - alpha = DCS_UCS2; - break; - } - } else if (cgbits == 0xc || cgbits == 0xd) - alpha = DCS_7BIT_DEFAULT; - else if (cgbits == 0xe) - alpha = DCS_UCS2; - else if (cgbits == 0xf) { - if (dcs & 4) - alpha = DCS_8BIT_DATA; - else - alpha = DCS_7BIT_DEFAULT; - } - - return alpha; -} - -static int gsm340_rx_sms_submit(struct msgb *msg, struct sms_submit *sms, - struct gsm_sms *gsms) -{ - if (db_sms_store(gsms) != 0) { - DEBUGP(DSMS, "Failed to store SMS in Database\n"); - free(sms); - free(gsms); - return -EIO; - } - return 0; -} - -/* process an incoming TPDU (called from RP-DATA) */ -static int gsm340_rx_tpdu(struct msgb *msg) -{ - u_int8_t *smsp = msgb_sms(msg); - struct sms_submit *sms; - struct gsm_sms *gsms; - u_int8_t da_len_bytes; - u_int8_t address_lv[12]; /* according to 03.40 / 9.1.2.5 */ - int rc = 0; - - sms = malloc(sizeof(*sms)); - if (!sms) - return -ENOMEM; - memset(sms, 0, sizeof(*sms)); - - gsms = malloc(sizeof(*gsms)); - if (!gsms) { - free(sms); - return -ENOMEM; - } - memset(gsms, 0, sizeof(*gsms)); - - /* invert those fields where 0 means active/present */ - sms->mti = *smsp & 0x03; - sms->mms = !!(*smsp & 0x04); - sms->vpf = (*smsp & 0x18) >> 3; - sms->sri = !!(*smsp & 0x20); - sms->udhi= !!(*smsp & 0x40); - sms->rp = !!(*smsp & 0x80); - - smsp++; - sms->msg_ref = *smsp++; - - /* length in bytes of the destination address */ - da_len_bytes = 2 + *smsp/2 + *smsp%2; - if (da_len_bytes > 12) { - DEBUGP(DSMS, "Destination Address > 12 bytes ?!?\n"); - rc = -EIO; - goto out; - } - memcpy(address_lv, smsp, da_len_bytes); - /* mangle first byte to reflect length in bytes, not digits */ - address_lv[0] = da_len_bytes; - /* convert to real number */ - decode_bcd_number(sms->dest_addr, sizeof(sms->dest_addr), address_lv); - - smsp += da_len_bytes; - - sms->pid = *smsp++; - - sms->dcs = *smsp++; - sms->alphabet = gsm338_get_sms_alphabet(sms->dcs); - - switch (sms->vpf) { - case GSM340_TP_VPF_RELATIVE: - sms->vp = smsp++; - break; - case GSM340_TP_VPF_ABSOLUTE: - case GSM340_TP_VPF_ENHANCED: - sms->vp = smsp; - smsp += 7; - break; - default: - DEBUGP(DSMS, "SMS Validity period not implemented: 0x%02x\n", - sms->vpf); - } - sms->ud_len = *smsp++; - if (sms->ud_len) - sms->user_data = smsp; - else - sms->user_data = NULL; - - if (sms->ud_len) { - switch (sms->alphabet) { - case DCS_7BIT_DEFAULT: - gsm_7bit_decode(sms->decoded, smsp, sms->ud_len); - break; - case DCS_8BIT_DATA: - case DCS_UCS2: - case DCS_NONE: - memcpy(sms->decoded, sms->user_data, sms->ud_len); - break; - } - } - - DEBUGP(DSMS, "SMS:\nMTI: 0x%02x, VPF: 0x%02x, MR: 0x%02x " - "PID: 0x%02x, DCS: 0x%02x, DA: %s, UserDataLength: 0x%02x " - "UserData: \"%s\"\n", sms->mti, sms->vpf, sms->msg_ref, - sms->pid, sms->dcs, sms->dest_addr, sms->ud_len, - sms->alphabet == DCS_7BIT_DEFAULT ? sms->decoded : hexdump(sms->user_data, sms->ud_len)); - - dispatch_signal(SS_SMS, 0, sms); - - gsms->sender = msg->lchan->subscr; - /* FIXME: sender refcount */ - - /* determine gsms->receiver based on dialled number */ - gsms->receiver = subscr_get_by_extension(sms->dest_addr); - if (!gsms->receiver) { - rc = 1; /* cause 1: unknown subscriber */ - goto out; - } - - if (sms->user_data) - strncpy(gsms->text, sms->decoded, sizeof(gsms->text)); - - switch (sms->mti) { - case GSM340_SMS_SUBMIT_MS2SC: - /* MS is submitting a SMS */ - rc = gsm340_rx_sms_submit(msg, sms, gsms); - break; - case GSM340_SMS_COMMAND_MS2SC: - case GSM340_SMS_DELIVER_REP_MS2SC: - DEBUGP(DSMS, "Unimplemented MTI 0x%02x\n", sms->mti); - break; - default: - DEBUGP(DSMS, "Undefined MTI 0x%02x\n", sms->mti); - break; - } - -out: - free(gsms); - free(sms); - - return rc; -} - -static int gsm411_send_rp_ack(struct gsm_lchan *lchan, u_int8_t trans_id, - u_int8_t msg_ref) -{ - struct msgb *msg = gsm411_msgb_alloc(); - struct gsm48_hdr *gh; - struct gsm411_rp_hdr *rp; - - msg->lchan = lchan; - - gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - // Outgoing needs the highest bit set - gh->proto_discr = GSM48_PDISC_SMS | trans_id<<4 | 0x80; - gh->msg_type = GSM411_MT_CP_DATA; - - rp = (struct gsm411_rp_hdr *)msgb_put(msg, sizeof(*rp)); - rp->len = 2; - rp->msg_type = GSM411_MT_RP_ACK_MT; - rp->msg_ref = msg_ref; - - DEBUGP(DSMS, "TX: SMS RP ACK\n"); - - return gsm0411_sendmsg(msg); -} - -static int gsm411_send_rp_error(struct gsm_lchan *lchan, u_int8_t trans_id, - u_int8_t msg_ref, u_int8_t cause) -{ - struct msgb *msg = gsm411_msgb_alloc(); - struct gsm48_hdr *gh; - struct gsm411_rp_hdr *rp; - - msg->lchan = lchan; - - gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - // Outgoing needs the highest bit set - gh->proto_discr = GSM48_PDISC_SMS | trans_id<<4 | 0x80; - gh->msg_type = GSM411_MT_CP_DATA; - - rp = (struct gsm411_rp_hdr *)msgb_put(msg, sizeof(*rp)); - rp->msg_type = GSM411_MT_RP_ERROR_MT; - rp->msg_ref = msg_ref; - msgb_tv_put(msg, 1, cause); - - DEBUGP(DSMS, "TX: SMS RP ERROR (cause %02d)\n", cause); - - return gsm0411_sendmsg(msg); -} - -/* Receive a 04.11 TPDU inside RP-DATA / user data */ -static int gsm411_rx_rp_ud(struct msgb *msg, struct gsm411_rp_hdr *rph, - u_int8_t src_len, u_int8_t *src, - u_int8_t dst_len, u_int8_t *dst, - u_int8_t tpdu_len, u_int8_t *tpdu) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - u_int8_t trans_id = gh->proto_discr >> 4; - int rc = 0; - - if (src_len && src) - DEBUGP(DSMS, "RP-DATA (MO) with SRC ?!?\n"); - - if (!dst_len || !dst || !tpdu_len || !tpdu) { - DEBUGP(DSMS, "RP-DATA (MO) without DST or TPDU ?!?\n"); - return -EIO; - } - msg->smsh = tpdu; - - DEBUGP(DSMS, "DST(%u,%s)\n", dst_len, hexdump(dst, dst_len)); - //return gsm411_send_rp_error(msg->lchan, trans_id, rph->msg_ref, rc); - - rc = gsm340_rx_tpdu(msg); - if (rc == 0) - return gsm411_send_rp_ack(msg->lchan, trans_id, rph->msg_ref); - else if (rc > 0) - return gsm411_send_rp_error(msg->lchan, trans_id, rph->msg_ref, rc); - else - return rc; -} - -/* Receive a 04.11 RP-DATA message in accordance with Section 7.3.1.2 */ -static int gsm411_rx_rp_data(struct msgb *msg, struct gsm411_rp_hdr *rph) -{ - u_int8_t src_len, dst_len, rpud_len; - u_int8_t *src = NULL, *dst = NULL , *rp_ud = NULL; - - /* in the MO case, this should always be zero length */ - src_len = rph->data[0]; - if (src_len) - src = &rph->data[1]; - - dst_len = rph->data[1+src_len]; - if (dst_len) - dst = &rph->data[1+src_len+1]; - - rpud_len = rph->data[1+src_len+1+dst_len]; - if (rpud_len) - rp_ud = &rph->data[1+src_len+1+dst_len+1]; - - DEBUGP(DSMS, "RX_RP-DATA: src_len=%u, dst_len=%u ud_len=%u\n", src_len, dst_len, rpud_len); - return gsm411_rx_rp_ud(msg, rph, src_len, src, dst_len, dst, - rpud_len, rp_ud); -} - -static int gsm411_rx_cp_data(struct msgb *msg, struct gsm48_hdr *gh) -{ - struct gsm411_rp_hdr *rp_data = (struct gsm411_rp_hdr*)&gh->data; - u_int8_t msg_type = rp_data->msg_type & 0x07; - int rc = 0; - - switch (msg_type) { - case GSM411_MT_RP_DATA_MO: - DEBUGP(DSMS, "SMS RP-DATA (MO)\n"); - rc = gsm411_rx_rp_data(msg, rp_data); - break; - case GSM411_MT_RP_ACK_MO: - /* Acnkowledgement to MT RP_DATA */ - case GSM411_MT_RP_ERROR_MO: - /* Error in response to MT RP_DATA */ - case GSM411_MT_RP_SMMA_MO: - /* MS tells us that it has memory for more SMS, we need - * to check if we have any pending messages for it and then - * transfer those */ - DEBUGP(DSMS, "Unimplemented RP type 0x%02x\n", msg_type); - break; - default: - DEBUGP(DSMS, "Invalid RP type 0x%02x\n", msg_type); - break; - } - - return rc; -} - -int gsm0411_rcv_sms(struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - u_int8_t msg_type = gh->msg_type; - int rc = 0; - - switch(msg_type) { - case GSM411_MT_CP_DATA: - DEBUGP(DSMS, "SMS CP-DATA\n"); - rc = gsm411_rx_cp_data(msg, gh); - break; - case GSM411_MT_CP_ACK: - DEBUGP(DSMS, "SMS CP-ACK\n"); - break; - case GSM411_MT_CP_ERROR: - DEBUGP(DSMS, "SMS CP-ERROR, cause 0x%02x\n", gh->data[0]); - break; - default: - DEBUGP(DSMS, "Unimplemented CP msg_type: 0x%02x\n", msg_type); - break; - } - - - return rc; -} - -/* Test TPDU - 25c3 welcome */ -#if 0 -static u_int8_t tpdu_test[] = { - 0x04, 0x04, 0x81, 0x32, 0x24, 0x00, 0x00, 0x80, 0x21, 0x92, 0x90, 0x32, - 0x24, 0x40, 0x4D, 0xB2, 0xDA, 0x70, 0xD6, 0x9A, 0x97, 0xE5, 0xF6, 0xF4, - 0xB8, 0x0C, 0x0A, 0xBB, 0xDD, 0xEF, 0xBA, 0x7B, 0x5C, 0x6E, 0x97, 0xDD, - 0x74, 0x1D, 0x08, 0xCA, 0x2E, 0x87, 0xE7, 0x65, 0x50, 0x98, 0x4E, 0x2F, - 0xBB, 0xC9, 0x20, 0x3A, 0xBA, 0x0C, 0x3A, 0x4E, 0x9B, 0x20, 0x7A, 0x98, - 0xBD, 0x06, 0x85, 0xE9, 0xA0, 0x58, 0x4C, 0x37, 0x83, 0x81, 0xD2, 0x6E, - 0xD0, 0x34, 0x1C, 0x66, 0x83, 0x62, 0x21, 0x90, 0xAE, 0x95, 0x02 -}; -#else -/* Test TPDU - ALL YOUR */ -static u_int8_t tpdu_test[] = { - 0x04, 0x04, 0x81, 0x32, 0x24, 0x00, 0x00, 0x80, 0x21, 0x03, 0x41, 0x24, - 0x32, 0x40, 0x1F, 0x41, 0x26, 0x13, 0x94, 0x7D, 0x56, 0xA5, 0x20, 0x28, - 0xF2, 0xE9, 0x2C, 0x82, 0x82, 0xD2, 0x22, 0x48, 0x58, 0x64, 0x3E, 0x9D, - 0x47, 0x10, 0xF5, 0x09, 0xAA, 0x4E, 0x01 -}; -#endif - -int gsm0411_send_sms(struct gsm_lchan *lchan, struct sms_deliver *sms) -{ - struct msgb *msg = gsm411_msgb_alloc(); - struct gsm48_hdr *gh; - struct gsm411_rp_hdr *rp; - u_int8_t *data; - - msg->lchan = lchan; - - gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - gh->proto_discr = GSM48_PDISC_SMS; - gh->msg_type = GSM411_MT_CP_DATA; - - rp = (struct gsm411_rp_hdr *)msgb_put(msg, sizeof(*rp)); - rp->len = sizeof(tpdu_test) + 10; - rp->msg_type = GSM411_MT_RP_DATA_MT; - rp->msg_ref = 42; /* FIXME: Choose randomly */ - /* Hardcode OA for now */ - data = (u_int8_t *)msgb_put(msg, 8); - data[0] = 0x07; - data[1] = 0x91; - data[2] = 0x44; - data[3] = 0x77; - data[4] = 0x58; - data[5] = 0x10; - data[6] = 0x06; - data[7] = 0x50; - data = (u_int8_t *)msgb_put(msg, 1); - data[0] = 0; - - /* FIXME: Hardcoded for now */ - //smslen = gsm0411_tpdu_from_sms(tpdu, sms); - - /* RPDU length */ - data = (u_int8_t *)msgb_put(msg, 1); - data[0] = sizeof(tpdu_test); - - data = (u_int8_t *)msgb_put(msg, sizeof(tpdu_test)); - - //memcpy(data, tpdu, smslen); - memcpy(data, tpdu_test, sizeof(tpdu_test)); - - DEBUGP(DSMS, "TX: SMS SUBMIT\n"); - - return gsm0411_sendmsg(msg); -} diff --git a/src/gsm_data.c b/src/gsm_data.c deleted file mode 100644 index a78425f95..000000000 --- a/src/gsm_data.c +++ /dev/null @@ -1,209 +0,0 @@ -/* (C) 2008-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 <stdio.h> -#include <string.h> - -#include <openbsc/gsm_data.h> - -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) -{ - ts->e1_link.e1_nr = e1_nr; - ts->e1_link.e1_ts = e1_ts; - ts->e1_link.e1_ts_ss = e1_ts_ss; -} - -static const char *pchan_names[] = { - [GSM_PCHAN_NONE] = "NONE", - [GSM_PCHAN_CCCH] = "CCCH", - [GSM_PCHAN_CCCH_SDCCH4] = "CCCH+SDCCH4", - [GSM_PCHAN_TCH_F] = "TCH/F", - [GSM_PCHAN_TCH_H] = "TCH/H", - [GSM_PCHAN_SDCCH8_SACCH8C] = "SDCCH8", - [GSM_PCHAN_UNKNOWN] = "UNKNOWN", -}; - -const char *gsm_pchan_name(enum gsm_phys_chan_config c) -{ - if (c >= ARRAY_SIZE(pchan_names)) - return "INVALID"; - - return pchan_names[c]; -} - -static const char *lchan_names[] = { - [GSM_LCHAN_NONE] = "NONE", - [GSM_LCHAN_SDCCH] = "SDCCH", - [GSM_LCHAN_TCH_F] = "TCH/F", - [GSM_LCHAN_TCH_H] = "TCH/H", - [GSM_LCHAN_UNKNOWN] = "UNKNOWN", -}; - -const char *gsm_lchan_name(enum gsm_chan_t c) -{ - if (c >= ARRAY_SIZE(lchan_names)) - return "INVALID"; - - return lchan_names[c]; -} - -static const char *chreq_names[] = { - [GSM_CHREQ_REASON_EMERG] = "EMERGENCY", - [GSM_CHREQ_REASON_PAG] = "PAGING", - [GSM_CHREQ_REASON_CALL] = "CALL", - [GSM_CHREQ_REASON_LOCATION_UPD] = "LOCATION_UPDATE", - [GSM_CHREQ_REASON_OTHER] = "OTHER", -}; - -const char *gsm_chreq_name(enum gsm_chreq_reason_t c) -{ - if (c >= ARRAY_SIZE(chreq_names)) - return "INVALID"; - - return chreq_names[c]; -} - -struct gsm_network *gsm_network_init(unsigned int num_bts, enum gsm_bts_type bts_type, - u_int16_t country_code, u_int16_t network_code) -{ - int i; - struct gsm_network *net; - - if (num_bts > GSM_MAX_BTS) - return NULL; - - net = malloc(sizeof(*net)); - if (!net) - return NULL; - memset(net, 0, sizeof(*net)); - - net->country_code = country_code; - net->network_code = network_code; - net->num_bts = num_bts; - - for (i = 0; i < num_bts; i++) { - struct gsm_bts *bts = &net->bts[i]; - int j; - - bts->network = net; - bts->nr = i; - bts->type = bts_type; - bts->tsc = HARDCODED_TSC; - bts->bsic = HARDCODED_BSIC; - - for (j = 0; j < BTS_MAX_TRX; j++) { - struct gsm_bts_trx *trx = &bts->trx[j]; - int k; - - trx->bts = bts; - trx->nr = j; - - for (k = 0; k < 8; k++) { - struct gsm_bts_trx_ts *ts = &trx->ts[k]; - int l; - - ts->trx = trx; - ts->nr = k; - ts->pchan = GSM_PCHAN_NONE; - - for (l = 0; l < TS_MAX_LCHAN; l++) { - struct gsm_lchan *lchan; - lchan = &ts->lchan[l]; - - lchan->ts = ts; - lchan->nr = l; - lchan->type = GSM_LCHAN_NONE; - } - } - } - - bts->num_trx = 1; /* FIXME */ -#ifdef HAVE_TRX1 - bts->num_trx++; -#endif - bts->c0 = &bts->trx[0]; - bts->c0->ts[0].pchan = GSM_PCHAN_CCCH_SDCCH4; - } - return net; -} - -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); - - return ts2str; -} - -static const char *bts_types[] = { - [GSM_BTS_TYPE_UNKNOWN] = "unknown", - [GSM_BTS_TYPE_BS11] = "bs11", - [GSM_BTS_TYPE_NANOBTS_900] = "nanobts900", - [GSM_BTS_TYPE_NANOBTS_1800] = "nanobts1800", -}; - -enum gsm_bts_type parse_btstype(char *arg) -{ - int i; - for (i = 0; i < ARRAY_SIZE(bts_types); i++) { - if (!strcmp(arg, bts_types[i])) - return i; - } - return GSM_BTS_TYPE_BS11; /* Default: BS11 */ -} - -char *btstype2str(enum gsm_bts_type type) -{ - if (type > ARRAY_SIZE(bts_types)) - return "undefined"; - return bts_types[type]; -} - -/* Search for a BTS in the given Location Area; optionally start searching - * with start_bts (for continuing to search after the first result) */ -struct gsm_bts *gsm_bts_by_lac(struct gsm_network *net, unsigned int lac, - struct gsm_bts *start_bts) -{ - int i; - struct gsm_bts *bts; - int skip = 0; - - if (start_bts) - skip = 1; - - for (i = 0; i < net->num_bts; i++) { - bts = &net->bts[i]; - - if (skip) { - if (start_bts == bts) - skip = 0; - continue; - } - - if (bts->location_area_code == lac) - return bts; - } - return NULL; -} diff --git a/src/gsm_subscriber.c b/src/gsm_subscriber.c deleted file mode 100644 index 3f608ec30..000000000 --- a/src/gsm_subscriber.c +++ /dev/null @@ -1,143 +0,0 @@ -/* Dummy implementation of a subscriber database, roghly HLR/VLR functionality */ - -/* (C) 2008 by Harald Welte <laforge@gnumonks.org> - * (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. - * - */ - -#include <unistd.h> -#include <stdlib.h> -#include <stdio.h> -#include <string.h> - -#include <openbsc/gsm_subscriber.h> -#include <openbsc/debug.h> -#include <openbsc/db.h> - - -LLIST_HEAD(active_subscribers); - -struct gsm_subscriber *subscr_alloc(void) -{ - struct gsm_subscriber *s; - - s = malloc(sizeof(struct gsm_subscriber)); - if (!s) - return NULL; - - memset(s, 0, sizeof(*s)); - llist_add_tail(&s->entry, &active_subscribers); - s->use_count = 1; - - return s; -} - -static void subscr_free(struct gsm_subscriber *subscr) -{ - llist_del(&subscr->entry); - free(subscr); -} - -struct gsm_subscriber *subscr_get_by_tmsi(const char *tmsi) -{ - struct gsm_subscriber *subscr; - - /* we might have a record in memory already */ - llist_for_each_entry(subscr, &active_subscribers, entry) { - if (strcmp(subscr->tmsi, tmsi) == 0) - return subscr_get(subscr); - } - - return db_get_subscriber(GSM_SUBSCRIBER_TMSI, tmsi); -} - -struct gsm_subscriber *subscr_get_by_imsi(const char *imsi) -{ - struct gsm_subscriber *subscr; - - llist_for_each_entry(subscr, &active_subscribers, entry) { - if (strcmp(subscr->imsi, imsi) == 0) - return subscr_get(subscr); - } - - return db_get_subscriber(GSM_SUBSCRIBER_IMSI, imsi); -} - -struct gsm_subscriber *subscr_get_by_extension(const char *ext) -{ - struct gsm_subscriber *subscr; - - llist_for_each_entry(subscr, &active_subscribers, entry) { - if (strcmp(subscr->extension, ext) == 0) - return subscr_get(subscr); - } - - return db_get_subscriber(GSM_SUBSCRIBER_EXTENSION, ext); -} - -int subscr_update(struct gsm_subscriber *s, struct gsm_bts *bts, int reason) -{ - /* FIXME: Migrate pending requests from one BSC to another */ - switch (reason) { - case GSM_SUBSCRIBER_UPDATE_ATTACHED: - /* Indicate "attached to LAC" */ - s->lac = bts->location_area_code; - break; - case GSM_SUBSCRIBER_UPDATE_DETACHED: - /* Only detach if we are currently in this area */ - if (bts->location_area_code == s->lac) - s->lac = 0; - - break; - default: - fprintf(stderr, "subscr_update with unknown reason: %d\n", - reason); - break; - }; - return db_sync_subscriber(s); -} - -struct gsm_subscriber *subscr_get(struct gsm_subscriber *subscr) -{ - subscr->use_count++; - DEBUGP(DCC, "subscr %s usage increases usage to: %d\n", - subscr->extension, subscr->use_count); - return subscr; -} - -struct gsm_subscriber *subscr_put(struct gsm_subscriber *subscr) -{ - subscr->use_count--; - DEBUGP(DCC, "subscr %s usage decreased usage to: %d\n", - subscr->extension, subscr->use_count); - if (subscr->use_count <= 0) - subscr_free(subscr); - return NULL; -} - -void subscr_put_channel(struct gsm_lchan *lchan) -{ - /* - * FIXME: Continue with other requests now... by checking - * the gsm_subscriber inside the gsm_lchan. Drop the ref count - * of the lchan after having asked the next requestee to handle - * the channel. - */ - put_lchan(lchan); -} diff --git a/src/gsm_utils.c b/src/gsm_utils.c deleted file mode 100644 index 0c25b28c6..000000000 --- a/src/gsm_utils.c +++ /dev/null @@ -1,73 +0,0 @@ -/* - * (C) 2008 by Daniel Willmann <daniel@totalueberwachung.de> - * (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org> - * (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 <openbsc/gsm_utils.h> -#include <stdlib.h> -#include <string.h> - -/* GSM 03.38 6.2.1 Charachter packing */ -int gsm_7bit_decode(char *text, const u_int8_t *user_data, u_int8_t length) -{ - u_int8_t d_off = 0, b_off = 0; - u_int8_t i; - - for (i=0;i<length;i++) { - text[i] = ((user_data[d_off] + (user_data[d_off+1]<<8)) & (0x7f<<b_off))>>b_off; - b_off += 7; - if (b_off >= 8) { - d_off += 1; - b_off -= 8; - } - } - text[i] = '\0'; - return 0; -} - -/* GSM 03.38 6.2.1 Charachter packing */ -int gsm_7bit_encode(u_int8_t *result, const char *data) -{ - int i; - u_int8_t d_off = 0, b_off = 0; - const int length = strlen(data); - int out_length = (length * 8)/7; - - memset(result, 0, out_length); - - for (i = 0; i < length; ++i) { - u_int8_t first = (data[i] & 0x7f) << b_off; - u_int8_t second = (data[i] & 0x7f) >> (8 - b_off); - - result[d_off] |= first; - if (second != 0) - result[d_off + 1] = second; - - b_off += 7; - - if (b_off >= 8) { - d_off += 1; - b_off -= 8; - } - } - - return out_length; -} diff --git a/src/input/ipaccess.c b/src/input/ipaccess.c deleted file mode 100644 index ea7f847c2..000000000 --- a/src/input/ipaccess.c +++ /dev/null @@ -1,597 +0,0 @@ -/* OpenBSC Abis input driver for ip.access */ - -/* (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 <stdio.h> -#include <unistd.h> -#include <stdlib.h> -#include <errno.h> -#include <string.h> -#include <time.h> -#include <sys/fcntl.h> -#include <sys/types.h> -#include <sys/socket.h> -#include <sys/ioctl.h> -#include <arpa/inet.h> - -#include <openbsc/select.h> -#include <openbsc/tlv.h> -#include <openbsc/msgb.h> -#include <openbsc/debug.h> -#include <openbsc/gsm_data.h> -#include <openbsc/abis_nm.h> -#include <openbsc/abis_rsl.h> -#include <openbsc/subchan_demux.h> -#include <openbsc/e1_input.h> -#include <openbsc/ipaccess.h> - -/* data structure for one E1 interface with A-bis */ -struct ia_e1_handle { - struct bsc_fd listen_fd; - struct bsc_fd rsl_listen_fd; - struct gsm_network *gsmnet; -}; - -static struct ia_e1_handle *e1h; - - -#define TS1_ALLOC_SIZE 300 - -static const u_int8_t pong[] = { 0, 1, IPAC_PROTO_IPACCESS, IPAC_MSGT_PONG }; -static const u_int8_t id_ack[] = { 0, 1, IPAC_PROTO_IPACCESS, IPAC_MSGT_ID_ACK }; -static const u_int8_t id_req[] = { 0, 17, IPAC_PROTO_IPACCESS, IPAC_MSGT_ID_GET, - 0x01, IPAC_IDTAG_UNIT, - 0x01, IPAC_IDTAG_MACADDR, - 0x01, IPAC_IDTAG_LOCATION1, - 0x01, IPAC_IDTAG_LOCATION2, - 0x01, IPAC_IDTAG_EQUIPVERS, - 0x01, IPAC_IDTAG_SWVERSION, - 0x01, IPAC_IDTAG_UNITNAME, - 0x01, IPAC_IDTAG_SERNR, - }; - -static const char *idtag_names[] = { - [IPAC_IDTAG_SERNR] = "Serial_Number", - [IPAC_IDTAG_UNITNAME] = "Unit_Name", - [IPAC_IDTAG_LOCATION1] = "Location_1", - [IPAC_IDTAG_LOCATION2] = "Location_2", - [IPAC_IDTAG_EQUIPVERS] = "Equipment_Version", - [IPAC_IDTAG_SWVERSION] = "Software_Version", - [IPAC_IDTAG_IPADDR] = "IP_Address", - [IPAC_IDTAG_MACADDR] = "MAC_Address", - [IPAC_IDTAG_UNIT] = "Unit_ID", -}; - -static const char *ipac_idtag_name(int tag) -{ - if (tag >= ARRAY_SIZE(idtag_names)) - return "unknown"; - - return idtag_names[tag]; -} - -static int ipac_idtag_parse(struct tlv_parsed *dec, unsigned char *buf, int len) -{ - u_int8_t t_len; - u_int8_t t_tag; - u_int8_t *cur = buf; - - while (cur < buf + len) { - t_len = *cur++; - t_tag = *cur++; - - DEBUGPC(DMI, "%s='%s' ", ipac_idtag_name(t_tag), cur); - - dec->lv[t_tag].len = t_len; - dec->lv[t_tag].val = cur; - - cur += t_len; - } - return 0; -} - -struct gsm_bts *find_bts_by_unitid(struct gsm_network *net, - u_int16_t site_id, u_int16_t bts_id) -{ - int i; - - for (i = 0; i < net->num_bts; i++) { - struct gsm_bts *bts = &net->bts[i]; - - if (!is_ipaccess_bts(bts)) - continue; - - if (bts->ip_access.site_id == site_id && - bts->ip_access.bts_id == bts_id) - return bts; - } - - return NULL; -} - -static int parse_unitid(const char *str, u_int16_t *site_id, u_int16_t *bts_id, - u_int16_t *trx_id) -{ - unsigned long ul; - char *endptr; - const char *nptr; - - nptr = str; - ul = strtoul(nptr, &endptr, 10); - if (endptr <= nptr) - return -EINVAL; - if (site_id) - *site_id = ul & 0xffff; - - if (*endptr++ != '/') - return -EINVAL; - - nptr = endptr; - ul = strtoul(nptr, &endptr, 10); - if (endptr <= nptr) - return -EINVAL; - if (bts_id) - *bts_id = ul & 0xffff; - - if (*endptr++ != '/') - return -EINVAL; - - nptr = endptr; - ul = strtoul(nptr, &endptr, 10); - if (endptr <= nptr) - return -EINVAL; - if (trx_id) - *trx_id = ul & 0xffff; - - return 0; -} - -static int ipaccess_rcvmsg(struct e1inp_line *line, struct msgb *msg, - struct bsc_fd *bfd) -{ - struct tlv_parsed tlvp; - u_int8_t msg_type = *(msg->l2h); - u_int16_t site_id, bts_id, trx_id; - struct gsm_bts *bts; - int ret = 0; - - switch (msg_type) { - case IPAC_MSGT_PING: - ret = write(bfd->fd, pong, sizeof(pong)); - break; - case IPAC_MSGT_PONG: - DEBUGP(DMI, "PONG!\n"); - break; - case IPAC_MSGT_ID_RESP: - DEBUGP(DMI, "ID_RESP "); - /* parse tags, search for Unit ID */ - ipac_idtag_parse(&tlvp, (u_int8_t *)msg->l2h + 2, - msgb_l2len(msg)-2); - DEBUGP(DMI, "\n"); - - if (!TLVP_PRESENT(&tlvp, IPAC_IDTAG_UNIT)) - break; - - /* lookup BTS, create sign_link, ... */ - parse_unitid((char *)TLVP_VAL(&tlvp, IPAC_IDTAG_UNIT), - &site_id, &bts_id, &trx_id); - bts = find_bts_by_unitid(e1h->gsmnet, site_id, bts_id); - if (!bts) { - DEBUGP(DINP, "Unable to find BTS configuration for " - " %u/%u/%u, disconnecting\n", site_id, bts_id, - trx_id); - return -EIO; - } - DEBUGP(DINP, "Identified BTS %u/%u/%u\n", site_id, bts_id, trx_id); - if (bfd->priv_nr == 1) { - bts->oml_link = e1inp_sign_link_create(&line->ts[1-1], - E1INP_SIGN_OML, bts->c0, - 0, 0xff); - } else if (bfd->priv_nr == 2) { - struct e1inp_ts *e1i_ts; - struct bsc_fd *newbfd; - - /* FIXME: implement this for non-0 TRX */ - bfd->data = line = bts->oml_link->ts->line; - e1i_ts = &line->ts[2-1]; - newbfd = &e1i_ts->driver.ipaccess.fd; - - bts->c0->rsl_link = - e1inp_sign_link_create(e1i_ts, - E1INP_SIGN_RSL, bts->c0, - 0, 0); - /* get rid of our old temporary bfd */ - memcpy(newbfd, bfd, sizeof(*newbfd)); - bsc_unregister_fd(bfd); - bsc_register_fd(newbfd); - free(bfd); - } - break; - case IPAC_MSGT_ID_ACK: - DEBUGP(DMI, "ID_ACK? -> ACK!\n"); - ret = write(bfd->fd, id_ack, sizeof(id_ack)); - break; - } - return 0; -} - -/* FIXME: this is per BTS */ -static int oml_up = 0; -static int rsl_up = 0; - -static int handle_ts1_read(struct bsc_fd *bfd) -{ - struct e1inp_line *line = bfd->data; - unsigned int ts_nr = bfd->priv_nr; - struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1]; - struct e1inp_sign_link *link; - struct msgb *msg = msgb_alloc(TS1_ALLOC_SIZE); - struct ipaccess_head *hh; - int ret; - - if (!msg) - return -ENOMEM; - - /* first read our 3-byte header */ - hh = (struct ipaccess_head *) msg->data; - ret = recv(bfd->fd, msg->data, 3, 0); - if (ret < 0) { - fprintf(stderr, "recv error %s\n", strerror(errno)); - return ret; - } - if (ret == 0) { - fprintf(stderr, "BTS disappeared, dead socket\n"); - e1inp_event(e1i_ts, EVT_E1_TEI_DN, 0, IPAC_PROTO_RSL); - e1inp_event(e1i_ts, EVT_E1_TEI_DN, 0, IPAC_PROTO_OML); - bsc_unregister_fd(bfd); - close(bfd->fd); - bfd->fd = -1; - } - msgb_put(msg, ret); - - /* 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) { - fprintf(stderr, "short read!\n"); - msgb_free(msg); - return -EIO; - } - msgb_put(msg, ret); - DEBUGP(DMI, "RX %u: %s\n", ts_nr, hexdump(msgb_l2(msg), ret)); - - if (hh->proto == IPAC_PROTO_IPACCESS) { - ret = ipaccess_rcvmsg(line, msg, bfd); - if (ret < 0) { - e1inp_event(e1i_ts, EVT_E1_TEI_DN, 0, IPAC_PROTO_RSL); - e1inp_event(e1i_ts, EVT_E1_TEI_DN, 0, IPAC_PROTO_OML); - bsc_unregister_fd(bfd); - close(bfd->fd); - bfd->fd = -1; - } - msgb_free(msg); - return ret; - } - /* 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); - if (!link) { - printf("no matching signalling link for hh->proto=0x%02x\n", hh->proto); - msgb_free(msg); - return -EIO; - } - msg->trx = link->trx; - - switch (hh->proto) { - case IPAC_PROTO_RSL: - if (!rsl_up) { - e1inp_event(e1i_ts, EVT_E1_TEI_UP, 0, IPAC_PROTO_RSL); - rsl_up = 1; - } - ret = abis_rsl_rcvmsg(msg); - break; - case IPAC_PROTO_OML: - if (!oml_up) { - e1inp_event(e1i_ts, EVT_E1_TEI_UP, 0, IPAC_PROTO_OML); - oml_up = 1; - } - ret = abis_nm_rcvmsg(msg); - break; - default: - DEBUGP(DMI, "Unknown IP.access protocol proto=0x%02x\n", hh->proto); - msgb_free(msg); - break; - } - return ret; -} - -static int handle_ts1_write(struct bsc_fd *bfd) -{ - struct e1inp_line *line = bfd->data; - unsigned int ts_nr = bfd->priv_nr; - struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1]; - struct e1inp_sign_link *sign_link; - struct msgb *msg; - struct ipaccess_head *hh; - u_int8_t *l2_data; - int ret; - - /* get the next msg for this timeslot */ - msg = e1inp_tx_ts(e1i_ts, &sign_link); - if (!msg) { - bfd->when &= ~BSC_FD_WRITE; - return 0; - } - - l2_data = msg->data; - - /* prepend the ip.access header */ - hh = (struct ipaccess_head *) msgb_push(msg, sizeof(*hh)); - hh->zero = 0; - hh->len = msg->len - sizeof(*hh); - - switch (sign_link->type) { - case E1INP_SIGN_OML: - hh->proto = IPAC_PROTO_OML; - break; - case E1INP_SIGN_RSL: - hh->proto = IPAC_PROTO_RSL; - break; - default: - msgb_free(msg); - return -EINVAL; - } - - DEBUGP(DMI, "TX %u: %s\n", ts_nr, hexdump(l2_data, hh->len)); - - ret = send(bfd->fd, msg->data, msg->len, 0); - msgb_free(msg); - usleep(100000); - - return ret; -} - -/* callback from select.c in case one of the fd's can be read/written */ -static int ipaccess_fd_cb(struct bsc_fd *bfd, unsigned int what) -{ - struct e1inp_line *line = bfd->data; - unsigned int ts_nr = bfd->priv_nr; - unsigned int idx = ts_nr-1; - struct e1inp_ts *e1i_ts; - int rc = 0; - - /* In case of early RSL we might not yet have a line */ - - if (line) - e1i_ts = &line->ts[idx]; - - if (!line || e1i_ts->type == E1INP_TS_TYPE_SIGN) { - if (what & BSC_FD_READ) - rc = handle_ts1_read(bfd); - if (what & BSC_FD_WRITE) - rc = handle_ts1_write(bfd); - } else - fprintf(stderr, "unknown E1 TS type %u\n", e1i_ts->type); - - return rc; -} - - -static int ts_want_write(struct e1inp_ts *e1i_ts) -{ - e1i_ts->driver.ipaccess.fd.when |= BSC_FD_WRITE; - - return 0; -} - -struct e1inp_driver ipaccess_driver = { - .name = "ip.access", - .want_write = ts_want_write, -}; - -/* callback of the OML listening filedescriptor */ -static int listen_fd_cb(struct bsc_fd *listen_bfd, unsigned int what) -{ - int ret; - int idx = 0; - struct e1inp_line *line; - struct e1inp_ts *e1i_ts; - struct bsc_fd *bfd; - struct sockaddr_in sa; - socklen_t sa_len = sizeof(sa); - - if (!(what & BSC_FD_READ)) - return 0; - - ret = accept(listen_bfd->fd, (struct sockaddr *) &sa, &sa_len); - if (ret < 0) { - perror("accept"); - return ret; - } - DEBUGP(DINP, "accept()ed new OML link from %s\n", inet_ntoa(sa.sin_addr)); - - line = malloc(sizeof(*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]; - - bfd = &e1i_ts->driver.ipaccess.fd; - bfd->fd = ret; - bfd->data = line; - bfd->priv_nr = 1; - bfd->cb = ipaccess_fd_cb; - bfd->when = BSC_FD_READ; - ret = bsc_register_fd(bfd); - if (ret < 0) { - fprintf(stderr, "could not register FD\n"); - close(bfd->fd); - free(line); - return ret; - } - - /* Request ID. FIXME: request LOCATION, HW/SW VErsion, Unit Name, Serno */ - ret = write(bfd->fd, id_req, sizeof(id_req)); - - return e1inp_line_register(line); -} - -static int rsl_listen_fd_cb(struct bsc_fd *listen_bfd, unsigned int what) -{ - struct sockaddr_in sa; - socklen_t sa_len = sizeof(sa); - struct bsc_fd *bfd = malloc(sizeof(*bfd)); - int ret; - - if (!(what & BSC_FD_READ)) - return 0; - - /* 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 */ - - bfd->fd = accept(listen_bfd->fd, (struct sockaddr *) &sa, &sa_len); - if (bfd->fd < 0) { - perror("accept"); - return bfd->fd; - } - DEBUGP(DINP, "accept()ed new RSL link from %s\n", inet_ntoa(sa.sin_addr)); - bfd->priv_nr = 2; - bfd->cb = ipaccess_fd_cb; - bfd->when = BSC_FD_READ; - ret = bsc_register_fd(bfd); - if (ret < 0) { - fprintf(stderr, "could not register FD\n"); - close(bfd->fd); - free(bfd); - return ret; - } - /* Request ID. FIXME: request LOCATION, HW/SW VErsion, Unit Name, Serno */ - ret = write(bfd->fd, id_req, sizeof(id_req)); - - return 0; -} - -static int make_sock(struct bsc_fd *bfd, u_int16_t port, - int (*cb)(struct bsc_fd *fd, unsigned int what)) -{ - struct sockaddr_in addr; - int ret, on = 1; - - bfd->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - bfd->cb = cb; - bfd->when = BSC_FD_READ; - //bfd->data = line; - - memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_port = htons(port); - addr.sin_addr.s_addr = INADDR_ANY; - - setsockopt(bfd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); - - ret = bind(bfd->fd, (struct sockaddr *) &addr, sizeof(addr)); - if (ret < 0) { - fprintf(stderr, "could not bind l2 socket %s\n", - strerror(errno)); - return -EIO; - } - - ret = listen(bfd->fd, 1); - if (ret < 0) { - perror("listen"); - return ret; - } - - ret = bsc_register_fd(bfd); - if (ret < 0) { - perror("register_listen_fd"); - return ret; - } - return 0; -} - -/* Actively connect to a BTS. Currently used by ipaccess-config.c */ -int ipaccess_connect(struct e1inp_line *line, struct sockaddr_in *sa) -{ - struct e1inp_ts *e1i_ts = &line->ts[0]; - struct bsc_fd *bfd = &e1i_ts->driver.ipaccess.fd; - int ret, on = 1; - - bfd->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - bfd->cb = ipaccess_fd_cb; - bfd->when = BSC_FD_READ | BSC_FD_WRITE; - bfd->data = line; - bfd->priv_nr = 1; - - setsockopt(bfd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); - - ret = connect(bfd->fd, (struct sockaddr *) sa, sizeof(*sa)); - if (ret < 0) { - fprintf(stderr, "could not connect socket\n"); - close(bfd->fd); - return ret; - } - - ret = bsc_register_fd(bfd); - if (ret < 0) { - close(bfd->fd); - return ret; - } - - line->driver = &ipaccess_driver; - - return e1inp_line_register(line); -} - -int ipaccess_setup(struct gsm_network *gsmnet) -{ - int ret; - - /* register the driver with the core */ - /* FIXME: do this in the plugin initializer function */ - ret = e1inp_driver_register(&ipaccess_driver); - if (ret) - return ret; - - e1h = malloc(sizeof(*e1h)); - memset(e1h, 0, sizeof(*e1h)); - e1h->gsmnet = gsmnet; - - /* Listen for OML connections */ - ret = make_sock(&e1h->listen_fd, 3002, listen_fd_cb); - if (ret < 0) - return ret; - - /* Listen for RSL connections */ - ret = make_sock(&e1h->rsl_listen_fd, 3003, rsl_listen_fd_cb); - - return ret; -} diff --git a/src/input/misdn.c b/src/input/misdn.c deleted file mode 100644 index de8d41f21..000000000 --- a/src/input/misdn.c +++ /dev/null @@ -1,542 +0,0 @@ -/* OpenBSC Abis input driver for mISDNuser */ - -/* (C) 2008-2009 by Harald Welte <laforge@gnumonks.org> - * (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. - * - */ - -#include <stdio.h> -#include <unistd.h> -#include <stdlib.h> -#include <errno.h> -#include <string.h> -#include <time.h> -#include <sys/fcntl.h> -#include <sys/types.h> -#include <sys/socket.h> -#include <sys/ioctl.h> -#include <arpa/inet.h> -#include <mISDNif.h> - -//#define AF_COMPATIBILITY_FUNC -//#include <compat_af_isdn.h> -#define AF_ISDN 34 -#define PF_ISDN AF_ISDN - -#include <openbsc/select.h> -#include <openbsc/msgb.h> -#include <openbsc/debug.h> -#include <openbsc/gsm_data.h> -#include <openbsc/abis_nm.h> -#include <openbsc/abis_rsl.h> -#include <openbsc/subchan_demux.h> -#include <openbsc/e1_input.h> - -/* data structure for one E1 interface with A-bis */ -struct mi_e1_handle { - /* The mISDN card number of the card we use */ - int cardnr; -}; - -#define TS1_ALLOC_SIZE 300 - -struct prim_name { - unsigned int prim; - const char *name; -}; - -const struct prim_name prim_names[] = { - { PH_CONTROL_IND, "PH_CONTROL_IND" }, - { PH_DATA_IND, "PH_DATA_IND" }, - { PH_DATA_CNF, "PH_DATA_CNF" }, - { PH_ACTIVATE_IND, "PH_ACTIVATE_IND" }, - { DL_ESTABLISH_IND, "DL_ESTABLISH_IND" }, - { DL_ESTABLISH_CNF, "DL_ESTABLISH_CNF" }, - { DL_RELEASE_IND, "DL_RELEASE_IND" }, - { DL_RELEASE_CNF, "DL_RELEASE_CNF" }, - { DL_DATA_IND, "DL_DATA_IND" }, - { DL_UNITDATA_IND, "DL_UNITDATA_IND" }, - { DL_INFORMATION_IND, "DL_INFORMATION_IND" }, - { MPH_ACTIVATE_IND, "MPH_ACTIVATE_IND" }, - { MPH_DEACTIVATE_IND, "MPH_DEACTIVATE_IND" }, -}; - -const char *get_prim_name(unsigned int prim) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(prim_names); i++) { - if (prim_names[i].prim == prim) - return prim_names[i].name; - } - - return "UNKNOWN"; -} - -static int handle_ts1_read(struct bsc_fd *bfd) -{ - struct e1inp_line *line = bfd->data; - unsigned int ts_nr = bfd->priv_nr; - struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1]; - struct e1inp_sign_link *link; - struct msgb *msg = msgb_alloc(TS1_ALLOC_SIZE); - struct sockaddr_mISDN l2addr; - struct mISDNhead *hh; - socklen_t alen; - int ret; - - if (!msg) - return -ENOMEM; - - hh = (struct mISDNhead *) msg->data; - - alen = sizeof(l2addr); - ret = recvfrom(bfd->fd, msg->data, 300, 0, - (struct sockaddr *) &l2addr, &alen); - if (ret < 0) { - fprintf(stderr, "recvfrom error %s\n", strerror(errno)); - return ret; - } - - if (alen != sizeof(l2addr)) { - fprintf(stderr, "%s error len\n", __func__); - return -EINVAL; - } - - msgb_put(msg, ret); - - DEBUGP(DMI, "alen =%d, dev(%d) channel(%d) sapi(%d) tei(%d)\n", - alen, l2addr.dev, l2addr.channel, l2addr.sapi, l2addr.tei); - - DEBUGP(DMI, "<= len = %d, prim(0x%x) id(0x%x): %s\n", - ret, hh->prim, hh->id, get_prim_name(hh->prim)); - - switch (hh->prim) { - case DL_INFORMATION_IND: - /* mISDN tells us which channel number is allocated for this - * tuple of (SAPI, TEI). */ - DEBUGP(DMI, "DL_INFORMATION_IND: use channel(%d) sapi(%d) tei(%d) for now\n", - l2addr.channel, l2addr.sapi, l2addr.tei); - link = e1inp_lookup_sign_link(e1i_ts, l2addr.tei, l2addr.sapi); - if (!link) { - DEBUGPC(DMI, "mISDN message for unknown sign_link\n"); - free(msg); - return -EINVAL; - } - /* save the channel number in the driver private struct */ - link->driver.misdn.channel = l2addr.channel; - break; - case DL_ESTABLISH_IND: - DEBUGP(DMI, "DL_ESTABLISH_IND: channel(%d) sapi(%d) tei(%d)\n", - l2addr.channel, l2addr.sapi, l2addr.tei); - ret = e1inp_event(e1i_ts, EVT_E1_TEI_UP, l2addr.tei, l2addr.sapi); - break; - case DL_RELEASE_IND: - DEBUGP(DMI, "DL_RELEASE_IND: channel(%d) sapi(%d) tei(%d)\n", - l2addr.channel, l2addr.sapi, l2addr.tei); - ret = e1inp_event(e1i_ts, EVT_E1_TEI_DN, l2addr.tei, l2addr.sapi); - break; - case DL_DATA_IND: - msg->l2h = msg->data + MISDN_HEADER_LEN; - DEBUGP(DMI, "RX: %s\n", hexdump(msgb_l2(msg), ret - MISDN_HEADER_LEN)); - ret = e1inp_rx_ts(e1i_ts, msg, l2addr.tei, l2addr.sapi); - break; - case PH_ACTIVATE_IND: - DEBUGP(DMI, "PH_ACTIVATE_IND: channel(%d) sapi(%d) tei(%d)\n", - l2addr.channel, l2addr.sapi, l2addr.tei); - break; - case PH_DEACTIVATE_IND: - DEBUGP(DMI, "PH_DEACTIVATE_IND: channel(%d) sapi(%d) tei(%d)\n", - l2addr.channel, l2addr.sapi, l2addr.tei); - break; - default: - break; - } - return ret; -} - -static int ts_want_write(struct e1inp_ts *e1i_ts) -{ - /* We never include the mISDN B-Channel FD into the - * writeset, since it doesn't support poll() based - * write flow control */ - if (e1i_ts->type == E1INP_TS_TYPE_TRAU) - return 0; - - e1i_ts->driver.misdn.fd.when |= BSC_FD_WRITE; - - return 0; -} - -static void timeout_ts1_write(void *data) -{ - struct e1inp_ts *e1i_ts = (struct e1inp_ts *)data; - - /* trigger write of ts1, due to tx delay timer */ - ts_want_write(e1i_ts); -} - -static int handle_ts1_write(struct bsc_fd *bfd) -{ - struct e1inp_line *line = bfd->data; - unsigned int ts_nr = bfd->priv_nr; - struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1]; - struct e1inp_sign_link *sign_link; - struct sockaddr_mISDN sa; - struct msgb *msg; - struct mISDNhead *hh; - u_int8_t *l2_data; - int ret; - - bfd->when &= ~BSC_FD_WRITE; - - /* get the next msg for this timeslot */ - msg = e1inp_tx_ts(e1i_ts, &sign_link); - if (!msg) { - /* no message after tx delay timer */ - return 0; - } - - l2_data = msg->data; - - /* prepend the mISDNhead */ - hh = (struct mISDNhead *) msgb_push(msg, sizeof(*hh)); - hh->prim = DL_DATA_REQ; - - DEBUGP(DMI, "TX TEI(%d) SAPI(%d): %s\n", sign_link->tei, - sign_link->sapi, hexdump(l2_data, msg->len - MISDN_HEADER_LEN)); - - /* construct the sockaddr */ - sa.family = AF_ISDN; - sa.sapi = sign_link->sapi; - sa.dev = sign_link->tei; - sa.channel = sign_link->driver.misdn.channel; - - ret = sendto(bfd->fd, msg->data, msg->len, 0, - (struct sockaddr *)&sa, sizeof(sa)); - if (ret < 0) - fprintf(stderr, "%s sendto failed %d\n", __func__, ret); - msgb_free(msg); - - /* set tx delay timer for next event */ - e1i_ts->sign.tx_timer.cb = timeout_ts1_write; - e1i_ts->sign.tx_timer.data = e1i_ts; - bsc_schedule_timer(&e1i_ts->sign.tx_timer, 0, 50000); - - return ret; -} - -#define BCHAN_TX_GRAN 160 -/* write to a B channel TS */ -static int handle_tsX_write(struct bsc_fd *bfd) -{ - struct e1inp_line *line = bfd->data; - unsigned int ts_nr = bfd->priv_nr; - struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1]; - struct mISDNhead *hh; - u_int8_t tx_buf[BCHAN_TX_GRAN + sizeof(*hh)]; - struct subch_mux *mx = &e1i_ts->trau.mux; - int ret; - - hh = (struct mISDNhead *) tx_buf; - hh->prim = PH_DATA_REQ; - - subchan_mux_out(mx, tx_buf+sizeof(*hh), BCHAN_TX_GRAN); - - DEBUGP(DMIB, "BCHAN TX: %s\n", - hexdump(tx_buf+sizeof(*hh), BCHAN_TX_GRAN)); - - 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, - sizeof(*hh) + BCHAN_TX_GRAN); - - return ret; -} - -#define TSX_ALLOC_SIZE 4096 -/* FIXME: read from a B channel TS */ -static int handle_tsX_read(struct bsc_fd *bfd) -{ - struct e1inp_line *line = bfd->data; - unsigned int ts_nr = bfd->priv_nr; - struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1]; - struct msgb *msg = msgb_alloc(TSX_ALLOC_SIZE); - struct mISDNhead *hh; - int ret; - - if (!msg) - return -ENOMEM; - - hh = (struct mISDNhead *) msg->data; - - ret = recv(bfd->fd, msg->data, TSX_ALLOC_SIZE, 0); - if (ret < 0) { - fprintf(stderr, "recvfrom error %s\n", strerror(errno)); - return ret; - } - - msgb_put(msg, ret); - - if (hh->prim != PH_CONTROL_IND) - DEBUGP(DMIB, "<= BCHAN len = %d, prim(0x%x) id(0x%x): %s\n", - ret, hh->prim, hh->id, get_prim_name(hh->prim)); - - switch (hh->prim) { - case PH_DATA_IND: - msg->l2h = msg->data + MISDN_HEADER_LEN; - DEBUGP(DMIB, "BCHAN RX: %s\n", - hexdump(msgb_l2(msg), ret - MISDN_HEADER_LEN)); - ret = e1inp_rx_ts(e1i_ts, msg, 0, 0); - break; - case PH_ACTIVATE_IND: - case PH_DATA_CNF: - /* physical layer indicates that data has been sent, - * we thus can send some more data */ - ret = handle_tsX_write(bfd); - default: - break; - } - /* FIXME: why do we free signalling msgs in the caller, and trau not? */ - msgb_free(msg); - - return ret; -} - -/* callback from select.c in case one of the fd's can be read/written */ -static int misdn_fd_cb(struct bsc_fd *bfd, unsigned int what) -{ - struct e1inp_line *line = bfd->data; - unsigned int ts_nr = bfd->priv_nr; - unsigned int idx = ts_nr-1; - struct e1inp_ts *e1i_ts = &line->ts[idx]; - int rc = 0; - - switch (e1i_ts->type) { - case E1INP_TS_TYPE_SIGN: - if (what & BSC_FD_READ) - rc = handle_ts1_read(bfd); - if (what & BSC_FD_WRITE) - rc = handle_ts1_write(bfd); - break; - case E1INP_TS_TYPE_TRAU: - if (what & BSC_FD_READ) - rc = handle_tsX_read(bfd); - /* We never include the mISDN B-Channel FD into the - * writeset, since it doesn't support poll() based - * write flow control */ - break; - default: - fprintf(stderr, "unknown E1 TS type %u\n", e1i_ts->type); - break; - } - - return rc; -} - -static int activate_bchan(struct e1inp_line *line, int ts, int act) -{ - struct mISDNhead hh; - int ret; - unsigned int idx = ts-1; - struct e1inp_ts *e1i_ts = &line->ts[idx]; - struct bsc_fd *bfd = &e1i_ts->driver.misdn.fd; - - fprintf(stdout, "activate bchan\n"); - if (act) - hh.prim = PH_ACTIVATE_REQ; - else - hh.prim = PH_DEACTIVATE_REQ; - - hh.id = MISDN_ID_ANY; - ret = sendto(bfd->fd, &hh, sizeof(hh), 0, NULL, 0); - if (ret < 0) { - fprintf(stdout, "could not send ACTIVATE_RQ %s\n", - strerror(errno)); - } - - return ret; -} - -struct e1inp_driver misdn_driver = { - .name = "mISDNuser", - .want_write = ts_want_write, -}; - -static int mi_e1_setup(struct e1inp_line *line, int release_l2) -{ - struct mi_e1_handle *e1h = line->driver_data; - int ts, ret; - - /* TS0 is CRC4, don't need any fd for it */ - for (ts = 1; ts < NUM_E1_TS; ts++) { - unsigned int idx = ts-1; - struct e1inp_ts *e1i_ts = &line->ts[idx]; - struct bsc_fd *bfd = &e1i_ts->driver.misdn.fd; - struct sockaddr_mISDN addr; - - bfd->data = line; - bfd->priv_nr = ts; - bfd->cb = misdn_fd_cb; - - switch (e1i_ts->type) { - case E1INP_TS_TYPE_NONE: - continue; - break; - case E1INP_TS_TYPE_SIGN: - bfd->fd = socket(PF_ISDN, SOCK_DGRAM, ISDN_P_LAPD_NT); - bfd->when = BSC_FD_READ; - break; - case E1INP_TS_TYPE_TRAU: - bfd->fd = socket(PF_ISDN, SOCK_DGRAM, ISDN_P_B_RAW); - /* We never include the mISDN B-Channel FD into the - * writeset, since it doesn't support poll() based - * write flow control */ - bfd->when = BSC_FD_READ; - break; - } - - if (bfd->fd < 0) { - fprintf(stderr, "%s could not open socket %s\n", - __func__, strerror(errno)); - return bfd->fd; - } - - memset(&addr, 0, sizeof(addr)); - addr.family = AF_ISDN; - addr.dev = e1h->cardnr; - switch (e1i_ts->type) { - case E1INP_TS_TYPE_SIGN: - addr.channel = 0; - /* SAPI not supported yet in kernel */ - //addr.sapi = e1inp_ts->sign.sapi; - addr.sapi = 0; - addr.tei = GROUP_TEI; - break; - case E1INP_TS_TYPE_TRAU: - addr.channel = ts; - break; - default: - DEBUGP(DMI, "unsupported E1 TS type: %u\n", - e1i_ts->type); - break; - } - - ret = bind(bfd->fd, (struct sockaddr *) &addr, sizeof(addr)); - if (ret < 0) { - fprintf(stderr, "could not bind l2 socket %s\n", - strerror(errno)); - return -EIO; - } - - if (e1i_ts->type == E1INP_TS_TYPE_SIGN) { - ret = ioctl(bfd->fd, IMCLEAR_L2, &release_l2); - if (ret < 0) { - fprintf(stderr, "could not send IOCTL IMCLEAN_L2 %s\n", strerror(errno)); - return -EIO; - } - } - - /* FIXME: only activate B-Channels once we start to - * use them to conserve CPU power */ - if (e1i_ts->type == E1INP_TS_TYPE_TRAU) - activate_bchan(line, ts, 1); - - ret = bsc_register_fd(bfd); - if (ret < 0) { - fprintf(stderr, "could not register FD: %s\n", - strerror(ret)); - return ret; - } - } - - return 0; -} - -int mi_setup(int cardnr, struct e1inp_line *line, int release_l2) -{ - struct mi_e1_handle *e1h; - int sk, ret, cnt; - struct mISDN_devinfo devinfo; - - /* register the driver with the core */ - /* FIXME: do this in the plugin initializer function */ - ret = e1inp_driver_register(&misdn_driver); - if (ret) - return ret; - - /* create the actual line instance */ - /* FIXME: do this independent of driver registration */ - e1h = malloc(sizeof(*e1h)); - memset(e1h, 0, sizeof(*e1h)); - - e1h->cardnr = cardnr; - - line->driver = &misdn_driver; - line->driver_data = e1h; - - /* open the ISDN card device */ - sk = socket(PF_ISDN, SOCK_RAW, ISDN_P_BASE); - if (sk < 0) { - fprintf(stderr, "%s could not open socket %s\n", - __func__, strerror(errno)); - return sk; - } - - ret = ioctl(sk, IMGETCOUNT, &cnt); - if (ret) { - fprintf(stderr, "%s error getting interf count: %s\n", - __func__, strerror(errno)); - close(sk); - return -ENODEV; - } - //DEBUGP(DMI,"%d device%s found\n", cnt, (cnt==1)?"":"s"); - printf("%d device%s found\n", cnt, (cnt==1)?"":"s"); -#if 1 - devinfo.id = e1h->cardnr; - ret = ioctl(sk, IMGETDEVINFO, &devinfo); - if (ret < 0) { - fprintf(stdout, "error getting info for device %d: %s\n", - e1h->cardnr, strerror(errno)); - return -ENODEV; - } - fprintf(stdout, " id: %d\n", devinfo.id); - fprintf(stdout, " Dprotocols: %08x\n", devinfo.Dprotocols); - fprintf(stdout, " Bprotocols: %08x\n", devinfo.Bprotocols); - fprintf(stdout, " protocol: %d\n", devinfo.protocol); - fprintf(stdout, " nrbchan: %d\n", devinfo.nrbchan); - fprintf(stdout, " name: %s\n", devinfo.name); -#endif - - if (!(devinfo.Dprotocols & (1 << ISDN_P_NT_E1))) { - fprintf(stderr, "error: card is not of type E1 (NT-mode)\n"); - return -EINVAL; - } - - if (devinfo.nrbchan != 30) { - fprintf(stderr, "error: E1 card has no 30 B-channels\n"); - return -EINVAL; - } - - ret = mi_e1_setup(line, release_l2); - if (ret) - return ret; - - return e1inp_line_register(line); -} diff --git a/src/ipaccess-config.c b/src/ipaccess-config.c deleted file mode 100644 index b74e46e89..000000000 --- a/src/ipaccess-config.c +++ /dev/null @@ -1,198 +0,0 @@ -/* ip.access nanoBTS configuration tool */ - -/* (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 <unistd.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <getopt.h> -#include <sys/types.h> - -#include <sys/socket.h> -#include <netinet/in.h> -#include <arpa/inet.h> - - -#include <openbsc/select.h> -#include <openbsc/timer.h> -#include <openbsc/ipaccess.h> -#include <openbsc/gsm_data.h> -#include <openbsc/e1_input.h> -#include <openbsc/abis_nm.h> - -static struct gsm_network *gsmnet; - -static int restart; -static char *prim_oml_ip; -static char *unit_id; - -/* -static u_int8_t prim_oml_attr[] = { 0x95, 0x00, 7, 0x88, 192, 168, 100, 11, 0x00, 0x00 }; -static u_int8_t unit_id_attr[] = { 0x91, 0x00, 9, '2', '3', '4', '2', '/' , '0', '/', '0', 0x00 }; -*/ - -static void bootstrap_om(struct gsm_bts *bts) -{ - int len; - static u_int8_t buf[1024]; - - printf("OML link established\n"); - - if (unit_id) { - len = strlen(unit_id); - if (len > sizeof(buf)-10) - return; - buf[0] = NM_ATT_IPACC_UNIT_ID; - buf[1] = (len+1) >> 8; - buf[2] = (len+1) & 0xff; - memcpy(buf+3, unit_id, len); - buf[3+len] = 0; - printf("setting Unit ID to '%s'\n", unit_id); - abis_nm_ipaccess_set_nvattr(bts, buf, 3+len+1); - } - if (prim_oml_ip) { - struct in_addr ia; - u_int8_t *cur = buf; - - if (!inet_aton(prim_oml_ip, &ia)) { - fprintf(stderr, "invalid IP address: %s\n", - prim_oml_ip); - return; - } - - /* 0x88 + IP + port */ - len = 1 + sizeof(ia) + 2; - - *cur++ = NM_ATT_IPACC_PRIM_OML_IP; - *cur++ = (len) >> 8; - *cur++ = (len) & 0xff; - *cur++ = 0x88; - memcpy(cur, &ia, sizeof(ia)); - cur += sizeof(ia); - *cur++ = 0; - *cur++ = 0; - printf("setting primary OML link IP to '%s'\n", inet_ntoa(ia)); - abis_nm_ipaccess_set_nvattr(bts, buf, 3+len); - } - - if (restart) { - printf("restarting BTS\n"); - abis_nm_ipaccess_restart(bts); - } -} - -void input_event(int event, enum e1inp_sign_type type, struct gsm_bts_trx *trx) -{ - switch (event) { - case EVT_E1_TEI_UP: - switch (type) { - case E1INP_SIGN_OML: - bootstrap_om(trx->bts); - break; - case E1INP_SIGN_RSL: - /* FIXME */ - break; - default: - break; - } - break; - case EVT_E1_TEI_DN: - fprintf(stderr, "Lost some E1 TEI link\n"); - /* FIXME: deal with TEI or L1 link loss */ - break; - default: - break; - } -} - -int nm_state_event(enum nm_evt evt, u_int8_t obj_class, void *obj, - struct gsm_nm_state *old_state, struct gsm_nm_state *new_state) -{ - return 0; -} - -int main(int argc, char **argv) -{ - struct gsm_bts *bts; - struct sockaddr_in sin; - int rc, option_index = 0; - - printf("ipaccess-config (C) 2009 by Harald Welte\n"); - printf("This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY\n\n"); - - while (1) { - int c; - static struct option long_options[] = { - { "unit-id", 1, 0, 'u' }, - { "oml-ip", 1, 0, 'o' }, - { "restart", 0, 0, 'r' }, - }; - - c = getopt_long(argc, argv, "u:o:r", long_options, - &option_index); - - if (c == -1) - break; - - switch (c) { - case 'u': - unit_id = optarg; - break; - case 'o': - prim_oml_ip = optarg; - break; - case 'r': - restart = 1; - break; - } - }; - - if (optind >= argc) { - fprintf(stderr, "you have to specify the IP address of the BTS\n"); - exit(2); - } - - gsmnet = gsm_network_init( 1, GSM_BTS_TYPE_NANOBTS_900, 1, 1); - if (!gsmnet) - exit(1); - - bts = &gsmnet->bts[0]; - - printf("Trying to connect to ip.access BTS ...\n"); - - memset(&sin, 0, sizeof(sin)); - sin.sin_family = AF_INET; - inet_aton(argv[optind], &sin.sin_addr); - rc = ia_config_connect(bts, &sin); - if (rc < 0) { - perror("Error connecting to the BTS"); - exit(1); - } - - while (1) { - rc = bsc_select_main(0); - if (rc < 0) - exit(3); - } - - exit(0); -} - diff --git a/src/ipaccess-find.c b/src/ipaccess-find.c deleted file mode 100644 index b3e9814a9..000000000 --- a/src/ipaccess-find.c +++ /dev/null @@ -1,180 +0,0 @@ - -#include <unistd.h> -#include <stdio.h> -#include <stdlib.h> -#include <sys/types.h> -#include <sys/socket.h> -#include <netinet/in.h> -#include <arpa/inet.h> - - -#include <openbsc/select.h> -#include <openbsc/timer.h> -#include <openbsc/ipaccess.h> -#include <openbsc/gsm_data.h> - -static const char *idtag_names[] = { - [IPAC_IDTAG_SERNR] = "Serial Number", - [IPAC_IDTAG_UNITNAME] = "Unit Name", - [IPAC_IDTAG_LOCATION1] = "Location 1", - [IPAC_IDTAG_LOCATION2] = "Location 2", - [IPAC_IDTAG_EQUIPVERS] = "Equipment Version", - [IPAC_IDTAG_SWVERSION] = "Software Version", - [IPAC_IDTAG_IPADDR] = "IP Address", - [IPAC_IDTAG_MACADDR] = "MAC Address", - [IPAC_IDTAG_UNIT] = "Unit ID", -}; - -static const char *ipac_idtag_name(int tag) -{ - if (tag >= ARRAY_SIZE(idtag_names)) - return "unknown"; - - return idtag_names[tag]; -} - -static int udp_sock(void) -{ - int fd, rc, bc = 1; - struct sockaddr_in sa; - - fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); - if (fd < 0) - return fd; - - sa.sin_family = AF_INET; - sa.sin_port = htons(3006); - sa.sin_addr.s_addr = INADDR_ANY; - inet_aton("192.168.100.11", &sa.sin_addr); - - rc = bind(fd, (struct sockaddr *)&sa, sizeof(sa)); - if (rc < 0) - goto err; - - rc = setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &bc, sizeof(bc)); - if (rc < 0) - goto err; - -#if 0 - rc = connect(fd, (struct sockaddr *)&sa, sizeof(sa)); - if (rc < 0) - goto err; -#endif - return fd; - -err: - close(fd); - return rc; -} - -const unsigned char find_pkt[] = { 0x00, 0x0b+8, IPAC_PROTO_IPACCESS, 0x00, - IPAC_MSGT_ID_GET, - 0x01, IPAC_IDTAG_MACADDR, - 0x01, IPAC_IDTAG_IPADDR, - 0x01, IPAC_IDTAG_UNIT, - 0x01, IPAC_IDTAG_LOCATION1, - 0x01, IPAC_IDTAG_LOCATION2, - 0x01, IPAC_IDTAG_EQUIPVERS, - 0x01, IPAC_IDTAG_SWVERSION, - 0x01, IPAC_IDTAG_UNITNAME, - 0x01, IPAC_IDTAG_SERNR, - }; - - -static int bcast_find(int fd) -{ - struct sockaddr_in sa; - - sa.sin_family = AF_INET; - sa.sin_port = htons(3006); - inet_aton("255.255.255.255", &sa.sin_addr); - - return sendto(fd, find_pkt, sizeof(find_pkt), 0, (struct sockaddr *) &sa, sizeof(sa)); -} - -static int parse_response(unsigned char *buf, int len) -{ - u_int8_t t_len; - u_int8_t t_tag; - u_int8_t *cur = buf; - - while (cur < buf + len) { - t_len = *cur++; - t_tag = *cur++; - - printf("%s='%s' ", ipac_idtag_name(t_tag), cur); - - cur += t_len; - } - printf("\n"); - return 0; -} - -static int read_response(int fd) -{ - unsigned char buf[255]; - struct sockaddr_in sa; - int len; - socklen_t sa_len = sizeof(sa); - - len = recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *)&sa, &sa_len); - if (len < 0) - return len; - - return parse_response(buf+6, len-6); -} - -static int bfd_cb(struct bsc_fd *bfd, unsigned int flags) -{ - if (flags & BSC_FD_READ) - return read_response(bfd->fd); - if (flags & BSC_FD_WRITE) { - bfd->when &= ~BSC_FD_WRITE; - return bcast_find(bfd->fd); - } - return 0; -} - -static struct timer_list timer; - -static void timer_cb(void *_data) -{ - struct bsc_fd *bfd = _data; - - bfd->when |= BSC_FD_WRITE; - - bsc_schedule_timer(&timer, 5, 0); -} - -int main(int argc, char **argv) -{ - struct bsc_fd bfd; - int rc; - - printf("ipaccess-find (C) 2009 by Harald Welte\n"); - printf("This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY\n\n"); - - bfd.cb = bfd_cb; - bfd.when = BSC_FD_READ | BSC_FD_WRITE; - bfd.fd = udp_sock(); - if (bfd.fd < 0) - exit(2); - - bsc_register_fd(&bfd); - - timer.cb = timer_cb; - timer.data = &bfd; - - bsc_schedule_timer(&timer, 5, 0); - - printf("Trying to find ip.access BTS by broadcast UDP...\n"); - - while (1) { - rc = bsc_select_main(0); - if (rc < 0) - exit(3); - } - - exit(0); -} - diff --git a/src/isdnsync.c b/src/isdnsync.c deleted file mode 100644 index d8819ac6b..000000000 --- a/src/isdnsync.c +++ /dev/null @@ -1,192 +0,0 @@ -/* isdnsync.c - * - * Author Andreas Eversberg <jolly@eversberg.eu> - * - * 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 <unistd.h> -#include <sys/ioctl.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <fcntl.h> -#include <errno.h> -#include <sys/socket.h> -#include "mISDNif.h" -#define MISDN_OLD_AF_COMPATIBILITY -#define AF_COMPATIBILITY_FUNC -#include "compat_af_isdn.h" - -int card = 0; -int sock = -1; - -int mISDN_open(void) -{ - int fd, ret; - struct mISDN_devinfo devinfo; - struct sockaddr_mISDN l2addr; - - fd = socket(PF_ISDN, SOCK_RAW, ISDN_P_BASE); - if (fd < 0) { - fprintf(stderr, "could not open socket (%s)\n", strerror(errno)); - return fd; - } - devinfo.id = card; - ret = ioctl(fd, IMGETDEVINFO, &devinfo); - if (ret < 0) { - fprintf(stderr,"could not send IOCTL IMGETCOUNT (%s)\n", strerror(errno)); - close(fd); - return ret; - } - close(fd); - if (!(devinfo.Dprotocols & (1 << ISDN_P_TE_S0)) - && !(devinfo.Dprotocols & (1 << ISDN_P_TE_E1))) { - fprintf(stderr,"Interface does not support TE mode (%s)\n", strerror(errno)); - close(fd); - return ret; - } - fd = socket(PF_ISDN, SOCK_DGRAM, ISDN_P_LAPD_TE); - if (fd < 0) { - fprintf(stderr,"could not open ISDN_P_LAPD_TE socket (%s)\n", strerror(errno)); - return fd; - } - l2addr.family = AF_ISDN; - l2addr.dev = card; - l2addr.channel = 0; - l2addr.sapi = 0; - l2addr.tei = 0; - ret = bind(fd, (struct sockaddr *)&l2addr, sizeof(l2addr)); - if (ret < 0) { - fprintf(stderr,"could not bind socket for card %d (%s)\n", card, strerror(errno)); - close(fd); - return ret; - } - sock = fd; - - return sock; -} - - -void mISDN_handle(void) -{ - int ret; - fd_set rfd; - struct timeval tv; - struct sockaddr_mISDN addr; - socklen_t alen; - unsigned char buffer[2048]; - struct mISDNhead *hh = (struct mISDNhead *)buffer; - int l1 = 0, l2 = 0, tei = 0; - - while(1) { -again: - FD_ZERO(&rfd); - FD_SET(sock, &rfd); - tv.tv_sec = 2; - tv.tv_usec = 0; - ret = select(sock+1, &rfd, NULL, NULL, &tv); - if (ret < 0) { - if (errno == EINTR) - continue; - fprintf(stderr, "%s aborted: %s\n", __FUNCTION__, strerror(errno)); - break; - } - if (FD_ISSET(sock, &rfd)) { - alen = sizeof(addr); - ret = recvfrom(sock, buffer, sizeof(buffer), 0, (struct sockaddr *) &addr, &alen); - if (ret < 0) { - fprintf(stderr, "%s read socket error %s\n", __FUNCTION__, strerror(errno)); - } else if (ret < MISDN_HEADER_LEN) { - fprintf(stderr, "%s read socket shor frame\n", __FUNCTION__); - } else { - switch(hh->prim) { - case MPH_ACTIVATE_IND: - case PH_ACTIVATE_IND: - if (!l1) { - printf("PH_ACTIVATE\n"); - printf("*** Sync available from interface :-)\n"); - l1 = 1; - } - goto again; - break; - case MPH_DEACTIVATE_IND: - case PH_DEACTIVATE_IND: - if (l1) { - printf("PH_DEACTIVATE\n"); - printf("*** Lost sync on interface :-(\n"); - l1 = 0; - } - goto again; - break; - case DL_ESTABLISH_IND: - case DL_ESTABLISH_CNF: - printf("DL_ESTABLISH\n"); - l2 = 1; - goto again; - break; - case DL_RELEASE_IND: - case DL_RELEASE_CNF: - printf("DL_RELEASE\n"); - l2 = 0; - goto again; - break; - case DL_INFORMATION_IND: - printf("DL_INFORMATION (tei %d sapi %d)\n", addr.tei, addr.sapi); - tei = 1; - break; - default: -// printf("prim %x\n", hh->prim); - goto again; - } - } - } - if (tei && !l2) { - hh->prim = DL_ESTABLISH_REQ; - printf("-> activating layer 2\n"); - sendto(sock, buffer, MISDN_HEADER_LEN, 0, (struct sockaddr *) &addr, alen); - } - } -} - -int main(int argc, char *argv[]) -{ - int ret; - - if (argc <= 1) - { - printf("Usage: %s <card>\n\n", argv[0]); - printf("Opens given card number in TE-mode PTP and tries to keep layer 2 established.\n"); - printf("This keeps layer 1 activated to retrieve a steady sync signal from network.\n"); - return(0); - } - - card = atoi(argv[1]); - - init_af_isdn(); - - if ((ret = mISDN_open() < 0)) - return(ret); - - mISDN_handle(); - - close(sock); - - return 0; -} diff --git a/src/msgb.c b/src/msgb.c deleted file mode 100644 index ce390e84a..000000000 --- a/src/msgb.c +++ /dev/null @@ -1,70 +0,0 @@ -/* (C) 2008 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 <unistd.h> -#include <string.h> -#include <stdlib.h> -#include <sys/types.h> - -#include <openbsc/msgb.h> - -struct msgb *msgb_alloc(u_int16_t size) -{ - struct msgb *msg = malloc(sizeof(*msg) + size); - - if (!msg) - return NULL; - memset(msg, 0, sizeof(*msg)+size); - - msg->data_len = size; - msg->len = 0; - msg->data = msg->_data; - - msg->head = msg->data; - msg->data = msg->data; - /* reset tail pointer */ - msg->tail = msg->data; - //msg->end = msg->tail + size; - - return msg; -} - -void msgb_free(struct msgb *m) -{ - free(m); -} - -void msgb_enqueue(struct llist_head *queue, struct msgb *msg) -{ - llist_add_tail(&msg->list, queue); -} - -struct msgb *msgb_dequeue(struct llist_head *queue) -{ - struct llist_head *lh; - - if (llist_empty(queue)) - return NULL; - - lh = queue->next; - llist_del(lh); - - return llist_entry(lh, struct msgb, list); -} diff --git a/src/paging.c b/src/paging.c deleted file mode 100644 index d777d6642..000000000 --- a/src/paging.c +++ /dev/null @@ -1,262 +0,0 @@ -/* Paging helper and manager.... */ -/* (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. - * - */ - -/* - * Relevant specs: - * 12.21: - * - 9.4.12 for CCCH Local Threshold - * - * 05.58: - * - 8.5.2 CCCH Load indication - * - 9.3.15 Paging Load - * - * Approach: - * - Send paging command to subscriber - * - On Channel Request we will remember the reason - * - After the ACK we will request the identity - * - Then we will send assign the gsm_subscriber and - * - and call a callback - */ - -#include <stdio.h> -#include <stdlib.h> -#include <assert.h> - -#include <openbsc/paging.h> -#include <openbsc/debug.h> -#include <openbsc/signal.h> -#include <openbsc/abis_rsl.h> -#include <openbsc/gsm_data.h> - -#define PAGING_TIMEOUT 1, 75000 -#define MAX_PAGING_REQUEST 750 - -static unsigned int calculate_group(struct gsm_bts *bts, struct gsm_subscriber *subscr) -{ - int ccch_conf; - int bs_cc_chans; - int blocks; - unsigned int group; - - ccch_conf = bts->chan_desc.ccch_conf; - bs_cc_chans = rsl_ccch_conf_to_bs_cc_chans(ccch_conf); - /* code word + 2, as 2 channels equals 0x0 */ - blocks = rsl_number_of_paging_subchannels(bts); - group = get_paging_group(str_to_imsi(subscr->imsi), - bs_cc_chans, blocks); - return group; -} - -/* - * Kill one paging request update the internal list... - */ -static void paging_remove_request(struct gsm_bts_paging_state *paging_bts, - struct gsm_paging_request *to_be_deleted) -{ - /* Update the last_request if that is necessary */ - if (to_be_deleted == paging_bts->last_request) { - paging_bts->last_request = - (struct gsm_paging_request *)paging_bts->last_request->entry.next; - if (&to_be_deleted->entry == &paging_bts->pending_requests) - paging_bts->last_request = NULL; - } - - bsc_del_timer(&to_be_deleted->T3113); - llist_del(&to_be_deleted->entry); - subscr_put(to_be_deleted->subscr); - free(to_be_deleted); -} - -static void page_ms(struct gsm_paging_request *request) -{ - u_int8_t mi[128]; - unsigned long int tmsi; - unsigned int mi_len; - unsigned int page_group; - - DEBUGP(DPAG, "Going to send paging commands: '%s'\n", - request->subscr->imsi); - - page_group = calculate_group(request->bts, request->subscr); - tmsi = strtoul(request->subscr->tmsi, NULL, 10); - mi_len = generate_mid_from_tmsi(mi, tmsi); - rsl_paging_cmd(request->bts, page_group, mi_len, mi, - request->chan_type); -} - -static void paging_move_to_next(struct gsm_bts_paging_state *paging_bts) -{ - paging_bts->last_request = - (struct gsm_paging_request *)paging_bts->last_request->entry.next; - if (&paging_bts->last_request->entry == &paging_bts->pending_requests) - paging_bts->last_request = NULL; -} - -/* - * This is kicked by the periodic PAGING LOAD Indicator - * coming from abis_rsl.c - * - * We attempt to iterate once over the list of items but - * only upto available_slots. - */ -static void paging_handle_pending_requests(struct gsm_bts_paging_state *paging_bts) -{ - struct gsm_paging_request *initial_request = NULL; - struct gsm_paging_request *current_request = NULL; - - /* - * Determine if the pending_requests list is empty and - * return then. - */ - if (llist_empty(&paging_bts->pending_requests)) { - paging_bts->last_request = NULL; - /* since the list is empty, no need to reschedule the timer */ - return; - } - - if (!paging_bts->last_request) - paging_bts->last_request = - (struct gsm_paging_request *)paging_bts->pending_requests.next; - - assert(paging_bts->last_request); - initial_request = paging_bts->last_request; - current_request = initial_request; - - do { - /* handle the paging request now */ - page_ms(current_request); - paging_bts->available_slots--; - - /* - * move to the next item. We might wrap around - * this means last_request will be NULL and we just - * call paging_page_to_next again. It it guranteed - * that the list is not empty. - */ - paging_move_to_next(paging_bts); - if (!paging_bts->last_request) - paging_bts->last_request = - (struct gsm_paging_request *)paging_bts->pending_requests.next; - current_request = paging_bts->last_request; - } while (paging_bts->available_slots > 0 - && initial_request != current_request); - - bsc_schedule_timer(&paging_bts->work_timer, 1, 0); -} - -static void paging_worker(void *data) -{ - struct gsm_bts_paging_state *paging_bts = data; - - paging_handle_pending_requests(paging_bts); -} - -void paging_init(struct gsm_bts *bts) -{ - bts->paging.bts = bts; - INIT_LLIST_HEAD(&bts->paging.pending_requests); - bts->paging.work_timer.cb = paging_worker; - bts->paging.work_timer.data = &bts->paging; - - /* Large number, until we get a proper message */ - bts->paging.available_slots = 100; -} - -static int paging_pending_request(struct gsm_bts_paging_state *bts, - struct gsm_subscriber *subscr) { - struct gsm_paging_request *req; - - llist_for_each_entry(req, &bts->pending_requests, entry) { - if (subscr == req->subscr) - return 1; - } - - return 0; -} - -static void paging_T3113_expired(void *data) -{ - struct gsm_paging_request *req = (struct gsm_paging_request *)data; - struct paging_signal_data sig_data; - - DEBUGP(DPAG, "T3113 expired for request %p (%s)\n", - req, req->subscr->imsi); - - sig_data.subscr = req->subscr, - 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); - paging_remove_request(&req->bts->paging, req); -} - -void paging_request(struct gsm_bts *bts, struct gsm_subscriber *subscr, - int type, gsm_cbfn *cbfn, void *data) -{ - struct gsm_bts_paging_state *bts_entry = &bts->paging; - struct gsm_paging_request *req; - - if (paging_pending_request(bts_entry, subscr)) { - DEBUGP(DPAG, "Paging request already pending\n"); - return; - } - - req = (struct gsm_paging_request *)malloc(sizeof(*req)); - memset(req, 0, sizeof(*req)); - req->subscr = subscr_get(subscr); - req->bts = bts; - req->chan_type = type; - req->cbfn = cbfn; - req->cbfn_param = data; - req->T3113.cb = paging_T3113_expired; - req->T3113.data = req; - bsc_schedule_timer(&req->T3113, T3113_VALUE); - llist_add_tail(&req->entry, &bts_entry->pending_requests); - - if (!bsc_timer_pending(&bts_entry->work_timer)) - bsc_schedule_timer(&bts_entry->work_timer, 1, 0); -} - -/* we consciously ignore the type of the request here */ -void paging_request_stop(struct gsm_bts *bts, struct gsm_subscriber *subscr, - struct gsm_lchan *lchan) -{ - struct gsm_bts_paging_state *bts_entry = &bts->paging; - struct gsm_paging_request *req, *req2; - - llist_for_each_entry_safe(req, req2, &bts_entry->pending_requests, - entry) { - if (req->subscr == subscr) { - if (req->cbfn) - req->cbfn(GSM_HOOK_RR_PAGING, GSM_PAGING_SUCCEEDED, - NULL, lchan, req->cbfn_param); - paging_remove_request(&bts->paging, req); - break; - } - } -} - -void paging_update_buffer_space(struct gsm_bts *bts, u_int16_t free_slots) -{ - bts->paging.available_slots = free_slots; -} diff --git a/src/rs232.c b/src/rs232.c deleted file mode 100644 index 2a64de5ef..000000000 --- a/src/rs232.c +++ /dev/null @@ -1,249 +0,0 @@ -/* OpenBSC BS-11 T-Link interface using POSIX serial port */ - -/* (C) 2008-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 <unistd.h> -#include <stdlib.h> -#include <stdio.h> -#include <errno.h> -#include <string.h> -#include <termios.h> -#include <fcntl.h> - -#include <openbsc/select.h> -#include <openbsc/msgb.h> -#include <openbsc/debug.h> -#include <openbsc/gsm_data.h> -#include <openbsc/rs232.h> - -/* adaption layer from GSM 08.59 + 12.21 to RS232 */ - -struct serial_handle { - struct bsc_fd fd; - struct llist_head tx_queue; - - struct msgb *rx_msg; - unsigned int rxmsg_bytes_missing; - - unsigned int delay_ms; - struct gsm_bts *bts; -}; - -/* FIXME: this needs to go */ -static struct serial_handle _ser_handle, *ser_handle = &_ser_handle; - -#define LAPD_HDR_LEN 10 - -static int handle_ser_write(struct bsc_fd *bfd); - -/* callback from abis_nm */ -int _abis_nm_sendmsg(struct msgb *msg) -{ - struct serial_handle *sh = ser_handle; - u_int8_t *lapd; - unsigned int len; - - msg->l2h = msg->data; - - /* prepend LAPD header */ - lapd = msgb_push(msg, LAPD_HDR_LEN); - - len = msg->len - 2; - - lapd[0] = (len >> 8) & 0xff; - lapd[1] = len & 0xff; /* length of bytes startign at lapd[2] */ - lapd[2] = 0x00; - lapd[3] = 0x07; - lapd[4] = 0x01; - lapd[5] = 0x3e; - lapd[6] = 0x00; - lapd[7] = 0x00; - lapd[8] = msg->len - 10; /* length of bytes starting at lapd[10] */ - lapd[9] = lapd[8] ^ 0x38; - - msgb_enqueue(&sh->tx_queue, msg); - sh->fd.when |= BSC_FD_WRITE; - - /* we try to immediately send */ - handle_ser_write(&sh->fd); - - return 0; -} - -/* select.c callback in case we can write to the RS232 */ -static int handle_ser_write(struct bsc_fd *bfd) -{ - struct serial_handle *sh = bfd->data; - struct msgb *msg; - int written; - - msg = msgb_dequeue(&sh->tx_queue); - if (!msg) { - bfd->when &= ~BSC_FD_WRITE; - return 0; - } - - DEBUGP(DMI, "RS232 TX: %s\n", hexdump(msg->data, msg->len)); - - /* send over serial line */ - written = write(bfd->fd, msg->data, msg->len); - if (written < msg->len) { - perror("short write:"); - msgb_free(msg); - return -1; - } - - msgb_free(msg); - usleep(sh->delay_ms*1000); - - return 0; -} - -#define SERIAL_ALLOC_SIZE 300 - -/* select.c callback in case we can read from the RS232 */ -static int handle_ser_read(struct bsc_fd *bfd) -{ - struct serial_handle *sh = bfd->data; - struct msgb *msg; - int rc = 0; - - if (!sh->rx_msg) { - sh->rx_msg = msgb_alloc(SERIAL_ALLOC_SIZE); - sh->rx_msg->l2h = NULL; - sh->rx_msg->trx = sh->bts->c0; - } - msg = sh->rx_msg; - - /* first read two byes to obtain length */ - if (msg->len < 2) { - rc = read(sh->fd.fd, msg->tail, 2 - msg->len); - if (rc < 0) { - perror("ERROR reading from serial port"); - msgb_free(msg); - return rc; - } - msgb_put(msg, rc); - - if (msg->len >= 2) { - /* parse LAPD payload length */ - if (msg->data[0] != 0) - fprintf(stderr, "Suspicious header byte 0: 0x%02x\n", - msg->data[0]); - - sh->rxmsg_bytes_missing = msg->data[0] << 8; - sh->rxmsg_bytes_missing += msg->data[1]; - - if (sh->rxmsg_bytes_missing < LAPD_HDR_LEN -2) - fprintf(stderr, "Invalid length in hdr: %u\n", - sh->rxmsg_bytes_missing); - } - } else { - /* try to read as many of the missing bytes as are available */ - rc = read(sh->fd.fd, msg->tail, sh->rxmsg_bytes_missing); - if (rc < 0) { - perror("ERROR reading from serial port"); - msgb_free(msg); - return rc; - } - msgb_put(msg, rc); - sh->rxmsg_bytes_missing -= rc; - - if (sh->rxmsg_bytes_missing == 0) { - /* we have one complete message now */ - sh->rx_msg = NULL; - - if (msg->len > LAPD_HDR_LEN) - msg->l2h = msg->data + LAPD_HDR_LEN; - - DEBUGP(DMI, "RS232 RX: %s\n", hexdump(msg->data, msg->len)); - rc = handle_serial_msg(msg); - } - } - - return rc; -} - -/* select.c callback */ -static int serial_fd_cb(struct bsc_fd *bfd, unsigned int what) -{ - int rc = 0; - - if (what & BSC_FD_READ) - rc = handle_ser_read(bfd); - - if (rc < 0) - return rc; - - if (what & BSC_FD_WRITE) - rc = handle_ser_write(bfd); - - return rc; -} - -int rs232_setup(const char *serial_port, unsigned int delay_ms, - struct gsm_bts *bts) -{ - int rc, serial_fd; - struct termios tio; - - serial_fd = open(serial_port, O_RDWR); - if (serial_fd < 0) { - perror("cannot open serial port:"); - return serial_fd; - } - - /* set baudrate */ - rc = tcgetattr(serial_fd, &tio); - if (rc < 0) { - perror("tcgetattr()"); - return rc; - } - cfsetispeed(&tio, B19200); - cfsetospeed(&tio, B19200); - tio.c_cflag |= (CREAD | CLOCAL | CS8); - tio.c_cflag &= ~(PARENB | CSTOPB | CSIZE | CRTSCTS); - tio.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); - tio.c_iflag |= (INPCK | ISTRIP); - tio.c_iflag &= ~(ISTRIP | IXON | IXOFF | IGNBRK | INLCR | ICRNL | IGNCR); - tio.c_oflag &= ~(OPOST); - rc = tcsetattr(serial_fd, TCSADRAIN, &tio); - if (rc < 0) { - perror("tcsetattr()"); - return rc; - } - - INIT_LLIST_HEAD(&ser_handle->tx_queue); - ser_handle->fd.fd = serial_fd; - ser_handle->fd.when = BSC_FD_READ; - ser_handle->fd.cb = serial_fd_cb; - ser_handle->fd.data = ser_handle; - ser_handle->delay_ms = delay_ms; - ser_handle->bts = bts; - rc = bsc_register_fd(&ser_handle->fd); - if (rc < 0) { - fprintf(stderr, "could not register FD: %s\n", - strerror(rc)); - return rc; - } - - return 0; -} diff --git a/src/select.c b/src/select.c deleted file mode 100644 index 11b7e6b49..000000000 --- a/src/select.c +++ /dev/null @@ -1,107 +0,0 @@ -/* select filedescriptor handling, taken from: - * userspace logging daemon for the iptables ULOG target - * of the linux 2.4 netfilter subsystem. - * - * (C) 2000-2009 by Harald Welte <laforge@gnumonks.org> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation - * - * 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 - */ - -#include <fcntl.h> -#include <openbsc/select.h> -#include <openbsc/linuxlist.h> -#include <openbsc/timer.h> - -static int maxfd = 0; -static LLIST_HEAD(bsc_fds); - -int bsc_register_fd(struct bsc_fd *fd) -{ - int flags; - - /* make FD nonblocking */ - flags = fcntl(fd->fd, F_GETFL); - if (flags < 0) - return flags; - flags |= O_NONBLOCK; - flags = fcntl(fd->fd, F_SETFL, flags); - if (flags < 0) - return flags; - - /* Register FD */ - if (fd->fd > maxfd) - maxfd = fd->fd; - - llist_add_tail(&fd->list, &bsc_fds); - - return 0; -} - -void bsc_unregister_fd(struct bsc_fd *fd) -{ - llist_del(&fd->list); -} - -int bsc_select_main(int polling) -{ - struct bsc_fd *ufd, *tmp; - fd_set readset, writeset, exceptset; - int work = 0, rc; - struct timeval no_time = {0, 0}; - - FD_ZERO(&readset); - FD_ZERO(&writeset); - FD_ZERO(&exceptset); - - /* prepare read and write fdsets */ - llist_for_each_entry(ufd, &bsc_fds, list) { - if (ufd->when & BSC_FD_READ) - FD_SET(ufd->fd, &readset); - - if (ufd->when & BSC_FD_WRITE) - FD_SET(ufd->fd, &writeset); - - if (ufd->when & BSC_FD_EXCEPT) - FD_SET(ufd->fd, &exceptset); - } - - if (!polling) - bsc_prepare_timers(); - rc = select(maxfd+1, &readset, &writeset, &exceptset, polling ? &no_time : bsc_nearest_timer()); - if (rc < 0) - return 0; - - /* fire timers */ - bsc_update_timers(); - - /* call registered callback functions */ - llist_for_each_entry_safe(ufd, tmp, &bsc_fds, list) { - int flags = 0; - - if (FD_ISSET(ufd->fd, &readset)) - flags |= BSC_FD_READ; - - if (FD_ISSET(ufd->fd, &writeset)) - flags |= BSC_FD_WRITE; - - if (FD_ISSET(ufd->fd, &exceptset)) - flags |= BSC_FD_EXCEPT; - - if (flags) { - work = 1; - ufd->cb(ufd, flags); - } - } - return work; -} diff --git a/src/signal.c b/src/signal.c deleted file mode 100644 index 4227c6dc1..000000000 --- a/src/signal.c +++ /dev/null @@ -1,80 +0,0 @@ -/* Generic signalling/notification infrastructure */ -/* (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. - * - */ - -#include <openbsc/signal.h> -#include <stdlib.h> -#include <string.h> - - -static LLIST_HEAD(signal_handler_list); - -struct signal_handler { - struct llist_head entry; - unsigned int subsys; - signal_cbfn *cbfn; - void *data; -}; - - -int register_signal_handler(unsigned int subsys, signal_cbfn *cbfn, void *data) -{ - struct signal_handler *sig_data = malloc(sizeof(*sig_data)); - - if (!sig_data) - return -ENOMEM; - - memset(sig_data, 0, sizeof(*sig_data)); - - sig_data->subsys = subsys; - sig_data->data = data; - sig_data->cbfn = cbfn; - - /* FIXME: check if we already have a handler for this subsys/cbfn/data */ - - llist_add_tail(&sig_data->entry, &signal_handler_list); - - return 0; -} - -void unregister_signal_handler(unsigned int subsys, signal_cbfn *cbfn, void *data) -{ - struct signal_handler *handler; - - llist_for_each_entry(handler, &signal_handler_list, entry) { - if (handler->cbfn == cbfn && handler->data == data - && subsys == handler->subsys) { - llist_del(&handler->entry); - free(handler); - break; - } - } -} - - -void dispatch_signal(unsigned int subsys, unsigned int signal, void *signal_data) -{ - struct signal_handler *handler; - - llist_for_each_entry(handler, &signal_handler_list, entry) { - if (handler->subsys != subsys) - continue; - (*handler->cbfn)(subsys, signal, handler->data, signal_data); - } -} diff --git a/src/subchan_demux.c b/src/subchan_demux.c deleted file mode 100644 index c662bcd0e..000000000 --- a/src/subchan_demux.c +++ /dev/null @@ -1,319 +0,0 @@ -/* A E1 sub-channel (de)multiplexer with TRAU frame sync */ - -/* (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 <unistd.h> -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <errno.h> - -#include <openbsc/subchan_demux.h> -#include <openbsc/trau_frame.h> -#include <openbsc/debug.h> - -static inline void append_bit(struct demux_subch *sch, u_int8_t bit) -{ - sch->out_bitbuf[sch->out_idx++] = bit; -} - -#define SYNC_HDR_BITS 16 -static const u_int8_t nullbytes[SYNC_HDR_BITS]; - -/* check if we have just completed the 16 bit zero sync header, - * in accordance with GSM TS 08.60 Chapter 4.8.1 */ -static int sync_hdr_complete(struct demux_subch *sch, u_int8_t bit) -{ - if (bit == 0) - sch->consecutive_zeros++; - else - sch->consecutive_zeros = 0; - - if (sch->consecutive_zeros >= SYNC_HDR_BITS) { - sch->consecutive_zeros = 0; - return 1; - } - - return 0; -} - -/* resynchronize to current location */ -static void resync_to_here(struct demux_subch *sch) -{ - memset(sch->out_bitbuf, 0, SYNC_HDR_BITS); - - /* set index in a way that we can continue receiving bits after - * the end of the SYNC header */ - sch->out_idx = SYNC_HDR_BITS; - sch->in_sync = 1; -} - -int subch_demux_init(struct subch_demux *dmx) -{ - int i; - - dmx->chan_activ = 0; - for (i = 0; i < NR_SUBCH; i++) { - struct demux_subch *sch = &dmx->subch[i]; - sch->out_idx = 0; - memset(sch->out_bitbuf, 0xff, sizeof(sch->out_bitbuf)); - } - return 0; -} - -/* input some arbitrary (modulo 4) number of bytes of a 64k E1 channel, - * split it into the 16k subchannels */ -int subch_demux_in(struct subch_demux *dmx, u_int8_t *data, int len) -{ - int i, c; - - /* we avoid partially filled bytes in outbuf */ - if (len % 4) - return -EINVAL; - - for (i = 0; i < len; i++) { - u_int8_t inbyte = data[i]; - - for (c = 0; c < NR_SUBCH; c++) { - struct demux_subch *sch = &dmx->subch[c]; - u_int8_t inbits; - u_int8_t bit; - - /* ignore inactive subchannels */ - if (!(dmx->chan_activ & (1 << c))) - continue; - - inbits = inbyte >> (c << 1); - - /* two bits for each subchannel */ - if (inbits & 0x01) - bit = 1; - else - bit = 0; - append_bit(sch, bit); - - if (sync_hdr_complete(sch, bit)) - resync_to_here(sch); - - if (inbits & 0x02) - bit = 1; - else - bit = 0; - append_bit(sch, bit); - - if (sync_hdr_complete(sch, bit)) - resync_to_here(sch); - - /* FIXME: verify the first bit in octet 2, 4, 6, ... - * according to TS 08.60 4.8.1 */ - - /* once we have reached TRAU_FRAME_BITS, call - * the TRAU frame handler callback function */ - if (sch->out_idx >= TRAU_FRAME_BITS) { - if (sch->in_sync) { - dmx->out_cb(dmx, c, sch->out_bitbuf, - sch->out_idx, dmx->data); - sch->in_sync = 0; - } - sch->out_idx = 0; - } - } - } - return i; -} - -int subch_demux_activate(struct subch_demux *dmx, int subch) -{ - if (subch >= NR_SUBCH) - return -EINVAL; - - dmx->chan_activ |= (1 << subch); - return 0; -} - -int subch_demux_deactivate(struct subch_demux *dmx, int subch) -{ - if (subch >= NR_SUBCH) - return -EINVAL; - - dmx->chan_activ &= ~(1 << subch); - return 0; -} - -/* MULTIPLEXER */ - -static int alloc_add_idle_frame(struct subch_mux *mx, int sch_nr) -{ - /* allocate and initialize with idle pattern */ - return subchan_mux_enqueue(mx, sch_nr, trau_idle_frame(), - TRAU_FRAME_BITS); -} - -/* return the requested number of bits from the specified subchannel */ -static int get_subch_bits(struct subch_mux *mx, int subch, - u_int8_t *bits, int num_requested) -{ - struct mux_subch *sch = &mx->subch[subch]; - int num_bits = 0; - - while (num_bits < num_requested) { - struct subch_txq_entry *txe; - int num_bits_left; - int num_bits_thistime; - - /* make sure we have a valid entry at top of tx queue. - * if not, add an idle frame */ - if (llist_empty(&sch->tx_queue)) - alloc_add_idle_frame(mx, subch); - - if (llist_empty(&sch->tx_queue)) - return -EIO; - - txe = llist_entry(sch->tx_queue.next, struct subch_txq_entry, list); - num_bits_left = txe->bit_len - txe->next_bit; - - if (num_bits_left < num_requested) - num_bits_thistime = num_bits_left; - else - num_bits_thistime = num_requested; - - /* pull the bits from the txe */ - memcpy(bits + num_bits, txe->bits + txe->next_bit, num_bits_thistime); - txe->next_bit += num_bits_thistime; - - /* free the tx_queue entry if it is fully consumed */ - if (txe->next_bit >= txe->bit_len) { - llist_del(&txe->list); - free(txe); - } - - /* increment global number of bits dequeued */ - num_bits += num_bits_thistime; - } - - return num_requested; -} - -/* compact an array of 8 single-bit bytes into one byte of 8 bits */ -static u_int8_t compact_bits(const u_int8_t *bits) -{ - u_int8_t ret = 0; - int i; - - for (i = 0; i < 8; i++) - ret |= (bits[i] ? 1 : 0) << i; - - return ret; -} - -/* obtain a single output byte from the subchannel muxer */ -static int mux_output_byte(struct subch_mux *mx, u_int8_t *byte) -{ - u_int8_t bits[8]; - int rc; - - /* combine two bits of every subchan */ - rc = get_subch_bits(mx, 0, &bits[0], 2); - rc = get_subch_bits(mx, 1, &bits[2], 2); - rc = get_subch_bits(mx, 2, &bits[4], 2); - rc = get_subch_bits(mx, 3, &bits[6], 2); - - *byte = compact_bits(bits); - - return rc; -} - -/* Request the output of some muxed bytes from the subchan muxer */ -int subchan_mux_out(struct subch_mux *mx, u_int8_t *data, int len) -{ - int i; - - for (i = 0; i < len; i++) { - int rc; - rc = mux_output_byte(mx, &data[i]); - if (rc < 0) - break; - } - return i; -} - -static int llist_len(struct llist_head *head) -{ - struct llist_head *entry; - int i = 0; - - llist_for_each(entry, head) - i++; - - return i; -} - -/* evict the 'num_evict' number of oldest entries in the queue */ -static void tx_queue_evict(struct mux_subch *sch, int num_evict) -{ - struct subch_txq_entry *tqe; - int i; - - for (i = 0; i < num_evict; i++) { - if (llist_empty(&sch->tx_queue)) - return; - - tqe = llist_entry(sch->tx_queue.next, struct subch_txq_entry, list); - llist_del(&tqe->list); - free(tqe); - } -} - -/* enqueue some data into the tx_queue of a given subchannel */ -int subchan_mux_enqueue(struct subch_mux *mx, int s_nr, const u_int8_t *data, - int len) -{ - struct mux_subch *sch = &mx->subch[s_nr]; - struct subch_txq_entry *tqe = malloc(sizeof(*tqe) + len); - int list_len = llist_len(&sch->tx_queue); - - if (!tqe) - return -ENOMEM; - - memset(tqe, 0, sizeof(*tqe)); - tqe->bit_len = len; - memcpy(tqe->bits, data, len); - - if (list_len > 2) - tx_queue_evict(sch, list_len-2); - - llist_add_tail(&tqe->list, &sch->tx_queue); - - return 0; -} - -/* initialize one subchannel muxer instance */ -int subchan_mux_init(struct subch_mux *mx) -{ - int i; - - memset(mx, 0, sizeof(*mx)); - for (i = 0; i < NR_SUBCH; i++) { - struct mux_subch *sch = &mx->subch[i]; - INIT_LLIST_HEAD(&sch->tx_queue); - } - - return 0; -} diff --git a/src/telnet_interface.c b/src/telnet_interface.c deleted file mode 100644 index f4ffb529e..000000000 --- a/src/telnet_interface.c +++ /dev/null @@ -1,236 +0,0 @@ -/* minimalistic telnet/network interface it might turn into a wire interface */ -/* (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. - * - */ - -#include <sys/socket.h> -#include <netinet/in.h> -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <unistd.h> - -#include <openbsc/telnet_interface.h> -#include <openbsc/gsm_subscriber.h> -#include <openbsc/chan_alloc.h> -#include <openbsc/gsm_04_08.h> -#include <openbsc/gsm_04_11.h> -#include <openbsc/msgb.h> -#include <openbsc/abis_rsl.h> -#include <openbsc/paging.h> -#include <openbsc/signal.h> - -#include <vty/buffer.h> - -#define WRITE_CONNECTION(fd, msg...) \ - int ret; \ - char buf[4096]; \ - snprintf(buf, sizeof(buf), msg); \ - ret = write(fd, buf, strlen(buf)); - - -/* per connection data */ -LLIST_HEAD(active_connections); - -/* per network data */ -static int telnet_new_connection(struct bsc_fd *fd, unsigned int what); -#if 0 -static int telnet_paging_callback(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data); -static int telnet_sms_callback(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data); -#endif - -static struct bsc_fd server_socket = { - .when = BSC_FD_READ, - .cb = telnet_new_connection, - .priv_nr = 0, -}; - -void telnet_init(struct gsm_network *network, int port) { - struct sockaddr_in sock_addr; - int fd, on = 1; - - bsc_vty_init(network); - - fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); - - if (fd < 0) { - perror("Telnet interface socket creation failed"); - return; - } - - setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); - - memset(&sock_addr, 0, sizeof(sock_addr)); - sock_addr.sin_family = AF_INET; - sock_addr.sin_port = htons(port); - sock_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - - if (bind(fd, (struct sockaddr*)&sock_addr, sizeof(sock_addr)) < 0) { - perror("Telnet interface failed to bind"); - return; - } - - if (listen(fd, 0) < 0) { - perror("Telnet interface failed to listen"); - return; - } - - server_socket.data = network; - server_socket.fd = fd; - bsc_register_fd(&server_socket); - - /* register callbacks */ -#if 0 - register_signal_handler(SS_PAGING, telnet_paging_callback, network); - register_signal_handler(SS_SMS, telnet_sms_callback, network); -#endif -} - -static void print_welcome(int fd) { - int ret; - static char *msg = - "Welcome to the OpenBSC Control interface\n" - "Copyright (C) 2008, 2009 Harald Welte\n" - "Contributions by Daniel Willmann, Jan Lübbe, " - "Stefan Schmidt, Holger Freyther\n\n" - "License GPLv2+: GNU GPL version 2 or later " - "<http://gnu.org/licenses/gpl.html>\n" - "This is free software: you are free to change " - "and redistribute it.\n" - "There is NO WARRANTY, to the extent permitted " - "by law.\nType \"help\" to get a short introduction.\n"; - - ret = write(fd, msg, strlen(msg)); -} - -int telnet_close_client(struct bsc_fd *fd) { - struct telnet_connection *conn = (struct telnet_connection*)fd->data; - - close(fd->fd); - bsc_unregister_fd(fd); - llist_del(&conn->entry); - free(conn); - return 0; -} - -static int client_data(struct bsc_fd *fd, unsigned int what) -{ - struct telnet_connection *conn = fd->data; - int rc = 0; - - if (what & BSC_FD_READ) { - conn->fd.when &= ~BSC_FD_READ; - rc = vty_read(conn->vty); - } - - if (what & BSC_FD_WRITE) { - rc = buffer_flush_all(conn->vty->obuf, fd->fd); - if (rc == BUFFER_EMPTY) - conn->fd.when &= ~BSC_FD_WRITE; - } - - return rc; -} - -static int telnet_new_connection(struct bsc_fd *fd, unsigned int what) { - struct telnet_connection *connection; - struct sockaddr_in sockaddr; - socklen_t len = sizeof(sockaddr); - int new_connection = accept(fd->fd, (struct sockaddr*)&sockaddr, &len); - - if (new_connection < 0) { - perror("telnet accept failed"); - return -1; - } - - - connection = (struct telnet_connection*)malloc(sizeof(*connection)); - memset(connection, 0, sizeof(*connection)); - connection->network = (struct gsm_network*)fd->data; - connection->fd.data = connection; - 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); - - print_welcome(new_connection); - - connection->vty = vty_create(new_connection, connection); - if (!connection->vty) - return -1; - - return 0; -} - -/* callback from VTY code */ -void vty_event(enum event event, int sock, struct vty *vty) -{ - struct telnet_connection *connection = vty->priv; - struct bsc_fd *bfd = &connection->fd; - - switch (event) { - case VTY_READ: - bfd->when |= BSC_FD_READ; - break; - case VTY_WRITE: - bfd->when |= BSC_FD_WRITE; - break; - default: - break; - } -} - -#if 0 -static int telnet_paging_callback(unsigned int subsys, unsigned int singal, - void *handler_data, void *signal_data) -{ - struct paging_signal_data *paging = signal_data; - struct telnet_connection *con; - - llist_for_each_entry(con, &active_connections, entry) { - if (paging->lchan) { - WRITE_CONNECTION(con->fd.fd, "Paging succeeded\n"); - show_lchan(con->fd.fd, paging->lchan); - } else { - WRITE_CONNECTION(con->fd.fd, "Paging failed for subscriber: %s/%s/%s\n", - paging->subscr->imsi, - paging->subscr->tmsi, - paging->subscr->name); - } - } - - return 0; -} - -static int telnet_sms_callback(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data) -{ - struct sms_submit *sms = signal_data; - struct telnet_connection *con; - - llist_for_each_entry(con, &active_connections, entry) { - WRITE_CONNECTION(con->fd.fd, "Incoming SMS: %s\n", sms->user_data); - } - - return 0; -} -#endif diff --git a/src/timer.c b/src/timer.c deleted file mode 100644 index a942ffd6d..000000000 --- a/src/timer.c +++ /dev/null @@ -1,174 +0,0 @@ -/* - * (C) 2008,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. - * - */ - -#include <assert.h> -#include <string.h> -#include <openbsc/timer.h> - -static LLIST_HEAD(timer_list); -static struct timeval s_nearest_time; -static struct timeval s_select_time; - -#define MICRO_SECONDS 1000000LL - -#define TIME_SMALLER(left, right) \ - (left.tv_sec*MICRO_SECONDS+left.tv_usec) <= (right.tv_sec*MICRO_SECONDS+right.tv_usec) - -void bsc_add_timer(struct timer_list *timer) -{ - struct timer_list *list_timer; - - /* TODO: Optimize and remember the closest item... */ - timer->active = 1; - - /* this might be called from within update_timers */ - llist_for_each_entry(list_timer, &timer_list, entry) - if (timer == list_timer) - return; - - timer->in_list = 1; - llist_add(&timer->entry, &timer_list); -} - -void bsc_schedule_timer(struct timer_list *timer, int seconds, int microseconds) -{ - struct timeval current_time; - - gettimeofday(¤t_time, NULL); - unsigned long long currentTime = current_time.tv_sec * MICRO_SECONDS + current_time.tv_usec; - currentTime += seconds * MICRO_SECONDS + microseconds; - timer->timeout.tv_sec = currentTime / MICRO_SECONDS; - timer->timeout.tv_usec = currentTime % MICRO_SECONDS; - bsc_add_timer(timer); -} - -void bsc_del_timer(struct timer_list *timer) -{ - if (timer->in_list) { - timer->active = 0; - timer->in_list = 0; - llist_del(&timer->entry); - } -} - -int bsc_timer_pending(struct timer_list *timer) -{ - return timer->active; -} - -/* - * if we have a nearest time return the delta between the current - * time and the time of the nearest timer. - * If the nearest timer timed out return NULL and then we will - * dispatch everything after the select - */ -struct timeval *bsc_nearest_timer() -{ - struct timeval current_time; - - if (s_nearest_time.tv_sec == 0 && s_nearest_time.tv_usec == 0) - return NULL; - - if (gettimeofday(¤t_time, NULL) == -1) - return NULL; - - unsigned long long nearestTime = s_nearest_time.tv_sec * MICRO_SECONDS + s_nearest_time.tv_usec; - unsigned long long currentTime = current_time.tv_sec * MICRO_SECONDS + current_time.tv_usec; - - if (nearestTime < currentTime) { - s_select_time.tv_sec = 0; - s_select_time.tv_usec = 0; - } else { - s_select_time.tv_sec = (nearestTime - currentTime) / MICRO_SECONDS; - s_select_time.tv_usec = (nearestTime - currentTime) % MICRO_SECONDS; - } - - return &s_select_time; -} - -/* - * Find the nearest time and update s_nearest_time - */ -void bsc_prepare_timers() -{ - struct timer_list *timer, *nearest_timer = NULL; - llist_for_each_entry(timer, &timer_list, entry) { - if (!nearest_timer || TIME_SMALLER(timer->timeout, nearest_timer->timeout)) { - nearest_timer = timer; - } - } - - if (nearest_timer) { - s_nearest_time = nearest_timer->timeout; - } else { - memset(&s_nearest_time, 0, sizeof(struct timeval)); - } -} - -/* - * fire all timers... and remove them - */ -int bsc_update_timers() -{ - struct timeval current_time; - struct timer_list *timer, *tmp; - int work = 0; - - gettimeofday(¤t_time, NULL); - - /* - * The callbacks might mess with our list and in this case - * even llist_for_each_entry_safe is not safe to use. To allow - * del_timer, add_timer, schedule_timer to be called from within - * the callback we jump through some loops. - * - * First we set the handled flag of each active timer to zero, - * then we iterate over the list and execute the callbacks. As the - * list might have been changed (specially the next) from within - * the callback we have to start over again. Once every callback - * is dispatched we will remove the non-active from the list. - * - * TODO: If this is a performance issue we can poison a global - * variable in add_timer and del_timer and only then restart. - */ - llist_for_each_entry(timer, &timer_list, entry) { - timer->handled = 0; - } - -restart: - llist_for_each_entry(timer, &timer_list, entry) { - if (!timer->handled && TIME_SMALLER(timer->timeout, current_time)) { - timer->handled = 1; - timer->active = 0; - (*timer->cb)(timer->data); - work = 1; - goto restart; - } - } - - llist_for_each_entry_safe(timer, tmp, &timer_list, entry) { - timer->handled = 0; - if (!timer->active) { - bsc_del_timer(timer); - } - } - - return work; -} diff --git a/src/tlv_parser.c b/src/tlv_parser.c deleted file mode 100644 index e835f951f..000000000 --- a/src/tlv_parser.c +++ /dev/null @@ -1,105 +0,0 @@ -#include <stdio.h> -#include <openbsc/tlv.h> - -int tlv_dump(struct tlv_parsed *dec) -{ - int i; - - for (i = 0; i <= 0xff; i++) { - if (!dec->lv[i].val) - continue; - printf("T=%02x L=%d\n", i, dec->lv[i].len); - } - return 0; -} - -/* 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 - * buf_len: input: the length of the input data buffer - * lv_tag: input: an initial LV tag at the start of the buffer - * lv_tag2: input: a second initial LV tag following lv_tag - */ -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; - - memset(dec, 0, sizeof(*dec)); - - if (lv_tag) { - if (pos > buf + buf_len) - return -1; - dec->lv[lv_tag].val = pos+1; - dec->lv[lv_tag].len = *pos; - len = dec->lv[lv_tag].len + 1; - if (pos + len > buf + buf_len) - return -2; - num_parsed++; - pos += len; - } - if (lv_tag2) { - if (pos > buf + buf_len) - return -1; - dec->lv[lv_tag2].val = pos+1; - dec->lv[lv_tag2].len = *pos; - len = dec->lv[lv_tag2].len + 1; - if (pos + len > buf + buf_len) - return -2; - num_parsed++; - pos += 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; - } - } - //tlv_dump(dec); - return num_parsed; -} - diff --git a/src/trau_frame.c b/src/trau_frame.c deleted file mode 100644 index aa039574b..000000000 --- a/src/trau_frame.c +++ /dev/null @@ -1,255 +0,0 @@ -/* TRAU frame handling according to GSM TS 08.60 */ - -/* (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 <unistd.h> -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <errno.h> - -#include <openbsc/trau_frame.h> -#include <openbsc/subchan_demux.h> -#include <openbsc/debug.h> - -static u_int32_t get_bits(const u_int8_t *bitbuf, int offset, int num) -{ - int i; - u_int32_t ret = 0; - - for (i = offset; i < offset + num; i++) { - ret = ret << 1; - if (bitbuf[i]) - ret |= 1; - } - return ret; -} - -/* Decode according to 3.1.1 */ -static void decode_fr(struct decoded_trau_frame *fr, const u_int8_t *trau_bits) -{ - int i; - int d_idx = 0; - - /* C1 .. C15 */ - memcpy(fr->c_bits+0, trau_bits+17, 15); - /* C16 .. C21 */ - memcpy(fr->c_bits+15, trau_bits+310, 6); - /* T1 .. T4 */ - memcpy(fr->t_bits+0, trau_bits+316, 4); - /* D1 .. D255 */ - for (i = 32; i < 304; i+= 16) { - memcpy(fr->d_bits + d_idx, trau_bits+i+1, 15); - d_idx += 15; - } - /* D256 .. D260 */ - memcpy(fr->d_bits + d_idx, trau_bits + 305, 5); -} - -/* Decode according to 3.1.2 */ -static void decode_amr(struct decoded_trau_frame *fr, const u_int8_t *trau_bits) -{ - int i; - int d_idx = 0; - - /* C1 .. C15 */ - memcpy(fr->c_bits+0, trau_bits+17, 15); - /* C16 .. C25 */ - memcpy(fr->c_bits+15, trau_bits+33, 10); - /* T1 .. T4 */ - memcpy(fr->t_bits+0, trau_bits+316, 4); - /* D1 .. D5 */ - memcpy(fr->d_bits, trau_bits+43, 5); - /* D6 .. D245 */ - for (i = 48; i < 304; i += 16) { - memcpy(fr->d_bits + d_idx, trau_bits+i+1, 15); - d_idx += 15; - } - /* D246 .. D256 */ - memcpy(fr->d_bits + d_idx, trau_bits + 305, 11); -} - -int decode_trau_frame(struct decoded_trau_frame *fr, const u_int8_t *trau_bits) -{ - u_int8_t cbits5 = get_bits(trau_bits, 17, 5); - - switch (cbits5) { - case TRAU_FT_FR_UP: - case TRAU_FT_FR_DOWN: - case TRAU_FT_IDLE_UP: - case TRAU_FT_IDLE_DOWN: - case TRAU_FT_EFR: - decode_fr(fr, trau_bits); - break; - case TRAU_FT_AMR: - decode_amr(fr, trau_bits); - break; - case TRAU_FT_OM_UP: - case TRAU_FT_OM_DOWN: - case TRAU_FT_DATA_UP: - case TRAU_FT_DATA_DOWN: - case TRAU_FT_D145_SYNC: - case TRAU_FT_EDATA: - DEBUGP(DMUX, "can't decode unimplemented TRAU Frame Type 0x%02x\n", cbits5); - return -1; - break; - default: - DEBUGP(DMUX, "can't decode unknown TRAU Frame Type 0x%02x\n", cbits5); - return -1; - break; - } - - return 0; -} - -const u_int8_t ft_fr_down_bits[] = { 1, 1, 1, 0, 0 }; -const u_int8_t ft_idle_down_bits[] = { 0, 1, 1, 1, 0 }; - -/* modify an uplink TRAU frame so we can send it downlink */ -int trau_frame_up2down(struct decoded_trau_frame *fr) -{ - u_int8_t cbits5 = get_bits(fr->c_bits, 0, 5); - - switch (cbits5) { - case TRAU_FT_FR_UP: - memcpy(fr->c_bits, ft_fr_down_bits, 5); - /* clear time alignment */ - memset(fr->c_bits+5, 0, 6); - /* FIXME: SP / BFI in case of DTx */ - /* C12 .. C21 are spare and coded as '1' */ - memset(fr->c_bits+11, 0x01, 10); - break; - case TRAU_FT_EFR: - /* clear time alignment */ - memset(fr->c_bits+5, 0, 6); - /* FIXME: set UFE appropriately */ - /* FIXME: SP / BFI in case of DTx */ - break; - case TRAU_FT_IDLE_UP: - memcpy(fr->c_bits, ft_idle_down_bits, 5); - /* clear time alignment */ - memset(fr->c_bits+5, 0, 6); - /* FIXME: SP / BFI in case of DTx */ - /* C12 .. C21 are spare and coded as '1' */ - memset(fr->c_bits+11, 0x01, 10); - break; - case TRAU_FT_FR_DOWN: - case TRAU_FT_IDLE_DOWN: - case TRAU_FT_OM_DOWN: - case TRAU_FT_DATA_DOWN: - /* we cannot convert a downlink to a downlink frame */ - return -EINVAL; - break; - case TRAU_FT_AMR: - case TRAU_FT_OM_UP: - case TRAU_FT_DATA_UP: - case TRAU_FT_D145_SYNC: - case TRAU_FT_EDATA: - DEBUGP(DMUX, "unimplemented TRAU Frame Type 0x%02x\n", cbits5); - return -1; - break; - default: - DEBUGP(DMUX, "unknown TRAU Frame Type 0x%02x\n", cbits5); - return -1; - break; - } - - return 0; - -} - -static void encode_fr(u_int8_t *trau_bits, const struct decoded_trau_frame *fr) -{ - int i; - int d_idx = 0; - - trau_bits[16] = 1; - /* C1 .. C15 */ - memcpy(trau_bits+17, fr->c_bits+0, 15); - /* D1 .. D255 */ - for (i = 32; i < 304; i+= 16) { - trau_bits[i] = 1; - memcpy(trau_bits+i+1, fr->d_bits + d_idx, 15); - d_idx += 15; - } - /* D256 .. D260 */ - trau_bits[304] = 1; - memcpy(trau_bits + 305, fr->d_bits + d_idx, 5); - /* C16 .. C21 */ - memcpy(trau_bits+310, fr->c_bits+15, 6); - - /* FIXME: handle timing adjustment */ - - /* T1 .. T4 */ - memcpy(trau_bits+316, fr->t_bits+0, 4); -} - - -int encode_trau_frame(u_int8_t *trau_bits, const struct decoded_trau_frame *fr) -{ - u_int8_t cbits5 = get_bits(fr->c_bits, 0, 5); - - /* 16 bits of sync header */ - memset(trau_bits, 0, 16); - - switch (cbits5) { - case TRAU_FT_FR_UP: - case TRAU_FT_FR_DOWN: - case TRAU_FT_IDLE_UP: - case TRAU_FT_IDLE_DOWN: - case TRAU_FT_EFR: - encode_fr(trau_bits, fr); - break; - case TRAU_FT_AMR: - case TRAU_FT_OM_UP: - case TRAU_FT_OM_DOWN: - case TRAU_FT_DATA_UP: - case TRAU_FT_DATA_DOWN: - case TRAU_FT_D145_SYNC: - case TRAU_FT_EDATA: - DEBUGP(DMUX, "unimplemented TRAU Frame Type 0x%02x\n", cbits5); - return -1; - break; - default: - DEBUGP(DMUX, "unknown TRAU Frame Type 0x%02x\n", cbits5); - return -1; - break; - } - - return 0; -} - -static struct decoded_trau_frame fr_idle_frame = { - .c_bits = { 0, 1, 1, 1, 0 }, /* IDLE DOWNLINK 3.5.5 */ - .t_bits = { 1, 1, 1, 1 }, -}; -static u_int8_t encoded_idle_frame[TRAU_FRAME_BITS]; -static int dbits_initted; - -u_int8_t *trau_idle_frame(void) -{ - /* only initialize during the first call */ - if (!dbits_initted) { - /* set all D-bits to 1 */ - memset(&fr_idle_frame.d_bits, 0x01, 260); - encode_fr(encoded_idle_frame, &fr_idle_frame); - } - return encoded_idle_frame; -} diff --git a/src/trau_mux.c b/src/trau_mux.c deleted file mode 100644 index 96f858992..000000000 --- a/src/trau_mux.c +++ /dev/null @@ -1,212 +0,0 @@ -/* Simple TRAU frame reflector to route voice calls */ - -/* (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 <errno.h> -#include <stdlib.h> -#include <string.h> -#include <sys/types.h> - -#include <openbsc/gsm_data.h> -#include <openbsc/trau_frame.h> -#include <openbsc/trau_mux.h> -#include <openbsc/subchan_demux.h> -#include <openbsc/e1_input.h> -#include <openbsc/debug.h> - -struct map_entry { - struct llist_head list; - struct gsm_e1_subslot src, dst; -}; - -struct upqueue_entry { - struct llist_head list; - struct gsm_network *net; - struct gsm_e1_subslot src; - u_int32_t callref; -}; - -static LLIST_HEAD(ss_map); -static LLIST_HEAD(ss_upqueue); - -/* map one particular subslot to another subslot */ -int trau_mux_map(const struct gsm_e1_subslot *src, - const struct gsm_e1_subslot *dst) -{ - struct map_entry *me = malloc(sizeof(*me)); - if (!me) - return -ENOMEM; - - DEBUGP(DCC, "Setting up TRAU mux map between (e1=%u,ts=%u,ss=%u) " - "and (e1=%u,ts=%u,ss=%u)\n", - src->e1_nr, src->e1_ts, src->e1_ts_ss, - dst->e1_nr, dst->e1_ts, dst->e1_ts_ss); - - /* make sure to get rid of any stale old mappings */ - trau_mux_unmap(src, 0); - trau_mux_unmap(dst, 0); - - memcpy(&me->src, src, sizeof(me->src)); - memcpy(&me->dst, dst, sizeof(me->dst)); - llist_add(&me->list, &ss_map); - - return 0; -} - -int trau_mux_map_lchan(const struct gsm_lchan *src, - const struct gsm_lchan *dst) -{ - struct gsm_e1_subslot *src_ss, *dst_ss; - - src_ss = &src->ts->e1_link; - dst_ss = &dst->ts->e1_link; - - return trau_mux_map(src_ss, dst_ss); -} - - -/* unmap one particular subslot from another subslot */ -int trau_mux_unmap(const struct gsm_e1_subslot *ss, u_int32_t callref) -{ - struct map_entry *me, *me2; - struct upqueue_entry *ue, *ue2; - - if (ss) - llist_for_each_entry_safe(me, me2, &ss_map, list) { - if (!memcmp(&me->src, ss, sizeof(*ss)) || - !memcmp(&me->dst, ss, sizeof(*ss))) { - llist_del(&me->list); - return 0; - } - } - llist_for_each_entry_safe(ue, ue2, &ss_upqueue, list) { - if (ue->callref == callref) { - llist_del(&ue->list); - return 0; - } - if (ss && !memcmp(&ue->src, ss, sizeof(*ss))) { - llist_del(&ue->list); - return 0; - } - } - return -ENOENT; -} - -/* look-up an enty in the TRAU mux map */ -static struct gsm_e1_subslot * -lookup_trau_mux_map(const struct gsm_e1_subslot *src) -{ - struct map_entry *me; - - llist_for_each_entry(me, &ss_map, list) { - if (!memcmp(&me->src, src, sizeof(*src))) - return &me->dst; - if (!memcmp(&me->dst, src, sizeof(*src))) - return &me->src; - } - return NULL; -} - -/* look-up an enty in the TRAU upqueue */ -struct upqueue_entry * -lookup_trau_upqueue(const struct gsm_e1_subslot *src) -{ - struct upqueue_entry *ue; - - llist_for_each_entry(ue, &ss_upqueue, list) { - if (!memcmp(&ue->src, src, sizeof(*src))) - return ue; - } - return NULL; -} - -/* we get called by subchan_demux */ -int trau_mux_input(struct gsm_e1_subslot *src_e1_ss, - const u_int8_t *trau_bits, int num_bits) -{ - struct decoded_trau_frame tf; - u_int8_t trau_bits_out[TRAU_FRAME_BITS]; - struct gsm_e1_subslot *dst_e1_ss = lookup_trau_mux_map(src_e1_ss); - struct subch_mux *mx; - int rc; - - /* decode TRAU, change it to downlink, re-encode */ - rc = decode_trau_frame(&tf, trau_bits); - if (rc) - return rc; - - if (!dst_e1_ss) - return -EINVAL; - - mx = e1inp_get_mux(dst_e1_ss->e1_nr, dst_e1_ss->e1_ts); - if (!mx) - return -EINVAL; - - trau_frame_up2down(&tf); - encode_trau_frame(trau_bits_out, &tf); - - /* and send it to the muxer */ - return subchan_mux_enqueue(mx, dst_e1_ss->e1_ts_ss, trau_bits_out, - TRAU_FRAME_BITS); -} - -/* add receiver instance for lchan and callref */ -int trau_recv_lchan(struct gsm_lchan *lchan, u_int32_t callref) -{ - struct gsm_e1_subslot *src_ss; - struct upqueue_entry *ue = malloc(sizeof(*ue)); - - if (!ue) - return -ENOMEM; - - src_ss = &lchan->ts->e1_link; - - DEBUGP(DCC, "Setting up TRAU receiver (e1=%u,ts=%u,ss=%u) " - "and (callref 0x%x)\n", - src_ss->e1_nr, src_ss->e1_ts, src_ss->e1_ts_ss, - callref); - - /* make sure to get rid of any stale old mappings */ - trau_mux_unmap(src_ss, callref); - - memcpy(&ue->src, src_ss, sizeof(ue->src)); - ue->net = lchan->ts->trx->bts->network; - ue->callref = callref; - llist_add(&ue->list, &ss_upqueue); - - return 0; -} - -int trau_send_lchan(struct gsm_lchan *lchan, struct decoded_trau_frame *tf) -{ - u_int8_t trau_bits_out[TRAU_FRAME_BITS]; - struct gsm_e1_subslot *dst_e1_ss = &lchan->ts->e1_link; - struct subch_mux *mx; - - mx = e1inp_get_mux(dst_e1_ss->e1_nr, dst_e1_ss->e1_ts); - if (!mx) - return -EINVAL; - - encode_trau_frame(trau_bits_out, tf); - - /* and send it to the muxer */ - return subchan_mux_enqueue(mx, dst_e1_ss->e1_ts_ss, trau_bits_out, - TRAU_FRAME_BITS); -} diff --git a/src/vty/buffer.c b/src/vty/buffer.c deleted file mode 100644 index 63661004d..000000000 --- a/src/vty/buffer.c +++ /dev/null @@ -1,460 +0,0 @@ -/* - * Buffering of output and input. - * Copyright (C) 1998 Kunihiro Ishiguro - * - * This file is part of GNU Zebra. - * - * GNU Zebra 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, or (at your - * option) any later version. - * - * GNU Zebra 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 GNU Zebra; see the file COPYING. If not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#include <stdio.h> -#include <stdlib.h> -#include <unistd.h> -#include <string.h> -#include <errno.h> -#include <stddef.h> -#include <sys/uio.h> - -#include <vty/buffer.h> -#include <vty/vty.h> - -/* Buffer master. */ -struct buffer { - /* Data list. */ - struct buffer_data *head; - struct buffer_data *tail; - - /* Size of each buffer_data chunk. */ - size_t size; -}; - -/* Data container. */ -struct buffer_data { - struct buffer_data *next; - - /* Location to add new data. */ - size_t cp; - - /* Pointer to data not yet flushed. */ - size_t sp; - - /* Actual data stream (variable length). */ - unsigned char data[0]; /* real dimension is buffer->size */ -}; - -/* It should always be true that: 0 <= sp <= cp <= size */ - -/* Default buffer size (used if none specified). It is rounded up to the - next page boundery. */ -#define BUFFER_SIZE_DEFAULT 4096 - -#define BUFFER_DATA_FREE(D) free((D)) - -/* Make new buffer. */ -struct buffer *buffer_new(size_t size) -{ - struct buffer *b; - - b = calloc(1, sizeof(struct buffer)); - - if (size) - b->size = size; - else { - static size_t default_size; - if (!default_size) { - long pgsz = sysconf(_SC_PAGESIZE); - default_size = - ((((BUFFER_SIZE_DEFAULT - 1) / pgsz) + 1) * pgsz); - } - b->size = default_size; - } - - return b; -} - -/* Free buffer. */ -void buffer_free(struct buffer *b) -{ - buffer_reset(b); - free(b); -} - -/* Make string clone. */ -char *buffer_getstr(struct buffer *b) -{ - size_t totlen = 0; - struct buffer_data *data; - char *s; - char *p; - - for (data = b->head; data; data = data->next) - totlen += data->cp - data->sp; - if (!(s = malloc(totlen + 1))) - return NULL; - p = s; - for (data = b->head; data; data = data->next) { - memcpy(p, data->data + data->sp, data->cp - data->sp); - p += data->cp - data->sp; - } - *p = '\0'; - return s; -} - -/* Return 1 if buffer is empty. */ -int buffer_empty(struct buffer *b) -{ - return (b->head == NULL); -} - -/* Clear and free all allocated data. */ -void buffer_reset(struct buffer *b) -{ - struct buffer_data *data; - struct buffer_data *next; - - for (data = b->head; data; data = next) { - next = data->next; - BUFFER_DATA_FREE(data); - } - b->head = b->tail = NULL; -} - -/* Add buffer_data to the end of buffer. */ -static struct buffer_data *buffer_add(struct buffer *b) -{ - struct buffer_data *d; - - d = malloc(offsetof(struct buffer_data, data[b->size])); - if (!d) - return NULL; - d->cp = d->sp = 0; - d->next = NULL; - - if (b->tail) - b->tail->next = d; - else - b->head = d; - b->tail = d; - - return d; -} - -/* Write data to buffer. */ -void buffer_put(struct buffer *b, const void *p, size_t size) -{ - struct buffer_data *data = b->tail; - const char *ptr = p; - - /* We use even last one byte of data buffer. */ - while (size) { - size_t chunk; - - /* If there is no data buffer add it. */ - if (data == NULL || data->cp == b->size) - data = buffer_add(b); - - chunk = - ((size <= - (b->size - data->cp)) ? size : (b->size - data->cp)); - memcpy((data->data + data->cp), ptr, chunk); - size -= chunk; - ptr += chunk; - data->cp += chunk; - } -} - -/* Insert character into the buffer. */ -void buffer_putc(struct buffer *b, u_char c) -{ - buffer_put(b, &c, 1); -} - -/* Put string to the buffer. */ -void buffer_putstr(struct buffer *b, const char *c) -{ - buffer_put(b, c, strlen(c)); -} - -/* Keep flushing data to the fd until the buffer is empty or an error is - encountered or the operation would block. */ -buffer_status_t buffer_flush_all(struct buffer *b, int fd) -{ - buffer_status_t ret; - struct buffer_data *head; - size_t head_sp; - - if (!b->head) - return BUFFER_EMPTY; - head_sp = (head = b->head)->sp; - /* Flush all data. */ - while ((ret = buffer_flush_available(b, fd)) == BUFFER_PENDING) { - if ((b->head == head) && (head_sp == head->sp) - && (errno != EINTR)) - /* No data was flushed, so kernel buffer must be full. */ - return ret; - head_sp = (head = b->head)->sp; - } - - return ret; -} - -#if 0 -/* Flush enough data to fill a terminal window of the given scene (used only - by vty telnet interface). */ -buffer_status_t -buffer_flush_window(struct buffer * b, int fd, int width, int height, - int erase_flag, int no_more_flag) -{ - int nbytes; - int iov_alloc; - int iov_index; - struct iovec *iov; - struct iovec small_iov[3]; - char more[] = " --More-- "; - char erase[] = - { 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, - ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', - 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08 - }; - struct buffer_data *data; - int column; - - if (!b->head) - return BUFFER_EMPTY; - - if (height < 1) { - zlog_warn - ("%s called with non-positive window height %d, forcing to 1", - __func__, height); - height = 1; - } else if (height >= 2) - height--; - if (width < 1) { - zlog_warn - ("%s called with non-positive window width %d, forcing to 1", - __func__, width); - width = 1; - } - - /* For erase and more data add two to b's buffer_data count. */ - if (b->head->next == NULL) { - iov_alloc = sizeof(small_iov) / sizeof(small_iov[0]); - iov = small_iov; - } else { - iov_alloc = ((height * (width + 2)) / b->size) + 10; - iov = XMALLOC(MTYPE_TMP, iov_alloc * sizeof(*iov)); - } - iov_index = 0; - - /* Previously print out is performed. */ - if (erase_flag) { - iov[iov_index].iov_base = erase; - iov[iov_index].iov_len = sizeof erase; - iov_index++; - } - - /* Output data. */ - column = 1; /* Column position of next character displayed. */ - for (data = b->head; data && (height > 0); data = data->next) { - size_t cp; - - cp = data->sp; - while ((cp < data->cp) && (height > 0)) { - /* Calculate lines remaining and column position after displaying - this character. */ - if (data->data[cp] == '\r') - column = 1; - else if ((data->data[cp] == '\n') || (column == width)) { - column = 1; - height--; - } else - column++; - cp++; - } - iov[iov_index].iov_base = (char *)(data->data + data->sp); - iov[iov_index++].iov_len = cp - data->sp; - data->sp = cp; - - if (iov_index == iov_alloc) - /* This should not ordinarily happen. */ - { - iov_alloc *= 2; - if (iov != small_iov) { - zlog_warn("%s: growing iov array to %d; " - "width %d, height %d, size %lu", - __func__, iov_alloc, width, height, - (u_long) b->size); - iov = - XREALLOC(MTYPE_TMP, iov, - iov_alloc * sizeof(*iov)); - } else { - /* This should absolutely never occur. */ - zlog_err - ("%s: corruption detected: iov_small overflowed; " - "head %p, tail %p, head->next %p", - __func__, b->head, b->tail, b->head->next); - iov = - XMALLOC(MTYPE_TMP, - iov_alloc * sizeof(*iov)); - memcpy(iov, small_iov, sizeof(small_iov)); - } - } - } - - /* In case of `more' display need. */ - if (b->tail && (b->tail->sp < b->tail->cp) && !no_more_flag) { - iov[iov_index].iov_base = more; - iov[iov_index].iov_len = sizeof more; - iov_index++; - } -#ifdef IOV_MAX - /* IOV_MAX are normally defined in <sys/uio.h> , Posix.1g. - example: Solaris2.6 are defined IOV_MAX size at 16. */ - { - struct iovec *c_iov = iov; - nbytes = 0; /* Make sure it's initialized. */ - - while (iov_index > 0) { - int iov_size; - - iov_size = - ((iov_index > IOV_MAX) ? IOV_MAX : iov_index); - if ((nbytes = writev(fd, c_iov, iov_size)) < 0) { - zlog_warn("%s: writev to fd %d failed: %s", - __func__, fd, safe_strerror(errno)); - break; - } - - /* move pointer io-vector */ - c_iov += iov_size; - iov_index -= iov_size; - } - } -#else /* IOV_MAX */ - if ((nbytes = writev(fd, iov, iov_index)) < 0) - zlog_warn("%s: writev to fd %d failed: %s", - __func__, fd, safe_strerror(errno)); -#endif /* IOV_MAX */ - - /* Free printed buffer data. */ - while (b->head && (b->head->sp == b->head->cp)) { - struct buffer_data *del; - if (!(b->head = (del = b->head)->next)) - b->tail = NULL; - BUFFER_DATA_FREE(del); - } - - if (iov != small_iov) - XFREE(MTYPE_TMP, iov); - - return (nbytes < 0) ? BUFFER_ERROR : - (b->head ? BUFFER_PENDING : BUFFER_EMPTY); -} -#endif - -/* This function (unlike other buffer_flush* functions above) is designed -to work with non-blocking sockets. It does not attempt to write out -all of the queued data, just a "big" chunk. It returns 0 if it was -able to empty out the buffers completely, 1 if more flushing is -required later, or -1 on a fatal write error. */ -buffer_status_t buffer_flush_available(struct buffer * b, int fd) -{ - -/* These are just reasonable values to make sure a significant amount of -data is written. There's no need to go crazy and try to write it all -in one shot. */ -#ifdef IOV_MAX -#define MAX_CHUNKS ((IOV_MAX >= 16) ? 16 : IOV_MAX) -#else -#define MAX_CHUNKS 16 -#endif -#define MAX_FLUSH 131072 - - struct buffer_data *d; - size_t written; - struct iovec iov[MAX_CHUNKS]; - size_t iovcnt = 0; - size_t nbyte = 0; - - for (d = b->head; d && (iovcnt < MAX_CHUNKS) && (nbyte < MAX_FLUSH); - d = d->next, iovcnt++) { - iov[iovcnt].iov_base = d->data + d->sp; - nbyte += (iov[iovcnt].iov_len = d->cp - d->sp); - } - - if (!nbyte) - /* No data to flush: should we issue a warning message? */ - return BUFFER_EMPTY; - - /* only place where written should be sign compared */ - if ((ssize_t) (written = writev(fd, iov, iovcnt)) < 0) { - if (ERRNO_IO_RETRY(errno)) - /* Calling code should try again later. */ - return BUFFER_PENDING; - return BUFFER_ERROR; - } - - /* Free printed buffer data. */ - while (written > 0) { - struct buffer_data *d; - if (!(d = b->head)) - break; - if (written < d->cp - d->sp) { - d->sp += written; - return BUFFER_PENDING; - } - - written -= (d->cp - d->sp); - if (!(b->head = d->next)) - b->tail = NULL; - BUFFER_DATA_FREE(d); - } - - return b->head ? BUFFER_PENDING : BUFFER_EMPTY; - -#undef MAX_CHUNKS -#undef MAX_FLUSH -} - -buffer_status_t -buffer_write(struct buffer * b, int fd, const void *p, size_t size) -{ - ssize_t nbytes; - -#if 0 - /* Should we attempt to drain any previously buffered data? This could help reduce latency in pushing out the data if we are stuck in a long-running thread that is preventing the main select loop from calling the flush thread... */ - - if (b->head && (buffer_flush_available(b, fd) == BUFFER_ERROR)) - return BUFFER_ERROR; -#endif - if (b->head) - /* Buffer is not empty, so do not attempt to write the new data. */ - nbytes = 0; - else if ((nbytes = write(fd, p, size)) < 0) { - if (ERRNO_IO_RETRY(errno)) - nbytes = 0; - else - return BUFFER_ERROR; - } - /* Add any remaining data to the buffer. */ - { - size_t written = nbytes; - if (written < size) - buffer_put(b, ((const char *)p) + written, - size - written); - } - return b->head ? BUFFER_PENDING : BUFFER_EMPTY; -} diff --git a/src/vty/cardshell.h b/src/vty/cardshell.h deleted file mode 100644 index d963a3810..000000000 --- a/src/vty/cardshell.h +++ /dev/null @@ -1,5 +0,0 @@ -#define QUAGGA_PROGNAME "OpenBSC" -#define QUAGGA_VERSION "0.01" -#define QUAGGA_COPYRIGHT "Harald Welte <laforge@gnumonks.org>" -#define CONFIGFILE_MASK 022 -#define SYSCONFDIR "/usr/local/etc" diff --git a/src/vty/command.c b/src/vty/command.c deleted file mode 100644 index 94a5c2af2..000000000 --- a/src/vty/command.c +++ /dev/null @@ -1,3416 +0,0 @@ -/* - $Id: command.c,v 1.47 2005/04/25 16:26:42 paul Exp $ - - Command interpreter routine for virtual terminal [aka TeletYpe] - Copyright (C) 1997, 98, 99 Kunihiro Ishiguro - -This file is part of GNU Zebra. - -GNU Zebra 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, or (at your -option) any later version. - -GNU Zebra 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 GNU Zebra; see the file COPYING. If not, write to the -Free Software Foundation, Inc., 59 Temple Place - Suite 330, -Boston, MA 02111-1307, USA. */ - -#include "cardshell.h" - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <syslog.h> -#include <errno.h> -#define _XOPEN_SOURCE -#include <unistd.h> -#include <assert.h> -#include <ctype.h> -#include <time.h> -#include <sys/time.h> - -//#include "memory.h" -//#include "log.h" -//#include <lib/version.h> -//#include "thread.h" -#include <vty/vector.h> -#include <vty/vty.h> -#include <vty/command.h> -//#include "workqueue.h" - -#include <openbsc/gsm_data.h> - -/* Command vector which includes some level of command lists. Normally - each daemon maintains each own cmdvec. */ -vector cmdvec; - -/* Host information structure. */ -struct host host; - -/* Standard command node structures. */ -struct cmd_node auth_node = { - AUTH_NODE, - "Password: ", -}; - -struct cmd_node view_node = { - VIEW_NODE, - "%s> ", -}; - -struct cmd_node auth_enable_node = { - AUTH_ENABLE_NODE, - "Password: ", -}; - -struct cmd_node enable_node = { - ENABLE_NODE, - "%s# ", -}; - -struct cmd_node config_node = { - CONFIG_NODE, - "%s(config)# ", - 1 -}; - -/* Default motd string. */ -const char *default_motd = "\r\n\ -Hello, this is " QUAGGA_PROGNAME " (version " QUAGGA_VERSION ").\r\n\ -" QUAGGA_COPYRIGHT "\r\n\ -\r\n"; - -#if 0 -static struct facility_map { - int facility; - const char *name; - size_t match; -} syslog_facilities[] = { - { - LOG_KERN, "kern", 1}, { - LOG_USER, "user", 2}, { - LOG_MAIL, "mail", 1}, { - LOG_DAEMON, "daemon", 1}, { - LOG_AUTH, "auth", 1}, { - LOG_SYSLOG, "syslog", 1}, { - LOG_LPR, "lpr", 2}, { - LOG_NEWS, "news", 1}, { - LOG_UUCP, "uucp", 2}, { - LOG_CRON, "cron", 1}, -#ifdef LOG_FTP - { - LOG_FTP, "ftp", 1}, -#endif - { - LOG_LOCAL0, "local0", 6}, { - LOG_LOCAL1, "local1", 6}, { - LOG_LOCAL2, "local2", 6}, { - LOG_LOCAL3, "local3", 6}, { - LOG_LOCAL4, "local4", 6}, { - LOG_LOCAL5, "local5", 6}, { - LOG_LOCAL6, "local6", 6}, { - LOG_LOCAL7, "local7", 6}, { -0, NULL, 0},}; - -static const char *facility_name(int facility) -{ - struct facility_map *fm; - - for (fm = syslog_facilities; fm->name; fm++) - if (fm->facility == facility) - return fm->name; - return ""; -} - -static int facility_match(const char *str) -{ - struct facility_map *fm; - - for (fm = syslog_facilities; fm->name; fm++) - if (!strncmp(str, fm->name, fm->match)) - return fm->facility; - return -1; -} - -static int level_match(const char *s) -{ - int level; - - for (level = 0; zlog_priority[level] != NULL; level++) - if (!strncmp(s, zlog_priority[level], 2)) - return level; - return ZLOG_DISABLED; -} -#endif - -/* This is called from main when a daemon is invoked with -v or --version. */ -void print_version(const char *progname) -{ - printf("%s version %s\n", progname, QUAGGA_VERSION); - printf("%s\n", QUAGGA_COPYRIGHT); -} - -/* Utility function to concatenate argv argument into a single string - with inserting ' ' character between each argument. */ -char *argv_concat(const char **argv, int argc, int shift) -{ - int i; - size_t len; - char *str; - char *p; - - len = 0; - for (i = shift; i < argc; i++) - len += strlen(argv[i]) + 1; - if (!len) - return NULL; - p = str = malloc(len); - for (i = shift; i < argc; i++) { - size_t arglen; - memcpy(p, argv[i], (arglen = strlen(argv[i]))); - p += arglen; - *p++ = ' '; - } - *(p - 1) = '\0'; - return str; -} - -/* Install top node of command vector. */ -void install_node(struct cmd_node *node, int (*func) (struct vty *)) -{ - vector_set_index(cmdvec, node->node, node); - node->func = func; - node->cmd_vector = vector_init(VECTOR_MIN_SIZE); -} - -/* Compare two command's string. Used in sort_node (). */ -static int cmp_node(const void *p, const void *q) -{ - struct cmd_element *a = *(struct cmd_element **)p; - struct cmd_element *b = *(struct cmd_element **)q; - - return strcmp(a->string, b->string); -} - -static int cmp_desc(const void *p, const void *q) -{ - struct desc *a = *(struct desc **)p; - struct desc *b = *(struct desc **)q; - - return strcmp(a->cmd, b->cmd); -} - -/* Sort each node's command element according to command string. */ -void sort_node() -{ - unsigned int i, j; - struct cmd_node *cnode; - vector descvec; - struct cmd_element *cmd_element; - - for (i = 0; i < vector_active(cmdvec); i++) - if ((cnode = vector_slot(cmdvec, i)) != NULL) { - vector cmd_vector = cnode->cmd_vector; - qsort(cmd_vector->index, vector_active(cmd_vector), - sizeof(void *), cmp_node); - - for (j = 0; j < vector_active(cmd_vector); j++) - if ((cmd_element = - vector_slot(cmd_vector, j)) != NULL - && vector_active(cmd_element->strvec)) { - descvec = - vector_slot(cmd_element->strvec, - vector_active - (cmd_element->strvec) - - 1); - qsort(descvec->index, - vector_active(descvec), - sizeof(void *), cmp_desc); - } - } -} - -/* Breaking up string into each command piece. I assume given - character is separated by a space character. Return value is a - vector which includes char ** data element. */ -vector cmd_make_strvec(const char *string) -{ - const char *cp, *start; - char *token; - int strlen; - vector strvec; - - if (string == NULL) - return NULL; - - cp = string; - - /* Skip white spaces. */ - while (isspace((int)*cp) && *cp != '\0') - cp++; - - /* Return if there is only white spaces */ - if (*cp == '\0') - return NULL; - - if (*cp == '!' || *cp == '#') - return NULL; - - /* Prepare return vector. */ - strvec = vector_init(VECTOR_MIN_SIZE); - - /* Copy each command piece and set into vector. */ - while (1) { - start = cp; - while (!(isspace((int)*cp) || *cp == '\r' || *cp == '\n') && - *cp != '\0') - cp++; - strlen = cp - start; - token = malloc(strlen + 1); - memcpy(token, start, strlen); - *(token + strlen) = '\0'; - vector_set(strvec, token); - - while ((isspace((int)*cp) || *cp == '\n' || *cp == '\r') && - *cp != '\0') - cp++; - - if (*cp == '\0') - return strvec; - } -} - -/* Free allocated string vector. */ -void cmd_free_strvec(vector v) -{ - unsigned int i; - char *cp; - - if (!v) - return; - - for (i = 0; i < vector_active(v); i++) - if ((cp = vector_slot(v, i)) != NULL) - free(cp); - - vector_free(v); -} - -/* Fetch next description. Used in cmd_make_descvec(). */ -static char *cmd_desc_str(const char **string) -{ - const char *cp, *start; - char *token; - int strlen; - - cp = *string; - - if (cp == NULL) - return NULL; - - /* Skip white spaces. */ - while (isspace((int)*cp) && *cp != '\0') - cp++; - - /* Return if there is only white spaces */ - if (*cp == '\0') - return NULL; - - start = cp; - - while (!(*cp == '\r' || *cp == '\n') && *cp != '\0') - cp++; - - strlen = cp - start; - token = malloc(strlen + 1); - memcpy(token, start, strlen); - *(token + strlen) = '\0'; - - *string = cp; - - return token; -} - -/* New string vector. */ -static vector cmd_make_descvec(const char *string, const char *descstr) -{ - int multiple = 0; - const char *sp; - char *token; - int len; - const char *cp; - const char *dp; - vector allvec; - vector strvec = NULL; - struct desc *desc; - - cp = string; - dp = descstr; - - if (cp == NULL) - return NULL; - - allvec = vector_init(VECTOR_MIN_SIZE); - - while (1) { - while (isspace((int)*cp) && *cp != '\0') - cp++; - - if (*cp == '(') { - multiple = 1; - cp++; - } - if (*cp == ')') { - multiple = 0; - cp++; - } - if (*cp == '|') { - if (!multiple) { - fprintf(stderr, "Command parse error!: %s\n", - string); - exit(1); - } - cp++; - } - - while (isspace((int)*cp) && *cp != '\0') - cp++; - - if (*cp == '(') { - multiple = 1; - cp++; - } - - if (*cp == '\0') - return allvec; - - sp = cp; - - while (! - (isspace((int)*cp) || *cp == '\r' || *cp == '\n' - || *cp == ')' || *cp == '|') && *cp != '\0') - cp++; - - len = cp - sp; - - token = malloc(len + 1); - memcpy(token, sp, len); - *(token + len) = '\0'; - - desc = calloc(1, sizeof(struct desc)); - desc->cmd = token; - desc->str = cmd_desc_str(&dp); - - if (multiple) { - if (multiple == 1) { - strvec = vector_init(VECTOR_MIN_SIZE); - vector_set(allvec, strvec); - } - multiple++; - } else { - strvec = vector_init(VECTOR_MIN_SIZE); - vector_set(allvec, strvec); - } - vector_set(strvec, desc); - } -} - -/* Count mandantory string vector size. This is to determine inputed - command has enough command length. */ -static int cmd_cmdsize(vector strvec) -{ - unsigned int i; - int size = 0; - vector descvec; - struct desc *desc; - - for (i = 0; i < vector_active(strvec); i++) - if ((descvec = vector_slot(strvec, i)) != NULL) { - if ((vector_active(descvec)) == 1 - && (desc = vector_slot(descvec, 0)) != NULL) { - if (desc->cmd == NULL || CMD_OPTION(desc->cmd)) - return size; - else - size++; - } else - size++; - } - return size; -} - -/* Return prompt character of specified node. */ -const char *cmd_prompt(enum node_type node) -{ - struct cmd_node *cnode; - - cnode = vector_slot(cmdvec, node); - return cnode->prompt; -} - -/* Install a command into a node. */ -void install_element(enum node_type ntype, struct cmd_element *cmd) -{ - struct cmd_node *cnode; - - cnode = vector_slot(cmdvec, ntype); - - if (cnode == NULL) { - fprintf(stderr, - "Command node %d doesn't exist, please check it\n", - ntype); - exit(1); - } - - vector_set(cnode->cmd_vector, cmd); - - cmd->strvec = cmd_make_descvec(cmd->string, cmd->doc); - cmd->cmdsize = cmd_cmdsize(cmd->strvec); -} - -static unsigned char itoa64[] = - "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; - -static void to64(char *s, long v, int n) -{ - while (--n >= 0) { - *s++ = itoa64[v & 0x3f]; - v >>= 6; - } -} - -static char *zencrypt(const char *passwd) -{ - char salt[6]; - struct timeval tv; - char *crypt(const char *, const char *); - - gettimeofday(&tv, 0); - - to64(&salt[0], random(), 3); - to64(&salt[3], tv.tv_usec, 3); - salt[5] = '\0'; - - return crypt(passwd, salt); -} - -/* This function write configuration of this host. */ -static int config_write_host(struct vty *vty) -{ -#if 0 - if (host.name) - vty_out(vty, "hostname %s%s", host.name, VTY_NEWLINE); - - if (host.encrypt) { - if (host.password_encrypt) - vty_out(vty, "password 8 %s%s", host.password_encrypt, - VTY_NEWLINE); - if (host.enable_encrypt) - vty_out(vty, "enable password 8 %s%s", - host.enable_encrypt, VTY_NEWLINE); - } else { - if (host.password) - vty_out(vty, "password %s%s", host.password, - VTY_NEWLINE); - if (host.enable) - vty_out(vty, "enable password %s%s", host.enable, - VTY_NEWLINE); - } - - if (zlog_default->default_lvl != LOG_DEBUG) { - vty_out(vty, "! N.B. The 'log trap' command is deprecated.%s", - VTY_NEWLINE); - vty_out(vty, "log trap %s%s", - zlog_priority[zlog_default->default_lvl], VTY_NEWLINE); - } - - if (host.logfile - && (zlog_default->maxlvl[ZLOG_DEST_FILE] != ZLOG_DISABLED)) { - vty_out(vty, "log file %s", host.logfile); - if (zlog_default->maxlvl[ZLOG_DEST_FILE] != - zlog_default->default_lvl) - vty_out(vty, " %s", - zlog_priority[zlog_default-> - maxlvl[ZLOG_DEST_FILE]]); - vty_out(vty, "%s", VTY_NEWLINE); - } - - if (zlog_default->maxlvl[ZLOG_DEST_STDOUT] != ZLOG_DISABLED) { - vty_out(vty, "log stdout"); - if (zlog_default->maxlvl[ZLOG_DEST_STDOUT] != - zlog_default->default_lvl) - vty_out(vty, " %s", - zlog_priority[zlog_default-> - maxlvl[ZLOG_DEST_STDOUT]]); - vty_out(vty, "%s", VTY_NEWLINE); - } - - if (zlog_default->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED) - vty_out(vty, "no log monitor%s", VTY_NEWLINE); - else if (zlog_default->maxlvl[ZLOG_DEST_MONITOR] != - zlog_default->default_lvl) - vty_out(vty, "log monitor %s%s", - zlog_priority[zlog_default->maxlvl[ZLOG_DEST_MONITOR]], - VTY_NEWLINE); - - if (zlog_default->maxlvl[ZLOG_DEST_SYSLOG] != ZLOG_DISABLED) { - vty_out(vty, "log syslog"); - if (zlog_default->maxlvl[ZLOG_DEST_SYSLOG] != - zlog_default->default_lvl) - vty_out(vty, " %s", - zlog_priority[zlog_default-> - maxlvl[ZLOG_DEST_SYSLOG]]); - vty_out(vty, "%s", VTY_NEWLINE); - } - - if (zlog_default->facility != LOG_DAEMON) - vty_out(vty, "log facility %s%s", - facility_name(zlog_default->facility), VTY_NEWLINE); - - if (zlog_default->record_priority == 1) - vty_out(vty, "log record-priority%s", VTY_NEWLINE); - - if (host.advanced) - vty_out(vty, "service advanced-vty%s", VTY_NEWLINE); - - if (host.encrypt) - vty_out(vty, "service password-encryption%s", VTY_NEWLINE); - - if (host.lines >= 0) - vty_out(vty, "service terminal-length %d%s", host.lines, - VTY_NEWLINE); - - if (host.motdfile) - vty_out(vty, "banner motd file %s%s", host.motdfile, - VTY_NEWLINE); - else if (!host.motd) - vty_out(vty, "no banner motd%s", VTY_NEWLINE); - -#endif - return 1; -} - -/* Utility function for getting command vector. */ -static vector cmd_node_vector(vector v, enum node_type ntype) -{ - struct cmd_node *cnode = vector_slot(v, ntype); - return cnode->cmd_vector; -} - -#if 0 -/* Filter command vector by symbol. This function is not actually used; - * should it be deleted? */ -static int cmd_filter_by_symbol(char *command, char *symbol) -{ - int i, lim; - - if (strcmp(symbol, "IPV4_ADDRESS") == 0) { - i = 0; - lim = strlen(command); - while (i < lim) { - if (! - (isdigit((int)command[i]) || command[i] == '.' - || command[i] == '/')) - return 1; - i++; - } - return 0; - } - if (strcmp(symbol, "STRING") == 0) { - i = 0; - lim = strlen(command); - while (i < lim) { - if (! - (isalpha((int)command[i]) || command[i] == '_' - || command[i] == '-')) - return 1; - i++; - } - return 0; - } - if (strcmp(symbol, "IFNAME") == 0) { - i = 0; - lim = strlen(command); - while (i < lim) { - if (!isalnum((int)command[i])) - return 1; - i++; - } - return 0; - } - return 0; -} -#endif - -/* Completion match types. */ -enum match_type { - no_match, - extend_match, - ipv4_prefix_match, - ipv4_match, - ipv6_prefix_match, - ipv6_match, - range_match, - vararg_match, - partly_match, - exact_match -}; - -static enum match_type cmd_ipv4_match(const char *str) -{ - const char *sp; - int dots = 0, nums = 0; - char buf[4]; - - if (str == NULL) - return partly_match; - - for (;;) { - memset(buf, 0, sizeof(buf)); - sp = str; - while (*str != '\0') { - if (*str == '.') { - if (dots >= 3) - return no_match; - - if (*(str + 1) == '.') - return no_match; - - if (*(str + 1) == '\0') - return partly_match; - - dots++; - break; - } - if (!isdigit((int)*str)) - return no_match; - - str++; - } - - if (str - sp > 3) - return no_match; - - strncpy(buf, sp, str - sp); - if (atoi(buf) > 255) - return no_match; - - nums++; - - if (*str == '\0') - break; - - str++; - } - - if (nums < 4) - return partly_match; - - return exact_match; -} - -static enum match_type cmd_ipv4_prefix_match(const char *str) -{ - const char *sp; - int dots = 0; - char buf[4]; - - if (str == NULL) - return partly_match; - - for (;;) { - memset(buf, 0, sizeof(buf)); - sp = str; - while (*str != '\0' && *str != '/') { - if (*str == '.') { - if (dots == 3) - return no_match; - - if (*(str + 1) == '.' || *(str + 1) == '/') - return no_match; - - if (*(str + 1) == '\0') - return partly_match; - - dots++; - break; - } - - if (!isdigit((int)*str)) - return no_match; - - str++; - } - - if (str - sp > 3) - return no_match; - - strncpy(buf, sp, str - sp); - if (atoi(buf) > 255) - return no_match; - - if (dots == 3) { - if (*str == '/') { - if (*(str + 1) == '\0') - return partly_match; - - str++; - break; - } else if (*str == '\0') - return partly_match; - } - - if (*str == '\0') - return partly_match; - - str++; - } - - sp = str; - while (*str != '\0') { - if (!isdigit((int)*str)) - return no_match; - - str++; - } - - if (atoi(sp) > 32) - return no_match; - - return exact_match; -} - -#define IPV6_ADDR_STR "0123456789abcdefABCDEF:.%" -#define IPV6_PREFIX_STR "0123456789abcdefABCDEF:.%/" -#define STATE_START 1 -#define STATE_COLON 2 -#define STATE_DOUBLE 3 -#define STATE_ADDR 4 -#define STATE_DOT 5 -#define STATE_SLASH 6 -#define STATE_MASK 7 - -#ifdef HAVE_IPV6 - -static enum match_type cmd_ipv6_match(const char *str) -{ - int state = STATE_START; - int colons = 0, nums = 0, double_colon = 0; - const char *sp = NULL; - struct sockaddr_in6 sin6_dummy; - int ret; - - if (str == NULL) - return partly_match; - - if (strspn(str, IPV6_ADDR_STR) != strlen(str)) - return no_match; - - /* use inet_pton that has a better support, - * for example inet_pton can support the automatic addresses: - * ::1.2.3.4 - */ - ret = inet_pton(AF_INET6, str, &sin6_dummy.sin6_addr); - - if (ret == 1) - return exact_match; - - while (*str != '\0') { - switch (state) { - case STATE_START: - if (*str == ':') { - if (*(str + 1) != ':' && *(str + 1) != '\0') - return no_match; - colons--; - state = STATE_COLON; - } else { - sp = str; - state = STATE_ADDR; - } - - continue; - case STATE_COLON: - colons++; - if (*(str + 1) == ':') - state = STATE_DOUBLE; - else { - sp = str + 1; - state = STATE_ADDR; - } - break; - case STATE_DOUBLE: - if (double_colon) - return no_match; - - if (*(str + 1) == ':') - return no_match; - else { - if (*(str + 1) != '\0') - colons++; - sp = str + 1; - state = STATE_ADDR; - } - - double_colon++; - nums++; - break; - case STATE_ADDR: - if (*(str + 1) == ':' || *(str + 1) == '\0') { - if (str - sp > 3) - return no_match; - - nums++; - state = STATE_COLON; - } - if (*(str + 1) == '.') - state = STATE_DOT; - break; - case STATE_DOT: - state = STATE_ADDR; - break; - default: - break; - } - - if (nums > 8) - return no_match; - - if (colons > 7) - return no_match; - - str++; - } - -#if 0 - if (nums < 11) - return partly_match; -#endif /* 0 */ - - return exact_match; -} - -static enum match_type cmd_ipv6_prefix_match(const char *str) -{ - int state = STATE_START; - int colons = 0, nums = 0, double_colon = 0; - int mask; - const char *sp = NULL; - char *endptr = NULL; - - if (str == NULL) - return partly_match; - - if (strspn(str, IPV6_PREFIX_STR) != strlen(str)) - return no_match; - - while (*str != '\0' && state != STATE_MASK) { - switch (state) { - case STATE_START: - if (*str == ':') { - if (*(str + 1) != ':' && *(str + 1) != '\0') - return no_match; - colons--; - state = STATE_COLON; - } else { - sp = str; - state = STATE_ADDR; - } - - continue; - case STATE_COLON: - colons++; - if (*(str + 1) == '/') - return no_match; - else if (*(str + 1) == ':') - state = STATE_DOUBLE; - else { - sp = str + 1; - state = STATE_ADDR; - } - break; - case STATE_DOUBLE: - if (double_colon) - return no_match; - - if (*(str + 1) == ':') - return no_match; - else { - if (*(str + 1) != '\0' && *(str + 1) != '/') - colons++; - sp = str + 1; - - if (*(str + 1) == '/') - state = STATE_SLASH; - else - state = STATE_ADDR; - } - - double_colon++; - nums += 1; - break; - case STATE_ADDR: - if (*(str + 1) == ':' || *(str + 1) == '.' - || *(str + 1) == '\0' || *(str + 1) == '/') { - if (str - sp > 3) - return no_match; - - for (; sp <= str; sp++) - if (*sp == '/') - return no_match; - - nums++; - - if (*(str + 1) == ':') - state = STATE_COLON; - else if (*(str + 1) == '.') - state = STATE_DOT; - else if (*(str + 1) == '/') - state = STATE_SLASH; - } - break; - case STATE_DOT: - state = STATE_ADDR; - break; - case STATE_SLASH: - if (*(str + 1) == '\0') - return partly_match; - - state = STATE_MASK; - break; - default: - break; - } - - if (nums > 11) - return no_match; - - if (colons > 7) - return no_match; - - str++; - } - - if (state < STATE_MASK) - return partly_match; - - mask = strtol(str, &endptr, 10); - if (*endptr != '\0') - return no_match; - - if (mask < 0 || mask > 128) - return no_match; - -/* I don't know why mask < 13 makes command match partly. - Forgive me to make this comments. I Want to set static default route - because of lack of function to originate default in ospf6d; sorry - yasu - if (mask < 13) - return partly_match; -*/ - - return exact_match; -} - -#endif /* HAVE_IPV6 */ - -#define DECIMAL_STRLEN_MAX 10 - -static int cmd_range_match(const char *range, const char *str) -{ - char *p; - char buf[DECIMAL_STRLEN_MAX + 1]; - char *endptr = NULL; - unsigned long min, max, val; - - if (str == NULL) - return 1; - - val = strtoul(str, &endptr, 10); - if (*endptr != '\0') - return 0; - - range++; - p = strchr(range, '-'); - if (p == NULL) - return 0; - if (p - range > DECIMAL_STRLEN_MAX) - return 0; - strncpy(buf, range, p - range); - buf[p - range] = '\0'; - min = strtoul(buf, &endptr, 10); - if (*endptr != '\0') - return 0; - - range = p + 1; - p = strchr(range, '>'); - if (p == NULL) - return 0; - if (p - range > DECIMAL_STRLEN_MAX) - return 0; - strncpy(buf, range, p - range); - buf[p - range] = '\0'; - max = strtoul(buf, &endptr, 10); - if (*endptr != '\0') - return 0; - - if (val < min || val > max) - return 0; - - return 1; -} - -/* Make completion match and return match type flag. */ -static enum match_type -cmd_filter_by_completion(char *command, vector v, unsigned int index) -{ - unsigned int i; - const char *str; - struct cmd_element *cmd_element; - enum match_type match_type; - vector descvec; - struct desc *desc; - - match_type = no_match; - - /* If command and cmd_element string does not match set NULL to vector */ - for (i = 0; i < vector_active(v); i++) - if ((cmd_element = vector_slot(v, i)) != NULL) { - if (index >= vector_active(cmd_element->strvec)) - vector_slot(v, i) = NULL; - else { - unsigned int j; - int matched = 0; - - descvec = - vector_slot(cmd_element->strvec, index); - - for (j = 0; j < vector_active(descvec); j++) - if ((desc = vector_slot(descvec, j))) { - str = desc->cmd; - - if (CMD_VARARG(str)) { - if (match_type < - vararg_match) - match_type = - vararg_match; - matched++; - } else if (CMD_RANGE(str)) { - if (cmd_range_match - (str, command)) { - if (match_type < - range_match) - match_type - = - range_match; - - matched++; - } - } -#ifdef HAVE_IPV6 - else if (CMD_IPV6(str)) { - if (cmd_ipv6_match - (command)) { - if (match_type < - ipv6_match) - match_type - = - ipv6_match; - - matched++; - } - } else if (CMD_IPV6_PREFIX(str)) { - if (cmd_ipv6_prefix_match(command)) { - if (match_type < - ipv6_prefix_match) - match_type - = - ipv6_prefix_match; - - matched++; - } - } -#endif /* HAVE_IPV6 */ - else if (CMD_IPV4(str)) { - if (cmd_ipv4_match - (command)) { - if (match_type < - ipv4_match) - match_type - = - ipv4_match; - - matched++; - } - } else if (CMD_IPV4_PREFIX(str)) { - if (cmd_ipv4_prefix_match(command)) { - if (match_type < - ipv4_prefix_match) - match_type - = - ipv4_prefix_match; - matched++; - } - } else - /* Check is this point's argument optional ? */ - if (CMD_OPTION(str) - || - CMD_VARIABLE(str)) { - if (match_type < - extend_match) - match_type = - extend_match; - matched++; - } else - if (strncmp - (command, str, - strlen(command)) == - 0) { - if (strcmp(command, str) - == 0) - match_type = - exact_match; - else { - if (match_type < - partly_match) - match_type - = - partly_match; - } - matched++; - } - } - if (!matched) - vector_slot(v, i) = NULL; - } - } - return match_type; -} - -/* Filter vector by command character with index. */ -static enum match_type -cmd_filter_by_string(char *command, vector v, unsigned int index) -{ - unsigned int i; - const char *str; - struct cmd_element *cmd_element; - enum match_type match_type; - vector descvec; - struct desc *desc; - - match_type = no_match; - - /* If command and cmd_element string does not match set NULL to vector */ - for (i = 0; i < vector_active(v); i++) - if ((cmd_element = vector_slot(v, i)) != NULL) { - /* If given index is bigger than max string vector of command, - set NULL */ - if (index >= vector_active(cmd_element->strvec)) - vector_slot(v, i) = NULL; - else { - unsigned int j; - int matched = 0; - - descvec = - vector_slot(cmd_element->strvec, index); - - for (j = 0; j < vector_active(descvec); j++) - if ((desc = vector_slot(descvec, j))) { - str = desc->cmd; - - if (CMD_VARARG(str)) { - if (match_type < - vararg_match) - match_type = - vararg_match; - matched++; - } else if (CMD_RANGE(str)) { - if (cmd_range_match - (str, command)) { - if (match_type < - range_match) - match_type - = - range_match; - matched++; - } - } -#ifdef HAVE_IPV6 - else if (CMD_IPV6(str)) { - if (cmd_ipv6_match - (command) == - exact_match) { - if (match_type < - ipv6_match) - match_type - = - ipv6_match; - matched++; - } - } else if (CMD_IPV6_PREFIX(str)) { - if (cmd_ipv6_prefix_match(command) == exact_match) { - if (match_type < - ipv6_prefix_match) - match_type - = - ipv6_prefix_match; - matched++; - } - } -#endif /* HAVE_IPV6 */ - else if (CMD_IPV4(str)) { - if (cmd_ipv4_match - (command) == - exact_match) { - if (match_type < - ipv4_match) - match_type - = - ipv4_match; - matched++; - } - } else if (CMD_IPV4_PREFIX(str)) { - if (cmd_ipv4_prefix_match(command) == exact_match) { - if (match_type < - ipv4_prefix_match) - match_type - = - ipv4_prefix_match; - matched++; - } - } else if (CMD_OPTION(str) - || CMD_VARIABLE(str)) - { - if (match_type < - extend_match) - match_type = - extend_match; - matched++; - } else { - if (strcmp(command, str) - == 0) { - match_type = - exact_match; - matched++; - } - } - } - if (!matched) - vector_slot(v, i) = NULL; - } - } - return match_type; -} - -/* Check ambiguous match */ -static int -is_cmd_ambiguous(char *command, vector v, int index, enum match_type type) -{ - unsigned int i; - unsigned int j; - const char *str = NULL; - struct cmd_element *cmd_element; - const char *matched = NULL; - vector descvec; - struct desc *desc; - - for (i = 0; i < vector_active(v); i++) - if ((cmd_element = vector_slot(v, i)) != NULL) { - int match = 0; - - descvec = vector_slot(cmd_element->strvec, index); - - for (j = 0; j < vector_active(descvec); j++) - if ((desc = vector_slot(descvec, j))) { - enum match_type ret; - - str = desc->cmd; - - switch (type) { - case exact_match: - if (! - (CMD_OPTION(str) - || CMD_VARIABLE(str)) -&& strcmp(command, str) == 0) - match++; - break; - case partly_match: - if (! - (CMD_OPTION(str) - || CMD_VARIABLE(str)) -&& strncmp(command, str, strlen(command)) == 0) { - if (matched - && strcmp(matched, - str) != 0) - return 1; /* There is ambiguous match. */ - else - matched = str; - match++; - } - break; - case range_match: - if (cmd_range_match - (str, command)) { - if (matched - && strcmp(matched, - str) != 0) - return 1; - else - matched = str; - match++; - } - break; -#ifdef HAVE_IPV6 - case ipv6_match: - if (CMD_IPV6(str)) - match++; - break; - case ipv6_prefix_match: - if ((ret = - cmd_ipv6_prefix_match - (command)) != no_match) { - if (ret == partly_match) - return 2; /* There is incomplete match. */ - - match++; - } - break; -#endif /* HAVE_IPV6 */ - case ipv4_match: - if (CMD_IPV4(str)) - match++; - break; - case ipv4_prefix_match: - if ((ret = - cmd_ipv4_prefix_match - (command)) != no_match) { - if (ret == partly_match) - return 2; /* There is incomplete match. */ - - match++; - } - break; - case extend_match: - if (CMD_OPTION(str) - || CMD_VARIABLE(str)) - match++; - break; - case no_match: - default: - break; - } - } - if (!match) - vector_slot(v, i) = NULL; - } - return 0; -} - -/* If src matches dst return dst string, otherwise return NULL */ -static const char *cmd_entry_function(const char *src, const char *dst) -{ - /* Skip variable arguments. */ - if (CMD_OPTION(dst) || CMD_VARIABLE(dst) || CMD_VARARG(dst) || - CMD_IPV4(dst) || CMD_IPV4_PREFIX(dst) || CMD_RANGE(dst)) - return NULL; - - /* In case of 'command \t', given src is NULL string. */ - if (src == NULL) - return dst; - - /* Matched with input string. */ - if (strncmp(src, dst, strlen(src)) == 0) - return dst; - - return NULL; -} - -/* If src matches dst return dst string, otherwise return NULL */ -/* This version will return the dst string always if it is - CMD_VARIABLE for '?' key processing */ -static const char *cmd_entry_function_desc(const char *src, const char *dst) -{ - if (CMD_VARARG(dst)) - return dst; - - if (CMD_RANGE(dst)) { - if (cmd_range_match(dst, src)) - return dst; - else - return NULL; - } -#ifdef HAVE_IPV6 - if (CMD_IPV6(dst)) { - if (cmd_ipv6_match(src)) - return dst; - else - return NULL; - } - - if (CMD_IPV6_PREFIX(dst)) { - if (cmd_ipv6_prefix_match(src)) - return dst; - else - return NULL; - } -#endif /* HAVE_IPV6 */ - - if (CMD_IPV4(dst)) { - if (cmd_ipv4_match(src)) - return dst; - else - return NULL; - } - - if (CMD_IPV4_PREFIX(dst)) { - if (cmd_ipv4_prefix_match(src)) - return dst; - else - return NULL; - } - - /* Optional or variable commands always match on '?' */ - if (CMD_OPTION(dst) || CMD_VARIABLE(dst)) - return dst; - - /* In case of 'command \t', given src is NULL string. */ - if (src == NULL) - return dst; - - if (strncmp(src, dst, strlen(src)) == 0) - return dst; - else - return NULL; -} - -/* Check same string element existence. If it isn't there return - 1. */ -static int cmd_unique_string(vector v, const char *str) -{ - unsigned int i; - char *match; - - for (i = 0; i < vector_active(v); i++) - if ((match = vector_slot(v, i)) != NULL) - if (strcmp(match, str) == 0) - return 0; - return 1; -} - -/* Compare string to description vector. If there is same string - return 1 else return 0. */ -static int desc_unique_string(vector v, const char *str) -{ - unsigned int i; - struct desc *desc; - - for (i = 0; i < vector_active(v); i++) - if ((desc = vector_slot(v, i)) != NULL) - if (strcmp(desc->cmd, str) == 0) - return 1; - return 0; -} - -static int cmd_try_do_shortcut(enum node_type node, char *first_word) -{ - if (first_word != NULL && - node != AUTH_NODE && - node != VIEW_NODE && - node != AUTH_ENABLE_NODE && - node != ENABLE_NODE && 0 == strcmp("do", first_word)) - return 1; - return 0; -} - -/* '?' describe command support. */ -static vector -cmd_describe_command_real(vector vline, struct vty *vty, int *status) -{ - unsigned int i; - vector cmd_vector; -#define INIT_MATCHVEC_SIZE 10 - vector matchvec; - struct cmd_element *cmd_element; - unsigned int index; - int ret; - enum match_type match; - char *command; - static struct desc desc_cr = { "<cr>", "" }; - - /* Set index. */ - if (vector_active(vline) == 0) { - *status = CMD_ERR_NO_MATCH; - return NULL; - } else - index = vector_active(vline) - 1; - - /* Make copy vector of current node's command vector. */ - cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node)); - - /* Prepare match vector */ - matchvec = vector_init(INIT_MATCHVEC_SIZE); - - /* Filter commands. */ - /* Only words precedes current word will be checked in this loop. */ - for (i = 0; i < index; i++) - if ((command = vector_slot(vline, i))) { - match = - cmd_filter_by_completion(command, cmd_vector, i); - - if (match == vararg_match) { - struct cmd_element *cmd_element; - vector descvec; - unsigned int j, k; - - for (j = 0; j < vector_active(cmd_vector); j++) - if ((cmd_element = - vector_slot(cmd_vector, j)) != NULL - && - (vector_active - (cmd_element->strvec))) { - descvec = - vector_slot(cmd_element-> - strvec, - vector_active - (cmd_element-> - strvec) - 1); - for (k = 0; - k < vector_active(descvec); - k++) { - struct desc *desc = - vector_slot(descvec, - k); - vector_set(matchvec, - desc); - } - } - - vector_set(matchvec, &desc_cr); - vector_free(cmd_vector); - - return matchvec; - } - - if ((ret = - is_cmd_ambiguous(command, cmd_vector, i, - match)) == 1) { - vector_free(cmd_vector); - *status = CMD_ERR_AMBIGUOUS; - return NULL; - } else if (ret == 2) { - vector_free(cmd_vector); - *status = CMD_ERR_NO_MATCH; - return NULL; - } - } - - /* Prepare match vector */ - /* matchvec = vector_init (INIT_MATCHVEC_SIZE); */ - - /* Make sure that cmd_vector is filtered based on current word */ - command = vector_slot(vline, index); - if (command) - match = cmd_filter_by_completion(command, cmd_vector, index); - - /* Make description vector. */ - for (i = 0; i < vector_active(cmd_vector); i++) - if ((cmd_element = vector_slot(cmd_vector, i)) != NULL) { - const char *string = NULL; - vector strvec = cmd_element->strvec; - - /* if command is NULL, index may be equal to vector_active */ - if (command && index >= vector_active(strvec)) - vector_slot(cmd_vector, i) = NULL; - else { - /* Check if command is completed. */ - if (command == NULL - && index == vector_active(strvec)) { - string = "<cr>"; - if (!desc_unique_string - (matchvec, string)) - vector_set(matchvec, &desc_cr); - } else { - unsigned int j; - vector descvec = - vector_slot(strvec, index); - struct desc *desc; - - for (j = 0; j < vector_active(descvec); - j++) - if ((desc = - vector_slot(descvec, j))) { - string = - cmd_entry_function_desc - (command, - desc->cmd); - if (string) { - /* Uniqueness check */ - if (!desc_unique_string(matchvec, string)) - vector_set - (matchvec, - desc); - } - } - } - } - } - vector_free(cmd_vector); - - if (vector_slot(matchvec, 0) == NULL) { - vector_free(matchvec); - *status = CMD_ERR_NO_MATCH; - } else - *status = CMD_SUCCESS; - - return matchvec; -} - -vector cmd_describe_command(vector vline, struct vty * vty, int *status) -{ - vector ret; - - if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) { - enum node_type onode; - vector shifted_vline; - unsigned int index; - - onode = vty->node; - vty->node = ENABLE_NODE; - /* We can try it on enable node, cos' the vty is authenticated */ - - shifted_vline = vector_init(vector_count(vline)); - /* use memcpy? */ - for (index = 1; index < vector_active(vline); index++) { - vector_set_index(shifted_vline, index - 1, - vector_lookup(vline, index)); - } - - ret = cmd_describe_command_real(shifted_vline, vty, status); - - vector_free(shifted_vline); - vty->node = onode; - return ret; - } - - return cmd_describe_command_real(vline, vty, status); -} - -/* Check LCD of matched command. */ -static int cmd_lcd(char **matched) -{ - int i; - int j; - int lcd = -1; - char *s1, *s2; - char c1, c2; - - if (matched[0] == NULL || matched[1] == NULL) - return 0; - - for (i = 1; matched[i] != NULL; i++) { - s1 = matched[i - 1]; - s2 = matched[i]; - - for (j = 0; (c1 = s1[j]) && (c2 = s2[j]); j++) - if (c1 != c2) - break; - - if (lcd < 0) - lcd = j; - else { - if (lcd > j) - lcd = j; - } - } - return lcd; -} - -/* Command line completion support. */ -static char **cmd_complete_command_real(vector vline, struct vty *vty, - int *status) -{ - unsigned int i; - vector cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node)); -#define INIT_MATCHVEC_SIZE 10 - vector matchvec; - struct cmd_element *cmd_element; - unsigned int index; - char **match_str; - struct desc *desc; - vector descvec; - char *command; - int lcd; - - if (vector_active(vline) == 0) { - *status = CMD_ERR_NO_MATCH; - return NULL; - } else - index = vector_active(vline) - 1; - - /* First, filter by preceeding command string */ - for (i = 0; i < index; i++) - if ((command = vector_slot(vline, i))) { - enum match_type match; - int ret; - - /* First try completion match, if there is exactly match return 1 */ - match = - cmd_filter_by_completion(command, cmd_vector, i); - - /* If there is exact match then filter ambiguous match else check - ambiguousness. */ - if ((ret = - is_cmd_ambiguous(command, cmd_vector, i, - match)) == 1) { - vector_free(cmd_vector); - *status = CMD_ERR_AMBIGUOUS; - return NULL; - } - /* - else if (ret == 2) - { - vector_free (cmd_vector); - *status = CMD_ERR_NO_MATCH; - return NULL; - } - */ - } - - /* Prepare match vector. */ - matchvec = vector_init(INIT_MATCHVEC_SIZE); - - /* Now we got into completion */ - for (i = 0; i < vector_active(cmd_vector); i++) - if ((cmd_element = vector_slot(cmd_vector, i))) { - const char *string; - vector strvec = cmd_element->strvec; - - /* Check field length */ - if (index >= vector_active(strvec)) - vector_slot(cmd_vector, i) = NULL; - else { - unsigned int j; - - descvec = vector_slot(strvec, index); - for (j = 0; j < vector_active(descvec); j++) - if ((desc = vector_slot(descvec, j))) { - if ((string = - cmd_entry_function - (vector_slot(vline, index), - desc->cmd))) - if (cmd_unique_string - (matchvec, string)) - vector_set - (matchvec, - strdup - (string)); - } - } - } - - /* We don't need cmd_vector any more. */ - vector_free(cmd_vector); - - /* No matched command */ - if (vector_slot(matchvec, 0) == NULL) { - vector_free(matchvec); - - /* In case of 'command \t' pattern. Do you need '?' command at - the end of the line. */ - if (vector_slot(vline, index) == '\0') - *status = CMD_ERR_NOTHING_TODO; - else - *status = CMD_ERR_NO_MATCH; - return NULL; - } - - /* Only one matched */ - if (vector_slot(matchvec, 1) == NULL) { - match_str = (char **)matchvec->index; - vector_only_wrapper_free(matchvec); - *status = CMD_COMPLETE_FULL_MATCH; - return match_str; - } - /* Make it sure last element is NULL. */ - vector_set(matchvec, NULL); - - /* Check LCD of matched strings. */ - if (vector_slot(vline, index) != NULL) { - lcd = cmd_lcd((char **)matchvec->index); - - if (lcd) { - int len = strlen(vector_slot(vline, index)); - - if (len < lcd) { - char *lcdstr; - - lcdstr = malloc(lcd + 1); - memcpy(lcdstr, matchvec->index[0], lcd); - lcdstr[lcd] = '\0'; - - /* match_str = (char **) &lcdstr; */ - - /* Free matchvec. */ - for (i = 0; i < vector_active(matchvec); i++) { - if (vector_slot(matchvec, i)) - free(vector_slot(matchvec, i)); - } - vector_free(matchvec); - - /* Make new matchvec. */ - matchvec = vector_init(INIT_MATCHVEC_SIZE); - vector_set(matchvec, lcdstr); - match_str = (char **)matchvec->index; - vector_only_wrapper_free(matchvec); - - *status = CMD_COMPLETE_MATCH; - return match_str; - } - } - } - - match_str = (char **)matchvec->index; - vector_only_wrapper_free(matchvec); - *status = CMD_COMPLETE_LIST_MATCH; - return match_str; -} - -char **cmd_complete_command(vector vline, struct vty *vty, int *status) -{ - char **ret; - - if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) { - enum node_type onode; - vector shifted_vline; - unsigned int index; - - onode = vty->node; - vty->node = ENABLE_NODE; - /* We can try it on enable node, cos' the vty is authenticated */ - - shifted_vline = vector_init(vector_count(vline)); - /* use memcpy? */ - for (index = 1; index < vector_active(vline); index++) { - vector_set_index(shifted_vline, index - 1, - vector_lookup(vline, index)); - } - - ret = cmd_complete_command_real(shifted_vline, vty, status); - - vector_free(shifted_vline); - vty->node = onode; - return ret; - } - - return cmd_complete_command_real(vline, vty, status); -} - -/* return parent node */ -/* MUST eventually converge on CONFIG_NODE */ -enum node_type node_parent(enum node_type node) -{ - enum node_type ret; - - assert(node > CONFIG_NODE); - - switch (node) { - case BGP_VPNV4_NODE: - case BGP_IPV4_NODE: - case BGP_IPV4M_NODE: - case BGP_IPV6_NODE: - ret = BGP_NODE; - break; - case KEYCHAIN_KEY_NODE: - ret = KEYCHAIN_NODE; - break; - default: - ret = CONFIG_NODE; - } - - return ret; -} - -/* Execute command by argument vline vector. */ -static int -cmd_execute_command_real(vector vline, struct vty *vty, - struct cmd_element **cmd) -{ - unsigned int i; - unsigned int index; - vector cmd_vector; - struct cmd_element *cmd_element; - struct cmd_element *matched_element; - unsigned int matched_count, incomplete_count; - int argc; - const char *argv[CMD_ARGC_MAX]; - enum match_type match = 0; - int varflag; - char *command; - - /* Make copy of command elements. */ - cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node)); - - for (index = 0; index < vector_active(vline); index++) - if ((command = vector_slot(vline, index))) { - int ret; - - match = - cmd_filter_by_completion(command, cmd_vector, - index); - - if (match == vararg_match) - break; - - ret = - is_cmd_ambiguous(command, cmd_vector, index, match); - - if (ret == 1) { - vector_free(cmd_vector); - return CMD_ERR_AMBIGUOUS; - } else if (ret == 2) { - vector_free(cmd_vector); - return CMD_ERR_NO_MATCH; - } - } - - /* Check matched count. */ - matched_element = NULL; - matched_count = 0; - incomplete_count = 0; - - for (i = 0; i < vector_active(cmd_vector); i++) - if ((cmd_element = vector_slot(cmd_vector, i))) { - if (match == vararg_match - || index >= cmd_element->cmdsize) { - matched_element = cmd_element; -#if 0 - printf("DEBUG: %s\n", cmd_element->string); -#endif - matched_count++; - } else { - incomplete_count++; - } - } - - /* Finish of using cmd_vector. */ - vector_free(cmd_vector); - - /* To execute command, matched_count must be 1. */ - if (matched_count == 0) { - if (incomplete_count) - return CMD_ERR_INCOMPLETE; - else - return CMD_ERR_NO_MATCH; - } - - if (matched_count > 1) - return CMD_ERR_AMBIGUOUS; - - /* Argument treatment */ - varflag = 0; - argc = 0; - - for (i = 0; i < vector_active(vline); i++) { - if (varflag) - argv[argc++] = vector_slot(vline, i); - else { - vector descvec = - vector_slot(matched_element->strvec, i); - - if (vector_active(descvec) == 1) { - struct desc *desc = vector_slot(descvec, 0); - - if (CMD_VARARG(desc->cmd)) - varflag = 1; - - if (varflag || CMD_VARIABLE(desc->cmd) - || CMD_OPTION(desc->cmd)) - argv[argc++] = vector_slot(vline, i); - } else - argv[argc++] = vector_slot(vline, i); - } - - if (argc >= CMD_ARGC_MAX) - return CMD_ERR_EXEED_ARGC_MAX; - } - - /* For vtysh execution. */ - if (cmd) - *cmd = matched_element; - - if (matched_element->daemon) - return CMD_SUCCESS_DAEMON; - - /* Execute matched command. */ - return (*matched_element->func) (matched_element, vty, argc, argv); -} - -int -cmd_execute_command(vector vline, struct vty *vty, struct cmd_element **cmd, - int vtysh) -{ - int ret, saved_ret, tried = 0; - enum node_type onode, try_node; - - onode = try_node = vty->node; - - if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) { - vector shifted_vline; - unsigned int index; - - vty->node = ENABLE_NODE; - /* We can try it on enable node, cos' the vty is authenticated */ - - shifted_vline = vector_init(vector_count(vline)); - /* use memcpy? */ - for (index = 1; index < vector_active(vline); index++) { - vector_set_index(shifted_vline, index - 1, - vector_lookup(vline, index)); - } - - ret = cmd_execute_command_real(shifted_vline, vty, cmd); - - vector_free(shifted_vline); - vty->node = onode; - return ret; - } - - saved_ret = ret = cmd_execute_command_real(vline, vty, cmd); - - if (vtysh) - return saved_ret; - - /* This assumes all nodes above CONFIG_NODE are childs of CONFIG_NODE */ - while (ret != CMD_SUCCESS && ret != CMD_WARNING - && vty->node > CONFIG_NODE) { - try_node = node_parent(try_node); - vty->node = try_node; - ret = cmd_execute_command_real(vline, vty, cmd); - tried = 1; - if (ret == CMD_SUCCESS || ret == CMD_WARNING) { - /* succesfull command, leave the node as is */ - return ret; - } - } - /* no command succeeded, reset the vty to the original node and - return the error for this node */ - if (tried) - vty->node = onode; - return saved_ret; -} - -/* Execute command by argument readline. */ -int -cmd_execute_command_strict(vector vline, struct vty *vty, - struct cmd_element **cmd) -{ - unsigned int i; - unsigned int index; - vector cmd_vector; - struct cmd_element *cmd_element; - struct cmd_element *matched_element; - unsigned int matched_count, incomplete_count; - int argc; - const char *argv[CMD_ARGC_MAX]; - int varflag; - enum match_type match = 0; - char *command; - - /* Make copy of command element */ - cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node)); - - for (index = 0; index < vector_active(vline); index++) - if ((command = vector_slot(vline, index))) { - int ret; - - match = cmd_filter_by_string(vector_slot(vline, index), - cmd_vector, index); - - /* If command meets '.VARARG' then finish matching. */ - if (match == vararg_match) - break; - - ret = - is_cmd_ambiguous(command, cmd_vector, index, match); - if (ret == 1) { - vector_free(cmd_vector); - return CMD_ERR_AMBIGUOUS; - } - if (ret == 2) { - vector_free(cmd_vector); - return CMD_ERR_NO_MATCH; - } - } - - /* Check matched count. */ - matched_element = NULL; - matched_count = 0; - incomplete_count = 0; - for (i = 0; i < vector_active(cmd_vector); i++) - if (vector_slot(cmd_vector, i) != NULL) { - cmd_element = vector_slot(cmd_vector, i); - - if (match == vararg_match - || index >= cmd_element->cmdsize) { - matched_element = cmd_element; - matched_count++; - } else - incomplete_count++; - } - - /* Finish of using cmd_vector. */ - vector_free(cmd_vector); - - /* To execute command, matched_count must be 1. */ - if (matched_count == 0) { - if (incomplete_count) - return CMD_ERR_INCOMPLETE; - else - return CMD_ERR_NO_MATCH; - } - - if (matched_count > 1) - return CMD_ERR_AMBIGUOUS; - - /* Argument treatment */ - varflag = 0; - argc = 0; - - for (i = 0; i < vector_active(vline); i++) { - if (varflag) - argv[argc++] = vector_slot(vline, i); - else { - vector descvec = - vector_slot(matched_element->strvec, i); - - if (vector_active(descvec) == 1) { - struct desc *desc = vector_slot(descvec, 0); - - if (CMD_VARARG(desc->cmd)) - varflag = 1; - - if (varflag || CMD_VARIABLE(desc->cmd) - || CMD_OPTION(desc->cmd)) - argv[argc++] = vector_slot(vline, i); - } else - argv[argc++] = vector_slot(vline, i); - } - - if (argc >= CMD_ARGC_MAX) - return CMD_ERR_EXEED_ARGC_MAX; - } - - /* For vtysh execution. */ - if (cmd) - *cmd = matched_element; - - if (matched_element->daemon) - return CMD_SUCCESS_DAEMON; - - /* Now execute matched command */ - return (*matched_element->func) (matched_element, vty, argc, argv); -} - -#if 0 -/* Configration make from file. */ -int config_from_file(struct vty *vty, FILE * fp) -{ - int ret; - vector vline; - - while (fgets(vty->buf, VTY_BUFSIZ, fp)) { - vline = cmd_make_strvec(vty->buf); - - /* In case of comment line */ - if (vline == NULL) - continue; - /* Execute configuration command : this is strict match */ - ret = cmd_execute_command_strict(vline, vty, NULL); - - /* Try again with setting node to CONFIG_NODE */ - while (ret != CMD_SUCCESS && ret != CMD_WARNING - && ret != CMD_ERR_NOTHING_TODO - && vty->node != CONFIG_NODE) { - vty->node = node_parent(vty->node); - ret = cmd_execute_command_strict(vline, vty, NULL); - } - - cmd_free_strvec(vline); - - if (ret != CMD_SUCCESS && ret != CMD_WARNING - && ret != CMD_ERR_NOTHING_TODO) - return ret; - } - return CMD_SUCCESS; -} -#endif - -/* Configration from terminal */ -DEFUN(config_terminal, - config_terminal_cmd, - "configure terminal", - "Configuration from vty interface\n" "Configuration terminal\n") -{ - if (vty_config_lock(vty)) - vty->node = CONFIG_NODE; - else { - vty_out(vty, "VTY configuration is locked by other VTY%s", - VTY_NEWLINE); - return CMD_WARNING; - } - return CMD_SUCCESS; -} - -/* Enable command */ -DEFUN(enable, config_enable_cmd, "enable", "Turn on privileged mode command\n") -{ - /* If enable password is NULL, change to ENABLE_NODE */ - if ((host.enable == NULL && host.enable_encrypt == NULL) || - vty->type == VTY_SHELL_SERV) - vty->node = ENABLE_NODE; - else - vty->node = AUTH_ENABLE_NODE; - - return CMD_SUCCESS; -} - -/* Disable command */ -DEFUN(disable, - config_disable_cmd, "disable", "Turn off privileged mode command\n") -{ - if (vty->node == ENABLE_NODE) - vty->node = VIEW_NODE; - return CMD_SUCCESS; -} - -/* Down vty node level. */ -DEFUN(config_exit, - config_exit_cmd, "exit", "Exit current mode and down to previous mode\n") -{ - switch (vty->node) { - case BTS_NODE: - vty->node = VIEW_NODE; - vty->index = NULL; - break; - case TRX_NODE: - vty->node = BTS_NODE; - { - /* set vty->index correctly ! */ - struct gsm_bts_trx *trx = vty->index; - vty->index = trx->bts; - } - break; - case TS_NODE: - vty->node = TRX_NODE; - { - /* set vty->index correctly ! */ - struct gsm_bts_trx_ts *ts = vty->index; - vty->index = ts->trx; - } - break; - case SUBSCR_NODE: - vty->node = VIEW_NODE; - subscr_put(vty->index); - vty->index = NULL; - break; - case VIEW_NODE: - case ENABLE_NODE: - if (0) //vty_shell (vty)) - exit(0); - else - vty->status = VTY_CLOSE; - break; - case CONFIG_NODE: - vty->node = ENABLE_NODE; - vty_config_unlock(vty); - break; - case INTERFACE_NODE: - case ZEBRA_NODE: - case BGP_NODE: - case RIP_NODE: - case RIPNG_NODE: - case OSPF_NODE: - case OSPF6_NODE: - case ISIS_NODE: - case KEYCHAIN_NODE: - case MASC_NODE: - case RMAP_NODE: - case VTY_NODE: - vty->node = CONFIG_NODE; - break; - case BGP_VPNV4_NODE: - case BGP_IPV4_NODE: - case BGP_IPV4M_NODE: - case BGP_IPV6_NODE: - vty->node = BGP_NODE; - break; - case KEYCHAIN_KEY_NODE: - vty->node = KEYCHAIN_NODE; - break; - default: - break; - } - return CMD_SUCCESS; -} - -/* quit is alias of exit. */ -ALIAS(config_exit, - config_quit_cmd, "quit", "Exit current mode and down to previous mode\n") - -/* End of configuration. */ - DEFUN(config_end, - config_end_cmd, "end", "End current mode and change to enable mode.") -{ - switch (vty->node) { - case VIEW_NODE: - case ENABLE_NODE: - /* Nothing to do. */ - break; - case CONFIG_NODE: - case INTERFACE_NODE: - case ZEBRA_NODE: - case RIP_NODE: - case RIPNG_NODE: - case BGP_NODE: - case BGP_VPNV4_NODE: - case BGP_IPV4_NODE: - case BGP_IPV4M_NODE: - case BGP_IPV6_NODE: - case RMAP_NODE: - case OSPF_NODE: - case OSPF6_NODE: - case ISIS_NODE: - case KEYCHAIN_NODE: - case KEYCHAIN_KEY_NODE: - case MASC_NODE: - case VTY_NODE: - vty_config_unlock(vty); - vty->node = ENABLE_NODE; - break; - default: - break; - } - return CMD_SUCCESS; -} - -/* Show version. */ -DEFUN(show_version, - show_version_cmd, "show version", SHOW_STR "Displays program version\n") -{ - vty_out(vty, "%s %s (%s).%s", QUAGGA_PROGNAME, QUAGGA_VERSION, - host.name ? host.name : "", VTY_NEWLINE); - vty_out(vty, "%s%s", QUAGGA_COPYRIGHT, VTY_NEWLINE); - - return CMD_SUCCESS; -} - -/* Help display function for all node. */ -DEFUN(config_help, - config_help_cmd, "help", "Description of the interactive help system\n") -{ - vty_out(vty, - "This VTY provides advanced help features. When you need help,%s\ -anytime at the command line please press '?'.%s\ -%s\ -If nothing matches, the help list will be empty and you must backup%s\ - until entering a '?' shows the available options.%s\ -Two styles of help are provided:%s\ -1. Full help is available when you are ready to enter a%s\ -command argument (e.g. 'show ?') and describes each possible%s\ -argument.%s\ -2. Partial help is provided when an abbreviated argument is entered%s\ - and you want to know what arguments match the input%s\ - (e.g. 'show me?'.)%s%s", VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, - VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE); - return CMD_SUCCESS; -} - -/* Help display function for all node. */ -DEFUN(config_list, config_list_cmd, "list", "Print command list\n") -{ - unsigned int i; - struct cmd_node *cnode = vector_slot(cmdvec, vty->node); - struct cmd_element *cmd; - - for (i = 0; i < vector_active(cnode->cmd_vector); i++) - if ((cmd = vector_slot(cnode->cmd_vector, i)) != NULL - && !(cmd->attr == CMD_ATTR_DEPRECATED - || cmd->attr == CMD_ATTR_HIDDEN)) - vty_out(vty, " %s%s", cmd->string, VTY_NEWLINE); - return CMD_SUCCESS; -} - -#if 0 -/* Write current configuration into file. */ -DEFUN(config_write_file, - config_write_file_cmd, - "write file", - "Write running configuration to memory, network, or terminal\n" - "Write to configuration file\n") -{ - unsigned int i; - int fd; - struct cmd_node *node; - char *config_file; - char *config_file_tmp = NULL; - char *config_file_sav = NULL; - struct vty *file_vty; - - /* Check and see if we are operating under vtysh configuration */ - if (host.config == NULL) { - vty_out(vty, "Can't save to configuration file, using vtysh.%s", - VTY_NEWLINE); - return CMD_WARNING; - } - - /* Get filename. */ - config_file = host.config; - - config_file_sav = - malloc(strlen(config_file) + strlen(CONF_BACKUP_EXT) + 1); - strcpy(config_file_sav, config_file); - strcat(config_file_sav, CONF_BACKUP_EXT); - - config_file_tmp = malloc(strlen(config_file) + 8); - sprintf(config_file_tmp, "%s.XXXXXX", config_file); - - /* Open file to configuration write. */ - fd = mkstemp(config_file_tmp); - if (fd < 0) { - vty_out(vty, "Can't open configuration file %s.%s", - config_file_tmp, VTY_NEWLINE); - free(config_file_tmp); - free(config_file_sav); - return CMD_WARNING; - } - - /* Make vty for configuration file. */ - file_vty = vty_new(); - file_vty->fd = fd; - file_vty->type = VTY_FILE; - - /* Config file header print. */ - vty_out(file_vty, "!\n! Zebra configuration saved from vty\n! "); - //vty_time_print (file_vty, 1); - vty_out(file_vty, "!\n"); - - for (i = 0; i < vector_active(cmdvec); i++) - if ((node = vector_slot(cmdvec, i)) && node->func) { - if ((*node->func) (file_vty)) - vty_out(file_vty, "!\n"); - } - vty_close(file_vty); - - if (unlink(config_file_sav) != 0) - if (errno != ENOENT) { - vty_out(vty, - "Can't unlink backup configuration file %s.%s", - config_file_sav, VTY_NEWLINE); - free(config_file_sav); - free(config_file_tmp); - unlink(config_file_tmp); - return CMD_WARNING; - } - if (link(config_file, config_file_sav) != 0) { - vty_out(vty, "Can't backup old configuration file %s.%s", - config_file_sav, VTY_NEWLINE); - free(config_file_sav); - free(config_file_tmp); - unlink(config_file_tmp); - return CMD_WARNING; - } - sync(); - if (unlink(config_file) != 0) { - vty_out(vty, "Can't unlink configuration file %s.%s", - config_file, VTY_NEWLINE); - free(config_file_sav); - free(config_file_tmp); - unlink(config_file_tmp); - return CMD_WARNING; - } - if (link(config_file_tmp, config_file) != 0) { - vty_out(vty, "Can't save configuration file %s.%s", config_file, - VTY_NEWLINE); - free(config_file_sav); - free(config_file_tmp); - unlink(config_file_tmp); - return CMD_WARNING; - } - unlink(config_file_tmp); - sync(); - - free(config_file_sav); - free(config_file_tmp); - - if (chmod(config_file, CONFIGFILE_MASK) != 0) { - vty_out(vty, "Can't chmod configuration file %s: %s (%d).%s", - config_file, safe_strerror(errno), errno, VTY_NEWLINE); - return CMD_WARNING; - } - - vty_out(vty, "Configuration saved to %s%s", config_file, VTY_NEWLINE); - return CMD_SUCCESS; -} - -ALIAS(config_write_file, - config_write_cmd, - "write", "Write running configuration to memory, network, or terminal\n") - - ALIAS(config_write_file, - config_write_memory_cmd, - "write memory", - "Write running configuration to memory, network, or terminal\n" - "Write configuration to the file (same as write file)\n") - - ALIAS(config_write_file, - copy_runningconfig_startupconfig_cmd, - "copy running-config startup-config", - "Copy configuration\n" - "Copy running config to... \n" - "Copy running config to startup config (same as write file)\n") - -/* Write current configuration into the terminal. */ - DEFUN(config_write_terminal, - config_write_terminal_cmd, - "write terminal", - "Write running configuration to memory, network, or terminal\n" - "Write to terminal\n") -{ - unsigned int i; - struct cmd_node *node; - - if (vty->type == VTY_SHELL_SERV) { - for (i = 0; i < vector_active(cmdvec); i++) - if ((node = vector_slot(cmdvec, i)) && node->func - && node->vtysh) { - if ((*node->func) (vty)) - vty_out(vty, "!%s", VTY_NEWLINE); - } - } else { - vty_out(vty, "%sCurrent configuration:%s", VTY_NEWLINE, - VTY_NEWLINE); - vty_out(vty, "!%s", VTY_NEWLINE); - - for (i = 0; i < vector_active(cmdvec); i++) - if ((node = vector_slot(cmdvec, i)) && node->func) { - if ((*node->func) (vty)) - vty_out(vty, "!%s", VTY_NEWLINE); - } - vty_out(vty, "end%s", VTY_NEWLINE); - } - return CMD_SUCCESS; -} - -/* Write current configuration into the terminal. */ -ALIAS(config_write_terminal, - show_running_config_cmd, - "show running-config", SHOW_STR "running configuration\n") - -/* Write startup configuration into the terminal. */ - DEFUN(show_startup_config, - show_startup_config_cmd, - "show startup-config", SHOW_STR "Contentes of startup configuration\n") -{ - char buf[BUFSIZ]; - FILE *confp; - - confp = fopen(host.config, "r"); - if (confp == NULL) { - vty_out(vty, "Can't open configuration file [%s]%s", - host.config, VTY_NEWLINE); - return CMD_WARNING; - } - - while (fgets(buf, BUFSIZ, confp)) { - char *cp = buf; - - while (*cp != '\r' && *cp != '\n' && *cp != '\0') - cp++; - *cp = '\0'; - - vty_out(vty, "%s%s", buf, VTY_NEWLINE); - } - - fclose(confp); - - return CMD_SUCCESS; -} -#endif - -/* Hostname configuration */ -DEFUN(config_hostname, - hostname_cmd, - "hostname WORD", - "Set system's network name\n" "This system's network name\n") -{ - if (!isalpha((int)*argv[0])) { - vty_out(vty, "Please specify string starting with alphabet%s", - VTY_NEWLINE); - return CMD_WARNING; - } - - if (host.name) - free(host.name); - - host.name = strdup(argv[0]); - return CMD_SUCCESS; -} - -DEFUN(config_no_hostname, - no_hostname_cmd, - "no hostname [HOSTNAME]", - NO_STR "Reset system's network name\n" "Host name of this router\n") -{ - if (host.name) - free(host.name); - host.name = NULL; - return CMD_SUCCESS; -} - -/* VTY interface password set. */ -DEFUN(config_password, password_cmd, - "password (8|) WORD", - "Assign the terminal connection password\n" - "Specifies a HIDDEN password will follow\n" - "dummy string \n" "The HIDDEN line password string\n") -{ - /* Argument check. */ - if (argc == 0) { - vty_out(vty, "Please specify password.%s", VTY_NEWLINE); - return CMD_WARNING; - } - - if (argc == 2) { - if (*argv[0] == '8') { - if (host.password) - free(host.password); - host.password = NULL; - if (host.password_encrypt) - free(host.password_encrypt); - host.password_encrypt = strdup(strdup(argv[1])); - return CMD_SUCCESS; - } else { - vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE); - return CMD_WARNING; - } - } - - if (!isalnum((int)*argv[0])) { - vty_out(vty, - "Please specify string starting with alphanumeric%s", - VTY_NEWLINE); - return CMD_WARNING; - } - - if (host.password) - free(host.password); - host.password = NULL; - -#ifdef VTY_CRYPT_PW - if (host.encrypt) { - if (host.password_encrypt) - free(host.password_encrypt); - host.password_encrypt = strdup(zencrypt(argv[0])); - } else -#endif - host.password = strdup(argv[0]); - - return CMD_SUCCESS; -} - -ALIAS(config_password, password_text_cmd, - "password LINE", - "Assign the terminal connection password\n" - "The UNENCRYPTED (cleartext) line password\n") - -/* VTY enable password set. */ - DEFUN(config_enable_password, enable_password_cmd, - "enable password (8|) WORD", - "Modify enable password parameters\n" - "Assign the privileged level password\n" - "Specifies a HIDDEN password will follow\n" - "dummy string \n" "The HIDDEN 'enable' password string\n") -{ - /* Argument check. */ - if (argc == 0) { - vty_out(vty, "Please specify password.%s", VTY_NEWLINE); - return CMD_WARNING; - } - - /* Crypt type is specified. */ - if (argc == 2) { - if (*argv[0] == '8') { - if (host.enable) - free(host.enable); - host.enable = NULL; - - if (host.enable_encrypt) - free(host.enable_encrypt); - host.enable_encrypt = strdup(argv[1]); - - return CMD_SUCCESS; - } else { - vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE); - return CMD_WARNING; - } - } - - if (!isalnum((int)*argv[0])) { - vty_out(vty, - "Please specify string starting with alphanumeric%s", - VTY_NEWLINE); - return CMD_WARNING; - } - - if (host.enable) - free(host.enable); - host.enable = NULL; - - /* Plain password input. */ -#ifdef VTY_CRYPT_PW - if (host.encrypt) { - if (host.enable_encrypt) - free(host.enable_encrypt); - host.enable_encrypt = strdup(zencrypt(argv[0])); - } else -#endif - host.enable = strdup(argv[0]); - - return CMD_SUCCESS; -} - -ALIAS(config_enable_password, - enable_password_text_cmd, - "enable password LINE", - "Modify enable password parameters\n" - "Assign the privileged level password\n" - "The UNENCRYPTED (cleartext) 'enable' password\n") - -/* VTY enable password delete. */ - DEFUN(no_config_enable_password, no_enable_password_cmd, - "no enable password", - NO_STR - "Modify enable password parameters\n" - "Assign the privileged level password\n") -{ - if (host.enable) - free(host.enable); - host.enable = NULL; - - if (host.enable_encrypt) - free(host.enable_encrypt); - host.enable_encrypt = NULL; - - return CMD_SUCCESS; -} - -#ifdef VTY_CRYPT_PW -DEFUN(service_password_encrypt, - service_password_encrypt_cmd, - "service password-encryption", - "Set up miscellaneous service\n" "Enable encrypted passwords\n") -{ - if (host.encrypt) - return CMD_SUCCESS; - - host.encrypt = 1; - - if (host.password) { - if (host.password_encrypt) - free(host.password_encrypt); - host.password_encrypt = strdup(zencrypt(host.password)); - } - if (host.enable) { - if (host.enable_encrypt) - free(host.enable_encrypt); - host.enable_encrypt = strdup(zencrypt(host.enable)); - } - - return CMD_SUCCESS; -} - -DEFUN(no_service_password_encrypt, - no_service_password_encrypt_cmd, - "no service password-encryption", - NO_STR "Set up miscellaneous service\n" "Enable encrypted passwords\n") -{ - if (!host.encrypt) - return CMD_SUCCESS; - - host.encrypt = 0; - - if (host.password_encrypt) - free(host.password_encrypt); - host.password_encrypt = NULL; - - if (host.enable_encrypt) - free(host.enable_encrypt); - host.enable_encrypt = NULL; - - return CMD_SUCCESS; -} -#endif - -DEFUN(config_terminal_length, config_terminal_length_cmd, - "terminal length <0-512>", - "Set terminal line parameters\n" - "Set number of lines on a screen\n" - "Number of lines on screen (0 for no pausing)\n") -{ - int lines; - char *endptr = NULL; - - lines = strtol(argv[0], &endptr, 10); - if (lines < 0 || lines > 512 || *endptr != '\0') { - vty_out(vty, "length is malformed%s", VTY_NEWLINE); - return CMD_WARNING; - } - vty->lines = lines; - - return CMD_SUCCESS; -} - -DEFUN(config_terminal_no_length, config_terminal_no_length_cmd, - "terminal no length", - "Set terminal line parameters\n" - NO_STR "Set number of lines on a screen\n") -{ - vty->lines = -1; - return CMD_SUCCESS; -} - -DEFUN(service_terminal_length, service_terminal_length_cmd, - "service terminal-length <0-512>", - "Set up miscellaneous service\n" - "System wide terminal length configuration\n" - "Number of lines of VTY (0 means no line control)\n") -{ - int lines; - char *endptr = NULL; - - lines = strtol(argv[0], &endptr, 10); - if (lines < 0 || lines > 512 || *endptr != '\0') { - vty_out(vty, "length is malformed%s", VTY_NEWLINE); - return CMD_WARNING; - } - host.lines = lines; - - return CMD_SUCCESS; -} - -DEFUN(no_service_terminal_length, no_service_terminal_length_cmd, - "no service terminal-length [<0-512>]", - NO_STR - "Set up miscellaneous service\n" - "System wide terminal length configuration\n" - "Number of lines of VTY (0 means no line control)\n") -{ - host.lines = -1; - return CMD_SUCCESS; -} - -DEFUN_HIDDEN(do_echo, - echo_cmd, - "echo .MESSAGE", - "Echo a message back to the vty\n" "The message to echo\n") -{ - char *message; - - vty_out(vty, "%s%s", - ((message = - argv_concat(argv, argc, 0)) ? message : ""), VTY_NEWLINE); - if (message) - free(message); - return CMD_SUCCESS; -} - -#if 0 -DEFUN(config_logmsg, - config_logmsg_cmd, - "logmsg " LOG_LEVELS " .MESSAGE", - "Send a message to enabled logging destinations\n" - LOG_LEVEL_DESC "The message to send\n") -{ - int level; - char *message; - - if ((level = level_match(argv[0])) == ZLOG_DISABLED) - return CMD_ERR_NO_MATCH; - - zlog(NULL, level, - ((message = argv_concat(argv, argc, 1)) ? message : "")); - if (message) - free(message); - return CMD_SUCCESS; -} - -DEFUN(show_logging, - show_logging_cmd, - "show logging", SHOW_STR "Show current logging configuration\n") -{ - struct zlog *zl = zlog_default; - - vty_out(vty, "Syslog logging: "); - if (zl->maxlvl[ZLOG_DEST_SYSLOG] == ZLOG_DISABLED) - vty_out(vty, "disabled"); - else - vty_out(vty, "level %s, facility %s, ident %s", - zlog_priority[zl->maxlvl[ZLOG_DEST_SYSLOG]], - facility_name(zl->facility), zl->ident); - vty_out(vty, "%s", VTY_NEWLINE); - - vty_out(vty, "Stdout logging: "); - if (zl->maxlvl[ZLOG_DEST_STDOUT] == ZLOG_DISABLED) - vty_out(vty, "disabled"); - else - vty_out(vty, "level %s", - zlog_priority[zl->maxlvl[ZLOG_DEST_STDOUT]]); - vty_out(vty, "%s", VTY_NEWLINE); - - vty_out(vty, "Monitor logging: "); - if (zl->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED) - vty_out(vty, "disabled"); - else - vty_out(vty, "level %s", - zlog_priority[zl->maxlvl[ZLOG_DEST_MONITOR]]); - vty_out(vty, "%s", VTY_NEWLINE); - - vty_out(vty, "File logging: "); - if ((zl->maxlvl[ZLOG_DEST_FILE] == ZLOG_DISABLED) || !zl->fp) - vty_out(vty, "disabled"); - else - vty_out(vty, "level %s, filename %s", - zlog_priority[zl->maxlvl[ZLOG_DEST_FILE]], - zl->filename); - vty_out(vty, "%s", VTY_NEWLINE); - - vty_out(vty, "Protocol name: %s%s", - zlog_proto_names[zl->protocol], VTY_NEWLINE); - vty_out(vty, "Record priority: %s%s", - (zl->record_priority ? "enabled" : "disabled"), VTY_NEWLINE); - - return CMD_SUCCESS; -} - -DEFUN(config_log_stdout, - config_log_stdout_cmd, - "log stdout", "Logging control\n" "Set stdout logging level\n") -{ - zlog_set_level(NULL, ZLOG_DEST_STDOUT, zlog_default->default_lvl); - return CMD_SUCCESS; -} - -DEFUN(config_log_stdout_level, - config_log_stdout_level_cmd, - "log stdout " LOG_LEVELS, - "Logging control\n" "Set stdout logging level\n" LOG_LEVEL_DESC) -{ - int level; - - if ((level = level_match(argv[0])) == ZLOG_DISABLED) - return CMD_ERR_NO_MATCH; - zlog_set_level(NULL, ZLOG_DEST_STDOUT, level); - return CMD_SUCCESS; -} - -DEFUN(no_config_log_stdout, - no_config_log_stdout_cmd, - "no log stdout [LEVEL]", - NO_STR "Logging control\n" "Cancel logging to stdout\n" "Logging level\n") -{ - zlog_set_level(NULL, ZLOG_DEST_STDOUT, ZLOG_DISABLED); - return CMD_SUCCESS; -} - -DEFUN(config_log_monitor, - config_log_monitor_cmd, - "log monitor", - "Logging control\n" "Set terminal line (monitor) logging level\n") -{ - zlog_set_level(NULL, ZLOG_DEST_MONITOR, zlog_default->default_lvl); - return CMD_SUCCESS; -} - -DEFUN(config_log_monitor_level, - config_log_monitor_level_cmd, - "log monitor " LOG_LEVELS, - "Logging control\n" - "Set terminal line (monitor) logging level\n" LOG_LEVEL_DESC) -{ - int level; - - if ((level = level_match(argv[0])) == ZLOG_DISABLED) - return CMD_ERR_NO_MATCH; - zlog_set_level(NULL, ZLOG_DEST_MONITOR, level); - return CMD_SUCCESS; -} - -DEFUN(no_config_log_monitor, - no_config_log_monitor_cmd, - "no log monitor [LEVEL]", - NO_STR - "Logging control\n" - "Disable terminal line (monitor) logging\n" "Logging level\n") -{ - zlog_set_level(NULL, ZLOG_DEST_MONITOR, ZLOG_DISABLED); - return CMD_SUCCESS; -} - -static int set_log_file(struct vty *vty, const char *fname, int loglevel) -{ - int ret; - char *p = NULL; - const char *fullpath; - - /* Path detection. */ - if (!IS_DIRECTORY_SEP(*fname)) { - char cwd[MAXPATHLEN + 1]; - cwd[MAXPATHLEN] = '\0'; - - if (getcwd(cwd, MAXPATHLEN) == NULL) { - zlog_err("config_log_file: Unable to alloc mem!"); - return CMD_WARNING; - } - - if ((p = malloc(strlen(cwd) + strlen(fname) + 2)) - == NULL) { - zlog_err("config_log_file: Unable to alloc mem!"); - return CMD_WARNING; - } - sprintf(p, "%s/%s", cwd, fname); - fullpath = p; - } else - fullpath = fname; - - ret = zlog_set_file(NULL, fullpath, loglevel); - - if (p) - free(p); - - if (!ret) { - vty_out(vty, "can't open logfile %s\n", fname); - return CMD_WARNING; - } - - if (host.logfile) - free(host.logfile); - - host.logfile = strdup(fname); - - return CMD_SUCCESS; -} - -DEFUN(config_log_file, - config_log_file_cmd, - "log file FILENAME", - "Logging control\n" "Logging to file\n" "Logging filename\n") -{ - return set_log_file(vty, argv[0], zlog_default->default_lvl); -} - -DEFUN(config_log_file_level, - config_log_file_level_cmd, - "log file FILENAME " LOG_LEVELS, - "Logging control\n" - "Logging to file\n" "Logging filename\n" LOG_LEVEL_DESC) -{ - int level; - - if ((level = level_match(argv[1])) == ZLOG_DISABLED) - return CMD_ERR_NO_MATCH; - return set_log_file(vty, argv[0], level); -} - -DEFUN(no_config_log_file, - no_config_log_file_cmd, - "no log file [FILENAME]", - NO_STR - "Logging control\n" "Cancel logging to file\n" "Logging file name\n") -{ - zlog_reset_file(NULL); - - if (host.logfile) - free(host.logfile); - - host.logfile = NULL; - - return CMD_SUCCESS; -} - -ALIAS(no_config_log_file, - no_config_log_file_level_cmd, - "no log file FILENAME LEVEL", - NO_STR - "Logging control\n" - "Cancel logging to file\n" "Logging file name\n" "Logging level\n") - - DEFUN(config_log_syslog, - config_log_syslog_cmd, - "log syslog", "Logging control\n" "Set syslog logging level\n") -{ - zlog_set_level(NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl); - return CMD_SUCCESS; -} - -DEFUN(config_log_syslog_level, - config_log_syslog_level_cmd, - "log syslog " LOG_LEVELS, - "Logging control\n" "Set syslog logging level\n" LOG_LEVEL_DESC) -{ - int level; - - if ((level = level_match(argv[0])) == ZLOG_DISABLED) - return CMD_ERR_NO_MATCH; - zlog_set_level(NULL, ZLOG_DEST_SYSLOG, level); - return CMD_SUCCESS; -} - -DEFUN_DEPRECATED(config_log_syslog_facility, - config_log_syslog_facility_cmd, - "log syslog facility " LOG_FACILITIES, - "Logging control\n" - "Logging goes to syslog\n" - "(Deprecated) Facility parameter for syslog messages\n" - LOG_FACILITY_DESC) -{ - int facility; - - if ((facility = facility_match(argv[0])) < 0) - return CMD_ERR_NO_MATCH; - - zlog_set_level(NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl); - zlog_default->facility = facility; - return CMD_SUCCESS; -} - -DEFUN(no_config_log_syslog, - no_config_log_syslog_cmd, - "no log syslog [LEVEL]", - NO_STR "Logging control\n" "Cancel logging to syslog\n" "Logging level\n") -{ - zlog_set_level(NULL, ZLOG_DEST_SYSLOG, ZLOG_DISABLED); - return CMD_SUCCESS; -} - -ALIAS(no_config_log_syslog, - no_config_log_syslog_facility_cmd, - "no log syslog facility " LOG_FACILITIES, - NO_STR - "Logging control\n" - "Logging goes to syslog\n" - "Facility parameter for syslog messages\n" LOG_FACILITY_DESC) - - DEFUN(config_log_facility, - config_log_facility_cmd, - "log facility " LOG_FACILITIES, - "Logging control\n" - "Facility parameter for syslog messages\n" LOG_FACILITY_DESC) -{ - int facility; - - if ((facility = facility_match(argv[0])) < 0) - return CMD_ERR_NO_MATCH; - zlog_default->facility = facility; - return CMD_SUCCESS; -} - -DEFUN(no_config_log_facility, - no_config_log_facility_cmd, - "no log facility [FACILITY]", - NO_STR - "Logging control\n" - "Reset syslog facility to default (daemon)\n" "Syslog facility\n") -{ - zlog_default->facility = LOG_DAEMON; - return CMD_SUCCESS; -} - -DEFUN_DEPRECATED(config_log_trap, - config_log_trap_cmd, - "log trap " LOG_LEVELS, - "Logging control\n" - "(Deprecated) Set logging level and default for all destinations\n" - LOG_LEVEL_DESC) -{ - int new_level; - int i; - - if ((new_level = level_match(argv[0])) == ZLOG_DISABLED) - return CMD_ERR_NO_MATCH; - - zlog_default->default_lvl = new_level; - for (i = 0; i < ZLOG_NUM_DESTS; i++) - if (zlog_default->maxlvl[i] != ZLOG_DISABLED) - zlog_default->maxlvl[i] = new_level; - return CMD_SUCCESS; -} - -DEFUN_DEPRECATED(no_config_log_trap, - no_config_log_trap_cmd, - "no log trap [LEVEL]", - NO_STR - "Logging control\n" - "Permit all logging information\n" "Logging level\n") -{ - zlog_default->default_lvl = LOG_DEBUG; - return CMD_SUCCESS; -} - -DEFUN(config_log_record_priority, - config_log_record_priority_cmd, - "log record-priority", - "Logging control\n" - "Log the priority of the message within the message\n") -{ - zlog_default->record_priority = 1; - return CMD_SUCCESS; -} - -DEFUN(no_config_log_record_priority, - no_config_log_record_priority_cmd, - "no log record-priority", - NO_STR - "Logging control\n" - "Do not log the priority of the message within the message\n") -{ - zlog_default->record_priority = 0; - return CMD_SUCCESS; -} -#endif - -DEFUN(banner_motd_file, - banner_motd_file_cmd, - "banner motd file [FILE]", - "Set banner\n" "Banner for motd\n" "Banner from a file\n" "Filename\n") -{ - if (host.motdfile) - free(host.motdfile); - host.motdfile = strdup(argv[0]); - - return CMD_SUCCESS; -} - -DEFUN(banner_motd_default, - banner_motd_default_cmd, - "banner motd default", - "Set banner string\n" "Strings for motd\n" "Default string\n") -{ - host.motd = default_motd; - return CMD_SUCCESS; -} - -DEFUN(no_banner_motd, - no_banner_motd_cmd, - "no banner motd", NO_STR "Set banner string\n" "Strings for motd\n") -{ - host.motd = NULL; - if (host.motdfile) - free(host.motdfile); - host.motdfile = NULL; - return CMD_SUCCESS; -} - -/* Set config filename. Called from vty.c */ -void host_config_set(char *filename) -{ - host.config = strdup(filename); -} - -void install_default(enum node_type node) -{ - install_element(node, &config_exit_cmd); - install_element(node, &config_quit_cmd); - install_element(node, &config_end_cmd); - install_element(node, &config_help_cmd); - install_element(node, &config_list_cmd); - -#if 0 - install_element(node, &config_write_terminal_cmd); - install_element(node, &config_write_file_cmd); - install_element(node, &config_write_memory_cmd); - install_element(node, &config_write_cmd); - install_element(node, &show_running_config_cmd); -#endif -} - -/* Initialize command interface. Install basic nodes and commands. */ -void cmd_init(int terminal) -{ - /* Allocate initial top vector of commands. */ - cmdvec = vector_init(VECTOR_MIN_SIZE); - - /* Default host value settings. */ - host.name = NULL; - //host.password = NULL; - host.password = "foo"; - host.enable = NULL; - host.logfile = NULL; - host.config = NULL; - host.lines = -1; - host.motd = default_motd; - host.motdfile = NULL; - - /* Install top nodes. */ - install_node(&view_node, NULL); - install_node(&enable_node, NULL); - install_node(&auth_node, NULL); - install_node(&auth_enable_node, NULL); - install_node(&config_node, config_write_host); - - /* Each node's basic commands. */ - install_element(VIEW_NODE, &show_version_cmd); - if (terminal) { - install_element(VIEW_NODE, &config_list_cmd); - install_element(VIEW_NODE, &config_exit_cmd); - install_element(VIEW_NODE, &config_quit_cmd); - install_element(VIEW_NODE, &config_help_cmd); - install_element(VIEW_NODE, &config_enable_cmd); - install_element(VIEW_NODE, &config_terminal_length_cmd); - install_element(VIEW_NODE, &config_terminal_no_length_cmd); - install_element(VIEW_NODE, &echo_cmd); - } - - if (terminal) { - install_default(ENABLE_NODE); - install_element(ENABLE_NODE, &config_disable_cmd); - install_element(ENABLE_NODE, &config_terminal_cmd); - //install_element (ENABLE_NODE, ©_runningconfig_startupconfig_cmd); - } - //install_element (ENABLE_NODE, &show_startup_config_cmd); - install_element(ENABLE_NODE, &show_version_cmd); - - if (terminal) { - install_element(ENABLE_NODE, &config_terminal_length_cmd); - install_element(ENABLE_NODE, &config_terminal_no_length_cmd); - install_element(ENABLE_NODE, &echo_cmd); - - install_default(CONFIG_NODE); - } - - install_element(CONFIG_NODE, &hostname_cmd); - install_element(CONFIG_NODE, &no_hostname_cmd); - - if (terminal) { - install_element(CONFIG_NODE, &password_cmd); - install_element(CONFIG_NODE, &password_text_cmd); - install_element(CONFIG_NODE, &enable_password_cmd); - install_element(CONFIG_NODE, &enable_password_text_cmd); - install_element(CONFIG_NODE, &no_enable_password_cmd); - -#ifdef VTY_CRYPT_PW - install_element(CONFIG_NODE, &service_password_encrypt_cmd); - install_element(CONFIG_NODE, &no_service_password_encrypt_cmd); -#endif - install_element(CONFIG_NODE, &banner_motd_default_cmd); - install_element(CONFIG_NODE, &banner_motd_file_cmd); - install_element(CONFIG_NODE, &no_banner_motd_cmd); - install_element(CONFIG_NODE, &service_terminal_length_cmd); - install_element(CONFIG_NODE, &no_service_terminal_length_cmd); - - } - srand(time(NULL)); -} diff --git a/src/vty/vector.c b/src/vty/vector.c deleted file mode 100644 index 76870105b..000000000 --- a/src/vty/vector.c +++ /dev/null @@ -1,186 +0,0 @@ -/* Generic vector interface routine - * Copyright (C) 1997 Kunihiro Ishiguro - * - * This file is part of GNU Zebra. - * - * GNU Zebra 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, or (at your option) any - * later version. - * - * GNU Zebra 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 GNU Zebra; see the file COPYING. If not, write to the Free - * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA - * 02111-1307, USA. - */ - -#include <stdlib.h> -#include <unistd.h> - -#include <vty/vector.h> -#include <memory.h> - -/* Initialize vector : allocate memory and return vector. */ -vector vector_init(unsigned int size) -{ - vector v = calloc(1, sizeof(struct _vector)); - if (!v) - return NULL; - - /* allocate at least one slot */ - if (size == 0) - size = 1; - - v->alloced = size; - v->active = 0; - v->index = calloc(1, sizeof(void *) * size); - if (!v->index) { - free(v); - return NULL; - } - return v; -} - -void vector_only_wrapper_free(vector v) -{ - free(v); -} - -void vector_only_index_free(void *index) -{ - free(index); -} - -void vector_free(vector v) -{ - free(v->index); - free(v); -} - -vector vector_copy(vector v) -{ - unsigned int size; - vector new = calloc(1, sizeof(struct _vector)); - if (!new) - return NULL; - - new->active = v->active; - new->alloced = v->alloced; - - size = sizeof(void *) * (v->alloced); - new->index = calloc(1, size); - if (!new->index) { - free(new); - return NULL; - } - memcpy(new->index, v->index, size); - - return new; -} - -/* Check assigned index, and if it runs short double index pointer */ -void vector_ensure(vector v, unsigned int num) -{ - if (v->alloced > num) - return; - - v->index = realloc(v->index, sizeof(void *) * (v->alloced * 2)); - memset(&v->index[v->alloced], 0, sizeof(void *) * v->alloced); - v->alloced *= 2; - - if (v->alloced <= num) - vector_ensure(v, num); -} - -/* This function only returns next empty slot index. It dose not mean - the slot's index memory is assigned, please call vector_ensure() - after calling this function. */ -int vector_empty_slot(vector v) -{ - unsigned int i; - - if (v->active == 0) - return 0; - - for (i = 0; i < v->active; i++) - if (v->index[i] == 0) - return i; - - return i; -} - -/* Set value to the smallest empty slot. */ -int vector_set(vector v, void *val) -{ - unsigned int i; - - i = vector_empty_slot(v); - vector_ensure(v, i); - - v->index[i] = val; - - if (v->active <= i) - v->active = i + 1; - - return i; -} - -/* Set value to specified index slot. */ -int vector_set_index(vector v, unsigned int i, void *val) -{ - vector_ensure(v, i); - - v->index[i] = val; - - if (v->active <= i) - v->active = i + 1; - - return i; -} - -/* Look up vector. */ -void *vector_lookup(vector v, unsigned int i) -{ - if (i >= v->active) - return NULL; - return v->index[i]; -} - -/* Lookup vector, ensure it. */ -void *vector_lookup_ensure(vector v, unsigned int i) -{ - vector_ensure(v, i); - return v->index[i]; -} - -/* Unset value at specified index slot. */ -void vector_unset(vector v, unsigned int i) -{ - if (i >= v->alloced) - return; - - v->index[i] = NULL; - - if (i + 1 == v->active) { - v->active--; - while (i && v->index[--i] == NULL && v->active--) ; /* Is this ugly ? */ - } -} - -/* Count the number of not emplty slot. */ -unsigned int vector_count(vector v) -{ - unsigned int i; - unsigned count = 0; - - for (i = 0; i < v->active; i++) - if (v->index[i] != NULL) - count++; - - return count; -} diff --git a/src/vty/vty.c b/src/vty/vty.c deleted file mode 100644 index ca6fff73c..000000000 --- a/src/vty/vty.c +++ /dev/null @@ -1,1634 +0,0 @@ - -#include <stdio.h> -#include <stdarg.h> -#include <stdlib.h> -#include <unistd.h> -#include <string.h> -#include <errno.h> -#include <ctype.h> -#include <termios.h> - -#include <sys/utsname.h> -#include <sys/param.h> - -#include <arpa/telnet.h> - -#include "cardshell.h" -#include <vty/vty.h> -#include <vty/command.h> -#include <vty/buffer.h> - -extern struct host host; - -/* Vector which store each vty structure. */ -static vector vtyvec; - -vector Vvty_serv_thread; - -char *vty_cwd = NULL; - -/* Configure lock. */ -static int vty_config; - -static int no_password_check = 1; - -static void vty_clear_buf(struct vty *vty) -{ - memset(vty->buf, 0, vty->max); -} - -/* Allocate new vty struct. */ -struct vty *vty_new() -{ - struct vty *new = malloc(sizeof(struct vty)); - - if (!new) - goto out; - - new->obuf = buffer_new(0); /* Use default buffer size. */ - if (!new->obuf) - goto out_new; - new->buf = calloc(1, VTY_BUFSIZ); - if (!new->buf) - goto out_obuf; - - new->max = VTY_BUFSIZ; - - return new; - -out_obuf: - free(new->obuf); -out_new: - free(new); - new = NULL; -out: - return new; -} - -/* Authentication of vty */ -static void vty_auth(struct vty *vty, char *buf) -{ - char *passwd = NULL; - enum node_type next_node = 0; - int fail; - char *crypt(const char *, const char *); - - switch (vty->node) { - case AUTH_NODE: -#ifdef VTY_CRYPT_PW - if (host.encrypt) - passwd = host.password_encrypt; - else -#endif - passwd = host.password; - if (host.advanced) - next_node = host.enable ? VIEW_NODE : ENABLE_NODE; - else - next_node = VIEW_NODE; - break; - case AUTH_ENABLE_NODE: -#ifdef VTY_CRYPT_PW - if (host.encrypt) - passwd = host.enable_encrypt; - else -#endif - passwd = host.enable; - next_node = ENABLE_NODE; - break; - } - - if (passwd) { -#ifdef VTY_CRYPT_PW - if (host.encrypt) - fail = strcmp(crypt(buf, passwd), passwd); - else -#endif - fail = strcmp(buf, passwd); - } else - fail = 1; - - if (!fail) { - vty->fail = 0; - vty->node = next_node; /* Success ! */ - } else { - vty->fail++; - if (vty->fail >= 3) { - if (vty->node == AUTH_NODE) { - vty_out(vty, - "%% Bad passwords, too many failures!%s", - VTY_NEWLINE); - vty->status = VTY_CLOSE; - } else { - /* AUTH_ENABLE_NODE */ - vty->fail = 0; - vty_out(vty, - "%% Bad enable passwords, too many failures!%s", - VTY_NEWLINE); - vty->node = VIEW_NODE; - } - } - } -} - -/* Close vty interface. */ -void vty_close(struct vty *vty) -{ - int i; - - /* Flush buffer. */ - buffer_flush_all(vty->obuf, vty->fd); - - /* Free input buffer. */ - buffer_free(vty->obuf); - - /* Free command history. */ - for (i = 0; i < VTY_MAXHIST; i++) - if (vty->hist[i]) - free(vty->hist[i]); - - /* Unset vector. */ - vector_unset(vtyvec, vty->fd); - - /* Close socket. */ - if (vty->fd > 0) - close(vty->fd); - - if (vty->buf) - free(vty->buf); - - /* Check configure. */ - vty_config_unlock(vty); - - /* OK free vty. */ - free(vty); -} - -int vty_shell(struct vty *vty) -{ - return vty->type == VTY_SHELL ? 1 : 0; -} - - -/* VTY standard output function. */ -int vty_out(struct vty *vty, const char *format, ...) -{ - va_list args; - int len = 0; - int size = 1024; - char buf[1024]; - char *p = NULL; - - if (vty_shell(vty)) { - va_start(args, format); - vprintf(format, args); - va_end(args); - } else { - /* Try to write to initial buffer. */ - va_start(args, format); - len = vsnprintf(buf, sizeof buf, format, args); - va_end(args); - - /* Initial buffer is not enough. */ - if (len < 0 || len >= size) { - while (1) { - if (len > -1) - size = len + 1; - else - size = size * 2; - - p = realloc(p, size); - if (!p) - return -1; - - va_start(args, format); - len = vsnprintf(p, size, format, args); - va_end(args); - - if (len > -1 && len < size) - break; - } - } - - /* When initial buffer is enough to store all output. */ - if (!p) - p = buf; - - /* Pointer p must point out buffer. */ - buffer_put(vty->obuf, (u_char *) p, len); - - /* If p is not different with buf, it is allocated buffer. */ - if (p != buf) - free(p); - } - - return len; -} - -int vty_out_newline(struct vty *vty) -{ - char *p = vty_newline(vty); - buffer_put(vty->obuf, p, strlen(p)); - return 0; -} - -int vty_config_lock(struct vty *vty) -{ - if (vty_config == 0) { - vty->config = 1; - vty_config = 1; - } - return vty->config; -} - -int vty_config_unlock(struct vty *vty) -{ - if (vty_config == 1 && vty->config == 1) { - vty->config = 0; - vty_config = 0; - } - return vty->config; -} - -/* Say hello to vty interface. */ -void vty_hello(struct vty *vty) -{ - if (host.motdfile) { - FILE *f; - char buf[4096]; - - f = fopen(host.motdfile, "r"); - if (f) { - while (fgets(buf, sizeof(buf), f)) { - char *s; - /* work backwards to ignore trailling isspace() */ - for (s = buf + strlen(buf); - (s > buf) && isspace(*(s - 1)); s--) ; - *s = '\0'; - vty_out(vty, "%s%s", buf, VTY_NEWLINE); - } - fclose(f); - } else - vty_out(vty, "MOTD file not found%s", VTY_NEWLINE); - } else if (host.motd) - vty_out(vty, host.motd); -} - -/* Put out prompt and wait input from user. */ -static void vty_prompt(struct vty *vty) -{ - struct utsname names; - const char *hostname; - - if (vty->type == VTY_TERM) { - hostname = host.name; - if (!hostname) { - uname(&names); - hostname = names.nodename; - } - vty_out(vty, cmd_prompt(vty->node), hostname); - } -} - -/* Command execution over the vty interface. */ -static int vty_command(struct vty *vty, char *buf) -{ - int ret; - vector vline; - - /* Split readline string up into the vector */ - vline = cmd_make_strvec(buf); - - if (vline == NULL) - return CMD_SUCCESS; - - ret = cmd_execute_command(vline, vty, NULL, 0); - if (ret != CMD_SUCCESS) - switch (ret) { - case CMD_WARNING: - if (vty->type == VTY_FILE) - vty_out(vty, "Warning...%s", VTY_NEWLINE); - break; - case CMD_ERR_AMBIGUOUS: - vty_out(vty, "%% Ambiguous command.%s", VTY_NEWLINE); - break; - case CMD_ERR_NO_MATCH: - vty_out(vty, "%% Unknown command.%s", VTY_NEWLINE); - break; - case CMD_ERR_INCOMPLETE: - vty_out(vty, "%% Command incomplete.%s", VTY_NEWLINE); - break; - } - cmd_free_strvec(vline); - - return ret; -} - -static const char telnet_backward_char = 0x08; -static const char telnet_space_char = ' '; - -/* Basic function to write buffer to vty. */ -static void vty_write(struct vty *vty, const char *buf, size_t nbytes) -{ - if ((vty->node == AUTH_NODE) || (vty->node == AUTH_ENABLE_NODE)) - return; - - /* Should we do buffering here ? And make vty_flush (vty) ? */ - buffer_put(vty->obuf, buf, nbytes); -} - -/* Ensure length of input buffer. Is buffer is short, double it. */ -static void vty_ensure(struct vty *vty, int length) -{ - if (vty->max <= length) { - vty->max *= 2; - vty->buf = realloc(vty->buf, vty->max); - // FIXME: check return - } -} - -/* Basic function to insert character into vty. */ -static void vty_self_insert(struct vty *vty, char c) -{ - int i; - int length; - - vty_ensure(vty, vty->length + 1); - length = vty->length - vty->cp; - memmove(&vty->buf[vty->cp + 1], &vty->buf[vty->cp], length); - vty->buf[vty->cp] = c; - - vty_write(vty, &vty->buf[vty->cp], length + 1); - for (i = 0; i < length; i++) - vty_write(vty, &telnet_backward_char, 1); - - vty->cp++; - vty->length++; -} - -/* Self insert character 'c' in overwrite mode. */ -static void vty_self_insert_overwrite(struct vty *vty, char c) -{ - vty_ensure(vty, vty->length + 1); - vty->buf[vty->cp++] = c; - - if (vty->cp > vty->length) - vty->length++; - - if ((vty->node == AUTH_NODE) || (vty->node == AUTH_ENABLE_NODE)) - return; - - vty_write(vty, &c, 1); -} - -/* Insert a word into vty interface with overwrite mode. */ -static void vty_insert_word_overwrite(struct vty *vty, char *str) -{ - int len = strlen(str); - vty_write(vty, str, len); - strcpy(&vty->buf[vty->cp], str); - vty->cp += len; - vty->length = vty->cp; -} - -/* Forward character. */ -static void vty_forward_char(struct vty *vty) -{ - if (vty->cp < vty->length) { - vty_write(vty, &vty->buf[vty->cp], 1); - vty->cp++; - } -} - -/* Backward character. */ -static void vty_backward_char(struct vty *vty) -{ - if (vty->cp > 0) { - vty->cp--; - vty_write(vty, &telnet_backward_char, 1); - } -} - -/* Move to the beginning of the line. */ -static void vty_beginning_of_line(struct vty *vty) -{ - while (vty->cp) - vty_backward_char(vty); -} - -/* Move to the end of the line. */ -static void vty_end_of_line(struct vty *vty) -{ - while (vty->cp < vty->length) - vty_forward_char(vty); -} - -/* Add current command line to the history buffer. */ -static void vty_hist_add(struct vty *vty) -{ - int index; - - if (vty->length == 0) - return; - - index = vty->hindex ? vty->hindex - 1 : VTY_MAXHIST - 1; - - /* Ignore the same string as previous one. */ - if (vty->hist[index]) - if (strcmp(vty->buf, vty->hist[index]) == 0) { - vty->hp = vty->hindex; - return; - } - - /* Insert history entry. */ - if (vty->hist[vty->hindex]) - free(vty->hist[vty->hindex]); - vty->hist[vty->hindex] = strdup(vty->buf); - - /* History index rotation. */ - vty->hindex++; - if (vty->hindex == VTY_MAXHIST) - vty->hindex = 0; - - vty->hp = vty->hindex; -} - -/* Get telnet window size. */ -static int -vty_telnet_option (struct vty *vty, unsigned char *buf, int nbytes) -{ -#ifdef TELNET_OPTION_DEBUG - int i; - - for (i = 0; i < nbytes; i++) - { - switch (buf[i]) - { - case IAC: - vty_out (vty, "IAC "); - break; - case WILL: - vty_out (vty, "WILL "); - break; - case WONT: - vty_out (vty, "WONT "); - break; - case DO: - vty_out (vty, "DO "); - break; - case DONT: - vty_out (vty, "DONT "); - break; - case SB: - vty_out (vty, "SB "); - break; - case SE: - vty_out (vty, "SE "); - break; - case TELOPT_ECHO: - vty_out (vty, "TELOPT_ECHO %s", VTY_NEWLINE); - break; - case TELOPT_SGA: - vty_out (vty, "TELOPT_SGA %s", VTY_NEWLINE); - break; - case TELOPT_NAWS: - vty_out (vty, "TELOPT_NAWS %s", VTY_NEWLINE); - break; - default: - vty_out (vty, "%x ", buf[i]); - break; - } - } - vty_out (vty, "%s", VTY_NEWLINE); - -#endif /* TELNET_OPTION_DEBUG */ - - switch (buf[0]) - { - case SB: - vty->sb_len = 0; - vty->iac_sb_in_progress = 1; - return 0; - break; - case SE: - { - if (!vty->iac_sb_in_progress) - return 0; - - if ((vty->sb_len == 0) || (vty->sb_buf[0] == '\0')) - { - vty->iac_sb_in_progress = 0; - return 0; - } - switch (vty->sb_buf[0]) - { - case TELOPT_NAWS: - if (vty->sb_len != TELNET_NAWS_SB_LEN) - vty_out(vty,"RFC 1073 violation detected: telnet NAWS option " - "should send %d characters, but we received %lu", - TELNET_NAWS_SB_LEN, (u_long)vty->sb_len); - else if (sizeof(vty->sb_buf) < TELNET_NAWS_SB_LEN) - vty_out(vty, "Bug detected: sizeof(vty->sb_buf) %lu < %d, " - "too small to handle the telnet NAWS option", - (u_long)sizeof(vty->sb_buf), TELNET_NAWS_SB_LEN); - else - { - vty->width = ((vty->sb_buf[1] << 8)|vty->sb_buf[2]); - vty->height = ((vty->sb_buf[3] << 8)|vty->sb_buf[4]); -#ifdef TELNET_OPTION_DEBUG - vty_out(vty, "TELNET NAWS window size negotiation completed: " - "width %d, height %d%s", - vty->width, vty->height, VTY_NEWLINE); -#endif - } - break; - } - vty->iac_sb_in_progress = 0; - return 0; - break; - } - default: - break; - } - return 1; -} - -/* Execute current command line. */ -static int vty_execute(struct vty *vty) -{ - int ret; - - ret = CMD_SUCCESS; - - switch (vty->node) { - case AUTH_NODE: - case AUTH_ENABLE_NODE: - vty_auth(vty, vty->buf); - break; - default: - ret = vty_command(vty, vty->buf); - if (vty->type == VTY_TERM) - vty_hist_add(vty); - break; - } - - /* Clear command line buffer. */ - vty->cp = vty->length = 0; - vty_clear_buf(vty); - - if (vty->status != VTY_CLOSE) - vty_prompt(vty); - - return ret; -} - -/* Send WILL TELOPT_ECHO to remote server. */ -static void -vty_will_echo (struct vty *vty) -{ - unsigned char cmd[] = { IAC, WILL, TELOPT_ECHO, '\0' }; - vty_out (vty, "%s", cmd); -} - -/* Make suppress Go-Ahead telnet option. */ -static void -vty_will_suppress_go_ahead (struct vty *vty) -{ - unsigned char cmd[] = { IAC, WILL, TELOPT_SGA, '\0' }; - vty_out (vty, "%s", cmd); -} - -/* Make don't use linemode over telnet. */ -static void -vty_dont_linemode (struct vty *vty) -{ - unsigned char cmd[] = { IAC, DONT, TELOPT_LINEMODE, '\0' }; - vty_out (vty, "%s", cmd); -} - -/* Use window size. */ -static void -vty_do_window_size (struct vty *vty) -{ - unsigned char cmd[] = { IAC, DO, TELOPT_NAWS, '\0' }; - vty_out (vty, "%s", cmd); -} - -static void vty_kill_line_from_beginning(struct vty *); -static void vty_redraw_line(struct vty *); - -/* Print command line history. This function is called from - vty_next_line and vty_previous_line. */ -static void vty_history_print(struct vty *vty) -{ - int length; - - vty_kill_line_from_beginning(vty); - - /* Get previous line from history buffer */ - length = strlen(vty->hist[vty->hp]); - memcpy(vty->buf, vty->hist[vty->hp], length); - vty->cp = vty->length = length; - - /* Redraw current line */ - vty_redraw_line(vty); -} - -/* Show next command line history. */ -static void vty_next_line(struct vty *vty) -{ - int try_index; - - if (vty->hp == vty->hindex) - return; - - /* Try is there history exist or not. */ - try_index = vty->hp; - if (try_index == (VTY_MAXHIST - 1)) - try_index = 0; - else - try_index++; - - /* If there is not history return. */ - if (vty->hist[try_index] == NULL) - return; - else - vty->hp = try_index; - - vty_history_print(vty); -} - -/* Show previous command line history. */ -static void vty_previous_line(struct vty *vty) -{ - int try_index; - - try_index = vty->hp; - if (try_index == 0) - try_index = VTY_MAXHIST - 1; - else - try_index--; - - if (vty->hist[try_index] == NULL) - return; - else - vty->hp = try_index; - - vty_history_print(vty); -} - -/* This function redraw all of the command line character. */ -static void vty_redraw_line(struct vty *vty) -{ - vty_write(vty, vty->buf, vty->length); - vty->cp = vty->length; -} - -/* Forward word. */ -static void vty_forward_word(struct vty *vty) -{ - while (vty->cp != vty->length && vty->buf[vty->cp] != ' ') - vty_forward_char(vty); - - while (vty->cp != vty->length && vty->buf[vty->cp] == ' ') - vty_forward_char(vty); -} - -/* Backward word without skipping training space. */ -static void vty_backward_pure_word(struct vty *vty) -{ - while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ') - vty_backward_char(vty); -} - -/* Backward word. */ -static void vty_backward_word(struct vty *vty) -{ - while (vty->cp > 0 && vty->buf[vty->cp - 1] == ' ') - vty_backward_char(vty); - - while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ') - vty_backward_char(vty); -} - -/* When '^D' is typed at the beginning of the line we move to the down - level. */ -static void vty_down_level(struct vty *vty) -{ - vty_out(vty, "%s", VTY_NEWLINE); - (*config_exit_cmd.func) (NULL, vty, 0, NULL); - vty_prompt(vty); - vty->cp = 0; -} - -/* When '^Z' is received from vty, move down to the enable mode. */ -static void vty_end_config(struct vty *vty) -{ - vty_out(vty, "%s", VTY_NEWLINE); - - switch (vty->node) { - case VIEW_NODE: - case ENABLE_NODE: - /* Nothing to do. */ - break; - case CONFIG_NODE: - case INTERFACE_NODE: - case ZEBRA_NODE: - case RIP_NODE: - case RIPNG_NODE: - case BGP_NODE: - case BGP_VPNV4_NODE: - case BGP_IPV4_NODE: - case BGP_IPV4M_NODE: - case BGP_IPV6_NODE: - case RMAP_NODE: - case OSPF_NODE: - case OSPF6_NODE: - case ISIS_NODE: - case KEYCHAIN_NODE: - case KEYCHAIN_KEY_NODE: - case MASC_NODE: - case VTY_NODE: - vty_config_unlock(vty); - vty->node = ENABLE_NODE; - break; - default: - /* Unknown node, we have to ignore it. */ - break; - } - - vty_prompt(vty); - vty->cp = 0; -} - -/* Delete a charcter at the current point. */ -static void vty_delete_char(struct vty *vty) -{ - int i; - int size; - - if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE) - return; - - if (vty->length == 0) { - vty_down_level(vty); - return; - } - - if (vty->cp == vty->length) - return; /* completion need here? */ - - size = vty->length - vty->cp; - - vty->length--; - memmove(&vty->buf[vty->cp], &vty->buf[vty->cp + 1], size - 1); - vty->buf[vty->length] = '\0'; - - vty_write(vty, &vty->buf[vty->cp], size - 1); - vty_write(vty, &telnet_space_char, 1); - - for (i = 0; i < size; i++) - vty_write(vty, &telnet_backward_char, 1); -} - -/* Delete a character before the point. */ -static void vty_delete_backward_char(struct vty *vty) -{ - if (vty->cp == 0) - return; - - vty_backward_char(vty); - vty_delete_char(vty); -} - -/* Kill rest of line from current point. */ -static void vty_kill_line(struct vty *vty) -{ - int i; - int size; - - size = vty->length - vty->cp; - - if (size == 0) - return; - - for (i = 0; i < size; i++) - vty_write(vty, &telnet_space_char, 1); - for (i = 0; i < size; i++) - vty_write(vty, &telnet_backward_char, 1); - - memset(&vty->buf[vty->cp], 0, size); - vty->length = vty->cp; -} - -/* Kill line from the beginning. */ -static void vty_kill_line_from_beginning(struct vty *vty) -{ - vty_beginning_of_line(vty); - vty_kill_line(vty); -} - -/* Delete a word before the point. */ -static void vty_forward_kill_word(struct vty *vty) -{ - while (vty->cp != vty->length && vty->buf[vty->cp] == ' ') - vty_delete_char(vty); - while (vty->cp != vty->length && vty->buf[vty->cp] != ' ') - vty_delete_char(vty); -} - -/* Delete a word before the point. */ -static void vty_backward_kill_word(struct vty *vty) -{ - while (vty->cp > 0 && vty->buf[vty->cp - 1] == ' ') - vty_delete_backward_char(vty); - while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ') - vty_delete_backward_char(vty); -} - -/* Transpose chars before or at the point. */ -static void vty_transpose_chars(struct vty *vty) -{ - char c1, c2; - - /* If length is short or point is near by the beginning of line then - return. */ - if (vty->length < 2 || vty->cp < 1) - return; - - /* In case of point is located at the end of the line. */ - if (vty->cp == vty->length) { - c1 = vty->buf[vty->cp - 1]; - c2 = vty->buf[vty->cp - 2]; - - vty_backward_char(vty); - vty_backward_char(vty); - vty_self_insert_overwrite(vty, c1); - vty_self_insert_overwrite(vty, c2); - } else { - c1 = vty->buf[vty->cp]; - c2 = vty->buf[vty->cp - 1]; - - vty_backward_char(vty); - vty_self_insert_overwrite(vty, c1); - vty_self_insert_overwrite(vty, c2); - } -} - -/* Do completion at vty interface. */ -static void vty_complete_command(struct vty *vty) -{ - int i; - int ret; - char **matched = NULL; - vector vline; - - if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE) - return; - - vline = cmd_make_strvec(vty->buf); - if (vline == NULL) - return; - - /* In case of 'help \t'. */ - if (isspace((int)vty->buf[vty->length - 1])) - vector_set(vline, '\0'); - - matched = cmd_complete_command(vline, vty, &ret); - - cmd_free_strvec(vline); - - vty_out(vty, "%s", VTY_NEWLINE); - switch (ret) { - case CMD_ERR_AMBIGUOUS: - vty_out(vty, "%% Ambiguous command.%s", VTY_NEWLINE); - vty_prompt(vty); - vty_redraw_line(vty); - break; - case CMD_ERR_NO_MATCH: - /* vty_out (vty, "%% There is no matched command.%s", VTY_NEWLINE); */ - vty_prompt(vty); - vty_redraw_line(vty); - break; - case CMD_COMPLETE_FULL_MATCH: - vty_prompt(vty); - vty_redraw_line(vty); - vty_backward_pure_word(vty); - vty_insert_word_overwrite(vty, matched[0]); - vty_self_insert(vty, ' '); - free(matched[0]); - break; - case CMD_COMPLETE_MATCH: - vty_prompt(vty); - vty_redraw_line(vty); - vty_backward_pure_word(vty); - vty_insert_word_overwrite(vty, matched[0]); - free(matched[0]); - vector_only_index_free(matched); - return; - break; - case CMD_COMPLETE_LIST_MATCH: - for (i = 0; matched[i] != NULL; i++) { - if (i != 0 && ((i % 6) == 0)) - vty_out(vty, "%s", VTY_NEWLINE); - vty_out(vty, "%-10s ", matched[i]); - free(matched[i]); - } - vty_out(vty, "%s", VTY_NEWLINE); - - vty_prompt(vty); - vty_redraw_line(vty); - break; - case CMD_ERR_NOTHING_TODO: - vty_prompt(vty); - vty_redraw_line(vty); - break; - default: - break; - } - if (matched) - vector_only_index_free(matched); -} - -static void -vty_describe_fold(struct vty *vty, int cmd_width, - unsigned int desc_width, struct desc *desc) -{ - char *buf; - const char *cmd, *p; - int pos; - - cmd = desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd; - - if (desc_width <= 0) { - vty_out(vty, " %-*s %s%s", cmd_width, cmd, desc->str, - VTY_NEWLINE); - return; - } - - buf = calloc(1, strlen(desc->str) + 1); - if (!buf) - return; - - for (p = desc->str; strlen(p) > desc_width; p += pos + 1) { - for (pos = desc_width; pos > 0; pos--) - if (*(p + pos) == ' ') - break; - - if (pos == 0) - break; - - strncpy(buf, p, pos); - buf[pos] = '\0'; - vty_out(vty, " %-*s %s%s", cmd_width, cmd, buf, VTY_NEWLINE); - - cmd = ""; - } - - vty_out(vty, " %-*s %s%s", cmd_width, cmd, p, VTY_NEWLINE); - - free(buf); -} - -/* Describe matched command function. */ -static void vty_describe_command(struct vty *vty) -{ - int ret; - vector vline; - vector describe; - unsigned int i, width, desc_width; - struct desc *desc, *desc_cr = NULL; - - vline = cmd_make_strvec(vty->buf); - - /* In case of '> ?'. */ - if (vline == NULL) { - vline = vector_init(1); - vector_set(vline, '\0'); - } else if (isspace((int)vty->buf[vty->length - 1])) - vector_set(vline, '\0'); - - describe = cmd_describe_command(vline, vty, &ret); - - vty_out(vty, "%s", VTY_NEWLINE); - - /* Ambiguous error. */ - switch (ret) { - case CMD_ERR_AMBIGUOUS: - cmd_free_strvec(vline); - vty_out(vty, "%% Ambiguous command.%s", VTY_NEWLINE); - vty_prompt(vty); - vty_redraw_line(vty); - return; - break; - case CMD_ERR_NO_MATCH: - cmd_free_strvec(vline); - vty_out(vty, "%% There is no matched command.%s", VTY_NEWLINE); - vty_prompt(vty); - vty_redraw_line(vty); - return; - break; - } - - /* Get width of command string. */ - width = 0; - for (i = 0; i < vector_active(describe); i++) - if ((desc = vector_slot(describe, i)) != NULL) { - unsigned int len; - - if (desc->cmd[0] == '\0') - continue; - - len = strlen(desc->cmd); - if (desc->cmd[0] == '.') - len--; - - if (width < len) - width = len; - } - - /* Get width of description string. */ - desc_width = vty->width - (width + 6); - - /* Print out description. */ - for (i = 0; i < vector_active(describe); i++) - if ((desc = vector_slot(describe, i)) != NULL) { - if (desc->cmd[0] == '\0') - continue; - - if (strcmp(desc->cmd, "<cr>") == 0) { - desc_cr = desc; - continue; - } - - if (!desc->str) - vty_out(vty, " %-s%s", - desc->cmd[0] == - '.' ? desc->cmd + 1 : desc->cmd, - VTY_NEWLINE); - else if (desc_width >= strlen(desc->str)) - vty_out(vty, " %-*s %s%s", width, - desc->cmd[0] == - '.' ? desc->cmd + 1 : desc->cmd, - desc->str, VTY_NEWLINE); - else - vty_describe_fold(vty, width, desc_width, desc); - -#if 0 - vty_out(vty, " %-*s %s%s", width - desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd, - desc->str ? desc->str : "", VTY_NEWLINE); -#endif /* 0 */ - } - - if ((desc = desc_cr)) { - if (!desc->str) - vty_out(vty, " %-s%s", - desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd, - VTY_NEWLINE); - else if (desc_width >= strlen(desc->str)) - vty_out(vty, " %-*s %s%s", width, - desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd, - desc->str, VTY_NEWLINE); - else - vty_describe_fold(vty, width, desc_width, desc); - } - - cmd_free_strvec(vline); - vector_free(describe); - - vty_prompt(vty); - vty_redraw_line(vty); -} - -/* ^C stop current input and do not add command line to the history. */ -static void vty_stop_input(struct vty *vty) -{ - vty->cp = vty->length = 0; - vty_clear_buf(vty); - vty_out(vty, "%s", VTY_NEWLINE); - - switch (vty->node) { - case VIEW_NODE: - case ENABLE_NODE: - /* Nothing to do. */ - break; - case CONFIG_NODE: - case INTERFACE_NODE: - case ZEBRA_NODE: - case RIP_NODE: - case RIPNG_NODE: - case BGP_NODE: - case RMAP_NODE: - case OSPF_NODE: - case OSPF6_NODE: - case ISIS_NODE: - case KEYCHAIN_NODE: - case KEYCHAIN_KEY_NODE: - case MASC_NODE: - case VTY_NODE: - vty_config_unlock(vty); - vty->node = ENABLE_NODE; - break; - default: - /* Unknown node, we have to ignore it. */ - break; - } - vty_prompt(vty); - - /* Set history pointer to the latest one. */ - vty->hp = vty->hindex; -} - -#define CONTROL(X) ((X) - '@') -#define VTY_NORMAL 0 -#define VTY_PRE_ESCAPE 1 -#define VTY_ESCAPE 2 - -/* Escape character command map. */ -static void vty_escape_map(unsigned char c, struct vty *vty) -{ - switch (c) { - case ('A'): - vty_previous_line(vty); - break; - case ('B'): - vty_next_line(vty); - break; - case ('C'): - vty_forward_char(vty); - break; - case ('D'): - vty_backward_char(vty); - break; - default: - break; - } - - /* Go back to normal mode. */ - vty->escape = VTY_NORMAL; -} - -/* Quit print out to the buffer. */ -static void vty_buffer_reset(struct vty *vty) -{ - buffer_reset(vty->obuf); - vty_prompt(vty); - vty_redraw_line(vty); -} - -/* Read data via vty socket. */ -int vty_read(struct vty *vty) -{ - int i; - int nbytes; - unsigned char buf[VTY_READ_BUFSIZ]; - - int vty_sock = vty->fd; - - /* Read raw data from socket */ - if ((nbytes = read(vty->fd, buf, VTY_READ_BUFSIZ)) <= 0) { - if (nbytes < 0) { - if (ERRNO_IO_RETRY(errno)) { - vty_event(VTY_READ, vty_sock, vty); - return 0; - } - } - buffer_reset(vty->obuf); - vty->status = VTY_CLOSE; - } - - for (i = 0; i < nbytes; i++) { - if (buf[i] == IAC) { - if (!vty->iac) { - vty->iac = 1; - continue; - } else { - vty->iac = 0; - } - } - - if (vty->iac_sb_in_progress && !vty->iac) { - if (vty->sb_len < sizeof(vty->sb_buf)) - vty->sb_buf[vty->sb_len] = buf[i]; - vty->sb_len++; - continue; - } - - if (vty->iac) { - /* In case of telnet command */ - int ret = 0; - ret = vty_telnet_option(vty, buf + i, nbytes - i); - vty->iac = 0; - i += ret; - continue; - } - - if (vty->status == VTY_MORE) { - switch (buf[i]) { - case CONTROL('C'): - case 'q': - case 'Q': - vty_buffer_reset(vty); - break; -#if 0 /* More line does not work for "show ip bgp". */ - case '\n': - case '\r': - vty->status = VTY_MORELINE; - break; -#endif - default: - break; - } - continue; - } - - /* Escape character. */ - if (vty->escape == VTY_ESCAPE) { - vty_escape_map(buf[i], vty); - continue; - } - - /* Pre-escape status. */ - if (vty->escape == VTY_PRE_ESCAPE) { - switch (buf[i]) { - case '[': - vty->escape = VTY_ESCAPE; - break; - case 'b': - vty_backward_word(vty); - vty->escape = VTY_NORMAL; - break; - case 'f': - vty_forward_word(vty); - vty->escape = VTY_NORMAL; - break; - case 'd': - vty_forward_kill_word(vty); - vty->escape = VTY_NORMAL; - break; - case CONTROL('H'): - case 0x7f: - vty_backward_kill_word(vty); - vty->escape = VTY_NORMAL; - break; - default: - vty->escape = VTY_NORMAL; - break; - } - continue; - } - - switch (buf[i]) { - case CONTROL('A'): - vty_beginning_of_line(vty); - break; - case CONTROL('B'): - vty_backward_char(vty); - break; - case CONTROL('C'): - vty_stop_input(vty); - break; - case CONTROL('D'): - vty_delete_char(vty); - break; - case CONTROL('E'): - vty_end_of_line(vty); - break; - case CONTROL('F'): - vty_forward_char(vty); - break; - case CONTROL('H'): - case 0x7f: - vty_delete_backward_char(vty); - break; - case CONTROL('K'): - vty_kill_line(vty); - break; - case CONTROL('N'): - vty_next_line(vty); - break; - case CONTROL('P'): - vty_previous_line(vty); - break; - case CONTROL('T'): - vty_transpose_chars(vty); - break; - case CONTROL('U'): - vty_kill_line_from_beginning(vty); - break; - case CONTROL('W'): - vty_backward_kill_word(vty); - break; - case CONTROL('Z'): - vty_end_config(vty); - break; - case '\n': - case '\r': - vty_out(vty, "%s", VTY_NEWLINE); - vty_execute(vty); - break; - case '\t': - vty_complete_command(vty); - break; - case '?': - if (vty->node == AUTH_NODE - || vty->node == AUTH_ENABLE_NODE) - vty_self_insert(vty, buf[i]); - else - vty_describe_command(vty); - break; - case '\033': - if (i + 1 < nbytes && buf[i + 1] == '[') { - vty->escape = VTY_ESCAPE; - i++; - } else - vty->escape = VTY_PRE_ESCAPE; - break; - default: - if (buf[i] > 31 && buf[i] < 127) - vty_self_insert(vty, buf[i]); - break; - } - } - - /* Check status. */ - if (vty->status == VTY_CLOSE) - vty_close(vty); - else { - vty_event(VTY_WRITE, vty_sock, vty); - vty_event(VTY_READ, vty_sock, vty); - } - return 0; -} - -/* Create new vty structure. */ -struct vty * -vty_create (int vty_sock, void *priv) -{ - struct vty *vty; - - struct termios t; - - tcgetattr(vty_sock, &t); - cfmakeraw(&t); - tcsetattr(vty_sock, TCSANOW, &t); - - /* Allocate new vty structure and set up default values. */ - vty = vty_new (); - vty->fd = vty_sock; - vty->priv = priv; - vty->type = VTY_TERM; - if (no_password_check) - { - if (host.advanced) - vty->node = ENABLE_NODE; - else - vty->node = VIEW_NODE; - } - else - vty->node = AUTH_NODE; - vty->fail = 0; - vty->cp = 0; - vty_clear_buf (vty); - vty->length = 0; - memset (vty->hist, 0, sizeof (vty->hist)); - vty->hp = 0; - vty->hindex = 0; - vector_set_index (vtyvec, vty_sock, vty); - vty->status = VTY_NORMAL; - if (host.lines >= 0) - vty->lines = host.lines; - else - vty->lines = -1; - - if (! no_password_check) - { - /* Vty is not available if password isn't set. */ - if (host.password == NULL && host.password_encrypt == NULL) - { - vty_out (vty, "Vty password is not set.%s", VTY_NEWLINE); - vty->status = VTY_CLOSE; - vty_close (vty); - return NULL; - } - } - - /* Say hello to the world. */ - vty_hello (vty); - if (! no_password_check) - vty_out (vty, "%sUser Access Verification%s%s", VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE); - - /* Setting up terminal. */ - vty_will_echo (vty); - vty_will_suppress_go_ahead (vty); - - vty_dont_linemode (vty); - vty_do_window_size (vty); - /* vty_dont_lflow_ahead (vty); */ - - vty_prompt (vty); - - /* Add read/write thread. */ - vty_event (VTY_WRITE, vty_sock, vty); - vty_event (VTY_READ, vty_sock, vty); - - return vty; -} - -DEFUN(config_who, config_who_cmd, "who", "Display who is on vty\n") -{ - unsigned int i; - struct vty *v; - - for (i = 0; i < vector_active(vtyvec); i++) - if ((v = vector_slot(vtyvec, i)) != NULL) - vty_out(vty, "%svty[%d] %s", - v->config ? "*" : " ", i, VTY_NEWLINE); - return CMD_SUCCESS; -} - -/* Move to vty configuration mode. */ -DEFUN(line_vty, - line_vty_cmd, - "line vty", "Configure a terminal line\n" "Virtual terminal\n") -{ - vty->node = VTY_NODE; - return CMD_SUCCESS; -} - -/* vty login. */ -DEFUN(vty_login, vty_login_cmd, "login", "Enable password checking\n") -{ - no_password_check = 0; - return CMD_SUCCESS; -} - -DEFUN(no_vty_login, - no_vty_login_cmd, "no login", NO_STR "Enable password checking\n") -{ - no_password_check = 1; - return CMD_SUCCESS; -} - -DEFUN(service_advanced_vty, - service_advanced_vty_cmd, - "service advanced-vty", - "Set up miscellaneous service\n" "Enable advanced mode vty interface\n") -{ - host.advanced = 1; - return CMD_SUCCESS; -} - -DEFUN(no_service_advanced_vty, - no_service_advanced_vty_cmd, - "no service advanced-vty", - NO_STR - "Set up miscellaneous service\n" "Enable advanced mode vty interface\n") -{ - host.advanced = 0; - return CMD_SUCCESS; -} - -DEFUN(terminal_monitor, - terminal_monitor_cmd, - "terminal monitor", - "Set terminal line parameters\n" - "Copy debug output to the current terminal line\n") -{ - vty->monitor = 1; - return CMD_SUCCESS; -} - -DEFUN(terminal_no_monitor, - terminal_no_monitor_cmd, - "terminal no monitor", - "Set terminal line parameters\n" - NO_STR "Copy debug output to the current terminal line\n") -{ - vty->monitor = 0; - return CMD_SUCCESS; -} - -DEFUN(show_history, - show_history_cmd, - "show history", SHOW_STR "Display the session command history\n") -{ - int index; - - for (index = vty->hindex + 1; index != vty->hindex;) { - if (index == VTY_MAXHIST) { - index = 0; - continue; - } - - if (vty->hist[index] != NULL) - vty_out(vty, " %s%s", vty->hist[index], VTY_NEWLINE); - - index++; - } - - return CMD_SUCCESS; -} - -/* Display current configuration. */ -static int vty_config_write(struct vty *vty) -{ - vty_out(vty, "line vty%s", VTY_NEWLINE); - - /* login */ - if (no_password_check) - vty_out(vty, " no login%s", VTY_NEWLINE); - - vty_out(vty, "!%s", VTY_NEWLINE); - - return CMD_SUCCESS; -} - -struct cmd_node vty_node = { - VTY_NODE, - "%s(config-line)# ", - 1, -}; - -/* Reset all VTY status. */ -void vty_reset() -{ - unsigned int i; - struct vty *vty; - struct thread *vty_serv_thread; - - for (i = 0; i < vector_active(vtyvec); i++) - if ((vty = vector_slot(vtyvec, i)) != NULL) { - buffer_reset(vty->obuf); - vty->status = VTY_CLOSE; - vty_close(vty); - } - - for (i = 0; i < vector_active(Vvty_serv_thread); i++) - if ((vty_serv_thread = - vector_slot(Vvty_serv_thread, i)) != NULL) { - //thread_cancel (vty_serv_thread); - vector_slot(Vvty_serv_thread, i) = NULL; - close(i); - } -} - -static void vty_save_cwd(void) -{ - char cwd[MAXPATHLEN]; - char *c; - - c = getcwd(cwd, MAXPATHLEN); - - if (!c) { - chdir(SYSCONFDIR); - getcwd(cwd, MAXPATHLEN); - } - - vty_cwd = malloc(strlen(cwd) + 1); - strcpy(vty_cwd, cwd); -} - -char *vty_get_cwd() -{ - return vty_cwd; -} - -int vty_shell_serv(struct vty *vty) -{ - return vty->type == VTY_SHELL_SERV ? 1 : 0; -} - -void vty_init_vtysh() -{ - vtyvec = vector_init(VECTOR_MIN_SIZE); -} - -/* Install vty's own commands like `who' command. */ -void vty_init() -{ - /* For further configuration read, preserve current directory. */ - vty_save_cwd(); - - vtyvec = vector_init(VECTOR_MIN_SIZE); - - /* Install bgp top node. */ - install_node(&vty_node, vty_config_write); - - install_element(VIEW_NODE, &config_who_cmd); - install_element(VIEW_NODE, &show_history_cmd); - install_element(ENABLE_NODE, &config_who_cmd); - install_element(CONFIG_NODE, &line_vty_cmd); - install_element(CONFIG_NODE, &service_advanced_vty_cmd); - install_element(CONFIG_NODE, &no_service_advanced_vty_cmd); - install_element(CONFIG_NODE, &show_history_cmd); - install_element(ENABLE_NODE, &terminal_monitor_cmd); - install_element(ENABLE_NODE, &terminal_no_monitor_cmd); - install_element(ENABLE_NODE, &show_history_cmd); - - install_default(VTY_NODE); -#if 0 - install_element(VTY_NODE, &vty_login_cmd); - install_element(VTY_NODE, &no_vty_login_cmd); -#endif -} diff --git a/src/vty_interface.c b/src/vty_interface.c deleted file mode 100644 index 44531dd50..000000000 --- a/src/vty_interface.c +++ /dev/null @@ -1,905 +0,0 @@ -/* OpenBSC interface to quagga VTY */ -/* (C) 2009 by Harald Welte <laforge@gnumonks.org> - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - */ - -#include <stdlib.h> -#include <unistd.h> -#include <sys/types.h> - -#include <vty/command.h> -#include <vty/vty.h> - -#include <arpa/inet.h> - -#include <openbsc/linuxlist.h> -#include <openbsc/gsm_data.h> -#include <openbsc/gsm_subscriber.h> -#include <openbsc/e1_input.h> -#include <openbsc/abis_nm.h> -#include <openbsc/db.h> - -static struct gsm_network *gsmnet; - -struct cmd_node bts_node = { - BTS_NODE, - "%s(bts)#", - 1, -}; - -struct cmd_node trx_node = { - TRX_NODE, - "%s(trx)#", - 1, -}; - -struct cmd_node ts_node = { - TS_NODE, - "%s(ts)#", - 1, -}; - -struct cmd_node subscr_node = { - SUBSCR_NODE, - "%s(subscriber)#", - 1, -}; - -static int dummy_config_write(struct vty *v) -{ - return CMD_SUCCESS; -} - -static void net_dump_nmstate(struct vty *vty, struct gsm_nm_state *nms) -{ - vty_out(vty,"Oper '%s', Admin %u, Avail '%s'%s", - nm_opstate_name(nms->operational), nms->administrative, - nm_avail_name(nms->availability), VTY_NEWLINE); -} - -static void net_dump_vty(struct vty *vty, struct gsm_network *net) -{ - vty_out(vty, "BSC is on Country Code %u, Network Code %u " - "and has %u BTS%s", net->country_code, net->network_code, - net->num_bts, VTY_NEWLINE); - vty_out(vty, " Long network name: '%s'%s", - net->name_long, VTY_NEWLINE); - vty_out(vty, " Short network name: '%s'%s", - net->name_short, VTY_NEWLINE); -} - -DEFUN(show_net, show_net_cmd, "show network", - SHOW_STR "Display information about a GSM NETWORK\n") -{ - struct gsm_network *net = gsmnet; - net_dump_vty(vty, net); - - return CMD_SUCCESS; -} - -static void e1isl_dump_vty(struct vty *vty, struct e1inp_sign_link *e1l) -{ - struct e1inp_line *line; - - if (!e1l) { - vty_out(vty, " None%s", VTY_NEWLINE); - return; - } - - line = e1l->ts->line; - - vty_out(vty, " E1 Line %u, Type %s: Timeslot %u, Mode %s%s", - line->num, line->driver->name, e1l->ts->num, - e1inp_signtype_name(e1l->type), VTY_NEWLINE); - vty_out(vty, " E1 TEI %u, SAPI %u%s", - e1l->tei, e1l->sapi, VTY_NEWLINE); -} - -static void bts_dump_vty(struct vty *vty, struct gsm_bts *bts) -{ - vty_out(vty, "BTS %u is of %s type, has LAC %u, BSIC %u, TSC %u and %u TRX%s", - bts->nr, btstype2str(bts->type), bts->location_area_code, - bts->bsic, bts->tsc, bts->num_trx, VTY_NEWLINE); - if (is_ipaccess_bts(bts)) - vty_out(vty, " Unit ID: %u/%u/0%s", - bts->ip_access.site_id, bts->ip_access.bts_id, - 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); - /* FIXME: oml_link, chan_desc */ -} - -DEFUN(show_bts, show_bts_cmd, "show bts [number]", - SHOW_STR "Display information about a BTS\n" - "BTS number") -{ - struct gsm_network *net = gsmnet; - int bts_nr; - - if (argc != 0) { - /* use the BTS number that the user has specified */ - bts_nr = atoi(argv[0]); - if (bts_nr > net->num_bts) { - vty_out(vty, "%% can't find BTS '%s'%s", argv[0], - VTY_NEWLINE); - return CMD_WARNING; - } - bts_dump_vty(vty, &net->bts[bts_nr]); - return CMD_SUCCESS; - } - /* print all BTS's */ - for (bts_nr = 0; bts_nr < net->num_bts; bts_nr++) - bts_dump_vty(vty, &net->bts[bts_nr]); - - return CMD_SUCCESS; -} - -static void trx_dump_vty(struct vty *vty, struct gsm_bts_trx *trx) -{ - vty_out(vty, "TRX %u of BTS %u is on ARFCN %u%s", - trx->nr, trx->bts->nr, trx->arfcn, VTY_NEWLINE); - vty_out(vty, " NM State: "); - 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); -} - -DEFUN(show_trx, - show_trx_cmd, - "show trx [bts_nr] [trx_nr]", - SHOW_STR "Display information about a TRX\n") -{ - struct gsm_network *net = gsmnet; - struct gsm_bts *bts = NULL; - struct gsm_bts_trx *trx; - int bts_nr, trx_nr; - - if (argc >= 1) { - /* use the BTS number that the user has specified */ - bts_nr = atoi(argv[0]); - if (bts_nr >= net->num_bts) { - vty_out(vty, "%% can't find BTS '%s'%s", argv[0], - VTY_NEWLINE); - return CMD_WARNING; - } - bts = &net->bts[bts_nr]; - } - if (argc >= 2) { - trx_nr = atoi(argv[1]); - if (trx_nr >= bts->num_trx) { - vty_out(vty, "%% can't find TRX '%s'%s", argv[1], - VTY_NEWLINE); - return CMD_WARNING; - } - trx = &bts->trx[trx_nr]; - trx_dump_vty(vty, trx); - return CMD_SUCCESS; - } - if (bts) { - /* print all TRX in this BTS */ - for (trx_nr = 0; trx_nr < bts->num_trx; trx_nr++) { - trx = &bts->trx[trx_nr]; - trx_dump_vty(vty, trx); - } - return CMD_SUCCESS; - } - - for (bts_nr = 0; bts_nr < net->num_bts; bts_nr++) { - bts = &net->bts[bts_nr]; - for (trx_nr = 0; trx_nr < bts->num_trx; trx_nr++) { - trx = &bts->trx[trx_nr]; - trx_dump_vty(vty, trx); - } - } - - return CMD_SUCCESS; -} - -static void ts_dump_vty(struct vty *vty, struct gsm_bts_trx_ts *ts) -{ - struct in_addr ia; - - vty_out(vty, "Timeslot %u of TRX %u in BTS %u, phys cfg %s%s", - ts->nr, ts->trx->nr, ts->trx->bts->nr, - gsm_pchan_name(ts->pchan), VTY_NEWLINE); - vty_out(vty, " NM State: "); - net_dump_nmstate(vty, &ts->nm_state); - if (is_ipaccess_bts(ts->trx->bts)) { - ia.s_addr = ts->abis_ip.bound_ip; - vty_out(vty, " Bound IP: %s Port %u FC=%u F8=%u%s", - inet_ntoa(ia), ts->abis_ip.bound_port, - ts->abis_ip.attr_fc, ts->abis_ip.attr_f8, - VTY_NEWLINE); - } else { - vty_out(vty, " E1 Line %u, Timeslot %u, Subslot %u%s", - ts->e1_link.e1_nr, ts->e1_link.e1_ts, - ts->e1_link.e1_ts_ss, VTY_NEWLINE); - } -} - -DEFUN(show_ts, - show_ts_cmd, - "show timeslot [bts_nr] [trx_nr] [ts_nr]", - SHOW_STR "Display information about a TS\n") -{ - struct gsm_network *net = gsmnet; - struct gsm_bts *bts; - struct gsm_bts_trx *trx; - struct gsm_bts_trx_ts *ts; - int bts_nr, trx_nr, ts_nr; - - if (argc >= 1) { - /* use the BTS number that the user has specified */ - bts_nr = atoi(argv[0]); - if (bts_nr >= net->num_bts) { - vty_out(vty, "%% can't find BTS '%s'%s", argv[0], - VTY_NEWLINE); - return CMD_WARNING; - } - bts = &net->bts[bts_nr]; - } - if (argc >= 2) { - trx_nr = atoi(argv[1]); - if (trx_nr >= bts->num_trx) { - vty_out(vty, "%% can't find TRX '%s'%s", argv[1], - VTY_NEWLINE); - return CMD_WARNING; - } - trx = &bts->trx[trx_nr]; - } - if (argc >= 3) { - ts_nr = atoi(argv[2]); - if (ts_nr >= TRX_NR_TS) { - vty_out(vty, "%% can't find TS '%s'%s", argv[2], - VTY_NEWLINE); - return CMD_WARNING; - } - ts = &trx->ts[ts_nr]; - ts_dump_vty(vty, ts); - return CMD_SUCCESS; - } - for (bts_nr = 0; bts_nr < net->num_bts; bts_nr++) { - bts = &net->bts[bts_nr]; - for (trx_nr = 0; trx_nr < bts->num_trx; trx_nr++) { - trx = &bts->trx[trx_nr]; - for (ts_nr = 0; ts_nr < TRX_NR_TS; ts_nr++) { - ts = &trx->ts[ts_nr]; - ts_dump_vty(vty, ts); - } - } - } - - return CMD_SUCCESS; -} - -static void subscr_dump_vty(struct vty *vty, struct gsm_subscriber *subscr) -{ - vty_out(vty, " ID: %lu, Authorized: %d%s", subscr->id, - subscr->authorized, VTY_NEWLINE); - if (subscr->name) - vty_out(vty, " Name: '%s'%s", subscr->name, VTY_NEWLINE); - if (subscr->extension) - vty_out(vty, " Extension: %s%s", subscr->extension, - VTY_NEWLINE); - if (subscr->imsi) - vty_out(vty, " IMSI: %s%s", subscr->imsi, VTY_NEWLINE); - if (subscr->tmsi) - vty_out(vty, " TMSI: %s%s", subscr->tmsi, VTY_NEWLINE); -} - -static void lchan_dump_vty(struct vty *vty, struct gsm_lchan *lchan) -{ - vty_out(vty, "Lchan %u in Timeslot %u of TRX %u in BTS %u, Type %s%s", - lchan->nr, lchan->ts->nr, lchan->ts->trx->nr, - lchan->ts->trx->bts->nr, gsm_lchan_name(lchan->type), - VTY_NEWLINE); - vty_out(vty, " Use Count: %u%s", lchan->use_count, VTY_NEWLINE); - vty_out(vty, " BS Power %u, MS Power %u%s", lchan->bs_power, - lchan->ms_power, VTY_NEWLINE); - if (lchan->subscr) { - vty_out(vty, " Subscriber:%s", VTY_NEWLINE); - subscr_dump_vty(vty, lchan->subscr); - } else - vty_out(vty, " No Subscriber%s", VTY_NEWLINE); -} - -static void call_dump_vty(struct vty *vty, struct gsm_call *call) -{ - vty_out(vty, "Call Type %u, State %u, Transaction ID %u%s", - call->type, call->state, call->transaction_id, VTY_NEWLINE); - - if (call->local_lchan) { - vty_out(vty, "Call Local Channel:%s", VTY_NEWLINE); - lchan_dump_vty(vty, call->local_lchan); - } else - vty_out(vty, "Call has no Local Channel%s", VTY_NEWLINE); - - if (call->remote_lchan) { - vty_out(vty, "Call Remote Channel:%s", VTY_NEWLINE); - lchan_dump_vty(vty, call->remote_lchan); - } else - vty_out(vty, "Call has no Remote Channel%s", VTY_NEWLINE); - - if (call->called_subscr) { - vty_out(vty, "Called Subscriber:%s", VTY_NEWLINE); - subscr_dump_vty(vty, call->called_subscr); - } else - vty_out(vty, "Call has no Called Subscriber%s", VTY_NEWLINE); -} - -DEFUN(show_lchan, - show_lchan_cmd, - "show lchan [bts_nr] [trx_nr] [ts_nr] [lchan_nr]", - SHOW_STR "Display information about a logical channel\n") -{ - struct gsm_network *net = gsmnet; - struct gsm_bts *bts; - struct gsm_bts_trx *trx; - struct gsm_bts_trx_ts *ts; - struct gsm_lchan *lchan; - int bts_nr, trx_nr, ts_nr, lchan_nr; - - if (argc >= 1) { - /* use the BTS number that the user has specified */ - bts_nr = atoi(argv[0]); - if (bts_nr >= net->num_bts) { - vty_out(vty, "%% can't find BTS %s%s", argv[0], - VTY_NEWLINE); - return CMD_WARNING; - } - bts = &net->bts[bts_nr]; - } - if (argc >= 2) { - trx_nr = atoi(argv[1]); - if (trx_nr >= bts->num_trx) { - vty_out(vty, "%% can't find TRX %s%s", argv[1], - VTY_NEWLINE); - return CMD_WARNING; - } - trx = &bts->trx[trx_nr]; - } - if (argc >= 3) { - ts_nr = atoi(argv[2]); - if (ts_nr >= TRX_NR_TS) { - vty_out(vty, "%% can't find TS %s%s", argv[2], - VTY_NEWLINE); - return CMD_WARNING; - } - ts = &trx->ts[ts_nr]; - } - if (argc >= 4) { - lchan_nr = atoi(argv[3]); - if (lchan_nr >= TS_MAX_LCHAN) { - vty_out(vty, "%% can't find LCHAN %s%s", argv[3], - VTY_NEWLINE); - return CMD_WARNING; - } - lchan = &ts->lchan[lchan_nr]; - lchan_dump_vty(vty, lchan); - return CMD_SUCCESS; - } - for (bts_nr = 0; bts_nr < net->num_bts; bts_nr++) { - bts = &net->bts[bts_nr]; - for (trx_nr = 0; trx_nr < bts->num_trx; trx_nr++) { - trx = &bts->trx[trx_nr]; - for (ts_nr = 0; ts_nr < TRX_NR_TS; ts_nr++) { - ts = &trx->ts[ts_nr]; - for (lchan_nr = 0; lchan_nr < TS_MAX_LCHAN; - lchan_nr++) { - lchan = &ts->lchan[lchan_nr]; - if (lchan->type == GSM_LCHAN_NONE) - continue; - lchan_dump_vty(vty, lchan); - } - } - } - } - - return CMD_SUCCESS; -} - -static void e1drv_dump_vty(struct vty *vty, struct e1inp_driver *drv) -{ - vty_out(vty, "E1 Input Driver %s%s", drv->name, VTY_NEWLINE); -} - -DEFUN(show_e1drv, - show_e1drv_cmd, - "show e1_driver", - SHOW_STR "Display information about available E1 drivers\n") -{ - struct e1inp_driver *drv; - - llist_for_each_entry(drv, &e1inp_driver_list, list) - e1drv_dump_vty(vty, drv); - - return CMD_SUCCESS; -} - -static void e1line_dump_vty(struct vty *vty, struct e1inp_line *line) -{ - vty_out(vty, "E1 Line Number %u, Name %s, Driver %s%s", - line->num, line->name ? line->name : "", - line->driver->name, VTY_NEWLINE); -} - -DEFUN(show_e1line, - show_e1line_cmd, - "show e1_line [line_nr]", - SHOW_STR "Display information about a E1 line\n") -{ - struct e1inp_line *line; - - if (argc >= 1) { - int num = atoi(argv[0]); - llist_for_each_entry(line, &e1inp_line_list, list) { - if (line->num == num) { - e1line_dump_vty(vty, line); - return CMD_SUCCESS; - } - } - return CMD_WARNING; - } - - llist_for_each_entry(line, &e1inp_line_list, list) - e1line_dump_vty(vty, line); - - return CMD_SUCCESS; -} - -static void e1ts_dump_vty(struct vty *vty, struct e1inp_ts *ts) -{ - vty_out(vty, "E1 Timeslot %2u of Line %u is Type %s%s", - ts->num, ts->line->num, e1inp_tstype_name(ts->type), - VTY_NEWLINE); -} - -DEFUN(show_e1ts, - show_e1ts_cmd, - "show e1_timeslot [line_nr] [ts_nr]", - SHOW_STR "Display information about a E1 timeslot\n") -{ - struct e1inp_line *line; - struct e1inp_ts *ts; - int ts_nr; - - if (argc == 0) { - llist_for_each_entry(line, &e1inp_line_list, list) { - for (ts_nr = 0; ts_nr < NUM_E1_TS; ts_nr++) { - ts = &line->ts[ts_nr]; - e1ts_dump_vty(vty, ts); - } - } - return CMD_SUCCESS; - } - if (argc >= 1) { - int num = atoi(argv[0]); - llist_for_each_entry(line, &e1inp_line_list, list) { - if (line->num == num) - break; - } - if (!line || line->num != num) { - vty_out(vty, "E1 line %s is invalid%s", - argv[0], VTY_NEWLINE); - return CMD_WARNING; - } - } - if (argc >= 2) { - ts_nr = atoi(argv[1]); - if (ts_nr > NUM_E1_TS) { - vty_out(vty, "E1 timeslot %s is invalid%s", - argv[1], VTY_NEWLINE); - return CMD_WARNING; - } - ts = &line->ts[ts_nr]; - e1ts_dump_vty(vty, ts); - return CMD_SUCCESS; - } else { - for (ts_nr = 0; ts_nr < NUM_E1_TS; ts_nr++) { - ts = &line->ts[ts_nr]; - e1ts_dump_vty(vty, ts); - } - return CMD_SUCCESS; - } - return CMD_SUCCESS; -} - -static void paging_dump_vty(struct vty *vty, struct gsm_paging_request *pag) -{ - vty_out(vty, "Paging on BTS %u%s", pag->bts->nr, VTY_NEWLINE); - subscr_dump_vty(vty, pag->subscr); -} - -static void bts_paging_dump_vty(struct vty *vty, struct gsm_bts *bts) -{ - struct gsm_paging_request *pag; - - llist_for_each_entry(pag, &bts->paging.pending_requests, entry) - paging_dump_vty(vty, pag); -} - -DEFUN(show_paging, - show_paging_cmd, - "show paging [bts_nr]", - SHOW_STR "Display information about paging reuqests of a BTS\n") -{ - struct gsm_network *net = gsmnet; - struct gsm_bts *bts; - int bts_nr; - - if (argc >= 1) { - /* use the BTS number that the user has specified */ - bts_nr = atoi(argv[0]); - if (bts_nr >= net->num_bts) { - vty_out(vty, "%% can't find BTS %s%s", argv[0], - VTY_NEWLINE); - return CMD_WARNING; - } - bts = &net->bts[bts_nr]; - bts_paging_dump_vty(vty, bts); - - return CMD_SUCCESS; - } - for (bts_nr = 0; bts_nr < net->num_bts; bts_nr++) { - bts = &net->bts[bts_nr]; - bts_paging_dump_vty(vty, bts); - } - - return CMD_SUCCESS; -} - -/* per-subscriber configuration */ -DEFUN(cfg_subscr, - cfg_subscr_cmd, - "subscriber IMSI", - "Select a Subscriber to configure\n") -{ - const char *imsi = argv[0]; - struct gsm_subscriber *subscr; - - subscr = subscr_get_by_imsi(imsi); - if (!subscr) { - vty_out(vty, "%% No subscriber for IMSI %s%s", - imsi, VTY_NEWLINE); - return CMD_WARNING; - } - - vty->index = subscr; - vty->node = SUBSCR_NODE; - - return CMD_SUCCESS; -} - - -/* per-BTS configuration */ -DEFUN(cfg_bts, - cfg_bts_cmd, - "bts BTS_NR", - "Select a BTS to configure\n") -{ - int bts_nr = atoi(argv[0]); - struct gsm_bts *bts; - - if (bts_nr >= GSM_MAX_BTS) { - vty_out(vty, "%% This Version of OpenBSC only supports %u BTS%s", - GSM_MAX_BTS, VTY_NEWLINE); - return CMD_WARNING; - } - bts = &gsmnet->bts[bts_nr]; - if (bts_nr >= gsmnet->num_bts) - gsmnet->num_bts = bts_nr + 1; - - vty->index = bts; - vty->node = BTS_NODE; - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_type, - cfg_bts_type_cmd, - "type TYPE", - "Set the BTS type\n") -{ - struct gsm_bts *bts = vty->index; - - //bts->type = - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_lac, - cfg_bts_lac_cmd, - "location_area_code <0-255>", - "Set the Location Area Code (LAC) of this BTS\n") -{ - struct gsm_bts *bts = vty->index; - int lac = atoi(argv[0]); - - if (lac < 0 || lac > 0xff) { - vty_out(vty, "%% LAC %d is not in the valid range (0-255)%s", - lac, VTY_NEWLINE); - return CMD_WARNING; - } - bts->location_area_code = lac; - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_tsc, - cfg_bts_tsc_cmd, - "training_sequence_code <0-255>", - "Set the Training Sequence Code (TSC) of this BTS\n") -{ - struct gsm_bts *bts = vty->index; - int tsc = atoi(argv[0]); - - if (tsc < 0 || tsc > 0xff) { - vty_out(vty, "%% TSC %d is not in the valid range (0-255)%s", - tsc, VTY_NEWLINE); - return CMD_WARNING; - } - bts->tsc = tsc; - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_bsic, - cfg_bts_bsic_cmd, - "base_station_id_code <0-63>", - "Set the Base Station Identity Code (BSIC) of this BTS\n") -{ - struct gsm_bts *bts = vty->index; - int bsic = atoi(argv[0]); - - if (bsic < 0 || bsic > 0x3f) { - vty_out(vty, "%% TSC %d is not in the valid range (0-255)%s", - bsic, VTY_NEWLINE); - return CMD_WARNING; - } - bts->bsic = bsic; - - return CMD_SUCCESS; -} - - -DEFUN(cfg_bts_unit_id, - cfg_bts_unit_id_cmd, - "unit_id <0-65534> <0-255>", - "Set the BTS Unit ID of this BTS\n") -{ - struct gsm_bts *bts = vty->index; - int site_id = atoi(argv[0]); - int bts_id = atoi(argv[1]); - - if (site_id < 0 || site_id > 65534) { - vty_out(vty, "%% Site ID %d is not in the valid range%s", - site_id, VTY_NEWLINE); - return CMD_WARNING; - } - if (site_id < 0 || site_id > 255) { - vty_out(vty, "%% BTS ID %d is not in the valid range%s", - bts_id, VTY_NEWLINE); - return CMD_WARNING; - } - bts->ip_access.site_id = site_id; - bts->ip_access.bts_id = bts_id; - - return CMD_SUCCESS; -} - -/* per TRX configuration */ -DEFUN(cfg_trx, - cfg_trx_cmd, - "trx TRX_NR", - "Select a TRX to configure") -{ - int trx_nr = atoi(argv[0]); - struct gsm_bts *bts = vty->index; - struct gsm_bts_trx *trx; - - if (trx_nr > BTS_MAX_TRX) { - vty_out(vty, "%% This version of OpenBSC only supports %u TRX%s", - BTS_MAX_TRX+1, VTY_NEWLINE); - return CMD_WARNING; - } - - if (trx_nr >= bts->num_trx) - bts->num_trx = trx_nr+1; - - trx = &bts->trx[trx_nr]; - - vty->index = trx; - vty->node = TRX_NODE; - - return CMD_SUCCESS; -} - -DEFUN(cfg_trx_arfcn, - cfg_trx_arfcn_cmd, - "arfcn <1-1024>", - "Set the ARFCN for this TRX\n") -{ - int arfcn = atoi(argv[0]); - struct gsm_bts_trx *trx = vty->index; - - /* FIXME: check if this ARFCN is supported by this TRX */ - - trx->arfcn = arfcn; - - /* FIXME: patch ARFCN into SYSTEM INFORMATION */ - /* FIXME: use OML layer to update the ARFCN */ - /* FIXME: use RSL layer to update SYSTEM INFORMATION */ - - return CMD_SUCCESS; -} - -/* per TS configuration */ -DEFUN(cfg_ts, - cfg_ts_cmd, - "timeslot TS_NR", - "Select a Timeslot to configure") -{ - int ts_nr = atoi(argv[0]); - struct gsm_bts_trx *trx = vty->index; - struct gsm_bts_trx_ts *ts; - - if (ts_nr >= TRX_NR_TS) { - vty_out(vty, "%% A GSM TRX only has %u Timeslots per TRX%s", - TRX_NR_TS, VTY_NEWLINE); - return CMD_WARNING; - } - - ts = &trx->ts[ts_nr]; - - vty->index = ts; - vty->node = TS_NODE; - - return CMD_SUCCESS; -} - - -/* Subscriber */ -DEFUN(show_subscr, - show_subscr_cmd, - "show subscriber [IMSI]", - SHOW_STR "Display information about a subscriber\n") -{ - const char *imsi; - struct gsm_subscriber *subscr; - - if (argc >= 1) { - imsi = argv[0]; - subscr = subscr_get_by_imsi(imsi); - if (!subscr) { - vty_out(vty, "%% unknown subscriber%s", - VTY_NEWLINE); - return CMD_WARNING; - } - subscr_dump_vty(vty, subscr); - - return CMD_SUCCESS; - } - - /* FIXME: iterate over all subscribers ? */ - return CMD_WARNING; - - return CMD_SUCCESS; -} - -DEFUN(cfg_subscr_name, - cfg_subscr_name_cmd, - "name NAME", - "Set the name of the subscriber") -{ - const char *name = argv[0]; - struct gsm_subscriber *subscr = vty->index; - - strncpy(subscr->name, name, sizeof(subscr->name)); - - db_sync_subscriber(subscr); - - return CMD_SUCCESS; -} - -DEFUN(cfg_subscr_extension, - cfg_subscr_extension_cmd, - "extension EXTENSION", - "Set the extension of the subscriber") -{ - const char *name = argv[0]; - struct gsm_subscriber *subscr = vty->index; - - strncpy(subscr->extension, name, sizeof(subscr->extension)); - - db_sync_subscriber(subscr); - - return CMD_SUCCESS; -} - -DEFUN(cfg_subscr_authorized, - cfg_subscr_authorized_cmd, - "auth <0-1>", - "Set the authorization status of the subscriber") -{ - int auth = atoi(argv[0]); - struct gsm_subscriber *subscr = vty->index; - - if (auth) - subscr->authorized = 1; - else - subscr->authorized = 0; - - db_sync_subscriber(subscr); - - return CMD_SUCCESS; -} - -int bsc_vty_init(struct gsm_network *net) -{ - gsmnet = net; - - cmd_init(1); - vty_init(); - - install_element(VIEW_NODE, &show_net_cmd); - install_element(VIEW_NODE, &show_bts_cmd); - install_element(VIEW_NODE, &show_trx_cmd); - install_element(VIEW_NODE, &show_ts_cmd); - install_element(VIEW_NODE, &show_lchan_cmd); - - install_element(VIEW_NODE, &show_e1drv_cmd); - install_element(VIEW_NODE, &show_e1line_cmd); - install_element(VIEW_NODE, &show_e1ts_cmd); - - install_element(VIEW_NODE, &show_paging_cmd); - - install_element(VIEW_NODE, &show_subscr_cmd); - - install_element(CONFIG_NODE, &cfg_bts_cmd); - install_node(&bts_node, dummy_config_write); - install_default(BTS_NODE); - install_element(BTS_NODE, &cfg_bts_type_cmd); - install_element(BTS_NODE, &cfg_bts_lac_cmd); - install_element(BTS_NODE, &cfg_bts_tsc_cmd); - install_element(BTS_NODE, &cfg_bts_unit_id_cmd); - - install_element(BTS_NODE, &cfg_trx_cmd); - install_node(&trx_node, dummy_config_write); - install_default(TRX_NODE); - install_element(TRX_NODE, &cfg_trx_arfcn_cmd); - - install_element(TRX_NODE, &cfg_ts_cmd); - install_node(&ts_node, dummy_config_write); - install_default(TS_NODE); - - install_element(CONFIG_NODE, &cfg_subscr_cmd); - install_node(&subscr_node, dummy_config_write); - install_default(SUBSCR_NODE); - install_element(SUBSCR_NODE, &cfg_subscr_name_cmd); - install_element(SUBSCR_NODE, &cfg_subscr_extension_cmd); - install_element(SUBSCR_NODE, &cfg_subscr_authorized_cmd); - - return 0; -} |