diff options
Diffstat (limited to 'src/VBox/HostDrivers/adpctl')
-rw-r--r-- | src/VBox/HostDrivers/adpctl/Makefile.kmk | 29 | ||||
-rw-r--r-- | src/VBox/HostDrivers/adpctl/VBoxNetAdpCtl.cpp | 807 |
2 files changed, 836 insertions, 0 deletions
diff --git a/src/VBox/HostDrivers/adpctl/Makefile.kmk b/src/VBox/HostDrivers/adpctl/Makefile.kmk new file mode 100644 index 00000000..ce0d57ab --- /dev/null +++ b/src/VBox/HostDrivers/adpctl/Makefile.kmk @@ -0,0 +1,29 @@ +# $Id: Makefile.kmk $ +## @file +# Sub-Makefile for VBoxAdpCtl +# +# VBoxAdpCtl is a tool for configuring vboxnetX adapters. +# + +# +# Copyright (C) 2009-2019 Oracle Corporation +# +# This file is part of VirtualBox Open Source Edition (OSE), as +# available from http://www.virtualbox.org. This file is free software; +# you can redistribute it and/or modify it under the terms of the GNU +# General Public License (GPL) as published by the Free Software +# Foundation, in version 2 as it comes in the "COPYING" file of the +# VirtualBox OSE distribution. VirtualBox OSE is distributed in the +# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. +# + +SUB_DEPTH = ../../../.. +include $(KBUILD_PATH)/subheader.kmk + +PROGRAMS += VBoxNetAdpCtl +### Another template? We must *not* set RPATH! +VBoxNetAdpCtl_TEMPLATE = VBOXR3HARDENEDEXE +VBoxNetAdpCtl_SOURCES = VBoxNetAdpCtl.cpp + +include $(FILE_KBUILD_SUB_FOOTER) + diff --git a/src/VBox/HostDrivers/adpctl/VBoxNetAdpCtl.cpp b/src/VBox/HostDrivers/adpctl/VBoxNetAdpCtl.cpp new file mode 100644 index 00000000..d9d7e66a --- /dev/null +++ b/src/VBox/HostDrivers/adpctl/VBoxNetAdpCtl.cpp @@ -0,0 +1,807 @@ +/* $Id: VBoxNetAdpCtl.cpp $ */ +/** @file + * Apps - VBoxAdpCtl, Configuration tool for vboxnetX adapters. + */ + +/* + * Copyright (C) 2009-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include <list> +#include <errno.h> +#include <stdio.h> +#include <stdarg.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/wait.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <fcntl.h> +#ifdef RT_OS_LINUX +# include <arpa/inet.h> +# include <net/if.h> +# include <linux/types.h> +/* Older versions of ethtool.h rely on these: */ +typedef unsigned long long u64; +typedef __uint32_t u32; +typedef __uint16_t u16; +typedef __uint8_t u8; +# include <limits.h> /* for INT_MAX */ +# include <linux/ethtool.h> +# include <linux/sockios.h> +#endif +#ifdef RT_OS_SOLARIS +# include <sys/ioccom.h> +#endif + +#define NOREF(x) (void)x + +/** @todo Error codes must be moved to some header file */ +#define ADPCTLERR_BAD_NAME 2 +#define ADPCTLERR_NO_CTL_DEV 3 +#define ADPCTLERR_IOCTL_FAILED 4 +#define ADPCTLERR_SOCKET_FAILED 5 + +/** @todo These are duplicates from src/VBox/HostDrivers/VBoxNetAdp/VBoxNetAdpInternal.h */ +#define VBOXNETADP_CTL_DEV_NAME "/dev/vboxnetctl" +#define VBOXNETADP_MAX_INSTANCES 128 +#define VBOXNETADP_NAME "vboxnet" +#define VBOXNETADP_MAX_NAME_LEN 32 +#define VBOXNETADP_CTL_ADD _IOWR('v', 1, VBOXNETADPREQ) +#define VBOXNETADP_CTL_REMOVE _IOW('v', 2, VBOXNETADPREQ) +typedef struct VBoxNetAdpReq +{ + char szName[VBOXNETADP_MAX_NAME_LEN]; +} VBOXNETADPREQ; +typedef VBOXNETADPREQ *PVBOXNETADPREQ; + +#define VBOXADPCTL_IFCONFIG_PATH1 "/sbin/ifconfig" +#define VBOXADPCTL_IFCONFIG_PATH2 "/bin/ifconfig" + + +static void showUsage(void) +{ + fprintf(stderr, "Usage: VBoxNetAdpCtl <adapter> <address> ([netmask <address>] | remove)\n"); + fprintf(stderr, " | VBoxNetAdpCtl [<adapter>] add\n"); + fprintf(stderr, " | VBoxNetAdpCtl <adapter> remove\n"); +} + + +/* + * A wrapper on standard list that provides '<<' operator for adding several list members in a single + * line dynamically. For example: "CmdList(arg1) << arg2 << arg3" produces a list with three members. + */ +class CmdList +{ +public: + /** Creates an empty list. */ + CmdList() {}; + /** Creates a list with a single member. */ + CmdList(const char *pcszCommand) { m_list.push_back(pcszCommand); }; + /** Provides access to the underlying standard list. */ + const std::list<const char *>& getList(void) const { return m_list; }; + /** Adds a member to the list. */ + CmdList& operator<<(const char *pcszArgument); +private: + std::list<const char *>m_list; +}; + +CmdList& CmdList::operator<<(const char *pcszArgument) +{ + m_list.push_back(pcszArgument); + return *this; +} + +/** Simple helper to distinguish IPv4 and IPv6 addresses. */ +inline bool isAddrV6(const char *pcszAddress) +{ + return !!(strchr(pcszAddress, ':')); +} + + +/********************************************************************************************************************************* +* Generic address commands. * +*********************************************************************************************************************************/ + +/** + * The base class for all address manipulation commands. While being an abstract class, + * it provides a generic implementation of 'set' and 'remove' methods, which rely on + * pure virtual methods like 'addV4' and 'removeV4' to perform actual command execution. + */ +class AddressCommand +{ +public: + AddressCommand() : m_pszPath(0) {}; + virtual ~AddressCommand() {}; + + /** returns true if underlying command (executable) is present in the system. */ + bool isAvailable(void) + { struct stat s; return (!stat(m_pszPath, &s) && S_ISREG(s.st_mode)); }; + + /* + * Someday we may want to support several IP addresses per adapter, but for now we + * have 'set' method only, which replaces all addresses with the one specifed. + * + * virtual int add(const char *pcszAdapter, const char *pcszAddress, const char *pcszNetmask = 0) = 0; + */ + /** replace existing address(es) */ + virtual int set(const char *pcszAdapter, const char *pcszAddress, const char *pcszNetmask = 0); + /** remove address */ + virtual int remove(const char *pcszAdapter, const char *pcszAddress); +protected: + /** IPv4-specific handler used by generic implementation of 'set' method if 'setV4' is not supported. */ + virtual int addV4(const char *pcszAdapter, const char *pcszAddress, const char *pcszNetmask = 0) = 0; + /** IPv6-specific handler used by generic implementation of 'set' method. */ + virtual int addV6(const char *pcszAdapter, const char *pcszAddress, const char *pcszNetmask = 0) = 0; + /** IPv4-specific handler used by generic implementation of 'set' method. */ + virtual int setV4(const char *pcszAdapter, const char *pcszAddress, const char *pcszNetmask = 0) = 0; + /** IPv4-specific handler used by generic implementation of 'remove' method. */ + virtual int removeV4(const char *pcszAdapter, const char *pcszAddress) = 0; + /** IPv6-specific handler used by generic implementation of 'remove' method. */ + virtual int removeV6(const char *pcszAdapter, const char *pcszAddress) = 0; + /** Composes the argument list of command that obtains all addresses assigned to the adapter. */ + virtual CmdList getShowCommand(const char *pcszAdapter) const = 0; + + /** Prepares an array of C strings needed for 'exec' call. */ + char * const * allocArgv(const CmdList& commandList); + /** Hides process creation details. To be used in derived classes. */ + int execute(CmdList& commandList); + + /** A path to executable command. */ + const char *m_pszPath; +private: + /** Removes all previously asssigned addresses of a particular protocol family. */ + int removeAddresses(const char *pcszAdapter, const char *pcszFamily); +}; + +/* + * A generic implementation of 'ifconfig' command for all platforms. + */ +class CmdIfconfig : public AddressCommand +{ +public: + CmdIfconfig() + { + struct stat s; + if ( !stat(VBOXADPCTL_IFCONFIG_PATH1, &s) + && S_ISREG(s.st_mode)) + m_pszPath = (char*)VBOXADPCTL_IFCONFIG_PATH1; + else + m_pszPath = (char*)VBOXADPCTL_IFCONFIG_PATH2; + }; + +protected: + /** Returns platform-specific subcommand to add an address. */ + virtual const char *addCmdArg(void) const = 0; + /** Returns platform-specific subcommand to remove an address. */ + virtual const char *delCmdArg(void) const = 0; + virtual CmdList getShowCommand(const char *pcszAdapter) const + { return CmdList(pcszAdapter); }; + virtual int addV4(const char *pcszAdapter, const char *pcszAddress, const char *pcszNetmask = 0) + { return ENOTSUP; NOREF(pcszAdapter); NOREF(pcszAddress); NOREF(pcszNetmask); }; + virtual int addV6(const char *pcszAdapter, const char *pcszAddress, const char *pcszNetmask = 0) + { + return execute(CmdList(pcszAdapter) << "inet6" << addCmdArg() << pcszAddress); + NOREF(pcszNetmask); + }; + virtual int setV4(const char *pcszAdapter, const char *pcszAddress, const char *pcszNetmask = 0) + { + if (!pcszNetmask) + return execute(CmdList(pcszAdapter) << pcszAddress); + return execute(CmdList(pcszAdapter) << pcszAddress << "netmask" << pcszNetmask); + }; + virtual int removeV4(const char *pcszAdapter, const char *pcszAddress) + { return execute(CmdList(pcszAdapter) << delCmdArg() << pcszAddress); }; + virtual int removeV6(const char *pcszAdapter, const char *pcszAddress) + { return execute(CmdList(pcszAdapter) << "inet6" << delCmdArg() << pcszAddress); }; +}; + + +/********************************************************************************************************************************* +* Platform-specific commands * +*********************************************************************************************************************************/ + +class CmdIfconfigLinux : public CmdIfconfig +{ +protected: + virtual int removeV4(const char *pcszAdapter, const char *pcszAddress) + { return execute(CmdList(pcszAdapter) << "0.0.0.0"); NOREF(pcszAddress); }; + virtual const char *addCmdArg(void) const { return "add"; }; + virtual const char *delCmdArg(void) const { return "del"; }; +}; + +class CmdIfconfigDarwin : public CmdIfconfig +{ +protected: + virtual const char *addCmdArg(void) const { return "add"; }; + virtual const char *delCmdArg(void) const { return "delete"; }; +}; + +class CmdIfconfigSolaris : public CmdIfconfig +{ +public: + virtual int set(const char *pcszAdapter, const char *pcszAddress, const char *pcszNetmask = 0) + { + const char *pcszFamily = isAddrV6(pcszAddress) ? "inet6" : "inet"; + if (execute(CmdList(pcszAdapter) << pcszFamily)) + execute(CmdList(pcszAdapter) << pcszFamily << "plumb" << "up"); + return CmdIfconfig::set(pcszAdapter, pcszAddress, pcszNetmask); + }; +protected: + /* We can umplumb IPv4 interfaces only! */ + virtual int removeV4(const char *pcszAdapter, const char *pcszAddress) + { + int rc = CmdIfconfig::removeV4(pcszAdapter, pcszAddress); + execute(CmdList(pcszAdapter) << "inet" << "unplumb"); + return rc; + }; + virtual const char *addCmdArg(void) const { return "addif"; }; + virtual const char *delCmdArg(void) const { return "removeif"; }; +}; + + +#ifdef RT_OS_LINUX +/* + * Helper class to incapsulate IPv4 address conversion. + */ +class AddressIPv4 +{ +public: + AddressIPv4(const char *pcszAddress, const char *pcszNetmask = 0) + { + if (pcszNetmask) + m_Prefix = maskToPrefix(pcszNetmask); + else + { + /* + * Since guessing network mask is probably futile we simply use 24, + * as it matches our defaults. When non-default values are used + * providing a proper netmask is up to the user. + */ + m_Prefix = 24; + } + inet_pton(AF_INET, pcszAddress, &(m_Address.sin_addr)); + snprintf(m_szAddressAndMask, sizeof(m_szAddressAndMask), "%s/%d", pcszAddress, m_Prefix); + m_Broadcast.sin_addr.s_addr = computeBroadcast(m_Address.sin_addr.s_addr, m_Prefix); + inet_ntop(AF_INET, &(m_Broadcast.sin_addr), m_szBroadcast, sizeof(m_szBroadcast)); + } + const char *getBroadcast() const { return m_szBroadcast; }; + const char *getAddressAndMask() const { return m_szAddressAndMask; }; +private: + unsigned int maskToPrefix(const char *pcszNetmask); + unsigned long computeBroadcast(unsigned long ulAddress, unsigned int uPrefix); + + unsigned int m_Prefix; + struct sockaddr_in m_Address; + struct sockaddr_in m_Broadcast; + char m_szAddressAndMask[INET_ADDRSTRLEN + 3]; /* e.g. 192.168.56.101/24 */ + char m_szBroadcast[INET_ADDRSTRLEN]; +}; + +unsigned int AddressIPv4::maskToPrefix(const char *pcszNetmask) +{ + unsigned cBits = 0; + unsigned m[4]; + + if (sscanf(pcszNetmask, "%u.%u.%u.%u", &m[0], &m[1], &m[2], &m[3]) == 4) + { + for (int i = 0; i < 4 && m[i]; ++i) + { + int mask = m[i]; + while (mask & 0x80) + { + cBits++; + mask <<= 1; + } + } + } + return cBits; +} + +unsigned long AddressIPv4::computeBroadcast(unsigned long ulAddress, unsigned int uPrefix) +{ + /* Note: the address is big-endian. */ + unsigned long ulNetworkMask = (1l << uPrefix) - 1; + return (ulAddress & ulNetworkMask) | ~ulNetworkMask; +} + + +/* + * Linux-specific implementation of 'ip' command, as other platforms do not support it. + */ +class CmdIpLinux : public AddressCommand +{ +public: + CmdIpLinux() { m_pszPath = "/sbin/ip"; }; + /** + * IPv4 and IPv6 syntax is the same, so we override `remove` instead of implementing + * family-specific commands. It would be easier to use the same body in both + * 'removeV4' and 'removeV6', so we override 'remove' to illustrate how to do common + * implementation. + */ + virtual int remove(const char *pcszAdapter, const char *pcszAddress) + { return execute(CmdList("addr") << "del" << pcszAddress << "dev" << pcszAdapter); }; +protected: + virtual int addV4(const char *pcszAdapter, const char *pcszAddress, const char *pcszNetmask = 0) + { + AddressIPv4 addr(pcszAddress, pcszNetmask); + bringUp(pcszAdapter); + return execute(CmdList("addr") << "add" << addr.getAddressAndMask() << + "broadcast" << addr.getBroadcast() << "dev" << pcszAdapter); + }; + virtual int addV6(const char *pcszAdapter, const char *pcszAddress, const char *pcszNetmask = 0) + { + bringUp(pcszAdapter); + return execute(CmdList("addr") << "add" << pcszAddress << "dev" << pcszAdapter); + NOREF(pcszNetmask); + }; + /** + * Our command does not support 'replacing' addresses. Reporting this fact to generic implementation + * of 'set' causes it to remove all assigned addresses, then 'add' the new one. + */ + virtual int setV4(const char *pcszAdapter, const char *pcszAddress, const char *pcszNetmask = 0) + { return ENOTSUP; NOREF(pcszAdapter); NOREF(pcszAddress); NOREF(pcszNetmask); }; + /** We use family-agnostic command syntax. See 'remove' above. */ + virtual int removeV4(const char *pcszAdapter, const char *pcszAddress) + { return ENOTSUP; NOREF(pcszAdapter); NOREF(pcszAddress); }; + /** We use family-agnostic command syntax. See 'remove' above. */ + virtual int removeV6(const char *pcszAdapter, const char *pcszAddress) + { return ENOTSUP; NOREF(pcszAdapter); NOREF(pcszAddress); }; + virtual CmdList getShowCommand(const char *pcszAdapter) const + { return CmdList("addr") << "show" << "dev" << pcszAdapter; }; +private: + /** Brings up the adapter */ + void bringUp(const char *pcszAdapter) + { execute(CmdList("link") << "set" << "dev" << pcszAdapter << "up"); }; +}; +#endif /* RT_OS_LINUX */ + + +/********************************************************************************************************************************* +* Generic address command implementations * +*********************************************************************************************************************************/ + +int AddressCommand::set(const char *pcszAdapter, const char *pcszAddress, const char *pcszNetmask) +{ + if (isAddrV6(pcszAddress)) + { + removeAddresses(pcszAdapter, "inet6"); + return addV6(pcszAdapter, pcszAddress, pcszNetmask); + } + int rc = setV4(pcszAdapter, pcszAddress, pcszNetmask); + if (rc == ENOTSUP) + { + removeAddresses(pcszAdapter, "inet"); + rc = addV4(pcszAdapter, pcszAddress, pcszNetmask); + } + return rc; +} + +int AddressCommand::remove(const char *pcszAdapter, const char *pcszAddress) +{ + if (isAddrV6(pcszAddress)) + return removeV6(pcszAdapter, pcszAddress); + return removeV4(pcszAdapter, pcszAddress); +} + +/* + * Allocate an array of exec arguments. In addition to arguments provided + * we need to include the full path to the executable as well as "terminating" + * null pointer marking the end of the array. + */ +char * const * AddressCommand::allocArgv(const CmdList& list) +{ + int i = 0; + std::list<const char *>::const_iterator it; + const char **argv = (const char **)calloc(list.getList().size() + 2, sizeof(const char *)); + if (argv) + { + argv[i++] = m_pszPath; + for (it = list.getList().begin(); it != list.getList().end(); ++it) + argv[i++] = *it; + argv[i++] = NULL; + } + return (char * const*)argv; +} + +int AddressCommand::execute(CmdList& list) +{ + char * const pEnv[] = { (char*)"LC_ALL=C", NULL }; + char * const* argv = allocArgv(list); + if (argv == NULL) + return EXIT_FAILURE; + + int rc = EXIT_SUCCESS; + pid_t childPid = fork(); + switch (childPid) + { + case -1: /* Something went wrong. */ + perror("fork() failed"); + rc = EXIT_FAILURE; + break; + case 0: /* Child process. */ + if (execve(argv[0], argv, pEnv) == -1) + rc = EXIT_FAILURE; + break; + default: /* Parent process. */ + waitpid(childPid, &rc, 0); + break; + } + + free((void*)argv); + return rc; +} + +#define MAX_ADDRESSES 128 +#define MAX_ADDRLEN 64 + +int AddressCommand::removeAddresses(const char *pcszAdapter, const char *pcszFamily) +{ + char szBuf[1024]; + char aszAddresses[MAX_ADDRESSES][MAX_ADDRLEN]; + int rc = EXIT_SUCCESS; + int fds[2]; + char * const * argv = allocArgv(getShowCommand(pcszAdapter)); + char * const envp[] = { (char*)"LC_ALL=C", NULL }; + + memset(aszAddresses, 0, sizeof(aszAddresses)); + + rc = pipe(fds); + if (rc < 0) + return errno; + + pid_t pid = fork(); + if (pid < 0) + return errno; + + if (pid == 0) + { + /* child */ + close(fds[0]); + close(STDOUT_FILENO); + rc = dup2(fds[1], STDOUT_FILENO); + if (rc >= 0) + if (execve(argv[0], argv, envp) == -1) + return errno; + return rc; + } + + /* parent */ + close(fds[1]); + FILE *fp = fdopen(fds[0], "r"); + if (!fp) + return false; + + int cAddrs; + for (cAddrs = 0; cAddrs < MAX_ADDRESSES && fgets(szBuf, sizeof(szBuf), fp);) + { + int cbSkipWS = strspn(szBuf, " \t"); + char *pszWord = strtok(szBuf + cbSkipWS, " "); + /* We are concerned with particular family address lines only. */ + if (!pszWord || strcmp(pszWord, pcszFamily)) + continue; + + pszWord = strtok(NULL, " "); + + /* Skip "addr:" word if present. */ + if (pszWord && !strcmp(pszWord, "addr:")) + pszWord = strtok(NULL, " "); + + /* Skip link-local address lines. */ + if (!pszWord || !strncmp(pszWord, "fe80", 4)) + continue; + strncpy(aszAddresses[cAddrs++], pszWord, MAX_ADDRLEN-1); + } + fclose(fp); + + for (int i = 0; i < cAddrs && rc == EXIT_SUCCESS; i++) + rc = remove(pcszAdapter, aszAddresses[i]); + + return rc; +} + + +/********************************************************************************************************************************* +* Adapter creation/removal implementations * +*********************************************************************************************************************************/ + +/* + * A generic implementation of adapter creation/removal ioctl calls. + */ +class Adapter +{ +public: + int add(char *pszNameInOut); + int remove(const char *pcszName); + int checkName(const char *pcszNameIn, char *pszNameOut, size_t cbNameOut); +protected: + virtual int doIOCtl(unsigned long iCmd, VBOXNETADPREQ *pReq); +}; + +/* + * Solaris does not support dynamic creation/removal of adapters. + */ +class AdapterSolaris : public Adapter +{ +protected: + virtual int doIOCtl(unsigned long iCmd, VBOXNETADPREQ *pReq) + { return 1 /*ENOTSUP*/; NOREF(iCmd); NOREF(pReq); }; +}; + +#if defined(RT_OS_LINUX) +/* + * Linux implementation provides a 'workaround' to obtain adapter speed. + */ +class AdapterLinux : public Adapter +{ +public: + int getSpeed(const char *pszName, unsigned *puSpeed); +}; + +int AdapterLinux::getSpeed(const char *pszName, unsigned *puSpeed) +{ + struct ifreq IfReq; + struct ethtool_value EthToolVal; + struct ethtool_cmd EthToolReq; + int fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd < 0) + { + fprintf(stderr, "VBoxNetAdpCtl: Error while retrieving link " + "speed for %s: ", pszName); + perror("VBoxNetAdpCtl: failed to open control socket"); + return ADPCTLERR_SOCKET_FAILED; + } + /* Get link status first. */ + memset(&EthToolVal, 0, sizeof(EthToolVal)); + memset(&IfReq, 0, sizeof(IfReq)); + snprintf(IfReq.ifr_name, sizeof(IfReq.ifr_name), "%s", pszName); + + EthToolVal.cmd = ETHTOOL_GLINK; + IfReq.ifr_data = (caddr_t)&EthToolVal; + int rc = ioctl(fd, SIOCETHTOOL, &IfReq); + if (rc == 0) + { + if (EthToolVal.data) + { + memset(&IfReq, 0, sizeof(IfReq)); + snprintf(IfReq.ifr_name, sizeof(IfReq.ifr_name), "%s", pszName); + EthToolReq.cmd = ETHTOOL_GSET; + IfReq.ifr_data = (caddr_t)&EthToolReq; + rc = ioctl(fd, SIOCETHTOOL, &IfReq); + if (rc == 0) + { + *puSpeed = EthToolReq.speed; + } + else + { + fprintf(stderr, "VBoxNetAdpCtl: Error while retrieving link " + "speed for %s: ", pszName); + perror("VBoxNetAdpCtl: ioctl failed"); + rc = ADPCTLERR_IOCTL_FAILED; + } + } + else + *puSpeed = 0; + } + else + { + fprintf(stderr, "VBoxNetAdpCtl: Error while retrieving link " + "status for %s: ", pszName); + perror("VBoxNetAdpCtl: ioctl failed"); + rc = ADPCTLERR_IOCTL_FAILED; + } + + close(fd); + return rc; +} +#endif /* defined(RT_OS_LINUX) */ + +int Adapter::add(char *pszName /* in/out */) +{ + VBOXNETADPREQ Req; + memset(&Req, '\0', sizeof(Req)); + snprintf(Req.szName, sizeof(Req.szName), "%s", pszName); + int rc = doIOCtl(VBOXNETADP_CTL_ADD, &Req); + if (rc == 0) + strncpy(pszName, Req.szName, VBOXNETADP_MAX_NAME_LEN); + return rc; +} + +int Adapter::remove(const char *pcszName) +{ + VBOXNETADPREQ Req; + memset(&Req, '\0', sizeof(Req)); + snprintf(Req.szName, sizeof(Req.szName), "%s", pcszName); + return doIOCtl(VBOXNETADP_CTL_REMOVE, &Req); +} + +int Adapter::checkName(const char *pcszNameIn, char *pszNameOut, size_t cbNameOut) +{ + int iAdapterIndex = -1; + + if ( strlen(pcszNameIn) >= VBOXNETADP_MAX_NAME_LEN + || sscanf(pcszNameIn, "vboxnet%d", &iAdapterIndex) != 1 + || iAdapterIndex < 0 || iAdapterIndex >= VBOXNETADP_MAX_INSTANCES ) + { + fprintf(stderr, "VBoxNetAdpCtl: Setting configuration for '%s' is not supported.\n", pcszNameIn); + return ADPCTLERR_BAD_NAME; + } + snprintf(pszNameOut, cbNameOut, "vboxnet%d", iAdapterIndex); + if (strcmp(pszNameOut, pcszNameIn)) + { + fprintf(stderr, "VBoxNetAdpCtl: Invalid adapter name '%s'.\n", pcszNameIn); + return ADPCTLERR_BAD_NAME; + } + + return 0; +} + +int Adapter::doIOCtl(unsigned long iCmd, VBOXNETADPREQ *pReq) +{ + int fd = open(VBOXNETADP_CTL_DEV_NAME, O_RDWR); + if (fd == -1) + { + fprintf(stderr, "VBoxNetAdpCtl: Error while %s %s: ", + iCmd == VBOXNETADP_CTL_REMOVE ? "removing" : "adding", + pReq->szName[0] ? pReq->szName : "new interface"); + perror("failed to open " VBOXNETADP_CTL_DEV_NAME); + return ADPCTLERR_NO_CTL_DEV; + } + + int rc = ioctl(fd, iCmd, pReq); + if (rc == -1) + { + fprintf(stderr, "VBoxNetAdpCtl: Error while %s %s: ", + iCmd == VBOXNETADP_CTL_REMOVE ? "removing" : "adding", + pReq->szName[0] ? pReq->szName : "new interface"); + perror("VBoxNetAdpCtl: ioctl failed for " VBOXNETADP_CTL_DEV_NAME); + rc = ADPCTLERR_IOCTL_FAILED; + } + + close(fd); + + return rc; +} + + +/********************************************************************************************************************************* +* Main logic, argument parsing, etc. * +*********************************************************************************************************************************/ + +#if defined(RT_OS_LINUX) +static CmdIfconfigLinux g_ifconfig; +static AdapterLinux g_adapter; +#elif defined(RT_OS_SOLARIS) +static CmdIfconfigSolaris g_ifconfig; +static AdapterSolaris g_adapter; +#else +static CmdIfconfigDarwin g_ifconfig; +static Adapter g_adapter; +#endif + +static AddressCommand& chooseAddressCommand() +{ +#if defined(RT_OS_LINUX) + static CmdIpLinux g_ip; + if (g_ip.isAvailable()) + return g_ip; +#endif + return g_ifconfig; +} + +int main(int argc, char *argv[]) +{ + char szAdapterName[VBOXNETADP_MAX_NAME_LEN]; + int rc = EXIT_SUCCESS; + + AddressCommand& cmd = chooseAddressCommand(); + + if (argc < 2) + { + fprintf(stderr, "Insufficient number of arguments\n\n"); + showUsage(); + return 1; + } + else if (argc == 2 && !strcmp("add", argv[1])) + { + /* Create a new interface */ + *szAdapterName = '\0'; + rc = g_adapter.add(szAdapterName); + if (rc == 0) + puts(szAdapterName); + return rc; + } +#ifdef RT_OS_LINUX + else if (argc == 3 && !strcmp("speed", argv[2])) + { + /* + * This ugly hack is needed for retrieving the link speed on + * pre-2.6.33 kernels (see @bugref{6345}). + */ + if (strlen(argv[1]) >= IFNAMSIZ) + { + showUsage(); + return -1; + } + unsigned uSpeed = 0; + rc = g_adapter.getSpeed(argv[1], &uSpeed); + if (!rc) + printf("%u", uSpeed); + return rc; + } +#endif + + rc = g_adapter.checkName(argv[1], szAdapterName, sizeof(szAdapterName)); + if (rc) + return rc; + + switch (argc) + { + case 5: + { + /* Add a netmask to existing interface */ + if (strcmp("netmask", argv[3])) + { + fprintf(stderr, "Invalid argument: %s\n\n", argv[3]); + showUsage(); + return 1; + } + rc = cmd.set(argv[1], argv[2], argv[4]); + break; + } + + case 4: + { + /* Remove a single address from existing interface */ + if (strcmp("remove", argv[3])) + { + fprintf(stderr, "Invalid argument: %s\n\n", argv[3]); + showUsage(); + return 1; + } + rc = cmd.remove(argv[1], argv[2]); + break; + } + + case 3: + { + if (strcmp("remove", argv[2]) == 0) + { + /* Remove an existing interface */ + rc = g_adapter.remove(argv[1]); + } + else if (strcmp("add", argv[2]) == 0) + { + /* Create an interface with given name */ + rc = g_adapter.add(szAdapterName); + if (rc == 0) + puts(szAdapterName); + } + else + rc = cmd.set(argv[1], argv[2]); + break; + } + + default: + fprintf(stderr, "Invalid number of arguments.\n\n"); + showUsage(); + return 1; + } + + return rc; +} + |