Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for Cisco IOL (and IOL-L2) #256

Merged
merged 13 commits into from
Oct 1, 2024
Merged
12 changes: 12 additions & 0 deletions cisco/iol-l2/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
VENDOR=Cisco
NAME=IOL-L2
IMAGE_FORMAT=bin
IMAGE_GLOB=*.bin
NOT_VM_IMAGE=1

# match versions like:
# cisco_iol-17.12.01.bin
VERSION=$(shell echo $(IMAGE) | sed -e 's/cisco_iol-\(.*\)\.bin/\1/')

-include ../../makefile-sanity.include
-include ../../makefile.include
70 changes: 70 additions & 0 deletions cisco/iol-l2/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# Cisco IOL-L2 (IOS on Linux)

This is the containerlab/vrnetlab image Cisco IOL.

CML recently introduced IOL-XE which compared to other Cisco images, runs very lightly since it executes purely as a binary and has no requirement for a virtualisation layer.

There are two types of IOL you can obtain:

- IOL, meant for Layer 3 operation as a router.
- IOL-L2, meant to act as a L2/L2+ switch.

## Building the image

> This README is for the IOL-L2 image, if you are trying to build the regular IOL/L3 image, go to the `../iol` directory.

Copy the `x86_64_crb_linux-adventerprisek9-ms` and rename it to `cisco_iol-x.y.z.bin` (x.y.z being the version number). For example `cisco_iol-17.12.01.bin`. The `.bin` extension is important.

> If you are getting the image from the CML refplat, the L2 image is under the `ioll2-xe-x.y.z` directory.

### License (.iourc)

Unlike the older IOU (IOS on Unix) a `.iourc` license file may not be required, however you still can provide one if necessary.

The `.iourc` license file can be placed next to the IOL binary (in this directory).

When supplying a `.iourc` license file, the hostname field must be changed to `iol`

**BEFORE**

```ini
[license]
hostname = a1b2c3d4e5f6g7
```

**AFTER**

```ini
[license]
iol = a1b2c3d4e5f6g7
```

### Build command

Execute

```
make docker-image
```

and the image will be built and tagged. You can view the image by executing `docker images`.

```
containerlab@containerlab:~$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
vrnetlab/vr-iol-l2 17.12.01 0e5ee8ee8746 9 minutes ago 644MB
```

## Usage

You can define the image easily and use it in a topology.

```yaml
# topology.clab.yaml
name: mylab
topology:
nodes:
iol:
kind: cisco_iol
image: vrnetlab/vr-iol-l2:<tag>
```
49 changes: 49 additions & 0 deletions cisco/iol-l2/docker/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
FROM public.ecr.aws/docker/library/debian:bookworm-slim

# Install dependencies
RUN apt-get update && \
apt-get install -y \
iproute2 \
iputils-ping \
net-tools \
sudo \
gnupg \
supervisor \
&& apt-get clean

# Add the GNS3 PPA for IOUYAP
RUN echo "deb http://ppa.launchpad.net/gns3/ppa/ubuntu trusty main" >> /etc/apt/sources.list
RUN apt-key adv --recv-keys --keyserver keyserver.ubuntu.com 9A2FD067A2E3EF7B

# Update and install IOUYAP
RUN apt-get update && \
apt-get install -y iouyap

# Set working directory
WORKDIR /opt/iol

# Add the IOL image and config files
COPY *.bin /opt/iol/iol.bin
COPY config.txt /opt/iol/config.txt

# Copy the supervisor file
COPY supervisord.conf /etc/supervisord.conf

# Copy the iourc file.
COPY .iour[c] /opt/iol/.iourc

# Copy the start-iol.sh script
COPY start-iol.sh /opt/iol/start-iol.sh

# Make the IOL image and script executable
RUN chmod +x /opt/iol/iol.bin
RUN chmod +x /opt/iol/start-iol.sh

# Copy the entrypoint script
COPY entrypoint.sh /opt/iol/entrypoint.sh

# Make the entrypoint script executable
RUN chmod +x /opt/iol/entrypoint.sh

# Set the entrypoint
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisord.conf"]
46 changes: 46 additions & 0 deletions cisco/iol-l2/docker/config.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
hostname <hostname>
!
no aaa new-model
!
ip domain name lab
!
ip cef
!
ipv6 unicast-routing
!
no ip domain lookup
!
username admin privilege 15 secret admin
!
vrf definition clab-mgmt
address-family ipv4
!
address-family ipv6
!
!
int Vlan 1
vrf forwarding clab-mgmt
ip address <ip_address> 255.255.255.0
ipv6 address <ipv6_address>/64
!
interface Ethernet0/0
switchport mode access
switchport access vlan1
<eth0_mac>
no shutdown
!
<interfaces>
!
ip forward-protocol nd
!
ip route vrf clab-mgmt 0.0.0.0 0.0.0.0 Vlan1 <ip_gateway>
ip route vrf clab-mgmt ::/0 Vlan1 <ipv6_gateway>
!
ip ssh version 2
crypto key generate rsa modulus 2048
!
line vty 0 4
login local
transport input ssh
!
end
48 changes: 48 additions & 0 deletions cisco/iol-l2/docker/entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#!/bin/bash

