VirtualBox

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

Last change on this file was 98288, checked in by vboxsync, 16 months ago

Main/src-server: rc -> hrc/vrc (partial). bugref:10223

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

© 2023 Oracle
ContactPrivacy policyTerms of Use