VirtualBox

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

Last change on this file since 33540 was 33540, checked in by vboxsync, 14 years ago

*: spelling fixes, thanks Timeless!

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 65.9 KB
Line 
1/* $Id: VBoxNetDHCP.cpp 33540 2010-10-28 09:27:05Z vboxsync $ */
2/** @file
3 * VBoxNetDHCP - DHCP Service for connecting to IntNet.
4 */
5
6/*
7 * Copyright (C) 2009 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 <iprt/alloca.h>
28#include <iprt/buildconfig.h>
29#include <iprt/err.h>
30#include <iprt/net.h> /* must come before getopt */
31#include <iprt/getopt.h>
32#include <iprt/initterm.h>
33#include <iprt/param.h>
34#include <iprt/path.h>
35#include <iprt/stream.h>
36#include <iprt/time.h>
37#include <iprt/string.h>
38
39#include <VBox/sup.h>
40#include <VBox/intnet.h>
41#include <VBox/intnetinline.h>
42#include <VBox/vmm.h>
43#include <VBox/version.h>
44
45#include "../NetLib/VBoxNetLib.h"
46
47#include <vector>
48#include <string>
49
50#ifdef RT_OS_WINDOWS /* WinMain */
51# include <Windows.h>
52# include <stdlib.h>
53#endif
54
55
56/*******************************************************************************
57* Structures and Typedefs *
58*******************************************************************************/
59
60/**
61 * DHCP configuration item.
62 *
63 * This is all public data because I'm too lazy to do it properly right now.
64 */
65class VBoxNetDhcpCfg
66{
67public:
68 /** The etheret addresses this matches config applies to.
69 * An empty vector means 'ANY'. */
70 std::vector<RTMAC> m_MacAddresses;
71 /** The upper address in the range. */
72 RTNETADDRIPV4 m_UpperAddr;
73 /** The lower address in the range. */
74 RTNETADDRIPV4 m_LowerAddr;
75
76 /** Option 1: The net mask. */
77 RTNETADDRIPV4 m_SubnetMask;
78 /* * Option 2: The time offset. */
79 /** Option 3: Routers for the subnet. */
80 std::vector<RTNETADDRIPV4> m_Routers;
81 /* * Option 4: Time server. */
82 /* * Option 5: Name server. */
83 /** Option 6: Domain Name Server (DNS) */
84 std::vector<RTNETADDRIPV4> m_DNSes;
85 /* * Option 7: Log server. */
86 /* * Option 8: Cookie server. */
87 /* * Option 9: LPR server. */
88 /* * Option 10: Impress server. */
89 /* * Option 11: Resource location server. */
90 /* * Option 12: Host name. */
91 std::string m_HostName;
92 /* * Option 13: Boot file size option. */
93 /* * Option 14: Merit dump file. */
94 /** Option 15: Domain name. */
95 std::string m_DomainName;
96 /* * Option 16: Swap server. */
97 /* * Option 17: Root path. */
98 /* * Option 18: Extension path. */
99 /* * Option 19: IP forwarding enable/disable. */
100 /* * Option 20: Non-local routing enable/disable. */
101 /* * Option 21: Policy filter. */
102 /* * Option 22: Maximum datagram reassembly size (MRS). */
103 /* * Option 23: Default IP time-to-live. */
104 /* * Option 24: Path MTU aging timeout. */
105 /* * Option 25: Path MTU plateau table. */
106 /* * Option 26: Interface MTU. */
107 /* * Option 27: All subnets are local. */
108 /* * Option 28: Broadcast address. */
109 /* * Option 29: Perform maximum discovery. */
110 /* * Option 30: Mask supplier. */
111 /* * Option 31: Perform route discovery. */
112 /* * Option 32: Router solicitation address. */
113 /* * Option 33: Static route. */
114 /* * Option 34: Trailer encapsulation. */
115 /* * Option 35: ARP cache timeout. */
116 /* * Option 36: Ethernet encapsulation. */
117 /* * Option 37: TCP Default TTL. */
118 /* * Option 38: TCP Keepalive Interval. */
119 /* * Option 39: TCP Keepalive Garbage. */
120 /* * Option 40: Network Information Service (NIS) Domain. */
121 /* * Option 41: Network Information Servers. */
122 /* * Option 42: Network Time Protocol Servers. */
123 /* * Option 43: Vendor Specific Information. */
124 /* * Option 44: NetBIOS over TCP/IP Name Server (NBNS). */
125 /* * Option 45: NetBIOS over TCP/IP Datagram distribution Server (NBDD). */
126 /* * Option 46: NetBIOS over TCP/IP Node Type. */
127 /* * Option 47: NetBIOS over TCP/IP Scope. */
128 /* * Option 48: X Window System Font Server. */
129 /* * Option 49: X Window System Display Manager. */
130
131 /** Option 51: IP Address Lease Time. */
132 uint32_t m_cSecLease;
133
134 /* * Option 64: Network Information Service+ Domain. */
135 /* * Option 65: Network Information Service+ Servers. */
136 /** Option 66: TFTP server name. */
137 std::string m_TftpServer;
138 /** Address for the bp_siaddr field corresponding to m_TftpServer. */
139 RTNETADDRIPV4 m_TftpServerAddr;
140 /** Option 67: Bootfile name. */
141 std::string m_BootfileName;
142
143 /* * Option 68: Mobile IP Home Agent. */
144 /* * Option 69: Simple Mail Transport Protocol (SMPT) Server. */
145 /* * Option 70: Post Office Protocol (POP3) Server. */
146 /* * Option 71: Network News Transport Protocol (NNTP) Server. */
147 /* * Option 72: Default World Wide Web (WWW) Server. */
148 /* * Option 73: Default Finger Server. */
149 /* * Option 74: Default Internet Relay Chat (IRC) Server. */
150 /* * Option 75: StreetTalk Server. */
151
152 /* * Option 119: Domain Search. */
153
154
155 VBoxNetDhcpCfg()
156 {
157 m_UpperAddr.u = UINT32_MAX;
158 m_LowerAddr.u = UINT32_MAX;
159 m_SubnetMask.u = UINT32_MAX;
160 m_cSecLease = 60*60; /* 1 hour */
161 }
162
163 /** Validates the configuration.
164 * @returns 0 on success, exit code + error message to stderr on failure. */
165 int validate(void)
166 {
167 if ( m_UpperAddr.u == UINT32_MAX
168 || m_LowerAddr.u == UINT32_MAX
169 || m_SubnetMask.u == UINT32_MAX)
170 {
171 RTStrmPrintf(g_pStdErr, "VBoxNetDHCP: Config is missing:");
172 if (m_UpperAddr.u == UINT32_MAX)
173 RTStrmPrintf(g_pStdErr, " --upper-ip");
174 if (m_LowerAddr.u == UINT32_MAX)
175 RTStrmPrintf(g_pStdErr, " --lower-ip");
176 if (m_SubnetMask.u == UINT32_MAX)
177 RTStrmPrintf(g_pStdErr, " --netmask");
178 return 2;
179 }
180
181 if (RT_N2H_U32(m_UpperAddr.u) < RT_N2H_U32(m_LowerAddr.u))
182 {
183 RTStrmPrintf(g_pStdErr, "VBoxNetDHCP: The --upper-ip value is lower than the --lower-ip one!\n"
184 " %d.%d.%d.%d < %d.%d.%d.%d\n",
185 m_UpperAddr.au8[0], m_UpperAddr.au8[1], m_UpperAddr.au8[2], m_UpperAddr.au8[3],
186 m_LowerAddr.au8[0], m_LowerAddr.au8[1], m_LowerAddr.au8[2], m_LowerAddr.au8[3]);
187 return 3;
188 }
189
190 /* the code goes insane if we have too many atm. lazy bird */
191 uint32_t cIPs = RT_N2H_U32(m_UpperAddr.u) - RT_N2H_U32(m_LowerAddr.u);
192 if (cIPs > 1024)
193 {
194 RTStrmPrintf(g_pStdErr, "VBoxNetDHCP: Too many IPs between --upper-ip and --lower-ip! %d (max 1024)\n"
195 " %d.%d.%d.%d < %d.%d.%d.%d\n",
196 cIPs,
197 m_UpperAddr.au8[0], m_UpperAddr.au8[1], m_UpperAddr.au8[2], m_UpperAddr.au8[3],
198 m_LowerAddr.au8[0], m_LowerAddr.au8[1], m_LowerAddr.au8[2], m_LowerAddr.au8[3]);
199 return 3;
200 }
201 return 0;
202 }
203
204 /**
205 * Is this config for one specific client?
206 *
207 * @return true / false.
208 */
209 bool isOneSpecificClient(void) const
210 {
211 return m_LowerAddr.u == m_UpperAddr.u
212 && m_MacAddresses.size() > 0;
213 }
214
215 /**
216 * Checks if this config matches the specified MAC address.
217 *
218 * @returns true / false.
219 *
220 * @param pMac The MAC address to match.
221 */
222 bool matchesMacAddress(PCRTMAC pMac) const
223 {
224 size_t i = m_MacAddresses.size();
225 if (RT_LIKELY(i < 1))
226 return true; /* no entries == ALL wildcard match */
227
228 while (i--)
229 {
230 PCRTMAC pCur = &m_MacAddresses[i];
231 if ( pCur->au16[0] == pMac->au16[0]
232 && pCur->au16[1] == pMac->au16[1]
233 && pCur->au16[2] == pMac->au16[2])
234 return true;
235 }
236 return false;
237 }
238
239};
240
241/**
242 * DHCP lease.
243 */
244class VBoxNetDhcpLease
245{
246public:
247 typedef enum State
248 {
249 /** Invalid. */
250 kState_Invalid = 0,
251 /** The lease is free / released. */
252 kState_Free,
253 /** An offer has been made.
254 * Expire time indicates when the offer expires. */
255 kState_Offer,
256 /** The lease is active.
257 * Expire time indicates when the lease expires. */
258 kState_Active
259 } State;
260
261 /** The client MAC address. */
262 RTMAC m_MacAddress;
263 /** The IPv4 address. */
264 RTNETADDRIPV4 m_IPv4Address;
265
266 /** The current lease state. */
267 State m_enmState;
268 /** The lease expiration time. */
269 RTTIMESPEC m_ExpireTime;
270 /** Transaction ID. */
271 uint32_t m_xid;
272 /** The configuration for this lease. */
273 VBoxNetDhcpCfg *m_pCfg;
274
275public:
276 /** Constructor taking an IPv4 address and a Config. */
277 VBoxNetDhcpLease(RTNETADDRIPV4 IPv4Addr, VBoxNetDhcpCfg *pCfg)
278 {
279 m_pCfg = pCfg;
280 m_IPv4Address = IPv4Addr;
281
282 m_MacAddress.au16[0] = m_MacAddress.au16[1] = m_MacAddress.au16[2] = 0xff;
283 m_enmState = kState_Free;
284 RTTimeSpecSetSeconds(&m_ExpireTime, 0);
285 m_xid = UINT32_MAX;
286 }
287
288 /** Destructor. */
289 ~VBoxNetDhcpLease()
290 {
291 m_IPv4Address.u = UINT32_MAX;
292 m_pCfg = NULL;
293 m_MacAddress.au16[0] = m_MacAddress.au16[1] = m_MacAddress.au16[2] = 0xff;
294 m_enmState = kState_Free;
295 m_xid = UINT32_MAX;
296 }
297
298 void offer(uint32_t xid);
299 void activate(void);
300 void activate(uint32_t xid);
301 void release(void);
302 bool hasExpired(void) const;
303
304 /**
305 * Checks if the lease is in use or not.
306 *
307 * @returns true if active, false if free or expired.
308 *
309 * @param pNow The current time to use. Optional.
310 */
311 bool isInUse(PCRTTIMESPEC pNow = NULL) const
312 {
313 if ( m_enmState == kState_Offer
314 || m_enmState == kState_Active)
315 {
316 RTTIMESPEC Now;
317 if (!pNow)
318 pNow = RTTimeNow(&Now);
319 return RTTimeSpecGetSeconds(&m_ExpireTime) > RTTimeSpecGetSeconds(pNow);
320 }
321 return false;
322 }
323
324 /**
325 * Is this lease for one specific client?
326 *
327 * @return true/false.
328 */
329 bool isOneSpecificClient(void) const
330 {
331 return m_pCfg
332 && m_pCfg->isOneSpecificClient();
333 }
334
335 /**
336 * Is this lease currently being offered to a client.
337 *
338 * @returns true / false.
339 */
340 bool isBeingOffered(void) const
341 {
342 return m_enmState == kState_Offer
343 && isInUse();
344 }
345
346 /**
347 * Is the lease in the current config or not.
348 *
349 * When updating the config we might leave active leases behind which aren't
350 * included in the new config. These will have m_pCfg set to NULL and should be
351 * freed up when they expired.
352 *
353 * @returns true / false.
354 */
355 bool isInCurrentConfig(void) const
356 {
357 return m_pCfg != NULL;
358 }
359};
360
361/**
362 * DHCP server instance.
363 */
364class VBoxNetDhcp
365{
366public:
367 VBoxNetDhcp();
368 virtual ~VBoxNetDhcp();
369
370 int parseArgs(int argc, char **argv);
371 int tryGoOnline(void);
372 int run(void);
373
374protected:
375 int addConfig(VBoxNetDhcpCfg *pCfg);
376 void explodeConfig(void);
377
378 bool handleDhcpMsg(uint8_t uMsgType, PCRTNETBOOTP pDhcpMsg, size_t cb);
379 bool handleDhcpReqDiscover(PCRTNETBOOTP pDhcpMsg, size_t cb);
380 bool handleDhcpReqRequest(PCRTNETBOOTP pDhcpMsg, size_t cb);
381 bool handleDhcpReqDecline(PCRTNETBOOTP pDhcpMsg, size_t cb);
382 bool handleDhcpReqRelease(PCRTNETBOOTP pDhcpMsg, size_t cb);
383 void makeDhcpReply(uint8_t uMsgType, VBoxNetDhcpLease *pLease, PCRTNETBOOTP pDhcpMsg, size_t cb);
384
385 VBoxNetDhcpLease *findLeaseByMacAddress(PCRTMAC pMacAddress, bool fAnyState);
386 VBoxNetDhcpLease *findLeaseByIpv4AndMacAddresses(RTNETADDRIPV4 IPv4Addr, PCRTMAC pMacAddress, bool fAnyState);
387 VBoxNetDhcpLease *newLease(PCRTNETBOOTP pDhcpMsg, size_t cb);
388
389 static uint8_t const *findOption(uint8_t uOption, PCRTNETBOOTP pDhcpMsg, size_t cb, size_t *pcbMaxOpt);
390 static bool findOptionIPv4Addr(uint8_t uOption, PCRTNETBOOTP pDhcpMsg, size_t cb, PRTNETADDRIPV4 pIPv4Addr);
391
392 inline void debugPrint( int32_t iMinLevel, bool fMsg, const char *pszFmt, ...) const;
393 void debugPrintV(int32_t iMinLevel, bool fMsg, const char *pszFmt, va_list va) const;
394 static const char *debugDhcpName(uint8_t uMsgType);
395
396protected:
397 /** @name The server configuration data members.
398 * @{ */
399 std::string m_Name;
400 std::string m_Network;
401 std::string m_TrunkName;
402 INTNETTRUNKTYPE m_enmTrunkType;
403 RTMAC m_MacAddress;
404 RTNETADDRIPV4 m_Ipv4Address;
405 std::string m_LeaseDBName;
406 /** @} */
407
408 /** The current configs. */
409 std::vector<VBoxNetDhcpCfg *> m_Cfgs;
410
411 /** The current leases. */
412 std::vector<VBoxNetDhcpLease> m_Leases;
413
414 /** @name The network interface
415 * @{ */
416 PSUPDRVSESSION m_pSession;
417 uint32_t m_cbSendBuf;
418 uint32_t m_cbRecvBuf;
419 INTNETIFHANDLE m_hIf; /**< The handle to the network interface. */
420 PINTNETBUF m_pIfBuf; /**< Interface buffer. */
421 /** @} */
422
423 /** @name Debug stuff
424 * @{ */
425 int32_t m_cVerbosity;
426 uint8_t m_uCurMsgType;
427 size_t m_cbCurMsg;
428 PCRTNETBOOTP m_pCurMsg;
429 VBOXNETUDPHDRS m_CurHdrs;
430 /** @} */
431};
432
433
434/*******************************************************************************
435* Global Variables *
436*******************************************************************************/
437/** Pointer to the DHCP server. */
438static VBoxNetDhcp *g_pDhcp;
439
440
441/**
442 * Offer this lease to a client.
443 *
444 * @param xid The transaction ID.
445 */
446void VBoxNetDhcpLease::offer(uint32_t xid)
447{
448 m_enmState = kState_Offer;
449 m_xid = xid;
450 RTTimeNow(&m_ExpireTime);
451 RTTimeSpecAddSeconds(&m_ExpireTime, 60);
452}
453
454
455/**
456 * Activate this lease (i.e. a client is now using it).
457 */
458void VBoxNetDhcpLease::activate(void)
459{
460 m_enmState = kState_Active;
461 RTTimeNow(&m_ExpireTime);
462 RTTimeSpecAddSeconds(&m_ExpireTime, m_pCfg ? m_pCfg->m_cSecLease : 60); /* m_pCfg can be NULL right now... */
463}
464
465
466/**
467 * Activate this lease with a new transaction ID.
468 *
469 * @param xid The transaction ID.
470 * @todo check if this is really necessary.
471 */
472void VBoxNetDhcpLease::activate(uint32_t xid)
473{
474 activate();
475 m_xid = xid;
476}
477
478
479/**
480 * Release a lease either upon client request or because it didn't quite match a
481 * DHCP_REQUEST.
482 */
483void VBoxNetDhcpLease::release(void)
484{
485 m_enmState = kState_Free;
486 RTTimeNow(&m_ExpireTime);
487 RTTimeSpecAddSeconds(&m_ExpireTime, 5);
488}
489
490
491/**
492 * Checks if the lease has expired or not.
493 *
494 * This just checks the expiration time not the state. This is so that this
495 * method will work for reusing RELEASEd leases when the client comes back after
496 * a reboot or ipconfig /renew. Callers not interested in info on released
497 * leases should check the state first.
498 *
499 * @returns true if expired, false if not.
500 */
501bool VBoxNetDhcpLease::hasExpired() const
502{
503 RTTIMESPEC Now;
504 return RTTimeSpecGetSeconds(&m_ExpireTime) > RTTimeSpecGetSeconds(RTTimeNow(&Now));
505}
506
507
508
509
510/**
511 * Construct a DHCP server with a default configuration.
512 */
513VBoxNetDhcp::VBoxNetDhcp()
514{
515 m_Name = "VBoxNetDhcp";
516 m_Network = "VBoxNetDhcp";
517 m_TrunkName = "";
518 m_enmTrunkType = kIntNetTrunkType_WhateverNone;
519 m_MacAddress.au8[0] = 0x08;
520 m_MacAddress.au8[1] = 0x00;
521 m_MacAddress.au8[2] = 0x27;
522 m_MacAddress.au8[3] = 0x40;
523 m_MacAddress.au8[4] = 0x41;
524 m_MacAddress.au8[5] = 0x42;
525 m_Ipv4Address.u = RT_H2N_U32_C(RT_BSWAP_U32_C(RT_MAKE_U32_FROM_U8( 10, 0, 2, 5)));
526
527 m_pSession = NIL_RTR0PTR;
528 m_cbSendBuf = 8192;
529 m_cbRecvBuf = 51200; /** @todo tune to 64 KB with help from SrvIntR0 */
530 m_hIf = INTNET_HANDLE_INVALID;
531 m_pIfBuf = NULL;
532
533 m_cVerbosity = 0;
534 m_uCurMsgType = UINT8_MAX;
535 m_cbCurMsg = 0;
536 m_pCurMsg = NULL;
537 memset(&m_CurHdrs, '\0', sizeof(m_CurHdrs));
538
539#if 0 /* enable to hack the code without a mile long argument list. */
540 VBoxNetDhcpCfg *pDefCfg = new VBoxNetDhcpCfg();
541 pDefCfg->m_LowerAddr.u = RT_H2N_U32_C(RT_BSWAP_U32_C(RT_MAKE_U32_FROM_U8( 10, 0, 2,100)));
542 pDefCfg->m_UpperAddr.u = RT_H2N_U32_C(RT_BSWAP_U32_C(RT_MAKE_U32_FROM_U8( 10, 0, 2,250)));
543 pDefCfg->m_SubnetMask.u = RT_H2N_U32_C(RT_BSWAP_U32_C(RT_MAKE_U32_FROM_U8(255,255,255, 0)));
544 RTNETADDRIPV4 Addr;
545 Addr.u = RT_H2N_U32_C(RT_BSWAP_U32_C(RT_MAKE_U32_FROM_U8( 10, 0, 2, 1)));
546 pDefCfg->m_Routers.push_back(Addr);
547 Addr.u = RT_H2N_U32_C(RT_BSWAP_U32_C(RT_MAKE_U32_FROM_U8( 10, 0, 2, 2)));
548 pDefCfg->m_DNSes.push_back(Addr);
549 pDefCfg->m_DomainName = "vboxnetdhcp.org";
550#if 0
551 pDefCfg->m_cSecLease = 60*60; /* 1 hour */
552#else
553 pDefCfg->m_cSecLease = 30; /* sec */
554#endif
555 pDefCfg->m_TftpServer = "10.0.2.3"; //??
556 this->addConfig(pDefCfg);
557#endif
558}
559
560
561/**
562 * Destruct a DHCP server.
563 */
564VBoxNetDhcp::~VBoxNetDhcp()
565{
566 /*
567 * Close the interface connection.
568 */
569 if (m_hIf != INTNET_HANDLE_INVALID)
570 {
571 INTNETIFCLOSEREQ CloseReq;
572 CloseReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
573 CloseReq.Hdr.cbReq = sizeof(CloseReq);
574 CloseReq.pSession = m_pSession;
575 CloseReq.hIf = m_hIf;
576 m_hIf = INTNET_HANDLE_INVALID;
577 int rc = SUPR3CallVMMR0Ex(NIL_RTR0PTR, NIL_VMCPUID, VMMR0_DO_INTNET_IF_CLOSE, 0, &CloseReq.Hdr);
578 AssertRC(rc);
579 }
580
581 if (m_pSession)
582 {
583 SUPR3Term(false /*fForced*/);
584 m_pSession = NIL_RTR0PTR;
585 }
586}
587
588
589/**
590 * Adds a config to the tail.
591 *
592 * @returns See VBoxNetDHCP::validate().
593 * @param pCfg The config too add.
594 * This object will be consumed by this call!
595 */
596int VBoxNetDhcp::addConfig(VBoxNetDhcpCfg *pCfg)
597{
598 int rc = 0;
599 if (pCfg)
600 {
601 rc = pCfg->validate();
602 if (!rc)
603 m_Cfgs.push_back(pCfg);
604 else
605 delete pCfg;
606 }
607 return rc;
608}
609
610
611/**
612 * Explodes the config into leases.
613 *
614 * @remarks This code is brute force and not very fast nor memory efficient.
615 * We will have to revisit this later.
616 *
617 * @remarks If an IP has been reconfigured for a fixed mac address and it's
618 * already leased to a client, we it won't be available until the
619 * client releases its lease or it expires.
620 */
621void VBoxNetDhcp::explodeConfig(void)
622{
623 RTTIMESPEC Now;
624 RTTimeNow(&Now);
625
626 /*
627 * Remove all non-active leases from the vector and zapping the
628 * config pointers of the once left behind.
629 */
630 std::vector<VBoxNetDhcpLease>::iterator Itr = m_Leases.begin();
631 while (Itr != m_Leases.end())
632 {
633 if (!Itr->isInUse(&Now))
634 Itr = m_Leases.erase(Itr);
635 else
636 {
637 Itr->m_pCfg = NULL;
638 Itr++;
639 }
640 }
641
642 /*
643 * Loop thru the configurations in reverse order, giving the last
644 * configs priority of the newer ones.
645 */
646 size_t iCfg = m_Cfgs.size();
647 while (iCfg-- > 0)
648 {
649 VBoxNetDhcpCfg *pCfg = m_Cfgs[iCfg];
650
651 /* Expand the IP lease range. */
652 uint32_t const uLast = RT_N2H_U32(pCfg->m_UpperAddr.u);
653 for (uint32_t i = RT_N2H_U32(pCfg->m_LowerAddr.u); i <= uLast; i++)
654 {
655 RTNETADDRIPV4 IPv4Addr;
656 IPv4Addr.u = RT_H2N_U32(i);
657
658 /* Check if it exists and is configured. */
659 VBoxNetDhcpLease *pLease = NULL;
660 for (size_t j = 0; j < m_Leases.size(); j++)
661 if (m_Leases[j].m_IPv4Address.u == IPv4Addr.u)
662 {
663 pLease = &m_Leases[j];
664 break;
665 }
666 if (pLease)
667 {
668 if (!pLease->m_pCfg)
669 pLease->m_pCfg = pCfg;
670 }
671 else
672 {
673 /* add it. */
674 VBoxNetDhcpLease NewLease(IPv4Addr, pCfg);
675 m_Leases.push_back(NewLease);
676 debugPrint(10, false, "exploseConfig: new lease %d.%d.%d.%d",
677 IPv4Addr.au8[0], IPv4Addr.au8[1], IPv4Addr.au8[2], IPv4Addr.au8[3]);
678 }
679 }
680 }
681}
682
683
684/**
685 * Parse the arguments.
686 *
687 * @returns 0 on success, fully bitched exit code on failure.
688 *
689 * @param argc Argument count.
690 * @param argv Argument vector.
691 */
692int VBoxNetDhcp::parseArgs(int argc, char **argv)
693{
694 static const RTGETOPTDEF s_aOptionDefs[] =
695 {
696 { "--name", 'N', RTGETOPT_REQ_STRING },
697 { "--network", 'n', RTGETOPT_REQ_STRING },
698 { "--trunk-name", 't', RTGETOPT_REQ_STRING },
699 { "--trunk-type", 'T', RTGETOPT_REQ_STRING },
700 { "--mac-address", 'a', RTGETOPT_REQ_MACADDR },
701 { "--ip-address", 'i', RTGETOPT_REQ_IPV4ADDR },
702 { "--lease-db", 'D', RTGETOPT_REQ_STRING },
703 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
704
705 { "--begin-config", 'b', RTGETOPT_REQ_NOTHING },
706 { "--gateway", 'g', RTGETOPT_REQ_IPV4ADDR },
707 { "--lower-ip", 'l', RTGETOPT_REQ_IPV4ADDR },
708 { "--upper-ip", 'u', RTGETOPT_REQ_IPV4ADDR },
709 { "--netmask", 'm', RTGETOPT_REQ_IPV4ADDR },
710 };
711
712 RTGETOPTSTATE State;
713 int rc = RTGetOptInit(&State, argc, argv, &s_aOptionDefs[0], RT_ELEMENTS(s_aOptionDefs), 0, 0 /*fFlags*/);
714 AssertRCReturn(rc, 49);
715
716 VBoxNetDhcpCfg *pCurCfg = NULL;
717 for (;;)
718 {
719 RTGETOPTUNION Val;
720 rc = RTGetOpt(&State, &Val);
721 if (!rc)
722 break;
723 switch (rc)
724 {
725 case 'N':
726 m_Name = Val.psz;
727 break;
728 case 'n':
729 m_Network = Val.psz;
730 break;
731 case 't':
732 m_TrunkName = Val.psz;
733 break;
734 case 'T':
735 if (!strcmp(Val.psz, "none"))
736 m_enmTrunkType = kIntNetTrunkType_None;
737 else if (!strcmp(Val.psz, "whatever"))
738 m_enmTrunkType = kIntNetTrunkType_WhateverNone;
739 else if (!strcmp(Val.psz, "netflt"))
740 m_enmTrunkType = kIntNetTrunkType_NetFlt;
741 else if (!strcmp(Val.psz, "netadp"))
742 m_enmTrunkType = kIntNetTrunkType_NetAdp;
743 else if (!strcmp(Val.psz, "srvnat"))
744 m_enmTrunkType = kIntNetTrunkType_SrvNat;
745 else
746 {
747 RTStrmPrintf(g_pStdErr, "Invalid trunk type '%s'\n", Val.psz);
748 return 1;
749 }
750 break;
751 case 'a':
752 m_MacAddress = Val.MacAddr;
753 break;
754 case 'i':
755 m_Ipv4Address = Val.IPv4Addr;
756 break;
757 case 'd':
758 m_LeaseDBName = Val.psz;
759 break;
760
761 case 'v':
762 m_cVerbosity++;
763 break;
764
765 /* Begin config. */
766 case 'b':
767 rc = addConfig(pCurCfg);
768 if (rc)
769 break;
770 pCurCfg = NULL;
771 /* fall thru */
772
773 /* config specific ones. */
774 case 'g':
775 case 'l':
776 case 'u':
777 case 'm':
778 if (!pCurCfg)
779 {
780 pCurCfg = new VBoxNetDhcpCfg();
781 if (!pCurCfg)
782 {
783 RTStrmPrintf(g_pStdErr, "VBoxNetDHCP: new VBoxDhcpCfg failed\n");
784 return 1;
785 }
786 }
787
788 switch (rc)
789 {
790 case 'g':
791 pCurCfg->m_Routers.push_back(Val.IPv4Addr);
792 break;
793
794 case 'l':
795 pCurCfg->m_LowerAddr = Val.IPv4Addr;
796 break;
797
798 case 'u':
799 pCurCfg->m_UpperAddr = Val.IPv4Addr;
800 break;
801
802 case 'm':
803 pCurCfg->m_SubnetMask = Val.IPv4Addr;
804 break;
805
806 case 0: /* ignore */ break;
807 default:
808 AssertMsgFailed(("%d", rc));
809 return 1;
810 }
811 break;
812
813 case 'V':
814 RTPrintf("%sr%u\n", RTBldCfgVersion(), RTBldCfgRevision());
815 return 1;
816
817 case 'h':
818 RTPrintf("VBoxNetDHCP Version %s\n"
819 "(C) 2009-" VBOX_C_YEAR " " VBOX_VENDOR "\n"
820 "All rights reserved.\n"
821 "\n"
822 "Usage: VBoxNetDHCP <options>\n"
823 "\n"
824 "Options:\n",
825 RTBldCfgVersion());
826 for (size_t i = 0; i < RT_ELEMENTS(s_aOptionDefs); i++)
827 RTPrintf(" -%c, %s\n", s_aOptionDefs[i].iShort, s_aOptionDefs[i].pszLong);
828 return 1;
829
830 default:
831 rc = RTGetOptPrintError(rc, &Val);
832 RTPrintf("Use --help for more information.\n");
833 return rc;
834 }
835 }
836
837 /*
838 * Do the reconfig. (move this later)
839 */
840 if (!rc)
841 explodeConfig();
842
843 return rc;
844}
845
846
847/**
848 * Tries to connect to the internal network.
849 *
850 * @returns 0 on success, exit code + error message to stderr on failure.
851 */
852int VBoxNetDhcp::tryGoOnline(void)
853{
854 /*
855 * Open the session, load ring-0 and issue the request.
856 */
857 int rc = SUPR3Init(&m_pSession);
858 if (RT_FAILURE(rc))
859 {
860 m_pSession = NIL_RTR0PTR;
861 RTStrmPrintf(g_pStdErr, "VBoxNetDHCP: SUPR3Init -> %Rrc", rc);
862 return 1;
863 }
864
865 char szPath[RTPATH_MAX];
866 rc = RTPathExecDir(szPath, sizeof(szPath) - sizeof("/VMMR0.r0"));
867 if (RT_FAILURE(rc))
868 {
869 RTStrmPrintf(g_pStdErr, "VBoxNetDHCP: RTPathProgram -> %Rrc", rc);
870 return 1;
871 }
872
873 rc = SUPR3LoadVMM(strcat(szPath, "/VMMR0.r0"));
874 if (RT_FAILURE(rc))
875 {
876 RTStrmPrintf(g_pStdErr, "VBoxNetDHCP: SUPR3LoadVMM(\"%s\") -> %Rrc", szPath, rc);
877 return 1;
878 }
879
880 /*
881 * Create the open request.
882 */
883 INTNETOPENREQ OpenReq;
884 OpenReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
885 OpenReq.Hdr.cbReq = sizeof(OpenReq);
886 OpenReq.pSession = m_pSession;
887 strncpy(OpenReq.szNetwork, m_Network.c_str(), sizeof(OpenReq.szNetwork));
888 OpenReq.szNetwork[sizeof(OpenReq.szNetwork) - 1] = '\0';
889 strncpy(OpenReq.szTrunk, m_TrunkName.c_str(), sizeof(OpenReq.szTrunk));
890 OpenReq.szTrunk[sizeof(OpenReq.szTrunk) - 1] = '\0';
891 OpenReq.enmTrunkType = m_enmTrunkType;
892 OpenReq.fFlags = 0; /** @todo check this */
893 OpenReq.cbSend = m_cbSendBuf;
894 OpenReq.cbRecv = m_cbRecvBuf;
895 OpenReq.hIf = INTNET_HANDLE_INVALID;
896
897 /*
898 * Issue the request.
899 */
900 debugPrint(2, false, "attempting to open/create network \"%s\"...", OpenReq.szNetwork);
901 rc = SUPR3CallVMMR0Ex(NIL_RTR0PTR, NIL_VMCPUID, VMMR0_DO_INTNET_OPEN, 0, &OpenReq.Hdr);
902 if (RT_SUCCESS(rc))
903 {
904 m_hIf = OpenReq.hIf;
905 debugPrint(1, false, "successfully opened/created \"%s\" - hIf=%#x", OpenReq.szNetwork, m_hIf);
906
907 /*
908 * Get the ring-3 address of the shared interface buffer.
909 */
910 INTNETIFGETBUFFERPTRSREQ GetBufferPtrsReq;
911 GetBufferPtrsReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
912 GetBufferPtrsReq.Hdr.cbReq = sizeof(GetBufferPtrsReq);
913 GetBufferPtrsReq.pSession = m_pSession;
914 GetBufferPtrsReq.hIf = m_hIf;
915 GetBufferPtrsReq.pRing3Buf = NULL;
916 GetBufferPtrsReq.pRing0Buf = NIL_RTR0PTR;
917 rc = SUPR3CallVMMR0Ex(NIL_RTR0PTR, NIL_VMCPUID, VMMR0_DO_INTNET_IF_GET_BUFFER_PTRS, 0, &GetBufferPtrsReq.Hdr);
918 if (RT_SUCCESS(rc))
919 {
920 PINTNETBUF pBuf = GetBufferPtrsReq.pRing3Buf;
921 debugPrint(1, false, "pBuf=%p cbBuf=%d cbSend=%d cbRecv=%d",
922 pBuf, pBuf->cbBuf, pBuf->cbSend, pBuf->cbRecv);
923 m_pIfBuf = pBuf;
924
925 /*
926 * Activate the interface.
927 */
928 INTNETIFSETACTIVEREQ ActiveReq;
929 ActiveReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
930 ActiveReq.Hdr.cbReq = sizeof(ActiveReq);
931 ActiveReq.pSession = m_pSession;
932 ActiveReq.hIf = m_hIf;
933 ActiveReq.fActive = true;
934 rc = SUPR3CallVMMR0Ex(NIL_RTR0PTR, NIL_VMCPUID, VMMR0_DO_INTNET_IF_SET_ACTIVE, 0, &ActiveReq.Hdr);
935 if (RT_SUCCESS(rc))
936 return 0;
937
938 /* bail out */
939 RTStrmPrintf(g_pStdErr, "VBoxNetDHCP: SUPR3CallVMMR0Ex(,VMMR0_DO_INTNET_IF_SET_PROMISCUOUS_MODE,) failed, rc=%Rrc\n", rc);
940 }
941 else
942 RTStrmPrintf(g_pStdErr, "VBoxNetDHCP: SUPR3CallVMMR0Ex(,VMMR0_DO_INTNET_IF_GET_BUFFER_PTRS,) failed, rc=%Rrc\n", rc);
943 }
944 else
945 RTStrmPrintf(g_pStdErr, "VBoxNetDHCP: SUPR3CallVMMR0Ex(,VMMR0_DO_INTNET_OPEN,) failed, rc=%Rrc\n", rc);
946
947 return RT_SUCCESS(rc) ? 0 : 1;
948}
949
950
951/**
952 * Runs the DHCP server.
953 *
954 * @returns exit code + error message to stderr on failure, won't return on
955 * success (you must kill this process).
956 */
957int VBoxNetDhcp::run(void)
958{
959 /*
960 * The loop.
961 */
962 PINTNETRINGBUF pRingBuf = &m_pIfBuf->Recv;
963 for (;;)
964 {
965 /*
966 * Wait for a packet to become available.
967 */
968 INTNETIFWAITREQ WaitReq;
969 WaitReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
970 WaitReq.Hdr.cbReq = sizeof(WaitReq);
971 WaitReq.pSession = m_pSession;
972 WaitReq.hIf = m_hIf;
973 WaitReq.cMillies = 2000; /* 2 secs - the sleep is for some reason uninterruptible... */ /** @todo fix interruptability in SrvIntNet! */
974 int rc = SUPR3CallVMMR0Ex(NIL_RTR0PTR, NIL_VMCPUID, VMMR0_DO_INTNET_IF_WAIT, 0, &WaitReq.Hdr);
975 if (RT_FAILURE(rc))
976 {
977 if (rc == VERR_TIMEOUT)
978 continue;
979 RTStrmPrintf(g_pStdErr, "VBoxNetDHCP: VMMR0_DO_INTNET_IF_WAIT returned %Rrc\n", rc);
980 return 1;
981 }
982
983 /*
984 * Process the receive buffer.
985 */
986 while (IntNetRingHasMoreToRead(pRingBuf))
987 {
988 size_t cb;
989 void *pv = VBoxNetUDPMatch(m_pIfBuf, RTNETIPV4_PORT_BOOTPS, &m_MacAddress,
990 VBOXNETUDP_MATCH_UNICAST | VBOXNETUDP_MATCH_BROADCAST | VBOXNETUDP_MATCH_CHECKSUM
991 | (m_cVerbosity > 2 ? VBOXNETUDP_MATCH_PRINT_STDERR : 0),
992 &m_CurHdrs, &cb);
993 if (pv && cb)
994 {
995 PCRTNETBOOTP pDhcpMsg = (PCRTNETBOOTP)pv;
996 m_pCurMsg = pDhcpMsg;
997 m_cbCurMsg = cb;
998
999 uint8_t uMsgType;
1000 if (RTNetIPv4IsDHCPValid(NULL /* why is this here? */, pDhcpMsg, cb, &uMsgType))
1001 {
1002 m_uCurMsgType = uMsgType;
1003 handleDhcpMsg(uMsgType, pDhcpMsg, cb);
1004 m_uCurMsgType = UINT8_MAX;
1005 }
1006 else
1007 debugPrint(1, true, "VBoxNetDHCP: Skipping invalid DHCP packet.\n"); /** @todo handle pure bootp clients too? */
1008
1009 m_pCurMsg = NULL;
1010 m_cbCurMsg = 0;
1011 }
1012 else if (VBoxNetArpHandleIt(m_pSession, m_hIf, m_pIfBuf, &m_MacAddress, m_Ipv4Address))
1013 {
1014 /* nothing */
1015 }
1016
1017 /* Advance to the next frame. */
1018 IntNetRingSkipFrame(pRingBuf);
1019 }
1020 }
1021
1022 return 0;
1023}
1024
1025
1026/**
1027 * Handles a DHCP message.
1028 *
1029 * @returns true if handled, false if not.
1030 * @param uMsgType The message type.
1031 * @param pDhcpMsg The DHCP message.
1032 * @param cb The size of the DHCP message.
1033 */
1034bool VBoxNetDhcp::handleDhcpMsg(uint8_t uMsgType, PCRTNETBOOTP pDhcpMsg, size_t cb)
1035{
1036 if (pDhcpMsg->bp_op == RTNETBOOTP_OP_REQUEST)
1037 {
1038 switch (uMsgType)
1039 {
1040 case RTNET_DHCP_MT_DISCOVER:
1041 return handleDhcpReqDiscover(pDhcpMsg, cb);
1042
1043 case RTNET_DHCP_MT_REQUEST:
1044 return handleDhcpReqRequest(pDhcpMsg, cb);
1045
1046 case RTNET_DHCP_MT_DECLINE:
1047 return handleDhcpReqDecline(pDhcpMsg, cb);
1048
1049 case RTNET_DHCP_MT_RELEASE:
1050 return handleDhcpReqRelease(pDhcpMsg, cb);
1051
1052 case RTNET_DHCP_MT_INFORM:
1053 debugPrint(0, true, "Should we handle this?");
1054 break;
1055
1056 default:
1057 debugPrint(0, true, "Unexpected.");
1058 break;
1059 }
1060 }
1061 return false;
1062}
1063
1064
1065/**
1066 * The client is requesting an offer.
1067 *
1068 * @returns true.
1069 *
1070 * @param pDhcpMsg The message.
1071 * @param cb The message size.
1072 */
1073bool VBoxNetDhcp::handleDhcpReqDiscover(PCRTNETBOOTP pDhcpMsg, size_t cb)
1074{
1075 /*
1076 * The newLease() method contains logic for finding current leases
1077 * and reusing them in case the client is forgetful.
1078 */
1079 VBoxNetDhcpLease *pLease = newLease(pDhcpMsg, cb);
1080 if (!pLease)
1081 return false;
1082 debugPrint(1, true, "Offering %d.%d.%d.%d to %.6Rhxs xid=%#x",
1083 pLease->m_IPv4Address.au8[0],
1084 pLease->m_IPv4Address.au8[1],
1085 pLease->m_IPv4Address.au8[2],
1086 pLease->m_IPv4Address.au8[3],
1087 &pDhcpMsg->bp_chaddr.Mac,
1088 pDhcpMsg->bp_xid);
1089 pLease->offer(pDhcpMsg->bp_xid);
1090
1091 makeDhcpReply(RTNET_DHCP_MT_OFFER, pLease, pDhcpMsg, cb);
1092 return true;
1093}
1094
1095
1096/**
1097 * The client is requesting an offer.
1098 *
1099 * @returns true.
1100 *
1101 * @param pDhcpMsg The message.
1102 * @param cb The message size.
1103 */
1104bool VBoxNetDhcp::handleDhcpReqRequest(PCRTNETBOOTP pDhcpMsg, size_t cb)
1105{
1106 /** @todo Probably need to match the server IP here to work correctly with
1107 * other servers. */
1108 /** @todo This code isn't entirely correct and quite a bit of a hack, but it
1109 * will have to do for now as the right thing (tm) is very complex.
1110 * Part of the fun is verifying that the request is something we can
1111 * and should handle. */
1112
1113 /*
1114 * Try find the lease by the requested address + client MAC address.
1115 */
1116 VBoxNetDhcpLease *pLease = NULL;
1117 RTNETADDRIPV4 IPv4Addr;
1118 bool fReqAddr = findOptionIPv4Addr(RTNET_DHCP_OPT_REQ_ADDR, pDhcpMsg, cb, &IPv4Addr);
1119 if (fReqAddr)
1120 {
1121 fReqAddr = true;
1122 pLease = findLeaseByIpv4AndMacAddresses(IPv4Addr, &pDhcpMsg->bp_chaddr.Mac, true /* fAnyState */);
1123 }
1124
1125 /*
1126 * Try find the lease by the client IP address + client MAC address.
1127 */
1128 if ( !pLease
1129 && pDhcpMsg->bp_ciaddr.u)
1130 pLease = findLeaseByIpv4AndMacAddresses(pDhcpMsg->bp_ciaddr, &pDhcpMsg->bp_chaddr.Mac, true /* fAnyState */);
1131
1132#if 0 /** @todo client id stuff - it doesn't make sense here imho, we need IP + MAC. What would make sense
1133 though is to compare the client id with what we've got in the lease and use it to root out
1134 bad requests. */
1135 /*
1136 * Try find the lease by using the client id.
1137 */
1138 if (!pLease)
1139 {
1140 size_t cbClientID = 0;
1141 uint8_t const *pbClientID = findOption(RTNET_DHCP_OPT_CLIENT_ID, pDhcpMsg, cb, &cbClientID);
1142 if ( pbClientID
1143 && cbClientID == sizeof(RTMAC) + 1
1144 && pbClientID[0] == RTNET_ARP_ETHER
1145 &&
1146 )
1147 {
1148 pLease = findLeaseByIpv4AndMacAddresses(pDhcpMsg->bp_ciaddr, &pDhcpMsg->bp_chaddr.Mac, true /* fAnyState */);
1149 }
1150 }
1151#endif
1152
1153 /*
1154 * Validate the lease that's requested.
1155 * We've already check the MAC and IP addresses.
1156 */
1157 bool fAckIt = false;
1158 if (pLease)
1159 {
1160 if (pLease->isBeingOffered())
1161 {
1162 if (pLease->m_xid == pDhcpMsg->bp_xid)
1163 debugPrint(2, true, "REQUEST for offered lease.");
1164 else
1165 debugPrint(2, true, "REQUEST for offered lease, xid mismatch. Expected %#x, got %#x.",
1166 pLease->m_xid, pDhcpMsg->bp_xid);
1167 pLease->activate(pDhcpMsg->bp_xid);
1168 fAckIt = true;
1169 }
1170 else if (!pLease->isInCurrentConfig())
1171 debugPrint(1, true, "REQUEST for obsolete lease -> NAK");
1172 else if (fReqAddr != (pDhcpMsg->bp_ciaddr.u != 0)) // ???
1173 {
1174 /** @todo this ain't safe. */
1175 debugPrint(1, true, "REQUEST for lease not on offer, assuming renewal. lease_xid=%#x bp_xid=%#x",
1176 pLease->m_xid, pDhcpMsg->bp_xid);
1177 fAckIt = true;
1178 pLease->activate(pDhcpMsg->bp_xid);
1179 }
1180 else
1181 debugPrint(1, true, "REQUEST for lease not on offer, NAK it.");
1182 }
1183
1184 /*
1185 * NAK if if no lease was found.
1186 */
1187 if (fAckIt)
1188 {
1189 debugPrint(1, false, "ACK'ing DHCP_REQUEST");
1190 makeDhcpReply(RTNET_DHCP_MT_ACK, pLease, pDhcpMsg, cb);
1191 }
1192 else
1193 {
1194 debugPrint(1, false, "NAK'ing DHCP_REQUEST");
1195 makeDhcpReply(RTNET_DHCP_MT_NAC, NULL, pDhcpMsg, cb);
1196 }
1197
1198 return true;
1199}
1200
1201
1202/**
1203 * The client is declining an offer we've made.
1204 *
1205 * @returns true.
1206 *
1207 * @param pDhcpMsg The message.
1208 * @param cb The message size.
1209 */
1210bool VBoxNetDhcp::handleDhcpReqDecline(PCRTNETBOOTP pDhcpMsg, size_t cb)
1211{
1212 /** @todo Probably need to match the server IP here to work correctly with
1213 * other servers. */
1214
1215 /*
1216 * The client is supposed to pass us option 50, requested address,
1217 * from the offer. We also match the lease state. Apparently the
1218 * MAC address is not supposed to be checked here.
1219 */
1220
1221 /** @todo this is not required in the initial implementation, do it later. */
1222 debugPrint(1, true, "DECLINE is not implemented");
1223 return true;
1224}
1225
1226
1227/**
1228 * The client is releasing its lease - good boy.
1229 *
1230 * @returns true.
1231 *
1232 * @param pDhcpMsg The message.
1233 * @param cb The message size.
1234 */
1235bool VBoxNetDhcp::handleDhcpReqRelease(PCRTNETBOOTP pDhcpMsg, size_t cb)
1236{
1237 /** @todo Probably need to match the server IP here to work correctly with
1238 * other servers. */
1239
1240 /*
1241 * The client may pass us option 61, client identifier, which we should
1242 * use to find the lease by.
1243 *
1244 * We're matching MAC address and lease state as well.
1245 */
1246
1247 /*
1248 * If no client identifier or if we couldn't find a lease by using it,
1249 * we will try look it up by the client IP address.
1250 */
1251
1252
1253 /*
1254 * If found, release it.
1255 */
1256
1257
1258 /** @todo this is not required in the initial implementation, do it later. */
1259 debugPrint(1, true, "RELEASE is not implemented");
1260 return true;
1261}
1262
1263
1264/**
1265 * Helper class for stuffing DHCP options into a reply packet.
1266 */
1267class VBoxNetDhcpWriteCursor
1268{
1269private:
1270 uint8_t *m_pbCur; /**< The current cursor position. */
1271 uint8_t *m_pbEnd; /**< The end the current option space. */
1272 uint8_t *m_pfOverload; /**< Pointer to the flags of the overload option. */
1273 uint8_t m_fUsed; /**< Overload fields that have been used. */
1274 PRTNETDHCPOPT m_pOpt; /**< The current option. */
1275 PRTNETBOOTP m_pDhcp; /**< The DHCP packet. */
1276 bool m_fOverflowed; /**< Set if we've overflowed, otherwise false. */
1277
1278public:
1279 /** Instantiate an option cursor for the specified DHCP message. */
1280 VBoxNetDhcpWriteCursor(PRTNETBOOTP pDhcp, size_t cbDhcp) :
1281 m_pbCur(&pDhcp->bp_vend.Dhcp.dhcp_opts[0]),
1282 m_pbEnd((uint8_t *)pDhcp + cbDhcp),
1283 m_pfOverload(NULL),
1284 m_fUsed(0),
1285 m_pOpt(NULL),
1286 m_pDhcp(pDhcp),
1287 m_fOverflowed(false)
1288 {
1289 AssertPtr(pDhcp);
1290 Assert(cbDhcp > RT_UOFFSETOF(RTNETBOOTP, bp_vend.Dhcp.dhcp_opts[10]));
1291 }
1292
1293 /** Destructor. */
1294 ~VBoxNetDhcpWriteCursor()
1295 {
1296 m_pbCur = m_pbEnd = m_pfOverload = NULL;
1297 m_pOpt = NULL;
1298 m_pDhcp = NULL;
1299 }
1300
1301 /**
1302 * Try use the bp_file field.
1303 * @returns true if not overloaded, false otherwise.
1304 */
1305 bool useBpFile(void)
1306 {
1307 if ( m_pfOverload
1308 && (*m_pfOverload & 1))
1309 return false;
1310 m_fUsed |= 1 /* bp_file flag*/;
1311 return true;
1312 }
1313
1314
1315 /**
1316 * Try overload more BOOTP fields
1317 */
1318 bool overloadMore(void)
1319 {
1320 /* switch option area. */
1321 uint8_t *pbNew;
1322 uint8_t *pbNewEnd;
1323 uint8_t fField;
1324 if (!(m_fUsed & 1))
1325 {
1326 fField = 1;
1327 pbNew = &m_pDhcp->bp_file[0];
1328 pbNewEnd = &m_pDhcp->bp_file[sizeof(m_pDhcp->bp_file)];
1329 }
1330 else if (!(m_fUsed & 2))
1331 {
1332 fField = 2;
1333 pbNew = &m_pDhcp->bp_sname[0];
1334 pbNewEnd = &m_pDhcp->bp_sname[sizeof(m_pDhcp->bp_sname)];
1335 }
1336 else
1337 return false;
1338
1339 if (!m_pfOverload)
1340 {
1341 /* Add an overload option. */
1342 *m_pbCur++ = RTNET_DHCP_OPT_OPTION_OVERLOAD;
1343 *m_pbCur++ = fField;
1344 m_pfOverload = m_pbCur;
1345 *m_pbCur++ = 1; /* bp_file flag */
1346 }
1347 else
1348 *m_pfOverload |= fField;
1349
1350 /* pad current option field */
1351 while (m_pbCur != m_pbEnd)
1352 *m_pbCur++ = RTNET_DHCP_OPT_PAD; /** @todo not sure if this stuff is at all correct... */
1353
1354 /* switch */
1355 m_pbCur = pbNew;
1356 m_pbEnd = pbNewEnd;
1357 return true;
1358 }
1359
1360 /**
1361 * Begin an option.
1362 *
1363 * @returns true on success, false if we're out of space.
1364 *
1365 * @param uOption The option number.
1366 * @param cb The amount of data.
1367 */
1368 bool begin(uint8_t uOption, size_t cb)
1369 {
1370 /* Check that the data of the previous option has all been written. */
1371 Assert( !m_pOpt
1372 || (m_pbCur - m_pOpt->dhcp_len == (uint8_t *)(m_pOpt + 1)));
1373 AssertMsg(cb <= 255, ("%#x\n", cb));
1374
1375 /* Check if we need to overload more stuff. */
1376 if ((uintptr_t)(m_pbEnd - m_pbCur) < cb + 2 + (m_pfOverload ? 1 : 3))
1377 {
1378 m_pOpt = NULL;
1379 if (!overloadMore())
1380 {
1381 m_fOverflowed = true;
1382 AssertMsgFailedReturn(("%u %#x\n", uOption, cb), false);
1383 }
1384 if ((uintptr_t)(m_pbEnd - m_pbCur) < cb + 2 + 1)
1385 {
1386 m_fOverflowed = true;
1387 AssertMsgFailedReturn(("%u %#x\n", uOption, cb), false);
1388 }
1389 }
1390
1391 /* Emit the option header. */
1392 m_pOpt = (PRTNETDHCPOPT)m_pbCur;
1393 m_pOpt->dhcp_opt = uOption;
1394 m_pOpt->dhcp_len = (uint8_t)cb;
1395 m_pbCur += 2;
1396 return true;
1397 }
1398
1399 /**
1400 * Puts option data.
1401 *
1402 * @param pvData The data.
1403 * @param cb The amount to put.
1404 */
1405 void put(void const *pvData, size_t cb)
1406 {
1407 Assert(m_pOpt || m_fOverflowed);
1408 if (RT_LIKELY(m_pOpt))
1409 {
1410 Assert((uintptr_t)m_pbCur - (uintptr_t)(m_pOpt + 1) + cb <= (size_t)m_pOpt->dhcp_len);
1411 memcpy(m_pbCur, pvData, cb);
1412 m_pbCur += cb;
1413 }
1414 }
1415
1416 /**
1417 * Puts an IPv4 Address.
1418 *
1419 * @param IPv4Addr The address.
1420 */
1421 void putIPv4Addr(RTNETADDRIPV4 IPv4Addr)
1422 {
1423 put(&IPv4Addr, 4);
1424 }
1425
1426 /**
1427 * Adds an IPv4 address option.
1428 *
1429 * @returns true/false just like begin().
1430 *
1431 * @param uOption The option number.
1432 * @param IPv4Addr The address.
1433 */
1434 bool optIPv4Addr(uint8_t uOption, RTNETADDRIPV4 IPv4Addr)
1435 {
1436 if (!begin(uOption, 4))
1437 return false;
1438 putIPv4Addr(IPv4Addr);
1439 return true;
1440 }
1441
1442 /**
1443 * Adds an option taking 1 or more IPv4 address.
1444 *
1445 * If the vector contains no addresses, the option will not be added.
1446 *
1447 * @returns true/false just like begin().
1448 *
1449 * @param uOption The option number.
1450 * @param rIPv4Addrs Reference to the address vector.
1451 */
1452 bool optIPv4Addrs(uint8_t uOption, std::vector<RTNETADDRIPV4> const &rIPv4Addrs)
1453 {
1454 size_t const c = rIPv4Addrs.size();
1455 if (!c)
1456 return true;
1457
1458 if (!begin(uOption, 4*c))
1459 return false;
1460 for (size_t i = 0; i < c; i++)
1461 putIPv4Addr(rIPv4Addrs[i]);
1462 return true;
1463 }
1464
1465 /**
1466 * Puts an 8-bit integer.
1467 *
1468 * @param u8 The integer.
1469 */
1470 void putU8(uint8_t u8)
1471 {
1472 put(&u8, 1);
1473 }
1474
1475 /**
1476 * Adds an 8-bit integer option.
1477 *
1478 * @returns true/false just like begin().
1479 *
1480 * @param uOption The option number.
1481 * @param u8 The integer
1482 */
1483 bool optU8(uint8_t uOption, uint8_t u8)
1484 {
1485 if (!begin(uOption, 1))
1486 return false;
1487 putU8(u8);
1488 return true;
1489 }
1490
1491 /**
1492 * Puts an 32-bit integer (network endian).
1493 *
1494 * @param u32Network The integer.
1495 */
1496 void putU32(uint32_t u32)
1497 {
1498 put(&u32, 4);
1499 }
1500
1501 /**
1502 * Adds an 32-bit integer (network endian) option.
1503 *
1504 * @returns true/false just like begin().
1505 *
1506 * @param uOption The option number.
1507 * @param u32Network The integer.
1508 */
1509 bool optU32(uint8_t uOption, uint32_t u32)
1510 {
1511 if (!begin(uOption, 4))
1512 return false;
1513 putU32(u32);
1514 return true;
1515 }
1516
1517 /**
1518 * Puts a std::string.
1519 *
1520 * @param rStr Reference to the string.
1521 */
1522 void putStr(std::string const &rStr)
1523 {
1524 put(rStr.c_str(), rStr.size());
1525 }
1526
1527 /**
1528 * Adds an std::string option if the string isn't empty.
1529 *
1530 * @returns true/false just like begin().
1531 *
1532 * @param uOption The option number.
1533 * @param rStr Reference to the string.
1534 */
1535 bool optStr(uint8_t uOption, std::string const &rStr)
1536 {
1537 const size_t cch = rStr.size();
1538 if (!cch)
1539 return true;
1540
1541 if (!begin(uOption, cch))
1542 return false;
1543 put(rStr.c_str(), cch);
1544 return true;
1545 }
1546
1547 /**
1548 * Whether we've overflowed.
1549 *
1550 * @returns true on overflow, false otherwise.
1551 */
1552 bool hasOverflowed(void) const
1553 {
1554 return m_fOverflowed;
1555 }
1556
1557 /**
1558 * Adds the terminating END option.
1559 *
1560 * The END will always be added as we're reserving room for it, however, we
1561 * might have dropped previous options due to overflows and that is what the
1562 * return status indicates.
1563 *
1564 * @returns true on success, false on a (previous) overflow.
1565 */
1566 bool optEnd(void)
1567 {
1568 Assert((uintptr_t)(m_pbEnd - m_pbCur) < 4096);
1569 *m_pbCur++ = RTNET_DHCP_OPT_END;
1570 return !hasOverflowed();
1571 }
1572};
1573
1574
1575/**
1576 * Constructs and sends a reply to a client.
1577 *
1578 * @returns
1579 * @param uMsgType The DHCP message type.
1580 * @param pLease The lease. This can be NULL for some replies.
1581 * @param pDhcpMsg The client message. We will dig out the MAC address,
1582 * transaction ID, and requested options from this.
1583 * @param cb The size of the client message.
1584 */
1585void VBoxNetDhcp::makeDhcpReply(uint8_t uMsgType, VBoxNetDhcpLease *pLease, PCRTNETBOOTP pDhcpMsg, size_t cb)
1586{
1587 size_t cbReply = RTNET_DHCP_NORMAL_SIZE; /** @todo respect the RTNET_DHCP_OPT_MAX_DHCP_MSG_SIZE option */
1588 PRTNETBOOTP pReply = (PRTNETBOOTP)alloca(cbReply);
1589
1590 /*
1591 * The fixed bits stuff.
1592 */
1593 pReply->bp_op = RTNETBOOTP_OP_REPLY;
1594 pReply->bp_htype = RTNET_ARP_ETHER;
1595 pReply->bp_hlen = sizeof(RTMAC);
1596 pReply->bp_hops = 0;
1597 pReply->bp_xid = pDhcpMsg->bp_xid;
1598 pReply->bp_secs = 0;
1599 pReply->bp_flags = 0; // (pDhcpMsg->bp_flags & RTNET_DHCP_FLAGS_NO_BROADCAST); ??
1600 pReply->bp_ciaddr.u = 0;
1601 pReply->bp_yiaddr.u = pLease ? pLease->m_IPv4Address.u : 0xffffffff;
1602 pReply->bp_siaddr.u = pLease && pLease->m_pCfg ? pLease->m_pCfg->m_TftpServerAddr.u : 0; /* (next server == TFTP)*/
1603 pReply->bp_giaddr.u = 0;
1604 memset(&pReply->bp_chaddr, '\0', sizeof(pReply->bp_chaddr));
1605 pReply->bp_chaddr.Mac = pDhcpMsg->bp_chaddr.Mac;
1606 memset(&pReply->bp_sname[0], '\0', sizeof(pReply->bp_sname));
1607 memset(&pReply->bp_file[0], '\0', sizeof(pReply->bp_file));
1608 pReply->bp_vend.Dhcp.dhcp_cookie = RT_H2N_U32_C(RTNET_DHCP_COOKIE);
1609 memset(&pReply->bp_vend.Dhcp.dhcp_opts[0], '\0', RTNET_DHCP_OPT_SIZE);
1610
1611 /*
1612 * The options - use a cursor class for dealing with the ugly stuff.
1613 */
1614 VBoxNetDhcpWriteCursor Cursor(pReply, cbReply);
1615
1616 /* The basics */
1617 Cursor.optU8(RTNET_DHCP_OPT_MSG_TYPE, uMsgType);
1618 Cursor.optIPv4Addr(RTNET_DHCP_OPT_SERVER_ID, m_Ipv4Address);
1619
1620 if (uMsgType != RTNET_DHCP_MT_NAC)
1621 {
1622 AssertReturnVoid(pLease && pLease->m_pCfg);
1623 const VBoxNetDhcpCfg *pCfg = pLease->m_pCfg; /* no need to retain it. */
1624
1625 /* The IP config. */
1626 Cursor.optU32(RTNET_DHCP_OPT_LEASE_TIME, RT_H2N_U32(pCfg->m_cSecLease));
1627 Cursor.optIPv4Addr(RTNET_DHCP_OPT_SUBNET_MASK, pCfg->m_SubnetMask);
1628 Cursor.optIPv4Addrs(RTNET_DHCP_OPT_ROUTERS, pCfg->m_Routers);
1629 Cursor.optIPv4Addrs(RTNET_DHCP_OPT_ROUTERS, pCfg->m_DNSes);
1630 Cursor.optStr(RTNET_DHCP_OPT_HOST_NAME, pCfg->m_HostName);
1631 Cursor.optStr(RTNET_DHCP_OPT_DOMAIN_NAME, pCfg->m_DomainName);
1632
1633 /* The PXE config. */
1634 if (pCfg->m_BootfileName.size())
1635 {
1636 if (Cursor.useBpFile())
1637 RTStrPrintf((char *)&pReply->bp_file[0], sizeof(pReply->bp_file), "%s", pCfg->m_BootfileName.c_str());
1638 else
1639 Cursor.optStr(RTNET_DHCP_OPT_BOOTFILE_NAME, pCfg->m_BootfileName);
1640 }
1641 }
1642
1643 /* Terminate the options. */
1644 if (!Cursor.optEnd())
1645 debugPrint(0, true, "option overflow\n");
1646
1647 /*
1648 * Send it.
1649 */
1650 int rc;
1651#if 0
1652 if (!(pDhcpMsg->bp_flags & RTNET_DHCP_FLAGS_NO_BROADCAST)) /** @todo need to see someone set this flag to check that it's correct. */
1653 {
1654 RTNETADDRIPV4 IPv4AddrBrdCast;
1655 IPv4AddrBrdCast.u = UINT32_C(0xffffffff); /* broadcast IP */
1656 rc = VBoxNetUDPUnicast(m_pSession, m_hIf, m_pIfBuf,
1657 m_Ipv4Address, &m_MacAddress, RTNETIPV4_PORT_BOOTPS, /* sender */
1658 IPv4AddrBrdCast, &pDhcpMsg->bp_chaddr.Mac, RTNETIPV4_PORT_BOOTPC, /* receiver */
1659 pReply, cbReply);
1660 }
1661 else
1662#endif
1663 rc = VBoxNetUDPBroadcast(m_pSession, m_hIf, m_pIfBuf,
1664 m_Ipv4Address, &m_MacAddress, RTNETIPV4_PORT_BOOTPS, /* sender */
1665 RTNETIPV4_PORT_BOOTPC, /* receiver port */
1666 pReply, cbReply);
1667 if (RT_FAILURE(rc))
1668 debugPrint(0, true, "error %Rrc when sending the reply", rc);
1669}
1670
1671
1672/**
1673 * Look up a lease by MAC address.
1674 *
1675 * @returns Pointer to the lease if found, NULL if not found.
1676 * @param pMacAddress The mac address.
1677 * @param fAnyState Any state.
1678 */
1679VBoxNetDhcpLease *VBoxNetDhcp::findLeaseByMacAddress(PCRTMAC pMacAddress, bool fAnyState)
1680{
1681 size_t iLease = m_Leases.size();
1682 while (iLease-- > 0)
1683 {
1684 VBoxNetDhcpLease *pLease = &m_Leases[iLease];
1685 if ( pLease
1686 && pLease->m_MacAddress.au16[0] == pMacAddress->au16[0]
1687 && pLease->m_MacAddress.au16[1] == pMacAddress->au16[1]
1688 && pLease->m_MacAddress.au16[2] == pMacAddress->au16[2]
1689 && ( fAnyState
1690 || (pLease->m_enmState != VBoxNetDhcpLease::kState_Free)) )
1691 return pLease;
1692 }
1693
1694 return NULL;
1695}
1696
1697
1698/**
1699 * Look up a lease by IPv4 and MAC addresses.
1700 *
1701 * @returns Pointer to the lease if found, NULL if not found.
1702 * @param IPv4Addr The IPv4 address.
1703 * @param pMacAddress The mac address.
1704 * @param fAnyState Any state.
1705 */
1706VBoxNetDhcpLease *VBoxNetDhcp::findLeaseByIpv4AndMacAddresses(RTNETADDRIPV4 IPv4Addr, PCRTMAC pMacAddress, bool fAnyState)
1707{
1708 size_t iLease = m_Leases.size();
1709 while (iLease-- > 0)
1710 {
1711 VBoxNetDhcpLease *pLease = &m_Leases[iLease];
1712 if ( pLease
1713 && pLease->m_IPv4Address.u == IPv4Addr.u
1714 && pLease->m_MacAddress.au16[0] == pMacAddress->au16[0]
1715 && pLease->m_MacAddress.au16[1] == pMacAddress->au16[1]
1716 && pLease->m_MacAddress.au16[2] == pMacAddress->au16[2]
1717 && ( fAnyState
1718 || (pLease->m_enmState != VBoxNetDhcpLease::kState_Free)) )
1719 return pLease;
1720 }
1721
1722 return NULL;
1723}
1724
1725
1726/**
1727 * Creates a new lease for the client specified in the DHCP message.
1728 *
1729 * The caller has already made sure it doesn't already have a lease.
1730 *
1731 * @returns Pointer to the lease if found, NULL+log if not found.
1732 * @param IPv4Addr The IPv4 address.
1733 * @param pMacAddress The MAC address.
1734 */
1735VBoxNetDhcpLease *VBoxNetDhcp::newLease(PCRTNETBOOTP pDhcpMsg, size_t cb)
1736{
1737 RTMAC const MacAddr = pDhcpMsg->bp_chaddr.Mac;
1738 RTTIMESPEC Now;
1739 RTTimeNow(&Now);
1740
1741 /*
1742 * Search the possible leases.
1743 *
1744 * We'll try do all the searches in one pass, that is to say, perfect
1745 * match, old lease, and next free/expired lease.
1746 */
1747 VBoxNetDhcpLease *pBest = NULL;
1748 VBoxNetDhcpLease *pOld = NULL;
1749 VBoxNetDhcpLease *pFree = NULL;
1750
1751 size_t cLeases = m_Leases.size();
1752 for (size_t i = 0; i < cLeases; i++)
1753 {
1754 VBoxNetDhcpLease *pCur = &m_Leases[i];
1755
1756 /* Skip it if no configuration, that means its not in the current config. */
1757 if (!pCur->m_pCfg)
1758 continue;
1759
1760 /* best */
1761 if ( pCur->isOneSpecificClient()
1762 && pCur->m_pCfg->matchesMacAddress(&MacAddr))
1763 {
1764 if ( !pBest
1765 || pBest->m_pCfg->m_MacAddresses.size() < pCur->m_pCfg->m_MacAddresses.size())
1766 pBest = pCur;
1767 }
1768
1769 /* old lease */
1770 if ( pCur->m_MacAddress.au16[0] == MacAddr.au16[0]
1771 && pCur->m_MacAddress.au16[1] == MacAddr.au16[1]
1772 && pCur->m_MacAddress.au16[2] == MacAddr.au16[2])
1773 {
1774 if ( !pOld
1775 || RTTimeSpecGetSeconds(&pCur->m_ExpireTime) > RTTimeSpecGetSeconds(&pFree->m_ExpireTime))
1776 pOld = pCur;
1777 }
1778
1779 /* expired lease */
1780 if (!pCur->isInUse(&Now))
1781 {
1782 if ( !pFree
1783 || RTTimeSpecGetSeconds(&pCur->m_ExpireTime) < RTTimeSpecGetSeconds(&pFree->m_ExpireTime))
1784 pFree = pCur;
1785 }
1786 }
1787
1788 VBoxNetDhcpLease *pNew = pBest;
1789 if (!pNew)
1790 pNew = pOld;
1791 if (!pNew)
1792 pNew = pFree;
1793 if (!pNew)
1794 {
1795 debugPrint(0, true, "No more leases.");
1796 return NULL;
1797 }
1798
1799 /*
1800 * Init the lease.
1801 */
1802 pNew->m_MacAddress = MacAddr;
1803 pNew->m_xid = pDhcpMsg->bp_xid;
1804 /** @todo extract the client id. */
1805
1806 return pNew;
1807}
1808
1809
1810/**
1811 * Finds an option.
1812 *
1813 * @returns On success, a pointer to the first byte in the option data (no none
1814 * then it'll be the byte following the 0 size field) and *pcbOpt set
1815 * to the option length.
1816 * On failure, NULL is returned and *pcbOpt unchanged.
1817 *
1818 * @param uOption The option to search for.
1819 * @param pDhcpMsg The DHCP message.
1820 * @param cb The size of the message.
1821 * @param pcbOpt Where to store the option size size. Optional. Note
1822 * that this is adjusted if the option length is larger
1823 * than the message buffer.
1824 */
1825/* static */ const uint8_t *
1826VBoxNetDhcp::findOption(uint8_t uOption, PCRTNETBOOTP pDhcpMsg, size_t cb, size_t *pcbOpt)
1827{
1828 Assert(uOption != RTNET_DHCP_OPT_PAD);
1829
1830 /*
1831 * Validate the DHCP bits and figure the max size of the options in the vendor field.
1832 */
1833 if (cb <= RT_UOFFSETOF(RTNETBOOTP, bp_vend.Dhcp.dhcp_opts))
1834 return NULL;
1835 if (pDhcpMsg->bp_vend.Dhcp.dhcp_cookie != RT_H2N_U32_C(RTNET_DHCP_COOKIE))
1836 return NULL;
1837 size_t cbLeft = cb - RT_UOFFSETOF(RTNETBOOTP, bp_vend.Dhcp.dhcp_opts);
1838 if (cbLeft > RTNET_DHCP_OPT_SIZE)
1839 cbLeft = RTNET_DHCP_OPT_SIZE;
1840
1841 /*
1842 * Search the vendor field.
1843 */
1844 bool fExtended = false;
1845 uint8_t const *pb = &pDhcpMsg->bp_vend.Dhcp.dhcp_opts[0];
1846 while (pb && cbLeft > 0)
1847 {
1848 uint8_t uCur = *pb;
1849 if (uCur == RTNET_DHCP_OPT_PAD)
1850 {
1851 cbLeft--;
1852 pb++;
1853 }
1854 else if (cbLeft <= 1)
1855 break;
1856 else
1857 {
1858 size_t cbCur = pb[1];
1859 if (cbCur > cbLeft - 2)
1860 cbCur = cbLeft - 2;
1861 if (uCur == uOption)
1862 {
1863 if (pcbOpt)
1864 *pcbOpt = cbCur;
1865 return pb+2;
1866 }
1867 pb += cbCur + 2;
1868 cbLeft -= cbCur - 2;
1869 }
1870 }
1871
1872 /** @todo search extended dhcp option field(s) when present */
1873
1874 return NULL;
1875}
1876
1877
1878/**
1879 * Locates an option with an IPv4 address in the DHCP message.
1880 *
1881 * @returns true and *pIpv4Addr if found, false if not.
1882 *
1883 * @param uOption The option to find.
1884 * @param pDhcpMsg The DHCP message.
1885 * @param cb The size of the message.
1886 * @param pIPv4Addr Where to put the address.
1887 */
1888/* static */ bool
1889VBoxNetDhcp::findOptionIPv4Addr(uint8_t uOption, PCRTNETBOOTP pDhcpMsg, size_t cb, PRTNETADDRIPV4 pIPv4Addr)
1890{
1891 size_t cbOpt;
1892 uint8_t const *pbOpt = findOption(uOption, pDhcpMsg, cb, &cbOpt);
1893 if (pbOpt)
1894 {
1895 if (cbOpt >= sizeof(RTNETADDRIPV4))
1896 {
1897 *pIPv4Addr = *(PCRTNETADDRIPV4)pbOpt;
1898 return true;
1899 }
1900 }
1901 return false;
1902}
1903
1904
1905/**
1906 * Print debug message depending on the m_cVerbosity level.
1907 *
1908 * @param iMinLevel The minimum m_cVerbosity level for this message.
1909 * @param fMsg Whether to dump parts for the current DHCP message.
1910 * @param pszFmt The message format string.
1911 * @param ... Optional arguments.
1912 */
1913inline void VBoxNetDhcp::debugPrint(int32_t iMinLevel, bool fMsg, const char *pszFmt, ...) const
1914{
1915 if (iMinLevel <= m_cVerbosity)
1916 {
1917 va_list va;
1918 va_start(va, pszFmt);
1919 debugPrintV(iMinLevel, fMsg, pszFmt, va);
1920 va_end(va);
1921 }
1922}
1923
1924
1925/**
1926 * Print debug message depending on the m_cVerbosity level.
1927 *
1928 * @param iMinLevel The minimum m_cVerbosity level for this message.
1929 * @param fMsg Whether to dump parts for the current DHCP message.
1930 * @param pszFmt The message format string.
1931 * @param va Optional arguments.
1932 */
1933void VBoxNetDhcp::debugPrintV(int iMinLevel, bool fMsg, const char *pszFmt, va_list va) const
1934{
1935 if (iMinLevel <= m_cVerbosity)
1936 {
1937 va_list vaCopy; /* This dude is *very* special, thus the copy. */
1938 va_copy(vaCopy, va);
1939 RTStrmPrintf(g_pStdErr, "VBoxNetDHCP: %s: %N\n", iMinLevel >= 2 ? "debug" : "info", pszFmt, &vaCopy);
1940 va_end(vaCopy);
1941
1942 if ( fMsg
1943 && m_cVerbosity >= 2
1944 && m_pCurMsg)
1945 {
1946 const char *pszMsg = m_uCurMsgType != UINT8_MAX ? debugDhcpName(m_uCurMsgType) : "";
1947 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",
1948 pszMsg,
1949 &m_pCurMsg->bp_chaddr,
1950 m_pCurMsg->bp_ciaddr.au8[0], m_pCurMsg->bp_ciaddr.au8[1], m_pCurMsg->bp_ciaddr.au8[2], m_pCurMsg->bp_ciaddr.au8[3],
1951 m_pCurMsg->bp_yiaddr.au8[0], m_pCurMsg->bp_yiaddr.au8[1], m_pCurMsg->bp_yiaddr.au8[2], m_pCurMsg->bp_yiaddr.au8[3],
1952 m_pCurMsg->bp_siaddr.au8[0], m_pCurMsg->bp_siaddr.au8[1], m_pCurMsg->bp_siaddr.au8[2], m_pCurMsg->bp_siaddr.au8[3],
1953 m_pCurMsg->bp_xid);
1954 }
1955 }
1956}
1957
1958
1959/**
1960 * Gets the name of given DHCP message type.
1961 *
1962 * @returns Readonly name.
1963 * @param uMsgType The message number.
1964 */
1965/* static */ const char *VBoxNetDhcp::debugDhcpName(uint8_t uMsgType)
1966{
1967 switch (uMsgType)
1968 {
1969 case 0: return "MT_00";
1970 case RTNET_DHCP_MT_DISCOVER: return "DISCOVER";
1971 case RTNET_DHCP_MT_OFFER: return "OFFER";
1972 case RTNET_DHCP_MT_REQUEST: return "REQUEST";
1973 case RTNET_DHCP_MT_DECLINE: return "DECLINE";
1974 case RTNET_DHCP_MT_ACK: return "ACK";
1975 case RTNET_DHCP_MT_NAC: return "NAC";
1976 case RTNET_DHCP_MT_RELEASE: return "RELEASE";
1977 case RTNET_DHCP_MT_INFORM: return "INFORM";
1978 case 9: return "MT_09";
1979 case 10: return "MT_0a";
1980 case 11: return "MT_0b";
1981 case 12: return "MT_0c";
1982 case 13: return "MT_0d";
1983 case 14: return "MT_0e";
1984 case 15: return "MT_0f";
1985 case 16: return "MT_10";
1986 case 17: return "MT_11";
1987 case 18: return "MT_12";
1988 case 19: return "MT_13";
1989 case UINT8_MAX: return "MT_ff";
1990 default: return "UNKNOWN";
1991 }
1992}
1993
1994
1995
1996/**
1997 * Entry point.
1998 */
1999extern "C" DECLEXPORT(int) TrustedMain(int argc, char **argv, char **envp)
2000{
2001 /*
2002 * Instantiate the DHCP server and hand it the options.
2003 */
2004 VBoxNetDhcp *pDhcp = new VBoxNetDhcp();
2005 if (!pDhcp)
2006 {
2007 RTStrmPrintf(g_pStdErr, "VBoxNetDHCP: new VBoxNetDhcp failed!\n");
2008 return 1;
2009 }
2010 int rc = pDhcp->parseArgs(argc - 1, argv + 1);
2011 if (rc)
2012 return rc;
2013
2014 /*
2015 * Try connect the server to the network.
2016 */
2017 rc = pDhcp->tryGoOnline();
2018 if (rc)
2019 {
2020 delete pDhcp;
2021 return rc;
2022 }
2023
2024 /*
2025 * Process requests.
2026 */
2027 g_pDhcp = pDhcp;
2028 rc = pDhcp->run();
2029 g_pDhcp = NULL;
2030 delete pDhcp;
2031
2032 return rc;
2033}
2034
2035
2036#ifndef VBOX_WITH_HARDENING
2037
2038int main(int argc, char **argv, char **envp)
2039{
2040 int rc = RTR3InitAndSUPLib();
2041 if (RT_FAILURE(rc))
2042 {
2043 RTStrmPrintf(g_pStdErr, "VBoxNetDHCP: RTR3InitAndSupLib failed, rc=%Rrc\n", rc);
2044 return 1;
2045 }
2046
2047 return TrustedMain(argc, argv, envp);
2048}
2049
2050# ifdef RT_OS_WINDOWS
2051
2052static LRESULT CALLBACK WindowProc(HWND hwnd,
2053 UINT uMsg,
2054 WPARAM wParam,
2055 LPARAM lParam
2056)
2057{
2058 if(uMsg == WM_DESTROY)
2059 {
2060 PostQuitMessage(0);
2061 return 0;
2062 }
2063 return DefWindowProc (hwnd, uMsg, wParam, lParam);
2064}
2065
2066static LPCSTR g_WndClassName = "VBoxNetDHCPClass";
2067
2068static DWORD WINAPI MsgThreadProc(__in LPVOID lpParameter)
2069{
2070 HWND hwnd = 0;
2071 HINSTANCE hInstance = (HINSTANCE)GetModuleHandle (NULL);
2072 bool bExit = false;
2073
2074 /* Register the Window Class. */
2075 WNDCLASS wc;
2076 wc.style = 0;
2077 wc.lpfnWndProc = WindowProc;
2078 wc.cbClsExtra = 0;
2079 wc.cbWndExtra = sizeof(void *);
2080 wc.hInstance = hInstance;
2081 wc.hIcon = NULL;
2082 wc.hCursor = NULL;
2083 wc.hbrBackground = (HBRUSH)(COLOR_BACKGROUND + 1);
2084 wc.lpszMenuName = NULL;
2085 wc.lpszClassName = g_WndClassName;
2086
2087 ATOM atomWindowClass = RegisterClass(&wc);
2088
2089 if (atomWindowClass != 0)
2090 {
2091 /* Create the window. */
2092 hwnd = CreateWindowEx (WS_EX_TOOLWINDOW | WS_EX_TRANSPARENT | WS_EX_TOPMOST,
2093 g_WndClassName, g_WndClassName,
2094 WS_POPUPWINDOW,
2095 -200, -200, 100, 100, NULL, NULL, hInstance, NULL);
2096
2097 if (hwnd)
2098 {
2099 SetWindowPos(hwnd, HWND_TOPMOST, -200, -200, 0, 0,
2100 SWP_NOACTIVATE | SWP_HIDEWINDOW | SWP_NOCOPYBITS | SWP_NOREDRAW | SWP_NOSIZE);
2101
2102 MSG msg;
2103 while (GetMessage(&msg, NULL, 0, 0))
2104 {
2105 TranslateMessage(&msg);
2106 DispatchMessage(&msg);
2107 }
2108
2109 DestroyWindow (hwnd);
2110
2111 bExit = true;
2112 }
2113
2114 UnregisterClass (g_WndClassName, hInstance);
2115 }
2116
2117 if(bExit)
2118 {
2119 /* no need any accuracy here, in anyway the DHCP server usually gets terminated with TerminateProcess */
2120 exit(0);
2121 }
2122
2123 return 0;
2124}
2125
2126
2127/** (We don't want a console usually.) */
2128int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
2129{
2130 NOREF(hInstance); NOREF(hPrevInstance); NOREF(lpCmdLine); NOREF(nCmdShow);
2131
2132 HANDLE hThread = CreateThread(
2133 NULL, /*__in_opt LPSECURITY_ATTRIBUTES lpThreadAttributes, */
2134 0, /*__in SIZE_T dwStackSize, */
2135 MsgThreadProc, /*__in LPTHREAD_START_ROUTINE lpStartAddress,*/
2136 NULL, /*__in_opt LPVOID lpParameter,*/
2137 0, /*__in DWORD dwCreationFlags,*/
2138 NULL /*__out_opt LPDWORD lpThreadId*/
2139 );
2140
2141 if(hThread != NULL)
2142 CloseHandle(hThread);
2143
2144 return main(__argc, __argv, environ);
2145}
2146# endif /* RT_OS_WINDOWS */
2147
2148#endif /* !VBOX_WITH_HARDENING */
2149
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