VirtualBox

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

Last change on this file was 106061, checked in by vboxsync, 3 months ago

Copyright year updates by scm.

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

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