Given MAC address, how to tell if interface is wifi

Is there a way, on the command line, given a MAC address of a local network interface on the current machine (e.g. 01:23:45:67:89:ab), to determine if that interface is WiFi vs wired?

I’m trying to do this in a shell script.

Asked By: Jason C

||

Kos’s answer is a better way, please use that. I am relying on naive text parsing here so if, by some weird chance, the string of the MAC address appears in some other part of the outputs I parse, I will incorrectly claim it is a wireless device. For example, if you search for ff:ff:ff:ff:ff:ff in the output of ip addr, you will find it, but that isn’t a valid MAC of an existing wireless device. Kos’s answer can handle this gracefully.


Here’s one way, but I am not sure how robust it is:

mac=01:23:45:67:89:ab
iwconfig 2>/dev/null | grep -w "$mac" && echo "Is wireless" || echo "Is not wireless"

The iwconfig command only shows information for wireless cards, so if your MAC isn’t in its output, then it shouldn’t be from a wireless device.

Alternatively, you could do something more roundabout like getting the device name from /proc/net/wireless and then checking the MAC in the output of ip. Like this:

device=$(awk -F":" '!/|/{print $1}' /proc/net/wireless )
ip addr show dev "$device" |
  grep -wq "$mac" && echo "Is wireless" || echo "Is not wireless"

On my system, /proc/net/wireless looks like this:

$ cat /proc/net/wireless
Inter-| sta-|   Quality        |   Discarded packets               | Missed | WE
 face | tus | link level noise |  nwid  crypt   frag  retry   misc | beacon | 22
wlp9s0: 0000   40.  -70.  -256        0      0      0      0   1249        0

So the awk command is using : as the field separator, so that the first field, $1, will be wlp9s0 and the !/|/ ensures we only print on lines that don’t contain | so we only get device lines and not headeres. Next, the output of ip addr looks like this:

$ ip addr show dev wlp9s0
3: wlp9s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether 70:cd:0d:57:0a:6c brd ff:ff:ff:ff:ff:ff
    inet 192.168.178.57/24 brd 192.168.178.255 scope global dynamic noprefixroute wlp9s0
       valid_lft 846797sec preferred_lft 846797sec
    inet6 2a02:8012:de3:1:929a:6dd0:d7df:53b1/128 scope global dynamic noprefixroute 
       valid_lft 5503sec preferred_lft 1903sec
    inet6 2a02:8012:de3:1:b9ec:8f1a:9da8:2c47/64 scope global dynamic noprefixroute 
       valid_lft 6907sec preferred_lft 3307sec
    inet6 fe80::929a:6dd0:d7df:53b1/64 scope link noprefixroute 
       valid_lft forever preferred_lft forever

So if that output contains your MAC, you can assume it is indeed from a wireless device. Kinda. If you set your MAC to be ff:ff:ff:ff:ff:ff

Of course, you might have more than one wireless device, so this might be a better option to cover that possibility:

devices=( $(awk -F":" '!/|/{print $1}' /proc/net/wireless ) )
for device in "${devices[@]}"; do
  ip addr show dev "$device" |
   grep -wq "$mac" && echo "Is wireless" || echo "Is not wireless"
done
Answered By: terdon

