VBoxNetDHCP.cpp revision 5c151cd77f89e923c6f2d3686c53733234d61ef1
/* $Id$ */
/** @file
* VBoxNetDHCP - DHCP Service for connecting to IntNet.
*/
/*
* Copyright (C) 2009 Sun Microsystems, Inc.
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
* Clara, CA 95054 USA or visit http://www.sun.com if you need
* additional information or have any questions.
*/
/** @page pg_net_dhcp VBoxNetDHCP
*
* Write a few words...
*
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#include <iprt/initterm.h>
#include "../UDPLib/VBoxNetUDP.h"
#include <vector>
#include <string>
/** @Todo move these: */
/** The requested address. */
#define RTNET_DHCP_OPT_REQUESTED_ADDRESS 50
/*******************************************************************************
* Structures and Typedefs *
*******************************************************************************/
/**
* DHCP configuration item.
*
* This is all public data because I'm too lazy to do it propertly right now.
*/
class VBoxNetDhcpCfg
{
public:
/** The etheret addresses this matches config applies to.
* An empty vector means 'ANY'. */
/** The upper address in the range. */
/** The lower address in the range. */
/** Option 1: The net mask. */
/* * Option 2: The time offset. */
/** Option 3: Routers for the subnet. */
/* * Option 4: Time server. */
/* * Option 5: Name server. */
/** Option 6: Domain Name Server (DNS) */
/* * 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<char> m_HostName;
/* * Option 13: Boot file size option. */
/* * Option 14: Merit dump file. */
/** Option 15: Domain name. */
/* * Option 16: Swap server. */
/* * Option 17: Root path. */
/* * Option 18: Extension path. */
/* * 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 48: X Window System Font Server. */
/* * Option 49: X Window System Display Manager. */
/** Option 51: IP Address Lease Time. */
/* * Option 64: Network Information Service+ Domain. */
/* * Option 65: Network Information Service+ Servers. */
/** Option 66: TFTP server name. */
/** Option 67: Bootfile name. */
/* * 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. */
{
m_UpperAddr.u = UINT32_MAX;
m_LowerAddr.u = UINT32_MAX;
m_SubnetMask.u = UINT32_MAX;
}
/** 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)
{
if (m_UpperAddr.u == UINT32_MAX)
if (m_LowerAddr.u == UINT32_MAX)
if (m_SubnetMask.u == UINT32_MAX)
return 2;
}
return 0;
}
};
/**
* DHCP lease.
*/
class VBoxNetDhcpLease
{
public:
typedef enum State
{
/** The lease is free / released. */
kState_Free = 0,
/** An offer has been made.
* Expire time indicates when the offer expires. */
/** The lease is active.
* Expire time indicates when the lease expires. */
} State;
void activate(void);
void release(void);
/** The client MAC address. */
/** The IPv4 address. */
/** The current lease state. */
/** The lease expiration time. */
/** Transaction ID. */
/** The configuration for this lease. */
};
/**
* DHCP server instance.
*/
class VBoxNetDhcp
{
public:
VBoxNetDhcp();
virtual ~VBoxNetDhcp();
int tryGoOnline(void);
int run(void);
protected:
static bool findOptionIPv4Addr(uint8_t uOption, PCRTNETBOOTP pDhcpMsg, size_t cb, PRTNETADDRIPV4 pIPv4Addr);
protected:
/** @name The server configuration data members.
* @{ */
/** @} */
/** The current configs. */
/** The current leases. */
/** @name The network interface
* @{ */
/** @} */
/** @name Debug stuff
* @{ */
/** @} */
};
/*******************************************************************************
* Global Variables *
*******************************************************************************/
/** Pointer to the DHCP server. */
static VBoxNetDhcp *g_pDhcp;
/**
* Construct a DHCP server with a default configuration.
*/
{
m_Name = "VBoxNetDhcp";
m_Network = "VBoxNetDhcp";
m_cbSendBuf = 8192;
m_cVerbosity = 0;
m_cbCurMsg = 0;
#if 1 /* while hacking. */
#endif
}
/**
* Destruct a DHCP server.
*/
{
/*
* Close the interface connection.
*/
if (m_hIf != INTNET_HANDLE_INVALID)
{
}
if (m_pSession)
{
SUPTerm(false /* not forced */);
}
}
/**
* 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 rc = 0;
if (pCfg)
{
if (!rc)
delete pCfg;
}
return rc;
}
/**
* Parse the arguments.
*
* @returns 0 on success, fully bitched exit code on failure.
*
* @param argc Argument count.
* @param argv Argument vector.
*/
{
static const RTGETOPTDEF s_aOptionDefs[] =
{
};
for (;;)
{
if (!rc)
break;
switch (rc)
{
case 'N':
break;
case 'n':
break;
case 'a':
break;
case 'i':
break;
case 'v':
m_cVerbosity++;
break;
/* Begin config. */
case 'b':
if (rc)
break;
/* fall thru */
/* config specific ones. */
case 'g':
case 'l':
case 'u':
case 'm':
if (!pCurCfg)
{
pCurCfg = new VBoxNetDhcpCfg();
if (!pCurCfg)
{
rc = 1;
break;
}
}
switch (rc)
{
case 'g':
break;
case 'l':
break;
case 'u':
break;
case 'm':
break;
case 0: /* ignore */ break;
default:
rc = 1;
break;
}
break;
case 'V':
rc = 0;
break;
case 'h':
RTPrintf("VBoxNetDHCP Version %s\n"
"(C) 2009 Sun Microsystems, Inc.\n"
"All rights reserved\n"
"\n"
"Usage:\n"
" TODO\n",
rc = 1;
break;
default:
break;
}
}
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.
*/
if (RT_FAILURE(rc))
{
return 1;
}
char szPath[RTPATH_MAX];
if (RT_FAILURE(rc))
{
return 1;
}
if (RT_FAILURE(rc))
{
return 1;
}
/*
* Create the open request.
*/
/*
* Issue the request.
*/
if (RT_SUCCESS(rc))
{
/*
* Get the ring-3 address of the shared interface buffer.
*/
if (RT_SUCCESS(rc))
{
/*
* Activate the interface.
*/
if (RT_SUCCESS(rc))
return 0;
/* bail out */
RTStrmPrintf(g_pStdErr, "VBoxNetDHCP: SUPCallVMMR0Ex(,VMMR0_DO_INTNET_IF_SET_PROMISCUOUS_MODE,) failed, rc=%Rrc\n", rc);
}
else
RTStrmPrintf(g_pStdErr, "VBoxNetDHCP: SUPCallVMMR0Ex(,VMMR0_DO_INTNET_IF_GET_RING3_BUFFER,) failed, rc=%Rrc\n", rc);
}
else
RTStrmPrintf(g_pStdErr, "VBoxNetDHCP: SUPCallVMMR0Ex(,VMMR0_DO_INTNET_OPEN,) failed, rc=%Rrc\n", rc);
}
/**
* 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.
*/
for (;;)
{
/*
* Wait for a packet to become available.
*/
WaitReq.cMillies = 2000; /* 2 secs - the sleep is for some reason uninterruptible... */ /** @todo fix interruptability in SrvIntNet! */
if (RT_FAILURE(rc))
{
if (rc == VERR_TIMEOUT)
continue;
return 1;
}
/*
* Process the receive buffer.
*/
while (INTNETRingGetReadable(pRingBuf) > 0)
{
{
m_cbCurMsg = cb;
{
}
else
debugPrint(1, true, "VBoxNetDHCP: Skipping invalid DHCP packet.\n"); /** @todo handle pure bootp clients too? */
m_cbCurMsg = 0;
}
/* Advance to the next frame. */
}
}
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.
*/
{
{
switch (uMsgType)
{
case RTNET_DHCP_MT_DISCOVER:
case RTNET_DHCP_MT_REQUEST:
case RTNET_DHCP_MT_DECLINE:
case RTNET_DHCP_MT_RELEASE:
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.
*/
{
/*
* First, see if there is already a lease for this client. It may have rebooted,
* crashed or whatever that have caused it to forget its existing lease.
* If none was found, create a new lease for it and then construct a reply.
*/
true /* fEnsureUpToDateConfig */);
if (!pLease)
if (!pLease)
return false;
return true;
}
/**
* The client is requesting an offer.
*
* @returns true.
*
* @param pDhcpMsg The message.
* @param cb The message size.
*/
{
/** @todo Probably need to match the server IP here to work correctly with
* other servers. */
/*
* Windows will reissue these requests when rejoining a network if it thinks it
* already has an address on the network. If we cannot find a valid lease,
* make a new one and return NAC.
*/
{
if (pLease)
{
/* Check if the xid matches, if it doesn't it's not our call. */
#if 0 /** @todo check how windows treats bp_xid here, it should match I think. If
* it doesn't we've no way of filtering out broadcast replies to other
* DHCP servers. Fix this later.
*/
{
return true;
}
#endif
/* Check if the config has changed since the offer was given? NAK it then? */
/*
* Ack it.
*/
}
else
{
/*
* Try make a new offer and see if we get the requested IP and config, that
* will make the (windows) client happy apparently...
*/
if ( pLease
/** @todo match requested config later */)
{
/* ACK it. */
}
else
{
/* NAK it */
if (pLease)
}
}
}
else
return true;
}
/**
* The client is declining an offer we've made.
*
* @returns true.
*
* @param pDhcpMsg The message.
* @param cb The message size.
*/
{
/** @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. */
return true;
}
/**
* The client is releasing its lease - good boy.
*
* @returns true.
*
* @param pDhcpMsg The message.
* @param cb The message size.
*/
{
/** @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. */
return true;
}
/**
* 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)
{
/** @todo this is required. :-) */
}
VBoxNetDhcpLease *VBoxNetDhcp::findLeaseByMacAddress(PCRTMAC pMacAddress, bool fEnsureUpToDateConfig)
{
return NULL;
}
VBoxNetDhcpLease *VBoxNetDhcp::findLeaseByIpv4AndMacAddresses(RTNETADDRIPV4 IPv4Addr, PCRTMAC pMacAddress)
{
return NULL;
}
{
return NULL;
}
/**
* Finds an option.
*
* @returns On success, a pointer to the option number within the DHCP message
* and *pcbMaxOpt set to the maximum number of bytes the option may
* contain.
* If not found NULL is returned and *pcbMaxOpt is not changed.
*
* @param uOption The option to search for.
* @param pDhcpMsg The DHCP message.
* @param cb The size of the message.
* @param pcbMaxOpt Where to store the max option size. Optional.
*/
/* static */ uint8_t *
{
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)
{
if (pbOpt)
{
}
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.
*/
{
if (iMinLevel <= m_cVerbosity)
{
}
}
/**
* 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.
*/
{
if (iMinLevel <= m_cVerbosity)
{
RTStrmPrintf(g_pStdErr, "VBoxNetDHCP: %s: %N\n", iMinLevel >= 2 ? "debug" : "info", pszFmt, &vaCopy);
if ( fMsg
&& m_cVerbosity >= 2
&& m_pCurMsg)
{
RTStrmPrintf(g_pStdErr, "VBoxNetDHCP: debug: %8s chaddr=%.6Rhxs ciaddr=%d.%d.%d.%d yiaddr=%d.%d.%d.%d siaddr=%d.%d.%d.%d\n",
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]);
}
}
}
/**
* Gets the name of given DHCP message type.
*
* @returns Readonly name.
* @param uMsgType The message number.
*/
{
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.
*/
{
/*
* Instantiate the DHCP server and hand it the options.
*/
if (!pDhcp)
{
return 1;
}
if (rc)
return rc;
/*
* Try connect the server to the network.
*/
if (rc)
{
delete pDhcp;
return rc;
}
/*
* Process requests.
*/
delete pDhcp;
return rc;
}
#ifndef VBOX_WITH_HARDENING
{
int rc = RTR3InitAndSUPLib();
if (RT_FAILURE(rc))
{
return 1;
}
}
#endif /* !VBOX_WITH_HARDENING */