Hatena::ブログ(Diary)

ザリガニが見ていた...。 このページをアンテナに追加 RSSフィード

2015-03-29

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

iMacのIPアドレスを確認したい。そんなの簡単、簡単。システム環境設定 >> ネットワークで確認すればいい。

上段のネットワークサービスほど優先順位が高くなる。よって、現在のIPアドレスは特に指定しない限り、10.0.1.20が利用されることになる。

f:id:zariganitosh:20150327175025p:image:w450

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


ipconfig

  • ipconfigにデバイス名(en0、en1など)を指定すると、そのデバイスに割り当てられたIPアドレスを取得できた。
$ ipconfig getifaddr en0
10.0.1.20

$ ipconfig getifaddr en1
10.0.1.102

しかし...

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

ifconfig

  • ifconfigなら、すべてのデバイスの接続状況が一覧できる。
$ ifconfig
lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> mtu 16384
	options=3<RXCSUM,TXCSUM>
	inet6 ::1 prefixlen 128 
	inet 127.0.0.1 netmask 0xff000000 
	inet6 fe80::1%lo0 prefixlen 64 scopeid 0x1 
	nd6 options=1<PERFORMNUD>
gif0: flags=8010<POINTOPOINT,MULTICAST> mtu 1280
stf0: flags=0<> mtu 1280
en0: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500
	options=10b<RXCSUM,TXCSUM,VLAN_HWTAGGING,AV>
	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<PERFORMNUD>
	media: autoselect (1000baseT <full-duplex,flow-control>)
	status: active
en1: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> 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<PERFORMNUD>
	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
  • awk使って、IPアドレスのみ取り出してみた。
$ ifconfig|awk '/inet /{print $2}'
127.0.0.1
10.0.1.20
10.0.1.102

しかし...

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

AppleScript

  • AppleScriptにはsystem infoがあり、その中にIPv4 addressというプロパティがあった。
  • そして、osascriptを使えば、コマンドラインからでもAppleScriptを実行できる。
$ osascript -e 'IPv4 address of (system info)'
10.0.1.20

しかし...

  • Wi-FiEthernetの両方を接続した時、Wi-Fiを優先しても、常にEthernetのIPアドレスが表示されてしまう...。なぜだろう?

いろいろな方法

  • 調べてみると、多くの方がコマンドでローカルIPアドレスを取得することに苦労しているようだ。
  • こうなったら上記ページで紹介されている方法を片っ端からすべて試してみる。
  • そしてついに「優先順位の高いローカルIPアドレス」を取得することができた!
$ 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 ブリッジ

  • Wi-FiのIPアドレスを取得できた!
$ 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

  • でもWi-Fiが切であっても、IPアドレスが表示されてしまう...。(固定IPアドレスを指定している場合)
$ 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を利用した場合はオフラインではルーターは未設定となるが、
    • 手入力ではオフラインでも入力済みのルーターアドレスが表示される。

f:id:zariganitosh:20150328173001p:image:w450

  • 上記のように設定しておくと、オフラインでも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

nsbynsby 2015/04/02 13:27 ルーティングテーブルをnetstat -rで調べれればわかるのでは?

$ netstat -rn -f inet
Routing tables

Internet:
Destination Gateway Flags Refs Use Netif Expire
default 192.168.0.250 UGSc 14 0 en1
default 192.168.0.250 UGScI 5 0 en0

最初のdefaultのen1が優先デバイスで、en1のアドレスを調べれればいいだけだと
思いますが

例えば、こんな感じでどうでしょう?
% ipconfig getifaddr `netstat -rn -f inet | grep '^default' | awk 'NR==1 {print $6}'`

# default routeがある前提です

zariganitoshzariganitosh 2015/04/03 18:03 コメントありがとうございます!
素晴らしいです!ばっちりです。
このようなシンプルなワンライナーで求めたいと思っていました。
しかも、OSXに限らず、多くのUNIX環境で利用できると思います。
いろいろ試してみると、現状の自分の環境ではipconfigは2つ目以降の引数を無視するようなので、さらに短く以下のワンライナーでもOKでした。

ipconfig getifaddr `netstat -rn -f inet | awk '/^default/{print $6}'`

また一つ、自分の知らない新たな知識が広がりました。
ブログ書いてて良かったなと思える瞬間です。
ありがとうございました!

nsbynsby 2015/04/04 03:41 お役に立てたようでなりよりです

こちらこそ、このブログは色々と深く掘り下げて書かれてあるのでかなり参考になります
awkのパターン指定方法を忘れて、ググったら見事「なるべく書かないawkの使い方」がHitして
思い出したしだいです(^^;

結局awkでdefaultを1個だけ切り出すことはできなかったんで、NR==1とか書いて逃げてました
今思えば、defaultを1個切り出すのではなく、1回表示したらexitすればよかったのかなと思います

ipconfig getifaddr `netstat -rn -f inet | awk '/^default/{print $6;exit}'`

これからも、深く掘り下げた記事を期待してます

スパム対策のためのダミーです。もし見えても何も入力しないでください
ゲスト


画像認証

トラックバック - http://d.hatena.ne.jp/zariganitosh/20150329/get_local_ip_address
リンク元