VirtualBox

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

Last change on this file since 66995 was 65659, checked in by vboxsync, 8 years ago

VBoxNetDHCP: fix option iteration.

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