VirtualBox

Changeset 5586 in vbox for trunk/src/VBox/Devices/Network


Ignore:
Timestamp:
Oct 31, 2007 6:24:35 PM (17 years ago)
Author:
vboxsync
Message:

Solaris crossbow integration, work in progress.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Devices/Network/DrvTAP.cpp

    r5298 r5586  
    5252# include <ctype.h>
    5353# include <stdlib.h>
     54# ifdef VBOX_WITH_CROSSBOW
     55#  include <limits.h>
     56#  include <libdlpi.h>
     57#  include <libdlvnic.h>
     58# endif
    5459#else
    5560# include <sys/fcntl.h>
     
    9398    char                   *pszDeviceName;
    9499#ifdef RT_OS_SOLARIS
    95     /** The actual TAP device name. */
     100    /** The actual TAP/VNIC device name. */
    96101    char                   *pszDeviceNameActual;
     102# ifdef VBOX_WITH_CROSSBOW
     103    /** Crossbow: MAC address of the device. */
     104    char                   *pszMACAddress;
     105    /** Crossbow: Handle of the NIC. */
     106    dlpi_handle_t           pDeviceHandle;
     107# else
    97108    /** IP device file handle (/dev/udp). */
    98109    RTFILE                  IPFileDevice;
     110# endif
    99111#endif
    100112    /** TAP setup application. */
     
    152164*******************************************************************************/
    153165#ifdef RT_OS_SOLARIS
    154 static DECLCALLBACK(int) SolarisTAPAttach(PPDMDRVINS pDrvIns);
     166# ifdef VBOX_WITH_CROSSBOW
     167static int              SolarisCreateVNIC(PDRVTAP pData);
     168static int              SolarisGetNIC(char *pszNICName, size_t cbSize);
     169static int              SolarisOpenNIC(PDRVTAP pData, const char *pszNICName, struct ether_addr *pEtherAddr);
     170static dladm_status_t   SolarisCompareVNIC(void* pArg, dladm_vnic_attr_sys_t *pVNICAttr);
     171# else
     172static int              SolarisTAPAttach(PPDMDRVINS pDrvIns);
     173# endif
    155174#endif
    156175
     
    296315            char achBuf[4096];
    297316            size_t cbRead = 0;
     317#ifdef VBOX_WITH_CROSSBOW
     318            rc = VINF_SUCCESS;
     319            cbRead = sizeof(achBuf);
     320            if (dlpi_recv(pData->pDeviceHandle, NULL, NULL, achBuf, &cbRead, -1, NULL) != DLPI_SUCCESS)
     321                rc = VERR_GENERAL_FAILURE;  /** @todo find a better return code.  */ /** r=bird: just make a conversion function. */
     322#else
    298323            rc = RTFileRead(pData->FileDevice, achBuf, sizeof(achBuf), &cbRead);
     324#endif
    299325            if (VBOX_SUCCESS(rc))
    300326            {
     
    428454                char        achBuf[4096];
    429455                size_t      cbRead = 0;
     456#ifdef VBOX_WITH_CROSSBOW
     457                int rc = VINF_SUCCESS;
     458                cbRead = sizeof(achBuf);
     459                if (dlpi_recv(pData->pDeviceHandle, NULL, NULL, achBuf, &cbRead, -1, NULL) != DLPI_SUCCESS)
     460                    rc = VERR_GENERAL_FAILURE; /** @todo find a better return code. */
     461#else
    430462                int rc = RTFileRead(pData->FileDevice, achBuf, RT_MIN(sizeof(achBuf), cbMax), &cbRead);
     463#endif
    431464                if (VBOX_SUCCESS(rc))
    432465                {
     
    554587
    555588#ifdef RT_OS_SOLARIS
     589# ifdef VBOX_WITH_CROSSBOW
     590/**
     591 * Crossbow: create a virtual NIC.
     592 *
     593 * @returns VBox error code.
     594 * @param   pData           The instance data.
     595 */
     596static int SolarisCreateVNIC(PDRVTAP pData)
     597{
     598    /*
     599     * Get a physical NIC.
     600     */
     601    /** @todo r=bird: I'm I right in thinking that this just gets the name of the
     602     * last ethernet NIC and binds us to that? If so, this really needs to be
     603     * a user option. On OS/2 this is passed in as 'ConnectTo', using the same name
     604     * is possibly a good idea even if the type is different (we need string not integer). */
     605    char szNICName[_LIFNAMSIZ];
     606    int ret = SolarisGetNIC(szNICName, sizeof(szNICName));
     607    if (VBOX_FAILURE(ret))
     608         return VERR_HOSTIF_INIT_FAILED;
     609
     610    /*
     611     * Get the MAC address with ':' seperators as ether_aton() needs those
     612     */
     613    /** @todo r=bird: ether_addr is just a byte array, just pass the PDMMAC
     614     * structure around and memcpy it, this is too much work. */
     615    struct ether_addr *pEtherAddr = NULL;
     616    if (pData->pszMACAddress && strlen(pData->pszMACAddress) == 12)
     617    {
     618        char szMACAddress[12 + 6 + 1];
     619        RTStrPrintf(szMACAddress, sizeof(szMACAddress), "%c%c:%c%c:%c%c:%c%c:%c%c:%c%c",
     620                    pData->pszMACAddress[0], pData->pszMACAddress[1], pData->pszMACAddress[2], pData->pszMACAddress[3],
     621                    pData->pszMACAddress[4], pData->pszMACAddress[5], pData->pszMACAddress[6], pData->pszMACAddress[7],
     622                    pData->pszMACAddress[8], pData->pszMACAddress[9], pData->pszMACAddress[10], pData->pszMACAddress[11]);
     623
     624        pEtherAddr = ether_aton(szMACAddress);
     625    }
     626    if (!pEtherAddr)
     627        return PDMDrvHlpVMSetError(pData->pDrvIns, VERR_HOSTIF_INIT_FAILED, RT_SRC_POS,
     628                               N_("Invalid MAC address %s"), pData->pszMACAddress);
     629
     630    /*
     631     * Setup VNIC parameters.
     632     */
     633    dladm_vnic_attr_sys_t VNICAttr;
     634    strncpy(VNICAttr.va_dev_name, szNICName, sizeof(VNICAttr.va_dev_name) - 1); /** @todo r=bird: don't ever use strncpy! Esp. not with an uninitialized structure. */
     635    memcpy(VNICAttr.va_mac_addr, (uchar_t *)pEtherAddr->ether_addr_octet, ETHERADDRL);
     636    VNICAttr.va_mac_len = ETHERADDRL;
     637
     638    uint_t VnicID;
     639    bool fAutoID = true;
     640#if 0
     641    /* Disabled for now, since Crossbow does not entirely respect our own VNIC ID.*/
     642    if (pData->pszDeviceName)
     643    {
     644        size_t cch = strlen(pData->pszDeviceName);
     645        if (cch > 1 && isdigit(pData->pszDeviceName[cch - 1]) != 0)
     646        {
     647            VnicID = pData->pszDeviceName[cch - 1] - '0';
     648            fAutoID = false;
     649        }
     650    }
     651#endif
     652
     653    /*
     654     * Create a VNIC if it doesn't already exist.
     655     * XXX: Perhaps VMs should not use existing VNICs???
     656     */
     657/** r=bird: The users should be able to create the vnic himself and pass it down. This would be the
     658 * same as the tapN interface name.  */
     659    dladm_status_t rc = dladm_vnic_walk_sys(SolarisCompareVNIC, &VNICAttr);
     660    if (rc == DLADM_STATUS_OK)
     661    {
     662        uint32_t flags = DLADM_VNIC_OPT_TEMP;
     663        if (fAutoID)
     664            flags |= DLADM_VNIC_OPT_AUTOID;
     665
     666        rc = dladm_vnic_create(fAutoID ? 0 : VnicID, szNICName, VNIC_MAC_ADDR_TYPE_FIXED,
     667                               (uchar_t *)pEtherAddr->ether_addr_octet, ETHERADDRL, &VnicID, flags);
     668        if (rc != DLADM_STATUS_OK)
     669            return PDMDrvHlpVMSetError(pData->pDrvIns, VERR_HOSTIF_INIT_FAILED, RT_SRC_POS,
     670                               N_("dladm_vnic_create() failed. NIC %s probably incorrect."), szNICName);
     671    }
     672    else
     673/** r=bird: This can't possibly fail in any way, or what? */
     674        VnicID = VNICAttr.va_vnic_id;
     675
     676
     677    pData->pszDeviceNameActual = NULL;
     678    RTStrAPrintf(&pData->pszDeviceNameActual, "vnic%u", VnicID);
     679
     680    ret = SolarisOpenNIC(pData, szNICName, pEtherAddr);
     681    if (VBOX_FAILURE(ret))
     682        return ret;
     683    return VINF_SUCCESS;
     684}
     685
     686
     687/**
     688 * Crossbow: Obtain a physical NIC for binding the virtual NIC.
     689 *
     690 * @returns VBox error code.
     691 * @param   pszNICName      Where to store the NIC name.
     692 * @param   cchNICName      The size of the buffer buffer pszNICName points to.
     693 */
     694static int SolarisGetNIC(char *pszNICName, size_t cchNICName)
     695{
     696    /*
     697     * Try and obtain the a physical NIC to bind the VNIC to.
     698     */
     699    int InetSocket = socket(AF_INET, SOCK_DGRAM, 0);
     700    if (RT_UNLIKELY(inetSocket == -1))
     701    {
     702        LogRel(("SolarisGetNIC: Socket creation for AF_INET family failed.\n"));
     703        return VERR_HOSTIF_INIT_FAILED;
     704    }
     705
     706    int rc;
     707    struct lifnum IfNum;
     708    IfNum.lifn_family = AF_UNSPEC;
     709    if (ioctl(InetSocket, SIOCGLIFNUM, (char *)&IfNum) >= 0)
     710    {
     711        caddr_t pBuf = (caddr_t)RTMemAlloc(IfNum.lifn_count * sizeof(struct lifreq));
     712        if (pszBuffer)
     713        {
     714            struct lifconf IfCfg;
     715            memset(&IfCfg, 0, sizeof(IfCfg));
     716            IfCfg.lifc_family = AF_UNSPEC;
     717            IfCfg.lifc_buf = pBuf
     718            if (ioctl(InetSocket, SIOCGLIFCONF, (char *)&IfCfg) >= 0)
     719            {
     720                /*
     721                 * Loop through all NICs on the machine. We'll use the first ethernet NIC
     722                 * that is not a loopback interface for binding the VNIC.
     723                 */
     724                rc = VERR_GENERAL_FAILURE;    /** @todo find a better return code. */
     725                struct lifreq *paIf = IfCfg.lifc_req;
     726                int iIf = IfCfg.lifc_len / sizeof(struct lifreq);
     727                while (iIf-- > 0)
     728                    if (strncmp(paIf[iIf].lifr_name, "lo", 2) != 0)
     729                    {
     730                        dlpi_handle_t hNIC = NULL;
     731                        if (dlpi_open(paIf[iIf].lifr_name, &hNIC, 0) == DLPI_SUCCESS)
     732                        {
     733                            dlpi_info_t NICInfo;
     734                            int rc2 = dlpi_info(hNIC, &NICInfo, 0);
     735                            dlpi_close(hNIC);
     736                            if (    rc2 == DLPI_SUCCESS
     737                                &&  NICInfo.di_mactype == DL_ETHER)
     738                            {
     739                                size_t cch = strlen(paIf[iIf].lifr_name);
     740                                if (cch < cchNICName)
     741                                {
     742                                    memcpy(pszNICName, paIf[iIf].lifr_name, cch + 1);
     743                                    rc = VINF_SUCCESS;
     744                                }
     745                                else
     746                                    rc = VERR_BUFFER_OVERFLOW;
     747                                break;
     748                            }
     749                        }
     750                    }
     751            }
     752            else
     753            {
     754                LogRel(("SolarisGetNIC: SIOCGLIFCONF failed\n"));
     755                rc = VERR_HOSTIF_INIT_FAILED;
     756            }
     757            free(pszBuffer);
     758        }
     759        else
     760            rc = VERR_NO_MEMORY;
     761    }
     762    else
     763    {
     764        LogRel(("SolarisGetNIC: SIOCGLIFNUM failed\n"));
     765        rc = VERR_HOSTIF_INIT_FAILED;
     766    }
     767    close(InetSocket);
     768    return rc;
     769}
     770
     771
     772/**
     773 * Crossbow: Open & configure the physical NIC.
     774 *
     775 * @returns VBox error code.
     776 * @param   pData           The instance data.
     777 * @param   pszNICName      Name of the physical NIC.
     778 * @param   pEtherAddr      Ethernet address to use for the VNIC.
     779 */
     780static int SolarisOpenNIC(PDRVTAP pData, const char *pszNICName, struct ether_addr *pEtherAddr)
     781{
     782    /*
     783     * Open & bind the NIC using the datalink provider routine.
     784     */
     785    int rc = dlpi_open(pszNICName, &pData->pDeviceHandle, DLPI_RAW);
     786    if (rc != DLPI_SUCCESS)
     787        return PDMDrvHlpVMSetError(pData->pDrvIns, VERR_HOSTIF_INIT_FAILED, RT_SRC_POS,
     788                           N_("Failed to open VNIC in raw mode."));
     789
     790    /*
     791     * If we decide to get NIC name directly from user/env var., we will
     792     * need to checks here to make sure the NIC has a ethernet address.
     793     */
     794    rc = dlpi_bind(pData->pDeviceHandle, DLPI_ANY_SAP, NULL);
     795    if (rc == DLPI_SUCCESS)
     796    {
     797        rc = dlpi_set_physaddr(pData->pDeviceHandle, DL_CURR_PHYS_ADDR, pEtherAddr->ether_addr_octet, ETHERADDRL);
     798        if (rc == DLPI_SUCCESS)
     799        {
     800            rc = dlpi_promiscon(pData->pDeviceHandle, DL_PROMISC_SAP);
     801            if (rc == DLPI_SUCCESS)
     802            {
     803                /* Need to use DL_PROMIS_PHYS (not multicast) as we cannot be sure what the guest needs. */
     804                rc = dlpi_promiscon(pData->pDeviceHandle, DL_PROMISC_PHYS);
     805                if (rc == DLPI_SUCCESS)
     806                {
     807                    pData->FileDevice = dlpi_fd(pData->pDeviceHandle);
     808                    if (pData->FileDevice >= 0)
     809                    {
     810                        Log(("SolarisOpenNIC: %s -> %d\n", pszNICName, pData->FileDevice));
     811                        return VINF_SUCCESS;
     812                    }
     813
     814                    rc = PDMDrvHlpVMSetError(pData->pDrvIns, VERR_HOSTIF_INIT_FAILED, RT_SRC_POS,
     815                                             N_("Failed to obtain file descriptor for VNIC."));
     816                }
     817                else
     818                    rc = PDMDrvHlpVMSetError(pData->pDrvIns, VERR_HOSTIF_INIT_FAILED, RT_SRC_POS,
     819                                             N_("Failed to set appropriate promiscous mode."));
     820            }
     821            else
     822                rc = PDMDrvHlpVMSetError(pData->pDrvIns, VERR_HOSTIF_INIT_FAILED, RT_SRC_POS,
     823                                         N_("Failed to activate promiscous mode for VNIC."));
     824        }
     825        else
     826            rc = PDMDrvHlpVMSetError(pData->pDrvIns, VERR_HOSTIF_INIT_FAILED, RT_SRC_POS,
     827                                     N_("Failed to set physical address for VNIC."));
     828    }
     829    else
     830        rc = PDMDrvHlpVMSetError(pData->pDrvIns, VERR_HOSTIF_INIT_FAILED, RT_SRC_POS,
     831                                 N_("Failed to bind VNIC."));
     832    dlpi_close(pData->pDeviceHandle);
     833    return rc;
     834}
     835
     836
     837/**
     838 * Crossbow: delete a virtual NIC.
     839 *
     840 * @returns VBox error code.
     841 * @param   pData           The instance data.
     842 */
     843static int SolarisDeleteVNIC(PDRVTAP pData)
     844{
     845    /*
     846     * Extract the VNIC ID from the name. e.g.: "vnic900" we need "900".
     847     */
     848    const char *pszVNICName = pData->pszDeviceNameActual;
     849    while (*pszVNICName && !isdigit(*pszVNICName))
     850        pszVNICName++;
     851
     852    if (pszVNICName)
     853    {
     854        /** @todo r=bird: what about just remembering the VNIC ID? (assuming it's the same as during creation.) */
     855        uint_t VnicID = atoi(pszVNICName);
     856        dladm_status_t rc = dladm_vnic_delete(VnicID, DLADM_VNIC_OPT_TEMP);
     857        if (rc == DLADM_STATUS_OK)
     858            return VINF_SUCCESS;
     859    }
     860    return VERR_HOSTIF_TERM_FAILED;
     861}
     862
     863
     864/**
     865 * Crossbow: VNIC comparison hook function.
     866 *
     867 * @returns VBox error code.
     868 * @param   pvArg          Opaque pointer to a VNIC.
     869 * @param   pVNICAttr      Pointer to another VNIC to compare with the first.
     870 */
     871static dladm_status_t SolarisCompareVNIC(void *pvArg, dladm_vnic_attr_sys_t *pVNICAttr)
     872{
     873    dladm_vnic_attr_sys_t *pVNICAttr2 = (dladm_vnic_attr_sys_t *)pvArg;
     874    if (    strcmp(pVNICAttr2->va_dev_name, pVNICAttr->va_dev_name) != 0
     875         || memcmp(pVNICAttr2->va_mac_addr, pVNICAttr->va_mac_addr, pVNICAttr2->va_mac_len) != 0
     876         || pVNICAttr2->va_mac_len != pVNICAttr->va_mac_len)
     877         return DLADM_STATUS_OK;
     878
     879    pVNICAttr->va_vnic_id = pVNICAttr2->va_vnic_id;
     880    return DLADM_STATUS_EXIST;
     881}
     882
     883# else  /* VBOX_WITH_CROSSBOW */
     884
    556885/** From net/if_tun.h, installed by Universal TUN/TAP driver */
    557886# define TUNNEWPPA                   (('T'<<16) | 0x0001)
     
    7181047}
    7191048
     1049# endif /* VBOX_WITH_CROSSBOW */
    7201050#endif  /* RT_OS_SOLARIS */
    7211051
     
    7951125
    7961126#ifdef RT_OS_SOLARIS
    797     if (pData->pszTerminateApplication)
    798         drvTAPTerminateApplication(pData);
    799 
     1127/** @todo r=bird: exactly where and when this is closed depends on how it was created, see ConsoleImpl.cpp. It's a bit complicated, I know :-/ */
     1128    if (pData->FileDevice != NIL_RTFILE)
     1129    {
     1130        int rc = RTFileClose(pData->FileDevice);
     1131        AssertRC(rc);
     1132        pData->FileDevice = NIL_RTFILE;
     1133    }
     1134
     1135# ifndef VBOX_WITH_CROSSBOW
    8001136    if (pData->IPFileDevice != NIL_RTFILE)
    8011137    {
     
    8041140        pData->IPFileDevice = NIL_RTFILE;
    8051141    }
     1142# endif
     1143
     1144    /*
     1145     * Call TerminateApplication after closing the device otherwise
     1146     * TerminateApplication would not be able to unplumb it.
     1147     */
     1148    if (pData->pszTerminateApplication)
     1149        drvTAPTerminateApplication(pData);
     1150
     1151# ifdef VBOX_WITH_CROSSBOW
     1152    /* Finally unregister the VNIC */
     1153    dlpi_close(pData->pDeviceHandle);
     1154    SolarisDeleteVNIC(pData);
     1155    MMR3HeapFree(pData->pszMACAddress);
     1156# endif
    8061157
    8071158    RTStrFree(pData->pszDeviceNameActual);
    808 #endif
     1159#endif  /* RT_OS_SOLARIS */
     1160
    8091161    MMR3HeapFree(pData->pszDeviceName);
    8101162    MMR3HeapFree(pData->pszSetupApplication);
     
    8351187#ifdef RT_OS_SOLARIS
    8361188    pData->pszDeviceNameActual          = NULL;
     1189# ifdef VBOX_WITH_CROSSBOW
     1190    pData->pszMACAddress                = NULL;
     1191    pData->pDeviceHandle                = NULL;
     1192# else
    8371193    pData->IPFileDevice                 = NIL_RTFILE;
     1194# endif
    8381195#endif
    8391196    pData->pszSetupApplication          = NULL;
     
    8541211     * Validate the config.
    8551212     */
    856     if (!CFGMR3AreValuesValid(pCfgHandle, "Device\0InitProg\0TermProg\0FileHandle\0TAPSetupApplication\0TAPTerminateApplication"))
     1213    if (!CFGMR3AreValuesValid(pCfgHandle, "Device\0InitProg\0TermProg\0FileHandle\0TAPSetupApplication\0TAPTerminateApplication\0MACAddress"))
    8571214        return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES, "");
    8581215
     
    8971254        return PDMDRV_SET_ERROR(pDrvIns, rc, N_("Configuration error: failed to query \"TAPTerminateApplication\""));
    8981255
     1256# ifdef VBOX_WITH_CROSSBOW
     1257    rc = CFGMR3QueryStringAlloc(pCfgHandle, "MACAddress", &pData->pszMACAddress); /** @todo r=bird: MACAddress -> MAC; pass bytes like for pcnet. See VBoxBFE and ConsoleImpl.cpp comments. */
     1258    if (VBOX_FAILURE(rc))
     1259            return PDMDrvHlpVMSetError(pDrvIns, VERR_HOSTIF_INIT_FAILED, RT_SRC_POS,
     1260                                       N_("Failed to query \"MACAddress\""));
     1261# endif
    8991262
    9001263    rc = CFGMR3QueryStringAlloc(pCfgHandle, "Device", &pData->pszDeviceName);
     
    9061269     * Do the setup.
    9071270     */
     1271# ifdef VBOX_WITH_CROSSBOW
     1272    rc = SolarisCreateVNIC(pData);
     1273# else
    9081274    rc = SolarisTAPAttach(pDrvIns);
     1275# endif
    9091276    if (VBOX_FAILURE(rc))
    9101277        return rc;
     
    9181285    }
    9191286
    920 #else /* !SOLARIS */
     1287#else /* !RT_OS_SOLARIS */
    9211288
    9221289    int32_t iFile;
     
    9291296        return PDMDrvHlpVMSetError(pDrvIns, VERR_INVALID_HANDLE, RT_SRC_POS,
    9301297                                   N_("The TAP file handle %RTfile is not valid!"), pData->FileDevice);
    931 #endif /* !SOLARIS */
     1298#endif /* !RT_OS_SOLARIS */
    9321299
    9331300    /*
Note: See TracChangeset for help on using the changeset viewer.

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