Ruby 2.1.0 では Socket.getifaddrs で簡単にインターフェイスのアドレスを取れる

# Ruby 2.1.0 に Socket.getifaddrs が登場

ネットワーク畑の人間からすれば、 Ruby 2.1.0 の目玉機能は間違いなく `Socket.getifaddrs` だ。これがあれば、「インターフェイス名からそのインターフェイスの持っているIPアドレスを確認する」ことが簡単にできるようになる。

## これまで
これまでは、 ifconfig や ip コマンドの結果から頑張って取ってこなくてはならなかった。

    res = `ifconfig eth0`
    res =~ /inet addr:(\d+\.\d+\.\d+\.\d+)/
    addr = $1

できるけど気持ち悪いし、v6アドレスになると死ぬし、外部コマンド実行をするとエラー対応とかセキュリティとかが厄介な問題になる。

## これから
Ruby 2.1.0 からは、 `Socket.getifaddrs` メソッドが追加されている。これからは、Ruby に完結したコードでアドレスが取れるようになる。

>
拡張ライブラリ socket に Socket.getifaddrs というメソッドを新設しています。 これは getifaddrs(3) のラッパで、Socket::Ifaddr というネットワークインタフェースの情報を保持するクラスを追加してその配列を返すようにしています。これによりローカルアドレスとそのネットワークインタフェースの対応やネットマスク、Macアドレスの対応などがわかるようになります。 ifconfig の結果を parse してゴニョゴニョやってたりしたのが簡単にできるようになるんじゃないかと思います。
http://d.hatena.ne.jp/nagachika/20130511/ruby_trunk_changes_40634_40646

ということで、あ、やっぱり ifconfig ゴニョゴニョするの楽にしたいって話はあったんだ。すごく…嬉しいです…。

# 使い方

## 単純に出力を見てみる

```ruby
require 'socket'
Socket.getifaddrs
#=> [#<Socket::Ifaddr lo UP,LOOPBACK,RUNNING,0x10000 PACKET[protocol=0 lo hatype=772 HOST hwaddr=00:00:00:00:00:00]>,
     #<Socket::Ifaddr eth0 UP,BROADCAST,RUNNING,MULTICAST,0x10000 PACKET[protocol=0 eth0 hatype=1 HOST hwaddr=00:0c:29:XX:XX:XX] broadcast=PACKET[protocol=0 eth0 hatype=1 HOST hwaddr=ff:ff:ff:ff:ff:ff]>,
     #<Socket::Ifaddr tun0 UP,POINTOPOINT,RUNNING,NOARP,MULTICAST,0x10000>,
     #<Socket::Ifaddr lo UP,LOOPBACK,RUNNING,0x10000 127.0.0.1 netmask=255.0.0.0>,
     #<Socket::Ifaddr eth0 UP,BROADCAST,RUNNING,MULTICAST,0x10000 192.168.106.129 netmask=255.255.255.0 broadcast=192.168.106.255>,
     #<Socket::Ifaddr tun0 UP,POINTOPOINT,RUNNING,NOARP,MULTICAST,0x10000 10.111.0.2 netmask=255.255.255.255 dstaddr=10.111.0.1>,
     #<Socket::Ifaddr lo UP,LOOPBACK,RUNNING,0x10000 ::1 netmask=ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff>,
     #<Socket::Ifaddr eth0 UP,BROADCAST,RUNNING,MULTICAST,0x10000 fe80::XXXX:XXXX:XXXX:XXXX%eth0 netmask=ffff:ffff:ffff:ffff::>]
```

`Socket.getifaddrs` は、 `Socket::Ifaddr` オブジェクトの配列を返す。

## インターフェイスに紐付くオブジェクトを取得する

返ってくるのが単なる `Array` オブジェクトなので、普通に `Array#select` で絞り込めばわかりやすい。

```ruby
require 'socket'
Socket.getifaddrs.select{|x| x.name == "eth0"}
#=> [#<Socket::Ifaddr eth0 UP,BROADCAST,RUNNING,MULTICAST,0x10000 PACKET[protocol=0 eth0 hatype=1 HOST hwaddr=00:0c:29:XX:XX:XX] broadcast=PACKET[protocol=0 eth0 hatype=1 HOST hwaddr=ff:ff:ff:ff:ff:ff]>,
     #<Socket::Ifaddr eth0 UP,BROADCAST,RUNNING,MULTICAST,0x10000 192.168.106.129 netmask=255.255.255.0 broadcast=192.168.106.255>,
     #<Socket::Ifaddr eth0 UP,BROADCAST,RUNNING,MULTICAST,0x10000 fe80::XXXX:XXXX:XXXX:XXXX%eth0 netmask=ffff:ffff:ffff:ffff::>]
```

これで、 `eth0` という名前のインターフェイスに紐付くアドレス情報がすべて手に入った。

## 欲しいアドレスファミリを指定する

IPv4 アドレスだけがほしいなら、条件をちょっと追加してやればいい。

```ruby
require 'socket'
Socket.getifaddrs.select{|x| x.name == "eth0" and x.addr.ipv4?}
#=> [#<Socket::Ifaddr eth0 UP,BROADCAST,RUNNING,MULTICAST,0x10000 192.168.106.129 netmask=255.255.255.0 broadcast=192.168.106.255>]
```

上の `x.addr` は `Addrinfo` オブジェクトなので、ほかにもアドレスに関する各種関数が利用できる。マルチキャストか、プライベートか、といったあたりなら一発だ。

## あるインターフェイスに紐付いた IPv4 アドレスを文字列として取得する
`eth0` に紐付いている IPv4 アドレスの文字列が欲しい！と思ったときは、下のように書けばいいことになる。

```ruby
require 'socket'
Socket.getifaddrs.select{|x|
  x.name == "eth0" and x.addr.ipv4?
}.first.addr.ip_address
#=> "192.168.106.129"
```

意味するところがとっても明確。
とはいえ、上の例のメソッドチェーンは長い。メソッドチェーンが長いってことは、真面目に作るならエラー処理もたくさん必要ってこと。実際にコードに利用する際は気をつけて。

# まとめ

ということで、 Ruby 2.1.0 からは `Socket.getifaddrs` メソッドが使用できるようになった。
これまでは、アドレス情報を取ろうとしたら ifconfig からカリカリと取得しなければならなかったけれど、これからはそんなことをしなくてもよくなった。もっと構造化された枠内で、 Ruby らしいコードでアドレスを取得できる。幸せ。

akrさん、usaさんはじめ、コミッタのみなさんに感謝。
https://groups.google.com/forum/#!msg/ruby-core-google/BnOLZkp1CQk/zqqQ-F57RxgJ
http://magazine.rubyist.net/?0044-CRubyCommittersWhosWho2013