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.
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
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
}
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).
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