tstIntNet-1.cpp revision ae2dc98b9ebb20c607be0ff6322f4ed68133edcc
/* $Id$ */
/** @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;
* 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 <iprt/initterm.h>
#include "../Pcap.h"
/*******************************************************************************
* Structures and Typedefs *
*******************************************************************************/
#pragma pack(1)
struct MyEthHdr
{
};
struct MyIpHdr
{
#ifdef RT_BIG_ENDIAN
unsigned int ip_v : 4;
unsigned int ip_hl : 4;
unsigned int ip_tos : 8;
unsigned int ip_len : 16;
#else
unsigned int ip_hl : 4;
unsigned int ip_v : 4;
unsigned int ip_tos : 8;
unsigned int ip_len : 16;
#endif
/* more */
};
struct MyUdpHdr
{
};
struct MyDhcpMsg
{
};
#pragma pack(0)
/*******************************************************************************
* Global Variables *
*******************************************************************************/
static int g_cErrors = 0;
static bool g_fDhcpReply = false;
/**
* 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.
*/
{
/*
* Try fit it all before the end of the buffer.
*/
{
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));
{
return VINF_SUCCESS;
}
}
/*
* The reader is ahead of the writer, try fit it into that space.
*/
{
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).
*/
static void doXmitFrame(INTNETIFHANDLE hIf, PSUPDRVSESSION pSession, PINTNETBUF pBuf, void *pvFrame, size_t cbFrame, PRTSTREAM pFileRaw)
{
/*
* Calcuate and append the checksum.
*/
/*
* 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.
*/
if (RT_SUCCESS(rc))
{
if (pFileRaw)
}
else
{
RTPrintf("tstIntNet-1: tstIntNetWriteFrame failed, %Rrc; cbFrame=%d pBuf->cbSend=%d\n", rc, cbFrame, pBuf->cbSend);
g_cErrors++;
}
if (RT_FAILURE(rc))
{
g_cErrors++;
}
}
/**
* Internt protocol checksumming
* This is great fun because of the pseudo header.
*/
static uint16_t tstIntNet1InetCheckSum(void const *pvBuf, size_t cbBuf, uint32_t u32Src, uint32_t u32Dst, uint8_t u8Proto)
{
/*
* Construct the pseudo header and sum it.
*/
struct pseudo_header
{
} s =
{
0,
};
AssertCompileSize(s, 12);
/*
* Continue with protocol header and data.
*/
while (cbBuf > 1)
{
cbBuf -= 2;
}
/* deal with odd size */
if (cbBuf)
{
u16.u = 0;
}
/* 16-bit one complement fun */
}
/**
* IP checksumming
*/
{
while (cbBuf > 1)
{
cbBuf -= 2;
}
/* deal with odd size */
if (cbBuf)
{
u16.u = 0;
}
/* 16-bit one complement fun */
}
/**
* 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).
*/
static void doXmitText(INTNETIFHANDLE hIf, PSUPDRVSESSION pSession, PINTNETBUF pBuf, PCPDMMAC pSrcMac, PRTSTREAM pFileRaw)
{
/*
* Create a simple DHCP broadcast request.
*/
*pbOpt++ = 1;
*pbOpt++ = 1;
/* UDP */
/* IP */
/* calc the UDP checksum. */
/* Ethernet */
doXmitFrame(hIf, pSession, pBuf, &abFrame[0], (uint8_t *)(pDhcpMsg + 1) - (uint8_t *)&abFrame[0], pFileRaw);
}
/**
* 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,
{
/*
* The loop.
*/
for (;;)
{
/*
* Wait for a packet to become available.
*/
if (cElapsedMillies >= cMillies)
break;
if (rc == VERR_TIMEOUT)
break;
if (RT_FAILURE(rc))
{
g_cErrors++;
break;
}
/*
* Process the receive buffer.
*/
while (INTNETRingGetReadable(pRingBuf) > 0)
{
{
if (pFileRaw)
if (pFileText)
/* Loop for the DHCP reply. */
if ( cbFrame > 64
{
{
{
g_fDhcpReply = true;
RTPrintf("tstIntNet-1: DHCP server reply! My IP: %d.%d.%d.%d\n",
}
}
}
}
else
{
g_cErrors++;
}
/* Advance to the next frame. */
}
}
"%3RU64.%09u: stopped. cRecvs=%RU64 cbRecv=%RU64 cLost=%RU64 cOYs=%RU64 cNYs=%RU64\n",
pBuf->cStatRecvs.c,
pBuf->cbStatRecv.c,
pBuf->cStatYieldsOk.c,
);
}
{
/*
* Init the runtime and parse the arguments.
*/
RTR3Init(false, 0);
static RTOPTIONDEF const s_aOptions[] =
{
};
#ifdef RT_OS_DARWIN
const char *pszIf = "en0";
#elif defined(RT_OS_LINUX)
const char *pszIf = "eth0";
#else
const char *pszIf = "em0";
#endif
bool fPromiscuous = false;
const char *pszNetwork = "tstIntNet-1";
bool fSniffer = false;
bool fXmitTest = false;
int rc;
int ch;
int iArg = 1;
switch (ch)
{
case 'd':
{
RTPrintf("tstIntNet-1: warning duration overflowed\n");
}
break;
case 'f':
if (RT_FAILURE(rc))
{
return 1;
}
break;
case 'i':
{
RTPrintf("tstIntNet-1: Interface name is too long (max %d chars): %s\n", INTNET_MAX_TRUNK_NAME - 1, pszIf);
return 1;
}
break;
case 'n':
{
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':
break;
case 's':
break;
case 'S':
fSniffer = true;
break;
case 't':
else
{
if (RT_FAILURE(rc))
{
return 1;
}
}
break;
case 'x':
fXmitTest = true;
break;
case '?':
case 'h':
RTPrintf("syntax: tstIntNet-1 [-pSt] [-d <secs>] [-f <file>] [-r <size>] [-s <size>]\n");
return 1;
default:
if (RT_SUCCESS(ch))
else
return 1;
}
{
return 1;
}
RTPrintf("tstIntNet-1: TESTING...\n");
/*
* Open the session, load ring-0 and issue the request.
*/
if (RT_FAILURE(rc))
{
return 1;
}
char szPath[RTPATH_MAX];
if (RT_FAILURE(rc))
{
return 1;
}
if (RT_FAILURE(rc))
{
return 1;
}
/*
* Create the request, picking the network and trunk names from argv[2]
* and argv[1] if present.
*/
/*
* Issue the request.
*/
if (RT_SUCCESS(rc))
{
/*
* Get the ring-3 address of the shared interface buffer.
*/
if (RT_SUCCESS(rc))
{
RTPrintf("tstIntNet-1: pBuf=%p cbBuf=%d cbSend=%d cbRecv=%d\n",
if (fPromiscuous)
{
PromiscReq.fPromiscuous = true;
if (RT_SUCCESS(rc))
RTPrintf("tstIntNet-1: interface in promiscuous mode\n");
}
if (RT_SUCCESS(rc))
{
/*
* Start the stop watch, init the pcap file.
*/
g_StartTS = RTTimeNanoTS();
if (pFileRaw)
/*
* Do the transmit test first and so we can sniff for the response.
*/
if (fXmitTest)
/*
* Either enter sniffing mode or do a timeout thing.
*/
if (fSniffer)
{
if (fXmitTest != g_fDhcpReply)
{
RTPrintf("tstIntNet-1: Error! The DHCP server didn't reply... (Perhaps you don't have one?)\n", rc);
g_cErrors++;
}
}
else
}
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
{
g_cErrors++;
}
SUPTerm(false /* not forced */);
/* close open files */
if (pFileRaw)
/*
* Summary.
*/
if (!g_cErrors)
RTPrintf("tstIntNet-1: SUCCESS\n");
else
return !!g_cErrors;
}