Cisco IOU Automation from the Console

I thought about publishing the article for a long time, and when I finally decided, I dedicated it to the interesting Cisco IOU emulator. Or rather, to the start-up phase of any complex circuit, without resorting to graphical tools such as GNS3 or IOU Web.

I won’t invent a bicycle and take @Avtandiko ’s article Raising the Simplified Provider Network at Home” as a basis , especially in the author’s comments they asked to share a stand.

The author used the images i86bi_linux-adventerprisek9-ms.152-4.M1 and i86bi_linux_l2-ipbasek9-ms.jan24-2013-B, but, in fact, this is not the point. I would like to offer an alternative, unfortunately, only works in ArchLinux. This is to build your IOU package.

Of the advantages: there is no need to carry images with you, a unified approach to launching different stands. I won’t tell in detail how to collect packages in ArchLinux, there is enough information on the Internet about this. I will give only PKBUILD and install. Additional components iou2net.pl, CiscoIOUKeygen.py, IOUsniffer, wrapper-linux are freely available.

PKBUILD
pkgname=iou
pkgver=1.2
pkgrel=1
pkgdesc="Cisco IOU"
arch=("any")
url="http://www.cisco.com/"
license=("custom")
depends=("libpcap"
         "cryptsetup"
         "lib32-openssl"
         "perl-net-pcap")
options=("emptydirs")
install="${pkgname}.install"
source=("${pkgname}.install"
        "iou2net.pl"
        "CiscoIOUKeygen.py"
        "IOUsniffer.tar.gz"
        "wrapper-linux.tar.gz"
        "i86bi_linux-adventerprisek9-ms.152-2.15.T"
        "i86bi_linux_l2-ipbasek9-ms.jan24-2013-team_track")
build() {
 cd "${srcdir}/IOUsniffer"    && make || return 1
 cd "${srcdir}/wrapper-linux" && make || return 1
}
package() {
 install -Dm 0755 "${srcdir}/iou2net.pl" "${pkgdir}/usr/bin/iou-live"
 install -Dm 0755 "${srcdir}/CiscoIOUKeygen.py" "${pkgdir}/usr/bin/iou-keygen"
 install -Dm 0755 "${srcdir}/IOUsniffer/iousniff" "${pkgdir}/usr/bin/iou-sniff"
 install -Dm 0755 "${srcdir}/wrapper-linux/wrapper-linux" "${pkgdir}/usr/bin/iou-wrapper"
 install -Dm 0755 "${srcdir}/i86bi_linux-adventerprisek9-ms.152-2.15.T" "${pkgdir}/usr/bin/iou-router"
 install -Dm 0755 "${srcdir}/i86bi_linux_l2-ipbasek9-ms.jan24-2013-team_track" "${pkgdir}/usr/bin/iou-switch"
 install -dm 0755 "${pkgdir}/usr/lib32/"
 ln -s "/usr/lib32/libcrypto.so.1.0.0" "${pkgdir}/usr/lib32/libcrypto.so.4"
}
md5sums=('SKIP'
         'SKIP'
         'SKIP'
         'SKIP'
         'SKIP'
         'SKIP'
         'SKIP')


iou.install
post_install() {
  grep xml.cisco.com /etc/hosts > /dev/null
  if [ $? -ne 0 ]
  then
    echo '127.0.0.127 xml.cisco.com' >> /etc/hosts
  fi
  /usr/bin/iou-keygen
}
post_upgrade() {
  grep xml.cisco.com /etc/hosts > /dev/null
  if [ $? -ne 0 ]
  then
    echo '127.0.0.127 xml.cisco.com' >> /etc/hosts
  fi
}
post_remove() {
  grep xml.cisco.com /etc/hosts > /dev/null
  if [ $? -ne 1 ]
  then
    sed -i '/xml.cisco.com/d' /etc/hosts
  fi
}


Next, let's move on to the scheme. Let me remind you (the image of the network diagram is taken from the above article):



The most meticulous step in this process is to compile NETMAP correctly.

NETMAP
1: 0/1 3: 0/1 1
2: 0/1 3: 0/2 1
2: 0/3 5: 1/1 1
2: 0/2 7: 0/0 1
2: 0/0 6 : 0/1 1
2: 1/0 15: 0/0 1
6: 1/0 15: 0/1 1
5: 1/0 6: 0/2 1
6: 0/3 7: 0/1 1
6 : 0/0 8: 0/0 1
3: 0/0 4: 0/0 1
3: 0/3 5: 0/0 1
4: 0/2 5: 1/2 1
5: 0/3 7: 1/0 1
7: 0/2 8: 0/2 1
8: 0/1 12: 0/0 1
4: 0/1 9: 0/0 1
5: 0/1 10: 0/0 1
5: 0/2 11: 0/0 1
7: 0/3 11: 0/1 1
9: 0/1 10: 0/2 1
9: 0/2 13: 0/0 1
10: 0/1 13: 0 / 1 1
11: 0/2 14: 0/0 1

Basically, nothing new, except for the number “1” at the end of each line. It is necessary for IOUSneffer, indicates the type of connection. In this case, Ethernet.

Well, the most interesting thing at the end is the stand launch script itself:

run.sh
#!/bin/bash
WRAPPER="/usr/bin/iou-wrapper"
ROUTER="/usr/bin/iou-router"
SWITCH="/usr/bin/iou-switch"
IOUSNIFF="/usr/bin/iou-sniff"
DUMPDIR="./dump"
DUMPDIRTMP="${DUMPDIR}/tmp"
DUMPFILE="${DUMPDIR}/dump-$(date +%F-%T).pcap"
DELAY="5"
MODE=${1:-"--help"}
#         Name     Type   ID     Port      Serial  Ethernet
DEVICES=( "R-01"   "R"    "1"    "10001"   "0"     "1"
          "R-02"   "R"    "2"    "10002"   "0"     "2"
          "R-03"   "R"    "3"    "10003"   "0"     "1"
          "R-04"   "R"    "4"    "10004"   "0"     "1"
          "R-05"   "R"    "5"    "10005"   "0"     "2"
          "R-06"   "R"    "6"    "10006"   "0"     "2"
          "R-07"   "R"    "7"    "10007"   "0"     "2"
          "R-08"   "R"    "8"    "10008"   "0"     "1"
          "R-09"   "R"    "9"    "10009"   "0"     "1"
          "R-10"   "R"    "10"   "10010"   "0"     "1"
          "R-11"   "R"    "11"   "10011"   "0"     "1"
          "SW-12"  "S"    "12"   "10012"   "0"     "1"
          "SW-13"  "S"    "13"   "10013"   "0"     "1"
          "SW-14"  "S"    "14"   "10014"   "0"     "1"
          "R-15"   "R"    "15"   "10015"   "0"     "1"
)
start-iou() {
    for ((index=0; index < ${#DEVICES[@]}/6; index++))
    do
        if [ "X_${DEVICES[${index}*6+1]}" == "X_R" ]
        then
            ${WRAPPER} -m ${ROUTER} -p ${DEVICES[${index}*6+3]} -- \
                                    -s ${DEVICES[${index}*6+4]} \
                                    -e ${DEVICES[${index}*6+5]} ${DEVICES[${index}*6+2]} &> /dev/null &
        else
            ${WRAPPER} -m ${SWITCH} -p ${DEVICES[${index}*6+3]} -- \
                                    -s ${DEVICES[${index}*6+4]} \
                                    -e ${DEVICES[${index}*6+5]} ${DEVICES[${index}*6+2]} &> /dev/null &
        fi
    done
}
start-konsole() {
    touch ./tabs
    for ((index=0; index < ${#DEVICES[@]}/6; index++))
    do
        echo "title: ${DEVICES[${index}*6]};; command: telnet localhost ${DEVICES[${index}*6+3]}" >> ./tabs
    done
    konsole --tabs-from-file ./tabs &> /dev/null
    rm ./tabs &> /dev/null
}
reset-konsole() {
    for ((index=0; index < ${#DEVICES[@]}/6; index++))
    do
        sudo kill -SIGHUP $(fuser -n tcp ${DEVICES[${index}*6+3]} 2>&1 | awk '{print $2}')
    done
}
start-sniff() {
    [ -d ${DUMPDIR} ] || mkdir -p ${DUMPDIR} &> /dev/null
    mkdir -p "${DUMPDIRTMP}" &> /dev/null
    ${IOUSNIFF} -f -o -i /tmp/netio$(id -u) -n ./NETMAP -s ${DUMPDIRTMP} &> /dev/null &
}
stop() {
    ps -e | grep "${1}" | awk '{ print $1 }' | xargs sudo kill "-${2}" &> /dev/null
}
case "${MODE}" in
  --start)
    start-iou
    sleep ${DELAY}
    start-konsole
  ;;
  --stop)
    stop "iou" "SIGKILL"
  ;;
  --start-iou)
    start-iou
  ;;
  --start-konsole)
    start-konsole
  ;;
  --reset-konsole)
    reset-konsole
  ;;
  --start-sniff)
    start-sniff
  ;;
  --stop-sniff)
    stop "iou-sniff" "SIGINT"
    fdupes -d -N ${DUMPDIRTMP} &> /dev/null
    mergecap -w ${DUMPFILE} ${DUMPDIRTMP}/*.pcap
    rm -rf ${DUMPDIRTMP} &> /dev/null
  ;;
  --help|*)
    echo -e "\nUsage: ${0} [OPTIONS]"
    echo -e "\nOPTIONS:\n"
    echo -e "\t--start \t- Start"
    echo -e "\t--stop \t\t- Stop"
    echo -e "\t--start-iou \t- Start IOU"
    echo -e "\t--start-sniff \t- Start Sniff"
    echo -e "\t--stop-sniff \t- Stop Sniff"
    echo -e "\t--start-konsole - Start konsole"
    echo -e "\t--reset-konsole - Reset konsole"
    echo -e "\t--help \t\t- Help\n"
  ;;
esac


The presented script is configured to work with the current stand. If you use it in another scheme, you only need to correctly populate the DEVICES array :

#         Name     Type   ID     Port      Serial  Ethernet
DEVICES=( "R-01"   "R"    "1"    "10001"   "0"     "1"
          "R-02"   "R"    "2"    "10002"   "0"     "2"
          "R-03"   "R"    "3"    "10003"   "0"     "1"
          "R-04"   "R"    "4"    "10004"   "0"     "1"
          "R-05"   "R"    "5"    "10005"   "0"     "2"
          "R-06"   "R"    "6"    "10006"   "0"     "2"
          "R-07"   "R"    "7"    "10007"   "0"     "2"
          "R-08"   "R"    "8"    "10008"   "0"     "1"
          "R-09"   "R"    "9"    "10009"   "0"     "1"
          "R-10"   "R"    "10"   "10010"   "0"     "1"
          "R-11"   "R"    "11"   "10011"   "0"     "1"
          "SW-12"  "S"    "12"   "10012"   "0"     "1"
          "SW-13"  "S"    "13"   "10013"   "0"     "1"
          "SW-14"  "S"    "14"   "10014"   "0"     "1"
          "R-15"   "R"    "15"   "10015"   "0"     "1"
)

The one-dimensional array DEVICES consists of six pseudo-columns (this approach has taken root from the time of using C, I do not exclude a more elegant solution to the problem).

  1. Name - The conditional name of the device that will be used when signing the konsole tabs;
  2. Type - Device Type: R - router, S - switch;
  3. ID - Device identifier used also in NETMAP;
  4. Port - The port number through which telnet can connect to the device’s console;
  5. Serial - The number of cards installed in the device with Serial ports;
  6. Ethernet - The number of cards installed in the device with Ethernet ports.


At the end of the article, I will reveal the script start options:
  1. --start - Full start of the stand with simultaneous connection to the console;
  2. --stop - Full shutdown of the stand;
  3. --start-iou - Launch all devices without additional connection to them via the console;
  4. --start-konsole - Connect to all devices through the console;
  5. --reset-konsole - If the console freezes, this sometimes happens. Using the current option, you can reset all connections without rebooting devices;
  6. --start-sniff - When it becomes necessary to sniff, you need to run a script with this option. To withstand the period of time during which the packet is captured. Next, you need to stop packet capture with the --stop-sniff option and you will get a complete dump of your network in the ./dump folder;
  7. --stop-sniff - Stop packet capture mode.

Also popular now: