VirtualBox

vbox的更動 67687 路徑 trunk/src/VBox/HostDrivers


忽略:
時間撮記:
2017-6-29 上午10:18:32 (7 年 以前)
作者:
vboxsync
訊息:

AdpCtl: (bugref:8866) Complete re-write with 'ip' command support

檔案:
修改 1 筆資料

圖例:

未更動
新增
刪除
  • trunk/src/VBox/HostDrivers/adpctl/VBoxNetAdpCtl.cpp

    r65649 r67687  
    2121*   Header Files                                                                                                                 *
    2222*********************************************************************************************************************************/
     23#include <list>
     24#include <errno.h>
    2325#include <stdio.h>
     26#include <stdarg.h>
    2427#include <stdlib.h>
    2528#include <string.h>
     
    4548#endif
    4649
     50#define NOREF(x) (void)x
     51
    4752/** @todo Error codes must be moved to some header file */
    4853#define ADPCTLERR_BAD_NAME         2
     
    6671#define VBOXADPCTL_IFCONFIG_PATH1 "/sbin/ifconfig"
    6772#define VBOXADPCTL_IFCONFIG_PATH2 "/bin/ifconfig"
    68 static char *g_pszIfConfig;
    69 
    70 #if defined(RT_OS_LINUX)
    71 # define VBOXADPCTL_DEL_CMD "del"
    72 # define VBOXADPCTL_ADD_CMD "add"
    73 #elif defined(RT_OS_SOLARIS)
    74 # define VBOXADPCTL_DEL_CMD "removeif"
    75 # define VBOXADPCTL_ADD_CMD "addif"
    76 #else
    77 # define VBOXADPCTL_DEL_CMD "delete"
    78 # define VBOXADPCTL_ADD_CMD "add"
    79 #endif
     73
    8074
    8175static void showUsage(void)
     
    8680}
    8781
    88 static void setPathIfConfig(void)
    89 {
    90     struct stat s;
    91     if (   !stat(VBOXADPCTL_IFCONFIG_PATH1, &s)
    92         && S_ISREG(s.st_mode))
    93         g_pszIfConfig = (char*)VBOXADPCTL_IFCONFIG_PATH1;
    94     else
    95         g_pszIfConfig = (char*)VBOXADPCTL_IFCONFIG_PATH2;
    96 }
    97 
    98 static int executeIfconfig(const char *pcszAdapterName, const char *pcszArg1,
    99                            const char *pcszArg2 = NULL,
    100                            const char *pcszArg3 = NULL,
    101                            const char *pcszArg4 = NULL,
    102                            const char *pcszArg5 = NULL)
    103 {
    104     const char * const argv[] =
    105     {
    106         g_pszIfConfig,
    107         pcszAdapterName,
    108         pcszArg1, /* [address family] */
    109         pcszArg2, /* address */
    110         pcszArg3, /* ['netmask'] */
    111         pcszArg4, /* [network mask] */
    112         pcszArg5, /* [network mask] */
    113         NULL  /* terminator */
    114     };
    115     char * const envp[] = { (char*)"LC_ALL=C", NULL };
     82
     83/*
     84 * A wrapper on standard list that provides '<<' operator for adding several list members in a single
     85 * line dynamically. For example: "CmdList(arg1) << arg2 << arg3" produces a list with three members.
     86 */
     87class CmdList
     88{
     89public:
     90    /** Creates an empty list. */
     91    CmdList() {};
     92    /** Creates a list with a single member. */
     93    CmdList(const char *pcszCommand) { m_list.push_back(pcszCommand); };
     94    /** Provides access to the underlying standard list. */
     95    const std::list<const char *>& getList(void) const { return m_list; };
     96    /** Adds a member to the list. */
     97    CmdList& operator<<(const char *pcszArgument);
     98private:
     99    std::list<const char *>m_list;
     100};
     101
     102CmdList& CmdList::operator<<(const char *pcszArgument)
     103{
     104    m_list.push_back(pcszArgument);
     105    return *this;
     106}
     107
     108/** Simple helper to distinguish IPv4 and IPv6 addresses. */
     109inline bool isAddrV6(const char *pcszAddress)
     110{
     111    return !!(strchr(pcszAddress, ':'));
     112}
     113
     114
     115/*********************************************************************************************************************************
     116*   Generic address commands.                                                                                                    *
     117*********************************************************************************************************************************/
     118
     119/**
     120 * The base class for all address manipulation commands. While being an abstract class,
     121 * it provides a generic implementation of 'set' and 'remove' methods, which rely on
     122 * pure virtual methods like 'addV4' and 'removeV4' to perform actual command execution.
     123 */
     124class AddressCommand
     125{
     126public:
     127    AddressCommand() : m_pszPath(0) {};
     128    virtual ~AddressCommand() {};
     129
     130    /** returns true if underlying command (executable) is present in the system. */
     131    bool isAvailable(void)
     132        { struct stat s; return (!stat(m_pszPath, &s) && S_ISREG(s.st_mode)); };
     133
     134    /*
     135     * Someday we may want to support several IP addresses per adapter, but for now we
     136     * have 'set' method only, which replaces all addresses with the one specifed.
     137     *
     138     * virtual int add(const char *pcszAdapter, const char *pcszAddress, const char *pcszNetmask = 0) = 0;
     139     */
     140    /** replace existing address(es) */
     141    virtual int set(const char *pcszAdapter, const char *pcszAddress, const char *pcszNetmask = 0);
     142    /** remove address */
     143    virtual int remove(const char *pcszAdapter, const char *pcszAddress);
     144protected:
     145    /** IPv4-specific handler used by generic implementation of 'set' method if 'setV4' is not supported. */
     146    virtual int addV4(const char *pcszAdapter, const char *pcszAddress, const char *pcszNetmask = 0) = 0;
     147    /** IPv6-specific handler used by generic implementation of 'set' method. */
     148    virtual int addV6(const char *pcszAdapter, const char *pcszAddress, const char *pcszNetmask = 0) = 0;
     149    /** IPv4-specific handler used by generic implementation of 'set' method. */
     150    virtual int setV4(const char *pcszAdapter, const char *pcszAddress, const char *pcszNetmask = 0) = 0;
     151    /** IPv4-specific handler used by generic implementation of 'remove' method. */
     152    virtual int removeV4(const char *pcszAdapter, const char *pcszAddress) = 0;
     153    /** IPv6-specific handler used by generic implementation of 'remove' method. */
     154    virtual int removeV6(const char *pcszAdapter, const char *pcszAddress) = 0;
     155    /** Composes the argument list of command that obtains all addresses assigned to the adapter. */
     156    virtual CmdList getShowCommand(const char *pcszAdapter) const = 0;
     157
     158    /** Prepares an array of C strings needed for 'exec' call. */
     159    char * const * allocArgv(const CmdList& commandList);
     160    /** Hides process creation details. To be used in derived classes. */
     161    int execute(CmdList& commandList);
     162
     163    /** A path to executable command. */
     164    const char *m_pszPath;
     165private:
     166    /** Removes all previously asssigned addresses of a particular protocol family. */
     167    int removeAddresses(const char *pcszAdapter, const char *pcszFamily);
     168};
     169
     170/*
     171 * A generic implementation of 'ifconfig' command for all platforms.
     172 */
     173class CmdIfconfig : public AddressCommand
     174{
     175public:
     176    CmdIfconfig()
     177        {
     178            struct stat s;
     179            if (   !stat(VBOXADPCTL_IFCONFIG_PATH1, &s)
     180                && S_ISREG(s.st_mode))
     181                m_pszPath = (char*)VBOXADPCTL_IFCONFIG_PATH1;
     182            else
     183                m_pszPath = (char*)VBOXADPCTL_IFCONFIG_PATH2;
     184        };
     185
     186protected:
     187    /** Returns platform-specific subcommand to add an address. */
     188    virtual const char *addCmdArg(void) const = 0;
     189    /** Returns platform-specific subcommand to remove an address. */
     190    virtual const char *delCmdArg(void) const = 0;
     191    virtual CmdList getShowCommand(const char *pcszAdapter) const
     192        { return CmdList(pcszAdapter); };
     193    virtual int addV4(const char *pcszAdapter, const char *pcszAddress, const char *pcszNetmask = 0)
     194        { return ENOTSUP; NOREF(pcszAdapter); NOREF(pcszAddress); NOREF(pcszNetmask); };
     195    virtual int addV6(const char *pcszAdapter, const char *pcszAddress, const char *pcszNetmask = 0)
     196        {
     197            return execute(CmdList(pcszAdapter) << "inet6" << addCmdArg() << pcszAddress);
     198            NOREF(pcszNetmask);
     199        };
     200    virtual int setV4(const char *pcszAdapter, const char *pcszAddress, const char *pcszNetmask = 0)
     201        {
     202            if (!pcszNetmask)
     203                return execute(CmdList(pcszAdapter) << pcszAddress);
     204            return execute(CmdList(pcszAdapter) << pcszAddress << "netmask" << pcszNetmask);
     205        };
     206    virtual int removeV4(const char *pcszAdapter, const char *pcszAddress)
     207        { return execute(CmdList(pcszAdapter) << delCmdArg() << pcszAddress); };
     208    virtual int removeV6(const char *pcszAdapter, const char *pcszAddress)
     209        { return execute(CmdList(pcszAdapter) << "inet6" << delCmdArg() << pcszAddress); };
     210};
     211
     212
     213/*********************************************************************************************************************************
     214*   Platform-specific commands                                                                                                   *
     215*********************************************************************************************************************************/
     216
     217class CmdIfconfigLinux : public CmdIfconfig
     218{
     219protected:
     220    virtual int removeV4(const char *pcszAdapter, const char *pcszAddress)
     221        { return execute(CmdList(pcszAdapter) << "0.0.0.0"); NOREF(pcszAddress); };
     222    virtual const char *addCmdArg(void) const { return "add"; };
     223    virtual const char *delCmdArg(void) const { return "del"; };
     224};
     225
     226class CmdIfconfigDarwin : public CmdIfconfig
     227{
     228protected:
     229    virtual const char *addCmdArg(void) const { return "add"; };
     230    virtual const char *delCmdArg(void) const { return "delete"; };
     231};
     232
     233class CmdIfconfigSolaris : public CmdIfconfig
     234{
     235public:
     236    virtual int set(const char *pcszAdapter, const char *pcszAddress, const char *pcszNetmask = 0)
     237        {
     238            const char *pcszFamily = isAddrV6(pcszAddress) ? "inet6" : "inet";
     239            if (execute(CmdList(pcszAdapter) << pcszFamily))
     240                execute(CmdList(pcszAdapter) << pcszFamily << "plumb" << "up");
     241            return CmdIfconfig::set(pcszAdapter, pcszAddress, pcszNetmask);
     242        };
     243protected:
     244    /* We can umplumb IPv4 interfaces only! */
     245    virtual int removeV4(const char *pcszAdapter, const char *pcszAddress)
     246        {
     247            int rc = CmdIfconfig::removeV4(pcszAdapter, pcszAddress);
     248            execute(CmdList(pcszAdapter) << "inet" << "unplumb");
     249            return rc;
     250        };
     251    virtual const char *addCmdArg(void) const { return "addif"; };
     252    virtual const char *delCmdArg(void) const { return "removeif"; };
     253};
     254
     255
     256/*
     257 * Linux-specific implementation of 'ip' command, as other platforms do not support it.
     258 */
     259class CmdIpLinux : public AddressCommand
     260{
     261public:
     262    CmdIpLinux() { pszBuffer = 0; m_pszPath = "/sbin/ip"; };
     263    virtual ~CmdIpLinux() { delete pszBuffer; };
     264    /**
     265     * IPv4 and IPv6 syntax is the same, so we override `remove` instead of implementing
     266     * family-specific commands. It would be easier to use the same body in both
     267     * 'removeV4' and 'removeV6', so we override 'remove' to illustrate how to do common
     268     * implementation.
     269     */
     270    virtual int remove(const char *pcszAdapter, const char *pcszAddress)
     271        { return execute(CmdList("addr") << "del" << pcszAddress << "dev" << pcszAdapter); };
     272protected:
     273    virtual int addV4(const char *pcszAdapter, const char *pcszAddress, const char *pcszNetmask = 0)
     274        {
     275            return execute(CmdList("addr") << "add" << combine(pcszAddress, pcszNetmask) <<
     276                           "dev" << pcszAdapter);
     277        };
     278    virtual int addV6(const char *pcszAdapter, const char *pcszAddress, const char *pcszNetmask = 0)
     279        {
     280            return execute(CmdList("addr") << "add" << pcszAddress << "dev" << pcszAdapter);
     281            NOREF(pcszNetmask);
     282        };
     283    /**
     284     * Our command does not support 'replacing' addresses. Reporting this fact to generic implementation
     285     * of 'set' causes it to remove all assigned addresses, then 'add' the new one.
     286     */
     287    virtual int setV4(const char *pcszAdapter, const char *pcszAddress, const char *pcszNetmask = 0)
     288        { return ENOTSUP; NOREF(pcszAdapter); NOREF(pcszAddress); NOREF(pcszNetmask); };
     289    /** We use family-agnostic command syntax. See 'remove' above. */
     290    virtual int removeV4(const char *pcszAdapter, const char *pcszAddress)
     291        { return ENOTSUP; NOREF(pcszAdapter); NOREF(pcszAddress); };
     292    /** We use family-agnostic command syntax. See 'remove' above. */
     293    virtual int removeV6(const char *pcszAdapter, const char *pcszAddress)
     294        { return ENOTSUP; NOREF(pcszAdapter); NOREF(pcszAddress); };
     295    virtual CmdList getShowCommand(const char *pcszAdapter) const
     296        { return CmdList("addr") << "show" << "dev" << pcszAdapter; };
     297private:
     298    /** Converts address and network mask into a single string in CIDR notation (like 192.168.1.1/24) */
     299    const char *combine(const char *pcszAddress, const char *pcszNetmask);
     300
     301    char *pszBuffer;
     302};
     303
     304const char * CmdIpLinux::combine(const char *pcszAddress, const char *pcszNetmask)
     305{
     306    delete pszBuffer;
     307    if (pcszNetmask)
     308    {
     309        unsigned cBits = 0;
     310        unsigned m[4];
     311        if (sscanf(pcszNetmask, "%u.%u.%u.%u", &m[0], &m[1], &m[2], &m[3]) == 4)
     312        {
     313            for (int i = 0; i < 4 && m[i]; ++i)
     314            {
     315                int mask = m[i];
     316                while (mask & 0x80)
     317                {
     318                    cBits++;
     319                    mask <<= 1;
     320                }
     321            }
     322            pszBuffer = new char[strlen(pcszAddress) + 4]; // '/xx\0'
     323            sprintf(pszBuffer, "%s/%u", pcszAddress, cBits);
     324            return pszBuffer;
     325        }
     326    }
     327    return pcszAddress;
     328}
     329
     330
     331
     332/*********************************************************************************************************************************
     333*   Generic address command implementations                                                                                      *
     334*********************************************************************************************************************************/
     335
     336int AddressCommand::set(const char *pcszAdapter, const char *pcszAddress, const char *pcszNetmask)
     337{
     338    if (isAddrV6(pcszAddress))
     339    {
     340        removeAddresses(pcszAdapter, "inet6");
     341        return addV6(pcszAdapter, pcszAddress, pcszNetmask);
     342    }
     343    int rc = setV4(pcszAdapter, pcszAddress, pcszNetmask);
     344    if (rc == ENOTSUP)
     345    {
     346        removeAddresses(pcszAdapter, "inet");
     347        rc = addV4(pcszAdapter, pcszAddress, pcszNetmask);
     348    }
     349    return rc;
     350}
     351
     352int AddressCommand::remove(const char *pcszAdapter, const char *pcszAddress)
     353{
     354    if (isAddrV6(pcszAddress))
     355        return removeV6(pcszAdapter, pcszAddress);
     356    return removeV4(pcszAdapter, pcszAddress);
     357}
     358
     359/*
     360 * Allocate an array of exec arguments. In addition to arguments provided
     361 * we need to include the full path to the executable as well as "terminating"
     362 * null pointer marking the end of the array.
     363 */
     364char * const * AddressCommand::allocArgv(const CmdList& list)
     365{
     366    int i = 0;
     367    std::list<const char *>::const_iterator it;
     368    const char **argv = (const char **)calloc(list.getList().size() + 2, sizeof(const char *));
     369    if (argv)
     370    {
     371        argv[i++] = m_pszPath;
     372        for (it = list.getList().begin(); it != list.getList().end(); ++it)
     373            argv[i++] = *it;
     374        argv[i++] = NULL;
     375    }
     376    return (char * const*)argv;
     377}
     378
     379int AddressCommand::execute(CmdList& list)
     380{
     381    char * const pEnv[] = { (char*)"LC_ALL=C", NULL };
     382    char * const* argv = allocArgv(list);
     383    if (argv == NULL)
     384        return EXIT_FAILURE;
     385
    116386    int rc = EXIT_SUCCESS;
    117387    pid_t childPid = fork();
     
    123393            break;
    124394        case 0: /* Child process. */
    125             if (execve(argv[0], (char * const*)argv, envp) == -1)
     395            if (execve(argv[0], argv, pEnv) == -1)
    126396                rc = EXIT_FAILURE;
    127397            break;
     
    131401    }
    132402
     403    free((void*)argv);
    133404    return rc;
    134405}
     
    137408#define MAX_ADDRLEN   64
    138409
    139 static bool removeAddresses(char *pszAdapterName)
     410int AddressCommand::removeAddresses(const char *pcszAdapter, const char *pcszFamily)
    140411{
    141412    char szBuf[1024];
    142413    char aszAddresses[MAX_ADDRESSES][MAX_ADDRLEN];
    143     int rc;
     414    int rc = EXIT_SUCCESS;
    144415    int fds[2];
    145     char * const argv[] = { g_pszIfConfig, pszAdapterName, NULL };
     416    char * const * argv = allocArgv(getShowCommand(pcszAdapter));
    146417    char * const envp[] = { (char*)"LC_ALL=C", NULL };
    147418
     
    150421    rc = pipe(fds);
    151422    if (rc < 0)
    152         return false;
     423        return errno;
    153424
    154425    pid_t pid = fork();
    155426    if (pid < 0)
    156         return false;
     427        return errno;
    157428
    158429    if (pid == 0)
     
    163434        rc = dup2(fds[1], STDOUT_FILENO);
    164435        if (rc >= 0)
    165             execve(argv[0], argv, envp);
    166         return false;
     436            if (execve(argv[0], argv, envp) == -1)
     437                return errno;
     438        return rc;
    167439    }
    168440
     
    178450        int cbSkipWS = strspn(szBuf, " \t");
    179451        char *pszWord = strtok(szBuf + cbSkipWS, " ");
    180         /* We are concerned with IPv6 address lines only. */
    181         if (!pszWord || strcmp(pszWord, "inet6"))
     452        /* We are concerned with particular family address lines only. */
     453        if (!pszWord || strcmp(pszWord, pcszFamily))
    182454            continue;
    183 #ifdef RT_OS_LINUX
     455
    184456        pszWord = strtok(NULL, " ");
    185         /* Skip "addr:". */
    186         if (!pszWord || strcmp(pszWord, "addr:"))
    187             continue;
    188 #endif
    189         pszWord = strtok(NULL, " ");
    190         /* Skip link-local addresses. */
     457
     458        /* Skip "addr:" word if present. */
     459        if (pszWord && !strcmp(pszWord, "addr:"))
     460            pszWord = strtok(NULL, " ");
     461
     462        /* Skip link-local address lines. */
    191463        if (!pszWord || !strncmp(pszWord, "fe80", 4))
    192464            continue;
     
    195467    fclose(fp);
    196468
    197     for (int i = 0; i < cAddrs; i++)
    198     {
    199         if (executeIfconfig(pszAdapterName, "inet6",
    200                             VBOXADPCTL_DEL_CMD, aszAddresses[i]) != EXIT_SUCCESS)
    201             return false;
    202     }
    203 
    204     return true;
    205 }
    206 
    207 
    208 #ifndef RT_OS_SOLARIS
    209 static int doIOCtl(unsigned long iCmd, VBOXNETADPREQ *pReq)
     469    for (int i = 0; i < cAddrs && rc == EXIT_SUCCESS; i++)
     470        rc = remove(pcszAdapter, aszAddresses[i]);
     471
     472    return rc;
     473}
     474
     475
     476/*********************************************************************************************************************************
     477*   Adapter creation/removal implementations                                                                                     *
     478*********************************************************************************************************************************/
     479
     480/*
     481 * A generic implementation of adapter creation/removal ioctl calls.
     482 */
     483class Adapter
     484{
     485public:
     486    int add(char *pszNameInOut);
     487    int remove(const char *pcszName);
     488    int checkName(const char *pcszNameIn, char *pszNameOut);
     489protected:
     490    virtual int doIOCtl(unsigned long iCmd, VBOXNETADPREQ *pReq);
     491};
     492
     493/*
     494 * Solaris does not support dynamic creation/removal of adapters.
     495 */
     496class AdapterSolaris : public Adapter
     497{
     498protected:
     499    virtual int doIOCtl(unsigned long iCmd, VBOXNETADPREQ *pReq)
     500        { return 1 /*ENOTSUP*/; NOREF(iCmd); NOREF(pReq); };
     501};
     502
     503#if defined(RT_OS_LINUX)
     504/*
     505 * Linux implementation provides a 'workaround' to obtain adapter speed.
     506 */
     507class AdapterLinux : public Adapter
     508{
     509public:
     510    int getSpeed(const char *pszName, unsigned *puSpeed);
     511};
     512
     513int AdapterLinux::getSpeed(const char *pszName, unsigned *puSpeed)
     514{
     515    struct ifreq IfReq;
     516    struct ethtool_value EthToolVal;
     517    struct ethtool_cmd EthToolReq;
     518    int fd = socket(AF_INET, SOCK_DGRAM, 0);
     519    if (fd < 0)
     520    {
     521        fprintf(stderr, "VBoxNetAdpCtl: Error while retrieving link "
     522                "speed for %s: ", pszName);
     523        perror("VBoxNetAdpCtl: failed to open control socket");
     524        return ADPCTLERR_SOCKET_FAILED;
     525    }
     526    /* Get link status first. */
     527    memset(&EthToolVal, 0, sizeof(EthToolVal));
     528    memset(&IfReq, 0, sizeof(IfReq));
     529    snprintf(IfReq.ifr_name, sizeof(IfReq.ifr_name), "%s", pszName);
     530
     531    EthToolVal.cmd = ETHTOOL_GLINK;
     532    IfReq.ifr_data = (caddr_t)&EthToolVal;
     533    int rc = ioctl(fd, SIOCETHTOOL, &IfReq);
     534    if (rc == 0)
     535    {
     536        if (EthToolVal.data)
     537        {
     538            memset(&IfReq, 0, sizeof(IfReq));
     539            snprintf(IfReq.ifr_name, sizeof(IfReq.ifr_name), "%s", pszName);
     540            EthToolReq.cmd = ETHTOOL_GSET;
     541            IfReq.ifr_data = (caddr_t)&EthToolReq;
     542            rc = ioctl(fd, SIOCETHTOOL, &IfReq);
     543            if (rc == 0)
     544            {
     545                *puSpeed = EthToolReq.speed;
     546            }
     547            else
     548            {
     549                fprintf(stderr, "VBoxNetAdpCtl: Error while retrieving link "
     550                        "speed for %s: ", pszName);
     551                perror("VBoxNetAdpCtl: ioctl failed");
     552                rc = ADPCTLERR_IOCTL_FAILED;
     553            }
     554        }
     555        else
     556            *puSpeed = 0;
     557    }
     558    else
     559    {
     560        fprintf(stderr, "VBoxNetAdpCtl: Error while retrieving link "
     561                "status for %s: ", pszName);
     562        perror("VBoxNetAdpCtl: ioctl failed");
     563        rc = ADPCTLERR_IOCTL_FAILED;
     564    }
     565
     566    close(fd);
     567    return rc;
     568}
     569#endif /* defined(RT_OS_LINUX) */
     570
     571int Adapter::add(char *pszName /* in/out */)
     572{
     573    VBOXNETADPREQ Req;
     574    memset(&Req, '\0', sizeof(Req));
     575    snprintf(Req.szName, sizeof(Req.szName), "%s", pszName);
     576    int rc = doIOCtl(VBOXNETADP_CTL_ADD, &Req);
     577    if (rc == 0)
     578        strncpy(pszName, Req.szName, VBOXNETADP_MAX_NAME_LEN);
     579    return rc;
     580}
     581
     582int Adapter::remove(const char *pcszName)
     583{
     584    VBOXNETADPREQ Req;
     585    memset(&Req, '\0', sizeof(Req));
     586    snprintf(Req.szName, sizeof(Req.szName), "%s", pcszName);
     587    return doIOCtl(VBOXNETADP_CTL_REMOVE, &Req);
     588}
     589
     590int Adapter::checkName(const char *pcszNameIn, char *pszNameOut)
     591{
     592    int iAdapterIndex = -1;
     593
     594    if (   strlen(pcszNameIn) >= VBOXNETADP_MAX_NAME_LEN
     595        || sscanf(pcszNameIn, "vboxnet%d", &iAdapterIndex) != 1
     596        || iAdapterIndex < 0 || iAdapterIndex >= VBOXNETADP_MAX_INSTANCES )
     597    {
     598        fprintf(stderr, "VBoxNetAdpCtl: Setting configuration for '%s' is not supported.\n", pcszNameIn);
     599        return ADPCTLERR_BAD_NAME;
     600    }
     601    sprintf(pszNameOut, "vboxnet%d", iAdapterIndex);
     602    if (strcmp(pszNameOut, pcszNameIn))
     603    {
     604        fprintf(stderr, "VBoxNetAdpCtl: Invalid adapter name '%s'.\n", pcszNameIn);
     605        return ADPCTLERR_BAD_NAME;
     606    }
     607
     608    return 0;
     609}
     610
     611int Adapter::doIOCtl(unsigned long iCmd, VBOXNETADPREQ *pReq)
    210612{
    211613    int fd = open(VBOXNETADP_CTL_DEV_NAME, O_RDWR);
     
    233635    return rc;
    234636}
    235 #endif /* !RT_OS_SOLARIS */
    236 
    237 
    238 static int checkAdapterName(const char *pcszNameIn, char *pszNameOut)
    239 {
    240     int iAdapterIndex = -1;
    241 
    242     if (   strlen(pcszNameIn) >= VBOXNETADP_MAX_NAME_LEN
    243         || sscanf(pcszNameIn, "vboxnet%d", &iAdapterIndex) != 1
    244         || iAdapterIndex < 0 || iAdapterIndex >= VBOXNETADP_MAX_INSTANCES )
    245     {
    246         fprintf(stderr, "VBoxNetAdpCtl: Setting configuration for '%s' is not supported.\n", pcszNameIn);
    247         return ADPCTLERR_BAD_NAME;
    248     }
    249     sprintf(pszNameOut, "vboxnet%d", iAdapterIndex);
    250     if (strcmp(pszNameOut, pcszNameIn))
    251     {
    252         fprintf(stderr, "VBoxNetAdpCtl: Invalid adapter name '%s'.\n", pcszNameIn);
    253         return ADPCTLERR_BAD_NAME;
    254     }
    255 
    256     return 0;
     637
     638/*********************************************************************************************************************************
     639*   Main logic, argument parsing, etc.                                                                                           *
     640*********************************************************************************************************************************/
     641
     642#if defined(RT_OS_LINUX)
     643static CmdIfconfigLinux g_ifconfig;
     644static AdapterLinux g_adapter;
     645#elif defined(RT_OS_SOLARIS)
     646static CmdIfconfigSolaris g_ifconfig;
     647static AdapterSolaris g_adapter;
     648#else
     649static CmdIfconfigDarwin g_ifconfig;
     650static Adapter g_adapter;
     651#endif
     652
     653static AddressCommand& chooseAddressCommand()
     654{
     655#if defined(RT_OS_LINUX)
     656    static CmdIpLinux g_ip;
     657    if (g_ip.isAvailable())
     658        return g_ip;
     659#endif
     660    return g_ifconfig;
    257661}
    258662
     
    260664{
    261665    char szAdapterName[VBOXNETADP_MAX_NAME_LEN];
    262     char *pszAdapterName = NULL;
    263     const char *pszAddress = NULL;
    264     const char *pszNetworkMask = NULL;
    265     const char *pszOption = NULL;
    266666    int rc = EXIT_SUCCESS;
    267     bool fRemove = false;
    268     VBOXNETADPREQ Req;
    269 
    270     setPathIfConfig();
     667
     668    AddressCommand& cmd = chooseAddressCommand();
     669
     670    if (argc < 2)
     671    {
     672        fprintf(stderr, "Insufficient number of arguments\n\n");
     673        showUsage();
     674        return 1;
     675    }
     676    else if (argc == 2 && !strcmp("add", argv[1]))
     677    {
     678        /* Create a new interface */
     679        *szAdapterName = '\0';
     680        rc = g_adapter.add(szAdapterName);
     681        if (rc == 0)
     682            puts(szAdapterName);
     683        return rc;
     684    }
     685#ifdef RT_OS_LINUX
     686    else if (argc == 3 && !strcmp("speed", argv[2]))
     687    {
     688        /*
     689         * This ugly hack is needed for retrieving the link speed on
     690         * pre-2.6.33 kernels (see @bugref{6345}).
     691         */
     692        if (strlen(argv[1]) >= IFNAMSIZ)
     693        {
     694            showUsage();
     695            return -1;
     696        }
     697        unsigned uSpeed = 0;
     698        rc = g_adapter.getSpeed(argv[1], &uSpeed);
     699        if (!rc)
     700            printf("%u", uSpeed);
     701        return rc;
     702    }
     703#endif
     704   
     705    rc = g_adapter.checkName(argv[1], szAdapterName);
     706    if (rc)
     707        return rc;
    271708
    272709    switch (argc)
     
    281718                return 1;
    282719            }
    283             pszOption = "netmask";
    284             pszNetworkMask = argv[4];
    285             pszAdapterName = argv[1];
    286             pszAddress = argv[2];
     720            rc = cmd.set(argv[1], argv[2], argv[4]);
    287721            break;
    288722        }
     
    297731                return 1;
    298732            }
    299             fRemove = true;
    300             pszAdapterName = argv[1];
    301             pszAddress = argv[2];
     733            rc = cmd.remove(argv[1], argv[2]);
    302734            break;
    303735        }
     
    305737        case 3:
    306738        {
    307             pszAdapterName = argv[1];
    308             memset(&Req, '\0', sizeof(Req));
    309 #ifdef RT_OS_LINUX
    310             if (strcmp("speed", argv[2]) == 0)
    311             {
    312                 /*
    313                  * This ugly hack is needed for retrieving the link speed on
    314                  * pre-2.6.33 kernels (see @bugref{6345}).
    315                  */
    316                 if (strlen(pszAdapterName) >= IFNAMSIZ)
    317                 {
    318                     showUsage();
    319                     return -1;
    320                 }
    321                 struct ifreq IfReq;
    322                 struct ethtool_value EthToolVal;
    323                 struct ethtool_cmd EthToolReq;
    324                 int fd = socket(AF_INET, SOCK_DGRAM, 0);
    325                 if (fd < 0)
    326                 {
    327                     fprintf(stderr, "VBoxNetAdpCtl: Error while retrieving link "
    328                             "speed for %s: ", pszAdapterName);
    329                     perror("VBoxNetAdpCtl: failed to open control socket");
    330                     return ADPCTLERR_SOCKET_FAILED;
    331                 }
    332                 /* Get link status first. */
    333                 memset(&EthToolVal, 0, sizeof(EthToolVal));
    334                 memset(&IfReq, 0, sizeof(IfReq));
    335                 snprintf(IfReq.ifr_name, sizeof(IfReq.ifr_name), "%s", pszAdapterName);
    336 
    337                 EthToolVal.cmd = ETHTOOL_GLINK;
    338                 IfReq.ifr_data = (caddr_t)&EthToolVal;
    339                 rc = ioctl(fd, SIOCETHTOOL, &IfReq);
    340                 if (rc == 0)
    341                 {
    342                     if (EthToolVal.data)
    343                     {
    344                         memset(&IfReq, 0, sizeof(IfReq));
    345                         snprintf(IfReq.ifr_name, sizeof(IfReq.ifr_name), "%s", pszAdapterName);
    346                         EthToolReq.cmd = ETHTOOL_GSET;
    347                         IfReq.ifr_data = (caddr_t)&EthToolReq;
    348                         rc = ioctl(fd, SIOCETHTOOL, &IfReq);
    349                         if (rc == 0)
    350                         {
    351                             printf("%u", EthToolReq.speed);
    352                         }
    353                         else
    354                         {
    355                             fprintf(stderr, "VBoxNetAdpCtl: Error while retrieving link "
    356                                     "speed for %s: ", pszAdapterName);
    357                             perror("VBoxNetAdpCtl: ioctl failed");
    358                             rc = ADPCTLERR_IOCTL_FAILED;
    359                         }
    360                     }
    361                     else
    362                         printf("0");
    363                 }
    364                 else
    365                 {
    366                     fprintf(stderr, "VBoxNetAdpCtl: Error while retrieving link "
    367                             "status for %s: ", pszAdapterName);
    368                     perror("VBoxNetAdpCtl: ioctl failed");
    369                     rc = ADPCTLERR_IOCTL_FAILED;
    370                 }
    371 
    372                 close(fd);
    373                 return rc;
    374             }
    375 #endif
    376             rc = checkAdapterName(pszAdapterName, szAdapterName);
    377             if (rc)
    378                 return rc;
    379             snprintf(Req.szName, sizeof(Req.szName), "%s", szAdapterName);
    380             pszAddress = argv[2];
    381             if (strcmp("remove", pszAddress) == 0)
     739            if (strcmp("remove", argv[2]) == 0)
    382740            {
    383741                /* Remove an existing interface */
    384 #ifdef RT_OS_SOLARIS
    385                 return 1;
    386 #else
    387                 return doIOCtl(VBOXNETADP_CTL_REMOVE, &Req);
    388 #endif
     742                rc = g_adapter.remove(argv[1]);
    389743            }
    390             else if (strcmp("add", pszAddress) == 0)
     744            else if (strcmp("add", argv[2]) == 0)
    391745            {
    392746                /* Create an interface with given name */
    393 #ifdef RT_OS_SOLARIS
    394                 return 1;
    395 #else
    396                 rc = doIOCtl(VBOXNETADP_CTL_ADD, &Req);
     747                rc = g_adapter.add(szAdapterName);
    397748                if (rc == 0)
    398                     puts(Req.szName);
    399 #endif
    400                 return rc;
     749                    puts(szAdapterName);
    401750            }
     751            else
     752                rc = cmd.set(argv[1], argv[2]);
    402753            break;
    403754        }
    404755
    405         case 2:
    406         {
    407             /* Create a new interface */
    408             if (strcmp("add", argv[1]) == 0)
    409             {
    410 #ifdef RT_OS_SOLARIS
    411                 return 1;
    412 #else
    413                 memset(&Req, '\0', sizeof(Req));
    414                 rc = doIOCtl(VBOXNETADP_CTL_ADD, &Req);
    415                 if (rc == 0)
    416                     puts(Req.szName);
    417 #endif
    418                 return rc;
    419             }
    420         }
    421         /* fall thru */
    422 
    423756        default:
    424757            fprintf(stderr, "Invalid number of arguments.\n\n");
    425             /* fall thru */
    426         case 1:
    427758            showUsage();
    428759            return 1;
    429760    }
    430761
    431     rc = checkAdapterName(pszAdapterName, szAdapterName);
    432     if (rc)
    433         return rc;
    434 
    435     pszAdapterName = szAdapterName;
    436 
    437     if (fRemove)
    438     {
    439         if (strchr(pszAddress, ':'))
    440             rc = executeIfconfig(pszAdapterName, "inet6", VBOXADPCTL_DEL_CMD, pszAddress);
    441         else
    442         {
    443 #if defined(RT_OS_LINUX)
    444             rc = executeIfconfig(pszAdapterName, "0.0.0.0");
    445 #else
    446             rc = executeIfconfig(pszAdapterName, VBOXADPCTL_DEL_CMD, pszAddress);
    447 #endif
    448 
    449 #ifdef RT_OS_SOLARIS
    450             /* On Solaris we can unplumb the ipv4 interface */
    451             executeIfconfig(pszAdapterName, "inet", "unplumb");
    452 #endif
    453         }
    454     }
    455     else
    456     {
    457         /* We are setting/replacing address. */
    458         if (strchr(pszAddress, ':'))
    459         {
    460 #ifdef RT_OS_SOLARIS
    461             /* On Solaris we need to plumb the interface first if it's not already plumbed. */
    462             if (executeIfconfig(pszAdapterName, "inet6") != 0)
    463                 executeIfconfig(pszAdapterName, "inet6", "plumb", "up");
    464 #endif
    465             /*
    466              * Before we set IPv6 address we'd like to remove
    467              * all previously assigned addresses except the
    468              * self-assigned one.
    469              */
    470             if (!removeAddresses(pszAdapterName))
    471                 rc = EXIT_FAILURE;
    472             else
    473                 rc = executeIfconfig(pszAdapterName, "inet6", VBOXADPCTL_ADD_CMD, pszAddress, pszOption, pszNetworkMask);
    474         }
    475         else
    476         {
    477 #ifdef RT_OS_SOLARIS
    478             /* On Solaris we need to plumb the interface first if it's not already plumbed. */
    479             if (executeIfconfig(pszAdapterName, "inet") != 0)
    480                 executeIfconfig(pszAdapterName, "plumb", "up");
    481 #endif
    482             rc = executeIfconfig(pszAdapterName, pszAddress, pszOption, pszNetworkMask);
    483         }
    484     }
    485762    return rc;
    486763}
注意: 瀏覽 TracChangeset 來幫助您使用更動檢視器

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette