VBoxNetLwipNAT.cpp revision 314a7f04c5224519620af617f00bae44adaff41a
/* $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.
*/
#include "winutils.h"
#include <iprt/initterm.h>
#ifndef RT_OS_WINDOWS
#endif
#include <iprt/semaphore.h>
#define LOG_GROUP LOG_GROUP_NAT_SERVICE
#include <VBox/intnetinline.h>
#ifndef RT_OS_WINDOWS
# ifdef RT_OS_LINUX
# endif
#endif
#include <map>
#include <vector>
#include <string>
#include "../NetLib/VBoxNetLib.h"
#include "../NetLib/VBoxNetBaseService.h"
#include "VBoxLwipCore.h"
extern "C"
{
/* bunch of LWIP headers */
#include "proxy.h"
#include "pxremap.h"
#include "portfwd.h"
}
#if defined(VBOX_RAWSOCK_DEBUG_HELPER) \
&& (defined(VBOX_WITH_HARDENING) \
|| defined(RT_OS_WINDOWS) \
|| defined(RT_OS_DARWIN))
#endif
#ifdef VBOX_RAWSOCK_DEBUG_HELPER
#endif
#include "../NetLib/VBoxPortForwardString.h"
static RTGETOPTDEF g_aGetOptDef[] =
{
};
typedef struct NATSEVICEPORTFORWARDRULE
{
{
friend class NATNetworkListener;
public:
virtual ~VBoxNetLwipNAT();
void usage(){ /* @todo: should be implemented */ };
int run();
virtual int init(void);
/* VBoxNetNAT always needs Main */
virtual bool isMainNeeded() const { return true; }
virtual int processFrame(void *, size_t);
private:
struct proxy_options m_ProxyOptions;
struct sockaddr_in m_src4;
struct sockaddr_in6 m_src6;
/**
* place for registered local interfaces.
*/
/* Our NAT network descriptor in Main */
const char **getHostNameservers();
/* Only for debug needs, by default NAT service should load rules from SVC
* on startup, and then on sync them on events.
*/
bool fDontLoadRulesOnStartup;
static void onLwipTcpIpInit(void *arg);
static void onLwipTcpIpFini(void *arg);
static int intNetThreadRecv(RTTHREAD, void *);
};
static VBoxNetLwipNAT *g_pLwipNat;
/**
* @note: this work on Event thread.
*/
{
switch (aEventType)
{
{
// XXX: only handle IPv6 default route for now
if (!m_ProxyOptions.ipv6_enabled)
{
break;
}
{
break;
}
break;
}
{
RT_ZERO(r);
{
hrc = E_INVALIDARG;
goto port_forward_done;
}
switch (proto)
{
case NATProtocol_TCP:
break;
case NATProtocol_UDP:
break;
default:
goto port_forward_done;
}
/* XXX: limits should be checked */
/* XXX: limits should be checked */
if (fCreateFW) /* Addition */
{
}
else /* Deletion */
{
{
/* compare */
{
if (!pFwCopy)
{
break;
}
/* We shouldn't care about pFwCopy this memory will be freed when
* will message will arrive to the destination.
*/
break;
}
} /* loop over vector elements */
} /* condition add or delete */
/* clean up strings */
break;
}
{
const char **ppcszNameServers = getHostNameservers();
/* :block */ 0);
{
}
break;
}
}
return hrc;
}
{
/* lwip thread */
&LwipIpAddr /* IP address*/,
&LwipIpNetMask /* Network mask */,
&LwipIpAddr /* gateway address, @todo: is self IP acceptable? */,
g_pLwipNat /* state */,
tcpip_input /* netif_input_fn */);
LogRel(("netif %c%c%d: mac %RTmac\n",
LogRel(("netif %c%c%d: inet %RTnaipv4 netmask %RTnaipv4\n",
for (int i = 0; i < LWIP_IPV6_NUM_ADDRESSES; ++i) {
LogRel(("netif %c%c%d: inet6 %RTnaipv6\n",
netif_ip6_addr(pNetif, i)));
}
}
/*
* XXX: lwIP currently only ever calls mld6_joingroup() in
* nd6_tmr() for fresh tentative addresses, which is a wrong place
* to do it - but I'm not keen on fixing this properly for now
* (with correct handling of interface up and down transitions,
* etc). So stick it here as a kludge.
*/
for (int i = 0; i <= 1; ++i) {
}
/*
* XXX: We must join the solicited-node multicast for the
* addresses we do IPv6 NA-proxy for. We map IPv6 loopback to
* proxy address + 1. We only need the low 24 bits, and those are
* fixed.
*/
{
/* last 24 bits of the address */
PP_HTONL(0x00000002));
}
}
}
{
/* XXX: proxy finalization */
}
/*
* Callback for netif_add() to initialize the interface.
*/
{
/* validity */
| NETIF_FLAG_ETHARP /* Don't bother driver with ARP and let Lwip resolve ARP handling */
| NETIF_FLAG_ETHERNET; /* Lwip works with ethernet too */
/* IPv6 link-local address in slot 0 */
/*
* RFC 4193 Locally Assigned Global ID (ULA) in slot 1
* [fd17:625c:f037:XXXX::1] where XXXX, 16 bit Subnet ID, are two
* bytes from the middle of the IPv4 address, e.g. :dead: for
* 10.222.173.1
*/
#endif
}
return rcLwip;
}
{
LogFlowFunc(("ENTER: pNetif[%c%c%d], pPbuf:%p\n",
pPBuf));
{
#if ETH_PAD_SIZE
if (q == pPBuf)
{
}
else
#endif
{
}
}
return ERR_OK;
}
VBoxNetLwipNAT::VBoxNetLwipNAT(SOCKET icmpsock4, SOCKET icmpsock6) : VBoxNetBaseService("VBoxNetNAT", "nat-network")
{
#if HAVE_SA_LEN
#endif
fDontLoadRulesOnStartup = false;
for(unsigned int i = 0; i < RT_ELEMENTS(g_aGetOptDef); ++i)
}
{
{
}
}
{
int lrc = 0;
int rc = VINF_SUCCESS;
int socketSpec = SOCK_STREAM;
char *pszHostAddr;
{
case IPPROTO_TCP:
break;
case IPPROTO_UDP:
break;
default:
return VERR_IGNORED; /* Ah, just ignore the garbage */
}
/* XXX: workaround for inet_pton and an empty ipv4 address
* in rule declaration.
*/
if ( sockFamily == PF_INET
&& pszHostAddr[0] == 0)
/*
* We need pass the copy, because we can't be sure
* how much this pointer will be valid in LWIP environment.
*/
return VINF_SUCCESS;
}
{
if (RT_FAILURE(rc))
{
continue;
}
}
return VINF_SUCCESS;
}
/** This method executed on main thread, only at the end threr're one threads started explcitly (LWIP and later in ::run()
* RECV)
*/
int VBoxNetLwipNAT::init()
{
/* virtualbox initialized in super class */
// resolver changes are reported on vbox but are retrieved from
// host so stash a pointer for future lookups
if (fIPv6Enabled)
{
}
{
if (RT_SUCCESS(rc))
{
}
}
if (!fDontLoadRulesOnStartup)
{
} /* if (!fDontLoadRulesOnStartup) */
{
unsigned long i = 0;
++it, ++i)
{
}
}
{
char *pszStrTemp; // avoid const char ** vs char **
}
/* end of COM initialization */
if (RT_FAILURE(rc))
{
return rc;
}
/* this starts LWIP thread */
return rc;
}
const char **VBoxNetLwipNAT::getHostNameservers()
{
{
return NULL;
}
{
return NULL;
}
if (cNameServers == 0)
{
return NULL;
}
const char **ppcszNameServers =
if (ppcszNameServers == NULL)
{
return NULL;
}
for (size_t i = 0; i < cNameServers; ++i)
{
{
++idxLast;
}
}
if (idxLast == 0)
{
return NULL;
}
return ppcszNameServers;
}
{
switch (rc)
{
case 'p':
case 'P':
{
fDontLoadRulesOnStartup = true;
return VINF_SUCCESS;
}
default:;
}
return VERR_NOT_FOUND;
}
{
if (RT_UNLIKELY(p == NULL))
{
return VERR_NO_MEMORY;
}
/*
* The code below is inlined version of:
*
* pbuf_header(p, -ETH_PAD_SIZE); // hide padding
* pbuf_take(p, pvFrame, cbFrame);
* pbuf_header(p, ETH_PAD_SIZE); // reveal padding
*/
struct pbuf *q = p;
do {
#if ETH_PAD_SIZE
if (RT_LIKELY(q == p)) // single pbuf is large enough
{
payload += ETH_PAD_SIZE;
len -= ETH_PAD_SIZE;
}
#endif
q = q->next;
} while (RT_UNLIKELY(q != NULL));
return VINF_SUCCESS;
}
{
cbFrame - sizeof(PDMNETWORKGSO)))
return VERR_INVALID_PARAMETER;
cbFrame -= sizeof(PDMNETWORKGSO);
cbFrame);
{
void *pvSegFrame =
iSeg,
&cbSegFrame);
if (RT_FAILURE(rc))
{
return rc;
}
}
return VINF_SUCCESS;
}
int VBoxNetLwipNAT::run()
{
/* Father starts receiving thread and enter event loop. */
return VINF_SUCCESS;
}
/**
* Entry point.
*/
{
int rc;
#ifdef RT_OS_WINDOWS
int err;
if (err)
{
return 1;
}
#endif
#ifndef RT_OS_DARWIN
#else
/* on OS X it's not privileged */
const int icmpstype = SOCK_DGRAM;
#endif
if (icmpsock4 == INVALID_SOCKET)
{
perror("IPPROTO_ICMP");
#ifdef VBOX_RAWSOCK_DEBUG_HELPER
#endif
}
if (icmpsock4 != INVALID_SOCKET)
{
#ifdef ICMP_FILTER // Linux specific
struct icmp_filter flt = {
~(uint32_t)(
(1U << ICMP_ECHOREPLY)
| (1U << ICMP_DEST_UNREACH)
| (1U << ICMP_TIME_EXCEEDED)
)
};
if (status < 0)
{
perror("ICMP_FILTER");
}
#endif
}
if (icmpsock6 == INVALID_SOCKET)
{
perror("IPPROTO_ICMPV6");
#ifdef VBOX_RAWSOCK_DEBUG_HELPER
#endif
}
if (icmpsock6 != INVALID_SOCKET)
{
#ifdef ICMP6_FILTER // Windows doesn't support RFC 3542 API
/*
* XXX: We do this here for now, not in pxping.c, to avoid
* name clashes between lwIP and system headers.
*/
struct icmp6_filter flt;
if (status < 0)
{
perror("ICMP6_FILTER");
}
#endif
}
{
#ifdef VBOX_WITH_XPCOM
if (hrc == NS_ERROR_FILE_ACCESS_DENIED)
{
if (RT_SUCCESS(vrc))
{
return RTMsgErrorExit(RTEXITCODE_FAILURE,
"Failed to initialize COM: %s: %Rhrf",
}
}
#endif // VBOX_WITH_XPCOM
return RTMsgErrorExit(RTEXITCODE_FAILURE,
"Failed to initialize COM: %Rhrf", hrc);
}
// shall we bail if we failed to init logging?
Log2(("NAT: initialization\n"));
if (RT_SUCCESS(rc))
{
}
if (RT_SUCCESS(rc))
{
g_pLwipNat->run();
}
delete g_pLwipNat;
return 0;
}
{
int rc;
char szHome[RTPATH_MAX];
if (RT_FAILURE(rc))
return rc;
const char *pcszNetwork = NULL;
// XXX: This duplicates information from VBoxNetBaseService.cpp.
// Perhaps option definitions should be exported as public static
// member of VBoxNetBaseService?
static const RTGETOPTDEF s_aOptions[] = {
};
int ch;
{
if (ch == 'n')
{
break;
}
}
if (pcszNetwork == NULL)
{
return VERR_MISSING;
}
char szNetwork[RTPATH_MAX];
if (RT_FAILURE(rc))
{
return rc;
}
// sanitize network name to be usable as a path component
for (char *p = szNetwork; *p != '\0'; ++p)
{
if (RTPATH_IS_SEP(*p))
{
*p = '_';
}
}
char szLogFile[RTPATH_MAX];
{
return VERR_BUFFER_OVERFLOW;
}
// sanitize network name some more to be usable as environment variable
for (char *p = szNetwork; *p != '\0'; ++p)
{
if (*p != '_'
&& (*p < '0' || '9' < *p)
&& (*p < 'a' || 'z' < *p)
&& (*p < 'A' || 'Z' < *p))
{
*p = '_';
}
}
char szEnvVarBase[128];
"VBOXNET_%s_RELEASE_LOG", szNetwork);
if (cch >= sizeof(szEnvVarBase))
{
return VERR_BUFFER_OVERFLOW;
}
"all all.restrict -default.restrict",
32768 /* cMaxEntriesPerGroup */,
0 /* cHistory */,
0 /* uHistoryFileTime */,
0 /* uHistoryFileSize */,
return rc;
}
{
if (fIsIPv6)
else
{
if (RT_FAILURE(rc))
continue;
}
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 */