Spoofing SNMP Traps for testing

Somehow I missed the fact that JunOS allows you to spoof SNMP traps. I discovered this recently and must say it’s very handy, especially when testing new NNMi or other NMS incident configurations. It helpfully populates the varbinds for you with preset values (although you can specify them if desired).

This is done as follows from the JunOS command line:

user@SRX> request snmp spoof-trap ospfNbrStateChange variable-bindings "ospfNbrState = 8"

You can look up varbind names in the appropriate MIB.

A bit easier than the traditional equivalent in shell:

/usr/bin/snmptrap -v 2c -c mycommunity nms.mydomain.com:162 '' .1.3.6.1.2.1.14.16.2.0.2 \
.1.3.6.1.2.1.14.1.1 a 0.0.0.0 \
.1.3.6.1.2.1.14.10.1.1 a "1.2.3.4" \
.1.3.6.1.2.1.14.10.1.2 i "0" \
.1.3.6.1.2.1.14.10.1.3 a "2.3.4.5" \
.1.3.6.1.2.1.14.10.1.6 i "8"

Drawing ASCII graphs!

I found these while digging through some old scripts of mine and thought they were quite cool. There may be some bad old habits in them as I’ve not had time to rewrite them completely, but I had a quick tidy up and thought they were worth sharing.

Each script takes in a csv file as input and “plots” the values as an ASCII graph in the terminal. It’s questionable how useful this actually is, but it’s a bit of fun at least. The SCALE variable controls the amount of characters used for the width of the plot area (not including the table containing labels and values).

Simple: Plot Values of each item

input file: sales

january,140
february,29
march,26
april,54
may,72
june,86

output: ./vgraph sales

 Relative Value Chart

Name      Value (Max is 140)
____________________0_________._________|_________._________|100% (140)
january      140    |========================================
february     29     |========
march        26     |=======
april        54     |===============
may          72     |====================
june         86     |========================

Percentage graph using value and max for each item

input file: stock-list

apples,20,100
pears,40,50
cherries,100,150
mangoes,45,85
tomatoes,30,30

output: ./pgraph stock-list

 Percentage Chart
____________________0_________._________|_________._________|100%
apples       ( 20%) |========
pears        ( 80%) |================================
cherries     ( 66%) |==========================
mangoes      ( 52%) |=====================
tomatoes     (100%) |========================================

And the scripts…. They are both very similar, just some minor tweaks to make them do what I wanted.

vgraph

#!/bin/bash
#
# Value Graph (vgraph)
# Basic ASCII Graphing Tool
#
# Plot values and scale to max
#
# CSV format: Name, Value
#
# Awk isn't perfect at rounding.. .5 rounds down
#
# v1.1 sol@subnetzero.org

if [ -z $1 ]; then
        printf "Usage: pgraph [datafile]\n"
        exit 1
fi

# Set Vars
# FILLER and ENDDELIM are used for drawing bars.
ENDDELIM="="
FILLER="="
SCALE=40
INPUTFILE=$1
NAME=(`awk -F"," '{print $1}' < "$INPUTFILE"`)
TOTAL=(`awk -F"," '{print $2}' < "$INPUTFILE"`)

# Get Max qty for scaling
MAXQTY=0
for VALUE in ${TOTAL[*]}
do
        if [ "$VALUE" -gt "$MAXQTY" ]; then
                MAXQTY=$VALUE
        fi
done

# Make graph header and markings
printf "\n Relative Value Chart\n"
printf "\nName      Value (Max is $MAXQTY)\n"
printf "____________________0"
QTRSCALE=`echo "$SCALE / 4" | bc -l | awk '{printf("%.0f",$0)}'`
HALFSCALE=`echo "$SCALE / 2" | bc -l | awk '{printf("%.0f",$0)}'`
THRSCALE=`echo "$SCALE * 0.75" | bc -l | awk '{printf("%.0f",$0)}'`
LCNT=1
while [ "$LCNT" -le "$SCALE" ];
do
        case $LCNT in
                $QTRSCALE)      printf ".";;
                $HALFSCALE)     printf "|";;
                $THRSCALE)      printf ".";;
                $SCALE)         printf "|100%% ($MAXQTY)\n";;
                *)              printf "_";;
        esac
        LCNT=$(( $LCNT + 1 ))
done

# Draw graph bars
i=0
for ITEM in ${NAME[*]}
do
        # Print Category name in format along with info and bars
        LENGTH=`echo "scale=2;(( ${TOTAL[$i]} / $MAXQTY ) * $SCALE )" |\
                bc |\
                awk '{printf("%.0f",$0)}'`
        printf "%-12.12s %-6.6s |" "$ITEM" "${TOTAL[$i]}"
        BLOCKS=""
        while [ "$LENGTH" -gt "0" ]; do
                if [ "$LENGTH" -eq "1" ]; then
                        BLOCKS="$BLOCKS$ENDDELIM"
                else
                        BLOCKS="$BLOCKS$FILLER"
                fi
                LENGTH=$(( $LENGTH - 1 ))
        done
        printf "$BLOCKS\n"
        i=$(( $i + 1 ))
