/* $Id: tstIntNet-1.cpp 11822 2008-08-29 14:21:03Z vboxsync $ */ /** @file * VBox - Testcase for internal networking, simple NetFlt trunk creation. */ /* * Copyright (C) 2006-2007 Sun Microsystems, Inc. * * 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. * * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa * Clara, CA 95054 USA or visit http://www.sun.com if you need * additional information or have any questions. */ /******************************************************************************* * Header Files * *******************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../Pcap.h" /******************************************************************************* * Global Variables * *******************************************************************************/ static int g_cErrors = 0; static uint64_t g_StartTS = 0; static uint32_t g_DhcpXID = 0; static bool g_fDhcpReply = false; static bool g_fPingReply = false; static uint32_t g_cOtherPkts = 0; static uint32_t g_cArpPkts = 0; static uint32_t g_cIpv4Pkts = 0; static uint32_t g_cUdpPkts = 0; static uint32_t g_cDhcpPkts = 0; static uint32_t g_cTcpPkts = 0; /** * Error reporting wrapper. * * @param pErrStrm The stream to write the error message to. Can be NULL. * @param pszFormat The message format string. * @param ... Format arguments. */ static void tstIntNetError(PRTSTREAM pErrStrm, const char *pszFormat, ...) { if (!pErrStrm) pErrStrm = g_pStdOut; va_list va; va_start(va, pszFormat); RTStrmPrintf(pErrStrm, "tstIntNet-1: ERROR - "); RTStrmPrintfV(pErrStrm, pszFormat, va); va_end(va); g_cErrors++; } /** * Parses a frame an runs in thru the RTNet validation code so it gets * some exercise. * * @param pvFrame Pointer to the ethernet frame. * @param cbFrame The size of the ethernet frame. * @param pErrStrm The error stream. */ static void tstIntNetTestFrame(void const *pvFrame, size_t cbFrame, PRTSTREAM pErrStrm) { /* * Ethernet header. */ PCRTNETETHERHDR pEtherHdr = (PCRTNETETHERHDR)pvFrame; if (cbFrame <= sizeof(*pEtherHdr)) return tstIntNetError(pErrStrm, "cbFrame=%#x <= %#x (ether)\n", cbFrame, sizeof(*pEtherHdr)); ssize_t cbLeft = cbFrame - sizeof(*pEtherHdr); uint8_t const *pbCur = (uint8_t const *)(pEtherHdr + 1); switch (RT_BE2H_U16(pEtherHdr->EtherType)) { case RTNET_ETHERTYPE_ARP: { g_cArpPkts++; break; } case RTNET_ETHERTYPE_IPV4: { g_cIpv4Pkts++; PCRTNETIPV4 pIpHdr = (PCRTNETIPV4)pbCur; if (!RTNetIPv4IsHdrValid(pIpHdr, cbLeft, cbLeft)) return tstIntNetError(pErrStrm, "RTNetIPv4IsHdrValid failed\n"); pbCur += pIpHdr->ip_hl * 4; cbLeft -= pIpHdr->ip_hl * 4; AssertFatal(cbLeft >= 0); switch (pIpHdr->ip_p) { case RTNETIPV4_PROT_ICMP: { /** @todo ICMP? */ break; } case RTNETIPV4_PROT_UDP: { g_cUdpPkts++; PCRTNETUDP pUdpHdr = (PCRTNETUDP)pbCur; if (!RTNetIPv4IsUDPValid(pIpHdr, pUdpHdr, pUdpHdr + 1, cbLeft)) return tstIntNetError(pErrStrm, "RTNetIPv4IsUDPValid failed\n"); pbCur += sizeof(*pUdpHdr); cbLeft -= sizeof(*pUdpHdr); if (RT_BE2H_U16(pUdpHdr->uh_dport) == RTNETIPV4_PORT_BOOTPS) { g_cDhcpPkts++; PCRTNETBOOTP pDhcp = (PCRTNETBOOTP)pbCur; if (!RTNetIPv4IsDHCPValid(pUdpHdr, pDhcp, cbLeft, NULL)) return tstIntNetError(pErrStrm, "RTNetIPv4IsDHCPValid failed\n"); } break; } case RTNETIPV4_PROT_TCP: { g_cTcpPkts++; PCRTNETTCP pTcpHdr = (PCRTNETTCP)pbCur; if (!RTNetIPv4IsTCPValid(pIpHdr, pTcpHdr, cbLeft, NULL, cbLeft)) return tstIntNetError(pErrStrm, "RTNetIPv4IsTCPValid failed\n"); break; } } break; } //case RTNET_ETHERTYPE_IPV6: default: g_cOtherPkts++; break; } } /** * Writes a frame packet to the buffer. * * @returns VBox status code. * @param pBuf The buffer. * @param pRingBuf The ring buffer to read from. * @param pvFrame The frame to write. * @param cbFrame The size of the frame. * @remark This is the same as INTNETRingWriteFrame and drvIntNetRingWriteFrame. */ static int tstIntNetWriteFrame(PINTNETBUF pBuf, PINTNETRINGBUF pRingBuf, const void *pvFrame, uint32_t cbFrame) { /* * Validate input. */ Assert(pBuf); Assert(pRingBuf); Assert(pvFrame); Assert(cbFrame >= sizeof(RTMAC) * 2); uint32_t offWrite = pRingBuf->offWrite; Assert(offWrite == RT_ALIGN_32(offWrite, sizeof(INTNETHDR))); uint32_t offRead = pRingBuf->offRead; Assert(offRead == RT_ALIGN_32(offRead, sizeof(INTNETHDR))); const uint32_t cb = RT_ALIGN_32(cbFrame, sizeof(INTNETHDR)); if (offRead <= offWrite) { /* * Try fit it all before the end of the buffer. */ if (pRingBuf->offEnd - offWrite >= cb + sizeof(INTNETHDR)) { PINTNETHDR pHdr = (PINTNETHDR)((uint8_t *)pBuf + offWrite); pHdr->u16Type = INTNETHDR_TYPE_FRAME; pHdr->cbFrame = cbFrame; pHdr->offFrame = sizeof(INTNETHDR); memcpy(pHdr + 1, pvFrame, cbFrame); offWrite += cb + sizeof(INTNETHDR); Assert(offWrite <= pRingBuf->offEnd && offWrite >= pRingBuf->offStart); if (offWrite >= pRingBuf->offEnd) offWrite = pRingBuf->offStart; Log2(("WriteFrame: offWrite: %#x -> %#x (1)\n", pRingBuf->offWrite, offWrite)); ASMAtomicXchgU32(&pRingBuf->offWrite, offWrite); return VINF_SUCCESS; } /* * Try fit the frame at the start of the buffer. * (The header fits before the end of the buffer because of alignment.) */ AssertMsg(pRingBuf->offEnd - offWrite >= sizeof(INTNETHDR), ("offEnd=%x offWrite=%x\n", pRingBuf->offEnd, offWrite)); if (offRead - pRingBuf->offStart > cb) /* not >= ! */ { PINTNETHDR pHdr = (PINTNETHDR)((uint8_t *)pBuf + offWrite); void *pvFrameOut = (PINTNETHDR)((uint8_t *)pBuf + pRingBuf->offStart); pHdr->u16Type = INTNETHDR_TYPE_FRAME; pHdr->cbFrame = cbFrame; pHdr->offFrame = (intptr_t)pvFrameOut - (intptr_t)pHdr; memcpy(pvFrameOut, pvFrame, cbFrame); offWrite = pRingBuf->offStart + cb; ASMAtomicXchgU32(&pRingBuf->offWrite, offWrite); Log2(("WriteFrame: offWrite: %#x -> %#x (2)\n", pRingBuf->offWrite, offWrite)); return VINF_SUCCESS; } } /* * The reader is ahead of the writer, try fit it into that space. */ else if (offRead - offWrite > cb + sizeof(INTNETHDR)) /* not >= ! */ { PINTNETHDR pHdr = (PINTNETHDR)((uint8_t *)pBuf + offWrite); pHdr->u16Type = INTNETHDR_TYPE_FRAME; pHdr->cbFrame = cbFrame; pHdr->offFrame = sizeof(INTNETHDR); memcpy(pHdr + 1, pvFrame, cbFrame); offWrite += cb + sizeof(INTNETHDR); ASMAtomicXchgU32(&pRingBuf->offWrite, offWrite); Log2(("WriteFrame: offWrite: %#x -> %#x (3)\n", pRingBuf->offWrite, offWrite)); return VINF_SUCCESS; } /* (it didn't fit) */ /** @todo stats */ return VERR_BUFFER_OVERFLOW; } /** * Transmits one frame after appending the CRC. * * @param hIf The interface handle. * @param pSession The session. * @param pBuf The shared interface buffer. * @param pvFrame The frame without a crc. * @param cbFrame The size of it. * @param pFileRaw The file to write the raw data to (optional). * @param pFileText The file to write a textual packet summary to (optional). */ static void doXmitFrame(INTNETIFHANDLE hIf, PSUPDRVSESSION pSession, PINTNETBUF pBuf, void *pvFrame, size_t cbFrame, PRTSTREAM pFileRaw, PRTSTREAM pFileText) { /* * Log it. */ if (pFileText) { PCRTNETETHERHDR pEthHdr = (PCRTNETETHERHDR)pvFrame; uint64_t NanoTS = RTTimeNanoTS() - g_StartTS; RTStrmPrintf(pFileText, "%3RU64.%09u: cb=%04x dst=%.6Rhxs src=%.6Rhxs type=%04x Send!\n", NanoTS / 1000000000, (uint32_t)(NanoTS % 1000000000), cbFrame, &pEthHdr->SrcMac, &pEthHdr->DstMac, RT_BE2H_U16(pEthHdr->EtherType)); } /* * Run in thru the frame validator to test the RTNet code. */ tstIntNetTestFrame(pvFrame, cbFrame, pFileText); /* * Write the frame and push the queue. * * Don't bother with dealing with overflows like DrvIntNet does, because * it's not supposed to happen here in this testcase. */ int rc = tstIntNetWriteFrame(pBuf, &pBuf->Send, pvFrame, cbFrame); if (RT_SUCCESS(rc)) { if (pFileRaw) PcapStreamFrame(pFileRaw, g_StartTS, pvFrame, cbFrame, 0xffff); } else { RTPrintf("tstIntNet-1: tstIntNetWriteFrame failed, %Rrc; cbFrame=%d pBuf->cbSend=%d\n", rc, cbFrame, pBuf->cbSend); g_cErrors++; } INTNETIFSENDREQ SendReq; SendReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC; SendReq.Hdr.cbReq = sizeof(SendReq); SendReq.pSession = pSession; SendReq.hIf = hIf; rc = SUPCallVMMR0Ex(NIL_RTR0PTR, VMMR0_DO_INTNET_IF_SEND, 0, &SendReq.Hdr); if (RT_FAILURE(rc)) { RTPrintf("tstIntNet-1: SUPCallVMMR0Ex(,VMMR0_DO_INTNET_IF_SEND,) failed, rc=%Rrc\n", rc); g_cErrors++; } } /** * Does the transmit test. * * @param hIf The interface handle. * @param pSession The session. * @param pBuf The shared interface buffer. * @param pSrcMac The mac address to use as source. * @param pFileRaw The file to write the raw data to (optional). * @param pFileText The file to write a textual packet summary to (optional). */ static void doXmitTest(INTNETIFHANDLE hIf, PSUPDRVSESSION pSession, PINTNETBUF pBuf, PCRTMAC pSrcMac, PRTSTREAM pFileRaw, PRTSTREAM pFileText) { uint8_t abFrame[4096]; PRTNETETHERHDR pEthHdr = (PRTNETETHERHDR)&abFrame[0]; PRTNETIPV4 pIpHdr = (PRTNETIPV4) (pEthHdr + 1); PRTNETUDP pUdpHdr = (PRTNETUDP) (pIpHdr + 1); PRTNETDHCP pDhcpMsg = (PRTNETDHCP) (pUdpHdr + 1); /* * Create a simple DHCP broadcast request. */ memset(&abFrame, 0, sizeof(abFrame)); pDhcpMsg->Op = 1; /* request */ pDhcpMsg->HType = 1; /* ethernet */ pDhcpMsg->HLen = sizeof(RTMAC); pDhcpMsg->Hops = 0; pDhcpMsg->XID = g_DhcpXID = RTRandU32(); pDhcpMsg->Secs = 0; pDhcpMsg->Flags = 0; /* unicast */ //RT_H2BE_U16(0x8000); /* broadcast */ pDhcpMsg->CIAddr.u = 0; pDhcpMsg->YIAddr.u = 0; pDhcpMsg->SIAddr.u = 0; pDhcpMsg->GIAddr.u = 0; memset(&pDhcpMsg->CHAddr[0], '\0', sizeof(pDhcpMsg->CHAddr)); memcpy(&pDhcpMsg->CHAddr[0], pSrcMac, sizeof(*pSrcMac)); memset(&pDhcpMsg->SName[0], '\0', sizeof(pDhcpMsg->SName)); memset(&pDhcpMsg->File[0], '\0', sizeof(pDhcpMsg->File)); pDhcpMsg->abMagic[0] = 99; pDhcpMsg->abMagic[1] = 130; pDhcpMsg->abMagic[2] = 83; pDhcpMsg->abMagic[3] = 99; pDhcpMsg->DhcpOpt = 53; /* DHCP Msssage Type option */ pDhcpMsg->DhcpLen = 1; pDhcpMsg->DhcpReq = 1; /* DHCPDISCOVER */ memset(&pDhcpMsg->abOptions[0], '\0', sizeof(pDhcpMsg->abOptions)); uint8_t *pbOpt = &pDhcpMsg->abOptions[0]; *pbOpt++ = 116; /* DHCP Auto-Configure */ *pbOpt++ = 1; *pbOpt++ = 1; *pbOpt++ = 61; /* Client identifier */ *pbOpt++ = 1 + sizeof(*pSrcMac); *pbOpt++ = 1; /* hw type: ethernet */ memcpy(pbOpt, pSrcMac, sizeof(*pSrcMac)); pbOpt += sizeof(*pSrcMac); *pbOpt++ = 12; /* Host name */ *pbOpt++ = sizeof("tstIntNet-1") - 1; memcpy(pbOpt, "tstIntNet-1", sizeof("tstIntNet-1") - 1); pbOpt += sizeof("tstIntNet-1") - 1; *pbOpt = 0xff; /* the end */ /* UDP */ pUdpHdr->uh_sport = RT_H2BE_U16(68); /* bootp */ pUdpHdr->uh_dport = RT_H2BE_U16(67); /* bootps */ pUdpHdr->uh_ulen = RT_H2BE_U16(sizeof(*pDhcpMsg) + sizeof(*pUdpHdr)); pUdpHdr->uh_sum = 0; /* pretend checksumming is disabled */ /* IP */ pIpHdr->ip_v = 4; pIpHdr->ip_hl = sizeof(*pIpHdr) / sizeof(uint32_t); pIpHdr->ip_tos = 0; pIpHdr->ip_len = RT_H2BE_U16(sizeof(*pDhcpMsg) + sizeof(*pUdpHdr) + sizeof(*pIpHdr)); pIpHdr->ip_id = (uint16_t)RTRandU32(); pIpHdr->ip_off = 0; pIpHdr->ip_ttl = 255; pIpHdr->ip_p = 0x11; /* UDP */ pIpHdr->ip_sum = 0; pIpHdr->ip_src.u = 0; pIpHdr->ip_dst.u = UINT32_C(0xffffffff); /* broadcast */ pIpHdr->ip_sum = RTNetIPv4HdrChecksum(pIpHdr); /* calc the UDP checksum. */ pUdpHdr->uh_sum = RTNetIPv4UDPChecksum(pIpHdr, pUdpHdr, pUdpHdr + 1); /* Ethernet */ memset(&pEthHdr->DstMac, 0xff, sizeof(pEthHdr->DstMac)); /* broadcast */ pEthHdr->SrcMac = *pSrcMac; pEthHdr->EtherType = RT_H2BE_U16(RTNET_ETHERTYPE_IPV4); /* IP */ doXmitFrame(hIf, pSession, pBuf, &abFrame[0], (uint8_t *)(pDhcpMsg + 1) - (uint8_t *)&abFrame[0], pFileRaw, pFileText); } static uint16_t icmpChecksum(PRTNETICMPV4HDR pHdr, int cbHdr) { int cbLeft = cbHdr; uint16_t *pbSrc = (uint16_t *)pHdr; uint16_t oddByte = 0; int cSum = 0; while (cbLeft > 1) { cSum += *pbSrc++; cbLeft -= 2; } if (cbLeft == 1) { *(uint16_t *)(&oddByte) = *(uint16_t *)pbSrc; cSum += oddByte; } cSum = (cSum >> 16) + (cSum & 0xffff); cSum += (cSum >> 16); uint16_t Result = ~cSum; return Result; } /** * Does the rudimentary ping test with fixed destination and source IPs. * * @param hIf The interface handle. * @param pSession The session. * @param pBuf The shared interface buffer. * @param pSrcMac The mac address to use as source. * @param pFileRaw The file to write the raw data to (optional). * @param pFileText The file to write a textual packet summary to (optional). */ static void doPingTest(INTNETIFHANDLE hIf, PSUPDRVSESSION pSession, PINTNETBUF pBuf, PCRTMAC pSrcMac, PRTSTREAM pFileRaw, PRTSTREAM pFileText) { uint8_t abFrame[4096]; PRTNETETHERHDR pEthHdr = (PRTNETETHERHDR)&abFrame[0]; PRTNETIPV4 pIpHdr = (PRTNETIPV4) (pEthHdr + 1); PRTNETICMPV4ECHO pIcmpEcho = (PRTNETICMPV4ECHO) (pIpHdr + 1); /* * Create a simple ping request. */ memset(&abFrame, 0, sizeof(abFrame)); pIcmpEcho->Hdr.icmp_type = RTNETICMPV4_TYPE_ECHO_REQUEST; pIcmpEcho->Hdr.icmp_code = 0; pIcmpEcho->icmp_id = 0x06; pIcmpEcho->icmp_seq = 0x05; size_t cbPad = 56; memset(&pIcmpEcho->icmp_data, '\0', cbPad); pIcmpEcho->Hdr.icmp_cksum = icmpChecksum(&pIcmpEcho->Hdr, cbPad + 8); /* IP */ pIpHdr->ip_v = 4; pIpHdr->ip_hl = sizeof(*pIpHdr) / sizeof(uint32_t); pIpHdr->ip_tos = 0; pIpHdr->ip_len = RT_H2BE_U16(sizeof(*pIcmpEcho) + cbPad + sizeof(*pIpHdr)); pIpHdr->ip_id = (uint16_t)RTRandU32(); pIpHdr->ip_off = 0; pIpHdr->ip_ttl = 255; pIpHdr->ip_p = 0x01; /*ICMP */ pIpHdr->ip_sum = 0; pIpHdr->ip_src.u = UINT32_C(0x9701A8C0); /* 192.168.1.151 */ pIpHdr->ip_dst.u = UINT32_C(0xF9A344D0); /* 208.68.163.249 */ pIpHdr->ip_sum = RTNetIPv4HdrChecksum(pIpHdr); /* Ethernet */ memset(&pEthHdr->DstMac, 0xff, sizeof(pEthHdr->DstMac)); /* broadcast */ pEthHdr->SrcMac = *pSrcMac; #if 0 /* Enable with host's real Mac address for testing of the testcase. */ pEthHdr->SrcMac.au8[0] = 0x00; pEthHdr->SrcMac.au8[1] = 0x1b; pEthHdr->SrcMac.au8[2] = 0x24; pEthHdr->SrcMac.au8[3] = 0xa0; pEthHdr->SrcMac.au8[4] = 0x2f; pEthHdr->SrcMac.au8[5] = 0xce; #endif pEthHdr->EtherType = RT_H2BE_U16(RTNET_ETHERTYPE_IPV4); /* IP */ doXmitFrame(hIf, pSession, pBuf, &abFrame[0], (uint8_t *)(pIcmpEcho + 1) + cbPad - (uint8_t *)&abFrame[0], pFileRaw, pFileText); } /** * Does packet sniffing for a given period of time. * * @param hIf The interface handle. * @param pSession The session. * @param pBuf The shared interface buffer. * @param cMillies The time period, ms. * @param pFileRaw The file to write the raw data to (optional). * @param pFileText The file to write a textual packet summary to (optional). * @param pSrcMac Out MAC address. */ static void doPacketSniffing(INTNETIFHANDLE hIf, PSUPDRVSESSION pSession, PINTNETBUF pBuf, uint32_t cMillies, PRTSTREAM pFileRaw, PRTSTREAM pFileText, PCRTMAC pSrcMac) { /* * The loop. */ PINTNETRINGBUF pRingBuf = &pBuf->Recv; for (;;) { /* * Wait for a packet to become available. */ uint64_t cElapsedMillies = (RTTimeNanoTS() - g_StartTS) / 1000000; if (cElapsedMillies >= cMillies) break; INTNETIFWAITREQ WaitReq; WaitReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC; WaitReq.Hdr.cbReq = sizeof(WaitReq); WaitReq.pSession = pSession; WaitReq.hIf = hIf; WaitReq.cMillies = cMillies - (uint32_t)cElapsedMillies; int rc = SUPCallVMMR0Ex(NIL_RTR0PTR, VMMR0_DO_INTNET_IF_WAIT, 0, &WaitReq.Hdr); if (rc == VERR_TIMEOUT) break; if (RT_FAILURE(rc)) { g_cErrors++; RTPrintf("tstIntNet-1: VMMR0_DO_INTNET_IF_WAIT returned %Rrc\n", rc); break; } /* * Process the receive buffer. */ while (INTNETRingGetReadable(pRingBuf) > 0) { PINTNETHDR pHdr = (PINTNETHDR)((uintptr_t)pBuf + pRingBuf->offRead); if (pHdr->u16Type == INTNETHDR_TYPE_FRAME) { size_t cbFrame = pHdr->cbFrame; const void *pvFrame = INTNETHdrGetFramePtr(pHdr, pBuf); uint64_t NanoTS = RTTimeNanoTS() - g_StartTS; if (pFileRaw) PcapStreamFrame(pFileRaw, g_StartTS, pvFrame, cbFrame, 0xffff); PCRTNETETHERHDR pEthHdr = (PCRTNETETHERHDR)pvFrame; if (pFileText) RTStrmPrintf(pFileText, "%3RU64.%09u: cb=%04x dst=%.6Rhxs src=%.6Rhxs type=%04x%s\n", NanoTS / 1000000000, (uint32_t)(NanoTS % 1000000000), cbFrame, &pEthHdr->DstMac, &pEthHdr->SrcMac, RT_BE2H_U16(pEthHdr->EtherType), !memcmp(&pEthHdr->DstMac, pSrcMac, sizeof(*pSrcMac)) ? " Mine!" : ""); tstIntNetTestFrame(pvFrame, cbFrame, pFileText); /* Loop for the DHCP reply. */ if ( cbFrame > 64 && RT_BE2H_U16(pEthHdr->EtherType) == 0x0800 /* EtherType == IP */) { PCRTNETIPV4 pIpHdr = (PCRTNETIPV4)(pEthHdr + 1); PCRTNETUDP pUdpHdr = (PCRTNETUDP)((uint32_t *)pIpHdr + pIpHdr->ip_hl); if ( pIpHdr->ip_p == 0x11 /*UDP*/ && RT_BE2H_U16(pUdpHdr->uh_dport) == 68 /* bootp */ && RT_BE2H_U16(pUdpHdr->uh_sport) == 67 /* bootps */) { PCRTNETDHCP pDhcpMsg = (PCRTNETDHCP)(pUdpHdr + 1); if ( pDhcpMsg->Op == 2 /* boot reply */ && pDhcpMsg->HType == 1 /* ethernet */ && pDhcpMsg->HLen == sizeof(RTMAC) && (pDhcpMsg->XID == g_DhcpXID || !g_DhcpXID) && !memcmp(&pDhcpMsg->CHAddr[0], pSrcMac, sizeof(*pSrcMac))) { g_fDhcpReply = true; RTPrintf("tstIntNet-1: DHCP server reply! My IP: %d.%d.%d.%d\n", pDhcpMsg->YIAddr.au8[0], pDhcpMsg->YIAddr.au8[1], pDhcpMsg->YIAddr.au8[2], pDhcpMsg->YIAddr.au8[3]); } } else if (pIpHdr->ip_p == 0x01) /* ICMP */ { PRTNETICMPV4HDR pIcmpHdr = (PRTNETICMPV4HDR)(pIpHdr + 1); PRTNETICMPV4ECHO pIcmpEcho = (PRTNETICMPV4ECHO)(pIpHdr + 1); if ( pIcmpHdr->icmp_type == RTNETICMPV4_TYPE_ECHO_REPLY && pIcmpEcho->icmp_seq == 0x05 && pIpHdr->ip_dst.u == UINT32_C(0x9701A8C0) #if 0 /** Enable with the host's real Mac address for testing of the testcase.*/ && pEthHdr->DstMac.au8[0] == 0x00 && pEthHdr->DstMac.au8[1] == 0x1b && pEthHdr->DstMac.au8[2] == 0x24 && pEthHdr->DstMac.au8[3] == 0xa0 && pEthHdr->DstMac.au8[4] == 0x2f && pEthHdr->DstMac.au8[5] == 0xce #else && pEthHdr->DstMac.au16[0] == pSrcMac->au16[0] && pEthHdr->DstMac.au16[1] == pSrcMac->au16[1] && pEthHdr->DstMac.au16[2] == pSrcMac->au16[2] #endif ) { g_fPingReply = true; RTPrintf("tstIntNet-1: Ping reply! From %d.%d.%d.%d\n", pIpHdr->ip_src.au8[0], pIpHdr->ip_src.au8[1], pIpHdr->ip_src.au8[2], pIpHdr->ip_src.au8[3]); } else RTPrintf("type=%d seq=%d dstmac=%.6Rhxs ip=%d.%d.%d.%d\n", pIcmpHdr->icmp_type, pIcmpEcho->icmp_seq, &pEthHdr->DstMac, pIpHdr->ip_dst.au8[0], pIpHdr->ip_dst.au8[1], pIpHdr->ip_dst.au8[2], pIpHdr->ip_dst.au8[3]); } } } else { RTPrintf("tstIntNet-1: Unknown frame type %d\n", pHdr->u16Type); g_cErrors++; } /* Advance to the next frame. */ INTNETRingSkipFrame(pBuf, pRingBuf); } } uint64_t NanoTS = RTTimeNanoTS() - g_StartTS; RTStrmPrintf(pFileText ? pFileText : g_pStdOut, "%3RU64.%09u: stopped. cRecvs=%RU64 cbRecv=%RU64 cLost=%RU64 cOYs=%RU64 cNYs=%RU64\n", NanoTS / 1000000000, (uint32_t)(NanoTS % 1000000000), pBuf->cStatRecvs.c, pBuf->cbStatRecv.c, pBuf->cStatLost.c, pBuf->cStatYieldsOk.c, pBuf->cStatYieldsNok.c ); RTStrmPrintf(pFileText ? pFileText : g_pStdOut, "%3RU64.%09u: cOtherPkts=%RU32 cArpPkts=%RU32 cIpv4Pkts=%RU32 cTcpPkts=%RU32 cUdpPkts=%RU32 cDhcpPkts=%RU32\n", NanoTS / 1000000000, (uint32_t)(NanoTS % 1000000000), g_cOtherPkts, g_cArpPkts, g_cIpv4Pkts, g_cTcpPkts, g_cUdpPkts, g_cDhcpPkts); } int main(int argc, char **argv) { /* * Init the runtime and parse the arguments. */ RTR3Init(); static RTOPTIONDEF const s_aOptions[] = { { "--duration", 'd', RTGETOPT_REQ_UINT32 }, { "--file", 'f', RTGETOPT_REQ_STRING }, { "--interface", 'i', RTGETOPT_REQ_STRING }, { "--mac-sharing", 'm', RTGETOPT_REQ_NOTHING }, { "--network", 'n', RTGETOPT_REQ_STRING }, { "--promiscuous", 'p', RTGETOPT_REQ_NOTHING }, { "--recv-buffer", 'r', RTGETOPT_REQ_UINT32 }, { "--send-buffer", 's', RTGETOPT_REQ_UINT32 }, { "--sniffer", 'S', RTGETOPT_REQ_NOTHING }, { "--text-file", 't', RTGETOPT_REQ_STRING }, { "--xmit-test", 'x', RTGETOPT_REQ_NOTHING }, { "--ping-test", 'P', RTGETOPT_REQ_NOTHING }, { "--help", 'h', RTGETOPT_REQ_NOTHING }, { "--?", '?', RTGETOPT_REQ_NOTHING }, }; uint32_t cMillies = 1000; PRTSTREAM pFileRaw = NULL; #ifdef RT_OS_DARWIN const char *pszIf = "en0"; #elif defined(RT_OS_LINUX) const char *pszIf = "eth0"; #elif defined(RT_OS_SOLARIS) const char* pszIf = "rge0"; #else const char *pszIf = "em0"; #endif bool fMacSharing = false; const char *pszNetwork = "tstIntNet-1"; bool fPromiscuous = false; uint32_t cbRecv = 0; uint32_t cbSend = 0; bool fSniffer = false; PRTSTREAM pFileText = g_pStdOut; bool fXmitTest = false; bool fPingTest = false; RTMAC SrcMac; SrcMac.au8[0] = 0x08; SrcMac.au8[1] = 0x03; SrcMac.au8[2] = 0x86; RTRandBytes(&SrcMac.au8[3], sizeof(SrcMac) - 3); int rc; int ch; int iArg = 1; RTOPTIONUNION Value; while ((ch = RTGetOpt(argc,argv, &s_aOptions[0], RT_ELEMENTS(s_aOptions), &iArg, &Value))) switch (ch) { case 'd': cMillies = Value.u32 * 1000; if (cMillies / 1000 != Value.u32) { RTPrintf("tstIntNet-1: warning duration overflowed\n"); cMillies = UINT32_MAX - 1; } break; case 'f': rc = RTStrmOpen(Value.psz, "w+b", &pFileRaw); if (RT_FAILURE(rc)) { RTPrintf("tstIntNet-1: Failed to creating \"%s\" for writing: %Rrc\n", Value.psz, rc); return 1; } break; case 'i': pszIf = Value.psz; if (strlen(pszIf) >= INTNET_MAX_TRUNK_NAME) { RTPrintf("tstIntNet-1: Interface name is too long (max %d chars): %s\n", INTNET_MAX_TRUNK_NAME - 1, pszIf); return 1; } break; case 'm': fMacSharing = true; break; case 'n': pszNetwork = Value.psz; if (strlen(pszNetwork) >= INTNET_MAX_NETWORK_NAME) { RTPrintf("tstIntNet-1: Network name is too long (max %d chars): %s\n", INTNET_MAX_NETWORK_NAME - 1, pszNetwork); return 1; } break; case 'p': fPromiscuous = true; break; case 'r': cbRecv = Value.u32; break; case 's': cbSend = Value.u32; break; case 'S': fSniffer = true; break; case 't': if (!*Value.psz) pFileText = NULL; else if (!strcmp(Value.psz, "-")) pFileText = g_pStdOut; else if (!strcmp(Value.psz, "!")) pFileText = g_pStdErr; else { rc = RTStrmOpen(Value.psz, "w", &pFileText); if (RT_FAILURE(rc)) { RTPrintf("tstIntNet-1: Failed to creating \"%s\" for writing: %Rrc\n", Value.psz, rc); return 1; } } break; case 'x': fXmitTest = true; break; case 'P': fPingTest = true; break; case '?': case 'h': RTPrintf("syntax: tstIntNet-1 [-pStx-] [-d ] [-f ] [-r ] [-s ]\n"); return 1; default: if (RT_SUCCESS(ch)) RTPrintf("tstIntNetR0: invalid argument (%#x): %s\n", ch, Value.psz); else if (Value.pDef) RTPrintf("tstIntNetR0: invalid argument: %Rrc - %s\n", ch, Value.pDef->pszLong); else RTPrintf("tstIntNetR0: invalid argument: %Rrc - %s\n", ch, argv[iArg]); return 1; } if (iArg < argc) { RTPrintf("tstIntNetR0: invalid argument: %s\n", argv[iArg]); return 1; } RTPrintf("tstIntNet-1: TESTING...\n"); /* * Open the session, load ring-0 and issue the request. */ PSUPDRVSESSION pSession; rc = SUPR3Init(&pSession); if (RT_FAILURE(rc)) { RTPrintf("tstIntNet-1: SUPR3Init -> %Rrc\n", rc); return 1; } char szPath[RTPATH_MAX]; rc = RTPathProgram(szPath, sizeof(szPath) - sizeof("/../VMMR0.r0")); if (RT_FAILURE(rc)) { RTPrintf("tstIntNet-1: RTPathProgram -> %Rrc\n", rc); return 1; } rc = SUPLoadVMM(strcat(szPath, "/../VMMR0.r0")); if (RT_FAILURE(rc)) { RTPrintf("tstIntNet-1: SUPLoadVMM(\"%s\") -> %Rrc\n", szPath, rc); return 1; } /* * Create the request, picking the network and trunk names from argv[2] * and argv[1] if present. */ INTNETOPENREQ OpenReq; OpenReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC; OpenReq.Hdr.cbReq = sizeof(OpenReq); OpenReq.pSession = pSession; strncpy(OpenReq.szNetwork, pszNetwork, sizeof(OpenReq.szNetwork)); strncpy(OpenReq.szTrunk, pszIf, sizeof(OpenReq.szTrunk)); OpenReq.enmTrunkType = kIntNetTrunkType_NetFlt; OpenReq.fFlags = fMacSharing ? INTNET_OPEN_FLAGS_SHARED_MAC_ON_WIRE : 0; OpenReq.cbSend = cbSend; OpenReq.cbRecv = cbRecv; OpenReq.hIf = INTNET_HANDLE_INVALID; /* * Issue the request. */ RTPrintf("tstIntNet-1: attempting to open/create network \"%s\" with NetFlt trunk \"%s\"...\n", OpenReq.szNetwork, OpenReq.szTrunk); RTStrmFlush(g_pStdOut); rc = SUPCallVMMR0Ex(NIL_RTR0PTR, VMMR0_DO_INTNET_OPEN, 0, &OpenReq.Hdr); if (RT_SUCCESS(rc)) { RTPrintf("tstIntNet-1: successfully opened/created \"%s\" with NetFlt trunk \"%s\" - hIf=%#x\n", OpenReq.szNetwork, OpenReq.szTrunk, OpenReq.hIf); RTStrmFlush(g_pStdOut); /* * Get the ring-3 address of the shared interface buffer. */ INTNETIFGETRING3BUFFERREQ GetRing3BufferReq; GetRing3BufferReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC; GetRing3BufferReq.Hdr.cbReq = sizeof(GetRing3BufferReq); GetRing3BufferReq.pSession = pSession; GetRing3BufferReq.hIf = OpenReq.hIf; GetRing3BufferReq.pRing3Buf = NULL; rc = SUPCallVMMR0Ex(NIL_RTR0PTR, VMMR0_DO_INTNET_IF_GET_RING3_BUFFER, 0, &GetRing3BufferReq.Hdr); if (RT_SUCCESS(rc)) { PINTNETBUF pBuf = GetRing3BufferReq.pRing3Buf; RTPrintf("tstIntNet-1: pBuf=%p cbBuf=%d cbSend=%d cbRecv=%d\n", pBuf, pBuf->cbBuf, pBuf->cbSend, pBuf->cbRecv); RTStrmFlush(g_pStdOut); if (fPromiscuous) { INTNETIFSETPROMISCUOUSMODEREQ PromiscReq; PromiscReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC; PromiscReq.Hdr.cbReq = sizeof(PromiscReq); PromiscReq.pSession = pSession; PromiscReq.hIf = OpenReq.hIf; PromiscReq.fPromiscuous = true; rc = SUPCallVMMR0Ex(NIL_RTR0PTR, VMMR0_DO_INTNET_IF_SET_PROMISCUOUS_MODE, 0, &PromiscReq.Hdr); if (RT_SUCCESS(rc)) RTPrintf("tstIntNet-1: interface in promiscuous mode\n"); } if (RT_SUCCESS(rc)) { /* * Activate the interface. */ INTNETIFSETACTIVEREQ ActiveReq; ActiveReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC; ActiveReq.Hdr.cbReq = sizeof(ActiveReq); ActiveReq.pSession = pSession; ActiveReq.hIf = OpenReq.hIf; ActiveReq.fActive = true; rc = SUPCallVMMR0Ex(NIL_RTR0PTR, VMMR0_DO_INTNET_IF_SET_ACTIVE, 0, &ActiveReq.Hdr); if (RT_SUCCESS(rc)) { /* * Start the stop watch, init the pcap file. */ g_StartTS = RTTimeNanoTS(); if (pFileRaw) PcapStreamHdr(pFileRaw, g_StartTS); /* * Do the transmit test first and so we can sniff for the response. */ if (fXmitTest) doXmitTest(OpenReq.hIf, pSession, pBuf, &SrcMac, pFileRaw, pFileText); if (fPingTest) doPingTest(OpenReq.hIf, pSession, pBuf, &SrcMac, pFileRaw, pFileText); /* * Either enter sniffing mode or do a timeout thing. */ if (fSniffer) { doPacketSniffing(OpenReq.hIf, pSession, pBuf, cMillies, pFileRaw, pFileText, &SrcMac); if ( fXmitTest && !g_fDhcpReply) { RTPrintf("tstIntNet-1: Error! The DHCP server didn't reply... (Perhaps you don't have one?)\n", rc); g_cErrors++; } if ( fPingTest && !g_fPingReply) { RTPrintf("tstIntNet-1: Error! No reply for ping request...\n", rc); g_cErrors++; } } else RTThreadSleep(cMillies); } else { RTPrintf("tstIntNet-1: SUPCallVMMR0Ex(,VMMR0_DO_INTNET_IF_SET_PROMISCUOUS_MODE,) failed, rc=%Rrc\n", rc); g_cErrors++; } } else { RTPrintf("tstIntNet-1: SUPCallVMMR0Ex(,VMMR0_DO_INTNET_IF_SET_PROMISCUOUS_MODE,) failed, rc=%Rrc\n", rc); g_cErrors++; } } else { RTPrintf("tstIntNet-1: SUPCallVMMR0Ex(,VMMR0_DO_INTNET_IF_GET_RING3_BUFFER,) failed, rc=%Rrc\n", rc); g_cErrors++; } } else { RTPrintf("tstIntNet-1: SUPCallVMMR0Ex(,VMMR0_DO_INTNET_OPEN,) failed, rc=%Rrc\n", rc); g_cErrors++; } SUPTerm(false /* not forced */); /* close open files */ if (pFileRaw) RTStrmClose(pFileRaw); if (pFileText && pFileText != g_pStdErr && pFileText != g_pStdOut) RTStrmClose(pFileText); /* * Summary. */ if (!g_cErrors) RTPrintf("tstIntNet-1: SUCCESS\n"); else RTPrintf("tstIntNet-1: FAILURE - %d errors\n", g_cErrors); return !!g_cErrors; }