優先順位の高いローカルIPアドレスを取得するコマンド

iMacIPアドレスを確認したい。そんなの簡単、簡単。システム環境設定 >> ネットワークで確認すればいい。
上段のネットワークサービスほど優先順位が高くなる。よって、現在のIPアドレスは特に指定しない限り、10.0.1.20が利用されることになる。

このようにGUIでは何の苦労もなく確認できるのだけど、コマンドを使って確認しようとすると、途端に深い悩みとなった...。

ipconfig

$ ipconfig getifaddr en0
10.0.1.20

$ ipconfig getifaddr en1
10.0.1.102


しかし...

  • バイス名を指定する必要があり、すべてを網羅する自信がない。
  • また、どちらの優先順位が高いのかは分からない...。

ifconfig

  • ifconfigなら、すべてのデバイスの接続状況が一覧できる。
$ ifconfig
lo0: flags=8049 mtu 16384
	options=3
	inet6 ::1 prefixlen 128 
	inet 127.0.0.1 netmask 0xff000000 
	inet6 fe80::1%lo0 prefixlen 64 scopeid 0x1 
	nd6 options=1
gif0: flags=8010 mtu 1280
stf0: flags=0<> mtu 1280
en0: flags=8863 mtu 1500
	options=10b
	ether xx:xx:xx:xx:xx:xx 
	inet6 xxxx::xxxx:xxxx:xxxx:xxxx%en0 prefixlen 64 scopeid 0x4 
	inet 10.0.1.20 netmask 0xffffff00 broadcast 10.0.1.255
	nd6 options=1
	media: autoselect (1000baseT )
	status: active
en1: flags=8863 mtu 1500
	ether xx:xx:xx:xx:xx:xx
	inet6 xxxx::xxxx:xxxx:xxxx:xxxx%en1 prefixlen 64 scopeid 0x5 
	inet 10.0.1.102 netmask 0xffffff00 broadcast 10.0.1.255
	nd6 options=1
	media: autoselect
	status: active
...中略...
  • リスト中の「inet 」の項目がIPアドレスIPv4)らしいので、抜き出してみた。
$ ifconfig|grep 'inet '
	inet 127.0.0.1 netmask 0xff000000 
	inet 10.0.1.20 netmask 0xffffff00 broadcast 10.0.1.255
	inet 10.0.1.102 netmask 0xffffff00 broadcast 10.0.1.255
$ ifconfig|awk '/inet /{print $2}'
127.0.0.1
10.0.1.20
10.0.1.102


しかし...

  • どちらの優先順位が高いのかは分からない...という問題は依然残る...。

AppleScript

$ osascript -e 'IPv4 address of (system info)'
10.0.1.20


しかし...

いろいろな方法

$ python -c "import socket;print([(s.connect( ('8.8.8.8', 80) ), s.getsockname()[0], s.close()) for s in [socket.socket(socket.AF_INET, socket.SOCK_DGRAM)]][0][1])"
10.0.1.20
  • OSX標準で、余分なライブラリをインストールせずに、優先順位の高いローカルIPアドレスを取得する方法は、これ一つだった。


しかし...

  • 8.8.8.8という外部のDNSサーバーへの接続に依存しているところに不満が残る。
    • 8.8.8.8を指定するなら、必ずインターネットに接続している必要がある。
  • 8.8.8.8の部分はLAN内のDNSサーバー10.0.1.1などを指定してもOKなのだけど、
    • 192.168.x.xとか、172.16.x.xなどもあり得る。環境に合わせて変更するのは面倒だ。
  • 自分自身で設定したネットワーク設定なのだから、外部に頼らず、自己解決したい気がする。

networksetup

  • OSXのネットワーク環境を設定するコマンドとして、networksetupが用意されている。
  • networksetupを使えば、システム環境設定 >> ネットワークと同様の設定ができるらしい。
  • 優先度順にネットワークサービス名のリストが表示された!
$ networksetup -listallnetworkservices
An asterisk (*) denotes that a network service is disabled.
Wi-Fi
Ethernet
Thunderbolt Ethernet 2
Thunderbolt ブリッジ
$ networksetup -getinfo Wi-Fi
Manually Using DHCP Router Configuration
IP address: 10.0.1.102
Subnet mask: 255.255.255.0
Router: 10.0.1.1
IPv6: Automatic
IPv6 IP address: none
IPv6 Router: none
Wi-Fi ID: xx:xx:xx:xx:xx:xx
$ networksetup -getinfo Wi-Fi
Manually Using DHCP Router Configuration
IP address: 10.0.1.102
IPv6: Automatic
IPv6 IP address: none
IPv6 Router: none
Wi-Fi ID: xx:xx:xx:xx:xx:xx
  • Subnet maskRouterが表示されていなければ、オフラインと考えてよいのだろうか?
  • ならば、オフラインを除外してネットワークサービスの順番にIPアドレスを調べればいいのだ!
$ networksetup -listallnetworkservices|sed '1d'|while read s; do networksetup -getinfo "$s"|grep -q '^Router: ' && networksetup -getinfo "$s"|awk -F': ' '/^IP address: /{print $2}'; done|head -1
10.0.1.20
networksetup -listallnetworkservices | sed '1d' | while read s
do 
  if networksetup -getinfo "$s" | grep -q '^Router: '; then
    networksetup -getinfo "$s" | awk -F': ' '/^IP address: /{print $2}'
  fi
done | head -1


しかし...

  • 手入力でTCP/IPを設定した場合は、オフラインであってもSubnet maskRouterが表示されてしまった...。
  • 結局、networksetup -getinfoはネットワークサービスのTCP/IPに設定された情報を表示しているに過ぎない。
    • DHCPを利用した場合はオフラインではルーターは未設定となるが、
    • 手入力ではオフラインでも入力済みのルーターアドレスが表示される。

  • 上記のように設定しておくと、オフラインでもnetworksetup -getinfo Wi-Fiは以下の情報を出力してしまう...。
$ networksetup -getinfo Wi-Fi
Manual Configuration
IP address: 10.0.1.102
Subnet mask: 255.255.255.0
Router: 10.0.1.1
IPv6: Automatic
IPv6 IP address: none
IPv6 Router: none
Wi-Fi ID: b8:09:8a:b9:c8:5d

ifconfig+networksetup

  • こうなったら、ifconfigとnetworksetupの合わせ技でやってみる。
    • ifconfigでオンラインのIPアドレスを確認して、
    • networksetupでネットワークサービスの優先順位を確認するのだ。
$ networksetup -listallnetworkservices|sed '1d'|while read s; do networksetup -getinfo "$s"|grep -q '^IP address: ' && ifconfig|awk '/inet /{print $2}'|grep `networksetup -getinfo "$s"|awk -F': ' '/^IP address: /{print $2}'`; done|head -1
10.0.1.20
networksetup -listallnetworkservices | sed '1d' | while read s
do 
  if networksetup -getinfo "$s" | grep -q '^IP address: '; then
    i=`networksetup -getinfo "$s" | awk -F': ' '/^IP address: /{print $2}'`
    ifconfig | awk '/inet /{print $2}' | grep "$i"
  fi
done | head -1


できた!


ipconfig+netstat

  • コメントで教えて頂いた方法が素晴らしい!(nsbyさんに感謝!)
  • きっと経験豊富なネットワーク管理者はこの方法を使っているはず。
$ ipconfig getifaddr `netstat -rn -f inet | awk '/^default/{print $6;exit}'`
10.0.1.20
  • networksetupのようなOSXに依存するコマンドも使わないので、多くのUNIX環境で利用できるはず。


仕組みとしては...

  • 例えばipconfig getifaddr en0を実行すれば、en0に割り当てられたIPv4アドレスが求められる。
  • ならば、en0の部分に、優先順位の高いネットワーク インターフェース名を指定すればいいのだ。
  • それを求めるのが`netstat -rn -f inet | awk '/^default/{print $6;exit}'`の部分。
    • netstat -rn -f inetコマンドでルーティングテーブルを表示している。
      • rオプションは、ルーティングテーブルを表示する指定。
      • nオプションは、ホストやユーザーの名前解決を行わず数字のまま出力する指定。(たぶんコマンドの動作が軽くなる)
      • -f inetオプションは、IPv4のルーティングテーブルのみに限定する指定。
    • 上記出力からawk '/^default/{print $6;exit}'コマンドで、デフォルトのNetifのみ取り出している。
      • exitによって、最初の(優先順位の高い)defaultを見つけたら、その後の処理を中断している。
  • シンプルな動作の組合せなので、このワンライナーを丸暗記しなくても覚えられそう。
  • (忘れやすいので)できる限りオプション指定しないで入力してみると、以下でもOKだった。
ipconfig getifaddr `netstat -r | awk '/^default/{print $6}'`
  • xargsを利用して、以下のように表現してもOKだった。
netstat -r | awk '/^default/{print $6}' | xargs ipconfig getifaddr