discovery.sh: update to v2.64

arm-master
rs232 1 week ago committed by pedro
parent 86ea31a1d6
commit 5a020a04f1

@ -15,6 +15,7 @@
#include <sys/socket.h>
#include <arpa/inet.h>
#include <time.h>
#include <ctype.h>
#define V_NONE VT_NONE, { }, { }
#define V_01 VT_RANGE, { .l = 0 }, { .l = 1 }
@ -2112,13 +2113,55 @@ static void asp_css(int argc, char **argv)
#if defined(TCONFIG_BCMARM) || defined(TCONFIG_MIPSR2)
static void asp_discovery(int argc, char **argv)
{
char buf[64] = "/usr/sbin/discovery.sh ";
char buf[128] = "/usr/sbin/discovery.sh ";
unsigned int i;
if (strncmp(argv[0], "off", 3) == 0)
if (argc == 0 || (argc == 1 && strcmp(argv[0], "off") == 0))
return;
else if (strncmp(argv[0], "traceroute", 10) == 0)
strlcat(buf, argv[0], sizeof(buf));
/* include 'arping' as a valid command */
const char* valid_commands[] = {"arping", "traceroute", "nc", "all"};
int valid_command = 0;
for (i = 0; i < sizeof(valid_commands)/sizeof(valid_commands[0]); i++) {
if (strcmp(argv[0], valid_commands[i]) == 0) {
valid_command = 1;
strlcat(buf, argv[0], sizeof(buf));
break;
}
}
if (!valid_command) {
fprintf(stderr, "Invalid discovery command: %s\n", argv[0]);
return;
}
/* append target (wan/lan/both) */
if (argc > 1) {
const char *target = argv[1];
if (strcmp(target, "lan") == 0 || strcmp(target, "wan") == 0 || strcmp(target, "both") == 0) {
strlcat(buf, " ", sizeof(buf));
strlcat(buf, target, sizeof(buf));
}
}
/* append 'clear' flag */
if (argc > 2 && strcmp(argv[2], "clear") == 0) {
strlcat(buf, " clear", sizeof(buf));
}
/* append probe limit (numeric) */
if (argc > 3) {
int is_number = 1;
for (const char *p = argv[3]; *p; ++p) {
if (!isdigit(*p)) {
is_number = 0;
break;
}
}
if (is_number)
snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), " %s", argv[3]);
}
system(buf);
}
#endif

