VirtualBox

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

Last change on this file since 78273 was 77436, checked in by vboxsync, 6 years ago

Main: Eradicate the use of BSTR in regular API code, there were leaks in almost every occurrence. Also do some Bstr->Utf8Str shuffling to reduce the number of conversions.

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