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; }
/** * 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); }
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; }
/** * 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; }
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; }
/** * 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; RT_ZERO(opt); /* 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; }
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; }
bool operator< (const Lease& lhs, const Lease& rhs) { return ( (lhs.getAddress() < rhs.getAddress()) || (lhs.issued() < rhs.issued())); }
/** * 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 NetworkManager::processParameterReqList(const Client& client, const uint8_t *pu8ReqList, int cReqList, std::vector<RawOption>& extra) { int rc; const Lease l = client.lease(); const NetworkConfigEntity *pNetCfg = l.getConfig(); /* * XXX: Brute-force. Unfortunately, there's no notification event * for changes. Should at least cache the options for a short * time, enough to last discover/offer/request/ack cycle. */ typedef std::map< int, std::pair<std::string, int> > DhcpOptionMap; DhcpOptionMap OptMap; if (!m->m_DhcpServer.isNull()) { com::SafeArray<BSTR> strings; com::Bstr str; HRESULT hrc; int OptCode, OptEncoding; char *pszOptText; strings.setNull(); hrc = m->m_DhcpServer->COMGETTER(GlobalOptions)(ComSafeArrayAsOutParam(strings)); AssertComRC(hrc); for (size_t i = 0; i < strings.size(); ++i) { com::Utf8Str encoded(strings[i]); rc = parseDhcpOptionText(encoded.c_str(), &OptCode, &pszOptText, &OptEncoding); if (!RT_SUCCESS(rc)) continue; OptMap[OptCode] = std::make_pair(pszOptText, OptEncoding); } const RTMAC &mac = client.getMacAddress(); char strMac[6*2+1] = ""; RTStrPrintf(strMac, sizeof(strMac), "%02x%02x%02x%02x%02x%02x", mac.au8[0], mac.au8[1], mac.au8[2], mac.au8[3], mac.au8[4], mac.au8[5]); strings.setNull(); hrc = m->m_DhcpServer->GetMacOptions(com::Bstr(strMac).raw(), ComSafeArrayAsOutParam(strings)); AssertComRC(hrc); for (size_t i = 0; i < strings.size(); ++i) { com::Utf8Str text(strings[i]); rc = parseDhcpOptionText(text.c_str(), &OptCode, &pszOptText, &OptEncoding); if (!RT_SUCCESS(rc)) continue; OptMap[OptCode] = std::make_pair(pszOptText, OptEncoding); } } /* 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: { DhcpOptionMap::const_iterator it = OptMap.find((int)u8Req); if (it == OptMap.end()) { Log(("opt: %d is ignored\n", u8Req)); fIgnore = true; } else { std::string OptText((*it).second.first); int OptEncoding((*it).second.second); rc = fillDhcpOption(opt, OptText, OptEncoding); if (!RT_SUCCESS(rc)) { fIgnore = true; break; } } } break; } if (!fIgnore) extra.push_back(opt); } return VINF_SUCCESS; }