# Function to update the configuration files
update_configs() {
# Get the eth0 IP address
eth0_ip=$(ip addr show eth0 | grep "inet\b" | awk '{print $2}' | cut -d/ -f1)
eth0_ipv6=$( ip addr show eth0 | grep "inet6\b" | awk '{print $2}' | cut -d/ -f1 | head -n 1)
# Get the default gateway IP address
gateway_ip=$(ip route | grep default | awk '{print $3}')
gateway_ipv6=$(ip -6 route | grep default | awk '{print $3}' | head -n 1)

# Replace <hostname>, <ip_address>, and <ip_gateway> in config.txt
sed -i "s/<hostname>/$HOSTNAME/g" /opt/iol/config.txt
sed -i "s/<ip_address>/$eth0_ip/g" /opt/iol/config.txt
sed -i "s/<ip_gateway>/$gateway_ip/g" /opt/iol/config.txt
sed -i "s/<ipv6_address>/$eth0_ipv6/g" /opt/iol/config.txt
sed -i "s/<ipv6_gateway>/$gateway_ipv6/g" /opt/iol/config.txt

# Update the iourc file with the new hostname
sed -i "s/^iol = /$HOSTNAME = /" /opt/iol/.iourc
}

# Function to flush eth0 IP address
flush_eth0_ip() {
ip addr flush dev eth0
ip -6 addr flush dev eth0
}

# Function to run the startup script after a delay
run_startup_script() {
# Wait for 10 seconds to ensure eth0 is configured
sleep 10

# Update configuration files
update_configs

# Flush eth0 IP address
flush_eth0_ip

# Run the IOL image using the start-iol.sh script
supervisorctl start iol
}

# Start the function in the background
run_startup_script &

# Start bash interactively
exec "$@"
71 changes: 71 additions & 0 deletions cisco/iol-l2/docker/start-iol.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
#!/bin/bash

# Function to generate a random MAC address in the format aaaa.bbbb.cccc
generate_random_mac() {
hexchars="0123456789abcdef"
bbbb=$( for i in {1..4}; do echo -n ${hexchars:$(( $RANDOM % 16 )):1}; done )
cccc=$( for i in {1..4}; do echo -n ${hexchars:$(( $RANDOM % 16 )):1}; done )
echo "aaaa.${bbbb}.${cccc}"
}

# Function to create IOUYAP and NETMAP configurations
create_iouyap_and_netmap() {
local base_port=49000
local iouyap_file="/opt/iol/iouyap.ini"
local netmap_file="/opt/iol/NETMAP"
local config_file="/opt/iol/config.txt"
local interfaces_text=""

# Initialize the IOUYAP config
echo "[default]" > $iouyap_file
echo "base_port = $base_port" >> $iouyap_file
echo "netmap = $netmap_file" >> $iouyap_file

# Initialize the NETMAP file
> $netmap_file

# Loop over each available eth interface and create the configuration
for eth in $(ls /sys/class/net | grep eth); do
# Extract the number from the eth interface name
index=$(echo $eth | grep -o -E '[0-9]+')

# Calculate the group and port
local group=$((index / 4)) # Integer division to get the group
local port=$((index % 4)) # Remainder to get the port

echo "[513:${group}/${port}]" >> $iouyap_file
echo "eth_dev = $eth" >> $iouyap_file
echo "1:${group}/${port} 513:${group}/${port}" >> $netmap_file

# Generate a random MAC address for each eth interface
new_mac=$(generate_random_mac)

if [[ $eth == "eth0" ]]; then
# Replace <eth0_mac> with the generated MAC address for eth0
sed -i "s/<eth0_mac>/mac-address ${new_mac}/" $config_file
else
# Generate the interface configuration text for other eth interfaces
interfaces_text+="interface Ethernet ${group}/${port}\n mac-address ${new_mac}\n!\n"
fi
done

# Replace <interfaces> in config.txt with the generated interface text
sed -i "/<interfaces>/c\\${interfaces_text}" $config_file
}

# Create IOUYAP and NETMAP configurations
create_iouyap_and_netmap

# Run IOUYAP
sh -c "/usr/bin/iouyap 513 -q" &

# Get the highest numbered eth interface
max_eth=$(ls /sys/class/net | grep eth | grep -o -E '[0-9]+' | sort -n | tail -1)

# Calculate the number of groups of 4 interfaces
num_groups=$(( (max_eth + 4) / 4 ))

# Run the IOL image with the NETMAP file
export IOURC=/opt/iol/.iourc
set -x
exec /opt/iol/iol.bin 1 -e $num_groups -s 0 -d 0 -c config.txt -- -n 1024 -q -m 1024
30 changes: 30 additions & 0 deletions cisco/iol-l2/docker/supervisord.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
[supervisord]
user = root
nodaemon=true

[unix_http_server]
file=/tmp/supervisord.sock
chmod = 0777
username = supervisord
password = supervisord

[rpcinterface:supervisor]
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface

[supervisorctl]
serverurl = unix:///tmp/supervisord.sock
username = supervisord
password = supervisord

[program:entrypoint]
command=/opt/iol/entrypoint.sh
redirect_stderr=true
autorestart=false

[program:iol]
command=/opt/iol/start-iol.sh
redirect_stderr=true
autorestart=false
autostart=false
startsecs=0

12 changes: 12 additions & 0 deletions cisco/iol/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
VENDOR=Cisco
NAME=IOL
IMAGE_FORMAT=bin
IMAGE_GLOB=*.bin
NOT_VM_IMAGE=1

# match versions like:
# cisco_iol-17.12.01.bin
VERSION=$(shell echo $(IMAGE) | sed -e 's/cisco_iol-\(.*\)\.bin/\1/')

-include ../../makefile-sanity.include
-include ../../makefile.include
Loading