VirtualBox

source: vbox/trunk/src/VBox/Main/src-server/linux/NetIf-linux.cpp

Last change on this file was 108852, checked in by vboxsync, 7 weeks ago

Main: Linux: Improve check if given network interface has wireless capability (scm fix), bugref:8915, ticketref:22362.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 12.6 KB
Line 
1/* $Id: NetIf-linux.cpp 108852 2025-04-04 14:01:52Z vboxsync $ */
2/** @file
3 * Main - NetIfList, Linux implementation.
4 */
5
6/*
7 * Copyright (C) 2008-2024 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.215389.xyz.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28
29
30/*********************************************************************************************************************************
31* Header Files *
32*********************************************************************************************************************************/
33#define LOG_GROUP LOG_GROUP_MAIN_HOST
34
35#include <iprt/linux/sysfs.h>
36#include <iprt/file.h>
37#include <iprt/errcore.h>
38#include <list>
39#include <sys/ioctl.h>
40#include <sys/socket.h>
41#include <linux/wireless.h>
42#include <net/if_arp.h>
43#include <net/route.h>
44#include <netinet/in.h>
45#include <stdio.h>
46#include <unistd.h>
47#include <iprt/asm.h>
48#include <errno.h>
49
50#include "HostNetworkInterfaceImpl.h"
51#include "netif.h"
52#include "LoggingNew.h"
53
54/**
55 * Obtain the name of the interface used for default routing.
56 *
57 * NOTE: There is a copy in Devices/Network/testcase/tstIntNet-1.cpp.
58 *
59 * @returns VBox status code.
60 *
61 * @param pszName The buffer where to put the name.
62 * @param cbName Size of of the destination buffer.
63 */
64static int getDefaultIfaceName(char *pszName, size_t cbName)
65{
66 FILE *fp = fopen("/proc/net/route", "r");
67 char szBuf[1024];
68 char szIfName[17];
69 uint32_t uAddr;
70 uint32_t uGateway;
71 uint32_t uMask;
72 int iTmp;
73 unsigned uFlags;
74
75 if (fp)
76 {
77 while (fgets(szBuf, sizeof(szBuf)-1, fp))
78 {
79 int n = sscanf(szBuf, "%16s %x %x %x %d %d %d %x %d %d %d\n",
80 szIfName, &uAddr, &uGateway, &uFlags, &iTmp, &iTmp, &iTmp,
81 &uMask, &iTmp, &iTmp, &iTmp);
82 if (n < 10 || !(uFlags & RTF_UP))
83 continue;
84
85 if (uAddr == 0 && uMask == 0)
86 {
87 fclose(fp);
88 szIfName[sizeof(szIfName) - 1] = '\0';
89 return RTStrCopy(pszName, cbName, szIfName);
90 }
91 }
92 fclose(fp);
93 }
94 return VERR_INTERNAL_ERROR;
95}
96
97static uint32_t getInterfaceSpeed(const char *pszName)
98{
99 /*
100 * I wish I could do simple ioctl here, but older kernels require root
101 * privileges for any ethtool commands.
102 */
103 char szBuf[256];
104 uint32_t uSpeed = 0;
105 /* First, we try to retrieve the speed via sysfs. */
106 RTStrPrintf(szBuf, sizeof(szBuf), "/sys/class/net/%s/speed", pszName);
107 FILE *fp = fopen(szBuf, "r");
108 if (fp)
109 {
110 if (fscanf(fp, "%u", &uSpeed) != 1)
111 uSpeed = 0;
112 fclose(fp);
113 }
114 if (uSpeed == 10)
115 {
116 /* Check the cable is plugged in at all */
117 unsigned uCarrier = 0;
118 RTStrPrintf(szBuf, sizeof(szBuf), "/sys/class/net/%s/carrier", pszName);
119 fp = fopen(szBuf, "r");
120 if (fp)
121 {
122 if (fscanf(fp, "%u", &uCarrier) != 1 || uCarrier == 0)
123 uSpeed = 0;
124 fclose(fp);
125 }
126 }
127
128 if (uSpeed == 0)
129 {
130 /* Failed to get speed via sysfs, go to plan B. */
131 int vrc = NetIfAdpCtlOut(pszName, "speed", szBuf, sizeof(szBuf));
132 if (RT_SUCCESS(vrc))
133 uSpeed = RTStrToUInt32(szBuf);
134 }
135 return uSpeed;
136}
137
138static int getInterfaceInfo(int iSocket, const char *pszName, PNETIFINFO pInfo)
139{
140 // Zeroing out pInfo is a bad idea as it should contain both short and long names at
141 // this point. So make sure the structure is cleared by the caller if necessary!
142 // memset(pInfo, 0, sizeof(*pInfo));
143 struct ifreq Req;
144 RT_ZERO(Req);
145 RTStrCopy(Req.ifr_name, sizeof(Req.ifr_name), pszName);
146 if (ioctl(iSocket, SIOCGIFHWADDR, &Req) >= 0)
147 {
148 switch (Req.ifr_hwaddr.sa_family)
149 {
150 case ARPHRD_ETHER:
151 pInfo->enmMediumType = NETIF_T_ETHERNET;
152 break;
153 default:
154 pInfo->enmMediumType = NETIF_T_UNKNOWN;
155 break;
156 }
157 /* Generate UUID from name and MAC address. */
158 RTUUID uuid;
159 RTUuidClear(&uuid);
160#ifdef VBOXNETFLT_LINUX_NAMESPACE_SUPPORT
161 uuid.au32[0] = 0; /* Use 0 as the indicator of missing namespace info. */
162 /*
163 * Namespace links use the following naming convention: "net:[1234567890]".
164 * The maximum value of inode number is 4294967295, which gives up precisely
165 * 16 characters without terminating zero.
166 */
167 char szBuf[24];
168 ssize_t len = readlink("/proc/self/ns/net", szBuf, sizeof(szBuf) - 1);
169 if (len == -1)
170 Log(("NetIfList: Failed to get namespace for VBoxSVC, error %d\n", errno));
171 else if (!RTStrStartsWith(szBuf, "net:["))
172 Log(("NetIfList: Failed to get network namespace inode from %s\n", szBuf));
173 else
174 uuid.au32[0] = RTStrToUInt32(szBuf + 5);
175 Log(("NetIfList: VBoxSVC namespace inode %u\n", uuid.au32[0]));
176 /* Hashing the name is probably an overkill as MAC addresses should ensure uniqueness */
177 uuid.au32[1] = RTStrHash1(pszName);
178#else /* !VBOXNETFLT_LINUX_NAMESPACE_SUPPORT */
179 memcpy(&uuid, Req.ifr_name, RT_MIN(sizeof(Req.ifr_name), sizeof(uuid)));
180#endif /* !VBOXNETFLT_LINUX_NAMESPACE_SUPPORT */
181 uuid.Gen.u8ClockSeqHiAndReserved = (uint8_t)((uuid.Gen.u8ClockSeqHiAndReserved & 0x3f) | 0x80);
182 uuid.Gen.u16TimeHiAndVersion = (uint16_t)((uuid.Gen.u16TimeHiAndVersion & 0x0fff) | 0x4000);
183 memcpy(uuid.Gen.au8Node, &Req.ifr_hwaddr.sa_data, sizeof(uuid.Gen.au8Node));
184 pInfo->Uuid = uuid;
185
186 memcpy(&pInfo->MACAddress, Req.ifr_hwaddr.sa_data, sizeof(pInfo->MACAddress));
187
188 if (ioctl(iSocket, SIOCGIFADDR, &Req) >= 0)
189 memcpy(pInfo->IPAddress.au8,
190 &((struct sockaddr_in *)&Req.ifr_addr)->sin_addr.s_addr,
191 sizeof(pInfo->IPAddress.au8));
192
193 if (ioctl(iSocket, SIOCGIFNETMASK, &Req) >= 0)
194 memcpy(pInfo->IPNetMask.au8,
195 &((struct sockaddr_in *)&Req.ifr_addr)->sin_addr.s_addr,
196 sizeof(pInfo->IPNetMask.au8));
197
198 if (ioctl(iSocket, SIOCGIFFLAGS, &Req) >= 0)
199 pInfo->enmStatus = Req.ifr_flags & IFF_UP ? NETIF_S_UP : NETIF_S_DOWN;
200
201 /* Detect if given interfece is wireless, assume 'no' by default. */
202 pInfo->fWireless = false;
203
204 RTFILE hFile;
205 int vrc = RTLinuxSysFsOpen(&hFile, "class/net/%s/uevent", pszName);
206 if (RT_SUCCESS(vrc))
207 {
208 char szSysFsBuf[256];
209 size_t cchRead = 0;
210
211 RT_ZERO(szBuf);
212 vrc = RTLinuxSysFsReadStr(hFile, szSysFsBuf, sizeof(szSysFsBuf) - 1, &cchRead);
213 if (RT_SUCCESS(vrc))
214 pInfo->fWireless = RTStrStr(szSysFsBuf, "DEVTYPE=wlan") != NULL;
215
216 RTFileClose(hFile);
217 }
218
219 /* Fallback to legacy way of checking if iface is wireless. */
220 if (!pInfo->fWireless)
221 {
222 struct iwreq WRq;
223 RT_ZERO(WRq);
224 RTStrCopy(WRq.ifr_name, sizeof(WRq.ifr_name), pszName);
225 pInfo->fWireless = ioctl(iSocket, SIOCGIWNAME, &WRq) >= 0;
226 }
227
228 FILE *fp = fopen("/proc/net/if_inet6", "r");
229 if (fp)
230 {
231 RTNETADDRIPV6 IPv6Address;
232 unsigned uIndex, uLength, uScope, uTmp;
233 char szName[30];
234 for (;;)
235 {
236 RT_ZERO(szName);
237 int n = fscanf(fp,
238 "%08x%08x%08x%08x"
239 " %02x %02x %02x %02x %20s\n",
240 &IPv6Address.au32[0], &IPv6Address.au32[1],
241 &IPv6Address.au32[2], &IPv6Address.au32[3],
242 &uIndex, &uLength, &uScope, &uTmp, szName);
243 if (n == EOF)
244 break;
245 if (n != 9 || uLength > 128)
246 {
247 Log(("getInterfaceInfo: Error while reading /proc/net/if_inet6, n=%d uLength=%u\n",
248 n, uLength));
249 break;
250 }
251 if (!strcmp(Req.ifr_name, szName))
252 {
253 pInfo->IPv6Address.au32[0] = htonl(IPv6Address.au32[0]);
254 pInfo->IPv6Address.au32[1] = htonl(IPv6Address.au32[1]);
255 pInfo->IPv6Address.au32[2] = htonl(IPv6Address.au32[2]);
256 pInfo->IPv6Address.au32[3] = htonl(IPv6Address.au32[3]);
257 RTNetPrefixToMaskIPv6((int)uLength, &pInfo->IPv6NetMask);
258 }
259 }
260 fclose(fp);
261 }
262 /*
263 * Don't even try to get speed for non-Ethernet interfaces, it only
264 * produces errors.
265 */
266 if (pInfo->enmMediumType == NETIF_T_ETHERNET)
267 pInfo->uSpeedMbits = getInterfaceSpeed(pszName);
268 else
269 pInfo->uSpeedMbits = 0;
270 }
271 return VINF_SUCCESS;
272}
273
274int NetIfList(std::list <ComObjPtr<HostNetworkInterface> > &list)
275{
276 char szDefaultIface[256];
277 int vrc = getDefaultIfaceName(szDefaultIface, sizeof(szDefaultIface));
278 if (RT_FAILURE(vrc))
279 {
280 Log(("NetIfList: Failed to find default interface.\n"));
281 szDefaultIface[0] = '\0';
282 }
283 int sock = socket(AF_INET, SOCK_DGRAM, 0);
284 if (sock >= 0)
285 {
286 FILE *fp = fopen("/proc/net/dev", "r");
287 if (fp)
288 {
289 char buf[256];
290 while (fgets(buf, sizeof(buf), fp))
291 {
292 char *pszEndOfName = strchr(buf, ':');
293 if (!pszEndOfName)
294 continue;
295 *pszEndOfName = 0;
296 size_t iFirstNonWS = strspn(buf, " ");
297 char *pszName = buf + iFirstNonWS;
298 NETIFINFO Info;
299 RT_ZERO(Info);
300 vrc = getInterfaceInfo(sock, pszName, &Info);
301 if (RT_FAILURE(vrc))
302 break;
303 if (Info.enmMediumType == NETIF_T_ETHERNET)
304 {
305 ComObjPtr<HostNetworkInterface> IfObj;
306 IfObj.createObject();
307
308 HostNetworkInterfaceType_T enmType;
309 if (strncmp(pszName, RT_STR_TUPLE("vboxnet")))
310 enmType = HostNetworkInterfaceType_Bridged;
311 else
312 enmType = HostNetworkInterfaceType_HostOnly;
313
314 if (SUCCEEDED(IfObj->init(pszName, enmType, &Info)))
315 {
316 if (strcmp(pszName, szDefaultIface) == 0)
317 list.push_front(IfObj);
318 else
319 list.push_back(IfObj);
320 }
321 }
322
323 }
324 fclose(fp);
325 }
326 close(sock);
327 }
328 else
329 vrc = VERR_INTERNAL_ERROR;
330
331 return vrc;
332}
333
334int NetIfGetConfigByName(PNETIFINFO pInfo)
335{
336 int sock = socket(AF_INET, SOCK_DGRAM, 0);
337 if (sock < 0)
338 return VERR_NOT_IMPLEMENTED;
339 int vrc = getInterfaceInfo(sock, pInfo->szShortName, pInfo);
340 close(sock);
341 return vrc;
342}
343
344/**
345 * Retrieve the physical link speed in megabits per second. If the interface is
346 * not up or otherwise unavailable the zero speed is returned.
347 *
348 * @returns VBox status code.
349 *
350 * @param pcszIfName Interface name.
351 * @param puMbits Where to store the link speed.
352 */
353int NetIfGetLinkSpeed(const char *pcszIfName, uint32_t *puMbits)
354{
355 int sock = socket(AF_INET, SOCK_DGRAM, 0);
356 if (sock < 0)
357 return VERR_OUT_OF_RESOURCES;
358 struct ifreq Req;
359 RT_ZERO(Req);
360 RTStrCopy(Req.ifr_name, sizeof(Req.ifr_name), pcszIfName);
361 if (ioctl(sock, SIOCGIFHWADDR, &Req) >= 0)
362 {
363 if (ioctl(sock, SIOCGIFFLAGS, &Req) >= 0)
364 if (Req.ifr_flags & IFF_UP)
365 {
366 close(sock);
367 *puMbits = getInterfaceSpeed(pcszIfName);
368 return VINF_SUCCESS;
369 }
370 }
371 close(sock);
372 *puMbits = 0;
373 return VWRN_NOT_FOUND;
374}
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette