VirtualBox

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

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

Networking: Preparing to make the driver return a send buffer to the device emulation.

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