VirtualBox

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

Last change on this file since 38791 was 38636, checked in by vboxsync, 13 years ago

*,IPRT: Redid the ring-3 init to always convert the arguments to UTF-8.

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