diff options
author | Lorry Tar Creator <lorry-tar-importer@baserock.org> | 2014-03-26 19:21:20 +0000 |
---|---|---|
committer | <> | 2014-05-08 15:03:54 +0000 |
commit | fb123f93f9f5ce42c8e5785d2f8e0edaf951740e (patch) | |
tree | c2103d76aec5f1f10892cd1d3a38e24f665ae5db /src/VBox/NetworkServices/DHCP | |
parent | 58ed4748338f9466599adfc8a9171280ed99e23f (diff) | |
download | VirtualBox-master.tar.gz |
Imported from /home/lorry/working-area/delta_VirtualBox/VirtualBox-4.3.10.tar.bz2.HEADVirtualBox-4.3.10master
Diffstat (limited to 'src/VBox/NetworkServices/DHCP')
-rw-r--r-- | src/VBox/NetworkServices/DHCP/ClientDataInt.h | 67 | ||||
-rw-r--r-- | src/VBox/NetworkServices/DHCP/Config.cpp | 1305 | ||||
-rw-r--r-- | src/VBox/NetworkServices/DHCP/Config.h | 829 | ||||
-rw-r--r-- | src/VBox/NetworkServices/DHCP/Makefile.kmk | 11 | ||||
-rw-r--r-- | src/VBox/NetworkServices/DHCP/NetworkManagerDhcp.cpp | 188 | ||||
-rw-r--r-- | src/VBox/NetworkServices/DHCP/VBoxNetDHCP.cpp | 2080 | ||||
-rw-r--r-- | src/VBox/NetworkServices/DHCP/VBoxNetDHCPHardened.cpp | 2 |
7 files changed, 2740 insertions, 1742 deletions
diff --git a/src/VBox/NetworkServices/DHCP/ClientDataInt.h b/src/VBox/NetworkServices/DHCP/ClientDataInt.h new file mode 100644 index 00000000..3c544efb --- /dev/null +++ b/src/VBox/NetworkServices/DHCP/ClientDataInt.h @@ -0,0 +1,67 @@ +/* $Id: ClientDataInt.h $ */ +/** @file + * Config.h + */ + +/* + * Copyright (C) 2013 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 __CLIENT_DATA_INT_H__ +#define __CLIENT_DATA_INT_H__ + +class ClientData +{ +public: + ClientData() + { + m_address.u = 0; + m_network.u = 0; + fHasLease = false; + fHasClient = false; + fBinding = true; + u64TimestampBindingStarted = 0; + u64TimestampLeasingStarted = 0; + u32LeaseExpirationPeriod = 0; + u32BindExpirationPeriod = 0; + pCfg = NULL; + + } + ~ClientData(){} + + /* client information */ + RTNETADDRIPV4 m_address; + RTNETADDRIPV4 m_network; + RTMAC m_mac; + + bool fHasClient; + + /* Lease part */ + bool fHasLease; + /** lease isn't commited */ + bool fBinding; + + /** Timestamp when lease commited. */ + uint64_t u64TimestampLeasingStarted; + /** Period when lease is expired in secs. */ + uint32_t u32LeaseExpirationPeriod; + + /** timestamp when lease was bound */ + uint64_t u64TimestampBindingStarted; + /* Period when binding is expired in secs. */ + uint32_t u32BindExpirationPeriod; + + MapOptionId2RawOption options; + + NetworkConfigEntity *pCfg; +}; + +#endif diff --git a/src/VBox/NetworkServices/DHCP/Config.cpp b/src/VBox/NetworkServices/DHCP/Config.cpp new file mode 100644 index 00000000..2c76b5e2 --- /dev/null +++ b/src/VBox/NetworkServices/DHCP/Config.cpp @@ -0,0 +1,1305 @@ +/* $Id: Config.cpp $ */ +/** @file + * Configuration for DHCP. + */ + +/* + * Copyright (C) 2013-2014 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. + */ + + +/** + * XXX: license. + */ + +#include <iprt/asm.h> +#include <iprt/getopt.h> +#include <iprt/net.h> +#include <iprt/time.h> + +#include <VBox/sup.h> +#include <VBox/intnet.h> +#include <VBox/intnetinline.h> +#include <VBox/vmm/vmm.h> +#include <VBox/version.h> + +#include <VBox/com/string.h> + +#include <iprt/cpp/xml.h> + +#define BASE_SERVICES_ONLY +#include "../NetLib/VBoxNetBaseService.h" +#include "../NetLib/VBoxNetLib.h" +#include "../NetLib/shared_ptr.h" + +#include <list> +#include <vector> +#include <map> +#include <string> + +#include "Config.h" +#include "ClientDataInt.h" + +bool operator== (const Lease& lhs, const Lease& rhs) +{ + return (lhs.m.get() == rhs.m.get()); +} + + +bool operator!= (const Lease& lhs, const Lease& rhs) +{ + return !(lhs == rhs); +} + + +bool operator< (const Lease& lhs, const Lease& rhs) +{ + return ( (lhs.getAddress() < rhs.getAddress()) + || (lhs.issued() < rhs.issued())); +} +/* consts */ + +const NullConfigEntity *g_NullConfig = new NullConfigEntity(); +RootConfigEntity *g_RootConfig = new RootConfigEntity(std::string("ROOT"), 1200 /* 20 min. */); +const ClientMatchCriteria *g_AnyClient = new AnyClientMatchCriteria(); + +static ConfigurationManager *g_ConfigurationManager = ConfigurationManager::getConfigurationManager(); + +static NetworkManager *g_NetworkManager = NetworkManager::getNetworkManager(); + +bool MACClientMatchCriteria::check(const Client& client) const +{ + return (client == m_mac); +} + + +int BaseConfigEntity::match(Client& client, BaseConfigEntity **cfg) +{ + int iMatch = (m_criteria && m_criteria->check(client)? m_MatchLevel: 0); + if (m_children.empty()) + { + if (iMatch > 0) + { + *cfg = this; + return iMatch; + } + } + else + { + *cfg = this; + /* XXX: hack */ + BaseConfigEntity *matching = this; + int matchingLevel = m_MatchLevel; + + for (std::vector<BaseConfigEntity *>::iterator it = m_children.begin(); + it != m_children.end(); + ++it) + { + iMatch = (*it)->match(client, &matching); + if (iMatch > matchingLevel) + { + *cfg = matching; + matchingLevel = iMatch; + } + } + return matchingLevel; + } + return iMatch; +} + +/* Client */ +/* Configs + NetworkConfigEntity(std::string name, + ConfigEntity* pCfg, + ClientMatchCriteria* criteria, + RTNETADDRIPV4& networkID, + RTNETADDRIPV4& networkMask) +*/ +static const RTNETADDRIPV4 g_AnyIpv4 = {0}; +static const RTNETADDRIPV4 g_AllIpv4 = {0xffffffff}; +RootConfigEntity::RootConfigEntity(std::string name, uint32_t expPeriod): + NetworkConfigEntity(name, g_NullConfig, g_AnyClient, g_AnyIpv4, g_AllIpv4) +{ + m_MatchLevel = 2; + m_u32ExpirationPeriod = expPeriod; +} + +/* Configuration Manager */ +struct ConfigurationManager::Data +{ + Data():fFileExists(false){} + + MapLease2Ip4Address m_allocations; + Ipv4AddressContainer m_nameservers; + Ipv4AddressContainer m_routers; + + std::string m_domainName; + VecClient m_clients; + com::Utf8Str m_leaseStorageFilename; + bool fFileExists; +}; + +ConfigurationManager *ConfigurationManager::getConfigurationManager() +{ + if (!g_ConfigurationManager) + + + { + g_ConfigurationManager = new ConfigurationManager(); + g_ConfigurationManager->init(); + } + + return g_ConfigurationManager; +} + + +const std::string tagXMLLeases = "Leases"; +const std::string tagXMLLeasesAttributeVersion = "version"; +const std::string tagXMLLeasesVersion_1_0 = "1.0"; +const std::string tagXMLLease = "Lease"; +const std::string tagXMLLeaseAttributeMac = "mac"; +const std::string tagXMLLeaseAttributeNetwork = "network"; +const std::string tagXMLLeaseAddress = "Address"; +const std::string tagXMLAddressAttributeValue = "value"; +const std::string tagXMLLeaseTime = "Time"; +const std::string tagXMLTimeAttributeIssued = "issued"; +const std::string tagXMLTimeAttributeExpiration = "expiration"; +const std::string tagXMLLeaseOptions = "Options"; + +/** + * <Leases version="1.0"> + * <Lease mac="" network=""/> + * <Address value=""/> + * <Time issued="" expiration=""/> + * <options> + * <option name="" type=""/> + * </option> + * </options> + * </Lease> + * </Leases> + */ +int ConfigurationManager::loadFromFile(const com::Utf8Str& leaseStorageFileName) +{ + m->m_leaseStorageFilename = leaseStorageFileName; + + xml::XmlFileParser parser; + xml::Document doc; + + try { + parser.read(m->m_leaseStorageFilename.c_str(), doc); + } + catch (...) + { + return VINF_SUCCESS; + } + + /* XML parsing */ + xml::ElementNode *root = doc.getRootElement(); + + if (!root || !root->nameEquals(tagXMLLeases.c_str())) + { + m->fFileExists = false; + return VERR_NOT_FOUND; + } + + com::Utf8Str version; + if (root) + root->getAttributeValue(tagXMLLeasesAttributeVersion.c_str(), version); + + /* XXX: version check */ + xml::NodesLoop leases(*root); + + bool valueExists; + const xml::ElementNode *lease; + while ((lease = leases.forAllNodes())) + { + if (!lease->nameEquals(tagXMLLease.c_str())) + continue; + + ClientData *data = new ClientData(); + Lease l(data); + if (l.fromXML(lease)) + { + + m->m_allocations.insert(MapLease2Ip4AddressPair(l, l.getAddress())); + + + NetworkConfigEntity *pNetCfg = NULL; + Client c(data); + int rc = g_RootConfig->match(c, (BaseConfigEntity **)&pNetCfg); + Assert(rc >= 0 && pNetCfg); + + l.setConfig(pNetCfg); + + m->m_clients.push_back(c); + } + } + + return VINF_SUCCESS; +} + + +int ConfigurationManager::saveToFile() +{ + if (m->m_leaseStorageFilename.isEmpty()) + return VINF_SUCCESS; + + xml::Document doc; + + xml::ElementNode *root = doc.createRootElement(tagXMLLeases.c_str()); + if (!root) + return VERR_INTERNAL_ERROR; + + root->setAttribute(tagXMLLeasesAttributeVersion.c_str(), tagXMLLeasesVersion_1_0.c_str()); + + for(MapLease2Ip4AddressConstIterator it = m->m_allocations.begin(); + it != m->m_allocations.end(); ++it) + { + xml::ElementNode *lease = root->createChild(tagXMLLease.c_str()); + if (!it->first.toXML(lease)) + { + /* XXX: todo logging + error handling */ + } + } + + try { + xml::XmlFileWriter writer(doc); + writer.write(m->m_leaseStorageFilename.c_str(), true); + } catch(...){} + + return VINF_SUCCESS; +} + + +int ConfigurationManager::extractRequestList(PCRTNETBOOTP pDhcpMsg, size_t cbDhcpMsg, RawOption& rawOpt) +{ + return ConfigurationManager::findOption(RTNET_DHCP_OPT_PARAM_REQ_LIST, pDhcpMsg, cbDhcpMsg, rawOpt); +} + + +Client ConfigurationManager::getClientByDhcpPacket(const RTNETBOOTP *pDhcpMsg, size_t cbDhcpMsg) +{ + + VecClientIterator it; + bool fDhcpValid = false; + uint8_t uMsgType = 0; + + fDhcpValid = RTNetIPv4IsDHCPValid(NULL, pDhcpMsg, cbDhcpMsg, &uMsgType); + AssertReturn(fDhcpValid, Client::NullClient); + + LogFlowFunc(("dhcp:mac:%RTmac\n", &pDhcpMsg->bp_chaddr.Mac)); + /* 1st. client IDs */ + for ( it = m->m_clients.begin(); + it != m->m_clients.end(); + ++it) + { + if ((*it) == pDhcpMsg->bp_chaddr.Mac) + { + LogFlowFunc(("client:mac:%RTmac\n", it->getMacAddress())); + /* check timestamp that request wasn't expired. */ + return (*it); + } + } + + if (it == m->m_clients.end()) + { + /* We hasn't got any session for this client */ + Client c; + c.initWithMac(pDhcpMsg->bp_chaddr.Mac); + m->m_clients.push_back(c); + return m->m_clients.back(); + } + + return Client::NullClient; +} + +/** + * 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. + * that this is adjusted if the option length is larger + * than the message buffer. + */ +int +ConfigurationManager::findOption(uint8_t uOption, PCRTNETBOOTP pDhcpMsg, size_t cbDhcpMsg, RawOption& opt) +{ + Assert(uOption != RTNET_DHCP_OPT_PAD); + + /* + * Validate the DHCP bits and figure the max size of the options in the vendor field. + */ + if (cbDhcpMsg <= RT_UOFFSETOF(RTNETBOOTP, bp_vend.Dhcp.dhcp_opts)) + return VERR_INVALID_PARAMETER; + + if (pDhcpMsg->bp_vend.Dhcp.dhcp_cookie != RT_H2N_U32_C(RTNET_DHCP_COOKIE)) + return VERR_INVALID_PARAMETER; + + size_t cbLeft = cbDhcpMsg - 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) + { + opt.u8OptId = uCur; + memcpy(opt.au8RawOpt, pb+2, cbCur); + opt.cbRawOpt = cbCur; + return VINF_SUCCESS; + } + pb += cbCur + 2; + cbLeft -= cbCur - 2; + } + } + + /** @todo search extended dhcp option field(s) when present */ + + return VERR_NOT_FOUND; +} + + +/** + * We bind lease for client till it continue with it on DHCPREQUEST. + */ +Lease ConfigurationManager::allocateLease4Client(const Client& client, PCRTNETBOOTP pDhcpMsg, size_t cbDhcpMsg) +{ + { + /** + * This mean that client has already bound or commited lease. + * If we've it happens it means that we received DHCPDISCOVER twice. + */ + const Lease l = client.lease(); + if (l != Lease::NullLease) + { + /* Here we should take lease from the m_allocation which was feed with leases + * on start + */ + if (l.isExpired()) + { + expireLease4Client(const_cast<Client&>(client)); + if (!l.isExpired()) + return l; + } + else + { + AssertReturn(l.getAddress().u != 0, Lease::NullLease); + return l; + } + } + } + + RTNETADDRIPV4 hintAddress; + RawOption opt; + NetworkConfigEntity *pNetCfg; + + Client cl(client); + AssertReturn(g_RootConfig->match(cl, (BaseConfigEntity **)&pNetCfg) > 0, Lease::NullLease); + + /* DHCPDISCOVER MAY contain request address */ + hintAddress.u = 0; + int rc = findOption(RTNET_DHCP_OPT_REQ_ADDR, pDhcpMsg, cbDhcpMsg, opt); + if (RT_SUCCESS(rc)) + { + hintAddress.u = *(uint32_t *)opt.au8RawOpt; + if ( RT_H2N_U32(hintAddress.u) < RT_H2N_U32(pNetCfg->lowerIp().u) + || RT_H2N_U32(hintAddress.u) > RT_H2N_U32(pNetCfg->upperIp().u)) + hintAddress.u = 0; /* clear hint */ + } + + if ( hintAddress.u + && !isAddressTaken(hintAddress)) + { + Lease l(cl); + l.setConfig(pNetCfg); + l.setAddress(hintAddress); + m->m_allocations.insert(MapLease2Ip4AddressPair(l, hintAddress)); + return l; + } + + uint32_t u32 = 0; + for(u32 = RT_H2N_U32(pNetCfg->lowerIp().u); + u32 <= RT_H2N_U32(pNetCfg->upperIp().u); + ++u32) + { + RTNETADDRIPV4 address; + address.u = RT_H2N_U32(u32); + if (!isAddressTaken(address)) + { + Lease l(cl); + l.setConfig(pNetCfg); + l.setAddress(address); + m->m_allocations.insert(MapLease2Ip4AddressPair(l, address)); + return l; + } + } + + return Lease::NullLease; +} + + +int ConfigurationManager::commitLease4Client(Client& client) +{ + Lease l = client.lease(); + AssertReturn(l != Lease::NullLease, VERR_INTERNAL_ERROR); + + l.bindingPhase(false); + const NetworkConfigEntity *pCfg = l.getConfig(); + + AssertPtr(pCfg); + l.setExpiration(pCfg->expirationPeriod()); + l.phaseStart(RTTimeMilliTS()); + + saveToFile(); + + return VINF_SUCCESS; +} + + +int ConfigurationManager::expireLease4Client(Client& client) +{ + Lease l = client.lease(); + AssertReturn(l != Lease::NullLease, VERR_INTERNAL_ERROR); + + if (l.isInBindingPhase()) + { + + MapLease2Ip4AddressIterator it = m->m_allocations.find(l); + AssertReturn(it != m->m_allocations.end(), VERR_NOT_FOUND); + + /* + * XXX: perhaps it better to keep this allocation ???? + */ + m->m_allocations.erase(it); + + l.expire(); + return VINF_SUCCESS; + } + + l = Lease(client); /* re-new */ + return VINF_SUCCESS; +} + + +bool ConfigurationManager::isAddressTaken(const RTNETADDRIPV4& addr, Lease& lease) +{ + MapLease2Ip4AddressIterator it; + + for (it = m->m_allocations.begin(); + it != m->m_allocations.end(); + ++it) + { + if (it->second.u == addr.u) + { + if (lease != Lease::NullLease) + lease = it->first; + + return true; + } + } + lease = Lease::NullLease; + return false; +} + + +bool ConfigurationManager::isAddressTaken(const RTNETADDRIPV4& addr) +{ + Lease ignore; + return isAddressTaken(addr, ignore); +} + + +NetworkConfigEntity *ConfigurationManager::addNetwork(NetworkConfigEntity *, + const RTNETADDRIPV4& networkId, + const RTNETADDRIPV4& netmask, + RTNETADDRIPV4& LowerAddress, + RTNETADDRIPV4& UpperAddress) +{ + static int id; + char name[64]; + + RTStrPrintf(name, RT_ELEMENTS(name), "network-%d", id); + std::string strname(name); + id++; + + + if (!LowerAddress.u) + LowerAddress = networkId; + + if (!UpperAddress.u) + UpperAddress.u = networkId.u | (~netmask.u); + + return new NetworkConfigEntity(strname, + g_RootConfig, + g_AnyClient, + 5, + networkId, + netmask, + LowerAddress, + UpperAddress); +} + +HostConfigEntity *ConfigurationManager::addHost(NetworkConfigEntity* pCfg, + const RTNETADDRIPV4& address, + ClientMatchCriteria *criteria) +{ + static int id; + char name[64]; + + RTStrPrintf(name, RT_ELEMENTS(name), "host-%d", id); + std::string strname(name); + id++; + + return new HostConfigEntity(address, strname, pCfg, criteria); +} + +int ConfigurationManager::addToAddressList(uint8_t u8OptId, RTNETADDRIPV4& address) +{ + switch(u8OptId) + { + case RTNET_DHCP_OPT_DNS: + m->m_nameservers.push_back(address); + break; + case RTNET_DHCP_OPT_ROUTERS: + m->m_routers.push_back(address); + break; + default: + Log(("dhcp-opt: list (%d) unsupported\n", u8OptId)); + } + return VINF_SUCCESS; +} + + +int ConfigurationManager::flushAddressList(uint8_t u8OptId) +{ + switch(u8OptId) + { + case RTNET_DHCP_OPT_DNS: + m->m_nameservers.clear(); + break; + case RTNET_DHCP_OPT_ROUTERS: + m->m_routers.clear(); + break; + default: + Log(("dhcp-opt: list (%d) unsupported\n", u8OptId)); + } + return VINF_SUCCESS; +} + + +const Ipv4AddressContainer& ConfigurationManager::getAddressList(uint8_t u8OptId) +{ + switch(u8OptId) + { + case RTNET_DHCP_OPT_DNS: + return m->m_nameservers; + + case RTNET_DHCP_OPT_ROUTERS: + return m->m_routers; + + } + /* XXX: Grrr !!! */ + return m_empty; +} + + +int ConfigurationManager::setString(uint8_t u8OptId, const std::string& str) +{ + switch (u8OptId) + { + case RTNET_DHCP_OPT_DOMAIN_NAME: + m->m_domainName = str; + break; + default: + break; + } + + return VINF_SUCCESS; +} + + +const std::string& ConfigurationManager::getString(uint8_t u8OptId) +{ + switch (u8OptId) + { + case RTNET_DHCP_OPT_DOMAIN_NAME: + if (m->m_domainName.length()) + return m->m_domainName; + else + return m_noString; + default: + break; + } + + return m_noString; +} + + +void ConfigurationManager::init() +{ + m = new ConfigurationManager::Data(); +} + + +ConfigurationManager::~ConfigurationManager() { if (m) delete m; } + +/** + * Network manager + */ +struct NetworkManager::Data +{ + Data() + { + RT_ZERO(BootPReplyMsg); + cbBooPReplyMsg = 0; + + m_OurAddress.u = 0; + m_OurNetmask.u = 0; + RT_ZERO(m_OurMac); + } + + union { + RTNETBOOTP BootPHeader; + uint8_t au8Storage[1024]; + } BootPReplyMsg; + int cbBooPReplyMsg; + + RTNETADDRIPV4 m_OurAddress; + RTNETADDRIPV4 m_OurNetmask; + RTMAC m_OurMac; + const VBoxNetHlpUDPService *m_service; +}; + + +NetworkManager::NetworkManager():m(NULL) +{ + m = new NetworkManager::Data(); +} + + +NetworkManager::~NetworkManager() +{ + delete m; + m = NULL; +} + + +NetworkManager *NetworkManager::getNetworkManager() +{ + if (!g_NetworkManager) + g_NetworkManager = new NetworkManager(); + + return g_NetworkManager; +} + + +const RTNETADDRIPV4& NetworkManager::getOurAddress() const +{ + return m->m_OurAddress; +} + + +const RTNETADDRIPV4& NetworkManager::getOurNetmask() const +{ + return m->m_OurNetmask; +} + + +const RTMAC& NetworkManager::getOurMac() const +{ + return m->m_OurMac; +} + + +void NetworkManager::setOurAddress(const RTNETADDRIPV4& aAddress) +{ + m->m_OurAddress = aAddress; +} + + +void NetworkManager::setOurNetmask(const RTNETADDRIPV4& aNetmask) +{ + m->m_OurNetmask = aNetmask; +} + + +void NetworkManager::setOurMac(const RTMAC& aMac) +{ + m->m_OurMac = aMac; +} + + +void NetworkManager::setService(const VBoxNetHlpUDPService *srv) +{ + m->m_service = srv; +} + +/** + * Network manager creates DHCPOFFER datagramm + */ +int NetworkManager::offer4Client(const Client& client, uint32_t u32Xid, + uint8_t *pu8ReqList, int cReqList) +{ + Lease l(client); /* XXX: oh, it looks badly, but now we have lease */ + prepareReplyPacket4Client(client, u32Xid); + + RTNETADDRIPV4 address = l.getAddress(); + m->BootPReplyMsg.BootPHeader.bp_yiaddr = address; + + /* Ubuntu ???*/ + m->BootPReplyMsg.BootPHeader.bp_ciaddr = address; + + /* options: + * - IP lease time + * - message type + * - server identifier + */ + RawOption opt; + RT_ZERO(opt); + + std::vector<RawOption> extra; + opt.u8OptId = RTNET_DHCP_OPT_MSG_TYPE; + opt.au8RawOpt[0] = RTNET_DHCP_MT_OFFER; + opt.cbRawOpt = 1; + extra.push_back(opt); + + opt.u8OptId = RTNET_DHCP_OPT_LEASE_TIME; + + const NetworkConfigEntity *pCfg = l.getConfig(); + AssertPtr(pCfg); + + *(uint32_t *)opt.au8RawOpt = RT_H2N_U32(pCfg->expirationPeriod()); + opt.cbRawOpt = sizeof(RTNETADDRIPV4); + + extra.push_back(opt); + + processParameterReqList(client, pu8ReqList, cReqList, extra); + + return doReply(client, extra); +} + +/** + * Network manager creates DHCPACK + */ +int NetworkManager::ack(const Client& client, uint32_t u32Xid, + uint8_t *pu8ReqList, int cReqList) +{ + RTNETADDRIPV4 address; + + prepareReplyPacket4Client(client, u32Xid); + + Lease l = client.lease(); + address = l.getAddress(); + m->BootPReplyMsg.BootPHeader.bp_ciaddr = address; + + + /* rfc2131 4.3.1 is about DHCPDISCOVER and this value is equal to ciaddr from + * DHCPREQUEST or 0 ... + * XXX: Using addressHint is not correct way to initialize [cy]iaddress... + */ + m->BootPReplyMsg.BootPHeader.bp_ciaddr = address; + m->BootPReplyMsg.BootPHeader.bp_yiaddr = address; + + Assert(m->BootPReplyMsg.BootPHeader.bp_yiaddr.u); + + /* options: + * - IP address lease time (if DHCPREQUEST) + * - message type + * - server identifier + */ + RawOption opt; + RT_ZERO(opt); + + std::vector<RawOption> extra; + opt.u8OptId = RTNET_DHCP_OPT_MSG_TYPE; + opt.au8RawOpt[0] = RTNET_DHCP_MT_ACK; + opt.cbRawOpt = 1; + extra.push_back(opt); + + /* + * XXX: lease time should be conditional. If on dhcprequest then tim should be provided, + * else on dhcpinform it mustn't. + */ + opt.u8OptId = RTNET_DHCP_OPT_LEASE_TIME; + *(uint32_t *)opt.au8RawOpt = RT_H2N_U32(l.getExpiration()); + opt.cbRawOpt = sizeof(RTNETADDRIPV4); + extra.push_back(opt); + + processParameterReqList(client, pu8ReqList, cReqList, extra); + + return doReply(client, extra); +} + +/** + * Network manager creates DHCPNAK + */ +int NetworkManager::nak(const Client& client, uint32_t u32Xid) +{ + + Lease l = client.lease(); + if (l == Lease::NullLease) + return VERR_INTERNAL_ERROR; + + prepareReplyPacket4Client(client, u32Xid); + + /* this field filed in prepareReplyPacket4Session, and + * RFC 2131 require to have it zero fo NAK. + */ + m->BootPReplyMsg.BootPHeader.bp_yiaddr.u = 0; + + /* options: + * - message type (if DHCPREQUEST) + * - server identifier + */ + RawOption opt; + std::vector<RawOption> extra; + + opt.u8OptId = RTNET_DHCP_OPT_MSG_TYPE; + opt.au8RawOpt[0] = RTNET_DHCP_MT_NAC; + opt.cbRawOpt = 1; + extra.push_back(opt); + + return doReply(client, extra); +} + +/** + * + */ +int NetworkManager::prepareReplyPacket4Client(const Client& client, uint32_t u32Xid) +{ + RT_ZERO(m->BootPReplyMsg); + + m->BootPReplyMsg.BootPHeader.bp_op = RTNETBOOTP_OP_REPLY; + m->BootPReplyMsg.BootPHeader.bp_htype = RTNET_ARP_ETHER; + m->BootPReplyMsg.BootPHeader.bp_hlen = sizeof(RTMAC); + m->BootPReplyMsg.BootPHeader.bp_hops = 0; + m->BootPReplyMsg.BootPHeader.bp_xid = u32Xid; + m->BootPReplyMsg.BootPHeader.bp_secs = 0; + /* XXX: bp_flags should be processed specially */ + m->BootPReplyMsg.BootPHeader.bp_flags = 0; + m->BootPReplyMsg.BootPHeader.bp_ciaddr.u = 0; + m->BootPReplyMsg.BootPHeader.bp_giaddr.u = 0; + + m->BootPReplyMsg.BootPHeader.bp_chaddr.Mac = client.getMacAddress(); + + const Lease l = client.lease(); + m->BootPReplyMsg.BootPHeader.bp_yiaddr = l.getAddress(); + m->BootPReplyMsg.BootPHeader.bp_siaddr.u = 0; + + + m->BootPReplyMsg.BootPHeader.bp_vend.Dhcp.dhcp_cookie = RT_H2N_U32_C(RTNET_DHCP_COOKIE); + + memset(&m->BootPReplyMsg.BootPHeader.bp_vend.Dhcp.dhcp_opts[0], + '\0', + RTNET_DHCP_OPT_SIZE); + + return VINF_SUCCESS; +} + + +int NetworkManager::doReply(const Client& client, const std::vector<RawOption>& extra) +{ + int rc; + + /* + Options.... + */ + VBoxNetDhcpWriteCursor Cursor(&m->BootPReplyMsg.BootPHeader, RTNET_DHCP_NORMAL_SIZE); + + /* The basics */ + + Cursor.optIPv4Addr(RTNET_DHCP_OPT_SERVER_ID, m->m_OurAddress); + + const Lease l = client.lease(); + const std::map<uint8_t, RawOption>& options = l.options(); + + for(std::vector<RawOption>::const_iterator it = extra.begin(); + it != extra.end(); ++it) + { + if (!Cursor.begin(it->u8OptId, it->cbRawOpt)) + break; + Cursor.put(it->au8RawOpt, it->cbRawOpt); + + } + + for(std::map<uint8_t, RawOption>::const_iterator it = options.begin(); + it != options.end(); ++it) + { + if (!Cursor.begin(it->second.u8OptId, it->second.cbRawOpt)) + break; + Cursor.put(it->second.au8RawOpt, it->second.cbRawOpt); + + } + + Cursor.optEnd(); + + /* + */ +#if 0 + /** @todo need to see someone set this flag to check that it's correct. */ + if (!(pDhcpMsg->bp_flags & RTNET_DHCP_FLAGS_NO_BROADCAST)) + { + rc = VBoxNetUDPUnicast(m_pSession, + m_hIf, + m_pIfBuf, + m_OurAddress, + &m_OurMac, + RTNETIPV4_PORT_BOOTPS, /* sender */ + IPv4AddrBrdCast, + &BootPReplyMsg.BootPHeader->bp_chaddr.Mac, + RTNETIPV4_PORT_BOOTPC, /* receiver */ + &BootPReplyMsg, cbBooPReplyMsg); + } + else +#endif + rc = m->m_service->hlpUDPBroadcast(RTNETIPV4_PORT_BOOTPS, /* sender */ + RTNETIPV4_PORT_BOOTPC, + &m->BootPReplyMsg, + RTNET_DHCP_NORMAL_SIZE); + + AssertRCReturn(rc,rc); + + return VINF_SUCCESS; +} + + +int NetworkManager::processParameterReqList(const Client& client, const uint8_t *pu8ReqList, + int cReqList, std::vector<RawOption>& extra) +{ + const Lease l = client.lease(); + + const NetworkConfigEntity *pNetCfg = l.getConfig(); + + /* request parameter list */ + RawOption opt; + bool fIgnore; + uint8_t u8Req; + for (int idxParam = 0; idxParam < cReqList; ++idxParam) + { + fIgnore = false; + RT_ZERO(opt); + u8Req = opt.u8OptId = pu8ReqList[idxParam]; + + switch(u8Req) + { + case RTNET_DHCP_OPT_SUBNET_MASK: + ((PRTNETADDRIPV4)opt.au8RawOpt)->u = pNetCfg->netmask().u; + opt.cbRawOpt = sizeof(RTNETADDRIPV4); + + break; + + case RTNET_DHCP_OPT_ROUTERS: + case RTNET_DHCP_OPT_DNS: + { + const Ipv4AddressContainer lst = + g_ConfigurationManager->getAddressList(u8Req); + PRTNETADDRIPV4 pAddresses = (PRTNETADDRIPV4)&opt.au8RawOpt[0]; + + for (Ipv4AddressConstIterator it = lst.begin(); + it != lst.end(); + ++it) + { + *pAddresses = (*it); + pAddresses++; + opt.cbRawOpt += sizeof(RTNETADDRIPV4); + } + + if (lst.empty()) + fIgnore = true; + } + break; + case RTNET_DHCP_OPT_DOMAIN_NAME: + { + std::string domainName = g_ConfigurationManager->getString(u8Req); + if (domainName == g_ConfigurationManager->m_noString) + { + fIgnore = true; + break; + } + + char *pszDomainName = (char *)&opt.au8RawOpt[0]; + + strcpy(pszDomainName, domainName.c_str()); + opt.cbRawOpt = domainName.length(); + } + break; + default: + Log(("opt: %d is ignored\n", u8Req)); + fIgnore = true; + break; + } + + if (!fIgnore) + extra.push_back(opt); + + } + + return VINF_SUCCESS; +} + +/* Client */ +Client::Client() +{ + m = SharedPtr<ClientData>(); +} + + +void Client::initWithMac(const RTMAC& mac) +{ + m = SharedPtr<ClientData>(new ClientData()); + m->m_mac = mac; +} + + +bool Client::operator== (const RTMAC& mac) const +{ + return (m.get() && m->m_mac == mac); +} + + +const RTMAC& Client::getMacAddress() const +{ + return m->m_mac; +} + + +Lease Client::lease() +{ + if (!m.get()) return Lease::NullLease; + + if (m->fHasLease) + return Lease(*this); + else + return Lease::NullLease; +} + + +const Lease Client::lease() const +{ + return const_cast<Client *>(this)->lease(); +} + + +Client::Client(ClientData *data):m(SharedPtr<ClientData>(data)){} + +/* Lease */ +Lease::Lease() +{ + m = SharedPtr<ClientData>(); +} + + +Lease::Lease (const Client& c) +{ + m = SharedPtr<ClientData>(c.m); + if ( !m->fHasLease + || ( isExpired() + && !isInBindingPhase())) + { + m->fHasLease = true; + m->fBinding = true; + phaseStart(RTTimeMilliTS()); + } +} + + +bool Lease::isExpired() const +{ + AssertPtrReturn(m.get(), false); + + if (!m->fBinding) + return (ASMDivU64ByU32RetU32(RTTimeMilliTS() - m->u64TimestampLeasingStarted, 1000) + > m->u32LeaseExpirationPeriod); + else + return (ASMDivU64ByU32RetU32(RTTimeMilliTS() - m->u64TimestampBindingStarted, 1000) + > m->u32BindExpirationPeriod); +} + + +void Lease::expire() +{ + /* XXX: TODO */ +} + + +void Lease::phaseStart(uint64_t u64Start) +{ + if (m->fBinding) + m->u64TimestampBindingStarted = u64Start; + else + m->u64TimestampLeasingStarted = u64Start; +} + + +void Lease::bindingPhase(bool fOnOff) +{ + m->fBinding = fOnOff; +} + + +bool Lease::isInBindingPhase() const +{ + return m->fBinding; +} + + +uint64_t Lease::issued() const +{ + return m->u64TimestampLeasingStarted; +} + + +void Lease::setExpiration(uint32_t exp) +{ + if (m->fBinding) + m->u32BindExpirationPeriod = exp; + else + m->u32LeaseExpirationPeriod = exp; +} + + +uint32_t Lease::getExpiration() const +{ + if (m->fBinding) + return m->u32BindExpirationPeriod; + else + return m->u32LeaseExpirationPeriod; +} + + +RTNETADDRIPV4 Lease::getAddress() const +{ + return m->m_address; +} + + +void Lease::setAddress(RTNETADDRIPV4 address) +{ + m->m_address = address; +} + + +const NetworkConfigEntity *Lease::getConfig() const +{ + return m->pCfg; +} + + +void Lease::setConfig(NetworkConfigEntity *pCfg) +{ + m->pCfg = pCfg; +} + + +const MapOptionId2RawOption& Lease::options() const +{ + return m->options; +} + + +Lease::Lease(ClientData *pd):m(SharedPtr<ClientData>(pd)){} + + +bool Lease::toXML(xml::ElementNode *node) const +{ + bool valueAddition = node->setAttribute(tagXMLLeaseAttributeMac.c_str(), com::Utf8StrFmt("%RTmac", &m->m_mac)); + if (!valueAddition) return false; + + valueAddition = node->setAttribute(tagXMLLeaseAttributeNetwork.c_str(), com::Utf8StrFmt("%RTnaipv4", m->m_network)); + if (!valueAddition) return false; + + xml::ElementNode *address = node->createChild(tagXMLLeaseAddress.c_str()); + if (!address) return false; + + valueAddition = address->setAttribute(tagXMLAddressAttributeValue.c_str(), com::Utf8StrFmt("%RTnaipv4", m->m_address)); + if (!valueAddition) return false; + + xml::ElementNode *time = node->createChild(tagXMLLeaseTime.c_str()); + if (!time) return false; + + valueAddition = time->setAttribute(tagXMLTimeAttributeIssued.c_str(), + m->u64TimestampLeasingStarted); + if (!valueAddition) return false; + + valueAddition = time->setAttribute(tagXMLTimeAttributeExpiration.c_str(), + m->u32LeaseExpirationPeriod); + if (!valueAddition) return false; + + return true; +} + + +bool Lease::fromXML(const xml::ElementNode *node) +{ + com::Utf8Str mac; + bool valueExists = node->getAttributeValue(tagXMLLeaseAttributeMac.c_str(), mac); + if (!valueExists) return false; + int rc = RTNetStrToMacAddr(mac.c_str(), &m->m_mac); + if (RT_FAILURE(rc)) return false; + + com::Utf8Str network; + valueExists = node->getAttributeValue(tagXMLLeaseAttributeNetwork.c_str(), network); + if (!valueExists) return false; + rc = RTNetStrToIPv4Addr(network.c_str(), &m->m_network); + if (RT_FAILURE(rc)) return false; + + /* Address */ + const xml::ElementNode *address = node->findChildElement(tagXMLLeaseAddress.c_str()); + if (!address) return false; + com::Utf8Str addressValue; + valueExists = address->getAttributeValue(tagXMLAddressAttributeValue.c_str(), addressValue); + if (!valueExists) return false; + rc = RTNetStrToIPv4Addr(addressValue.c_str(), &m->m_address); + + /* Time */ + const xml::ElementNode *time = node->findChildElement(tagXMLLeaseTime.c_str()); + if (!time) return false; + + valueExists = time->getAttributeValue(tagXMLTimeAttributeIssued.c_str(), + &m->u64TimestampLeasingStarted); + if (!valueExists) return false; + m->fBinding = false; + + valueExists = time->getAttributeValue(tagXMLTimeAttributeExpiration.c_str(), + &m->u32LeaseExpirationPeriod); + if (!valueExists) return false; + + m->fHasLease = true; + return true; +} + + +const Lease Lease::NullLease; + +const Client Client::NullClient; diff --git a/src/VBox/NetworkServices/DHCP/Config.h b/src/VBox/NetworkServices/DHCP/Config.h new file mode 100644 index 00000000..7d20a72b --- /dev/null +++ b/src/VBox/NetworkServices/DHCP/Config.h @@ -0,0 +1,829 @@ +/* $Id: Config.h $ */ +/** + * This file contains declarations of DHCP config. + */ + +#ifndef _CONFIG_H_ +#define _CONFIG_H_ + +#include <iprt/asm-math.h> +#include <iprt/cpp/utils.h> +#include <VBox/com/string.h> + +#include "../NetLib/cpp/utils.h" + + +static bool operator > (const RTNETADDRIPV4& a, const RTNETADDRIPV4& b) +{ + return (b < a); +} + + +class RawOption +{ +public: + RawOption() + { + RT_ZERO(*this); + } + uint8_t u8OptId; + uint8_t cbRawOpt; + uint8_t au8RawOpt[255]; +}; + +class ClientData; +class Client; +class Lease; +class BaseConfigEntity; + +class NetworkConfigEntity; +class HostConfigEntity; +class ClientMatchCriteria; +class ConfigurationManager; + +/* + * it's a basic representation of + * of out undestanding what client is + * XXX: Client might sends Option 61 (RFC2132 9.14 "Client-identifier") signalling + * that we may identify it in special way + * + * XXX: Client might send Option 60 (RFC2132 9.13 "Vendor class undentifier") + * in response it's expected server sends Option 43 (RFC2132 8.4. "Vendor Specific Information") + */ +class Client +{ + friend class Lease; + friend class ConfigurationManager; + + public: + Client(); + void initWithMac(const RTMAC& mac); + bool operator== (const RTMAC& mac) const; + const RTMAC& getMacAddress() const; + + /** Dumps client query */ + void dump(); + + Lease lease(); + const Lease lease() const; + + public: + static const Client NullClient; + + private: + Client(ClientData *); + SharedPtr<ClientData> m; +}; + + +bool operator== (const Lease&, const Lease&); +bool operator!= (const Lease&, const Lease&); +bool operator< (const Lease&, const Lease&); + + +typedef std::map<uint8_t, RawOption> MapOptionId2RawOption; +typedef MapOptionId2RawOption::iterator MapOptionId2RawOptionIterator; +typedef MapOptionId2RawOption::const_iterator MapOptionId2RawOptionConstIterator; +typedef MapOptionId2RawOption::value_type MapOptionId2RawOptionValue; + +namespace xml { + class ElementNode; +} + +class Lease +{ + friend class Client; + friend bool operator== (const Lease&, const Lease&); + //friend int ConfigurationManager::loadFromFile(const std::string&); + friend class ConfigurationManager; + + public: + Lease(); + Lease(const Client&); + + bool isExpired() const; + void expire(); + + /* Depending on phase *Expiration and phaseStart initialize different values. */ + void bindingPhase(bool); + void phaseStart(uint64_t u64Start); + bool isInBindingPhase() const; + /* returns 0 if in binding state */ + uint64_t issued() const; + + void setExpiration(uint32_t); + uint32_t getExpiration() const; + + RTNETADDRIPV4 getAddress() const; + void setAddress(RTNETADDRIPV4); + + const NetworkConfigEntity *getConfig() const; + void setConfig(NetworkConfigEntity *); + + const MapOptionId2RawOption& options() const; + + bool toXML(xml::ElementNode *) const; + bool fromXML(const xml::ElementNode *); + + public: + static const Lease NullLease; + + private: + Lease(ClientData *); + SharedPtr<ClientData> m; +}; + + +typedef std::vector<Client> VecClient; +typedef VecClient::iterator VecClientIterator; +typedef VecClient::const_iterator VecClientConstIterator; + +typedef std::vector<RTMAC> MacAddressContainer; +typedef MacAddressContainer::iterator MacAddressIterator; + +typedef std::vector<RTNETADDRIPV4> Ipv4AddressContainer; +typedef Ipv4AddressContainer::iterator Ipv4AddressIterator; +typedef Ipv4AddressContainer::const_iterator Ipv4AddressConstIterator; + +typedef std::map<Lease, RTNETADDRIPV4> MapLease2Ip4Address; +typedef MapLease2Ip4Address::iterator MapLease2Ip4AddressIterator; +typedef MapLease2Ip4Address::const_iterator MapLease2Ip4AddressConstIterator; +typedef MapLease2Ip4Address::value_type MapLease2Ip4AddressPair; + +/** + * + */ +class ClientMatchCriteria +{ + public: + virtual bool check(const Client&) const {return false;}; +}; + + +class ORClientMatchCriteria: ClientMatchCriteria +{ + ClientMatchCriteria* m_left; + ClientMatchCriteria* m_right; + ORClientMatchCriteria(ClientMatchCriteria *left, ClientMatchCriteria *right) + { + m_left = left; + m_right = right; + } + + virtual bool check(const Client& client) const + { + return (m_left->check(client) || m_right->check(client)); + } +}; + + +class ANDClientMatchCriteria: ClientMatchCriteria +{ +public: + ANDClientMatchCriteria(ClientMatchCriteria *left, ClientMatchCriteria *right) + { + m_left = left; + m_right = right; + } + + virtual bool check(const Client& client) const + { + return (m_left->check(client) && m_right->check(client)); + } + +private: + ClientMatchCriteria* m_left; + ClientMatchCriteria* m_right; + +}; + + +class AnyClientMatchCriteria: public ClientMatchCriteria +{ +public: + virtual bool check(const Client&) const + { + return true; + } +}; + + +class MACClientMatchCriteria: public ClientMatchCriteria +{ +public: + MACClientMatchCriteria(const RTMAC& mac):m_mac(mac){} + + virtual bool check(const Client& client) const; + +private: + RTMAC m_mac; +}; + + +#if 0 +/* XXX: Later */ +class VmSlotClientMatchCriteria: public ClientMatchCriteria +{ + str::string VmName; + uint8_t u8Slot; + virtual bool check(const Client& client) + { + return ( client.VmName == VmName + && ( u8Slot == (uint8_t)~0 /* any */ + || client.u8Slot == u8Slot)); + } +}; +#endif + + +/* Option 60 */ +class ClassClientMatchCriteria: ClientMatchCriteria{}; +/* Option 61 */ +class ClientIdentifierMatchCriteria: ClientMatchCriteria{}; + + +class BaseConfigEntity +{ + public: + BaseConfigEntity(const ClientMatchCriteria *criteria = NULL, + int matchingLevel = 0) + : m_criteria(criteria), + m_MatchLevel(matchingLevel){}; + virtual ~BaseConfigEntity(){}; + /* XXX */ + int add(BaseConfigEntity *cfg) + { + m_children.push_back(cfg); + return 0; + } + + /* Should return how strong matching */ + virtual int match(Client& client, BaseConfigEntity **cfg); + virtual uint32_t expirationPeriod() const = 0; + + protected: + const ClientMatchCriteria *m_criteria; + int m_MatchLevel; + std::vector<BaseConfigEntity *> m_children; +}; + + +class NullConfigEntity: public BaseConfigEntity +{ + public: + NullConfigEntity(){} + virtual ~NullConfigEntity(){} + int add(BaseConfigEntity *) const { return 0;} + virtual uint32_t expirationPeriod() const {return 0;} +}; + + +class ConfigEntity: public BaseConfigEntity +{ + public: + /* range */ + /* match conditions */ + ConfigEntity(std::string& name, + const BaseConfigEntity *cfg, + const ClientMatchCriteria *criteria, + int matchingLevel = 0): + BaseConfigEntity(criteria, matchingLevel), + m_name(name), + m_parentCfg(cfg), + m_u32ExpirationPeriod(0) + { + unconst(m_parentCfg)->add(this); + } + + virtual uint32_t expirationPeriod() const + { + if (!m_u32ExpirationPeriod) + return m_parentCfg->expirationPeriod(); + else + return m_u32ExpirationPeriod; + } + + /* XXX: private:*/ + std::string m_name; + const BaseConfigEntity *m_parentCfg; + uint32_t m_u32ExpirationPeriod; +}; + + +/** + * Network specific entries + */ +class NetworkConfigEntity:public ConfigEntity +{ +public: + /* Address Pool matching with network declaration */ + NetworkConfigEntity(std::string name, + const BaseConfigEntity *cfg, + const ClientMatchCriteria *criteria, + int matchlvl, + const RTNETADDRIPV4& networkID, + const RTNETADDRIPV4& networkMask, + const RTNETADDRIPV4& lowerIP, + const RTNETADDRIPV4& upperIP): + ConfigEntity(name, cfg, criteria, matchlvl), + m_NetworkID(networkID), + m_NetworkMask(networkMask), + m_UpperIP(upperIP), + m_LowerIP(lowerIP) + { + }; + + NetworkConfigEntity(std::string name, + const BaseConfigEntity *cfg, + const ClientMatchCriteria *criteria, + const RTNETADDRIPV4& networkID, + const RTNETADDRIPV4& networkMask): + ConfigEntity(name, cfg, criteria, 5), + m_NetworkID(networkID), + m_NetworkMask(networkMask) + { + m_UpperIP.u = m_NetworkID.u | (~m_NetworkMask.u); + m_LowerIP.u = m_NetworkID.u; + }; + + const RTNETADDRIPV4& upperIp() const {return m_UpperIP;} + const RTNETADDRIPV4& lowerIp() const {return m_LowerIP;} + const RTNETADDRIPV4& networkId() const {return m_NetworkID;} + const RTNETADDRIPV4& netmask() const {return m_NetworkMask;} + + private: + RTNETADDRIPV4 m_NetworkID; + RTNETADDRIPV4 m_NetworkMask; + RTNETADDRIPV4 m_UpperIP; + RTNETADDRIPV4 m_LowerIP; +}; + + +/** + * Host specific entry + * Address pool is contains one element + */ +class HostConfigEntity: public NetworkConfigEntity +{ +public: + HostConfigEntity(const RTNETADDRIPV4& addr, + std::string name, + const NetworkConfigEntity *cfg, + const ClientMatchCriteria *criteria): + NetworkConfigEntity(name, + static_cast<const ConfigEntity*>(cfg), criteria, 10, + cfg->networkId(), cfg->netmask(), addr, addr) + { + /* upper addr == lower addr */ + } +}; + +class RootConfigEntity: public NetworkConfigEntity +{ +public: + RootConfigEntity(std::string name, uint32_t expirationPeriod); + virtual ~RootConfigEntity(){}; +}; + + +#if 0 +/** + * Shared regions e.g. some of configured networks declarations + * are cover each other. + * XXX: Shared Network is join on Network config entities with possible + * overlaps in address pools. for a moment we won't configure and use them them + */ +class SharedNetworkConfigEntity: public NetworkEntity +{ +public: + SharedNetworkConfigEntity(){} + int match(const Client& client) const { return m_criteria.match(client)? 3 : 0;} + + SharedNetworkConfigEntity(NetworkEntity& network) + { + Networks.push_back(network); + } + virtual ~SharedNetworkConfigEntity(){} + + std::vector<NetworkConfigEntity> Networks; +}; +#endif + +class ConfigurationManager +{ +public: + static ConfigurationManager* getConfigurationManager(); + static int extractRequestList(PCRTNETBOOTP pDhcpMsg, size_t cbDhcpMsg, RawOption& rawOpt); + + int loadFromFile(const com::Utf8Str&); + int saveToFile(); + /** + * + */ + Client getClientByDhcpPacket(const RTNETBOOTP *pDhcpMsg, size_t cbDhcpMsg); + + /** + * XXX: it's could be done on DHCPOFFER or on DHCPACK (rfc2131 gives freedom here + * 3.1.2, what is strict that allocation should do address check before real + * allocation)... + */ + Lease allocateLease4Client(const Client& client, PCRTNETBOOTP pDhcpMsg, size_t cbDhcpMsg); + + /** + * We call this before DHCPACK sent and after DHCPREQUEST received ... + * when requested configuration is acceptable. + */ + int commitLease4Client(Client& client); + + /** + * Expires client lease. + */ + int expireLease4Client(Client& client); + + static int findOption(uint8_t uOption, PCRTNETBOOTP pDhcpMsg, size_t cbDhcpMsg, RawOption& opt); + + NetworkConfigEntity *addNetwork(NetworkConfigEntity *pCfg, + const RTNETADDRIPV4& networkId, + const RTNETADDRIPV4& netmask, + RTNETADDRIPV4& UpperAddress, + RTNETADDRIPV4& LowerAddress); + + HostConfigEntity *addHost(NetworkConfigEntity*, const RTNETADDRIPV4&, ClientMatchCriteria*); + int addToAddressList(uint8_t u8OptId, RTNETADDRIPV4& address); + int flushAddressList(uint8_t u8OptId); + int setString(uint8_t u8OptId, const std::string& str); + const std::string& getString(uint8_t u8OptId); + const Ipv4AddressContainer& getAddressList(uint8_t u8OptId); + +private: + ConfigurationManager():m(NULL){} + void init(); + + ~ConfigurationManager(); + bool isAddressTaken(const RTNETADDRIPV4& addr, Lease& lease); + bool isAddressTaken(const RTNETADDRIPV4& addr); + +public: + /* nulls */ + const Ipv4AddressContainer m_empty; + const std::string m_noString; + +private: + struct Data; + Data *m; +}; + + +class NetworkManager +{ +public: + static NetworkManager *getNetworkManager(); + + const RTNETADDRIPV4& getOurAddress() const; + const RTNETADDRIPV4& getOurNetmask() const; + const RTMAC& getOurMac() const; + + void setOurAddress(const RTNETADDRIPV4& aAddress); + void setOurNetmask(const RTNETADDRIPV4& aNetmask); + void setOurMac(const RTMAC& aMac); + + 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 setService(const VBoxNetHlpUDPService *); +private: + NetworkManager(); + ~NetworkManager(); + + int offer4Client(const Client& lease, uint32_t u32Xid, uint8_t *pu8ReqList, int cReqList); + int ack(const Client& lease, uint32_t u32Xid, uint8_t *pu8ReqList, int cReqList); + int nak(const Client& lease, uint32_t u32Xid); + + int prepareReplyPacket4Client(const Client& client, uint32_t u32Xid); + int doReply(const Client& client, const std::vector<RawOption>& extra); + int processParameterReqList(const Client& client, const uint8_t *pu8ReqList, int cReqList, std::vector<RawOption>& extra); + +private: + struct Data; + Data *m; + +}; + + +extern const ClientMatchCriteria *g_AnyClient; +extern RootConfigEntity *g_RootConfig; +extern const NullConfigEntity *g_NullConfig; + +/** + * 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(); + } +}; + +#endif diff --git a/src/VBox/NetworkServices/DHCP/Makefile.kmk b/src/VBox/NetworkServices/DHCP/Makefile.kmk index f04b6285..623b9d92 100644 --- a/src/VBox/NetworkServices/DHCP/Makefile.kmk +++ b/src/VBox/NetworkServices/DHCP/Makefile.kmk @@ -40,17 +40,18 @@ VBoxNetDHCPHardened_NAME = VBoxNetDHCP # # VBoxNetDHCP # -VBoxNetDHCP_TEMPLATE = -VBoxNetDHCP_TEMPLATE := VBOXR3$(if-expr defined(VBOX_WITH_HARDENING),,EXE) +VBoxNetDHCP_TEMPLATE := VBOX$(if-expr defined(VBOX_WITH_HARDENING),MAINDLL,MAINCLIENTEXE) VBoxNetDHCP_SOURCES = \ VBoxNetDHCP.cpp \ + Config.cpp \ + NetworkManagerDhcp.cpp \ ../NetLib/VBoxNetIntIf.cpp \ ../NetLib/VBoxNetUDP.cpp \ - ../NetLib/VBoxNetARP.cpp + ../NetLib/VBoxNetARP.cpp \ + ../NetLib/VBoxNetBaseService.cpp \ + ../NetLib/ComHostUtils.cpp VBoxNetDHCP_LIBS = \ $(LIB_RUNTIME) VBoxNetDHCP_LDFLAGS.win = /SUBSYSTEM:windows - include $(FILE_KBUILD_SUB_FOOTER) - diff --git a/src/VBox/NetworkServices/DHCP/NetworkManagerDhcp.cpp b/src/VBox/NetworkServices/DHCP/NetworkManagerDhcp.cpp new file mode 100644 index 00000000..415e635c --- /dev/null +++ b/src/VBox/NetworkServices/DHCP/NetworkManagerDhcp.cpp @@ -0,0 +1,188 @@ +/* $Id: NetworkManagerDhcp.cpp $ */ +/** @file + * NetworkManagerDhcp - Network Manager part handling Dhcp. + */ + +/* + * Copyright (C) 2013 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 * +*******************************************************************************/ +#include <iprt/asm.h> +#include <iprt/cdefs.h> +#include <iprt/getopt.h> +#include <iprt/net.h> +#include <iprt/param.h> +#include <iprt/path.h> +#include <iprt/stream.h> +#include <iprt/time.h> +#include <iprt/string.h> + +#include "../NetLib/shared_ptr.h" + +#include <vector> +#include <list> +#include <string> +#include <map> + +#include <VBox/sup.h> +#include <VBox/intnet.h> + +#define BASE_SERVICES_ONLY +#include "../NetLib/VBoxNetBaseService.h" +#include "Config.h" +#include "ClientDataInt.h" + +/** + * The client is requesting an offer. + * + * @returns true. + * + * @param pDhcpMsg The message. + * @param cb The message size. + */ +bool NetworkManager::handleDhcpReqDiscover(PCRTNETBOOTP pDhcpMsg, size_t cb) +{ + RawOption opt; + memset(&opt, 0, sizeof(RawOption)); + /* 1. Find client */ + ConfigurationManager *confManager = ConfigurationManager::getConfigurationManager(); + Client client = confManager->getClientByDhcpPacket(pDhcpMsg, cb); + + /* 2. Find/Bind lease for client */ + Lease lease = confManager->allocateLease4Client(client, pDhcpMsg, cb); + AssertReturn(lease != Lease::NullLease, VINF_SUCCESS); + + int rc = ConfigurationManager::extractRequestList(pDhcpMsg, cb, opt); + + /* 3. Send of offer */ + + lease.bindingPhase(true); + lease.phaseStart(RTTimeMilliTS()); + lease.setExpiration(300); /* 3 min. */ + offer4Client(client, pDhcpMsg->bp_xid, opt.au8RawOpt, opt.cbRawOpt); + + return VINF_SUCCESS; +} + + +/** + * The client is requesting an offer. + * + * @returns true. + * + * @param pDhcpMsg The message. + * @param cb The message size. + */ +bool NetworkManager::handleDhcpReqRequest(PCRTNETBOOTP pDhcpMsg, size_t cb) +{ + ConfigurationManager *confManager = ConfigurationManager::getConfigurationManager(); + + /* 1. find client */ + Client client = confManager->getClientByDhcpPacket(pDhcpMsg, cb); + + /* 2. find bound lease */ + Lease l = client.lease(); + if (l != Lease::NullLease) + { + + if (l.isExpired()) + { + /* send client to INIT state */ + Client c(client); + nak(client, pDhcpMsg->bp_xid); + confManager->expireLease4Client(c); + return true; + } + else { + /* XXX: Validate request */ + RawOption opt; + RT_ZERO(opt); + + Client c(client); + int rc = confManager->commitLease4Client(c); + AssertRCReturn(rc, false); + + rc = ConfigurationManager::extractRequestList(pDhcpMsg, cb, opt); + AssertRCReturn(rc, false); + + ack(client, pDhcpMsg->bp_xid, opt.au8RawOpt, opt.cbRawOpt); + } + } + else + { + nak(client, pDhcpMsg->bp_xid); + } + return true; +} + + +/** + * The client is declining an offer we've made. + * + * @returns true. + * + * @param pDhcpMsg The message. + * @param cb The message size. + */ +bool NetworkManager::handleDhcpReqDecline(PCRTNETBOOTP, size_t) +{ + /** @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. + */ +bool NetworkManager::handleDhcpReqRelease(PCRTNETBOOTP, size_t) +{ + /** @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; +} + diff --git a/src/VBox/NetworkServices/DHCP/VBoxNetDHCP.cpp b/src/VBox/NetworkServices/DHCP/VBoxNetDHCP.cpp index 2ea54b66..6496b8ac 100644 --- a/src/VBox/NetworkServices/DHCP/VBoxNetDHCP.cpp +++ b/src/VBox/NetworkServices/DHCP/VBoxNetDHCP.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2009 Oracle Corporation + * 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; @@ -24,6 +24,16 @@ /******************************************************************************* * Header Files * *******************************************************************************/ +#include <VBox/com/com.h> +#include <VBox/com/listeners.h> +#include <VBox/com/string.h> +#include <VBox/com/Guid.h> +#include <VBox/com/array.h> +#include <VBox/com/ErrorInfo.h> +#include <VBox/com/errorprint.h> +#include <VBox/com/EventQueue.h> +#include <VBox/com/VirtualBox.h> + #include <iprt/alloca.h> #include <iprt/buildconfig.h> #include <iprt/err.h> @@ -43,383 +53,102 @@ #include <VBox/vmm/vmm.h> #include <VBox/version.h> + #include "../NetLib/VBoxNetLib.h" +#include "../NetLib/shared_ptr.h" #include <vector> +#include <list> #include <string> +#include <map> + +#include "../NetLib/VBoxNetBaseService.h" +#include "../NetLib/utils.h" #ifdef RT_OS_WINDOWS /* WinMain */ # include <Windows.h> # include <stdlib.h> +# ifdef INET_ADDRSTRLEN +/* On Windows INET_ADDRSTRLEN defined as 22 Ws2ipdef.h, because it include port number */ +# undef INET_ADDRSTRLEN +# endif +# define INET_ADDRSTRLEN 16 +#else +# include <netinet/in.h> #endif +#include "Config.h" /******************************************************************************* * 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 +class VBoxNetDhcp: public VBoxNetBaseService, public NATNetworkEventAdapter { public: VBoxNetDhcp(); virtual ~VBoxNetDhcp(); - int parseArgs(int argc, char **argv); - int tryGoOnline(void); - int run(void); + int init(); + void usage(void) { /* XXX: document options */ }; + int parseOpt(int rc, const RTGETOPTUNION& getOptVal); + int processFrame(void *, size_t) {return VERR_IGNORED; }; + int processGSO(PCPDMNETWORKGSO, size_t) {return VERR_IGNORED; }; + int processUDP(void *, size_t); 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); +private: + int initNoMain(); + int initWithMain(); + HRESULT HandleEvent(VBoxEventType_T aEventType, IEvent *pEvent); + int fetchAndUpdateDnsInfo(); + protected: - /** @name The server configuration data members. + /** @name The DHCP server specific configuration data members. * @{ */ - std::string m_Name; - std::string m_Network; - std::string m_TrunkName; - INTNETTRUNKTYPE m_enmTrunkType; - RTMAC m_MacAddress; - RTNETADDRIPV4 m_Ipv4Address; + /* + * XXX: what was the plan? SQL3 or plain text file? + * How it will coexists with managment from VBoxManagement, who should manage db + * in that case (VBoxManage, VBoxSVC ???) + */ std::string m_LeaseDBName; + /** @} */ - /** The current configs. */ - std::vector<VBoxNetDhcpCfg *> m_Cfgs; + /* corresponding dhcp server description in Main */ + ComPtr<IDHCPServer> m_DhcpServer; - /** The current leases. */ - std::vector<VBoxNetDhcpLease> m_Leases; + ComPtr<INATNetwork> m_NATNetwork; - /** @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. */ - /** @} */ + /** Listener for Host DNS changes */ + ComPtr<NATNetworkListenerImpl> m_vboxListener; + /* + * We will ignore cmd line parameters IFF there will be some DHCP specific arguments + * otherwise all paramters will come from Main. + */ + bool m_fIgnoreCmdLineParameters; + + /* + * -b -n 10.0.1.2 -m 255.255.255.0 -> to the list processing in + */ + typedef struct + { + char Key; + std::string strValue; + } CMDLNPRM; + std::list<CMDLNPRM> CmdParameterll; + typedef std::list<CMDLNPRM>::iterator CmdParameterIterator; /** @name Debug stuff * @{ */ @@ -432,130 +161,68 @@ protected: }; -/******************************************************************************* -* 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) +static inline int configGetBoundryAddress(const ComDhcpServerPtr& dhcp, bool fUpperBoundry, RTNETADDRIPV4& boundryAddress) { - m_enmState = kState_Offer; - m_xid = xid; - RTTimeNow(&m_ExpireTime); - RTTimeSpecAddSeconds(&m_ExpireTime, 60); -} + boundryAddress.u = INADDR_ANY; + HRESULT hrc; + com::Bstr strAddress; + if (fUpperBoundry) + hrc = dhcp->COMGETTER(UpperIP)(strAddress.asOutParam()); + else + hrc = dhcp->COMGETTER(LowerIP)(strAddress.asOutParam()); + AssertComRCReturn(hrc, VERR_INTERNAL_ERROR); -/** - * 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... */ + return RTNetStrToIPv4Addr(com::Utf8Str(strAddress).c_str(), &boundryAddress); } +/******************************************************************************* +* Global Variables * +*******************************************************************************/ +/** Pointer to the DHCP server. */ +static VBoxNetDhcp *g_pDhcp; -/** - * 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) +/* DHCP server specific options */ +static RTGETOPTDEF g_aOptionDefs[] = { - 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); -} - + { "--lease-db", 'D', RTGETOPT_REQ_STRING }, + { "--begin-config", 'b', RTGETOPT_REQ_NOTHING }, + { "--gateway", 'g', RTGETOPT_REQ_IPV4ADDR }, + { "--lower-ip", 'l', RTGETOPT_REQ_IPV4ADDR }, + { "--upper-ip", 'u', RTGETOPT_REQ_IPV4ADDR }, +}; /** - * 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. + * Construct a DHCP server with a default configuration. */ -bool VBoxNetDhcpLease::hasExpired() const +VBoxNetDhcp::VBoxNetDhcp():VBoxNetBaseService("VBoxNetDhcp", "VBoxNetDhcp") { - RTTIMESPEC Now; - return RTTimeSpecGetSeconds(&m_ExpireTime) > RTTimeSpecGetSeconds(RTTimeNow(&Now)); -} - + /* m_enmTrunkType = kIntNetTrunkType_WhateverNone; */ + RTMAC mac; + mac.au8[0] = 0x08; + mac.au8[1] = 0x00; + mac.au8[2] = 0x27; + mac.au8[3] = 0x40; + mac.au8[4] = 0x41; + mac.au8[5] = 0x42; + setMacAddress(mac); + RTNETADDRIPV4 address; + address.u = RT_H2N_U32_C(RT_BSWAP_U32_C(RT_MAKE_U32_FROM_U8( 10, 0, 2, 5))); + setIpv4Address(address); + setSendBufSize(8 * _1K); + setRecvBufSize(50 * _1K); -/** - * 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 + m_fIgnoreCmdLineParameters = true; + + for(unsigned int i = 0; i < RT_ELEMENTS(g_aOptionDefs); ++i) + addCommandLineOption(&g_aOptionDefs[i]); } @@ -564,463 +231,101 @@ VBoxNetDhcp::VBoxNetDhcp() */ 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. + * Parse the DHCP specific arguments. * - * @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. + * This callback caled for each paramenter so + * .... + * we nee post analisys of the parameters, at least + * for -b, -g, -l, -u, -m */ -void VBoxNetDhcp::explodeConfig(void) +int VBoxNetDhcp::parseOpt(int rc, const RTGETOPTUNION& Val) { - RTTIMESPEC Now; - RTTimeNow(&Now); + CMDLNPRM prm; - /* - * 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++; - } - } + /* Ok, we've entered here, thus we can't ignore cmd line parameters anymore */ + m_fIgnoreCmdLineParameters = false; - /* - * 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]; + prm.Key = rc; - /* 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++) + switch (rc) + { + case 'l': + case 'u': + case 'g': { - 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]); - } + char buf[17]; + RTStrPrintf(buf, 17, "%RTnaipv4", Val.IPv4Addr.u); + prm.strValue = buf; + CmdParameterll.push_back(prm); } - } -} + break; - -/** - * 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) + case 'b': // ignore + case 'D': // ignore 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; - } + 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; + return VINF_SUCCESS; } - -/** - * Tries to connect to the internal network. - * - * @returns 0 on success, exit code + error message to stderr on failure. - */ -int VBoxNetDhcp::tryGoOnline(void) +int VBoxNetDhcp::init() { - /* - * 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; - } + int rc = this->VBoxNetBaseService::init(); + AssertRCReturn(rc, rc); - /* - * 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; + NetworkManager *netManager = NetworkManager::getNetworkManager(); - /* - * 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; + netManager->setOurAddress(getIpv4Address()); + netManager->setOurNetmask(getIpv4Netmask()); + netManager->setOurMac(getMacAddress()); + netManager->setService(this); - /* - * 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); - } + if (isMainNeeded()) + rc = initWithMain(); else - RTStrmPrintf(g_pStdErr, "VBoxNetDHCP: SUPR3CallVMMR0Ex(,VMMR0_DO_INTNET_OPEN,) failed, rc=%Rrc\n", rc); + rc = initNoMain(); + + AssertRCReturn(rc, rc); - return RT_SUCCESS(rc) ? 0 : 1; + return VINF_SUCCESS; } -/** - * 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) +int VBoxNetDhcp::processUDP(void *pv, size_t cbPv) { - /* - * 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; - } + PCRTNETBOOTP pDhcpMsg = (PCRTNETBOOTP)pv; + m_pCurMsg = pDhcpMsg; + m_cbCurMsg = cbPv; - /* - * Process the receive buffer. - */ - while (IntNetRingHasMoreToRead(pRingBuf)) + uint8_t uMsgType; + if (RTNetIPv4IsDHCPValid(NULL /* why is this here? */, pDhcpMsg, cbPv, &uMsgType)) + { + m_uCurMsgType = uMsgType; { - 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); + /* To avoid fight with event processing thread */ + VBoxNetALock(this); + handleDhcpMsg(uMsgType, pDhcpMsg, cbPv); } + m_uCurMsgType = UINT8_MAX; } + else + debugPrint(1, true, "VBoxNetDHCP: Skipping invalid DHCP packet.\n"); /** @todo handle pure bootp clients too? */ - return 0; + m_pCurMsg = NULL; + m_cbCurMsg = 0; + + return VINF_SUCCESS; } @@ -1036,19 +341,21 @@ bool VBoxNetDhcp::handleDhcpMsg(uint8_t uMsgType, PCRTNETBOOTP pDhcpMsg, size_t { if (pDhcpMsg->bp_op == RTNETBOOTP_OP_REQUEST) { + NetworkManager *networkManager = NetworkManager::getNetworkManager(); + switch (uMsgType) { case RTNET_DHCP_MT_DISCOVER: - return handleDhcpReqDiscover(pDhcpMsg, cb); + return networkManager->handleDhcpReqDiscover(pDhcpMsg, cb); case RTNET_DHCP_MT_REQUEST: - return handleDhcpReqRequest(pDhcpMsg, cb); + return networkManager->handleDhcpReqRequest(pDhcpMsg, cb); case RTNET_DHCP_MT_DECLINE: - return handleDhcpReqDecline(pDhcpMsg, cb); + return networkManager->handleDhcpReqDecline(pDhcpMsg, cb); case RTNET_DHCP_MT_RELEASE: - return handleDhcpReqRelease(pDhcpMsg, cb); + return networkManager->handleDhcpReqRelease(pDhcpMsg, cb); case RTNET_DHCP_MT_INFORM: debugPrint(0, true, "Should we handle this?"); @@ -1062,946 +369,245 @@ bool VBoxNetDhcp::handleDhcpMsg(uint8_t uMsgType, PCRTNETBOOTP pDhcpMsg, size_t 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. + * Print debug message depending on the m_cVerbosity level. * - * @param pDhcpMsg The message. - * @param cb The message size. + * @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. */ -bool VBoxNetDhcp::handleDhcpReqRequest(PCRTNETBOOTP pDhcpMsg, size_t cb) +void VBoxNetDhcp::debugPrintV(int iMinLevel, bool fMsg, const char *pszFmt, va_list va) const { - /** @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) + if (iMinLevel <= m_cVerbosity) { - 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 + 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); - /* - * 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)) // ??? + if ( fMsg + && m_cVerbosity >= 2 + && m_pCurMsg) { - /** @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); + /* XXX: export this to debugPrinfDhcpMsg or variant and other method export + * to base class + */ + 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); } - 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. + * Gets the name of given DHCP message type. * - * @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. + * @returns Readonly name. + * @param uMsgType The message number. */ -class VBoxNetDhcpWriteCursor +/* static */ const char *VBoxNetDhcp::debugDhcpName(uint8_t uMsgType) { -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) + switch (uMsgType) { - if ( m_pfOverload - && (*m_pfOverload & 1)) - return false; - m_fUsed |= 1 /* bp_file flag*/; - return true; + 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"; } +} - /** - * 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; +int VBoxNetDhcp::initNoMain() +{ + CmdParameterIterator it; - 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; + RTNETADDRIPV4 address = getIpv4Address(); + RTNETADDRIPV4 netmask = getIpv4Netmask(); + RTNETADDRIPV4 networkId; + networkId.u = address.u & netmask.u; - /* 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... */ + RTNETADDRIPV4 UpperAddress; + RTNETADDRIPV4 LowerAddress = networkId; + UpperAddress.u = RT_H2N_U32(RT_N2H_U32(LowerAddress.u) | RT_N2H_U32(netmask.u)); - /* 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) + for (it = CmdParameterll.begin(); it != CmdParameterll.end(); ++it) { - /* 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)) + switch(it->Key) { - 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); - } - } + case 'l': + RTNetStrToIPv4Addr(it->strValue.c_str(), &LowerAddress); + break; - /* 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; - } + case 'u': + RTNetStrToIPv4Addr(it->strValue.c_str(), &UpperAddress); + break; + case 'b': + break; - /** - * 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); - } + ConfigurationManager *confManager = ConfigurationManager::getConfigurationManager(); + AssertPtrReturn(confManager, VERR_INTERNAL_ERROR); + confManager->addNetwork(unconst(g_RootConfig), + networkId, + netmask, + LowerAddress, + UpperAddress); - /** - * 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; - } + return VINF_SUCCESS; +} - /** - * 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. +int VBoxNetDhcp::initWithMain() +{ + /* ok, here we should initiate instance of dhcp server + * and listener for Dhcp configuration events */ - void putU8(uint8_t u8) - { - put(&u8, 1); - } + AssertRCReturn(virtualbox.isNull(), VERR_INTERNAL_ERROR); + std::string networkName = getNetwork(); - /** - * 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; - } + int rc = findDhcpServer(virtualbox, networkName, m_DhcpServer); + AssertRCReturn(rc, rc); - /** - * Puts an 32-bit integer (network endian). - * - * @param u32Network The integer. - */ - void putU32(uint32_t u32) - { - put(&u32, 4); - } + rc = findNatNetwork(virtualbox, networkName, m_NATNetwork); + AssertRCReturn(rc, rc); - /** - * 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; - } + BOOL fNeedDhcpServer = isDhcpRequired(m_NATNetwork); + if (!fNeedDhcpServer) + return VERR_CANCELLED; - /** - * Puts a std::string. - * - * @param rStr Reference to the string. - */ - void putStr(std::string const &rStr) - { - put(rStr.c_str(), rStr.size()); - } + RTNETADDRIPV4 gateway; + com::Bstr strGateway; + HRESULT hrc = m_NATNetwork->COMGETTER(Gateway)(strGateway.asOutParam()); + AssertComRCReturn(hrc, VERR_INTERNAL_ERROR); + RTNetStrToIPv4Addr(com::Utf8Str(strGateway).c_str(), &gateway); - /** - * 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; - } + ConfigurationManager *confManager = ConfigurationManager::getConfigurationManager(); + AssertPtrReturn(confManager, VERR_INTERNAL_ERROR); + confManager->addToAddressList(RTNET_DHCP_OPT_ROUTERS, gateway); - /** - * Whether we've overflowed. - * - * @returns true on overflow, false otherwise. - */ - bool hasOverflowed(void) const - { - return m_fOverflowed; - } + rc = fetchAndUpdateDnsInfo(); + AssertMsgRCReturn(rc, ("Wasn't able to fetch Dns info"), rc); - /** - * 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(); - } -}; + ComEventTypeArray aVBoxEvents; + aVBoxEvents.push_back(VBoxEventType_OnHostNameResolutionConfigurationChange); + rc = createNatListener(m_vboxListener, virtualbox, this, aVBoxEvents); + AssertRCReturn(rc, rc); + RTNETADDRIPV4 LowerAddress; + rc = configGetBoundryAddress(m_DhcpServer, false, LowerAddress); + AssertMsgRCReturn(rc, ("can't get lower boundrary adderss'"),rc); -/** - * 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); + RTNETADDRIPV4 UpperAddress; + rc = configGetBoundryAddress(m_DhcpServer, true, UpperAddress); + AssertMsgRCReturn(rc, ("can't get upper boundrary adderss'"),rc); - /* - * 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); + RTNETADDRIPV4 address = getIpv4Address(); + RTNETADDRIPV4 netmask = getIpv4Netmask(); + RTNETADDRIPV4 networkId = networkid(address, netmask); + std::string name = std::string("default"); - /* - * The options - use a cursor class for dealing with the ugly stuff. - */ - VBoxNetDhcpWriteCursor Cursor(pReply, cbReply); + confManager->addNetwork(unconst(g_RootConfig), + networkId, + netmask, + LowerAddress, + UpperAddress); - /* The basics */ - Cursor.optU8(RTNET_DHCP_OPT_MSG_TYPE, uMsgType); - Cursor.optIPv4Addr(RTNET_DHCP_OPT_SERVER_ID, m_Ipv4Address); + com::Bstr bstr; + hrc = virtualbox->COMGETTER(HomeFolder)(bstr.asOutParam()); + com::Utf8StrFmt strXmlLeaseFile("%ls%c%s.leases", + bstr.raw(), RTPATH_DELIMITER, networkName.c_str()); + confManager->loadFromFile(strXmlLeaseFile); - 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); + return VINF_SUCCESS; } -/** - * 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) +int VBoxNetDhcp::fetchAndUpdateDnsInfo() { - size_t iLease = m_Leases.size(); - while (iLease-- > 0) + ComHostPtr host; + if (SUCCEEDED(virtualbox->COMGETTER(Host)(host.asOutParam()))) { - 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; - } + AddressToOffsetMapping mapIp4Addr2Off; + int rc = localMappings(m_NATNetwork, mapIp4Addr2Off); + /* XXX: here could be several cases: 1. COM error, 2. not found (empty) 3. ? */ + AssertMsgRCReturn(rc, ("Can't fetch local mappings"), rc); - return NULL; -} + RTNETADDRIPV4 address = getIpv4Address(); + RTNETADDRIPV4 netmask = getIpv4Netmask(); + AddressList nameservers; + rc = hostDnsServers(host, networkid(address, netmask), mapIp4Addr2Off, nameservers); + AssertMsgRCReturn(rc, ("Debug me!!!"), rc); + /* XXX: Search strings */ -/** - * 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; -} - + std::string domain; + rc = hostDnsDomain(host, domain); + AssertMsgRCReturn(rc, ("Debug me!!"), rc); -/** - * 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; - } + VBoxNetALock(this); + ConfigurationManager *confManager = ConfigurationManager::getConfigurationManager(); + confManager->flushAddressList(RTNET_DHCP_OPT_DNS); - /* 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; - } + for (AddressList::iterator it = nameservers.begin(); it != nameservers.end(); ++it) + confManager->addToAddressList(RTNET_DHCP_OPT_DNS, *it); - /* expired lease */ - if (!pCur->isInUse(&Now)) - { - if ( !pFree - || RTTimeSpecGetSeconds(&pCur->m_ExpireTime) < RTTimeSpecGetSeconds(&pFree->m_ExpireTime)) - pFree = pCur; + confManager->setString(RTNET_DHCP_OPT_DOMAIN_NAME, domain); } } - 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; + return VINF_SUCCESS; } -/** - * 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) +HRESULT VBoxNetDhcp::HandleEvent(VBoxEventType_T aEventType, IEvent *pEvent) { - 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) + switch(aEventType) { - uint8_t uCur = *pb; - if (uCur == RTNET_DHCP_OPT_PAD) - { - cbLeft--; - pb++; - } - else if (cbLeft <= 1) + case VBoxEventType_OnHostNameResolutionConfigurationChange: + fetchAndUpdateDnsInfo(); 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"; - } + return S_OK; } - - /** * Entry point. */ -extern "C" DECLEXPORT(int) TrustedMain(int argc, char **argv, char **envp) +extern "C" DECLEXPORT(int) TrustedMain(int argc, char **argv) { /* * Instantiate the DHCP server and hand it the options. */ + VBoxNetDhcp *pDhcp = new VBoxNetDhcp(); if (!pDhcp) { @@ -2012,14 +618,16 @@ extern "C" DECLEXPORT(int) TrustedMain(int argc, char **argv, char **envp) if (rc) return rc; + pDhcp->init(); + /* * Try connect the server to the network. */ rc = pDhcp->tryGoOnline(); - if (rc) + if (RT_FAILURE(rc)) { delete pDhcp; - return rc; + return 1; } /* @@ -2030,19 +638,19 @@ extern "C" DECLEXPORT(int) TrustedMain(int argc, char **argv, char **envp) g_pDhcp = NULL; delete pDhcp; - return rc; + return 0; } #ifndef VBOX_WITH_HARDENING -int main(int argc, char **argv, char **envp) +int main(int argc, char **argv) { int rc = RTR3InitExe(argc, &argv, RTR3INIT_FLAGS_SUPLIB); if (RT_FAILURE(rc)) return RTMsgInitFailure(rc); - return TrustedMain(argc, argv, envp); + return TrustedMain(argc, argv); } # ifdef RT_OS_WINDOWS @@ -2061,7 +669,7 @@ static LRESULT CALLBACK WindowProc(HWND hwnd, return DefWindowProc (hwnd, uMsg, wParam, lParam); } -static LPCSTR g_WndClassName = "VBoxNetDHCPClass"; +static LPCWSTR g_WndClassName = L"VBoxNetDHCPClass"; static DWORD WINAPI MsgThreadProc(__in LPVOID lpParameter) { @@ -2139,7 +747,7 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine if(hThread != NULL) CloseHandle(hThread); - return main(__argc, __argv, environ); + return main(__argc, __argv); } # endif /* RT_OS_WINDOWS */ diff --git a/src/VBox/NetworkServices/DHCP/VBoxNetDHCPHardened.cpp b/src/VBox/NetworkServices/DHCP/VBoxNetDHCPHardened.cpp index 7d0e1bef..3142bbd7 100644 --- a/src/VBox/NetworkServices/DHCP/VBoxNetDHCPHardened.cpp +++ b/src/VBox/NetworkServices/DHCP/VBoxNetDHCPHardened.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2009 Oracle Corporation + * Copyright (C) 2009-2010 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; |