Skip to content

Instantly share code, notes, and snippets.

@Cyberistic
Last active March 16, 2026 08:31
Show Gist options
  • Select an option

  • Save Cyberistic/fa984c73280d733beb443f7436e1cdf7 to your computer and use it in GitHub Desktop.

Select an option

Save Cyberistic/fa984c73280d733beb443f7436e1cdf7 to your computer and use it in GitHub Desktop.
Discover network devices through SSH (to be easily used with zerotier)
#!/bin/bash
# Filename: ~/usr/local/bin/show-network-devices.sh
# LAN Device Viewer (ping sweep + ARP)
# Detect LAN interface and subnet
LAN_IFACE=$(ip route get 1 | awk '{print $5; exit}')
LAN_SUBNET=$(ip -4 addr show "$LAN_IFACE" | awk '/inet /{print $2}')
LAN_SUBNET_PREFIX=$(echo "$LAN_SUBNET" | cut -d/ -f1 | sed 's/\.[0-9]*$//')
# OUI vendor lookup
get_vendor() {
local mac="$1"
[[ -z "$mac" || "$mac" == "-" ]] && return
local oui=$(echo "$mac" | tr -d ':' | tr '[:lower:]' '[:upper:]' | cut -c1-6)
if [[ -f /usr/share/nmap/nmap-mac-prefixes ]]; then
grep -i "^$oui" /usr/share/nmap/nmap-mac-prefixes 2>/dev/null | awk '{$1=""; print substr($0,2)}'
fi
}
# Get hostname via nmap (fast)
get_hostname() {
local ip="$1"
nmap -sn --max-retries 1 --host-timeout 2s "$ip" 2>/dev/null | awk '/Nmap scan report/{print $5; exit}'
}
# Get live devices
get_live_devices() {
echo "Pinging subnet..."
for ip in $(seq 1 254); do
ping -c 1 -W 1 "$LAN_SUBNET_PREFIX.$ip" >/dev/null 2>&1 &
done
wait
echo "Reading ARP table..."
timeout=10
while [[ $timeout -gt 0 ]]; do
mapfile -t devs < <(ip -4 neigh show dev "$LAN_IFACE" | grep -E "REACHABLE|STALE" | awk '{print $1, $3}')
[[ ${#devs[@]} -ge 3 ]] && break
sleep 1
((timeout--))
done
echo "Found ${#devs[@]} reachable devices"
# Own machine at top
self_ip=$(ip -4 addr show "$LAN_IFACE" | awk '/inet /{print $2}' | cut -d/ -f1)
self_mac=$(ip link show "$LAN_IFACE" | awk '/link\/ether/ {print $2}')
self_name=$(hostname)
devices+=("$self_ip $self_mac $self_name")
# Process ARP entries
echo "Resolving hostnames..."
for d in "${devs[@]}"; do
ip_only=$(echo "$d" | awk '{print $1}')
mac=$(echo "$d" | awk '{print $2}')
[[ "$ip_only" == "$self_ip" ]] && continue
[[ -z "$mac" ]] && mac="-"
hostname=$(get_hostname "$ip_only")
[[ -z "$hostname" || "$hostname" == "$ip_only" ]] && hostname="-"
vendor=$(get_vendor "$mac")
[[ -z "$vendor" ]] && vendor="-"
devices+=("$ip_only $mac $hostname $vendor")
done
}
# Scan all devices
scan_devices() {
for ip in $(seq 1 254); do
ping -c 1 -W 1 "$LAN_SUBNET_PREFIX.$ip" >/dev/null 2>&1 &
done
wait
sleep 1
mapfile -t all_devs < <(ip neigh show dev "$LAN_IFACE" | grep -v ":" | awk '{print $1, $5}')
}
# Display devices
display_devices() {
local list=("$@")
printf "%-4s %-15s %-20s %-25s %-25s\n" "#" "IP" "MAC" "HOSTNAME" "VENDOR"
echo "-----------------------------------------------------------------------------------------------------------------"
for i in "${!list[@]}"; do
parts=(${list[$i]})
ip="${parts[0]}"
mac="${parts[1]:--}"
hostname="${parts[2]:--}"
vendor="${parts[3]:--}"
if [[ "$vendor" == "-" ]] && [[ "$mac" != "-" ]]; then
vendor=$(get_vendor "$mac")
[[ -z "$vendor" ]] && vendor="-"
fi
if [[ " ${devices[*]} " =~ "$ip" ]]; then
color="\e[1;32m"
else
color="\e[1;31m"
fi
printf "${color}%-4s %-15s %-20s %-25s %-25s\e[0m\n" "$((i+1))" "$ip" "$mac" "$hostname" "$vendor"
done
}
# Main loop
while true; do
clear
echo -e "\e[1;36m=== LAN Devices ===\e[0m"
echo -e "Interface: $LAN_IFACE | Subnet: $LAN_SUBNET"
echo ""
get_live_devices
if [ ${#devices[@]} -eq 0 ]; then
echo "No live devices detected."
else
display_devices "${devices[@]}"
fi
echo ""
echo "Enter 0 to scan ALL devices"
echo "Enter number to show IP"
echo "Press Enter or Ctrl+C to exit"
echo -n "> "
read -r choice
[[ -z "$choice" ]] && break
if [[ "$choice" == "0" ]]; then
echo -e "\e[1;34mScanning all devices on LAN...\e[0m"
scan_devices
all_devs_display=("$self_ip $self_mac $self_name")
for entry in "${all_devs[@]}"; do
ip_only=$(echo "$entry" | awk '{print $1}')
[[ "$ip_only" == "$self_ip" ]] && continue
all_devs_display+=("$entry - -")
done
display_devices "${all_devs_display[@]}"
echo -n "Press Enter to continue..."
read -r
elif [[ "$choice" =~ ^[0-9]+$ ]] && (( choice >= 1 && choice <= ${#devices[@]} )); then
ip=$(echo "${devices[$((choice-1))]}" | awk '{print $1}')
echo "$ip"
echo -n "Press Enter to continue..."
read -r
else
break
fi
done
  1. Copy script and store it in /usr/local/bin, make it executable:
cd /usr/local/bin
sudo nano show-network-devices.sh
sudo chmod +x show-network-devices.sh

paste there and save file.

  1. install dependancies:
sudo apt update
sudo apt install arp-scan nmap avahi-daemon

Note that avahi-daemon is optional (See step 7).

  1. Create "Devices" user:
sudo adduser --disabled-login --gecos "LAN Devices User" devices
sudo passwd -d devices  # Remove password for LAN access

Caution

I set this up to be passwordless, since anyone with access to your LAN network can trivially retrieve this information anyways. Do your due deligance if you don't trust everyone on your lan, or if your server is exposed publically and you don't want this info to leak.

  1. Lock SSH to the script:
sudo nano /etc/ssh/sshd_config

and paste the following:

Match User devices
    PermitTTY yes
    ForceCommand /usr/local/bin/show-network-devices.sh
    # UNCOMMENT LINE BELLOW IF YOU WANT PASSWORDLESS, CHECK CAUTION PLZ
    # PermitEmptyPasswords yes 

then save the file.

  1. Reload ssh:
sudo systemctl restart ssh
  1. (OPTIONAL) Allow ARP scan without sudo password

Please read the caution warning above.

sudo visudo

and add this at the bottom then save file:

devices ALL=(ALL) NOPASSWD: /usr/bin/arp-scan
  1. (OPTIONAL) mDNS hostname devices.local If you'd like to access the device by doing ssh devices@devices.local instead of ssh devices@<device_ip>, do the following:
sudo hostnamectl set-hostname devices
sudo systemctl restart avahi-daemon

How I use this with ZeroTier to access local devices through ssh:

Problem

I have a main server device with ZeroTier installed (+ ztnet), I don't want to install ZeroTier on EACH device on the network just to ssh into it. Ideally, the server has ssh and acts as a relay to other devices

Solution

  1. Download ZeroTier:
curl -s https://install.zerotier.com | sudo bash
  1. Join my network:
sudo zerotier-cli join #your_network_id#

200 join OK

Note

I am self-hosting my own ZeroTier network controller using ztnet and my Docker compose script for one click deployment.

You'll have to accept this device through the controller, and note down its ip. Then save this device's ip on the ZeroTier network, or create an mDNS alias.

  1. Using the script above, I do:
ssh devices@<zerotier_device_ip>

which lists all devices on that network:

=== LAN Devices ===
Interface: eno1 | Subnet: 192.168.8.5/24

Pinging subnet...
Reading ARP table...
Found 4 reachable devices
Resolving hostnames...
#    IP              MAC                  HOSTNAME                  VENDOR
-----------------------------------------------------------------------------------------------------------------
1    192.168.8.5     10:e7:c6:42:2d:8a    server                    Hewlett Packard
2    192.168.8.9     2c:cf:67:9f:ab:d0    raspberrypi               Raspberry
3    192.168.8.14    92:d6:0d:41:bc:f5    Mac                       -
4    192.168.8.165   2c:cf:67:9f:a8:6c    raspberrypi               Raspberry
5    192.168.8.1     82:9a:e8:2f:9d:73    homerouter.cpe            -

Enter 0 to scan ALL devices
Enter number to show IP
Press Enter or Ctrl+C to exit
  1. ssh jump Say I want to connect to the pi, and I have an account called admin on the server, I simply do:
ssh -J admin@<zerotier_server_ip pi@192.168.8.165

That's it :D This allows me to ssh to the pi through the server as a relay without having to deal with downloading zerotier on each device

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment