Only consider the VM interfaces in the IPv6 fixup.
Vagrant should only consider the host-only interfaces used by the virtual machine in the IPv6 fixup code. There may be other interfaces present on the system with IPv6 addresses that for various reasons would fail the routing check (for example, an interface with no machines attached). The patch changes the behavior to not scan all of the host-only interfaces and adds a unit test for the behavior (that the correct IP is validated). Lastly, there is a small fix here that may not be an issue for most people where the IPv6 prefix was asummed to be a multiple of 16 for the purposes of constructing the UDP probe datagram. This assumption has been removed. Fixes #6586
This commit is contained in:
parent
a8d0c225c6
commit
bcf61d001b
|
@ -41,21 +41,22 @@ module VagrantPlugins
|
|||
# If we have no IPv6, forget it
|
||||
return if !has_v6
|
||||
|
||||
# We do, so fix them if we must
|
||||
env[:machine].provider.driver.read_host_only_interfaces.each do |interface|
|
||||
# Ignore interfaces without an IPv6 address
|
||||
next if interface[:ipv6] == ""
|
||||
|
||||
# Make the test IP. This is just the highest value IP
|
||||
ip = IPAddr.new(interface[:ipv6])
|
||||
ip |= IPAddr.new(":#{":FFFF" * (interface[:ipv6_prefix].to_i / 16)}")
|
||||
|
||||
ifaces = env[:machine].provider.driver.read_network_interfaces
|
||||
ifaces.select! { |id, iface| iface[:type] == :hostonly }
|
||||
iface_names = ifaces.values.map { |iface| iface[:hostonly] }
|
||||
networks = env[:machine].provider.driver.read_host_only_interfaces
|
||||
networks.select! { |network| iface_names.include?(network[:name]) }
|
||||
networks.each do |network|
|
||||
next if network[:ipv6] == ""
|
||||
next if network[:status] != 'Up'
|
||||
ip = IPAddr.new(network[:ipv6]) |
|
||||
('1' * (128 - network[:ipv6_prefix].to_i)).to_i(2)
|
||||
@logger.info("testing IPv6: #{ip}")
|
||||
begin
|
||||
UDPSocket.new(Socket::AF_INET6).connect(ip.to_s, 80)
|
||||
rescue Errno::EHOSTUNREACH
|
||||
@logger.info("IPv6 host unreachable. Fixing: #{ip}")
|
||||
env[:machine].provider.driver.reconfig_host_only(interface)
|
||||
env[:machine].provider.driver.reconfig_host_only(network)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
require_relative "../base"
|
||||
require 'socket'
|
||||
|
||||
describe VagrantPlugins::ProviderVirtualBox::Action::NetworkFixIPv6 do
|
||||
include_context "unit"
|
||||
|
@ -11,11 +12,14 @@ describe VagrantPlugins::ProviderVirtualBox::Action::NetworkFixIPv6 do
|
|||
end
|
||||
|
||||
let(:machine) do
|
||||
iso_env.machine(iso_env.machine_names[0], :dummy)
|
||||
iso_env.machine(iso_env.machine_names[0], :dummy).tap do |m|
|
||||
m.provider.stub(driver: driver)
|
||||
end
|
||||
end
|
||||
|
||||
let(:env) {{ machine: machine }}
|
||||
let(:app) { lambda { |*args| }}
|
||||
let(:driver) { double("driver") }
|
||||
|
||||
subject { described_class.new(app, env) }
|
||||
|
||||
|
@ -30,4 +34,132 @@ describe VagrantPlugins::ProviderVirtualBox::Action::NetworkFixIPv6 do
|
|||
.and_return(private_network: { ip: "" })
|
||||
expect { subject.call(env) }.to_not raise_error
|
||||
end
|
||||
|
||||
context "with IPv6 interfaces" do
|
||||
let(:socket) { double("socket") }
|
||||
|
||||
before do
|
||||
# This address is only used to trigger the fixup code. It doesn't matter
|
||||
# what it is.
|
||||
allow(machine.config.vm).to receive(:networks)
|
||||
.and_return(private_network: { ip: 'fe:80::' })
|
||||
allow(UDPSocket).to receive(:new).with(Socket::AF_INET6)
|
||||
.and_return(socket)
|
||||
socket.stub(:connect)
|
||||
end
|
||||
|
||||
it "only checks the interfaces associated with the VM" do
|
||||
all_networks = [{name: "vboxnet0",
|
||||
ipv6: "dead:beef::",
|
||||
ipv6_prefix: 64,
|
||||
status: 'Up'
|
||||
},
|
||||
{name: "vboxnet1",
|
||||
ipv6: "badd:badd::",
|
||||
ipv6_prefix: 64,
|
||||
status: 'Up'
|
||||
}
|
||||
]
|
||||
ifaces = { 1 => {type: :hostonly, hostonly: "vboxnet0"}
|
||||
}
|
||||
allow(machine.provider.driver).to receive(:read_network_interfaces)
|
||||
.and_return(ifaces)
|
||||
allow(machine.provider.driver).to receive(:read_host_only_interfaces)
|
||||
.and_return(all_networks)
|
||||
subject.call(env)
|
||||
expect(socket).to have_received(:connect)
|
||||
.with(all_networks[0][:ipv6] + (['ffff']*4).join(':'), 80)
|
||||
end
|
||||
|
||||
it "correctly uses the netmask to figure out the probe address" do
|
||||
all_networks = [{name: "vboxnet0",
|
||||
ipv6: "dead:beef::",
|
||||
ipv6_prefix: 113,
|
||||
status: 'Up'
|
||||
}
|
||||
]
|
||||
ifaces = { 1 => {type: :hostonly, hostonly: "vboxnet0"}
|
||||
}
|
||||
allow(machine.provider.driver).to receive(:read_network_interfaces)
|
||||
.and_return(ifaces)
|
||||
allow(machine.provider.driver).to receive(:read_host_only_interfaces)
|
||||
.and_return(all_networks)
|
||||
subject.call(env)
|
||||
expect(socket).to have_received(:connect)
|
||||
.with(all_networks[0][:ipv6] + '7fff', 80)
|
||||
end
|
||||
|
||||
it "should ignore interfaces that are down" do
|
||||
all_networks = [{name: "vboxnet0",
|
||||
ipv6: "dead:beef::",
|
||||
ipv6_prefix: 64,
|
||||
status: 'Down'
|
||||
}
|
||||
]
|
||||
ifaces = { 1 => {type: :hostonly, hostonly: "vboxnet0"}
|
||||
}
|
||||
allow(machine.provider.driver).to receive(:read_network_interfaces)
|
||||
.and_return(ifaces)
|
||||
allow(machine.provider.driver).to receive(:read_host_only_interfaces)
|
||||
.and_return(all_networks)
|
||||
subject.call(env)
|
||||
expect(socket).to_not have_received(:connect)
|
||||
end
|
||||
|
||||
it "should ignore interfaces without an IPv6 address" do
|
||||
all_networks = [{name: "vboxnet0",
|
||||
ipv6: "",
|
||||
ipv6_prefix: 0,
|
||||
status: 'Up'
|
||||
}
|
||||
]
|
||||
ifaces = { 1 => {type: :hostonly, hostonly: "vboxnet0"}
|
||||
}
|
||||
allow(machine.provider.driver).to receive(:read_network_interfaces)
|
||||
.and_return(ifaces)
|
||||
allow(machine.provider.driver).to receive(:read_host_only_interfaces)
|
||||
.and_return(all_networks)
|
||||
subject.call(env)
|
||||
expect(socket).to_not have_received(:connect)
|
||||
end
|
||||
|
||||
it "should ignore nat interfaces" do
|
||||
all_networks = [{name: "vboxnet0",
|
||||
ipv6: "",
|
||||
ipv6_prefix: 0,
|
||||
status: 'Up'
|
||||
}
|
||||
]
|
||||
ifaces = { 1 => {type: :nat}
|
||||
}
|
||||
allow(machine.provider.driver).to receive(:read_network_interfaces)
|
||||
.and_return(ifaces)
|
||||
allow(machine.provider.driver).to receive(:read_host_only_interfaces)
|
||||
.and_return(all_networks)
|
||||
subject.call(env)
|
||||
expect(socket).to_not have_received(:connect)
|
||||
end
|
||||
|
||||
it "should reconfigure an interface if unreachable" do
|
||||
all_networks = [{name: "vboxnet0",
|
||||
ipv6: "dead:beef::",
|
||||
ipv6_prefix: 64,
|
||||
status: 'Up'
|
||||
}
|
||||
]
|
||||
ifaces = { 1 => {type: :hostonly, hostonly: "vboxnet0"}
|
||||
}
|
||||
allow(machine.provider.driver).to receive(:read_network_interfaces)
|
||||
.and_return(ifaces)
|
||||
allow(machine.provider.driver).to receive(:read_host_only_interfaces)
|
||||
.and_return(all_networks)
|
||||
allow(socket).to receive(:connect)
|
||||
.with(all_networks[0][:ipv6] + (['ffff']*4).join(':'), 80)
|
||||
.and_raise Errno::EHOSTUNREACH
|
||||
allow(machine.provider.driver).to receive(:reconfig_host_only)
|
||||
subject.call(env)
|
||||
expect(machine.provider.driver).to have_received(:reconfig_host_only)
|
||||
.with(all_networks[0])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue