#!/usr/bin/env bash set -euo pipefail # scan_multi_concurrent.sh # # Author: Victor Bishop (Heretic312) # Date: 10/29/2025 # About: # Multi-Subnet Scanner that produces a CSV with ip, hostname, mac, vendor. # # Key Features: # Accepts subnets with an argument or environment variable, else fallback to default subnet. # Passing one or more subnets on the command line (./scan_multi_concurrent.sh 192.168.1.0/24 10.0.0.0/16) # Comma-separated SUBNETS environment variable (SUBNETS="10.0.0.0/8,192.168.1.0/24" ./scan_multi_concurrent.sh) # File listed in SUBNET_FILE where each line is a subnet/CIDR # Validates each subnet with Python's "ipaddress" # Saves per-subnet XML results for accurate parsing of MAC/vendor/hostnames. # Consolidates all results into internal_ips_YYYY-MM-DD_HH-MM-SS.csv. # Includes a short note about the MAC/vendor limitation. # # Usage examples: # ./scan_multi_concurrent.sh 192.168.1.0/24 10.0.0.0/24 # SUBNETS="192.168.1.0/24,10.0.0.0/24" ./scan_multi_concurrent.sh # SUBNET_FILE=subnets.txt CONCURRENCY=6 ./scan_multi_concurrent.sh # # Output: # internal_ips_.csv (columns: ip,hostname,mac,vendor) # per-subnet XML files used for parsing # # Notes: # - Requires: nmap, python3, xargs (for parallel). If you prefer GNU parallel, you can modify. # - MAC & vendor info is only available for hosts on the same L2 (local VLAN). Remote routed hosts won't show MACs. # - Run as root to get MAC/vendor on local networks: sudo ./scan_multi_concurrent.sh 192.168.1.0/24 192.168.2.0/24 # - Increase concurrency by settings CONCURRENCY: CONCURRENCY=8 ./scan_multi_concurrent.sh ... # Default values DEFAULT_SUBNET="10.2.1.110/14" CONCURRENCY="${CONCURRENCY:-4}" # default number of parallel nmap jobs WORKDIR="${WORKDIR:-./scan_results}" # gather subnets into an array subnets=() if [ "$#" -gt 0 ]; then for a in "$@"; do subnets+=("$a"); done elif [ -n "${SUBNETS:-}" ]; then IFS=',' read -r -a tmp <<< "${SUBNETS}" for s in "${tmp[@]}"; do subnets+=("$(echo "$s" | xargs)"); done elif [ -n "${SUBNET_FILE:-}" ]; then while IFS= read -r line; do line="$(echo "$line" | xargs)" # trim whitespace [ -z "$line" ] && continue subnets+=("$line") done < "$SUBNET_FILE" else subnets+=("$DEFAULT_SUBNET") fi # checks for cmd in python3 nmap xargs; do if ! command -v "$cmd" >/dev/null 2>&1; then echo "ERROR: required command '$cmd' not found in PATH." >&2 exit 2 fi done timestamp="$(date +%F_%H-%M-%S)" mkdir -p "$WORKDIR" # canonicalize subnets (use python ipaddress) canonicalize() { local raw="$1" python3 - <&2 fi done if [ ${#canonical_subnets[@]} -eq 0 ]; then echo "No valid subnets to scan. Exiting." >&2 exit 1 fi echo "Starting concurrent scans (concurrency=$CONCURRENCY). Workdir: $WORKDIR" echo "Subnets:" for s in "${canonical_subnets[@]}"; do printf " - %s\n" "$s"; done # Create a commands file for xargs; each line runs one nmap scan producing XML cmdfile="$(mktemp)" trap 'rm -f "$cmdfile"' EXIT for can in "${canonical_subnets[@]}"; do safe="${can//\//-}" xmlout="${WORKDIR}/nmap_${safe}_${timestamp}.xml" # use -sn for ping/host discovery and -oX for XML; use --privileged behavior (run as root to get MAC info on L2) # Note: running as root yields better ARP discovery and MAC vendor info on local networks. printf "nmap -sn %s -oX %s\n" "$can" "$xmlout" >> "$cmdfile" done # run the commands in parallel using xargs -P # xargs will take each line and run it via sh -c echo "Launching scans..." cat "$cmdfile" | xargs -I CMD -P "$CONCURRENCY" sh -c 'echo "CMD: $0"; $0' echo "All nmap scans finished. Parsing XML files to CSV..." # Python parser: read all xml files in WORKDIR with the timestamp and produce CSV csv_out="internal_ips_${timestamp}.csv" python3 - <