summaryrefslogtreecommitdiff
path: root/src/VBox/NetworkServices
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@baserock.org>2012-10-26 16:25:44 +0000
committer <>2012-11-12 12:15:52 +0000
commit58ed4748338f9466599adfc8a9171280ed99e23f (patch)
tree02027d99ded4fb56a64aa9489ac2eb487e7858ab /src/VBox/NetworkServices
downloadVirtualBox-58ed4748338f9466599adfc8a9171280ed99e23f.tar.gz
Imported from /home/lorry/working-area/delta_VirtualBox/VirtualBox-4.2.4.tar.bz2.VirtualBox-4.2.4
Diffstat (limited to 'src/VBox/NetworkServices')
-rw-r--r--src/VBox/NetworkServices/DHCP/Makefile.kmk56
-rw-r--r--src/VBox/NetworkServices/DHCP/VBoxNetDHCP.cpp2147
-rw-r--r--src/VBox/NetworkServices/DHCP/VBoxNetDHCPHardened.cpp25
-rw-r--r--src/VBox/NetworkServices/Makefile.kmk28
-rw-r--r--src/VBox/NetworkServices/NAT/Makefile.kmk58
-rw-r--r--src/VBox/NetworkServices/NAT/VBoxNetNAT.cpp815
-rw-r--r--src/VBox/NetworkServices/NAT/VBoxNetNATHardened.cpp24
-rw-r--r--src/VBox/NetworkServices/NetLib/Makefile.kup0
-rw-r--r--src/VBox/NetworkServices/NetLib/VBoxNetARP.cpp154
-rw-r--r--src/VBox/NetworkServices/NetLib/VBoxNetBaseService.cpp363
-rw-r--r--src/VBox/NetworkServices/NetLib/VBoxNetBaseService.h61
-rw-r--r--src/VBox/NetworkServices/NetLib/VBoxNetIntIf.cpp137
-rw-r--r--src/VBox/NetworkServices/NetLib/VBoxNetLib.h69
-rw-r--r--src/VBox/NetworkServices/NetLib/VBoxNetUDP.cpp303
14 files changed, 4240 insertions, 0 deletions
diff --git a/src/VBox/NetworkServices/DHCP/Makefile.kmk b/src/VBox/NetworkServices/DHCP/Makefile.kmk
new file mode 100644
index 00000000..f04b6285
--- /dev/null
+++ b/src/VBox/NetworkServices/DHCP/Makefile.kmk
@@ -0,0 +1,56 @@
+ # $Id: Makefile.kmk $
+## @file
+# Sub-Makefile for VBoxNetDHCP.
+#
+
+#
+# Copyright (C) 2009-2012 Oracle Corporation
+#
+# 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.
+#
+
+SUB_DEPTH = ../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+#
+# Targets.
+#
+ifdef VBOX_WITH_HARDENING
+ PROGRAMS += VBoxNetDHCPHardened
+ DLLS += VBoxNetDHCP
+else
+ PROGRAMS += VBoxNetDHCP
+endif
+
+
+#
+# Hardened VBoxNetDHCP.
+#
+VBoxNetDHCPHardened_TEMPLATE = VBOXR3HARDENEDEXE
+VBoxNetDHCPHardened_SOURCES = VBoxNetDHCPHardened.cpp
+VBoxNetDHCPHardened_NAME = VBoxNetDHCP
+
+
+#
+# VBoxNetDHCP
+#
+VBoxNetDHCP_TEMPLATE =
+VBoxNetDHCP_TEMPLATE := VBOXR3$(if-expr defined(VBOX_WITH_HARDENING),,EXE)
+VBoxNetDHCP_SOURCES = \
+ VBoxNetDHCP.cpp \
+ ../NetLib/VBoxNetIntIf.cpp \
+ ../NetLib/VBoxNetUDP.cpp \
+ ../NetLib/VBoxNetARP.cpp
+VBoxNetDHCP_LIBS = \
+ $(LIB_RUNTIME)
+VBoxNetDHCP_LDFLAGS.win = /SUBSYSTEM:windows
+
+
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/NetworkServices/DHCP/VBoxNetDHCP.cpp b/src/VBox/NetworkServices/DHCP/VBoxNetDHCP.cpp
new file mode 100644
index 00000000..2ea54b66
--- /dev/null
+++ b/src/VBox/NetworkServices/DHCP/VBoxNetDHCP.cpp
@@ -0,0 +1,2147 @@
+/* $Id: VBoxNetDHCP.cpp $ */
+/** @file
+ * VBoxNetDHCP - DHCP 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;
+ * 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.
+ */
+
+/** @page pg_net_dhcp VBoxNetDHCP
+ *
+ * Write a few words...
+ *
+ */
+
+/*******************************************************************************
+* Header Files *
+*******************************************************************************/
+#include <iprt/alloca.h>
+#include <iprt/buildconfig.h>
+#include <iprt/err.h>
+#include <iprt/net.h> /* must come before getopt */
+#include <iprt/getopt.h>
+#include <iprt/initterm.h>
+#include <iprt/message.h>
+#include <iprt/param.h>
+#include <iprt/path.h>
+#include <iprt/stream.h>
+#include <iprt/time.h>
+#include <iprt/string.h>
+
+#include <VBox/sup.h>
+#include <VBox/intnet.h>
+#include <VBox/intnetinline.h>
+#include <VBox/vmm/vmm.h>
+#include <VBox/version.h>
+
+#include "../NetLib/VBoxNetLib.h"
+
+#include <vector>
+#include <string>
+
+#ifdef RT_OS_WINDOWS /* WinMain */
+# include <Windows.h>
+# include <stdlib.h>
+#endif
+
+
+/*******************************************************************************
+* Structures and Typedefs *
+*******************************************************************************/
+
+/**
+ * DHCP configuration item.
+ *
+ * This is all public data because I'm too lazy to do it properly right now.
+ */
+class VBoxNetDhcpCfg
+{
+public:
+ /** The etheret addresses this matches config applies to.
+ * An empty vector means 'ANY'. */
+ std::vector<RTMAC> m_MacAddresses;
+ /** The upper address in the range. */
+ RTNETADDRIPV4 m_UpperAddr;
+ /** The lower address in the range. */
+ RTNETADDRIPV4 m_LowerAddr;
+
+ /** Option 1: The net mask. */
+ RTNETADDRIPV4 m_SubnetMask;
+ /* * Option 2: The time offset. */
+ /** Option 3: Routers for the subnet. */
+ std::vector<RTNETADDRIPV4> m_Routers;
+ /* * Option 4: Time server. */
+ /* * Option 5: Name server. */
+ /** Option 6: Domain Name Server (DNS) */
+ std::vector<RTNETADDRIPV4> m_DNSes;
+ /* * Option 7: Log server. */
+ /* * Option 8: Cookie server. */
+ /* * Option 9: LPR server. */
+ /* * Option 10: Impress server. */
+ /* * Option 11: Resource location server. */
+ /* * Option 12: Host name. */
+ std::string m_HostName;
+ /* * Option 13: Boot file size option. */
+ /* * Option 14: Merit dump file. */
+ /** Option 15: Domain name. */
+ std::string m_DomainName;
+ /* * Option 16: Swap server. */
+ /* * Option 17: Root path. */
+ /* * Option 18: Extension path. */
+ /* * Option 19: IP forwarding enable/disable. */
+ /* * Option 20: Non-local routing enable/disable. */
+ /* * Option 21: Policy filter. */
+ /* * Option 22: Maximum datagram reassembly size (MRS). */
+ /* * Option 23: Default IP time-to-live. */
+ /* * Option 24: Path MTU aging timeout. */
+ /* * Option 25: Path MTU plateau table. */
+ /* * Option 26: Interface MTU. */
+ /* * Option 27: All subnets are local. */
+ /* * Option 28: Broadcast address. */
+ /* * Option 29: Perform maximum discovery. */
+ /* * Option 30: Mask supplier. */
+ /* * Option 31: Perform route discovery. */
+ /* * Option 32: Router solicitation address. */
+ /* * Option 33: Static route. */
+ /* * Option 34: Trailer encapsulation. */
+ /* * Option 35: ARP cache timeout. */
+ /* * Option 36: Ethernet encapsulation. */
+ /* * Option 37: TCP Default TTL. */
+ /* * Option 38: TCP Keepalive Interval. */
+ /* * Option 39: TCP Keepalive Garbage. */
+ /* * Option 40: Network Information Service (NIS) Domain. */
+ /* * Option 41: Network Information Servers. */
+ /* * Option 42: Network Time Protocol Servers. */
+ /* * Option 43: Vendor Specific Information. */
+ /* * Option 44: NetBIOS over TCP/IP Name Server (NBNS). */
+ /* * Option 45: NetBIOS over TCP/IP Datagram distribution Server (NBDD). */
+ /* * Option 46: NetBIOS over TCP/IP Node Type. */
+ /* * Option 47: NetBIOS over TCP/IP Scope. */
+ /* * Option 48: X Window System Font Server. */
+ /* * Option 49: X Window System Display Manager. */
+
+ /** Option 51: IP Address Lease Time. */
+ uint32_t m_cSecLease;
+
+ /* * Option 64: Network Information Service+ Domain. */
+ /* * Option 65: Network Information Service+ Servers. */
+ /** Option 66: TFTP server name. */
+ std::string m_TftpServer;
+ /** Address for the bp_siaddr field corresponding to m_TftpServer. */
+ RTNETADDRIPV4 m_TftpServerAddr;
+ /** Option 67: Bootfile name. */
+ std::string m_BootfileName;
+
+ /* * Option 68: Mobile IP Home Agent. */
+ /* * Option 69: Simple Mail Transport Protocol (SMPT) Server. */
+ /* * Option 70: Post Office Protocol (POP3) Server. */
+ /* * Option 71: Network News Transport Protocol (NNTP) Server. */
+ /* * Option 72: Default World Wide Web (WWW) Server. */
+ /* * Option 73: Default Finger Server. */
+ /* * Option 74: Default Internet Relay Chat (IRC) Server. */
+ /* * Option 75: StreetTalk Server. */
+
+ /* * Option 119: Domain Search. */
+
+
+ VBoxNetDhcpCfg()
+ {
+ m_UpperAddr.u = UINT32_MAX;
+ m_LowerAddr.u = UINT32_MAX;
+ m_SubnetMask.u = UINT32_MAX;
+ m_cSecLease = 60*60; /* 1 hour */
+ }
+
+ /** Validates the configuration.
+ * @returns 0 on success, exit code + error message to stderr on failure. */
+ int validate(void)
+ {
+ if ( m_UpperAddr.u == UINT32_MAX
+ || m_LowerAddr.u == UINT32_MAX
+ || m_SubnetMask.u == UINT32_MAX)
+ {
+ RTStrmPrintf(g_pStdErr, "VBoxNetDHCP: Config is missing:");
+ if (m_UpperAddr.u == UINT32_MAX)
+ RTStrmPrintf(g_pStdErr, " --upper-ip");
+ if (m_LowerAddr.u == UINT32_MAX)
+ RTStrmPrintf(g_pStdErr, " --lower-ip");
+ if (m_SubnetMask.u == UINT32_MAX)
+ RTStrmPrintf(g_pStdErr, " --netmask");
+ return 2;
+ }
+
+ if (RT_N2H_U32(m_UpperAddr.u) < RT_N2H_U32(m_LowerAddr.u))
+ {
+ RTStrmPrintf(g_pStdErr, "VBoxNetDHCP: The --upper-ip value is lower than the --lower-ip one!\n"
+ " %d.%d.%d.%d < %d.%d.%d.%d\n",
+ m_UpperAddr.au8[0], m_UpperAddr.au8[1], m_UpperAddr.au8[2], m_UpperAddr.au8[3],
+ m_LowerAddr.au8[0], m_LowerAddr.au8[1], m_LowerAddr.au8[2], m_LowerAddr.au8[3]);
+ return 3;
+ }
+
+ /* the code goes insane if we have too many atm. lazy bird */
+ uint32_t cIPs = RT_N2H_U32(m_UpperAddr.u) - RT_N2H_U32(m_LowerAddr.u);
+ if (cIPs > 1024)
+ {
+ RTStrmPrintf(g_pStdErr, "VBoxNetDHCP: Too many IPs between --upper-ip and --lower-ip! %d (max 1024)\n"
+ " %d.%d.%d.%d < %d.%d.%d.%d\n",
+ cIPs,
+ m_UpperAddr.au8[0], m_UpperAddr.au8[1], m_UpperAddr.au8[2], m_UpperAddr.au8[3],
+ m_LowerAddr.au8[0], m_LowerAddr.au8[1], m_LowerAddr.au8[2], m_LowerAddr.au8[3]);
+ return 3;
+ }
+ return 0;
+ }
+
+ /**
+ * Is this config for one specific client?
+ *
+ * @return true / false.
+ */
+ bool isOneSpecificClient(void) const
+ {
+ return m_LowerAddr.u == m_UpperAddr.u
+ && m_MacAddresses.size() > 0;
+ }
+
+ /**
+ * Checks if this config matches the specified MAC address.
+ *
+ * @returns true / false.
+ *
+ * @param pMac The MAC address to match.
+ */
+ bool matchesMacAddress(PCRTMAC pMac) const
+ {
+ size_t i = m_MacAddresses.size();
+ if (RT_LIKELY(i < 1))
+ return true; /* no entries == ALL wildcard match */
+
+ while (i--)
+ {
+ PCRTMAC pCur = &m_MacAddresses[i];
+ if ( pCur->au16[0] == pMac->au16[0]
+ && pCur->au16[1] == pMac->au16[1]
+ && pCur->au16[2] == pMac->au16[2])
+ return true;
+ }
+ return false;
+ }
+
+};
+
+/**
+ * DHCP lease.
+ */
+class VBoxNetDhcpLease
+{
+public:
+ typedef enum State
+ {
+ /** Invalid. */
+ kState_Invalid = 0,
+ /** The lease is free / released. */
+ kState_Free,
+ /** An offer has been made.
+ * Expire time indicates when the offer expires. */
+ kState_Offer,
+ /** The lease is active.
+ * Expire time indicates when the lease expires. */
+ kState_Active
+ } State;
+
+ /** The client MAC address. */
+ RTMAC m_MacAddress;
+ /** The IPv4 address. */
+ RTNETADDRIPV4 m_IPv4Address;
+
+ /** The current lease state. */
+ State m_enmState;
+ /** The lease expiration time. */
+ RTTIMESPEC m_ExpireTime;
+ /** Transaction ID. */
+ uint32_t m_xid;
+ /** The configuration for this lease. */
+ VBoxNetDhcpCfg *m_pCfg;
+
+public:
+ /** Constructor taking an IPv4 address and a Config. */
+ VBoxNetDhcpLease(RTNETADDRIPV4 IPv4Addr, VBoxNetDhcpCfg *pCfg)
+ {
+ m_pCfg = pCfg;
+ m_IPv4Address = IPv4Addr;
+
+ m_MacAddress.au16[0] = m_MacAddress.au16[1] = m_MacAddress.au16[2] = 0xff;
+ m_enmState = kState_Free;
+ RTTimeSpecSetSeconds(&m_ExpireTime, 0);
+ m_xid = UINT32_MAX;
+ }
+
+ /** Destructor. */
+ ~VBoxNetDhcpLease()
+ {
+ m_IPv4Address.u = UINT32_MAX;
+ m_pCfg = NULL;
+ m_MacAddress.au16[0] = m_MacAddress.au16[1] = m_MacAddress.au16[2] = 0xff;
+ m_enmState = kState_Free;
+ m_xid = UINT32_MAX;
+ }
+
+ void offer(uint32_t xid);
+ void activate(void);
+ void activate(uint32_t xid);
+ void release(void);
+ bool hasExpired(void) const;
+
+ /**
+ * Checks if the lease is in use or not.
+ *
+ * @returns true if active, false if free or expired.
+ *
+ * @param pNow The current time to use. Optional.
+ */
+ bool isInUse(PCRTTIMESPEC pNow = NULL) const
+ {
+ if ( m_enmState == kState_Offer
+ || m_enmState == kState_Active)
+ {
+ RTTIMESPEC Now;
+ if (!pNow)
+ pNow = RTTimeNow(&Now);
+ return RTTimeSpecGetSeconds(&m_ExpireTime) > RTTimeSpecGetSeconds(pNow);
+ }
+ return false;
+ }
+
+ /**
+ * Is this lease for one specific client?
+ *
+ * @return true/false.
+ */
+ bool isOneSpecificClient(void) const
+ {
+ return m_pCfg
+ && m_pCfg->isOneSpecificClient();
+ }
+
+ /**
+ * Is this lease currently being offered to a client.
+ *
+ * @returns true / false.
+ */
+ bool isBeingOffered(void) const
+ {
+ return m_enmState == kState_Offer
+ && isInUse();
+ }
+
+ /**
+ * Is the lease in the current config or not.
+ *
+ * When updating the config we might leave active leases behind which aren't
+ * included in the new config. These will have m_pCfg set to NULL and should be
+ * freed up when they expired.
+ *
+ * @returns true / false.
+ */
+ bool isInCurrentConfig(void) const
+ {
+ return m_pCfg != NULL;
+ }
+};
+
+/**
+ * DHCP server instance.
+ */
+class VBoxNetDhcp
+{
+public:
+ VBoxNetDhcp();
+ virtual ~VBoxNetDhcp();
+
+ int parseArgs(int argc, char **argv);
+ int tryGoOnline(void);
+ int run(void);
+
+protected:
+ int addConfig(VBoxNetDhcpCfg *pCfg);
+ void explodeConfig(void);
+
+ bool handleDhcpMsg(uint8_t uMsgType, PCRTNETBOOTP pDhcpMsg, size_t cb);
+ bool handleDhcpReqDiscover(PCRTNETBOOTP pDhcpMsg, size_t cb);
+ bool handleDhcpReqRequest(PCRTNETBOOTP pDhcpMsg, size_t cb);
+ bool handleDhcpReqDecline(PCRTNETBOOTP pDhcpMsg, size_t cb);
+ bool handleDhcpReqRelease(PCRTNETBOOTP pDhcpMsg, size_t cb);
+ void makeDhcpReply(uint8_t uMsgType, VBoxNetDhcpLease *pLease, PCRTNETBOOTP pDhcpMsg, size_t cb);
+
+ VBoxNetDhcpLease *findLeaseByMacAddress(PCRTMAC pMacAddress, bool fAnyState);
+ VBoxNetDhcpLease *findLeaseByIpv4AndMacAddresses(RTNETADDRIPV4 IPv4Addr, PCRTMAC pMacAddress, bool fAnyState);
+ VBoxNetDhcpLease *newLease(PCRTNETBOOTP pDhcpMsg, size_t cb);
+
+ static uint8_t const *findOption(uint8_t uOption, PCRTNETBOOTP pDhcpMsg, size_t cb, size_t *pcbMaxOpt);
+ static bool findOptionIPv4Addr(uint8_t uOption, PCRTNETBOOTP pDhcpMsg, size_t cb, PRTNETADDRIPV4 pIPv4Addr);
+
+ inline void debugPrint( int32_t iMinLevel, bool fMsg, const char *pszFmt, ...) const;
+ void debugPrintV(int32_t iMinLevel, bool fMsg, const char *pszFmt, va_list va) const;
+ static const char *debugDhcpName(uint8_t uMsgType);
+
+protected:
+ /** @name The server configuration data members.
+ * @{ */
+ std::string m_Name;
+ std::string m_Network;
+ std::string m_TrunkName;
+ INTNETTRUNKTYPE m_enmTrunkType;
+ RTMAC m_MacAddress;
+ RTNETADDRIPV4 m_Ipv4Address;
+ std::string m_LeaseDBName;
+ /** @} */
+
+ /** The current configs. */
+ std::vector<VBoxNetDhcpCfg *> m_Cfgs;
+
+ /** The current leases. */
+ std::vector<VBoxNetDhcpLease> m_Leases;
+
+ /** @name The network interface
+ * @{ */
+ PSUPDRVSESSION m_pSession;
+ uint32_t m_cbSendBuf;
+ uint32_t m_cbRecvBuf;
+ INTNETIFHANDLE m_hIf; /**< The handle to the network interface. */
+ PINTNETBUF m_pIfBuf; /**< Interface buffer. */
+ /** @} */
+
+ /** @name Debug stuff
+ * @{ */
+ int32_t m_cVerbosity;
+ uint8_t m_uCurMsgType;
+ size_t m_cbCurMsg;
+ PCRTNETBOOTP m_pCurMsg;
+ VBOXNETUDPHDRS m_CurHdrs;
+ /** @} */
+};
+
+
+/*******************************************************************************
+* Global Variables *
+*******************************************************************************/
+/** Pointer to the DHCP server. */
+static VBoxNetDhcp *g_pDhcp;
+
+
+/**
+ * Offer this lease to a client.
+ *
+ * @param xid The transaction ID.
+ */
+void VBoxNetDhcpLease::offer(uint32_t xid)
+{
+ m_enmState = kState_Offer;
+ m_xid = xid;
+ RTTimeNow(&m_ExpireTime);
+ RTTimeSpecAddSeconds(&m_ExpireTime, 60);
+}
+
+
+/**
+ * Activate this lease (i.e. a client is now using it).
+ */
+void VBoxNetDhcpLease::activate(void)
+{
+ m_enmState = kState_Active;
+ RTTimeNow(&m_ExpireTime);
+ RTTimeSpecAddSeconds(&m_ExpireTime, m_pCfg ? m_pCfg->m_cSecLease : 60); /* m_pCfg can be NULL right now... */
+}
+
+
+/**
+ * Activate this lease with a new transaction ID.
+ *
+ * @param xid The transaction ID.
+ * @todo check if this is really necessary.
+ */
+void VBoxNetDhcpLease::activate(uint32_t xid)
+{
+ activate();
+ m_xid = xid;
+}
+
+
+/**
+ * Release a lease either upon client request or because it didn't quite match a
+ * DHCP_REQUEST.
+ */
+void VBoxNetDhcpLease::release(void)
+{
+ m_enmState = kState_Free;
+ RTTimeNow(&m_ExpireTime);
+ RTTimeSpecAddSeconds(&m_ExpireTime, 5);
+}
+
+
+/**
+ * Checks if the lease has expired or not.
+ *
+ * This just checks the expiration time not the state. This is so that this
+ * method will work for reusing RELEASEd leases when the client comes back after
+ * a reboot or ipconfig /renew. Callers not interested in info on released
+ * leases should check the state first.
+ *
+ * @returns true if expired, false if not.
+ */
+bool VBoxNetDhcpLease::hasExpired() const
+{
+ RTTIMESPEC Now;
+ return RTTimeSpecGetSeconds(&m_ExpireTime) > RTTimeSpecGetSeconds(RTTimeNow(&Now));
+}
+
+
+
+
+/**
+ * Construct a DHCP server with a default configuration.
+ */
+VBoxNetDhcp::VBoxNetDhcp()
+{
+ m_Name = "VBoxNetDhcp";
+ m_Network = "VBoxNetDhcp";
+ m_TrunkName = "";
+ m_enmTrunkType = kIntNetTrunkType_WhateverNone;
+ m_MacAddress.au8[0] = 0x08;
+ m_MacAddress.au8[1] = 0x00;
+ m_MacAddress.au8[2] = 0x27;
+ m_MacAddress.au8[3] = 0x40;
+ m_MacAddress.au8[4] = 0x41;
+ m_MacAddress.au8[5] = 0x42;
+ m_Ipv4Address.u = RT_H2N_U32_C(RT_BSWAP_U32_C(RT_MAKE_U32_FROM_U8( 10, 0, 2, 5)));
+
+ m_pSession = NIL_RTR0PTR;
+ m_cbSendBuf = 8192;
+ m_cbRecvBuf = 51200; /** @todo tune to 64 KB with help from SrvIntR0 */
+ m_hIf = INTNET_HANDLE_INVALID;
+ m_pIfBuf = NULL;
+
+ m_cVerbosity = 0;
+ m_uCurMsgType = UINT8_MAX;
+ m_cbCurMsg = 0;
+ m_pCurMsg = NULL;
+ memset(&m_CurHdrs, '\0', sizeof(m_CurHdrs));
+
+#if 0 /* enable to hack the code without a mile long argument list. */
+ VBoxNetDhcpCfg *pDefCfg = new VBoxNetDhcpCfg();
+ pDefCfg->m_LowerAddr.u = RT_H2N_U32_C(RT_BSWAP_U32_C(RT_MAKE_U32_FROM_U8( 10, 0, 2,100)));
+ pDefCfg->m_UpperAddr.u = RT_H2N_U32_C(RT_BSWAP_U32_C(RT_MAKE_U32_FROM_U8( 10, 0, 2,250)));
+ pDefCfg->m_SubnetMask.u = RT_H2N_U32_C(RT_BSWAP_U32_C(RT_MAKE_U32_FROM_U8(255,255,255, 0)));
+ RTNETADDRIPV4 Addr;
+ Addr.u = RT_H2N_U32_C(RT_BSWAP_U32_C(RT_MAKE_U32_FROM_U8( 10, 0, 2, 1)));
+ pDefCfg->m_Routers.push_back(Addr);
+ Addr.u = RT_H2N_U32_C(RT_BSWAP_U32_C(RT_MAKE_U32_FROM_U8( 10, 0, 2, 2)));
+ pDefCfg->m_DNSes.push_back(Addr);
+ pDefCfg->m_DomainName = "vboxnetdhcp.org";
+#if 0
+ pDefCfg->m_cSecLease = 60*60; /* 1 hour */
+#else
+ pDefCfg->m_cSecLease = 30; /* sec */
+#endif
+ pDefCfg->m_TftpServer = "10.0.2.3"; //??
+ this->addConfig(pDefCfg);
+#endif
+}
+
+
+/**
+ * Destruct a DHCP server.
+ */
+VBoxNetDhcp::~VBoxNetDhcp()
+{
+ /*
+ * Close the interface connection.
+ */
+ if (m_hIf != INTNET_HANDLE_INVALID)
+ {
+ INTNETIFCLOSEREQ CloseReq;
+ CloseReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
+ CloseReq.Hdr.cbReq = sizeof(CloseReq);
+ CloseReq.pSession = m_pSession;
+ CloseReq.hIf = m_hIf;
+ m_hIf = INTNET_HANDLE_INVALID;
+ int rc = SUPR3CallVMMR0Ex(NIL_RTR0PTR, NIL_VMCPUID, VMMR0_DO_INTNET_IF_CLOSE, 0, &CloseReq.Hdr);
+ AssertRC(rc);
+ }
+
+ if (m_pSession)
+ {
+ SUPR3Term(false /*fForced*/);
+ m_pSession = NIL_RTR0PTR;
+ }
+}
+
+
+/**
+ * Adds a config to the tail.
+ *
+ * @returns See VBoxNetDHCP::validate().
+ * @param pCfg The config too add.
+ * This object will be consumed by this call!
+ */
+int VBoxNetDhcp::addConfig(VBoxNetDhcpCfg *pCfg)
+{
+ int rc = 0;
+ if (pCfg)
+ {
+ rc = pCfg->validate();
+ if (!rc)
+ m_Cfgs.push_back(pCfg);
+ else
+ delete pCfg;
+ }
+ return rc;
+}
+
+
+/**
+ * Explodes the config into leases.
+ *
+ * @remarks This code is brute force and not very fast nor memory efficient.
+ * We will have to revisit this later.
+ *
+ * @remarks If an IP has been reconfigured for a fixed mac address and it's
+ * already leased to a client, we it won't be available until the
+ * client releases its lease or it expires.
+ */
+void VBoxNetDhcp::explodeConfig(void)
+{
+ RTTIMESPEC Now;
+ RTTimeNow(&Now);
+
+ /*
+ * Remove all non-active leases from the vector and zapping the
+ * config pointers of the once left behind.
+ */
+ std::vector<VBoxNetDhcpLease>::iterator Itr = m_Leases.begin();
+ while (Itr != m_Leases.end())
+ {
+ if (!Itr->isInUse(&Now))
+ Itr = m_Leases.erase(Itr);
+ else
+ {
+ Itr->m_pCfg = NULL;
+ Itr++;
+ }
+ }
+
+ /*
+ * Loop thru the configurations in reverse order, giving the last
+ * configs priority of the newer ones.
+ */
+ size_t iCfg = m_Cfgs.size();
+ while (iCfg-- > 0)
+ {
+ VBoxNetDhcpCfg *pCfg = m_Cfgs[iCfg];
+
+ /* Expand the IP lease range. */
+ uint32_t const uLast = RT_N2H_U32(pCfg->m_UpperAddr.u);
+ for (uint32_t i = RT_N2H_U32(pCfg->m_LowerAddr.u); i <= uLast; i++)
+ {
+ RTNETADDRIPV4 IPv4Addr;
+ IPv4Addr.u = RT_H2N_U32(i);
+
+ /* Check if it exists and is configured. */
+ VBoxNetDhcpLease *pLease = NULL;
+ for (size_t j = 0; j < m_Leases.size(); j++)
+ if (m_Leases[j].m_IPv4Address.u == IPv4Addr.u)
+ {
+ pLease = &m_Leases[j];
+ break;
+ }
+ if (pLease)
+ {
+ if (!pLease->m_pCfg)
+ pLease->m_pCfg = pCfg;
+ }
+ else
+ {
+ /* add it. */
+ VBoxNetDhcpLease NewLease(IPv4Addr, pCfg);
+ m_Leases.push_back(NewLease);
+ debugPrint(10, false, "exploseConfig: new lease %d.%d.%d.%d",
+ IPv4Addr.au8[0], IPv4Addr.au8[1], IPv4Addr.au8[2], IPv4Addr.au8[3]);
+ }
+ }
+ }
+}
+
+
+/**
+ * Parse the arguments.
+ *
+ * @returns 0 on success, fully bitched exit code on failure.
+ *
+ * @param argc Argument count.
+ * @param argv Argument vector.
+ */
+int VBoxNetDhcp::parseArgs(int argc, char **argv)
+{
+ static const RTGETOPTDEF s_aOptionDefs[] =
+ {
+ { "--name", 'N', RTGETOPT_REQ_STRING },
+ { "--network", 'n', RTGETOPT_REQ_STRING },
+ { "--trunk-name", 't', RTGETOPT_REQ_STRING },
+ { "--trunk-type", 'T', RTGETOPT_REQ_STRING },
+ { "--mac-address", 'a', RTGETOPT_REQ_MACADDR },
+ { "--ip-address", 'i', RTGETOPT_REQ_IPV4ADDR },
+ { "--lease-db", 'D', RTGETOPT_REQ_STRING },
+ { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
+
+ { "--begin-config", 'b', RTGETOPT_REQ_NOTHING },
+ { "--gateway", 'g', RTGETOPT_REQ_IPV4ADDR },
+ { "--lower-ip", 'l', RTGETOPT_REQ_IPV4ADDR },
+ { "--upper-ip", 'u', RTGETOPT_REQ_IPV4ADDR },
+ { "--netmask", 'm', RTGETOPT_REQ_IPV4ADDR },
+ };
+
+ RTGETOPTSTATE State;
+ int rc = RTGetOptInit(&State, argc, argv, &s_aOptionDefs[0], RT_ELEMENTS(s_aOptionDefs), 0, 0 /*fFlags*/);
+ AssertRCReturn(rc, 49);
+
+ VBoxNetDhcpCfg *pCurCfg = NULL;
+ for (;;)
+ {
+ RTGETOPTUNION Val;
+ rc = RTGetOpt(&State, &Val);
+ if (!rc)
+ break;
+ switch (rc)
+ {
+ case 'N':
+ m_Name = Val.psz;
+ break;
+ case 'n':
+ m_Network = Val.psz;
+ break;
+ case 't':
+ m_TrunkName = Val.psz;
+ break;
+ case 'T':
+ if (!strcmp(Val.psz, "none"))
+ m_enmTrunkType = kIntNetTrunkType_None;
+ else if (!strcmp(Val.psz, "whatever"))
+ m_enmTrunkType = kIntNetTrunkType_WhateverNone;
+ else if (!strcmp(Val.psz, "netflt"))
+ m_enmTrunkType = kIntNetTrunkType_NetFlt;
+ else if (!strcmp(Val.psz, "netadp"))
+ m_enmTrunkType = kIntNetTrunkType_NetAdp;
+ else if (!strcmp(Val.psz, "srvnat"))
+ m_enmTrunkType = kIntNetTrunkType_SrvNat;
+ else
+ {
+ RTStrmPrintf(g_pStdErr, "Invalid trunk type '%s'\n", Val.psz);
+ return 1;
+ }
+ break;
+ case 'a':
+ m_MacAddress = Val.MacAddr;
+ break;
+ case 'i':
+ m_Ipv4Address = Val.IPv4Addr;
+ break;
+ case 'd':
+ m_LeaseDBName = Val.psz;
+ break;
+
+ case 'v':
+ m_cVerbosity++;
+ break;
+
+ /* Begin config. */
+ case 'b':
+ rc = addConfig(pCurCfg);
+ if (rc)
+ break;
+ pCurCfg = NULL;
+ /* fall thru */
+
+ /* config specific ones. */
+ case 'g':
+ case 'l':
+ case 'u':
+ case 'm':
+ if (!pCurCfg)
+ {
+ pCurCfg = new VBoxNetDhcpCfg();
+ if (!pCurCfg)
+ {
+ RTStrmPrintf(g_pStdErr, "VBoxNetDHCP: new VBoxDhcpCfg failed\n");
+ return 1;
+ }
+ }
+
+ switch (rc)
+ {
+ case 'g':
+ pCurCfg->m_Routers.push_back(Val.IPv4Addr);
+ break;
+
+ case 'l':
+ pCurCfg->m_LowerAddr = Val.IPv4Addr;
+ break;
+
+ case 'u':
+ pCurCfg->m_UpperAddr = Val.IPv4Addr;
+ break;
+
+ case 'm':
+ pCurCfg->m_SubnetMask = Val.IPv4Addr;
+ break;
+
+ case 0: /* ignore */ break;
+ default:
+ AssertMsgFailed(("%d", rc));
+ return 1;
+ }
+ break;
+
+ case 'V':
+ RTPrintf("%sr%u\n", RTBldCfgVersion(), RTBldCfgRevision());
+ return 1;
+
+ case 'h':
+ RTPrintf("VBoxNetDHCP Version %s\n"
+ "(C) 2009-" VBOX_C_YEAR " " VBOX_VENDOR "\n"
+ "All rights reserved.\n"
+ "\n"
+ "Usage: VBoxNetDHCP <options>\n"
+ "\n"
+ "Options:\n",
+ RTBldCfgVersion());
+ for (size_t i = 0; i < RT_ELEMENTS(s_aOptionDefs); i++)
+ RTPrintf(" -%c, %s\n", s_aOptionDefs[i].iShort, s_aOptionDefs[i].pszLong);
+ return 1;
+
+ default:
+ rc = RTGetOptPrintError(rc, &Val);
+ RTPrintf("Use --help for more information.\n");
+ return rc;
+ }
+ }
+
+ /*
+ * Do the reconfig. (move this later)
+ */
+ if (!rc)
+ explodeConfig();
+
+ return rc;
+}
+
+
+/**
+ * Tries to connect to the internal network.
+ *
+ * @returns 0 on success, exit code + error message to stderr on failure.
+ */
+int VBoxNetDhcp::tryGoOnline(void)
+{
+ /*
+ * Open the session, load ring-0 and issue the request.
+ */
+ int rc = SUPR3Init(&m_pSession);
+ if (RT_FAILURE(rc))
+ {
+ m_pSession = NIL_RTR0PTR;
+ RTStrmPrintf(g_pStdErr, "VBoxNetDHCP: SUPR3Init -> %Rrc", rc);
+ return 1;
+ }
+
+ char szPath[RTPATH_MAX];
+ rc = RTPathExecDir(szPath, sizeof(szPath) - sizeof("/VMMR0.r0"));
+ if (RT_FAILURE(rc))
+ {
+ RTStrmPrintf(g_pStdErr, "VBoxNetDHCP: RTPathProgram -> %Rrc", rc);
+ return 1;
+ }
+
+ rc = SUPR3LoadVMM(strcat(szPath, "/VMMR0.r0"));
+ if (RT_FAILURE(rc))
+ {
+ RTStrmPrintf(g_pStdErr, "VBoxNetDHCP: SUPR3LoadVMM(\"%s\") -> %Rrc", szPath, rc);
+ return 1;
+ }
+
+ /*
+ * Create the open request.
+ */
+ INTNETOPENREQ OpenReq;
+ OpenReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
+ OpenReq.Hdr.cbReq = sizeof(OpenReq);
+ OpenReq.pSession = m_pSession;
+ strncpy(OpenReq.szNetwork, m_Network.c_str(), sizeof(OpenReq.szNetwork));
+ OpenReq.szNetwork[sizeof(OpenReq.szNetwork) - 1] = '\0';
+ strncpy(OpenReq.szTrunk, m_TrunkName.c_str(), sizeof(OpenReq.szTrunk));
+ OpenReq.szTrunk[sizeof(OpenReq.szTrunk) - 1] = '\0';
+ OpenReq.enmTrunkType = m_enmTrunkType;
+ OpenReq.fFlags = 0; /** @todo check this */
+ OpenReq.cbSend = m_cbSendBuf;
+ OpenReq.cbRecv = m_cbRecvBuf;
+ OpenReq.hIf = INTNET_HANDLE_INVALID;
+
+ /*
+ * Issue the request.
+ */
+ debugPrint(2, false, "attempting to open/create network \"%s\"...", OpenReq.szNetwork);
+ rc = SUPR3CallVMMR0Ex(NIL_RTR0PTR, NIL_VMCPUID, VMMR0_DO_INTNET_OPEN, 0, &OpenReq.Hdr);
+ if (RT_SUCCESS(rc))
+ {
+ m_hIf = OpenReq.hIf;
+ debugPrint(1, false, "successfully opened/created \"%s\" - hIf=%#x", OpenReq.szNetwork, m_hIf);
+
+ /*
+ * Get the ring-3 address of the shared interface buffer.
+ */
+ INTNETIFGETBUFFERPTRSREQ GetBufferPtrsReq;
+ GetBufferPtrsReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
+ GetBufferPtrsReq.Hdr.cbReq = sizeof(GetBufferPtrsReq);
+ GetBufferPtrsReq.pSession = m_pSession;
+ GetBufferPtrsReq.hIf = m_hIf;
+ GetBufferPtrsReq.pRing3Buf = NULL;
+ GetBufferPtrsReq.pRing0Buf = NIL_RTR0PTR;
+ rc = SUPR3CallVMMR0Ex(NIL_RTR0PTR, NIL_VMCPUID, VMMR0_DO_INTNET_IF_GET_BUFFER_PTRS, 0, &GetBufferPtrsReq.Hdr);
+ if (RT_SUCCESS(rc))
+ {
+ PINTNETBUF pBuf = GetBufferPtrsReq.pRing3Buf;
+ debugPrint(1, false, "pBuf=%p cbBuf=%d cbSend=%d cbRecv=%d",
+ pBuf, pBuf->cbBuf, pBuf->cbSend, pBuf->cbRecv);
+ m_pIfBuf = pBuf;
+
+ /*
+ * Activate the interface.
+ */
+ INTNETIFSETACTIVEREQ ActiveReq;
+ ActiveReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
+ ActiveReq.Hdr.cbReq = sizeof(ActiveReq);
+ ActiveReq.pSession = m_pSession;
+ ActiveReq.hIf = m_hIf;
+ ActiveReq.fActive = true;
+ rc = SUPR3CallVMMR0Ex(NIL_RTR0PTR, NIL_VMCPUID, VMMR0_DO_INTNET_IF_SET_ACTIVE, 0, &ActiveReq.Hdr);
+ if (RT_SUCCESS(rc))
+ return 0;
+
+ /* bail out */
+ RTStrmPrintf(g_pStdErr, "VBoxNetDHCP: SUPR3CallVMMR0Ex(,VMMR0_DO_INTNET_IF_SET_PROMISCUOUS_MODE,) failed, rc=%Rrc\n", rc);
+ }
+ else
+ RTStrmPrintf(g_pStdErr, "VBoxNetDHCP: SUPR3CallVMMR0Ex(,VMMR0_DO_INTNET_IF_GET_BUFFER_PTRS,) failed, rc=%Rrc\n", rc);
+ }
+ else
+ RTStrmPrintf(g_pStdErr, "VBoxNetDHCP: SUPR3CallVMMR0Ex(,VMMR0_DO_INTNET_OPEN,) failed, rc=%Rrc\n", rc);
+
+ return RT_SUCCESS(rc) ? 0 : 1;
+}
+
+
+/**
+ * Runs the DHCP server.
+ *
+ * @returns exit code + error message to stderr on failure, won't return on
+ * success (you must kill this process).
+ */
+int VBoxNetDhcp::run(void)
+{
+ /*
+ * The loop.
+ */
+ PINTNETRINGBUF pRingBuf = &m_pIfBuf->Recv;
+ for (;;)
+ {
+ /*
+ * Wait for a packet to become available.
+ */
+ INTNETIFWAITREQ WaitReq;
+ WaitReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
+ WaitReq.Hdr.cbReq = sizeof(WaitReq);
+ WaitReq.pSession = m_pSession;
+ WaitReq.hIf = m_hIf;
+ WaitReq.cMillies = 2000; /* 2 secs - the sleep is for some reason uninterruptible... */ /** @todo fix interruptability in SrvIntNet! */
+ int rc = SUPR3CallVMMR0Ex(NIL_RTR0PTR, NIL_VMCPUID, VMMR0_DO_INTNET_IF_WAIT, 0, &WaitReq.Hdr);
+ if (RT_FAILURE(rc))
+ {
+ if (rc == VERR_TIMEOUT || rc == VERR_INTERRUPTED)
+ continue;
+ RTStrmPrintf(g_pStdErr, "VBoxNetDHCP: VMMR0_DO_INTNET_IF_WAIT returned %Rrc\n", rc);
+ return 1;
+ }
+
+ /*
+ * Process the receive buffer.
+ */
+ while (IntNetRingHasMoreToRead(pRingBuf))
+ {
+ size_t cb;
+ void *pv = VBoxNetUDPMatch(m_pIfBuf, RTNETIPV4_PORT_BOOTPS, &m_MacAddress,
+ VBOXNETUDP_MATCH_UNICAST | VBOXNETUDP_MATCH_BROADCAST | VBOXNETUDP_MATCH_CHECKSUM
+ | (m_cVerbosity > 2 ? VBOXNETUDP_MATCH_PRINT_STDERR : 0),
+ &m_CurHdrs, &cb);
+ if (pv && cb)
+ {
+ PCRTNETBOOTP pDhcpMsg = (PCRTNETBOOTP)pv;
+ m_pCurMsg = pDhcpMsg;
+ m_cbCurMsg = cb;
+
+ uint8_t uMsgType;
+ if (RTNetIPv4IsDHCPValid(NULL /* why is this here? */, pDhcpMsg, cb, &uMsgType))
+ {
+ m_uCurMsgType = uMsgType;
+ handleDhcpMsg(uMsgType, pDhcpMsg, cb);
+ m_uCurMsgType = UINT8_MAX;
+ }
+ else
+ debugPrint(1, true, "VBoxNetDHCP: Skipping invalid DHCP packet.\n"); /** @todo handle pure bootp clients too? */
+
+ m_pCurMsg = NULL;
+ m_cbCurMsg = 0;
+ }
+ else if (VBoxNetArpHandleIt(m_pSession, m_hIf, m_pIfBuf, &m_MacAddress, m_Ipv4Address))
+ {
+ /* nothing */
+ }
+
+ /* Advance to the next frame. */
+ IntNetRingSkipFrame(pRingBuf);
+ }
+ }
+
+ return 0;
+}
+
+
+/**
+ * Handles a DHCP message.
+ *
+ * @returns true if handled, false if not.
+ * @param uMsgType The message type.
+ * @param pDhcpMsg The DHCP message.
+ * @param cb The size of the DHCP message.
+ */
+bool VBoxNetDhcp::handleDhcpMsg(uint8_t uMsgType, PCRTNETBOOTP pDhcpMsg, size_t cb)
+{
+ if (pDhcpMsg->bp_op == RTNETBOOTP_OP_REQUEST)
+ {
+ switch (uMsgType)
+ {
+ case RTNET_DHCP_MT_DISCOVER:
+ return handleDhcpReqDiscover(pDhcpMsg, cb);
+
+ case RTNET_DHCP_MT_REQUEST:
+ return handleDhcpReqRequest(pDhcpMsg, cb);
+
+ case RTNET_DHCP_MT_DECLINE:
+ return handleDhcpReqDecline(pDhcpMsg, cb);
+
+ case RTNET_DHCP_MT_RELEASE:
+ return handleDhcpReqRelease(pDhcpMsg, cb);
+
+ case RTNET_DHCP_MT_INFORM:
+ debugPrint(0, true, "Should we handle this?");
+ break;
+
+ default:
+ debugPrint(0, true, "Unexpected.");
+ break;
+ }
+ }
+ return false;
+}
+
+
+/**
+ * The client is requesting an offer.
+ *
+ * @returns true.
+ *
+ * @param pDhcpMsg The message.
+ * @param cb The message size.
+ */
+bool VBoxNetDhcp::handleDhcpReqDiscover(PCRTNETBOOTP pDhcpMsg, size_t cb)
+{
+ /*
+ * The newLease() method contains logic for finding current leases
+ * and reusing them in case the client is forgetful.
+ */
+ VBoxNetDhcpLease *pLease = newLease(pDhcpMsg, cb);
+ if (!pLease)
+ return false;
+ debugPrint(1, true, "Offering %d.%d.%d.%d to %.6Rhxs xid=%#x",
+ pLease->m_IPv4Address.au8[0],
+ pLease->m_IPv4Address.au8[1],
+ pLease->m_IPv4Address.au8[2],
+ pLease->m_IPv4Address.au8[3],
+ &pDhcpMsg->bp_chaddr.Mac,
+ pDhcpMsg->bp_xid);
+ pLease->offer(pDhcpMsg->bp_xid);
+
+ makeDhcpReply(RTNET_DHCP_MT_OFFER, pLease, pDhcpMsg, cb);
+ return true;
+}
+
+
+/**
+ * The client is requesting an offer.
+ *
+ * @returns true.
+ *
+ * @param pDhcpMsg The message.
+ * @param cb The message size.
+ */
+bool VBoxNetDhcp::handleDhcpReqRequest(PCRTNETBOOTP pDhcpMsg, size_t cb)
+{
+ /** @todo Probably need to match the server IP here to work correctly with
+ * other servers. */
+ /** @todo This code isn't entirely correct and quite a bit of a hack, but it
+ * will have to do for now as the right thing (tm) is very complex.
+ * Part of the fun is verifying that the request is something we can
+ * and should handle. */
+
+ /*
+ * Try find the lease by the requested address + client MAC address.
+ */
+ VBoxNetDhcpLease *pLease = NULL;
+ RTNETADDRIPV4 IPv4Addr;
+ bool fReqAddr = findOptionIPv4Addr(RTNET_DHCP_OPT_REQ_ADDR, pDhcpMsg, cb, &IPv4Addr);
+ if (fReqAddr)
+ {
+ fReqAddr = true;
+ pLease = findLeaseByIpv4AndMacAddresses(IPv4Addr, &pDhcpMsg->bp_chaddr.Mac, true /* fAnyState */);
+ }
+
+ /*
+ * Try find the lease by the client IP address + client MAC address.
+ */
+ if ( !pLease
+ && pDhcpMsg->bp_ciaddr.u)
+ pLease = findLeaseByIpv4AndMacAddresses(pDhcpMsg->bp_ciaddr, &pDhcpMsg->bp_chaddr.Mac, true /* fAnyState */);
+
+#if 0 /** @todo client id stuff - it doesn't make sense here imho, we need IP + MAC. What would make sense
+ though is to compare the client id with what we've got in the lease and use it to root out
+ bad requests. */
+ /*
+ * Try find the lease by using the client id.
+ */
+ if (!pLease)
+ {
+ size_t cbClientID = 0;
+ uint8_t const *pbClientID = findOption(RTNET_DHCP_OPT_CLIENT_ID, pDhcpMsg, cb, &cbClientID);
+ if ( pbClientID
+ && cbClientID == sizeof(RTMAC) + 1
+ && pbClientID[0] == RTNET_ARP_ETHER
+ &&
+ )
+ {
+ pLease = findLeaseByIpv4AndMacAddresses(pDhcpMsg->bp_ciaddr, &pDhcpMsg->bp_chaddr.Mac, true /* fAnyState */);
+ }
+ }
+#endif
+
+ /*
+ * Validate the lease that's requested.
+ * We've already check the MAC and IP addresses.
+ */
+ bool fAckIt = false;
+ if (pLease)
+ {
+ if (pLease->isBeingOffered())
+ {
+ if (pLease->m_xid == pDhcpMsg->bp_xid)
+ debugPrint(2, true, "REQUEST for offered lease.");
+ else
+ debugPrint(2, true, "REQUEST for offered lease, xid mismatch. Expected %#x, got %#x.",
+ pLease->m_xid, pDhcpMsg->bp_xid);
+ pLease->activate(pDhcpMsg->bp_xid);
+ fAckIt = true;
+ }
+ else if (!pLease->isInCurrentConfig())
+ debugPrint(1, true, "REQUEST for obsolete lease -> NAK");
+ else if (fReqAddr != (pDhcpMsg->bp_ciaddr.u != 0)) // ???
+ {
+ /** @todo this ain't safe. */
+ debugPrint(1, true, "REQUEST for lease not on offer, assuming renewal. lease_xid=%#x bp_xid=%#x",
+ pLease->m_xid, pDhcpMsg->bp_xid);
+ fAckIt = true;
+ pLease->activate(pDhcpMsg->bp_xid);
+ }
+ else
+ debugPrint(1, true, "REQUEST for lease not on offer, NAK it.");
+ }
+
+ /*
+ * NAK if if no lease was found.
+ */
+ if (fAckIt)
+ {
+ debugPrint(1, false, "ACK'ing DHCP_REQUEST");
+ makeDhcpReply(RTNET_DHCP_MT_ACK, pLease, pDhcpMsg, cb);
+ }
+ else
+ {
+ debugPrint(1, false, "NAK'ing DHCP_REQUEST");
+ makeDhcpReply(RTNET_DHCP_MT_NAC, NULL, pDhcpMsg, cb);
+ }
+
+ return true;
+}
+
+
+/**
+ * The client is declining an offer we've made.
+ *
+ * @returns true.
+ *
+ * @param pDhcpMsg The message.
+ * @param cb The message size.
+ */
+bool VBoxNetDhcp::handleDhcpReqDecline(PCRTNETBOOTP pDhcpMsg, size_t cb)
+{
+ /** @todo Probably need to match the server IP here to work correctly with
+ * other servers. */
+
+ /*
+ * The client is supposed to pass us option 50, requested address,
+ * from the offer. We also match the lease state. Apparently the
+ * MAC address is not supposed to be checked here.
+ */
+
+ /** @todo this is not required in the initial implementation, do it later. */
+ debugPrint(1, true, "DECLINE is not implemented");
+ return true;
+}
+
+
+/**
+ * The client is releasing its lease - good boy.
+ *
+ * @returns true.
+ *
+ * @param pDhcpMsg The message.
+ * @param cb The message size.
+ */
+bool VBoxNetDhcp::handleDhcpReqRelease(PCRTNETBOOTP pDhcpMsg, size_t cb)
+{
+ /** @todo Probably need to match the server IP here to work correctly with
+ * other servers. */
+
+ /*
+ * The client may pass us option 61, client identifier, which we should
+ * use to find the lease by.
+ *
+ * We're matching MAC address and lease state as well.
+ */
+
+ /*
+ * If no client identifier or if we couldn't find a lease by using it,
+ * we will try look it up by the client IP address.
+ */
+
+
+ /*
+ * If found, release it.
+ */
+
+
+ /** @todo this is not required in the initial implementation, do it later. */
+ debugPrint(1, true, "RELEASE is not implemented");
+ return true;
+}
+
+
+/**
+ * Helper class for stuffing DHCP options into a reply packet.
+ */
+class VBoxNetDhcpWriteCursor
+{
+private:
+ uint8_t *m_pbCur; /**< The current cursor position. */
+ uint8_t *m_pbEnd; /**< The end the current option space. */
+ uint8_t *m_pfOverload; /**< Pointer to the flags of the overload option. */
+ uint8_t m_fUsed; /**< Overload fields that have been used. */
+ PRTNETDHCPOPT m_pOpt; /**< The current option. */
+ PRTNETBOOTP m_pDhcp; /**< The DHCP packet. */
+ bool m_fOverflowed; /**< Set if we've overflowed, otherwise false. */
+
+public:
+ /** Instantiate an option cursor for the specified DHCP message. */
+ VBoxNetDhcpWriteCursor(PRTNETBOOTP pDhcp, size_t cbDhcp) :
+ m_pbCur(&pDhcp->bp_vend.Dhcp.dhcp_opts[0]),
+ m_pbEnd((uint8_t *)pDhcp + cbDhcp),
+ m_pfOverload(NULL),
+ m_fUsed(0),
+ m_pOpt(NULL),
+ m_pDhcp(pDhcp),
+ m_fOverflowed(false)
+ {
+ AssertPtr(pDhcp);
+ Assert(cbDhcp > RT_UOFFSETOF(RTNETBOOTP, bp_vend.Dhcp.dhcp_opts[10]));
+ }
+
+ /** Destructor. */
+ ~VBoxNetDhcpWriteCursor()
+ {
+ m_pbCur = m_pbEnd = m_pfOverload = NULL;
+ m_pOpt = NULL;
+ m_pDhcp = NULL;
+ }
+
+ /**
+ * Try use the bp_file field.
+ * @returns true if not overloaded, false otherwise.
+ */
+ bool useBpFile(void)
+ {
+ if ( m_pfOverload
+ && (*m_pfOverload & 1))
+ return false;
+ m_fUsed |= 1 /* bp_file flag*/;
+ return true;
+ }
+
+
+ /**
+ * Try overload more BOOTP fields
+ */
+ bool overloadMore(void)
+ {
+ /* switch option area. */
+ uint8_t *pbNew;
+ uint8_t *pbNewEnd;
+ uint8_t fField;
+ if (!(m_fUsed & 1))
+ {
+ fField = 1;
+ pbNew = &m_pDhcp->bp_file[0];
+ pbNewEnd = &m_pDhcp->bp_file[sizeof(m_pDhcp->bp_file)];
+ }
+ else if (!(m_fUsed & 2))
+ {
+ fField = 2;
+ pbNew = &m_pDhcp->bp_sname[0];
+ pbNewEnd = &m_pDhcp->bp_sname[sizeof(m_pDhcp->bp_sname)];
+ }
+ else
+ return false;
+
+ if (!m_pfOverload)
+ {
+ /* Add an overload option. */
+ *m_pbCur++ = RTNET_DHCP_OPT_OPTION_OVERLOAD;
+ *m_pbCur++ = fField;
+ m_pfOverload = m_pbCur;
+ *m_pbCur++ = 1; /* bp_file flag */
+ }
+ else
+ *m_pfOverload |= fField;
+
+ /* pad current option field */
+ while (m_pbCur != m_pbEnd)
+ *m_pbCur++ = RTNET_DHCP_OPT_PAD; /** @todo not sure if this stuff is at all correct... */
+
+ /* switch */
+ m_pbCur = pbNew;
+ m_pbEnd = pbNewEnd;
+ return true;
+ }
+
+ /**
+ * Begin an option.
+ *
+ * @returns true on success, false if we're out of space.
+ *
+ * @param uOption The option number.
+ * @param cb The amount of data.
+ */
+ bool begin(uint8_t uOption, size_t cb)
+ {
+ /* Check that the data of the previous option has all been written. */
+ Assert( !m_pOpt
+ || (m_pbCur - m_pOpt->dhcp_len == (uint8_t *)(m_pOpt + 1)));
+ AssertMsg(cb <= 255, ("%#x\n", cb));
+
+ /* Check if we need to overload more stuff. */
+ if ((uintptr_t)(m_pbEnd - m_pbCur) < cb + 2 + (m_pfOverload ? 1 : 3))
+ {
+ m_pOpt = NULL;
+ if (!overloadMore())
+ {
+ m_fOverflowed = true;
+ AssertMsgFailedReturn(("%u %#x\n", uOption, cb), false);
+ }
+ if ((uintptr_t)(m_pbEnd - m_pbCur) < cb + 2 + 1)
+ {
+ m_fOverflowed = true;
+ AssertMsgFailedReturn(("%u %#x\n", uOption, cb), false);
+ }
+ }
+
+ /* Emit the option header. */
+ m_pOpt = (PRTNETDHCPOPT)m_pbCur;
+ m_pOpt->dhcp_opt = uOption;
+ m_pOpt->dhcp_len = (uint8_t)cb;
+ m_pbCur += 2;
+ return true;
+ }
+
+ /**
+ * Puts option data.
+ *
+ * @param pvData The data.
+ * @param cb The amount to put.
+ */
+ void put(void const *pvData, size_t cb)
+ {
+ Assert(m_pOpt || m_fOverflowed);
+ if (RT_LIKELY(m_pOpt))
+ {
+ Assert((uintptr_t)m_pbCur - (uintptr_t)(m_pOpt + 1) + cb <= (size_t)m_pOpt->dhcp_len);
+ memcpy(m_pbCur, pvData, cb);
+ m_pbCur += cb;
+ }
+ }
+
+ /**
+ * Puts an IPv4 Address.
+ *
+ * @param IPv4Addr The address.
+ */
+ void putIPv4Addr(RTNETADDRIPV4 IPv4Addr)
+ {
+ put(&IPv4Addr, 4);
+ }
+
+ /**
+ * Adds an IPv4 address option.
+ *
+ * @returns true/false just like begin().
+ *
+ * @param uOption The option number.
+ * @param IPv4Addr The address.
+ */
+ bool optIPv4Addr(uint8_t uOption, RTNETADDRIPV4 IPv4Addr)
+ {
+ if (!begin(uOption, 4))
+ return false;
+ putIPv4Addr(IPv4Addr);
+ return true;
+ }
+
+ /**
+ * Adds an option taking 1 or more IPv4 address.
+ *
+ * If the vector contains no addresses, the option will not be added.
+ *
+ * @returns true/false just like begin().
+ *
+ * @param uOption The option number.
+ * @param rIPv4Addrs Reference to the address vector.
+ */
+ bool optIPv4Addrs(uint8_t uOption, std::vector<RTNETADDRIPV4> const &rIPv4Addrs)
+ {
+ size_t const c = rIPv4Addrs.size();
+ if (!c)
+ return true;
+
+ if (!begin(uOption, 4*c))
+ return false;
+ for (size_t i = 0; i < c; i++)
+ putIPv4Addr(rIPv4Addrs[i]);
+ return true;
+ }
+
+ /**
+ * Puts an 8-bit integer.
+ *
+ * @param u8 The integer.
+ */
+ void putU8(uint8_t u8)
+ {
+ put(&u8, 1);
+ }
+
+ /**
+ * Adds an 8-bit integer option.
+ *
+ * @returns true/false just like begin().
+ *
+ * @param uOption The option number.
+ * @param u8 The integer
+ */
+ bool optU8(uint8_t uOption, uint8_t u8)
+ {
+ if (!begin(uOption, 1))
+ return false;
+ putU8(u8);
+ return true;
+ }
+
+ /**
+ * Puts an 32-bit integer (network endian).
+ *
+ * @param u32Network The integer.
+ */
+ void putU32(uint32_t u32)
+ {
+ put(&u32, 4);
+ }
+
+ /**
+ * Adds an 32-bit integer (network endian) option.
+ *
+ * @returns true/false just like begin().
+ *
+ * @param uOption The option number.
+ * @param u32Network The integer.
+ */
+ bool optU32(uint8_t uOption, uint32_t u32)
+ {
+ if (!begin(uOption, 4))
+ return false;
+ putU32(u32);
+ return true;
+ }
+
+ /**
+ * Puts a std::string.
+ *
+ * @param rStr Reference to the string.
+ */
+ void putStr(std::string const &rStr)
+ {
+ put(rStr.c_str(), rStr.size());
+ }
+
+ /**
+ * Adds an std::string option if the string isn't empty.
+ *
+ * @returns true/false just like begin().
+ *
+ * @param uOption The option number.
+ * @param rStr Reference to the string.
+ */
+ bool optStr(uint8_t uOption, std::string const &rStr)
+ {
+ const size_t cch = rStr.size();
+ if (!cch)
+ return true;
+
+ if (!begin(uOption, cch))
+ return false;
+ put(rStr.c_str(), cch);
+ return true;
+ }
+
+ /**
+ * Whether we've overflowed.
+ *
+ * @returns true on overflow, false otherwise.
+ */
+ bool hasOverflowed(void) const
+ {
+ return m_fOverflowed;
+ }
+
+ /**
+ * Adds the terminating END option.
+ *
+ * The END will always be added as we're reserving room for it, however, we
+ * might have dropped previous options due to overflows and that is what the
+ * return status indicates.
+ *
+ * @returns true on success, false on a (previous) overflow.
+ */
+ bool optEnd(void)
+ {
+ Assert((uintptr_t)(m_pbEnd - m_pbCur) < 4096);
+ *m_pbCur++ = RTNET_DHCP_OPT_END;
+ return !hasOverflowed();
+ }
+};
+
+
+/**
+ * Constructs and sends a reply to a client.
+ *
+ * @returns
+ * @param uMsgType The DHCP message type.
+ * @param pLease The lease. This can be NULL for some replies.
+ * @param pDhcpMsg The client message. We will dig out the MAC address,
+ * transaction ID, and requested options from this.
+ * @param cb The size of the client message.
+ */
+void VBoxNetDhcp::makeDhcpReply(uint8_t uMsgType, VBoxNetDhcpLease *pLease, PCRTNETBOOTP pDhcpMsg, size_t cb)
+{
+ size_t cbReply = RTNET_DHCP_NORMAL_SIZE; /** @todo respect the RTNET_DHCP_OPT_MAX_DHCP_MSG_SIZE option */
+ PRTNETBOOTP pReply = (PRTNETBOOTP)alloca(cbReply);
+
+ /*
+ * The fixed bits stuff.
+ */
+ pReply->bp_op = RTNETBOOTP_OP_REPLY;
+ pReply->bp_htype = RTNET_ARP_ETHER;
+ pReply->bp_hlen = sizeof(RTMAC);
+ pReply->bp_hops = 0;
+ pReply->bp_xid = pDhcpMsg->bp_xid;
+ pReply->bp_secs = 0;
+ pReply->bp_flags = 0; // (pDhcpMsg->bp_flags & RTNET_DHCP_FLAGS_NO_BROADCAST); ??
+ pReply->bp_ciaddr.u = 0;
+ pReply->bp_yiaddr.u = pLease ? pLease->m_IPv4Address.u : 0xffffffff;
+ pReply->bp_siaddr.u = pLease && pLease->m_pCfg ? pLease->m_pCfg->m_TftpServerAddr.u : 0; /* (next server == TFTP)*/
+ pReply->bp_giaddr.u = 0;
+ memset(&pReply->bp_chaddr, '\0', sizeof(pReply->bp_chaddr));
+ pReply->bp_chaddr.Mac = pDhcpMsg->bp_chaddr.Mac;
+ memset(&pReply->bp_sname[0], '\0', sizeof(pReply->bp_sname));
+ memset(&pReply->bp_file[0], '\0', sizeof(pReply->bp_file));
+ pReply->bp_vend.Dhcp.dhcp_cookie = RT_H2N_U32_C(RTNET_DHCP_COOKIE);
+ memset(&pReply->bp_vend.Dhcp.dhcp_opts[0], '\0', RTNET_DHCP_OPT_SIZE);
+
+ /*
+ * The options - use a cursor class for dealing with the ugly stuff.
+ */
+ VBoxNetDhcpWriteCursor Cursor(pReply, cbReply);
+
+ /* The basics */
+ Cursor.optU8(RTNET_DHCP_OPT_MSG_TYPE, uMsgType);
+ Cursor.optIPv4Addr(RTNET_DHCP_OPT_SERVER_ID, m_Ipv4Address);
+
+ if (uMsgType != RTNET_DHCP_MT_NAC)
+ {
+ AssertReturnVoid(pLease && pLease->m_pCfg);
+ const VBoxNetDhcpCfg *pCfg = pLease->m_pCfg; /* no need to retain it. */
+
+ /* The IP config. */
+ Cursor.optU32(RTNET_DHCP_OPT_LEASE_TIME, RT_H2N_U32(pCfg->m_cSecLease));
+ Cursor.optIPv4Addr(RTNET_DHCP_OPT_SUBNET_MASK, pCfg->m_SubnetMask);
+ Cursor.optIPv4Addrs(RTNET_DHCP_OPT_ROUTERS, pCfg->m_Routers);
+ Cursor.optIPv4Addrs(RTNET_DHCP_OPT_ROUTERS, pCfg->m_DNSes);
+ Cursor.optStr(RTNET_DHCP_OPT_HOST_NAME, pCfg->m_HostName);
+ Cursor.optStr(RTNET_DHCP_OPT_DOMAIN_NAME, pCfg->m_DomainName);
+
+ /* The PXE config. */
+ if (pCfg->m_BootfileName.size())
+ {
+ if (Cursor.useBpFile())
+ RTStrPrintf((char *)&pReply->bp_file[0], sizeof(pReply->bp_file), "%s", pCfg->m_BootfileName.c_str());
+ else
+ Cursor.optStr(RTNET_DHCP_OPT_BOOTFILE_NAME, pCfg->m_BootfileName);
+ }
+ }
+
+ /* Terminate the options. */
+ if (!Cursor.optEnd())
+ debugPrint(0, true, "option overflow\n");
+
+ /*
+ * Send it.
+ */
+ int rc;
+#if 0
+ if (!(pDhcpMsg->bp_flags & RTNET_DHCP_FLAGS_NO_BROADCAST)) /** @todo need to see someone set this flag to check that it's correct. */
+ {
+ RTNETADDRIPV4 IPv4AddrBrdCast;
+ IPv4AddrBrdCast.u = UINT32_C(0xffffffff); /* broadcast IP */
+ rc = VBoxNetUDPUnicast(m_pSession, m_hIf, m_pIfBuf,
+ m_Ipv4Address, &m_MacAddress, RTNETIPV4_PORT_BOOTPS, /* sender */
+ IPv4AddrBrdCast, &pDhcpMsg->bp_chaddr.Mac, RTNETIPV4_PORT_BOOTPC, /* receiver */
+ pReply, cbReply);
+ }
+ else
+#endif
+ rc = VBoxNetUDPBroadcast(m_pSession, m_hIf, m_pIfBuf,
+ m_Ipv4Address, &m_MacAddress, RTNETIPV4_PORT_BOOTPS, /* sender */
+ RTNETIPV4_PORT_BOOTPC, /* receiver port */
+ pReply, cbReply);
+ if (RT_FAILURE(rc))
+ debugPrint(0, true, "error %Rrc when sending the reply", rc);
+}
+
+
+/**
+ * Look up a lease by MAC address.
+ *
+ * @returns Pointer to the lease if found, NULL if not found.
+ * @param pMacAddress The mac address.
+ * @param fAnyState Any state.
+ */
+VBoxNetDhcpLease *VBoxNetDhcp::findLeaseByMacAddress(PCRTMAC pMacAddress, bool fAnyState)
+{
+ size_t iLease = m_Leases.size();
+ while (iLease-- > 0)
+ {
+ VBoxNetDhcpLease *pLease = &m_Leases[iLease];
+ if ( pLease
+ && pLease->m_MacAddress.au16[0] == pMacAddress->au16[0]
+ && pLease->m_MacAddress.au16[1] == pMacAddress->au16[1]
+ && pLease->m_MacAddress.au16[2] == pMacAddress->au16[2]
+ && ( fAnyState
+ || (pLease->m_enmState != VBoxNetDhcpLease::kState_Free)) )
+ return pLease;
+ }
+
+ return NULL;
+}
+
+
+/**
+ * Look up a lease by IPv4 and MAC addresses.
+ *
+ * @returns Pointer to the lease if found, NULL if not found.
+ * @param IPv4Addr The IPv4 address.
+ * @param pMacAddress The mac address.
+ * @param fAnyState Any state.
+ */
+VBoxNetDhcpLease *VBoxNetDhcp::findLeaseByIpv4AndMacAddresses(RTNETADDRIPV4 IPv4Addr, PCRTMAC pMacAddress, bool fAnyState)
+{
+ size_t iLease = m_Leases.size();
+ while (iLease-- > 0)
+ {
+ VBoxNetDhcpLease *pLease = &m_Leases[iLease];
+ if ( pLease
+ && pLease->m_IPv4Address.u == IPv4Addr.u
+ && pLease->m_MacAddress.au16[0] == pMacAddress->au16[0]
+ && pLease->m_MacAddress.au16[1] == pMacAddress->au16[1]
+ && pLease->m_MacAddress.au16[2] == pMacAddress->au16[2]
+ && ( fAnyState
+ || (pLease->m_enmState != VBoxNetDhcpLease::kState_Free)) )
+ return pLease;
+ }
+
+ return NULL;
+}
+
+
+/**
+ * Creates a new lease for the client specified in the DHCP message.
+ *
+ * The caller has already made sure it doesn't already have a lease.
+ *
+ * @returns Pointer to the lease if found, NULL+log if not found.
+ * @param IPv4Addr The IPv4 address.
+ * @param pMacAddress The MAC address.
+ */
+VBoxNetDhcpLease *VBoxNetDhcp::newLease(PCRTNETBOOTP pDhcpMsg, size_t cb)
+{
+ RTMAC const MacAddr = pDhcpMsg->bp_chaddr.Mac;
+ RTTIMESPEC Now;
+ RTTimeNow(&Now);
+
+ /*
+ * Search the possible leases.
+ *
+ * We'll try do all the searches in one pass, that is to say, perfect
+ * match, old lease, and next free/expired lease.
+ */
+ VBoxNetDhcpLease *pBest = NULL;
+ VBoxNetDhcpLease *pOld = NULL;
+ VBoxNetDhcpLease *pFree = NULL;
+
+ size_t cLeases = m_Leases.size();
+ for (size_t i = 0; i < cLeases; i++)
+ {
+ VBoxNetDhcpLease *pCur = &m_Leases[i];
+
+ /* Skip it if no configuration, that means its not in the current config. */
+ if (!pCur->m_pCfg)
+ continue;
+
+ /* best */
+ if ( pCur->isOneSpecificClient()
+ && pCur->m_pCfg->matchesMacAddress(&MacAddr))
+ {
+ if ( !pBest
+ || pBest->m_pCfg->m_MacAddresses.size() < pCur->m_pCfg->m_MacAddresses.size())
+ pBest = pCur;
+ }
+
+ /* old lease */
+ if ( pCur->m_MacAddress.au16[0] == MacAddr.au16[0]
+ && pCur->m_MacAddress.au16[1] == MacAddr.au16[1]
+ && pCur->m_MacAddress.au16[2] == MacAddr.au16[2])
+ {
+ if ( !pOld
+ || RTTimeSpecGetSeconds(&pCur->m_ExpireTime) > RTTimeSpecGetSeconds(&pFree->m_ExpireTime))
+ pOld = pCur;
+ }
+
+ /* expired lease */
+ if (!pCur->isInUse(&Now))
+ {
+ if ( !pFree
+ || RTTimeSpecGetSeconds(&pCur->m_ExpireTime) < RTTimeSpecGetSeconds(&pFree->m_ExpireTime))
+ pFree = pCur;
+ }
+ }
+
+ VBoxNetDhcpLease *pNew = pBest;
+ if (!pNew)
+ pNew = pOld;
+ if (!pNew)
+ pNew = pFree;
+ if (!pNew)
+ {
+ debugPrint(0, true, "No more leases.");
+ return NULL;
+ }
+
+ /*
+ * Init the lease.
+ */
+ pNew->m_MacAddress = MacAddr;
+ pNew->m_xid = pDhcpMsg->bp_xid;
+ /** @todo extract the client id. */
+
+ return pNew;
+}
+
+
+/**
+ * Finds an option.
+ *
+ * @returns On success, a pointer to the first byte in the option data (no none
+ * then it'll be the byte following the 0 size field) and *pcbOpt set
+ * to the option length.
+ * On failure, NULL is returned and *pcbOpt unchanged.
+ *
+ * @param uOption The option to search for.
+ * @param pDhcpMsg The DHCP message.
+ * @param cb The size of the message.
+ * @param pcbOpt Where to store the option size size. Optional. Note
+ * that this is adjusted if the option length is larger
+ * than the message buffer.
+ */
+/* static */ const uint8_t *
+VBoxNetDhcp::findOption(uint8_t uOption, PCRTNETBOOTP pDhcpMsg, size_t cb, size_t *pcbOpt)
+{
+ Assert(uOption != RTNET_DHCP_OPT_PAD);
+
+ /*
+ * Validate the DHCP bits and figure the max size of the options in the vendor field.
+ */
+ if (cb <= RT_UOFFSETOF(RTNETBOOTP, bp_vend.Dhcp.dhcp_opts))
+ return NULL;
+ if (pDhcpMsg->bp_vend.Dhcp.dhcp_cookie != RT_H2N_U32_C(RTNET_DHCP_COOKIE))
+ return NULL;
+ size_t cbLeft = cb - RT_UOFFSETOF(RTNETBOOTP, bp_vend.Dhcp.dhcp_opts);
+ if (cbLeft > RTNET_DHCP_OPT_SIZE)
+ cbLeft = RTNET_DHCP_OPT_SIZE;
+
+ /*
+ * Search the vendor field.
+ */
+ bool fExtended = false;
+ uint8_t const *pb = &pDhcpMsg->bp_vend.Dhcp.dhcp_opts[0];
+ while (pb && cbLeft > 0)
+ {
+ uint8_t uCur = *pb;
+ if (uCur == RTNET_DHCP_OPT_PAD)
+ {
+ cbLeft--;
+ pb++;
+ }
+ else if (cbLeft <= 1)
+ break;
+ else
+ {
+ size_t cbCur = pb[1];
+ if (cbCur > cbLeft - 2)
+ cbCur = cbLeft - 2;
+ if (uCur == uOption)
+ {
+ if (pcbOpt)
+ *pcbOpt = cbCur;
+ return pb+2;
+ }
+ pb += cbCur + 2;
+ cbLeft -= cbCur - 2;
+ }
+ }
+
+ /** @todo search extended dhcp option field(s) when present */
+
+ return NULL;
+}
+
+
+/**
+ * Locates an option with an IPv4 address in the DHCP message.
+ *
+ * @returns true and *pIpv4Addr if found, false if not.
+ *
+ * @param uOption The option to find.
+ * @param pDhcpMsg The DHCP message.
+ * @param cb The size of the message.
+ * @param pIPv4Addr Where to put the address.
+ */
+/* static */ bool
+VBoxNetDhcp::findOptionIPv4Addr(uint8_t uOption, PCRTNETBOOTP pDhcpMsg, size_t cb, PRTNETADDRIPV4 pIPv4Addr)
+{
+ size_t cbOpt;
+ uint8_t const *pbOpt = findOption(uOption, pDhcpMsg, cb, &cbOpt);
+ if (pbOpt)
+ {
+ if (cbOpt >= sizeof(RTNETADDRIPV4))
+ {
+ *pIPv4Addr = *(PCRTNETADDRIPV4)pbOpt;
+ return true;
+ }
+ }
+ return false;
+}
+
+
+/**
+ * Print debug message depending on the m_cVerbosity level.
+ *
+ * @param iMinLevel The minimum m_cVerbosity level for this message.
+ * @param fMsg Whether to dump parts for the current DHCP message.
+ * @param pszFmt The message format string.
+ * @param ... Optional arguments.
+ */
+inline void VBoxNetDhcp::debugPrint(int32_t iMinLevel, bool fMsg, const char *pszFmt, ...) const
+{
+ if (iMinLevel <= m_cVerbosity)
+ {
+ va_list va;
+ va_start(va, pszFmt);
+ debugPrintV(iMinLevel, fMsg, pszFmt, va);
+ va_end(va);
+ }
+}
+
+
+/**
+ * Print debug message depending on the m_cVerbosity level.
+ *
+ * @param iMinLevel The minimum m_cVerbosity level for this message.
+ * @param fMsg Whether to dump parts for the current DHCP message.
+ * @param pszFmt The message format string.
+ * @param va Optional arguments.
+ */
+void VBoxNetDhcp::debugPrintV(int iMinLevel, bool fMsg, const char *pszFmt, va_list va) const
+{
+ if (iMinLevel <= m_cVerbosity)
+ {
+ va_list vaCopy; /* This dude is *very* special, thus the copy. */
+ va_copy(vaCopy, va);
+ RTStrmPrintf(g_pStdErr, "VBoxNetDHCP: %s: %N\n", iMinLevel >= 2 ? "debug" : "info", pszFmt, &vaCopy);
+ va_end(vaCopy);
+
+ if ( fMsg
+ && m_cVerbosity >= 2
+ && m_pCurMsg)
+ {
+ const char *pszMsg = m_uCurMsgType != UINT8_MAX ? debugDhcpName(m_uCurMsgType) : "";
+ RTStrmPrintf(g_pStdErr, "VBoxNetDHCP: debug: %8s chaddr=%.6Rhxs ciaddr=%d.%d.%d.%d yiaddr=%d.%d.%d.%d siaddr=%d.%d.%d.%d xid=%#x\n",
+ pszMsg,
+ &m_pCurMsg->bp_chaddr,
+ m_pCurMsg->bp_ciaddr.au8[0], m_pCurMsg->bp_ciaddr.au8[1], m_pCurMsg->bp_ciaddr.au8[2], m_pCurMsg->bp_ciaddr.au8[3],
+ m_pCurMsg->bp_yiaddr.au8[0], m_pCurMsg->bp_yiaddr.au8[1], m_pCurMsg->bp_yiaddr.au8[2], m_pCurMsg->bp_yiaddr.au8[3],
+ m_pCurMsg->bp_siaddr.au8[0], m_pCurMsg->bp_siaddr.au8[1], m_pCurMsg->bp_siaddr.au8[2], m_pCurMsg->bp_siaddr.au8[3],
+ m_pCurMsg->bp_xid);
+ }
+ }
+}
+
+
+/**
+ * Gets the name of given DHCP message type.
+ *
+ * @returns Readonly name.
+ * @param uMsgType The message number.
+ */
+/* static */ const char *VBoxNetDhcp::debugDhcpName(uint8_t uMsgType)
+{
+ switch (uMsgType)
+ {
+ case 0: return "MT_00";
+ case RTNET_DHCP_MT_DISCOVER: return "DISCOVER";
+ case RTNET_DHCP_MT_OFFER: return "OFFER";
+ case RTNET_DHCP_MT_REQUEST: return "REQUEST";
+ case RTNET_DHCP_MT_DECLINE: return "DECLINE";
+ case RTNET_DHCP_MT_ACK: return "ACK";
+ case RTNET_DHCP_MT_NAC: return "NAC";
+ case RTNET_DHCP_MT_RELEASE: return "RELEASE";
+ case RTNET_DHCP_MT_INFORM: return "INFORM";
+ case 9: return "MT_09";
+ case 10: return "MT_0a";
+ case 11: return "MT_0b";
+ case 12: return "MT_0c";
+ case 13: return "MT_0d";
+ case 14: return "MT_0e";
+ case 15: return "MT_0f";
+ case 16: return "MT_10";
+ case 17: return "MT_11";
+ case 18: return "MT_12";
+ case 19: return "MT_13";
+ case UINT8_MAX: return "MT_ff";
+ default: return "UNKNOWN";
+ }
+}
+
+
+
+/**
+ * Entry point.
+ */
+extern "C" DECLEXPORT(int) TrustedMain(int argc, char **argv, char **envp)
+{
+ /*
+ * Instantiate the DHCP server and hand it the options.
+ */
+ VBoxNetDhcp *pDhcp = new VBoxNetDhcp();
+ if (!pDhcp)
+ {
+ RTStrmPrintf(g_pStdErr, "VBoxNetDHCP: new VBoxNetDhcp failed!\n");
+ return 1;
+ }
+ int rc = pDhcp->parseArgs(argc - 1, argv + 1);
+ if (rc)
+ return rc;
+
+ /*
+ * Try connect the server to the network.
+ */
+ rc = pDhcp->tryGoOnline();
+ if (rc)
+ {
+ delete pDhcp;
+ return rc;
+ }
+
+ /*
+ * Process requests.
+ */
+ g_pDhcp = pDhcp;
+ rc = pDhcp->run();
+ g_pDhcp = NULL;
+ delete pDhcp;
+
+ return rc;
+}
+
+
+#ifndef VBOX_WITH_HARDENING
+
+int main(int argc, char **argv, char **envp)
+{
+ int rc = RTR3InitExe(argc, &argv, RTR3INIT_FLAGS_SUPLIB);
+ if (RT_FAILURE(rc))
+ return RTMsgInitFailure(rc);
+
+ return TrustedMain(argc, argv, envp);
+}
+
+# ifdef RT_OS_WINDOWS
+
+static LRESULT CALLBACK WindowProc(HWND hwnd,
+ UINT uMsg,
+ WPARAM wParam,
+ LPARAM lParam
+)
+{
+ if(uMsg == WM_DESTROY)
+ {
+ PostQuitMessage(0);
+ return 0;
+ }
+ return DefWindowProc (hwnd, uMsg, wParam, lParam);
+}
+
+static LPCSTR g_WndClassName = "VBoxNetDHCPClass";
+
+static DWORD WINAPI MsgThreadProc(__in LPVOID lpParameter)
+{
+ HWND hwnd = 0;
+ HINSTANCE hInstance = (HINSTANCE)GetModuleHandle (NULL);
+ bool bExit = false;
+
+ /* Register the Window Class. */
+ WNDCLASS wc;
+ wc.style = 0;
+ wc.lpfnWndProc = WindowProc;
+ wc.cbClsExtra = 0;
+ wc.cbWndExtra = sizeof(void *);
+ wc.hInstance = hInstance;
+ wc.hIcon = NULL;
+ wc.hCursor = NULL;
+ wc.hbrBackground = (HBRUSH)(COLOR_BACKGROUND + 1);
+ wc.lpszMenuName = NULL;
+ wc.lpszClassName = g_WndClassName;
+
+ ATOM atomWindowClass = RegisterClass(&wc);
+
+ if (atomWindowClass != 0)
+ {
+ /* Create the window. */
+ hwnd = CreateWindowEx (WS_EX_TOOLWINDOW | WS_EX_TRANSPARENT | WS_EX_TOPMOST,
+ g_WndClassName, g_WndClassName,
+ WS_POPUPWINDOW,
+ -200, -200, 100, 100, NULL, NULL, hInstance, NULL);
+
+ if (hwnd)
+ {
+ SetWindowPos(hwnd, HWND_TOPMOST, -200, -200, 0, 0,
+ SWP_NOACTIVATE | SWP_HIDEWINDOW | SWP_NOCOPYBITS | SWP_NOREDRAW | SWP_NOSIZE);
+
+ MSG msg;
+ while (GetMessage(&msg, NULL, 0, 0))
+ {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+
+ DestroyWindow (hwnd);
+
+ bExit = true;
+ }
+
+ UnregisterClass (g_WndClassName, hInstance);
+ }
+
+ 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.) */
+int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
+{
+ NOREF(hInstance); NOREF(hPrevInstance); NOREF(lpCmdLine); NOREF(nCmdShow);
+
+ HANDLE hThread = CreateThread(
+ 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*/
+ );
+
+ if(hThread != NULL)
+ CloseHandle(hThread);
+
+ return main(__argc, __argv, environ);
+}
+# endif /* RT_OS_WINDOWS */
+
+#endif /* !VBOX_WITH_HARDENING */
+
diff --git a/src/VBox/NetworkServices/DHCP/VBoxNetDHCPHardened.cpp b/src/VBox/NetworkServices/DHCP/VBoxNetDHCPHardened.cpp
new file mode 100644
index 00000000..7d0e1bef
--- /dev/null
+++ b/src/VBox/NetworkServices/DHCP/VBoxNetDHCPHardened.cpp
@@ -0,0 +1,25 @@
+/* $Id: VBoxNetDHCPHardened.cpp $ */
+/** @file
+ * VBoxNetDHCP - Hardened main().
+ */
+
+/*
+ * 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;
+ * 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.
+ */
+
+#include <VBox/sup.h>
+
+
+int main(int argc, char **argv, char **envp)
+{
+ return SUPR3HardenedMain("VBoxNetDHCP", 0 /* fFlags */, argc, argv, envp);
+}
+
diff --git a/src/VBox/NetworkServices/Makefile.kmk b/src/VBox/NetworkServices/Makefile.kmk
new file mode 100644
index 00000000..7d0e87e3
--- /dev/null
+++ b/src/VBox/NetworkServices/Makefile.kmk
@@ -0,0 +1,28 @@
+# $Id: Makefile.kmk $
+## @file
+# Top-level makefile for the VBox Network Services.
+#
+
+#
+# Copyright (C) 2009-2012 Oracle Corporation
+#
+# 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.
+#
+
+SUB_DEPTH = ../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+# Include sub-makefiles.
+include $(PATH_SUB_CURRENT)/DHCP/Makefile.kmk
+ifdef VBOX_WITH_NAT_SERVICE
+include $(PATH_SUB_CURRENT)/NAT/Makefile.kmk
+endif
+
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/NetworkServices/NAT/Makefile.kmk b/src/VBox/NetworkServices/NAT/Makefile.kmk
new file mode 100644
index 00000000..9c821cab
--- /dev/null
+++ b/src/VBox/NetworkServices/NAT/Makefile.kmk
@@ -0,0 +1,58 @@
+# $Id: Makefile.kmk $
+## @file
+#
+
+#
+# Copyright (C) 2006-2012 Oracle Corporation
+#
+# 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.
+#
+
+SUB_DEPTH = ../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+ifdef VBOX_WITH_HARDENING
+ PROGRAMS += VBoxNetNATHardened
+ DLLS += VBoxNetNAT
+else
+ PROGRAMS += VBoxNetNAT
+endif
+VBoxNetNAT_TEMPLATE =
+VBoxNetNAT_TEMPLATE := VBOXR3$(if-expr defined(VBOX_WITH_HARDENING),,EXE)
+VBoxNetNAT_INCS = ../../Devices/Network/slirp
+VBoxNetNAT_DEFS = VBOX_WITH_NAT_SERVICE
+VBoxNetNAT_SOURCES += VBoxNetNAT.cpp
+VBoxNetNAT_DEFS += VBOX_WITH_NAT_SERVICE
+
+#define def_vbox_slirp_service_cflags
+# $(file)_DEFS += VBOX_WITH_NAT_SERVICE
+#endef
+
+define def_vbox_slirp_service_sources
+ VBoxNetNAT_SOURCES += $1/$(file)
+endef
+VBOX_NOT_IN_NATSERVICE = Network/DrvNAT.cpp
+$(foreach file,$(filter-out $(VBOX_NOT_IN_NATSERVICE), $(VBOX_SLIRP_SOURCES)),$(eval $(call def_vbox_slirp_service_sources, ../../Devices)))
+$(foreach file,$(VBOX_SLIRP_ALIAS_SOURCES),$(eval $(call def_vbox_slirp_service_sources, ../../Devices)))
+$(foreach file,$(VBOX_SLIRP_BSD_SOURCES),$(eval $(call def_vbox_slirp_service_sources, ../../Devices)))
+
+$(foreach file,$(addprefix ../../Devices/, $(VBOX_SLIRP_BSD_SOURCES)),$(eval $(call def_vbox_slirp_cflags, ../../Devices/Network)))
+$(foreach file,$(addprefix ../../Devices/, $(VBOX_SLIRP_BSD_SOURCES)),$(eval $(call def_vbox_slirp_bsd_cflags, ../../Devices/Network)))
+$(foreach file,$(addprefix ../../Devices/, $(filter-out $(VBOX_WITH_NAT_SERVICE), $(VBOX_SLIRP_SOURCES))),$(eval $(call def_vbox_slirp_service_cflags, ../../Devices/Network)))
+$(foreach file,$(addprefix ../../Devices/, $(filter-out $(VBOX_WITH_NAT_SERVICE), $(VBOX_SLIRP_SOURCES))),$(eval $(call def_vbox_slirp_cflags, ../../Devices/Network)))
+$(foreach file,$(addprefix ../../Devices/, $(VBOX_SLIRP_ALIAS_SOURCES)),$(eval $(call def_vbox_slirp_cflags, ../../Devices/Network)))
+$(foreach file,$(addprefix ../../Devices/, $(VBOX_SLIRP_ALIAS_SOURCES)),$(eval $(call def_vbox_slirp_alias_cflags, ../../Devices/Network)))
+
+VBoxNetNAT_SOURCES += ../NetLib/VBoxNetBaseService.cpp
+
+VBoxNetNAT_LIBS = \
+ $(LIB_RUNTIME)
+VBoxNetNAT_LDFLAGS.win = /SUBSYSTEM:windows
+
+include $(FILE_KBUILD_SUB_FOOTER)
diff --git a/src/VBox/NetworkServices/NAT/VBoxNetNAT.cpp b/src/VBox/NetworkServices/NAT/VBoxNetNAT.cpp
new file mode 100644
index 00000000..d031777c
--- /dev/null
+++ b/src/VBox/NetworkServices/NAT/VBoxNetNAT.cpp
@@ -0,0 +1,815 @@
+/* $Id: VBoxNetNAT.cpp $ */
+/** @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;
+ * 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.
+ */
+
+/** @page pg_net_nat VBoxNetNAT
+ *
+ * Write a few words...
+ *
+ */
+
+/*******************************************************************************
+* Header Files *
+*******************************************************************************/
+#include <iprt/net.h>
+#include <iprt/initterm.h>
+#include <iprt/alloca.h>
+#include <iprt/err.h>
+#include <iprt/time.h>
+#include <iprt/timer.h>
+#include <iprt/thread.h>
+#include <iprt/stream.h>
+#include <iprt/path.h>
+#include <iprt/param.h>
+#include <iprt/pipe.h>
+#include <iprt/getopt.h>
+#include <iprt/string.h>
+#include <iprt/mem.h>
+#include <iprt/message.h>
+#include <iprt/req.h>
+#include <iprt/file.h>
+#include <iprt/semaphore.h>
+#define LOG_GROUP LOG_GROUP_NAT_SERVICE
+#include <VBox/log.h>
+
+#include <VBox/sup.h>
+#include <VBox/intnet.h>
+#include <VBox/intnetinline.h>
+#include <VBox/vmm/pdmnetinline.h>
+#include <VBox/vmm/vmm.h>
+#include <VBox/version.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[] =
+{
+ { "--pf", 'p', RTGETOPT_REQ_STRING }
+};
+
+typedef struct NATSEVICEPORTFORWARDRULE
+{
+ char *pszPortForwardRuleName;
+ struct in_addr IpV4HostAddr;
+ uint16_t u16HostPort;
+ struct in_addr IpV4GuestAddr;
+ uint16_t u16GuestPort;
+ bool fUdp;
+ char *pszStrRaw;
+} NATSEVICEPORTFORWARDRULE, *PNATSEVICEPORTFORWARDRULE;
+
+class VBoxNetNAT : public VBoxNetBaseService
+{
+public:
+ VBoxNetNAT();
+ virtual ~VBoxNetNAT();
+ void usage(void);
+ void run(void);
+ virtual int init(void);
+ virtual int parseOpt(int rc, const RTGETOPTUNION& getOptVal);
+
+public:
+ PNATState m_pNATState;
+ RTNETADDRIPV4 m_Ipv4Netmask;
+ bool m_fPassDomain;
+ RTTHREAD m_ThrNAT;
+ RTTHREAD m_ThrSndNAT;
+ RTTHREAD m_ThrUrgSndNAT;
+#ifdef RT_OS_WINDOWS
+ HANDLE m_hWakeupEvent;
+#else
+ RTPIPE m_hPipeWrite;
+ RTPIPE m_hPipeRead;
+#endif
+ /** Queue for NAT-thread-external events. */
+ /** event to wakeup the guest receive thread */
+ RTSEMEVENT m_EventSend;
+ /** event to wakeup the guest urgent receive thread */
+ RTSEMEVENT m_EventUrgSend;
+
+ RTREQQUEUE m_hReqQueue;
+ RTREQQUEUE m_hSendQueue;
+ RTREQQUEUE m_hUrgSendQueue;
+ volatile uint32_t cUrgPkt;
+ volatile uint32_t cPkt;
+ bool fIsRunning;
+ std::vector<PNATSEVICEPORTFORWARDRULE> m_vecPortForwardRuleFromCmdLine;
+};
+
+
+
+/*******************************************************************************
+* Global Variables *
+*******************************************************************************/
+/** Pointer to the NAT server. */
+class VBoxNetNAT *g_pNAT;
+static DECLCALLBACK(int) AsyncIoThread(RTTHREAD pThread, void *pvUser);
+static DECLCALLBACK(int) natSndThread(RTTHREAD pThread, void *pvUser);
+static DECLCALLBACK(int) natUrgSndThread(RTTHREAD pThread, void *pvUser);
+static void SendWorker(struct mbuf *m, size_t cb);
+static void IntNetSendWorker(bool urg, void *pvFrame, size_t cbFrame, struct mbuf *m);
+
+
+static void natNotifyNATThread(void)
+{
+ int rc;
+#ifndef RT_OS_WINDOWS
+ /* kick select() */
+ size_t cbIgnored;
+ rc = RTPipeWrite(g_pNAT->m_hPipeWrite, "", 1, &cbIgnored);
+#else
+ /* kick WSAWaitForMultipleEvents */
+ rc = WSASetEvent(g_pNAT->hWakeupEvent);
+#endif
+ AssertRC(rc);
+}
+
+VBoxNetNAT::VBoxNetNAT()
+{
+#if defined(RT_OS_WINDOWS)
+ /*@todo check if we can remove this*/
+ VBoxNetBaseService();
+#endif
+ m_enmTrunkType = kIntNetTrunkType_WhateverNone;
+ m_TrunkName = "";
+ m_MacAddress.au8[0] = 0x08;
+ m_MacAddress.au8[1] = 0x00;
+ m_MacAddress.au8[2] = 0x27;
+ m_MacAddress.au8[3] = 0x40;
+ m_MacAddress.au8[4] = 0x41;
+ m_MacAddress.au8[5] = 0x42;
+ m_Ipv4Address.u = RT_H2N_U32_C(RT_BSWAP_U32_C(RT_MAKE_U32_FROM_U8( 10, 0, 2, 2)));
+ m_Ipv4Netmask.u = RT_H2N_U32_C(0xffffff);
+ cPkt = 0;
+ cUrgPkt = 0;
+ VBoxNetBaseService::init();
+ for(unsigned int i = 0; i < RT_ELEMENTS(g_aGetOptDef); ++i)
+ m_vecOptionDefs.push_back(&g_aGetOptDef[i]);
+}
+
+VBoxNetNAT::~VBoxNetNAT() { }
+int VBoxNetNAT::init()
+{
+ int rc;
+#if 0
+ using namespace com;
+ HRESULT hrc = com::Initialize();
+ if (FAILED(hrc))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to initialize 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);
+ AssertReleaseRC(rc);
+
+ /* Why ? */
+ slirp_set_ethaddr_and_activate_port_forwarding(m_pNATState, &m_MacAddress.au8[0], INADDR_ANY);
+#if 0
+ in_addr ipv4HostAddr;
+ in_addr ipv4GuestAddr;
+ ipv4GuestAddr.s_addr = RT_H2N_U32_C(RT_BSWAP_U32_C(RT_MAKE_U32_FROM_U8( 10, 0, 2, 15)));
+ ipv4HostAddr.s_addr = INADDR_ANY;
+ slirp_add_redirect(m_pNATState, false, ipv4HostAddr, 2022, ipv4GuestAddr , 22, NULL);
+#endif
+ std::vector<PNATSEVICEPORTFORWARDRULE>::iterator it;
+ 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);
+ RTStrFree((*it)->pszStrRaw);
+ RTMemFree((*it));
+ }
+ m_vecPortForwardRuleFromCmdLine.clear();
+#ifndef RT_OS_WINDOWS
+ /*
+ * Create the control pipe.
+ */
+ rc = RTPipeCreate(&m_hPipeRead, &m_hPipeWrite, 0 /*fFlags*/);
+ AssertReleaseRC(rc);
+#else
+ m_hWakeupEvent = CreateEvent(NULL, FALSE, FALSE, NULL); /* auto-reset event */
+ AssertReleaseRC(m_hWakeupEvent != NULL);
+ slirp_register_external_event(m_pNATState, m_hWakeupEvent, VBOX_WAKEUP_EVENT_INDEX);
+#endif
+ rc = RTReqQueueCreate(&m_hReqQueue);
+ AssertReleaseRC(rc);
+
+ rc = RTReqQueueCreate(&m_hSendQueue);
+ AssertReleaseRC(rc);
+
+ rc = RTReqQueueCreate(&m_hUrgSendQueue);
+ AssertReleaseRC(rc);
+
+ g_pNAT->fIsRunning = true;
+ rc = RTThreadCreate(&m_ThrNAT, AsyncIoThread, this, 128 * _1K, RTTHREADTYPE_DEFAULT, 0, "NAT");
+ rc = RTThreadCreate(&m_ThrSndNAT, natSndThread, this, 128 * _1K, RTTHREADTYPE_DEFAULT, 0, "SndNAT");
+ rc = RTThreadCreate(&m_ThrUrgSndNAT, natUrgSndThread, this, 128 * _1K, RTTHREADTYPE_DEFAULT, 0, "UrgSndNAT");
+ rc = RTSemEventCreate(&m_EventSend);
+ rc = RTSemEventCreate(&m_EventUrgSend);
+ AssertReleaseRC(rc);
+ return VINF_SUCCESS;
+}
+
+/* Mandatory functions */
+void VBoxNetNAT::run()
+{
+
+ /*
+ * The loop.
+ */
+ fIsRunning = true;
+ PINTNETRINGBUF pRingBuf = &m_pIfBuf->Recv;
+ RTThreadSetType(RTThreadSelf(), RTTHREADTYPE_IO);
+ for (;;)
+ {
+ /*
+ * Wait for a packet to become available.
+ */
+ INTNETIFWAITREQ WaitReq;
+ WaitReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
+ WaitReq.Hdr.cbReq = sizeof(WaitReq);
+ WaitReq.pSession = m_pSession;
+ WaitReq.hIf = m_hIf;
+ WaitReq.cMillies = 2000; /* 2 secs - the sleep is for some reason uninterruptible... */ /** @todo fix interruptability in SrvIntNet! */
+#if 0
+ RTReqProcess(m_hSendQueue, 0);
+ RTReqProcess(m_hUrgSendQueue, 0);
+#endif
+ int rc = SUPR3CallVMMR0Ex(NIL_RTR0PTR, NIL_VMCPUID, VMMR0_DO_INTNET_IF_WAIT, 0, &WaitReq.Hdr);
+ if (RT_FAILURE(rc))
+ {
+ if (rc == VERR_TIMEOUT || rc == VERR_INTERRUPTED)
+ {
+ natNotifyNATThread();
+ continue;
+ }
+ LogRel(("VBoxNetNAT: VMMR0_DO_INTNET_IF_WAIT returned %Rrc\n", rc));
+ return;
+ }
+
+ /*
+ * Process the receive buffer.
+ */
+ PCINTNETHDR pHdr;
+ while ((pHdr = IntNetRingGetNextFrameToRead(pRingBuf)) != NULL)
+ {
+ uint16_t const u16Type = pHdr->u16Type;
+ size_t cbFrame = pHdr->cbFrame;
+ size_t cbIgnored;
+ void *pvSlirpFrame;
+ struct mbuf *m;
+ switch (u16Type)
+ {
+ case INTNETHDR_TYPE_FRAME:
+ m = slirp_ext_m_get(g_pNAT->m_pNATState, cbFrame, &pvSlirpFrame, &cbIgnored);
+ if (!m)
+ {
+ LogRel(("NAT: Can't allocate send buffer cbFrame=%u\n", cbFrame));
+ break;
+ }
+ memcpy(pvSlirpFrame, IntNetHdrGetFramePtr(pHdr, m_pIfBuf), cbFrame);
+#if 0
+ IntNetRingSkipFrame(&m_pIfBuf->Recv);
+#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,
+ (PFNRT)SendWorker, 2, m, cbFrame);
+ natNotifyNATThread();
+ AssertReleaseRC(rc);
+ break;
+ case INTNETHDR_TYPE_GSO:
+#if 1
+ {
+ /** @todo pass these unmodified. */
+ PCPDMNETWORKGSO pGso = IntNetHdrGetGsoContext(pHdr, m_pIfBuf);
+ if (!PDMNetGsoIsValid(pGso, cbFrame, cbFrame - sizeof(*pGso)))
+ {
+ IntNetRingSkipFrame(&m_pIfBuf->Recv);
+ STAM_REL_COUNTER_INC(&m_pIfBuf->cStatBadFrames);
+ continue;
+ }
+
+ uint8_t abHdrScratch[256];
+ cbFrame -= sizeof(PDMNETWORKGSO);
+ uint32_t const cSegs = PDMNetGsoCalcSegmentCount(pGso, cbFrame);
+ for (uint32_t iSeg = 0; iSeg < cSegs; iSeg++)
+ {
+ uint32_t cbSegFrame;
+ void *pvSegFrame = PDMNetGsoCarveSegmentQD(pGso, (uint8_t *)(pGso + 1), cbFrame, abHdrScratch,
+ iSeg, cSegs, &cbSegFrame);
+ m = slirp_ext_m_get(g_pNAT->m_pNATState, cbSegFrame, &pvSlirpFrame, &cbIgnored);
+ if (!m)
+ {
+ LogRel(("NAT: Can't allocate send buffer cbSegFrame=%u seg=%u/%u\n",
+ cbSegFrame, iSeg, cSegs));
+ break;
+ }
+ memcpy(pvSlirpFrame, pvSegFrame, cbSegFrame);
+
+ rc = RTReqQueueCallEx(m_hReqQueue, NULL /*ppReq*/, 0 /*cMillies*/,
+ RTREQFLAGS_VOID | RTREQFLAGS_NO_WAIT,
+ (PFNRT)SendWorker, 2, m, cbSegFrame);
+ natNotifyNATThread();
+ AssertReleaseRC(rc);
+ }
+ }
+ break;
+#endif
+ case INTNETHDR_TYPE_PADDING:
+ break;
+ default:
+ STAM_REL_COUNTER_INC(&m_pIfBuf->cStatBadFrames);
+ break;
+ }
+
+ IntNetRingSkipFrame(&m_pIfBuf->Recv);
+ }
+ }
+ fIsRunning = false;
+}
+
+void VBoxNetNAT::usage()
+{
+}
+
+int VBoxNetNAT::parseOpt(int rc, const RTGETOPTUNION& Val)
+{
+ switch (rc)
+ {
+ case 'p':
+ {
+#define ITERATE_TO_NEXT_TERM(ch, pRule, strRaw) \
+ do { \
+ while (*ch != ',') \
+ { \
+ if (*ch == 0) \
+ { \
+ if (pRule) \
+ RTMemFree(pRule); \
+ if(strRaw) \
+ RTStrFree(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;
+ char *strRaw = RTStrDup(Val.psz);
+ char *ch = strRaw;
+ if (!strRaw)
+ {
+ RTMemFree(pRule);
+ return VERR_NO_MEMORY;
+ }
+
+ strName = RTStrStrip(ch);
+ ITERATE_TO_NEXT_TERM(ch, pRule, strRaw);
+ strProto = RTStrStrip(ch);
+ ITERATE_TO_NEXT_TERM(ch, pRule, strRaw);
+ strHostIp = RTStrStrip(ch);
+ ITERATE_TO_NEXT_TERM(ch, pRule, strRaw);
+ strHostPort = RTStrStrip(ch);
+ ITERATE_TO_NEXT_TERM(ch, pRule, strRaw);
+ strGuestIp = RTStrStrip(ch);
+ ITERATE_TO_NEXT_TERM(ch, pRule, strRaw);
+ strGuestPort = RTStrStrip(ch);
+ if (RTStrICmp(strProto, "udp") == 0)
+ pRule->fUdp = true;
+ else if (RTStrICmp(strProto, "tcp") == 0)
+ pRule->fUdp = false;
+ else
+ {
+ RTStrFree(strRaw);
+ RTMemFree(pRule);
+ return VERR_INVALID_PARAMETER;
+ }
+ if ( strHostIp == NULL
+ || inet_aton(strHostIp, &pRule->IpV4HostAddr) == 0)
+ pRule->IpV4HostAddr.s_addr = INADDR_ANY;
+ if ( strGuestIp == NULL
+ || inet_aton(strGuestIp, &pRule->IpV4GuestAddr) == 0)
+ {
+ RTMemFree(pRule);
+ RTMemFree(strRaw);
+ return VERR_INVALID_PARAMETER;
+ }
+ pRule->u16HostPort = RTStrToUInt16(strHostPort);
+ pRule->u16GuestPort = RTStrToUInt16(strGuestPort);
+ if ( !pRule->u16HostPort
+ || !pRule->u16GuestPort)
+ {
+ RTMemFree(pRule);
+ RTMemFree(strRaw);
+ return VERR_INVALID_PARAMETER;
+ }
+ pRule->pszStrRaw = strRaw;
+ m_vecPortForwardRuleFromCmdLine.push_back(pRule);
+ return VINF_SUCCESS;
+#undef ITERATE_TO_NEXT_TERM
+ }
+ default:;
+ }
+ return VERR_NOT_FOUND;
+}
+
+/**
+ * Entry point.
+ */
+extern "C" DECLEXPORT(int) TrustedMain(int argc, char **argv, char **envp)
+{
+ Log2(("NAT: main\n"));
+ g_pNAT = new VBoxNetNAT();
+ Log2(("NAT: initialization\n"));
+ int rc = g_pNAT->parseArgs(argc - 1, argv + 1);
+ if (!rc)
+ {
+ g_pNAT->init();
+ Log2(("NAT: parsing command line\n"));
+ Log2(("NAT: try go online\n"));
+ g_pNAT->tryGoOnline();
+ Log2(("NAT: main loop\n"));
+ g_pNAT->run();
+ }
+ delete g_pNAT;
+ return 0;
+}
+
+
+/** slirp's hooks */
+extern "C" int slirp_can_output(void * pvUser)
+{
+ return 1;
+}
+
+extern "C" void slirp_urg_output(void *pvUser, struct mbuf *m, const uint8_t *pu8Buf, int cb)
+{
+ LogFlowFunc(("ENTER: m:%p, pu8Buf:%p, cb:%d\n", m, pu8Buf, cb));
+ int rc = RTReqQueueCallEx(g_pNAT->m_hUrgSendQueue, NULL /*ppReq*/, 0 /*cMillies*/, RTREQFLAGS_VOID | RTREQFLAGS_NO_WAIT,
+ (PFNRT)IntNetSendWorker, 4, (uintptr_t)1, (uintptr_t)pu8Buf, (uintptr_t)cb, (uintptr_t)m);
+ ASMAtomicIncU32(&g_pNAT->cUrgPkt);
+ RTSemEventSignal(g_pNAT->m_EventUrgSend);
+ AssertReleaseRC(rc);
+ LogFlowFuncLeave();
+}
+extern "C" void slirp_output(void *pvUser, struct mbuf *m, const uint8_t *pu8Buf, int cb)
+{
+ LogFlowFunc(("ENTER: m:%p, pu8Buf:%p, cb:%d\n", m, pu8Buf, cb));
+ AssertRelease(g_pNAT == pvUser);
+ int rc = RTReqQueueCallEx(g_pNAT->m_hSendQueue, NULL /*ppReq*/, 0 /*cMillies*/, RTREQFLAGS_VOID | RTREQFLAGS_NO_WAIT,
+ (PFNRT)IntNetSendWorker, 4, (uintptr_t)0, (uintptr_t)pu8Buf, (uintptr_t)cb, (uintptr_t)m);
+ ASMAtomicIncU32(&g_pNAT->cPkt);
+ RTSemEventSignal(g_pNAT->m_EventSend);
+ AssertReleaseRC(rc);
+ LogFlowFuncLeave();
+}
+
+extern "C" void slirp_output_pending(void *pvUser)
+{
+ AssertMsgFailed(("Unimplemented"));
+}
+
+/**
+ * Worker function for drvNATSend().
+ * @thread "NAT" thread.
+ */
+static void SendWorker(struct mbuf *m, size_t cb)
+{
+ LogFlowFunc(("ENTER: m:%p ,cb:%d\n", m, cb));
+ slirp_input(g_pNAT->m_pNATState, m, cb);
+ LogFlowFuncLeave();
+}
+
+static void IntNetSendWorker(bool fUrg, void *pvFrame, size_t cbFrame, struct mbuf *m)
+{
+ VBoxNetNAT *pThis = g_pNAT;
+ INTNETIFSENDREQ SendReq;
+ int rc;
+
+ LogFlowFunc(("ENTER: urg:%RTbool ,pvFrame:%p, cbFrame:%d, m:%p\n", fUrg, pvFrame, cbFrame, m));
+ if (!fUrg)
+ {
+ /* non-urgent datagramm sender */
+ while ( ASMAtomicReadU32(&g_pNAT->cUrgPkt) != 0
+ || ASMAtomicReadU32(&g_pNAT->cPkt) == 0)
+ rc = RTSemEventWait(g_pNAT->m_EventSend, RT_INDEFINITE_WAIT);
+ }
+ else
+ {
+ while (ASMAtomicReadU32(&g_pNAT->cUrgPkt) == 0)
+ rc = RTSemEventWait(g_pNAT->m_EventUrgSend, RT_INDEFINITE_WAIT);
+ }
+ rc = IntNetRingWriteFrame(&pThis->m_pIfBuf->Send, pvFrame, cbFrame);
+ if (RT_FAILURE(rc))
+ {
+ SendReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
+ SendReq.Hdr.cbReq = sizeof(SendReq);
+ SendReq.pSession = pThis->m_pSession;
+ SendReq.hIf = pThis->m_hIf;
+ rc = SUPR3CallVMMR0Ex(NIL_RTR0PTR, NIL_VMCPUID, VMMR0_DO_INTNET_IF_SEND, 0, &SendReq.Hdr);
+
+ rc = IntNetRingWriteFrame(&pThis->m_pIfBuf->Send, pvFrame, cbFrame);
+
+ }
+ if (RT_SUCCESS(rc))
+ {
+ SendReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
+ SendReq.Hdr.cbReq = sizeof(SendReq);
+ SendReq.pSession = pThis->m_pSession;
+ SendReq.hIf = pThis->m_hIf;
+ rc = SUPR3CallVMMR0Ex(NIL_RTR0PTR, NIL_VMCPUID, VMMR0_DO_INTNET_IF_SEND, 0, &SendReq.Hdr);
+ }
+ AssertRC((rc));
+ if (RT_FAILURE(rc))
+ Log2(("VBoxNetNAT: Failed to send packet; rc=%Rrc\n", rc));
+
+ if (!fUrg)
+ {
+ ASMAtomicDecU32(&g_pNAT->cPkt);
+ }
+ else {
+ if (ASMAtomicDecU32(&g_pNAT->cUrgPkt) == 0)
+ RTSemEventSignal(g_pNAT->m_EventSend);
+ }
+ slirp_ext_m_free(pThis->m_pNATState, m, (uint8_t *)pvFrame);
+ natNotifyNATThread();
+ LogFlowFuncLeave();
+}
+
+static DECLCALLBACK(int) AsyncIoThread(RTTHREAD pThread, void *pvUser)
+{
+ VBoxNetNAT *pThis = (VBoxNetNAT *)pvUser;
+ int nFDs = -1;
+#ifdef RT_OS_WINDOWS
+ HANDLE *pahEvents = slirp_get_events(pThis->m_pNATState);
+#else /* RT_OS_WINDOWS */
+ unsigned int cPollNegRet = 0;
+#endif /* !RT_OS_WINDOWS */
+
+ LogFlow(("drvNATAsyncIoThread: pThis=%p\n", pThis));
+
+ /*
+ * Polling loop.
+ */
+ for(;;)
+ {
+ /*
+ * To prevent concurrent execution of sending/receiving threads
+ */
+#ifndef RT_OS_WINDOWS
+ nFDs = slirp_get_nsock(pThis->m_pNATState);
+ /* allocation for all sockets + Management pipe */
+ struct pollfd *polls = (struct pollfd *)RTMemAlloc((1 + nFDs) * sizeof(struct pollfd) + sizeof(uint32_t));
+ if (polls == NULL)
+ return VERR_NO_MEMORY;
+
+ /* don't pass the management pipe */
+ slirp_select_fill(pThis->m_pNATState, &nFDs, &polls[1]);
+ unsigned int cMsTimeout = slirp_get_timeout_ms(pThis->m_pNATState);
+
+ polls[0].fd = RTPipeToNative(pThis->m_hPipeRead);
+ /* POLLRDBAND usually doesn't used on Linux but seems used on Solaris */
+ polls[0].events = POLLRDNORM|POLLPRI|POLLRDBAND;
+ polls[0].revents = 0;
+
+ int cChangedFDs = poll(polls, nFDs + 1, cMsTimeout);
+ if (cChangedFDs < 0)
+ {
+ if (errno == EINTR)
+ {
+ 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)
+ {
+ LogRel(("NAT:Poll returns (%s) suppressed %d\n", strerror(errno), cPollNegRet));
+ cPollNegRet = 0;
+ }
+ }
+
+ if (cChangedFDs >= 0)
+ {
+ slirp_select_poll(pThis->m_pNATState, &polls[1], nFDs);
+ if (polls[0].revents & (POLLRDNORM|POLLPRI|POLLRDBAND))
+ {
+ /* 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;
+ size_t cbRead;
+ RTPipeRead(pThis->m_hPipeRead, &ch, 1, &cbRead);
+ }
+ }
+ /* process _all_ outstanding requests but don't wait */
+ RTReqQueueProcess(pThis->m_hReqQueue, 0);
+ RTMemFree(polls);
+
+#else /* RT_OS_WINDOWS */
+ nFDs = -1;
+ slirp_select_fill(pThis->m_pNATState, &nFDs);
+ DWORD dwEvent = WSAWaitForMultipleEvents(nFDs, pahEvents, FALSE,
+ slirp_get_timeout_ms(pThis->m_pNATState),
+ FALSE);
+ if ( (dwEvent < WSA_WAIT_EVENT_0 || dwEvent > WSA_WAIT_EVENT_0 + nFDs - 1)
+ && dwEvent != WSA_WAIT_TIMEOUT)
+ {
+ int error = WSAGetLastError();
+ LogRel(("NAT: WSAWaitForMultipleEvents returned %d (error %d)\n", dwEvent, error));
+ RTAssertReleasePanic();
+ }
+
+ if (dwEvent == WSA_WAIT_TIMEOUT)
+ {
+ /* only check for slow/fast timers */
+ slirp_select_poll(pThis->m_pNATState, /* fTimeout=*/true, /*fIcmp=*/false);
+ 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 */
+ RTReqQueueProcess(pThis->m_hReqQueue, 0);
+#endif /* RT_OS_WINDOWS */
+ }
+
+ return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(int) natSndThread(RTTHREAD pThread, void *pvUser)
+{
+ while (g_pNAT->fIsRunning)
+ RTReqQueueProcess(g_pNAT->m_hSendQueue, 0);
+ return VINF_SUCCESS;
+}
+static DECLCALLBACK(int) natUrgSndThread(RTTHREAD pThread, void *pvUser)
+{
+ while (g_pNAT->fIsRunning)
+ RTReqQueueProcess(g_pNAT->m_hUrgSendQueue, 0);
+ return VINF_SUCCESS;
+}
+
+#ifndef VBOX_WITH_HARDENING
+
+int main(int argc, char **argv, char **envp)
+{
+ int rc = RTR3InitExe(argc, &argv, RTR3INIT_FLAGS_SUPLIB);
+ if (RT_FAILURE(rc))
+ return RTMsgInitFailure(rc);
+
+ return TrustedMain(argc, argv, envp);
+}
+
+# if defined(RT_OS_WINDOWS)
+
+static LRESULT CALLBACK WindowProc(HWND hwnd,
+ UINT uMsg,
+ WPARAM wParam,
+ LPARAM lParam
+)
+{
+ if(uMsg == WM_DESTROY)
+ {
+ PostQuitMessage(0);
+ return 0;
+ }
+ return DefWindowProc (hwnd, uMsg, wParam, lParam);
+}
+
+static LPCSTR g_WndClassName = "VBoxNetNatClass";
+
+static DWORD WINAPI MsgThreadProc(__in LPVOID lpParameter)
+{
+ HWND hwnd = 0;
+ HINSTANCE hInstance = (HINSTANCE)GetModuleHandle (NULL);
+ bool bExit = false;
+
+ /* Register the Window Class. */
+ WNDCLASS wc;
+ wc.style = 0;
+ wc.lpfnWndProc = WindowProc;
+ wc.cbClsExtra = 0;
+ wc.cbWndExtra = sizeof(void *);
+ wc.hInstance = hInstance;
+ wc.hIcon = NULL;
+ wc.hCursor = NULL;
+ wc.hbrBackground = (HBRUSH)(COLOR_BACKGROUND + 1);
+ wc.lpszMenuName = NULL;
+ wc.lpszClassName = g_WndClassName;
+
+ ATOM atomWindowClass = RegisterClass(&wc);
+
+ if (atomWindowClass != 0)
+ {
+ /* Create the window. */
+ hwnd = CreateWindowEx (WS_EX_TOOLWINDOW | WS_EX_TRANSPARENT | WS_EX_TOPMOST,
+ g_WndClassName, g_WndClassName,
+ WS_POPUPWINDOW,
+ -200, -200, 100, 100, NULL, NULL, hInstance, NULL);
+
+ if (hwnd)
+ {
+ SetWindowPos(hwnd, HWND_TOPMOST, -200, -200, 0, 0,
+ SWP_NOACTIVATE | SWP_HIDEWINDOW | SWP_NOCOPYBITS | SWP_NOREDRAW | SWP_NOSIZE);
+
+ MSG msg;
+ while (GetMessage(&msg, NULL, 0, 0))
+ {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+
+ DestroyWindow (hwnd);
+
+ bExit = true;
+ }
+
+ UnregisterClass (g_WndClassName, hInstance);
+ }
+
+ 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.) */
+int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
+{
+#if 0
+ NOREF(hInstance); NOREF(hPrevInstance); NOREF(lpCmdLine); NOREF(nCmdShow);
+
+ HANDLE hThread = CreateThread(
+ 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*/
+ );
+
+ if(hThread != NULL)
+ CloseHandle(hThread);
+
+#endif
+ return main(__argc, __argv, environ);
+}
+# endif /* RT_OS_WINDOWS */
+
+#endif /* !VBOX_WITH_HARDENING */
+
diff --git a/src/VBox/NetworkServices/NAT/VBoxNetNATHardened.cpp b/src/VBox/NetworkServices/NAT/VBoxNetNATHardened.cpp
new file mode 100644
index 00000000..b0d74e73
--- /dev/null
+++ b/src/VBox/NetworkServices/NAT/VBoxNetNATHardened.cpp
@@ -0,0 +1,24 @@
+/* $Id: VBoxNetNATHardened.cpp $ */
+/** @file
+ * VBoxNetNAT - Hardened main().
+ */
+
+/*
+ * 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;
+ * 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.
+ */
+
+#include <VBox/sup.h>
+
+
+int main(int argc, char **argv, char **envp)
+{
+ return SUPR3HardenedMain("VBoxNetNAT", 0 /* fFlags */, argc, argv, envp);
+}
diff --git a/src/VBox/NetworkServices/NetLib/Makefile.kup b/src/VBox/NetworkServices/NetLib/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/NetworkServices/NetLib/Makefile.kup
diff --git a/src/VBox/NetworkServices/NetLib/VBoxNetARP.cpp b/src/VBox/NetworkServices/NetLib/VBoxNetARP.cpp
new file mode 100644
index 00000000..ec5ed275
--- /dev/null
+++ b/src/VBox/NetworkServices/NetLib/VBoxNetARP.cpp
@@ -0,0 +1,154 @@
+/* $Id: VBoxNetARP.cpp $ */
+/** @file
+ * VBoxNetARP - IntNet ARP Client Routines.
+ */
+
+/*
+ * 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;
+ * 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.
+ */
+
+/*******************************************************************************
+* Header Files *
+*******************************************************************************/
+#define LOG_GROUP LOG_GROUP_DEFAULT
+#include "VBoxNetLib.h"
+#include <iprt/string.h>
+#include <VBox/intnetinline.h>
+#include <VBox/log.h>
+
+
+/**
+ * Deal with ARP queries.
+ *
+ * @returns true if ARP.
+ *
+ * @param pSession The support driver session.
+ * @param hIf The internal network interface handle.
+ * @param pBuf The internal network interface buffer.
+ * @param pMacAddr Our MAC address.
+ * @param IPv4Addr Our IPv4 address.
+ */
+bool VBoxNetArpHandleIt(PSUPDRVSESSION pSession, INTNETIFHANDLE hIf, PINTNETBUF pBuf, PCRTMAC pMacAddr, RTNETADDRIPV4 IPv4Addr)
+{
+ /*
+ * Valid IntNet Ethernet frame? Skip GSO, no ARP in there.
+ */
+ PCINTNETHDR pHdr = IntNetRingGetNextFrameToRead(&pBuf->Recv);
+ if ( !pHdr
+ || pHdr->u16Type != INTNETHDR_TYPE_FRAME)
+ return false;
+
+ size_t cbFrame = pHdr->cbFrame;
+ const void *pvFrame = IntNetHdrGetFramePtr(pHdr, pBuf);
+ PCRTNETETHERHDR pEthHdr = (PCRTNETETHERHDR)pvFrame;
+
+ /*
+ * Arp frame?
+ */
+ if (pEthHdr->EtherType != RT_H2N_U16_C(RTNET_ETHERTYPE_ARP))
+ return false;
+ if ( ( pEthHdr->DstMac.au16[0] != 0xffff
+ || pEthHdr->DstMac.au16[1] != 0xffff
+ || pEthHdr->DstMac.au16[2] != 0xffff)
+ && ( pEthHdr->DstMac.au16[0] != pMacAddr->au16[0]
+ || pEthHdr->DstMac.au16[1] != pMacAddr->au16[1]
+ || pEthHdr->DstMac.au16[2] != pMacAddr->au16[2])
+ )
+ return false;
+ if (cbFrame < sizeof(RTNETARPIPV4) + sizeof(RTNETETHERHDR))
+ return false;
+
+ PCRTNETARPHDR pArpHdr = (PCRTNETARPHDR)(pEthHdr + 1);
+ if (pArpHdr->ar_htype != RT_H2N_U16_C(RTNET_ARP_ETHER))
+ return false;
+ if (pArpHdr->ar_hlen != sizeof(RTMAC))
+ return false;
+ if (pArpHdr->ar_ptype != RT_H2N_U16_C(RTNET_ETHERTYPE_IPV4))
+ return false;
+ if (pArpHdr->ar_plen != sizeof(RTNETADDRIPV4))
+ return false;
+
+ /* It's ARP, alright. Anything we need to do something about. */
+ PCRTNETARPIPV4 pArp = (PCRTNETARPIPV4)pArpHdr;
+ switch (pArp->Hdr.ar_oper)
+ {
+ case RT_H2N_U16_C(RTNET_ARPOP_REQUEST):
+ case RT_H2N_U16_C(RTNET_ARPOP_REVREQUEST):
+ case RT_H2N_U16_C(RTNET_ARPOP_INVREQUEST):
+ break;
+ default:
+ return true;
+ }
+
+ /*
+ * Deal with the queries.
+ */
+ RTNETARPIPV4 Reply;
+ switch (pArp->Hdr.ar_oper)
+ {
+ /* 'Who has ar_tpa? Tell ar_spa.' */
+ case RT_H2N_U16_C(RTNET_ARPOP_REQUEST):
+ if (pArp->ar_tpa.u != IPv4Addr.u)
+ return true;
+ Reply.Hdr.ar_oper = RT_H2N_U16_C(RTNET_ARPOP_REPLY);
+ break;
+
+ case RT_H2N_U16_C(RTNET_ARPOP_REVREQUEST):
+ if ( pArp->ar_tha.au16[0] != pMacAddr->au16[0]
+ || pArp->ar_tha.au16[1] != pMacAddr->au16[1]
+ || pArp->ar_tha.au16[2] != pMacAddr->au16[2])
+ return true;
+ Reply.Hdr.ar_oper = RT_H2N_U16_C(RTNET_ARPOP_REVREPLY);
+ break;
+
+ case RT_H2N_U16_C(RTNET_ARPOP_INVREQUEST):
+ /** @todo RTNET_ARPOP_INVREQUEST */
+ return true;
+ //Reply.Hdr.ar_oper = RT_H2N_U16_C(RTNET_ARPOP_INVREPLY);
+ //break;
+ }
+
+ /*
+ * Complete the reply and send it.
+ */
+ Reply.Hdr.ar_htype = RT_H2N_U16_C(RTNET_ARP_ETHER);
+ Reply.Hdr.ar_ptype = RT_H2N_U16_C(RTNET_ETHERTYPE_IPV4);
+ Reply.Hdr.ar_hlen = sizeof(RTMAC);
+ Reply.Hdr.ar_plen = sizeof(RTNETADDRIPV4);
+ Reply.ar_sha = *pMacAddr;
+ Reply.ar_spa = IPv4Addr;
+ Reply.ar_tha = pArp->ar_sha;
+ Reply.ar_tpa = pArp->ar_spa;
+
+
+ RTNETETHERHDR EthHdr;
+ EthHdr.DstMac = pArp->ar_sha;
+ EthHdr.SrcMac = *pMacAddr;
+ EthHdr.EtherType = RT_H2N_U16_C(RTNET_ETHERTYPE_ARP);
+
+ uint8_t abTrailer[60 - sizeof(Reply) - sizeof(EthHdr)];
+ memset(abTrailer, '\0', sizeof(abTrailer));
+
+ INTNETSEG aSegs[3];
+ aSegs[0].cb = sizeof(EthHdr);
+ aSegs[0].pv = &EthHdr;
+
+ aSegs[1].pv = &Reply;
+ aSegs[1].cb = sizeof(Reply);
+
+ aSegs[2].pv = &abTrailer[0];
+ aSegs[2].cb = sizeof(abTrailer);
+
+ VBoxNetIntIfSend(pSession, hIf, pBuf, RT_ELEMENTS(aSegs), &aSegs[0], true /* fFlush */);
+
+ return true;
+}
+
diff --git a/src/VBox/NetworkServices/NetLib/VBoxNetBaseService.cpp b/src/VBox/NetworkServices/NetLib/VBoxNetBaseService.cpp
new file mode 100644
index 00000000..435a1cb8
--- /dev/null
+++ b/src/VBox/NetworkServices/NetLib/VBoxNetBaseService.cpp
@@ -0,0 +1,363 @@
+/* $Id: VBoxNetBaseService.cpp $ */
+/** @file
+ * VBoxNetDHCP - DHCP Service for connecting to IntNet.
+ */
+/** @todo r=bird: Cut&Past rules... Please fix DHCP refs! */
+
+/*
+ * Copyright (C) 2009-2011 Oracle Corporation
+ *
+ * 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.
+ */
+
+/*******************************************************************************
+* Header Files *
+*******************************************************************************/
+#define LOG_GROUP LOG_GROUP_NET_SERVICE
+
+#include <iprt/alloca.h>
+#include <iprt/buildconfig.h>
+#include <iprt/err.h>
+#include <iprt/net.h> /* must come before getopt.h. */
+#include <iprt/getopt.h>
+#include <iprt/initterm.h>
+#include <iprt/param.h>
+#include <iprt/path.h>
+#include <iprt/stream.h>
+#include <iprt/string.h>
+#include <iprt/time.h>
+#include <iprt/mem.h>
+
+#include <VBox/sup.h>
+#include <VBox/intnet.h>
+#include <VBox/vmm/vmm.h>
+#include <VBox/version.h>
+
+#include <vector>
+#include <string>
+
+#include <VBox/log.h>
+
+#include "VBoxNetLib.h"
+#include "VBoxNetBaseService.h"
+
+#ifdef RT_OS_WINDOWS /* WinMain */
+# include <Windows.h>
+# include <stdlib.h>
+#endif
+
+
+/*******************************************************************************
+* Structures and Typedefs *
+*******************************************************************************/
+static RTGETOPTDEF g_aGetOptDef[] =
+{
+ { "--name", 'N', RTGETOPT_REQ_STRING },
+ { "--network", 'n', RTGETOPT_REQ_STRING },
+ { "--trunk-name", 't', RTGETOPT_REQ_STRING },
+ { "--trunk-type", 'T', RTGETOPT_REQ_STRING },
+ { "--mac-address", 'a', RTGETOPT_REQ_MACADDR },
+ { "--ip-address", 'i', RTGETOPT_REQ_IPV4ADDR },
+ { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
+};
+VBoxNetBaseService::VBoxNetBaseService()
+{
+}
+VBoxNetBaseService::~VBoxNetBaseService()
+{
+ /*
+ * Close the interface connection.
+ */
+ if (m_hIf != INTNET_HANDLE_INVALID)
+ {
+ INTNETIFCLOSEREQ CloseReq;
+ CloseReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
+ CloseReq.Hdr.cbReq = sizeof(CloseReq);
+ CloseReq.pSession = m_pSession;
+ CloseReq.hIf = m_hIf;
+ m_hIf = INTNET_HANDLE_INVALID;
+ int rc = SUPR3CallVMMR0Ex(NIL_RTR0PTR, NIL_RTCPUID, VMMR0_DO_INTNET_IF_CLOSE, 0, &CloseReq.Hdr);
+ AssertRC(rc);
+ }
+
+ if (m_pSession)
+ {
+ SUPR3Term(false /*fForced*/);
+ m_pSession = NIL_RTR0PTR;
+ }
+}
+
+int VBoxNetBaseService::init()
+{
+ /* numbers from DrvIntNet */
+ m_cbSendBuf = 36 * _1K;
+ m_cbRecvBuf = 218 * _1K;
+ m_hIf = INTNET_HANDLE_INVALID;
+ m_pIfBuf = NULL;
+
+ m_cVerbosity = 0;
+ m_Name = "VBoxNetNAT";
+ m_Network = "intnet";
+ for(unsigned int i = 0; i < RT_ELEMENTS(g_aGetOptDef); ++i)
+ m_vecOptionDefs.push_back(&g_aGetOptDef[i]);
+ return VINF_SUCCESS;
+}
+/**
+ * Parse the arguments.
+ *
+ * @returns 0 on success, fully bitched exit code on failure.
+ *
+ * @param argc Argument count.
+ * @param argv Argument vector.
+ */
+int VBoxNetBaseService::parseArgs(int argc, char **argv)
+{
+
+ RTGETOPTSTATE State;
+ PRTGETOPTDEF paOptionArray = getOptionsPtr();
+ int rc = RTGetOptInit(&State, argc, argv, paOptionArray, m_vecOptionDefs.size(), 0, 0 /*fFlags*/);
+ AssertRCReturn(rc, 49);
+ Log2(("BaseService: parseArgs enter\n"));
+
+ for (;;)
+ {
+ RTGETOPTUNION Val;
+ rc = RTGetOpt(&State, &Val);
+ if (!rc)
+ break;
+ switch (rc)
+ {
+ case 'N':
+ m_Name = Val.psz;
+ break;
+ case 'n':
+ m_Network = Val.psz;
+ break;
+ case 't':
+ m_TrunkName = Val.psz;
+ break;
+ case 'T':
+ if (!strcmp(Val.psz, "none"))
+ m_enmTrunkType = kIntNetTrunkType_None;
+ else if (!strcmp(Val.psz, "whatever"))
+ m_enmTrunkType = kIntNetTrunkType_WhateverNone;
+ else if (!strcmp(Val.psz, "netflt"))
+ m_enmTrunkType = kIntNetTrunkType_NetFlt;
+ else if (!strcmp(Val.psz, "netadp"))
+ m_enmTrunkType = kIntNetTrunkType_NetAdp;
+ else if (!strcmp(Val.psz, "srvnat"))
+ m_enmTrunkType = kIntNetTrunkType_SrvNat;
+ else
+ {
+ RTStrmPrintf(g_pStdErr, "Invalid trunk type '%s'\n", Val.psz);
+ return 1;
+ }
+ break;
+ case 'a':
+ m_MacAddress = Val.MacAddr;
+ break;
+ case 'i':
+ m_Ipv4Address = Val.IPv4Addr;
+ break;
+
+ case 'v':
+ m_cVerbosity++;
+ break;
+
+ case 'V':
+ RTPrintf("%sr%u\n", RTBldCfgVersion(), RTBldCfgRevision());
+ return 1;
+
+ case 'h':
+ RTPrintf("VBoxNetDHCP Version %s\n"
+ "(C) 2009-" VBOX_C_YEAR " " VBOX_VENDOR "\n"
+ "All rights reserved.\n"
+ "\n"
+ "Usage: VBoxNetDHCP <options>\n"
+ "\n"
+ "Options:\n",
+ RTBldCfgVersion());
+ for (unsigned int i = 0; i < m_vecOptionDefs.size(); i++)
+ RTPrintf(" -%c, %s\n", m_vecOptionDefs[i]->iShort, m_vecOptionDefs[i]->pszLong);
+ usage(); /* to print Service Specific usage */
+ return 1;
+
+ default:
+ int rc1 = parseOpt(rc, Val);
+ if (RT_FAILURE(rc1))
+ {
+ rc = RTGetOptPrintError(rc, &Val);
+ RTPrintf("Use --help for more information.\n");
+ return rc;
+ }
+ }
+ }
+
+ RTMemFree(paOptionArray);
+ return rc;
+}
+
+int VBoxNetBaseService::tryGoOnline(void)
+{
+ /*
+ * Open the session, load ring-0 and issue the request.
+ */
+ int rc = SUPR3Init(&m_pSession);
+ if (RT_FAILURE(rc))
+ {
+ m_pSession = NIL_RTR0PTR;
+ LogRel(("VBoxNetBaseService: SUPR3Init -> %Rrc\n", rc));
+ return 1;
+ }
+
+ char szPath[RTPATH_MAX];
+ rc = RTPathExecDir(szPath, sizeof(szPath) - sizeof("/VMMR0.r0"));
+ if (RT_FAILURE(rc))
+ {
+ LogRel(("VBoxNetBaseService: RTPathExecDir -> %Rrc\n", rc));
+ return 1;
+ }
+
+ rc = SUPR3LoadVMM(strcat(szPath, "/VMMR0.r0"));
+ if (RT_FAILURE(rc))
+ {
+ LogRel(("VBoxNetBaseService: SUPR3LoadVMM(\"%s\") -> %Rrc\n", szPath, rc));
+ return 1;
+ }
+
+ /*
+ * Create the open request.
+ */
+ PINTNETBUF pBuf;
+ INTNETOPENREQ OpenReq;
+ OpenReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
+ OpenReq.Hdr.cbReq = sizeof(OpenReq);
+ OpenReq.pSession = m_pSession;
+ strncpy(OpenReq.szNetwork, m_Network.c_str(), sizeof(OpenReq.szNetwork));
+ OpenReq.szNetwork[sizeof(OpenReq.szNetwork) - 1] = '\0';
+ strncpy(OpenReq.szTrunk, m_TrunkName.c_str(), sizeof(OpenReq.szTrunk));
+ OpenReq.szTrunk[sizeof(OpenReq.szTrunk) - 1] = '\0';
+ OpenReq.enmTrunkType = m_enmTrunkType;
+ OpenReq.fFlags = 0; /** @todo check this */
+ OpenReq.cbSend = m_cbSendBuf;
+ OpenReq.cbRecv = m_cbRecvBuf;
+ OpenReq.hIf = INTNET_HANDLE_INVALID;
+
+ /*
+ * Issue the request.
+ */
+ Log2(("attempting to open/create network \"%s\"...\n", OpenReq.szNetwork));
+ rc = SUPR3CallVMMR0Ex(NIL_RTR0PTR, NIL_VMCPUID, VMMR0_DO_INTNET_OPEN, 0, &OpenReq.Hdr);
+ if (RT_FAILURE(rc))
+ {
+ Log2(("VBoxNetBaseService: SUPR3CallVMMR0Ex(,VMMR0_DO_INTNET_OPEN,) failed, rc=%Rrc\n", rc));
+ goto bad;
+ }
+ m_hIf = OpenReq.hIf;
+ Log2(("successfully opened/created \"%s\" - hIf=%#x\n", OpenReq.szNetwork, m_hIf));
+
+ /*
+ * Get the ring-3 address of the shared interface buffer.
+ */
+ INTNETIFGETBUFFERPTRSREQ GetBufferPtrsReq;
+ GetBufferPtrsReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
+ GetBufferPtrsReq.Hdr.cbReq = sizeof(GetBufferPtrsReq);
+ GetBufferPtrsReq.pSession = m_pSession;
+ GetBufferPtrsReq.hIf = m_hIf;
+ GetBufferPtrsReq.pRing3Buf = NULL;
+ GetBufferPtrsReq.pRing0Buf = NIL_RTR0PTR;
+ rc = SUPR3CallVMMR0Ex(NIL_RTR0PTR, NIL_VMCPUID, VMMR0_DO_INTNET_IF_GET_BUFFER_PTRS, 0, &GetBufferPtrsReq.Hdr);
+ if (RT_FAILURE(rc))
+ {
+ Log2(("VBoxNetBaseService: SUPR3CallVMMR0Ex(,VMMR0_DO_INTNET_IF_GET_BUFFER_PTRS,) failed, rc=%Rrc\n", rc));
+ goto bad;
+ }
+ pBuf = GetBufferPtrsReq.pRing3Buf;
+ Log2(("pBuf=%p cbBuf=%d cbSend=%d cbRecv=%d\n",
+ pBuf, pBuf->cbBuf, pBuf->cbSend, pBuf->cbRecv));
+ m_pIfBuf = pBuf;
+
+ /*
+ * Activate the interface.
+ */
+ INTNETIFSETACTIVEREQ ActiveReq;
+ ActiveReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
+ ActiveReq.Hdr.cbReq = sizeof(ActiveReq);
+ ActiveReq.pSession = m_pSession;
+ ActiveReq.hIf = m_hIf;
+ ActiveReq.fActive = true;
+ rc = SUPR3CallVMMR0Ex(NIL_RTR0PTR, NIL_VMCPUID, VMMR0_DO_INTNET_IF_SET_ACTIVE, 0, &ActiveReq.Hdr);
+ if (RT_SUCCESS(rc))
+ return 0;
+
+ /* bail out */
+ Log2(("VBoxNetBaseService: SUPR3CallVMMR0Ex(,VMMR0_DO_INTNET_IF_SET_PROMISCUOUS_MODE,) failed, rc=%Rrc\n", rc));
+
+ return 0;
+ bad:
+ return 1;
+}
+
+void VBoxNetBaseService::shutdown(void)
+{
+}
+
+/**
+ * Print debug message depending on the m_cVerbosity level.
+ *
+ * @param iMinLevel The minimum m_cVerbosity level for this message.
+ * @param fMsg Whether to dump parts for the current DHCP message.
+ * @param pszFmt The message format string.
+ * @param ... Optional arguments.
+ */
+inline void VBoxNetBaseService::debugPrint(int32_t iMinLevel, bool fMsg, const char *pszFmt, ...) const
+{
+ if (iMinLevel <= m_cVerbosity)
+ {
+ va_list va;
+ va_start(va, pszFmt);
+ debugPrintV(iMinLevel, fMsg, pszFmt, va);
+ va_end(va);
+ }
+}
+
+
+/**
+ * Print debug message depending on the m_cVerbosity level.
+ *
+ * @param iMinLevel The minimum m_cVerbosity level for this message.
+ * @param fMsg Whether to dump parts for the current DHCP message.
+ * @param pszFmt The message format string.
+ * @param va Optional arguments.
+ */
+void VBoxNetBaseService::debugPrintV(int iMinLevel, bool fMsg, const char *pszFmt, va_list va) const
+{
+ if (iMinLevel <= m_cVerbosity)
+ {
+ va_list vaCopy; /* This dude is *very* special, thus the copy. */
+ va_copy(vaCopy, va);
+ RTStrmPrintf(g_pStdErr, "VBoxNetDHCP: %s: %N\n", iMinLevel >= 2 ? "debug" : "info", pszFmt, &vaCopy);
+ va_end(vaCopy);
+ }
+
+}
+
+PRTGETOPTDEF VBoxNetBaseService::getOptionsPtr()
+{
+ PRTGETOPTDEF pOptArray = NULL;
+ pOptArray = (PRTGETOPTDEF)RTMemAlloc(sizeof(RTGETOPTDEF) * m_vecOptionDefs.size());
+ if (!pOptArray)
+ return NULL;
+ for (unsigned int i = 0; i < m_vecOptionDefs.size(); ++i)
+ {
+ PRTGETOPTDEF pOpt = m_vecOptionDefs[i];
+ memcpy(&pOptArray[i], m_vecOptionDefs[i], sizeof(RTGETOPTDEF));
+ }
+ return pOptArray;
+}
diff --git a/src/VBox/NetworkServices/NetLib/VBoxNetBaseService.h b/src/VBox/NetworkServices/NetLib/VBoxNetBaseService.h
new file mode 100644
index 00000000..eab9a515
--- /dev/null
+++ b/src/VBox/NetworkServices/NetLib/VBoxNetBaseService.h
@@ -0,0 +1,61 @@
+/* $Id: VBoxNetBaseService.h $ */
+/** @file
+ * VBoxNetUDP - IntNet Client Library.
+ */
+
+/*
+ * 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;
+ * 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.
+ */
+
+#ifndef ___VBoxNetBaseService_h___
+#define ___VBoxNetBaseService_h___
+class VBoxNetBaseService
+{
+public:
+ VBoxNetBaseService();
+ virtual ~VBoxNetBaseService();
+ int parseArgs(int argc, char **argv);
+ int tryGoOnline(void);
+ void shutdown(void);
+ virtual void usage(void) = 0;
+ virtual void run(void) = 0;
+ virtual int init(void);
+ virtual int parseOpt(int rc, const RTGETOPTUNION& getOptVal) = 0;
+
+ inline void debugPrint( int32_t iMinLevel, bool fMsg, const char *pszFmt, ...) const;
+ void debugPrintV(int32_t iMinLevel, bool fMsg, const char *pszFmt, va_list va) const;
+public:
+ /** @name The server configuration data members.
+ * @{ */
+ std::string m_Name;
+ std::string m_Network;
+ std::string m_TrunkName;
+ INTNETTRUNKTYPE m_enmTrunkType;
+ RTMAC m_MacAddress;
+ RTNETADDRIPV4 m_Ipv4Address;
+ /** @} */
+ /** @name The network interface
+ * @{ */
+ PSUPDRVSESSION m_pSession;
+ uint32_t m_cbSendBuf;
+ uint32_t m_cbRecvBuf;
+ INTNETIFHANDLE m_hIf; /**< The handle to the network interface. */
+ PINTNETBUF m_pIfBuf; /**< Interface buffer. */
+ std::vector<PRTGETOPTDEF> m_vecOptionDefs;
+ /** @} */
+ /** @name Debug stuff
+ * @{ */
+ int32_t m_cVerbosity;
+private:
+ PRTGETOPTDEF getOptionsPtr();
+ /** @} */
+};
+#endif
diff --git a/src/VBox/NetworkServices/NetLib/VBoxNetIntIf.cpp b/src/VBox/NetworkServices/NetLib/VBoxNetIntIf.cpp
new file mode 100644
index 00000000..74a3998a
--- /dev/null
+++ b/src/VBox/NetworkServices/NetLib/VBoxNetIntIf.cpp
@@ -0,0 +1,137 @@
+/* $Id: VBoxNetIntIf.cpp $ */
+/** @file
+ * VBoxNetIntIf - IntNet Interface Client Routines.
+ */
+
+/*
+ * 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;
+ * 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.
+ */
+
+/*******************************************************************************
+* Header Files *
+*******************************************************************************/
+#define LOG_GROUP LOG_GROUP_DEFAULT
+#include "VBoxNetLib.h"
+#include <VBox/intnet.h>
+#include <VBox/intnetinline.h>
+#include <VBox/sup.h>
+#include <VBox/vmm/vmm.h>
+#include <VBox/err.h>
+#include <VBox/log.h>
+
+#include <iprt/string.h>
+
+
+
+/**
+ * Flushes the send buffer.
+ *
+ * @returns VBox status code.
+ * @param pSession The support driver session.
+ * @param hIf The interface handle to flush.
+ */
+int VBoxNetIntIfFlush(PSUPDRVSESSION pSession, INTNETIFHANDLE hIf)
+{
+ INTNETIFSENDREQ SendReq;
+ SendReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
+ SendReq.Hdr.cbReq = sizeof(SendReq);
+ SendReq.pSession = pSession;
+ SendReq.hIf = hIf;
+ return SUPR3CallVMMR0Ex(NIL_RTR0PTR, NIL_VMCPUID, VMMR0_DO_INTNET_IF_SEND, 0, &SendReq.Hdr);
+}
+
+
+/**
+ * Copys the SG segments into the specified fram.
+ *
+ * @param pvFrame The frame buffer.
+ * @param cSegs The number of segments.
+ * @param paSegs The segments.
+ */
+static void vboxnetIntIfCopySG(void *pvFrame, size_t cSegs, PCINTNETSEG paSegs)
+{
+ uint8_t *pbDst = (uint8_t *)pvFrame;
+ for (size_t iSeg = 0; iSeg < cSegs; iSeg++)
+ {
+ memcpy(pbDst, paSegs[iSeg].pv, paSegs[iSeg].cb);
+ pbDst += paSegs[iSeg].cb;
+ }
+}
+
+
+/**
+ * Writes a frame packet to the buffer.
+ *
+ * @returns VBox status code.
+ * @param pBuf The buffer.
+ * @param pRingBuf The ring buffer to read from.
+ * @param cSegs The number of segments.
+ * @param paSegs The segments.
+ */
+int VBoxNetIntIfRingWriteFrame(PINTNETBUF pBuf, PINTNETRINGBUF pRingBuf, size_t cSegs, PCINTNETSEG paSegs)
+{
+ /*
+ * Validate input.
+ */
+ AssertPtr(pBuf);
+ AssertPtr(pRingBuf);
+ AssertPtr(paSegs);
+ Assert(cSegs > 0);
+
+ /*
+ * Calc frame size.
+ */
+ uint32_t cbFrame = 0;
+ for (size_t iSeg = 0; iSeg < cSegs; iSeg++)
+ cbFrame += paSegs[iSeg].cb;
+ Assert(cbFrame >= sizeof(RTMAC) * 2);
+
+ /*
+ * Allocate a frame, copy the data and commit it.
+ */
+ PINTNETHDR pHdr = NULL;
+ void *pvFrame = NULL;
+ int rc = IntNetRingAllocateFrame(pRingBuf, cbFrame, &pHdr, &pvFrame);
+ if (RT_SUCCESS(rc))
+ {
+ vboxnetIntIfCopySG(pvFrame, cSegs, paSegs);
+ IntNetRingCommitFrame(pRingBuf, pHdr);
+ return VINF_SUCCESS;
+ }
+
+ return rc;
+}
+
+
+/**
+ * Sends a frame
+ *
+ * @returns VBox status code.
+ * @param pSession The support driver session.
+ * @param hIf The interface handle.
+ * @param pBuf The interface buffer.
+ * @param cSegs The number of segments.
+ * @param paSegs The segments.
+ * @param fFlush Whether to flush the write.
+ */
+int VBoxNetIntIfSend(PSUPDRVSESSION pSession, INTNETIFHANDLE hIf, PINTNETBUF pBuf,
+ size_t cSegs, PCINTNETSEG paSegs, bool fFlush)
+{
+ int rc = VBoxNetIntIfRingWriteFrame(pBuf, &pBuf->Send, cSegs, paSegs);
+ if (rc == VERR_BUFFER_OVERFLOW)
+ {
+ VBoxNetIntIfFlush(pSession, hIf);
+ rc = VBoxNetIntIfRingWriteFrame(pBuf, &pBuf->Send, cSegs, paSegs);
+ }
+ if (RT_SUCCESS(rc) && fFlush)
+ rc = VBoxNetIntIfFlush(pSession, hIf);
+ return rc;
+}
diff --git a/src/VBox/NetworkServices/NetLib/VBoxNetLib.h b/src/VBox/NetworkServices/NetLib/VBoxNetLib.h
new file mode 100644
index 00000000..e1150bfb
--- /dev/null
+++ b/src/VBox/NetworkServices/NetLib/VBoxNetLib.h
@@ -0,0 +1,69 @@
+/* $Id: VBoxNetLib.h $ */
+/** @file
+ * VBoxNetUDP - IntNet Client Library.
+ */
+
+/*
+ * 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;
+ * 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.
+ */
+
+#ifndef ___VBoxNetUDP_h___
+#define ___VBoxNetUDP_h___
+
+#include <iprt/net.h>
+#include <VBox/intnet.h>
+
+RT_C_DECLS_BEGIN
+
+
+/**
+ * Header pointers optionally returned by VBoxNetUDPMatch.
+ */
+typedef struct VBOXNETUDPHDRS
+{
+ PCRTNETETHERHDR pEth; /**< Pointer to the ethernet header. */
+ PCRTNETIPV4 pIpv4; /**< Pointer to the IPV4 header if IPV4 packet. */
+ PCRTNETUDP pUdp; /**< Pointer to the UDP header. */
+} VBOXNETUDPHDRS;
+/** Pointer to a VBOXNETUDPHDRS structure. */
+typedef VBOXNETUDPHDRS *PVBOXNETUDPHDRS;
+
+
+/** @name VBoxNetUDPMatch flags.
+ * @{ */
+#define VBOXNETUDP_MATCH_UNICAST RT_BIT_32(0)
+#define VBOXNETUDP_MATCH_BROADCAST RT_BIT_32(1)
+#define VBOXNETUDP_MATCH_CHECKSUM RT_BIT_32(2)
+#define VBOXNETUDP_MATCH_REQUIRE_CHECKSUM RT_BIT_32(3)
+#define VBOXNETUDP_MATCH_PRINT_STDERR RT_BIT_32(31)
+/** @} */
+
+void * VBoxNetUDPMatch(PINTNETBUF pBuf, unsigned uDstPort, PCRTMAC pDstMac, uint32_t fFlags, PVBOXNETUDPHDRS pHdrs, size_t *pcb);
+int VBoxNetUDPUnicast(PSUPDRVSESSION pSession, INTNETIFHANDLE hIf, PINTNETBUF pBuf,
+ RTNETADDRIPV4 SrcIPv4Addr, PCRTMAC SrcMacAddr, unsigned uSrcPort,
+ RTNETADDRIPV4 DstIPv4Addr, PCRTMAC DstMacAddr, unsigned uDstPort,
+ void const *pvData, size_t cbData);
+int VBoxNetUDPBroadcast(PSUPDRVSESSION pSession, INTNETIFHANDLE hIf, PINTNETBUF pBuf,
+ RTNETADDRIPV4 SrcIPv4Addr, PCRTMAC SrcMacAddr, unsigned uSrcPort,
+ unsigned uDstPort,
+ void const *pvData, size_t cbData);
+
+bool VBoxNetArpHandleIt(PSUPDRVSESSION pSession, INTNETIFHANDLE hIf, PINTNETBUF pBuf, PCRTMAC pMacAddr, RTNETADDRIPV4 IPv4Addr);
+
+int VBoxNetIntIfFlush(PSUPDRVSESSION pSession, INTNETIFHANDLE hIf);
+int VBoxNetIntIfRingWriteFrame(PINTNETBUF pBuf, PINTNETRINGBUF pRingBuf, size_t cSegs, PCINTNETSEG paSegs);
+int VBoxNetIntIfSend(PSUPDRVSESSION pSession, INTNETIFHANDLE hIf, PINTNETBUF pBuf, size_t cSegs, PCINTNETSEG paSegs, bool fFlush);
+
+
+RT_C_DECLS_END
+
+#endif
+
diff --git a/src/VBox/NetworkServices/NetLib/VBoxNetUDP.cpp b/src/VBox/NetworkServices/NetLib/VBoxNetUDP.cpp
new file mode 100644
index 00000000..79c40c27
--- /dev/null
+++ b/src/VBox/NetworkServices/NetLib/VBoxNetUDP.cpp
@@ -0,0 +1,303 @@
+/* $Id: VBoxNetUDP.cpp $ */
+/** @file
+ * VBoxNetUDP - IntNet UDP Client Routines.
+ */
+
+/*
+ * 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;
+ * 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.
+ */
+
+/*******************************************************************************
+* Header Files *
+*******************************************************************************/
+#define LOG_GROUP LOG_GROUP_DEFAULT
+#include "VBoxNetLib.h"
+#include <iprt/stream.h>
+#include <iprt/string.h>
+#include <iprt/rand.h>
+#include <VBox/log.h>
+#include <VBox/vmm/pdmnetinline.h>
+#include <VBox/intnetinline.h>
+
+
+/**
+ * Checks if the head of the receive ring is a UDP packet matching the given
+ * criteria.
+ *
+ * @returns Pointer to the data if it matches.
+ * @param pBuf The IntNet buffers.
+ * @param uDstPort The destination port to match.
+ * @param pDstMac The destination address to match if
+ * VBOXNETUDP_MATCH_UNICAST is specied.
+ * @param fFlags Flags indicating what to match and some debug stuff.
+ * See VBOXNETUDP_MATCH_*.
+ * @param pHdrs Where to return the pointers to the headers.
+ * Optional.
+ * @param pcb Where to return the size of the data on success.
+ */
+void *VBoxNetUDPMatch(PINTNETBUF pBuf, unsigned uDstPort, PCRTMAC pDstMac, uint32_t fFlags, PVBOXNETUDPHDRS pHdrs, size_t *pcb)
+{
+ /*
+ * Clear return values so we can return easier on mismatch.
+ */
+ *pcb = 0;
+ if (pHdrs)
+ {
+ pHdrs->pEth = NULL;
+ pHdrs->pIpv4 = NULL;
+ pHdrs->pUdp = NULL;
+ }
+
+ /*
+ * Valid IntNet Ethernet frame?
+ */
+ PCINTNETHDR pHdr = IntNetRingGetNextFrameToRead(&pBuf->Recv);
+ if ( !pHdr
+ || ( pHdr->u16Type != INTNETHDR_TYPE_FRAME
+ && pHdr->u16Type != INTNETHDR_TYPE_GSO))
+ return NULL;
+
+ size_t cbFrame = pHdr->cbFrame;
+ const void *pvFrame = IntNetHdrGetFramePtr(pHdr, pBuf);
+ PCPDMNETWORKGSO pGso = NULL;
+ if (pHdr->u16Type == INTNETHDR_TYPE_GSO)
+ {
+ pGso = (PCPDMNETWORKGSO)pvFrame;
+ if (!PDMNetGsoIsValid(pGso, cbFrame, cbFrame - sizeof(*pGso)))
+ return NULL;
+ /** @todo IPv6 UDP support, goes for this entire function really. Not really
+ * important yet since this is currently only used by the DHCP server. */
+ if (pGso->u8Type != PDMNETWORKGSOTYPE_IPV4_UDP)
+ return NULL;
+ pvFrame = pGso + 1;
+ cbFrame -= sizeof(*pGso);
+ }
+
+ PCRTNETETHERHDR pEthHdr = (PCRTNETETHERHDR)pvFrame;
+ if (pHdrs)
+ pHdrs->pEth = pEthHdr;
+
+#ifdef IN_RING3
+ /* Dump if to stderr/log if that's wanted. */
+ if (fFlags & VBOXNETUDP_MATCH_PRINT_STDERR)
+ {
+ RTStrmPrintf(g_pStdErr, "frame: cb=%04x dst=%.6Rhxs src=%.6Rhxs type=%04x%s\n",
+ cbFrame, &pEthHdr->DstMac, &pEthHdr->SrcMac, RT_BE2H_U16(pEthHdr->EtherType),
+ !memcmp(&pEthHdr->DstMac, pDstMac, sizeof(*pDstMac)) ? " Mine!" : "");
+ }
+#endif
+
+ /*
+ * Ethernet matching.
+ */
+
+ /* Ethernet min frame size. */
+ if (cbFrame < 64)
+ return NULL;
+
+ /* Match Ethertype: IPV4? */
+ /** @todo VLAN tagging? */
+ if (pEthHdr->EtherType != RT_H2BE_U16_C(RTNET_ETHERTYPE_IPV4))
+ return NULL;
+
+ /* Match destination address (ethernet) */
+ if ( ( !(fFlags & VBOXNETUDP_MATCH_UNICAST)
+ || memcmp(&pEthHdr->DstMac, pDstMac, sizeof(pEthHdr->DstMac)))
+ && ( !(fFlags & VBOXNETUDP_MATCH_BROADCAST)
+ || pEthHdr->DstMac.au16[0] != 0xffff
+ || pEthHdr->DstMac.au16[1] != 0xffff
+ || pEthHdr->DstMac.au16[2] != 0xffff))
+ return NULL;
+
+ /*
+ * If we're working on a GSO frame, we need to make sure the length fields
+ * are set correctly (they are usually set to 0).
+ */
+ if (pGso)
+ PDMNetGsoPrepForDirectUse(pGso, (void *)pvFrame, cbFrame, PDMNETCSUMTYPE_NONE);
+
+ /*
+ * IP validation and matching.
+ */
+ PCRTNETIPV4 pIpHdr = (PCRTNETIPV4)(pEthHdr + 1);
+ if (pHdrs)
+ pHdrs->pIpv4 = pIpHdr;
+
+ /* Protocol: UDP */
+ if (pIpHdr->ip_p != RTNETIPV4_PROT_UDP)
+ return NULL;
+
+ /* Valid IPv4 header? */
+ size_t const offIpHdr = (uintptr_t)pIpHdr - (uintptr_t)pEthHdr;
+ if (!RTNetIPv4IsHdrValid(pIpHdr, cbFrame - offIpHdr, cbFrame - offIpHdr, !pGso /*fChecksum*/))
+ return NULL;
+
+ /*
+ * UDP matching and validation.
+ */
+ PCRTNETUDP pUdpHdr = (PCRTNETUDP)((uint32_t *)pIpHdr + pIpHdr->ip_hl);
+ if (pHdrs)
+ pHdrs->pUdp = pUdpHdr;
+
+ /* Destination port */
+ if (RT_BE2H_U16(pUdpHdr->uh_dport) != uDstPort)
+ return NULL;
+
+ if (!pGso)
+ {
+ /* Validate the UDP header according to flags. */
+ size_t offUdpHdr = (uintptr_t)pUdpHdr - (uintptr_t)pEthHdr;
+ if (fFlags & (VBOXNETUDP_MATCH_CHECKSUM | VBOXNETUDP_MATCH_REQUIRE_CHECKSUM))
+ {
+ if (!RTNetIPv4IsUDPValid(pIpHdr, pUdpHdr, pUdpHdr + 1, cbFrame - offUdpHdr, true /*fChecksum*/))
+ return NULL;
+ if ( (fFlags & VBOXNETUDP_MATCH_REQUIRE_CHECKSUM)
+ && !pUdpHdr->uh_sum)
+ return NULL;
+ }
+ else
+ {
+ if (!RTNetIPv4IsUDPSizeValid(pIpHdr, pUdpHdr, cbFrame - offUdpHdr))
+ return NULL;
+ }
+ }
+
+ /*
+ * We've got a match!
+ */
+ *pcb = pUdpHdr->uh_ulen - sizeof(*pUdpHdr);
+ return (void *)(pUdpHdr + 1);
+}
+
+
+/** Internal worker for VBoxNetUDPUnicast and VBoxNetUDPBroadcast. */
+static int vboxnetudpSend(PSUPDRVSESSION pSession, INTNETIFHANDLE hIf, PINTNETBUF pBuf,
+ RTNETADDRIPV4 SrcIPv4Addr, PCRTMAC pSrcMacAddr, unsigned uSrcPort,
+ RTNETADDRIPV4 DstIPv4Addr, PCRTMAC pDstMacAddr, unsigned uDstPort,
+ void const *pvData, size_t cbData)
+{
+ INTNETSEG aSegs[4];
+
+ /* the Ethernet header */
+ RTNETETHERHDR EtherHdr;
+ EtherHdr.DstMac = *pDstMacAddr;
+ EtherHdr.SrcMac = *pSrcMacAddr;
+ EtherHdr.EtherType = RT_H2BE_U16_C(RTNET_ETHERTYPE_IPV4);
+
+ aSegs[0].pv = &EtherHdr;
+ aSegs[0].cb = sizeof(EtherHdr);
+ aSegs[0].Phys = NIL_RTHCPHYS;
+
+ /* the IP header */
+ RTNETIPV4 IpHdr;
+ unsigned cbIdHdr = RT_UOFFSETOF(RTNETIPV4, ip_options);
+ IpHdr.ip_v = 4;
+ IpHdr.ip_hl = cbIdHdr >> 2;
+ IpHdr.ip_tos = 0;
+ IpHdr.ip_len = RT_H2BE_U16((uint16_t)(cbData + sizeof(RTNETUDP) + cbIdHdr));
+ IpHdr.ip_id = (uint16_t)RTRandU32();
+ IpHdr.ip_off = 0;
+ IpHdr.ip_ttl = 255;
+ IpHdr.ip_p = RTNETIPV4_PROT_UDP;
+ IpHdr.ip_sum = 0;
+ IpHdr.ip_src = SrcIPv4Addr;
+ IpHdr.ip_dst = DstIPv4Addr;
+ IpHdr.ip_sum = RTNetIPv4HdrChecksum(&IpHdr);
+
+ aSegs[1].pv = &IpHdr;
+ aSegs[1].cb = cbIdHdr;
+ aSegs[1].Phys = NIL_RTHCPHYS;
+
+
+ /* the UDP bit */
+ RTNETUDP UdpHdr;
+ UdpHdr.uh_sport = RT_H2BE_U16(uSrcPort);
+ UdpHdr.uh_dport = RT_H2BE_U16(uDstPort);
+ UdpHdr.uh_ulen = RT_H2BE_U16((uint16_t)(cbData + sizeof(RTNETUDP)));
+#if 0
+ UdpHdr.uh_sum = 0; /* pretend checksumming is disabled */
+#else
+ UdpHdr.uh_sum = RTNetIPv4UDPChecksum(&IpHdr, &UdpHdr, pvData);
+#endif
+
+ aSegs[2].pv = &UdpHdr;
+ aSegs[2].cb = sizeof(UdpHdr);
+ aSegs[2].Phys = NIL_RTHCPHYS;
+
+ /* the payload */
+ aSegs[3].pv = (void *)pvData;
+ aSegs[3].cb = (uint32_t)cbData;
+ aSegs[3].Phys = NIL_RTHCPHYS;
+
+
+ /* send it */
+ return VBoxNetIntIfSend(pSession, hIf, pBuf, RT_ELEMENTS(aSegs), &aSegs[0], true /* fFlush */);
+}
+
+
+/**
+ * Sends an unicast UDP packet.
+ *
+ * @returns VBox status code.
+ * @param pSession The support driver session handle.
+ * @param hIf The interface handle.
+ * @param pBuf The interface buffer.
+ * @param SrcIPv4Addr The source IPv4 address.
+ * @param pSrcMacAddr The source MAC address.
+ * @param uSrcPort The source port number.
+ * @param DstIPv4Addr The destination IPv4 address. Can be broadcast.
+ * @param pDstMacAddr The destination MAC address.
+ * @param uDstPort The destination port number.
+ * @param pvData The data payload.
+ * @param cbData The size of the data payload.
+ */
+int VBoxNetUDPUnicast(PSUPDRVSESSION pSession, INTNETIFHANDLE hIf, PINTNETBUF pBuf,
+ RTNETADDRIPV4 SrcIPv4Addr, PCRTMAC pSrcMacAddr, unsigned uSrcPort,
+ RTNETADDRIPV4 DstIPv4Addr, PCRTMAC pDstMacAddr, unsigned uDstPort,
+ void const *pvData, size_t cbData)
+{
+ return vboxnetudpSend(pSession, hIf, pBuf,
+ SrcIPv4Addr, pSrcMacAddr, uSrcPort,
+ DstIPv4Addr, pDstMacAddr, uDstPort,
+ pvData, cbData);
+}
+
+
+/**
+ * Sends a broadcast UDP packet.
+ *
+ * @returns VBox status code.
+ * @param pSession The support driver session handle.
+ * @param hIf The interface handle.
+ * @param pBuf The interface buffer.
+ * @param SrcIPv4Addr The source IPv4 address.
+ * @param pSrcMacAddr The source MAC address.
+ * @param uSrcPort The source port number.
+ * @param uDstPort The destination port number.
+ * @param pvData The data payload.
+ * @param cbData The size of the data payload.
+ */
+int VBoxNetUDPBroadcast(PSUPDRVSESSION pSession, INTNETIFHANDLE hIf, PINTNETBUF pBuf,
+ RTNETADDRIPV4 SrcIPv4Addr, PCRTMAC pSrcMacAddr, unsigned uSrcPort,
+ unsigned uDstPort,
+ void const *pvData, size_t cbData)
+{
+ RTNETADDRIPV4 IPv4AddrBrdCast;
+ IPv4AddrBrdCast.u = UINT32_C(0xffffffff);
+ RTMAC MacBrdCast;
+ MacBrdCast.au16[0] = MacBrdCast.au16[1] = MacBrdCast.au16[2] = UINT16_C(0xffff);
+
+ return vboxnetudpSend(pSession, hIf, pBuf,
+ SrcIPv4Addr, pSrcMacAddr, uSrcPort,
+ IPv4AddrBrdCast, &MacBrdCast, uDstPort,
+ pvData, cbData);
+}
+