Skip to content
Snippets Groups Projects
Commit b8cba609 authored by Thomas Bellman's avatar Thomas Bellman
Browse files

resolve_ipnets(): Handle order of address families better.

The old implementation of the 'stable' flag to the resolve_ipnets()
function just sorted the addresses for each host based on their
textual representation, without regard for which address family they
belonged to.  That meant that 192.0.2.17 would always come before
2001:db8:17:23::99, which would always come before 203.0.113.23.  That
is probably not the best behaviour, since you sometimes want to prefer
one address family over the other.

We improve on this by coalescing and grouping addresses by address
family, and only sort the addresses within each family.  This may
still break the Default Address Selection order (RFC 3484/6724)
implemented by the getaddrinfo(3) function, but it is at least better
than before.

We now also obey the order that the 'ipv4' and 'ipv6' flags are given
in; if 'ipv6' is specified before 'ipv4', IPv6 addresses will be
returned before IPv4 addresses, and vice versa.  Also, if no address
family is specified, we now pass AF_UNSPEC to getaddrinfo() instead of
querying for IPv4 addresses and IPv6 addresses separately in whatever
order they were stored in the RESOLVE_IPNETS__IPFAMILIES hash.  Thus,
we will then obey the ordering returned by getaddrinfo() a little bit
better.  (But remember that resolve_ipnets() is evaluated on the
Puppet master, not on the node being configured, so the order may
still not be what is wanted.)

This was originally commit 4d5b4bfe3462 in nsc-puppet-utils.
parent 6fc6df40
No related branches found
No related tags found
No related merge requests found
...@@ -7,6 +7,7 @@ require 'ipaddr' ...@@ -7,6 +7,7 @@ require 'ipaddr'
module Puppet::Parser::Functions module Puppet::Parser::Functions
RESOLVE_IPNETS__IPFAMILIES = { RESOLVE_IPNETS__IPFAMILIES = {
'__ANY' => Socket::AF_UNSPEC, # Internal use
'ipv4' => Socket::AF_INET, 'ipv4' => Socket::AF_INET,
'ipv6' => Socket::AF_INET6, 'ipv6' => Socket::AF_INET6,
} }
...@@ -48,7 +49,12 @@ module Puppet::Parser::Functions ...@@ -48,7 +49,12 @@ module Puppet::Parser::Functions
end end
# Then do hostname lookups # Then do hostname lookups
ips = [] # Order between address families returned should be retained even
# when returning a stable order; thus collect per family and remember
# the order we found the families in. Then sort each family, and
# concatenate.
ips = { } # Mapping from IP family to list of addresses
found_families = [ ]
err = nil err = nil
wanted_families.each do |family| wanted_families.each do |family|
family = RESOLVE_IPNETS__IPFAMILIES[family] family = RESOLVE_IPNETS__IPFAMILIES[family]
...@@ -56,17 +62,27 @@ module Puppet::Parser::Functions ...@@ -56,17 +62,27 @@ module Puppet::Parser::Functions
# Ruby 1.9 has a 7th argument to avoid reverse lookups, but # Ruby 1.9 has a 7th argument to avoid reverse lookups, but
# we need to support 1.8 as well # we need to support 1.8 as well
ipinfo = Socket.getaddrinfo(hostname, nil, family) ipinfo = Socket.getaddrinfo(hostname, nil, family)
ips += ipinfo.collect {|info| info[3] } ipinfo.each do |info|
if ! found_families.include?(info[4])
ips[info[4]] = []
found_families << info[4]
end
ips[info[4]] << info[3]
end
rescue SocketError => err rescue SocketError => err
# Errors handled after loop # Errors handled after loop
end end
end end
if ips.length == 0 ipaddrs = [ ]
found_families.each do |family|
ips[family].sort! if do_partsort
ipaddrs += ips[family]
end
if ipaddrs.length == 0
# Note: this will only show the last error message # Note: this will only show the last error message
raise err.class.new(err.to_s + ', ' + addr) raise err.class.new(err.to_s + ', ' + addr)
end end
ips.sort! if do_partsort return ipaddrs.collect {|ip| ip + maskspec }
return ips.collect {|ip| ip + maskspec }
end end
module_function :resolve_ipnets__netspec module_function :resolve_ipnets__netspec
...@@ -97,10 +113,18 @@ module Puppet::Parser::Functions ...@@ -97,10 +113,18 @@ module Puppet::Parser::Functions
to the hostnames in the input will be preserved, so if the input to the hostnames in the input will be preserved, so if the input
is ["host-a", "host-b"], then the addresses for host-a will come is ["host-a", "host-b"], then the addresses for host-a will come
before those for host-b in the return value. before those for host-b in the return value.
NOTE: This breaks the address selection order (RFC 3484/6724)
that the getaddrinfo(3) function implements.
NOTE: The stable flag does not help aginst e.g. DNS servers
responding with a different set of addresses each time.
Both "ipv4" and "ipv6" may be specified at the same time, in which Both "ipv4" and "ipv6" may be specified at the same time, in which
case both kinds of addresses will be returned. The function will case both kinds of addresses will be returned, in the order the
only return an error if neither family resolves. flags were specifed. The function will only return an error if
neither family resolves.
If no address family is specified, then the order of the addresses
families will be as determined by the resolver.
A network specification is on the form A network specification is on the form
...@@ -162,7 +186,7 @@ module Puppet::Parser::Functions ...@@ -162,7 +186,7 @@ module Puppet::Parser::Functions
end end
end end
if ipfamilies.length == 0 if ipfamilies.length == 0
ipfamilies = RESOLVE_IPNETS__IPFAMILIES.keys ipfamilies = '__ANY'
end end
res = [] res = []
[netspecs].flatten.each do |netspec| [netspecs].flatten.each do |netspec|
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment