OpenWrt Forum Archive

Topic: Simple script for wireless stations monitoring

The content of this topic has been archived on 24 Apr 2018. There are no obvious gaps in this topic, but there may still be some posts missing at the end.

Hi everyone,
here is my little bash script which purpose is to take the advantage of the new Atheros wireless chipsets and the "iw" package for managing the wireless interface mainly on atheros chips. The aim is to give us a live stats monitor of wireless stations' link quality and some additional useful information regarding the bit rate, Tx/Rx packets, wireless signal strength, etc. The other goal is to make the output a little more comprehensive, featured and easily to give you an idea of what is happening with the stations associated to your AP, as giving you their IPs and eventual hostnames (if DHCP associated) as an addition to the STA's MAC address. The other added functionality is the ability to monitor the stats only of a single wireless STA, by specifying its MAC, IP or hostname if present.
May put some lines in the wiki if you like.

Here is my code, it's pure bash and one may find it too dirty, but, anyway,I am not a bash expert and hope some will find it useful. Probably there are currently several bugs, but I will happily fix them on your report.
Also, feel free to modify and tune it, and if so, give us updates here,

Unfortunately, I couldn't find how to attach a file in this forum....

Copy -> Paste -> Save as "wlan-monitor.sh" somewhere on your router -> make it executable (chmod +x wlan-monitor-sh) -> run ./wlan-monitor-sh -h

#!/bin/bash
#
# WLAN Minitoring Script Version v2.1
# OpenWrt Atheros wireless radio STA status monitor
# Developed by dir2cas <kalin.t.ivanov@gmail.com>
#
# Comments:
# Required packages: bash, iw, kmod- packages&drivers to get your wireless interface running
# Optional packages: 
# install with $ opkg update; opkg install iw bash ....
# To check if it's installed on your OpenWrt box, try $ opkg list-installed | grep "iw", etc.
# It is also recommended to have bash shell installed on your OpenWrt router
#
# Generally, the script should work without modifying the source, but you are free to do it if satisfying your needs.

SCRIPTNAME="wlan-monitor"

################ SCRIPT GLOBAL VARIABLES ################ 
# Defining varialbles (usually no need to modify)
DIR="/tmp/${SCRIPTNAME}"
FILE="${SCRIPTNAME}.log"
ARP_CACHE="/proc/net/arp"
#########################################################

function Usage() {
# Define the help/warning message:
echo -e "\tUsage: ./${SCRIPTNAME}.sh [OPTION 1] [ARG 1]...[OPTION n] [ARG n]\n\n\tList of available options:\n\t-i :\tscan intensity in seconds\n\t-d :\twireless device/interface to monitor on\n\t-t :\t[STA_MAC | STA_IP | STA_Hostname]\n\t-h, --help :\tshow this menu\n\tDefault is to monitor all stations associated\n\tusing wireless device ${WLAN_DEFAULT} and scan intensity ${MON_INTERVAL_DEFAULT} sec"
}

