Fix crash on scan failure
[khome.git] / home / bin / arp-recon
CommitLineData
29e5f1be
SK
1#! /bin/bash
2
3set -e
4#set -u # Error on unset var
5set -o pipefail
6
7# commands:
8# - log (to stdout or file):
9# run arp-scan and convert output to our log format
10# - options
11# - interval
12# - file
13# - status (from stdin or file):
14# read log and report
15# - seen devices, sorted by last-seen
16# - ip changes?
17# - options
18# - file
19#
20# TODO
21# - [ ] Gather more info on each device. How? nmap?
22# ...
23#
24
25_debug=''
26
27_log() {
28 local -r level="$1"; shift
29 local -r fmt="$1\n"; shift
30 local -r args="$*"
31
32 printf '%s [%s] ' "$(date '+%Y-%m-%d %H:%M:%S')" "$level" >&2
33 printf "$fmt" $args >&2
34}
35
36error() {
37 _log 'error' "$@"
38}
39
40debug() {
41 if [[ -n "$_debug" ]]; then
42 _log 'debug' "$@"
43 fi
44}
45
46log() {
47 local -r interval="$1"
48 local -r log_file="$2"
49
50 while :; do
51 debug '(>) scan'
44be784b
SK
52 if ! sudo arp-scan --localnet; then
53 error 'scan failed'
54 fi
29e5f1be
SK
55 debug '(.) scan'
56 sleep "$interval";
57 done \
58 | stdbuf -o L awk '
59 /^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+/ {
60 ip = $1
61 mac = $2
62 print mac, ip
63 }' \
64 | ts '%.s' \
65 >> "$log_file"
66}
67
68status() {
69 local -r log_file="$1"
70
71 (
83b73f79
SK
72 echo 'mac ip staleness_cur staleness_avg age freq dist'
73 echo '--- -- ------------- ------------- --- ---- ----'
29e5f1be
SK
74 sort -n -k 1 "$log_file" \
75 | awk -v now="$(date '+%s')" \
76 '
77 {
78 ts = $1
79 mac = $2
80 ip = $3
81
83b73f79 82 interval[mac, ip, intervals[mac, ip]++] = ts - seen_last[mac, ip]
29e5f1be
SK
83 freq[mac, ip]++
84 if (!seen_last[mac, ip] || ts > seen_last[mac, ip] ) seen_last[mac, ip] = ts
85 if (!seen_first[mac, ip] || ts < seen_first[mac, ip]) seen_first[mac, ip] = ts
86 }
87
88 END {
89 for (key in freq) {
90 split(key, macip, SUBSEP)
91 mac = macip[1]
92 ip = macip[2]
83b73f79
SK
93 staleness_cur = now - seen_last[mac, ip]
94 age = now - seen_first[mac, ip]
29e5f1be 95 dist = 100 * (freq[mac, ip] / NR)
83b73f79
SK
96 intervals_sum = 0
97 for (i=1; i<=intervals[mac, ip]; i++)
98 intervals_sum += interval[mac, ip, i]
99 staleness_avg = intervals_sum / intervals[mac, ip]
29e5f1be
SK
100 print \
101 mac, \
102 ip, \
83b73f79
SK
103 sprintf("%d", staleness_cur), \
104 sprintf("%d", staleness_avg), \
105 sprintf("%d", age), \
29e5f1be
SK
106 freq[mac, ip], \
107 sprintf("%d", dist)
108 }
109 }
110 ' \
111 | sort -n -k 3 \
112 ) \
113 | column -t
114}
115
116main() {
117 local cmd
118 local interval
119 local log_file
120
121 case "$1" in
122 '-d')
123 _debug='yes'
124 shift
125 ;;
126 esac
127 cmd="$1"
128 case "$cmd" in
129 'log')
130 interval=60
131 log_file='/dev/stdout'
132
133 if [[ -n "$2" ]]; then
134 interval="$2"
135 if [[ -n "$3" ]]; then
136 log_file="$3"
137 fi
138 fi
139 debug '(>) log | interval:"%s" log_file:"%s"' "$interval" "$log_file"
140 log "$interval" "$log_file"
141 debug '(.) log | interval:"%s" log_file:"%s"' "$interval" "$log_file"
142 ;;
143 'status')
144 log_file='/dev/stdin'
145 if [[ -n "$2" ]]; then
146 log_file="$2"
147 fi
148 debug '(>) status | log_file:"%s"' "$log_file"
149 status "$log_file"
150 debug '(.) status | log_file:"%s"' "$log_file"
151 ;;
152 *)
153 error 'Unknown command: "%s"' "$cmd"
154 exit 1
155 ;;
156 esac
157}
158
159main "$@"
This page took 0.036964 seconds and 4 git commands to generate.