VirtualBox

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

Last change on this file since 48418 was 48367, checked in by vboxsync, 11 years ago

NATNetwork/DHCP: send DNS list to guest.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 18.2 KB
Line 
1/* $Id: Config.cpp 48367 2013-09-06 15:57:46Z vboxsync $ */
2
3/**
4 * XXX: license.
5 */
6
7#include <VBox/com/com.h>
8#include <VBox/com/listeners.h>
9#include <VBox/com/string.h>
10#include <VBox/com/Guid.h>
11#include <VBox/com/array.h>
12#include <VBox/com/ErrorInfo.h>
13#include <VBox/com/errorprint.h>
14#include <VBox/com/EventQueue.h>
15#include <VBox/com/VirtualBox.h>
16
17#include <iprt/asm.h>
18#include <iprt/net.h>
19#include <iprt/time.h>
20
21#include <VBox/sup.h>
22#include <VBox/intnet.h>
23#include <VBox/intnetinline.h>
24#include <VBox/vmm/vmm.h>
25#include <VBox/version.h>
26
27#include "../NetLib/VBoxNetLib.h"
28
29#include <list>
30#include <vector>
31#include <map>
32#include <string>
33
34#include "Config.h"
35
36
37const NullConfigEntity *g_NullConfig = new NullConfigEntity();
38RootConfigEntity *g_RootConfig = new RootConfigEntity(std::string("ROOT"), 1200 /* 20 min. */);
39const ClientMatchCriteria *g_AnyClient = new AnyClientMatchCriteria();
40
41static ConfigurationManager *g_ConfigurationManager = ConfigurationManager::getConfigurationManager();
42
43static NetworkManager *g_NetworkManager = NetworkManager::getNetworkManager();
44
45
46
47/* Client */
48
49Client::Client(const RTMAC& mac)
50{
51 m_mac = mac;
52 m_lease = NULL;
53}
54
55/* Configs
56 NetworkConfigEntity(std::string name,
57 ConfigEntity* pCfg,
58 ClientMatchCriteria* criteria,
59 RTNETADDRIPV4& networkID,
60 RTNETADDRIPV4& networkMask)
61*/
62static const RTNETADDRIPV4 g_AnyIpv4 = {0};
63static const RTNETADDRIPV4 g_AllIpv4 = {0xffffffff};
64RootConfigEntity::RootConfigEntity(std::string name, uint32_t expPeriod):
65 NetworkConfigEntity(name, g_NullConfig, g_AnyClient, g_AnyIpv4, g_AllIpv4)
66{
67 m_MatchLevel = 2;
68 m_u32ExpirationPeriod = expPeriod;
69}
70
71
72/* Configuration Manager */
73ConfigurationManager *ConfigurationManager::getConfigurationManager()
74{
75 if (!g_ConfigurationManager)
76 g_ConfigurationManager = new ConfigurationManager();
77
78 return g_ConfigurationManager;
79}
80
81
82int ConfigurationManager::extractRequestList(PCRTNETBOOTP pDhcpMsg, size_t cbDhcpMsg, RawOption& rawOpt)
83{
84 return ConfigurationManager::findOption(RTNET_DHCP_OPT_PARAM_REQ_LIST, pDhcpMsg, cbDhcpMsg, rawOpt);
85}
86
87
88Client *ConfigurationManager::getClientByDhcpPacket(const RTNETBOOTP *pDhcpMsg, size_t cbDhcpMsg)
89{
90
91 VecClientIterator it;
92 bool fDhcpValid = false;
93 uint8_t uMsgType = 0;
94
95 fDhcpValid = RTNetIPv4IsDHCPValid(NULL, pDhcpMsg, cbDhcpMsg, &uMsgType);
96 AssertReturn(fDhcpValid, NULL);
97
98 LogFlowFunc(("dhcp:mac:%RTmac\n", &pDhcpMsg->bp_chaddr.Mac));
99 /* 1st. client IDs */
100 for ( it = m_clients.begin();
101 it != m_clients.end();
102 ++it)
103 {
104 if (*(*it) == pDhcpMsg->bp_chaddr.Mac)
105 {
106 LogFlowFunc(("client:mac:%RTmac\n", &(*it)->m_mac));
107 /* check timestamp that request wasn't expired. */
108 return (*it);
109 }
110 }
111
112 if (it == m_clients.end())
113 {
114 /* We hasn't got any session for this client */
115 m_clients.push_back(new Client(pDhcpMsg->bp_chaddr.Mac));
116 return m_clients.back();
117 }
118
119 return NULL;
120}
121
122/**
123 * Finds an option.
124 *
125 * @returns On success, a pointer to the first byte in the option data (no none
126 * then it'll be the byte following the 0 size field) and *pcbOpt set
127 * to the option length.
128 * On failure, NULL is returned and *pcbOpt unchanged.
129 *
130 * @param uOption The option to search for.
131 * @param pDhcpMsg The DHCP message.
132 * that this is adjusted if the option length is larger
133 * than the message buffer.
134 */
135int
136ConfigurationManager::findOption(uint8_t uOption, PCRTNETBOOTP pDhcpMsg, size_t cbDhcpMsg, RawOption& opt)
137{
138 Assert(uOption != RTNET_DHCP_OPT_PAD);
139
140 /*
141 * Validate the DHCP bits and figure the max size of the options in the vendor field.
142 */
143 if (cbDhcpMsg <= RT_UOFFSETOF(RTNETBOOTP, bp_vend.Dhcp.dhcp_opts))
144 return VERR_INVALID_PARAMETER;
145
146 if (pDhcpMsg->bp_vend.Dhcp.dhcp_cookie != RT_H2N_U32_C(RTNET_DHCP_COOKIE))
147 return VERR_INVALID_PARAMETER;
148
149 size_t cbLeft = cbDhcpMsg - RT_UOFFSETOF(RTNETBOOTP, bp_vend.Dhcp.dhcp_opts);
150 if (cbLeft > RTNET_DHCP_OPT_SIZE)
151 cbLeft = RTNET_DHCP_OPT_SIZE;
152
153 /*
154 * Search the vendor field.
155 */
156 bool fExtended = false;
157 uint8_t const *pb = &pDhcpMsg->bp_vend.Dhcp.dhcp_opts[0];
158 while (pb && cbLeft > 0)
159 {
160 uint8_t uCur = *pb;
161 if (uCur == RTNET_DHCP_OPT_PAD)
162 {
163 cbLeft--;
164 pb++;
165 }
166 else if (cbLeft <= 1)
167 break;
168 else
169 {
170 size_t cbCur = pb[1];
171 if (cbCur > cbLeft - 2)
172 cbCur = cbLeft - 2;
173 if (uCur == uOption)
174 {
175 opt.u8OptId = uCur;
176 memcpy(opt.au8RawOpt, pb+2, cbCur);
177 opt.cbRawOpt = cbCur;
178 return VINF_SUCCESS;
179 }
180 pb += cbCur + 2;
181 cbLeft -= cbCur - 2;
182 }
183 }
184
185 /** @todo search extended dhcp option field(s) when present */
186
187 return VERR_NOT_FOUND;
188}
189
190
191/**
192 * We bind lease for client till it continue with it on DHCPREQUEST.
193 */
194Lease *ConfigurationManager::allocateLease4Client(Client *client, PCRTNETBOOTP pDhcpMsg, size_t cbDhcpMsg)
195{
196 /**
197 * Well, session hasn't get the config.
198 */
199 AssertPtrReturn(client, NULL);
200
201 /**
202 * This mean that client has already bound or commited lease.
203 * If we've it happens it means that we received DHCPDISCOVER twice.
204 */
205 if (client->m_lease)
206 {
207 if (client->m_lease->isExpired())
208 expireLease4Client(client);
209 else
210 {
211 AssertReturn(client->m_lease->m_address.u != 0,NULL);
212 return client->m_lease;
213 }
214 }
215
216 RTNETADDRIPV4 hintAddress;
217 RawOption opt;
218 Lease *please = NULL;
219
220 NetworkConfigEntity *pNetCfg;
221
222 AssertReturn(g_RootConfig->match(*client, (BaseConfigEntity **)&pNetCfg) > 0, NULL);
223
224 /* DHCPDISCOVER MAY contain request address */
225 hintAddress.u = 0;
226 int rc = findOption(RTNET_DHCP_OPT_REQ_ADDR, pDhcpMsg, cbDhcpMsg, opt);
227 if (RT_SUCCESS(rc))
228 {
229 hintAddress.u = *(uint32_t *)opt.au8RawOpt;
230 if ( RT_H2N_U32(hintAddress.u) < RT_H2N_U32(pNetCfg->lowerIp().u)
231 || RT_H2N_U32(hintAddress.u) > RT_H2N_U32(pNetCfg->upperIp().u))
232 hintAddress.u = 0; /* clear hint */
233 }
234
235 if ( hintAddress.u
236 && !isAddressTaken(hintAddress, NULL))
237 {
238 please = new Lease();
239 please->pCfg = pNetCfg;
240 please->m_client = client;
241 client->m_lease = please;
242 client->m_lease->m_address = hintAddress;
243 m_allocations[please] = hintAddress;
244 return please;
245 }
246
247 uint32_t u32 = 0;
248 for(u32 = RT_H2N_U32(pNetCfg->lowerIp().u);
249 u32 <= RT_H2N_U32(pNetCfg->upperIp().u);
250 ++u32)
251 {
252 RTNETADDRIPV4 address;
253 address.u = RT_H2N_U32(u32);
254 if (!isAddressTaken(address, NULL))
255 {
256 please = new Lease();
257 please->pCfg = pNetCfg;
258 please->m_client = client;
259 client->m_lease = please;
260 client->m_lease->m_address = address;
261 m_allocations[please] = client->m_lease->m_address;
262 return please;
263 }
264 }
265
266 return NULL;
267}
268
269
270int ConfigurationManager::commitLease4Client(Client *client)
271{
272 client->m_lease->u64TimestampBindingStarted = 0;
273 client->m_lease->u32LeaseExpirationPeriod = client->m_lease->pCfg->expirationPeriod();
274 client->m_lease->u64TimestampLeasingStarted = RTTimeMilliTS();
275 client->m_lease->fBinding = false;
276 return VINF_SUCCESS;
277}
278
279int ConfigurationManager::expireLease4Client(Client *client)
280{
281 MapLease2Ip4AddressIterator it = m_allocations.find(client->m_lease);
282 AssertReturn(it != m_allocations.end(), VERR_NOT_FOUND);
283
284 m_allocations.erase(it);
285
286 delete client->m_lease;
287 client->m_lease = NULL;
288
289 return VINF_SUCCESS;
290}
291
292bool ConfigurationManager::isAddressTaken(const RTNETADDRIPV4& addr, Lease** ppLease)
293{
294 MapLease2Ip4AddressIterator it;
295
296 for (it = m_allocations.begin();
297 it != m_allocations.end();
298 ++it)
299 {
300 if (it->second.u == addr.u)
301 {
302 if (ppLease)
303 *ppLease = it->first;
304
305 return true;
306 }
307 }
308 return false;
309}
310
311NetworkConfigEntity *ConfigurationManager::addNetwork(NetworkConfigEntity *pCfg,
312 const RTNETADDRIPV4& networkId,
313 const RTNETADDRIPV4& netmask,
314 RTNETADDRIPV4& LowerAddress,
315 RTNETADDRIPV4& UpperAddress)
316{
317 static int id;
318 char name[64];
319
320 RTStrPrintf(name, RT_ELEMENTS(name), "network-%d", id);
321 std::string strname(name);
322 id++;
323
324
325 if (!LowerAddress.u)
326 LowerAddress = networkId;
327
328 if (!UpperAddress.u)
329 UpperAddress.u = networkId.u | (~netmask.u);
330
331 return new NetworkConfigEntity(strname,
332 g_RootConfig,
333 g_AnyClient,
334 5,
335 networkId,
336 netmask,
337 LowerAddress,
338 UpperAddress);
339}
340
341HostConfigEntity *ConfigurationManager::addHost(NetworkConfigEntity* pCfg,
342 const RTNETADDRIPV4& address,
343 ClientMatchCriteria *criteria)
344{
345 static int id;
346 char name[64];
347
348 RTStrPrintf(name, RT_ELEMENTS(name), "host-%d", id);
349 std::string strname(name);
350 id++;
351
352 return new HostConfigEntity(address, strname, pCfg, criteria);
353}
354
355/**
356 * Network manager
357 */
358NetworkManager *NetworkManager::getNetworkManager()
359{
360 if (!g_NetworkManager)
361 g_NetworkManager = new NetworkManager();
362
363 return g_NetworkManager;
364}
365
366
367/**
368 * Network manager creates DHCPOFFER datagramm
369 */
370int NetworkManager::offer4Client(Client *client, uint32_t u32Xid,
371 uint8_t *pu8ReqList, int cReqList)
372{
373 AssertPtrReturn(client, VERR_INTERNAL_ERROR);
374 AssertPtrReturn(client->m_lease, VERR_INTERNAL_ERROR);
375
376 prepareReplyPacket4Client(client, u32Xid);
377
378
379 RTNETADDRIPV4 address = client->m_lease->m_address;
380 BootPReplyMsg.BootPHeader.bp_yiaddr = address;
381
382 /* Ubuntu ???*/
383 BootPReplyMsg.BootPHeader.bp_ciaddr = address;
384
385 /* options:
386 * - IP lease time
387 * - message type
388 * - server identifier
389 */
390 RawOption opt;
391 RT_ZERO(opt);
392
393 /* XXX: can't store options per session */
394 AssertPtr(client);
395
396 opt.u8OptId = RTNET_DHCP_OPT_MSG_TYPE;
397 opt.au8RawOpt[0] = RTNET_DHCP_MT_OFFER;
398 opt.cbRawOpt = 1;
399 client->rawOptions.push_back(opt);
400
401 opt.u8OptId = RTNET_DHCP_OPT_LEASE_TIME;
402 *(uint32_t *)opt.au8RawOpt = RT_H2N_U32(client->m_lease->pCfg->expirationPeriod());
403 opt.cbRawOpt = sizeof(RTNETADDRIPV4);
404 client->rawOptions.push_back(opt);
405
406 processParameterReqList(client, pu8ReqList, cReqList);
407
408 return doReply(client);
409}
410
411
412/**
413 * Network manager creates DHCPACK
414 */
415int NetworkManager::ack(Client *client, uint32_t u32Xid,
416 uint8_t *pu8ReqList, int cReqList)
417{
418 AssertPtrReturn(client, VERR_INTERNAL_ERROR);
419 AssertPtrReturn(client->m_lease, VERR_INTERNAL_ERROR);
420
421 RTNETADDRIPV4 address;
422
423 prepareReplyPacket4Client(client, u32Xid);
424
425 address = client->m_lease->m_address;
426 BootPReplyMsg.BootPHeader.bp_ciaddr = address;
427
428
429 /* rfc2131 4.3.1 is about DHCPDISCOVER and this value is equal to ciaddr from
430 * DHCPREQUEST or 0 ...
431 * XXX: Using addressHint is not correct way to initialize [cy]iaddress...
432 */
433 BootPReplyMsg.BootPHeader.bp_ciaddr = client->m_lease->m_address;
434 BootPReplyMsg.BootPHeader.bp_yiaddr = client->m_lease->m_address;
435
436 Assert(BootPReplyMsg.BootPHeader.bp_yiaddr.u);
437
438 /* options:
439 * - IP address lease time (if DHCPREQUEST)
440 * - message type
441 * - server identifier
442 */
443 RawOption opt;
444 RT_ZERO(opt);
445
446 opt.u8OptId = RTNET_DHCP_OPT_MSG_TYPE;
447 opt.au8RawOpt[0] = RTNET_DHCP_MT_ACK;
448 opt.cbRawOpt = 1;
449 client->rawOptions.push_back(opt);
450
451 /*
452 * XXX: lease time should be conditional. If on dhcprequest then tim should be provided,
453 * else on dhcpinform it mustn't.
454 */
455 opt.u8OptId = RTNET_DHCP_OPT_LEASE_TIME;
456 *(uint32_t *)opt.au8RawOpt = RT_H2N_U32(client->m_lease->u32LeaseExpirationPeriod);
457 opt.cbRawOpt = sizeof(RTNETADDRIPV4);
458 client->rawOptions.push_back(opt);
459
460 processParameterReqList(client, pu8ReqList, cReqList);
461
462 return doReply(client);
463}
464
465
466/**
467 * Network manager creates DHCPNAK
468 */
469int NetworkManager::nak(Client* client, uint32_t u32Xid)
470{
471 AssertPtrReturn(client, VERR_INTERNAL_ERROR);
472 AssertPtrReturn(client->m_lease, VERR_INTERNAL_ERROR);
473
474 prepareReplyPacket4Client(client, u32Xid);
475
476 /* this field filed in prepareReplyPacket4Session, and
477 * RFC 2131 require to have it zero fo NAK.
478 */
479 BootPReplyMsg.BootPHeader.bp_yiaddr.u = 0;
480
481 /* options:
482 * - message type (if DHCPREQUEST)
483 * - server identifier
484 */
485 RawOption opt;
486 RT_ZERO(opt);
487
488 opt.u8OptId = RTNET_DHCP_OPT_MSG_TYPE;
489 opt.au8RawOpt[0] = RTNET_DHCP_MT_NAC;
490 opt.cbRawOpt = 1;
491 client->rawOptions.push_back(opt);
492
493 return doReply(client);
494}
495
496
497/**
498 *
499 */
500int NetworkManager::prepareReplyPacket4Client(Client *client, uint32_t u32Xid)
501{
502 AssertPtrReturn(client, VERR_INTERNAL_ERROR);
503 AssertPtrReturn(client->m_lease, VERR_INTERNAL_ERROR);
504
505 memset(&BootPReplyMsg, 0, sizeof(BootPReplyMsg));
506
507 BootPReplyMsg.BootPHeader.bp_op = RTNETBOOTP_OP_REPLY;
508 BootPReplyMsg.BootPHeader.bp_htype = RTNET_ARP_ETHER;
509 BootPReplyMsg.BootPHeader.bp_hlen = sizeof(RTMAC);
510 BootPReplyMsg.BootPHeader.bp_hops = 0;
511 BootPReplyMsg.BootPHeader.bp_xid = u32Xid;
512 BootPReplyMsg.BootPHeader.bp_secs = 0;
513 /* XXX: bp_flags should be processed specially */
514 BootPReplyMsg.BootPHeader.bp_flags = 0;
515 BootPReplyMsg.BootPHeader.bp_ciaddr.u = 0;
516 BootPReplyMsg.BootPHeader.bp_giaddr.u = 0;
517
518 BootPReplyMsg.BootPHeader.bp_chaddr.Mac = client->m_mac;
519
520 BootPReplyMsg.BootPHeader.bp_yiaddr = client->m_lease->m_address;
521 BootPReplyMsg.BootPHeader.bp_siaddr.u = 0;
522
523
524 BootPReplyMsg.BootPHeader.bp_vend.Dhcp.dhcp_cookie = RT_H2N_U32_C(RTNET_DHCP_COOKIE);
525
526 memset(&BootPReplyMsg.BootPHeader.bp_vend.Dhcp.dhcp_opts[0],
527 '\0',
528 RTNET_DHCP_OPT_SIZE);
529
530 return VINF_SUCCESS;
531}
532
533
534int NetworkManager::doReply(Client *client)
535{
536 int rc;
537
538 AssertPtrReturn(client, VERR_INTERNAL_ERROR);
539 AssertPtrReturn(client->m_lease, VERR_INTERNAL_ERROR);
540
541 /*
542 Options....
543 */
544 VBoxNetDhcpWriteCursor Cursor(&BootPReplyMsg.BootPHeader, RTNET_DHCP_NORMAL_SIZE);
545
546 /* The basics */
547
548 Cursor.optIPv4Addr(RTNET_DHCP_OPT_SERVER_ID, m_OurAddress);
549
550 while(!client->rawOptions.empty())
551 {
552 RawOption opt = client->rawOptions.back();
553 if (!Cursor.begin(opt.u8OptId, opt.cbRawOpt))
554 break;
555 Cursor.put(opt.au8RawOpt, opt.cbRawOpt);
556
557 client->rawOptions.pop_back();
558 }
559
560
561 if (!client->rawOptions.empty())
562 {
563 Log(("Wasn't able to put all options\n"));
564 /* force clean up */
565 client->rawOptions.clear();
566 }
567
568 Cursor.optEnd();
569
570 /*
571 */
572#if 0
573 if (!(pDhcpMsg->bp_flags & RTNET_DHCP_FLAGS_NO_BROADCAST)) /** @todo need to see someone set this flag to check that it's correct. */
574 {
575 rc = VBoxNetUDPUnicast(m_pSession,
576 m_hIf,
577 m_pIfBuf,
578 m_OurAddress,
579 &m_OurMac,
580 RTNETIPV4_PORT_BOOTPS, /* sender */
581 IPv4AddrBrdCast,
582 &BootPReplyMsg.BootPHeader->bp_chaddr.Mac,
583 RTNETIPV4_PORT_BOOTPC, /* receiver */
584 &BootPReplyMsg, cbBooPReplyMsg);
585 }
586 else
587#endif
588 rc = VBoxNetUDPBroadcast(m_pSession,
589 m_hIf,
590 m_pIfBuf,
591 m_OurAddress,
592 &m_OurMac,
593 RTNETIPV4_PORT_BOOTPS, /* sender */
594 RTNETIPV4_PORT_BOOTPC,
595 &BootPReplyMsg, RTNET_DHCP_NORMAL_SIZE);
596
597 AssertRCReturn(rc,rc);
598
599 return VINF_SUCCESS;
600}
601
602
603int NetworkManager::processParameterReqList(Client* client, uint8_t *pu8ReqList, int cReqList)
604{
605 /* request parameter list */
606 RawOption opt;
607 int idxParam = 0;
608
609 AssertPtrReturn(client, VERR_INTERNAL_ERROR);
610
611 uint8_t *pReqList = pu8ReqList;
612
613 const NetworkConfigEntity *pNetCfg = client->m_lease->pCfg;
614
615 for (idxParam = 0; idxParam < cReqList; ++idxParam)
616 {
617
618 RT_ZERO(opt);
619 opt.u8OptId = pReqList[idxParam];
620 switch(pReqList[idxParam])
621 {
622 case RTNET_DHCP_OPT_SUBNET_MASK:
623 ((PRTNETADDRIPV4)opt.au8RawOpt)->u = pNetCfg->netmask().u;
624 opt.cbRawOpt = sizeof(RTNETADDRIPV4);
625 client->rawOptions.push_back(opt);
626 break;
627
628 case RTNET_DHCP_OPT_ROUTERS:
629 case RTNET_DHCP_OPT_DNS:
630 {
631 const Ipv4AddressContainer lst =
632 g_ConfigurationManager->getAddressList(pReqList[idxParam]);
633 PRTNETADDRIPV4 pAddresses = (PRTNETADDRIPV4)&opt.au8RawOpt[0];
634
635 for (Ipv4AddressConstIterator it = lst.begin();
636 it != lst.end();
637 ++it)
638 {
639 *pAddresses = (*it);
640 pAddresses++;
641 opt.cbRawOpt += sizeof(RTNETADDRIPV4);
642 }
643
644 if (!lst.empty())
645 client->rawOptions.push_back(opt);
646 }
647 break;
648 case RTNET_DHCP_OPT_DOMAIN_NAME:
649 break;
650 default:
651 Log(("opt: %d is ignored\n", pReqList[idxParam]));
652 break;
653 }
654 }
655
656 return VINF_SUCCESS;
657}
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