Priority of Wireless over Wired in OS X 10.10.5

bpavlov
Honored Contributor

We are more than half way through our upgrade process. Clients are on 10.10.5 and hardware are a variety of Retina MacBook Pros and MacBook Airs. Image was created in AutoDMG. Usually the users may go back to their desk and plug into Ethernet using Apple Thunderbolt or USB adapters after they've come back from a meeting. However, it seems that the wireless still seems to have priority even though Ethernet is detected. You have to actively turn off and on wireless for Ethernet to take priority again. This becomes very obvious when uploading or downloading files from/to a server share.

Apple by default sets Thunderbolt and USB adapters to be above Wireless and that's the case across all these machines. Has anyone seen this behavior?

6 REPLIES 6

mm2270
Legendary Contributor III

We have a wireless "switcher" process, run by a LaunchDaemon that detects when Ethernet is plugged in and turns off wireless. We do this to prevent network bridging for one thing, but also because, why hog a wireless connection if your Mac has a valid Ethernet connection? You might want to consider using something like that to help. As far as I know, this behavior isn't really new to 10.10.5. We've had similar issues of the network connection going through wireless even when Enet is active for years now (hence why we have the switcher process)

bpavlov
Honored Contributor

Can you share more a bit more on that switcher? Scripts, etc.

Also, this didn't seem to be an issue in 10.8 or 10.9 for our users.

mm2270
Legendary Contributor III

Sanitized version of the LaunchDaemon (names changed, but the basics are all there)

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>com.company.wiredswitcher</string>
    <key>ProgramArguments</key>
    <array>
        <string>/Library/Scripts/company/airport_off.sh</string>
    </array>
    <key>WatchPaths</key>
    <array>
        <string>/Library/Preferences/SystemConfiguration</string>
    </array>
    <key>UserName</key>
    <string>root</string>
    <key>RunAtLoad</key>
    <true/>
</dict>
</plist>

I will see if I can post the script later. It was put together by someone else before I came onboard so I just need to get an OK to post it, or post an alternate version if need be, but high level, it figures out which "port" is wireless (Wi-Fi or AirPort depending on the OS), then looks at all "Ethernet" connections and sees if any of them are set to "active" by doing something like

ifconfig en1 | awk -F': ' '/status/{print $NF}'

If any "Ethernet" designated port is showing as "active" from the above command, it will run a:

networksetup -setairportpower $wifi off

on whichever port is the wireless one.

mm2270
Legendary Contributor III

Hey @bpavlov Just as a quick follow up, here's a script that can be used in conjunction with the above LaunchDaemon (you could potentially also use a cached offline policy that uses the Network State change trigger, but may not be as reliable as a local daemon)
I reworked this script almost from scratch since I didn't feel comfortable posting the original version, so this is a bit different than what we use, but has the same principle.

#!/bin/bash

WIFIPORT=$(networksetup -listallhardwareports | awk '/Wi-Fi|AirPort/{getline; print $NF}')
ETHERPORTS=$(networksetup -listallhardwareports | grep -A1 "Hardware Port:.*Ethernet" | awk -F': ' '/Device:/{print $NF}')
ETHERNAMES=$(networksetup -listallhardwareports | awk -F': ' '/Hardware Port:.*Ethernet/{print $NF}')

for i in $ETHERPORTS; do
    ENETPORTS+=("$i")
done

for a in "$ETHERNAMES"; do
    ENETNAMES+=("$a")
done

x=0
while read PORT; do
    if [[ $(ifconfig ${ENETPORTS[$x]} | awk '/status:/{print $NF}') == "active" ]]; then
        echo "${ENETNAMES[$x]} was active. Setting Wi-Fi to Off."
        networksetup -setairportpower $WIFIPORT off
        WIFIOFF="yes"
        break
    else
        echo "${ENETNAMES[$x]} was not active. Continuing..."
    fi
    let x=$((x+1))
