VirtualBox

source: vbox/trunk/src/VBox/NetworkServices/DHCP/VBoxNetDHCP.cpp@ 49409

Last change on this file since 49409 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: 33.5 KB
Line 
1/* $Id: VBoxNetDHCP.cpp 49328 2013-10-30 05:14:53Z vboxsync $ */
2/** @file
3 * VBoxNetDHCP - DHCP Service for connecting to IntNet.
4 */
5
6/*
7 * Copyright (C) 2009-2011 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/** @page pg_net_dhcp VBoxNetDHCP
19 *
20 * Write a few words...
21 *
22 */
23
24/*******************************************************************************
25* Header Files *
26*******************************************************************************/
27#include <VBox/com/com.h>
28#include <VBox/com/listeners.h>
29#include <VBox/com/string.h>
30#include <VBox/com/Guid.h>
31#include <VBox/com/array.h>
32#include <VBox/com/ErrorInfo.h>
33#include <VBox/com/errorprint.h>
34#include <VBox/com/EventQueue.h>
35#include <VBox/com/VirtualBox.h>
36
37#include <iprt/alloca.h>
38#include <iprt/buildconfig.h>
39#include <iprt/err.h>
40#include <iprt/net.h> /* must come before getopt */
41#include <iprt/getopt.h>
42#include <iprt/initterm.h>
43#include <iprt/message.h>
44#include <iprt/param.h>
45#include <iprt/path.h>
46#include <iprt/stream.h>
47#include <iprt/time.h>
48#include <iprt/string.h>
49
50#include <VBox/sup.h>
51#include <VBox/intnet.h>
52#include <VBox/intnetinline.h>
53#include <VBox/vmm/vmm.h>
54#include <VBox/version.h>
55
56
57#include "../NetLib/VBoxNetLib.h"
58#include "../NetLib/shared_ptr.h"
59
60#include <vector>
61#include <list>
62#include <string>
63#include <map>
64
65#include "../NetLib/VBoxNetBaseService.h"
66
67#ifdef RT_OS_WINDOWS /* WinMain */
68# include <Windows.h>
69# include <stdlib.h>
70# ifdef INET_ADDRSTRLEN
71/* On Windows INET_ADDRSTRLEN defined as 22 Ws2ipdef.h, because it include port number */
72# undef INET_ADDRSTRLEN
73# endif
74# define INET_ADDRSTRLEN 16
75#else
76# include <netinet/in.h>
77#endif
78
79
80#include "Config.h"
81/*******************************************************************************
82* Structures and Typedefs *
83*******************************************************************************/
84/**
85 * DHCP server instance.
86 */
87class VBoxNetDhcp: public VBoxNetBaseService
88{
89public:
90 VBoxNetDhcp();
91 virtual ~VBoxNetDhcp();
92
93 int init();
94 int run(void);
95 void usage(void) { /* XXX: document options */ };
96 int parseOpt(int rc, const RTGETOPTUNION& getOptVal);
97
98protected:
99 bool handleDhcpMsg(uint8_t uMsgType, PCRTNETBOOTP pDhcpMsg, size_t cb);
100 bool handleDhcpReqDiscover(PCRTNETBOOTP pDhcpMsg, size_t cb);
101 bool handleDhcpReqRequest(PCRTNETBOOTP pDhcpMsg, size_t cb);
102 bool handleDhcpReqDecline(PCRTNETBOOTP pDhcpMsg, size_t cb);
103 bool handleDhcpReqRelease(PCRTNETBOOTP pDhcpMsg, size_t cb);
104
105 void debugPrintV(int32_t iMinLevel, bool fMsg, const char *pszFmt, va_list va) const;
106 static const char *debugDhcpName(uint8_t uMsgType);
107
108protected:
109 /** @name The DHCP server specific configuration data members.
110 * @{ */
111 /*
112 * XXX: what was the plan? SQL3 or plain text file?
113 * How it will coexists with managment from VBoxManagement, who should manage db
114 * in that case (VBoxManage, VBoxSVC ???)
115 */
116 std::string m_LeaseDBName;
117
118 /** @} */
119
120 /* corresponding dhcp server description in Main */
121 ComPtr<IDHCPServer> m_DhcpServer;
122
123 ComPtr<INATNetwork> m_NATNetwork;
124
125 /*
126 * We will ignore cmd line parameters IFF there will be some DHCP specific arguments
127 * otherwise all paramters will come from Main.
128 */
129 bool m_fIgnoreCmdLineParameters;
130
131 /*
132 * -b -n 10.0.1.2 -m 255.255.255.0 -> to the list processing in
133 */
134 typedef struct
135 {
136 char Key;
137 std::string strValue;
138 } CMDLNPRM;
139 std::list<CMDLNPRM> CmdParameterll;
140 typedef std::list<CMDLNPRM>::iterator CmdParameterIterator;
141
142 /** @name Debug stuff
143 * @{ */
144 int32_t m_cVerbosity;
145 uint8_t m_uCurMsgType;
146 size_t m_cbCurMsg;
147 PCRTNETBOOTP m_pCurMsg;
148 VBOXNETUDPHDRS m_CurHdrs;
149 /** @} */
150};
151#if 0
152/* XXX: clean up it. */
153typedef std::vector<VBoxNetDhcpLease> DhcpLeaseContainer;
154typedef DhcpLeaseContainer::iterator DhcpLeaseIterator;
155typedef DhcpLeaseContainer::reverse_iterator DhcpLeaseRIterator;
156typedef DhcpLeaseContainer::const_iterator DhcpLeaseCIterator;
157#endif
158
159/*******************************************************************************
160* Global Variables *
161*******************************************************************************/
162/** Pointer to the DHCP server. */
163static VBoxNetDhcp *g_pDhcp;
164
165/* DHCP server specific options */
166static const RTGETOPTDEF g_aOptionDefs[] =
167{
168 { "--lease-db", 'D', RTGETOPT_REQ_STRING },
169 { "--begin-config", 'b', RTGETOPT_REQ_NOTHING },
170 { "--gateway", 'g', RTGETOPT_REQ_IPV4ADDR },
171 { "--lower-ip", 'l', RTGETOPT_REQ_IPV4ADDR },
172 { "--upper-ip", 'u', RTGETOPT_REQ_IPV4ADDR },
173};
174
175#if 0
176/* XXX this will gone */
177/**
178 * Offer this lease to a client.
179 *
180 * @param xid The transaction ID.
181 */
182void VBoxNetDhcpLease::offer(uint32_t xid)
183{
184 m_enmState = kState_Offer;
185 m_xid = xid;
186 RTTimeNow(&m_ExpireTime);
187 RTTimeSpecAddSeconds(&m_ExpireTime, 60);
188}
189
190
191/**
192 * Activate this lease (i.e. a client is now using it).
193 */
194void VBoxNetDhcpLease::activate(void)
195{
196 m_enmState = kState_Active;
197 RTTimeNow(&m_ExpireTime);
198 RTTimeSpecAddSeconds(&m_ExpireTime, m_pCfg ? m_pCfg->m_cSecLease : 60); /* m_pCfg can be NULL right now... */
199}
200
201
202/**
203 * Activate this lease with a new transaction ID.
204 *
205 * @param xid The transaction ID.
206 * @todo check if this is really necessary.
207 */
208void VBoxNetDhcpLease::activate(uint32_t xid)
209{
210 activate();
211 m_xid = xid;
212}
213
214
215/**
216 * Release a lease either upon client request or because it didn't quite match a
217 * DHCP_REQUEST.
218 */
219void VBoxNetDhcpLease::release(void)
220{
221 m_enmState = kState_Free;
222 RTTimeNow(&m_ExpireTime);
223 RTTimeSpecAddSeconds(&m_ExpireTime, 5);
224}
225
226
227/**
228 * Checks if the lease has expired or not.
229 *
230 * This just checks the expiration time not the state. This is so that this
231 * method will work for reusing RELEASEd leases when the client comes back after
232 * a reboot or ipconfig /renew. Callers not interested in info on released
233 * leases should check the state first.
234 *
235 * @returns true if expired, false if not.
236 */
237bool VBoxNetDhcpLease::hasExpired() const
238{
239 RTTIMESPEC Now;
240 return RTTimeSpecGetSeconds(&m_ExpireTime) > RTTimeSpecGetSeconds(RTTimeNow(&Now));
241}
242#endif
243
244/**
245 * Construct a DHCP server with a default configuration.
246 */
247VBoxNetDhcp::VBoxNetDhcp()
248{
249 m_Name = "VBoxNetDhcp";
250 m_Network = "VBoxNetDhcp";
251 m_TrunkName = "";
252 m_enmTrunkType = kIntNetTrunkType_WhateverNone;
253 m_MacAddress.au8[0] = 0x08;
254 m_MacAddress.au8[1] = 0x00;
255 m_MacAddress.au8[2] = 0x27;
256 m_MacAddress.au8[3] = 0x40;
257 m_MacAddress.au8[4] = 0x41;
258 m_MacAddress.au8[5] = 0x42;
259 m_Ipv4Address.u = RT_H2N_U32_C(RT_BSWAP_U32_C(RT_MAKE_U32_FROM_U8( 10, 0, 2, 5)));
260
261 m_pSession = NIL_RTR0PTR;
262 m_cbSendBuf = 8192;
263 m_cbRecvBuf = 51200; /** @todo tune to 64 KB with help from SrvIntR0 */
264 m_hIf = INTNET_HANDLE_INVALID;
265 m_pIfBuf = NULL;
266
267 m_cVerbosity = 0;
268 m_uCurMsgType = UINT8_MAX;
269 m_cbCurMsg = 0;
270 m_pCurMsg = NULL;
271 memset(&m_CurHdrs, '\0', sizeof(m_CurHdrs));
272
273 m_fIgnoreCmdLineParameters = true;
274
275#if 0 /* enable to hack the code without a mile long argument list. */
276 VBoxNetDhcpCfg *pDefCfg = new VBoxNetDhcpCfg();
277 pDefCfg->m_LowerAddr.u = RT_H2N_U32_C(RT_BSWAP_U32_C(RT_MAKE_U32_FROM_U8( 10, 0, 2,100)));
278 pDefCfg->m_UpperAddr.u = RT_H2N_U32_C(RT_BSWAP_U32_C(RT_MAKE_U32_FROM_U8( 10, 0, 2,250)));
279 pDefCfg->m_SubnetMask.u = RT_H2N_U32_C(RT_BSWAP_U32_C(RT_MAKE_U32_FROM_U8(255,255,255, 0)));
280 RTNETADDRIPV4 Addr;
281 Addr.u = RT_H2N_U32_C(RT_BSWAP_U32_C(RT_MAKE_U32_FROM_U8( 10, 0, 2, 1)));
282 pDefCfg->m_Routers.push_back(Addr);
283 Addr.u = RT_H2N_U32_C(RT_BSWAP_U32_C(RT_MAKE_U32_FROM_U8( 10, 0, 2, 2)));
284 pDefCfg->m_DNSes.push_back(Addr);
285 pDefCfg->m_DomainName = "vboxnetdhcp.org";
286# if 0
287 pDefCfg->m_cSecLease = 60*60; /* 1 hour */
288# else
289 pDefCfg->m_cSecLease = 30; /* sec */
290# endif
291 pDefCfg->m_TftpServer = "10.0.2.3"; //??
292 this->addConfig(pDefCfg);
293#endif
294}
295
296
297/**
298 * Destruct a DHCP server.
299 */
300VBoxNetDhcp::~VBoxNetDhcp()
301{
302}
303
304
305
306
307/**
308 * Parse the DHCP specific arguments.
309 *
310 * This callback caled for each paramenter so
311 * ....
312 * we nee post analisys of the parameters, at least
313 * for -b, -g, -l, -u, -m
314 */
315int VBoxNetDhcp::parseOpt(int rc, const RTGETOPTUNION& Val)
316{
317 CMDLNPRM prm;
318
319 /* Ok, we've entered here, thus we can't ignore cmd line parameters anymore */
320 m_fIgnoreCmdLineParameters = false;
321
322 prm.Key = rc;
323
324 switch (rc)
325 {
326 /* Begin config. */
327 case 'b':
328 CmdParameterll.push_back(prm);
329 break;
330
331 case 'l':
332 case 'u':
333 case 'm':
334 case 'g':
335 prm.strValue = std::string(Val.psz);
336 CmdParameterll.push_back(prm);
337 break;
338
339 case 'D':
340 break;
341
342 default:
343 rc = RTGetOptPrintError(rc, &Val);
344 RTPrintf("Use --help for more information.\n");
345 return rc;
346 }
347
348 return rc;
349}
350
351int VBoxNetDhcp::init()
352{
353 HRESULT hrc = S_OK;
354 /* ok, here we should initiate instance of dhcp server
355 * and listener for Dhcp configuration events
356 */
357 AssertRCReturn(virtualbox.isNull(), VERR_INTERNAL_ERROR);
358
359 hrc = virtualbox->FindDHCPServerByNetworkName(com::Bstr(m_Network.c_str()).raw(),
360 m_DhcpServer.asOutParam());
361 AssertComRCReturn(hrc, VERR_INTERNAL_ERROR);
362
363 hrc = virtualbox->FindNATNetworkByName(com::Bstr(m_Network.c_str()).raw(),
364 m_NATNetwork.asOutParam());
365
366 /* This isn't fatal in general case.
367 * AssertComRCReturn(hrc, VERR_INTERNAL_ERROR);
368 */
369
370 ConfigurationManager *confManager = ConfigurationManager::getConfigurationManager();
371 AssertPtrReturn(confManager, VERR_INTERNAL_ERROR);
372
373 /*
374 * if we have nat netework of the same name
375 * this is good chance that we are assigned to this network.
376 */
377 BOOL fNeedDhcpServer = false;
378 if ( !m_NATNetwork.isNull()
379 && SUCCEEDED(m_NATNetwork->COMGETTER(NeedDhcpServer)(&fNeedDhcpServer))
380 && fNeedDhcpServer)
381 {
382 /* 90% we are servicing NAT network */
383 RTNETADDRIPV4 gateway;
384 com::Bstr strGateway;
385 hrc = m_NATNetwork->COMGETTER(Gateway)(strGateway.asOutParam());
386 AssertComRCReturn(hrc, VERR_INTERNAL_ERROR);
387 RTNetStrToIPv4Addr(com::Utf8Str(strGateway).c_str(), &gateway);
388
389 confManager->addToAddressList(RTNET_DHCP_OPT_ROUTERS, gateway);
390
391 unsigned int i;
392 unsigned int count_strs;
393 com::SafeArray<BSTR> strs;
394 std::map<RTNETADDRIPV4, uint32_t> MapIp4Addr2Off;
395
396 hrc = m_NATNetwork->COMGETTER(LocalMappings)(ComSafeArrayAsOutParam(strs));
397 if ( SUCCEEDED(hrc)
398 && (count_strs = strs.size()))
399 {
400 for (i = 0; i < count_strs; ++i)
401 {
402 char szAddr[17];
403 RTNETADDRIPV4 ip4addr;
404 char *pszTerm;
405 uint32_t u32Off;
406 com::Utf8Str strLo2Off(strs[i]);
407 const char *pszLo2Off = strLo2Off.c_str();
408
409 RT_ZERO(szAddr);
410
411 pszTerm = RTStrStr(pszLo2Off, "=");
412
413 if ( pszTerm
414 && (pszTerm - pszLo2Off) <= INET_ADDRSTRLEN)
415 {
416 memcpy(szAddr, pszLo2Off, (pszTerm - pszLo2Off));
417 int rc = RTNetStrToIPv4Addr(szAddr, &ip4addr);
418 if (RT_SUCCESS(rc))
419 {
420 u32Off = RTStrToUInt32(pszTerm + 1);
421 if (u32Off != 0)
422 MapIp4Addr2Off.insert(
423 std::map<RTNETADDRIPV4,uint32_t>::value_type(ip4addr, u32Off));
424 }
425 }
426 }
427 }
428
429 strs.setNull();
430 ComPtr<IHost> host;
431 if (SUCCEEDED(virtualbox->COMGETTER(Host)(host.asOutParam())))
432 {
433 if (SUCCEEDED(host->COMGETTER(NameServers)(ComSafeArrayAsOutParam(strs))))
434 {
435 RTNETADDRIPV4 addr;
436 confManager->flushAddressList(RTNET_DHCP_OPT_DNS);
437 int rc;
438 for (i = 0; i < strs.size(); ++i)
439 {
440 rc = RTNetStrToIPv4Addr(com::Utf8Str(strs[i]).c_str(), &addr);
441 if (RT_SUCCESS(rc))
442 {
443 if (addr.au8[0] == 127)
444 {
445 if (MapIp4Addr2Off[addr] != 0)
446 {
447 addr.u = RT_H2N_U32(RT_N2H_U32(m_Ipv4Address.u & m_Ipv4Netmask.u)
448 + MapIp4Addr2Off[addr]);
449 }
450 else
451 continue;
452 }
453
454 confManager->addToAddressList(RTNET_DHCP_OPT_DNS, addr);
455 }
456 }
457 }
458
459 strs.setNull();
460#if 0
461 if (SUCCEEDED(host->COMGETTER(SearchStrings)(ComSafeArrayAsOutParam(strs))))
462 {
463 /* XXX: todo. */;
464 }
465 strs.setNull();
466#endif
467 com::Bstr domain;
468 if (SUCCEEDED(host->COMGETTER(DomainName)(domain.asOutParam())))
469 confManager->setString(RTNET_DHCP_OPT_DOMAIN_NAME, std::string(com::Utf8Str(domain).c_str()));
470
471
472 }
473
474 }
475
476 NetworkManager *netManager = NetworkManager::getNetworkManager();
477
478 netManager->setOurAddress(m_Ipv4Address);
479 netManager->setOurNetmask(m_Ipv4Netmask);
480 netManager->setOurMac(m_MacAddress);
481
482 /* Configuration fetching */
483 if (m_fIgnoreCmdLineParameters)
484 {
485 /* just fetch option array and add options to config */
486 /* per VM-settings ???
487 *
488 * - we have vms with attached adapters with known mac-addresses
489 * - mac-addresses might be changed as well as names, how keep our config cleaned ????
490 */
491 com::SafeArray<BSTR> sf;
492 hrc = m_DhcpServer->COMGETTER(GlobalOptions)(ComSafeArrayAsOutParam(sf));
493 AssertComRCReturn(hrc, VERR_INTERNAL_ERROR);
494
495#if 0
496 for (int i = 0; i < sf.size(); ++i)
497 {
498 RTPrintf("%d: %s\n", i, com::Utf8Str(sf[i]).c_str());
499 }
500
501#endif
502 com::Bstr strUpperIp, strLowerIp;
503
504 RTNETADDRIPV4 LowerAddress;
505 RTNETADDRIPV4 UpperAddress;
506
507 hrc = m_DhcpServer->COMGETTER(UpperIP)(strUpperIp.asOutParam());
508 AssertComRCReturn(hrc, VERR_INTERNAL_ERROR);
509 RTNetStrToIPv4Addr(com::Utf8Str(strUpperIp).c_str(), &UpperAddress);
510
511
512 hrc = m_DhcpServer->COMGETTER(LowerIP)(strLowerIp.asOutParam());
513 AssertComRCReturn(hrc, VERR_INTERNAL_ERROR);
514 RTNetStrToIPv4Addr(com::Utf8Str(strLowerIp).c_str(), &LowerAddress);
515
516 RTNETADDRIPV4 networkId;
517 networkId.u = m_Ipv4Address.u & m_Ipv4Netmask.u;
518 std::string name = std::string("default");
519
520 NetworkConfigEntity *pCfg = confManager->addNetwork(unconst(g_RootConfig),
521 networkId,
522 m_Ipv4Netmask,
523 LowerAddress,
524 UpperAddress);
525
526 com::Bstr bstr;
527 hrc = virtualbox->COMGETTER(HomeFolder)(bstr.asOutParam());
528 std::string strXmlLeaseFile(com::Utf8StrFmt("%ls%c%s.leases",
529 bstr.raw(), RTPATH_DELIMITER, m_Network.c_str()).c_str());
530 confManager->loadFromFile(strXmlLeaseFile);
531
532 } /* if(m_fIgnoreCmdLineParameters) */
533 else
534 {
535 CmdParameterIterator it;
536
537 RTNETADDRIPV4 networkId;
538 networkId.u = m_Ipv4Address.u & m_Ipv4Netmask.u;
539 RTNETADDRIPV4 netmask = m_Ipv4Netmask;
540 RTNETADDRIPV4 LowerAddress;
541 RTNETADDRIPV4 UpperAddress;
542
543 LowerAddress = networkId;
544 UpperAddress.u = RT_H2N_U32(RT_N2H_U32(LowerAddress.u) | RT_N2H_U32(netmask.u));
545
546 int idx = 0;
547 char name[64];
548
549
550 for (it = CmdParameterll.begin(); it != CmdParameterll.end(); ++it)
551 {
552 idx++;
553 RTStrPrintf(name, RT_ELEMENTS(name), "network-%d", idx);
554 std::string strname(name);
555
556 switch(it->Key)
557 {
558 case 'b':
559 /* config */
560 NetworkConfigEntity(strname,
561 g_RootConfig,
562 g_AnyClient,
563 5,
564 networkId,
565 netmask,
566 LowerAddress,
567 UpperAddress);
568 case 'l':
569 case 'u':
570 case 'm':
571 case 'g':
572 /* XXX: TBD */
573 break;
574 }
575 }
576 }
577 return VINF_SUCCESS;
578}
579
580/**
581 * Runs the DHCP server.
582 *
583 * @returns exit code + error message to stderr on failure, won't return on
584 * success (you must kill this process).
585 */
586int VBoxNetDhcp::run(void)
587{
588
589 /* XXX: shortcut should be hidden from network manager */
590 NetworkManager *netManager = NetworkManager::getNetworkManager();
591 netManager->m_pSession = m_pSession;
592 netManager->m_hIf = m_hIf;
593 netManager->m_pIfBuf = m_pIfBuf;
594
595 /*
596 * The loop.
597 */
598 PINTNETRINGBUF pRingBuf = &m_pIfBuf->Recv;
599 for (;;)
600 {
601 /*
602 * Wait for a packet to become available.
603 */
604 INTNETIFWAITREQ WaitReq;
605 WaitReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
606 WaitReq.Hdr.cbReq = sizeof(WaitReq);
607 WaitReq.pSession = m_pSession;
608 WaitReq.hIf = m_hIf;
609 WaitReq.cMillies = 2000; /* 2 secs - the sleep is for some reason uninterruptible... */ /** @todo fix interruptability in SrvIntNet! */
610 int rc = SUPR3CallVMMR0Ex(NIL_RTR0PTR, NIL_VMCPUID, VMMR0_DO_INTNET_IF_WAIT, 0, &WaitReq.Hdr);
611 if (RT_FAILURE(rc))
612 {
613 if (rc == VERR_TIMEOUT || rc == VERR_INTERRUPTED)
614 continue;
615 RTStrmPrintf(g_pStdErr, "VBoxNetDHCP: VMMR0_DO_INTNET_IF_WAIT returned %Rrc\n", rc);
616 return 1;
617 }
618
619 /*
620 * Process the receive buffer.
621 */
622 while (IntNetRingHasMoreToRead(pRingBuf))
623 {
624 size_t cb;
625 void *pv = VBoxNetUDPMatch(m_pIfBuf, RTNETIPV4_PORT_BOOTPS, &m_MacAddress,
626 VBOXNETUDP_MATCH_UNICAST | VBOXNETUDP_MATCH_BROADCAST | VBOXNETUDP_MATCH_CHECKSUM
627 | (m_cVerbosity > 2 ? VBOXNETUDP_MATCH_PRINT_STDERR : 0),
628 &m_CurHdrs, &cb);
629 if (pv && cb)
630 {
631 PCRTNETBOOTP pDhcpMsg = (PCRTNETBOOTP)pv;
632 m_pCurMsg = pDhcpMsg;
633 m_cbCurMsg = cb;
634
635 uint8_t uMsgType;
636 if (RTNetIPv4IsDHCPValid(NULL /* why is this here? */, pDhcpMsg, cb, &uMsgType))
637 {
638 m_uCurMsgType = uMsgType;
639 handleDhcpMsg(uMsgType, pDhcpMsg, cb);
640 m_uCurMsgType = UINT8_MAX;
641 }
642 else
643 debugPrint(1, true, "VBoxNetDHCP: Skipping invalid DHCP packet.\n"); /** @todo handle pure bootp clients too? */
644
645 m_pCurMsg = NULL;
646 m_cbCurMsg = 0;
647 }
648 else if (VBoxNetArpHandleIt(m_pSession, m_hIf, m_pIfBuf, &m_MacAddress, m_Ipv4Address))
649 {
650 /* nothing */
651 }
652
653 /* Advance to the next frame. */
654 IntNetRingSkipFrame(pRingBuf);
655 }
656 }
657
658 return 0;
659}
660
661
662/**
663 * Handles a DHCP message.
664 *
665 * @returns true if handled, false if not.
666 * @param uMsgType The message type.
667 * @param pDhcpMsg The DHCP message.
668 * @param cb The size of the DHCP message.
669 */
670bool VBoxNetDhcp::handleDhcpMsg(uint8_t uMsgType, PCRTNETBOOTP pDhcpMsg, size_t cb)
671{
672 if (pDhcpMsg->bp_op == RTNETBOOTP_OP_REQUEST)
673 {
674 switch (uMsgType)
675 {
676 case RTNET_DHCP_MT_DISCOVER:
677 return handleDhcpReqDiscover(pDhcpMsg, cb);
678
679 case RTNET_DHCP_MT_REQUEST:
680 return handleDhcpReqRequest(pDhcpMsg, cb);
681
682 case RTNET_DHCP_MT_DECLINE:
683 return handleDhcpReqDecline(pDhcpMsg, cb);
684
685 case RTNET_DHCP_MT_RELEASE:
686 return handleDhcpReqRelease(pDhcpMsg, cb);
687
688 case RTNET_DHCP_MT_INFORM:
689 debugPrint(0, true, "Should we handle this?");
690 break;
691
692 default:
693 debugPrint(0, true, "Unexpected.");
694 break;
695 }
696 }
697 return false;
698}
699
700
701/**
702 * The client is requesting an offer.
703 *
704 * @returns true.
705 *
706 * @param pDhcpMsg The message.
707 * @param cb The message size.
708 */
709bool VBoxNetDhcp::handleDhcpReqDiscover(PCRTNETBOOTP pDhcpMsg, size_t cb)
710{
711
712 /* let's main first */
713 if (!m_DhcpServer.isNull())
714 {
715#if 0
716 HRESULT hrc;
717 com::SafeArray<BSTR> sf;
718 hrc = m_DhcpServer->GetMacOptions(com::BstrFmt("%02X%02X%02X%02X%02X%02X",
719 pDhcpMsg->bp_chaddr.Mac.au8[0],
720 pDhcpMsg->bp_chaddr.Mac.au8[1],
721 pDhcpMsg->bp_chaddr.Mac.au8[2],
722 pDhcpMsg->bp_chaddr.Mac.au8[3],
723 pDhcpMsg->bp_chaddr.Mac.au8[4],
724 pDhcpMsg->bp_chaddr.Mac.au8[5]).raw(),
725 ComSafeArrayAsOutParam(sf));
726 if (SUCCEEDED(hrc))
727 {
728 /* XXX: per-host configuration */
729 }
730#endif
731 RawOption opt;
732 memset(&opt, 0, sizeof(RawOption));
733 /* 1. Find client */
734 ConfigurationManager *confManager = ConfigurationManager::getConfigurationManager();
735 Client client = confManager->getClientByDhcpPacket(pDhcpMsg, cb);
736
737 /* 2. Find/Bind lease for client */
738 Lease lease = confManager->allocateLease4Client(client, pDhcpMsg, cb);
739 AssertReturn(lease != Lease::NullLease, VINF_SUCCESS);
740
741 int rc = ConfigurationManager::extractRequestList(pDhcpMsg, cb, opt);
742
743 /* 3. Send of offer */
744 NetworkManager *networkManager = NetworkManager::getNetworkManager();
745
746 lease.bindingPhase(true);
747 lease.phaseStart(RTTimeMilliTS());
748 lease.setExpiration(300); /* 3 min. */
749 networkManager->offer4Client(client, pDhcpMsg->bp_xid, opt.au8RawOpt, opt.cbRawOpt);
750 } /* end of if(!m_DhcpServer.isNull()) */
751
752 return VINF_SUCCESS;
753}
754
755
756/**
757 * The client is requesting an offer.
758 *
759 * @returns true.
760 *
761 * @param pDhcpMsg The message.
762 * @param cb The message size.
763 */
764bool VBoxNetDhcp::handleDhcpReqRequest(PCRTNETBOOTP pDhcpMsg, size_t cb)
765{
766 ConfigurationManager *confManager = ConfigurationManager::getConfigurationManager();
767 NetworkManager *networkManager = NetworkManager::getNetworkManager();
768
769 /* 1. find client */
770 Client client = confManager->getClientByDhcpPacket(pDhcpMsg, cb);
771
772 /* 2. find bound lease */
773 Lease l = client.lease();
774 if (l != Lease::NullLease)
775 {
776
777 if (l.isExpired())
778 {
779 /* send client to INIT state */
780 Client c(client);
781 networkManager->nak(client, pDhcpMsg->bp_xid);
782 confManager->expireLease4Client(c);
783 return true;
784 }
785 else {
786 /* XXX: Validate request */
787 RawOption opt;
788 RT_ZERO(opt);
789
790 Client c(client);
791 int rc = confManager->commitLease4Client(c);
792 AssertRCReturn(rc, false);
793
794 rc = ConfigurationManager::extractRequestList(pDhcpMsg, cb, opt);
795 AssertRCReturn(rc, false);
796
797 networkManager->ack(client, pDhcpMsg->bp_xid, opt.au8RawOpt, opt.cbRawOpt);
798 }
799 }
800 else
801 {
802 networkManager->nak(client, pDhcpMsg->bp_xid);
803 }
804 return true;
805}
806
807
808/**
809 * The client is declining an offer we've made.
810 *
811 * @returns true.
812 *
813 * @param pDhcpMsg The message.
814 * @param cb The message size.
815 */
816bool VBoxNetDhcp::handleDhcpReqDecline(PCRTNETBOOTP, size_t)
817{
818 /** @todo Probably need to match the server IP here to work correctly with
819 * other servers. */
820
821 /*
822 * The client is supposed to pass us option 50, requested address,
823 * from the offer. We also match the lease state. Apparently the
824 * MAC address is not supposed to be checked here.
825 */
826
827 /** @todo this is not required in the initial implementation, do it later. */
828 debugPrint(1, true, "DECLINE is not implemented");
829 return true;
830}
831
832
833/**
834 * The client is releasing its lease - good boy.
835 *
836 * @returns true.
837 *
838 * @param pDhcpMsg The message.
839 * @param cb The message size.
840 */
841bool VBoxNetDhcp::handleDhcpReqRelease(PCRTNETBOOTP, size_t)
842{
843 /** @todo Probably need to match the server IP here to work correctly with
844 * other servers. */
845
846 /*
847 * The client may pass us option 61, client identifier, which we should
848 * use to find the lease by.
849 *
850 * We're matching MAC address and lease state as well.
851 */
852
853 /*
854 * If no client identifier or if we couldn't find a lease by using it,
855 * we will try look it up by the client IP address.
856 */
857
858
859 /*
860 * If found, release it.
861 */
862
863
864 /** @todo this is not required in the initial implementation, do it later. */
865 debugPrint(1, true, "RELEASE is not implemented");
866 return true;
867}
868
869
870/**
871 * Print debug message depending on the m_cVerbosity level.
872 *
873 * @param iMinLevel The minimum m_cVerbosity level for this message.
874 * @param fMsg Whether to dump parts for the current DHCP message.
875 * @param pszFmt The message format string.
876 * @param va Optional arguments.
877 */
878void VBoxNetDhcp::debugPrintV(int iMinLevel, bool fMsg, const char *pszFmt, va_list va) const
879{
880 if (iMinLevel <= m_cVerbosity)
881 {
882 va_list vaCopy; /* This dude is *very* special, thus the copy. */
883 va_copy(vaCopy, va);
884 RTStrmPrintf(g_pStdErr, "VBoxNetDHCP: %s: %N\n", iMinLevel >= 2 ? "debug" : "info", pszFmt, &vaCopy);
885 va_end(vaCopy);
886
887 if ( fMsg
888 && m_cVerbosity >= 2
889 && m_pCurMsg)
890 {
891 /* XXX: export this to debugPrinfDhcpMsg or variant and other method export
892 * to base class
893 */
894 const char *pszMsg = m_uCurMsgType != UINT8_MAX ? debugDhcpName(m_uCurMsgType) : "";
895 RTStrmPrintf(g_pStdErr, "VBoxNetDHCP: debug: %8s chaddr=%.6Rhxs ciaddr=%d.%d.%d.%d yiaddr=%d.%d.%d.%d siaddr=%d.%d.%d.%d xid=%#x\n",
896 pszMsg,
897 &m_pCurMsg->bp_chaddr,
898 m_pCurMsg->bp_ciaddr.au8[0], m_pCurMsg->bp_ciaddr.au8[1], m_pCurMsg->bp_ciaddr.au8[2], m_pCurMsg->bp_ciaddr.au8[3],
899 m_pCurMsg->bp_yiaddr.au8[0], m_pCurMsg->bp_yiaddr.au8[1], m_pCurMsg->bp_yiaddr.au8[2], m_pCurMsg->bp_yiaddr.au8[3],
900 m_pCurMsg->bp_siaddr.au8[0], m_pCurMsg->bp_siaddr.au8[1], m_pCurMsg->bp_siaddr.au8[2], m_pCurMsg->bp_siaddr.au8[3],
901 m_pCurMsg->bp_xid);
902 }
903 }
904}
905
906
907/**
908 * Gets the name of given DHCP message type.
909 *
910 * @returns Readonly name.
911 * @param uMsgType The message number.
912 */
913/* static */ const char *VBoxNetDhcp::debugDhcpName(uint8_t uMsgType)
914{
915 switch (uMsgType)
916 {
917 case 0: return "MT_00";
918 case RTNET_DHCP_MT_DISCOVER: return "DISCOVER";
919 case RTNET_DHCP_MT_OFFER: return "OFFER";
920 case RTNET_DHCP_MT_REQUEST: return "REQUEST";
921 case RTNET_DHCP_MT_DECLINE: return "DECLINE";
922 case RTNET_DHCP_MT_ACK: return "ACK";
923 case RTNET_DHCP_MT_NAC: return "NAC";
924 case RTNET_DHCP_MT_RELEASE: return "RELEASE";
925 case RTNET_DHCP_MT_INFORM: return "INFORM";
926 case 9: return "MT_09";
927 case 10: return "MT_0a";
928 case 11: return "MT_0b";
929 case 12: return "MT_0c";
930 case 13: return "MT_0d";
931 case 14: return "MT_0e";
932 case 15: return "MT_0f";
933 case 16: return "MT_10";
934 case 17: return "MT_11";
935 case 18: return "MT_12";
936 case 19: return "MT_13";
937 case UINT8_MAX: return "MT_ff";
938 default: return "UNKNOWN";
939 }
940}
941
942
943
944/**
945 * Entry point.
946 */
947extern "C" DECLEXPORT(int) TrustedMain(int argc, char **argv)
948{
949 /*
950 * Instantiate the DHCP server and hand it the options.
951 */
952 HRESULT hrc = com::Initialize();
953 Assert(!FAILED(hrc));
954
955 VBoxNetDhcp *pDhcp = new VBoxNetDhcp();
956 if (!pDhcp)
957 {
958 RTStrmPrintf(g_pStdErr, "VBoxNetDHCP: new VBoxNetDhcp failed!\n");
959 return 1;
960 }
961 int rc = pDhcp->parseArgs(argc - 1, argv + 1);
962 if (rc)
963 return rc;
964
965 pDhcp->init();
966
967 /*
968 * Try connect the server to the network.
969 */
970 rc = pDhcp->tryGoOnline();
971 if (rc)
972 {
973 delete pDhcp;
974 return rc;
975 }
976
977 /*
978 * Process requests.
979 */
980 g_pDhcp = pDhcp;
981 rc = pDhcp->run();
982 g_pDhcp = NULL;
983 delete pDhcp;
984
985 return rc;
986}
987
988
989#ifndef VBOX_WITH_HARDENING
990
991int main(int argc, char **argv)
992{
993 int rc = RTR3InitExe(argc, &argv, RTR3INIT_FLAGS_SUPLIB);
994 if (RT_FAILURE(rc))
995 return RTMsgInitFailure(rc);
996
997 return TrustedMain(argc, argv);
998}
999
1000# ifdef RT_OS_WINDOWS
1001
1002static LRESULT CALLBACK WindowProc(HWND hwnd,
1003 UINT uMsg,
1004 WPARAM wParam,
1005 LPARAM lParam
1006)
1007{
1008 if(uMsg == WM_DESTROY)
1009 {
1010 PostQuitMessage(0);
1011 return 0;
1012 }
1013 return DefWindowProc (hwnd, uMsg, wParam, lParam);
1014}
1015
1016static LPCWSTR g_WndClassName = L"VBoxNetDHCPClass";
1017
1018static DWORD WINAPI MsgThreadProc(__in LPVOID lpParameter)
1019{
1020 HWND hwnd = 0;
1021 HINSTANCE hInstance = (HINSTANCE)GetModuleHandle (NULL);
1022 bool bExit = false;
1023
1024 /* Register the Window Class. */
1025 WNDCLASS wc;
1026 wc.style = 0;
1027 wc.lpfnWndProc = WindowProc;
1028 wc.cbClsExtra = 0;
1029 wc.cbWndExtra = sizeof(void *);
1030 wc.hInstance = hInstance;
1031 wc.hIcon = NULL;
1032 wc.hCursor = NULL;
1033 wc.hbrBackground = (HBRUSH)(COLOR_BACKGROUND + 1);
1034 wc.lpszMenuName = NULL;
1035 wc.lpszClassName = g_WndClassName;
1036
1037 ATOM atomWindowClass = RegisterClass(&wc);
1038
1039 if (atomWindowClass != 0)
1040 {
1041 /* Create the window. */
1042 hwnd = CreateWindowEx (WS_EX_TOOLWINDOW | WS_EX_TRANSPARENT | WS_EX_TOPMOST,
1043 g_WndClassName, g_WndClassName,
1044 WS_POPUPWINDOW,
1045 -200, -200, 100, 100, NULL, NULL, hInstance, NULL);
1046
1047 if (hwnd)
1048 {
1049 SetWindowPos(hwnd, HWND_TOPMOST, -200, -200, 0, 0,
1050 SWP_NOACTIVATE | SWP_HIDEWINDOW | SWP_NOCOPYBITS | SWP_NOREDRAW | SWP_NOSIZE);
1051
1052 MSG msg;
1053 while (GetMessage(&msg, NULL, 0, 0))
1054 {
1055 TranslateMessage(&msg);
1056 DispatchMessage(&msg);
1057 }
1058
1059 DestroyWindow (hwnd);
1060
1061 bExit = true;
1062 }
1063
1064 UnregisterClass (g_WndClassName, hInstance);
1065 }
1066
1067 if(bExit)
1068 {
1069 /* no need any accuracy here, in anyway the DHCP server usually gets terminated with TerminateProcess */
1070 exit(0);
1071 }
1072
1073 return 0;
1074}
1075
1076
1077/** (We don't want a console usually.) */
1078int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
1079{
1080 NOREF(hInstance); NOREF(hPrevInstance); NOREF(lpCmdLine); NOREF(nCmdShow);
1081
1082 HANDLE hThread = CreateThread(
1083 NULL, /*__in_opt LPSECURITY_ATTRIBUTES lpThreadAttributes, */
1084 0, /*__in SIZE_T dwStackSize, */
1085 MsgThreadProc, /*__in LPTHREAD_START_ROUTINE lpStartAddress,*/
1086 NULL, /*__in_opt LPVOID lpParameter,*/
1087 0, /*__in DWORD dwCreationFlags,*/
1088 NULL /*__out_opt LPDWORD lpThreadId*/
1089 );
1090
1091 if(hThread != NULL)
1092 CloseHandle(hThread);
1093
1094 return main(__argc, __argv);
1095}
1096# endif /* RT_OS_WINDOWS */
1097
1098#endif /* !VBOX_WITH_HARDENING */
1099
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