VirtualBox

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

Last change on this file since 54582 was 54544, checked in by vboxsync, 10 years ago

VBoxNetDHCP: Removed XXX as this is the correct way to call a method. In contrast to accessing attributes (like for instance the attribute LowerIP)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 37.5 KB
Line 
1/* $Id: Config.cpp 54544 2015-02-27 07:35:23Z vboxsync $ */
2/** @file
3 * Configuration for DHCP.
4 */
5
6/*
7 * Copyright (C) 2013-2014 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 * <Leases version="1.0">
180 * <Lease mac="" network=""/>
181 * <Address value=""/>
182 * <Time issued="" expiration=""/>
183 * <options>
184 * <option name="" type=""/>
185 * </option>
186 * </options>
187 * </Lease>
188 * </Leases>
189 */
190int ConfigurationManager::loadFromFile(const com::Utf8Str& leaseStorageFileName)
191{
192 m->m_leaseStorageFilename = leaseStorageFileName;
193
194 xml::XmlFileParser parser;
195 xml::Document doc;
196
197 try {
198 parser.read(m->m_leaseStorageFilename.c_str(), doc);
199 }
200 catch (...)
201 {
202 return VINF_SUCCESS;
203 }
204
205 /* XML parsing */
206 xml::ElementNode *root = doc.getRootElement();
207
208 if (!root || !root->nameEquals(tagXMLLeases.c_str()))
209 {
210 m->fFileExists = false;
211 return VERR_NOT_FOUND;
212 }
213
214 com::Utf8Str version;
215 if (root)
216 root->getAttributeValue(tagXMLLeasesAttributeVersion.c_str(), version);
217
218 /* XXX: version check */
219 xml::NodesLoop leases(*root);
220
221 bool valueExists;
222 const xml::ElementNode *lease;
223 while ((lease = leases.forAllNodes()))
224 {
225 if (!lease->nameEquals(tagXMLLease.c_str()))
226 continue;
227
228 ClientData *data = new ClientData();
229 Lease l(data);
230 if (l.fromXML(lease))
231 {
232
233 m->m_allocations.insert(MapLease2Ip4AddressPair(l, l.getAddress()));
234
235
236 NetworkConfigEntity *pNetCfg = NULL;
237 Client c(data);
238 int rc = g_RootConfig->match(c, (BaseConfigEntity **)&pNetCfg);
239 Assert(rc >= 0 && pNetCfg);
240
241 l.setConfig(pNetCfg);
242
243 m->m_clients.push_back(c);
244 }
245 }
246
247 return VINF_SUCCESS;
248}
249
250
251int ConfigurationManager::saveToFile()
252{
253 if (m->m_leaseStorageFilename.isEmpty())
254 return VINF_SUCCESS;
255
256 xml::Document doc;
257
258 xml::ElementNode *root = doc.createRootElement(tagXMLLeases.c_str());
259 if (!root)
260 return VERR_INTERNAL_ERROR;
261
262 root->setAttribute(tagXMLLeasesAttributeVersion.c_str(), tagXMLLeasesVersion_1_0.c_str());
263
264 for(MapLease2Ip4AddressConstIterator it = m->m_allocations.begin();
265 it != m->m_allocations.end(); ++it)
266 {
267 xml::ElementNode *lease = root->createChild(tagXMLLease.c_str());
268 if (!it->first.toXML(lease))
269 {
270 /* XXX: todo logging + error handling */
271 }
272 }
273
274 try {
275 xml::XmlFileWriter writer(doc);
276 writer.write(m->m_leaseStorageFilename.c_str(), true);
277 } catch(...){}
278
279 return VINF_SUCCESS;
280}
281
282
283int ConfigurationManager::extractRequestList(PCRTNETBOOTP pDhcpMsg, size_t cbDhcpMsg, RawOption& rawOpt)
284{
285 return ConfigurationManager::findOption(RTNET_DHCP_OPT_PARAM_REQ_LIST, pDhcpMsg, cbDhcpMsg, rawOpt);
286}
287
288
289Client ConfigurationManager::getClientByDhcpPacket(const RTNETBOOTP *pDhcpMsg, size_t cbDhcpMsg)
290{
291
292 VecClientIterator it;
293 bool fDhcpValid = false;
294 uint8_t uMsgType = 0;
295
296 fDhcpValid = RTNetIPv4IsDHCPValid(NULL, pDhcpMsg, cbDhcpMsg, &uMsgType);
297 AssertReturn(fDhcpValid, Client::NullClient);
298
299 LogFlowFunc(("dhcp:mac:%RTmac\n", &pDhcpMsg->bp_chaddr.Mac));
300 /* 1st. client IDs */
301 for ( it = m->m_clients.begin();
302 it != m->m_clients.end();
303 ++it)
304 {
305 if ((*it) == pDhcpMsg->bp_chaddr.Mac)
306 {
307 LogFlowFunc(("client:mac:%RTmac\n", it->getMacAddress()));
308 /* check timestamp that request wasn't expired. */
309 return (*it);
310 }
311 }
312
313 if (it == m->m_clients.end())
314 {
315 /* We hasn't got any session for this client */
316 Client c;
317 c.initWithMac(pDhcpMsg->bp_chaddr.Mac);
318 m->m_clients.push_back(c);
319 return m->m_clients.back();
320 }
321
322 return Client::NullClient;
323}
324
325/**
326 * Finds an option.
327 *
328 * @returns On success, a pointer to the first byte in the option data (no none
329 * then it'll be the byte following the 0 size field) and *pcbOpt set
330 * to the option length.
331 * On failure, NULL is returned and *pcbOpt unchanged.
332 *
333 * @param uOption The option to search for.
334 * @param pDhcpMsg The DHCP message.
335 * that this is adjusted if the option length is larger
336 * than the message buffer.
337 */
338int
339ConfigurationManager::findOption(uint8_t uOption, PCRTNETBOOTP pDhcpMsg, size_t cbDhcpMsg, RawOption& opt)
340{
341 Assert(uOption != RTNET_DHCP_OPT_PAD);
342
343 /*
344 * Validate the DHCP bits and figure the max size of the options in the vendor field.
345 */
346 if (cbDhcpMsg <= RT_UOFFSETOF(RTNETBOOTP, bp_vend.Dhcp.dhcp_opts))
347 return VERR_INVALID_PARAMETER;
348
349 if (pDhcpMsg->bp_vend.Dhcp.dhcp_cookie != RT_H2N_U32_C(RTNET_DHCP_COOKIE))
350 return VERR_INVALID_PARAMETER;
351
352 size_t cbLeft = cbDhcpMsg - RT_UOFFSETOF(RTNETBOOTP, bp_vend.Dhcp.dhcp_opts);
353 if (cbLeft > RTNET_DHCP_OPT_SIZE)
354 cbLeft = RTNET_DHCP_OPT_SIZE;
355
356 /*
357 * Search the vendor field.
358 */
359 bool fExtended = false;
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 size_t cbCur = pb[1];
374 if (cbCur > cbLeft - 2)
375 cbCur = 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 else
661 return m_noString;
662 default:
663 break;
664 }
665
666 return m_noString;
667}
668
669
670void ConfigurationManager::init()
671{
672 m = new ConfigurationManager::Data();
673}
674
675
676ConfigurationManager::~ConfigurationManager() { if (m) delete m; }
677
678/**
679 * Network manager
680 */
681struct NetworkManager::Data
682{
683 Data()
684 {
685 RT_ZERO(BootPReplyMsg);
686 cbBooPReplyMsg = 0;
687
688 m_OurAddress.u = 0;
689 m_OurNetmask.u = 0;
690 RT_ZERO(m_OurMac);
691 }
692
693 union {
694 RTNETBOOTP BootPHeader;
695 uint8_t au8Storage[1024];
696 } BootPReplyMsg;
697 int cbBooPReplyMsg;
698
699 RTNETADDRIPV4 m_OurAddress;
700 RTNETADDRIPV4 m_OurNetmask;
701 RTMAC m_OurMac;
702
703 ComPtr<IDHCPServer> m_DhcpServer;
704 const VBoxNetHlpUDPService *m_service;
705};
706
707
708NetworkManager::NetworkManager():m(NULL)
709{
710 m = new NetworkManager::Data();
711}
712
713
714NetworkManager::~NetworkManager()
715{
716 delete m;
717 m = NULL;
718}
719
720
721NetworkManager *NetworkManager::getNetworkManager(ComPtr<IDHCPServer> aDhcpServer)
722{
723 if (!g_NetworkManager)
724 {
725 g_NetworkManager = new NetworkManager();
726 g_NetworkManager->m->m_DhcpServer = aDhcpServer;
727 }
728
729 return g_NetworkManager;
730}
731
732
733const RTNETADDRIPV4& NetworkManager::getOurAddress() const
734{
735 return m->m_OurAddress;
736}
737
738
739const RTNETADDRIPV4& NetworkManager::getOurNetmask() const
740{
741 return m->m_OurNetmask;
742}
743
744
745const RTMAC& NetworkManager::getOurMac() const
746{
747 return m->m_OurMac;
748}
749
750
751void NetworkManager::setOurAddress(const RTNETADDRIPV4& aAddress)
752{
753 m->m_OurAddress = aAddress;
754}
755
756
757void NetworkManager::setOurNetmask(const RTNETADDRIPV4& aNetmask)
758{
759 m->m_OurNetmask = aNetmask;
760}
761
762
763void NetworkManager::setOurMac(const RTMAC& aMac)
764{
765 m->m_OurMac = aMac;
766}
767
768
769void NetworkManager::setService(const VBoxNetHlpUDPService *srv)
770{
771 m->m_service = srv;
772}
773
774/**
775 * Network manager creates DHCPOFFER datagramm
776 */
777int NetworkManager::offer4Client(const Client& client, uint32_t u32Xid,
778 uint8_t *pu8ReqList, int cReqList)
779{
780 Lease l(client); /* XXX: oh, it looks badly, but now we have lease */
781 prepareReplyPacket4Client(client, u32Xid);
782
783 RTNETADDRIPV4 address = l.getAddress();
784 m->BootPReplyMsg.BootPHeader.bp_yiaddr = address;
785
786 /* Ubuntu ???*/
787 m->BootPReplyMsg.BootPHeader.bp_ciaddr = address;
788
789 /* options:
790 * - IP lease time
791 * - message type
792 * - server identifier
793 */
794 RawOption opt;
795 RT_ZERO(opt);
796
797 std::vector<RawOption> extra;
798 opt.u8OptId = RTNET_DHCP_OPT_MSG_TYPE;
799 opt.au8RawOpt[0] = RTNET_DHCP_MT_OFFER;
800 opt.cbRawOpt = 1;
801 extra.push_back(opt);
802
803 opt.u8OptId = RTNET_DHCP_OPT_LEASE_TIME;
804
805 const NetworkConfigEntity *pCfg = l.getConfig();
806 AssertPtr(pCfg);
807
808 *(uint32_t *)opt.au8RawOpt = RT_H2N_U32(pCfg->expirationPeriod());
809 opt.cbRawOpt = sizeof(RTNETADDRIPV4);
810
811 extra.push_back(opt);
812
813 processParameterReqList(client, pu8ReqList, cReqList, extra);
814
815 return doReply(client, extra);
816}
817
818/**
819 * Network manager creates DHCPACK
820 */
821int NetworkManager::ack(const Client& client, uint32_t u32Xid,
822 uint8_t *pu8ReqList, int cReqList)
823{
824 RTNETADDRIPV4 address;
825
826 prepareReplyPacket4Client(client, u32Xid);
827
828 Lease l = client.lease();
829 address = l.getAddress();
830 m->BootPReplyMsg.BootPHeader.bp_ciaddr = address;
831
832
833 /* rfc2131 4.3.1 is about DHCPDISCOVER and this value is equal to ciaddr from
834 * DHCPREQUEST or 0 ...
835 * XXX: Using addressHint is not correct way to initialize [cy]iaddress...
836 */
837 m->BootPReplyMsg.BootPHeader.bp_ciaddr = address;
838 m->BootPReplyMsg.BootPHeader.bp_yiaddr = address;
839
840 Assert(m->BootPReplyMsg.BootPHeader.bp_yiaddr.u);
841
842 /* options:
843 * - IP address lease time (if DHCPREQUEST)
844 * - message type
845 * - server identifier
846 */
847 RawOption opt;
848 RT_ZERO(opt);
849
850 std::vector<RawOption> extra;
851 opt.u8OptId = RTNET_DHCP_OPT_MSG_TYPE;
852 opt.au8RawOpt[0] = RTNET_DHCP_MT_ACK;
853 opt.cbRawOpt = 1;
854 extra.push_back(opt);
855
856 /*
857 * XXX: lease time should be conditional. If on dhcprequest then tim should be provided,
858 * else on dhcpinform it mustn't.
859 */
860 opt.u8OptId = RTNET_DHCP_OPT_LEASE_TIME;
861 *(uint32_t *)opt.au8RawOpt = RT_H2N_U32(l.getExpiration());
862 opt.cbRawOpt = sizeof(RTNETADDRIPV4);
863 extra.push_back(opt);
864
865 processParameterReqList(client, pu8ReqList, cReqList, extra);
866
867 return doReply(client, extra);
868}
869
870/**
871 * Network manager creates DHCPNAK
872 */
873int NetworkManager::nak(const Client& client, uint32_t u32Xid)
874{
875
876 Lease l = client.lease();
877 if (l == Lease::NullLease)
878 return VERR_INTERNAL_ERROR;
879
880 prepareReplyPacket4Client(client, u32Xid);
881
882 /* this field filed in prepareReplyPacket4Session, and
883 * RFC 2131 require to have it zero fo NAK.
884 */
885 m->BootPReplyMsg.BootPHeader.bp_yiaddr.u = 0;
886
887 /* options:
888 * - message type (if DHCPREQUEST)
889 * - server identifier
890 */
891 RawOption opt;
892 std::vector<RawOption> extra;
893
894 opt.u8OptId = RTNET_DHCP_OPT_MSG_TYPE;
895 opt.au8RawOpt[0] = RTNET_DHCP_MT_NAC;
896 opt.cbRawOpt = 1;
897 extra.push_back(opt);
898
899 return doReply(client, extra);
900}
901
902/**
903 *
904 */
905int NetworkManager::prepareReplyPacket4Client(const Client& client, uint32_t u32Xid)
906{
907 RT_ZERO(m->BootPReplyMsg);
908
909 m->BootPReplyMsg.BootPHeader.bp_op = RTNETBOOTP_OP_REPLY;
910 m->BootPReplyMsg.BootPHeader.bp_htype = RTNET_ARP_ETHER;
911 m->BootPReplyMsg.BootPHeader.bp_hlen = sizeof(RTMAC);
912 m->BootPReplyMsg.BootPHeader.bp_hops = 0;
913 m->BootPReplyMsg.BootPHeader.bp_xid = u32Xid;
914 m->BootPReplyMsg.BootPHeader.bp_secs = 0;
915 /* XXX: bp_flags should be processed specially */
916 m->BootPReplyMsg.BootPHeader.bp_flags = 0;
917 m->BootPReplyMsg.BootPHeader.bp_ciaddr.u = 0;
918 m->BootPReplyMsg.BootPHeader.bp_giaddr.u = 0;
919
920 m->BootPReplyMsg.BootPHeader.bp_chaddr.Mac = client.getMacAddress();
921
922 const Lease l = client.lease();
923 m->BootPReplyMsg.BootPHeader.bp_yiaddr = l.getAddress();
924 m->BootPReplyMsg.BootPHeader.bp_siaddr.u = 0;
925
926
927 m->BootPReplyMsg.BootPHeader.bp_vend.Dhcp.dhcp_cookie = RT_H2N_U32_C(RTNET_DHCP_COOKIE);
928
929 memset(&m->BootPReplyMsg.BootPHeader.bp_vend.Dhcp.dhcp_opts[0],
930 '\0',
931 RTNET_DHCP_OPT_SIZE);
932
933 return VINF_SUCCESS;
934}
935
936
937int NetworkManager::doReply(const Client& client, const std::vector<RawOption>& extra)
938{
939 int rc;
940
941 /*
942 Options....
943 */
944 VBoxNetDhcpWriteCursor Cursor(&m->BootPReplyMsg.BootPHeader, RTNET_DHCP_NORMAL_SIZE);
945
946 /* The basics */
947
948 Cursor.optIPv4Addr(RTNET_DHCP_OPT_SERVER_ID, m->m_OurAddress);
949
950 const Lease l = client.lease();
951 const std::map<uint8_t, RawOption>& options = l.options();
952
953 for(std::vector<RawOption>::const_iterator it = extra.begin();
954 it != extra.end(); ++it)
955 {
956 if (!Cursor.begin(it->u8OptId, it->cbRawOpt))
957 break;
958 Cursor.put(it->au8RawOpt, it->cbRawOpt);
959
960 }
961
962 for(std::map<uint8_t, RawOption>::const_iterator it = options.begin();
963 it != options.end(); ++it)
964 {
965 if (!Cursor.begin(it->second.u8OptId, it->second.cbRawOpt))
966 break;
967 Cursor.put(it->second.au8RawOpt, it->second.cbRawOpt);
968
969 }
970
971 Cursor.optEnd();
972
973 /*
974 */
975#if 0
976 /** @todo need to see someone set this flag to check that it's correct. */
977 if (!(pDhcpMsg->bp_flags & RTNET_DHCP_FLAGS_NO_BROADCAST))
978 {
979 rc = VBoxNetUDPUnicast(m_pSession,
980 m_hIf,
981 m_pIfBuf,
982 m_OurAddress,
983 &m_OurMac,
984 RTNETIPV4_PORT_BOOTPS, /* sender */
985 IPv4AddrBrdCast,
986 &BootPReplyMsg.BootPHeader->bp_chaddr.Mac,
987 RTNETIPV4_PORT_BOOTPC, /* receiver */
988 &BootPReplyMsg, cbBooPReplyMsg);
989 }
990 else
991#endif
992 rc = m->m_service->hlpUDPBroadcast(RTNETIPV4_PORT_BOOTPS, /* sender */
993 RTNETIPV4_PORT_BOOTPC,
994 &m->BootPReplyMsg,
995 RTNET_DHCP_NORMAL_SIZE);
996
997 AssertRCReturn(rc,rc);
998
999 return VINF_SUCCESS;
1000}
1001
1002
1003/*
1004 * XXX: TODO: Share decoding code with DHCPServer::addOption.
1005 */
1006static int parseDhcpOptionText(const char *pszText,
1007 int *pOptCode, char **ppszOptText, int *pOptEncoding)
1008{
1009 uint8_t u8Code;
1010 uint32_t u32Enc;
1011 char *pszNext;
1012 int rc;
1013
1014 rc = RTStrToUInt8Ex(pszText, &pszNext, 10, &u8Code);
1015 if (!RT_SUCCESS(rc))
1016 return VERR_PARSE_ERROR;
1017
1018 switch (*pszNext)
1019 {
1020 case ':': /* support legacy format too */
1021 {
1022 u32Enc = 0;
1023 break;
1024 }
1025
1026 case '=':
1027 {
1028 u32Enc = 1;
1029 break;
1030 }
1031
1032 case '@':
1033 {
1034 rc = RTStrToUInt32Ex(pszNext + 1, &pszNext, 10, &u32Enc);
1035 if (!RT_SUCCESS(rc))
1036 return VERR_PARSE_ERROR;
1037 if (*pszNext != '=')
1038 return VERR_PARSE_ERROR;
1039 break;
1040 }
1041
1042 default:
1043 return VERR_PARSE_ERROR;
1044 }
1045
1046 *pOptCode = u8Code;
1047 *ppszOptText = pszNext + 1;
1048 *pOptEncoding = (int)u32Enc;
1049
1050 return VINF_SUCCESS;
1051}
1052
1053
1054/*
1055 * XXX: Since encoding info is "smuggled" through the API and is not
1056 * exposed properly we don't have a common definition we can use here.
1057 *
1058 * TODO: We can define the encodings enum in the IDL without breaking
1059 * backward compatibility. This will provide the authoritative
1060 * definition.
1061 */
1062static int fillDhcpOption(RawOption &opt, const std::string &OptText, int OptEncoding)
1063{
1064 int rc;
1065
1066 if (OptEncoding == /* HEX */ 1)
1067 {
1068 if (OptText.empty())
1069 return VERR_INVALID_PARAMETER;
1070
1071 size_t cbRawOpt = 0;
1072 char *pszNext = const_cast<char *>(OptText.c_str());
1073 while (*pszNext != '\0')
1074 {
1075 if (cbRawOpt == 256)
1076 return VERR_INVALID_PARAMETER;
1077
1078 uint8_t u8Byte;
1079 rc = RTStrToUInt8Ex(pszNext, &pszNext, 16, &u8Byte);
1080 if (!RT_SUCCESS(rc))
1081 return rc;
1082
1083 if (*pszNext == ':')
1084 ++pszNext;
1085 else if (*pszNext != '\0')
1086 return VERR_PARSE_ERROR;
1087
1088 opt.au8RawOpt[cbRawOpt] = u8Byte;
1089 ++cbRawOpt;
1090 }
1091 opt.cbRawOpt = (uint8_t)cbRawOpt;
1092 }
1093 else if (OptEncoding == /* LEGACY */ 0)
1094 {
1095 /*
1096 * XXX: TODO: encode "known" option opt.u8OptId
1097 */
1098 return VERR_INVALID_PARAMETER;
1099 }
1100
1101 return VINF_SUCCESS;
1102}
1103
1104
1105int NetworkManager::processParameterReqList(const Client& client, const uint8_t *pu8ReqList,
1106 int cReqList, std::vector<RawOption>& extra)
1107{
1108 int rc;
1109
1110 const Lease l = client.lease();
1111
1112 const NetworkConfigEntity *pNetCfg = l.getConfig();
1113
1114 /*
1115 * XXX: Brute-force. Unfortunately, there's no notification event
1116 * for changes. Should at least cache the options for a short
1117 * time, enough to last discover/offer/request/ack cycle.
1118 */
1119 typedef std::map< int, std::pair<std::string, int> > DhcpOptionMap;
1120 DhcpOptionMap OptMap;
1121
1122 if (!m->m_DhcpServer.isNull())
1123 {
1124 com::SafeArray<BSTR> strings;
1125 com::Bstr str;
1126 HRESULT hrc;
1127 int OptCode, OptEncoding;
1128 char *pszOptText;
1129
1130 strings.setNull();
1131 hrc = m->m_DhcpServer->COMGETTER(GlobalOptions)(ComSafeArrayAsOutParam(strings));
1132 AssertComRC(hrc);
1133 for (size_t i = 0; i < strings.size(); ++i)
1134 {
1135 com::Utf8Str encoded(strings[i]);
1136 rc = parseDhcpOptionText(encoded.c_str(),
1137 &OptCode, &pszOptText, &OptEncoding);
1138 if (!RT_SUCCESS(rc))
1139 continue;
1140
1141 OptMap[OptCode] = std::make_pair(pszOptText, OptEncoding);
1142 }
1143
1144 const RTMAC &mac = client.getMacAddress();
1145 char strMac[6*2+1] = "";
1146 RTStrPrintf(strMac, sizeof(strMac), "%02x%02x%02x%02x%02x%02x",
1147 mac.au8[0], mac.au8[1], mac.au8[2],
1148 mac.au8[3], mac.au8[4], mac.au8[5]);
1149
1150 strings.setNull();
1151 hrc = m->m_DhcpServer->GetMacOptions(com::Bstr(strMac).raw(),
1152 ComSafeArrayAsOutParam(strings));
1153 AssertComRC(hrc);
1154 for (size_t i = 0; i < strings.size(); ++i)
1155 {
1156 com::Utf8Str text(strings[i]);
1157 rc = parseDhcpOptionText(text.c_str(),
1158 &OptCode, &pszOptText, &OptEncoding);
1159 if (!RT_SUCCESS(rc))
1160 continue;
1161
1162 OptMap[OptCode] = std::make_pair(pszOptText, OptEncoding);
1163 }
1164 }
1165
1166 /* request parameter list */
1167 RawOption opt;
1168 bool fIgnore;
1169 uint8_t u8Req;
1170 for (int idxParam = 0; idxParam < cReqList; ++idxParam)
1171 {
1172 fIgnore = false;
1173 RT_ZERO(opt);
1174 u8Req = opt.u8OptId = pu8ReqList[idxParam];
1175
1176 switch(u8Req)
1177 {
1178 case RTNET_DHCP_OPT_SUBNET_MASK:
1179 ((PRTNETADDRIPV4)opt.au8RawOpt)->u = pNetCfg->netmask().u;
1180 opt.cbRawOpt = sizeof(RTNETADDRIPV4);
1181
1182 break;
1183
1184 case RTNET_DHCP_OPT_ROUTERS:
1185 case RTNET_DHCP_OPT_DNS:
1186 {
1187 const Ipv4AddressContainer lst =
1188 g_ConfigurationManager->getAddressList(u8Req);
1189 PRTNETADDRIPV4 pAddresses = (PRTNETADDRIPV4)&opt.au8RawOpt[0];
1190
1191 for (Ipv4AddressConstIterator it = lst.begin();
1192 it != lst.end();
1193 ++it)
1194 {
1195 *pAddresses = (*it);
1196 pAddresses++;
1197 opt.cbRawOpt += sizeof(RTNETADDRIPV4);
1198 }
1199
1200 if (lst.empty())
1201 fIgnore = true;
1202 }
1203 break;
1204 case RTNET_DHCP_OPT_DOMAIN_NAME:
1205 {
1206 std::string domainName = g_ConfigurationManager->getString(u8Req);
1207 if (domainName == g_ConfigurationManager->m_noString)
1208 {
1209 fIgnore = true;
1210 break;
1211 }
1212
1213 char *pszDomainName = (char *)&opt.au8RawOpt[0];
1214
1215 strcpy(pszDomainName, domainName.c_str());
1216 opt.cbRawOpt = domainName.length();
1217 }
1218 break;
1219 default:
1220 {
1221 DhcpOptionMap::const_iterator it = OptMap.find((int)u8Req);
1222 if (it == OptMap.end())
1223 {
1224 Log(("opt: %d is ignored\n", u8Req));
1225 fIgnore = true;
1226 }
1227 else
1228 {
1229 std::string OptText((*it).second.first);
1230 int OptEncoding((*it).second.second);
1231
1232 rc = fillDhcpOption(opt, OptText, OptEncoding);
1233 if (!RT_SUCCESS(rc))
1234 {
1235 fIgnore = true;
1236 break;
1237 }
1238 }
1239 }
1240 break;
1241 }
1242
1243 if (!fIgnore)
1244 extra.push_back(opt);
1245
1246 }
1247
1248 return VINF_SUCCESS;
1249}
1250
1251/* Client */
1252Client::Client()
1253{
1254 m = SharedPtr<ClientData>();
1255}
1256
1257
1258void Client::initWithMac(const RTMAC& mac)
1259{
1260 m = SharedPtr<ClientData>(new ClientData());
1261 m->m_mac = mac;
1262}
1263
1264
1265bool Client::operator== (const RTMAC& mac) const
1266{
1267 return (m.get() && m->m_mac == mac);
1268}
1269
1270
1271const RTMAC& Client::getMacAddress() const
1272{
1273 return m->m_mac;
1274}
1275
1276
1277Lease Client::lease()
1278{
1279 if (!m.get()) return Lease::NullLease;
1280
1281 if (m->fHasLease)
1282 return Lease(*this);
1283 else
1284 return Lease::NullLease;
1285}
1286
1287
1288const Lease Client::lease() const
1289{
1290 return const_cast<Client *>(this)->lease();
1291}
1292
1293
1294Client::Client(ClientData *data):m(SharedPtr<ClientData>(data)){}
1295
1296/* Lease */
1297Lease::Lease()
1298{
1299 m = SharedPtr<ClientData>();
1300}
1301
1302
1303Lease::Lease (const Client& c)
1304{
1305 m = SharedPtr<ClientData>(c.m);
1306 if ( !m->fHasLease
1307 || ( isExpired()
1308 && !isInBindingPhase()))
1309 {
1310 m->fHasLease = true;
1311 m->fBinding = true;
1312 phaseStart(RTTimeMilliTS());
1313 }
1314}
1315
1316
1317bool Lease::isExpired() const
1318{
1319 AssertPtrReturn(m.get(), false);
1320
1321 if (!m->fBinding)
1322 return (ASMDivU64ByU32RetU32(RTTimeMilliTS() - m->u64TimestampLeasingStarted, 1000)
1323 > m->u32LeaseExpirationPeriod);
1324 else
1325 return (ASMDivU64ByU32RetU32(RTTimeMilliTS() - m->u64TimestampBindingStarted, 1000)
1326 > m->u32BindExpirationPeriod);
1327}
1328
1329
1330void Lease::expire()
1331{
1332 /* XXX: TODO */
1333}
1334
1335
1336void Lease::phaseStart(uint64_t u64Start)
1337{
1338 if (m->fBinding)
1339 m->u64TimestampBindingStarted = u64Start;
1340 else
1341 m->u64TimestampLeasingStarted = u64Start;
1342}
1343
1344
1345void Lease::bindingPhase(bool fOnOff)
1346{
1347 m->fBinding = fOnOff;
1348}
1349
1350
1351bool Lease::isInBindingPhase() const
1352{
1353 return m->fBinding;
1354}
1355
1356
1357uint64_t Lease::issued() const
1358{
1359 return m->u64TimestampLeasingStarted;
1360}
1361
1362
1363void Lease::setExpiration(uint32_t exp)
1364{
1365 if (m->fBinding)
1366 m->u32BindExpirationPeriod = exp;
1367 else
1368 m->u32LeaseExpirationPeriod = exp;
1369}
1370
1371
1372uint32_t Lease::getExpiration() const
1373{
1374 if (m->fBinding)
1375 return m->u32BindExpirationPeriod;
1376 else
1377 return m->u32LeaseExpirationPeriod;
1378}
1379
1380
1381RTNETADDRIPV4 Lease::getAddress() const
1382{
1383 return m->m_address;
1384}
1385
1386
1387void Lease::setAddress(RTNETADDRIPV4 address)
1388{
1389 m->m_address = address;
1390}
1391
1392
1393const NetworkConfigEntity *Lease::getConfig() const
1394{
1395 return m->pCfg;
1396}
1397
1398
1399void Lease::setConfig(NetworkConfigEntity *pCfg)
1400{
1401 m->pCfg = pCfg;
1402}
1403
1404
1405const MapOptionId2RawOption& Lease::options() const
1406{
1407 return m->options;
1408}
1409
1410
1411Lease::Lease(ClientData *pd):m(SharedPtr<ClientData>(pd)){}
1412
1413
1414bool Lease::toXML(xml::ElementNode *node) const
1415{
1416 bool valueAddition = node->setAttribute(tagXMLLeaseAttributeMac.c_str(), com::Utf8StrFmt("%RTmac", &m->m_mac));
1417 if (!valueAddition) return false;
1418
1419 valueAddition = node->setAttribute(tagXMLLeaseAttributeNetwork.c_str(), com::Utf8StrFmt("%RTnaipv4", m->m_network));
1420 if (!valueAddition) return false;
1421
1422 xml::ElementNode *address = node->createChild(tagXMLLeaseAddress.c_str());
1423 if (!address) return false;
1424
1425 valueAddition = address->setAttribute(tagXMLAddressAttributeValue.c_str(), com::Utf8StrFmt("%RTnaipv4", m->m_address));
1426 if (!valueAddition) return false;
1427
1428 xml::ElementNode *time = node->createChild(tagXMLLeaseTime.c_str());
1429 if (!time) return false;
1430
1431 valueAddition = time->setAttribute(tagXMLTimeAttributeIssued.c_str(),
1432 m->u64TimestampLeasingStarted);
1433 if (!valueAddition) return false;
1434
1435 valueAddition = time->setAttribute(tagXMLTimeAttributeExpiration.c_str(),
1436 m->u32LeaseExpirationPeriod);
1437 if (!valueAddition) return false;
1438
1439 return true;
1440}
1441
1442
1443bool Lease::fromXML(const xml::ElementNode *node)
1444{
1445 com::Utf8Str mac;
1446 bool valueExists = node->getAttributeValue(tagXMLLeaseAttributeMac.c_str(), mac);
1447 if (!valueExists) return false;
1448 int rc = RTNetStrToMacAddr(mac.c_str(), &m->m_mac);
1449 if (RT_FAILURE(rc)) return false;
1450
1451 com::Utf8Str network;
1452 valueExists = node->getAttributeValue(tagXMLLeaseAttributeNetwork.c_str(), network);
1453 if (!valueExists) return false;
1454 rc = RTNetStrToIPv4Addr(network.c_str(), &m->m_network);
1455 if (RT_FAILURE(rc)) return false;
1456
1457 /* Address */
1458 const xml::ElementNode *address = node->findChildElement(tagXMLLeaseAddress.c_str());
1459 if (!address) return false;
1460 com::Utf8Str addressValue;
1461 valueExists = address->getAttributeValue(tagXMLAddressAttributeValue.c_str(), addressValue);
1462 if (!valueExists) return false;
1463 rc = RTNetStrToIPv4Addr(addressValue.c_str(), &m->m_address);
1464
1465 /* Time */
1466 const xml::ElementNode *time = node->findChildElement(tagXMLLeaseTime.c_str());
1467 if (!time) return false;
1468
1469 valueExists = time->getAttributeValue(tagXMLTimeAttributeIssued.c_str(),
1470 &m->u64TimestampLeasingStarted);
1471 if (!valueExists) return false;
1472 m->fBinding = false;
1473
1474 valueExists = time->getAttributeValue(tagXMLTimeAttributeExpiration.c_str(),
1475 &m->u32LeaseExpirationPeriod);
1476 if (!valueExists) return false;
1477
1478 m->fHasLease = true;
1479 return true;
1480}
1481
1482
1483const Lease Lease::NullLease;
1484
1485const 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