Added support for Port forwarding in an IP aliased environment. The change
makes the following forwarding rule(s) possible.

Ex: eth0 is ip aliased to have a range of IP addresses 10.20.30.0/24.

In the Vagrant file, we can now have an entry like the following and
it will just work! Note the host port 8081 is the same for both .1 and .2.

  Vagrant.configure("2") do |config|
    config.vm.network "forwarded_port", guest: 80, host: 8080
    config.vm.network "forwarded_port", guest: 81, host: 8081, host_ip: 10.20.30.1
    config.vm.network "forwarded_port", guest: 82, host: 8081, host_ip: 10.20.30.2
  end
This commit is contained in:
Pravinchandar Raajendiran 2016-02-14 22:16:24 +11:00
parent b99ed21506
commit af9d0df635
4 changed files with 63 additions and 25 deletions

View File

@ -62,7 +62,7 @@ module Vagrant
@logger.info("Detecting any forwarded port collisions...")
# Get the extra ports we consider in use
extra_in_use = env[:port_collision_extra_in_use] || []
extra_in_use = env[:port_collision_extra_in_use] || {}
# Get the remap
remap = env[:port_collision_remap] || {}
@ -81,7 +81,7 @@ module Vagrant
# Determine a list of usable ports for repair
usable_ports = Set.new(env[:machine].config.vm.usable_port_range)
usable_ports.subtract(extra_in_use)
usable_ports.subtract(extra_in_use.keys)
# Pass one, remove all defined host ports from usable ports
with_forwarded_ports(env) do |options|
@ -92,6 +92,7 @@ module Vagrant
with_forwarded_ports(env) do |options|
guest_port = options[:guest]
host_port = options[:host]
host_ip = options[:host_ip]
if options[:protocol] && options[:protocol] != "tcp"
@logger.debug("Skipping #{host_port} because UDP protocol.")
@ -105,9 +106,9 @@ module Vagrant
end
# If the port is open (listening for TCP connections)
in_use = extra_in_use.include?(host_port) ||
port_checker[host_port] ||
lease_check(host_port)
in_use = is_forwarded_already(extra_in_use, host_port, host_ip) ||
port_checker[host_ip, host_port] ||
lease_check(host_ip, host_port)
if in_use
if !repair || !options[:auto_correct]
raise Errors::ForwardPortCollision,
@ -124,9 +125,9 @@ module Vagrant
usable_ports.delete(repaired_port)
# If the port is in use, then we can't use this either...
in_use = extra_in_use.include?(repaired_port) ||
port_checker[repaired_port] ||
lease_check(repaired_port)
in_use = is_forwarded_already(extra_in_use, repaired_port, host_ip) ||
port_checker[host_ip, repaired_port] ||
lease_check(host_ip, repaired_port)
if in_use
@logger.info("Repaired port also in use: #{repaired_port}. Trying another...")
next
@ -158,13 +159,19 @@ module Vagrant
end
end
def lease_check(port)
def lease_check(host_ip=nil, host_port)
# Check if this port is "leased". We use a leasing system of
# about 60 seconds to avoid any forwarded port collisions in
# a highly parallelized environment.
leasedir = @machine.env.data_dir.join("fp-leases")
leasedir.mkpath
if host_ip.nil?
lease_file_name = host_port.to_s
else
lease_file_name = "#{host_ip.gsub('.','_')}_#{host_port.to_s}"
end
invalid = false
oldest = Time.now.to_i - 60
leasedir.children.each do |child|
@ -173,7 +180,7 @@ module Vagrant
child.delete
end
if child.basename.to_s == port.to_s
if child.basename.to_s == lease_file_name
invalid = true
end
end
@ -182,13 +189,13 @@ module Vagrant
return true if invalid
# Otherwise, create the lease
leasedir.join(port.to_s).open("w+") do |f|
leasedir.join(lease_file_name).open("w+") do |f|
f.binmode
f.write(Time.now.to_i.to_s + "\n")
end
# Add to the leased array so we unlease it right away
@leased << port.to_s
@leased << lease_file_name
# Things look good to us!
false
@ -203,8 +210,32 @@ module Vagrant
end
end
def port_check(port)
is_port_open?("127.0.0.1", port)
# This functions checks to see if the current instance's hostport and
# hostip for forwarding is in use by the virtual machines created
# previously.
def is_forwarded_already(extra_in_use, hostport, hostip)
hostip = '*' if hostip.nil? || hostip.empty?
# ret. false if none of the VMs we spun up had this port forwarded.
return false if not extra_in_use.has_key?(hostport)
# ret. true if the user has requested to bind on all interfaces but
# we already have a rule in one the VMs we spun up.
if hostip == '*'
if extra_in_use.fetch(hostport).size != 0
return true
else
return false
end
end
return extra_in_use.fetch(hostport).include?(hostip)
end
def port_check(host_ip, host_port)
# If the user hasn't specified a host_ip, his/her intention is taken to be
# to listen on all interfaces.
return is_port_open?("0.0.0.0", host_port) if host_ip.nil?
return is_port_open?(host_ip, host_port)
end
def with_forwarded_ports(env)

View File

@ -244,9 +244,11 @@ module VagrantPlugins
default_id = nil
if type == :forwarded_port
# For forwarded ports, set the default ID to the
# host port so that host ports overwrite each other.
default_id = "#{options[:protocol]}#{options[:host]}"
# For forwarded ports, set the default ID to be the
# concat of host_ip, proto and host_port. This would ensure Vagrant
# caters for port forwarding in an IP aliased environment where
# different host IP addresses are to be listened on the same port.
default_id = "#{options[:host_ip]}#{options[:protocol]}#{options[:host]}"
end
options[:id] = default_id || SecureRandom.uuid
@ -676,7 +678,7 @@ module VagrantPlugins
end
if options[:host]
key = "#{options[:protocol]}#{options[:host]}"
key = "#{options[:host_ip]}#{options[:protocol]}#{options[:host]}"
if fp_used.include?(key)
errors << I18n.t("vagrant.config.vm.network_fp_host_not_unique",
host: options[:host].to_s,

View File

@ -366,8 +366,10 @@ module VagrantPlugins
end
# Parse out the forwarded port information
if line =~ /^Forwarding.+?="(.+?),.+?,.*?,(.+?),.*?,(.+?)"$/
result = [current_nic, $1.to_s, $2.to_i, $3.to_i]
# Forwarding(1)="172.22.8.201tcp32977,tcp,172.22.8.201,32977,,3777"
# Forwarding(2)="tcp32978,tcp,,32978,,3777"
if line =~ /^Forwarding.+?="(.+?),.+?,(.*?),(.+?),.*?,(.+?)"$/
result = [current_nic, $1.to_s, $3.to_i, $4.to_i, $2]
@logger.debug(" - #{result.inspect}")
results << result
end
@ -556,7 +558,7 @@ module VagrantPlugins
end
def read_used_ports
ports = []
used_ports = Hash.new{|hash, key| hash[key] = Set.new}
execute("list", "vms", retryable: true).split("\n").each do |line|
if line =~ /^".+?" \{(.+?)\}$/
uuid = $1.to_s
@ -564,13 +566,14 @@ module VagrantPlugins
# Ignore our own used ports
next if uuid == @uuid
read_forwarded_ports(uuid, true).each do |_, _, hostport, _|
ports << hostport
read_forwarded_ports(uuid, true).each do |_, _, hostport, _, hostip|
hostip = '*' if hostip.nil? || hostip.empty?
used_ports[hostport].add?(hostip)
end
end
end
ports
used_ports
end
def read_vms

View File

@ -15,6 +15,7 @@ module VagrantPlugins
if type == :forwarded_port
guest_port = options[:guest]
host_port = options[:host]
host_ip = options[:host_ip]
protocol = options[:protocol] || "tcp"
options = scoped_hash_override(options, :virtualbox)
id = options[:id]
@ -22,7 +23,8 @@ module VagrantPlugins
# If the forwarded port was marked as disabled, ignore.
next if options[:disabled]
mappings[host_port.to_s + protocol.to_s] =
key = "#{host_ip}#{protocol}#{host_port}"
mappings[key] =
Model::ForwardedPort.new(id, host_port, guest_port, options)
end
end