#!/pkg/bin/ksh
# ---------------------------------------------------------------------
# show_tech_mcast -- Runs plaform independent commands for 'show 
#                    tech-support multicast'
#
# November 2003 - Adam Yeung
#
# Copyright (c) 2003-2011, 2013-2014, 2016, 2018-2020 by cisco Systems, Inc.
# All rights reserved.
#----------------------------------------------------------------------

#
# This script runs all the show commands required for the show tech-support
# multicast command. 
# 
# Options:
#   -f disk filename  Writes output to disk and filename specified.
#   -g <group-addr>   Run commands for specified group addr (in hex).
#   -h                Obtains only hardware information.
#   -i ipv4|ipv6      Obtains address family specific information.
#   -n nodeid         Gives information only for specified nodeid, default is 
#                     to log information on all nodes.
#   -s <source-addr>  Run commands for specified source addr (in hex).
#   -v vrf            Run commands for a specific vrf 
#
# Note that the contents of this script should all remain in one script.
# Because show tech-support multicast needs to be able to operate in low 
# memory conditions, the number of scripts run by the command needs to be 
# minimized. 
#       
# This script gets invoked by the show tech multicast classic command. 
# This script uses the show tech infra api'. This is setup as follows.
# 1. Create an array of show commands to execute. 
# 2. Run the show tech infra script - "show_tech_main_fragment".
#    Define a display() function and specify the array(s) of commands to execute. 
#    This is done by passing the array as an argument to the infra "run_commands" 
#    function.
# 3. Run the show tech infra script - "show_tech_file_fragment".


# Routines used by the script 
##############################

