Merge branch 'release-3.15.1.0' into develop

Merging in latest release-3.15.1.0 changes into develop to provide a foundation with recent release fixes for the develop branch.

This should be 3.15.1.0b03 code.

Conflicts:
	files/etc/crontabs/root
	files/usr/local/bin/wscan
	files/www/cgi-bin/sysinfo.json
This commit is contained in:
Conrad Lara - KG6JEI 2015-11-15 19:28:56 -08:00
commit 48cb37bd05
26 changed files with 590 additions and 93 deletions

View File

@ -1,4 +1,4 @@
In accordance with the permissions granted under section 10(e) of In accordance with the permissions granted under section 7(e) of
the GPLv3 License: the GPLv3 License:
The AREDN(TM) trademark and AREDN(TM) logo and for use in official The AREDN(TM) trademark and AREDN(TM) logo and for use in official

View File

@ -9,6 +9,7 @@ cat feeds.conf.default >> feeds.conf
./scripts/feeds install -p arednpackages olsrd ./scripts/feeds install -p arednpackages olsrd
./scripts/feeds install perl ./scripts/feeds install perl
./scripts/feeds install -p arednpackages vtun ./scripts/feeds install -p arednpackages vtun
./scripts/feeds install -a -p arednpackages
./scripts/feeds install snmpd ./scripts/feeds install snmpd
./scripts/feeds install ntpclient ./scripts/feeds install ntpclient

View File

@ -0,0 +1,32 @@
# This file contains a list of files to retain over a sysupgrade with "Keep Settings" in the GUI.
# This list will be used instead of the list normally used by sysupgrade.
/etc/config.ap/_setup
/etc/config.ap/_setup.dhcp
/etc/config.ap/_setup.ports
/etc/config.client/_setup
/etc/config.client/_setup.dhcp
/etc/config.client/_setup.ports
/etc/config.mesh_ap/_setup
/etc/config.mesh_ap/_setup.dhcp
/etc/config.mesh_ap/_setup.ports
/etc/config.mesh/_setup
/etc/config.mesh/_setup.dhcp.dmz
/etc/config.mesh/_setup.dhcp.nat
/etc/config.mesh/_setup.ports.dmz
/etc/config.mesh/_setup.ports.nat
/etc/config.mesh/_setup.services.dmz
/etc/config.mesh/_setup.services.nat
/etc/config.mesh/vtun
/etc/config.router/_setup
/etc/config.router/_setup.dhcp
/etc/config.router/_setup.ports
/etc/dropbear/dropbear_dss_host_key
/etc/dropbear/dropbear_rsa_host_key
/etc/firewall.user
/etc/group
/etc/hosts
/etc/httpd.conf
/etc/local/services
/etc/local/uci/hsmmmesh
/etc/passwd
/etc/shadow

View File

@ -11,6 +11,7 @@ lan_proto = static
lan_ip = 172.27.1.1 lan_ip = 172.27.1.1
lan_mask = 255.255.255.0 lan_mask = 255.255.255.0
lan_dhcp = 1 lan_dhcp = 1
lan_dhcp_noroute = 0
dhcp_start = 5 dhcp_start = 5
dhcp_end = 25 dhcp_end = 25

View File

@ -8,6 +8,7 @@ lan_proto = static
lan_ip = 172.27.2.1 lan_ip = 172.27.2.1
lan_mask = 255.255.255.0 lan_mask = 255.255.255.0
lan_dhcp = 1 lan_dhcp = 1
lan_dhcp_noroute = 0
dhcp_start = 5 dhcp_start = 5
dhcp_end = 25 dhcp_end = 25

View File

@ -14,6 +14,7 @@ lan_proto = static
lan_ip = 172.27.0.1 lan_ip = 172.27.0.1
lan_mask = 255.255.255.0 lan_mask = 255.255.255.0
lan_dhcp = 1 lan_dhcp = 1
lan_dhcp_noroute = 0
dhcp_start = 5 dhcp_start = 5
dhcp_end = 25 dhcp_end = 25

View File

@ -12,6 +12,7 @@ lan_ip = 172.27.0.2
lan_mask = 255.255.255.0 lan_mask = 255.255.255.0
lan_dhcp = 0 lan_dhcp = 0
lan_gw = 172.27.0.1 lan_gw = 172.27.0.1
lan_dhcp_noroute = 0
dhcp_start = 5 dhcp_start = 5
dhcp_end = 25 dhcp_end = 25

View File

@ -4,6 +4,7 @@ lan_proto = static
lan_ip = 172.27.3.1 lan_ip = 172.27.3.1
lan_mask = 255.255.255.0 lan_mask = 255.255.255.0
lan_dhcp = 1 lan_dhcp = 1
lan_dhcp_noroute = 0
dhcp_start = 5 dhcp_start = 5
dhcp_end = 25 dhcp_end = 25

View File

@ -1,2 +1,4 @@
*/5 * * * * /usr/local/bin/fccid */5 * * * * /usr/local/bin/fccid
*/1 * * * * /usr/local/bin/rssi_monitor
* * * * * /usr/local/bin/snrlog * * * * * /usr/local/bin/snrlog

View File

@ -0,0 +1,20 @@
#!/bin/sh
IFS="
"
addedpaths="/cgi-bin/vpn
/cgi-bin/vpnc
/cgi-bin/supporttool
"
currentpwd=$(grep "/cgi-bin/setup" /etc/httpd.conf |cut -d ':' -f 3)
for protectedpath in $addedpaths
do
if grep -q "$protectedpath" "/etc/httpd.conf"; then
continue
fi
echo "$protectedpath:root:$currentpwd" >> /etc/httpd.conf
done

View File

