ソースコードから見るVagrantのネットワーク(1)

Vagrantrubyで書かれているのでプログラム経験のないど素人の私でも眺めているとなんとなくわかる部分があります。そこでソースコードからVagrantでどのようにゲストOSのネットワーク設定をしているのか見てみたいと思います。

Vagrantのネットワーク設定はnetwork.rbに記述されています。
https://github.com/mitchellh/vagrant/blob/master/plugins/providers/virtualbox/action/network.rb

500行弱の短いコードですが、今回は肩ならしとしてnetwork.rbから外部モジュールとして呼び出されているnetwork_ip.rbを見てみたいと思います。
https://github.com/mitchellh/vagrant/blob/master/lib/vagrant/util/network_ip.rb

network_ip.rbはネットワークアドレスを取得するためのモジュールです。 network_addressメソッドとip_partsメソッドの2つで構成されています。

ip_partsメソッド

ip_partsメソッドはIPアドレスオクテットごとの配列として返すメソッドです。

def ip_parts(ip)
  ip.split(".").map { |i| i.to_i }
end

ip.split(".")の部分でsplitメソッドを使って、IPアドレスの文字列を引数に"."を区切り文字として配列を返している。
http://ref.xaio.jp/ruby/classes/string/split

~irbを使用した出力例~
irb(main):001:0> ip = "192.168.1.1"
=> "192.168.1.1"
irb(main):002:0> p ip.split(".")
["192", "168", "1", "1"]
=> ["192", "168", "1", "1"]

続いてmapメソッドを使い、文字列の配列を数値の配列に変換します。 mapメソッドは配列の各要素(item)を取り出しblockを実行し、blockの戻り値を集めた配列を 作成して返します。
http://ref.xaio.jp/ruby/classes/array/map

array.map{|item|block}
~irbを使用した出力例~
irb(main):003:0> p ip.split(".").map{|i|i.to_i}
[192, 168, 1, 1]
=> [192, 168, 1, 1]

blockで使用しているto_iは文字列を10進数の整数に変換するメソッドです。
http://ref.xaio.jp/ruby/classes/string/to_i

network_addressメソッド

続いてnetwork_addressメソッドです。

def network_address(ip, subnet)
  ip      = ip_parts(ip)
  netmask = ip_parts(subnet)
  
  ip.map { |part| part & netmask.shift }.join(".")
end

ipとsubnetの2つの引数をさきほど定義したip_partsメソッドでIPアドレス(もしくはサブネットマスク) の文字列をオクテットごとの数字の配列に変換しています。 配列ipをip_partsメソッドでも利用したmapメソッドで配列の要素ごとにある処理をしていきます。ここでしている処理とはまず配列netmaskをshiftメソッドで配列の要素をひとつひとつ取り出していきます。
http://ref.xaio.jp/ruby/classes/array/shift

~irbを使用した出力例~
irb(main):002:0> netmask = [255,255,255,0]
=> [255, 255, 255, 0]
irb(main):003:0> netmask.shift
=> 255
irb(main):004:0> netmask
=> [255, 255, 0]
irb(main):005:0> netmask.shift
=> 255
irb(main):006:0> netmask
=> [255, 0]

続いて取り出してきた要素を配列ipとビット演算をします。
http://www9.plala.or.jp/sgwr-t/c/sec14.html

~irbを使用した出力例~
irb(main):001:0> 192 & 255
=> 192
irb(main):002:0> 168 & 255
=> 168
irb(main):003:0> 1 & 255
=> 1
irb(main):004:0> 1 & 0
=> 0

irb(main):001:0> ip = [192,168,1,1]
=> [192, 168, 1, 1]
irb(main):002:0> netmask = [255,255,255,0]
=> [255, 255, 255, 0]
irb(main):003:0> ip.map{|part|part & netmask.shift}
=> [192, 168, 1, 0]

最後に得られた配列をjoinメソッドを用いて結合します。
http://ref.xaio.jp/ruby/classes/array/join

~irbを使用した出力例~
irb(main):008:0> [192,168,1,0].join(".")
=> "192.168.1.0"

これでIPアドレスとネットマスクからネットワークアドレスが取得できました。