Advertisement
eliphas

Ubiquiti NanoBeam/XW compliance test

May 19th, 2015
653
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Bash 17.37 KB | None | 0 0
  1. #!/bin/sh
  2. # vim:sw=8:ts=8:ai:foldmethod=indent
  3. # Compliance Test scanner tabajara.
  4. # You should be using your country's channels only, but who is me to judge...
  5. VERSION="2014.12.26.01"
  6. # Tested and confirmed working on:
  7. # NANOBEAM M5 19db with FW version(s):
  8. # XW.v5.5.9
  9. # XW.v5.5.10
  10.  
  11. # Ubiquiti AirOS NanoBeam/NanoStation/Rocket XW compliance test kludge script
  12. # Please report other successful deployments in forums and share your improvements!
  13. # If I can merge it back, please say so in your posts. Thanks ':)
  14.  
  15. cat <<EOF >/dev/null
  16. ******************************** READ ME FIRST ********************************
  17. MODE OF OPERATION:
  18. - Can be run manually for one scan, with no bandwidth change.
  19. - Designed to be run from rc.poststart, with "auto" argument. Will background
  20.   itself.
  21. - Refuses to run two or more instances (checks for a lock file)
  22. - Refuses to run in AP mode (only managed/"station")
  23. - Refuses to run if channel list is enabled (if you have a list, you already
  24.   did the hard work yourself!)
  25. - Writes to the system logger and /tmp/scriptname.log (only to console when in
  26.   manual mode) with all it is doing. Keep the system log enabled!
  27. - Bandwitdh hopping: Read below!
  28.  
  29. THEORY OF OPERATION:
  30. - Because of the quantity of available channels on compliance test mode, the
  31.   station will take an eternity plus a day to link to some channels, and
  32.   sometimes will not detect the AP on the site survey.
  33. - If the radio is associated while the script is running, it just sleeps.
  34. - If not associated, the script will limit the quantity of channels to be
  35.   scanned, and submit a survey, pausing a little, then checking if it is
  36.   associated. Rinse and repeat until no more channels are to be tested.
  37. - If bandwidth hopping is disabled, it will just start from scratch trying all
  38.   channels again. To enable hopping, see below.
  39.  
  40. BANDWIDTH HOPPING BEHAVIOR:
  41. - After trying all channels on current bandwidth mode, if it STILL is not
  42.   associated, it will:
  43.   * Note original bandwidth and count of retries it can make
  44.   * Change to another bandwidth, reboot, scan
  45.   * When every bandwidth is tested, it adds to the counter of retries
  46.   * When retries count is reached, reverts to original bandwidth and wait.
  47.   * If the association is successful, reset counter and wait.
  48. - This is managed by the "startup date" option. If it is UNCHECKED, the script
  49.   will NEVER try to change BW mode. If CHECKED, the numeric DAY of the startup
  50.   date is the count of tries the script will use to try scanning all the
  51.   bandwidths. If it is 1, the script will stop trying new bandwidths after one
  52.   try on all the ones. I would recommend setting it to 2 or 3 at most -- if it
  53.   cannot do it in three times, the AP is dead.
  54. - Yeah, you only have 31 tries at most, but it is better than rebooting ad
  55.   infinitum if the AP is down. You can always ask someone to unplug/plug the
  56.   power and it will start over again from where it stopped.
  57. - We save every change. If you reboot the radio while it is not associated yet,
  58.   it will continue scanning where it left off. Mind this!
  59.  
  60. PARAMETERS:
  61. Set "bw_all" to the ordered list of your most used bandwidths.
  62. Experiment with "try_count" and "try_sleep" variables, on a radio you can
  63. locally SSH into and kill the script. In my experience too much "try_count" and
  64. too little "try_sleep" IS BAD.
  65.  
  66. EOF
  67.  
  68. # CHANGELOG:
  69. # - 2014.12.26.01
  70. # + Fixes:
  71. #   Script name was hardcoded on install() - using basename $0 now.
  72. # - 2014.11.15.03
  73. # + Fixes:
  74. #   Randomization of undetected channels being re-sorted before use
  75. #   Wait in auto mode until system is up, so can write logs
  76. # + Cosmetic:
  77. #   Split logged/printed channel list to at most 15 channels/line
  78. #   Print board name and firmware version at startup
  79. #   Print running parameters when unassociated
  80. # + Improvements:
  81. #   Parameters f_log, f_config, max_log_lines added
  82. #   Truncate f_log to $max_log_lines to save memory
  83. #   Will read file $f_config for parameters on each loop - this means that
  84. #     you can now modify the parameters at runtime!
  85. #   Removing $f_lock file (rm /var/lock/CTscan.lock) makes the script exit.
  86. #
  87. # - 2014.11.03.32
  88. # + First internal release
  89.  
  90. ### PARAMETERS ###
  91. # List of bandwidth ranges - change ranges in this order after trying the
  92. # current bandwidth and not associating.
  93. # Notice the trailing space for consistency. Keep it.
  94. # You could add 20MHz too but it is redundant as 20/40 does it.
  95. bw_all="20/40 30 10 5 25 "
  96. # Channel count to be locked on each try - will build a random list of N channels
  97. try_count=50
  98. # Wait time (seconds) for each channel list try.
  99. try_sleep=5
  100. # Wait time (seconds) on the main loop - the "are we associated yet?" scan
  101. loop_sleep=5
  102. # Max log lines to keep on $f_logfile
  103. max_log_lines=100
  104. ### END OF PARAMETERS ###
  105.  
  106. # Files used (other than tmp ones)
  107. f_lock=/var/lock/$(basename $0).lock # refuse to run while this file is in place.
  108. f_persist=$0.persist # here we save our progress between reboots
  109. f_syscfg=/tmp/system.cfg # when changing bandwidth, will write this
  110. f_poststart=/etc/persistent/rc.poststart
  111. f_log=/tmp/$(basename $0).log
  112. f_config=$0.config
  113.  
  114. # Bandwidth variables.
  115. bw_20_40="0 1 1 11naht40";
  116. bw_30="30 1 0 11naht20";
  117. bw_25="25 1 0 11naht20"
  118. bw_20="0 1 0 11naht20";
  119. bw_10="0 2 0 11naht20";
  120. bw_5="0 4 0 11naht20"
  121. getbw() { # get configured bandwidth in MHz, from the config file
  122.     chanbw=$(sed -n 's/^radio\.1\.chanbw=//p' $f_syscfg)
  123.     clksel=$(sed -n 's/^radio\.1\.clksel=//p' $f_syscfg)
  124.     cwmmode=$(sed -n 's/^radio\.1\.cwm\.mode=//p' $f_syscfg)
  125.     ieee_mode=$(sed -n 's/^radio\.1\.ieee_mode=//p' $f_syscfg)
  126.     bw="$chanbw $clksel $cwmmode $ieee_mode"
  127.     [ "$bw" = "$bw_20_40" ] && echo 20/40 && return 0
  128.     [ "$bw" = "$bw_30" ] && echo 30 && return 0
  129.     [ "$bw" = "$bw_25" ] && echo 25 && return 0
  130.     [ "$bw" = "$bw_20" ] && echo 20 && return 0
  131.     [ "$bw" = "$bw_10" ] && echo 10 && return 0
  132.     [ "$bw" = "$bw_5"  ] && echo 5  && return 0
  133. }
  134. setbw() { # set bandwith on config file
  135.     if   [ "$1" = "20/40" ]; then bw=$bw_20_40
  136.     elif [ "$1" = "30" ]; then bw=$bw_30
  137.     elif [ "$1" = "25" ]; then bw=$bw_25
  138.     elif [ "$1" = "20" ]; then bw=$bw_20
  139.     elif [ "$1" = "10" ]; then bw=$bw_10
  140.     elif [ "$1" = "5" ];  then bw=$bw_5
  141.     else return 1
  142.     fi
  143.     set - $bw
  144.     sed -i "
  145.     s/^\(radio\.1\.chanbw\)=.*$/\1=$1/
  146.     s/^\(radio\.1\.clksel\)=.*$/\1=$2/
  147.     s/^\(radio\.1\.cwm.mode\)=.*$/\1=$3/
  148.     s/^\(radio\.1\.ieee_mode\)=.*$/\1=$4/
  149.     " $f_syscfg
  150. }
  151.  
  152. # Lines which the script will add/remove from poststart
  153. enableCTkey="### CTSCAN-enable"
  154. enableCTstr="echo '<option value=\"511\">Compliance Test</option>' >/etc/ccodes.inc $enableCTkey"
  155. enableSCkey="### CTSCAN-start"
  156. enableSCstr="/bin/sh /etc/persistent/$(basename $0) auto $enableSCkey"
  157. poststartCreate() { # creates poststart if not found
  158.     if ! [ -f "$f_poststart" ]; then
  159.         echo '#!/bin/sh' > $f_poststart
  160.         chmod +x $f_poststart
  161.     fi
  162. }
  163. install() { # add autostart
  164.     poststartCreate
  165.     sed -i "/$enableSCkey/d; $ a\
  166. $enableSCstr" $f_poststart
  167.     echo "CTscan added to autostart, save and reboot to apply."
  168. }
  169. uninstall() { # remove autostart
  170.     sed -i "/$enableSCkey/d" $f_poststart
  171.     echo "CTscan removed from autostart, save and reboot to apply."
  172. }
  173. enableCT() { # enable compliance test.
  174.     poststartCreate
  175.     sed -i '/^radio\.\(1\.\)\?countrycode/s/=.*/=511/' $f_syscfg
  176.     sed -i "/$enableCTkey/d; $ a\
  177. $enableCTstr" $f_poststart
  178.     echo "Compliance test enabled, save and reboot to apply."
  179. }
  180. disableCT() { # disable compliance test.
  181.     sed -i '/^radio\.\(1\.\)\?countrycode/s/=.*/=840/' $f_syscfg
  182.     sed -i "/$enableCTkey/d" $f_poststart
  183.     echo "Compliance test disabled, save and reboot to apply."
  184.     echo "NOTE: You will then be required to set your country on the web interface!"
  185. }
  186.  
  187. getstartupday() { # get date enabled, and if so, try count
  188.     if fgrep -q 'system.date.status=enabled' $f_syscfg; then
  189.         day=$(sed -n 's/^system\.date\.timestamp=..\([0-9]\{2\}\).*$/\1/p' $f_syscfg)
  190.         echo $((day)) # remove leading zero if exists
  191.         return 0
  192.     else
  193.         echo 0
  194.         return 1
  195.     fi
  196. }
  197. checkassoc() {
  198.     # returns true if associated (mind the starting !)
  199.     ! iwconfig ath0 | grep -q 'Access Point: Not-Associated'
  200.     return $?
  201. }
  202. grepchan() {
  203.     # Parses "iwlist scanning" command and returns only the channels found,
  204.     # sorted, unique; removes channels already tried (kept in $f_fail temp file)
  205.     sed -n "
  206.     # Look for ESSID
  207.     # XXX FIXME I hate putting variables inside a script... This is a point of
  208.     # failure, if YOUR OWN SSID have some special character.
  209.     /ESSID:$1/{
  210.         # Join two lines down that
  211.         N; N;
  212.         # Catch only the frequency
  213.         s/.*Frequency:\([0-9.]\+\) GHz.*/\1/;
  214.         # Format it in MHz
  215.         s/\.//; s/$/000/; s/^\([0-9]\{4\}\).*/\1/;
  216.         # Print
  217.         p;
  218.     }" $f_scan | sort -n | uniq | grep -v -f $f_fail
  219. }
  220. scan() { # Main script loop. Will check, find, change BW
  221.     if [ "$p" = "echolog" ]; then # Wait the system do fs check, mount
  222.         while true; do
  223.             sleep 2
  224.             pgrep syscheck >&- || break
  225.         done
  226.     fi
  227.     $p "CTscan version $VERSION: starting up."
  228.     $p "Firmware version $(cat /etc/version) on a $(sed -n '/^board.name=/s/.*=//p' /etc/board.info)"
  229.     bw_changes_stop=0 # when we already tried it all and gave up change bw, this will be 1
  230.     bw_curr=$(getbw)
  231.     while true; do
  232.         # Reload config
  233.         [ -f "$f_config" ] && source $f_config
  234.         checkdisabled
  235.         [ "$p" = "echolog" ] && sleep $loop_sleep # Early sleep is by design
  236.         if ! checkassoc; then
  237.             # Keeps the log file small
  238.             cur_log_lines=$(wc -l <$f_log)
  239.             [ "$cur_log_lines" -gt "$max_log_lines" ] && sed -i 1,$((cur_log_lines-max_log_lines))d $f_log
  240.             # Find!
  241.             $p 'Trying to find AP by locking onto some channels. Current parameters:'
  242.             $p "bw_curr=$bw_curr bw_all='$bw_all' try_count=$try_count try_sleep=$try_sleep loop_sleep=$loop_sleep"
  243.             if findchan; then
  244.                 # Success, reset/remove session and quiet down.
  245.                 bw_changes_stop=0
  246.                 [ -f "$f_persist" ] && rm $f_persist && savecfg
  247.                 [ "$p" = "echo" ] && return 0
  248.                 continue
  249.             fi
  250.             [ "$p" = "echo" ] && return 1 # Manual mode ends here
  251.             if [ "$bw_changes_stop" = "1" ]; then
  252.                 # We have given up, keep in this bw until someone reboots
  253.                 continue
  254.             fi
  255.             bw_changes_allowed=$(getstartupday)
  256.             if [ "$bw_changes_allowed" = "0" ]; then
  257.                 # bw change is disabled (startup date not enabled!), respect it and keep bw
  258.                 bw_changes_stop=1
  259.                 [ -f "$f_persist" ] && rm $f_persist && savecfg
  260.                 continue
  261.             fi
  262.             # bw change enabled, look for active session (or create one)
  263.             getsession
  264.             if [ "$bw_changes_stop" = "1" ]; then
  265.                 # We already tried looping ($bw_changes_allowed times) and failed in all.
  266.                 # The session file is here just to hint me to give up. So I will.
  267.                 # We should be on the last bandwidth set that worked, keep on it until reboot
  268.                 [ -f "$f_persist" ] && rm $f_persist && savecfg
  269.                 continue
  270.             fi
  271.             checkdisabled
  272.             # DOWN HERE IS THE DESPAIR... REBOOTS
  273.             if [ -n "$bw_new" ]; then
  274.                 # New bw waiting to try, do it
  275.                 sed -i "\,^bw_new=$bw_new$,d" $f_persist
  276.                 $p 'Rebooting on new bandwidth:' $bw_new 'MHz' # Shouldn't have time to read this, but...
  277.                 setbw $bw_new; savecfg; restart
  278.             else
  279.                 # THE END OF THE INTERNET
  280.                 $p 'Raising retry counter'
  281.                 bw_try_count=$((bw_try_count+1))
  282.                 if [ "$bw_try_count" -lt "$bw_changes_allowed" ]; then
  283.                     # We can retry! Add bws again
  284.                     $p 'Resetting bandwidths to try again (try '$bw_try_count')'
  285.                     echobandwidths >> $f_persist
  286.                     sed -i "/^bw_try_count=/s/=.*/=$bw_try_count/" $f_persist
  287.                 else
  288.                     $p 'Retry count exceeded, will stay after reboot'
  289.                     echo "bw_changes_stop=1" > $f_persist
  290.                 fi
  291.             fi
  292.             # Revert to first bw and reboot
  293.             $p 'Reverting to first bandwidth ('$bw_orig'MHz)'
  294.             setbw $bw_orig; savecfg; restart
  295.             # Now it is the end. Really. Remember me. Goodbye. Sniff.
  296.         fi
  297.     done
  298. }
  299. splitN() {
  300.     qt_split=$1
  301.     awk '
  302.     {
  303.         if( (NR%'$qt_split')==0 ) {
  304.             printf("%s\n",$0)
  305.         } else {
  306.             printf("%s ",$0)
  307.         }
  308.     }
  309.     END {
  310.         if( (NR%'$qt_split')!=0 )print ""
  311.     }'
  312. }
  313. findchan() {
  314.     f_scan=$(mktemp -t) # temp file for iwconfig scanned list output
  315.     f_fail=$(mktemp -t) # temp file for failed channels tried
  316.     # grep -f pattern_file will barf if this file is empty
  317.     echo ABCDEFGHIJKLMNOPQRSTUVWXYZ > $f_fail
  318.  
  319.     $p 'Enabling all channels'
  320.     athchans -i ath0 all
  321.  
  322.     $p 'Available channels:'
  323.     full_range=$(athchans -i ath0 -g | sed 's/ /\n/g')
  324.     echo "$full_range" | splitN 15 | while read line; do $p $line; done
  325.  
  326.     $p 'Searching all channels range'
  327.     iwlist ath0 scanning > $f_scan
  328.     $p 'Active channels search complete'
  329.  
  330.     ssid=$(sed -n '/wireless\.[0-9]\.ssid=/{s/.*=//;p;q;}' $f_syscfg)
  331.     $p 'SSID is set to: "'$ssid'"'
  332.  
  333.     checkdisabled
  334.     # First test - try the channels that match the SSID, if any
  335.     test_channels=$(grepchan '"'$ssid'"')
  336.     failcode=1
  337.     if [ -n "$test_channels" ]; then
  338.         $p 'SSID found on full scan, channel(s): '$test_channels
  339.         trychans && failcode=0
  340.         echo "$test_channels" > $f_fail
  341.     fi
  342.     checkdisabled
  343.     # Second test - try every other channel detected
  344.     # Maybe hidden SSID and stuff.
  345.     if [ "$failcode" = "1" ]; then
  346.         test_channels=$(grepchan '')
  347.         if [ -n "$test_channels" ]; then
  348.             $p 'Trying all detected channels'
  349.             trychans && failcode=0
  350.             echo "$test_channels" >> $f_fail
  351.         fi
  352.     fi
  353.     checkdisabled
  354.     # Third -test - try everything else.
  355.     if [ "$failcode" = "1" ]; then
  356.         # Randomize full range list so each channel has a chance to be first
  357.         full_range=$(echo "$full_range" | awk 'BEGIN{srand();}{print rand()"\t"$0}'|sort -n|cut -f2-)
  358.         test_channels=$(echo "$full_range" | grep -v -f $f_fail)
  359.         if [ -n "$test_channels" ]; then
  360.             $p 'Trying the other undetected channels'
  361.             trychans && failcode=0
  362.         fi
  363.     fi
  364.     if [ "$failcode" = "1" ]; then
  365.         $p 'Still unassociated!'
  366.     else
  367.         $p 'We are now officially associated! :)'
  368.         $p $(iwlist ath0 channel|grep Current)
  369.     fi
  370.     rm $f_scan $f_fail
  371.     return $failcode   
  372. }
  373. trychans() {
  374.     echo "$test_channels" | splitN $try_count | while read chan_list; do
  375.         checkdisabled
  376.         $p 'Trying at most '$try_count' channels in '$bw_curr'MHz bandwidth:'
  377.         # Sort the channels printed on the screen, easy to read ;)
  378.         echo "$chan_list" | sed 's/ /\n/g' | sort -n | splitN 15 |
  379.             while read line; do $p $line; done
  380.         # Now set the channels and
  381.         athchans -i ath0 $chan_list
  382.         #iwconfig ath0 commit # beams do not need this, but potato
  383.         $p 'Surveying and waiting for '$try_sleep' seconds'
  384.         # This somehow works faster than just waiting for a fix
  385.         iwlist ath0 scanning >/dev/null
  386.         # Wait...
  387.         waitfor=$try_sleep
  388.         while true; do
  389.             checkassoc && break
  390.             waitfor=$((waitfor-1))
  391.             [ "$waitfor" = "0" ] && break
  392.             sleep 1
  393.         done
  394.         # Check and get outta here
  395.         checkassoc && break
  396.     done
  397.     # Return the results of the last check
  398.     checkassoc
  399.     return $?
  400. }
  401. getsession() {
  402.     bw_new=""
  403.     if [ ! -f "$f_persist" ]; then # no session file, create it
  404.         $p 'Creating session file list of pending bandwidths to try'
  405.         echo bw_orig=$bw_curr > $f_persist
  406.         echo bw_changes_stop=0 >> $f_persist
  407.         echo bw_try_count=0 >> $f_persist
  408.         echobandwidths >> $f_persist
  409.     fi
  410.     source $f_persist
  411. }
  412. echobandwidths() {
  413.     # reverse bandwidths to try, so the more preferred gets set after all others
  414.     echo "$bw_all" | sed 's/ /\n/g' | sed '1!G;h;$!d' | while read bw; do
  415.         [ "$bw" = "$bw_curr" ] && continue
  416.         [ -z "$bw" ] && continue
  417.         echo bw_new=$bw
  418.     done
  419. }
  420. savecfg() {
  421.     $p 'Saving'
  422.     cfgmtd -w -p /etc/
  423.     return 0
  424. }
  425. restart() {
  426.     # I won't take chances with softrestart :)
  427.     #/usr/etc/rc.d/rc.softrestart save
  428.     rm $f_lock # just in case
  429.     reboot
  430.     exit 1 # just in case too
  431. }
  432. echolog() {
  433.     logger $@
  434.     echo $(date) $@ >> $f_log
  435.     return 0
  436. }
  437. checkdisabled() {
  438.     if [ ! -f "$f_lock" ]; then
  439.         $p 'lock file removed, exiting at user request'
  440.         exit 99
  441.     fi
  442. }
  443.  
  444. if [ "$1" = "enableCT" -o "$1" = "disableCT" -o "$1" = "install" -o "$1" = "uninstall" ]; then
  445.     $1 # XXX this can be easily be overlooked - it calls the functions of same name ok?
  446. elif [ "$1" = "auto" -o "$1" = "manual" ]; then
  447.     if ! grep -q 'countrycode=511' $f_syscfg; then
  448.         echo 'Error: Compliance test mode is not enabled in config. Enable, save, reboot.'
  449.     elif ! iwconfig ath0 | grep -q Mode:Managed; then
  450.         echo 'Error: Only managed mode (station/client) supported!'
  451.     elif ! grep -q 'wireless\.1\.scan_list\.status=disabled' $f_syscfg; then
  452.         echo 'Error: Channel list has to be DISABLED!'
  453.     elif [ -f $f_lock ]; then
  454.         echo -e "Error: Refusing to run - file $f_lock already exists.\n\tKill existing script if any, remove it and try again."
  455.     elif [ "$1" = "auto" ]; then
  456.         p=echolog
  457.         echo 'Backgrounding auto scan'
  458.         scan &
  459.         echo "$!" > $f_lock
  460.     else
  461.         p=echo
  462.         scan
  463.     fi
  464.     return $?
  465. elif [ "$1" = "getbw" ]; then
  466.     echo $(getbw)MHz
  467. elif [ "$1" = "setbw" -a -n "$2" ]; then
  468.     setbw $2
  469.     echo $(getbw)MHz
  470. else
  471.     echo 'Invalid usage. Arguments available:'
  472.     echo $0 '[ install | uninstall | enableCT | disableCT ]'
  473.     echo $0 '[ auto | manual | getbw | setbw ]'
  474.     echo
  475.     echo 'install | uninstall'
  476.     echo '  Add/remove call to this script to rc.poststart.'
  477.     echo 'enableCT | disableCT'
  478.     echo '  Adds/removes compliance test (only) to web interface and enables it in the'
  479.     echo '  config file. Reboot to apply.'
  480.     echo 'auto'
  481.     echo '  Runs continually in the background trying to associate on all available channels'
  482.     echo 'manual'
  483.     echo '  One-time scan/association try, only on current bandwidth.'
  484.     echo 'getbw/setbw'
  485.     echo '  Shortcut to get/set current bandwidth. Valid values for setting: 20/40 30 25 20 10 5'
  486.     echo '  Do not forget to save/reboot after setting bandwidth.'
  487.     exit 1
  488. fi
  489. exit 0
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement