Merge commit '33f0fc3c9565308044c934c9e0c1bb81c1a26311'
authorHarald Welte <laforge@gnumonks.org>
Tue, 13 Jul 2010 11:51:42 +0000 (13:51 +0200)
committerHarald Welte <laforge@gnumonks.org>
Tue, 13 Jul 2010 11:51:42 +0000 (13:51 +0200)
1  2 
src/shared/libosmocore/include/osmocore/protocol/gsm_08_58.h
src/shared/libosmocore/include/osmocore/rsl.h
src/shared/libosmocore/src/rsl.c
src/shared/libosmocore/src/vty/command.c

index 7dc3569,0000000..5fe332e
mode 100644,000000..100644
--- /dev/null
@@@ -1,516 -1,0 +1,525 @@@
 +#ifndef PROTO_GSM_08_58_H
 +#define PROTO_GSM_08_58_H
 +
 +/* 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 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 <stdint.h>
 +
 +struct abis_rsl_common_hdr {
 +      uint8_t msg_discr;
 +      uint8_t msg_type;
 +      uint8_t data[0];
 +} __attribute__ ((packed));
 +
 +/* Chapter 8.3 */
 +struct abis_rsl_rll_hdr {
 +      struct abis_rsl_common_hdr c;
 +      uint8_t ie_chan;
 +      uint8_t chan_nr;
 +      uint8_t ie_link_id;
 +      uint8_t link_id;
 +      uint8_t data[0];
 +} __attribute__ ((packed));
 +
 +/* Chapter 8.3 and 8.4 */
 +struct abis_rsl_dchan_hdr {
 +      struct abis_rsl_common_hdr c;
 +      uint8_t ie_chan;
 +      uint8_t chan_nr;
 +      uint8_t data[0];
 +} __attribute__ ((packed));
 +
++/* Chapter 8.5 */
++struct abis_rsl_cchan_hdr {
++      struct abis_rsl_common_hdr c;
++      uint8_t ie_chan;
++      uint8_t chan_nr;
++      uint8_t data[0];
++} __attribute__ ((packed));
++
 +
 +/* Chapter 9.1 */
 +#define ABIS_RSL_MDISC_RLL            0x02
 +#define ABIS_RSL_MDISC_DED_CHAN               0x08
 +#define ABIS_RSL_MDISC_COM_CHAN               0x0c
 +#define ABIS_RSL_MDISC_TRX            0x10
 +#define ABIS_RSL_MDISC_LOC            0x20
 +#define ABIS_RSL_MDISC_IPACCESS               0x7e
 +#define ABIS_RSL_MDISC_TRANSP         0x01
 +
 +#define ABIS_RSL_MDISC_IS_TRANSP(x)   (x & 0x01)
 +
 +/* Chapter 9.1 */
 +enum abis_rsl_msgtype {
 +      /* Radio Link Layer Management */
 +      RSL_MT_DATA_REQ                 = 0x01,
 +      RSL_MT_DATA_IND,
 +      RSL_MT_ERROR_IND,
 +      RSL_MT_EST_REQ,
 +      RSL_MT_EST_CONF,
 +      RSL_MT_EST_IND,
 +      RSL_MT_REL_REQ,
 +      RSL_MT_REL_CONF,
 +      RSL_MT_REL_IND,
 +      RSL_MT_UNIT_DATA_REQ,
 +      RSL_MT_UNIT_DATA_IND,           /* 0x0b */
 +      RSL_MT_SUSP_REQ,                /* non-standard elements */
 +      RSL_MT_SUSP_CONF,
 +      RSL_MT_RES_REQ,
 +      RSL_MT_RECON_REQ,               /* 0x0f */
 +
 +      /* Common Channel Management / TRX Management */
 +      RSL_MT_BCCH_INFO                        = 0x11,
 +      RSL_MT_CCCH_LOAD_IND,
 +      RSL_MT_CHAN_RQD,
 +      RSL_MT_DELETE_IND,
 +      RSL_MT_PAGING_CMD,
 +      RSL_MT_IMMEDIATE_ASSIGN_CMD,
 +      RSL_MT_SMS_BC_REQ,
++      RSL_MT_CHAN_CONF,               /* non-standard element */
 +      /* empty */
 +      RSL_MT_RF_RES_IND                       = 0x19,
 +      RSL_MT_SACCH_FILL,
 +      RSL_MT_OVERLOAD,
 +      RSL_MT_ERROR_REPORT,
 +      RSL_MT_SMS_BC_CMD,
 +      RSL_MT_CBCH_LOAD_IND,
 +      RSL_MT_NOT_CMD,                 /* 0x1f */
 +
 +      /* Dedicate Channel Management */
 +      RSL_MT_CHAN_ACTIV                       = 0x21,
 +      RSL_MT_CHAN_ACTIV_ACK,
 +      RSL_MT_CHAN_ACTIV_NACK,
 +      RSL_MT_CONN_FAIL,
 +      RSL_MT_DEACTIVATE_SACCH,
 +      RSL_MT_ENCR_CMD,
 +      RSL_MT_HANDO_DET,
 +      RSL_MT_MEAS_RES,
 +      RSL_MT_MODE_MODIFY_REQ,
 +      RSL_MT_MODE_MODIFY_ACK,
 +      RSL_MT_MODE_MODIFY_NACK,
 +      RSL_MT_PHY_CONTEXT_REQ,
 +      RSL_MT_PHY_CONTEXT_CONF,
 +      RSL_MT_RF_CHAN_REL,
 +      RSL_MT_MS_POWER_CONTROL,
 +      RSL_MT_BS_POWER_CONTROL,                /* 0x30 */
 +      RSL_MT_PREPROC_CONFIG,
 +      RSL_MT_PREPROC_MEAS_RES,
 +      RSL_MT_RF_CHAN_REL_ACK,
 +      RSL_MT_SACCH_INFO_MODIFY,
 +      RSL_MT_TALKER_DET,
 +      RSL_MT_LISTENER_DET,
 +      RSL_MT_REMOTE_CODEC_CONF_REP,
 +      RSL_MT_RTD_REP,
 +      RSL_MT_PRE_HANDO_NOTIF,
 +      RSL_MT_MR_CODEC_MOD_REQ,
 +      RSL_MT_MR_CODEC_MOD_ACK,
 +      RSL_MT_MR_CODEC_MOD_NACK,
 +      RSL_MT_MR_CODEC_MOD_PER,
 +      RSL_MT_TFO_REP,
 +      RSL_MT_TFO_MOD_REQ,             /* 0x3f */
 +      RSL_MT_LOCATION_INFO            = 0x41,
 +
 +      /* ip.access specific RSL message types */
 +      RSL_MT_IPAC_DIR_RETR_ENQ        = 0x40,
 +      RSL_MT_IPAC_PDCH_ACT            = 0x48,
 +      RSL_MT_IPAC_PDCH_ACT_ACK,
 +      RSL_MT_IPAC_PDCH_ACT_NACK,
 +      RSL_MT_IPAC_PDCH_DEACT          = 0x4b,
 +      RSL_MT_IPAC_PDCH_DEACT_ACK,
 +      RSL_MT_IPAC_PDCH_DEACT_NACK,
 +      RSL_MT_IPAC_CONNECT_MUX         = 0x50,
 +      RSL_MT_IPAC_CONNECT_MUX_ACK,
 +      RSL_MT_IPAC_CONNECT_MUX_NACK,
 +      RSL_MT_IPAC_BIND_MUX            = 0x53,
 +      RSL_MT_IPAC_BIND_MUX_ACK,
 +      RSL_MT_IPAC_BIND_MUX_NACK,
 +      RSL_MT_IPAC_DISC_MUX            = 0x56,
 +      RSL_MT_IPAC_DISC_MUX_ACK,
 +      RSL_MT_IPAC_DISC_MUX_NACK,
 +      RSL_MT_IPAC_CRCX                = 0x70,         /* Bind to local BTS RTP port */
 +      RSL_MT_IPAC_CRCX_ACK,
 +      RSL_MT_IPAC_CRCX_NACK,
 +      RSL_MT_IPAC_MDCX                = 0x73,
 +      RSL_MT_IPAC_MDCX_ACK,
 +      RSL_MT_IPAC_MDCX_NACK,
 +      RSL_MT_IPAC_DLCX_IND            = 0x76,
 +      RSL_MT_IPAC_DLCX                = 0x77,
 +      RSL_MT_IPAC_DLCX_ACK,
 +      RSL_MT_IPAC_DLCX_NACK,
 +};
 +
 +/* Siemens vendor-specific */
 +enum abis_rsl_msgtype_siemens {
 +      RSL_MT_SIEMENS_MRPCI            = 0x41,
 +      RSL_MT_SIEMENS_INTRAC_HO_COND_IND = 0x42,
 +      RSL_MT_SIEMENS_INTERC_HO_COND_IND = 0x43,
 +      RSL_MT_SIEMENS_FORCED_HO_REQ    = 0x44,
 +      RSL_MT_SIEMENS_PREF_AREA_REQ    = 0x45,
 +      RSL_MT_SIEMENS_PREF_AREA        = 0x46,
 +      RSL_MT_SIEMENS_START_TRACE      = 0x47,
 +      RSL_MT_SIEMENS_START_TRACE_ACK  = 0x48,
 +      RSL_MT_SIEMENS_STOP_TRACE       = 0x49,
 +      RSL_MT_SIEMENS_TRMR             = 0x4a,
 +      RSL_MT_SIEMENS_HO_FAIL_IND      = 0x4b,
 +      RSL_MT_SIEMENS_STOP_TRACE_ACK   = 0x4c,
 +      RSL_MT_SIEMENS_UPLF             = 0x4d,
 +      RSL_MT_SIEMENS_UPLB             = 0x4e,
 +      RSL_MT_SIEMENS_SET_SYS_INFO_10  = 0x4f,
 +      RSL_MT_SIEMENS_MODIF_COND_IND   = 0x50,
 +};
 +
 +/* Chapter 9.3 */
 +enum abis_rsl_ie {
 +      RSL_IE_CHAN_NR                  = 0x01,
 +      RSL_IE_LINK_IDENT,
 +      RSL_IE_ACT_TYPE,
 +      RSL_IE_BS_POWER,
 +      RSL_IE_CHAN_IDENT,
 +      RSL_IE_CHAN_MODE,
 +      RSL_IE_ENCR_INFO,
 +      RSL_IE_FRAME_NUMBER,
 +      RSL_IE_HANDO_REF,
 +      RSL_IE_L1_INFO,
 +      RSL_IE_L3_INFO,
 +      RSL_IE_MS_IDENTITY,
 +      RSL_IE_MS_POWER,
 +      RSL_IE_PAGING_GROUP,
 +      RSL_IE_PAGING_LOAD,
 +      RSL_IE_PYHS_CONTEXT             = 0x10,
 +      RSL_IE_ACCESS_DELAY,
 +      RSL_IE_RACH_LOAD,
 +      RSL_IE_REQ_REFERENCE,
 +      RSL_IE_RELEASE_MODE,
 +      RSL_IE_RESOURCE_INFO,
 +      RSL_IE_RLM_CAUSE,
 +      RSL_IE_STARTNG_TIME,
 +      RSL_IE_TIMING_ADVANCE,
 +      RSL_IE_UPLINK_MEAS,
 +      RSL_IE_CAUSE,
 +      RSL_IE_MEAS_RES_NR,
 +      RSL_IE_MSG_ID,
 +      /* reserved */
 +      RSL_IE_SYSINFO_TYPE             = 0x1e,
 +      RSL_IE_MS_POWER_PARAM,
 +      RSL_IE_BS_POWER_PARAM,
 +      RSL_IE_PREPROC_PARAM,
 +      RSL_IE_PREPROC_MEAS,
 +      RSL_IE_IMM_ASS_INFO,            /* Phase 1 (3.6.0), later Full below */
 +      RSL_IE_SMSCB_INFO               = 0x24,
 +      RSL_IE_MS_TIMING_OFFSET,
 +      RSL_IE_ERR_MSG,
 +      RSL_IE_FULL_BCCH_INFO,
 +      RSL_IE_CHAN_NEEDED,
 +      RSL_IE_CB_CMD_TYPE,
 +      RSL_IE_SMSCB_MSG,
 +      RSL_IE_FULL_IMM_ASS_INFO,
 +      RSL_IE_SACCH_INFO,
 +      RSL_IE_CBCH_LOAD_INFO,
 +      RSL_IE_SMSCB_CHAN_INDICATOR,
 +      RSL_IE_GROUP_CALL_REF,
 +      RSL_IE_CHAN_DESC                = 0x30,
 +      RSL_IE_NCH_DRX_INFO,
 +      RSL_IE_CMD_INDICATOR,
 +      RSL_IE_EMLPP_PRIO,
 +      RSL_IE_UIC,
 +      RSL_IE_MAIN_CHAN_REF,
 +      RSL_IE_MR_CONFIG,
 +      RSL_IE_MR_CONTROL,
 +      RSL_IE_SUP_CODEC_TYPES,
 +      RSL_IE_CODEC_CONFIG,
 +      RSL_IE_RTD,
 +      RSL_IE_TFO_STATUS,
 +      RSL_IE_LLP_APDU,
 +      /* Siemens vendor-specific */
 +      RSL_IE_SIEMENS_MRPCI            = 0x40,
 +      RSL_IE_SIEMENS_PREF_AREA_TYPE   = 0x43,
 +      RSL_IE_SIEMENS_ININ_CELL_HO_PAR = 0x45,
 +      RSL_IE_SIEMENS_TRACE_REF_NR     = 0x46,
 +      RSL_IE_SIEMENS_INT_TRACE_IDX    = 0x47,
 +      RSL_IE_SIEMENS_L2_HDR_INFO      = 0x48,
 +      RSL_IE_SIEMENS_HIGHEST_RATE     = 0x4e,
 +      RSL_IE_SIEMENS_SUGGESTED_RATE   = 0x4f,
 +
 +      /* ip.access */
 +      RSL_IE_IPAC_SRTP_CONFIG = 0xe0,
 +      RSL_IE_IPAC_PROXY_UDP   = 0xe1,
 +      RSL_IE_IPAC_BSCMPL_TOUT = 0xe2,
 +      RSL_IE_IPAC_REMOTE_IP   = 0xf0,
 +      RSL_IE_IPAC_REMOTE_PORT = 0xf1,
 +      RSL_IE_IPAC_RTP_PAYLOAD = 0xf2,
 +      RSL_IE_IPAC_LOCAL_PORT  = 0xf3,
 +      RSL_IE_IPAC_SPEECH_MODE = 0xf4,
 +      RSL_IE_IPAC_LOCAL_IP    = 0xf5,
 +      RSL_IE_IPAC_CONN_STAT   = 0xf6,
 +      RSL_IE_IPAC_HO_C_PARMS  = 0xf7,
 +      RSL_IE_IPAC_CONN_ID     = 0xf8,
 +      RSL_IE_IPAC_RTP_CSD_FMT = 0xf9,
 +      RSL_IE_IPAC_RTP_JIT_BUF = 0xfa,
 +      RSL_IE_IPAC_RTP_COMPR   = 0xfb,
 +      RSL_IE_IPAC_RTP_PAYLOAD2= 0xfc,
 +      RSL_IE_IPAC_RTP_MPLEX   = 0xfd,
 +      RSL_IE_IPAC_RTP_MPLEX_ID= 0xfe,
 +};
 +
 +/* Chapter 9.3.1 */
 +#define RSL_CHAN_NR_MASK      0xf8
 +#define RSL_CHAN_Bm_ACCHs     0x08
 +#define RSL_CHAN_Lm_ACCHs     0x10    /* .. 0x18 */
 +#define RSL_CHAN_SDCCH4_ACCH  0x20    /* .. 0x38 */
 +#define RSL_CHAN_SDCCH8_ACCH  0x40    /* ...0x78 */
 +#define RSL_CHAN_BCCH         0x80
 +#define RSL_CHAN_RACH         0x88
 +#define RSL_CHAN_PCH_AGCH     0x90
 +
 +/* Chapter 9.3.3 */
 +#define RSL_ACT_TYPE_INITIAL  0x00
 +#define RSL_ACT_TYPE_REACT    0x80
 +#define RSL_ACT_INTRA_IMM_ASS 0x00
 +#define RSL_ACT_INTRA_NORM_ASS        0x01
 +#define RSL_ACT_INTER_ASYNC   0x02
 +#define RSL_ACT_INTER_SYNC    0x03
 +#define RSL_ACT_SECOND_ADD    0x04
 +#define RSL_ACT_SECOND_MULTI  0x05
 +
 +/* Chapter 9.3.6 */
 +struct rsl_ie_chan_mode {
 +      uint8_t dtx_dtu;
 +      uint8_t spd_ind;
 +      uint8_t chan_rt;
 +      uint8_t chan_rate;
 +} __attribute__ ((packed));
 +#define RSL_CMOD_DTXu         0x01    /* uplink */
 +#define RSL_CMOD_DTXd         0x02    /* downlink */
 +enum rsl_cmod_spd {
 +      RSL_CMOD_SPD_SPEECH     = 0x01,
 +      RSL_CMOD_SPD_DATA       = 0x02,
 +      RSL_CMOD_SPD_SIGN       = 0x03,
 +};
 +#define RSL_CMOD_CRT_SDCCH    0x01
 +#define RSL_CMOD_CRT_TCH_Bm   0x08    /* full-rate */
 +#define RSL_CMOD_CRT_TCH_Lm   0x09    /* half-rate */
 +/* FIXME: More CRT types */
 +/* Speech */
 +#define RSL_CMOD_SP_GSM1      0x01
 +#define RSL_CMOD_SP_GSM2      0x11
 +#define RSL_CMOD_SP_GSM3      0x21
 +/* Data */
 +#define RSL_CMOD_SP_NT_14k5   0x58
 +#define RSL_CMOD_SP_NT_12k0   0x50
 +#define RSL_CMOD_SP_NT_6k0    0x51
 +
 +/* Chapter 9.3.5 */
 +struct rsl_ie_chan_ident {
 +      /* GSM 04.08 10.5.2.5 */
 +      struct {
 +              uint8_t iei;
 +              uint8_t chan_nr;        /* enc_chan_nr */
 +              uint8_t oct3;
 +              uint8_t oct4;
 +      } chan_desc;
 +#if 0 /* spec says we need this but Abissim doesn't use it */
 +      struct {
 +              uint8_t tag;
 +              uint8_t len;
 +      } mobile_alloc;
 +#endif
 +} __attribute__ ((packed));
 +
 +/* Chapter 9.3.22 */
 +#define RLL_CAUSE_T200_EXPIRED                0x01
 +#define RLL_CAUSE_REEST_REQ           0x02
 +#define RLL_CAUSE_UNSOL_UA_RESP               0x03
 +#define RLL_CAUSE_UNSOL_DM_RESP               0x04
 +#define RLL_CAUSE_UNSOL_DM_RESP_MF    0x05
 +#define RLL_CAUSE_UNSOL_SPRV_RESP     0x06
 +#define RLL_CAUSE_SEQ_ERR             0x07
 +#define RLL_CAUSE_UFRM_INC_PARAM      0x08
 +#define RLL_CAUSE_SFRM_INC_PARAM      0x09
 +#define RLL_CAUSE_IFRM_INC_MBITS      0x0a
 +#define RLL_CAUSE_IFRM_INC_LEN                0x0b
 +#define RLL_CAUSE_FRM_UNIMPL          0x0c
 +#define RLL_CAUSE_SABM_MF             0x0d
 +#define RLL_CAUSE_SABM_INFO_NOTALL    0x0e
 +
 +/* Chapter 9.3.26 */
 +#define RSL_ERRCLS_NORMAL             0x00
 +#define RSL_ERRCLS_RESOURCE_UNAVAIL   0x20
 +#define RSL_ERRCLS_SERVICE_UNAVAIL    0x30
 +#define RSL_ERRCLS_SERVICE_UNIMPL     0x40
 +#define RSL_ERRCLS_INVAL_MSG          0x50
 +#define RSL_ERRCLS_PROTO_ERROR                0x60
 +#define RSL_ERRCLS_INTERWORKING               0x70
 +
 +/* normal event */
 +#define RSL_ERR_RADIO_IF_FAIL         0x00
 +#define RSL_ERR_RADIO_LINK_FAIL               0x01
 +#define RSL_ERR_HANDOVER_ACC_FAIL     0x02
 +#define RSL_ERR_TALKER_ACC_FAIL               0x03
 +#define RSL_ERR_OM_INTERVENTION               0x07
 +#define RSL_ERR_NORMAL_UNSPEC         0x0f
 +#define RSL_ERR_T_MSRFPCI_EXP         0x18
 +/* resource unavailable */
 +#define RSL_ERR_EQUIPMENT_FAIL                0x20
 +#define RSL_ERR_RR_UNAVAIL            0x21
 +#define RSL_ERR_TERR_CH_FAIL          0x22
 +#define RSL_ERR_CCCH_OVERLOAD         0x23
 +#define RSL_ERR_ACCH_OVERLOAD         0x24
 +#define RSL_ERR_PROCESSOR_OVERLOAD    0x25
 +#define RSL_ERR_RES_UNAVAIL           0x2f
 +/* service or option not available */
 +#define RSL_ERR_TRANSC_UNAVAIL                0x30
 +#define RSL_ERR_SERV_OPT_UNAVAIL      0x3f
 +/* service or option not implemented */
 +#define RSL_ERR_ENCR_UNIMPL           0x40
 +#define RSL_ERR_SERV_OPT_UNIMPL               0x4f
 +/* invalid message */
 +#define RSL_ERR_RCH_ALR_ACTV_ALLOC    0x50
 +#define RSL_ERR_INVALID_MESSAGE               0x5f
 +/* protocol error */
 +#define RSL_ERR_MSG_DISCR             0x60
 +#define RSL_ERR_MSG_TYPE              0x61
 +#define RSL_ERR_MSG_SEQ                       0x62
 +#define RSL_ERR_IE_ERROR              0x63
 +#define RSL_ERR_MAND_IE_ERROR         0x64
 +#define RSL_ERR_OPT_IE_ERROR          0x65
 +#define RSL_ERR_IE_NONEXIST           0x66
 +#define RSL_ERR_IE_LENGTH             0x67
 +#define RSL_ERR_IE_CONTENT            0x68
 +#define RSL_ERR_PROTO                 0x6f
 +/* interworking */
 +#define RSL_ERR_INTERWORKING          0x7f
 +
 +/* Chapter 9.3.30 */
 +#define RSL_SYSTEM_INFO_8     0x00
 +#define RSL_SYSTEM_INFO_1     0x01
 +#define RSL_SYSTEM_INFO_2     0x02
 +#define RSL_SYSTEM_INFO_3     0x03
 +#define RSL_SYSTEM_INFO_4     0x04
 +#define RSL_SYSTEM_INFO_5     0x05
 +#define RSL_SYSTEM_INFO_6     0x06
 +#define RSL_SYSTEM_INFO_7     0x07
 +#define RSL_SYSTEM_INFO_16    0x08
 +#define RSL_SYSTEM_INFO_17    0x09
 +#define RSL_SYSTEM_INFO_2bis  0x0a
 +#define RSL_SYSTEM_INFO_2ter  0x0b
 +#define RSL_SYSTEM_INFO_5bis  0x0d
 +#define RSL_SYSTEM_INFO_5ter  0x0e
 +#define RSL_SYSTEM_INFO_10    0x0f
 +#define REL_EXT_MEAS_ORDER    0x47
 +#define RSL_MEAS_INFO         0x48
 +#define RSL_SYSTEM_INFO_13    0x28
 +#define RSL_SYSTEM_INFO_2quater       0x29
 +#define RSL_SYSTEM_INFO_9     0x2a
 +#define RSL_SYSTEM_INFO_18    0x2b
 +#define RSL_SYSTEM_INFO_19    0x2c
 +#define RSL_SYSTEM_INFO_20    0x2d
 +
 +/* Chapter 9.3.40 */
 +#define RSL_CHANNEED_ANY      0x00
 +#define RSL_CHANNEED_SDCCH    0x01
 +#define RSL_CHANNEED_TCH_F    0x02
 +#define RSL_CHANNEED_TCH_ForH 0x03
 +
 +/* Chapter 3.3.2.3 Brocast control channel */
 +/* CCCH-CONF, NC is not combined */
 +#define RSL_BCCH_CCCH_CONF_1_NC       0x00
 +#define RSL_BCCH_CCCH_CONF_1_C        0x01
 +#define RSL_BCCH_CCCH_CONF_2_NC       0x02
 +#define RSL_BCCH_CCCH_CONF_3_NC       0x04
 +#define RSL_BCCH_CCCH_CONF_4_NC       0x06
 +
 +/* BS-PA-MFRMS */
 +#define RSL_BS_PA_MFRMS_2     0x00
 +#define RSL_BS_PA_MFRMS_3     0x01
 +#define RSL_BS_PA_MFRMS_4     0x02
 +#define RSL_BS_PA_MFRMS_5     0x03
 +#define RSL_BS_PA_MFRMS_6     0x04
 +#define RSL_BS_PA_MFRMS_7     0x05
 +#define RSL_BS_PA_MFRMS_8     0x06
 +#define RSL_BS_PA_MFRMS_9     0x07
 +
 +/* RSL_IE_IPAC_RTP_PAYLOAD[2] */
 +enum rsl_ipac_rtp_payload {
 +      RSL_IPAC_RTP_GSM        = 1,
 +      RSL_IPAC_RTP_EFR,
 +      RSL_IPAC_RTP_AMR,
 +      RSL_IPAC_RTP_CSD,
 +      RSL_IPAC_RTP_MUX,
 +};
 +
 +/* RSL_IE_IPAC_SPEECH_MODE, lower four bits */
 +enum rsl_ipac_speech_mode_s {
 +      RSL_IPAC_SPEECH_GSM_FR = 0,     /* GSM FR (Type 1, FS) */
 +      RSL_IPAC_SPEECH_GSM_EFR = 1,    /* GSM EFR (Type 2, FS) */
 +      RSL_IPAC_SPEECH_GSM_AMR_FR = 2, /* GSM AMR/FR (Type 3, FS) */
 +      RSL_IPAC_SPEECH_GSM_HR = 3,     /* GSM HR (Type 1, HS) */
 +      RSL_IPAC_SPEECH_GSM_AMR_HR = 5, /* GSM AMR/hr (Type 3, HS) */
 +      RSL_IPAC_SPEECH_AS_RTP = 0xf,   /* As specified by RTP Payload IE */
 +};
 +/* RSL_IE_IPAC_SPEECH_MODE, upper four bits */
 +enum rsl_ipac_speech_mode_m {
 +      RSL_IPAC_SPEECH_M_RXTX = 0,     /* Send and Receive */
 +      RSL_IPAC_SPEECH_M_RX = 1,       /* Receive only */
 +      RSL_IPAC_SPEECH_M_TX = 2,       /* Send only */
 +};
 +
 +/* RSL_IE_IPAC_RTP_CSD_FMT, lower four bits */
 +enum rsl_ipac_rtp_csd_format_d {
 +      RSL_IPAC_RTP_CSD_EXT_TRAU = 0,
 +      RSL_IPAC_RTP_CSD_NON_TRAU = 1,
 +      RSL_IPAC_RTP_CSD_TRAU_BTS = 2,
 +      RSL_IPAC_RTP_CSD_IWF_FREE = 3,
 +};
 +/* RSL_IE_IPAC_RTP_CSD_FMT, upper four bits */
 +enum rsl_ipac_rtp_csd_format_ir {
 +      RSL_IPAC_RTP_CSD_IR_8k = 0,
 +      RSL_IPAC_RTP_CSD_IR_16k = 1,
 +      RSL_IPAC_RTP_CSD_IR_32k = 2,
 +      RSL_IPAC_RTP_CSD_IR_64k = 3,
 +};
 +
 +/* Siemens vendor-specific RSL extensions */
 +struct rsl_mrpci {
 +      uint8_t power_class:3,
 +               vgcs_capable:1,
 +               vbs_capable:1,
 +               gsm_phase:2;
 +} __attribute__ ((packed));
 +
 +enum rsl_mrpci_pwrclass {
 +      RSL_MRPCI_PWRC_1        = 0,
 +      RSL_MRPCI_PWRC_2        = 1,
 +      RSL_MRPCI_PWRC_3        = 2,
 +      RSL_MRPCI_PWRC_4        = 3,
 +      RSL_MRPCI_PWRC_5        = 4,
 +};
 +enum rsl_mrpci_phase {
 +      RSL_MRPCI_PHASE_1       = 0,
 +      /* reserved */
 +      RSL_MRPCI_PHASE_2       = 2,
 +      RSL_MRPCI_PHASE_2PLUS   = 3,
 +};
 +
 +
 +#endif /* PROTO_GSM_08_58_H */
