接上回的 在MBP苏醒时执行自动任务 ,继续说。

今次的重点在于网络切换。

这个任务,要被细分为很多细节,不过并没那么难,都很容易找到恰当的办法。

 

使用 macOS 的 “位置”

首先,经验告诉我们,在多种场所切换不同的网络配置,最佳方案是“位置 / Location”:

Location

Location

所以,如图所示,首先设定两个位置,一个是Home,一个是办公室。Home设置只有WIFI,IP地址是预分配的:192.168.0.71;办公室呢,WIFI被加入设施列表,但设置为关闭状态,此外Wireless被加入设置列表,作为主要的网络接口,办公室也是DHCP预分配的,WIFI为192.168.0.172,Wireless为192.168.0.72,网关为 192.168.0.254。

为什么不把HOME IP设置的和办公室一样呢?其实也是可以的,因为办公室和家里的网关不同,可以用于区别。我这里设置的不一样更多是一个历史缘由,可以忽略。

Office Location

HOME Location

macOS的位置功能,允许你将全部网络设施选择性地配置到不同的位置。所以我花了不少时间来试验和选择出了最佳的组合方案,当然,下一次增加更多位置时就简单了,我想我会很快搞的定。

 

脚本操作

全局变量

PAC='http://127.0.0.1:9050/pac'
BYPASS='127.0.0.1 localhost test.local 163.com sina.com.cn'

WIFI=Wi-Fi
LOC_WIFI='HOME'
IP_WIFI='192.168.0.71'

WIRELESS='Thunderbolt Bridge'
LOC_WIRELESS='SUWEI-OFFICE'
IP_WIRELESS='192.168.0.72'
IP_WIRELESS_wifi='192.168.0.172'

TD=NONE
pac_url=NONE
loc=$(c-location)
ip=$(c-ip)

操作网络位置:

c-location(){ networksetup -getcurrentlocation; }

c-new-location(){
 local loc=${1:-$LOC_WIFI}
 networksetup -switchtolocation $loc
 say="Using New Location: $loc ($ip).\n$(c-print-autoproxy)"
 echo "New Location: $loc"
}

当前IP

c-ip(){
 local ip=$(ifconfig|grep inet|grep -Eiv '127.0.0.1|::'|grep -o ' 192\.168\.[^ ]* ')
 ip=$(echo $ip)
 echo $ip
}

测试网关

is-office() { ping -c 1 192.168.0.254 >/dev/null; }
is-home() { ping -c 1 192.168.0.1 >/dev/null; }

自动代理

c-set-autoproxy(){
 pac_url=${pac_url:-$(networksetup -getautoproxyurl "$TD")}
 echo "Using new autoproxy: '$TD' '$pac_url'"
 networksetup -setautoproxyurl "$TD" "$pac_url"
 # networksetup -getproxybypassdomains "$TD"
 networksetup -setproxybypassdomains "$TD" $BYPASS
 say="${say} [P]"
}
c-print-autoproxy(){
 scutil --proxy
}

隧道或者代理

c-set-tunnel(){
 killall cow
 launchctl start homebrew.mxcl.cow
 say="${say} [COW]"
}

这里自己编制了一个 cow 的服务并做好了配置,放在后面不管了。

say=’…’ 都可以忽略,echo … 都可以忽略。

好,下面是入口代码以及判定代码:

入口代码

echo "---"
location=$(c-location)
ip=$(c-ip)
TD=NONE
pac_url=NONE
case $1 in
    print|info)
        c-info
        ;;
    reset|reload|restart|--reload|--restart|--reset)
        c-set-tunnel
        sleep 5
        c-print-autoproxy
        lsof -Pni :1080
        lsof -Pni :9050
        ;;
    office)
        x-set-office
        sleep 5
        echo; c-info
        ;;
    home)
        x-set-home
        sleep 5
        echo; c-info
        ;;
    *)
        x-set | tee /tmp/switch-location.log
        ;;
esac

