Changeset 5586 in vbox for trunk/src/VBox/Devices/Network
- Timestamp:
- Oct 31, 2007 6:24:35 PM (17 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Devices/Network/DrvTAP.cpp
r5298 r5586 52 52 # include <ctype.h> 53 53 # include <stdlib.h> 54 # ifdef VBOX_WITH_CROSSBOW 55 # include <limits.h> 56 # include <libdlpi.h> 57 # include <libdlvnic.h> 58 # endif 54 59 #else 55 60 # include <sys/fcntl.h> … … 93 98 char *pszDeviceName; 94 99 #ifdef RT_OS_SOLARIS 95 /** The actual TAP device name. */100 /** The actual TAP/VNIC device name. */ 96 101 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 97 108 /** IP device file handle (/dev/udp). */ 98 109 RTFILE IPFileDevice; 110 # endif 99 111 #endif 100 112 /** TAP setup application. */ … … 152 164 *******************************************************************************/ 153 165 #ifdef RT_OS_SOLARIS 154 static DECLCALLBACK(int) SolarisTAPAttach(PPDMDRVINS pDrvIns); 166 # ifdef VBOX_WITH_CROSSBOW 167 static int SolarisCreateVNIC(PDRVTAP pData); 168 static int SolarisGetNIC(char *pszNICName, size_t cbSize); 169 static int SolarisOpenNIC(PDRVTAP pData, const char *pszNICName, struct ether_addr *pEtherAddr); 170 static dladm_status_t SolarisCompareVNIC(void* pArg, dladm_vnic_attr_sys_t *pVNICAttr); 171 # else 172 static int SolarisTAPAttach(PPDMDRVINS pDrvIns); 173 # endif 155 174 #endif 156 175 … … 296 315 char achBuf[4096]; 297 316 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 298 323 rc = RTFileRead(pData->FileDevice, achBuf, sizeof(achBuf), &cbRead); 324 #endif 299 325 if (VBOX_SUCCESS(rc)) 300 326 { … … 428 454 char achBuf[4096]; 429 455 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 430 462 int rc = RTFileRead(pData->FileDevice, achBuf, RT_MIN(sizeof(achBuf), cbMax), &cbRead); 463 #endif 431 464 if (VBOX_SUCCESS(rc)) 432 465 { … … 554 587 555 588 #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 */ 596 static 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 */ 694 static 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 */ 780 static 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 */ 843 static 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 */ 871 static 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 556 885 /** From net/if_tun.h, installed by Universal TUN/TAP driver */ 557 886 # define TUNNEWPPA (('T'<<16) | 0x0001) … … 718 1047 } 719 1048 1049 # endif /* VBOX_WITH_CROSSBOW */ 720 1050 #endif /* RT_OS_SOLARIS */ 721 1051 … … 795 1125 796 1126 #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 800 1136 if (pData->IPFileDevice != NIL_RTFILE) 801 1137 { … … 804 1140 pData->IPFileDevice = NIL_RTFILE; 805 1141 } 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 806 1157 807 1158 RTStrFree(pData->pszDeviceNameActual); 808 #endif 1159 #endif /* RT_OS_SOLARIS */ 1160 809 1161 MMR3HeapFree(pData->pszDeviceName); 810 1162 MMR3HeapFree(pData->pszSetupApplication); … … 835 1187 #ifdef RT_OS_SOLARIS 836 1188 pData->pszDeviceNameActual = NULL; 1189 # ifdef VBOX_WITH_CROSSBOW 1190 pData->pszMACAddress = NULL; 1191 pData->pDeviceHandle = NULL; 1192 # else 837 1193 pData->IPFileDevice = NIL_RTFILE; 1194 # endif 838 1195 #endif 839 1196 pData->pszSetupApplication = NULL; … … 854 1211 * Validate the config. 855 1212 */ 856 if (!CFGMR3AreValuesValid(pCfgHandle, "Device\0InitProg\0TermProg\0FileHandle\0TAPSetupApplication\0TAPTerminateApplication "))1213 if (!CFGMR3AreValuesValid(pCfgHandle, "Device\0InitProg\0TermProg\0FileHandle\0TAPSetupApplication\0TAPTerminateApplication\0MACAddress")) 857 1214 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES, ""); 858 1215 … … 897 1254 return PDMDRV_SET_ERROR(pDrvIns, rc, N_("Configuration error: failed to query \"TAPTerminateApplication\"")); 898 1255 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 899 1262 900 1263 rc = CFGMR3QueryStringAlloc(pCfgHandle, "Device", &pData->pszDeviceName); … … 906 1269 * Do the setup. 907 1270 */ 1271 # ifdef VBOX_WITH_CROSSBOW 1272 rc = SolarisCreateVNIC(pData); 1273 # else 908 1274 rc = SolarisTAPAttach(pDrvIns); 1275 # endif 909 1276 if (VBOX_FAILURE(rc)) 910 1277 return rc; … … 918 1285 } 919 1286 920 #else /* ! SOLARIS */1287 #else /* !RT_OS_SOLARIS */ 921 1288 922 1289 int32_t iFile; … … 929 1296 return PDMDrvHlpVMSetError(pDrvIns, VERR_INVALID_HANDLE, RT_SRC_POS, 930 1297 N_("The TAP file handle %RTfile is not valid!"), pData->FileDevice); 931 #endif /* ! SOLARIS */1298 #endif /* !RT_OS_SOLARIS */ 932 1299 933 1300 /*
Note:
See TracChangeset
for help on using the changeset viewer.