~44 min read
Lab 12 - Fundamental Network Topologies
Exploration of fundamental network topologies through ARP analysis, ICMP testing and Layer 3 path mapping.

📝 Problem Overview
Containerized Hosts
- single-pc1: 172.30.20.10, 02:12:20:00:00:01.
- single-pc2: 172.30.20.11, 02:12:20:00:00:02.
- single-pc3: 172.30.20.12, 02:12:20:00:00:03.
- ring-node1: 172.30.40.2, 172.30.40.34, 02:14:40:00:00:01.
- ring-node2: 172.30.40.3, 172.30.40.10, 02:14:40:00:00:02.
- ring-node3: 172.30.40.11, 172.30.40.18, 02:14:40:00:00:03.
- ring-node4: 172.30.40.19, 172.30.40.26, 02:14:40:00:00:04.
- ring-node5: 172.30.40.27, 172.30.40.35, 02:14:40:00:00:05.
- star-core: 172.30.30.2, 172.30.30.10, 172.30.30.18, 02:13:30:00:00:00.
- star-leaf1: 172.30.30.3, 02:13:30:00:00:01.
- star-leaf2: 172.30.30.11, 02:13:30:00:00:02.
- star-leaf3: 172.30.30.19, 02:13:30:00:00:03.
Networks
- single-net: 172.30.20.0/24.
- ring-ab: 172.30.40.0/29.
- ring-bc: 172.30.40.8/29.
- ring-cd: 172.30.40.16/29.
- ring-de: 172.30.40.24/29.
- ring-ea: 172.30.40.32/29.
- star-net-a: 172.30.30.0/29.
- star-net-b: 172.30.30.8/29.
- star-net-c: 172.30.30.16/29.
Note: The single-net is defined as a simple, flat Layer 2 network composed of a single Ethernet segment. The ring topology, consisting of the segments ring-ab, ring-bc, ring-cd, ring-de, and ring-ea, is conceptual and implemented using static routes only; therefore, no reconvergence occurs in the event of a link failure. The star topology comprises a central node (star-core) operating as a router and three connected subnets (star-net-a, star-net-b, and star-net-c).
📝 Tasks
- 1️⃣ Access single-pc1 using the command ./OOTab12 -run single1 and list the ARP cache by executing arp -a. Then, access single-pc2 and single-pc3 in two additional terminal sessions. On both single-pc2 and single-pc3 hosts, initiate ICMP traffic monitoring using tcpdump -i any icmp. From single-pc1, perform a ping sweep across the single-net network: fping -a -g <single-net_IPIrange>. After completing the sweep, run again arp -a. Analyze and discuss the changes observed in the ARP cache.
- 2️⃣ On single-pc1, execute a ping scan using nmap -sn <single-net_IPIrange>. During this scan, capture ARP traffic on single-pc2 and single-pc3 using: tcpdump -i any arp. Identify and report which hosts are detected as up.
- 3️⃣ Access ring-node1 using: ./OOTab12 -run ring1. Evaluate the logical paths to ring-node2 ,ring-node3, ring-node4, and ring-node5 using: traceroute <destination_IP>. Map the routes, evaluate the hop sequences, and draw a network topology diagram.
- 4️⃣ Disable one logical interface on ring-node1: ip link set eth1 down. Repeat the traceroute procedure to ring-node2, ring-node3, ring-node4, and ring-node5. Analyze the impact of the interface disruption on path continuity. Note that the OTLab12 uses static routing and no automatic failover is implemented. In contrast, real-world ring topologies typically rely on dynamic routing protocols or redundancy mechanisms.
- 5️⃣ Access star-leaf1: ./OOTab12 -run star1. Test connectivity by sending ICMP echo requests to star-leaf2 and star-leaf3. Map the routes, evaluate the hop sequences, and draw a network topology diagram.
- 6️⃣ Restart OTLab12 to restore all hosts to their default configuration. In separate terminals for single-pc2 and single-pc3, monitor ARP traffic: tcpdump -i any arp. On single-pc1, generate ARP broadcasts:
arping -c 3 172.30.20.11. Document the results and identify the broadcast domains. - 7️⃣ In separate terminals for ring-node2 and ring-node3, monitor ARP traffic:
tcpdump -i any arp. From ring-node1, execute:arping -c 3 172.30.40.3. Document the results and identify the broadcast domains. - 8️⃣ In separate terminals for star-leaf2 and star-leaf3, monitor ARP traffic:
tcpdump -i any arp. From star-leaf1, execute: arping -c 3 172.30.30.2. Document the results and identify the broadcast domains. - 9️⃣ Use the arp-scan utility to validate and confirm broadcast domains in each of the three network scenarios implemented in OTLab12.
- 🔟 For each of the three OTLab12 network scenarios, map the Layer 3 topology using:
nmap -sn --traceroute <sequential_list_of_all_IPIaddresses>. Analyze the resulting routing paths and compare them with the expected logical topology. Depending on the responses observed during host discovery and topology reconstruction, the following alternative command may be helpful for your analysis:nmap -sn -PE -vv --packet-trace <sequential_list_of_all_IPIaddresses>.
🛠️ Tools
- The following tools are available for completing OTLab 12: arp, arp-scan, arping, fping, nmap, ping, tcpdump, and traceroute.
🔖 Nomenclature
- ARP: Address resolution protocol.
- ICMP: Internet control message protocol.
- IP: Internet protocol.
📝 RESOLUTION (view content) ▼
#!/bin/bash
lab_name="OTLab12"
compose_file="${lab_name}.yml"
# Containers
## Shared L2 domain
single1_name="single-pc1"
single2_name="single-pc2"
single3_name="single-pc3"
## Ring topology
ring1_name="ring-node1"
ring2_name="ring-node2"
ring3_name="ring-node3"
ring4_name="ring-node4"
ring5_name="ring-node5"
## Star topology
star_core_name="star-core"
star1_name="star-leaf1"
star2_name="star-leaf2"
star3_name="star-leaf3"
# Images
parrot_image="parrotsec/core"
ubuntu_image="ubuntu:22.04"
# Networks
## Single-segment Ethernet
single_net="single-net" # 172.30.20.0/24
## Ring
ring_net_ab="ring-ab" # 172.30.40.0/29 (ring-node1 <-> ring-node2)
ring_net_bc="ring-bc" # 172.30.40.8/29 (ring-node2 <-> ring-node3)
ring_net_cd="ring-cd" # 172.30.40.16/29 (ring-node3 <-> ring-node4)
ring_net_de="ring-de" # 172.30.40.24/29 (ring-node4 <-> ring-node5)
ring_net_ea="ring-ea" # 172.30.40.32/29 (ring-node5 <-> ring-node1)
## Star
star_net_a="star-net-a" # 172.30.30.0/29
star_net_b="star-net-b" # 172.30.30.8/29
star_net_c="star-net-c" # 172.30.30.16/29
# ----------------------------------------
# Function to display the banner
show_banner() {
printf "\033[1;33m" # Yellow and bold
echo " _____ _____ __ _ "
echo "| |_ _| | ___| |_ "
echo "| | | | | | |__| .'| . |"
echo "|_____| |_| |_____|__,|___|"
printf "\033[1;37m" # White and bold
printf "Exercise: 12-Fundamental Network Topologies\n"
printf "Version: 1.0\n"
printf "Author: substationworm\n"
printf "Contact: in/lffreitas-gutierres\n"
printf "\033[0m" # Reset all styles
echo ""
}
# ----------------------------------------
# Function to generate Docker Compose file
generate_compose_file() {
local base_image="$1"
cat > "$compose_file" <<EOF
services:
# ----------------------------------------
# Single-segment Ethernet
# ----------------------------------------
$single1_name:
image: $base_image
container_name: $single1_name
hostname: $single1_name
mac_address: 02:12:20:00:00:01
entrypoint: []
command: bash -c "apt update && apt install -y iputils-ping iproute2 net-tools ipcalc traceroute nmap masscan tcpdump arp-scan arping fping && tail -f /dev/null"
cap_add:
- NET_ADMIN
- NET_RAW
networks:
$single_net:
ipv4_address: 172.30.20.10
$single2_name:
image: $base_image
container_name: $single2_name
hostname: $single2_name
mac_address: 02:12:20:00:00:02
entrypoint: []
command: bash -c "apt update && apt install -y iputils-ping iproute2 net-tools ipcalc traceroute nmap masscan tcpdump arp-scan arping fping && tail -f /dev/null"
cap_add:
- NET_ADMIN
- NET_RAW
networks:
$single_net:
ipv4_address: 172.30.20.11
$single3_name:
image: $base_image
container_name: $single3_name
hostname: $single3_name
mac_address: 02:12:20:00:00:03
entrypoint: []
command: bash -c "apt update && apt install -y iputils-ping iproute2 net-tools ipcalc traceroute nmap masscan tcpdump arp-scan arping fping && tail -f /dev/null"
cap_add:
- NET_ADMIN
- NET_RAW
networks:
$single_net:
ipv4_address: 172.30.20.12
# ----------------------------------------
# Ring
# ----------------------------------------
$ring1_name:
image: $base_image
container_name: $ring1_name
hostname: $ring1_name
mac_address: 02:14:40:00:00:01
entrypoint: []
command: bash -c "apt update && apt install -y iputils-ping iproute2 net-tools ipcalc traceroute nmap masscan tcpdump arp-scan arping fping && \
ip route add 172.30.40.8/29 via 172.30.40.3 && \
ip route add 172.30.40.16/29 via 172.30.40.3 && \
ip route add 172.30.40.24/29 via 172.30.40.3 && \
tail -f /dev/null"
cap_add:
- NET_ADMIN
- NET_RAW
sysctls:
net.ipv4.ip_forward: "1"
networks:
$ring_net_ab:
ipv4_address: 172.30.40.2
$ring_net_ea:
ipv4_address: 172.30.40.34
$ring2_name:
image: $base_image
container_name: $ring2_name
hostname: $ring2_name
mac_address: 02:14:40:00:00:02
entrypoint: []
command: bash -c "apt update && apt install -y iputils-ping iproute2 net-tools ipcalc traceroute nmap masscan tcpdump arp-scan arping fping && \
ip route add 172.30.40.16/29 via 172.30.40.11 && \
ip route add 172.30.40.24/29 via 172.30.40.11 && \
ip route add 172.30.40.32/29 via 172.30.40.11 && \
tail -f /dev/null"
cap_add:
- NET_ADMIN
- NET_RAW
sysctls:
net.ipv4.ip_forward: "1"
networks:
$ring_net_ab:
ipv4_address: 172.30.40.3
$ring_net_bc:
ipv4_address: 172.30.40.10
$ring3_name:
image: $base_image
container_name: $ring3_name
hostname: $ring3_name
mac_address: 02:14:40:00:00:03
entrypoint: []
command: bash -c "apt update && apt install -y iputils-ping iproute2 net-tools ipcalc traceroute nmap masscan tcpdump arp-scan arping fping && \
ip route add 172.30.40.24/29 via 172.30.40.19 && \
ip route add 172.30.40.32/29 via 172.30.40.19 && \
ip route add 172.30.40.0/29 via 172.30.40.19 && \
tail -f /dev/null"
cap_add:
- NET_ADMIN
- NET_RAW
sysctls:
net.ipv4.ip_forward: "1"
networks:
$ring_net_bc:
ipv4_address: 172.30.40.11
$ring_net_cd:
ipv4_address: 172.30.40.18
$ring4_name:
image: $base_image
container_name: $ring4_name
hostname: $ring4_name
mac_address: 02:14:40:00:00:04
entrypoint: []
command: bash -c "apt update && apt install -y iputils-ping iproute2 net-tools ipcalc traceroute nmap masscan tcpdump arp-scan arping fping && \
ip route add 172.30.40.32/29 via 172.30.40.27 && \
ip route add 172.30.40.0/29 via 172.30.40.27 && \
ip route add 172.30.40.8/29 via 172.30.40.27 && \
tail -f /dev/null"
cap_add:
- NET_ADMIN
- NET_RAW
sysctls:
net.ipv4.ip_forward: "1"
networks:
$ring_net_cd:
ipv4_address: 172.30.40.19
$ring_net_de:
ipv4_address: 172.30.40.26
$ring5_name:
image: $base_image
container_name: $ring5_name
hostname: $ring5_name
mac_address: 02:14:40:00:00:05
entrypoint: []
command: bash -c "apt update && apt install -y iputils-ping iproute2 net-tools ipcalc traceroute nmap masscan tcpdump arp-scan arping fping && \
ip route add 172.30.40.0/29 via 172.30.40.34 && \
ip route add 172.30.40.8/29 via 172.30.40.34 && \
ip route add 172.30.40.16/29 via 172.30.40.34 && \
tail -f /dev/null"
cap_add:
- NET_ADMIN
- NET_RAW
sysctls:
net.ipv4.ip_forward: "1"
networks:
$ring_net_de:
ipv4_address: 172.30.40.27
$ring_net_ea:
ipv4_address: 172.30.40.35
# ----------------------------------------
# Star
# ----------------------------------------
$star_core_name:
image: $base_image
container_name: $star_core_name
hostname: $star_core_name
mac_address: 02:13:30:00:00:00
entrypoint: []
command: bash -c "apt update && apt install -y iputils-ping iproute2 net-tools ipcalc traceroute nmap masscan tcpdump arp-scan arping fping && tail -f /dev/null"
cap_add:
- NET_ADMIN
- NET_RAW
sysctls:
net.ipv4.ip_forward: "1"
networks:
$star_net_a:
ipv4_address: 172.30.30.2
$star_net_b:
ipv4_address: 172.30.30.10
$star_net_c:
ipv4_address: 172.30.30.18
$star1_name:
image: $base_image
container_name: $star1_name
hostname: $star1_name
mac_address: 02:13:30:00:00:01
entrypoint: []
command: bash -c "apt update && apt install -y iputils-ping iproute2 net-tools ipcalc traceroute nmap masscan tcpdump arp-scan arping fping && \
ip route replace default via 172.30.30.2 && \
tail -f /dev/null"
cap_add:
- NET_ADMIN
- NET_RAW
networks:
$star_net_a:
ipv4_address: 172.30.30.3
$star2_name:
image: $base_image
container_name: $star2_name
hostname: $star2_name
mac_address: 02:13:30:00:00:02
entrypoint: []
command: bash -c "apt update && apt install -y iputils-ping iproute2 net-tools ipcalc traceroute nmap masscan tcpdump arp-scan arping fping && \
ip route replace default via 172.30.30.10 && \
tail -f /dev/null"
cap_add:
- NET_ADMIN
- NET_RAW
networks:
$star_net_b:
ipv4_address: 172.30.30.11
$star3_name:
image: $base_image
container_name: $star3_name
hostname: $star3_name
mac_address: 02:13:30:00:00:03
entrypoint: []
command: bash -c "apt update && apt install -y iputils-ping iproute2 net-tools ipcalc traceroute nmap masscan tcpdump arp-scan arping fping && \
ip route replace default via 172.30.30.18 && \
tail -f /dev/null"
cap_add:
- NET_ADMIN
- NET_RAW
networks:
$star_net_c:
ipv4_address: 172.30.30.19
networks:
$single_net:
name: $single_net
driver: bridge
ipam:
config:
- subnet: 172.30.20.0/24
$ring_net_ab:
name: $ring_net_ab
driver: bridge
ipam:
config:
- subnet: 172.30.40.0/29
$ring_net_bc:
name: $ring_net_bc
driver: bridge
ipam:
config:
- subnet: 172.30.40.8/29
$ring_net_cd:
name: $ring_net_cd
driver: bridge
ipam:
config:
- subnet: 172.30.40.16/29
$ring_net_de:
name: $ring_net_de
driver: bridge
ipam:
config:
- subnet: 172.30.40.24/29
$ring_net_ea:
name: $ring_net_ea
driver: bridge
ipam:
config:
- subnet: 172.30.40.32/29
$star_net_a:
name: $star_net_a
driver: bridge
ipam:
config:
- subnet: 172.30.30.0/29
$star_net_b:
name: $star_net_b
driver: bridge
ipam:
config:
- subnet: 172.30.30.8/29
$star_net_c:
name: $star_net_c
driver: bridge
ipam:
config:
- subnet: 172.30.30.16/29
EOF
}
# ----------------------------------------
# System requirements check
check_requirements() {
error_flag=0
if ! command -v docker >/dev/null 2>&1; then
printf "\033[31m[Error]\033[0m Docker is not installed on this system.\n"
error_flag=1
elif ! docker info >/dev/null 2>&1; then
printf "\033[31m[Error]\033[0m Docker is installed, but not accessible.\n"
error_flag=1
fi
if command -v docker-compose >/dev/null 2>&1; then
DOCKER_COMPOSE_CMD="docker-compose"
elif docker compose version >/dev/null 2>&1; then
DOCKER_COMPOSE_CMD="docker compose"
else
printf "\033[31m[Error]\033[0m Docker Compose is not installed.\n"
error_flag=1
fi
if [ "$error_flag" -eq 1 ]; then
printf "\033[31m✘ The $lab_name system requirements check failed.\033[0m\n"
exit 1
fi
}
# ----------------------------------------
# Function to check if a container exists
container_exists() {
docker ps -a --format '{{.Names}}' | grep -q "^$1$"
}
# ----------------------------------------
# Command handling
case "$1" in
-start)
show_banner
distro="${2:-ubuntu}"
if [[ "$distro" == "parrot" ]]; then
selected_image="$parrot_image"
elif [[ "$distro" == "ubuntu" ]]; then
selected_image="$ubuntu_image"
else
printf "\033[31m[Error]\033[0m Invalid distro. Please use 'parrot' or 'ubuntu'.\n"
exit 1
fi
check_requirements
printf "\033[1;33m[Working]\033[0m Starting $lab_name. . .\n"
generate_compose_file "$selected_image"
$DOCKER_COMPOSE_CMD -f "$compose_file" up -d
if [ $? -eq 0 ]; then
printf "\033[32m✔ $lab_name started.\033[0m\n"
else
printf "\033[31m✘ $lab_name failed to start.\033[0m\n"
exit 1
fi
;;
-stop)
show_banner
if container_exists "$single1_name" || container_exists "$single2_name" || container_exists "$single3_name" || \
container_exists "$ring1_name" || container_exists "$ring2_name" || container_exists "$ring3_name" || \
container_exists "$ring4_name" || container_exists "$ring5_name" || \
container_exists "$star_core_name" || container_exists "$star1_name" || container_exists "$star2_name" || container_exists "$star3_name"; then
check_requirements
printf "\033[1;33m[Working]\033[0m Stopping $lab_name. . .\n"
$DOCKER_COMPOSE_CMD -f "$compose_file" stop
printf "\033[32m✔ $lab_name stopped.\033[0m\n"
else
printf "\033[34m[Information]\033[0m No containers to stop.\n"
exit 1
fi
;;
-clean)
show_banner
check_requirements
printf "\033[1;33m[Working]\033[0m Cleaning up all $lab_name resources. . .\n"
$DOCKER_COMPOSE_CMD -f "$compose_file" down -v 2>/dev/null
docker network rm "$single_net" "$ring_net_ab" "$ring_net_bc" "$ring_net_cd" "$ring_net_de" "$ring_net_ea" "$star_net_a" "$star_net_b" "$star_net_c" 2>/dev/null
rm -f "$compose_file"
printf "\033[32m✔ All $lab_name resources removed.\033[0m\n"
;;
-run)
show_banner
# Standard: single-pc1
target="${2:-single1}"
case "$target" in
single1) target_name="$single1_name" ;;
single2) target_name="$single2_name" ;;
single3) target_name="$single3_name" ;;
ring1) target_name="$ring1_name" ;;
ring2) target_name="$ring2_name" ;;
ring3) target_name="$ring3_name" ;;
ring4) target_name="$ring4_name" ;;
ring5) target_name="$ring5_name" ;;
star-core) target_name="$star_core_name" ;;
star1) target_name="$star1_name" ;;
star2) target_name="$star2_name" ;;
star3) target_name="$star3_name" ;;
*)
printf "\033[31m[Error]\033[0m Invalid target. Use one of: single1|single2|single3|ring1|ring2|ring3|ring4|ring5|star-core|star1|star2|star3\n"
exit 1
;;
esac
if container_exists "$target_name"; then
check_requirements
printf "\033[1;33m[Working]\033[0m Accessing $target_name terminal. . .\n"
docker exec -it "$target_name" bash
else
printf "\033[31m[Error]\033[0m Container $target_name not found.\n"
exit 1
fi
;;
-restart)
show_banner
check_requirements
if [ ! -f "$compose_file" ]; then
printf "\033[31m[Error]\033[0m Cannot restart: $compose_file not found.\n"
exit 1
fi
printf "\033[1;33m[Working]\033[0m Restarting $lab_name. . .\n"
$DOCKER_COMPOSE_CMD -f "$compose_file" up -d
if [ $? -eq 0 ]; then
printf "\033[32m✔ $lab_name restarted.\033[0m\n"
else
printf "\033[31m✘ $lab_name failed to restart.\033[0m\n"
exit 1
fi
;;
-status)
show_banner
check_requirements
$DOCKER_COMPOSE_CMD -f "$compose_file" ps
;;
*)
show_banner
echo "Usage: $0 -start [parrot|ubuntu] | -stop | -clean | -run [target] | -restart | -status"
echo ""
echo " -start Start the $lab_name environment using the specified distro (default: ubuntu)"
echo " Valid options: parrot (parrotsec/core) or ubuntu (22.04)"
echo " -run Open a terminal inside one container"
echo " Targets: single1|single2|single3|ring1|ring2|ring3|ring4|ring5|star-core|star1|star2|star3"
echo " -clean Remove containers, volumes, and networks"
echo " -stop Stop all containers"
echo " -restart Restart previously stopped containers"
echo " -status Show current containers status"
exit 1
;;
esac
📝 RESOLUTION OFFLINE (view content) ▼
#!/bin/bash
lab_name="OTLab12"
compose_file="${lab_name}.yml"
# Containers
## Shared L2 domain
single1_name="single-pc1"
single2_name="single-pc2"
single3_name="single-pc3"
## Ring topology
ring1_name="ring-node1"
ring2_name="ring-node2"
ring3_name="ring-node3"
ring4_name="ring-node4"
ring5_name="ring-node5"
## Star topology
star_core_name="star-core"
star1_name="star-leaf1"
star2_name="star-leaf2"
star3_name="star-leaf3"
# Images
parrot_image="ews-image-parrot04"
ubuntu_image="ews-image-ubuntu04"
# Networks
## Single-segment Ethernet
single_net="single-net" # 172.30.20.0/24
## Ring
ring_net_ab="ring-ab" # 172.30.40.0/29 (ring-node1 <-> ring-node2)
ring_net_bc="ring-bc" # 172.30.40.8/29 (ring-node2 <-> ring-node3)
ring_net_cd="ring-cd" # 172.30.40.16/29 (ring-node3 <-> ring-node4)
ring_net_de="ring-de" # 172.30.40.24/29 (ring-node4 <-> ring-node5)
ring_net_ea="ring-ea" # 172.30.40.32/29 (ring-node5 <-> ring-node1)
## Star
star_net_a="star-net-a" # 172.30.30.0/29
star_net_b="star-net-b" # 172.30.30.8/29
star_net_c="star-net-c" # 172.30.30.16/29
# ----------------------------------------
# Function to display the banner
show_banner() {
printf "\033[1;33m" # Yellow and bold
echo " _____ _____ __ _ "
echo "| |_ _| | ___| |_ "
echo "| | | | | | |__| .'| . |"
echo "|_____| |_| |_____|__,|___|"
printf "\033[1;37m" # White and bold
printf "Exercise: 12-Fundamental Network Topologies\n"
printf "Version: 1.0-Offline\n"
printf "Author: substationworm\n"
printf "Contact: in/lffreitas-gutierres\n"
printf "\033[0m" # Reset all styles
echo ""
}
# ----------------------------------------
# Function to generate Docker Compose file
generate_compose_file() {
local base_image="$1"
cat > "$compose_file" <<EOF
services:
# ----------------------------------------
# Single-segment Ethernet
# ----------------------------------------
$single1_name:
image: $base_image
container_name: $single1_name
hostname: $single1_name
mac_address: 02:12:20:00:00:01
entrypoint: []
cap_add:
- NET_ADMIN
- NET_RAW
networks:
$single_net:
ipv4_address: 172.30.20.10
$single2_name:
image: $base_image
container_name: $single2_name
hostname: $single2_name
mac_address: 02:12:20:00:00:02
entrypoint: []
cap_add:
- NET_ADMIN
- NET_RAW
networks:
$single_net:
ipv4_address: 172.30.20.11
$single3_name:
image: $base_image
container_name: $single3_name
hostname: $single3_name
mac_address: 02:12:20:00:00:03
entrypoint: []
cap_add:
- NET_ADMIN
- NET_RAW
networks:
$single_net:
ipv4_address: 172.30.20.12
# ----------------------------------------
# Ring
# ----------------------------------------
$ring1_name:
image: $base_image
container_name: $ring1_name
hostname: $ring1_name
mac_address: 02:14:40:00:00:01
entrypoint: []
command: bash -c "ip route add 172.30.40.8/29 via 172.30.40.3 && \
ip route add 172.30.40.16/29 via 172.30.40.3 && \
ip route add 172.30.40.24/29 via 172.30.40.3 && \
tail -f /dev/null"
cap_add:
- NET_ADMIN
- NET_RAW
sysctls:
net.ipv4.ip_forward: "1"
networks:
$ring_net_ab:
ipv4_address: 172.30.40.2
$ring_net_ea:
ipv4_address: 172.30.40.34
$ring2_name:
image: $base_image
container_name: $ring2_name
hostname: $ring2_name
mac_address: 02:14:40:00:00:02
entrypoint: []
command: bash -c "ip route add 172.30.40.16/29 via 172.30.40.11 && \
ip route add 172.30.40.24/29 via 172.30.40.11 && \
ip route add 172.30.40.32/29 via 172.30.40.11 && \
tail -f /dev/null"
cap_add:
- NET_ADMIN
- NET_RAW
sysctls:
net.ipv4.ip_forward: "1"
networks:
$ring_net_ab:
ipv4_address: 172.30.40.3
$ring_net_bc:
ipv4_address: 172.30.40.10
$ring3_name:
image: $base_image
container_name: $ring3_name
hostname: $ring3_name
mac_address: 02:14:40:00:00:03
entrypoint: []
command: bash -c "ip route add 172.30.40.24/29 via 172.30.40.19 && \
ip route add 172.30.40.32/29 via 172.30.40.19 && \
ip route add 172.30.40.0/29 via 172.30.40.19 && \
tail -f /dev/null"
cap_add:
- NET_ADMIN
- NET_RAW
sysctls:
net.ipv4.ip_forward: "1"
networks:
$ring_net_bc:
ipv4_address: 172.30.40.11
$ring_net_cd:
ipv4_address: 172.30.40.18
$ring4_name:
image: $base_image
container_name: $ring4_name
hostname: $ring4_name
mac_address: 02:14:40:00:00:04
entrypoint: []
command: bash -c "ip route add 172.30.40.32/29 via 172.30.40.27 && \
ip route add 172.30.40.0/29 via 172.30.40.27 && \
ip route add 172.30.40.8/29 via 172.30.40.27 && \
tail -f /dev/null"
cap_add:
- NET_ADMIN
- NET_RAW
sysctls:
net.ipv4.ip_forward: "1"
networks:
$ring_net_cd:
ipv4_address: 172.30.40.19
$ring_net_de:
ipv4_address: 172.30.40.26
$ring5_name:
image: $base_image
container_name: $ring5_name
hostname: $ring5_name
mac_address: 02:14:40:00:00:05
entrypoint: []
command: bash -c "ip route add 172.30.40.0/29 via 172.30.40.34 && \
ip route add 172.30.40.8/29 via 172.30.40.34 && \
ip route add 172.30.40.16/29 via 172.30.40.34 && \
tail -f /dev/null"
cap_add:
- NET_ADMIN
- NET_RAW
sysctls:
net.ipv4.ip_forward: "1"
networks:
$ring_net_de:
ipv4_address: 172.30.40.27
$ring_net_ea:
ipv4_address: 172.30.40.35
# ----------------------------------------
# Star
# ----------------------------------------
$star_core_name:
image: $base_image
container_name: $star_core_name
hostname: $star_core_name
mac_address: 02:13:30:00:00:00
entrypoint: []
cap_add:
- NET_ADMIN
- NET_RAW
sysctls:
net.ipv4.ip_forward: "1"
networks:
$star_net_a:
ipv4_address: 172.30.30.2
$star_net_b:
ipv4_address: 172.30.30.10
$star_net_c:
ipv4_address: 172.30.30.18
$star1_name:
image: $base_image
container_name: $star1_name
hostname: $star1_name
mac_address: 02:13:30:00:00:01
entrypoint: []
command: bash -c "ip route replace default via 172.30.30.2 && \
tail -f /dev/null"
cap_add:
- NET_ADMIN
- NET_RAW
networks:
$star_net_a:
ipv4_address: 172.30.30.3
$star2_name:
image: $base_image
container_name: $star2_name
hostname: $star2_name
mac_address: 02:13:30:00:00:02
entrypoint: []
command: bash -c "ip route replace default via 172.30.30.10 && \
tail -f /dev/null"
cap_add:
- NET_ADMIN
- NET_RAW
networks:
$star_net_b:
ipv4_address: 172.30.30.11
$star3_name:
image: $base_image
container_name: $star3_name
hostname: $star3_name
mac_address: 02:13:30:00:00:03
entrypoint: []
command: bash -c "ip route replace default via 172.30.30.18 && \
tail -f /dev/null"
cap_add:
- NET_ADMIN
- NET_RAW
networks:
$star_net_c:
ipv4_address: 172.30.30.19
networks:
$single_net:
name: $single_net
driver: bridge
ipam:
config:
- subnet: 172.30.20.0/24
$ring_net_ab:
name: $ring_net_ab
driver: bridge
ipam:
config:
- subnet: 172.30.40.0/29
$ring_net_bc:
name: $ring_net_bc
driver: bridge
ipam:
config:
- subnet: 172.30.40.8/29
$ring_net_cd:
name: $ring_net_cd
driver: bridge
ipam:
config:
- subnet: 172.30.40.16/29
$ring_net_de:
name: $ring_net_de
driver: bridge
ipam:
config:
- subnet: 172.30.40.24/29
$ring_net_ea:
name: $ring_net_ea
driver: bridge
ipam:
config:
- subnet: 172.30.40.32/29
$star_net_a:
name: $star_net_a
driver: bridge
ipam:
config:
- subnet: 172.30.30.0/29
$star_net_b:
name: $star_net_b
driver: bridge
ipam:
config:
- subnet: 172.30.30.8/29
$star_net_c:
name: $star_net_c
driver: bridge
ipam:
config:
- subnet: 172.30.30.16/29
EOF
}
# ----------------------------------------
# System requirements check
check_requirements() {
error_flag=0
if ! command -v docker >/dev/null 2>&1; then
printf "\033[31m[Error]\033[0m Docker is not installed on this system.\n"
error_flag=1
elif ! docker info >/dev/null 2>&1; then
printf "\033[31m[Error]\033[0m Docker is installed, but not accessible.\n"
error_flag=1
fi
if command -v docker-compose >/dev/null 2>&1; then
DOCKER_COMPOSE_CMD="docker-compose"
elif docker compose version >/dev/null 2>&1; then
DOCKER_COMPOSE_CMD="docker compose"
else
printf "\033[31m[Error]\033[0m Docker Compose is not installed.\n"
error_flag=1
fi
if [ "$error_flag" -eq 1 ]; then
printf "\033[31m✘ The $lab_name system requirements check failed.\033[0m\n"
exit 1
fi
}
# ----------------------------------------
# Function to check if a container exists
container_exists() {
docker ps -a --format '{{.Names}}' | grep -q "^$1$"
}
# ----------------------------------------
# Command handling
case "$1" in
-start)
show_banner
distro="${2:-ubuntu}"
if [[ "$distro" == "parrot" ]]; then
selected_image="$parrot_image"
elif [[ "$distro" == "ubuntu" ]]; then
selected_image="$ubuntu_image"
else
printf "\033[31m[Error]\033[0m Invalid distro. Please use 'parrot' or 'ubuntu'.\n"
exit 1
fi
check_requirements
printf "\033[1;33m[Working]\033[0m Starting $lab_name. . .\n"
generate_compose_file "$selected_image"
$DOCKER_COMPOSE_CMD -f "$compose_file" up -d
if [ $? -eq 0 ]; then
printf "\033[32m✔ $lab_name started.\033[0m\n"
else
printf "\033[31m✘ $lab_name failed to start.\033[0m\n"
exit 1
fi
;;
-stop)
show_banner
if container_exists "$single1_name" || container_exists "$single2_name" || container_exists "$single3_name" || \
container_exists "$ring1_name" || container_exists "$ring2_name" || container_exists "$ring3_name" || \
container_exists "$ring4_name" || container_exists "$ring5_name" || \
container_exists "$star_core_name" || container_exists "$star1_name" || container_exists "$star2_name" || container_exists "$star3_name"; then
check_requirements
printf "\033[1;33m[Working]\033[0m Stopping $lab_name. . .\n"
$DOCKER_COMPOSE_CMD -f "$compose_file" stop
printf "\033[32m✔ $lab_name stopped.\033[0m\n"
else
printf "\033[34m[Information]\033[0m No containers to stop.\n"
exit 1
fi
;;
-clean)
show_banner
check_requirements
printf "\033[1;33m[Working]\033[0m Cleaning up all $lab_name resources. . .\n"
$DOCKER_COMPOSE_CMD -f "$compose_file" down -v 2>/dev/null
docker network rm "$single_net" "$ring_net_ab" "$ring_net_bc" "$ring_net_cd" "$ring_net_de" "$ring_net_ea" "$star_net_a" "$star_net_b" "$star_net_c" 2>/dev/null
rm -f "$compose_file"
printf "\033[32m✔ All $lab_name resources removed.\033[0m\n"
;;
-run)
show_banner
# Standard: single-pc1
target="${2:-single1}"
case "$target" in
single1) target_name="$single1_name" ;;
single2) target_name="$single2_name" ;;
single3) target_name="$single3_name" ;;
ring1) target_name="$ring1_name" ;;
ring2) target_name="$ring2_name" ;;
ring3) target_name="$ring3_name" ;;
ring4) target_name="$ring4_name" ;;
ring5) target_name="$ring5_name" ;;
star-core) target_name="$star_core_name" ;;
star1) target_name="$star1_name" ;;
star2) target_name="$star2_name" ;;
star3) target_name="$star3_name" ;;
*)
printf "\033[31m[Error]\033[0m Invalid target. Use one of: single1|single2|single3|ring1|ring2|ring3|ring4|ring5|star-core|star1|star2|star3\n"
exit 1
;;
esac
if container_exists "$target_name"; then
check_requirements
printf "\033[1;33m[Working]\033[0m Accessing $target_name terminal. . .\n"
docker exec -it "$target_name" bash
else
printf "\033[31m[Error]\033[0m Container $target_name not found.\n"
exit 1
fi
;;
-restart)
show_banner
check_requirements
if [ ! -f "$compose_file" ]; then
printf "\033[31m[Error]\033[0m Cannot restart: $compose_file not found.\n"
exit 1
fi
printf "\033[1;33m[Working]\033[0m Restarting $lab_name. . .\n"
$DOCKER_COMPOSE_CMD -f "$compose_file" up -d
if [ $? -eq 0 ]; then
printf "\033[32m✔ $lab_name restarted.\033[0m\n"
else
printf "\033[31m✘ $lab_name failed to restart.\033[0m\n"
exit 1
fi
;;
-status)
show_banner
check_requirements
$DOCKER_COMPOSE_CMD -f "$compose_file" ps
;;
*)
show_banner
echo "Usage: $0 -start [parrot|ubuntu] | -stop | -clean | -run [target] | -restart | -status"
echo ""
echo " -start Start the $lab_name environment using the specified distro (default: ubuntu)"
echo " Valid options: parrotsec (core) or ubuntu (22.04)"
echo " -run Open a terminal inside one container"
echo " Targets: single1|single2|single3|ring1|ring2|ring3|ring4|ring5|star-core|star1|star2|star3"
echo " -clean Remove containers, volumes, and networks"
echo " -stop Stop all containers"
echo " -restart Restart previously stopped containers"
echo " -status Show current containers status"
echo ""
echo "[i] Local images required: ews-image-parrot04 and ews-image-ubuntu04"
exit 1
;;
esac
Tags:
OT
ICS
Network Topology
ARP
ARP Scan
ARPing
ICMP
Traceroute
Nmap
Tcpdump
Broadcast Domains
Layer 2
Layer 3
Static Routing