# 
# This function converts a v4 ip address from hex to dotted decimal
# The requirement is for display purposes
# 
hex2addr_v4() { 
    hex=$1 

    # Strip all leading space and 0x.
    hex=${hex##*0x}
   
    # Strip all trailing space
    hex=${hex%% *}

    if [[ ${#hex} != 8 ]]; then
        echo "invalid"
    else
        ((first_byte=(16#$hex >> 24) & 16#000000ff))
        ((second_byte=(16#$hex >> 16) & 16#000000ff))
        ((third_byte=(16#$hex >> 8) & 16#000000ff))
        ((fourth_byte=16#$hex & 16#000000ff))
        echo "$first_byte.$second_byte.$third_byte.$fourth_byte"
    fi

} 

# 
# This function converts a v6 ip address from hex to colon delimited
# The requirement is for display purposes
# 
hex2addr_v6() { 
    hex=$1 

    # Strip all leading space and 0x.
    hex=${hex##*0x}
   
    # Strip all trailing space
    hex=${hex%% *}

    if [[ ${#hex} != 32 ]]; then
        echo "invalid"
    else
	
      # Insert a ":" after every four hex digits 

      # NOTE: Getting a "bad substitution" error with ksh pattern operations 
      # and hence using the following technique

      # blkptr and blkptr_end is used to point to the begining and end of
      # a 4 hex digit block
      typeset -i blkptr=1
      typeset -i blkptr_end

      # This variable  stores the resultant string 
      v6_addr_str=""
  
      # For each set of 4 digits
      while [ blkptr -lt 32 ] ; do
          blkptr_end=$blkptr+3
  
          v6_addr_part=$(echo ${hex} | cut -c ${blkptr}-${blkptr_end})
          v6_addr_str="$v6_addr_str:$v6_addr_part"
  
          blkptr=$blkptr+4
      done

      # Remove the leading ":"
      v6_addr_str=${v6_addr_str#:}
  
      echo $v6_addr_str 

    fi

} 

#
# This function is used to create a new array of location specific 
# commands. 
# The show tech infra scripts execute arrays of commands that we specify. 
# In some cases  like the fwd commands we may have to specify the location 
# information as part of the show command and invoke the command for all 
# the locations. To do this we create a new array of commands specifying 
# the location information in the command and creating the command for all 
# locations. This function is used to create this new array. In the case 
# that a location is specified as part of the show tech multicast command
# then this api is not called. 
# An example: 
# mfwd commands are to be run for a single location if a location is specified 
# otherwise they need to run on all instances  
create_location_specific_array () {

    orig_array=$1
    new_array=$2

    # new array index  
    j=0 

    # Create commands for each location
    for node in $(ls /net/* | grep "/") ; do

        # remove trailing ":"
        node=${node%:} 

        # get the location information
        # this gets all locations across a multi-chassis
        nodename=${node#/net/*}
        location=`node_conversion -E $nodename`

        # Check if this is a fabric card and ignore it. 
        # This is CRS specific
        if [[ $location != *SP* ]] ; then

           # reset the original array index 
           i=0

           # first command from the original array 
           cmd_line=$(eval "echo \${command_"$orig_array"[$i]}") 

           # add to the new array for all commands
           while [ -n "$cmd_line" ]; do

	       # The new command
	       cmd_line_new="${cmd_line} location $location"
   
	       # Store the new command in the new_array
               eval $(echo "command_$new_array[$j]=\"$cmd_line_new\"")
      
               # increment the new array index
               ((j=$j+1))

               # next command from original array
               i=$(($i + 1))
               cmd_line=$(eval "echo \${command_"$orig_array"[$i]}") 

          done # end while 
          
       fi # locatoin != *SP*

    done # end for 

}

#
# This function is used to create the platform specific 
# command array. For most parts this function works like 
# create_location_specific_array. In addition it adds some 
# platform specific parameters to the commands. 
# Also, this function is called irrespective of whether the 
# command is to be run for a specific location or all locations
# 
init_platform_showtech_mcast () { 

    # If this is for a gsr platform add the additional parameters
    get_platform
    if [[ "$platform" != "prp" ]]; then
       plat_gsr_opts=""
    fi
    plat_opts="$plat_opts $plat_gsr_opts"

    # If no location is specified prepare an array of commands to run on 
    # all locations of a particular rack.
    if [[ $node_name = "" ]]; then 
        i=1 
        for node in $(ls /net/* | grep "/"); do
            # remove trailing ":"
            node=${node%:}

            # get the location information
            # e.g. 
            # nodename=node0_4_CPU0
            # location=0/4/0
            # pq_nodeid=0x2a000040
            # fq_nodeid=64
            nodename=${node#/net/*}
            location=`node_conversion -E $nodename`
            pq_nodeid=`node_conversion -I $location`
            fq_nodeid=`node_conversion -i $node`
        

            # Check if this is a fabric card and ignore it. 
            # This is CRS specific
            if [[ $location != *SP* ]] ; then

                # create the platform array
                platform__ksh[$i]="platform_show_tech_mcast -n $fq_nodeid -m $location $plat_opts"
                platform_exec[$i]="platform show tech multicast $location"
                platform__ksh[$i+1]=''
     
                ((i=$i+1))
           
            fi # location != *SP*

        done # end for

    # If location is specified create an array with a single entry
    else 
        platform__ksh[1]="platform_show_tech_mcast $plat_opts"
        platform_exec[1]="platform show tech multicast"
        platform__exec[2]=''
    fi 
} 

# ##################################################################
# Main starts here. 
# ##################################################################

# Run the initial show tech infra script
. /pkg/bin/show_tech_main_fragment

# 
# Set the default values for all the arguments"
addr_type="ipv4"
group_addr_hex=""
group_addr_str=""
file_name=""
node_name=""
source_addr_hex=""
source_addr_str=""
vrf_name=""
hardware_flag=0
debug_flag=0
plat_opts=""
plat_gsr_opts=""

#
# Read in the arguments to the script and also set some option
# that are to be passed to the platform scripts
#
# Note that it is important for security reasons that users can only enter 
# alphanumeric filenames and nodes and that anywhere calling this script must
# enforce this.  
#

# save this for debug purposes
arg_list=$(echo $@)

while [ $# -gt 0 ]; do
  case "$1" in 
     -i) addr_type="$2"; shift 2;;
     -g) group_addr_hex="$2"; plat_gsr_opts="$plat_gsr_opts -x $2"; shift 2;;
     -n) node_name="location $3"; plat_opts="$plat_opts -m $3 -n $2"; 
         shift 3;;
     -s) source_addr_hex="$2"; plat_gsr_opts="$plat_gsr_opts -y $2"; shift 2;;
     -v) vrf_name="vrf $2"; plat_opts="$plat_opts -v $2"; shift 2;;
     -h) hardware_flag=1; shift 1;;
     *)  default_parser_function "$@"; shift $#;;
  esac
done

# Get the string equivalent of the group hex address 
# and also set the corresponding platform option.
if [[ $group_addr_hex != "" ]];  then 
    if [[ $addr_type = "ipv6" ]]; then 
        group_addr_str=$(hex2addr_v6 $group_addr_hex)
    else
        group_addr_str=$(hex2addr_v4 $group_addr_hex)
    fi

    # set the platform option as well 
    plat_opts="$plat_opts -g $group_addr_str"     
fi 

# Get the string equivalent of the source hex address 
# and also set the corresponding platform option.
if [[ $source_addr_hex != "" ]];  then 
    if [[ $addr_type = "ipv6" ]]; then 
        source_addr_str=$(hex2addr_v6 $source_addr_hex)
    else
        source_addr_str=$(hex2addr_v4 $source_addr_hex)
    fi 
   
    # set the platform option as well 
    plat_opts="$plat_opts -s $source_addr_str"     
fi 

# Set address-family specific multicast addresses
if [ $addr_type = "ipv4" ] ;then 
    all_routers_addr="224.0.0.2"
    all_pim_routers_addr="224.0.0.13"
    group_membership_addr="224.0.0.22"
    cisco_rp_announce_addr="224.0.1.39"
    cisco_rp_discovery_addr="224.0.1.40"
else
    all_routers_addr="FF01:0:0:0:0:0:0:2"
    all_pim_routers_addr="FF02:0:0:0:0:0:0:D"
    group_membership_addr="FF02:0:0:0:0:0:0:16"
fi 

# Specify the command arrays 
# **************************
       
# This is a list of commands to be run by the show tech-support that are 
# platform-independent and have a location all option. 

#  General commands for show tech-support multicast 
command_general[0]="show version"
command_general[1]="show running-config"
command_general[2]="show ip interface brief"
command_general[3]="show install"
command_general[4]="show install inactive"
command_general[5]="show platform"
command_general[6]="show access-lists $addr_type"
command_general[7]=''

# General commands for show tech-support multicast (all nodes)
# These commands run on all nodes irrespective of location specified
command_general_all[0]="show processes aborts location all"
command_general_all[1]="show processes blocked location all" 
command_general_all[2]="show context location all"
command_general_all[3]="show memory summary location all"
command_general_all[4]=''

#  MSDP commands for show tech-support multicast
command_msdp[0]="show msdp summary"
command_msdp[1]="show msdp globals"
command_msdp[2]="show msdp sa-cache summary"
command_msdp[3]="show msdp statistics peer"
command_msdp[4]="show msdp sa-cache  all" 
command_msdp[5]="show msdp sa-cache  $group_addr_str"
command_msdp[6]=''

#  PIM commands for show tech-support multicast
command_pim[0]="show pim $vrf_name $addr_type topology $source_addr_str $group_addr_str detail private"
command_pim[1]="show pim vrf all toplogy summary" 
command_pim[2]="show pim $vrf_name $addr_type group-map"
command_pim[3]="show pim $vrf_name $addr_type topology route-count"
command_pim[4]="show pim $vrf_name $addr_type rpf"
command_pim[5]="show pim $vrf_name $addr_type rpf summary"
command_pim[6]="show pim $vrf_name $addr_type traffic"
command_pim[7]="show pim $vrf_name $addr_type join-prune statistic"
command_pim[8]="show pim $vrf_name $addr_type interface state-on detail "
command_pim[9]="show pim $vrf_name $addr_type tunnel info all"
command_pim[10]="show pim $vrf_name $addr_type neighbor"
command_pim[11]="show pim nsf"
command_pim[12]="show pim $vrf_name $addr_type summary"
command_pim[13]="show pim $vrf_name context detail private" 
command_pim[14]="show pim $vrf_name bgp-safi" 
command_pim[15]="show pim $vrf_name mdt cache summary" 
command_pim[16]="show pim $vrf_name $addr_type mdt prefix local private"
command_pim[17]="show pim $vrf_name $addr_type mdt prefix remote private"
command_pim[18]="show mvpn $vrf_name $addr_type pe"
command_pim[19]="show mvpn segmented private detail"
command_pim[20]="show mvpn $vrf_name $addr_type database ingress-replication"
command_pim[21]="show mvpn $vrf_name $addr_type database p2mp-te"
command_pim[22]="show mvpn $vrf_name $addr_type database sr-p2mp"

if [ $addr_type = "ipv4" ] ;then 
    command_pim[23]="show pim $vrf_name topology $cisco_rp_discovery_addr"
    command_pim[24]="show pim $vrf_name topology $cisco_rp_announce_addr"
    command_pim[25]=''
else
    command_pim[23]=''
fi 

# IGMP commands for show tech-support multicast 
if [ $addr_type = "ipv4" ] ;then 
    command_igmp[0]="show igmp $vrf_name groups summary" 
    command_igmp[1]="show igmp $vrf_name groups"
    command_igmp[2]="show igmp $vrf_name interface"
    command_igmp[3]="show igmp $vrf_name traffic"
    command_igmp[4]="show igmp nsf"
    command_igmp[5]="show igmp $vrf_name summary"
    command_igmp[6]=''
else
    command_mld[0]="show mld $vrf_name groups summary" 
    command_mld[1]="show mld $vrf_name groups"
    command_mld[2]="show mld $vrf_name interface"
    command_mld[3]="show mld $vrf_name traffic"
    command_mld[4]="show mld nsf"
    command_mld[5]="show mld $vrf_name summary"
    command_mld[6]=''
fi

#  MRIB commands for show tech-support multicast
command_mrib[0]="show mrib $vrf_name $addr_type client filter" 
command_mrib[1]="show mrib $vrf_name $addr_type client detail"
command_mrib[2]="show mrib $vrf_name $addr_type route summary"
command_mrib[3]="show mrib $vrf_name $addr_type route $source_addr_str $group_addr_str detail private"
command_mrib[4]="show mrib nsf"
command_mrib[5]="show mrib $addr_type encap-id private"
command_mrib[6]="show mrib $addr_type regdb private"
command_mrib[7]="show mrib evpn bucket-db private"

if [ $addr_type = "ipv4" ] ;then 
    command_mrib[8]="show mrib $vrf_name route $cisco_rp_discovery_addr"
    command_mrib[9]="show mrib $vrf_name route $cisco_rp_announce_addr"
    command_mrib[10]=''
else
    command_mrib[8]=''
fi 


#  LSM commands for show tech-support multicast
command_lsm[0]="show mrib $addr_type mpls forwarding detail"
command_lsm[1]="show mrib $addr_type mpls forwarding private"
command_lsm[2]="show mrib $addr_type label-table-info"
command_lsm[3]="show mrib $addr_type mpls traffic-eng backup database"
command_lsm[4]=''

# MLDP commands for show tech-support multicast
command_mldp[0]="show mpls mldp database detail"
command_mldp[1]="show mpls mldp neighbors detail"
command_mldp[2]="show mpls mldp root detail"
command_mldp[3]="show mpls mldp forwarding"

#  Trace commands for show tech-support multicast 

# the command_trace array has trace commands that are run once. 
command_trace[0]="show pim  $addr_type trace"
command_trace[1]="show mrib $addr_type trace"
command_trace[2]="show mrib $addr_type trace lsm"
command_trace[3]="show lmrib client trace mpls_ldp"
command_trace[4]="show mpls mldp trace"

if [ $addr_type = "ipv4" ] ;then 
    command_trace[5]="show igmp trace"
    command_trace[6]="show msdp trace"
    command_trace[7]=''
else
    command_trace[5]="show mld trace"
    command_trace[6]=''
fi 

# the command_trace_a array has trace commands that are run on all locations. 
command_trace_a[0]="show mfib $addr_type trace $node_name"
command_trace_a[1]="show mfib $addr_type trace interface $node_name"
command_trace_a[2]="show mfib $addr_type trace error $node_name"
command_trace_a[3]="show mlib $addr_type trace $node_name "
command_trace_a[4]="show mlib $addr_type trace error $node_name "
command_trace_a[5]=''

#  Forwarding commands for show tech-support multicast
command_fwd[0]="show mfib $vrf_name route summary $node_name"
command_fwd[1]="show mfib $vrf_name $addr_type route $source_addr_str $group_addr_str detail $node_name"
command_fwd[2]="show mfib $vrf_name $addr_type route $source_addr_str $group_addr_str private $node_name"
command_fwd[3]="show mfib $vrf_name $addr_type counter $node_name"
command_fwd[4]="show mfib $addr_type nsf $node_name"
command_fwd[5]="show mfib $addr_type encap identifier $node_name"
command_fwd[6]="show mfib $addr_type encap registration $node_name"

if [ $addr_type = "ipv4" ] ;then 
    command_fwd[7]="show mfib $vrf_name route $cisco_rp_discovery_addr $node_name"
    command_fwd[8]="show mfib $vrf_name route $cisco_rp_announce_addr $node_name"
    command_fwd[9]=''
else
    command_fwd[7]=''
fi 

# Platform script for show tech-support multicast 
platform__ksh[1]="show_tech_mcast_plat  $plat_opts"
platform_exec[1]="platform show tech multicast"

# This routine is called by show tech infra.
display() { 

    # Run only the platform commands if the hardware_flag is set.
    if [[ $hardware_flag -eq 1 ]] ; then 

        # initialize the platform__ksh and platform_exec arrays
        init_platform_showtech_mcast
  
        # run the platform commands 
        exec_commands platform
    else 
        run_commands _general  
        run_commands _general_all 
        run_commands _pim 
        if [ $addr_type = "ipv4" ] ;then 
            run_commands _msdp 
            run_commands _igmp 
        else 
            run_commands _mld 
        fi
        run_commands _mrib 
        run_commands _lsm
        run_commands _mldp 
        run_commands _trace 

        # For the mfwd commands  and some of the trace commands if a 
        # location is not specified we have to run it for  all locations. 
        # So we create a new array of commands specifying all locations
        if [[ $node_name = "" ]] ; then 

            # create the new array of trace_all commands
            create_location_specific_array trace_a trace_all

            # run the new array of trace_all commands
            run_commands _trace_all


            # create the new array of mfwd commands
            create_location_specific_array fwd fwd_all

            # run the new array of mfwd commands
            run_commands _fwd_all

        else 

            run_commands _trace_a  
            run_commands _fwd 
        fi 
  fi 

} # end display()

# This function calls the display() function and sends the output to file if
# the file option has been set. 
. /pkg/bin/show_tech_file_fragment
