VirtualBox

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

Last change on this file since 49469 was 49328, checked in by vboxsync, 11 years ago

NetServices/DHCP: XML lease serialization/deserialization.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 31.9 KB
Line 
1/* $Id: Config.cpp 49328 2013-10-30 05:14:53Z vboxsync $ */
2
3/**
4 * XXX: license.
5 */
6
7#include <iprt/asm.h>
8#include <iprt/net.h>
9#include <iprt/time.h>
10
11#include <VBox/sup.h>
12#include <VBox/intnet.h>
13#include <VBox/intnetinline.h>
14#include <VBox/vmm/vmm.h>
15#include <VBox/version.h>
16
17#include <VBox/com/string.h>
18
19#include <iprt/cpp/xml.h>
20
21#include "../NetLib/VBoxNetLib.h"
22#include "../NetLib/shared_ptr.h"
23
24#include <list>
25#include <vector>
26#include <map>
27#include <string>
28
29#include "Config.h"
30
31/* types */
32class ClientData
33{
34public:
35 ClientData()
36 {
37 m_address.u = 0;
38 m_network.u = 0;
39 fHasLease = false;
40 fHasClient = false;
41 fBinding = true;
42 u64TimestampBindingStarted = 0;
43 u64TimestampLeasingStarted = 0;
44 u32LeaseExpirationPeriod = 0;
45 u32BindExpirationPeriod = 0;
46 pCfg = NULL;
47
48 }
49 ~ClientData(){}
50
51 /* client information */
52 RTNETADDRIPV4 m_address;
53 RTNETADDRIPV4 m_network;
54 RTMAC m_mac;
55
56 bool fHasClient;
57
58 /* Lease part */
59 bool fHasLease;
60 /** lease isn't commited */
61 bool fBinding;
62
63 /** Timestamp when lease commited. */
64 uint64_t u64TimestampLeasingStarted;
65 /** Period when lease is expired in secs. */
66 uint32_t u32LeaseExpirationPeriod;
67
68 /** timestamp when lease was bound */
69 uint64_t u64TimestampBindingStarted;
70 /* Period when binding is expired in secs. */
71 uint32_t u32BindExpirationPeriod;
72
73 MapOptionId2RawOption options;
74
75 NetworkConfigEntity *pCfg;
76};
77
78
79bool operator== (const Lease& lhs, const Lease& rhs)
80{
81 return (lhs.m.get() == rhs.m.get());
82}
83
84
85bool operator!= (const Lease& lhs, const Lease& rhs)
86{
87 return !(lhs == rhs);
88}
89
90
91bool operator< (const Lease& lhs, const Lease& rhs)
92{
93 return ( (lhs.getAddress() < rhs.getAddress())
94 || (lhs.issued() < rhs.issued()));
95}
96/* consts */
97
98const NullConfigEntity *g_NullConfig = new NullConfigEntity();
99RootConfigEntity *g_RootConfig = new RootConfigEntity(std::string("ROOT"), 1200 /* 20 min. */);
100const ClientMatchCriteria *g_AnyClient = new AnyClientMatchCriteria();
101
102static ConfigurationManager *g_ConfigurationManager = ConfigurationManager::getConfigurationManager();
103
104static NetworkManager *g_NetworkManager = NetworkManager::getNetworkManager();
105
106int BaseConfigEntity::match(Client& client, BaseConfigEntity **cfg)
107{
108 int iMatch = (m_criteria && m_criteria->check(client)? m_MatchLevel: 0);
109 if (m_children.empty())
110 {
111 if (iMatch > 0)
112 {
113 *cfg = this;
114 return iMatch;
115 }
116 }
117 else
118 {
119 *cfg = this;
120 /* XXX: hack */
121 BaseConfigEntity *matching = this;
122 int matchingLevel = m_MatchLevel;
123
124 for (std::vector<BaseConfigEntity *>::iterator it = m_children.begin();
125 it != m_children.end();
126 ++it)
127 {
128 iMatch = (*it)->match(client, &matching);
129 if (iMatch > matchingLevel)
130 {
131 *cfg = matching;
132 matchingLevel = iMatch;
133 }
134 }
135 return matchingLevel;
136 }
137 return iMatch;
138}
139
140/* Client */
141/* Configs
142 NetworkConfigEntity(std::string name,
143 ConfigEntity* pCfg,
144 ClientMatchCriteria* criteria,
145 RTNETADDRIPV4& networkID,
146 RTNETADDRIPV4& networkMask)
147*/
148static const RTNETADDRIPV4 g_AnyIpv4 = {0};
149static const RTNETADDRIPV4 g_AllIpv4 = {0xffffffff};
150RootConfigEntity::RootConfigEntity(std::string name, uint32_t expPeriod):
151 NetworkConfigEntity(name, g_NullConfig, g_AnyClient, g_AnyIpv4, g_AllIpv4)
152{
153 m_MatchLevel = 2;
154 m_u32ExpirationPeriod = expPeriod;
155}
156
157/* Configuration Manager */
158struct ConfigurationManager::Data
159{
160 Data():fFileExists(false){}
161
162 MapLease2Ip4Address m_allocations;
163 Ipv4AddressContainer m_nameservers;
164 Ipv4AddressContainer m_routers;
165
166 std::string m_domainName;
167 VecClient m_clients;
168 std::string m_leaseStorageFilename;
169 bool fFileExists;
170};
171
172ConfigurationManager *ConfigurationManager::getConfigurationManager()
173{
174 if (!g_ConfigurationManager)
175
176
177 {
178 g_ConfigurationManager = new ConfigurationManager();
179 g_ConfigurationManager->init();
180 }
181
182 return g_ConfigurationManager;
183}
184
185
186const std::string tagXMLLeases = "Leases";
187const std::string tagXMLLeasesAttributeVersion = "version";
188const std::string tagXMLLeasesVersion_1_0 = "1.0";
189const std::string tagXMLLease = "Lease";
190const std::string tagXMLLeaseAttributeMac = "mac";
191const std::string tagXMLLeaseAttributeNetwork = "network";
192const std::string tagXMLLeaseAddress = "Address";
193const std::string tagXMLAddressAttributeValue = "value";
194const std::string tagXMLLeaseTime = "Time";
195const std::string tagXMLTimeAttributeIssued = "issued";
196const std::string tagXMLTimeAttributeExpiration = "expiration";
197const std::string tagXMLLeaseOptions = "Options";
198
199/**
200 * <Leases version="1.0">
201 * <Lease mac="" network=""/>
202 * <Address value=""/>
203 * <Time issued="" expiration=""/>
204 * <options>
205 * <option name="" type=""/>
206 * </option>
207 * </options>
208 * </Lease>
209 * </Leases>
210 */
211int ConfigurationManager::loadFromFile(const std::string& leaseStorageFileName)
212{
213 m->m_leaseStorageFilename = leaseStorageFileName;
214
215 xml::XmlFileParser parser;
216 xml::Document doc;
217
218 try {
219 parser.read(m->m_leaseStorageFilename.c_str(), doc);
220 }
221 catch (...)
222 {
223 return VINF_SUCCESS;
224 }
225
226 /* XML parsing */
227 xml::ElementNode *root = doc.getRootElement();
228
229 if (!root || !root->nameEquals(tagXMLLeases.c_str()))
230 {
231 m->fFileExists = false;
232 return VERR_NOT_FOUND;
233 }
234
235 com::Utf8Str version;
236 if (root)
237 root->getAttributeValue(tagXMLLeasesAttributeVersion.c_str(), version);
238
239 /* XXX: version check */
240 xml::NodesLoop leases(*root);
241
242 bool valueExists;
243 const xml::ElementNode *lease;
244 while ((lease = leases.forAllNodes()))
245 {
246 if (!lease->nameEquals(tagXMLLease.c_str()))
247 continue;
248
249 ClientData *data = new ClientData();
250 Lease l(data);
251 if (l.fromXML(lease))
252 {
253
254 m->m_allocations.insert(MapLease2Ip4AddressPair(l, l.getAddress()));
255
256
257 NetworkConfigEntity *pNetCfg = NULL;
258 Client c(data);
259 int rc = g_RootConfig->match(c, (BaseConfigEntity **)&pNetCfg);
260 Assert(rc >= 0 && pNetCfg);
261
262 l.setConfig(pNetCfg);
263
264 m->m_clients.push_back(c);
265 }
266 }
267
268 return VINF_SUCCESS;
269}
270
271
272int ConfigurationManager::saveToFile()
273{
274 if (m->m_leaseStorageFilename.empty())
275 return VINF_SUCCESS;
276
277 xml::Document doc;
278
279 xml::ElementNode *root = doc.createRootElement(tagXMLLeases.c_str());
280 if (!root)
281 return VERR_INTERNAL_ERROR;
282
283 root->setAttribute(tagXMLLeasesAttributeVersion.c_str(), tagXMLLeasesVersion_1_0.c_str());
284
285 for(MapLease2Ip4AddressConstIterator it = m->m_allocations.begin();
286 it != m->m_allocations.end(); ++it)
287 {
288 xml::ElementNode *lease = root->createChild(tagXMLLease.c_str());
289 if (!it->first.toXML(lease))
290 {
291 /* XXX: todo logging + error handling */
292 }
293 }
294
295 try {
296 xml::XmlFileWriter writer(doc);
297 writer.write(m->m_leaseStorageFilename.c_str(), true);
298 } catch(...){}
299
300 return VINF_SUCCESS;
301}
302
303
304int ConfigurationManager::extractRequestList(PCRTNETBOOTP pDhcpMsg, size_t cbDhcpMsg, RawOption& rawOpt)
305{
306 return ConfigurationManager::findOption(RTNET_DHCP_OPT_PARAM_REQ_LIST, pDhcpMsg, cbDhcpMsg, rawOpt);
307}
308
309
310Client ConfigurationManager::getClientByDhcpPacket(const RTNETBOOTP *pDhcpMsg, size_t cbDhcpMsg)
311{
312
313 VecClientIterator it;
314 bool fDhcpValid = false;
315 uint8_t uMsgType = 0;
316
317 fDhcpValid = RTNetIPv4IsDHCPValid(NULL, pDhcpMsg, cbDhcpMsg, &uMsgType);
318 AssertReturn(fDhcpValid, Client::NullClient);
319
320 LogFlowFunc(("dhcp:mac:%RTmac\n", &pDhcpMsg->bp_chaddr.Mac));
321 /* 1st. client IDs */
322 for ( it = m->m_clients.begin();
323 it != m->m_clients.end();
324 ++it)
325 {
326 if ((*it) == pDhcpMsg->bp_chaddr.Mac)
327 {
328 LogFlowFunc(("client:mac:%RTmac\n", it->getMacAddress()));
329 /* check timestamp that request wasn't expired. */
330 return (*it);
331 }
332 }
333
334 if (it == m->m_clients.end())
335 {
336 /* We hasn't got any session for this client */
337 Client c;
338 c.initWithMac(pDhcpMsg->bp_chaddr.Mac);
339 m->m_clients.push_back(c);
340 return m->m_clients.back();
341 }
342
343 return Client::NullClient;
344}
345
346/**
347 * Finds an option.
348 *
349 * @returns On success, a pointer to the first byte in the option data (no none
350 * then it'll be the byte following the 0 size field) and *pcbOpt set
351 * to the option length.
352 * On failure, NULL is returned and *pcbOpt unchanged.
353 *
354 * @param uOption The option to search for.
355 * @param pDhcpMsg The DHCP message.
356 * that this is adjusted if the option length is larger
357 * than the message buffer.
358 */
359int
360ConfigurationManager::findOption(uint8_t uOption, PCRTNETBOOTP pDhcpMsg, size_t cbDhcpMsg, RawOption& opt)
361{
362 Assert(uOption != RTNET_DHCP_OPT_PAD);
363
364 /*
365 * Validate the DHCP bits and figure the max size of the options in the vendor field.
366 */
367 if (cbDhcpMsg <= RT_UOFFSETOF(RTNETBOOTP, bp_vend.Dhcp.dhcp_opts))
368 return VERR_INVALID_PARAMETER;
369
370 if (pDhcpMsg->bp_vend.Dhcp.dhcp_cookie != RT_H2N_U32_C(RTNET_DHCP_COOKIE))
371 return VERR_INVALID_PARAMETER;
372
373 size_t cbLeft = cbDhcpMsg - RT_UOFFSETOF(RTNETBOOTP, bp_vend.Dhcp.dhcp_opts);
374 if (cbLeft > RTNET_DHCP_OPT_SIZE)
375 cbLeft = RTNET_DHCP_OPT_SIZE;
376
377 /*
378 * Search the vendor field.
379 */
380 bool fExtended = false;
381 uint8_t const *pb = &pDhcpMsg->bp_vend.Dhcp.dhcp_opts[0];
382 while (pb && cbLeft > 0)
383 {
384 uint8_t uCur = *pb;
385 if (uCur == RTNET_DHCP_OPT_PAD)
386 {
387 cbLeft--;
388 pb++;
389 }
390 else if (cbLeft <= 1)
391 break;
392 else
393 {
394 size_t cbCur = pb[1];
395 if (cbCur > cbLeft - 2)
396 cbCur = cbLeft - 2;
397 if (uCur == uOption)
398 {
399 opt.u8OptId = uCur;
400 memcpy(opt.au8RawOpt, pb+2, cbCur);
401 opt.cbRawOpt = cbCur;
402 return VINF_SUCCESS;
403 }
404 pb += cbCur + 2;
405 cbLeft -= cbCur - 2;
406 }
407 }
408
409 /** @todo search extended dhcp option field(s) when present */
410
411 return VERR_NOT_FOUND;
412}
413
414
415/**
416 * We bind lease for client till it continue with it on DHCPREQUEST.
417 */
418Lease ConfigurationManager::allocateLease4Client(const Client& client, PCRTNETBOOTP pDhcpMsg, size_t cbDhcpMsg)
419{
420 {
421 /**
422 * This mean that client has already bound or commited lease.
423 * If we've it happens it means that we received DHCPDISCOVER twice.
424 */
425 const Lease l = client.lease();
426 if (l != Lease::NullLease)
427 {
428 /* Here we should take lease from the m_allocation which was feed with leases
429 * on start
430 */
431 if (l.isExpired())
432 {
433 expireLease4Client(const_cast<Client&>(client));
434 if (!l.isExpired())
435 return l;
436 }
437 else
438 {
439 AssertReturn(l.getAddress().u != 0, Lease::NullLease);
440 return l;
441 }
442 }
443 }
444
445 RTNETADDRIPV4 hintAddress;
446 RawOption opt;
447 NetworkConfigEntity *pNetCfg;
448
449 Client cl(client);
450 AssertReturn(g_RootConfig->match(cl, (BaseConfigEntity **)&pNetCfg) > 0, Lease::NullLease);
451
452 /* DHCPDISCOVER MAY contain request address */
453 hintAddress.u = 0;
454 int rc = findOption(RTNET_DHCP_OPT_REQ_ADDR, pDhcpMsg, cbDhcpMsg, opt);
455 if (RT_SUCCESS(rc))
456 {
457 hintAddress.u = *(uint32_t *)opt.au8RawOpt;
458 if ( RT_H2N_U32(hintAddress.u) < RT_H2N_U32(pNetCfg->lowerIp().u)
459 || RT_H2N_U32(hintAddress.u) > RT_H2N_U32(pNetCfg->upperIp().u))
460 hintAddress.u = 0; /* clear hint */
461 }
462
463 if ( hintAddress.u
464 && !isAddressTaken(hintAddress))
465 {
466 Lease l(cl);
467 l.setConfig(pNetCfg);
468 l.setAddress(hintAddress);
469 m->m_allocations.insert(MapLease2Ip4AddressPair(l, hintAddress));
470 return l;
471 }
472
473 uint32_t u32 = 0;
474 for(u32 = RT_H2N_U32(pNetCfg->lowerIp().u);
475 u32 <= RT_H2N_U32(pNetCfg->upperIp().u);
476 ++u32)
477 {
478 RTNETADDRIPV4 address;
479 address.u = RT_H2N_U32(u32);
480 if (!isAddressTaken(address))
481 {
482 Lease l(cl);
483 l.setConfig(pNetCfg);
484 l.setAddress(address);
485 m->m_allocations.insert(MapLease2Ip4AddressPair(l, address));
486 return l;
487 }
488 }
489
490 return Lease::NullLease;
491}
492
493
494int ConfigurationManager::commitLease4Client(Client& client)
495{
496 Lease l = client.lease();
497 AssertReturn(l != Lease::NullLease, VERR_INTERNAL_ERROR);
498
499 l.bindingPhase(false);
500 const NetworkConfigEntity *pCfg = l.getConfig();
501
502 AssertPtr(pCfg);
503 l.setExpiration(pCfg->expirationPeriod());
504 l.phaseStart(RTTimeMilliTS());
505
506 saveToFile();
507
508 return VINF_SUCCESS;
509}
510
511
512int ConfigurationManager::expireLease4Client(Client& client)
513{
514 Lease l = client.lease();
515 AssertReturn(l != Lease::NullLease, VERR_INTERNAL_ERROR);
516
517 if (l.isInBindingPhase())
518 {
519
520 MapLease2Ip4AddressIterator it = m->m_allocations.find(l);
521 AssertReturn(it != m->m_allocations.end(), VERR_NOT_FOUND);
522
523 /*
524 * XXX: perhaps it better to keep this allocation ????
525 */
526 m->m_allocations.erase(it);
527
528 l.expire();
529 return VINF_SUCCESS;
530 }
531
532 l = Lease(client); /* re-new */
533 return VINF_SUCCESS;
534}
535
536bool ConfigurationManager::isAddressTaken(const RTNETADDRIPV4& addr, Lease& lease)
537{
538 MapLease2Ip4AddressIterator it;
539
540 for (it = m->m_allocations.begin();
541 it != m->m_allocations.end();
542 ++it)
543 {
544 if (it->second.u == addr.u)
545 {
546 if (lease != Lease::NullLease)
547 lease = it->first;
548
549 return true;
550 }
551 }
552 lease = Lease::NullLease;
553 return false;
554}
555
556
557NetworkConfigEntity *ConfigurationManager::addNetwork(NetworkConfigEntity *,
558 const RTNETADDRIPV4& networkId,
559 const RTNETADDRIPV4& netmask,
560 RTNETADDRIPV4& LowerAddress,
561 RTNETADDRIPV4& UpperAddress)
562{
563 static int id;
564 char name[64];
565
566 RTStrPrintf(name, RT_ELEMENTS(name), "network-%d", id);
567 std::string strname(name);
568 id++;
569
570
571 if (!LowerAddress.u)
572 LowerAddress = networkId;
573
574 if (!UpperAddress.u)
575 UpperAddress.u = networkId.u | (~netmask.u);
576
577 return new NetworkConfigEntity(strname,
578 g_RootConfig,
579 g_AnyClient,
580 5,
581 networkId,
582 netmask,
583 LowerAddress,
584 UpperAddress);
585}
586
587HostConfigEntity *ConfigurationManager::addHost(NetworkConfigEntity* pCfg,
588 const RTNETADDRIPV4& address,
589 ClientMatchCriteria *criteria)
590{
591 static int id;
592 char name[64];
593
594 RTStrPrintf(name, RT_ELEMENTS(name), "host-%d", id);
595 std::string strname(name);
596 id++;
597
598 return new HostConfigEntity(address, strname, pCfg, criteria);
599}
600
601int ConfigurationManager::addToAddressList(uint8_t u8OptId, RTNETADDRIPV4& address)
602{
603 switch(u8OptId)
604 {
605 case RTNET_DHCP_OPT_DNS:
606 m->m_nameservers.push_back(address);
607 break;
608 case RTNET_DHCP_OPT_ROUTERS:
609 m->m_routers.push_back(address);
610 break;
611 default:
612 Log(("dhcp-opt: list (%d) unsupported\n", u8OptId));
613 }
614 return VINF_SUCCESS;
615}
616
617
618int ConfigurationManager::flushAddressList(uint8_t u8OptId)
619{
620 switch(u8OptId)
621 {
622 case RTNET_DHCP_OPT_DNS:
623 m->m_nameservers.clear();
624 break;
625 case RTNET_DHCP_OPT_ROUTERS:
626 m->m_routers.clear();
627 break;
628 default:
629 Log(("dhcp-opt: list (%d) unsupported\n", u8OptId));
630 }
631 return VINF_SUCCESS;
632}
633
634
635const Ipv4AddressContainer& ConfigurationManager::getAddressList(uint8_t u8OptId)
636{
637 switch(u8OptId)
638 {
639 case RTNET_DHCP_OPT_DNS:
640 return m->m_nameservers;
641
642 case RTNET_DHCP_OPT_ROUTERS:
643 return m->m_routers;
644
645 }
646 /* XXX: Grrr !!! */
647 return m_empty;
648}
649
650
651int ConfigurationManager::setString(uint8_t u8OptId, const std::string& str)
652{
653 switch (u8OptId)
654 {
655 case RTNET_DHCP_OPT_DOMAIN_NAME:
656 m->m_domainName = str;
657 break;
658 default:
659 break;
660 }
661
662 return VINF_SUCCESS;
663}
664
665
666const std::string& ConfigurationManager::getString(uint8_t u8OptId)
667{
668 switch (u8OptId)
669 {
670 case RTNET_DHCP_OPT_DOMAIN_NAME:
671 if (m->m_domainName.length())
672 return m->m_domainName;
673 else
674 return m_noString;
675 default:
676 break;
677 }
678
679 return m_noString;
680}
681
682
683void ConfigurationManager::init()
684{
685 m = new ConfigurationManager::Data();
686}
687
688
689ConfigurationManager::~ConfigurationManager() { if (m) delete m; }
690
691/**
692 * Network manager
693 */
694NetworkManager *NetworkManager::getNetworkManager()
695{
696 if (!g_NetworkManager)
697 g_NetworkManager = new NetworkManager();
698
699 return g_NetworkManager;
700}
701
702
703/**
704 * Network manager creates DHCPOFFER datagramm
705 */
706int NetworkManager::offer4Client(const Client& client, uint32_t u32Xid,
707 uint8_t *pu8ReqList, int cReqList)
708{
709 Lease l(client); /* XXX: oh, it looks badly, but now we have lease */
710 prepareReplyPacket4Client(client, u32Xid);
711
712 RTNETADDRIPV4 address = l.getAddress();
713 BootPReplyMsg.BootPHeader.bp_yiaddr = address;
714
715 /* Ubuntu ???*/
716 BootPReplyMsg.BootPHeader.bp_ciaddr = address;
717
718 /* options:
719 * - IP lease time
720 * - message type
721 * - server identifier
722 */
723 RawOption opt;
724 RT_ZERO(opt);
725
726 std::vector<RawOption> extra(2);
727 opt.u8OptId = RTNET_DHCP_OPT_MSG_TYPE;
728 opt.au8RawOpt[0] = RTNET_DHCP_MT_OFFER;
729 opt.cbRawOpt = 1;
730 extra.push_back(opt);
731
732 opt.u8OptId = RTNET_DHCP_OPT_LEASE_TIME;
733
734 const NetworkConfigEntity *pCfg = l.getConfig();
735 AssertPtr(pCfg);
736
737 *(uint32_t *)opt.au8RawOpt = RT_H2N_U32(pCfg->expirationPeriod());
738 opt.cbRawOpt = sizeof(RTNETADDRIPV4);
739
740 extra.push_back(opt);
741
742 processParameterReqList(client, pu8ReqList, cReqList);
743
744 return doReply(client, extra);
745}
746
747
748/**
749 * Network manager creates DHCPACK
750 */
751int NetworkManager::ack(const Client& client, uint32_t u32Xid,
752 uint8_t *pu8ReqList, int cReqList)
753{
754 RTNETADDRIPV4 address;
755
756 prepareReplyPacket4Client(client, u32Xid);
757
758 Lease l = client.lease();
759 address = l.getAddress();
760 BootPReplyMsg.BootPHeader.bp_ciaddr = address;
761
762
763 /* rfc2131 4.3.1 is about DHCPDISCOVER and this value is equal to ciaddr from
764 * DHCPREQUEST or 0 ...
765 * XXX: Using addressHint is not correct way to initialize [cy]iaddress...
766 */
767 BootPReplyMsg.BootPHeader.bp_ciaddr = address;
768 BootPReplyMsg.BootPHeader.bp_yiaddr = address;
769
770 Assert(BootPReplyMsg.BootPHeader.bp_yiaddr.u);
771
772 /* options:
773 * - IP address lease time (if DHCPREQUEST)
774 * - message type
775 * - server identifier
776 */
777 RawOption opt;
778 RT_ZERO(opt);
779
780 std::vector<RawOption> extra(2);
781 opt.u8OptId = RTNET_DHCP_OPT_MSG_TYPE;
782 opt.au8RawOpt[0] = RTNET_DHCP_MT_ACK;
783 opt.cbRawOpt = 1;
784 extra.push_back(opt);
785
786 /*
787 * XXX: lease time should be conditional. If on dhcprequest then tim should be provided,
788 * else on dhcpinform it mustn't.
789 */
790 opt.u8OptId = RTNET_DHCP_OPT_LEASE_TIME;
791 *(uint32_t *)opt.au8RawOpt = RT_H2N_U32(l.getExpiration());
792 opt.cbRawOpt = sizeof(RTNETADDRIPV4);
793 extra.push_back(opt);
794
795 processParameterReqList(client, pu8ReqList, cReqList);
796
797 return doReply(client, extra);
798}
799
800
801/**
802 * Network manager creates DHCPNAK
803 */
804int NetworkManager::nak(const Client& client, uint32_t u32Xid)
805{
806
807 Lease l = client.lease();
808 if (l == Lease::NullLease)
809 return VERR_INTERNAL_ERROR;
810
811 prepareReplyPacket4Client(client, u32Xid);
812
813 /* this field filed in prepareReplyPacket4Session, and
814 * RFC 2131 require to have it zero fo NAK.
815 */
816 BootPReplyMsg.BootPHeader.bp_yiaddr.u = 0;
817
818 /* options:
819 * - message type (if DHCPREQUEST)
820 * - server identifier
821 */
822 RawOption opt;
823 std::vector<RawOption> extra;
824
825 opt.u8OptId = RTNET_DHCP_OPT_MSG_TYPE;
826 opt.au8RawOpt[0] = RTNET_DHCP_MT_NAC;
827 opt.cbRawOpt = 1;
828 extra.push_back(opt);
829
830 return doReply(client, extra);
831}
832
833
834/**
835 *
836 */
837int NetworkManager::prepareReplyPacket4Client(const Client& client, uint32_t u32Xid)
838{
839 memset(&BootPReplyMsg, 0, sizeof(BootPReplyMsg));
840
841 BootPReplyMsg.BootPHeader.bp_op = RTNETBOOTP_OP_REPLY;
842 BootPReplyMsg.BootPHeader.bp_htype = RTNET_ARP_ETHER;
843 BootPReplyMsg.BootPHeader.bp_hlen = sizeof(RTMAC);
844 BootPReplyMsg.BootPHeader.bp_hops = 0;
845 BootPReplyMsg.BootPHeader.bp_xid = u32Xid;
846 BootPReplyMsg.BootPHeader.bp_secs = 0;
847 /* XXX: bp_flags should be processed specially */
848 BootPReplyMsg.BootPHeader.bp_flags = 0;
849 BootPReplyMsg.BootPHeader.bp_ciaddr.u = 0;
850 BootPReplyMsg.BootPHeader.bp_giaddr.u = 0;
851
852 BootPReplyMsg.BootPHeader.bp_chaddr.Mac = client.getMacAddress();
853
854 const Lease l = client.lease();
855 BootPReplyMsg.BootPHeader.bp_yiaddr = l.getAddress();
856 BootPReplyMsg.BootPHeader.bp_siaddr.u = 0;
857
858
859 BootPReplyMsg.BootPHeader.bp_vend.Dhcp.dhcp_cookie = RT_H2N_U32_C(RTNET_DHCP_COOKIE);
860
861 memset(&BootPReplyMsg.BootPHeader.bp_vend.Dhcp.dhcp_opts[0],
862 '\0',
863 RTNET_DHCP_OPT_SIZE);
864
865 return VINF_SUCCESS;
866}
867
868
869int NetworkManager::doReply(const Client& client, const std::vector<RawOption>& extra)
870{
871 int rc;
872
873 /*
874 Options....
875 */
876 VBoxNetDhcpWriteCursor Cursor(&BootPReplyMsg.BootPHeader, RTNET_DHCP_NORMAL_SIZE);
877
878 /* The basics */
879
880 Cursor.optIPv4Addr(RTNET_DHCP_OPT_SERVER_ID, m_OurAddress);
881
882 const Lease l = client.lease();
883 const std::map<uint8_t, RawOption>& options = l.options();
884
885 for(std::vector<RawOption>::const_iterator it = extra.begin();
886 it != extra.end(); ++it)
887 {
888 if (!Cursor.begin(it->u8OptId, it->cbRawOpt))
889 break;
890 Cursor.put(it->au8RawOpt, it->cbRawOpt);
891
892 }
893
894 for(std::map<uint8_t, RawOption>::const_iterator it = options.begin();
895 it != options.end(); ++it)
896 {
897 if (!Cursor.begin(it->second.u8OptId, it->second.cbRawOpt))
898 break;
899 Cursor.put(it->second.au8RawOpt, it->second.cbRawOpt);
900
901 }
902
903 Cursor.optEnd();
904
905 /*
906 */
907#if 0
908 /** @todo need to see someone set this flag to check that it's correct. */
909 if (!(pDhcpMsg->bp_flags & RTNET_DHCP_FLAGS_NO_BROADCAST))
910 {
911 rc = VBoxNetUDPUnicast(m_pSession,
912 m_hIf,
913 m_pIfBuf,
914 m_OurAddress,
915 &m_OurMac,
916 RTNETIPV4_PORT_BOOTPS, /* sender */
917 IPv4AddrBrdCast,
918 &BootPReplyMsg.BootPHeader->bp_chaddr.Mac,
919 RTNETIPV4_PORT_BOOTPC, /* receiver */
920 &BootPReplyMsg, cbBooPReplyMsg);
921 }
922 else
923#endif
924 rc = VBoxNetUDPBroadcast(m_pSession,
925 m_hIf,
926 m_pIfBuf,
927 m_OurAddress,
928 &m_OurMac,
929 RTNETIPV4_PORT_BOOTPS, /* sender */
930 RTNETIPV4_PORT_BOOTPC,
931 &BootPReplyMsg, RTNET_DHCP_NORMAL_SIZE);
932
933 AssertRCReturn(rc,rc);
934
935 return VINF_SUCCESS;
936}
937
938
939int NetworkManager::processParameterReqList(const Client& client, uint8_t *pu8ReqList, int cReqList)
940{
941 /* request parameter list */
942 RawOption opt;
943 int idxParam = 0;
944
945 uint8_t *pReqList = pu8ReqList;
946
947 const Lease const_l = client.lease();
948 Lease l = Lease(const_l);
949
950 const NetworkConfigEntity *pNetCfg = l.getConfig();
951
952 for (idxParam = 0; idxParam < cReqList; ++idxParam)
953 {
954
955 bool fIgnore = false;
956 RT_ZERO(opt);
957 opt.u8OptId = pReqList[idxParam];
958
959 switch(pReqList[idxParam])
960 {
961 case RTNET_DHCP_OPT_SUBNET_MASK:
962 ((PRTNETADDRIPV4)opt.au8RawOpt)->u = pNetCfg->netmask().u;
963 opt.cbRawOpt = sizeof(RTNETADDRIPV4);
964
965 break;
966
967 case RTNET_DHCP_OPT_ROUTERS:
968 case RTNET_DHCP_OPT_DNS:
969 {
970 const Ipv4AddressContainer lst =
971 g_ConfigurationManager->getAddressList(pReqList[idxParam]);
972 PRTNETADDRIPV4 pAddresses = (PRTNETADDRIPV4)&opt.au8RawOpt[0];
973
974 for (Ipv4AddressConstIterator it = lst.begin();
975 it != lst.end();
976 ++it)
977 {
978 *pAddresses = (*it);
979 pAddresses++;
980 opt.cbRawOpt += sizeof(RTNETADDRIPV4);
981 }
982
983 if (lst.empty())
984 fIgnore = true;
985 }
986 break;
987 case RTNET_DHCP_OPT_DOMAIN_NAME:
988 {
989 std::string domainName = g_ConfigurationManager->getString(pReqList[idxParam]);
990 if (domainName == g_ConfigurationManager->m_noString)
991 {
992 fIgnore = true;
993 break;
994 }
995
996 char *pszDomainName = (char *)&opt.au8RawOpt[0];
997
998 strcpy(pszDomainName, domainName.c_str());
999 opt.cbRawOpt = domainName.length();
1000 }
1001 break;
1002 default:
1003 Log(("opt: %d is ignored\n", pReqList[idxParam]));
1004 fIgnore = true;
1005 break;
1006 }
1007
1008 if (!fIgnore)
1009 l.options().insert(std::map<uint8_t, RawOption>::value_type(opt.u8OptId, opt));
1010
1011 }
1012
1013 return VINF_SUCCESS;
1014}
1015
1016/* Utility */
1017bool operator== (const RTMAC& lhs, const RTMAC& rhs)
1018{
1019 return ( lhs.au16[0] == rhs.au16[0]
1020 && lhs.au16[1] == rhs.au16[1]
1021 && lhs.au16[2] == rhs.au16[2]);
1022}
1023
1024
1025/* Client */
1026Client::Client()
1027{
1028 m = SharedPtr<ClientData>();
1029}
1030
1031
1032void Client::initWithMac(const RTMAC& mac)
1033{
1034 m = SharedPtr<ClientData>(new ClientData());
1035 m->m_mac = mac;
1036}
1037
1038
1039bool Client::operator== (const RTMAC& mac) const
1040{
1041 return (m.get() && m->m_mac == mac);
1042}
1043
1044
1045const RTMAC& Client::getMacAddress() const
1046{
1047 return m->m_mac;
1048}
1049
1050
1051Lease Client::lease()
1052{
1053 if (!m.get()) return Lease::NullLease;
1054
1055 if (m->fHasLease)
1056 return Lease(*this);
1057 else
1058 return Lease::NullLease;
1059}
1060
1061
1062const Lease Client::lease() const
1063{
1064 return const_cast<Client *>(this)->lease();
1065}
1066
1067
1068Client::Client(ClientData *data):m(SharedPtr<ClientData>(data)){}
1069
1070/* Lease */
1071Lease::Lease()
1072{
1073 m = SharedPtr<ClientData>();
1074}
1075
1076
1077Lease::Lease (const Client& c)
1078{
1079 m = SharedPtr<ClientData>(c.m);
1080 if ( !m->fHasLease
1081 || ( isExpired()
1082 && !isInBindingPhase()))
1083 {
1084 m->fHasLease = true;
1085 m->fBinding = true;
1086 phaseStart(RTTimeMilliTS());
1087 }
1088}
1089
1090
1091bool Lease::isExpired() const
1092{
1093 AssertPtrReturn(m.get(), false);
1094
1095 if (!m->fBinding)
1096 return (ASMDivU64ByU32RetU32(RTTimeMilliTS() - m->u64TimestampLeasingStarted, 1000)
1097 > m->u32LeaseExpirationPeriod);
1098 else
1099 return (ASMDivU64ByU32RetU32(RTTimeMilliTS() - m->u64TimestampBindingStarted, 1000)
1100 > m->u32BindExpirationPeriod);
1101}
1102
1103
1104void Lease::expire()
1105{
1106 /* XXX: TODO */
1107}
1108
1109
1110void Lease::phaseStart(uint64_t u64Start)
1111{
1112 if (m->fBinding)
1113 m->u64TimestampBindingStarted = u64Start;
1114 else
1115 m->u64TimestampLeasingStarted = u64Start;
1116}
1117
1118
1119void Lease::bindingPhase(bool fOnOff)
1120{
1121 m->fBinding = fOnOff;
1122}
1123
1124
1125bool Lease::isInBindingPhase() const
1126{
1127 return m->fBinding;
1128}
1129
1130
1131uint64_t Lease::issued() const
1132{
1133 return m->u64TimestampLeasingStarted;
1134}
1135
1136
1137void Lease::setExpiration(uint32_t exp)
1138{
1139 if (m->fBinding)
1140 m->u32BindExpirationPeriod = exp;
1141 else
1142 m->u32LeaseExpirationPeriod = exp;
1143}
1144
1145
1146uint32_t Lease::getExpiration() const
1147{
1148 if (m->fBinding)
1149 return m->u32BindExpirationPeriod;
1150 else
1151 return m->u32LeaseExpirationPeriod;
1152}
1153
1154
1155RTNETADDRIPV4 Lease::getAddress() const
1156{
1157 return m->m_address;
1158}
1159
1160
1161void Lease::setAddress(RTNETADDRIPV4 address)
1162{
1163 m->m_address = address;
1164}
1165
1166
1167const NetworkConfigEntity *Lease::getConfig() const
1168{
1169 return m->pCfg;
1170}
1171
1172
1173void Lease::setConfig(NetworkConfigEntity *pCfg)
1174{
1175 m->pCfg = pCfg;
1176}
1177
1178
1179const MapOptionId2RawOption& Lease::options() const
1180{
1181 return m->options;
1182}
1183
1184
1185MapOptionId2RawOption& Lease::options()
1186{
1187 return m->options;
1188}
1189
1190
1191Lease::Lease(ClientData *pd):m(SharedPtr<ClientData>(pd)){}
1192
1193
1194bool Lease::toXML(xml::ElementNode *node) const
1195{
1196 bool valueAddition = node->setAttribute(tagXMLLeaseAttributeMac.c_str(), com::Utf8StrFmt("%RTmac", &m->m_mac));
1197 if (!valueAddition) return false;
1198
1199 valueAddition = node->setAttribute(tagXMLLeaseAttributeNetwork.c_str(), com::Utf8StrFmt("%RTnaipv4", m->m_network));
1200 if (!valueAddition) return false;
1201
1202 xml::ElementNode *address = node->createChild(tagXMLLeaseAddress.c_str());
1203 if (!address) return false;
1204
1205 valueAddition = address->setAttribute(tagXMLAddressAttributeValue.c_str(), com::Utf8StrFmt("%RTnaipv4", m->m_address));
1206 if (!valueAddition) return false;
1207
1208 xml::ElementNode *time = node->createChild(tagXMLLeaseTime.c_str());
1209 if (!time) return false;
1210
1211 valueAddition = time->setAttribute(tagXMLTimeAttributeIssued.c_str(),
1212 m->u64TimestampLeasingStarted);
1213 if (!valueAddition) return false;
1214
1215 valueAddition = time->setAttribute(tagXMLTimeAttributeExpiration.c_str(),
1216 m->u32LeaseExpirationPeriod);
1217 if (!valueAddition) return false;
1218
1219 return true;
1220}
1221
1222
1223bool Lease::fromXML(const xml::ElementNode *node)
1224{
1225 com::Utf8Str mac;
1226 bool valueExists = node->getAttributeValue(tagXMLLeaseAttributeMac.c_str(), mac);
1227 if (!valueExists) return false;
1228 int rc = RTNetStrToMacAddr(mac.c_str(), &m->m_mac);
1229 if (RT_FAILURE(rc)) return false;
1230
1231 com::Utf8Str network;
1232 valueExists = node->getAttributeValue(tagXMLLeaseAttributeNetwork.c_str(), network);
1233 if (!valueExists) return false;
1234 rc = RTNetStrToIPv4Addr(network.c_str(), &m->m_network);
1235 if (RT_FAILURE(rc)) return false;
1236
1237 /* Address */
1238 const xml::ElementNode *address = node->findChildElement(tagXMLLeaseAddress.c_str());
1239 if (!address) return false;
1240 com::Utf8Str addressValue;
1241 valueExists = address->getAttributeValue(tagXMLAddressAttributeValue.c_str(), addressValue);
1242 if (!valueExists) return false;
1243 rc = RTNetStrToIPv4Addr(addressValue.c_str(), &m->m_address);
1244
1245 /* Time */
1246 const xml::ElementNode *time = node->findChildElement(tagXMLLeaseTime.c_str());
1247 if (!time) return false;
1248
1249 valueExists = time->getAttributeValue(tagXMLTimeAttributeIssued.c_str(),
1250 &m->u64TimestampLeasingStarted);
1251 if (!valueExists) return false;
1252 m->fBinding = false;
1253
1254 valueExists = time->getAttributeValue(tagXMLTimeAttributeExpiration.c_str(),
1255 &m->u32LeaseExpirationPeriod);
1256 if (!valueExists) return false;
1257
1258 m->fHasLease = true;
1259 return true;
1260}
1261
1262
1263const Lease Lease::NullLease;
1264
1265
1266const 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