# Defining the Away() function to terminate the script
function Away() {
# Show the message sent to the terminating function, if any
echo -e >&2 "$@\n"
sleep 1

#do some cleaning
rm -f ${DIR}/* > /dev/null

unset INTERFACE
unset DIR
unset FILE
unset CLIENTS
unset ARP_CACHE
unset HOST
unset WLAN_MON_CMD
unset sta_num
unset sta_list
unset sta_count
unset sta[@]
unset sta_ip[@]
unset sta_hostname[@]
unset sta_linenum[@]
unset sta_mac_arp
unset sta_mac
unset flag
sleep 1
exit
}

# Defining the Scan() function to print the current status of the wireless clients to the temp file:
function Scan() {
#clear
local WLAN_MON_CMD="$1"
${WLAN_MON_CMD} > ${DIR}/${FILE}
}

# Defining the GetHosts() function to learn the IPs and Hostnames of the wireless clients' stations (STAs):
function GetHosts() {
local i=1
sta_num=$(grep -c 'Station' ${DIR}/${FILE})

# Check if there are STAs connected to the AP
if [ "${sta_num}" -eq "0" ]; then
    #exiting
    Away "NO Wireless STAs associated"
fi 

# Get list with the associated STAs' MAC addresses
sta_list=$(cat ${DIR}/${FILE} | grep -e "Station" | cut -f 2 -d" ")

if [ -z "${sta_list}" ]; then     # -n the argument is non empty, -z the argument is empty
    #exiting
    Away "NO Wireless STAs associated"
fi

# Check again the real scanned wireless STAs (MAC addresses in the array)
sta_count=$(echo ${sta_list} | wc -w)

# Get each STA its IP and Hostname 
while [ "${i}" -le "${sta_count}" ]
do
    sta[${i}]=$(echo ${sta_list} | awk -v num=$i '{print $num}')
    #sta[${i}]=$(echo ${sta_list} | cut -f${i} -d" "

    sta_ip[${i}]=$(cat ${CLIENTS} | grep "${sta[${i}]}" | awk '{print $3}')
    if [ -z "${sta_ip[${i}]}" ]; then     # -n the argument is non empty, -z the argument is empty    
        sta_ip[${i}]=$(grep -e "${sta[${i}]}" "${ARP_CACHE}" | awk '{print $1}')
    fi

    sta_hostname[${i}]=$(cat ${CLIENTS} | grep "${sta[${i}]}" | awk '{print $4}')
    if [ -z "${sta_hostname[${i}]}" ] || [ "${sta_hostname[${i}]}" = "*" ]; then   # fix * if hostname for the device is not set    
        sta_hostname[${i}]="noname"
    fi
    #identify the line number of the beginning of each entry
    sta_linenum[${i}]=$(grep -n -E "${sta[${i}]}" ${DIR}/${FILE} | cut -f1 -d: )
    let "i += 1"
done  
}

# Defining the Format() function to prepare the final output of the script
function Format() {
local i=1
local divider="----------------------------------------------------------------------"

while [ "${i}" -le "${sta_count}" ]
do
    line=$(grep -e "${sta[${i}]}" ${DIR}/${FILE})
    sed -i '/'"${sta[${i}]}"'/ s/.*/'"${line} => IP:${sta_ip[${i}]} | Host:${sta_hostname[${i}]}"'/g' ${DIR}/${FILE}
    sed -i '/'"${sta[${i}]}"'/ i'"${divider}"'' ${DIR}/${FILE}
    let "i += 1"
done
}

# Defining the STA_select() function to choose the correct STA, specified by the user with an IP or hostname
function STA_select() {
#Initialize the script argument
local ARG="$1"
local i=1

#Get the probable STA MAC address from the arp cache (the STA may have a static IP, not found in the dhcp file)
#sta_mac_arp=$(arp | grep "${ARG}" | awk '{print $4}')   #this method seems broken, if no arp binary
sta_mac_arp=$(grep -e "${ARG}" "${ARP_CACHE}" | awk '{print $4}')

#Loop to find the STA MAC address that corresponds to the given argument
while [ "${i}" -le "${sta_count}" ]
do
    if [ "${ARG}" = "${sta_hostname[${i}]}" ] || [ "${ARG}" = "${sta_ip[${i}]}" ] || [ "${sta_mac_arp}" = "${sta[${i}]}" ] ; then
        #if the corresponding MAC is found(confirmed by dhcp), parse it to a variable that will be returned
        sta_mac=${sta[${i}]}
    fi
    let "i += 1"    
done

if [ -n "${sta_mac}" ]; then   # -n the argument is non empty, -z the argument is empty
    return "1"
else
    #the given IP or hosname may be of a device that is not a wireless STA, in this case, exiting
    Away "The given IP/Hostname <${ARG}> is not a Wireless STA"
fi
}