@ -1,41 +1,185 @@
#!/bin/sh
export PATH=/bin:/usr/bin:/sbin:/usr/sbin:/home/root
# Network discovery script v2.0 - ARM and MIPSR2 - 05/2024 - rs232
# Network discovery script v2.64 - ARM and MIPSR2 - 02/2025 - rs232
#------------------------------------------------------
# FreshTomato Network Discovery - usage
# FreshTomato Network Discovery - usage
#
# default (or arping) = arping scanning
# traceroute = uses a traceroute discovery
# arping = * scanning via arping (preferred)
# traceroute = scanning via traceroute
# nc = scanning via netcat
# all = all the above in round-robin
#
# lan = * only LANs interfaces
# wan = only WANs interfaces
# both = scan LANs and WANs
#
# clear = removes dirty records from the arp table post scan
#
# <5-200> = default 60, maximum number of concurrent scans
#------------------------------------------------------
script_name=$(basename "$0")
pid_file="/tmp/var/run/${script_name}.pid"
cleanup() {
rm -f "$pid_file"
exit 0
}
trap cleanup INT TERM EXIT
if [ -e "$pid_file" ]; then
pid_age=$(($(date +%s) - $(stat -c %Y "$pid_file")))
if [ "$pid_age" -gt 120 ]; then
echo "PID file is older than 2 minutes. Removing stale PID file."
rm -f "$pid_file"
fi
fi
debug=$(nvram get discovery_debug 2>/dev/null)
debug="${debug:-0}"
[ "$debug" -eq 1 ] && { echo "Debugging..."; date >> /tmp/discovery.debug; echo "$@" >> /tmp/discovery.debug; }
lim=$(echo "$@" | grep -oE '[0-9]+' | head -n 1 | awk '{if($1 < 5) print 5; else if($1 > 200) print 200; else print $1}')
[ -z "$lim" ] && lim=60
alias slp='usleep 250000'
alias low='nice -n 19'
tmp="/tmp/discovery.tmp"
scan=0
cl=0
runlan=0
runwan=0
fkill=0
iplist() {
input_cidr="$1"
subnet_mask=$(echo "$input_cidr" | cut -d '/' -f 2)
num_addresses=$((2 ** (32 - subnet_mask)))
ip_address=$(echo "$input_cidr" | cut -d '/' -f 1)
ip_int=0
for octet in $(echo "$ip_address" | tr '.' ' '); do
ip_int=$((ip_int * 256 + octet))
done
first_ip_int=$((ip_int & ~(num_addresses - 1)))
last_ip_int=$((first_ip_int + num_addresses - 1))
for i in $(seq 1 $((num_addresses - 2))); do
current_ip_int=$((first_ip_int + i))
printf "%d.%d.%d.%d\n" $((current_ip_int >> 24 & 255)) $((current_ip_int >> 16 & 255)) $((current_ip_int >> 8 & 255)) $((current_ip_int & 255))
input_cidr="$1"
cidr=$(echo "$input_cidr" | cut -d '/' -f 2)
ip=$(echo "$input_cidr" | cut -d '/' -f 1)
awk -v cidr="$cidr" -v ip="$ip" '
BEGIN {
split(ip, a, ".");
ip_int = a[1] * 256^3 + a[2] * 256^2 + a[3] * 256 + a[4];
num_addrs = 2^(32 - cidr);
base_ip = int(ip_int / num_addrs) * num_addrs;
for (i = 1; i < num_addrs - 1; i++) {
current_ip = base_ip + i;
printf "%d.%d.%d.%d\n",
int(current_ip / 256^3) % 256,
int(current_ip / 256^2) % 256,
int(current_ip / 256) % 256,
current_ip % 256;
}
}'
}
check_procs() {
case "$1" in
arping) pidof arping ;;
traceroute) pidof traceroute ;;
nc) pidof nc ;;
esac | wc -w
}
md5_param=$(echo "$@" | md5sum | cut -d' ' -f1)
if [ -f "$tmp" ]; then
if ! grep -q "$md5_param" "$tmp"; then
# Kill previous instances with different parameters
for PID in $(pidof "$script_name"); do
[ "$PID" != "$$" ] && kill -9 "$PID" &>/dev/null
done
# Kill scanning tools
for tool in arping traceroute nc; do
[ "$(check_procs "$tool")" -gt 0 ] && killall -q "$tool"
done
sleep 1
fkill=1
fi
else
echo 0 > "$tmp"
echo "$md5_param" >> "$tmp"
fi
for param in "$@"; do
case "$param" in
clear) cl=1 ;;
lan) runlan=1 ;;
wan) runlan=0; runwan=1 ;;
both) runlan=1; runwan=1 ;;
traceroute) scan=1 ;;
nc) scan=2 ;;
all) scan=$(head -1 "$tmp") ;;
esac
done
[ "$runlan" -eq 0 ] && [ "$runwan" -eq 0 ] && runlan=1
if [ -e "$pid_file" -a "$fkill" -ne 1 ]; then
echo "Status-devices - Background discovery processes already running; skipping this run." | tee /dev/tty | logger
cleanup
fi
scanthis() {
for iface in $this; do
cidr=$(ip a l dev "$iface" | grep inet | awk '{print $2}')
[ "$(echo "$cidr" | cut -d/ -f2)" -lt 22 ] && {
echo "$iface - subnet mask shorter than /22. Too many IPs to scan - skipping..." | tee /dev/tty | logger
continue
}
[ "$(echo "$cidr" | cut -d/ -f2)" -lt 32 ] && {
ip=$(echo "$cidr" | cut -d/ -f1)
ipn=$(ip neigh show dev "$iface" | grep 'REACHABLE\|PERMANENT' | grep -Eo '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}')
iplist "$cidr" | grep -vwE "$ip|$(echo "$ipn" | tr ' ' '\n')" | while read -r ip_addr; do
if [ "$scan" -eq 0 ]; then
while [ "$(check_procs arping)" -gt "$lim" ]; do slp; done
low arping -I "$iface" -q -c1 -w1 "$ip_addr" &>/dev/null &
elif [ "$scan" -eq 1 ]; then
while [ "$(check_procs traceroute)" -gt "$lim" ]; do slp; done
low traceroute -i "$iface" -r -F -m1 -q1 -s "$ip" "$ip_addr" &>/dev/null &
elif [ "$scan" -eq 2 ]; then
while [ "$(check_procs nc)" -gt "$lim" ]; do slp; done
low nc -w 1 "$ip_addr" &>/dev/null &
fi
done
wait
}
done
}
[ "$(ps w | grep 'traceroute -i\ | arping -q' | grep -v grep | wc -l)" -gt 0 ] && {
logger "Device List Discovery already running - exiting ..."
exit
prune() {
ip n | grep -w "$1" | while read -r badip; do
ip=$(echo "$badip" | awk '{print $1}')
dev=$(echo "$badip" | awk '{print $3}')
ip neigh change "$ip" nud none dev "$dev"
done
}
echo $$ > "$pid_file"
[ "$runlan" -eq 1 ] && {
this=$(brctl show | grep -Eo ^br[0-9])
scanthis
}
for bridge in $(brctl show | grep -Eo ^br[0-9]); do
cidr=$(ip addr list dev $bridge | grep inet | awk '{print $2}')
ip=$(echo $cidr | cut -d/ -f1)
iplist $cidr | while read -r ip_address; do
if [ -z $1 ] || [ $1 == "arping" ]; then usleep 1500 && arping -q -c1 -w1 -I $bridge $ip_address &>/dev/null &
elif [ $1 == "traceroute" ]; then traceroute -i $bridge -r -F -m1 -q1 -s $ip $ip_address &>/dev/null &
fi
unset wans
[ "$runwan" -eq 1 ] && {
[ "$(ip rule | grep -Eo 'WAN[1-4]' | sort -u | wc -l)" -gt 0 ] && {
for WAN in $(ip rule | grep -Eo 'WAN[1-4]' | sort -u); do
wans="$wans $(ip r l t "$WAN" | grep default | grep -Eo '[vlan|eth]+[0-9]{1,4}')"
done
done
} || {
wans=$(ip route | grep ^default | awk '{print $5}')
}
this="$wans"
scanthis
}
[ "$cl" -eq 1 ] && {
prune "FAILED\|INCOMPLETE"
usleep $((lim * 30000))
prune "STALE\|DELAY\|PROBE"
}
scan=$(((${scan:-0} + 1) % 3))
{
echo "$scan"
echo "$md5_param"
} >> "$tmp"
cleanup

@ -32,9 +32,13 @@ var xob = null;
var cmd = null;
var wol = null;
var cmdresult = '';
/* DISCOVERY-BEGIN */
var cprefix = 'status_devices';
var discovery_mode = cookie.get(cprefix+'_discovery') || 'off';
/* DISCOVERY-BEGIN */
var discovery_clear = parseInt(cookie.get(cprefix + '_discovery_clear')) || 0;
var clear2 = (discovery_clear === 1) ? 'clear' : '';
var discovery_limit = cookie.get(cprefix+'_discovery_limit') || '60';
var discovery_target = cookie.get(cprefix+'_discovery_target') || 'lan';
var discovery_mode = cookie.get(cprefix+'_discovery_mode') || 'off';
var wait = gc_time;
var time_o;
/* DISCOVERY-END */
@ -60,7 +64,7 @@ ref.refresh = function(text) {
}
/* DISCOVERY-BEGIN */
var discovery = new TomatoRefresh('update.cgi', 'exec=discovery&arg0='+discovery_mode, gc_time, '', 1);
var discovery = new TomatoRefresh('update.cgi', 'exec=discovery&arg0='+discovery_mode+'&arg1='+discovery_target+'&arg2='+clear2+'&arg3='+discovery_limit, gc_time, '', 1);
discovery.refresh = function() { }
/* DISCOVERY-END */
@ -651,16 +655,18 @@ function tick() {
var clock = E('wait');
var spin = E('spin');
if (ref.running && discovery_mode != 'off' && E('refresh-button').value == 'Stop' && E('refresh-time').value != 0) {
if (ref.running && discovery_mode !== 'off' && E('refresh-button').value == 'Stop' && E('refresh-time').value != 0) {
elem.setInnerHTML(clock, wait+' sec');
clock.style.display = 'inline-block';
spin.style.display = 'inline';
if (wait-- <= 0) {
if (wait <= 0) {
discovery.initPage(0, gc_time);
wait = gc_time;
clearTimeout(time_o);
}
else {
wait--;
}
time_o = setTimeout(tick, 1000);
}
else {
@ -672,17 +678,23 @@ function tick() {
}
function verifyFields(f, c) {
if (discovery.running)
discovery.stop();
if (discovery.running) discovery.stop();
discovery_clear = E('_discovery_clear').checked ? 1 : 0;
cookie.set(cprefix+'_discovery_clear', discovery_clear);
clear2 = (discovery_clear === 1) ? 'clear' : '';
discovery_limit = E('_discovery_limit').value;
cookie.set(cprefix+'_discovery_limit', discovery_limit);
discovery_target = E('_discovery_target').value;
cookie.set(cprefix+'_discovery_target', discovery_target);
discovery_mode = E('_discovery_mode').value;
cookie.set(cprefix+'_discovery', discovery_mode);
discovery = new TomatoRefresh('update.cgi', 'exec=discovery&arg0='+discovery_mode, gc_time, '', 1);
cookie.set(cprefix+'_discovery_mode', discovery_mode);
discovery = new TomatoRefresh('update.cgi', 'exec=discovery&arg0='+discovery_mode+'&arg1='+discovery_target+'&arg2='+clear2+'&arg3='+discovery_limit, gc_time, '', 1);
discovery.refresh = function() { }
if (ref.running)
discovery.initPage(0, gc_time);
if (discovery_mode != 'off') {
if (discovery_mode !== 'off') {
wait = gc_time;
clearTimeout(time_o);
tick();
@ -717,11 +729,16 @@ function earlyInit() {
setNoiseBar(uidx, wlnoise[uidx]);
}
}
dg.setup();
/* DISCOVERY-BEGIN */
E('_discovery_clear').checked = (discovery_clear === 1);
/* DISCOVERY-END */
}
function init() {
var c;
if (((c = cookie.get(cprefix+'_notes_vis')) != null) && (c == '1'))
toggleVisibility(cprefix, 'notes');
dg.recolor();
ref.initPage(3000, 3);
@ -760,15 +777,52 @@ function init() {
if (nvram['wl'+u+'_radio'] == 1 && wl_sunit(uidx) < 0)
f.push( { title: '<span id="nf'+u+'" title="Noise Floor"><b>Noise<\/b> '+wl_display_ifname(uidx)+'&nbsp;<b>:<\/b><\/span>', prefix: '<span id="noiseimg_'+uidx+'"><\/span>&nbsp;<span id="noise'+uidx+'">', custom: wlnoise[uidx], suffix: '<\/span>&nbsp;<small>dBm<\/small>' } );
}
/* DISCOVERY-BEGIN */
f.push(
null,
{ title: 'Network Discovery mode', name: 'discovery_mode', type: 'select', options: [['off','off'],['arping','arping (preferred)'],['traceroute','traceroute']], suffix: '&nbsp; <img src="spin.gif" alt="" id="spin"><div id="wait"><\/div>', value: discovery_mode }
);
/* DISCOVERY-END */
createFieldTable('', f);
</script>
</div>
<!-- DISCOVERY-BEGIN -->
<div class="section-title">Network Discovery</div>
<div class="section">
<script>
createFieldTable('', [
{ title: 'Sanitize results', name: 'discovery_clear', type: 'checkbox', value: 'clear', checked: (discovery_clear === 1) ? 'checked' : '' },
{ title: 'Max Probes', name: 'discovery_limit', type: 'text', maxlen: 3, size: 3, value: discovery_limit, placeholder: '60', suffix: '<\/span>&nbsp;<small> 5 - 200<\/small>' },
{ title: 'Scan Target', name: 'discovery_target', type: 'select', options: [['lan','LANs *'],['wan','WANs'],['both','LANs & WANs']], value: discovery_target },
{ title: 'Scan Mode', name: 'discovery_mode', type: 'select', options: [['off','Off *'],['arping','arping (preferred)'],['traceroute','traceroute'],['nc','netcat'],['all','all (round-robin)']], suffix: '&nbsp; <img src="spin.gif" alt="" id="spin"><div id="wait"><\/div>', value: discovery_mode }
]);
</script>
</div>
<!-- DISCOVERY-END -->
<!-- / / / -->
<div class="section-title">Notes <small><i><a href='javascript:toggleVisibility(cprefix,"notes");'><span id="sesdiv_notes_showhide">(Show)</span></a></i></small></div>
<div class="section" id="sesdiv_notes" style="display:none">
<b>Device List</b>
<ul>
<li>You can hover over the fields in device list to find shortcuts to pre-populated pages: [DR] DHCP Reservation, [BWL] BandWidth Limiter, [AR] Access Restriction and [WLF] WireLess Filter.</li>
<li>Clicking on the MAC address will lookup the manufacturer by looking at the first half of the MAC address, this is purely informational.</li>
<li>When present, pressing an ON/OFF icon will send a Wake-up On Line datagram to the device, if the device supports that it will become active.</li>
<li>Clicking on the remaining lease time lets you terminate that lease, and if it is wireless connected it will also de-authenticate it. use with care.</li>
</ul>
<!-- DISCOVERY-BEGIN -->
<b>Network Discovery</b>
<ul>
<li><b>Sanitize results:</b> Before and after discovery has run, the reported state of devices known to FT may not have settled completely, but eventually it will by itself. Ticking "Sanitize results" will speed up that process after the scan has run.</li>
<li><b>Max Probes:</b> determines the maximum concurrent number of probes. Each eligible IP address is probed, not limiting the simultaneous probes may be too large a load for the router and negatively affect its core job: routing traffic.Range is from 5 to 200 simultaneous probes, default 60, which is the optimum on the most popular routers at the moment. That's why it is not an good idea to leave the discovery running with the web page open.</li>
<li><b>Scan Target:</b> You can scan LANs ( default ), WANs or both, but not individual LANs or individual WANs if you have multiple. The maximum size of a subnet to scan is /22, being 1022 individual IP addresses. There is no need to do a WAN scan if the FT router and an upstream device are the only two devices in that WAN.</li>
<li><b>Scan Mode:</b> selects the method to obtain the status of a device on the network. the choices are:
<ul>
<li>Off - This is the default. No scanning is done at all. The device list is populated only by devices which have frequent contact with the router themselves. WAN devices other than the upstream router will often go undetected.</li>
<li>arping - the most lightweight and preferred method</li>
<li>traceroute - alternative might work better with certain devices (e.g. old Apple kit)</li>
<li>nc - netcat is a well known alternative for scanning</li>
<li>all - Each consecutive discovery scan is done round robin with the next discovery method</li>
</li></ul>
<li>When enabled the discovery runs once in 60 or 120 seconds (depending on the device) when on the right side the screen refresh is activated. As the discovery itself runs for a number of seconds - depending on your choices, network and router this may be between 10 and 30 seconds, or even more - be prepared that it may take some time before the results that appear have settled. When "One off" is chosen, please refresh the screen by ctrl-F5 instead of pressing "Refresh" again.</li>
</ul>
<!-- DISCOVERY-END -->
</div>
<!-- / / / -->

Loading…
Cancel
Save