Skip to content
Permalink
Branch: master
Find file Copy path
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
333 lines (300 sloc) 14.1 KB
#include <internal/facts/resolvers/networking_resolver.hpp>
#include <facter/facts/fact.hpp>
#include <facter/facts/collection.hpp>
#include <facter/facts/map_value.hpp>
#include <facter/facts/array_value.hpp>
#include <facter/facts/scalar_value.hpp>
#include <leatherman/logging/logging.hpp>
#include <boost/format.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/asio/ip/address_v6.hpp>
#include <sstream>
using namespace std;
namespace facter { namespace facts { namespace resolvers {
networking_resolver::networking_resolver() :
resolver(
"networking",
{
fact::networking,
fact::hostname,
fact::ipaddress,
fact::ipaddress6,
fact::netmask,
fact::netmask6,
fact::network,
fact::network6,
fact::scope6,
fact::macaddress,
fact::interfaces,
fact::domain,
fact::fqdn,
fact::dhcp_servers,
},
{
string("^") + fact::ipaddress + "_",
string("^") + fact::ipaddress6 + "_",
string("^") + fact::mtu + "_",
string("^") + fact::netmask + "_",
string("^") + fact::netmask6 + "_",
string("^") + fact::network + "_",
string("^") + fact::network6 + "_",
string("^") + fact::scope6 + "_",
string("^") + fact::macaddress + "_",
})
{
}
void networking_resolver::resolve(collection& facts)
{
auto data = collect_data(facts);
// Some queries, such as /etc/resolv.conf, can return domains with a trailing dot (.).
// We want to strip the trailing dot, as it is not useful as a valid domain or fqdn.
boost::trim_right_if(data.domain, boost::is_any_of("."));
// If no FQDN, set it to the hostname + domain
if (!data.hostname.empty() && data.fqdn.empty()) {
data.fqdn = data.hostname + (data.domain.empty() ? "" : ".") + data.domain;
}
// If no primary interface was found, default to the first interface with a valid address
if (data.primary_interface.empty()) {
LOG_DEBUG("no primary interface found: using the first interface with an assigned address as the primary interface.");
auto primary = find_primary_interface(data.interfaces);
if (primary) {
data.primary_interface = primary->name;
}
}
auto networking = make_value<map_value>();
// Add the interface data
ostringstream interface_names;
auto dhcp_servers = make_value<map_value>(true);
auto interfaces = make_value<map_value>();
for (auto& interface : data.interfaces) {
bool primary = interface.name == data.primary_interface;
auto value = make_value<map_value>();
// Add the ipv4 bindings
add_bindings(interface, primary, true, facts, *networking, *value);
// Add the ipv6 bindings
add_bindings(interface, primary, false, facts, *networking, *value);
// Add the MAC address
if (!interface.macaddress.empty()) {
facts.add(string(fact::macaddress) + "_" + interface.name, make_value<string_value>(interface.macaddress, true));
if (primary) {
facts.add(fact::macaddress, make_value<string_value>(interface.macaddress, true));
networking->add("mac", make_value<string_value>(interface.macaddress));
}
value->add("mac", make_value<string_value>(move(interface.macaddress)));
}
// Add the DHCP server
if (!interface.dhcp_server.empty()) {
if (primary) {
dhcp_servers->add("system", make_value<string_value>(interface.dhcp_server));
networking->add("dhcp", make_value<string_value>(interface.dhcp_server));
}
dhcp_servers->add(string(interface.name), make_value<string_value>(interface.dhcp_server));
value->add("dhcp", make_value<string_value>(move(interface.dhcp_server)));
}
// Add the interface MTU
if (interface.mtu) {
facts.add(string(fact::mtu) + "_" + interface.name, make_value<integer_value>(*interface.mtu, true));
if (primary) {
networking->add("mtu", make_value<integer_value>(*interface.mtu));
}
value->add("mtu", make_value<integer_value>(*interface.mtu));
}
// Add the interface to the list of names
if (interface_names.tellp() != 0) {
interface_names << ",";
}
interface_names << interface.name;
interfaces->add(move(interface.name), move(value));
}
// Add top-level network data
if (!data.hostname.empty()) {
facts.add(fact::hostname, make_value<string_value>(data.hostname, true));
networking->add("hostname", make_value<string_value>(move(data.hostname)));
}
if (!data.domain.empty()) {
facts.add(fact::domain, make_value<string_value>(data.domain, true));
networking->add("domain", make_value<string_value>(move(data.domain)));
}
if (!data.fqdn.empty()) {
facts.add(fact::fqdn, make_value<string_value>(data.fqdn, true));
networking->add("fqdn", make_value<string_value>(move(data.fqdn)));
}
if (!data.primary_interface.empty()) {
networking->add("primary", make_value<string_value>(move(data.primary_interface)));
}
if (interface_names.tellp() != 0) {
facts.add(fact::interfaces, make_value<string_value>(interface_names.str(), true));
}
if (!dhcp_servers->empty()) {
facts.add(fact::dhcp_servers, move(dhcp_servers));
}
if (!interfaces->empty()) {
networking->add("interfaces", move(interfaces));
}
if (!networking->empty()) {
facts.add(fact::networking, move(networking));
}
}
string networking_resolver::get_scope(const string& address)
{
std::ostringstream ostream;
boost::asio::ip::address_v6 addr;
try {
addr = boost::asio::ip::address_v6::from_string(address);
} catch (boost::exception &e) {
return ostream.str();
}
if (addr.is_v4_compatible()) {
ostream << "compat,";
}
if (addr.is_link_local())
ostream << "link";
else if (addr.is_site_local())
ostream << "site";
else if (addr.is_loopback())
ostream << "host";
else
ostream << "global";
return ostream.str();
}
string networking_resolver::macaddress_to_string(uint8_t const* bytes, uint8_t byte_count)
{
if (!bytes || (byte_count != 6 && byte_count != 20)) {
return {};
}
// Ignore MAC address "0"
bool nonzero = false;
for (size_t i = 0; i < byte_count; ++i) {
if (bytes[i] != 0) {
nonzero = true;
break;
}
}
if (!nonzero) {
return {};
}
if (byte_count == 6) {
return (boost::format("%02x:%02x:%02x:%02x:%02x:%02x") %
static_cast<int>(bytes[0]) % static_cast<int>(bytes[1]) %
static_cast<int>(bytes[2]) % static_cast<int>(bytes[3]) %
static_cast<int>(bytes[4]) % static_cast<int>(bytes[5])).str();
} else if (byte_count == 20) {
return (boost::format("%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x") %
static_cast<int>(bytes[0]) % static_cast<int>(bytes[1]) %
static_cast<int>(bytes[2]) % static_cast<int>(bytes[3]) %
static_cast<int>(bytes[4]) % static_cast<int>(bytes[5]) %
static_cast<int>(bytes[6]) % static_cast<int>(bytes[7]) %
static_cast<int>(bytes[8]) % static_cast<int>(bytes[9]) %
static_cast<int>(bytes[10]) % static_cast<int>(bytes[11]) %
static_cast<int>(bytes[12]) % static_cast<int>(bytes[13]) %
static_cast<int>(bytes[14]) % static_cast<int>(bytes[15]) %
static_cast<int>(bytes[16]) % static_cast<int>(bytes[17]) %
static_cast<int>(bytes[18]) % static_cast<int>(bytes[19])).str();
} else {
return {};
}
}
bool networking_resolver::ignored_ipv4_address(string const& addr)
{
// Excluding localhost and 169.254.x.x in Windows - this is the DHCP APIPA, meaning that if the node cannot
// get an ip address from the dhcp server, it auto-assigns a private ip address
return addr.empty() || boost::starts_with(addr, "127.") || boost::starts_with(addr, "169.254.");
}
bool networking_resolver::ignored_ipv6_address(string const& addr)
{
return addr.empty() || addr == "::1" || boost::starts_with(addr, "fe80");
}
networking_resolver::binding const* networking_resolver::find_default_binding(vector<binding> const& bindings, function<bool(string const&)> const& ignored)
{
for (auto& binding : bindings) {
if (!ignored(binding.address)) {
return &binding;
}
}
return bindings.empty() ? nullptr : &bindings.front();
}
void networking_resolver::add_bindings(interface& iface, bool primary, bool ipv4, collection& facts, map_value& networking, map_value& iface_value)
{
auto ip_fact = ipv4 ? fact::ipaddress : fact::ipaddress6;
auto ip_name = ipv4 ? "ip" : "ip6";
auto netmask_fact = ipv4 ? fact::netmask : fact::netmask6;
auto netmask_name = ipv4 ? "netmask" : "netmask6";
auto network_fact = ipv4 ? fact::network : fact::network6;
auto network_name = ipv4 ? "network" : "network6";
auto& bindings = ipv4 ? iface.ipv4_bindings : iface.ipv6_bindings;
auto bindings_name = ipv4 ? "bindings" : "bindings6";
auto ignored = ipv4 ? &ignored_ipv4_address : &ignored_ipv6_address;
// Add the default binding to the collection and interface
auto binding = find_default_binding(bindings, ignored);
if (binding) {
if (!binding->address.empty()) {
facts.add(string(ip_fact) + "_" + iface.name, make_value<string_value>(binding->address, true));
iface_value.add(ip_name, make_value<string_value>(binding->address));
if (!ipv4) {
facts.add(string(fact::scope6) + "_" + iface.name, make_value<string_value>(get_scope(binding->address), true));
iface_value.add("scope6", make_value<string_value>(get_scope(binding->address)));
}
if (primary) {
facts.add(ip_fact, make_value<string_value>(binding->address, true));
networking.add(ip_name, make_value<string_value>(binding->address));
if (!ipv4) {
facts.add(fact::scope6, make_value<string_value>(get_scope(binding->address), true));
networking.add("scope6", make_value<string_value>(get_scope(binding->address)));
}
}
}
if (!binding->netmask.empty()) {
facts.add(string(netmask_fact) + "_" + iface.name, make_value<string_value>(binding->netmask, true));
if (primary) {
facts.add(netmask_fact, make_value<string_value>(binding->netmask, true));
networking.add(netmask_name, make_value<string_value>(binding->netmask));
}
iface_value.add(netmask_name, make_value<string_value>(binding->netmask));
}
if (!binding->network.empty()) {
facts.add(string(network_fact) + "_" + iface.name, make_value<string_value>(binding->network, true));
if (primary) {
facts.add(network_fact, make_value<string_value>(binding->network, true));
networking.add(network_name, make_value<string_value>(binding->network));
}
iface_value.add(network_name, make_value<string_value>(binding->network));
}
}
// Set the bindings in the interface
if (!bindings.empty()) {
auto bindings_value = make_value<array_value>();
for (auto& binding : bindings) {
auto binding_value = make_value<map_value>();
if (!binding.address.empty()) {
binding_value->add("address", make_value<string_value>(move(binding.address)));
}
if (!binding.netmask.empty()) {
binding_value->add("netmask", make_value<string_value>(move(binding.netmask)));
}
if (!binding.network.empty()) {
binding_value->add("network", make_value<string_value>(move(binding.network)));
}
if (!binding_value->empty()) {
bindings_value->add(move(binding_value));
}
}
iface_value.add(bindings_name, move(bindings_value));
}
}
networking_resolver::interface const* networking_resolver::find_primary_interface(vector<interface> const& interfaces)
{
for (auto const& interface : interfaces) {
for (auto const& binding : interface.ipv4_bindings) {
if (!ignored_ipv4_address(binding.address)) {
return &interface;
}
}
for (auto const& binding : interface.ipv6_bindings) {
if (!ignored_ipv6_address(binding.address)) {
return &interface;
}
}
}
return nullptr;
}
}}} // namespace facter::facts::posix
You can’t perform that action at this time.