done
printf "\n\n"

pgraph

#!/bin/bash
#
# PercentageGraph (pgraph)
# Basic ASCII Graphing Tool
#
# CSV format: Name,Used,Total (or Maximum)
#
# Awk isn't perfect at rounding.. .5 rounds down
#
# v1.1 sol@subnetzero.org

if [ -z $1 ]; then
        printf "Usage: pgraph [datafile]\n"
        exit 1
fi

# Set Vars
# FILLER and ENDDELIM are used for drawing bars.
ENDDELIM="="
FILLER="="
SCALE=40
INPUTFILE=$1
NAME=(`awk -F"," '{print $1}' < "$INPUTFILE"`)
USED=(`awk -F"," '{print $2}' < "$INPUTFILE"`)
TOTAL=(`awk -F"," '{print $3}' < "$INPUTFILE"`)

# Get Max qty for scaling
MAXQTY=0
for VALUE in ${TOTAL[*]}
do
        if [ "$VALUE" -gt "$MAXQTY" ]; then
                MAXQTY=$VALUE
        fi
done

echo "max is $MAXQTY"

# Make graph header and markings
printf "\n Percentage Chart\n"
printf "____________________0"
QTRSCALE=`echo "$SCALE / 4" | bc -l | awk '{printf("%.0f",$0)}'`
HALFSCALE=`echo "$SCALE / 2" | bc -l | awk '{printf("%.0f",$0)}'`
THRSCALE=`echo "$SCALE * 0.75" | bc -l | awk '{printf("%.0f",$0)}'`
LCNT=1
while [ "$LCNT" -le "$SCALE" ];
do
        case $LCNT in
                $QTRSCALE)      printf ".";;
                $HALFSCALE)     printf "|";;
                $THRSCALE)      printf ".";;
                $SCALE)         printf "|100%%\n";;
                *)              printf "_";;
        esac
        LCNT=$(( $LCNT + 1 ))
done

# Draw graph bars
i=0
for ITEM in ${NAME[*]}
do
        # Print Category name in format along with info and bars
        LENGTH=`echo "scale=2;(( ${USED[$i]} / ${TOTAL[$i]} ) * $SCALE )" |\
                bc | \
                awk '{printf("%.0f",$0)}'`
        PCT=`echo "scale=2;(( ${USED[$i]} / ${TOTAL[$i]} ) * 100)" |\
             bc |\
             awk '{printf("%.0f",$0)}'`
        printf "%-12.12s (%3.3s%%) |" "$ITEM" "$PCT"
        BLOCKS=""
        while [ "$LENGTH" -gt "0" ]; do
                if [ "$LENGTH" -eq "1" ]; then
                        BLOCKS="$BLOCKS$ENDDELIM"
                else
                        BLOCKS="$BLOCKS$FILLER"
                fi
                LENGTH=$(( $LENGTH - 1 ))
        done
        printf "$BLOCKS\n"
        i=$(( $i + 1 ))
done
printf "\n\n"

Measuring milliseconds elapsed in shell scripts.

Interesting problem.

How can you measure the time elapsed to execute a command in seconds and milliseconds?

Well, it turns out there are readily available tools to do this, and I only found this out after doing it the hard way (ie: writing something that was quickly made unnecessary and obsolete). :/

[solm@testbox ~]$ time sleep 2

real    0m2.008s
user    0m0.000s
sys     0m0.001s

The problem here is that the output doesn’t seem to be “catchable” easily – it seems to write the results to stderr looking at an strace but you can’t catch it with a simple pipe. Example:

[sol@testbox ~]$ time sleep 2 2>&1 | awk '{print $2}'

real    0m2.005s
user    0m0.000s
sys     0m0.006s

Strange… but launch in a subshell and you can catch it…

[manwaringm@neptune ~]$ ( time sleep 2 ) 2>&1 | awk '{print $2}'

0m2.003s
0m0.000s
0m0.002s

Enough to drive you nuts…

Anyway… before I knew about time (and times), I did it the hard way! Because up until recently I didn’t even know %N existed for the date command. :D

The snippet shown below is a little crude, but it works. The sleep command is in place just as an example, but could be anything useful that you want a transaction turnaround measurement on…

#!/bin/sh
# Start Timestamp
STARTTIME=`date +%s.%N`

# Commands here (eg: TCP connect test or something useful)
sleep 2.02

# End timestamp
ENDTIME=`date +%s.%N`

# Convert nanoseconds to milliseconds
# crudely by taking first 3 decimal places
TIMEDIFF=`echo "$ENDTIME - $STARTTIME" | bc | awk -F"." '{print $1"."substr($2,1,3)}'`
echo "Time diff is: $TIMEDIFF"

Running the script yields a result that’s close enough for most applications. A little extra delay is incurred from running the date command itself.

[sol@testbox ~]$ ./timediffms
Time diff is: 2.023

Create a file with a timestamp in the past

Interesting one this. I’d not come across this particular option (-t) with touch before. I’d always used perl for time and date stuff as well until I discovered the -d and %s options for date some time ago. It makes it pretty easy to do calculations without having to rely on perl.

You can see how these work below:

[sol@testbox ~]$ date +%s
1367139219
[sol@testbox ~]$ date -d@1367139219
Sun Apr 28 08:53:39 UTC 2013

Anyway, to fulfil the requirement of creating a file with a previous timestamp, set SECSAGO to the amount of seconds ago the timestamp should be in relation to the current time. eg: 600 = 10 mins.

#!/bin/sh
SECSAGO=600
FILENAME=test1.txt
PREVEPOCHTIME=`date +%s" -$SECSAGO" | bc`
PREVHUMANTIME=`date -d @$PREVEPOCHTIME +%Y%m%d%H%M.%S`
echo "Touching $FILENAME with timestamp of $PREVHUMANTIME"
touch -t $PREVHUMANTIME $FILENAME

EDIT: Thanks to “Shell Scripting” on Facebook, I discovered it’s possible to do:

touch -t '10 minutes ago'

I honestly thought that ’10 minutes ago’ was not literal and didn’t even think it’d work until I tried it!

Generate a range of IP addresses from a shell script.

Sometimes you need to generate a range of IP addresses to run through a command or shell script. Typically, this will be ping, nslookup, or something similar. Fortunately nmap accepts CIDR notation but many older commands don’t!

Excel quickly proves to be a pain in the rectum as far as this is concerned.

This script allows you to specify a start and end IP address, and will generate a sequential list.

#!/bin/bash
#
# Function: Generate a range of IP Addresses
# Version:  1.1
# Author:   sol@subnetzero.org
#
# v1.1
#
# Convention for octets:  A.B.C.D

# Variable setup section...
#
# Split up IP addresses into seperate variables for each octet
IPLO=(`echo "$1" | awk '{split($1,a,"."); print a[1]" "a[2]" "a[3]" "a[4]}'`)
IPHI=(`echo "$2" | awk '{split($1,a,"."); print a[1]" "a[2]" "a[3]" "a[4]}'`)
#
# Put array contents into nicely named vars for less confusion
#
OCTA=${IPLO[0]}
OCTB=${IPLO[1]}
OCTC=${IPLO[2]}
OCTD=${IPLO[3]}
OCTAHI=${IPHI[0]}
OCTBHI=${IPHI[1]}
OCTCHI=${IPHI[2]}
OCTDHI=${IPHI[3]}
OCTDMAX=255             # Max default value for D Octet to loop to
FINISHED=0              # Variable used for loop state checking

# Syntax sanity check; check all vars are populated etc
for i in 0 1 2 3
do
        if [ -z "${IPLO[$i]}" ] || [ -z "${IPHI[$i]}" ]; then
                echo "Usage: $0 [from ip] [to ip]"
                exit 1
        elif [ "${IPLO[$i]}" -gt "255" ] || [ "${IPHI[$i]}" -gt "255" ];then
                echo "One of your values is broken (greater than 255)."
                exit 1
        fi

done

# Until FINISHED variable is set to 1, loop the loop.
# FINISHED var is used to determine when done as the increments
# to the vars in the loop will mean that when we get to the done
# statement, the values won't be the same as printed.
#
# The D Octet loop is the heart of this script, as it's easiest
# to check whether we've reached the target or not from here.
#
until [ "$FINISHED" -eq "1" ];
do
        # If first 3 octets match hi values, use OCTDHI as max value for 4th octet.
        if [ "$OCTA" -eq "$OCTAHI" ] && \
           [ "$OCTB" -eq "$OCTBHI" ] && \
           [ "$OCTC" -eq "$OCTCHI" ]; then
                OCTDMAX=$OCTDHI
        fi

        # Loop octet D up to OCTDMAX 255 unless above criteria satisfied
        while [ "$OCTD" -le "$OCTDMAX" ];
        do
                # Print out current IP
                printf "$OCTA.$OCTB.$OCTC.$OCTD\n"
                # Check if this is the last IP to print
                if [ "$OCTA" -eq "$OCTAHI" ] && \
                   [ "$OCTB" -eq "$OCTBHI" ] && \
                   [ "$OCTC" -eq "$OCTCHI" ] && \
                   [ "$OCTD" -eq "$OCTDHI" ]; then
                        FINISHED=1
                fi
                OCTD=$(( $OCTD + 1 ))
        done

        # Now D loop has completed, set C + 1 and reset D to 0
        OCTC=$(( $OCTC + 1 ))
        OCTD="0"

        # if C is over 255 then set B + 1 and reset C to 0
        if [ "$OCTC" -gt "255" ]; then
                OCTB=$(( $OCTB + 1 ))
                OCTC="0"
        fi

        # If B is over 255 then set A + 1 and reset B to 0
        if [ "$OCTB" -gt "255" ]; then
                OCTA=$(( $OCTA + 1 ))
                OCTB="0"
        fi

        # If A is over 255 for whatever reason then set FINISHED=1
        if [ "$OCTA" -gt "255" ]; then
                FINISHED=1
        fi
done
exit 0