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