--- /dev/null
+#!/bin/sh
+
+[ $# = 7 ] || exit 1
+
+ENDPOINT=$1
+HW=$2
+SW=$3
+COMMAND=$4
+FILE1=$5
+FILE2=$6
+FILE3=$7
+MYDIR=`dirname $0`
+ostype=`uname -s`
+case "$ostype" in
+ Linux)
+ SEDFLAG='-r'
+ ;;
+ FreeBSD)
+ SEDFLAG='-E'
+ ;;
+ *)
+ exit 7
+esac
+
+
+prepare_connect_commands()
+{
+ [ $# = 1 ] || exit 2
+ local skip=yes cval found=no
+ while read line; do
+ if [ "$skip" = "yes" -a "$line" = "# S-T-A-R-T" ]; then
+ skip=no
+ continue
+ fi
+ if [ "$skip" = "no" -a "$line" = "# S-T-O-P" ]; then
+ skip=yes
+ continue
+ fi
+ [ "$skip" = "yes" ] && continue
+ # Allow comments.
+ [ -z "${line###*}" ] && continue
+
+ # First endpoint string/regexp match is sufficient for us.
+ cval=`echo $line | cut -s -d' ' -f1`
+ if [ -z "${1##$cval}" ]; then
+ found=yes
+ # Don't be too smart at the moment, just be able to handle
+ # the known-good case ;-)
+
+ username=`echo $line | cut -s -d' ' -f5`
+ [ "$username" != "-" ] && echo $username > $CMDS1
+ # access password
+ echo $line | cut -s -d' ' -f6 >> $CMDS1
+ enable_password=`echo $line | cut -s -d' ' -f7`
+ [ "$enable_password" != "-" ] && {
+ echo en >> $CMDS1
+ echo $enable_password >> $CMDS1
+ }
+ # same for ports
+ cat "$CMDS1" > "$CMDS2"
+ # ...and MAC addresses
+ cat "$CMDS1" > "$CMDS3"
+ break
+ fi
+ done < "$MYDIR/cisco.secrets.php"
+ [ "$found" = "yes" ] && return
+ echo "E!connector could not find credentials for $1" >> "$FILE2"
+ exit 3
+}
+
+prepare_fetch_commands()
+{
+ printf 'term len 0\nshow vlan brief\nquit\n' >> $CMDS1
+ printf 'term len 0\nshow int status\nquit\n' >> $CMDS2
+ printf 'term len 0\nshow mac-address-table\nquit\n' >> $CMDS3
+}
+
+prepare_push_commands()
+{
+ printf 'term len 0\nconf t\n' >> $CMDS1
+ while read portname vlanid; do
+ if [ -z "$portname" -o -z "$vlanid" ]; then
+ echo "E!could not parse input in connector" >> "$FILE2"
+ continue
+ fi
+ if [ "$vlanid" = "trunk" ]; then
+ echo "E!trunking is not allowed" >> "$FILE2"
+ continue
+ fi
+ printf "int $portname\n" >> $CMDS1
+ if [ $vlanid -lt 4096 ]; then
+ printf "no description\n" >> $CMDS1
+ else
+ printf "descr VLAN$vlanid\n" >> $CMDS1
+ fi
+ "$MYDIR/vlandecoder" $vlanid >> $CMDS1
+ printf "exit\n" >> $CMDS1
+ echo "C!64!$portname!$ENDPOINT!$vlanid" >> "$FILE2"
+ done < "$FILE1"
+ printf "end\nwri\nquit\n" >> $CMDS1
+}
+
+do_fetch()
+{
+ local tmp_ifname tmp_ifdescr tmp_status tmp_vlanid
+ nc $ENDPOINT 23 < $CMDS1 > "$OUT1"
+ if fgrep -q '% Bad passwords' "$OUT1"; then
+ echo "E!password mismatch while trying to connect to $ENDPOINT" >> "$FILE2"
+ exit 4
+ fi
+ nc $ENDPOINT 23 < $CMDS2 > "$OUT2a"
+ nc $ENDPOINT 23 < $CMDS3 > "$OUT3"
+ cat "$OUT1" | fgrep ' active ' | sed $SEDFLAG 's/^([[:digit:]]+)[[:space:]]+(.+)[[:space:]]+active (.*)/\1=\2/;s/[[:space:]]+$//' > $FILE1
+ # Add trunk data, if appropriate.
+ [ -s "$MYDIR/vlantable" ] && cat "$MYDIR/vlantable" >> $FILE1
+
+ # First extract structured info about VLAN membership, then map
+ # special descriptions into VLAN IDs.
+ cat "$OUT2a" | egrep '^(Et|Fa|Gi|Te)' | sed $SEDFLAG 's/[~%]/__RTTMP_percent_sign__/g;s/^([A-Za-z/0-9]+) +(.*) +(connected|notconnect|disabled|err-disabled|monitoring|suspended) +/\1~\2%\3%/;s/%(trunk|routed|([0-9]+)) .*$/%\1/;s/%(monitoring|suspended)%/%connected%/;s/%(err-disabled)%/%disabled%/;s/ +%/%/;s/~/%/' > $OUT2b
+ while read line; do
+ tmp_ifname=`echo $line | cut -d% -f1`
+ tmp_ifdescr=`echo $line | cut -d% -f2`
+ tmp_status=`echo $line | cut -d% -f3`
+ tmp_vlanid=`echo $line | cut -d% -f4`
+ # If the port has a description pretending to be a martian VLAN, map it onto the VLAN ID.
+ if [ -n "$tmp_ifdescr" -a -z "${tmp_ifdescr##VLAN*}" ]; then
+ tmp_vlanid=${tmp_ifdescr##VLAN}
+ fi
+ echo "$tmp_ifname=$tmp_status,$tmp_vlanid" >> $FILE2
+ done < $OUT2b
+ # FIXME
+ # Here we need to distinguish between different platforms and IOS version,
+ # cause they produce output in different formats.
+ if [ "$SW" = "Cisco+IOS+12.0" ]; then
+ cat "$OUT3" | LC_ALL=C tr -d '\r' | fgrep Dynamic | sed $SEDFLAG 's/ +Dynamic +([0-9]+) +(.+)/=\1@\2/;s/FastEthernet/Fa/;s/GigabitEthernet/Gi/' > "$FILE3"
+ elif [ "$SW" = "Cisco+IOS+12.2" -o "$SW" = "Cisco+IOS+12.1" ]; then
+ case "$HW" in
+ Cisco+Catalyst+35*|Cisco+Catalyst+37*|Cisco+Catalyst+29*)
+ cat "$OUT3" | LC_ALL=C tr -d '\r' | egrep 'STATIC|DYNAMIC' | \
+ sed $SEDFLAG 's/ +([0-9]+|All) +(.+) (DYNAMIC|STATIC) +(.+)/\2=\1@\4/;s/FastEthernet/Fa/;s/GigabitEthernet/Gi/' > "$FILE3"
+ ;;
+ Cisco+Catalyst+49*)
+ cat "$OUT3" | LC_ALL=C tr -d '\r' | fgrep dynamic | \
+ sed $SEDFLAG 's/ +([0-9]+) +([0-9a-f\.]+) dynamic ip +([a-zA-Z/0-9]+) */\2=\1@\3/;s/FastEthernet/Fa/;s/GigabitEthernet/Gi/;s/TenGi/Te/' > "$FILE3"
+ ;;
+ esac
+ fi
+}
+
+do_push()
+{
+ nc $ENDPOINT 23 < $CMDS1 >/dev/null
+}
+
+remove_tempfiles()
+{
+ [ -f "$CMDS1" ] && rm -f "$CMDS1"
+ [ -f "$CMDS2" ] && rm -f "$CMDS2"
+ [ -f "$CMDS3" ] && rm -f "$CMDS3"
+ [ -f "$OUT1" ] && rm -f "$OUT1"
+ [ -f "$OUT2a" ] && rm -f "$OUT2a"
+ [ -f "$OUT2b" ] && rm -f "$OUT2b"
+ [ -f "$OUT3" ] && rm -f "$OUT3"
+}
+
+create_tempfiles()
+{
+ # This one is for VLAN list.
+ CMDS1=`mktemp /tmp/cisco.connector.XXXX`
+ # And this one holds ports list...
+ CMDS2=`mktemp /tmp/cisco.connector.XXXX`
+ # ...and one more for MAC address table
+ CMDS3=`mktemp /tmp/cisco.connector.XXXX`
+ # The following are buffers to hold the whole switch output
+ # before filtering.
+ OUT1=`mktemp /tmp/cisco.connector.XXXX`
+ OUT2a=`mktemp /tmp/cisco.connector.XXXX`
+ OUT2b=`mktemp /tmp/cisco.connector.XXXX`
+ OUT3=`mktemp /tmp/cisco.connector.XXXX`
+ [ -f "$CMDS1" -a -f "$CMDS2" -a -f "$CMDS3" -a -f "$OUT1" -a -f "$OUT2a" -a -f "$OUT2b" -a -f "$OUT3" ] && return
+ echo "E!connector cannot create tempfiles" >> "$FILE2"
+ remove_tempfiles
+ exit 5
+}
+
+case $COMMAND in
+ fetch)
+ create_tempfiles
+ prepare_connect_commands $ENDPOINT
+ prepare_fetch_commands
+ do_fetch
+ remove_tempfiles
+ ;;
+ push)
+ create_tempfiles
+ prepare_connect_commands $ENDPOINT
+ prepare_push_commands
+ do_push
+ remove_tempfiles
+ ;;
+ *)
+ echo "E!unknown command for connector" >> "$FILE2"
+ exit 6
+ ;;
+esac
+
+exit 0
--- /dev/null
+<?php
+echo "Unauthorized access attempt has been logged. Cheers.";
+exit();
+/*
+
+# Syntax:
+# <endpoint|*> <telnet> <hostname|-> <port|-> <username|-> <line password> <enable password>
+# FIXME: <endpoint|*> <rsh> <username>
+
+# S-T-A-R-T
+switch1 telnet - - - password2 enablepassword
+switch2 telnet - - - password enablepassword
+switch3 telnet - - username3 password3 enablepassword3
+# S-T-O-P
+
+*/
+?>
--- /dev/null
+#!/bin/sh
+
+# This is a RackTables gateway for changing switch ports membership
+# across VLANs. It works accordingly to the gateway protocol described
+# in gateways.php and accepts the following commands on its stdin:
+#
+# * connect: connect to a switch, fetch all necessary data, store and
+# disconnect
+#
+# * listvlans: list all VLANs found on the switch, propably filtering
+# out those administratively prohibited. Only the VLANs from this
+# list will be allowed as new destination for 'set' command.
+#
+# * listports: list all ports on the switch and their current status.
+# Untagged (switchport mode access) ports will be shown with their
+# VLAN ID and tagged ports will be shown as 'trunk' regardless of
+# how many VLANs they are members of.
+#
+# * listmacs: output unsorted list of all dynamically learned MAC
+# addresses present on the switch
+#
+
+endpoint=
+hw=
+sw=
+user=
+handler=
+CONNECTED=0
+MYDIR=`dirname $0`
+
+decode_error()
+{
+ case "$1" in
+ 0)
+ echo -n 'success'
+ ;;
+ 1)
+ echo -n 'internal error 1'
+ ;;
+ 2)
+ echo -n 'internal error 2'
+ ;;
+ 3)
+ echo -n 'password not found'
+ ;;
+ 4)
+ echo -n 'invalid password'
+ ;;
+ 5)
+ echo -n 'cannot create temporary files'
+ ;;
+ 6)
+ echo -n 'invalid command'
+ ;;
+ 7)
+ echo -n 'unknown host OS'
+ ;;
+ *)
+ echo -n 'unknown error'
+ ;;
+ esac
+}
+
+# Not only connect, but gather all the data at once and remember the context.
+do_connect()
+{
+ endpoint=`echo $args | cut -s -d' ' -f1`
+ hw=`echo $args | cut -s -d' ' -f2`
+ sw=`echo $args | cut -s -d' ' -f3`
+ user=`echo $args | cut -s -d' ' -f4`
+ # sanity checks
+ if [ -z "$endpoint" -o -z "$hw" -o -z "$sw" -o -z "$user" ]; then
+ echo 'ERR!too few arguments to connect'
+ return
+ fi
+ case "$sw" in
+ Cisco+IOS+12.0|Cisco+IOS+12.1|Cisco+IOS+12.2)
+ handler=cisco
+ ;;
+ *)
+ echo "ERR!Don't know how to handle $sw on $endpoint"
+ return
+ ;;
+ esac
+
+ # prepare temp files
+ PORTINFO=`mktemp /tmp/racktables.XXXX`
+ if ! [ -f "$PORTINFO" ]; then
+ echo 'ERR!could not create portinfo tmpfile'
+ return
+ fi
+ VLANINFO=`mktemp /tmp/racktables.XXXX`
+ if ! [ -f "$VLANINFO" ]; then
+ echo 'ERR!could not create vlaninfo tmpfile'
+ rm -f "$PORTINFO"
+ return
+ fi
+ MACINFO=`mktemp /tmp/racktables.XXXX`
+ if ! [ -f "$MACINFO" ]; then
+ echo 'ERR!could not create MACinfo tmpfile'
+ rm -f "$PORTINFO" "$VLANINFO"
+ return
+ fi
+
+ # get the data
+ "$MYDIR/$handler.connector" $endpoint $hw $sw fetch "$VLANINFO" "$PORTINFO" "$MACINFO"
+ ret=$?
+ if [ $ret = 0 ]; then
+ CONNECTED=1
+ echo "OK!connected to $endpoint";
+ else
+ echo -n "ERR!Cannot connect to $endpoint ("
+ decode_error $ret
+ echo ')'
+ fi
+}
+
+do_listfile()
+{
+ local F=$1
+ if ! [ -f "$F" ]; then
+ echo "ERR!Lost temp file '$F' on the way"
+ return
+ fi
+ echo -n 'OK!'
+ local semicolon=''
+ # tr might do the work, but use our chance to perform filtering once more
+ cat "$F" | while read line; do
+ [ "$line" = "" ] && continue
+ echo -n "$semicolon$line"
+ semicolon=';'
+ done
+ echo
+}
+
+do_set()
+{
+ # sanity checks
+ local setline=$1
+ if [ -z "$setline" ]; then
+ echo 'ERR!missing set argument'
+ return
+ fi
+ local REQUESTS=`mktemp /tmp/racktables.XXXX`
+ local REPLIES=`mktemp /tmp/racktables.XXXX`
+ echo $1 | tr ';' '\n' | while read setexpr; do
+ portname=`echo $setexpr | cut -s -d'=' -f1`
+ newvlan=`echo $setexpr | cut -s -d'=' -f2`
+ curvlan=`egrep "^$portname=" $PORTINFO | cut -s -d'=' -f2 | cut -d',' -f2`
+ if [ -z "$curvlan" ]; then
+ echo "C!167!$portname" >> "$REPLIES"
+ continue
+ fi
+ if [ "$curvlan" = "trunk" ]; then
+ echo "C!168!$portname" >> "$REPLIES"
+ continue
+ fi
+ [ "$curvlan" = "$newvlan" ] && continue
+ echo "$portname $newvlan" >> "$REQUESTS"
+ cmembers=`grep -c ",$newvlan$" "$PORTINFO"`
+ if [ "$cmembers" = "0" -a $newvlan -lt 4096 ]; then
+ echo "C!203!$portname!$newvlan" >> "$REPLIES"
+ echo "C!204" >> "$REPLIES"
+ fi
+ done
+ nr=`egrep -c '^C!1.' "$REPLIES"`
+ if [ "$nr" -ge 1 ]; then
+ echo "C!205!$nr" >> "$REPLIES"
+ fi
+
+ nq=`egrep -c '^.' "$REQUESTS"`
+ if [ "$nq" -ge 1 ]; then
+ # Go!
+ "$MYDIR/$handler.connector" $endpoint $hw $sw push "$REQUESTS" "$REPLIES" "$MACINFO"
+ local ret=$?
+
+ if [ $ret != 0 ]; then
+ echo "C!169!$endpoint!$ret"
+ return
+ fi
+ echo "C!63!$nq" >> "$REPLIES"
+ fi
+ echo -n 'OK!'
+ local SEMICOLON=
+ while read reply; do
+ echo -n $SEMICOLON$reply
+ SEMICOLON=';'
+ timestamp=`date '+%Y-%m-%d %H:%M:%S'`
+ [ -w "$MYDIR/changes.log" ] && echo "$timestamp $user@$endpoint $reply" >> "$MYDIR/changes.log"
+ done < "$REPLIES"
+ echo
+ rm -f "$REQUESTS" "$REPLIES"
+}
+
+# main loop
+while read cmd args; do
+ case $cmd in
+ connect)
+ if [ $CONNECTED = 1 ]; then
+ echo 'ERR!Already connected'
+ else
+ do_connect $args
+ fi
+ ;;
+ listvlans)
+ if [ $CONNECTED = 1 ]; then
+ do_listfile "$VLANINFO"
+ else
+ echo 'ERR!Not connected'
+ fi
+ ;;
+ listports)
+ if [ $CONNECTED = 1 ]; then
+ do_listfile "$PORTINFO"
+ else
+ echo 'ERR!Not connected'
+ fi
+ ;;
+ listmacs)
+ if [ $CONNECTED = 1 ]; then
+ do_listfile "$MACINFO"
+ else
+ echo 'ERR!Not connected'
+ fi
+ ;;
+ set)
+ if [ $CONNECTED = 1 ]; then
+ do_set $args
+ else
+ echo 'ERR!Not connected'
+ fi
+ ;;
+ *)
+ echo "ERR!unknown command $cmd"
+ esac
+done
+
+rm -f "$PORTINFO" "$VLANINFO" "$MACINFO"
+exit 0