VirtualBox

source: vbox/trunk/src/VBox/NetworkServices/DHCP/Config.cpp@ 65440

Last change on this file since 65440 was 65119, checked in by vboxsync, 8 years ago

NetworkServices/DHCP: doxygen fixes

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 37.7 KB
Line 
1/* $Id: Config.cpp 65119 2017-01-04 17:10:02Z vboxsync $ */
2/** @file
3 * Configuration for DHCP.
4 */
5
6/*
7 * Copyright (C) 2013-2016 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/**
20 * XXX: license.
21 */
22
23#include <iprt/asm.h>
24#include <iprt/getopt.h>
25#include <iprt/net.h>
26#include <iprt/time.h>
27
28#include <VBox/sup.h>
29#include <VBox/intnet.h>
30#include <VBox/intnetinline.h>
31#include <VBox/vmm/vmm.h>
32#include <VBox/version.h>
33
34#include <VBox/com/array.h>
35#include <VBox/com/string.h>
36
37#include <iprt/cpp/xml.h>
38
39#define BASE_SERVICES_ONLY
40#include "../NetLib/VBoxNetBaseService.h"
41#include "../NetLib/VBoxNetLib.h"
42#include "../NetLib/shared_ptr.h"
43
44#include <list>
45#include <vector>
46#include <map>
47#include <string>
48
49#include "Config.h"
50#include "ClientDataInt.h"
51
52bool operator== (const Lease& lhs, const Lease& rhs)
53{
54 return (lhs.m.get() == rhs.m.get());
55}
56
57
58bool operator!= (const Lease& lhs, const Lease& rhs)
59{
60 return !(lhs == rhs);
61}
62
63
64bool operator< (const Lease& lhs, const Lease& rhs)
65{
66 return ( (lhs.getAddress() < rhs.getAddress())
67 || (lhs.issued() < rhs.issued()));
68}
69/* consts */
70
71const NullConfigEntity *g_NullConfig = new NullConfigEntity();
72RootConfigEntity *g_RootConfig = new RootConfigEntity(std::string("ROOT"), 1200 /* 20 min. */);
73const ClientMatchCriteria *g_AnyClient = new AnyClientMatchCriteria();
74
75static ConfigurationManager *g_ConfigurationManager = ConfigurationManager::getConfigurationManager();
76
77NetworkManager *NetworkManager::g_NetworkManager;
78
79bool MACClientMatchCriteria::check(const Client& client) const
80{
81 return (client == m_mac);
82}
83
84
85int BaseConfigEntity::match(Client& client, BaseConfigEntity **cfg)
86{
87 int iMatch = (m_criteria && m_criteria->check(client) ? m_MatchLevel : 0);
88 if (m_children.empty())
89 {
90 if (iMatch > 0)
91 {
92 *cfg = this;
93 return iMatch;
94 }
95 }
96 else
97 {
98 *cfg = this;
99 /* XXX: hack */
100 BaseConfigEntity *matching = this;
101 int matchingLevel = m_MatchLevel;
102
103 for (std::vector<BaseConfigEntity *>::iterator it = m_children.begin();
104 it != m_children.end();
105 ++it)
106 {
107 iMatch = (*it)->match(client, &matching);
108 if (iMatch > matchingLevel)
109 {
110 *cfg = matching;
111 matchingLevel = iMatch;
112 }
113 }
114 return matchingLevel;
115 }
116 return iMatch;
117}
118
119/* Client */
120/* Configs
121 NetworkConfigEntity(std::string name,
122 ConfigEntity* pCfg,
123 ClientMatchCriteria* criteria,
124 RTNETADDRIPV4& networkID,
125 RTNETADDRIPV4& networkMask)
126*/
127static const RTNETADDRIPV4 g_AnyIpv4 = {0};
128static const RTNETADDRIPV4 g_AllIpv4 = {0xffffffff};
129RootConfigEntity::RootConfigEntity(std::string name, uint32_t expPeriod):
130 NetworkConfigEntity(name, g_NullConfig, g_AnyClient, g_AnyIpv4, g_AllIpv4)
131{
132 m_MatchLevel = 2;
133 m_u32ExpirationPeriod = expPeriod;
134}
135
136/* Configuration Manager */
137struct ConfigurationManager::Data
138{
139 Data():fFileExists(false){}
140
141 MapLease2Ip4Address m_allocations;
142 Ipv4AddressContainer m_nameservers;
143 Ipv4AddressContainer m_routers;
144
145 std::string m_domainName;
146 VecClient m_clients;
147 com::Utf8Str m_leaseStorageFilename;
148 bool fFileExists;
149};
150
151ConfigurationManager *ConfigurationManager::getConfigurationManager()
152{
153 if (!g_ConfigurationManager)
154
155
156 {
157 g_ConfigurationManager = new ConfigurationManager();
158 g_ConfigurationManager->init();
159 }
160
161 return g_ConfigurationManager;
162}
163
164
165const std::string tagXMLLeases = "Leases";
166const std::string tagXMLLeasesAttributeVersion = "version";
167const std::string tagXMLLeasesVersion_1_0 = "1.0";
168const std::string tagXMLLease = "Lease";
169const std::string tagXMLLeaseAttributeMac = "mac";
170const std::string tagXMLLeaseAttributeNetwork = "network";
171const std::string tagXMLLeaseAddress = "Address";
172const std::string tagXMLAddressAttributeValue = "value";
173const std::string tagXMLLeaseTime = "Time";
174const std::string tagXMLTimeAttributeIssued = "issued";
175const std::string tagXMLTimeAttributeExpiration = "expiration";
176const std::string tagXMLLeaseOptions = "Options";
177
178/**
179 * @verbatim
180 <Leases version="1.0">
181 <Lease mac="" network=""/>
182 <Address value=""/>
183 <Time issued="" expiration=""/>
184 <options>
185 <option name="" type=""/>
186 </option>
187 </options>
188 </Lease>
189 </Leases>
190 @endverbatim
191 */
192int ConfigurationManager::loadFromFile(const com::Utf8Str& leaseStorageFileName)
193{
194 m->m_leaseStorageFilename = leaseStorageFileName;
195
196 xml::XmlFileParser parser;
197 xml::Document doc;
198
199 try {
200 parser.read(m->m_leaseStorageFilename.c_str(), doc);
201 }
202 catch (...)
203 {
204 return VINF_SUCCESS;
205 }
206
207 /* XML parsing */
208 xml::ElementNode *root = doc.getRootElement();
209
210 if (!root || !root->nameEquals(tagXMLLeases.c_str()))
211 {
212 m->fFileExists = false;
213 return VERR_NOT_FOUND;
214 }
215
216 com::Utf8Str version;
217 if (root)
218 root->getAttributeValue(tagXMLLeasesAttributeVersion.c_str(), version);
219
220 /* XXX: version check */
221 xml::NodesLoop leases(*root);
222
223 const xml::ElementNode *lease;
224 while ((lease = leases.forAllNodes()))
225 {
226 if (!lease->nameEquals(tagXMLLease.c_str()))
227 continue;
228
229 ClientData *data = new ClientData();
230 Lease l(data);
231 if (l.fromXML(lease))
232 {
233
234 m->m_allocations.insert(MapLease2Ip4AddressPair(l, l.getAddress()));
235
236
237 NetworkConfigEntity *pNetCfg = NULL;
238 Client c(data);
239 int rc = g_RootConfig->match(c, (BaseConfigEntity **)&pNetCfg);
240 Assert(rc >= 0 && pNetCfg); RT_NOREF(rc);
241
242 l.setConfig(pNetCfg);
243
244 m->m_clients.push_back(c);
245 }
246 }
247
248 return VINF_SUCCESS;
249}
250
251
252int ConfigurationManager::saveToFile()
253{
254 if (m->m_leaseStorageFilename.isEmpty())
255 return VINF_SUCCESS;
256
257 xml::Document doc;
258
259 xml::ElementNode *root = doc.createRootElement(tagXMLLeases.c_str());
260 if (!root)
261 return VERR_INTERNAL_ERROR;
262
263 root->setAttribute(tagXMLLeasesAttributeVersion.c_str(), tagXMLLeasesVersion_1_0.c_str());
264
265 for(MapLease2Ip4AddressConstIterator it = m->m_allocations.begin();
266 it != m->m_allocations.end(); ++it)
267 {
268 xml::ElementNode *lease = root->createChild(tagXMLLease.c_str());
269 if (!it->first.toXML(lease))
270 {
271 /* XXX: todo logging + error handling */
272 }
273 }
274
275 try {
276 xml::XmlFileWriter writer(doc);
277 writer.write(m->m_leaseStorageFilename.c_str(), true);
278 } catch(...){}
279
280 return VINF_SUCCESS;
281}
282
283
284int ConfigurationManager::extractRequestList(PCRTNETBOOTP pDhcpMsg, size_t cbDhcpMsg, RawOption& rawOpt)
285{
286 return ConfigurationManager::findOption(RTNET_DHCP_OPT_PARAM_REQ_LIST, pDhcpMsg, cbDhcpMsg, rawOpt);
287}
288
289
290Client ConfigurationManager::getClientByDhcpPacket(const RTNETBOOTP *pDhcpMsg, size_t cbDhcpMsg)
291{
292
293 VecClientIterator it;
294 bool fDhcpValid = false;
295 uint8_t uMsgType = 0;
296
297 fDhcpValid = RTNetIPv4IsDHCPValid(NULL, pDhcpMsg, cbDhcpMsg, &uMsgType);
298 AssertReturn(fDhcpValid, Client::NullClient);
299
300 LogFlowFunc(("dhcp:mac:%RTmac\n", &pDhcpMsg->bp_chaddr.Mac));
301 /* 1st. client IDs */
302 for ( it = m->m_clients.begin();
303 it != m->m_clients.end();
304 ++it)
305 {
306 if ((*it) == pDhcpMsg->bp_chaddr.Mac)
307 {
308 LogFlowFunc(("client:mac:%RTmac\n", it->getMacAddress()));
309 /* check timestamp that request wasn't expired. */
310 return (*it);
311 }
312 }
313
314 if (it == m->m_clients.end())
315 {
316 /* We hasn't got any session for this client */
317 Client c;
318 c.initWithMac(pDhcpMsg->bp_chaddr.Mac);
319 m->m_clients.push_back(c);
320 return m->m_clients.back();
321 }
322
323 return Client::NullClient;
324}
325
326/**
327 * Finds an option.
328 *
329 * @returns On success, a pointer to the first byte in the option data (no none
330 * then it'll be the byte following the 0 size field) and *pcbOpt set
331 * to the option length.
332 * On failure, NULL is returned and *pcbOpt unchanged.
333 *
334 * @param uOption The option to search for.
335 * @param pDhcpMsg The DHCP message.
336 * that this is adjusted if the option length is larger
337 * than the message buffer.
338 * @param cbDhcpMsg Size of the DHCP message.
339 * @param opt The actual option we found.
340 */
341int
342ConfigurationManager::findOption(uint8_t uOption, PCRTNETBOOTP pDhcpMsg, size_t cbDhcpMsg, RawOption& opt)
343{
344 Assert(uOption != RTNET_DHCP_OPT_PAD);
345
346 /*
347 * Validate the DHCP bits and figure the max size of the options in the vendor field.
348 */
349 if (cbDhcpMsg <= RT_UOFFSETOF(RTNETBOOTP, bp_vend.Dhcp.dhcp_opts))
350 return VERR_INVALID_PARAMETER;
351
352 if (pDhcpMsg->bp_vend.Dhcp.dhcp_cookie != RT_H2N_U32_C(RTNET_DHCP_COOKIE))
353 return VERR_INVALID_PARAMETER;
354
355 size_t cbLeft = cbDhcpMsg - RT_UOFFSETOF(RTNETBOOTP, bp_vend.Dhcp.dhcp_opts);
356 if (cbLeft > RTNET_DHCP_OPT_SIZE)
357 cbLeft = RTNET_DHCP_OPT_SIZE;
358
359 /*
360 * Search the vendor field.
361 */
362 uint8_t const *pb = &pDhcpMsg->bp_vend.Dhcp.dhcp_opts[0];
363 while (pb && cbLeft > 0)
364 {
365 uint8_t uCur = *pb;
366 if (uCur == RTNET_DHCP_OPT_PAD)
367 {
368 cbLeft--;
369 pb++;
370 }
371 else if (cbLeft <= 1)
372 break;
373 else
374 {
375 uint8_t cbCur = pb[1];
376 if (cbCur > cbLeft - 2)
377 cbCur = (uint8_t)(cbLeft - 2);
378 if (uCur == uOption)
379 {
380 opt.u8OptId = uCur;
381 memcpy(opt.au8RawOpt, pb+2, cbCur);
382 opt.cbRawOpt = cbCur;
383 return VINF_SUCCESS;
384 }
385 pb += cbCur + 2;
386 cbLeft -= cbCur - 2;
387 }
388 }
389
390 /** @todo search extended dhcp option field(s) when present */
391
392 return VERR_NOT_FOUND;
393}
394
395
396/**
397 * We bind lease for client till it continue with it on DHCPREQUEST.
398 */
399Lease ConfigurationManager::allocateLease4Client(const Client& client, PCRTNETBOOTP pDhcpMsg, size_t cbDhcpMsg)
400{
401 {
402 /**
403 * This mean that client has already bound or commited lease.
404 * If we've it happens it means that we received DHCPDISCOVER twice.
405 */
406 const Lease l = client.lease();
407 if (l != Lease::NullLease)
408 {
409 /* Here we should take lease from the m_allocation which was feed with leases
410 * on start
411 */
412 if (l.isExpired())
413 {
414 expireLease4Client(const_cast<Client&>(client));
415 if (!l.isExpired())
416 return l;
417 }
418 else
419 {
420 AssertReturn(l.getAddress().u != 0, Lease::NullLease);
421 return l;
422 }
423 }
424 }
425
426 RTNETADDRIPV4 hintAddress;
427 RawOption opt;
428 NetworkConfigEntity *pNetCfg;
429
430 Client cl(client);
431 AssertReturn(g_RootConfig->match(cl, (BaseConfigEntity **)&pNetCfg) > 0, Lease::NullLease);
432
433 /* DHCPDISCOVER MAY contain request address */
434 hintAddress.u = 0;
435 int rc = findOption(RTNET_DHCP_OPT_REQ_ADDR, pDhcpMsg, cbDhcpMsg, opt);
436 if (RT_SUCCESS(rc))
437 {
438 hintAddress.u = *(uint32_t *)opt.au8RawOpt;
439 if ( RT_H2N_U32(hintAddress.u) < RT_H2N_U32(pNetCfg->lowerIp().u)
440 || RT_H2N_U32(hintAddress.u) > RT_H2N_U32(pNetCfg->upperIp().u))
441 hintAddress.u = 0; /* clear hint */
442 }
443
444 if ( hintAddress.u
445 && !isAddressTaken(hintAddress))
446 {
447 Lease l(cl);
448 l.setConfig(pNetCfg);
449 l.setAddress(hintAddress);
450 m->m_allocations.insert(MapLease2Ip4AddressPair(l, hintAddress));
451 return l;
452 }
453
454 uint32_t u32 = 0;
455 for(u32 = RT_H2N_U32(pNetCfg->lowerIp().u);
456 u32 <= RT_H2N_U32(pNetCfg->upperIp().u);
457 ++u32)
458 {
459 RTNETADDRIPV4 address;
460 address.u = RT_H2N_U32(u32);
461 if (!isAddressTaken(address))
462 {
463 Lease l(cl);
464 l.setConfig(pNetCfg);
465 l.setAddress(address);
466 m->m_allocations.insert(MapLease2Ip4AddressPair(l, address));
467 return l;
468 }
469 }
470
471 return Lease::NullLease;
472}
473
474
475int ConfigurationManager::commitLease4Client(Client& client)
476{
477 Lease l = client.lease();
478 AssertReturn(l != Lease::NullLease, VERR_INTERNAL_ERROR);
479
480 l.bindingPhase(false);
481 const NetworkConfigEntity *pCfg = l.getConfig();
482
483 AssertPtr(pCfg);
484 l.setExpiration(pCfg->expirationPeriod());
485 l.phaseStart(RTTimeMilliTS());
486
487 saveToFile();
488
489 return VINF_SUCCESS;
490}
491
492
493int ConfigurationManager::expireLease4Client(Client& client)
494{
495 Lease l = client.lease();
496 AssertReturn(l != Lease::NullLease, VERR_INTERNAL_ERROR);
497
498 if (l.isInBindingPhase())
499 {
500
501 MapLease2Ip4AddressIterator it = m->m_allocations.find(l);
502 AssertReturn(it != m->m_allocations.end(), VERR_NOT_FOUND);
503
504 /*
505 * XXX: perhaps it better to keep this allocation ????
506 */
507 m->m_allocations.erase(it);
508
509 l.expire();
510 return VINF_SUCCESS;
511 }
512
513 l = Lease(client); /* re-new */
514 return VINF_SUCCESS;
515}
516
517
518bool ConfigurationManager::isAddressTaken(const RTNETADDRIPV4& addr, Lease& lease)
519{
520 MapLease2Ip4AddressIterator it;
521
522 for (it = m->m_allocations.begin();
523 it != m->m_allocations.end();
524 ++it)
525 {
526 if (it->second.u == addr.u)
527 {
528 if (lease != Lease::NullLease)
529 lease = it->first;
530
531 return true;
532 }
533 }
534 lease = Lease::NullLease;
535 return false;
536}
537
538
539bool ConfigurationManager::isAddressTaken(const RTNETADDRIPV4& addr)
540{
541 Lease ignore;
542 return isAddressTaken(addr, ignore);
543}
544
545
546NetworkConfigEntity *ConfigurationManager::addNetwork(NetworkConfigEntity *,
547 const RTNETADDRIPV4& networkId,
548 const RTNETADDRIPV4& netmask,
549 RTNETADDRIPV4& LowerAddress,
550 RTNETADDRIPV4& UpperAddress)
551{
552 static int id;
553 char name[64];
554
555 RTStrPrintf(name, RT_ELEMENTS(name), "network-%d", id);
556 std::string strname(name);
557 id++;
558
559
560 if (!LowerAddress.u)
561 LowerAddress = networkId;
562
563 if (!UpperAddress.u)
564 UpperAddress.u = networkId.u | (~netmask.u);
565
566 return new NetworkConfigEntity(strname,
567 g_RootConfig,
568 g_AnyClient,
569 5,
570 networkId,
571 netmask,
572 LowerAddress,
573 UpperAddress);
574}
575
576HostConfigEntity *ConfigurationManager::addHost(NetworkConfigEntity* pCfg,
577 const RTNETADDRIPV4& address,
578 ClientMatchCriteria *criteria)
579{
580 static int id;
581 char name[64];
582
583 RTStrPrintf(name, RT_ELEMENTS(name), "host-%d", id);
584 std::string strname(name);
585 id++;
586
587 return new HostConfigEntity(address, strname, pCfg, criteria);
588}
589
590int ConfigurationManager::addToAddressList(uint8_t u8OptId, RTNETADDRIPV4& address)
591{
592 switch(u8OptId)
593 {
594 case RTNET_DHCP_OPT_DNS:
595 m->m_nameservers.push_back(address);
596 break;
597 case RTNET_DHCP_OPT_ROUTERS:
598 m->m_routers.push_back(address);
599 break;
600 default:
601 Log(("dhcp-opt: list (%d) unsupported\n", u8OptId));
602 }
603 return VINF_SUCCESS;
604}
605
606
607int ConfigurationManager::flushAddressList(uint8_t u8OptId)
608{
609 switch(u8OptId)
610 {
611 case RTNET_DHCP_OPT_DNS:
612 m->m_nameservers.clear();
613 break;
614 case RTNET_DHCP_OPT_ROUTERS:
615 m->m_routers.clear();
616 break;
617 default:
618 Log(("dhcp-opt: list (%d) unsupported\n", u8OptId));
619 }
620 return VINF_SUCCESS;
621}
622
623
624const Ipv4AddressContainer& ConfigurationManager::getAddressList(uint8_t u8OptId)
625{
626 switch(u8OptId)
627 {
628 case RTNET_DHCP_OPT_DNS:
629 return m->m_nameservers;
630
631 case RTNET_DHCP_OPT_ROUTERS:
632 return m->m_routers;
633
634 }
635 /* XXX: Grrr !!! */
636 return m_empty;
637}
638
639
640int ConfigurationManager::setString(uint8_t u8OptId, const std::string& str)
641{
642 switch (u8OptId)
643 {
644 case RTNET_DHCP_OPT_DOMAIN_NAME:
645 m->m_domainName = str;
646 break;
647 default:
648 break;
649 }
650
651 return VINF_SUCCESS;
652}
653
654
655const std::string &ConfigurationManager::getString(uint8_t u8OptId)
656{
657 switch (u8OptId)
658 {
659 case RTNET_DHCP_OPT_DOMAIN_NAME:
660 if (m->m_domainName.length())
661 return m->m_domainName;
662 return m_noString;
663 default:
664 break;
665 }
666
667 return m_noString;
668}
669
670
671void ConfigurationManager::init()
672{
673 m = new ConfigurationManager::Data();
674}
675
676
677ConfigurationManager::~ConfigurationManager() { if (m) delete m; }
678
679/**
680 * Network manager
681 */
682struct NetworkManager::Data
683{
684 Data()
685 {
686 RT_ZERO(BootPReplyMsg);
687 cbBooPReplyMsg = 0;
688
689 m_OurAddress.u = 0;
690 m_OurNetmask.u = 0;
691 RT_ZERO(m_OurMac);
692 }
693
694 union {
695 RTNETBOOTP BootPHeader;
696 uint8_t au8Storage[1024];
697 } BootPReplyMsg;
698 int cbBooPReplyMsg;
699
700 RTNETADDRIPV4 m_OurAddress;
701 RTNETADDRIPV4 m_OurNetmask;
702 RTMAC m_OurMac;
703
704 ComPtr<IDHCPServer> m_DhcpServer;
705 const VBoxNetHlpUDPService *m_service;
706};
707
708
709NetworkManager::NetworkManager():m(NULL)
710{
711 m = new NetworkManager::Data();
712}
713
714
715NetworkManager::~NetworkManager()
716{
717 delete m;
718 m = NULL;
719}
720
721
722NetworkManager *NetworkManager::getNetworkManager(ComPtr<IDHCPServer> aDhcpServer)
723{
724 if (!g_NetworkManager)
725 {
726 g_NetworkManager = new NetworkManager();
727 g_NetworkManager->m->m_DhcpServer = aDhcpServer;
728 }
729
730 return g_NetworkManager;
731}
732
733
734const RTNETADDRIPV4& NetworkManager::getOurAddress() const
735{
736 return m->m_OurAddress;
737}
738
739
740const RTNETADDRIPV4& NetworkManager::getOurNetmask() const
741{
742 return m->m_OurNetmask;
743}
744
745
746const RTMAC& NetworkManager::getOurMac() const
747{
748 return m->m_OurMac;
749}
750
751
752void NetworkManager::setOurAddress(const RTNETADDRIPV4& aAddress)
753{
754 m->m_OurAddress = aAddress;
755}
756
757
758void NetworkManager::setOurNetmask(const RTNETADDRIPV4& aNetmask)
759{
760 m->m_OurNetmask = aNetmask;
761}
762
763
764void NetworkManager::setOurMac(const RTMAC& aMac)
765{
766 m->m_OurMac = aMac;
767}
768
769
770void NetworkManager::setService(const VBoxNetHlpUDPService *srv)
771{
772 m->m_service = srv;
773}
774
775/**
776 * Network manager creates DHCPOFFER datagramm
777 */
778int NetworkManager::offer4Client(const Client& client, uint32_t u32Xid,
779 uint8_t *pu8ReqList, int cReqList)
780{
781 Lease l(client); /* XXX: oh, it looks badly, but now we have lease */
782 prepareReplyPacket4Client(client, u32Xid);
783
784 RTNETADDRIPV4 address = l.getAddress();
785 m->BootPReplyMsg.BootPHeader.bp_yiaddr = address;
786
787 /* Ubuntu ???*/
788 m->BootPReplyMsg.BootPHeader.bp_ciaddr = address;
789
790 /* options:
791 * - IP lease time
792 * - message type
793 * - server identifier
794 */
795 RawOption opt;
796 RT_ZERO(opt);
797
798 std::vector<RawOption> extra;
799 opt.u8OptId = RTNET_DHCP_OPT_MSG_TYPE;
800 opt.au8RawOpt[0] = RTNET_DHCP_MT_OFFER;
801 opt.cbRawOpt = 1;
802 extra.push_back(opt);
803
804 opt.u8OptId = RTNET_DHCP_OPT_LEASE_TIME;
805
806 const NetworkConfigEntity *pCfg = l.getConfig();
807 AssertPtr(pCfg);
808
809 *(uint32_t *)opt.au8RawOpt = RT_H2N_U32(pCfg->expirationPeriod());
810 opt.cbRawOpt = sizeof(RTNETADDRIPV4);
811
812 extra.push_back(opt);
813
814 processParameterReqList(client, pu8ReqList, cReqList, extra);
815
816 return doReply(client, extra);
817}
818
819/**
820 * Network manager creates DHCPACK
821 */
822int NetworkManager::ack(const Client& client, uint32_t u32Xid,
823 uint8_t *pu8ReqList, int cReqList)
824{
825 RTNETADDRIPV4 address;
826
827 prepareReplyPacket4Client(client, u32Xid);
828
829 Lease l = client.lease();
830 address = l.getAddress();
831 m->BootPReplyMsg.BootPHeader.bp_ciaddr = address;
832
833
834 /* rfc2131 4.3.1 is about DHCPDISCOVER and this value is equal to ciaddr from
835 * DHCPREQUEST or 0 ...
836 * XXX: Using addressHint is not correct way to initialize [cy]iaddress...
837 */
838 m->BootPReplyMsg.BootPHeader.bp_ciaddr = address;
839 m->BootPReplyMsg.BootPHeader.bp_yiaddr = address;
840
841 Assert(m->BootPReplyMsg.BootPHeader.bp_yiaddr.u);
842
843 /* options:
844 * - IP address lease time (if DHCPREQUEST)
845 * - message type
846 * - server identifier
847 */
848 RawOption opt;
849 RT_ZERO(opt);
850
851 std::vector<RawOption> extra;
852 opt.u8OptId = RTNET_DHCP_OPT_MSG_TYPE;
853 opt.au8RawOpt[0] = RTNET_DHCP_MT_ACK;
854 opt.cbRawOpt = 1;
855 extra.push_back(opt);
856
857 /*
858 * XXX: lease time should be conditional. If on dhcprequest then tim should be provided,
859 * else on dhcpinform it mustn't.
860 */
861 opt.u8OptId = RTNET_DHCP_OPT_LEASE_TIME;
862 *(uint32_t *)opt.au8RawOpt = RT_H2N_U32(l.getExpiration());
863 opt.cbRawOpt = sizeof(RTNETADDRIPV4);
864 extra.push_back(opt);
865
866 processParameterReqList(client, pu8ReqList, cReqList, extra);
867
868 return doReply(client, extra);
869}
870
871/**
872 * Network manager creates DHCPNAK
873 */
874int NetworkManager::nak(const Client& client, uint32_t u32Xid)
875{
876
877 Lease l = client.lease();
878 if (l == Lease::NullLease)
879 return VERR_INTERNAL_ERROR;
880
881 prepareReplyPacket4Client(client, u32Xid);
882
883 /* this field filed in prepareReplyPacket4Session, and
884 * RFC 2131 require to have it zero fo NAK.
885 */
886 m->BootPReplyMsg.BootPHeader.bp_yiaddr.u = 0;
887
888 /* options:
889 * - message type (if DHCPREQUEST)
890 * - server identifier
891 */
892 RawOption opt;
893 std::vector<RawOption> extra;
894
895 opt.u8OptId = RTNET_DHCP_OPT_MSG_TYPE;
896 opt.au8RawOpt[0] = RTNET_DHCP_MT_NAC;
897 opt.cbRawOpt = 1;
898 extra.push_back(opt);
899
900 return doReply(client, extra);
901}
902
903/**
904 *
905 */
906int NetworkManager::prepareReplyPacket4Client(const Client& client, uint32_t u32Xid)
907{
908 RT_ZERO(m->BootPReplyMsg);
909
910 m->BootPReplyMsg.BootPHeader.bp_op = RTNETBOOTP_OP_REPLY;
911 m->BootPReplyMsg.BootPHeader.bp_htype = RTNET_ARP_ETHER;
912 m->BootPReplyMsg.BootPHeader.bp_hlen = sizeof(RTMAC);
913 m->BootPReplyMsg.BootPHeader.bp_hops = 0;
914 m->BootPReplyMsg.BootPHeader.bp_xid = u32Xid;
915 m->BootPReplyMsg.BootPHeader.bp_secs = 0;
916 /* XXX: bp_flags should be processed specially */
917 m->BootPReplyMsg.BootPHeader.bp_flags = 0;
918 m->BootPReplyMsg.BootPHeader.bp_ciaddr.u = 0;
919 m->BootPReplyMsg.BootPHeader.bp_giaddr.u = 0;
920
921 m->BootPReplyMsg.BootPHeader.bp_chaddr.Mac = client.getMacAddress();
922
923 const Lease l = client.lease();
924 m->BootPReplyMsg.BootPHeader.bp_yiaddr = l.getAddress();
925 m->BootPReplyMsg.BootPHeader.bp_siaddr.u = 0;
926
927
928 m->BootPReplyMsg.BootPHeader.bp_vend.Dhcp.dhcp_cookie = RT_H2N_U32_C(RTNET_DHCP_COOKIE);
929
930 memset(&m->BootPReplyMsg.BootPHeader.bp_vend.Dhcp.dhcp_opts[0],
931 '\0',
932 RTNET_DHCP_OPT_SIZE);
933
934 return VINF_SUCCESS;
935}
936
937
938int NetworkManager::doReply(const Client& client, const std::vector<RawOption>& extra)
939{
940 int rc;
941
942 /*
943 Options....
944 */
945 VBoxNetDhcpWriteCursor Cursor(&m->BootPReplyMsg.BootPHeader, RTNET_DHCP_NORMAL_SIZE);
946
947 /* The basics */
948
949 Cursor.optIPv4Addr(RTNET_DHCP_OPT_SERVER_ID, m->m_OurAddress);
950
951 const Lease l = client.lease();
952 const std::map<uint8_t, RawOption>& options = l.options();
953
954 for(std::vector<RawOption>::const_iterator it = extra.begin();
955 it != extra.end(); ++it)
956 {
957 if (!Cursor.begin(it->u8OptId, it->cbRawOpt))
958 break;
959 Cursor.put(it->au8RawOpt, it->cbRawOpt);
960
961 }
962
963 for(std::map<uint8_t, RawOption>::const_iterator it = options.begin();
964 it != options.end(); ++it)
965 {
966 if (!Cursor.begin(it->second.u8OptId, it->second.cbRawOpt))
967 break;
968 Cursor.put(it->second.au8RawOpt, it->second.cbRawOpt);
969
970 }
971
972 Cursor.optEnd();
973
974 /*
975 */
976#if 0
977 /** @todo need to see someone set this flag to check that it's correct. */
978 if (!(pDhcpMsg->bp_flags & RTNET_DHCP_FLAGS_NO_BROADCAST))
979 {
980 rc = VBoxNetUDPUnicast(m_pSession,
981 m_hIf,
982 m_pIfBuf,
983 m_OurAddress,
984 &m_OurMac,
985 RTNETIPV4_PORT_BOOTPS, /* sender */
986 IPv4AddrBrdCast,
987 &BootPReplyMsg.BootPHeader->bp_chaddr.Mac,
988 RTNETIPV4_PORT_BOOTPC, /* receiver */
989 &BootPReplyMsg, cbBooPReplyMsg);
990 }
991 else
992#endif
993 rc = m->m_service->hlpUDPBroadcast(RTNETIPV4_PORT_BOOTPS, /* sender */
994 RTNETIPV4_PORT_BOOTPC,
995 &m->BootPReplyMsg,
996 RTNET_DHCP_NORMAL_SIZE);
997
998 AssertRCReturn(rc,rc);
999
1000 return VINF_SUCCESS;
1001}
1002
1003
1004/*
1005 * XXX: TODO: Share decoding code with DHCPServer::addOption.
1006 */
1007static int parseDhcpOptionText(const char *pszText,
1008 int *pOptCode, char **ppszOptText, int *pOptEncoding)
1009{
1010 uint8_t u8Code;
1011 uint32_t u32Enc;
1012 char *pszNext;
1013 int rc;
1014
1015 rc = RTStrToUInt8Ex(pszText, &pszNext, 10, &u8Code);
1016 if (!RT_SUCCESS(rc))
1017 return VERR_PARSE_ERROR;
1018
1019 switch (*pszNext)
1020 {
1021 case ':': /* support legacy format too */
1022 {
1023 u32Enc = 0;
1024 break;
1025 }
1026
1027 case '=':
1028 {
1029 u32Enc = 1;
1030 break;
1031 }
1032
1033 case '@':
1034 {
1035 rc = RTStrToUInt32Ex(pszNext + 1, &pszNext, 10, &u32Enc);
1036 if (!RT_SUCCESS(rc))
1037 return VERR_PARSE_ERROR;
1038 if (*pszNext != '=')
1039 return VERR_PARSE_ERROR;
1040 break;
1041 }
1042
1043 default:
1044 return VERR_PARSE_ERROR;
1045 }
1046
1047 *pOptCode = u8Code;
1048 *ppszOptText = pszNext + 1;
1049 *pOptEncoding = (int)u32Enc;
1050
1051 return VINF_SUCCESS;
1052}
1053
1054
1055static int fillDhcpOption(RawOption &opt, const std::string &OptText, int OptEncoding)
1056{
1057 int rc;
1058
1059 if (OptEncoding == DhcpOptEncoding_Hex)
1060 {
1061 if (OptText.empty())
1062 return VERR_INVALID_PARAMETER;
1063
1064 size_t cbRawOpt = 0;
1065 char *pszNext = const_cast<char *>(OptText.c_str());
1066 while (*pszNext != '\0')
1067 {
1068 if (cbRawOpt >= RT_ELEMENTS(opt.au8RawOpt))
1069 return VERR_INVALID_PARAMETER;
1070
1071 uint8_t u8Byte;
1072 rc = RTStrToUInt8Ex(pszNext, &pszNext, 16, &u8Byte);
1073 if (!RT_SUCCESS(rc))
1074 return rc;
1075
1076 if (*pszNext == ':')
1077 ++pszNext;
1078 else if (*pszNext != '\0')
1079 return VERR_PARSE_ERROR;
1080
1081 opt.au8RawOpt[cbRawOpt] = u8Byte;
1082 ++cbRawOpt;
1083 }
1084 opt.cbRawOpt = (uint8_t)cbRawOpt;
1085 }
1086 else if (OptEncoding == DhcpOptEncoding_Legacy)
1087 {
1088 /*
1089 * XXX: TODO: encode "known" option opt.u8OptId
1090 */
1091 return VERR_INVALID_PARAMETER;
1092 }
1093
1094 return VINF_SUCCESS;
1095}
1096
1097
1098int NetworkManager::processParameterReqList(const Client& client, const uint8_t *pu8ReqList,
1099 int cReqList, std::vector<RawOption>& extra)
1100{
1101 int rc;
1102
1103 const Lease l = client.lease();
1104
1105 const NetworkConfigEntity *pNetCfg = l.getConfig();
1106
1107 /*
1108 * XXX: Brute-force. Unfortunately, there's no notification event
1109 * for changes. Should at least cache the options for a short
1110 * time, enough to last discover/offer/request/ack cycle.
1111 */
1112 typedef std::map< int, std::pair<std::string, int> > DhcpOptionMap;
1113 DhcpOptionMap OptMap;
1114
1115 if (!m->m_DhcpServer.isNull())
1116 {
1117 com::SafeArray<BSTR> strings;
1118 com::Bstr str;
1119 HRESULT hrc;
1120 int OptCode, OptEncoding;
1121 char *pszOptText;
1122
1123 strings.setNull();
1124 hrc = m->m_DhcpServer->COMGETTER(GlobalOptions)(ComSafeArrayAsOutParam(strings));
1125 AssertComRC(hrc);
1126 for (size_t i = 0; i < strings.size(); ++i)
1127 {
1128 com::Utf8Str encoded(strings[i]);
1129 rc = parseDhcpOptionText(encoded.c_str(),
1130 &OptCode, &pszOptText, &OptEncoding);
1131 if (!RT_SUCCESS(rc))
1132 continue;
1133
1134 OptMap[OptCode] = std::make_pair(pszOptText, OptEncoding);
1135 }
1136
1137 const RTMAC &mac = client.getMacAddress();
1138 char strMac[6*2+1] = "";
1139 RTStrPrintf(strMac, sizeof(strMac), "%02x%02x%02x%02x%02x%02x",
1140 mac.au8[0], mac.au8[1], mac.au8[2],
1141 mac.au8[3], mac.au8[4], mac.au8[5]);
1142
1143 strings.setNull();
1144 hrc = m->m_DhcpServer->GetMacOptions(com::Bstr(strMac).raw(),
1145 ComSafeArrayAsOutParam(strings));
1146 AssertComRC(hrc);
1147 for (size_t i = 0; i < strings.size(); ++i)
1148 {
1149 com::Utf8Str text(strings[i]);
1150 rc = parseDhcpOptionText(text.c_str(),
1151 &OptCode, &pszOptText, &OptEncoding);
1152 if (!RT_SUCCESS(rc))
1153 continue;
1154
1155 OptMap[OptCode] = std::make_pair(pszOptText, OptEncoding);
1156 }
1157 }
1158
1159 /* request parameter list */
1160 RawOption opt;
1161 bool fIgnore;
1162 uint8_t u8Req;
1163 for (int idxParam = 0; idxParam < cReqList; ++idxParam)
1164 {
1165 fIgnore = false;
1166 RT_ZERO(opt);
1167 u8Req = opt.u8OptId = pu8ReqList[idxParam];
1168
1169 switch(u8Req)
1170 {
1171 case RTNET_DHCP_OPT_SUBNET_MASK:
1172 ((PRTNETADDRIPV4)opt.au8RawOpt)->u = pNetCfg->netmask().u;
1173 opt.cbRawOpt = sizeof(RTNETADDRIPV4);
1174
1175 break;
1176
1177 case RTNET_DHCP_OPT_ROUTERS:
1178 case RTNET_DHCP_OPT_DNS:
1179 {
1180 const Ipv4AddressContainer lst =
1181 g_ConfigurationManager->getAddressList(u8Req);
1182 PRTNETADDRIPV4 pAddresses = (PRTNETADDRIPV4)&opt.au8RawOpt[0];
1183
1184 for (Ipv4AddressConstIterator it = lst.begin();
1185 it != lst.end();
1186 ++it)
1187 {
1188 *pAddresses = (*it);
1189 pAddresses++;
1190 opt.cbRawOpt += sizeof(RTNETADDRIPV4);
1191 }
1192
1193 if (lst.empty())
1194 fIgnore = true;
1195 }
1196 break;
1197 case RTNET_DHCP_OPT_DOMAIN_NAME:
1198 {
1199 std::string domainName = g_ConfigurationManager->getString(u8Req);
1200 if (domainName == g_ConfigurationManager->m_noString)
1201 {
1202 fIgnore = true;
1203 break;
1204 }
1205
1206 size_t cchLength = domainName.length();
1207 if (cchLength >= sizeof(opt.au8RawOpt))
1208 cchLength = sizeof(opt.au8RawOpt) - 1;
1209 memcpy(&opt.au8RawOpt[0], domainName.c_str(), cchLength);
1210 opt.au8RawOpt[cchLength] = '\0';
1211 opt.cbRawOpt = (uint8_t)cchLength;
1212 }
1213 break;
1214 default:
1215 {
1216 DhcpOptionMap::const_iterator it = OptMap.find((int)u8Req);
1217 if (it == OptMap.end())
1218 {
1219 Log(("opt: %d is ignored\n", u8Req));
1220 fIgnore = true;
1221 }
1222 else
1223 {
1224 std::string OptText((*it).second.first);
1225 int OptEncoding((*it).second.second);
1226
1227 rc = fillDhcpOption(opt, OptText, OptEncoding);
1228 if (!RT_SUCCESS(rc))
1229 {
1230 fIgnore = true;
1231 break;
1232 }
1233 }
1234 }
1235 break;
1236 }
1237
1238 if (!fIgnore)
1239 extra.push_back(opt);
1240
1241 }
1242
1243 return VINF_SUCCESS;
1244}
1245
1246/* Client */
1247Client::Client()
1248{
1249 m = SharedPtr<ClientData>();
1250}
1251
1252
1253void Client::initWithMac(const RTMAC& mac)
1254{
1255 m = SharedPtr<ClientData>(new ClientData());
1256 m->m_mac = mac;
1257}
1258
1259
1260bool Client::operator== (const RTMAC& mac) const
1261{
1262 return (m.get() && m->m_mac == mac);
1263}
1264
1265
1266const RTMAC& Client::getMacAddress() const
1267{
1268 return m->m_mac;
1269}
1270
1271
1272Lease Client::lease()
1273{
1274 if (!m.get()) return Lease::NullLease;
1275
1276 if (m->fHasLease)
1277 return Lease(*this);
1278 else
1279 return Lease::NullLease;
1280}
1281
1282
1283const Lease Client::lease() const
1284{
1285 return const_cast<Client *>(this)->lease();
1286}
1287
1288
1289Client::Client(ClientData *data):m(SharedPtr<ClientData>(data)){}
1290
1291/* Lease */
1292Lease::Lease()
1293{
1294 m = SharedPtr<ClientData>();
1295}
1296
1297
1298Lease::Lease (const Client& c)
1299{
1300 m = SharedPtr<ClientData>(c.m);
1301 if ( !m->fHasLease
1302 || ( isExpired()
1303 && !isInBindingPhase()))
1304 {
1305 m->fHasLease = true;
1306 m->fBinding = true;
1307 phaseStart(RTTimeMilliTS());
1308 }
1309}
1310
1311
1312bool Lease::isExpired() const
1313{
1314 AssertPtrReturn(m.get(), false);
1315
1316 if (!m->fBinding)
1317 return (ASMDivU64ByU32RetU32(RTTimeMilliTS() - m->u64TimestampLeasingStarted, 1000)
1318 > m->u32LeaseExpirationPeriod);
1319 else
1320 return (ASMDivU64ByU32RetU32(RTTimeMilliTS() - m->u64TimestampBindingStarted, 1000)
1321 > m->u32BindExpirationPeriod);
1322}
1323
1324
1325void Lease::expire()
1326{
1327 /* XXX: TODO */
1328}
1329
1330
1331void Lease::phaseStart(uint64_t u64Start)
1332{
1333 if (m->fBinding)
1334 m->u64TimestampBindingStarted = u64Start;
1335 else
1336 m->u64TimestampLeasingStarted = u64Start;
1337}
1338
1339
1340void Lease::bindingPhase(bool fOnOff)
1341{
1342 m->fBinding = fOnOff;
1343}
1344
1345
1346bool Lease::isInBindingPhase() const
1347{
1348 return m->fBinding;
1349}
1350
1351
1352uint64_t Lease::issued() const
1353{
1354 return m->u64TimestampLeasingStarted;
1355}
1356
1357
1358void Lease::setExpiration(uint32_t exp)
1359{
1360 if (m->fBinding)
1361 m->u32BindExpirationPeriod = exp;
1362 else
1363 m->u32LeaseExpirationPeriod = exp;
1364}
1365
1366
1367uint32_t Lease::getExpiration() const
1368{
1369 if (m->fBinding)
1370 return m->u32BindExpirationPeriod;
1371 else
1372 return m->u32LeaseExpirationPeriod;
1373}
1374
1375
1376RTNETADDRIPV4 Lease::getAddress() const
1377{
1378 return m->m_address;
1379}
1380
1381
1382void Lease::setAddress(RTNETADDRIPV4 address)
1383{
1384 m->m_address = address;
1385}
1386
1387
1388const NetworkConfigEntity *Lease::getConfig() const
1389{
1390 return m->pCfg;
1391}
1392
1393
1394void Lease::setConfig(NetworkConfigEntity *pCfg)
1395{
1396 m->pCfg = pCfg;
1397}
1398
1399
1400const MapOptionId2RawOption& Lease::options() const
1401{
1402 return m->options;
1403}
1404
1405
1406Lease::Lease(ClientData *pd):m(SharedPtr<ClientData>(pd)){}
1407
1408
1409bool Lease::toXML(xml::ElementNode *node) const
1410{
1411 xml::AttributeNode *pAttribNode = node->setAttribute(tagXMLLeaseAttributeMac.c_str(),
1412 com::Utf8StrFmt("%RTmac", &m->m_mac));
1413 if (!pAttribNode)
1414 return false;
1415
1416 pAttribNode = node->setAttribute(tagXMLLeaseAttributeNetwork.c_str(),
1417 com::Utf8StrFmt("%RTnaipv4", m->m_network));
1418 if (!pAttribNode)
1419 return false;
1420
1421 xml::ElementNode *pLeaseAddress = node->createChild(tagXMLLeaseAddress.c_str());
1422 if (!pLeaseAddress)
1423 return false;
1424
1425 pAttribNode = pLeaseAddress->setAttribute(tagXMLAddressAttributeValue.c_str(),
1426 com::Utf8StrFmt("%RTnaipv4", m->m_address));
1427 if (!pAttribNode)
1428 return false;
1429
1430 xml::ElementNode *pLeaseTime = node->createChild(tagXMLLeaseTime.c_str());
1431 if (!pLeaseTime)
1432 return false;
1433
1434 pAttribNode = pLeaseTime->setAttribute(tagXMLTimeAttributeIssued.c_str(),
1435 m->u64TimestampLeasingStarted);
1436 if (!pAttribNode)
1437 return false;
1438
1439 pAttribNode = pLeaseTime->setAttribute(tagXMLTimeAttributeExpiration.c_str(),
1440 m->u32LeaseExpirationPeriod);
1441 if (!pAttribNode)
1442 return false;
1443
1444 return true;
1445}
1446
1447
1448bool Lease::fromXML(const xml::ElementNode *node)
1449{
1450 com::Utf8Str mac;
1451 bool valueExists = node->getAttributeValue(tagXMLLeaseAttributeMac.c_str(), mac);
1452 if (!valueExists) return false;
1453 int rc = RTNetStrToMacAddr(mac.c_str(), &m->m_mac);
1454 if (RT_FAILURE(rc)) return false;
1455
1456 com::Utf8Str network;
1457 valueExists = node->getAttributeValue(tagXMLLeaseAttributeNetwork.c_str(), network);
1458 if (!valueExists) return false;
1459 rc = RTNetStrToIPv4Addr(network.c_str(), &m->m_network);
1460 if (RT_FAILURE(rc)) return false;
1461
1462 /* Address */
1463 const xml::ElementNode *address = node->findChildElement(tagXMLLeaseAddress.c_str());
1464 if (!address) return false;
1465 com::Utf8Str addressValue;
1466 valueExists = address->getAttributeValue(tagXMLAddressAttributeValue.c_str(), addressValue);
1467 if (!valueExists) return false;
1468 rc = RTNetStrToIPv4Addr(addressValue.c_str(), &m->m_address);
1469
1470 /* Time */
1471 const xml::ElementNode *time = node->findChildElement(tagXMLLeaseTime.c_str());
1472 if (!time) return false;
1473
1474 valueExists = time->getAttributeValue(tagXMLTimeAttributeIssued.c_str(),
1475 &m->u64TimestampLeasingStarted);
1476 if (!valueExists) return false;
1477 m->fBinding = false;
1478
1479 valueExists = time->getAttributeValue(tagXMLTimeAttributeExpiration.c_str(),
1480 &m->u32LeaseExpirationPeriod);
1481 if (!valueExists) return false;
1482
1483 m->fHasLease = true;
1484 return true;
1485}
1486
1487
1488const Lease Lease::NullLease;
1489
1490const Client Client::NullClient;
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette