blob: 3dc5f2d3e969f5de06253d8d54b195b2a79b8708 [file]
/** @file
Support class for describing the local machine.
@section license License
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include "iocore/utils/Machine.h"
#include "tscore/Diags.h"
#include "tscore/SnowflakeID.h"
#include "tscore/ink_assert.h"
#include "tscore/ink_inet.h"
#if HAVE_IFADDRS_H
#include <ifaddrs.h>
#endif
// Singleton
Machine *Machine::_instance = nullptr;
/** Compute the FNV-1a hash for the given string.
* @param[in] str The string to hash.
* @return The 64-bit FNV-1a hash of the string.
*/
static constexpr uint64_t
compute_fnv1a(std::string_view str)
{
// Parameters set under guidance from:
// http://isthe.com/chongo/tech/comp/fnv/#google_vignette.
constexpr uint64_t FNV_PRIME = 1099511628211u;
constexpr uint64_t FNV_OFFSET = 14695981039346656037u;
uint64_t hash = FNV_OFFSET;
// Process each character in lowercase
for (char c : str) {
hash ^= static_cast<unsigned int>(c);
hash *= FNV_PRIME;
}
return hash;
}
// Verify our FNV-1a hash implementation.
static_assert(compute_fnv1a("") == 14695981039346656037u, "64 bit FNV-1 hash for '' should be 14695981039346656037");
static_assert(compute_fnv1a("e1.myedge.colo.acme.com") == 9637442596227468504u,
"64 bit FNV-1 hash for 'e1.myedge.colo.acme.com' should be 9637442596227468504");
Machine *
Machine::instance()
{
ink_assert(_instance || !"Machine instance accessed before initialization");
return Machine::_instance;
}
Machine *
Machine::init(char const *name, sockaddr const *ip)
{
ink_assert(!_instance || !"Machine instance initialized twice.");
Machine::_instance = new Machine(name, ip);
return Machine::_instance;
}
Machine::Machine(char const *the_hostname, sockaddr const *addr)
{
// Initialize the machine IP information
bzero(&ip, sizeof(ip));
bzero(&ip4, sizeof(ip4));
bzero(&ip6, sizeof(ip6));
int status; // return for system calls.
ip_text_buffer ip_strbuf;
char localhost[1024];
process_uuid.initialize(TS_UUID_V4);
ink_release_assert(nullptr != process_uuid.getString()); // The Process UUID must be available on startup
if (!ats_is_ip(addr)) {
if (!the_hostname) {
// @c gethostname has a broken interface - there's no way to determine the actual size of
// the host name explicitly - the error case doesn't return the size. The standards based
// limit is 63, or 255 for a FQDN.
auto result = gethostname(localhost, sizeof(localhost));
ink_release_assert(result == 0);
host_name.assign(localhost, strlen(localhost));
insert_id(localhost);
}
#if HAVE_IFADDRS_H
ifaddrs *ifa_addrs = nullptr;
status = getifaddrs(&ifa_addrs);
#else
int s = socket(AF_INET, SOCK_DGRAM, 0);
// This number is hard to determine, but needs to be much larger than
// you would expect. On a normal system with just two interfaces and
// one address / interface the return count is 120. Stack space is
// cheap so it's best to go big.
static constexpr int N_REQ = 1024;
ifconf conf;
ifreq req[N_REQ];
if (0 <= s) {
conf.ifc_len = sizeof(req);
conf.ifc_req = req;
status = ioctl(s, SIOCGIFCONF, &conf);
} else {
status = -1;
}
#endif
if (0 != status) {
Warning("Unable to determine local host '%.*s' address information - %s", int(host_name.size()), host_name.data(),
strerror(errno));
} else {
// Loop through the interface addresses and prefer by type.
enum {
NA, // Not an (IP) Address.
LO, // Loopback.
LL, // Link Local
PR, // Private.
MC, // Multicast.
GL // Global.
} spot_type = NA,
ip4_type = NA, ip6_type = NA;
sockaddr const *ifip;
unsigned int ifflags;
for (
#if HAVE_IFADDRS_H
ifaddrs *spot = ifa_addrs; spot; spot = spot->ifa_next
#else
ifreq *spot = req, *req_limit = req + (conf.ifc_len / sizeof(*req)); spot < req_limit; ++spot
#endif
) {
#if HAVE_IFADDRS_H
ifip = spot->ifa_addr;
ifflags = spot->ifa_flags;
#else
ifip = &spot->ifr_addr;
// get the interface's flags
struct ifreq ifr;
ink_strlcpy(ifr.ifr_name, spot->ifr_name, IFNAMSIZ);
if (ioctl(s, SIOCGIFFLAGS, &ifr) == 0) {
ifflags = ifr.ifr_flags;
} else {
ifflags = 0; // flags not available, default to just looking at IP
}
#endif
if (!ats_is_ip(ifip)) {
spot_type = NA;
} else if (ats_is_ip_loopback(ifip) || (IFF_LOOPBACK & ifflags)) {
spot_type = LO;
} else if (ats_is_ip_linklocal(ifip)) {
spot_type = LL;
} else if (ats_is_ip_private(ifip)) {
spot_type = PR;
} else if (ats_is_ip_multicast(ifip)) {
spot_type = MC;
} else {
spot_type = GL;
}
if (spot_type == NA) {
continue; // Next!
}
if (ats_is_ip4(ifip) || ats_is_ip6(ifip)) {
ink_zero(ip_strbuf);
ink_zero(localhost);
ats_ip_ntop(ifip, ip_strbuf, sizeof(ip_strbuf));
insert_id(ip_strbuf);
if (spot_type != LL && getnameinfo(ifip, ats_ip_size(ifip), localhost, sizeof(localhost) - 1, nullptr, 0, 0) == 0) {
insert_id(localhost);
}
insert_id(IpAddr(ifip));
if (ats_is_ip4(ifip)) {
if (spot_type > ip4_type) {
ats_ip_copy(&ip4, ifip);
ip4_type = spot_type;
}
} else if (ats_is_ip6(ifip)) {
if (spot_type > ip6_type) {
ats_ip_copy(&ip6, ifip);
ip6_type = spot_type;
}
}
}
}
#if HAVE_IFADDRS_H
freeifaddrs(ifa_addrs);
#endif
// What about the general address? Prefer IPv4?
if (ip4_type >= ip6_type) {
ats_ip_copy(&ip.sa, &ip4.sa);
} else {
ats_ip_copy(&ip.sa, &ip6.sa);
}
}
#if !HAVE_IFADDRS_H
close(s);
#endif
} else { // address provided.
ats_ip_copy(&ip, addr);
if (ats_is_ip4(addr)) {
ats_ip_copy(&ip4, addr);
} else if (ats_is_ip6(addr)) {
ats_ip_copy(&ip6, addr);
}
status = getnameinfo(addr, ats_ip_size(addr), localhost, sizeof(localhost) - 1, nullptr, 0, 0); // no flags
if (0 != status) {
ip_text_buffer ipbuff;
Warning("Failed to find hostname for address '%s' - %s", ats_ip_ntop(addr, ipbuff, sizeof(ipbuff)), gai_strerror(status));
} else {
host_name.assign(localhost);
insert_id(localhost);
}
}
host_fnv1a = compute_fnv1a(host_name);
SnowflakeIDUtils::set_machine_id(host_fnv1a);
process_snowflake_id = std::make_unique<SnowflakeIdNoSequence>();
char hex_buff[TS_IP6_SIZE * 2 + 1];
ats_ip_to_hex(&ip.sa, hex_buff, sizeof(hex_buff));
ip_hex_string.assign(hex_buff);
}
Machine::~Machine() {}
bool
Machine::is_self(std::string const &name)
{
return machine_id_strings.find(name) != machine_id_strings.end();
}
bool
Machine::is_self(std::string_view name)
{
return this->is_self(std::string(name));
}
bool
Machine::is_self(char const *name)
{
return this->is_self(std::string(name));
}
bool
Machine::is_self(IpAddr const &ipaddr)
{
return machine_id_ipaddrs.end() != machine_id_ipaddrs.find(ipaddr);
}
bool
Machine::is_self(struct sockaddr const *addr)
{
return machine_id_ipaddrs.find(IpAddr(addr)) != machine_id_ipaddrs.end();
}
void
Machine::insert_id(char const *id)
{
machine_id_strings.emplace(id);
}
void
Machine::insert_id(IpAddr const &ipaddr)
{
ip_text_buffer buff;
ipaddr.toString(buff, sizeof(buff));
machine_id_strings.emplace(buff);
machine_id_ipaddrs.emplace(ipaddr);
}