Converting MAC address to another format

It often annoys me that I ask for MAC addresses from other people in other departments, only for them to supply them in a format that I can’t just copy and paste into my terminal for use in a script or cisco CLI. To this end, I came up with this short conversion script:

#!/bin/bash
#
# Function: MAC Address conversion tool
# Author:   sol@subnetzero.org
#
# Usage example: "echo 0000.0000.0000" | macconvert 4d2d

4dto2d () {
awk '{
print substr($0,1,2)"-"\
substr($0,3,2)"-"\
substr($0,6,2)"-"\
substr($0,8,2)"-"\
substr($0,11,2)"-"\
substr($0,13,2)}'
}

4dto4c () {
awk -F"." '{print $1":"$2":"$3}'
}

2dto4d () {
awk -F"-" '{print $1$2"."$3$4"."$5$6}'
}

2cto4d () {
awk -F":" '{print $1$2"."$3$4"."$5$6}'
}

2cto2d () {
sed 's/:/./g'
}

USAGE="$0 [4d2d] [2d4d] [2c4d] [2c2d]
4d2d = Convert xxxx.xxxx.xxxx to xx-xx-xx-xx-xx-xx
4d4d = Convert xxxx.xxxx.xxxx to xxxx:xxxx:xxxx
2d4d = Convert xx-xx-xx-xx-xx-xx to xxxx.xxxx.xxxx
2c4d = Convert xx:xx:xx:xx:xx:xx to xxxx.xxxx.xxxx
2c2d = Convert xx:xx:xx:xx:xx:xx to xx.xx.xx.xx.xx.xx

Requires input stream from redirect or pipe.
"

case $1 in
        4d2d)   4dto2d;;
        4d4c)   4dto4c;;
        2d4d)   2dto4d;;
        2c4d)   2cto4d;;
        2c2d)   2cto2d;;
        *)      echo "$USAGE";
                exit 1;;
esac

Usage… either echo the MAC addess, or create a text file with MAC addresses in it, then pipe it into this script (or redirect it into it) with the required conversion option.

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!

Writing simple games in bash

I bought a book a while ago called “Shell Scripting” by Steve Parker (ISBN 1118024486). It has some pretty impressive ideas, including an example where the author has written a version of Space Invaders in bash. Video Here!

I thought I’d have a go at writing something myself, so chose to write a version of the old “Snake” game.

snake

You can reduce the game area to make it more of a challenge by tweaking the following variables:

LASTCOL=40                              # Last col of game area
LASTROW=20                              # Last row of game area

Code below:

#!/bin/bash
#
# Centipede game
#
# v2.0
#
# Author: sol@subnetzero.org
#
# Functions

drawborder() {
   # Draw top
   tput setf 6
   tput cup $FIRSTROW $FIRSTCOL
   x=$FIRSTCOL
   while [ "$x" -le "$LASTCOL" ];
   do
      printf %b "$WALLCHAR"
      x=$(( $x + 1 ));
   done

   # Draw sides
   x=$FIRSTROW
   while [ "$x" -le "$LASTROW" ];
   do
      tput cup $x $FIRSTCOL; printf %b "$WALLCHAR"
      tput cup $x $LASTCOL; printf %b "$WALLCHAR"
      x=$(( $x + 1 ));
   done

   # Draw bottom
   tput cup $LASTROW $FIRSTCOL
   x=$FIRSTCOL
   while [ "$x" -le "$LASTCOL" ];
   do
      printf %b "$WALLCHAR"
      x=$(( $x + 1 ));
   done
   tput setf 9
}

apple() {
   # Pick coordinates within the game area
   APPLEX=$[( $RANDOM % ( $[ $AREAMAXX - $AREAMINX ] + 1 ) ) + $AREAMINX ]
   APPLEY=$[( $RANDOM % ( $[ $AREAMAXY - $AREAMINY ] + 1 ) ) + $AREAMINY ]
}

drawapple() {
   # Check we haven't picked an occupied space
   LASTEL=$(( ${#LASTPOSX[@]} - 1 ))
   x=0
   apple
   while [ "$x" -le "$LASTEL" ];
   do
      if [ "$APPLEX" = "${LASTPOSX[$x]}" ] && [ "$APPLEY" = "${LASTPOSY[$x]}" ];
      then
         # Invalid coords... in use
         x=0
         apple
      else
         x=$(( $x + 1 ))
      fi
   done
   tput setf 4
   tput cup $APPLEY $APPLEX
   printf %b "$APPLECHAR"
   tput setf 9
}

growsnake() {
   # Pad out the arrays with oldest position 3 times to make snake bigger
   LASTPOSX=( ${LASTPOSX[0]} ${LASTPOSX[0]} ${LASTPOSX[0]} ${LASTPOSX[@]} )
   LASTPOSY=( ${LASTPOSY[0]} ${LASTPOSY[0]} ${LASTPOSY[0]} ${LASTPOSY[@]} )
   RET=1
   while [ "$RET" -eq "1" ];
   do
      apple
      RET=$?
   done
   drawapple
}

move() {
   case "$DIRECTION" in
      u) POSY=$(( $POSY - 1 ));;
      d) POSY=$(( $POSY + 1 ));;
      l) POSX=$(( $POSX - 1 ));;
      r) POSX=$(( $POSX + 1 ));;
   esac

   # Collision detection
   ( sleep $DELAY && kill -ALRM $$ ) &
   if [ "$POSX" -le "$FIRSTCOL" ] || [ "$POSX" -ge "$LASTCOL" ] ; then
      tput cup $(( $LASTROW + 1 )) 0
      stty echo
      echo " GAME OVER! You hit a wall!"
      gameover
   elif [ "$POSY" -le "$FIRSTROW" ] || [ "$POSY" -ge "$LASTROW" ] ; then
      tput cup $(( $LASTROW + 1 )) 0
      stty echo
      echo " GAME OVER! You hit a wall!"
      gameover
   fi

   # Get Last Element of Array ref
   LASTEL=$(( ${#LASTPOSX[@]} - 1 ))
   #tput cup $ROWS 0
   #printf "LASTEL: $LASTEL"

   x=1 # set starting element to 1 as pos 0 should be undrawn further down (end of tail)
   while [ "$x" -le "$LASTEL" ];
   do
      if [ "$POSX" = "${LASTPOSX[$x]}" ] && [ "$POSY" = "${LASTPOSY[$x]}" ];
      then
         tput cup $(( $LASTROW + 1 )) 0
         echo " GAME OVER! YOU ATE YOURSELF!"
         gameover
      fi
      x=$(( $x + 1 ))
   done

   # clear the oldest position on screen
   tput cup ${LASTPOSY[0]} ${LASTPOSX[0]}
   printf " "

   # truncate position history by 1 (get rid of oldest)
   LASTPOSX=( `echo "${LASTPOSX[@]}" | cut -d " " -f 2-` $POSX )
   LASTPOSY=( `echo "${LASTPOSY[@]}" | cut -d " " -f 2-` $POSY )
   tput cup 1 10
   #echo "LASTPOSX array ${LASTPOSX[@]} LASTPOSY array ${LASTPOSY[@]}"
   tput cup 2 10
   echo "SIZE=${#LASTPOSX[@]}"

   # update position history (add last to highest val)
   LASTPOSX[$LASTEL]=$POSX
   LASTPOSY[$LASTEL]=$POSY

   # plot new position
   tput setf 2
   tput cup $POSY $POSX
   printf %b "$SNAKECHAR"
   tput setf 9

   # Check if we hit an apple
   if [ "$POSX" -eq "$APPLEX" ] && [ "$POSY" -eq "$APPLEY" ]; then
      growsnake
      updatescore 10
   fi
}

updatescore() {
   SCORE=$(( $SCORE + $1 ))
   tput cup 2 30
   printf "SCORE: $SCORE"
}
randomchar() {
    [ $# -eq 0 ] && return 1
    n=$(( ($RANDOM % $#) + 1 ))
    eval DIRECTION=\${$n}
}

gameover() {
   tput cvvis
   stty echo
   sleep $DELAY
   trap exit ALRM
   tput cup $ROWS 0
   exit
}

###########################END OF FUNCS##########################

# Prettier characters but not supported
# by all termtypes/locales
#SNAKECHAR="\0256"                      # Character to use for snake
#WALLCHAR="\0244"                       # Character to use for wall
#APPLECHAR="\0362"                      # Character to use for apples
#
# Normal boring ASCII Chars
SNAKECHAR="@"                           # Character to use for snake
WALLCHAR="X"                            # Character to use for wall
APPLECHAR="o"                           # Character to use for apples
#
SNAKESIZE=3                             # Initial Size of array aka snake
DELAY=0.2                               # Timer delay for move function
FIRSTROW=3                              # First row of game area
FIRSTCOL=1                              # First col of game area
LASTCOL=40                              # Last col of game area
LASTROW=20                              # Last row of game area
AREAMAXX=$(( $LASTCOL - 1 ))            # Furthest right play area X
AREAMINX=$(( $FIRSTCOL + 1 ))           # Furthest left play area X
AREAMAXY=$(( $LASTROW - 1 ))            # Lowest play area Y
AREAMINY=$(( $FIRSTROW + 1))            # Highest play area Y
ROWS=`tput lines`                       # Rows in terminal
ORIGINX=$(( $LASTCOL / 2 ))             # Start point X - use bc as it will round
ORIGINY=$(( $LASTROW / 2 ))             # Start point Y - use bc as it will round
POSX=$ORIGINX                           # Set POSX to start pos
POSY=$ORIGINY                           # Set POSY to start pos

# Pad out arrays
ZEROES=`echo |awk '{printf("%0"'"$SNAKESIZE"'"d\n",$1)}' | sed 's/0/0 /g'`
LASTPOSX=( $ZEROES )                    # Pad with zeroes to start with
LASTPOSY=( $ZEROES )                    # Pad with zeroes to start with

SCORE=0                                 # Starting score

clear
echo "
Keys:

 W - UP
 S - DOWN
 A - LEFT
 D - RIGHT
 X - QUIT

If characters do not display properly, consider changing
SNAKECHAR, APPLECHAR and WALLCHAR variables in script.
Characters supported depend upon your terminal setup.

Press Return to continue
"

stty -echo
tput civis
read RTN
tput setb 0
tput bold
clear
drawborder
updatescore 0

# Draw the first apple on the screen
# (has collision detection to ensure we don't draw
# over snake)
drawapple
sleep 1
trap move ALRM

# Pick a random direction to start moving in
DIRECTIONS=( u d l r )
randomchar "${DIRECTIONS[@]}"

sleep 1
move
while :
do
   read -s -n 1 key
   case "$key" in
   w)   DIRECTION="u";;
   s)   DIRECTION="d";;
   a)   DIRECTION="l";;
   d)   DIRECTION="r";;
   x)   tput cup $COLS 0
        echo "Quitting..."
        tput cvvis
        stty echo
        tput reset
        printf "Bye Bye!\n"
        trap exit ALRM
        sleep $DELAY
        exit 0
        ;;
   esac
done

Cisco: setup express and the potential nightmares.

Some Cisco switches these days have an annoying extra feature called express setup.

What you may find, sooner or later, is that someone does some cabling in a cabinet which then causes a cable loom or RJ45 plug to rest against the mode button on your switch. Suddenly, you’re called out at silly o’clock because the switch lost its config and is no longer functioning as expected. :(

To avoid this nightmare scenario is simple. Add this to your configuration standards documentation where applicable:

Switch(config)# no setup express

Headache averted!

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