index 99b90d6,0000000..cd84057
mode 100644,000000..100644
--- /dev/null
@@@ -1,32 -1,0 +1,38 @@@
 +#ifndef _OSMOCORE_RSL_H
 +#define _OSMOCORE_RSL_H
 +
 +#include <stdint.h>
 +#include <osmocore/utils.h>
 +#include <osmocore/protocol/gsm_08_58.h>
 +
 +void rsl_init_rll_hdr(struct abis_rsl_rll_hdr *dh, uint8_t msg_type);
 +
++void rsl_init_cchan_hdr(struct abis_rsl_cchan_hdr *ch, uint8_t msg_type);
++
 +extern const struct tlv_definition rsl_att_tlvdef;
 +#define rsl_tlv_parse(dec, buf, len)     \
 +                      tlv_parse(dec, &rsl_att_tlvdef, buf, len, 0, 0)
 +
 +/* encode channel number as per Section 9.3.1 */
 +uint8_t rsl_enc_chan_nr(uint8_t type, uint8_t subch, uint8_t timeslot);
 +/* decode channel number as per Section 9.3.1 */
 +int rsl_dec_chan_nr(uint8_t chan_nr, uint8_t *type, uint8_t *subch, uint8_t *timeslot);
 +
 +const char *rsl_err_name(uint8_t err);
 +const char *rsl_rlm_cause_name(uint8_t err);
 +
 +/* Section 3.3.2.3 TS 05.02. I think this looks like a table */
 +int rsl_ccch_conf_to_bs_cc_chans(int ccch_conf);
 +