判定、以及其他

headline() { printf "\e[0;1m$@\e[0m:\n"; }
headline-begin() { printf "\e[0;1m"; }
headline-end() { printf "\e[0m:\n"; }

c-info(){
 echo "Location: $(c-location)"
 c-print-autoproxy
 lsof -Pni :1080 && echo
 lsof -Pni :9050 && echo
}

x-set(){
 # networksetup -listallnetworkservices
 say="location: $location. IP: $ip."; # echo $say; echo
 c-choose-location && {
 c-set-autoproxy
 sleep 3; #c-print-autoproxy
 c-set-tunnel

# cd /usr/local/bin && echo -e "#!/bin/bash\n/usr/bin/osascript -e \"display notification \\\"\$*\\\"\"" > notify && chmod +x notify;cd -

# say "network changed."
 # echo $say
 /usr/bin/osascript -e "display notification \"$say\" with title \"Network Changed\""
 # say $say
 } || :
}
x-set-home(){
 TD=$WIFI
 location=$LOC_WIFI
 pac_url=$PAC
 c-new-location $location
 c-set-autoproxy
 sleep 5
 c-set-tunnel
}
x-set-office(){
 TD=$WIRELESS
 location=$LOC_WIRELESS
 pac_url=$PAC
 c-new-location $location
 c-set-autoproxy
 sleep 5
 c-set-tunnel
}



c-choose-location(){
    headline "Choosing new location"
    if [[ $ip == $IP_WIFI ]]; then
        if is-home; then
            if [[ $location != $LOC_WIFI ]]; then
                TD=$WIFI
                c-new-location $LOC_WIFI
                echo "    choosed 1: '$TD'"
            else
                echo "skip 1"
                c-print-autoproxy; false
            fi
        elif is-office; then
            if [[ $location == $LOC_WIFI ]]; then
                TD=$WIRELESS
                c-new-location $LOC_WIRELESS
                echo "    choosed 1-1: '$TD'"
            else
                echo "skip 1.1"
                c-print-autoproxy; false
            fi
        else
            echo "skip 1.2"
            c-print-autoproxy; false
        fi
    elif [[ $ip == $IP_WIRELESS ]]; then
        if [[ $location != $LOC_WIRELESS ]]; then
            TD=$WIRELESS
            c-new-location $LOC_WIRELESS
            echo "    choosed 2: '$TD'"
        else
            echo "skip 2"
            c-print-autoproxy;false
        fi
    elif [[ $ip == $IP_WIRELESS_wifi ]]; then
        if is-office; then
            if [[ $location != $LOC_WIFI ]]; then
                echo "skip 3"
                c-print-autoproxy;false
            else
                TD=$WIRELESS
                c-new-location $LOC_WIRELESS
                echo "    choosed 2: '$TD'"
            fi
        elif is-home; then
            if [[ $location != $LOC_WIFI ]]; then
                TD=$WIFI
                c-new-location $LOC_WIFI
                echo "    choosed 3: '$TD'"
            else
                echo "skip 3.1"
                c-print-autoproxy;false
            fi
        else
            echo "skip 3.1"
            c-print-autoproxy;false
        fi
    else
        if [[ $ip == '' ]]; then
            echo "no ip, set home and wifi and waiting 8s..."
            x-set-home
            sleep 8
            if is-office; then
                x-set-office
            elif is-home; then
                x-set-home
            else
                c-print-autoproxy;false
            fi
        else
            echo "skip 4"
            c-print-autoproxy;false
        fi
    fi
}

判定算法虽长,但是逻辑很工整、而且也不复杂。

不过这个博客贴脚本一向很弱智,稍后再考虑要不要改进。

 

整合到一起

上面脚本已经有了,所以在 ~/.wakeup 中加入到该脚本的指向就完成了。

实际经过了若干天的来回测试,确认了能够有效工作。

 

不过,判定部分是没有通用解决方案的,因为这取决于你如何依据不同场所来编制自己的位置。