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...") @logger.info("Detecting any forwarded port collisions...")
# Get the extra ports we consider in use # 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 # Get the remap
remap = env[:port_collision_remap] || {} remap = env[:port_collision_remap] || {}
@ -81,7 +81,7 @@ module Vagrant
# Determine a list of usable ports for repair # Determine a list of usable ports for repair
usable_ports = Set.new(env[:machine].config.vm.usable_port_range) 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 # Pass one, remove all defined host ports from usable ports
with_forwarded_ports(env) do |options| with_forwarded_ports(env) do |options|
@ -92,6 +92,7 @@ module Vagrant
with_forwarded_ports(env) do |options| with_forwarded_ports(env) do |options|
guest_port = options[:guest] guest_port = options[:guest]
host_port = options[:host] host_port = options[:host]
host_ip = options[:host_ip]
if options[:protocol] && options[:protocol] != "tcp" if options[:protocol] && options[:protocol] != "tcp"
@logger.debug("Skipping #{host_port} because UDP protocol.") @logger.debug("Skipping #{host_port} because UDP protocol.")
@ -105,9 +106,9 @@ module Vagrant
end end
# If the port is open (listening for TCP connections) # If the port is open (listening for TCP connections)
in_use = extra_in_use.include?(host_port) || in_use = is_forwarded_already(extra_in_use, host_port, host_ip) ||
port_checker[host_port] || port_checker[host_ip, host_port] ||
lease_check(host_port) lease_check(host_ip, host_port)
if in_use if in_use
if !repair || !options[:auto_correct] if !repair || !options[:auto_correct]
raise Errors::ForwardPortCollision, raise Errors::ForwardPortCollision,
@ -124,9 +125,9 @@ module Vagrant
usable_ports.delete(repaired_port) usable_ports.delete(repaired_port)
# If the port is in use, then we can't use this either... # If the port is in use, then we can't use this either...
in_use = extra_in_use.include?(repaired_port) || in_use = is_forwarded_already(extra_in_use, repaired_port, host_ip) ||
port_checker[repaired_port] || port_checker[host_ip, repaired_port] ||
lease_check(repaired_port) lease_check(host_ip, repaired_port)
if in_use if in_use
@logger.info("Repaired port also in use: #{repaired_port}. Trying another...") @logger.info("Repaired port also in use: #{repaired_port}. Trying another...")
next next
@ -158,13 +159,19 @@ module Vagrant
end end
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 # Check if this port is "leased". We use a leasing system of
# about 60 seconds to avoid any forwarded port collisions in # about 60 seconds to avoid any forwarded port collisions in
# a highly parallelized environment. # a highly parallelized environment.
leasedir = @machine.env.data_dir.join("fp-leases") leasedir = @machine.env.data_dir.join("fp-leases")
leasedir.mkpath 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 invalid = false
oldest = Time.now.to_i - 60 oldest = Time.now.to_i - 60
leasedir.children.each do |child| leasedir.children.each do |child|
@ -173,7 +180,7 @@ module Vagrant
child.delete child.delete
end end
if child.basename.to_s == port.to_s if child.basename.to_s == lease_file_name
invalid = true invalid = true
end end
end end
@ -182,13 +189,13 @@ module Vagrant
return true if invalid return true if invalid
# Otherwise, create the lease # 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.binmode
f.write(Time.now.to_i.to_s + "\n") f.write(Time.now.to_i.to_s + "\n")
end end
# Add to the leased array so we unlease it right away # Add to the leased array so we unlease it right away
@leased << port.to_s @leased << lease_file_name
# Things look good to us! # Things look good to us!
false false
@ -203,8 +210,32 @@ module Vagrant
end end
end end
def port_check(port) # This functions checks to see if the current instance's hostport and
is_port_open?("127.0.0.1", port) # 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 end
def with_forwarded_ports(env) def with_forwarded_ports(env)

View File

@ -244,9 +244,11 @@ module VagrantPlugins
default_id = nil default_id = nil
if type == :forwarded_port if type == :forwarded_port
# For forwarded ports, set the default ID to the # For forwarded ports, set the default ID to be the
# host port so that host ports overwrite each other. # concat of host_ip, proto and host_port. This would ensure Vagrant
default_id = "#{options[:protocol]}#{options[:host]}" # 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 end
options[:id] = default_id || SecureRandom.uuid options[:id] = default_id || SecureRandom.uuid
@ -676,7 +678,7 @@ module VagrantPlugins
end end
if options[:host] if options[:host]
key = "#{options[:protocol]}#{options[:host]}" key = "#{options[:host_ip]}#{options[:protocol]}#{options[:host]}"
if fp_used.include?(key) if fp_used.include?(key)
errors << I18n.t("vagrant.config.vm.network_fp_host_not_unique", errors << I18n.t("vagrant.config.vm.network_fp_host_not_unique",
host: options[:host].to_s, host: options[:host].to_s,

View File

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

View File

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