# Defining the Main() function
function Main() {

#Define what to do when Ctrl-C is pressed - in this case - to terminate the script jumping to a terminating function
trap '{ echo -e "\nControl-C trap caught, exiting"; Away; }' INT #traps Ctrl-C

#Run an initial scan for wireless clients first to get the information needed:
WLAN_MON_CMD="iw dev ${INTERFACE} station dump"
Scan "${WLAN_MON_CMD}"
GetHosts

#Check which mode of the script to be run:
if [ -z "${HOST}" ]; then     # -n the argument is non empty, -z the argument is empty
    #if no argument given, the script will monitor all STAs, default mode
    WLAN_MON_CMD="iw dev ${INTERFACE} station dump"
else 
    STA_select "${HOST}"
    WLAN_MON_CMD="iw dev ${INTERFACE} station get ${sta_mac}"
fi 

# Start actual monitoring
while :
do
Scan "${WLAN_MON_CMD}"
GetHosts
Format
clear
printf "%s STA(s) associated\n" "${sta_num}"
cat ${DIR}/${FILE}
sleep ${MON_INTERVAL}
done
}

#################################################################################################
#MAIN
#echo "Script parameters are: $@ \nNumber of parameters in the Array: ${#ARGUMENTS[@]}"
declare -a ARGUMENTS=("$@")

# Global variables defaults
WLAN_DEFAULT="wlan0"
#Monitor interval in seconds
MON_INTERVAL_DEFAULT="3"

# Initialize script parameters and usage
if [ "$#" -gt "6" ]; then
    #Usage
    Away "Too many arguments provided\n"
fi

# Handling Script Arguments
for (( i=0;i<${#ARGUMENTS[@]};i++ )); do
case ${ARGUMENTS[${i}]} in
    "-i" )
        if [ -z "${ARGUMENTS[$((${i}+1))]}" ] || [ "${ARGUMENTS[$((${i}+1))]}" = "-d" ] || [ "${ARGUMENTS[$((${i}+1))]}" = "-t" ]; then 
            Away "###WARNING###\n<${ARGUMENTS[${i}]}> monitoring intensity provided without argument"
        fi    
    ;;
    "-d" )
        if [ -z "${ARGUMENTS[$((${i}+1))]}" ] || [ "${ARGUMENTS[$((${i}+1))]}" = "-i" ] || [ "${ARGUMENTS[$((${i}+1))]}" = "-t" ]; then 
            Away "###WARNING###\n<${ARGUMENTS[${i}]}> wireless device provided without argument"
        fi
    ;;
    "-t" )
        if [ -z "${ARGUMENTS[$((${i}+1))]}" ] || [ "${ARGUMENTS[$((${i}+1))]}" = "-i" ] || [ "${ARGUMENTS[$((${i}+1))]}" = "-d" ]; then 
            Away "###WARNING###\n<${ARGUMENTS[${i}]}> target station provided without argument"
        fi
    ;;
    *help|-h )
        Usage; exit 0;
    ;;
    * )
        if [ "${ARGUMENTS[$((${i}-1))]}" = "-d" ]; then INTERFACE="${ARGUMENTS[${i}]}";
        elif [ "${ARGUMENTS[$((${i}-1))]}" = "-t" ]; then HOST="${ARGUMENTS[${i}]}";
        elif [ "${ARGUMENTS[$((${i}-1))]}" = "-i" ]; then MON_INTERVAL="${ARGUMENTS[${i}]}";
        else
            Away "###WARNING###\n<${ARGUMENTS[${i}]}> is invalid script option\nCheck the help option for a list of available options"
        fi
    ;;
esac
done

# Defining unspecified vital global parameters
if [ -z "${HOST}" ]; then echo -e "Force scanning of all associated STAs"; unset ${HOST}; fi
if [ -z "${INTERFACE}" ]; then echo -e "Force usage of default wireless interface ${WLAN_DEFAULT}"; INTERFACE="${WLAN_DEFAULT}"; fi
if [ -z "${MON_INTERVAL}" ]; then echo -e "Force usage of default scan intensity ${MON_INTERVAL_DEFAULT} seconds"; MON_INTERVAL="${MON_INTERVAL_DEFAULT}"; fi 

