3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Macのネットワーク環境を、WiFi接続先により自動で切り替える

Last updated at Posted at 2022-06-02

MacOSはネットワーク周りの設定をまとめて管理できる、 「ネットワーク環境」 と言う仕組みを持っています。これは繋ぐネットワーク先によって手動で設定する必要がある場合に、簡単に切り替えることができて 一応便利 です。
ところが何故か接続先に応じて自動でネットワーク環境が切り替わる機能がありません。そこで以下のページにあるスクリプトを元に手直しした物を使わせて頂いています。

時が経ちオリジナルのままでは動作が怪しいところも出てきたので、何かの参考になればと思い私のバージョンを公開します。

動作環境

  • MacBook Pro 14inch(2021)/macOS Monterey(12.4)
  • MacBook 12inch(2016)/macOS Mojave

ソース

wifienv_selector.sh

(オリジナル:wifi_environment_auto_selector.sh

wifienv_selector.sh
#!/bin/sh

# Set PATH strictly to avoid security hole
PATH=/usr/bin:/bin:/usr/sbin:/sbin:

#
# Configuration

logfile=${HOME}/logs/wifienv.log
ssidfile=${HOME}/.wifienv_ssid
wifiIF="en0"

log_enabled=1
# 0: do not log messages, 1: log messages

use_alert=1
# for sending messages to user,
#   0: use notification, 1: use alert dialog

#
# functions

plog(){
    if [ $log_enabled -ne 0 ]; then
	    /bin/echo -n `date +'%Y/%m/%d %H:%M:%S'`" " >> $logfile
	    /bin/echo $1 >> $logfile
    fi
}

last_ssid(){
    if [ -r $ssidfile ]; then
	    read ssid < $ssidfile
	    echo $ssid
    else
	    echo ""
    fi
}

save_ssid(){
    echo $1 > $ssidfile
}

dialog(){
    if [ $use_alert -ne 0 ]; then
	    osascript -e 'display alert "'"${1}"'" message "'"${2}"'" as critical giving up after 5' >/dev/null 2>&1
    else
	    osascript -e 'display notification "'"${2}"'" with title "'"${1}"'" subtitle "'"${3}"'"' >/dev/null 2>&1
    fi
}

switch2loc() {
    newloc=$1
    newssid=$2
    curloc=`networksetup -getcurrentlocation`
    lastssid=`last_ssid`
    save_ssid ${2}

    if [ x${lastssid} = x${newssid} ]; then
	    plog "SSID is not changed: ${newssid}"
	    return
    fi
    
    plog "SSID is changed: ${lastssid} -> ${newssid}"
    
    if [ x${curloc} = x${newloc} ]; then
	    plog "location is identical: (${curloc})"
	    dialog "SSIDが“${2}”になりました" \
	           "ネットワーク環境(${curloc})は変更ありません"
	    return
    fi
    
    if networksetup -switchtolocation ${newloc}; then
	    plog "location is changed: ${curloc} -> ${newloc}"
	    dialog "SSIDが“${newssid}”になりました" \
	           "ネットワーク環境を“${newloc}”に変更しました" 
    else
	    plog "failed to switch location from {$curloc} to ${newloc}"
    fi
}

IsWiFiPwrOff() {
    networksetup -getairportpower $1 | awk 'tolower($4) ~ /on/ { exit 1 }'
}

getSSID() {
    count=5

    while [ $count -gt 0 ]; do
		let count=count-1
		if ssid=`networksetup -getairportnetwork ${wifiIF} |\
                 awk '/^Current/ { print $NF; exit 0 }
       	   	 	  		   	     { exit 1 }'`; then
	        break
	    fi
	    sleep 1
    done

    if [ $count -le 0 ]; then
	    plog "failed to get SSID"
	    dialog "エラー" "SSIDが取得できません"
	    echo ""
    else
	    echo $ssid
    fi
}

#
# main

# check WiFi Power
if IsWiFiPwrOff ${wifiIF} ; then
    plog "WiFi is off"
    exit 0
fi

ssid=`getSSID`

case ${ssid} in
    "MyHome_a" | "MyHome_bg")
	    switch2loc "自宅" ${ssid}
	    ;;
    "Office_8F" | "Office_9F")
        switch2loc "Office" ${ssid}
    *)
	    switch2loc "自動" ${ssid}
	    ;;
esac

${HOME}/Library/LaunchAgents/wifienv_selector.plist

