|
#!/usr/bin/env bash |
|
set -euo pipefail |
|
|
|
# ============================================================ |
|
# Network Scanner using nmap |
|
# - Auto-detects active IP and subnet (Linux/macOS) |
|
# - Scans the subnet with nmap |
|
# - Outputs a table: IP | Hostname | MAC |
|
# |
|
# Requirements: |
|
# - nmap |
|
# - Linux: ip |
|
# - macOS: ifconfig, route, ipconfig |
|
# |
|
# Author: Fábio Nicolau | Banana Groove Studios |
|
# License: MIT |
|
# ============================================================ |
|
|
|
need() { command -v "$1" >/dev/null 2>&1 || { echo "❌ Error: '$1' not found. Please install it."; exit 1; }; } |
|
|
|
# Mandatory dependency |
|
need nmap |
|
|
|
OS="$(uname -s)" |
|
|
|
# Convert macOS netmask hex (e.g., 0xffffff00) to CIDR (/24) — portable (no ${var,,}, no here-strings) |
|
hexmask_to_cidr() { |
|
local hex bin cidr |
|
hex="$1" |
|
hex="${hex#0x}" |
|
# lower-case via tr for portability |
|
hex="$(echo "$hex" | tr 'A-F' 'a-f')" |
|
|
|
bin="" |
|
local i ch |
|
for ((i=0; i<${#hex}; i++)); do |
|
ch="${hex:$i:1}" |
|
case "$ch" in |
|
0) bin="${bin}0000";; |
|
1) bin="${bin}0001";; |
|
2) bin="${bin}0010";; |
|
3) bin="${bin}0011";; |
|
4) bin="${bin}0100";; |
|
5) bin="${bin}0101";; |
|
6) bin="${bin}0110";; |
|
7) bin="${bin}0111";; |
|
8) bin="${bin}1000";; |
|
9) bin="${bin}1001";; |
|
a) bin="${bin}1010";; |
|
b) bin="${bin}1011";; |
|
c) bin="${bin}1100";; |
|
d) bin="${bin}1101";; |
|
e) bin="${bin}1110";; |
|
f) bin="${bin}1111";; |
|
*) : ;; |
|
esac |
|
done |
|
|
|
cidr="$(echo "$bin" | grep -o "1" | wc -l | tr -d ' ')" |
|
echo "$cidr" |
|
} |
|
|
|
IP="" CIDR="" SUBNET="" IFACE="" |
|
|
|
if [[ "$OS" == "Linux" ]]; then |
|
need ip |
|
IFACE="$(ip route get 1.1.1.1 2>/dev/null | awk '/dev /{for (i=1;i<=NF;i++) if ($i=="dev") {print $(i+1); exit}}')" |
|
if [[ -z "${IFACE:-}" ]]; then |
|
echo "❌ Error: Could not detect default interface on Linux." |
|
exit 1 |
|
fi |
|
IP_CIDR="$(ip -o -f inet addr show dev "$IFACE" scope global | awk '{print $4}' | head -n1)" |
|
IP="${IP_CIDR%/*}" |
|
CIDR="${IP_CIDR#*/}" |
|
SUBNET="$IP/$CIDR" |
|
|
|
elif [[ "$OS" == "Darwin" ]]; then |
|
need route |
|
need ifconfig |
|
need ipconfig |
|
IFACE="$(route -n get default 2>/dev/null | awk '/interface:/{print $2}')" |
|
if [[ -z "${IFACE:-}" ]]; then |
|
echo "❌ Error: Could not detect default interface on macOS." |
|
exit 1 |
|
fi |
|
IP="$(ipconfig getifaddr "$IFACE" 2>/dev/null || true)" |
|
if [[ -z "${IP:-}" ]]; then |
|
IP="$(ifconfig "$IFACE" | awk '/inet /{print $2; exit}')" |
|
fi |
|
HEXMASK="$(ifconfig "$IFACE" | awk '/netmask/{print $4; exit}')" |
|
CIDR="$(hexmask_to_cidr "$HEXMASK")" |
|
SUBNET="$IP/$CIDR" |
|
|
|
else |
|
echo "❌ Error: Unsupported OS ($OS)" |
|
exit 1 |
|
fi |
|
|
|
if [[ -z "${IP:-}" || -z "${CIDR:-}" ]]; then |
|
echo "❌ Error: Could not detect IP/CIDR automatically." |
|
exit 1 |
|
fi |
|
|
|
echo "Active IP: $IP ($IFACE)" |
|
echo "Subnet (CIDR): $SUBNET" |
|
echo "Running nmap -sn $SUBNET ..." |
|
echo |
|
|
|
# Run nmap and format output (table IP | Hostname | MAC) |
|
nmap -sn "$SUBNET" -oG - 2>/dev/null | \ |
|
awk ' |
|
BEGIN { FS=" "; } |
|
/^Host: / && /Status: Up/ { |
|
ip=$2; |
|
host=$3; gsub(/[()]/,"",host); |
|
if (host=="" || host=="()") host="-"; |
|
hosts[ip]=host; |
|
order[++n]=ip; |
|
} |
|
/^MAC: / { |
|
mac=$2; |
|
macs[ip]=mac; |
|
} |
|
END { |
|
printf "%-15s %-40s %-17s\n", "IP", "Hostname", "MAC"; |
|
for (i=1;i<=n;i++) { |
|
ip=order[i]; |
|
mac=(macs[ip] ? macs[ip] : "-"); |
|
printf "%-15s %-40s %-17s\n", ip, hosts[ip], mac; |
|
} |
|
} |
|
' | sort -t. -k1,1n -k2,2n -k3,3n -k4,4n |