VirtualBox

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

Last change on this file since 57236 was 56318, checked in by vboxsync, 10 years ago

whitespace

  • 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 56318 2015-06-09 22:54:50Z vboxsync $ */
2/** @file
3 * Configuration for DHCP.
4 */
5
6/*
7 * Copyright (C) 2013-2015 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 const xml::ElementNode *lease;
222 while ((lease = leases.forAllNodes()))
223 {
224 if (!lease->nameEquals(tagXMLLease.c_str()))
225 continue;
226
227 ClientData *data = new ClientData();
228 Lease l(data);
229 if (l.fromXML(lease))
230 {
231
232 m->m_allocations.insert(MapLease2Ip4AddressPair(l, l.getAddress()));
233
234
235 NetworkConfigEntity *pNetCfg = NULL;
236 Client c(data);
237 int rc = g_RootConfig->match(c, (BaseConfigEntity **)&pNetCfg);
238 Assert(rc >= 0 && pNetCfg);
239
240 l.setConfig(pNetCfg);
241
242 m->m_clients.push_back(c);
243 }
244 }
245
246 return VINF_SUCCESS;
247}
248
249
250int ConfigurationManager::saveToFile()
251{
252 if (m->m_leaseStorageFilename.isEmpty())
253 return VINF_SUCCESS;
254
255 xml::Document doc;
256
257 xml::ElementNode *root = doc.createRootElement(tagXMLLeases.c_str());
258 if (!root)
259 return VERR_INTERNAL_ERROR;
260
261 root->setAttribute(tagXMLLeasesAttributeVersion.c_str(), tagXMLLeasesVersion_1_0.c_str());
262
263 for(MapLease2Ip4AddressConstIterator it = m->m_allocations.begin();
264 it != m->m_allocations.end(); ++it)
265 {
266 xml::ElementNode *lease = root->createChild(tagXMLLease.c_str());
267 if (!it->first.toXML(lease))
268 {
269 /* XXX: todo logging + error handling */
270 }
271 }
272
273 try {
274 xml::XmlFileWriter writer(doc);
275 writer.write(m->m_leaseStorageFilename.c_str(), true);
276 } catch(...){}
277
278 return VINF_SUCCESS;
279}
280
281
282int ConfigurationManager::extractRequestList(PCRTNETBOOTP pDhcpMsg, size_t cbDhcpMsg, RawOption& rawOpt)
283{
284 return ConfigurationManager::findOption(RTNET_DHCP_OPT_PARAM_REQ_LIST, pDhcpMsg, cbDhcpMsg, rawOpt);
285}
286
287
288Client ConfigurationManager::getClientByDhcpPacket(const RTNETBOOTP *pDhcpMsg, size_t cbDhcpMsg)
289{
290
291 VecClientIterator it;
292 bool fDhcpValid = false;
293 uint8_t uMsgType = 0;
294
295 fDhcpValid = RTNetIPv4IsDHCPValid(NULL, pDhcpMsg, cbDhcpMsg, &uMsgType);
296 AssertReturn(fDhcpValid, Client::NullClient);
297
298 LogFlowFunc(("dhcp:mac:%RTmac\n", &pDhcpMsg->bp_chaddr.Mac));
299 /* 1st. client IDs */
300 for ( it = m->m_clients.begin();
301 it != m->m_clients.end();
302 ++it)
303 {
304 if ((*it) == pDhcpMsg->bp_chaddr.Mac)
305 {
306 LogFlowFunc(("client:mac:%RTmac\n", it->getMacAddress()));
307 /* check timestamp that request wasn't expired. */
308 return (*it);
309 }
310 }
311
312 if (it == m->m_clients.end())
313 {
314 /* We hasn't got any session for this client */
315 Client c;
316 c.initWithMac(pDhcpMsg->bp_chaddr.Mac);
317 m->m_clients.push_back(c);
318 return m->m_clients.back();
319 }
320
321 return Client::NullClient;
322}
323
324/**
325 * Finds an option.
326 *
327 * @returns On success, a pointer to the first byte in the option data (no none
328 * then it'll be the byte following the 0 size field) and *pcbOpt set
329 * to the option length.
330 * On failure, NULL is returned and *pcbOpt unchanged.
331 *
332 * @param uOption The option to search for.
333 * @param pDhcpMsg The DHCP message.
334 * that this is adjusted if the option length is larger
335 * than the message buffer.
336 */
337int
338ConfigurationManager::findOption(uint8_t uOption, PCRTNETBOOTP pDhcpMsg, size_t cbDhcpMsg, RawOption& opt)
339{
340 Assert(uOption != RTNET_DHCP_OPT_PAD);
341
342 /*
343 * Validate the DHCP bits and figure the max size of the options in the vendor field.
344 */
345 if (cbDhcpMsg <= RT_UOFFSETOF(RTNETBOOTP, bp_vend.Dhcp.dhcp_opts))
346 return VERR_INVALID_PARAMETER;
347
348 if (pDhcpMsg->bp_vend.Dhcp.dhcp_cookie != RT_H2N_U32_C(RTNET_DHCP_COOKIE))
349 return VERR_INVALID_PARAMETER;
350
351 size_t cbLeft = cbDhcpMsg - RT_UOFFSETOF(RTNETBOOTP, bp_vend.Dhcp.dhcp_opts);
352 if (cbLeft > RTNET_DHCP_OPT_SIZE)
353 cbLeft = RTNET_DHCP_OPT_SIZE;
354
355 /*
356 * Search the vendor field.
357 */
358 bool fExtended = false;
359 uint8_t const *pb = &pDhcpMsg->bp_vend.Dhcp.dhcp_opts[0];
360 while (pb && cbLeft > 0)
361 {
362 uint8_t uCur = *pb;
363 if (uCur == RTNET_DHCP_OPT_PAD)
364 {
365 cbLeft--;
366 pb++;
367 }
368 else if (cbLeft <= 1)
369 break;
370 else
371 {
372 size_t cbCur = pb[1];
373 if (cbCur > cbLeft - 2)
374 cbCur = cbLeft - 2;
375 if (uCur == uOption)
376 {
377 opt.u8OptId = uCur;
378 memcpy(opt.au8RawOpt, pb+2, cbCur);
379 opt.cbRawOpt = cbCur;
380 return VINF_SUCCESS;
381 }
382 pb += cbCur + 2;
383 cbLeft -= cbCur - 2;
384 }
385 }
386
387 /** @todo search extended dhcp option field(s) when present */
388
389 return VERR_NOT_FOUND;
390}
391
392
393/**
394 * We bind lease for client till it continue with it on DHCPREQUEST.
395 */
396Lease ConfigurationManager::allocateLease4Client(const Client& client, PCRTNETBOOTP pDhcpMsg, size_t cbDhcpMsg)
397{
398 {
399 /**
400 * This mean that client has already bound or commited lease.
401 * If we've it happens it means that we received DHCPDISCOVER twice.
402 */
403 const Lease l = client.lease();
404 if (l != Lease::NullLease)
405 {
406 /* Here we should take lease from the m_allocation which was feed with leases
407 * on start
408 */
409 if (l.isExpired())
410 {
411 expireLease4Client(const_cast<Client&>(client));
412 if (!l.isExpired())
413 return l;
414 }
415 else
416 {
417 AssertReturn(l.getAddress().u != 0, Lease::NullLease);
418 return l;
419 }
420 }
421 }
422
423 RTNETADDRIPV4 hintAddress;
424 RawOption opt;
425 NetworkConfigEntity *pNetCfg;
426
427 Client cl(client);
428 AssertReturn(g_RootConfig->match(cl, (BaseConfigEntity **)&pNetCfg) > 0, Lease::NullLease);
429
430 /* DHCPDISCOVER MAY contain request address */
431 hintAddress.u = 0;
432 int rc = findOption(RTNET_DHCP_OPT_REQ_ADDR, pDhcpMsg, cbDhcpMsg, opt);
433 if (RT_SUCCESS(rc))
434 {
435 hintAddress.u = *(uint32_t *)opt.au8RawOpt;
436 if ( RT_H2N_U32(hintAddress.u) < RT_H2N_U32(pNetCfg->lowerIp().u)
437 || RT_H2N_U32(hintAddress.u) > RT_H2N_U32(pNetCfg->upperIp().u))
438 hintAddress.u = 0; /* clear hint */
439 }
440
441 if ( hintAddress.u
442 && !isAddressTaken(hintAddress))
443 {
444 Lease l(cl);
445 l.setConfig(pNetCfg);
446 l.setAddress(hintAddress);
447 m->m_allocations.insert(MapLease2Ip4AddressPair(l, hintAddress));
448 return l;
449 }
450
451 uint32_t u32 = 0;
452 for(u32 = RT_H2N_U32(pNetCfg->lowerIp().u);
453 u32 <= RT_H2N_U32(pNetCfg->upperIp().u);
454 ++u32)
455 {
456 RTNETADDRIPV4 address;
457 address.u = RT_H2N_U32(u32);
458 if (!isAddressTaken(address))
459 {
460 Lease l(cl);
461 l.setConfig(pNetCfg);
462 l.setAddress(address);
463 m->m_allocations.insert(MapLease2Ip4AddressPair(l, address));
464 return l;
465 }
466 }
467
468 return Lease::NullLease;
469}
470
471
472int ConfigurationManager::commitLease4Client(Client& client)
473{
474 Lease l = client.lease();
475 AssertReturn(l != Lease::NullLease, VERR_INTERNAL_ERROR);
476
477 l.bindingPhase(false);
478 const NetworkConfigEntity *pCfg = l.getConfig();
479
480 AssertPtr(pCfg);
481 l.setExpiration(pCfg->expirationPeriod());
482 l.phaseStart(RTTimeMilliTS());
483
484 saveToFile();
485
486 return VINF_SUCCESS;
487}
488
489
490int ConfigurationManager::expireLease4Client(Client& client)
491{
492 Lease l = client.lease();
493 AssertReturn(l != Lease::NullLease, VERR_INTERNAL_ERROR);
494
495 if (l.isInBindingPhase())
496 {
497
498 MapLease2Ip4AddressIterator it = m->m_allocations.find(l);
499 AssertReturn(it != m->m_allocations.end(), VERR_NOT_FOUND);
500
501 /*
502 * XXX: perhaps it better to keep this allocation ????
503 */
504 m->m_allocations.erase(it);
505
506 l.expire();
507 return VINF_SUCCESS;
508 }
509
510 l = Lease(client); /* re-new */
511 return VINF_SUCCESS;
512}
513
514
515bool ConfigurationManager::isAddressTaken(const RTNETADDRIPV4& addr, Lease& lease)
516{
517 MapLease2Ip4AddressIterator it;
518
519 for (it = m->m_allocations.begin();
520 it != m->m_allocations.end();
521 ++it)
522 {
523 if (it->second.u == addr.u)
524 {
525 if (lease != Lease::NullLease)
526 lease = it->first;
527
528 return true;
529 }
530 }
531 lease = Lease::NullLease;
532 return false;
533}
534
535
536bool ConfigurationManager::isAddressTaken(const RTNETADDRIPV4& addr)
537{
538 Lease ignore;
539 return isAddressTaken(addr, ignore);
540}
541
542
543NetworkConfigEntity *ConfigurationManager::addNetwork(NetworkConfigEntity *,
544 const RTNETADDRIPV4& networkId,
545 const RTNETADDRIPV4& netmask,
546 RTNETADDRIPV4& LowerAddress,
547 RTNETADDRIPV4& UpperAddress)
548{
549 static int id;
550 char name[64];
551
552 RTStrPrintf(name, RT_ELEMENTS(name), "network-%d", id);
553 std::string strname(name);
554 id++;
555
556
557 if (!LowerAddress.u)
558 LowerAddress = networkId;
559
560 if (!UpperAddress.u)
561 UpperAddress.u = networkId.u | (~netmask.u);
562
563 return new NetworkConfigEntity(strname,
564 g_RootConfig,
565 g_AnyClient,
566 5,
567 networkId,
568 netmask,
569 LowerAddress,
570 UpperAddress);
571}
572
573HostConfigEntity *ConfigurationManager::addHost(NetworkConfigEntity* pCfg,
574 const RTNETADDRIPV4& address,
575 ClientMatchCriteria *criteria)
576{
577 static int id;
578 char name[64];
579
580 RTStrPrintf(name, RT_ELEMENTS(name), "host-%d", id);
581 std::string strname(name);
582 id++;
583
584 return new HostConfigEntity(address, strname, pCfg, criteria);
585}
586
587int ConfigurationManager::addToAddressList(uint8_t u8OptId, RTNETADDRIPV4& address)
588{
589 switch(u8OptId)
590 {
591 case RTNET_DHCP_OPT_DNS:
592 m->m_nameservers.push_back(address);
593 break;
594 case RTNET_DHCP_OPT_ROUTERS:
595 m->m_routers.push_back(address);
596 break;
597 default:
598 Log(("dhcp-opt: list (%d) unsupported\n", u8OptId));
599 }
600 return VINF_SUCCESS;
601}
602
603
604int ConfigurationManager::flushAddressList(uint8_t u8OptId)
605{
606 switch(u8OptId)
607 {
608 case RTNET_DHCP_OPT_DNS:
609 m->m_nameservers.clear();
610 break;
611 case RTNET_DHCP_OPT_ROUTERS:
612 m->m_routers.clear();
613 break;
614 default:
615 Log(("dhcp-opt: list (%d) unsupported\n", u8OptId));
616 }
617 return VINF_SUCCESS;
618}
619
620
621const Ipv4AddressContainer& ConfigurationManager::getAddressList(uint8_t u8OptId)
622{
623 switch(u8OptId)
624 {
625 case RTNET_DHCP_OPT_DNS:
626 return m->m_nameservers;
627
628 case RTNET_DHCP_OPT_ROUTERS:
629 return m->m_routers;
630
631 }
632 /* XXX: Grrr !!! */
633 return m_empty;
634}
635
636
637int ConfigurationManager::setString(uint8_t u8OptId, const std::string& str)
638{
639 switch (u8OptId)
640 {
641 case RTNET_DHCP_OPT_DOMAIN_NAME:
642 m->m_domainName = str;
643 break;
644 default:
645 break;
646 }
647
648 return VINF_SUCCESS;
649}
650
651
652const std::string& ConfigurationManager::getString(uint8_t u8OptId)
653{
654 switch (u8OptId)
655 {
656 case RTNET_DHCP_OPT_DOMAIN_NAME:
657 if (m->m_domainName.length())
658 return m->m_domainName;
659 else
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 char *pszDomainName = (char *)&opt.au8RawOpt[0];
1205
1206 strcpy(pszDomainName, domainName.c_str());
1207 opt.cbRawOpt = domainName.length();
1208 }
1209 break;
1210 default:
1211 {
1212 DhcpOptionMap::const_iterator it = OptMap.find((int)u8Req);
1213 if (it == OptMap.end())
1214 {
1215 Log(("opt: %d is ignored\n", u8Req));
1216 fIgnore = true;
1217 }
1218 else
1219 {
1220 std::string OptText((*it).second.first);
1221 int OptEncoding((*it).second.second);
1222
1223 rc = fillDhcpOption(opt, OptText, OptEncoding);
1224 if (!RT_SUCCESS(rc))
1225 {
1226 fIgnore = true;
1227 break;
1228 }
1229 }
1230 }
1231 break;
1232 }
1233
1234 if (!fIgnore)
1235 extra.push_back(opt);
1236
1237 }
1238
1239 return VINF_SUCCESS;
1240}
1241
1242/* Client */
1243Client::Client()
1244{
1245 m = SharedPtr<ClientData>();
1246}
1247
1248
1249void Client::initWithMac(const RTMAC& mac)
1250{
1251 m = SharedPtr<ClientData>(new ClientData());
1252 m->m_mac = mac;
1253}
1254
1255
1256bool Client::operator== (const RTMAC& mac) const
1257{
1258 return (m.get() && m->m_mac == mac);
1259}
1260
1261
1262const RTMAC& Client::getMacAddress() const
1263{
1264 return m->m_mac;
1265}
1266
1267
1268Lease Client::lease()
1269{
1270 if (!m.get()) return Lease::NullLease;
1271
1272 if (m->fHasLease)
1273 return Lease(*this);
1274 else
1275 return Lease::NullLease;
1276}
1277
1278
1279const Lease Client::lease() const
1280{
1281 return const_cast<Client *>(this)->lease();
1282}
1283
1284
1285Client::Client(ClientData *data):m(SharedPtr<ClientData>(data)){}
1286
1287/* Lease */
1288Lease::Lease()
1289{
1290 m = SharedPtr<ClientData>();
1291}
1292
1293
1294Lease::Lease (const Client& c)
1295{
1296 m = SharedPtr<ClientData>(c.m);
1297 if ( !m->fHasLease
1298 || ( isExpired()
1299 && !isInBindingPhase()))
1300 {
1301 m->fHasLease = true;
1302 m->fBinding = true;
1303 phaseStart(RTTimeMilliTS());
1304 }
1305}
1306
1307
1308bool Lease::isExpired() const
1309{
1310 AssertPtrReturn(m.get(), false);
1311
1312 if (!m->fBinding)
1313 return (ASMDivU64ByU32RetU32(RTTimeMilliTS() - m->u64TimestampLeasingStarted, 1000)
1314 > m->u32LeaseExpirationPeriod);
1315 else
1316 return (ASMDivU64ByU32RetU32(RTTimeMilliTS() - m->u64TimestampBindingStarted, 1000)
1317 > m->u32BindExpirationPeriod);
1318}
1319
1320
1321void Lease::expire()
1322{
1323 /* XXX: TODO */
1324}
1325
1326
1327void Lease::phaseStart(uint64_t u64Start)
1328{
1329 if (m->fBinding)
1330 m->u64TimestampBindingStarted = u64Start;
1331 else
1332 m->u64TimestampLeasingStarted = u64Start;
1333}
1334
1335
1336void Lease::bindingPhase(bool fOnOff)
1337{
1338 m->fBinding = fOnOff;
1339}
1340
1341
1342bool Lease::isInBindingPhase() const
1343{
1344 return m->fBinding;
1345}
1346
1347
1348uint64_t Lease::issued() const
1349{
1350 return m->u64TimestampLeasingStarted;
1351}
1352
1353
1354void Lease::setExpiration(uint32_t exp)
1355{
1356 if (m->fBinding)
1357 m->u32BindExpirationPeriod = exp;
1358 else
1359 m->u32LeaseExpirationPeriod = exp;
1360}
1361
1362
1363uint32_t Lease::getExpiration() const
1364{
1365 if (m->fBinding)
1366 return m->u32BindExpirationPeriod;
1367 else
1368 return m->u32LeaseExpirationPeriod;
1369}
1370
1371
1372RTNETADDRIPV4 Lease::getAddress() const
1373{
1374 return m->m_address;
1375}
1376
1377
1378void Lease::setAddress(RTNETADDRIPV4 address)
1379{
1380 m->m_address = address;
1381}
1382
1383
1384const NetworkConfigEntity *Lease::getConfig() const
1385{
1386 return m->pCfg;
1387}
1388
1389
1390void Lease::setConfig(NetworkConfigEntity *pCfg)
1391{
1392 m->pCfg = pCfg;
1393}
1394
1395
1396const MapOptionId2RawOption& Lease::options() const
1397{
1398 return m->options;
1399}
1400
1401
1402Lease::Lease(ClientData *pd):m(SharedPtr<ClientData>(pd)){}
1403
1404
1405bool Lease::toXML(xml::ElementNode *node) const
1406{
1407 xml::AttributeNode *pAttribNode = node->setAttribute(tagXMLLeaseAttributeMac.c_str(),
1408 com::Utf8StrFmt("%RTmac", &m->m_mac));
1409 if (!pAttribNode)
1410 return false;
1411
1412 pAttribNode = node->setAttribute(tagXMLLeaseAttributeNetwork.c_str(),
1413 com::Utf8StrFmt("%RTnaipv4", m->m_network));
1414 if (!pAttribNode)
1415 return false;
1416
1417 xml::ElementNode *pLeaseAddress = node->createChild(tagXMLLeaseAddress.c_str());
1418 if (!pLeaseAddress)
1419 return false;
1420
1421 pAttribNode = pLeaseAddress->setAttribute(tagXMLAddressAttributeValue.c_str(),
1422 com::Utf8StrFmt("%RTnaipv4", m->m_address));
1423 if (!pAttribNode)
1424 return false;
1425
1426 xml::ElementNode *pLeaseTime = node->createChild(tagXMLLeaseTime.c_str());
1427 if (!pLeaseTime)
1428 return false;
1429
1430 pAttribNode = pLeaseTime->setAttribute(tagXMLTimeAttributeIssued.c_str(),
1431 m->u64TimestampLeasingStarted);
1432 if (!pAttribNode)
1433 return false;
1434
1435 pAttribNode = pLeaseTime->setAttribute(tagXMLTimeAttributeExpiration.c_str(),
1436 m->u32LeaseExpirationPeriod);
1437 if (!pAttribNode)
1438 return false;
1439
1440 return true;
1441}
1442
1443
1444bool Lease::fromXML(const xml::ElementNode *node)
1445{
1446 com::Utf8Str mac;
1447 bool valueExists = node->getAttributeValue(tagXMLLeaseAttributeMac.c_str(), mac);
1448 if (!valueExists) return false;
1449 int rc = RTNetStrToMacAddr(mac.c_str(), &m->m_mac);
1450 if (RT_FAILURE(rc)) return false;
1451
1452 com::Utf8Str network;
1453 valueExists = node->getAttributeValue(tagXMLLeaseAttributeNetwork.c_str(), network);
1454 if (!valueExists) return false;
1455 rc = RTNetStrToIPv4Addr(network.c_str(), &m->m_network);
1456 if (RT_FAILURE(rc)) return false;
1457
1458 /* Address */
1459 const xml::ElementNode *address = node->findChildElement(tagXMLLeaseAddress.c_str());
1460 if (!address) return false;
1461 com::Utf8Str addressValue;
1462 valueExists = address->getAttributeValue(tagXMLAddressAttributeValue.c_str(), addressValue);
1463 if (!valueExists) return false;
1464 rc = RTNetStrToIPv4Addr(addressValue.c_str(), &m->m_address);
1465
1466 /* Time */
1467 const xml::ElementNode *time = node->findChildElement(tagXMLLeaseTime.c_str());
1468 if (!time) return false;
1469
1470 valueExists = time->getAttributeValue(tagXMLTimeAttributeIssued.c_str(),
1471 &m->u64TimestampLeasingStarted);
1472 if (!valueExists) return false;
1473 m->fBinding = false;
1474
1475 valueExists = time->getAttributeValue(tagXMLTimeAttributeExpiration.c_str(),
1476 &m->u32LeaseExpirationPeriod);
1477 if (!valueExists) return false;
1478
1479 m->fHasLease = true;
1480 return true;
1481}
1482
1483
1484const Lease Lease::NullLease;
1485
1486const 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