check_bro.sh 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  1. #!/usr/bin/env bash
  2. # Author: Jon Schipp
  3. # Date: 11-08-2013
  4. ########
  5. # Examples:
  6. # 1.) Check status of all Bro workers
  7. # $ ./check_bro.sh -f /usr/local/bro-2.2/bin/broctl -T status
  8. # 2.) Return average packet loss for the 3 named bro workers
  9. # $ ./check_bro.sh -T loss -i "nids0,nids1,nids2"
  10. # 3.) Check average packet loss of all bro workers against warning and critical thresholds i.e > 10% or 20% packet loss.
  11. # $ ./check_bro.sh -T loss -i all -w 10 -c 20
  12. # 4.) Check packet loss percentage for the last most recent interval from Bro's capture_loss.log above 10% loss.
  13. # $ ./check_bro.sh -f /usr/local/bro-2.2/logs/current/capture_loss.log -T capture_loss -c 10
  14. # 5.) Check average packet loss reported by Myricom's SnifferG driver for each Bro node.
  15. # $ ./check_bro.sh -T myricom -i "192.168.1.254,192.168.1.253" -u bro
  16. # Nagios Exit Codes
  17. OK=0
  18. WARNING=1
  19. CRITICAL=2
  20. UNKNOWN=3
  21. # Default location of broctl
  22. # Set this to the proper location if your installation differs or use ``-f''
  23. BROCTL=/usr/local/bro/bin/broctl
  24. # Default location of myri_counters
  25. # Set this to the proper location if your installation differs
  26. MYRI_COUNTERS=/opt/snf/bin/myri_counters
  27. # Default location of logs
  28. # Set this to the proper location if your installation differs
  29. CAPTURE_LOG=/usr/local/bro/logs/current/capture_loss.log
  30. STATS_LOG=/usr/local/bro/logs/current/stats.log
  31. usage()
  32. {
  33. cat <<EOF
  34. Check status of Bro and Bro workers.
  35. This script should be run on the Bro manager.
  36. Options:
  37. -c <int> Critical threshold as percent of packet loss
  38. -f <path> Set optional absolute path for broctl, myri_counters, or capture_loss.log
  39. Use \`\`-f'' as first option on command-line.
  40. (def: $BROCTL,
  41. $MYRI_COUNTERS,
  42. $CAPTURE_LOG)
  43. -i <node/worker> Identifier for Bro instance(s). IP, FQDN, or name depending on the check. (sep:, )
  44. -p <name> Print the value of data from Bro e.g. Notice::suppressing or capture_filter
  45. -T <type> Check type, "status/loss/capture_loss/myricom/print"
  46. status - Check status of all Bro workers
  47. loss - Average packet loss by name for a single
  48. > (\`\`-i nids01''), set (\`\`-i "nids01,nids02"), or all workers (\`\`-i all'').
  49. capture_loss - Checks for packet loss in capture_loss.log
  50. myricom - Average Myricom Sniffer driver packet loss by IP or FQDN for a single-
  51. > (\`\`-i 192.168.1.1'') or set (\`\`-i "192.168.1.1,192.168.1.2") of Bro nodes
  52. > Connects to nodes via SSH (pub-key auth). If username is not root use ``-u''.
  53. print - Print Bro values
  54. -u <user> Username for the myricom check (def: root)
  55. -w <int> Warning threshold as percent of packet loss
  56. Usage: $0 -f /usr/local/bro-dev/bin/brotcl -T status
  57. $0 -f /usr/local/bro-2.2/logs/current/capture_loss.log -T capture_loss -c 20
  58. EOF
  59. }
  60. argcheck() {
  61. # if less than n argument
  62. if [ $ARGC -lt $1 ]; then
  63. echo "Missing arguments! Use \`\`-h'' for help."
  64. exit 1
  65. fi
  66. }
  67. # Initialize variables
  68. CRIT=0
  69. WARN=0
  70. LIST_NODES=0
  71. LOSS_CHECK=0
  72. CAPTURE_LOSS_CHECK=0
  73. STATUS_CHECK=0
  74. PRINT_CHECK=0
  75. MYRI_CHECK=0
  76. MYRI_STATS=0
  77. USER=root
  78. LOSS=0
  79. RECV=0
  80. RUNNING=0
  81. STOPPED=0
  82. CRASHED=0
  83. UNKNOWN_WORKER=0
  84. WORKERS=all
  85. EXCLUDE=none
  86. ARGC=$#
  87. argcheck 1
  88. while getopts "hfc:i:lm:p:T:u:w:" OPTION
  89. do
  90. case $OPTION in
  91. h) usage;;
  92. f)
  93. shift
  94. if [[ $1 == *broctl ]]; then
  95. BROCTL="$1"
  96. elif [[ $1 == *myri_counters ]]; then
  97. MYRI_COUNTERS=$1
  98. elif [[ $1 == *capture_loss.log ]]; then
  99. CAPTURE_LOG=$1
  100. # Custom thing written by jazoff - hopefully integrated into Bro upstream sometime
  101. elif [[ $1 == *current/stats.log ]]; then
  102. STATS_LOG=$1
  103. else
  104. echo "File name appears to be incorrect, maybe try setting the approprate variable in $0."
  105. fi
  106. ;;
  107. c)
  108. CHECK_THRESHOLD=1
  109. CRIT="$OPTARG"
  110. ;;
  111. l)
  112. LIST_NODES=1
  113. ;;
  114. i)
  115. if [ $LOSS_CHECK -eq 1 ] && [[ "$OPTARG" == all ]]; then
  116. WORKERS=".*"
  117. elif [ $LOSS_CHECK -eq 1 ]; then
  118. WORKERS=$(echo "$OPTARG" | sed 's/,/:\\|/g')
  119. elif [ $MYRI_CHECK -eq 1 ]; then
  120. NODE=$(echo "$OPTARG" | sed 's/,/ /g')
  121. else
  122. echo "ERROR: Argument is in incorrect format or \`\`-T <type>'' was not specified first"
  123. exit $UNKOWN
  124. fi
  125. ;;
  126. p)
  127. PRINT="$OPTARG"
  128. ;;
  129. T)
  130. if [[ "$OPTARG" == status ]]; then
  131. STATUS_CHECK=1
  132. elif [[ "$OPTARG" == myricom ]]; then
  133. MYRI_CHECK=1
  134. elif [[ "$OPTARG" == loss ]]; then
  135. LOSS_CHECK=1
  136. elif [[ "$OPTARG" == capture_loss ]]; then
  137. CAPTURE_LOSS_CHECK=1
  138. elif [[ "$OPTARG" == print ]]; then
  139. PRINT_CHECK=1
  140. else
  141. echo "Unknown argument type"
  142. exit $UNKNOWN
  143. fi
  144. ;;
  145. u)
  146. USER="$OPTARG"
  147. ;;
  148. w)
  149. WARN="$OPTARG"
  150. ;;
  151. \?)
  152. exit 1
  153. ;;
  154. esac
  155. done
  156. if [ $LOSS_CHECK -eq 1 ] || [ $STATUS_CHECK -eq 1 ] || [ $LIST_NODES -eq 1 ] || [ $PRINT_CHECK -eq 1 ] ; then
  157. if [ ! -f $BROCTL ];
  158. then
  159. echo "ERROR: Broctl has not been found. Update the BROCTL variable in $0 or specify the path with \`\`-f''"
  160. exit 1
  161. fi
  162. fi
  163. if [ $LIST_NODES -eq 1 ]; then
  164. $BROCTL nodes
  165. exit $OK
  166. fi
  167. if [ $LOSS_CHECK -eq 1 ]; then
  168. FLOAT_LOSS=$($BROCTL netstats | grep "$WORKERS" | sed 's/[a-z]*=//g' | awk '{ drop += $4 ; link += $5 } END { printf("%f\n", ((drop/NR) / (link/NR))* 100) }')
  169. LOSS=$(/usr/bin/printf "%d\n" $FLOAT_LOSS 2>/dev/null)
  170. if [ $LOSS -gt $CRIT ] ;then
  171. echo "Average packet loss is: $FLOAT_LOSS"
  172. exit $CRITICAL
  173. elif [ $LOSS -gt $WARN ]; then
  174. echo "Average packet loss is: $FLOAT_LOSS"
  175. exit $WARNING
  176. else
  177. echo "Average packet loss is: $FLOAT_LOSS"
  178. exit $OK
  179. fi
  180. fi
  181. # Check status of Bro workers
  182. if [ $STATUS_CHECK -eq 1 ]; then
  183. # Broctl stderr is whitespace separated and we need to match on entire line
  184. IFS=$'\n'
  185. MESSAGE=""
  186. CHECK_NAME="BROCTL STATUS"
  187. for line in $($BROCTL status 2>&1 | grep -v 'Name\|waiting\|Warning')
  188. do
  189. NAME=$(echo "$line" | awk '{ print $1 }')
  190. case "$line" in
  191. *stop*)
  192. MESSAGE="$MESSAGE $NAME has stopped,"
  193. STOPPED=$((STOPPED+1))
  194. ;;
  195. *fail*)
  196. MESSAGE="$MESSAGE $NAME has crashed,"
  197. CRASHED=$((CRASHED+1))
  198. ;;
  199. *run*)
  200. MESSAGE="$MESSAGE $NAME is running,"
  201. RUNNING=$((RUNNING+1))
  202. ;;
  203. *)
  204. MESSAGE="$MESSAGE Unknown status of worker: $NAME,"
  205. UNKNOWN_WORKER=$((UNKNOWN_WORKER+1))
  206. ;;
  207. esac
  208. done
  209. if [ $STOPPED -gt 0 ] || [ $CRASHED -gt 0 ] || [ $UNKNOWN_WORKER -gt 0 ]; then
  210. echo "$CHECK_NAME CRITICAL - $STOPPED stopped workers, $CRASHED crashed workers, $RUNNING running workers, and $UNKNOWN_WORKER workers with an unknown status:$MESSAGE"
  211. exit $CRITICAL
  212. else
  213. echo "$CHECK_NAME OK - All $RUNNING instances are running:$MESSAGE"
  214. exit $OK
  215. fi
  216. fi
  217. if [ $CAPTURE_LOSS_CHECK -eq 1 ]; then
  218. if [ ! -f $CAPTURE_LOG ]; then
  219. echo "capture_loss.log cannot be found, modify CAPTURE_LOG in $0 or use \`\`-f''"
  220. exit $UNKNOWN
  221. fi
  222. INTERVAL=$(awk 'NR == 9 { printf("%d\n", $2) }' $CAPTURE_LOG)
  223. TIME=$(date +"%s")
  224. RECENT=$(echo $((TIME-INTERVAL)))
  225. awk -v recent=$RECENT -v crit=$CRIT -v loss=0 -v threshold=0 '! /^#/ && $1 > recent && $4 > 0 \
  226. {
  227. loss++; decimal=sprintf("%d", $6);
  228. if ( strtonum(decimal) > crit ) {
  229. threshold++
  230. print "Peer: "$3,"\t","Loss:", $6;
  231. }
  232. }
  233. END {
  234. if ( loss >= 1 ) {
  235. print "\n--------------------\n"loss,"instances of loss with",threshold,"exceeding the threshold ("crit"%).";
  236. if ( threshold > 0 ) {
  237. exit 2
  238. }
  239. exit 0
  240. }
  241. else
  242. print "\nNo loss detected"; }' $CAPTURE_LOG
  243. if [ $? -eq 2 ]; then
  244. exit $CRITICAL
  245. else
  246. exit $OK
  247. fi
  248. fi
  249. if [ $PRINT_CHECK -eq 1 ]; then
  250. $BROCTL print $PRINT
  251. exit $OK
  252. fi
  253. if [ $MYRI_CHECK -eq 1 ]; then
  254. LOSS=0
  255. RECV=0
  256. COUNT=0
  257. LOSS_TOT=0
  258. RECV_TOT=0
  259. AVERAGE=0
  260. if [ ! -f $MYRI_COUNTERS ]; then
  261. echo "ERROR: myri_counters has not been found. Update the MYRI_COUNTERS variable in $0 or specify the path with \`\`-f''"
  262. exit $UNKOWN
  263. fi
  264. for node in $NODE
  265. do
  266. MYRI_STATS=$(ssh -l $USER $node $MYRI_COUNTERS | awk -F : '/SNF drop ring full|SNF recv pkts/ { print $2 }' | sed 's/[ \t]*//')
  267. RECV=$(echo $MYRI_STATS | awk '{ print $1 }')
  268. LOSS=$(echo $MYRI_STATS | awk '{ print $2 }')
  269. LOSS_TOT=$((LOSS+LOSS_TOT))
  270. RECV_TOT=$((RECV+RECV_TOT))
  271. COUNT=$((COUNT+1))
  272. echo "$node: Lost $LOSS of $RECV"
  273. done
  274. echo "--------------------"
  275. # Average
  276. FLOAT_LOSS=$(echo "(($LOSS_TOT / $COUNT) / ($RECV_TOT / $COUNT)) * 100" | bc -l)
  277. LOSS=$(/usr/bin/printf "%d\n" $FLOAT_LOSS 2>/dev/null)
  278. if [ $LOSS -gt $CRIT ]; then
  279. echo "Average packet loss is: $FLOAT_LOSS"
  280. exit $CRITICAL
  281. elif [ $LOSS -gt $WARN ]; then
  282. echo "Average packet loss is: $FLOAT_LOSS"
  283. exit $WARNING
  284. else
  285. echo "Average packet loss is: $FLOAT_LOSS"
  286. exit $OK
  287. fi
  288. fi