done < <(printf '%s
' "${ENETPORTS[@]}")

if [ "$WIFIOFF" ]; then
    echo "Wi-Fi was disabled"
else
    echo "Wi-Fi was not disabled"
fi

sleep 10

Essentially, figure out which port is Wi-Fi or AirPort, build a list of all hardware Ethernet ports, then place them into an array and loop over each "Ethernet" port and check to see if its status is "active" If so, run a command to turn off Wi-Fi and break from the loop. If none are active, just exit.
The 10 second sleep at the end is to prevent launchd from "throttling" the job run if it tries again too soon, and spewing a bunch of errors into the system.log about it.

sean
Valued Contributor

We have a similar thing. Turns off Wi-Fi if there is an active Ethernet and informs user as such. If the script turned off Wi-Fi, then on a disconnect it turns Wi-Fi back on, else Wi-Fi stays off on a disconnect. The script only bothers if it is on our domain. It also renews dhcp to ensure our DHCP/DNS servers have the correct record.

Same reasons, don't want people bridging networks and wasting Wi-Fi addresses. Same trigger method.

bpavlov
Honored Contributor

Hey @mm2270 I want to pick your brain on something in this script. Anyone is really welcome to answer this as I'm just trying to get a better understanding here. For the most part, I'm following the logic and flow of the script. With any script, I like to see what bash is actually doing when it executes so I ran it as bash -x /path/to/script.sh in the command line.

I've been testing it out and comparing the values of the variables and arrays. On my MacBook Retina, for example, I have just 1 TB Ethernet adapter which should mean the array "${ENETPORTS[@]}" should only have 1 entry which is en3 in my case. The for loop computes this correctly. In testing I could echo the values of variables and arrays before the while loop starts. Everything is adding up so far.

However it looks like for whatever reason the ENETPORTS array when fed into the while loop is counting ALL the network interfaces on my computer which is 9 when it should really just be counting 1 interface. I believe it has something to do specifically with this:
$(printf '%s ' "${ENETPORTS[@]}").

Am I going crazy? Shouldn't the while loop stop after it goes through all the ports that have Ethernet in it which should mean that it would stop after 1 run? As you will see the first run of the while loop does what it's supposed to. After that, it does not do what I expect it to do. Along the same lines if the logic I'm following holds true, is the "break" command actually necessary there as well?

For the record, I copied and pasted the script as was and it didn't execute so I had to fix the error on line 26 of your script. So I'm copying the script as I ran it with the minor changes I made:

#!/bin/bash -x

WIFIPORT=$(networksetup -listallhardwareports | awk '/Wi-Fi|AirPort/{getline; print $NF}')
ETHERPORTS=$(networksetup -listallhardwareports | grep -A1 "Hardware Port:.*Ethernet" | awk -F': ' '/Device:/{print $NF}')
ETHERNAMES=$(networksetup -listallhardwareports | awk -F': ' '/Hardware Port:.*Ethernet/{print $NF}')

for i in $ETHERPORTS; do
    ENETPORTS+=("$i")
done

for a in "$ETHERNAMES"; do
    ENETNAMES+=("$a")
done

x=0
while read PORT; do
    if [[ $(ifconfig ${ENETPORTS[$x]} | awk '/status:/{print $NF}') == "active" ]]; then
        echo "${ENETNAMES[$x]} is active. Setting Wi-Fi to Off."
        networksetup -setairportpower $WIFIPORT off
        break
    else
        echo "${ENETNAMES[$x]} is not active. Continuing..."
    fi
    let x=$((x+1))
done <<$(printf '%s
' "${ENETPORTS[@]}")

if [ $(networksetup -getairportpower "$WIFIPORT" | awk '{print $NF}') = "Off ]; then
    echo "Wi-Fi was disabled."
else
    echo "Wi-Fi was not disabled."
    exit 1
fi

sleep 10

Here's the executable output:

++ networksetup -listallhardwareports
++ awk '/Wi-Fi|AirPort/{getline; print $NF}'
+ WIFIPORT=en0
++ networksetup -listallhardwareports
++ grep -A1 'Hardware Port:.*Ethernet'
++ awk '-F: ' '/Device:/{print $NF}'
+ ETHERPORTS=en3
++ networksetup -listallhardwareports
++ awk '-F: ' '/Hardware Port:.*Ethernet/{print $NF}'
+ ETHERNAMES='Thunderbolt Ethernet'
+ for i in '$ETHERPORTS'
+ ENETPORTS+=("$i")
+ for a in '"$ETHERNAMES"'
+ ENETNAMES+=("$a")
+ x=0
++ networksetup -getairportpower en0
++ awk '{print $NF}'
+ read PORT
++ ifconfig en3
++ awk '/status:/{print $NF}'
+ [[ inactive == ac	ive ]]
+ echo 'Thunderbolt Ethernet is not active. Continuing...'
Thunderbolt Ethernet is not active. Continuing...
+ let x=1
+ read PORT
++ ifconfig
++ awk '/status:/{print $NF}'
+ [[ active
inactive
inactive
inactive
active
inactive
inactive == ac	ive ]]
+ echo ' is not active. Continuing...'
 is not active. Continuing...
+ let x=2
+ read PORT
++ ifconfig
++ awk '/status:/{print $NF}'
+ [[ active
inactive
inactive
inactive
active
inactive
inactive == ac	ive ]]
+ echo ' is not active. Continuing...'
 is not active. Continuing...
+ let x=3
+ read PORT
++ ifconfig
++ awk '/status:/{print $NF}'
+ [[ active
inactive
inactive
inactive
active
inactive
inactive == ac	ive ]]
+ echo ' is not active. Continuing...'
 is not active. Continuing...
+ let x=4
+ read PORT
++ ifconfig
++ awk '/status:/{print $NF}'
+ [[ active
inactive
inactive
inactive
active
inactive
inactive == ac	ive ]]
+ echo ' is not active. Continuing...'
 is not active. Continuing...
+ let x=5
+ read PORT
++ ifconfig
++ awk '/status:/{print $NF}'
+ [[ active
inactive
inactive
inactive
active
inactive
inactive == ac	ive ]]
+ echo ' is not active. Continuing...'
 is not active. Continuing...
+ let x=6
+ read PORT
++ ifconfig
++ awk '/status:/{print $NF}'
+ [[ active
inactive
inactive
inactive
active
inactive
inactive == ac	ive ]]
+ echo ' is not active. Continuing...'
 is not active. Continuing...
+ let x=7
+ read PORT
++ ifconfig
++ awk '/status:/{print $NF}'
+ [[ active
inactive
inactive
inactive
active
inactive
inactive == ac	ive ]]
+ echo ' is not active. Continuing...'
 is not active. Continuing...
+ let x=8
+ read PORT
++ ifconfig
++ awk '/status:/{print $NF}'
+ [[ active
inactive
inactive
inactive
active
inactive
inactive == ac	ive ]]
+ echo ' is not active. Continuing...'
 is not active. Continuing...
+ let x=9
+ read PORT