diff --git a/lib/vagrant/util/network_ip.rb b/lib/vagrant/util/network_ip.rb index fca98f8b4..5f22f69d4 100644 --- a/lib/vagrant/util/network_ip.rb +++ b/lib/vagrant/util/network_ip.rb @@ -1,10 +1,21 @@ +require "ipaddr" + module Vagrant module Util module NetworkIP # Returns the network address of the given IP and subnet. # + # If the IP address is an IPv6 address, subnet should be a prefix + # length such as "64". + # # @return [String] def network_address(ip, subnet) + # If this is an IPv6 address, then just mask it + if subnet.to_s =~ /^\d+$/ + ip = IPAddr.new(ip) + return ip.mask(subnet.to_i).to_s + end + ip = ip_parts(ip) netmask = ip_parts(subnet) diff --git a/plugins/providers/virtualbox/action/network.rb b/plugins/providers/virtualbox/action/network.rb index 3697cdfef..881fcdf51 100644 --- a/plugins/providers/virtualbox/action/network.rb +++ b/plugins/providers/virtualbox/action/network.rb @@ -1,3 +1,4 @@ +require "ipaddr" require "set" require "log4r" @@ -248,7 +249,6 @@ module VagrantPlugins auto_config: true, mac: nil, nic_type: nil, - netmask: "255.255.255.0", type: :static }.merge(options) @@ -258,31 +258,45 @@ module VagrantPlugins # Default IP is in the 20-bit private network block for DHCP based networks options[:ip] = "172.28.128.1" if options[:type] == :dhcp && !options[:ip] - # Calculate our network address for the given IP/netmask - netaddr = network_address(options[:ip], options[:netmask]) + ip = IPAddr.new(options[:ip]) + if !ip.ipv6? + options[:netmask] ||= "255.255.255.0" - # Verify that a host-only network subnet would not collide - # with a bridged networking interface. - # - # If the subnets overlap in any way then the host only network - # will not work because the routing tables will force the - # traffic onto the real interface rather than the VirtualBox - # interface. - @env[:machine].provider.driver.read_bridged_interfaces.each do |interface| - that_netaddr = network_address(interface[:ip], interface[:netmask]) - raise Vagrant::Errors::NetworkCollision if \ - netaddr == that_netaddr && interface[:status] != "Down" + # Calculate our network address for the given IP/netmask + netaddr = network_address(options[:ip], options[:netmask]) + + # Verify that a host-only network subnet would not collide + # with a bridged networking interface. + # + # If the subnets overlap in any way then the host only network + # will not work because the routing tables will force the + # traffic onto the real interface rather than the VirtualBox + # interface. + @env[:machine].provider.driver.read_bridged_interfaces.each do |interface| + that_netaddr = network_address(interface[:ip], interface[:netmask]) + raise Vagrant::Errors::NetworkCollision if \ + netaddr == that_netaddr && interface[:status] != "Down" + end + + # Split the IP address into its components + ip_parts = netaddr.split(".").map { |i| i.to_i } + + # Calculate the adapter IP, which we assume is the IP ".1" at + # the end usually. + adapter_ip = ip_parts.dup + adapter_ip[3] += 1 + options[:adapter_ip] ||= adapter_ip.join(".") + else + # Default subnet prefix length + options[:netmask] ||= 64 + + # IPv6 we just mask the address and use that as the adapter + options[:adapter_ip] ||= ip.mask(options[:netmask].to_i).to_s + + # Append a 6 to the end of the type + options[:type] = "#{options[:type]}6".to_sym end - # Split the IP address into its components - ip_parts = netaddr.split(".").map { |i| i.to_i } - - # Calculate the adapter IP, which we assume is the IP ".1" at - # the end usually. - adapter_ip = ip_parts.dup - adapter_ip[3] += 1 - options[:adapter_ip] ||= adapter_ip.join(".") - dhcp_options = {} if options[:type] == :dhcp # Calculate the DHCP server IP, which is the network address @@ -456,8 +470,16 @@ module VagrantPlugins @env[:machine].provider.driver.read_host_only_interfaces.each do |interface| return interface if config[:name] && config[:name] == interface[:name] - return interface if this_netaddr == \ - network_address(interface[:ip], interface[:netmask]) + + if interface[:ip] != "" + return interface if this_netaddr == \ + network_address(interface[:ip], interface[:netmask]) + end + + if interface[:ipv6] != "" + return interface if this_netaddr == \ + network_address(interface[:ipv6], interface[:ipv6_prefix]) + end end nil diff --git a/plugins/providers/virtualbox/driver/version_4_3.rb b/plugins/providers/virtualbox/driver/version_4_3.rb index b421e7911..ed4b7aa9e 100644 --- a/plugins/providers/virtualbox/driver/version_4_3.rb +++ b/plugins/providers/virtualbox/driver/version_4_3.rb @@ -1,3 +1,4 @@ +require 'ipaddr' require 'log4r' require "vagrant/util/platform" @@ -46,12 +47,21 @@ module VagrantPlugins def create_host_only_network(options) # Create the interface execute("hostonlyif", "create") =~ /^Interface '(.+?)' was successfully created$/ - name = $1.to_s + name = $1.to_s - # Configure it - execute("hostonlyif", "ipconfig", name, - "--ip", options[:adapter_ip], - "--netmask", options[:netmask]) + # Get the IP so we can determine v4 vs v6 + ip = IPAddr.new(options[:adapter_ip]) + + # Configure + if ip.ipv4? + execute("hostonlyif", "ipconfig", name, + "--ip", options[:adapter_ip], + "--netmask", options[:netmask]) + elsif ip.ipv6? + execute("hostonlyif", "ipconfig", name, + "--ipv6", options[:adapter_ip], + "--netmasklengthv6", options[:netmask]) + end # Return the details return { @@ -366,6 +376,10 @@ module VagrantPlugins info[:ip] = $1.to_s elsif line =~ /^NetworkMask:\s+(.+?)$/ info[:netmask] = $1.to_s + elsif line =~ /^IPV6Address:\s+(.+?)$/ + info[:ipv6] = $1.to_s.strip + elsif line =~ /^IPV6NetworkMaskPrefixLength:\s+(.+?)$/ + info[:ipv6_prefix] = $1.to_s.strip elsif line =~ /^Status:\s+(.+?)$/ info[:status] = $1.to_s end diff --git a/plugins/providers/virtualbox/driver/version_5_0.rb b/plugins/providers/virtualbox/driver/version_5_0.rb index 102d838bc..c8d659e11 100644 --- a/plugins/providers/virtualbox/driver/version_5_0.rb +++ b/plugins/providers/virtualbox/driver/version_5_0.rb @@ -366,6 +366,10 @@ module VagrantPlugins info[:ip] = $1.to_s elsif line =~ /^NetworkMask:\s+(.+?)$/ info[:netmask] = $1.to_s + elsif line =~ /^IPV6Address:\s+(.+?)$/ + info[:ipv6] = $1.to_s + elsif line =~ /^IPV6NetworkMaskPrefixLength:\s+(.+?)$/ + info[:ipv6_prefix] = $1.to_s elsif line =~ /^Status:\s+(.+?)$/ info[:status] = $1.to_s end diff --git a/templates/guests/debian/network_static6.erb b/templates/guests/debian/network_static6.erb new file mode 100644 index 000000000..7b9e8a694 --- /dev/null +++ b/templates/guests/debian/network_static6.erb @@ -0,0 +1,10 @@ +#VAGRANT-BEGIN +# The contents below are automatically generated by Vagrant. Do not modify. +auto eth<%= options[:interface] %> +iface eth<%= options[:interface] %> inet6 static + address <%= options[:ip] %> + netmask <%= options[:netmask] %> +<% if options[:gateway] %> + gateway <%= options[:gateway] %> +<% end %> +#VAGRANT-END diff --git a/test/unit/vagrant/util/network_ip_test.rb b/test/unit/vagrant/util/network_ip_test.rb index a4ff8944c..9d0d09b12 100644 --- a/test/unit/vagrant/util/network_ip_test.rb +++ b/test/unit/vagrant/util/network_ip_test.rb @@ -13,5 +13,17 @@ describe Vagrant::Util::NetworkIP do it "calculates it properly" do expect(klass.network_address("192.168.2.234", "255.255.255.0")).to eq("192.168.2.0") end + + it "calculates it properly with integer submask" do + expect(klass.network_address("192.168.2.234", "24")).to eq("192.168.2.0") + end + + it "calculates it properly for IPv6" do + expect(klass.network_address("fde4:8dba:82e1::c4", "64")).to eq("fde4:8dba:82e1::") + end + + it "calculates it properly for IPv6" do + expect(klass.network_address("fde4:8dba:82e1::c4", 64)).to eq("fde4:8dba:82e1::") + end end end