Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

provider/virtualbox: Refactor host-only network settings #7699

Merged
merged 3 commits into from
Nov 2, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 30 additions & 40 deletions plugins/providers/virtualbox/action/network.rb
Original file line number Diff line number Diff line change
Expand Up @@ -261,16 +261,26 @@ def hostonly_config(options)

begin
ip = IPAddr.new(options[:ip])
rescue IPAddr::InvalidAddressError => e
raise Vagrant::Errors::NetworkAddressInvalid, :ip => options[:ip], :error_msg => e.message
end
if ip.ipv4?
options[:netmask] ||= "255.255.255.0"
elsif ip.ipv6?
options[:netmask] ||= 64

if ip.ipv4?
options[:netmask] ||= "255.255.255.0"
# Append a 6 to the end of the type
options[:type] = "#{options[:type]}6".to_sym
else
raise IPAddr::AddressFamilyError, 'unknown address family'
end

# Calculate our network address for the given IP/netmask
netaddr = network_address(options[:ip], options[:netmask])
netaddr = IPAddr.new("#{options[:ip]}/#{options[:netmask]}")
rescue IPAddr::Error => e
raise Vagrant::Errors::NetworkAddressInvalid,
address: options[:ip], mask: options[:netmask],
error: e.message
end

if ip.ipv4?
# Verify that a host-only network subnet would not collide
# with a bridged networking interface.
#
Expand All @@ -287,44 +297,24 @@ def hostonly_config(options)
interface_name: interface[:name]
end
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(".")
elsif ip.ipv6?
# Default subnet prefix length
options[:netmask] ||= 64

# Set adapter IP to <prefix>::1
options[:adapter_ip] ||= (ip.mask(options[:netmask].to_i) | 1).to_s

# Append a 6 to the end of the type
options[:type] = "#{options[:type]}6".to_sym
else
raise "BUG: Unknown IP type: #{ip.inspect}"
end

# Calculate the adapter IP which is the network address with
# the final bit + 1. Usually it is "x.x.x.1" for IPv4 and
# "<prefix>::1" for IPv6
options[:adapter_ip] ||= (netaddr | 1).to_s

dhcp_options = {}
if options[:type] == :dhcp
# Calculate the DHCP server IP, which is the network address
# with the final octet + 2. So "172.28.0.0" turns into "172.28.0.2"
dhcp_ip = ip_parts.dup
dhcp_ip[3] += 2
dhcp_options[:dhcp_ip] = options[:dhcp_ip] || dhcp_ip.join(".")

# Calculate the lower and upper bound for the DHCP server
dhcp_lower = ip_parts.dup
dhcp_lower[3] += 3
dhcp_options[:dhcp_lower] = options[:dhcp_lower] || dhcp_lower.join(".")

dhcp_upper = ip_parts.dup
dhcp_upper[3] = 254
dhcp_options[:dhcp_upper] = options[:dhcp_upper] || dhcp_upper.join(".")
# Calculate the DHCP server IP and lower & upper bound
# Example: for "192.168.22.64/26" network range those are:
# dhcp_ip: "192.168.22.66",
# dhcp_lower: "192.168.22.67"
# dhcp_upper: "192.168.22.126"
ip_range = netaddr.to_range
dhcp_options[:dhcp_ip] = options[:dhcp_ip] || (ip_range.first | 2).to_s
dhcp_options[:dhcp_lower] = options[:dhcp_lower] || (ip_range.first | 3).to_s
dhcp_options[:dhcp_upper] = options[:dhcp_upper] || (ip_range.last(2).first).to_s
end

return {
Expand Down
9 changes: 6 additions & 3 deletions templates/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -972,10 +972,13 @@ en:
network_type_not_supported: |-
The %{type} network type is not supported for this box or guest.
network_address_invalid: |-
The IP address '%{ip}' is not valid. Please review the error message
below to help resolve the issue:
Network settings specified in your Vagrantfile define an invalid
IP address. Please review the error message below and update your
Vagrantfile network settings:

%{error_msg}
Address: %{address}
Netmask: %{mask}
Error: %{error}
network_manager_not_installed: |-
Vagrant was instructed to configure the %{device} network device to
be managed by NetworkManager. However, the configured guest VM does
Expand Down
18 changes: 18 additions & 0 deletions test/unit/plugins/providers/virtualbox/action/network_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,24 @@
end
end

context 'with invalid settings' do
[
{ ip: 'foo'},
{ ip: '1.2.3'},
{ ip: 'dead::beef::'},
{ ip: '172.28.128.3', netmask: 64},
{ ip: '172.28.128.3', netmask: 'ffff:ffff::'},
{ ip: 'dead:beef::', netmask: 'foo:bar::'},
{ ip: 'dead:beef::', netmask: '255.255.255.0'}
].each do |args|
it 'raises an exception' do
machine.config.vm.network 'private_network', **args
expect { subject.call(env) }.
to raise_error(Vagrant::Errors::NetworkAddressInvalid)
end
end
end

describe "#hostonly_find_matching_network" do
let(:ip){ "192.168.55.2" }
let(:config){ {ip: ip, netmask: "255.255.255.0"} }
Expand Down