@ -48,7 +48,7 @@ foreach $config ("ap","client","mesh","mesh_ap","router")
foreach $variable( sort keys %defaultcfg ) foreach $variable( sort keys %defaultcfg )
{ {
if ( $cfg{$variable} ) if ( defined $cfg{$variable} )
{ {
print TMPCONFFILE "$variable = $cfg{$variable}\n"; print TMPCONFFILE "$variable = $cfg{$variable}\n";
} }
@ -61,7 +61,7 @@ foreach $config ("ap","client","mesh","mesh_ap","router")
# Specific settings for variables that are not in the default config but are added by the system # Specific settings for variables that are not in the default config but are added by the system
foreach $variable( 'dmz_dhcp_end', 'dmz_dhcp_limit', 'dmz_dhcp_start', 'dmz_lan_ip', 'dmz_lan_mask', 'wifi_rxant', 'wifi_txant' ) foreach $variable( 'dmz_dhcp_end', 'dmz_dhcp_limit', 'dmz_dhcp_start', 'dmz_lan_ip', 'dmz_lan_mask', 'wifi_rxant', 'wifi_txant' )
{ {
if ( $cfg{$variable} ) if ( defined $cfg{$variable} )
{ {
print TMPCONFFILE "$variable = $cfg{$variable}\n"; print TMPCONFFILE "$variable = $cfg{$variable}\n";
} }

View File

@ -420,7 +420,7 @@ open(FILE, ">/etc/local/services") or die;
print FILE "#!/bin/sh\n"; print FILE "#!/bin/sh\n";
unless($cfg{wifi_proto} eq "disabled") unless($cfg{wifi_proto} eq "disabled")
{ {
$cfg{wifi_txpower} = wifi_maxpower() if not defined $cfg{wifi_txpower} or $cfg{wifi_txpower} > wifi_maxpower(); $cfg{wifi_txpower} = wifi_maxpower($cfg{wifi_channel}) if not defined $cfg{wifi_txpower} or $cfg{wifi_txpower} > wifi_maxpower($cfg{wifi_channel});
$cfg{wifi_txpower} = 1 if $cfg{wifi_txpower} < 1; $cfg{wifi_txpower} = 1 if $cfg{wifi_txpower} < 1;
print FILE "/usr/sbin/iw dev wlan0 set txpower fixed $cfg{wifi_txpower}00\n"; print FILE "/usr/sbin/iw dev wlan0 set txpower fixed $cfg{wifi_txpower}00\n";
if(defined $cfg{aprs_lat} and defined $cfg{aprs_lon}) if(defined $cfg{aprs_lat} and defined $cfg{aprs_lon})

View File

@ -101,9 +101,7 @@ foreach (@tunnelnames)
$section=&uci_get_named_section("vtun",$_); $section=&uci_get_named_section("vtun",$_);
if ($section->{enabled} eq 1) if ($section->{enabled} eq 1)
{ {
my $sip=$section->{serverip};
push(@tunnels,"tun${tunnum}"); push(@tunnels,"tun${tunnum}");
push @hosts, qq("$sip" "tun${tunnum}.$name.local.mesh");
$tunnum++; $tunnum++;
} }
} }
@ -115,13 +113,15 @@ foreach (@tunnelnames)
$section=&uci_get_named_section("vtun",$_); $section=&uci_get_named_section("vtun",$_);
if ($section->{enabled} eq 1) if ($section->{enabled} eq 1)
{ {
my $cip=$section->{clientip};
push(@tunnels,"tun${tunnum}"); push(@tunnels,"tun${tunnum}");
push @hosts, qq("$cip" "tun${tunnum}.$name.local.mesh");
$tunnum++; $tunnum++;
} }
} }
# add the nameservice plugin # add the nameservice plugin
push @file, qq(\nLoadPlugin "olsrd_nameservice.so.0.3"\n); push @file, qq(\nLoadPlugin "olsrd_nameservice.so.0.3"\n);
push @file, qq({\n); push @file, qq({\n);
@ -145,5 +145,6 @@ push @file, qq(\n{\n);
push @file, qq( Ip4Broadcast 255.255.255.255\n); push @file, qq( Ip4Broadcast 255.255.255.255\n);
push @file, qq(}\n); push @file, qq(}\n);
# write the file # write the file
print @file; print @file;

299
files/usr/local/bin/rssi_monitor Executable file
View File

@ -0,0 +1,299 @@
#!/usr/bin/perl
#
# GPL V2 or greater
# work around Atheros ANI overly attenuating recieve chain with tendency to become stuck
# Joe Ayers AE6XE ae6xe@arrl.net 2015-10-29
# A receive chain may go deaf at noise prone sites and some neighbors may drop out.
# The wireless driver poorly tunes and treats these neighbors as noise in error.
# This is a workaround until root cause driver updates occur.
$now=`cat /proc/uptime | cut -f1 -d" "`;
chomp $now;
exit 0 unless $now > 120;
$iface = "wlan0" ; # wireless interface
$datfile = "/tmp/rssi.dat";
$logfile = "/tmp/rssi.log";
open(my $lfh, '>>', $logfile) or die "Could not open file $logfile $!";
sub getRSSI
{
for (keys %rssi)
{
delete $rssi{$_};
}
open(FILE, "/usr/sbin/iw $iface station dump 2>&1 |") or die "/usr/sbin/iw failed $!";
$neighborCount = 0;
while($line = <FILE>)
{
if($line =~ /Station (\S+) \(on $iface\)/) { $mac = $1;}
if($antnum and $line =~ /signal:[ \t]+[-\d]+[ \t]*\[([-\d]+),[ \t]*([-\d]+)/)
{
$H = $1;
$V = $2;
}
if ((not $antnum) and $line =~ /signal:[ \t]+[-\d]+[ \t]*\[([-\d]+)\]/)
{
$H = $1;
}
if ($H)
{
if ($H < -95) { $rssi{$mac}{"Hrssi"}=-96 ; }
else { $rssi{$mac}{"Hrssi"}=$H ; }
undef $H;
$neighborCount += 1;
}
if ($V)
{
if ($V < -95) { $rssi{$mac}{"Vrssi"}=-96 ; }
else { $rssi{$mac}{"Vrssi"}=$V ; }
undef $V;
}
}
}
$antnum=`iw list | grep "Configured Antennas: TX" | cut -f6 -d" "`;
chomp $antnum;
if ($antnum eq "0x1")
{
$antnum=0;
}
else
{
$antnum=1; # more than one
}
if ( -e $datfile )
{
open(FILE, "<$datfile") or die "Unable to read \"$datfile\"";
while($line = <FILE>)
{
if ($antnum)
{
($mac, $aveH, $aveV, $stdDevH, $stdDevV, $numS, $last) = split /\|/, $line;
$rssiHist{$mac}{"aveV"} = $aveV;
$rssiHist{$mac}{"sdV"} = $stdDevV;
}
else
{
($mac, $aveH, $stdDevH, $numS, $last) = split /\|/, $line;
}
$rssiHist{$mac}{"aveH"} = $aveH;
$rssiHist{$mac}{"sdH"} = $stdDevH;
$rssiHist{$mac}{"num"} = $numS;
chomp $last;
$rssiHist{$mac}{"last"} = $last;
}
close FILE ;
}
$ofdm_level = `cat /sys/kernel/debug/ieee80211/phy0/ath9k/ani | grep "OFDM LEVEL" | cut -f2 -d: `;
$now=`cat /proc/uptime | cut -f1 -d" "`;
chomp $now;
getRSSI() ;
for (keys %rssi)
{
if ( $rssiHist{$_} and $now - $rssiHist{$_}{"last"} < 3600 )
{
$hit = 0 ;
$sdH3 = int(3 * $rssiHist{$_}{"sdH"} + .5);
# is the RSSI attenuated and 3 standard deviations away? Test is only 1 chain has dropped, not both.
if ($rssiHist{$_}{"aveH"} - $rssi{$_}{"Hrssi"} > $sdH3) { $hit += 1; }
if ( $antnum )
{
$sdV3 = int(3 * $rssiHist{$_}{"sdV"} + .5);
if ($rssiHist{$_}{"aveV"} - $rssi{$_}{"Vrssi"} > $sdV3) { $hit += 1; }
}
if ($rssiHist{$_}{"num"} > 9 and $ofdm_level <= 2 and $hit == 1)
{ # Overly Attenuated Chain Suspected
$datestring = localtime();
if ($antnum)
{
print $lfh "$datestring: Attenuated Suspect $_ [$rssi{$_}{'Hrssi'},$rssi{$_}{'Vrssi'}] $rssiHist{$_}{'aveH'} ";
print $lfh "$rssiHist{$_}{'aveV'} $rssiHist{$_}{'sdH'} $rssiHist{$_}{'sdV'}\n";
}
else
{
print $lfh "$datestring: Attenuated Suspect $_ [$rssi{$_}{'Hrssi'}] $rssiHist{$_}{'aveH'} $rssiHist{$_}{'sdH'}\n";
}
# find strongest signal to compare RSSI before/after reset
if ( $amac )
{
if ($antnum)
{
if ($rssi{$amac}{"Hrssi"} < $rssi{$amac}{"Vrssi"} ) { $strong1 = "Vrssi" ;} else {$strong1 = "Hrssi"; }
if ($rssi{$_}{"Hrssi"} < $rssi{$_}{"Vrssi"} ) { $strong2 = "Vrssi" ;} else {$strong2 = "Hrssi"; }
if ($rssi{$amac}{$strong1} < $rssi{$_}{$strong2} ) { $amac = $_ ;}
}
else
{
if ($rssi{$amac}{"Hrssi"} < $rssi{$_}{"Hrssi"} ) { $amac = $_ ;}
}
}
else { $amac = $_ ; }
next ; # do not update statistics when suspected condition
}
# unpdate statistics
$aveH = (($rssiHist{$_}{"aveH"}*$rssiHist{$_}{"num"})+$rssi{$_}{"Hrssi"}) / ($rssiHist{$_}{"num"} + 1 );
$sdH = sqrt((($rssiHist{$_}{"num"}-1)*($rssiHist{$_}{"sdH"}**2) +
(($rssi{$_}{"Hrssi"}-$aveH)*($rssi{$_}{"Hrssi"}-$rssiHist{$_}{"aveH"})))/$rssiHist{$_}{"num"});
chomp $aveH;
chomp $sdH;
if ($antnum)
{
$aveV = (($rssiHist{$_}{"aveV"}*$rssiHist{$_}{"num"})+$rssi{$_}{"Vrssi"}) / ($rssiHist{$_}{"num"} + 1 );
$sdV = sqrt((($rssiHist{$_}{"num"}-1)*($rssiHist{$_}{"sdV"}**2) +
(($rssi{$_}{"Vrssi"}-$aveV)*($rssi{$_}{"Vrssi"}-$rssiHist{$_}{"aveV"})))/$rssiHist{$_}{"num"});
chomp $aveV;
chomp $sdV;
}
$rssiHist{$_}{"aveH"} = $aveH;
$rssiHist{$_}{"sdH"} = $sdH;
$rssiHist{$_}{"last"} = $now;
if ($rssiHist{$_}{"num"} < 60 )
{
# keep statistics to 60 sample (minute) moving window
$rssiHist{$_}{"num"} += 1;
}
if ($antnum)
{
$rssiHist{$_}{"aveV"} = $aveV;
$rssiHist{$_}{"sdV"} = $sdV;
}
}
else
{ # new neigbor or data too old--restart history
$rssiHist{$_}{"aveH"} = $rssi{$_}{"Hrssi"};
$rssiHist{$_}{"sdH"} = 0;
$rssiHist{$_}{"num"} = 1;
$rssiHist{$_}{"last"} = $now;
if ($antnum)
{
$rssiHist{$_}{"aveV"} = $rssi{$_}{"Vrssi"};
$rssiHist{$_}{"sdV"} = 0;
}
}
}
if ($amac or not $neighborCount)
{
$chnum = `uci get wireless.radio0.channel`;
$chnum += 1;
if ($chnum == 8 or $chnum == 12 or $chnum == 100 or $chnum == 185) { $chnum -= 2; }
if ($chnum == 0) { $chnum = 1; }
$freq = `iw list | grep "\\\[$chnum\\\]" | head -1`;
$freq =~ /([\d]+)[ \t]+MHz[ \t]+/;
$freq = $1;
if ($amac)
{
$datestring = localtime();
if ($antnum) {print $lfh "$datestring: before $amac [ $rssi{$amac}{'Hrssi'}, $rssi{$amac}{'Vrssi'} ]\n";}
else {print $lfh "$datestring: before $amac [ $rssi{$amac}{'Hrssi'}]\n";}
}
system("/usr/sbin/iw $iface scan freq $freq passive > /dev/null");
if ($amac)
{
sleep 5;
$beforeH = $rssi{$amac}{"Hrssi"};
if ($antnum) { $beforeV = $rssi{$amac}{"Vrssi"}; }
getRSSI() ;
$datestring = localtime();
if ($antnum) {print $lfh "$datestring: after $amac [ $rssi{$amac}{'Hrssi'}, $rssi{$amac}{'Vrssi'} ]\n";}
else {print $lfh "$datestring: after $amac [ $rssi{$amac}{'Hrssi'}]\n";}
$falpos = 0;
if ($antnum)
{
if (abs ( $beforeH - $rssi{$amac}{"Hrssi"} ) <= 2 and
abs ( $beforeV - $rssi{$amac}{"Vrssi"} ) <= 2 ) { $falpos = 1; }
}
elsif (abs ( $beforeH - $rssi{$amac}{"Hrssi"} ) <= 2 ) { $falpos = 1; }
if ( $falpos )
{
# if a false-positive (within 2dB change after a reset), then add data point to statistics
$aveH = (($rssiHist{$amac}{"aveH"}*$rssiHist{$amac}{"num"})+ $beforeH )
/ ($rssiHist{$amac}{"num"} + 1 );
$sdH = sqrt((($rssiHist{$amac}{"num"}-1)*($rssiHist{$amac}{"sdH"}**2) +
(($beforeH-$aveH)*($beforeH-$rssiHist{$amac}{"aveH"})))
/$rssiHist{$amac}{"num"});
chomp $aveH;
chomp $sdH;
$rssiHist{$amac}{"aveH"} = $aveH;
$rssiHist{$amac}{"sdH"} = $sdH;
if ($antnum)
{
$aveV = (($rssiHist{$amac}{"aveV"}*$rssiHist{$amac}{"num"})+ $beforeV ) /
($rssiHist{$amac}{"num"} + 1 );
$sdV = sqrt((($rssiHist{$amac}{"num"}-1)*($rssiHist{$amac}{"sdV"}**2) +
(($beforeV-$aveV)*($beforeV-$rssiHist{$amac}{"aveV"}))) /
$rssiHist{$amac}{"num"});
chomp $aveV;
chomp $sdV;
$rssiHist{$amac}{"aveV"} = $aveV;
$rssiHist{$amac}{"sdV"} = $sdV;
}
if ($rssiHist{$amac}{"num"} < 60 )
{
# keep statistics to 60 sample (minute) moving window
$rssiHist{$amac}{"num"} += 1;
}
$rssiHist{$amac}{"last"} = $now + 5 ;
$datestring = localtime();
print $lfh "$datestring: $amac Possible valid data point, adding to statistics.\n";
}
}
}
close $lfh;
open($dfh, ">$datfile") or die "Unable to create \"$datfile\" $!";
for (keys %rssiHist)
{
if ($antnum)
{
print $dfh "$_|$rssiHist{$_}{'aveH'}|$rssiHist{$_}{'aveV'}|$rssiHist{$_}{'sdH'}|";
print $dfh "$rssiHist{$_}{'sdV'}|$rssiHist{$_}{'num'}|$rssiHist{$_}{'last'}\n";
}
else
{
print $dfh "$_|$rssiHist{$_}{'aveH'}|$rssiHist{$_}{'sdH'}|$rssiHist{$_}{'num'}|$rssiHist{$_}{'last'}\n";
}
}
close $dfh;
# when logfile gets 1k over $MAXSIZE, then chop down
$MAXSIZE = 2**14;
exit 0 unless -s $logfile > $MAXSIZE + 1024;
@ARGV = $logfile;
undef $/;
$^I = "";
while (<>)
{
substr($_, 0, length() - $MAXSIZE) = "";
s/.*\n//;
print;
}

View File

@ -50,46 +50,79 @@ sub usage
exit; exit;
} }
sub freq_to_chan
{
my ($freq) = @_;
if ($freq < 256 )
{
return $freq;
}
elsif ($freq == 2484)
{
return "14";
}
elsif ($freq == 2407)
{
return 0;
}
elsif ($freq < 2484)
{
return ($freq-2407)/5;
}
elsif ($freq < 5000)
{
return $freq ; # stay in freq, no ch #s
}
elsif ($freq < 5380) # these are 5Ghz channels 75 and below
{
return ($freq-5000)/5 ;
}
elsif ($freq < 5500) # ch 76 to 99 can only be 3Ghz freq, no part 15 here
{
return ($freq-2000); # return 3ghz freq (5ghz boards with -2Ghz transverter)
}
elsif ($freq < 6000)
{
return ($freq-5000)/5;
}
else
{
return $freq;
}
}
sub pushAP sub pushAP
{ {
my($signal, $chan, $key, $ssid, $mac, $mode) = @_; my($signal, $freq, $key, $ssid, $host, $mac, $mode) = @_;
return if $mode eq ""; return if $mac eq "";
return if $openap and ($key ne "off" or $mode ne "Master"); return if $openap and ($key ne "");
$chan=freq_to_chan($freq);
if($ssid eq "") { $ssid = "(hidden)" } if($ssid eq "") { $ssid = "(hidden)" }
if($chan eq "") { $chan = "?" }
if($key eq "none") { $key = " " } if($key eq "") { $key = " " }
else { $key = "*" } else { $key = "*" }
$mac =~ /^(\w\w):(\w\w):(\w\w):(\w\w):(\w\w):(\w\w)/;
$mac1 = $1 . $2 . $3;
$mac2 = $4 . $5 . $6;
$mac = $mac1 . $mac2;
if ($ssid =~ /AREDN-(5|10|20)-v[3456]$/) {$typenet = "AREDN" }
elsif ($ssid =~ /BroadbandHamnet-(5|10|20)-v[3456]$/) {$typenet = "BroadbandHamnet" }
elsif ($mode eq "Master") {$typenet = "AP" }
elsif ($mode eq "Managed") {$typenet = "Client"}
elsif ($mode eq "Ad-Hoc" and $ssid =~ /(.*)-(5|10|20)-v[3456]$/ ) {$typenet = $1 }
else {$typenet = $mode}
if($avg) if($avg)
{ {
$avgs{"$mac total"} += $signal; $avgs{"$mac total"} += $signal;
$avgs{"$mac num"} += 1; $avgs{"$mac num"} += 1;
$aphash{$mac} = sprintf "%2d %s %-32s %s:%s %s\n", $aphash{$mac} = sprintf "% 3d %s %-32s\t%s\t%s\t%s\n",
$chan, $key, $ssid, $mac1, $mac2, $typenet; $chan, $key, $ssid, $host, $mac, $mode;
} }
elsif($web) elsif($web)
{ {
push @list, sprintf "%03d|%d|%s|%s|%s:%s|%s", push @list, sprintf "% 3d|%d|%s|%s|%s|%s|%s",
$signal, $chan, $key, $ssid, $mac1, $mac2, $typenet; $signal, $chan, $key, $ssid, $host, $mac, $mode;
} }
else else
{ {
push @list, sprintf "%3d %2d %s %-32s %s:%s %s\n", push @list, sprintf "% 3d %2d %s %-32s\t%s\t%s\t%s\n",
$signal, $chan, $key, $ssid, $mac1, $mac2, $typenet; $signal, $chan, $key, $ssid, $host, $mac, $mode;
} }
} }
@ -124,8 +157,8 @@ die "bad interface" if not defined $iface;
if($raw) if($raw)
{ {
system("/usr/bin/iwinfo $iface scan"); system("/usr/bin/iw dev $iface scan");
system("/usr/sbin/iw $iface station dump"); system("/usr/sbin/iw dev $iface station dump");
exit; exit;
} }
@ -134,42 +167,60 @@ while(1)
$line = `grep ssid /etc/config/wireless | tail -1`; $line = `grep ssid /etc/config/wireless | tail -1`;
$line =~ /['"](.*-(5|10|20)-v[3456])/; $line =~ /['"](.*-(5|10|20)-v[3456])/;
$myssid = $1; $myssid = $1;
$myssid =~ /(.*)-(5|10|20)-v[3456]/; $mychan = `iw dev $iface info | grep channel | cut -d\\ -f2`;
$mymode = $1; if ($mychan >= 76 and $mychan <= 99)
{
$mychan = ($mychan*5+3000); # ch 76 - 99 are 3ghz since no part 15 usage (5ghz board with -2ghz transverter)
}
open(FILE, "/usr/bin/iwinfo $iface scan 2>&1 |") or die "iwinfo failed"; open(FILE, "/usr/sbin/iw dev $iface scan 2>&1 |") or die "iw scan failed";
$mode = ""; $mac = "";
$ssid = ""; $host = "N/A";
$signal = 0; $lastseen = 0;
$chan = 99;
@list = (); @list = ();
while($line = <FILE>) while($line = <FILE>)
{ {
if($line =~ /Cell \d+ - Address: (\S+)/ ) if($line =~ /BSS\s+(([[:xdigit:]]{2}:){5}[[:xdigit:]]{2})/)
{ {
if ($ssid ne $myssid) { pushAP($signal, $chan, $key, $ssid, $mac, $mode)} if ( $lastseen < 10000 ) { pushAP($signal, $chan, $key, $ssid, $host, $mac, $mode) }
$mac = $1; $mac = uc $1;
$mode = "AP";
$ssid = "";
$signal = 0;
$chan = "";
$key = "";
$lastseen = 0;
} }
if($line =~ /\bESSID: "(.*)"/) { $ssid = $1 } if($line =~ /BSS(\s+)([[:xdigit:]]{2}:){5}[[:xdigit:]]{2}.*joined/)
if($line =~ /\bESSID: unknown/) { $ssid = "unknown" } {
if($line =~ /\bMode: (\S+)/) { $mode = $1 } $mode = "My Ad-Hoc Network";
if($line =~ /\bChannel: (\d+)/) { $chan = $1 } $chan = $mychan;
if($line =~ /\bSignal: ([\d-]+)/) { $signal = $1 } }
if($line =~ /\bEncryption: (\w+)/) { $key = $1 } if($line =~ /\bSSID: (.*)/) { $ssid = $1 }
if($line =~ /\bSSID: unknown/) { $ssid = "unknown" }
if($line =~ /\bcapability: (IBSS)/ and $mode eq "AP")
{ $mode = "Foreign Ad-Hoc Network" }
if($line =~ /\bfreq: (\d+)/) { $chan = $1 }
if($line =~ /\bsignal: ([\d-]+)/) { $signal = $1 }
if($line =~ /\bGroup cipher:(.+)/) { $key = $1 }
if($line =~ /\blast seen: (\d+)/) { $lastseen = $1 }
} }
close(FILE); close(FILE);
if ($ssid ne $myssid) {pushAP($signal, $chan, $key, $ssid, $mac, $mode)} if ( $lastseen < 10000 ) { pushAP($signal, $chan, $key, $ssid, $host, $mac, $mode) }
sleep 1 if not scalar @list and $loops != 1; sleep 1 if not scalar @list and $loops != 1;
$mode = ""; $mac = "";
$mode = "Connected Ad-Hoc Station";
$signal = 0; $signal = 0;
$key = "none"; $key = "";
$lastseen = 0;
++$iters; ++$iters;
$bbchan = `iw $iface info | grep channel | cut -d\\ -f2`;
open(FILE, "/usr/sbin/iw $iface station dump 2>&1 |") or die "/usr/sbin/iw failed"; open(FILE, "/usr/sbin/iw $iface station dump 2>&1 |") or die "/usr/sbin/iw failed";
@ -177,30 +228,33 @@ while(1)
{ {
if($line =~ /Station (\S+) \(on $iface\)/) if($line =~ /Station (\S+) \(on $iface\)/)
{ {
pushAP($signal, $bbchan, $key, $ssid, $mac, $mode); if ( $lastseen < 10000 ) { pushAP($signal, $mychan, $key, $myssid, $host, $mac, $mode) }
$lastseen = 0;
$mac = $1; $mac = $1;
$ip = `grep $mac /proc/net/arp`; $ip = `grep $mac /proc/net/arp | egrep "^10.*\$" | tail -1`;
$mac = uc $mac; $mac = uc $mac;
if ( $ip ne "" ) if ( $ip ne "" )
{ {
$ip =~ s/[ \t].*$// ; $ip =~ s/[ \t].*$// ;
chomp($ip); chomp($ip);
$ssid = $ip; $host = $ip;
if( $ip ne "") if( $ip ne "")
{ {
foreach(`nslookup $ip`){ next unless ($ssid) = /Address 1: $ip (\S+)\.local\.mesh/ } foreach(`nslookup $ip`){ next unless ($host) = /Address 1: $ip (\S+)/ }
if ( $ssid eq "" ) { $ssid = $ip } if ( $host eq "" ) { $host = $ip }
} }
} }
else { $ssid = "????" } else { $host = "????" }
$mode=$mymode;
} }
if($line =~ /signal avg:[ \t]+([-\d]+)/) { $signal = $1 } if($line =~ /signal avg:[ \t]+([-\d]+)/) { $signal = $1 }
if($line =~ /inactive time:\t(\d+) ms/) { $lastseen = $1 }
} }
close(FILE); close(FILE);
pushAP($signal, $bbchan, $key, $ssid, $mac, $mode); if ( $lastseen < 10000 ) { pushAP($signal, $mychan, $key, $myssid, $host, $mac, $mode) }
sleep 1 if not scalar @list and $loops != 1; sleep 1 if not scalar @list and $loops != 1;
if(not $batch) if(not $batch)
@ -208,19 +262,19 @@ while(1)
if($avg) if($avg)
{ {
system "clear"; system "clear";
printf "Sig Rel Ch E SSID or Hostname MAC Mode %6d\n", $iters; printf "Sig Rel Ch E SSID Hostname MAC/BSSID 802.11 Mode %6d\n", $iters;
print "--- --- -- - -------------------------------- ------------- ------\n"; print "--- --- -- - -------------------------------- ----------------- ------------- -----------\n";
} }
elsif($web) elsif($web)
{ {
print "<table class=sortable border=1 cellpadding=5>\n"; print "<table border=1 cellpadding=5>\n";
print "<tr><th>Sig</th><th>Chan</th><th>Enc</th><th>SSID or Hostname</th><th>MAC</th><th>Mode</th></tr>\n"; print "<tr><th>Sig</th><th>Chan</th><th>Enc</th><th>SSID</th><th>Hostname</th><th>MAC/BSSID</th><th>802.11 Mode</th></tr>\n";
} }
else else
{ {
#system "clear"; #system "clear";
printf "Sig Ch E SSID or Hostname MAC Mode %6d\n", $iters; printf "Sig Ch E SSID Hostname MAC/BSSID 802.11 Mode %6d\n", $iters;
print "--- -- - -------------------------------- ------------- ------\n"; print "--- -- - -------------------------------- --------------------- ------------- ------------\n";
} }
} }
@ -251,7 +305,7 @@ while(1)
if($i++ == 3) { print "<td>$val</td>" } if($i++ == 3) { print "<td>$val</td>" }
else { print "<td align=center>$val</td>" } else { print "<td align=center>$val</td>" }
} }
print "<td>&nbsp;</td>" if $i < 6; print "<td>&nbsp;</td>" if $i < 7;
print "</tr>\n"; print "</tr>\n";
} }
print "</table>\n"; print "</table>\n";