++/* Push a RSL RLL header */
++void rsl_rll_push_hdr(struct msgb *msg, uint8_t msg_type, uint8_t chan_nr,
++                    uint8_t link_id, int transparent);
++
 +/* Push a RSL RLL header with L3_INFO IE */
 +void rsl_rll_push_l3(struct msgb *msg, uint8_t msg_type, uint8_t chan_nr,
 +                   uint8_t link_id, int transparent);
 +
 +/* Allocate msgb and fill with simple RSL RLL header */
 +struct msgb *rsl_rll_simple(uint8_t msg_type, uint8_t chan_nr,
 +                          uint8_t link_id, int transparent);
 +#endif /* _OSMOCORE_RSL_H */
index c002d33,0000000..7bc1712
mode 100644,000000..100644
--- /dev/null
@@@ -1,329 -1,0 +1,344 @@@
-       struct abis_rsl_rll_hdr *rh;
 +/* 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-2010 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 <stdint.h>
 +#include <errno.h>
 +
 +#include <osmocore/tlv.h>
 +#include <osmocore/rsl.h>
 +
 +#define RSL_ALLOC_SIZE                200
 +#define RSL_ALLOC_HEADROOM    56
 +
 +void rsl_init_rll_hdr(struct abis_rsl_rll_hdr *dh, uint8_t 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;
 +}
 +
++void rsl_init_cchan_hdr(struct abis_rsl_cchan_hdr *ch, uint8_t msg_type)
++{
++      ch->c.msg_discr = ABIS_RSL_MDISC_COM_CHAN;
++      ch->c.msg_type = msg_type;
++      ch->ie_chan = RSL_IE_CHAN_NR;
++}
++
 +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_SIEMENS_MRPCI]          = { TLV_TYPE_TV },
 +              [RSL_IE_IPAC_PROXY_UDP]         = { TLV_TYPE_FIXED, 2 },
 +              [RSL_IE_IPAC_BSCMPL_TOUT]       = { TLV_TYPE_TV },
 +              [RSL_IE_IPAC_REMOTE_IP]         = { TLV_TYPE_FIXED, 4 },
 +              [RSL_IE_IPAC_REMOTE_PORT]       = { TLV_TYPE_FIXED, 2 },
 +              [RSL_IE_IPAC_RTP_PAYLOAD]       = { TLV_TYPE_TV },
 +              [RSL_IE_IPAC_LOCAL_PORT]        = { TLV_TYPE_FIXED, 2 },
 +              [RSL_IE_IPAC_SPEECH_MODE]       = { TLV_TYPE_TV },
 +              [RSL_IE_IPAC_LOCAL_IP]          = { TLV_TYPE_FIXED, 4 },
 +              [RSL_IE_IPAC_CONN_ID]           = { TLV_TYPE_FIXED, 2 },
 +              [RSL_IE_IPAC_RTP_CSD_FMT]       = { TLV_TYPE_TV },
 +              [RSL_IE_IPAC_RTP_JIT_BUF]       = { TLV_TYPE_FIXED, 2 },
 +              [RSL_IE_IPAC_RTP_COMPR]         = { TLV_TYPE_TV },
 +              [RSL_IE_IPAC_RTP_PAYLOAD2]      = { TLV_TYPE_TV },
 +              [RSL_IE_IPAC_RTP_MPLEX]         = { TLV_TYPE_FIXED, 8 },
 +              [RSL_IE_IPAC_RTP_MPLEX_ID]      = { TLV_TYPE_TV },
 +      },
 +};
 +
 +/* encode channel number as per Section 9.3.1 */
 +uint8_t rsl_enc_chan_nr(uint8_t type, uint8_t subch, uint8_t timeslot)
 +{
 +      uint8_t ret;
 +
 +      ret = (timeslot & 0x07) | type;
 +
 +      switch (type) {
 +      case RSL_CHAN_Lm_ACCHs:
 +              subch &= 0x01;
 +              break;
 +      case RSL_CHAN_SDCCH4_ACCH:
 +              subch &= 0x03;
 +              break;
 +      case RSL_CHAN_SDCCH8_ACCH:
 +              subch &= 0x07;
 +              break;
 +      default:
 +              /* no subchannels allowed */
 +              subch = 0x00;
 +              break;
 +      }
 +      ret |= (subch << 3);
 +
 +      return ret;
 +}
 +
 +int rsl_dec_chan_nr(uint8_t chan_nr, uint8_t *type, uint8_t *subch, uint8_t *timeslot)
 +{
 +      *timeslot = chan_nr & 0x7;
 +
 +      if ((chan_nr & 0xf8) == RSL_CHAN_Bm_ACCHs) {
 +              *type = RSL_CHAN_Bm_ACCHs;
 +              *subch = 0;
 +      } else if ((chan_nr & 0xf0) == RSL_CHAN_Lm_ACCHs) {
 +              *type = RSL_CHAN_Lm_ACCHs;
 +              *subch = (chan_nr >> 3) & 0x1;
 +      } else if ((chan_nr & 0xe0) == RSL_CHAN_SDCCH4_ACCH) {
 +              *type = RSL_CHAN_SDCCH4_ACCH;
 +              *subch = (chan_nr >> 3) & 0x3;
 +      } else if ((chan_nr & 0xc0) == RSL_CHAN_SDCCH8_ACCH) {
 +              *type = RSL_CHAN_SDCCH8_ACCH;
 +              *subch = (chan_nr >> 3) & 0x7;
 +      } else if ((chan_nr & 0xf8) == RSL_CHAN_BCCH) {
 +              *type = RSL_CHAN_BCCH;
 +              *subch = 0;
 +      } else if ((chan_nr & 0xf8) == RSL_CHAN_RACH) {
 +              *type = RSL_CHAN_RACH;
 +              *subch = 0;
 +      } else if ((chan_nr & 0xf8) == RSL_CHAN_PCH_AGCH) {
 +              *type = RSL_CHAN_PCH_AGCH;
 +              *subch = 0;
 +      } else
 +              return -EINVAL;
 +
 +      return 0;
 +}
 +
 +static const struct value_string rsl_err_vals[] = {
 +      { RSL_ERR_RADIO_IF_FAIL,        "Radio Interface Failure" },
 +      { RSL_ERR_RADIO_LINK_FAIL,      "Radio Link Failure" },
 +      { RSL_ERR_HANDOVER_ACC_FAIL,    "Handover Access Failure" },
 +      { RSL_ERR_TALKER_ACC_FAIL,      "Talker Access Failure" },
 +      { RSL_ERR_OM_INTERVENTION,      "O&M Intervention" },
 +      { RSL_ERR_NORMAL_UNSPEC,        "Normal event, unspecified" },
 +      { RSL_ERR_T_MSRFPCI_EXP,        "Siemens: T_MSRFPCI Expired" },
 +      { RSL_ERR_EQUIPMENT_FAIL,       "Equipment Failure" },
 +      { RSL_ERR_RR_UNAVAIL,           "Radio Resource not available" },
 +      { RSL_ERR_TERR_CH_FAIL,         "Terrestrial Channel Failure" },
 +      { RSL_ERR_CCCH_OVERLOAD,        "CCCH Overload" },
 +      { RSL_ERR_ACCH_OVERLOAD,        "ACCH Overload" },
 +      { RSL_ERR_PROCESSOR_OVERLOAD,   "Processor Overload" },
 +      { RSL_ERR_RES_UNAVAIL,          "Resource not available, unspecified" },
 +      { RSL_ERR_TRANSC_UNAVAIL,       "Transcoding not available" },
 +      { RSL_ERR_SERV_OPT_UNAVAIL,     "Service or Option not available" },
 +      { RSL_ERR_ENCR_UNIMPL,          "Encryption algorithm not implemented" },
 +      { RSL_ERR_SERV_OPT_UNIMPL,      "Service or Option not implemented" },
 +      { RSL_ERR_RCH_ALR_ACTV_ALLOC,   "Radio channel already activated" },
 +      { RSL_ERR_INVALID_MESSAGE,      "Invalid Message, unspecified" },
 +      { RSL_ERR_MSG_DISCR,            "Message Discriminator Error" },
 +      { RSL_ERR_MSG_TYPE,             "Message Type Error" },
 +      { RSL_ERR_MSG_SEQ,              "Message Sequence Error" },
 +      { RSL_ERR_IE_ERROR,             "General IE error" },
 +      { RSL_ERR_MAND_IE_ERROR,        "Mandatory IE error" },
 +      { RSL_ERR_OPT_IE_ERROR,         "Optional IE error" },
 +      { RSL_ERR_IE_NONEXIST,          "IE non-existent" },
 +      { RSL_ERR_IE_LENGTH,            "IE length error" },
 +      { RSL_ERR_IE_CONTENT,           "IE content error" },
 +      { RSL_ERR_PROTO,                "Protocol error, unspecified" },
 +      { RSL_ERR_INTERWORKING,         "Interworking error, unspecified" },
 +      { 0,                            NULL }
 +};
 +
 +const char *rsl_err_name(uint8_t err)
 +{
 +      return get_value_string(rsl_err_vals, err);
 +}
 +
 +static const struct value_string rsl_rlm_cause_strs[] = {
 +      { RLL_CAUSE_T200_EXPIRED,       "Timer T200 expired (N200+1) times" },
 +      { RLL_CAUSE_REEST_REQ,          "Re-establishment request" },
 +      { RLL_CAUSE_UNSOL_UA_RESP,      "Unsolicited UA response" },
 +      { RLL_CAUSE_UNSOL_DM_RESP,      "Unsolicited DM response" },
 +      { RLL_CAUSE_UNSOL_DM_RESP_MF,   "Unsolicited DM response, multiple frame" },
 +      { RLL_CAUSE_UNSOL_SPRV_RESP,    "Unsolicited supervisory response" },
 +      { RLL_CAUSE_SEQ_ERR,            "Sequence Error" },
 +      { RLL_CAUSE_UFRM_INC_PARAM,     "U-Frame with incorrect parameters" },
 +      { RLL_CAUSE_SFRM_INC_PARAM,     "S-Frame with incorrect parameters" },
 +      { RLL_CAUSE_IFRM_INC_MBITS,     "I-Frame with incorrect use of M bit" },
 +      { RLL_CAUSE_IFRM_INC_LEN,       "I-Frame with incorrect length" },
 +      { RLL_CAUSE_FRM_UNIMPL,         "Fraeme not implemented" },
 +      { RLL_CAUSE_SABM_MF,            "SABM command, multiple frame established state" },
 +      { RLL_CAUSE_SABM_INFO_NOTALL,   "SABM frame with information not allowed in this state" },
 +      { 0,                            NULL },
 +};
 +
 +const char *rsl_rlm_cause_name(uint8_t err)
 +{
 +      return get_value_string(rsl_rlm_cause_strs, err);
 +}
 +
 +/* Section 3.3.2.3 TS 05.02. 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;
 +      }
 +}
 +
 +/* Section 3.3.2.3 TS 05.02 */
 +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;
 +      }
 +}
 +