(旧:wifi_environment_auto_selector.plist

wifienv_selector.plist
<?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>wifienv_selector</string>
    <key>ProgramArguments</key>
    <array>
        <string>/Users/yourname/bin/wifienv_selector.sh</string>
    </array>
    <key>RunAtLoad</key>
    <true/>
    <key>WatchPaths</key>
    <array>
        <!-- string>/var/run/resolv.conf</string -->
        <string>/Library/Preferences/com.apple.wifi.known-networks.plist</string>
    </array>
</dict>
</plist>

<key>ProgramArguments</key>内の<string>/Users/yourname/bin/wifienv_selector.sh</string>はwifienv_selector.shのpathです。適宜変更して下さい。

/etc/newsyslog.d/wifienv.conf

/etc/newsyslog.d/wifienv.conf
# logfilename: $HOME/logs/wifienv.log
# [owner:group]: yourname:staff
# mode: 640
# count: 12 (1 year)
# size: *
# when: $M1D0 (0:00 on 1st day of month)
# flags: J (compressed by bzip2)
/Users/yourname/logs/wifienv.log yourname:staff 640 12 * $M1D0 J

logfilename(/Users/yourname/logs/wifienv.log)や[owner:group]yourname:staff)は適宜変更して下さい。

使い方

上記ファイルを用意した後、シェルで

$ launchctl load ~/Library/LaunchAgents/wifienv_selector.plist

を実行します。
なお二度目以降は、以下の通り一旦unloadしてからloadします。

$ launchctl unload ~/Library/LaunchAgents/wifienv_selector.plist
$ launchctl load ~/Library/LaunchAgents/wifienv_selector.plist

主な変更点

wifienv_selector.sh

(オリジナル:wifi_environment_auto_selector.sh

通知はalert dialogで行う

私は良くnotificationを見落としてしまいます。また集中モードによっては、表示されない事もあります。その為alert dialogで通知するようにしました。なおOKボタンを押さずとも、5秒で閉じます。
notificationの方がお好みの方は、use_alert=10に変更して下さい。

SSIDのみの変更でも通知する

切り替わったのに気が付かないのは、やはり気持ち悪いので。

ログを取る

正常に動作しているのか不安になった時に、確認したい事もあります。不要な場合はlog_enabled=10にして下さい。ファイル名はlogfileで設定されています。

その他

  • scselectでネットワーク環境を変更していたのを、networksetup -switchtolocationで変更するようにしました。

  • networksetup -getairportnetworkでSSIDを得ていますが、タイミングによっては失敗する事があったので、修正しました。これはコマンドの出力が以下のように変化する為です。

(変更前) Current Wi-Fi Network: <last ssid>
  ↓
(一旦切断) You are not associated with an AirPort network.
  ↓
(再接続)  Current Wi-Fi Network: <new ssid>

wifienv_selector.plist

(オリジナル:wifi_environment_auto_selector.plist

WatchPathsを変更... しない方が良い

以下は接続先のすべてでDHCPが用意されている場合です。
従ってDHCPが用意されていないネットワークにつなぐ場合には機能しません。この場合はWatchPaths/Library/Preferences/com.apple.wifi.known-networks.plistにすべきです。

OSのバージョンによって、WiFi I/Fが変わった時に変更されるファイルは異なるようです。しかしUNIX系OS標準の/etc/resolv.confなら、DHCPでnameserverが変わる度に必ず更新される筈です。
調べてみたところ、

$ LC_ALL=C \ls -l /etc/resolv.conf
lrwxr-xr-x  1 root  wheel  22 May 10 06:30  /etc/resolv.conf -> ../var/run/resolv.conf

/var/run/resolv.confへのシンボリックリンクになっていました。この/var/run/resolv.confWatchPathsに設定した所、問題なく動いています。

課題など

Ethernetを扱えるか?

未調査ですが、Ethernetを接続した場合でもこれで扱えるようにしたい物です。

firewallの切り替え

wifienv_selector.shの最後のcase文の所を改変すれば、firewallの制御もできます。MacOS純正のfirewallなら、コマンドラインから切り替える事ができそうです。

こちらなどによれば、firewallをon/offするには:

# firewall OFF
$ sudo sudo defaults write /Library/Preferences/com.apple.alf globalstate -int 0
# firewall ON
$ sudo sudo defaults write /Library/Preferences/com.apple.alf globalstate -int 1

なのだそうです。そこで /Library/Preferences/com.apple.alfと言うファイルが無いか調べてみると、

$ ls /Library/Preferences/com.apple.alf*
/Library/Preferences/com.apple.alf.plist
$ file /Library/Preferences/com.apple.alf.plist
/Library/Preferences/com.apple.alf.plist: Apple binary property list

/Library/Preferences/com.apple.alf.plistと言うproperty list ファイルが見つかりました。これを plutil -pで覗いてみると、firewallの設定らしきものが書かれています。
よってこのファイルを複数用意し、差し替える事ができるのでは無いかと予想しています。

ただ私が使っているAnti Virus(ESET Cyber Security Pro)には接続先により動作を変えられるfirewallがあるので、実際に作ってはいません。予想される問題としてはfirewallを設定変更するにはroot権限が必要になるので、security holeにならないように注意する必要がある所でしょうか。

参考

3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?