CLIENTS=$(cat /etc/config/dhcp | grep "leasefile" | sed 's/.*leasefile//' | tr -d " '") >&- 2>&-
if [ -z "${CLIENTS}" ]; then     # -n the argument is non empty, -z the argument is empty
    CLIENTS="/tmp/dhcp.leases"
fi

# Check if the specified interface is a valid wireless device
$(which iw) dev ${INTERFACE} info 2>/dev/null > /dev/null || { Away "\n<${INTERFACE}> is not a valid wireless interface,\nor not supported by mac80211-based driver"; };
sleep 1

# Check for the script directory in /tmp (RAM):
if [ -d "${DIR}" ]; then
    #echo "Directory exists"
    rm -f ${DIR}/* > /dev/null
    cd ${DIR}
    Main
else 
    echo "Directory does not exists"
    echo "Creating directory"
    mkdir -p ${DIR}
    cd ${DIR}
    Main
fi

Cheers,
dir2cas

Updated v1.2
- source optimization;
Updated v1.3
- source optimization;
Updated to v1.4
- optimised script source;
- stability fix in the Away function
Updated to v2.0
- added several new features (see help)
      - option for defining the wireless interface as an argument;
      - option for defining the target station as an argument;
      - option for defining the scan intensity in seconds;
- internal checks for correct options and arguments usage;
- optimised script source;
- no need to edit the script source, every option could be defined as an argument parameter;
- predefined default values, to ensure proper default operation and partial compatibility;
- default operation if no options specified;
Updated to v2.1
- internal check for correct wireless interface/device usage (mac80211-based drivers (ath9k, b43, brcmfmac, etc.) currently supported);
- optimised script source;

- Example usage: ./wlan-monitor.sh -d wlan0 -t 192.168.1.101 -i 3

(Last edited by dir2cas on 11 Mar 2015, 15:55)

There’s no bash.

towolf wrote:

There’s no bash.

You may install it via

opkg install bash

or modify the script.

dir2cas wrote:
towolf wrote:

There’s no bash.

You may install it via

opkg install bash

or modify the script.

Perhaps, to make it more useful, you should try to remove bashisms and make it run with native busybox ash shell.

thanks very useful.

Hello,

This is a really beautiful tool... I want to ask some questions?

1. the interval time is 1s, does it exactitude to 1ms?

2. This bash monitor the associated stations, does it monitor the disassociate behavior from stations?

3. I my OpenWrt router, use "iw event -f" can monitor the connecting stations, however, it does not monitor the disconnecting stations.  I have read iw code, and believe that iw have implemented monitoring disconnecting behavior and print detail frame. However,it only display new stations info, no any other info to me. Is there something wrong for iw in my OpenWRT??

I have retry " connect and disconnect" 3 times ,it only display as follows:
root@OpenWrt:~# iw event -f
wlan0: new station 00:26:68:5c:9f:22
wlan0: new station 00:26:68:5c:9f:22
wlan0: new station 00:26:68:5c:9f:22

Even,I test to set power_save on, it shows "Operation not supported (-95)", this note can't be found in iw source code.
root@OpenWrt:~#
root@OpenWrt:~# iw wlan0 get power_save
Power save: off
root@OpenWrt:~# iw wlan0 set power_save on
command failed: Operation not supported (-95)
root@OpenWrt:~#


Thanks all~~

Hi,
the monitoring interval could be easily modified on your own if you wish. To make it easier, take a look at the updated first post in this thread (updated to v1.2), and particularly in the beginning section, where global variables are defined, you will find the following entry:

#Monitoring interval in seconds
MON_INTERVAL="3"

Currently it is set to 3 sec, but you may easily change it to your own needs. Since this is a MIPS system that is not optimized for such kind of computation some of the operations executed in the script are a little more CPU consuming than usual (easily observed via htop), so setting the script to refresh the information in too short intervals may lead to an excessive load of your board. 3 seconds is a compromise between CPU usage and time accuracy.

@cheewii, I am not sure I understood your questions right, I have never brought in use of iw event -f command, but it seems not to give us any significant information. My script dynamically monitors and refreshes the most meaningful data related only to the currently associated wireless clients. All of the information is dynamically updated and you may easily see that when you associate a new station to your AP, while at the same time running the script and watch, as the new station will appear in the list and the total number of wireless stations associated will also increase in a real time fashion.
Regards,

(Last edited by dir2cas on 1 May 2011, 22:29)

Great script! Thanks for taking the time to write it up!

I am going to modify it a bit for my use with a WNDR3700 with 2 wlan interfaces. I want to report stations for both wlan0 and wlan1.

Also, I am having an issue with arp not creating entries for associated wireless clients. I'm going to try and work through this issue myself, but I would greatly appriciate if anyone by chance has any insight as to why arp won't record the wireless client entries.

I will post my results if I get the dual radio version working and figure out my arp issue.

Oh sorry, I should mention, I do not have dhcp servers running on these boxes as they are just APs so I don't have a dhcp.leases file to check. I have a linux dhcp server so I will probably end up fetching it's dhcp.leases file via ssh for the IP and hostname lookup. I'll try and write it up as generic as possible for anyone in a situation like mine. But it will involve key pairs for automated data collection so be forewarned.

(Last edited by hattybin on 5 May 2011, 16:40)

Hi hattybin, and welcome to the forum,

As i mentioned in the first post, feel free to modify and optimize the script, and share your version and changes you made on it here in this thread. Since your case is a little specific it would be useful to have your version of monitoring wireless stations.
I am not sure if it is all like that about the arp cache in every router model, but there is no arp command in fact. It is a function written in your shell configuration file that says to cat he content of /proc/net/arp.

root@OpenWrt:~# type arp
arp is a function
arp () 
{ 
    cat /proc/net/arp
}

If it does not show anything, try if you have a real arp binary tool installed, otherwise, you may try installing iproute2 package and use:

ip neigh show

to discover the arp cache.
However, keep in mind that if you are using the router only as AP, and it does not perform any kind of routing of your wireless stations, but only L2 switching, it is normal not to have any arp entries about them in the ARP table/cache. This happens, no matter that you have an IP address set on the router's interface (let's say bt-lan interface - the bridge for your LAN network, joining one or more VLANs), because most probably the wireless clients use another IP address as a gateway, and the only way to have them in the arp cache is to force them to communicate with the router's IP address (on L3).

Can U change this script for #!/bin/sh ? smile

coverek wrote:

Can U change this script for #!/bin/sh ? smile

Hi, unfortunately some of the script constructions that are used are not available under /bin/sh.
Installing the bash package under openWrt is not such a problem.

Thanks for sharing, nice work! :-)

great!what i am looking for~

Hi All,

WLAN Minitoring Script Version v2.0 has been released.
Enjoy!

Hi. I installed bash, but I get the following error:

root@OpenWrt:/bin# sh wlan-monitor.sh
: not foundr.sh: line 15:
: not foundr.sh: line 17:
: not foundr.sh: line 24:
wlan-monitor.sh: line 25: syntax error: unexpected "("


And when I try:
root@OpenWrt:/bin# ./wlan-monitor.sh -h
-ash: ./wlan-monitor.sh: not found

tastywrt wrote:

Hi. I installed bash, but I get the following error:

root@OpenWrt:/bin# sh wlan-monitor.sh
: not foundr.sh: line 15:
: not foundr.sh: line 17:
: not foundr.sh: line 24:
wlan-monitor.sh: line 25: syntax error: unexpected "("


And when I try:
root@OpenWrt:/bin# ./wlan-monitor.sh -h
-ash: ./wlan-monitor.sh: not found

Hi,

You have installed bash package, but you have not set your user's shell - by default it is /bin/sh. You must open /etc/passwd and change root's user shell to
Ex.)
root:<your pass encrypted>:0:0:root:/root:/bin/bash

Then, you shoud logout and login again or simply type
bash

BR,
dir2cas

Hi, I have a problem running this code. The error is "Failed to allocate generic netlink cache". I have analized it and I found that it happens the same when I run "iw dev wl0 station dump". I am running Backfire on a WRT54gs v4 and AP mode.

Could you please help me if there is a problem running in this type of router and configuration?

Thanks for your help!!!

Your driver is not supported by the iw command.

I was looking for a simple script which sends an email if a specific wlan client logs in, but this script sadly is just for bash which doesnt make much sense, because bash shouldn't something you would want on your router because of several reasons. Is there a better/simpler script somewhere to find?

Anyway, isnt there a native option already maybe to run a custom script every-time a wifi client connects and give the script some variables like mac address? Where's the hook in point to do that, Im sure it should just be a line or two, so you wont need a watchdog script, when the call for a login can easily be done with the scripts openwrt already use. Or doing it via hotplug.

(Last edited by knuddel on 4 Mar 2015, 17:38)

geting some error on WDR4300 using CHAOS CALMER (Bleeding Edge, r44507):
./wlan-monitor.sh
Force scanning of all associated STAs
Force usage of default wireless interface wlan0
Force usage of default scan intensity 3 seconds
command failed: No such device (-19)
NO Wireless STAs associated

next tried by one interface
./wlan-monitor.sh -d r10v19
Force scanning of all associated STAs
Force usage of default scan intensity 3 seconds
command failed: No such device (-19)
NO Wireless STAs associated

fails, but i have one user connected, r10v19 is my 2,54GHz wifi interface, but works fine on 10v19 (5ghz wifi interface)

vilpalu wrote:

geting some error on WDR4300 using CHAOS CALMER (Bleeding Edge, r44507):
./wlan-monitor.sh
Force scanning of all associated STAs
Force usage of default wireless interface wlan0
Force usage of default scan intensity 3 seconds
command failed: No such device (-19)
NO Wireless STAs associated

next tried by one interface
./wlan-monitor.sh -d r10v19
Force scanning of all associated STAs
Force usage of default scan intensity 3 seconds
command failed: No such device (-19)
NO Wireless STAs associated

fails, but i have one user connected, r10v19 is my 2,54GHz wifi interface, but works fine on 10v19 (5ghz wifi interface)

I have not tested it on dual band devices so far. Probably both radios are running different drivers. You can check the radio interfaces that are currently supported issuing

iw dev

ps. I did a small change in the script source, so new version is v2.1. 1th post edited. As mentioned, currently the script can operate on mac80211-based drivers, check http://wiki.openwrt.org/doc/howto/wireless.utilities.

(Last edited by dir2cas on 11 Mar 2015, 15:59)

I got a "nl80211 not found." error when running iw. The wireless card is "MediaTek MT7620A CPU:580MHz, Bus:193MHz Uart:40MHz", is iw dosen't support this chip?

(Last edited by flintt on 9 Apr 2015, 02:16)

With bash dependency this script is useless. Study programming with busybox shell.

mitry wrote:

With bash dependency this script is useless. Study programming with busybox shell.

First post and declaring useless a script which others have found useful, and then giving orders. Perhaps if you have studied "programming with busybox shell" you could post your alternative (but I wouldn't word that as a command).

(Last edited by lizby on 30 May 2015, 17:41)

mitry wrote:

With bash dependency this script is useless. Study programming with busybox shell.

If you manage to cope with the arrays in busybox, you are welcome. Probably it is possible, but I doubt it is in the standard image busybox (like out-of-the box style). Busybox modifications need image rebuild, that I think is much more time and efforts consuming than simply installing the bash package, which will have many other goodies than simply the possibility to run my script.

It is my opinion, but if you ever want to do shell programming in real, you should concentrate your efforts and skills to bash, not to busybox in particular.

The discussion might have continued from here.