Besides the quirk that their method (I think) could also report virtual wireless interfaces as real wireless interfaces (which again I think should be easily solvable just by inspecting /sys/class/net/*/ a bit further) @PhilippWendler has an even simpler way down below that involves simply inspecting /sys/class/net, which I believe it’s as simple as it can get, so please check out their answer as well.


Another way using lshw + jq; it should be fairly robust as it relies on lshw for detection and on jq for parsing.

This assumes bash (because of the &>/dev/null redirection; &>/dev/null could be changed to >/dev/null 2>&1 for use with non bash-compatible shells and POSIX compliance):

{
    LANG=C lshw -json -class network | 
    jq -e --arg serial '01:23:45:67:89:ab' '.[] | select(.description == "Wireless interface" and .serial == $serial)'
} &>/dev/null && echo yes || echo no

This will echo "yes" only in case a "network" device is classified as a "Wireless interface" and its MAC address is "01:23:45:67:89:ab"; naturally this would best be refactored into a function for ease of use:

is_wireless_adapter() {
    {
        LANG=C lshw -json -class network | 
        jq -e --arg serial "$1" '.[] | select(.description == "Wireless interface" and .serial == $serial)'
    } &>/dev/null && echo yes || echo no
}

Usage:

is_wireless_adapter <MAC_ADDRESS>
% ip address
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
    valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host noprefixroute 
    valid_lft forever preferred_lft forever
2: enp100s0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc mq state DOWN group default qlen 1000
    link/ether 48:21:0b:3d:b7:87 brd ff:ff:ff:ff:ff:ff
3: wlo1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether e0:d0:45:66:b0:c5 brd ff:ff:ff:ff:ff:ff
    altname wlp0s20f3
    inet 192.168.1.3/24 brd 192.168.1.255 scope global dynamic noprefixroute wlo1
    valid_lft 86077sec preferred_lft 86077sec
    inet6 fe80::1c63:81ea:f46a:b3db/64 scope link noprefixroute 
    valid_lft forever preferred_lft forever
% is_wireless_adapter '48:21:0b:3d:b7:87'
no
% is_wireless_adapter 'e0:d0:45:66:b0:c5'
yes

You could also remove the echos in order to have the function return a status code and in order to be able to perform some different logic on it:

is_wireless_adapter() {
    {
        LANG=C lshw -json -class network | 
        jq -e --arg serial "$1" '.[] | select(.description == "Wireless interface" and .serial == $serial)'
    } &>/dev/null
}
Answered By: kos

On systems using NetworkManager, you could consider using nmcli to get the device MAC (known as HWADDR) and TYPE:

$ nmcli -e no -g GENERAL.HWADDR,GENERAL.TYPE dev show
08:00:27:8E:46:AB
ethernet

BC:AE:C5:7D:A0:AA
wifi

08:00:27:A7:C4:90
ethernet

00:00:00:00:00:00
loopback

It doesn’t appear to allow directly polling of devices by MAC, so post processing is still required – for example with awk

$ nmcli -e no -g GENERAL.HWADDR,GENERAL.TYPE dev show | 
    awk -vRS= -v mac='BC:AE:C5:7D:A0:AA' '$1 == mac {print $2}'
wifi

$ nmcli -e no -g GENERAL.HWADDR,GENERAL.TYPE dev show | 
    awk -vRS= -v mac='08:00:27:A7:C4:90' '$1 == mac {print $2}'
ethernet

If you want to turn that into some kind of indicator function, you could do something like this:

$ declare -f -p isWifi
isWifi () 
{ 
    nmcli -e no -g GENERAL.HWADDR,GENERAL.TYPE dev show | awk -vRS= -v mac="$1" '
    $1 == mac {found = 1; wifi = ($2 == "wifi"); exit} 
    END {exit(found == 1 ? (wifi == 1 ? 0 : 1) : 2)}
  '
}

Then

$ isWifi BC:AE:C5:7D:A0:AA; echo "$?"
0

(MAC BC:AE:C5:7D:A0:AA found and IS wifi)

$ isWifi 08:00:27:A7:C4:90; echo "$?"
1

(MAC 08:00:27:8E:46:AB found but is NOT wifi)

$ isWifi FF:FF:FF:FF:FF:FF; echo "$?"
2

(MAC FF:FF:FF:FF:FF:FF not found).

Answered By: steeldriver

It seems quite easy to extract this information from /sys without any special tools:

for i in /sys/class/net/*/; do 
  [[ 01:23:45:67:89:ab = $(cat "$i/address") && -d "$i/wireless" ]] && 
    echo "Device is wireless"
done
Answered By: Philipp Wendler
Categories: Answers Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.