++/* Push a RSL RLL header */
++void rsl_rll_push_hdr(struct msgb *msg, uint8_t msg_type, uint8_t chan_nr,
++                    uint8_t link_id, int transparent)
++{
++      struct abis_rsl_rll_hdr *rh;
++
++      rh = (struct abis_rsl_rll_hdr *) msgb_push(msg, sizeof(*rh));
++      rsl_init_rll_hdr(rh, msg_type);
++      if (transparent)
++              rh->c.msg_discr |= ABIS_RSL_MDISC_TRANSP;
++      rh->chan_nr = chan_nr;
++      rh->link_id = link_id;
++
++      /* set the l2 header pointer */
++      msg->l2h = (uint8_t *)rh;
++}
++
 +/* Push a RSL RLL header with L3_INFO IE */
 +void rsl_rll_push_l3(struct msgb *msg, uint8_t msg_type, uint8_t chan_nr,
 +                   uint8_t link_id, int transparent)
 +{
 +      uint8_t l3_len = msg->tail - (uint8_t *)msgb_l3(msg);
-       rh = (struct abis_rsl_rll_hdr *) msgb_push(msg, sizeof(*rh));
-       rsl_init_rll_hdr(rh, msg_type);
-       if (transparent)
-               rh->c.msg_discr |= ABIS_RSL_MDISC_TRANSP;
-       rh->chan_nr = chan_nr;
-       rh->link_id = link_id;
-       /* set the l2 header pointer */
-       msg->l2h = (uint8_t *)rh;
 +
 +      /* construct a RSLms RLL message (DATA INDICATION, UNIT DATA
 +       * INDICATION) and send it off via RSLms */
 +
 +      /* Push the L3 IE tag and lengh */
 +      msgb_tv16_push(msg, RSL_IE_L3_INFO, l3_len);
 +
 +      /* Then push the RSL header */
++      rsl_rll_push_hdr(msg, msg_type, chan_nr, link_id, transparent);
 +}
 +
 +struct msgb *rsl_rll_simple(uint8_t msg_type, uint8_t chan_nr,
 +                          uint8_t link_id, int transparent)
 +{
 +      struct abis_rsl_rll_hdr *rh;
 +      struct msgb *msg;
 +
 +      msg = msgb_alloc_headroom(RSL_ALLOC_SIZE+RSL_ALLOC_HEADROOM,
 +                                RSL_ALLOC_HEADROOM, "rsl_rll_simple");
 +
 +      if (!msg)
 +              return NULL;
 +
 +      /* put the RSL header */
 +      rh = (struct abis_rsl_rll_hdr *) msgb_put(msg, sizeof(*rh));
 +      rsl_init_rll_hdr(rh, msg_type);
 +      if (transparent)
 +              rh->c.msg_discr |= ABIS_RSL_MDISC_TRANSP;
 +      rh->chan_nr = chan_nr;
 +      rh->link_id = link_id;
 +
 +      /* set the l2 header pointer */
 +      msg->l2h = (uint8_t *)rh;
 +
 +      return msg;
 +}
