VirtualBox

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

Last change on this file since 18462 was 18462, checked in by vboxsync, 16 years ago

VBoxNetDHCP: r=bird: in WinMain(). size_t warnings.

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

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