VirtualBox

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

Last change on this file since 48366 was 48180, checked in by vboxsync, 11 years ago

Devices/Network/tstIntNet-1: Use the default route interface for testing on Linux + minor updates in route table parsing in NetIf-linux.cpp

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