VirtualBox

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

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

DHCPServer: Define DHCP option encodings as an enum. The enum is not
currently used in the API itself as we only smugle hex-encoded options
through existing API to remain backward compatible, but it provides
authoritative C definitions that we can now use on both sides of the API.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 37.2 KB
Line 
1/* $Id: Config.cpp 54584 2015-03-02 16:45:57Z 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
1054static int fillDhcpOption(RawOption &opt, const std::string &OptText, int OptEncoding)
1055{
1056 int rc;
1057
1058 if (OptEncoding == DhcpOptEncoding_Hex)
1059 {
1060 if (OptText.empty())
1061 return VERR_INVALID_PARAMETER;
1062
1063 size_t cbRawOpt = 0;
1064 char *pszNext = const_cast<char *>(OptText.c_str());
1065 while (*pszNext != '\0')
1066 {
1067 if (cbRawOpt == 256)
1068 return VERR_INVALID_PARAMETER;
1069
1070 uint8_t u8Byte;
1071 rc = RTStrToUInt8Ex(pszNext, &pszNext, 16, &u8Byte);
1072 if (!RT_SUCCESS(rc))
1073 return rc;
1074
1075 if (*pszNext == ':')
1076 ++pszNext;
1077 else if (*pszNext != '\0')
1078 return VERR_PARSE_ERROR;
1079
1080 opt.au8RawOpt[cbRawOpt] = u8Byte;
1081 ++cbRawOpt;
1082 }
1083 opt.cbRawOpt = (uint8_t)cbRawOpt;
1084 }
1085 else if (OptEncoding == DhcpOptEncoding_Legacy)
1086 {
1087 /*
1088 * XXX: TODO: encode "known" option opt.u8OptId
1089 */
1090 return VERR_INVALID_PARAMETER;
1091 }
1092
1093 return VINF_SUCCESS;
1094}
1095
1096
1097int NetworkManager::processParameterReqList(const Client& client, const uint8_t *pu8ReqList,
1098 int cReqList, std::vector<RawOption>& extra)
1099{
1100 int rc;
1101
1102 const Lease l = client.lease();
1103
1104 const NetworkConfigEntity *pNetCfg = l.getConfig();
1105
1106 /*
1107 * XXX: Brute-force. Unfortunately, there's no notification event
1108 * for changes. Should at least cache the options for a short
1109 * time, enough to last discover/offer/request/ack cycle.
1110 */
1111 typedef std::map< int, std::pair<std::string, int> > DhcpOptionMap;
1112 DhcpOptionMap OptMap;
1113
1114 if (!m->m_DhcpServer.isNull())
1115 {
1116 com::SafeArray<BSTR> strings;
1117 com::Bstr str;
1118 HRESULT hrc;
1119 int OptCode, OptEncoding;
1120 char *pszOptText;
1121
1122 strings.setNull();
1123 hrc = m->m_DhcpServer->COMGETTER(GlobalOptions)(ComSafeArrayAsOutParam(strings));
1124 AssertComRC(hrc);
1125 for (size_t i = 0; i < strings.size(); ++i)
1126 {
1127 com::Utf8Str encoded(strings[i]);
1128 rc = parseDhcpOptionText(encoded.c_str(),
1129 &OptCode, &pszOptText, &OptEncoding);
1130 if (!RT_SUCCESS(rc))
1131 continue;
1132
1133 OptMap[OptCode] = std::make_pair(pszOptText, OptEncoding);
1134 }
1135
1136 const RTMAC &mac = client.getMacAddress();
1137 char strMac[6*2+1] = "";
1138 RTStrPrintf(strMac, sizeof(strMac), "%02x%02x%02x%02x%02x%02x",
1139 mac.au8[0], mac.au8[1], mac.au8[2],
1140 mac.au8[3], mac.au8[4], mac.au8[5]);
1141
1142 strings.setNull();
1143 hrc = m->m_DhcpServer->GetMacOptions(com::Bstr(strMac).raw(),
1144 ComSafeArrayAsOutParam(strings));
1145 AssertComRC(hrc);
1146 for (size_t i = 0; i < strings.size(); ++i)
1147 {
1148 com::Utf8Str text(strings[i]);
1149 rc = parseDhcpOptionText(text.c_str(),
1150 &OptCode, &pszOptText, &OptEncoding);
1151 if (!RT_SUCCESS(rc))
1152 continue;
1153
1154 OptMap[OptCode] = std::make_pair(pszOptText, OptEncoding);
1155 }
1156 }
1157
1158 /* request parameter list */
1159 RawOption opt;
1160 bool fIgnore;
1161 uint8_t u8Req;
1162 for (int idxParam = 0; idxParam < cReqList; ++idxParam)
1163 {
1164 fIgnore = false;
1165 RT_ZERO(opt);
1166 u8Req = opt.u8OptId = pu8ReqList[idxParam];
1167
1168 switch(u8Req)
1169 {
1170 case RTNET_DHCP_OPT_SUBNET_MASK:
1171 ((PRTNETADDRIPV4)opt.au8RawOpt)->u = pNetCfg->netmask().u;
1172 opt.cbRawOpt = sizeof(RTNETADDRIPV4);
1173
1174 break;
1175
1176 case RTNET_DHCP_OPT_ROUTERS:
1177 case RTNET_DHCP_OPT_DNS:
1178 {
1179 const Ipv4AddressContainer lst =
1180 g_ConfigurationManager->getAddressList(u8Req);
1181 PRTNETADDRIPV4 pAddresses = (PRTNETADDRIPV4)&opt.au8RawOpt[0];
1182
1183 for (Ipv4AddressConstIterator it = lst.begin();
1184 it != lst.end();
1185 ++it)
1186 {
1187 *pAddresses = (*it);
1188 pAddresses++;
1189 opt.cbRawOpt += sizeof(RTNETADDRIPV4);
1190 }
1191
1192 if (lst.empty())
1193 fIgnore = true;
1194 }
1195 break;
1196 case RTNET_DHCP_OPT_DOMAIN_NAME:
1197 {
1198 std::string domainName = g_ConfigurationManager->getString(u8Req);
1199 if (domainName == g_ConfigurationManager->m_noString)
1200 {
1201 fIgnore = true;
1202 break;
1203 }
1204
1205 char *pszDomainName = (char *)&opt.au8RawOpt[0];
1206
1207 strcpy(pszDomainName, domainName.c_str());
1208 opt.cbRawOpt = domainName.length();
1209 }
1210 break;
1211 default:
1212 {
1213 DhcpOptionMap::const_iterator it = OptMap.find((int)u8Req);
1214 if (it == OptMap.end())
1215 {
1216 Log(("opt: %d is ignored\n", u8Req));
1217 fIgnore = true;
1218 }
1219 else
1220 {
1221 std::string OptText((*it).second.first);
1222 int OptEncoding((*it).second.second);
1223
1224 rc = fillDhcpOption(opt, OptText, OptEncoding);
1225 if (!RT_SUCCESS(rc))
1226 {
1227 fIgnore = true;
1228 break;
1229 }
1230 }
1231 }
1232 break;
1233 }
1234
1235 if (!fIgnore)
1236 extra.push_back(opt);
1237
1238 }
1239
1240 return VINF_SUCCESS;
1241}
1242
1243/* Client */
1244Client::Client()
1245{
1246 m = SharedPtr<ClientData>();
1247}
1248
1249
1250void Client::initWithMac(const RTMAC& mac)
1251{
1252 m = SharedPtr<ClientData>(new ClientData());
1253 m->m_mac = mac;
1254}
1255
1256
1257bool Client::operator== (const RTMAC& mac) const
1258{
1259 return (m.get() && m->m_mac == mac);
1260}
1261
1262
1263const RTMAC& Client::getMacAddress() const
1264{
1265 return m->m_mac;
1266}
1267
1268
1269Lease Client::lease()
1270{
1271 if (!m.get()) return Lease::NullLease;
1272
1273 if (m->fHasLease)
1274 return Lease(*this);
1275 else
1276 return Lease::NullLease;
1277}
1278
1279
1280const Lease Client::lease() const
1281{
1282 return const_cast<Client *>(this)->lease();
1283}
1284
1285
1286Client::Client(ClientData *data):m(SharedPtr<ClientData>(data)){}
1287
1288/* Lease */
1289Lease::Lease()
1290{
1291 m = SharedPtr<ClientData>();
1292}
1293
1294
1295Lease::Lease (const Client& c)
1296{
1297 m = SharedPtr<ClientData>(c.m);
1298 if ( !m->fHasLease
1299 || ( isExpired()
1300 && !isInBindingPhase()))
1301 {
1302 m->fHasLease = true;
1303 m->fBinding = true;
1304 phaseStart(RTTimeMilliTS());
1305 }
1306}
1307
1308
1309bool Lease::isExpired() const
1310{
1311 AssertPtrReturn(m.get(), false);
1312
1313 if (!m->fBinding)
1314 return (ASMDivU64ByU32RetU32(RTTimeMilliTS() - m->u64TimestampLeasingStarted, 1000)
1315 > m->u32LeaseExpirationPeriod);
1316 else
1317 return (ASMDivU64ByU32RetU32(RTTimeMilliTS() - m->u64TimestampBindingStarted, 1000)
1318 > m->u32BindExpirationPeriod);
1319}
1320
1321
1322void Lease::expire()
1323{
1324 /* XXX: TODO */
1325}
1326
1327
1328void Lease::phaseStart(uint64_t u64Start)
1329{
1330 if (m->fBinding)
1331 m->u64TimestampBindingStarted = u64Start;
1332 else
1333 m->u64TimestampLeasingStarted = u64Start;
1334}
1335
1336
1337void Lease::bindingPhase(bool fOnOff)
1338{
1339 m->fBinding = fOnOff;
1340}
1341
1342
1343bool Lease::isInBindingPhase() const
1344{
1345 return m->fBinding;
1346}
1347
1348
1349uint64_t Lease::issued() const
1350{
1351 return m->u64TimestampLeasingStarted;
1352}
1353
1354
1355void Lease::setExpiration(uint32_t exp)
1356{
1357 if (m->fBinding)
1358 m->u32BindExpirationPeriod = exp;
1359 else
1360 m->u32LeaseExpirationPeriod = exp;
1361}
1362
1363
1364uint32_t Lease::getExpiration() const
1365{
1366 if (m->fBinding)
1367 return m->u32BindExpirationPeriod;
1368 else
1369 return m->u32LeaseExpirationPeriod;
1370}
1371
1372
1373RTNETADDRIPV4 Lease::getAddress() const
1374{
1375 return m->m_address;
1376}
1377
1378
1379void Lease::setAddress(RTNETADDRIPV4 address)
1380{
1381 m->m_address = address;
1382}
1383
1384
1385const NetworkConfigEntity *Lease::getConfig() const
1386{
1387 return m->pCfg;
1388}
1389
1390
1391void Lease::setConfig(NetworkConfigEntity *pCfg)
1392{
1393 m->pCfg = pCfg;
1394}
1395
1396
1397const MapOptionId2RawOption& Lease::options() const
1398{
1399 return m->options;
1400}
1401
1402
1403Lease::Lease(ClientData *pd):m(SharedPtr<ClientData>(pd)){}
1404
1405
1406bool Lease::toXML(xml::ElementNode *node) const
1407{
1408 bool valueAddition = node->setAttribute(tagXMLLeaseAttributeMac.c_str(), com::Utf8StrFmt("%RTmac", &m->m_mac));
1409 if (!valueAddition) return false;
1410
1411 valueAddition = node->setAttribute(tagXMLLeaseAttributeNetwork.c_str(), com::Utf8StrFmt("%RTnaipv4", m->m_network));
1412 if (!valueAddition) return false;
1413
1414 xml::ElementNode *address = node->createChild(tagXMLLeaseAddress.c_str());
1415 if (!address) return false;
1416
1417 valueAddition = address->setAttribute(tagXMLAddressAttributeValue.c_str(), com::Utf8StrFmt("%RTnaipv4", m->m_address));
1418 if (!valueAddition) return false;
1419
1420 xml::ElementNode *time = node->createChild(tagXMLLeaseTime.c_str());
1421 if (!time) return false;
1422
1423 valueAddition = time->setAttribute(tagXMLTimeAttributeIssued.c_str(),
1424 m->u64TimestampLeasingStarted);
1425 if (!valueAddition) return false;
1426
1427 valueAddition = time->setAttribute(tagXMLTimeAttributeExpiration.c_str(),
1428 m->u32LeaseExpirationPeriod);
1429 if (!valueAddition) return false;
1430
1431 return true;
1432}
1433
1434
1435bool Lease::fromXML(const xml::ElementNode *node)
1436{
1437 com::Utf8Str mac;
1438 bool valueExists = node->getAttributeValue(tagXMLLeaseAttributeMac.c_str(), mac);
1439 if (!valueExists) return false;
1440 int rc = RTNetStrToMacAddr(mac.c_str(), &m->m_mac);
1441 if (RT_FAILURE(rc)) return false;
1442
1443 com::Utf8Str network;
1444 valueExists = node->getAttributeValue(tagXMLLeaseAttributeNetwork.c_str(), network);
1445 if (!valueExists) return false;
1446 rc = RTNetStrToIPv4Addr(network.c_str(), &m->m_network);
1447 if (RT_FAILURE(rc)) return false;
1448
1449 /* Address */
1450 const xml::ElementNode *address = node->findChildElement(tagXMLLeaseAddress.c_str());
1451 if (!address) return false;
1452 com::Utf8Str addressValue;
1453 valueExists = address->getAttributeValue(tagXMLAddressAttributeValue.c_str(), addressValue);
1454 if (!valueExists) return false;
1455 rc = RTNetStrToIPv4Addr(addressValue.c_str(), &m->m_address);
1456
1457 /* Time */
1458 const xml::ElementNode *time = node->findChildElement(tagXMLLeaseTime.c_str());
1459 if (!time) return false;
1460
1461 valueExists = time->getAttributeValue(tagXMLTimeAttributeIssued.c_str(),
1462 &m->u64TimestampLeasingStarted);
1463 if (!valueExists) return false;
1464 m->fBinding = false;
1465
1466 valueExists = time->getAttributeValue(tagXMLTimeAttributeExpiration.c_str(),
1467 &m->u32LeaseExpirationPeriod);
1468 if (!valueExists) return false;
1469
1470 m->fHasLease = true;
1471 return true;
1472}
1473
1474
1475const Lease Lease::NullLease;
1476
1477const 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