diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 16:49:04 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 16:49:04 +0000 |
commit | 16f504a9dca3fe3b70568f67b7d41241ae485288 (patch) | |
tree | c60f36ada0496ba928b7161059ba5ab1ab224f9d /src/VBox/Main/src-server/linux/NetIf-linux.cpp | |
parent | Initial commit. (diff) | |
download | virtualbox-16f504a9dca3fe3b70568f67b7d41241ae485288.tar.xz virtualbox-16f504a9dca3fe3b70568f67b7d41241ae485288.zip |
Adding upstream version 7.0.6-dfsg.upstream/7.0.6-dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/Main/src-server/linux/NetIf-linux.cpp')
-rw-r--r-- | src/VBox/Main/src-server/linux/NetIf-linux.cpp | 330 |
1 files changed, 330 insertions, 0 deletions
diff --git a/src/VBox/Main/src-server/linux/NetIf-linux.cpp b/src/VBox/Main/src-server/linux/NetIf-linux.cpp new file mode 100644 index 00000000..3d16f6ed --- /dev/null +++ b/src/VBox/Main/src-server/linux/NetIf-linux.cpp @@ -0,0 +1,330 @@ +/* $Id: NetIf-linux.cpp $ */ +/** @file + * Main - NetIfList, Linux implementation. + */ + +/* + * Copyright (C) 2008-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * SPDX-License-Identifier: GPL-3.0-only + */ + + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#define LOG_GROUP LOG_GROUP_MAIN_HOST + +#include <iprt/errcore.h> +#include <list> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <linux/wireless.h> +#include <net/if_arp.h> +#include <net/route.h> +#include <netinet/in.h> +#include <stdio.h> +#include <unistd.h> +#include <iprt/asm.h> + +#include "HostNetworkInterfaceImpl.h" +#include "netif.h" +#include "LoggingNew.h" + +/** + * Obtain the name of the interface used for default routing. + * + * NOTE: There is a copy in Devices/Network/testcase/tstIntNet-1.cpp. + * + * @returns VBox status code. + * + * @param pszName The buffer where to put the name. + * @param cbName Size of of the destination buffer. + */ +static int getDefaultIfaceName(char *pszName, size_t cbName) +{ + FILE *fp = fopen("/proc/net/route", "r"); + char szBuf[1024]; + char szIfName[17]; + uint32_t uAddr; + uint32_t uGateway; + uint32_t uMask; + int iTmp; + unsigned uFlags; + + if (fp) + { + while (fgets(szBuf, sizeof(szBuf)-1, fp)) + { + int n = sscanf(szBuf, "%16s %x %x %x %d %d %d %x %d %d %d\n", + szIfName, &uAddr, &uGateway, &uFlags, &iTmp, &iTmp, &iTmp, + &uMask, &iTmp, &iTmp, &iTmp); + if (n < 10 || !(uFlags & RTF_UP)) + continue; + + if (uAddr == 0 && uMask == 0) + { + fclose(fp); + szIfName[sizeof(szIfName) - 1] = '\0'; + return RTStrCopy(pszName, cbName, szIfName); + } + } + fclose(fp); + } + return VERR_INTERNAL_ERROR; +} + +static uint32_t getInterfaceSpeed(const char *pszName) +{ + /* + * I wish I could do simple ioctl here, but older kernels require root + * privileges for any ethtool commands. + */ + char szBuf[256]; + uint32_t uSpeed = 0; + /* First, we try to retrieve the speed via sysfs. */ + RTStrPrintf(szBuf, sizeof(szBuf), "/sys/class/net/%s/speed", pszName); + FILE *fp = fopen(szBuf, "r"); + if (fp) + { + if (fscanf(fp, "%u", &uSpeed) != 1) + uSpeed = 0; + fclose(fp); + } + if (uSpeed == 10) + { + /* Check the cable is plugged in at all */ + unsigned uCarrier = 0; + RTStrPrintf(szBuf, sizeof(szBuf), "/sys/class/net/%s/carrier", pszName); + fp = fopen(szBuf, "r"); + if (fp) + { + if (fscanf(fp, "%u", &uCarrier) != 1 || uCarrier == 0) + uSpeed = 0; + fclose(fp); + } + } + + if (uSpeed == 0) + { + /* Failed to get speed via sysfs, go to plan B. */ + int rc = NetIfAdpCtlOut(pszName, "speed", szBuf, sizeof(szBuf)); + if (RT_SUCCESS(rc)) + uSpeed = RTStrToUInt32(szBuf); + } + return uSpeed; +} + +static int getInterfaceInfo(int iSocket, const char *pszName, PNETIFINFO pInfo) +{ + // Zeroing out pInfo is a bad idea as it should contain both short and long names at + // this point. So make sure the structure is cleared by the caller if necessary! + // memset(pInfo, 0, sizeof(*pInfo)); + struct ifreq Req; + RT_ZERO(Req); + RTStrCopy(Req.ifr_name, sizeof(Req.ifr_name), pszName); + if (ioctl(iSocket, SIOCGIFHWADDR, &Req) >= 0) + { + switch (Req.ifr_hwaddr.sa_family) + { + case ARPHRD_ETHER: + pInfo->enmMediumType = NETIF_T_ETHERNET; + break; + default: + pInfo->enmMediumType = NETIF_T_UNKNOWN; + break; + } + /* Generate UUID from name and MAC address. */ + RTUUID uuid; + RTUuidClear(&uuid); + memcpy(&uuid, Req.ifr_name, RT_MIN(sizeof(Req.ifr_name), sizeof(uuid))); + uuid.Gen.u8ClockSeqHiAndReserved = (uint8_t)((uuid.Gen.u8ClockSeqHiAndReserved & 0x3f) | 0x80); + uuid.Gen.u16TimeHiAndVersion = (uint16_t)((uuid.Gen.u16TimeHiAndVersion & 0x0fff) | 0x4000); + memcpy(uuid.Gen.au8Node, &Req.ifr_hwaddr.sa_data, sizeof(uuid.Gen.au8Node)); + pInfo->Uuid = uuid; + + memcpy(&pInfo->MACAddress, Req.ifr_hwaddr.sa_data, sizeof(pInfo->MACAddress)); + + if (ioctl(iSocket, SIOCGIFADDR, &Req) >= 0) + memcpy(pInfo->IPAddress.au8, + &((struct sockaddr_in *)&Req.ifr_addr)->sin_addr.s_addr, + sizeof(pInfo->IPAddress.au8)); + + if (ioctl(iSocket, SIOCGIFNETMASK, &Req) >= 0) + memcpy(pInfo->IPNetMask.au8, + &((struct sockaddr_in *)&Req.ifr_addr)->sin_addr.s_addr, + sizeof(pInfo->IPNetMask.au8)); + + if (ioctl(iSocket, SIOCGIFFLAGS, &Req) >= 0) + pInfo->enmStatus = Req.ifr_flags & IFF_UP ? NETIF_S_UP : NETIF_S_DOWN; + + struct iwreq WRq; + RT_ZERO(WRq); + RTStrCopy(WRq.ifr_name, sizeof(WRq.ifr_name), pszName); + pInfo->fWireless = ioctl(iSocket, SIOCGIWNAME, &WRq) >= 0; + + FILE *fp = fopen("/proc/net/if_inet6", "r"); + if (fp) + { + RTNETADDRIPV6 IPv6Address; + unsigned uIndex, uLength, uScope, uTmp; + char szName[30]; + for (;;) + { + RT_ZERO(szName); + int n = fscanf(fp, + "%08x%08x%08x%08x" + " %02x %02x %02x %02x %20s\n", + &IPv6Address.au32[0], &IPv6Address.au32[1], + &IPv6Address.au32[2], &IPv6Address.au32[3], + &uIndex, &uLength, &uScope, &uTmp, szName); + if (n == EOF) + break; + if (n != 9 || uLength > 128) + { + Log(("getInterfaceInfo: Error while reading /proc/net/if_inet6, n=%d uLength=%u\n", + n, uLength)); + break; + } + if (!strcmp(Req.ifr_name, szName)) + { + pInfo->IPv6Address.au32[0] = htonl(IPv6Address.au32[0]); + pInfo->IPv6Address.au32[1] = htonl(IPv6Address.au32[1]); + pInfo->IPv6Address.au32[2] = htonl(IPv6Address.au32[2]); + pInfo->IPv6Address.au32[3] = htonl(IPv6Address.au32[3]); + RTNetPrefixToMaskIPv6(uLength, &pInfo->IPv6NetMask); + } + } + fclose(fp); + } + /* + * Don't even try to get speed for non-Ethernet interfaces, it only + * produces errors. + */ + if (pInfo->enmMediumType == NETIF_T_ETHERNET) + pInfo->uSpeedMbits = getInterfaceSpeed(pszName); + else + pInfo->uSpeedMbits = 0; + } + return VINF_SUCCESS; +} + +int NetIfList(std::list <ComObjPtr<HostNetworkInterface> > &list) +{ + char szDefaultIface[256]; + int rc = getDefaultIfaceName(szDefaultIface, sizeof(szDefaultIface)); + if (RT_FAILURE(rc)) + { + Log(("NetIfList: Failed to find default interface.\n")); + szDefaultIface[0] = '\0'; + } + int sock = socket(AF_INET, SOCK_DGRAM, 0); + if (sock >= 0) + { + FILE *fp = fopen("/proc/net/dev", "r"); + if (fp) + { + char buf[256]; + while (fgets(buf, sizeof(buf), fp)) + { + char *pszEndOfName = strchr(buf, ':'); + if (!pszEndOfName) + continue; + *pszEndOfName = 0; + size_t iFirstNonWS = strspn(buf, " "); + char *pszName = buf + iFirstNonWS; + NETIFINFO Info; + RT_ZERO(Info); + rc = getInterfaceInfo(sock, pszName, &Info); + if (RT_FAILURE(rc)) + break; + if (Info.enmMediumType == NETIF_T_ETHERNET) + { + ComObjPtr<HostNetworkInterface> IfObj; + IfObj.createObject(); + + HostNetworkInterfaceType_T enmType; + if (strncmp(pszName, RT_STR_TUPLE("vboxnet"))) + enmType = HostNetworkInterfaceType_Bridged; + else + enmType = HostNetworkInterfaceType_HostOnly; + + if (SUCCEEDED(IfObj->init(pszName, enmType, &Info))) + { + if (strcmp(pszName, szDefaultIface) == 0) + list.push_front(IfObj); + else + list.push_back(IfObj); + } + } + + } + fclose(fp); + } + close(sock); + } + else + rc = VERR_INTERNAL_ERROR; + + return rc; +} + +int NetIfGetConfigByName(PNETIFINFO pInfo) +{ + int rc = VINF_SUCCESS; + int sock = socket(AF_INET, SOCK_DGRAM, 0); + if (sock < 0) + return VERR_NOT_IMPLEMENTED; + rc = getInterfaceInfo(sock, pInfo->szShortName, pInfo); + close(sock); + return rc; +} + +/** + * Retrieve the physical link speed in megabits per second. If the interface is + * not up or otherwise unavailable the zero speed is returned. + * + * @returns VBox status code. + * + * @param pcszIfName Interface name. + * @param puMbits Where to store the link speed. + */ +int NetIfGetLinkSpeed(const char *pcszIfName, uint32_t *puMbits) +{ + int sock = socket(AF_INET, SOCK_DGRAM, 0); + if (sock < 0) + return VERR_OUT_OF_RESOURCES; + struct ifreq Req; + RT_ZERO(Req); + RTStrCopy(Req.ifr_name, sizeof(Req.ifr_name), pcszIfName); + if (ioctl(sock, SIOCGIFHWADDR, &Req) >= 0) + { + if (ioctl(sock, SIOCGIFFLAGS, &Req) >= 0) + if (Req.ifr_flags & IFF_UP) + { + close(sock); + *puMbits = getInterfaceSpeed(pcszIfName); + return VINF_SUCCESS; + } + } + close(sock); + *puMbits = 0; + return VWRN_NOT_FOUND; +} |