1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190
|
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <net/if.h>
#include <net/if_dl.h>
#include <net/if_media.h>
#include <netinet/in.h>
#include <netinet/in_var.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/types.h>
// net/if.h must be included before this.
#include <ifaddrs.h>
#include <algorithm>
#include <string>
#include <vector>
#include "platform/api/network_interface.h"
#include "platform/base/ip_address.h"
#include "platform/impl/network_interface.h"
#include "platform/impl/scoped_pipe.h"
#include "util/osp_logging.h"
namespace openscreen {
namespace {
// Assuming |netmask| consists of 0 to N*8 leftmost bits set followed by all
// unset bits, return the number of leftmost bits set. This also sanity-checks
// that there are no "holes" in the bit pattern, returning 0 if that check
// fails.
template <size_t N>
uint8_t ToPrefixLength(const uint8_t (&netmask)[N]) {
uint8_t result = 0;
size_t i = 0;
// Ensure all of the leftmost bits are set.
while (i < N && netmask[i] == UINT8_C(0xff)) {
result += 8;
++i;
}
// Check the intermediate byte, the first that is not 0xFF,
// e.g. 0b11100000 or 0x00
if (i < N && netmask[i] != UINT8_C(0x00)) {
uint8_t last_byte = netmask[i];
// Check the left most bit, bitshifting as we go.
while (last_byte & UINT8_C(0x80)) {
++result;
last_byte <<= 1;
}
OSP_CHECK(last_byte == UINT8_C(0x00));
++i;
}
// Ensure the rest of the bytes are zeroed out.
while (i < N) {
OSP_CHECK(netmask[i] == UINT8_C(0x00));
++i;
}
return result;
}
std::vector<InterfaceInfo> ProcessInterfacesList(ifaddrs* interfaces) {
// Socket used for querying interface media types.
const ScopedFd ioctl_socket(socket(AF_INET6, SOCK_DGRAM, 0));
// Walk the |interfaces| linked list, creating the hierarchical structure.
std::vector<InterfaceInfo> results;
for (ifaddrs* cur = interfaces; cur; cur = cur->ifa_next) {
// Skip: 1) interfaces that are down, 2) interfaces with no address
// configured.
if (!(IFF_RUNNING & cur->ifa_flags) || !cur->ifa_addr) {
continue;
}
// Look-up the InterfaceInfo entry by name. Auto-create a new one if none by
// the current name exists in |results|.
const std::string name = cur->ifa_name;
const auto it = std::find_if(
results.begin(), results.end(),
[&name](const InterfaceInfo& info) { return info.name == name; });
InterfaceInfo* interface;
if (it == results.end()) {
InterfaceInfo::Type type = InterfaceInfo::Type::kOther;
// Query for the interface media type and status. If not valid/active,
// skip further processing. Note that "active" here means the media is
// connected to the interface, which is different than the interface being
// up/down.
ifmediareq ifmr;
memset(&ifmr, 0, sizeof(ifmr));
// Note: Because of the memset(), memcpy() can be used to copy the
// ifmr.ifm_name string, and it will always be NUL terminated.
memcpy(ifmr.ifm_name, name.data(),
std::min(name.size(), sizeof(ifmr.ifm_name) - 1));
if (ioctl(ioctl_socket.get(), SIOCGIFMEDIA, &ifmr) >= 0) {
if (!((ifmr.ifm_status & IFM_AVALID) &&
(ifmr.ifm_status & IFM_ACTIVE))) {
continue; // Skip this interface since it's not valid or active.
}
if (ifmr.ifm_current & IFM_IEEE80211) {
type = InterfaceInfo::Type::kWifi;
} else if (ifmr.ifm_current & IFM_ETHER) {
type = InterfaceInfo::Type::kEthernet;
}
} else if (cur->ifa_flags & IFF_LOOPBACK) {
type = InterfaceInfo::Type::kLoopback;
} else {
continue;
}
// Start with an unknown hardware ethernet address, which should be
// updated as the linked list is walked.
const uint8_t kUnknownHardwareAddress[6] = {0, 0, 0, 0, 0, 0};
results.emplace_back(if_nametoindex(cur->ifa_name),
kUnknownHardwareAddress, name, type,
// IPSubnets to be filled-in later.
std::vector<IPSubnet>());
interface = &(results.back());
} else {
interface = &(*it);
}
// Add another address to the list of addresses for the current interface.
if (cur->ifa_addr->sa_family == AF_LINK) { // Hardware ethernet address.
auto* const addr_dl = reinterpret_cast<const sockaddr_dl*>(cur->ifa_addr);
const caddr_t lladdr = LLADDR(addr_dl);
static_assert(sizeof(lladdr) >= sizeof(interface->hardware_address),
"Platform defines too-small link addresses?");
memcpy(&interface->hardware_address[0], &lladdr[0],
sizeof(interface->hardware_address));
} else if (cur->ifa_addr->sa_family == AF_INET6) { // Ipv6 address.
struct in6_ifreq ifr = {};
// Reject network interfaces that have a deprecated flag set.
strncpy(ifr.ifr_name, cur->ifa_name, sizeof(ifr.ifr_name) - 1);
memcpy(&ifr.ifr_ifru.ifru_addr, cur->ifa_addr, cur->ifa_addr->sa_len);
if (ioctl(ioctl_socket.get(), SIOCGIFAFLAG_IN6, &ifr) != 0 ||
ifr.ifr_ifru.ifru_flags & IN6_IFF_DEPRECATED) {
continue;
}
auto* const addr_in6 =
reinterpret_cast<const sockaddr_in6*>(cur->ifa_addr);
uint8_t tmp[sizeof(addr_in6->sin6_addr.s6_addr)];
memcpy(tmp, &(addr_in6->sin6_addr.s6_addr), sizeof(tmp));
const IPAddress ip(IPAddress::Version::kV6, tmp);
memset(tmp, 0, sizeof(tmp));
if (cur->ifa_netmask && cur->ifa_netmask->sa_family == AF_INET6) {
memcpy(tmp,
&(reinterpret_cast<const sockaddr_in6*>(cur->ifa_netmask)
->sin6_addr.s6_addr),
sizeof(tmp));
}
interface->addresses.emplace_back(ip, ToPrefixLength(tmp));
} else if (cur->ifa_addr->sa_family == AF_INET) { // Ipv4 address.
auto* const addr_in = reinterpret_cast<const sockaddr_in*>(cur->ifa_addr);
uint8_t tmp[sizeof(addr_in->sin_addr.s_addr)];
memcpy(tmp, &(addr_in->sin_addr.s_addr), sizeof(tmp));
IPAddress ip(IPAddress::Version::kV4, tmp);
memset(tmp, 0, sizeof(tmp));
if (cur->ifa_netmask && cur->ifa_netmask->sa_family == AF_INET) {
memcpy(tmp,
&(reinterpret_cast<const sockaddr_in*>(cur->ifa_netmask)
->sin_addr.s_addr),
sizeof(tmp));
}
interface->addresses.emplace_back(ip, ToPrefixLength(tmp));
}
}
return results;
}
} // namespace
std::vector<InterfaceInfo> GetAllInterfaces() {
std::vector<InterfaceInfo> results;
ifaddrs* interfaces;
if (getifaddrs(&interfaces) == 0) {
results = ProcessInterfacesList(interfaces);
freeifaddrs(interfaces);
}
return results;
}
} // namespace openscreen
|