VirtualBox

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

Last change on this file since 28714 was 28714, checked in by vboxsync, 15 years ago

intnetinline.h: Changed the prefix to IntNet.

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette