VBoxNetNAT.cpp revision e2843ed205192b88e54eef60ad541d00bbbc932a
/* $Id$ */
/** @file
* VBoxNetNAT - NAT Service for connecting to IntNet.
*/
/*
* Copyright (C) 2009 Oracle Corporation
*
* 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.
*/
/** @page pg_net_nat VBoxNetNAT
*
* Write a few words...
*
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#include <iprt/initterm.h>
#include <iprt/semaphore.h>
#define LOG_GROUP LOG_GROUP_NAT_SERVICE
#include <VBox/intnetinline.h>
#include <vector>
#include <string>
#include "../NetLib/VBoxNetLib.h"
#include "../NetLib/VBoxNetBaseService.h"
#include <libslirp.h>
#ifdef RT_OS_WINDOWS /* WinMain */
# include <Windows.h>
# include <stdlib.h>
#else
# include <errno.h>
#endif
/*******************************************************************************
* Structures and Typedefs *
*******************************************************************************/
static RTGETOPTDEF g_aGetOptDef[] =
{
};
typedef struct NATSEVICEPORTFORWARDRULE
{
char *pszPortForwardRuleName;
struct in_addr IpV4HostAddr;
struct in_addr IpV4GuestAddr;
bool fUdp;
char *pszStrRaw;
class VBoxNetNAT : public VBoxNetBaseService
{
public:
VBoxNetNAT();
virtual ~VBoxNetNAT();
void usage(void);
void run(void);
virtual int init(void);
public:
bool m_fPassDomain;
#ifdef RT_OS_WINDOWS
#else
#endif
/** Queue for NAT-thread-external events. */
/** event to wakeup the guest receive thread */
/** event to wakeup the guest urgent receive thread */
bool fIsRunning;
};
/*******************************************************************************
* Global Variables *
*******************************************************************************/
/** Pointer to the NAT server. */
class VBoxNetNAT *g_pNAT;
static void natNotifyNATThread(void)
{
int rc;
#ifndef RT_OS_WINDOWS
/* kick select() */
#else
/* kick WSAWaitForMultipleEvents */
#endif
}
{
#if defined(RT_OS_WINDOWS)
/*@todo check if we can remove this*/
#endif
m_TrunkName = "";
cPkt = 0;
cUrgPkt = 0;
for(unsigned int i = 0; i < RT_ELEMENTS(g_aGetOptDef); ++i)
}
VBoxNetNAT::~VBoxNetNAT() { }
int VBoxNetNAT::init()
{
int rc;
#if 0
using namespace com;
#endif
/*
* Initialize slirp.
*/
rc = slirp_init(&m_pNATState, RT_H2N_U32_C(RT_BSWAP_U32_C(RT_MAKE_U32_FROM_U8( 10, 0, 2, 0))), m_Ipv4Netmask.u, m_fPassDomain, false, 0x40, 100, this);
/* Why ? */
#if 0
#endif
for (it = m_vecPortForwardRuleFromCmdLine.begin(); it != m_vecPortForwardRuleFromCmdLine.end(); ++it)
{
slirp_add_redirect(m_pNATState, (*it)->fUdp, (*it)->IpV4HostAddr, (*it)->u16HostPort, (*it)->IpV4GuestAddr , (*it)->u16GuestPort, NULL);
}
#ifndef RT_OS_WINDOWS
/*
* Create the control pipe.
*/
#else
#endif
g_pNAT->fIsRunning = true;
rc = RTThreadCreate(&m_ThrSndNAT, natSndThread, this, 128 * _1K, RTTHREADTYPE_DEFAULT, 0, "SndNAT");
rc = RTThreadCreate(&m_ThrUrgSndNAT, natUrgSndThread, this, 128 * _1K, RTTHREADTYPE_DEFAULT, 0, "UrgSndNAT");
return VINF_SUCCESS;
}
/* Mandatory functions */
void VBoxNetNAT::run()
{
/*
* The loop.
*/
fIsRunning = true;
for (;;)
{
/*
* Wait for a packet to become available.
*/
WaitReq.cMillies = 2000; /* 2 secs - the sleep is for some reason uninterruptible... */ /** @todo fix interruptability in SrvIntNet! */
#if 0
RTReqProcess(m_hSendQueue, 0);
#endif
if (RT_FAILURE(rc))
{
{
continue;
}
return;
}
/*
* Process the receive buffer.
*/
{
void *pvSlirpFrame;
struct mbuf *m;
switch (u16Type)
{
case INTNETHDR_TYPE_FRAME:
if (!m)
{
break;
}
#if 0
#endif
/* don't wait, we may have to wakeup the NAT thread first */
rc = RTReqQueueCallEx(m_hReqQueue, NULL /*ppReq*/, 0 /*cMillies*/, RTREQFLAGS_VOID | RTREQFLAGS_NO_WAIT,
break;
case INTNETHDR_TYPE_GSO:
#if 1
{
/** @todo pass these unmodified. */
{
continue;
}
cbFrame -= sizeof(PDMNETWORKGSO);
{
if (!m)
{
LogRel(("NAT: Can't allocate send buffer cbSegFrame=%u seg=%u/%u\n",
break;
}
}
}
break;
#endif
case INTNETHDR_TYPE_PADDING:
break;
default:
break;
}
}
}
fIsRunning = false;
}
void VBoxNetNAT::usage()
{
}
{
switch (rc)
{
case 'p':
{
do { \
while (*ch != ',') \
{ \
if (*ch == 0) \
{ \
if (pRule) \
if(strRaw) \
return VERR_INVALID_PARAMETER; \
} \
ch++; \
} \
*ch = '\0'; \
ch++; \
} while(0)
PNATSEVICEPORTFORWARDRULE pRule = (PNATSEVICEPORTFORWARDRULE)RTMemAlloc(sizeof(NATSEVICEPORTFORWARDRULE));
if (!pRule)
return VERR_NO_MEMORY;
char *strName;
char *strProto;
char *strHostIp;
char *strHostPort;
char *strGuestIp;
char *strGuestPort;
if (!strRaw)
{
return VERR_NO_MEMORY;
}
else
{
return VERR_INVALID_PARAMETER;
}
if ( strGuestIp == NULL
{
return VERR_INVALID_PARAMETER;
}
if ( !pRule->u16HostPort
|| !pRule->u16GuestPort)
{
return VERR_INVALID_PARAMETER;
}
return VINF_SUCCESS;
}
default:;
}
return VERR_NOT_FOUND;
}
/**
* Entry point.
*/
{
Log2(("NAT: main\n"));
g_pNAT = new VBoxNetNAT();
Log2(("NAT: initialization\n"));
if (!rc)
{
Log2(("NAT: parsing command line\n"));
Log2(("NAT: try go online\n"));
g_pNAT->tryGoOnline();
Log2(("NAT: main loop\n"));
}
delete g_pNAT;
return 0;
}
/** slirp's hooks */
{
return 1;
}
{
int rc = RTReqQueueCallEx(g_pNAT->m_hUrgSendQueue, NULL /*ppReq*/, 0 /*cMillies*/, RTREQFLAGS_VOID | RTREQFLAGS_NO_WAIT,
}
{
int rc = RTReqQueueCallEx(g_pNAT->m_hSendQueue, NULL /*ppReq*/, 0 /*cMillies*/, RTREQFLAGS_VOID | RTREQFLAGS_NO_WAIT,
}
{
AssertMsgFailed(("Unimplemented"));
}
/**
* Worker function for drvNATSend().
* @thread "NAT" thread.
*/
{
}
{
int rc;
if (!fUrg)
{
/* non-urgent datagramm sender */
}
else
{
}
if (RT_FAILURE(rc))
{
}
if (RT_SUCCESS(rc))
{
}
if (RT_FAILURE(rc))
if (!fUrg)
{
}
else {
}
}
{
int nFDs = -1;
#ifdef RT_OS_WINDOWS
#else /* RT_OS_WINDOWS */
unsigned int cPollNegRet = 0;
#endif /* !RT_OS_WINDOWS */
/*
* Polling loop.
*/
for(;;)
{
/*
*/
#ifndef RT_OS_WINDOWS
/* allocation for all sockets + Management pipe */
struct pollfd *polls = (struct pollfd *)RTMemAlloc((1 + nFDs) * sizeof(struct pollfd) + sizeof(uint32_t));
return VERR_NO_MEMORY;
/* don't pass the management pipe */
/* POLLRDBAND usually doesn't used on Linux but seems used on Solaris */
if (cChangedFDs < 0)
{
{
Log2(("NAT: signal was caught while sleep on poll\n"));
/* No error, just process all outstanding requests but don't wait */
cChangedFDs = 0;
}
else if (cPollNegRet++ > 128)
{
cPollNegRet = 0;
}
}
if (cChangedFDs >= 0)
{
{
/* drain the pipe
*
* Note!
* drvNATSend decoupled so we don't know how many times
* device's thread sends before we've entered multiplex,
* so to avoid false alarm drain pipe here to the very end
*
* @todo: Probably we should counter drvNATSend to count how
* deep pipe has been filed before drain.
*
*/
/** @todo XXX: Make it reading exactly we need to drain the
* pipe. */
char ch;
}
}
/* process _all_ outstanding requests but don't wait */
#else /* RT_OS_WINDOWS */
nFDs = -1;
FALSE);
&& dwEvent != WSA_WAIT_TIMEOUT)
{
int error = WSAGetLastError();
}
if (dwEvent == WSA_WAIT_TIMEOUT)
{
continue;
}
/* poll the sockets in any case */
slirp_select_poll(pThis->m_pNATState, /* fTimeout=*/false, /* fIcmp=*/(dwEvent == WSA_WAIT_EVENT_0));
/* process _all_ outstanding requests but don't wait */
#endif /* RT_OS_WINDOWS */
}
return VINF_SUCCESS;
}
{
while (g_pNAT->fIsRunning)
return VINF_SUCCESS;
}
{
while (g_pNAT->fIsRunning)
return VINF_SUCCESS;
}
#ifndef VBOX_WITH_HARDENING
{
if (RT_FAILURE(rc))
return RTMsgInitFailure(rc);
}
# if defined(RT_OS_WINDOWS)
)
{
if(uMsg == WM_DESTROY)
{
PostQuitMessage(0);
return 0;
}
}
{
bool bExit = false;
/* Register the Window Class. */
wc.cbClsExtra = 0;
wc.cbWndExtra = sizeof(void *);
if (atomWindowClass != 0)
{
/* Create the window. */
if (hwnd)
{
{
}
bExit = true;
}
}
if(bExit)
{
/* no need any accuracy here, in anyway the DHCP server usually gets terminated with TerminateProcess */
exit(0);
}
return 0;
}
/** (We don't want a console usually.) */
{
#if 0
NULL, /*__in_opt LPSECURITY_ATTRIBUTES lpThreadAttributes, */
0, /*__in SIZE_T dwStackSize, */
MsgThreadProc, /*__in LPTHREAD_START_ROUTINE lpStartAddress,*/
NULL, /*__in_opt LPVOID lpParameter,*/
0, /*__in DWORD dwCreationFlags,*/
NULL /*__out_opt LPDWORD lpThreadId*/
);
#endif
}
# endif /* RT_OS_WINDOWS */
#endif /* !VBOX_WITH_HARDENING */