View File

@ -49,7 +49,7 @@ and be marked in reasonable ways as differentiate it from the original
version.</br> version.</br>
</br> </br>
AREDN&trade; Terms:</br> AREDN&trade; Terms:</br>
In accordance with the permissions granted under section 10(e) of the GPLv3 License: </br> In accordance with the permissions granted under section 7(e) of the GPLv3 License: </br>
</br> </br>
The AREDN&trade; trademark and AREDN&trade; logo and for use in official Amateur Radio Emergency Data Network (AREDN&trade;) sanctioned builds. </br> The AREDN&trade; trademark and AREDN&trade; logo and for use in official Amateur Radio Emergency Data Network (AREDN&trade;) sanctioned builds. </br>
</br> </br>

View File

@ -228,7 +228,34 @@ if($fw_install and -f "$tmpdir/firmware")
{ {
if ( $parms{checkbox_keep_settings} ) if ( $parms{checkbox_keep_settings} )
{ {
open (my $SYSUPGRADECONF, "/etc/arednsysupgrade.conf") or die "Failed to open arednsysupgrade.conf";
open (my $TMPSYSUPGRADECONF, '>', "/tmp/sysupgradefilelist") or die "Failed to open TMPSYSUPGRADECONF";
while (<$SYSUPGRADECONF>){
chomp;
next if /^\#/ ;
if ( -e "$_" ) {
print $TMPSYSUPGRADECONF "$_\n";
}
}
close $SYSUPGRADECONF;
close $TMPSYSUPGRADECONF;
nvram_set("nodeupgraded","1"); nvram_set("nodeupgraded","1");
system("tar -czf /tmp/arednsysupgradebackup.tgz -T /tmp/sysupgradefilelist");
if ($? == 1) {
print "
<center><h2>ERROR: Could not backup filesystem.</h2>
<h3>An error occured trying to backup the file system
</center>
";
page_footer();
print "</body></html>";
nvram_set("nodeupgraded","0");
exit 1;
}
system("rm -f /tmp/sysupgradefilelist");
print " print "
<center><h2>Firmware will be written in the background.</h2> <center><h2>Firmware will be written in the background.</h2>
<h3>If you are connected to the LAN of this node you may need to acquire a new<br> <h3>If you are connected to the LAN of this node you may need to acquire a new<br>
@ -240,7 +267,7 @@ When the Status 4 LED is solid on you can get your new DHCP lease and reconnect
(This page will automatically reload in 2&frac12; minutes)</h3> (This page will automatically reload in 2&frac12; minutes)</h3>
</center></body></html> </center></body></html>
"; ";
open(FILE, "/sbin/sysupgrade -q $tmpdir/firmware 2>&1 |") or die; open(FILE, "/sbin/sysupgrade -f /tmp/arednsysupgradebackup.tgz -q $tmpdir/firmware 2>&1 |") or die;
} }
else else
{ {

View File

@ -954,19 +954,21 @@ sub hardware_info
'name' => 'TP-Link CPE210 v1.0', 'name' => 'TP-Link CPE210 v1.0',
'comment' => '', 'comment' => '',
'supported' => '-2', 'supported' => '-2',
'maxpower' => '27', 'maxpower' => '23',
'pwroffset' => '0', 'pwroffset' => '0',
'usechains' => 1, 'usechains' => 1,
'rfband' => '2400', 'rfband' => '2400',
'chanpower' => { 1 => '22', 14 => '23' },
}, },
'TP-Link CPE510 v1.0' => { 'TP-Link CPE510 v1.0' => {
'name' => 'TP-Link CPE510 v1.0', 'name' => 'TP-Link CPE510 v1.0',
'comment' => '', 'comment' => '',
'supported' => '-2', 'supported' => '-2',
'maxpower' => '27', 'maxpower' => '23',
'pwroffset' => '0', 'pwroffset' => '0',
'usechains' => 1, 'usechains' => 1,
'rfband' => '5800ubntus', 'rfband' => '5800ubntus',
'chanpower' => { 48 => '10', 149 => '17', 184 => '23' },
}, },
'0xc2a2' => { '0xc2a2' => {
'name' => 'Bullet 2 HP', 'name' => 'Bullet 2 HP',
@ -1036,6 +1038,15 @@ sub hardware_info
'usechains' => 1, 'usechains' => 1,
'rfband' => '2400', 'rfband' => '2400',
}, },
'0xe105' => {
'name' => 'Rocket M5',
'comment' => 'Rocket M5 with USB',
'supported' => '1',
'maxpower' => '22',
'pwroffset' => '5',
'usechains' => 1,
'rfband' => '5800ubntus',
},
'0xe1b2' => { '0xe1b2' => {
'name' => 'Rocket M2', 'name' => 'Rocket M2',
'comment' => '', 'comment' => '',
@ -1268,8 +1279,22 @@ sub hardware_info
# Return maximum dbm value for tx power # Return maximum dbm value for tx power
sub wifi_maxpower sub wifi_maxpower
{ {
my ($wifichannel) = @_;
$boardinfo = hardware_info(); $boardinfo = hardware_info();
if ( exists $boardinfo->{'maxpower'} ) {
if ( exists $boardinfo->{'chanpower'} ) {
my $chanpower=$boardinfo->{'chanpower'};
foreach ( sort {$a<=>$b} keys %{$chanpower} )
{
if ( $wifichannel <= $_ )
{
return $chanpower->{$_};
}
}
# We should never get here
return 27;
} elsif ( exists $boardinfo->{'maxpower'} ) {
return $boardinfo->{'maxpower'}; return $boardinfo->{'maxpower'};
} else } else
{ {

View File

@ -140,7 +140,7 @@ unless($parms{reload})
} }
# sanitize the active settings # sanitize the active settings
$wifi_txpower = wifi_maxpower() if not defined $wifi_txpower or $wifi_txpower > wifi_maxpower(); $wifi_txpower = wifi_maxpower($wifi_channel) if not defined $wifi_txpower or $wifi_txpower > wifi_maxpower($wifi_channel);
$wifi_txpower = 1 if $wifi_txpower < 1; $wifi_txpower = 1 if $wifi_txpower < 1;
$wifi_distance = 0 unless defined $wifi_distance; $wifi_distance = 0 unless defined $wifi_distance;
$wifi_distance = 0 if $wifi_distance =~ /\D/; $wifi_distance = 0 if $wifi_distance =~ /\D/;
@ -600,8 +600,8 @@ if($wifi_proto ne "disabled")
print "<tr><td><nobr>Tx Power</nobr></td>\n"; print "<tr><td><nobr>Tx Power</nobr></td>\n";
print "<td><select name=wifi_txpower>\n"; print "<td><select name=wifi_txpower>\n";
my $txpoweroffset = wifi_txpoweroffset(); my $txpoweroffset = wifi_txpoweroffset();
for($i = wifi_maxpower(); $i >= 1; --$i) { selopt($i+$txpoweroffset ." dBm", $i, $wifi_txpower) } for($i = wifi_maxpower($wifi_channel); $i >= 1; --$i) { selopt($i+$txpoweroffset ." dBm", $i, $wifi_txpower) }
print "</select></td></tr>\n"; print "</select>&nbsp;&nbsp;<a href=\"/help.html\#power\" target=\"_blank\"><img src=\"/qmark.png\"></a></td></tr>\n";
print "<tr><td>Distance</td>\n"; print "<tr><td>Distance</td>\n";
print "<td><input type=text size=8 name=wifi_distance value='$wifi_distance' title='Distance in meters to the farthest neighbor'>&nbsp;meters</td></tr>\n"; print "<td><input type=text size=8 name=wifi_distance value='$wifi_distance' title='Distance in meters to the farthest neighbor'>&nbsp;meters</td></tr>\n";

View File

@ -48,6 +48,8 @@ use perlfunc;
"/etc/mesh-release", "/etc/mesh-release",
"/tmp/etc/", "/tmp/etc/",
"/var/run/hosts_olsr", "/var/run/hosts_olsr",
"/tmp/rssi.dat",
"/tmp/rssi.log",
); );
@sensitive = ( "/etc/config/vtun", @sensitive = ( "/etc/config/vtun",
@ -74,6 +76,9 @@ use perlfunc;
"iwinfo", "iwinfo",
"iwinfo wlan0 assoclist", "iwinfo wlan0 assoclist",
"iw phy phy0 info", "iw phy phy0 info",
"iw dev wlan0 info",
"iw dev wlan0 scan",
"iw dev wlan0 station dump",
"logread", "logread",
"md5sum /www/cgi-bin/*", "md5sum /www/cgi-bin/*",
"echo /all | nc 127.0.0.1 2006", "echo /all | nc 127.0.0.1 2006",

View File

@ -84,6 +84,21 @@ $myssid = $1;
$myssid =~ /(.*)-(5|10|20)-v[3456]/; $myssid =~ /(.*)-(5|10|20)-v[3456]/;
$info{"ssid"}=$myssid; $info{"ssid"}=$myssid;
# ------- Wifi Channel Number
$mychan = `uci get wireless.radio0.channel`;
chomp($mychan);
# 3GHZ channel -> Freq conversion
if ($mychan >= 76 and $mychan <= 99) {
$info{"channel"}=($mychan * 5) + 3000;
} else {
$info{"channel"}=$mychan;
}
# ------- Wifi Bandwidth
$line = `uci get wireless.radio0.chanbw`;
chomp($line);
$info{"chanbw"}=$line;
# ------- ACTIVE TUNNELS # ------- ACTIVE TUNNELS
$active_tunnel_count=`ifconfig|grep tun|wc -l`; $active_tunnel_count=`ifconfig|grep tun|wc -l`;
chomp($active_tunnel_count); chomp($active_tunnel_count);

View File

@ -198,7 +198,7 @@ sub vpn_setup_required()
} }
print "<tr><td align=center><br><b>"; print "<tr><td align=center><br><b>";
print "Tunnel software needs to be installed.<br/>"; print "Tunnel software needs to be installed.<br/>";
print "<form method='post' action='/cgi-bin/vpn' enctype='multipart/form-data'>\n"; print "<form method='post' action='/cgi-bin/$navpage' enctype='multipart/form-data'>\n";
print "<input type=submit name=button_install value='Click to install' class='btn_tun_install' />"; print "<input type=submit name=button_install value='Click to install' class='btn_tun_install' />";
print "</form>"; print "</form>";
print "</b></td></tr>\n"; print "</b></td></tr>\n";

View File

@ -148,6 +148,7 @@ foreach $val (@list)
# password MUST be alphanumeric (no special chars) # password MUST be alphanumeric (no special chars)
push @cli_err, "The password cannot contain non-alphanumeric characters (#$client_num)" if ($passwd =~ m/[^a-zA-Z0-9@]/); push @cli_err, "The password cannot contain non-alphanumeric characters (#$client_num)" if ($passwd =~ m/[^a-zA-Z0-9@]/);
push @cli_err, "The password must contain at least one alphabetic character (#$client_num)" if ($passwd !~ /\D/);
push @cli_err, "A client name is required" if($name eq ""); push @cli_err, "A client name is required" if($name eq "");
push @cli_err, "A client password is required" if($passwd eq ""); push @cli_err, "A client password is required" if($passwd eq "");

View File

@ -133,7 +133,7 @@ foreach $val (@list)
} }
# password MUST be alphanumeric (no special chars) # password MUST be alphanumeric (no special chars)
push @conn_err, "The password cannot contain non-alphanumeric characters (#$conn_num)" if ($passwd =~ m/[^a-zA-Z0-9@]/); push @conn_err, "The password cannot contain non-alphanumeric characters (#$conn_num)" if ($passwd =~ m/[^a-zA-Z0-9@\-]/);
push @conn_err, "A connection server is required" if($host eq ""); push @conn_err, "A connection server is required" if($host eq "");
push @conn_err, "A connection password is required" if($passwd eq ""); push @conn_err, "A connection password is required" if($passwd eq "");
push @conn_err, "A connection network IP is required" if($netip eq ""); push @conn_err, "A connection network IP is required" if($netip eq "");
@ -222,16 +222,16 @@ push @hidden, "<input type=hidden name=reload value=1></td></tr>";
################# #################
# messages # messages
################# #################
if(@cli_err) if(@conn_err)
{ {
print "<tr><td align=center><b>ERROR:<br>"; print "<tr><td align=center><b>ERROR:<br>";
foreach(@cli_err) { print "$_<br>" } foreach(@conn_err) { print "$_<br>" }
print "</b></td></tr>\n"; print "</b></td></tr>\n";
} }
if($parms{button_save}) if($parms{button_save})
{ {
if(@cli_err) if(@conn_err)
{ {
print "<tr><td align=center><b>Configuration NOT saved!</b></td></tr>\n"; print "<tr><td align=center><b>Configuration NOT saved!</b></td></tr>\n";
} }
@ -412,18 +412,18 @@ sub save_connections()
my $vtun_node_name=uc "$node-$net"; my $vtun_node_name=uc "$node-$net";
$rc=&uci_set_named_option("vtun","server_$i","clientip",$clientip); $rc=&uci_set_named_option("vtun","server_$i","clientip",$clientip);
push(@cli_err,"Problem saving UCI vtun connection client IP (#$i)") if $rc; push(@conn_err,"Problem saving UCI vtun connection client IP (#$i)") if $rc;
$rc=&uci_set_named_option("vtun","server_$i","serverip",$serverip); $rc=&uci_set_named_option("vtun","server_$i","serverip",$serverip);
push(@cli_err,"Problem saving UCI vtun connection server IP (#$i)") if $rc; push(@conn_err,"Problem saving UCI vtun connection server IP (#$i)") if $rc;
$rc=&uci_set_named_option("vtun","server_$i","node",$vtun_node_name); $rc=&uci_set_named_option("vtun","server_$i","node",$vtun_node_name);
push(@cli_err,"Problem saving UCI vtun connection name (#$i)") if $rc; push(@conn_err,"Problem saving UCI vtun connection name (#$i)") if $rc;
foreach $var (qw(enabled host passwd netip)) foreach $var (qw(enabled host passwd netip))
{ {
$rc=&uci_set_named_option("vtun","server_$i",$var,$parms{"conn${i}_$var"}); $rc=&uci_set_named_option("vtun","server_$i",$var,$parms{"conn${i}_$var"});
push(@cli_err,"Problem saving UCI vtun connection (#$i)") if $rc; push(@conn_err,"Problem saving UCI vtun connection (#$i)") if $rc;
} }
$enabled_count++ if $parms{"conn${i}_enabled"}; $enabled_count++ if $parms{"conn${i}_enabled"};
} }

View File

@ -71,12 +71,14 @@ provided through those nodes.
provides which gives you detailed information about the current state of the provides which gives you detailed information about the current state of the
OLSR routing software.<br><br></li> OLSR routing software.<br><br></li>
<li><strong>WiFi Scan</strong> provides a list of the WiFi networks that the <li><strong>WiFi Scan</strong> displays a list of other 802.11 signals that the
node can see. It cannot show you which other nodes are visible, that is what the node can see and only of the same bandwidth. The 802.11 signals include
Mesh Status and OLSR Status pages are for. There is an automatic scan mode but it Access Points (AP), neighbor nodes (connected ad-hoc stations), and other mesh networks
is recommended that it not be used continuously because the mesh performance (foriegn ad-hoc networks). The AREDN mesh is created on top of an 802.11 'ad-hoc' network.
will suffer due to the node spending much of its time on other channels Consequently when multiple ad-hoc networks are visiable to each other (different SSID or
looking for other networks.<br><br></li> channel), the 'network' is displayed and not individual nodes (stations). There is also an
automatic scan mode. It is not recommend to run a wifi scan continously because this will degrade mesh
performance. A wifi scan transmits queries on all channels to discover other devices.<br><br></li>
<li><strong>Setup</strong> takes you to the setup pages of the web <li><strong>Setup</strong> takes you to the setup pages of the web
interface. You will need to supply a username and password to access those interface. You will need to supply a username and password to access those
@ -285,6 +287,14 @@ As always a dummy load on unused RF ports is recommended to keep out physical
contaminants and to avoid EMI/RFI interference. contaminants and to avoid EMI/RFI interference.
</p> </p>
<p> <p>
<a name="power"></a>The <strong>Power</strong> setting controls the max power
the unit may output. The node may decrease its power output as it enters higher
speed data rates to maintain a linear spectrum. Some devices may have max power
levels that change based on what channel/frequency the hardware is operating on,
in this case the max level will change when you save the settings and will be
capped at the max level supported by the hardware for that frequency.
</p>
<p>
The <strong>Distance</strong> setting adjusts the packet retry timer The <strong>Distance</strong> setting adjusts the packet retry timer
to account for stations that are very far away, presumably about 300 meters or to account for stations that are very far away, presumably about 300 meters or
more. The value should be set to the distance in meters to the farthest node more. The value should be set to the distance in meters to the farthest node

BIN
files/www/qmark.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 361 B