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 // clang-format: off
6 #include <sys/socket.h>
7 // clang-format: on
8
9 #include <linux/ethtool.h>
10 #include <linux/if_arp.h>
11 #include <linux/netlink.h>
12 #include <linux/rtnetlink.h>
13 #include <linux/sockios.h>
14 #include <linux/wireless.h>
15 #include <netinet/ip.h>
16 #include <string.h>
17 #include <sys/ioctl.h>
18 #include <sys/types.h>
19 #include <unistd.h>
20
21 #include <algorithm>
22 #include <cstring>
23
24 #include "absl/strings/string_view.h"
25 #include "absl/types/optional.h"
26 #include "platform/api/network_interface.h"
27 #include "platform/base/ip_address.h"
28 #include "platform/impl/network_interface.h"
29 #include "platform/impl/scoped_pipe.h"
30 #include "util/osp_logging.h"
31
32 namespace openscreen {
33 namespace {
34
35 constexpr int kNetlinkRecvmsgBufSize = 8192;
36
37 // Safely reads the system name for the interface from the (probably)
38 // null-terminated string |kernel_name| and returns a std::string.
GetInterfaceName(absl::string_view kernel_name)39 std::string GetInterfaceName(absl::string_view kernel_name) {
40 OSP_CHECK_LT(kernel_name.length(), IFNAMSIZ);
41 return std::string(kernel_name);
42 }
43
44 // Returns the type of the interface identified by the name |ifname|, if it can
45 // be determined, otherwise returns InterfaceInfo::Type::kOther.
GetInterfaceType(const std::string & ifname)46 InterfaceInfo::Type GetInterfaceType(const std::string& ifname) {
47 // Determine type after name has been set.
48 ScopedFd s(socket(AF_INET6, SOCK_DGRAM, 0));
49 if (!s) {
50 s = ScopedFd(socket(AF_INET, SOCK_DGRAM, 0));
51 if (!s)
52 return InterfaceInfo::Type::kOther;
53 }
54
55 // Note: This uses Wireless Extensions to test the interface, which is
56 // deprecated. However, it's much easier than using the new nl80211
57 // interface for this purpose. If Wireless Extensions are ever actually
58 // removed though, this will need to use nl80211.
59 struct iwreq wr;
60 static_assert(sizeof(wr.ifr_name) == IFNAMSIZ,
61 "expected size of interface name fields");
62 OSP_CHECK_LT(ifname.size(), IFNAMSIZ);
63 wr.ifr_name[IFNAMSIZ - 1] = 0;
64 strncpy(wr.ifr_name, ifname.c_str(), IFNAMSIZ - 1);
65 if (ioctl(s.get(), SIOCGIWNAME, &wr) != -1)
66 return InterfaceInfo::Type::kWifi;
67
68 struct ethtool_cmd ecmd;
69 ecmd.cmd = ETHTOOL_GSET;
70 struct ifreq ifr;
71 static_assert(sizeof(ifr.ifr_name) == IFNAMSIZ,
72 "expected size of interface name fields");
73 OSP_CHECK_LT(ifname.size(), IFNAMSIZ);
74 wr.ifr_name[IFNAMSIZ - 1] = 0;
75 strncpy(ifr.ifr_name, ifname.c_str(), IFNAMSIZ - 1);
76 ifr.ifr_data = &ecmd;
77 if (ioctl(s.get(), SIOCETHTOOL, &ifr) != -1) {
78 return InterfaceInfo::Type::kEthernet;
79 }
80
81 return InterfaceInfo::Type::kOther;
82 }
83
84 // Reads an interface's name, hardware address, and type from |rta| and places
85 // the results in |info|. |rta| is the first attribute structure returned as
86 // part of an RTM_NEWLINK message. |attrlen| is the total length of the buffer
87 // pointed to by |rta|.
GetInterfaceAttributes(struct rtattr * rta,unsigned int attrlen,bool is_loopback,InterfaceInfo * info)88 void GetInterfaceAttributes(struct rtattr* rta,
89 unsigned int attrlen,
90 bool is_loopback,
91 InterfaceInfo* info) {
92 for (; RTA_OK(rta, attrlen); rta = RTA_NEXT(rta, attrlen)) {
93 if (rta->rta_type == IFLA_IFNAME) {
94 info->name =
95 GetInterfaceName(reinterpret_cast<const char*>(RTA_DATA(rta)));
96 } else if (rta->rta_type == IFLA_ADDRESS) {
97 OSP_CHECK_EQ(sizeof(info->hardware_address), RTA_PAYLOAD(rta));
98 std::memcpy(info->hardware_address.data(), RTA_DATA(rta),
99 sizeof(info->hardware_address));
100 }
101 }
102
103 if (is_loopback) {
104 info->type = InterfaceInfo::Type::kLoopback;
105 } else {
106 info->type = GetInterfaceType(info->name);
107 }
108 }
109
110 // Reads the IPv4 or IPv6 address that comes from an RTM_NEWADDR message and
111 // places the result in |address|. |rta| is the first attribute structure
112 // returned by the message and |attrlen| is the total length of the buffer
113 // pointed to by |rta|. |ifname| is the name of the interface to which we
114 // believe the address belongs based on interface index matching. It is only
115 // used for sanity checking.
GetIPAddressOrNull(struct rtattr * rta,unsigned int attrlen,IPAddress::Version version,const std::string & ifname)116 absl::optional<IPAddress> GetIPAddressOrNull(struct rtattr* rta,
117 unsigned int attrlen,
118 IPAddress::Version version,
119 const std::string& ifname) {
120 const size_t expected_address_size = version == IPAddress::Version::kV4
121 ? IPAddress::kV4Size
122 : IPAddress::kV6Size;
123
124 bool have_local = false;
125 IPAddress address;
126 IPAddress local;
127 for (; RTA_OK(rta, attrlen); rta = RTA_NEXT(rta, attrlen)) {
128 if (rta->rta_type == IFA_LABEL) {
129 const char* const label = reinterpret_cast<const char*>(RTA_DATA(rta));
130 if (ifname != label) {
131 OSP_LOG_ERROR << "Interface label mismatch! Expected: " << ifname
132 << ", Have: " << label;
133 return absl::nullopt;
134 }
135 } else if (rta->rta_type == IFA_ADDRESS) {
136 OSP_DCHECK_EQ(expected_address_size, RTA_PAYLOAD(rta));
137 address = IPAddress(version, static_cast<uint8_t*>(RTA_DATA(rta)));
138 } else if (rta->rta_type == IFA_LOCAL) {
139 OSP_DCHECK_EQ(expected_address_size, RTA_PAYLOAD(rta));
140 have_local = true;
141 local = IPAddress(version, static_cast<uint8_t*>(RTA_DATA(rta)));
142 }
143 }
144 return have_local ? local : address;
145 }
146
GetLinkInfo()147 std::vector<InterfaceInfo> GetLinkInfo() {
148 ScopedFd fd(socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE));
149 if (!fd) {
150 OSP_LOG_WARN << "netlink socket() failed: " << errno << " - "
151 << strerror(errno);
152 return {};
153 }
154
155 {
156 // nl_pid = 0 for the kernel.
157 struct sockaddr_nl peer = {};
158 peer.nl_family = AF_NETLINK;
159 struct {
160 struct nlmsghdr header;
161 struct ifinfomsg msg;
162 } request;
163
164 request.header.nlmsg_len = sizeof(request);
165 request.header.nlmsg_type = RTM_GETLINK;
166 request.header.nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT;
167 request.header.nlmsg_seq = 0;
168 request.header.nlmsg_pid = 0;
169 request.msg.ifi_family = AF_UNSPEC;
170 struct iovec iov = {&request, request.header.nlmsg_len};
171 struct msghdr msg = {};
172 msg.msg_name = &peer;
173 msg.msg_namelen = sizeof(peer);
174 msg.msg_iov = &iov;
175 msg.msg_iovlen = 1;
176 msg.msg_control = nullptr;
177 msg.msg_controllen = 0;
178 msg.msg_flags = 0;
179 if (sendmsg(fd.get(), &msg, 0) < 0) {
180 OSP_LOG_ERROR << "netlink sendmsg() failed: " << errno << " - "
181 << strerror(errno);
182 return {};
183 }
184 }
185
186 std::vector<InterfaceInfo> info_list;
187 {
188 char buf[kNetlinkRecvmsgBufSize];
189 struct iovec iov = {buf, sizeof(buf)};
190 struct sockaddr_nl source_address;
191 struct msghdr msg = {};
192 struct nlmsghdr* netlink_header;
193
194 msg.msg_name = &source_address;
195 msg.msg_namelen = sizeof(source_address);
196 msg.msg_iov = &iov;
197 msg.msg_iovlen = 1,
198 msg.msg_control = nullptr,
199 msg.msg_controllen = 0,
200 msg.msg_flags = 0;
201
202 bool done = false;
203 while (!done) {
204 size_t len = recvmsg(fd.get(), &msg, 0);
205
206 for (netlink_header = reinterpret_cast<struct nlmsghdr*>(buf);
207 NLMSG_OK(netlink_header, len);
208 netlink_header = NLMSG_NEXT(netlink_header, len)) {
209 // The end of multipart message.
210 if (netlink_header->nlmsg_type == NLMSG_DONE) {
211 done = true;
212 break;
213 } else if (netlink_header->nlmsg_type == NLMSG_ERROR) {
214 done = true;
215 OSP_LOG_ERROR << "netlink error msg: "
216 << reinterpret_cast<struct nlmsgerr*>(
217 NLMSG_DATA(netlink_header))
218 ->error;
219 continue;
220 } else if ((netlink_header->nlmsg_flags & NLM_F_MULTI) == 0) {
221 // If this is not a multi-part message, we don't need to wait for an
222 // NLMSG_DONE message; this is the only message.
223 done = true;
224 }
225
226 // RTM_NEWLINK messages describe existing network links on the host.
227 if (netlink_header->nlmsg_type != RTM_NEWLINK)
228 continue;
229
230 struct ifinfomsg* interface_info =
231 static_cast<struct ifinfomsg*>(NLMSG_DATA(netlink_header));
232 // Only process interfaces which are active (up).
233 if (!(interface_info->ifi_flags & IFF_UP)) {
234 continue;
235 }
236
237 info_list.emplace_back();
238 InterfaceInfo& info = info_list.back();
239 info.index = interface_info->ifi_index;
240 GetInterfaceAttributes(IFLA_RTA(interface_info),
241 IFLA_PAYLOAD(netlink_header),
242 interface_info->ifi_flags & IFF_LOOPBACK, &info);
243 }
244 }
245 }
246
247 return info_list;
248 }
249
PopulateSubnetsOrClearList(std::vector<InterfaceInfo> * info_list)250 void PopulateSubnetsOrClearList(std::vector<InterfaceInfo>* info_list) {
251 ScopedFd fd(socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE));
252 if (!fd) {
253 OSP_LOG_ERROR << "netlink socket() failed: " << errno << " - "
254 << strerror(errno);
255 info_list->clear();
256 return;
257 }
258
259 {
260 // nl_pid = 0 for the kernel.
261 struct sockaddr_nl peer = {};
262 peer.nl_family = AF_NETLINK;
263 struct {
264 struct nlmsghdr header;
265 struct ifaddrmsg msg;
266 } request;
267
268 request.header.nlmsg_len = sizeof(request);
269 request.header.nlmsg_type = RTM_GETADDR;
270 request.header.nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT;
271 request.header.nlmsg_seq = 1;
272 request.header.nlmsg_pid = 0;
273 request.msg.ifa_family = AF_UNSPEC;
274 struct iovec iov = {&request, request.header.nlmsg_len};
275 struct msghdr msg = {};
276 msg.msg_name = &peer;
277 msg.msg_namelen = sizeof(peer);
278 msg.msg_iov = &iov;
279 msg.msg_iovlen = 1;
280 msg.msg_control = nullptr;
281 msg.msg_controllen = 0;
282 msg.msg_flags = 0;
283 if (sendmsg(fd.get(), &msg, 0) < 0) {
284 OSP_LOG_ERROR << "sendmsg failed: " << errno << " - " << strerror(errno);
285 info_list->clear();
286 return;
287 }
288 }
289
290 {
291 char buf[kNetlinkRecvmsgBufSize];
292 struct iovec iov = {buf, sizeof(buf)};
293 struct sockaddr_nl source_address;
294 struct msghdr msg = {};
295 struct nlmsghdr* netlink_header;
296
297 msg.msg_name = &source_address;
298 msg.msg_namelen = sizeof(source_address);
299 msg.msg_iov = &iov;
300 msg.msg_iovlen = 1;
301 msg.msg_control = nullptr;
302 msg.msg_controllen = 0;
303 msg.msg_flags = 0;
304 bool done = false;
305 while (!done) {
306 size_t len = recvmsg(fd.get(), &msg, 0);
307
308 for (netlink_header = reinterpret_cast<struct nlmsghdr*>(buf);
309 NLMSG_OK(netlink_header, len);
310 netlink_header = NLMSG_NEXT(netlink_header, len)) {
311 if (netlink_header->nlmsg_type == NLMSG_DONE) {
312 done = true;
313 break;
314 } else if (netlink_header->nlmsg_type == NLMSG_ERROR) {
315 done = true;
316 OSP_LOG_ERROR << "netlink error msg: "
317 << reinterpret_cast<struct nlmsgerr*>(
318 NLMSG_DATA(netlink_header))
319 ->error;
320 continue;
321 } else if ((netlink_header->nlmsg_flags & NLM_F_MULTI) == 0) {
322 // If this is not a multi-part message, we don't need to wait for an
323 // NLMSG_DONE message; this is the only message.
324 done = true;
325 }
326
327 if (netlink_header->nlmsg_type != RTM_NEWADDR)
328 continue;
329
330 struct ifaddrmsg* interface_address =
331 static_cast<struct ifaddrmsg*>(NLMSG_DATA(netlink_header));
332
333 const auto it = std::find_if(
334 info_list->begin(), info_list->end(),
335 [index = interface_address->ifa_index](const InterfaceInfo& info) {
336 return info.index == index;
337 });
338 if (it == info_list->end()) {
339 OSP_DVLOG << "skipping address for interface "
340 << interface_address->ifa_index;
341 continue;
342 }
343
344 if (interface_address->ifa_family == AF_INET ||
345 interface_address->ifa_family == AF_INET6) {
346 const auto address_or_null = GetIPAddressOrNull(
347 IFA_RTA(interface_address), IFA_PAYLOAD(netlink_header),
348 interface_address->ifa_family == AF_INET
349 ? IPAddress::Version::kV4
350 : IPAddress::Version::kV6,
351 it->name);
352 if (address_or_null) {
353 it->addresses.emplace_back(*address_or_null,
354 interface_address->ifa_prefixlen);
355 }
356 } else {
357 OSP_LOG_ERROR << "Unknown address family: "
358 << interface_address->ifa_family;
359 }
360 }
361 }
362 }
363 }
364
365 } // namespace
366
GetAllInterfaces()367 std::vector<InterfaceInfo> GetAllInterfaces() {
368 std::vector<InterfaceInfo> interfaces = GetLinkInfo();
369 PopulateSubnetsOrClearList(&interfaces);
370 return interfaces;
371 }
372
373 } // namespace openscreen
374