#!/bin/sh # man ip-neighbour: # STATE := { permanent | noarp | stale | reachable | none | incomplete | delay | probe | failed } ARPSTATES='(PERMANENT|NOARP|STALE|REACHABLE|NONE|INCOMPLETE|DELAY|PROBE|FAILED)' CIDRS= SORT= ARP= VERBOSE= usage() { pingsweep="${0##*/}" [ "$1" ] && printf '#\n# ERROR: %s\n#\n\n' "$1" >&2 cat<&2 exit 1 } for x in ipcalc grep cut ip ping sleep column; do which $x>/dev/null || { printf 'This scripts depends on printf, ipcalc, grep, cut, ip, ping & sleep.\nExecutable "%s" is not found in $PATH ... aborting!\n' $x 1>&2 exit 1 } done [ "$CIDRS" ] || usage "No CIDR or IP-address specified!" [ "$SORT" ] && { which sort>/dev/null || usage 'Executable "sort" not found in $PATH ... rerun without "--sort" to show unsorted results!' } for cidr in $CIDRS; do printf %s "$cidr" | grep -qE / || cidr="$cidr/32" ipcalc -bn "$cidr" 2>&1 | grep -qE '^Address:\s' || usage "Invalid CIDR: $cidr" done isup() { ROUTE=$(ip route get "$1" 2>&1 | grep via | cut -d' ' -f2-) if [ "$ROUTE" ]; then ROUTE="ROUTED $ROUTE" LOCAL= else ROUTE=$(ip route get "$1" 2>/dev/null | head -1 | cut -d' ' -f2-) [ "$ROUTE" ] || { [ "$VERBOSE" ] && printf "%s\tDOWN NO-ROUTE\n" "$1" return } ROUTE="LOCAL $ROUTE" LOCAL=$(ip route get $1 | grep -v via | grep -oE 'dev [^ ]+' | cut -d' ' -f2) [ "$LOCAL" ] || printf "WARNING: No interface for LOCAL addr %s!\n" "$1" >&2 fi if ping -q -w2 -c1 "$1" >/dev/null 2>&1; then if [ $VERBOSE ]; then printf '%s;UP PING %s\n' "$1" "$ROUTE" else printf '%s\n' "$1"; fi elif [ "$ARP" ] && [ "$LOCAL" ]; then STATE=$(ip neigh get dev $LOCAL $1 2>&1 | grep -oE "$ARPSTATES") printf '%s' "$STATE" | grep -qE 'PERMANENT|STALE|NONE|INCOMPLETE|DELAY|PROBE' && { ping -q -w1 -c1 "$1" >/dev/null sleep 1 STATE=$(ip neigh get dev $LOCAL $1 2>&1 | grep -oE "$ARPSTATES") } while printf "%s" "$STATE" | grep -qE 'INCOMPLETE|DELAY|PROBE'; do sleep 0.5 STATE=$(ip neigh get dev $LOCAL $1 2>&1 | grep -oE "$ARPSTATES") done if [ "REACHABLE" = "$STATE" ] && [ $VERBOSE ]; then printf "%s;REACHABLE ARP %s\n" "$1" "$ROUTE" elif [ "REACHABLE" = "$STATE" ]; then printf '%s\n' "$1" elif [ $VERBOSE ]; then printf "%s;DOWN ARP %s\n" "$1" "$ROUTE" fi elif [ $VERBOSE ]; then [ "$VERBOSE" ] && printf "%s;DOWN PING %s\n" "$1" "$ROUTE" fi } doit() { for cidr in $CIDRS; do printf %s "$cidr" | grep -qE / || cidr="$cidr/32" IPCALC=$(ipcalc -bn "$cidr" 2>&1) BC=$(printf '%s' "$IPCALC" | grep -E '^Broadcast:' | grep -oE '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+') if [ -z "$BC" ]; then NW=$(printf '%s' "$IPCALC" | grep -E '^Address:' | grep -oE '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+') BC=$NW else NW=$(printf '%s' "$IPCALC" | grep -E '^Network:' | grep -oE '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+') fi N1=$(printf '%s' "$NW" | cut -d. -f1) N2=$(printf '%s' "$NW" | cut -d. -f2) N3=$(printf '%s' "$NW" | cut -d. -f3) N4=$(printf '%s' "$NW" | cut -d. -f4) [ "$N1.$N2.$N3.$N4" = "$NW" ] || { printf 'INTERNAL ERROR!\n%s\n' "$IPCALC" >&2 ; exit 1 ;} while [ "$N1.$N2.$N3.$N4" != "$BC" ]; do isup "$N1.$N2.$N3.$N4" & N4=$(($N4+1)) [ $N4 -gt 255 ] && { N4=0 N3=$(($N3+1)) [ $N3 -gt 255 ] && { N3=0 N2=$(($N2+1)) [ $N2 -gt 255 ] && { N2=0 N1=$(($N1+1)) [ $N1 -gt 255 ] && { printf 'INTERNAL ERROR! N1\n' >&2 ; exit 1 ;} } } } done isup "$BC" done } if [ "$SORT" ]; then doit | sort -nt. -k1,1n -k2,2n -k3,3n -k4,4n | column -t -s';' else doit | column -t -s';' fi