VirtualBox

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

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

VBoxNetDHCP.cpp: uEnd (excludsive) -> uLast (inclusive).

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