VirtualBox

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

Last change on this file since 63470 was 63262, checked in by vboxsync, 8 years ago

Config.cpp: Impossible buffer overlow. warnings

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 37.6 KB
Line 
1/* $Id: Config.cpp 63262 2016-08-10 13:09:42Z 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 */
339int
340ConfigurationManager::findOption(uint8_t uOption, PCRTNETBOOTP pDhcpMsg, size_t cbDhcpMsg, RawOption& opt)
341{
342 Assert(uOption != RTNET_DHCP_OPT_PAD);
343
344 /*
345 * Validate the DHCP bits and figure the max size of the options in the vendor field.
346 */
347 if (cbDhcpMsg <= RT_UOFFSETOF(RTNETBOOTP, bp_vend.Dhcp.dhcp_opts))
348 return VERR_INVALID_PARAMETER;
349
350 if (pDhcpMsg->bp_vend.Dhcp.dhcp_cookie != RT_H2N_U32_C(RTNET_DHCP_COOKIE))
351 return VERR_INVALID_PARAMETER;
352
353 size_t cbLeft = cbDhcpMsg - RT_UOFFSETOF(RTNETBOOTP, bp_vend.Dhcp.dhcp_opts);
354 if (cbLeft > RTNET_DHCP_OPT_SIZE)
355 cbLeft = RTNET_DHCP_OPT_SIZE;
356
357 /*
358 * Search the vendor field.
359 */
360 uint8_t const *pb = &pDhcpMsg->bp_vend.Dhcp.dhcp_opts[0];
361 while (pb && cbLeft > 0)
362 {
363 uint8_t uCur = *pb;
364 if (uCur == RTNET_DHCP_OPT_PAD)
365 {
366 cbLeft--;
367 pb++;
368 }
369 else if (cbLeft <= 1)
370 break;
371 else
372 {
373 uint8_t cbCur = pb[1];
374 if (cbCur > cbLeft - 2)
375 cbCur = (uint8_t)(cbLeft - 2);
376 if (uCur == uOption)
377 {
378 opt.u8OptId = uCur;
379 memcpy(opt.au8RawOpt, pb+2, cbCur);
380 opt.cbRawOpt = cbCur;
381 return VINF_SUCCESS;
382 }
383 pb += cbCur + 2;
384 cbLeft -= cbCur - 2;
385 }
386 }
387
388 /** @todo search extended dhcp option field(s) when present */
389
390 return VERR_NOT_FOUND;
391}
392
393
394/**
395 * We bind lease for client till it continue with it on DHCPREQUEST.
396 */
397Lease ConfigurationManager::allocateLease4Client(const Client& client, PCRTNETBOOTP pDhcpMsg, size_t cbDhcpMsg)
398{
399 {
400 /**
401 * This mean that client has already bound or commited lease.
402 * If we've it happens it means that we received DHCPDISCOVER twice.
403 */
404 const Lease l = client.lease();
405 if (l != Lease::NullLease)
406 {
407 /* Here we should take lease from the m_allocation which was feed with leases
408 * on start
409 */
410 if (l.isExpired())
411 {
412 expireLease4Client(const_cast<Client&>(client));
413 if (!l.isExpired())
414 return l;
415 }
416 else
417 {
418 AssertReturn(l.getAddress().u != 0, Lease::NullLease);
419 return l;
420 }
421 }
422 }
423
424 RTNETADDRIPV4 hintAddress;
425 RawOption opt;
426 NetworkConfigEntity *pNetCfg;
427
428 Client cl(client);
429 AssertReturn(g_RootConfig->match(cl, (BaseConfigEntity **)&pNetCfg) > 0, Lease::NullLease);
430
431 /* DHCPDISCOVER MAY contain request address */
432 hintAddress.u = 0;
433 int rc = findOption(RTNET_DHCP_OPT_REQ_ADDR, pDhcpMsg, cbDhcpMsg, opt);
434 if (RT_SUCCESS(rc))
435 {
436 hintAddress.u = *(uint32_t *)opt.au8RawOpt;
437 if ( RT_H2N_U32(hintAddress.u) < RT_H2N_U32(pNetCfg->lowerIp().u)
438 || RT_H2N_U32(hintAddress.u) > RT_H2N_U32(pNetCfg->upperIp().u))
439 hintAddress.u = 0; /* clear hint */
440 }
441
442 if ( hintAddress.u
443 && !isAddressTaken(hintAddress))
444 {
445 Lease l(cl);
446 l.setConfig(pNetCfg);
447 l.setAddress(hintAddress);
448 m->m_allocations.insert(MapLease2Ip4AddressPair(l, hintAddress));
449 return l;
450 }
451
452 uint32_t u32 = 0;
453 for(u32 = RT_H2N_U32(pNetCfg->lowerIp().u);
454 u32 <= RT_H2N_U32(pNetCfg->upperIp().u);
455 ++u32)
456 {
457 RTNETADDRIPV4 address;
458 address.u = RT_H2N_U32(u32);
459 if (!isAddressTaken(address))
460 {
461 Lease l(cl);
462 l.setConfig(pNetCfg);
463 l.setAddress(address);
464 m->m_allocations.insert(MapLease2Ip4AddressPair(l, address));
465 return l;
466 }
467 }
468
469 return Lease::NullLease;
470}
471
472
473int ConfigurationManager::commitLease4Client(Client& client)
474{
475 Lease l = client.lease();
476 AssertReturn(l != Lease::NullLease, VERR_INTERNAL_ERROR);
477
478 l.bindingPhase(false);
479 const NetworkConfigEntity *pCfg = l.getConfig();
480
481 AssertPtr(pCfg);
482 l.setExpiration(pCfg->expirationPeriod());
483 l.phaseStart(RTTimeMilliTS());
484
485 saveToFile();
486
487 return VINF_SUCCESS;
488}
489
490
491int ConfigurationManager::expireLease4Client(Client& client)
492{
493 Lease l = client.lease();
494 AssertReturn(l != Lease::NullLease, VERR_INTERNAL_ERROR);
495
496 if (l.isInBindingPhase())
497 {
498
499 MapLease2Ip4AddressIterator it = m->m_allocations.find(l);
500 AssertReturn(it != m->m_allocations.end(), VERR_NOT_FOUND);
501
502 /*
503 * XXX: perhaps it better to keep this allocation ????
504 */
505 m->m_allocations.erase(it);
506
507 l.expire();
508 return VINF_SUCCESS;
509 }
510
511 l = Lease(client); /* re-new */
512 return VINF_SUCCESS;
513}
514
515
516bool ConfigurationManager::isAddressTaken(const RTNETADDRIPV4& addr, Lease& lease)
517{
518 MapLease2Ip4AddressIterator it;
519
520 for (it = m->m_allocations.begin();
521 it != m->m_allocations.end();
522 ++it)
523 {
524 if (it->second.u == addr.u)
525 {
526 if (lease != Lease::NullLease)
527 lease = it->first;
528
529 return true;
530 }
531 }
532 lease = Lease::NullLease;
533 return false;
534}
535
536
537bool ConfigurationManager::isAddressTaken(const RTNETADDRIPV4& addr)
538{
539 Lease ignore;
540 return isAddressTaken(addr, ignore);
541}
542
543
544NetworkConfigEntity *ConfigurationManager::addNetwork(NetworkConfigEntity *,
545 const RTNETADDRIPV4& networkId,
546 const RTNETADDRIPV4& netmask,
547 RTNETADDRIPV4& LowerAddress,
548 RTNETADDRIPV4& UpperAddress)
549{
550 static int id;
551 char name[64];
552
553 RTStrPrintf(name, RT_ELEMENTS(name), "network-%d", id);
554 std::string strname(name);
555 id++;
556
557
558 if (!LowerAddress.u)
559 LowerAddress = networkId;
560
561 if (!UpperAddress.u)
562 UpperAddress.u = networkId.u | (~netmask.u);
563
564 return new NetworkConfigEntity(strname,
565 g_RootConfig,
566 g_AnyClient,
567 5,
568 networkId,
569 netmask,
570 LowerAddress,
571 UpperAddress);
572}
573
574HostConfigEntity *ConfigurationManager::addHost(NetworkConfigEntity* pCfg,
575 const RTNETADDRIPV4& address,
576 ClientMatchCriteria *criteria)
577{
578 static int id;
579 char name[64];
580
581 RTStrPrintf(name, RT_ELEMENTS(name), "host-%d", id);
582 std::string strname(name);
583 id++;
584
585 return new HostConfigEntity(address, strname, pCfg, criteria);
586}
587
588int ConfigurationManager::addToAddressList(uint8_t u8OptId, RTNETADDRIPV4& address)
589{
590 switch(u8OptId)
591 {
592 case RTNET_DHCP_OPT_DNS:
593 m->m_nameservers.push_back(address);
594 break;
595 case RTNET_DHCP_OPT_ROUTERS:
596 m->m_routers.push_back(address);
597 break;
598 default:
599 Log(("dhcp-opt: list (%d) unsupported\n", u8OptId));
600 }
601 return VINF_SUCCESS;
602}
603
604
605int ConfigurationManager::flushAddressList(uint8_t u8OptId)
606{
607 switch(u8OptId)
608 {
609 case RTNET_DHCP_OPT_DNS:
610 m->m_nameservers.clear();
611 break;
612 case RTNET_DHCP_OPT_ROUTERS:
613 m->m_routers.clear();
614 break;
615 default:
616 Log(("dhcp-opt: list (%d) unsupported\n", u8OptId));
617 }
618 return VINF_SUCCESS;
619}
620
621
622const Ipv4AddressContainer& ConfigurationManager::getAddressList(uint8_t u8OptId)
623{
624 switch(u8OptId)
625 {
626 case RTNET_DHCP_OPT_DNS:
627 return m->m_nameservers;
628
629 case RTNET_DHCP_OPT_ROUTERS:
630 return m->m_routers;
631
632 }
633 /* XXX: Grrr !!! */
634 return m_empty;
635}
636
637
638int ConfigurationManager::setString(uint8_t u8OptId, const std::string& str)
639{
640 switch (u8OptId)
641 {
642 case RTNET_DHCP_OPT_DOMAIN_NAME:
643 m->m_domainName = str;
644 break;
645 default:
646 break;
647 }
648
649 return VINF_SUCCESS;
650}
651
652
653const std::string &ConfigurationManager::getString(uint8_t u8OptId)
654{
655 switch (u8OptId)
656 {
657 case RTNET_DHCP_OPT_DOMAIN_NAME:
658 if (m->m_domainName.length())
659 return m->m_domainName;
660 return m_noString;
661 default:
662 break;
663 }
664
665 return m_noString;
666}
667
668
669void ConfigurationManager::init()
670{
671 m = new ConfigurationManager::Data();
672}
673
674
675ConfigurationManager::~ConfigurationManager() { if (m) delete m; }
676
677/**
678 * Network manager
679 */
680struct NetworkManager::Data
681{
682 Data()
683 {
684 RT_ZERO(BootPReplyMsg);
685 cbBooPReplyMsg = 0;
686
687 m_OurAddress.u = 0;
688 m_OurNetmask.u = 0;
689 RT_ZERO(m_OurMac);
690 }
691
692 union {
693 RTNETBOOTP BootPHeader;
694 uint8_t au8Storage[1024];
695 } BootPReplyMsg;
696 int cbBooPReplyMsg;
697
698 RTNETADDRIPV4 m_OurAddress;
699 RTNETADDRIPV4 m_OurNetmask;
700 RTMAC m_OurMac;
701
702 ComPtr<IDHCPServer> m_DhcpServer;
703 const VBoxNetHlpUDPService *m_service;
704};
705
706
707NetworkManager::NetworkManager():m(NULL)
708{
709 m = new NetworkManager::Data();
710}
711
712
713NetworkManager::~NetworkManager()
714{
715 delete m;
716 m = NULL;
717}
718
719
720NetworkManager *NetworkManager::getNetworkManager(ComPtr<IDHCPServer> aDhcpServer)
721{
722 if (!g_NetworkManager)
723 {
724 g_NetworkManager = new NetworkManager();
725 g_NetworkManager->m->m_DhcpServer = aDhcpServer;
726 }
727
728 return g_NetworkManager;
729}
730
731
732const RTNETADDRIPV4& NetworkManager::getOurAddress() const
733{
734 return m->m_OurAddress;
735}
736
737
738const RTNETADDRIPV4& NetworkManager::getOurNetmask() const
739{
740 return m->m_OurNetmask;
741}
742
743
744const RTMAC& NetworkManager::getOurMac() const
745{
746 return m->m_OurMac;
747}
748
749
750void NetworkManager::setOurAddress(const RTNETADDRIPV4& aAddress)
751{
752 m->m_OurAddress = aAddress;
753}
754
755
756void NetworkManager::setOurNetmask(const RTNETADDRIPV4& aNetmask)
757{
758 m->m_OurNetmask = aNetmask;
759}
760
761
762void NetworkManager::setOurMac(const RTMAC& aMac)
763{
764 m->m_OurMac = aMac;
765}
766
767
768void NetworkManager::setService(const VBoxNetHlpUDPService *srv)
769{
770 m->m_service = srv;
771}
772
773/**
774 * Network manager creates DHCPOFFER datagramm
775 */
776int NetworkManager::offer4Client(const Client& client, uint32_t u32Xid,
777 uint8_t *pu8ReqList, int cReqList)
778{
779 Lease l(client); /* XXX: oh, it looks badly, but now we have lease */
780 prepareReplyPacket4Client(client, u32Xid);
781
782 RTNETADDRIPV4 address = l.getAddress();
783 m->BootPReplyMsg.BootPHeader.bp_yiaddr = address;
784
785 /* Ubuntu ???*/
786 m->BootPReplyMsg.BootPHeader.bp_ciaddr = address;
787
788 /* options:
789 * - IP lease time
790 * - message type
791 * - server identifier
792 */
793 RawOption opt;
794 RT_ZERO(opt);
795
796 std::vector<RawOption> extra;
797 opt.u8OptId = RTNET_DHCP_OPT_MSG_TYPE;
798 opt.au8RawOpt[0] = RTNET_DHCP_MT_OFFER;
799 opt.cbRawOpt = 1;
800 extra.push_back(opt);
801
802 opt.u8OptId = RTNET_DHCP_OPT_LEASE_TIME;
803
804 const NetworkConfigEntity *pCfg = l.getConfig();
805 AssertPtr(pCfg);
806
807 *(uint32_t *)opt.au8RawOpt = RT_H2N_U32(pCfg->expirationPeriod());
808 opt.cbRawOpt = sizeof(RTNETADDRIPV4);
809
810 extra.push_back(opt);
811
812 processParameterReqList(client, pu8ReqList, cReqList, extra);
813
814 return doReply(client, extra);
815}
816
817/**
818 * Network manager creates DHCPACK
819 */
820int NetworkManager::ack(const Client& client, uint32_t u32Xid,
821 uint8_t *pu8ReqList, int cReqList)
822{
823 RTNETADDRIPV4 address;
824
825 prepareReplyPacket4Client(client, u32Xid);
826
827 Lease l = client.lease();
828 address = l.getAddress();
829 m->BootPReplyMsg.BootPHeader.bp_ciaddr = address;
830
831
832 /* rfc2131 4.3.1 is about DHCPDISCOVER and this value is equal to ciaddr from
833 * DHCPREQUEST or 0 ...
834 * XXX: Using addressHint is not correct way to initialize [cy]iaddress...
835 */
836 m->BootPReplyMsg.BootPHeader.bp_ciaddr = address;
837 m->BootPReplyMsg.BootPHeader.bp_yiaddr = address;
838
839 Assert(m->BootPReplyMsg.BootPHeader.bp_yiaddr.u);
840
841 /* options:
842 * - IP address lease time (if DHCPREQUEST)
843 * - message type
844 * - server identifier
845 */
846 RawOption opt;
847 RT_ZERO(opt);
848
849 std::vector<RawOption> extra;
850 opt.u8OptId = RTNET_DHCP_OPT_MSG_TYPE;
851 opt.au8RawOpt[0] = RTNET_DHCP_MT_ACK;
852 opt.cbRawOpt = 1;
853 extra.push_back(opt);
854
855 /*
856 * XXX: lease time should be conditional. If on dhcprequest then tim should be provided,
857 * else on dhcpinform it mustn't.
858 */
859 opt.u8OptId = RTNET_DHCP_OPT_LEASE_TIME;
860 *(uint32_t *)opt.au8RawOpt = RT_H2N_U32(l.getExpiration());
861 opt.cbRawOpt = sizeof(RTNETADDRIPV4);
862 extra.push_back(opt);
863
864 processParameterReqList(client, pu8ReqList, cReqList, extra);
865
866 return doReply(client, extra);
867}
868
869/**
870 * Network manager creates DHCPNAK
871 */
872int NetworkManager::nak(const Client& client, uint32_t u32Xid)
873{
874
875 Lease l = client.lease();
876 if (l == Lease::NullLease)
877 return VERR_INTERNAL_ERROR;
878
879 prepareReplyPacket4Client(client, u32Xid);
880
881 /* this field filed in prepareReplyPacket4Session, and
882 * RFC 2131 require to have it zero fo NAK.
883 */
884 m->BootPReplyMsg.BootPHeader.bp_yiaddr.u = 0;
885
886 /* options:
887 * - message type (if DHCPREQUEST)
888 * - server identifier
889 */
890 RawOption opt;
891 std::vector<RawOption> extra;
892
893 opt.u8OptId = RTNET_DHCP_OPT_MSG_TYPE;
894 opt.au8RawOpt[0] = RTNET_DHCP_MT_NAC;
895 opt.cbRawOpt = 1;
896 extra.push_back(opt);
897
898 return doReply(client, extra);
899}
900
901/**
902 *
903 */
904int NetworkManager::prepareReplyPacket4Client(const Client& client, uint32_t u32Xid)
905{
906 RT_ZERO(m->BootPReplyMsg);
907
908 m->BootPReplyMsg.BootPHeader.bp_op = RTNETBOOTP_OP_REPLY;
909 m->BootPReplyMsg.BootPHeader.bp_htype = RTNET_ARP_ETHER;
910 m->BootPReplyMsg.BootPHeader.bp_hlen = sizeof(RTMAC);
911 m->BootPReplyMsg.BootPHeader.bp_hops = 0;
912 m->BootPReplyMsg.BootPHeader.bp_xid = u32Xid;
913 m->BootPReplyMsg.BootPHeader.bp_secs = 0;
914 /* XXX: bp_flags should be processed specially */
915 m->BootPReplyMsg.BootPHeader.bp_flags = 0;
916 m->BootPReplyMsg.BootPHeader.bp_ciaddr.u = 0;
917 m->BootPReplyMsg.BootPHeader.bp_giaddr.u = 0;
918
919 m->BootPReplyMsg.BootPHeader.bp_chaddr.Mac = client.getMacAddress();
920
921 const Lease l = client.lease();
922 m->BootPReplyMsg.BootPHeader.bp_yiaddr = l.getAddress();
923 m->BootPReplyMsg.BootPHeader.bp_siaddr.u = 0;
924
925
926 m->BootPReplyMsg.BootPHeader.bp_vend.Dhcp.dhcp_cookie = RT_H2N_U32_C(RTNET_DHCP_COOKIE);
927
928 memset(&m->BootPReplyMsg.BootPHeader.bp_vend.Dhcp.dhcp_opts[0],
929 '\0',
930 RTNET_DHCP_OPT_SIZE);
931
932 return VINF_SUCCESS;
933}
934
935
936int NetworkManager::doReply(const Client& client, const std::vector<RawOption>& extra)
937{
938 int rc;
939
940 /*
941 Options....
942 */
943 VBoxNetDhcpWriteCursor Cursor(&m->BootPReplyMsg.BootPHeader, RTNET_DHCP_NORMAL_SIZE);
944
945 /* The basics */
946
947 Cursor.optIPv4Addr(RTNET_DHCP_OPT_SERVER_ID, m->m_OurAddress);
948
949 const Lease l = client.lease();
950 const std::map<uint8_t, RawOption>& options = l.options();
951
952 for(std::vector<RawOption>::const_iterator it = extra.begin();
953 it != extra.end(); ++it)
954 {
955 if (!Cursor.begin(it->u8OptId, it->cbRawOpt))
956 break;
957 Cursor.put(it->au8RawOpt, it->cbRawOpt);
958
959 }
960
961 for(std::map<uint8_t, RawOption>::const_iterator it = options.begin();
962 it != options.end(); ++it)
963 {
964 if (!Cursor.begin(it->second.u8OptId, it->second.cbRawOpt))
965 break;
966 Cursor.put(it->second.au8RawOpt, it->second.cbRawOpt);
967
968 }
969
970 Cursor.optEnd();
971
972 /*
973 */
974#if 0
975 /** @todo need to see someone set this flag to check that it's correct. */
976 if (!(pDhcpMsg->bp_flags & RTNET_DHCP_FLAGS_NO_BROADCAST))
977 {
978 rc = VBoxNetUDPUnicast(m_pSession,
979 m_hIf,
980 m_pIfBuf,
981 m_OurAddress,
982 &m_OurMac,
983 RTNETIPV4_PORT_BOOTPS, /* sender */
984 IPv4AddrBrdCast,
985 &BootPReplyMsg.BootPHeader->bp_chaddr.Mac,
986 RTNETIPV4_PORT_BOOTPC, /* receiver */
987 &BootPReplyMsg, cbBooPReplyMsg);
988 }
989 else
990#endif
991 rc = m->m_service->hlpUDPBroadcast(RTNETIPV4_PORT_BOOTPS, /* sender */
992 RTNETIPV4_PORT_BOOTPC,
993 &m->BootPReplyMsg,
994 RTNET_DHCP_NORMAL_SIZE);
995
996 AssertRCReturn(rc,rc);
997
998 return VINF_SUCCESS;
999}
1000
1001
1002/*
1003 * XXX: TODO: Share decoding code with DHCPServer::addOption.
1004 */
1005static int parseDhcpOptionText(const char *pszText,
1006 int *pOptCode, char **ppszOptText, int *pOptEncoding)
1007{
1008 uint8_t u8Code;
1009 uint32_t u32Enc;
1010 char *pszNext;
1011 int rc;
1012
1013 rc = RTStrToUInt8Ex(pszText, &pszNext, 10, &u8Code);
1014 if (!RT_SUCCESS(rc))
1015 return VERR_PARSE_ERROR;
1016
1017 switch (*pszNext)
1018 {
1019 case ':': /* support legacy format too */
1020 {
1021 u32Enc = 0;
1022 break;
1023 }
1024
1025 case '=':
1026 {
1027 u32Enc = 1;
1028 break;
1029 }
1030
1031 case '@':
1032 {
1033 rc = RTStrToUInt32Ex(pszNext + 1, &pszNext, 10, &u32Enc);
1034 if (!RT_SUCCESS(rc))
1035 return VERR_PARSE_ERROR;
1036 if (*pszNext != '=')
1037 return VERR_PARSE_ERROR;
1038 break;
1039 }
1040
1041 default:
1042 return VERR_PARSE_ERROR;
1043 }
1044
1045 *pOptCode = u8Code;
1046 *ppszOptText = pszNext + 1;
1047 *pOptEncoding = (int)u32Enc;
1048
1049 return VINF_SUCCESS;
1050}
1051
1052
1053static int fillDhcpOption(RawOption &opt, const std::string &OptText, int OptEncoding)
1054{
1055 int rc;
1056
1057 if (OptEncoding == DhcpOptEncoding_Hex)
1058 {
1059 if (OptText.empty())
1060 return VERR_INVALID_PARAMETER;
1061
1062 size_t cbRawOpt = 0;
1063 char *pszNext = const_cast<char *>(OptText.c_str());
1064 while (*pszNext != '\0')
1065 {
1066 if (cbRawOpt >= RT_ELEMENTS(opt.au8RawOpt))
1067 return VERR_INVALID_PARAMETER;
1068
1069 uint8_t u8Byte;
1070 rc = RTStrToUInt8Ex(pszNext, &pszNext, 16, &u8Byte);
1071 if (!RT_SUCCESS(rc))
1072 return rc;
1073
1074 if (*pszNext == ':')
1075 ++pszNext;
1076 else if (*pszNext != '\0')
1077 return VERR_PARSE_ERROR;
1078
1079 opt.au8RawOpt[cbRawOpt] = u8Byte;
1080 ++cbRawOpt;
1081 }
1082 opt.cbRawOpt = (uint8_t)cbRawOpt;
1083 }
1084 else if (OptEncoding == DhcpOptEncoding_Legacy)
1085 {
1086 /*
1087 * XXX: TODO: encode "known" option opt.u8OptId
1088 */
1089 return VERR_INVALID_PARAMETER;
1090 }
1091
1092 return VINF_SUCCESS;
1093}
1094
1095
1096int NetworkManager::processParameterReqList(const Client& client, const uint8_t *pu8ReqList,
1097 int cReqList, std::vector<RawOption>& extra)
1098{
1099 int rc;
1100
1101 const Lease l = client.lease();
1102
1103 const NetworkConfigEntity *pNetCfg = l.getConfig();
1104
1105 /*
1106 * XXX: Brute-force. Unfortunately, there's no notification event
1107 * for changes. Should at least cache the options for a short
1108 * time, enough to last discover/offer/request/ack cycle.
1109 */
1110 typedef std::map< int, std::pair<std::string, int> > DhcpOptionMap;
1111 DhcpOptionMap OptMap;
1112
1113 if (!m->m_DhcpServer.isNull())
1114 {
1115 com::SafeArray<BSTR> strings;
1116 com::Bstr str;
1117 HRESULT hrc;
1118 int OptCode, OptEncoding;
1119 char *pszOptText;
1120
1121 strings.setNull();
1122 hrc = m->m_DhcpServer->COMGETTER(GlobalOptions)(ComSafeArrayAsOutParam(strings));
1123 AssertComRC(hrc);
1124 for (size_t i = 0; i < strings.size(); ++i)
1125 {
1126 com::Utf8Str encoded(strings[i]);
1127 rc = parseDhcpOptionText(encoded.c_str(),
1128 &OptCode, &pszOptText, &OptEncoding);
1129 if (!RT_SUCCESS(rc))
1130 continue;
1131
1132 OptMap[OptCode] = std::make_pair(pszOptText, OptEncoding);
1133 }
1134
1135 const RTMAC &mac = client.getMacAddress();
1136 char strMac[6*2+1] = "";
1137 RTStrPrintf(strMac, sizeof(strMac), "%02x%02x%02x%02x%02x%02x",
1138 mac.au8[0], mac.au8[1], mac.au8[2],
1139 mac.au8[3], mac.au8[4], mac.au8[5]);
1140
1141 strings.setNull();
1142 hrc = m->m_DhcpServer->GetMacOptions(com::Bstr(strMac).raw(),
1143 ComSafeArrayAsOutParam(strings));
1144 AssertComRC(hrc);
1145 for (size_t i = 0; i < strings.size(); ++i)
1146 {
1147 com::Utf8Str text(strings[i]);
1148 rc = parseDhcpOptionText(text.c_str(),
1149 &OptCode, &pszOptText, &OptEncoding);
1150 if (!RT_SUCCESS(rc))
1151 continue;
1152
1153 OptMap[OptCode] = std::make_pair(pszOptText, OptEncoding);
1154 }
1155 }
1156
1157 /* request parameter list */
1158 RawOption opt;
1159 bool fIgnore;
1160 uint8_t u8Req;
1161 for (int idxParam = 0; idxParam < cReqList; ++idxParam)
1162 {
1163 fIgnore = false;
1164 RT_ZERO(opt);
1165 u8Req = opt.u8OptId = pu8ReqList[idxParam];
1166
1167 switch(u8Req)
1168 {
1169 case RTNET_DHCP_OPT_SUBNET_MASK:
1170 ((PRTNETADDRIPV4)opt.au8RawOpt)->u = pNetCfg->netmask().u;
1171 opt.cbRawOpt = sizeof(RTNETADDRIPV4);
1172
1173 break;
1174
1175 case RTNET_DHCP_OPT_ROUTERS:
1176 case RTNET_DHCP_OPT_DNS:
1177 {
1178 const Ipv4AddressContainer lst =
1179 g_ConfigurationManager->getAddressList(u8Req);
1180 PRTNETADDRIPV4 pAddresses = (PRTNETADDRIPV4)&opt.au8RawOpt[0];
1181
1182 for (Ipv4AddressConstIterator it = lst.begin();
1183 it != lst.end();
1184 ++it)
1185 {
1186 *pAddresses = (*it);
1187 pAddresses++;
1188 opt.cbRawOpt += sizeof(RTNETADDRIPV4);
1189 }
1190
1191 if (lst.empty())
1192 fIgnore = true;
1193 }
1194 break;
1195 case RTNET_DHCP_OPT_DOMAIN_NAME:
1196 {
1197 std::string domainName = g_ConfigurationManager->getString(u8Req);
1198 if (domainName == g_ConfigurationManager->m_noString)
1199 {
1200 fIgnore = true;
1201 break;
1202 }
1203
1204 size_t cchLength = domainName.length();
1205 if (cchLength >= sizeof(opt.au8RawOpt))
1206 cchLength = sizeof(opt.au8RawOpt) - 1;
1207 memcpy(&opt.au8RawOpt[0], domainName.c_str(), cchLength);
1208 opt.au8RawOpt[cchLength] = '\0';
1209 opt.cbRawOpt = (uint8_t)cchLength;
1210 }
1211 break;
1212 default:
1213 {
1214 DhcpOptionMap::const_iterator it = OptMap.find((int)u8Req);
1215 if (it == OptMap.end())
1216 {
1217 Log(("opt: %d is ignored\n", u8Req));
1218 fIgnore = true;
1219 }
1220 else
1221 {
1222 std::string OptText((*it).second.first);
1223 int OptEncoding((*it).second.second);
1224
1225 rc = fillDhcpOption(opt, OptText, OptEncoding);
1226 if (!RT_SUCCESS(rc))
1227 {
1228 fIgnore = true;
1229 break;
1230 }
1231 }
1232 }
1233 break;
1234 }
1235
1236 if (!fIgnore)
1237 extra.push_back(opt);
1238
1239 }
1240
1241 return VINF_SUCCESS;
1242}
1243
1244/* Client */
1245Client::Client()
1246{
1247 m = SharedPtr<ClientData>();
1248}
1249
1250
1251void Client::initWithMac(const RTMAC& mac)
1252{
1253 m = SharedPtr<ClientData>(new ClientData());
1254 m->m_mac = mac;
1255}
1256
1257
1258bool Client::operator== (const RTMAC& mac) const
1259{
1260 return (m.get() && m->m_mac == mac);
1261}
1262
1263
1264const RTMAC& Client::getMacAddress() const
1265{
1266 return m->m_mac;
1267}
1268
1269
1270Lease Client::lease()
1271{
1272 if (!m.get()) return Lease::NullLease;
1273
1274 if (m->fHasLease)
1275 return Lease(*this);
1276 else
1277 return Lease::NullLease;
1278}
1279
1280
1281const Lease Client::lease() const
1282{
1283 return const_cast<Client *>(this)->lease();
1284}
1285
1286
1287Client::Client(ClientData *data):m(SharedPtr<ClientData>(data)){}
1288
1289/* Lease */
1290Lease::Lease()
1291{
1292 m = SharedPtr<ClientData>();
1293}
1294
1295
1296Lease::Lease (const Client& c)
1297{
1298 m = SharedPtr<ClientData>(c.m);
1299 if ( !m->fHasLease
1300 || ( isExpired()
1301 && !isInBindingPhase()))
1302 {
1303 m->fHasLease = true;
1304 m->fBinding = true;
1305 phaseStart(RTTimeMilliTS());
1306 }
1307}
1308
1309
1310bool Lease::isExpired() const
1311{
1312 AssertPtrReturn(m.get(), false);
1313
1314 if (!m->fBinding)
1315 return (ASMDivU64ByU32RetU32(RTTimeMilliTS() - m->u64TimestampLeasingStarted, 1000)
1316 > m->u32LeaseExpirationPeriod);
1317 else
1318 return (ASMDivU64ByU32RetU32(RTTimeMilliTS() - m->u64TimestampBindingStarted, 1000)
1319 > m->u32BindExpirationPeriod);
1320}
1321
1322
1323void Lease::expire()
1324{
1325 /* XXX: TODO */
1326}
1327
1328
1329void Lease::phaseStart(uint64_t u64Start)
1330{
1331 if (m->fBinding)
1332 m->u64TimestampBindingStarted = u64Start;
1333 else
1334 m->u64TimestampLeasingStarted = u64Start;
1335}
1336
1337
1338void Lease::bindingPhase(bool fOnOff)
1339{
1340 m->fBinding = fOnOff;
1341}
1342
1343
1344bool Lease::isInBindingPhase() const
1345{
1346 return m->fBinding;
1347}
1348
1349
1350uint64_t Lease::issued() const
1351{
1352 return m->u64TimestampLeasingStarted;
1353}
1354
1355
1356void Lease::setExpiration(uint32_t exp)
1357{
1358 if (m->fBinding)
1359 m->u32BindExpirationPeriod = exp;
1360 else
1361 m->u32LeaseExpirationPeriod = exp;
1362}
1363
1364
1365uint32_t Lease::getExpiration() const
1366{
1367 if (m->fBinding)
1368 return m->u32BindExpirationPeriod;
1369 else
1370 return m->u32LeaseExpirationPeriod;
1371}
1372
1373
1374RTNETADDRIPV4 Lease::getAddress() const
1375{
1376 return m->m_address;
1377}
1378
1379
1380void Lease::setAddress(RTNETADDRIPV4 address)
1381{
1382 m->m_address = address;
1383}
1384
1385
1386const NetworkConfigEntity *Lease::getConfig() const
1387{
1388 return m->pCfg;
1389}
1390
1391
1392void Lease::setConfig(NetworkConfigEntity *pCfg)
1393{
1394 m->pCfg = pCfg;
1395}
1396
1397
1398const MapOptionId2RawOption& Lease::options() const
1399{
1400 return m->options;
1401}
1402
1403
1404Lease::Lease(ClientData *pd):m(SharedPtr<ClientData>(pd)){}
1405
1406
1407bool Lease::toXML(xml::ElementNode *node) const
1408{
1409 xml::AttributeNode *pAttribNode = node->setAttribute(tagXMLLeaseAttributeMac.c_str(),
1410 com::Utf8StrFmt("%RTmac", &m->m_mac));
1411 if (!pAttribNode)
1412 return false;
1413
1414 pAttribNode = node->setAttribute(tagXMLLeaseAttributeNetwork.c_str(),
1415 com::Utf8StrFmt("%RTnaipv4", m->m_network));
1416 if (!pAttribNode)
1417 return false;
1418
1419 xml::ElementNode *pLeaseAddress = node->createChild(tagXMLLeaseAddress.c_str());
1420 if (!pLeaseAddress)
1421 return false;
1422
1423 pAttribNode = pLeaseAddress->setAttribute(tagXMLAddressAttributeValue.c_str(),
1424 com::Utf8StrFmt("%RTnaipv4", m->m_address));
1425 if (!pAttribNode)
1426 return false;
1427
1428 xml::ElementNode *pLeaseTime = node->createChild(tagXMLLeaseTime.c_str());
1429 if (!pLeaseTime)
1430 return false;
1431
1432 pAttribNode = pLeaseTime->setAttribute(tagXMLTimeAttributeIssued.c_str(),
1433 m->u64TimestampLeasingStarted);
1434 if (!pAttribNode)
1435 return false;
1436
1437 pAttribNode = pLeaseTime->setAttribute(tagXMLTimeAttributeExpiration.c_str(),
1438 m->u32LeaseExpirationPeriod);
1439 if (!pAttribNode)
1440 return false;
1441
1442 return true;
1443}
1444
1445
1446bool Lease::fromXML(const xml::ElementNode *node)
1447{
1448 com::Utf8Str mac;
1449 bool valueExists = node->getAttributeValue(tagXMLLeaseAttributeMac.c_str(), mac);
1450 if (!valueExists) return false;
1451 int rc = RTNetStrToMacAddr(mac.c_str(), &m->m_mac);
1452 if (RT_FAILURE(rc)) return false;
1453
1454 com::Utf8Str network;
1455 valueExists = node->getAttributeValue(tagXMLLeaseAttributeNetwork.c_str(), network);
1456 if (!valueExists) return false;
1457 rc = RTNetStrToIPv4Addr(network.c_str(), &m->m_network);
1458 if (RT_FAILURE(rc)) return false;
1459
1460 /* Address */
1461 const xml::ElementNode *address = node->findChildElement(tagXMLLeaseAddress.c_str());
1462 if (!address) return false;
1463 com::Utf8Str addressValue;
1464 valueExists = address->getAttributeValue(tagXMLAddressAttributeValue.c_str(), addressValue);
1465 if (!valueExists) return false;
1466 rc = RTNetStrToIPv4Addr(addressValue.c_str(), &m->m_address);
1467
1468 /* Time */
1469 const xml::ElementNode *time = node->findChildElement(tagXMLLeaseTime.c_str());
1470 if (!time) return false;
1471
1472 valueExists = time->getAttributeValue(tagXMLTimeAttributeIssued.c_str(),
1473 &m->u64TimestampLeasingStarted);
1474 if (!valueExists) return false;
1475 m->fBinding = false;
1476
1477 valueExists = time->getAttributeValue(tagXMLTimeAttributeExpiration.c_str(),
1478 &m->u32LeaseExpirationPeriod);
1479 if (!valueExists) return false;
1480
1481 m->fHasLease = true;
1482 return true;
1483}
1484
1485
1486const Lease Lease::NullLease;
1487
1488const 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