index 21afa5c,0000000..598e63c
mode 100644,000000..100644
--- /dev/null
@@@ -1,3177 -1,0 +1,3213 @@@
-       unsigned long min, max, val;
 +/*
 +   $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 <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 <sys/stat.h>
 +
 +#include <osmocom/vty/vector.h>
 +#include <osmocom/vty/vty.h>
 +#include <osmocom/vty/command.h>
 +
 +#include <osmocore/talloc.h>
 +
 +#define CONFIGFILE_MASK 022
 +
 +void *tall_vty_cmd_ctx;
 +
 +/* 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 = "";
 +
 +/* This is called from main when a daemon is invoked with -v or --version. */
 +void print_version(int print_copyright)
 +{
 +      printf("%s version %s\n", host.app_info->name, host.app_info->version);
 +      if (print_copyright)
 +              printf("\n%s\n", host.app_info->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 = _talloc_zero(tall_vty_cmd_ctx, len, "arvg_concat");
 +      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 = _talloc_zero(tall_vty_cmd_ctx, strlen + 1, "make_strvec");
 +              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)
 +                      talloc_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 = _talloc_zero(tall_vty_cmd_ctx, strlen + 1, "cmd_desc_str");
 +      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 = _talloc_zero(tall_vty_cmd_ctx, len + 1, "cmd_make_descvec");
 +              memcpy(token, sp, len);
 +              *(token + len) = '\0';
 +
 +              desc = talloc_zero(tall_vty_cmd_ctx, 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);
 +}
 +
 +/* Install a command into VIEW and ENABLE node */
 +void install_element_ve(struct cmd_element *cmd)
 +{
 +      install_element(VIEW_NODE, cmd);
 +      install_element(ENABLE_NODE, cmd);
 +}
 +
 +#ifdef VTY_CRYPT_PW
 +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);
 +}
 +#endif
 +
 +/* This function write configuration of this host. */
 +static int config_write_host(struct vty *vty)
 +{
 +      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 (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);
 +
 +      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;
 +}
 +
 +/* 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;
-       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;
 +
 +      if (str == NULL)
 +              return 1;
 +
++      if (range[1] == '-') {
++              signed long min = 0, max = 0, val;
++
++              val = strtol(str, &endptr, 10);
++              if (*endptr != '\0')
++                      return 0;
++
++              range += 2;
++              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 = -strtol(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 = strtol(buf, &endptr, 10);
++              if (*endptr != '\0')
++                      return 0;
++
++              if (val < min || val > max)
++                      return 0;
++      } else {
++              unsigned long min, max, val;
++
++              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, talloc_strdup(tall_vty_cmd_ctx, 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 = _talloc_zero(tall_vty_cmd_ctx, lcd + 1,
 +                                                    "complete-lcdstr");
 +                              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))
 +                                              talloc_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 vty_go_parent(struct vty *vty)
 +{
 +      assert(vty->node > CONFIG_NODE);
 +
 +      if (host.app_info->go_parent_cb)
 +              host.app_info->go_parent_cb(vty);
 +      else
 +              vty->node = CONFIG_NODE;
 +
 +      return vty->node;
 +}
 +
 +/* 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;
 +      void *oindex;
 +
 +      onode = vty->node;
 +      oindex = vty->index;
 +
 +      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) {
 +              vty_go_parent(vty);
 +              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;
 +              vty->index = oindex;
 +      }
 +      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);
 +}
 +
 +/* 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_go_parent(vty);
 +                      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;
 +}
 +
 +/* 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. */
 +gDEFUN(config_exit,
 +      config_exit_cmd, "exit", "Exit current mode and down to previous mode\n")
 +{
 +      switch (vty->node) {
 +      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 VTY_NODE:
 +              vty->node = CONFIG_NODE;
 +              break;
 +      default:
 +              break;
 +      }
 +      return CMD_SUCCESS;
 +}
 +
 +/* End of configuration. */
 +    gDEFUN(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 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", host.app_info->name,
 +              host.app_info->version,
 +              host.app_info->name ? host.app_info->name : "", VTY_NEWLINE);
 +      vty_out(vty, "%s%s", host.app_info->copyright, VTY_NEWLINE);
 +
 +      return CMD_SUCCESS;
 +}
 +
 +/* Help display function for all node. */
 +gDEFUN(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. */
 +gDEFUN(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;
 +}
 +
 +/* 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 =
 +          _talloc_zero(tall_vty_cmd_ctx,
 +                       strlen(config_file) + strlen(CONF_BACKUP_EXT) + 1,
 +                       "config_file_sav");
 +      strcpy(config_file_sav, config_file);
 +      strcat(config_file_sav, CONF_BACKUP_EXT);
 +
 +      config_file_tmp = _talloc_zero(tall_vty_cmd_ctx, strlen(config_file) + 8,
 +                                      "config_file_tmp");
 +      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);
 +              talloc_free(config_file_tmp);
 +              talloc_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! %s (%s) configuration saved from vty\n!",
 +              host.app_info->name, host.app_info->version);
 +      //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);
 +                      talloc_free(config_file_sav);
 +                      talloc_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);
 +              talloc_free(config_file_sav);
 +              talloc_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);
 +              talloc_free(config_file_sav);
 +              talloc_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);
 +              talloc_free(config_file_sav);
 +              talloc_free(config_file_tmp);
 +              unlink(config_file_tmp);
 +              return CMD_WARNING;
 +      }
 +      unlink(config_file_tmp);
 +      sync();
 +
 +      talloc_free(config_file_sav);
 +      talloc_free(config_file_tmp);
 +
 +      if (chmod(config_file, 0666 & ~CONFIGFILE_MASK) != 0) {
 +              vty_out(vty, "Can't chmod configuration file %s: %s (%d).%s",
 +                      config_file, 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;
 +}
 +
 +/* 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)
 +              talloc_free(host.name);
 +
 +      host.name = talloc_strdup(tall_vty_cmd_ctx, 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)
 +              talloc_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)
 +                              talloc_free(host.password);
 +                      host.password = NULL;
 +                      if (host.password_encrypt)
 +                              talloc_free(host.password_encrypt);
 +                      host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, 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)
 +              talloc_free(host.password);
 +      host.password = NULL;
 +
 +#ifdef VTY_CRYPT_PW
 +      if (host.encrypt) {
 +              if (host.password_encrypt)
 +                      talloc_free(host.password_encrypt);
 +              host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0]));
 +      } else
 +#endif
 +              host.password = talloc_strdup(tall_vty_cmd_ctx, 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)
 +                              talloc_free(host.enable);
 +                      host.enable = NULL;
 +
 +                      if (host.enable_encrypt)
 +                              talloc_free(host.enable_encrypt);
 +                      host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, 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)
 +              talloc_free(host.enable);
 +      host.enable = NULL;
 +
 +      /* Plain password input. */
 +#ifdef VTY_CRYPT_PW
 +      if (host.encrypt) {
 +              if (host.enable_encrypt)
 +                      talloc_free(host.enable_encrypt);
 +              host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0]));
 +      } else
 +#endif
 +              host.enable = talloc_strdup(tall_vty_cmd_ctx, 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)
 +              talloc_free(host.enable);
 +      host.enable = NULL;
 +
 +      if (host.enable_encrypt)
 +              talloc_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)
 +                      talloc_free(host.password_encrypt);
 +              host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(host.password));
 +      }
 +      if (host.enable) {
 +              if (host.enable_encrypt)
 +                      talloc_free(host.enable_encrypt);
 +              host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, 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)
 +              talloc_free(host.password_encrypt);
 +      host.password_encrypt = NULL;
 +
 +      if (host.enable_encrypt)
 +              talloc_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)
 +              talloc_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)
 +              talloc_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 = _talloc_zero(tall_vcmd_ctx,
 +                                    strlen(cwd) + strlen(fname) + 2),
 +                                    "set_log_file")
 +                  == 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)
 +              talloc_free(p);
 +
 +      if (!ret) {
 +              vty_out(vty, "can't open logfile %s\n", fname);
 +              return CMD_WARNING;
 +      }
 +
 +      if (host.logfile)
 +              talloc_free(host.logfile);
 +
 +      host.logfile = talloc_strdup(tall_vty_cmd_ctx, 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)
 +              talloc_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)
 +              talloc_free(host.motdfile);
 +      host.motdfile = talloc_strdup(tall_vty_cmd_ctx, 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)
 +              talloc_free(host.motdfile);
 +      host.motdfile = NULL;
 +      return CMD_SUCCESS;
 +}
 +
 +/* Set config filename.  Called from vty.c */
 +void host_config_set(const char *filename)
 +{
 +      host.config = talloc_strdup(tall_vty_cmd_ctx, filename);
 +}
 +
 +void install_default(enum node_type node)
 +{
 +      install_element(node, &config_help_cmd);
 +      install_element(node, &config_list_cmd);
 +
 +      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);
 +}
 +
 +/* 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.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_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_element(ENABLE_NODE, &config_exit_cmd);
 +              install_default(ENABLE_NODE);
 +              install_element(ENABLE_NODE, &config_disable_cmd);
 +              install_element(ENABLE_NODE, &config_terminal_cmd);
 +              install_element (ENABLE_NODE, &copy_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, &config_exit_cmd);
 +      }
 +
 +      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));
 +}