xref: /aosp_15_r20/external/openscreen/platform/impl/network_interface_linux.cc (revision 3f982cf4871df8771c9d4abe6e9a6f8d829b2736)
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