VirtualBox

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

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

s-bahn coding...

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 30.6 KB
Line 
1/* $Id: VBoxNetDHCP.cpp 17663 2009-03-11 08:23:26Z 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/initterm.h>
32#include <iprt/net.h>
33#include <iprt/err.h>
34#include <iprt/time.h>
35#include <iprt/stream.h>
36#include <iprt/path.h>
37#include <iprt/param.h>
38#include <iprt/getopt.h>
39
40#include <VBox/sup.h>
41#include <VBox/intnet.h>
42#include <VBox/vmm.h>
43#include <VBox/version.h>
44
45#include "../UDPLib/VBoxNetUDP.h"
46
47#include <vector>
48#include <string>
49
50
51/*******************************************************************************
52* Structures and Typedefs *
53*******************************************************************************/
54
55/**
56 * DHCP configuration item.
57 *
58 * This is all public data because I'm too lazy to do it propertly right now.
59 */
60class VBoxNetDhcpCfg
61{
62public:
63 /** The etheret addresses this matches config applies to.
64 * An empty vector means 'ANY'. */
65 std::vector<RTMAC> m_MacAddresses;
66 /** The upper address in the range. */
67 RTNETADDRIPV4 m_UpperAddr;
68 /** The lower address in the range. */
69 RTNETADDRIPV4 m_LowerAddr;
70
71 /** Option 1: The net mask. */
72 RTNETADDRIPV4 m_SubnetMask;
73 /* * Option 2: The time offset. */
74 /** Option 3: Routers for the subnet. */
75 std::vector<RTNETADDRIPV4> m_Routers;
76 /* * Option 4: Time server. */
77 /* * Option 5: Name server. */
78 /** Option 6: Domain Name Server (DNS) */
79 std::vector<RTNETADDRIPV4> m_DNSes;
80 /* * Option 7: Log server. */
81 /* * Option 8: Cookie server. */
82 /* * Option 9: LPR server. */
83 /* * Option 10: Impress server. */
84 /* * Option 11: Resource location server. */
85 /* * Option 12: Host name. */
86 //std::string<char> m_HostName;
87 /* * Option 13: Boot file size option. */
88 /* * Option 14: Merit dump file. */
89 /** Option 15: Domain name. */
90 std::string m_DomainName;
91 /* * Option 16: Swap server. */
92 /* * Option 17: Root path. */
93 /* * Option 18: Extension path. */
94 /* * Option 19: IP forwarding enable/disable. */
95 /* * Option 20: Non-local routing enable/disable. */
96 /* * Option 21: Policy filter. */
97 /* * Option 22: Maximum datagram reassembly size (MRS). */
98 /* * Option 23: Default IP time-to-live. */
99 /* * Option 24: Path MTU aging timeout. */
100 /* * Option 25: Path MTU plateau table. */
101 /* * Option 26: Interface MTU. */
102 /* * Option 27: All subnets are local. */
103 /* * Option 28: Broadcast address. */
104 /* * Option 29: Perform maximum discovery. */
105 /* * Option 30: Mask supplier. */
106 /* * Option 31: Perform route discovery. */
107 /* * Option 32: Router solicitation address. */
108 /* * Option 33: Static route. */
109 /* * Option 34: Trailer encapsulation. */
110 /* * Option 35: ARP cache timeout. */
111 /* * Option 36: Ethernet encapsulation. */
112 /* * Option 37: TCP Default TTL. */
113 /* * Option 38: TCP Keepalive Interval. */
114 /* * Option 39: TCP Keepalive Garbage. */
115 /* * Option 40: Network Information Service (NIS) Domain. */
116 /* * Option 41: Network Information Servers. */
117 /* * Option 42: Network Time Protocol Servers. */
118 /* * Option 43: Vendor Specific Information. */
119 /* * Option 44: NetBIOS over TCP/IP Name Server (NBNS). */
120 /* * Option 45: NetBIOS over TCP/IP Datagram distribution Server (NBDD). */
121 /* * Option 46: NetBIOS over TCP/IP Node Type. */
122 /* * Option 47: NetBIOS over TCP/IP Scope. */
123 /* * Option 48: X Window System Font Server. */
124 /* * Option 49: X Window System Display Manager. */
125
126 /** Option 51: IP Address Lease Time. */
127 uint32_t m_cSecLease;
128
129 /* * Option 64: Network Information Service+ Domain. */
130 /* * Option 65: Network Information Service+ Servers. */
131 /** Option 66: TFTP server name. */
132 std::string m_TftpServer;
133 /** Option 67: Bootfile name. */
134 std::string m_BootfileName;
135
136 /* * Option 68: Mobile IP Home Agent. */
137 /* * Option 69: Simple Mail Transport Protocol (SMPT) Server. */
138 /* * Option 70: Post Office Protocol (POP3) Server. */
139 /* * Option 71: Network News Transport Protocol (NNTP) Server. */
140 /* * Option 72: Default World Wide Web (WWW) Server. */
141 /* * Option 73: Default Finger Server. */
142 /* * Option 74: Default Internet Relay Chat (IRC) Server. */
143 /* * Option 75: StreetTalk Server. */
144
145 /* * Option 119: Domain Search. */
146
147
148 VBoxNetDhcpCfg()
149 {
150 m_UpperAddr.u = UINT32_MAX;
151 m_LowerAddr.u = UINT32_MAX;
152 m_SubnetMask.u = UINT32_MAX;
153 m_cSecLease = 60*60; /* 1 hour */
154 }
155
156 /** Validates the configuration.
157 * @returns 0 on success, exit code + error message to stderr on failure. */
158 int validate(void)
159 {
160 if ( m_UpperAddr.u == UINT32_MAX
161 || m_LowerAddr.u == UINT32_MAX
162 || m_SubnetMask.u == UINT32_MAX)
163 {
164 RTStrmPrintf(g_pStdErr, "VBoxNetDHCP: Config is missing:");
165 if (m_UpperAddr.u == UINT32_MAX)
166 RTStrmPrintf(g_pStdErr, " --upper-ip");
167 if (m_LowerAddr.u == UINT32_MAX)
168 RTStrmPrintf(g_pStdErr, " --lower-ip");
169 if (m_SubnetMask.u == UINT32_MAX)
170 RTStrmPrintf(g_pStdErr, " --netmask");
171 return 2;
172 }
173 return 0;
174 }
175
176};
177
178/**
179 * DHCP lease.
180 */
181class VBoxNetDhcpLease
182{
183public:
184 /** The client MAC address. */
185 RTMAC m_MacAddress;
186 /** The lease expiration time. */
187 RTTIMESPEC m_ExpireTime;
188};
189
190/**
191 * DHCP server instance.
192 */
193class VBoxNetDhcp
194{
195public:
196 VBoxNetDhcp();
197 virtual ~VBoxNetDhcp();
198
199 int parseArgs(int argc, char **argv);
200 int tryGoOnline(void);
201 int run(void);
202
203protected:
204 int addConfig(VBoxNetDhcpCfg *pCfg);
205 bool handleDhcpMsg(uint8_t uMsgType, PCRTNETBOOTP pDhcpMsg, size_t cb);
206 bool handleDhcpReqDiscover(PCRTNETBOOTP pDhcpMsg, size_t cb);
207 bool handleDhcpReqRequest(PCRTNETBOOTP pDhcpMsg, size_t cb);
208 bool handleDhcpReqDecline(PCRTNETBOOTP pDhcpMsg, size_t cb);
209 bool handleDhcpReqRelease(PCRTNETBOOTP pDhcpMsg, size_t cb);
210 void handleDhcpReply(uint8_t uMsgType, VBoxNetDhcpLease *pLease, PCRTNETBOOTP pDhcpMsg, size_t cb);
211
212 inline void debugPrint( int32_t iMinLevel, bool fMsg, const char *pszFmt, ...) const;
213 void debugPrintV(int32_t iMinLevel, bool fMsg, const char *pszFmt, va_list va) const;
214 static const char *debugDhcpName(uint8_t uMsgType);
215
216protected:
217 /** @name The server configuration data members.
218 * @{ */
219 std::string m_Name;
220 std::string m_Network;
221 RTMAC m_MacAddress;
222 RTNETADDRIPV4 m_IpAddress;
223 /** @} */
224
225 /** The current configs. */
226 std::vector<VBoxNetDhcpCfg> m_Cfgs;
227
228 /** The current leases. */
229 std::vector<VBoxNetDhcpLease> m_Leases;
230
231 /** @name The network interface
232 * @{ */
233 PSUPDRVSESSION m_pSession;
234 uint32_t m_cbSendBuf;
235 uint32_t m_cbRecvBuf;
236 INTNETIFHANDLE m_hIf; /**< The handle to the network interface. */
237 PINTNETBUF m_pIfBuf; /**< Interface buffer. */
238 /** @} */
239
240 /** @name Debug stuff
241 * @{ */
242 int32_t m_cVerbosity;
243 uint8_t m_uCurMsgType;
244 uint16_t m_cbCurMsg;
245 PCRTNETBOOTP m_pCurMsg;
246 VBOXNETUDPHDRS m_CurHdrs;
247 /** @} */
248};
249
250
251/*******************************************************************************
252* Global Variables *
253*******************************************************************************/
254/** Pointer to the DHCP server. */
255static VBoxNetDhcp *g_pDhcp;
256
257
258
259/**
260 * Construct a DHCP server with a default configuration.
261 */
262VBoxNetDhcp::VBoxNetDhcp()
263{
264 m_Name = "VBoxNetDhcp";
265 m_Network = "VBoxNetDhcp";
266 m_MacAddress.au8[0] = 0x08;
267 m_MacAddress.au8[1] = 0x00;
268 m_MacAddress.au8[2] = 0x27;
269 m_MacAddress.au8[3] = 0x40;
270 m_MacAddress.au8[4] = 0x41;
271 m_MacAddress.au8[5] = 0x42;
272 m_IpAddress.u = RT_H2N_U32_C(RT_BSWAP_U32_C(RT_MAKE_U32_FROM_U8( 10, 0, 2, 5)));
273
274 m_pSession = NULL;
275 m_cbSendBuf = 8192;
276 m_cbRecvBuf = 51200; /** @todo tune to 64 KB with help from SrvIntR0 */
277 m_hIf = INTNET_HANDLE_INVALID;
278 m_pIfBuf = NULL;
279
280 m_cVerbosity = 0;
281 m_uCurMsgType = UINT8_MAX;
282 m_cbCurMsg = 0;
283 m_pCurMsg = NULL;
284 memset(&m_CurHdrs, '\0', sizeof(m_CurHdrs));
285
286#if 1 /* while hacking. */
287 VBoxNetDhcpCfg DefCfg;
288 DefCfg.m_LowerAddr.u = RT_H2N_U32_C(RT_BSWAP_U32_C(RT_MAKE_U32_FROM_U8( 10, 0, 2,100)));
289 DefCfg.m_UpperAddr.u = RT_H2N_U32_C(RT_BSWAP_U32_C(RT_MAKE_U32_FROM_U8( 10, 0, 2,250)));
290 DefCfg.m_SubnetMask.u = RT_H2N_U32_C(RT_BSWAP_U32_C(RT_MAKE_U32_FROM_U8(255,255,255, 0)));
291 RTNETADDRIPV4 Addr;
292 Addr.u = RT_H2N_U32_C(RT_BSWAP_U32_C(RT_MAKE_U32_FROM_U8( 10, 0, 2, 1)));
293 DefCfg.m_Routers.push_back(Addr);
294 Addr.u = RT_H2N_U32_C(RT_BSWAP_U32_C(RT_MAKE_U32_FROM_U8( 10, 0, 2, 2)));
295 DefCfg.m_DNSes.push_back(Addr);
296 DefCfg.m_DomainName = "vboxnetdhcp.org";
297 DefCfg.m_cSecLease = 60*60; /* 1 hour */
298 DefCfg.m_TftpServer = "10.0.2.3"; //??
299#endif
300}
301
302
303/**
304 * Destruct a DHCP server.
305 */
306VBoxNetDhcp::~VBoxNetDhcp()
307{
308 /*
309 * Close the interface connection.
310 */
311 if (m_hIf != INTNET_HANDLE_INVALID)
312 {
313 INTNETIFCLOSEREQ CloseReq;
314 CloseReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
315 CloseReq.Hdr.cbReq = sizeof(CloseReq);
316 CloseReq.pSession = m_pSession;
317 CloseReq.hIf = m_hIf;
318 m_hIf = INTNET_HANDLE_INVALID;
319 int rc = SUPCallVMMR0Ex(NIL_RTR0PTR, VMMR0_DO_INTNET_IF_CLOSE, 0, &CloseReq.Hdr);
320 AssertRC(rc);
321 }
322
323 if (m_pSession)
324 {
325 SUPTerm(false /* not forced */);
326 m_pSession = NULL;
327 }
328}
329
330
331/**
332 * Adds a config to the tail.
333 *
334 * @returns See VBoxNetDHCP::validate().
335 * @param pCfg The config too add.
336 * This object will be consumed by this call!
337 */
338int VBoxNetDhcp::addConfig(VBoxNetDhcpCfg *pCfg)
339{
340 int rc = 0;
341 if (pCfg)
342 {
343 rc = pCfg->validate();
344 if (!rc)
345 m_Cfgs.push_back(*pCfg);
346 delete pCfg;
347 }
348 return rc;
349}
350
351
352/**
353 * Parse the arguments.
354 *
355 * @returns 0 on success, fully bitched exit code on failure.
356 *
357 * @param argc Argument count.
358 * @param argv Argument vector.
359 */
360int VBoxNetDhcp::parseArgs(int argc, char **argv)
361{
362 static const RTGETOPTDEF s_aOptionDefs[] =
363 {
364 { "--name", 'N', RTGETOPT_REQ_STRING },
365 { "--network", 'n', RTGETOPT_REQ_STRING },
366 { "--mac-address", 'a', RTGETOPT_REQ_MACADDR },
367 { "--ip-address", 'i', RTGETOPT_REQ_IPV4ADDR },
368 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
369
370 { "--begin-config", 'b', RTGETOPT_REQ_NOTHING },
371 { "--gateway", 'g', RTGETOPT_REQ_IPV4ADDR },
372 { "--lower-ip", 'l', RTGETOPT_REQ_IPV4ADDR },
373 { "--upper-ip", 'u', RTGETOPT_REQ_IPV4ADDR },
374 { "--netmask", 'm', RTGETOPT_REQ_IPV4ADDR },
375
376 { "--help", 'h', RTGETOPT_REQ_NOTHING },
377 { "--version ", 'V', RTGETOPT_REQ_NOTHING },
378 };
379
380 RTGETOPTSTATE State;
381 int rc = RTGetOptInit(&State, argc, argv, &s_aOptionDefs[0], RT_ELEMENTS(s_aOptionDefs), 0, 0);
382 AssertRCReturn(rc, 49);
383
384 VBoxNetDhcpCfg *pCurCfg = NULL;
385 for (;;)
386 {
387 RTGETOPTUNION Val;
388 rc = RTGetOpt(&State, &Val);
389 if (!rc)
390 break;
391 switch (rc)
392 {
393 case 'N':
394 m_Name = Val.psz;
395 break;
396 case 'n':
397 m_Network = Val.psz;
398 break;
399 case 'a':
400 m_MacAddress = Val.MacAddr;
401 break;
402 case 'i':
403 m_IpAddress = Val.IPv4Addr;
404 break;
405
406 case 'v':
407 m_cVerbosity++;
408 break;
409
410 /* Begin config. */
411 case 'b':
412 rc = addConfig(pCurCfg);
413 if (rc)
414 break;
415 pCurCfg = NULL;
416 /* fall thru */
417
418 /* config specific ones. */
419 case 'g':
420 case 'l':
421 case 'u':
422 case 'm':
423 if (!pCurCfg)
424 {
425 pCurCfg = new VBoxNetDhcpCfg();
426 if (!pCurCfg)
427 {
428 RTStrmPrintf(g_pStdErr, "VBoxNetDHCP: new VBoxDhcpCfg failed\n");
429 rc = 1;
430 break;
431 }
432 }
433
434 switch (rc)
435 {
436 case 'g':
437 pCurCfg->m_Routers.push_back(Val.IPv4Addr);
438 break;
439
440 case 'l':
441 pCurCfg->m_LowerAddr = Val.IPv4Addr;
442 break;
443
444 case 'u':
445 pCurCfg->m_UpperAddr = Val.IPv4Addr;
446 break;
447
448 case 'm':
449 pCurCfg->m_SubnetMask = Val.IPv4Addr;
450 break;
451
452 case 0: /* ignore */ break;
453 default:
454 AssertMsgFailed(("%d", rc));
455 rc = 1;
456 break;
457 }
458 break;
459
460 case 'V':
461 RTPrintf("%sr%d\n", VBOX_VERSION_STRING, VBOX_SVN_REV);
462 rc = 0;
463 break;
464
465 case 'h':
466 RTPrintf("VBoxNetDHCP Version %s\n"
467 "(C) 2009 Sun Microsystems, Inc.\n"
468 "All rights reserved\n"
469 "\n"
470 "Usage:\n"
471 " TODO\n",
472 VBOX_VERSION_STRING);
473 rc = 1;
474 break;
475
476 default:
477 break;
478 }
479 }
480
481 return rc;
482}
483
484
485/**
486 * Tries to connect to the internal network.
487 *
488 * @returns 0 on success, exit code + error message to stderr on failure.
489 */
490int VBoxNetDhcp::tryGoOnline(void)
491{
492 /*
493 * Open the session, load ring-0 and issue the request.
494 */
495 int rc = SUPR3Init(&m_pSession);
496 if (RT_FAILURE(rc))
497 {
498 m_pSession = NULL;
499 RTStrmPrintf(g_pStdErr, "VBoxNetDHCP: SUPR3Init -> %Rrc", rc);
500 return 1;
501 }
502
503 char szPath[RTPATH_MAX];
504 rc = RTPathProgram(szPath, sizeof(szPath) - sizeof("/VMMR0.r0"));
505 if (RT_FAILURE(rc))
506 {
507 RTStrmPrintf(g_pStdErr, "VBoxNetDHCP: RTPathProgram -> %Rrc", rc);
508 return 1;
509 }
510
511 rc = SUPLoadVMM(strcat(szPath, "/VMMR0.r0"));
512 if (RT_FAILURE(rc))
513 {
514 RTStrmPrintf(g_pStdErr, "VBoxNetDHCP: SUPLoadVMM(\"%s\") -> %Rrc", szPath, rc);
515 return 1;
516 }
517
518 /*
519 * Create the open request.
520 */
521 INTNETOPENREQ OpenReq;
522 OpenReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
523 OpenReq.Hdr.cbReq = sizeof(OpenReq);
524 OpenReq.pSession = m_pSession;
525 strncpy(OpenReq.szNetwork, m_Network.c_str(), sizeof(OpenReq.szNetwork));
526 OpenReq.szTrunk[0] = '\0';
527 OpenReq.enmTrunkType = kIntNetTrunkType_WhateverNone;
528 OpenReq.fFlags = 0; /** @todo check this */
529 OpenReq.cbSend = m_cbSendBuf;
530 OpenReq.cbRecv = m_cbRecvBuf;
531 OpenReq.hIf = INTNET_HANDLE_INVALID;
532
533 /*
534 * Issue the request.
535 */
536 debugPrint(2, false, "attempting to open/create network \"%s\"...", OpenReq.szNetwork);
537 rc = SUPCallVMMR0Ex(NIL_RTR0PTR, VMMR0_DO_INTNET_OPEN, 0, &OpenReq.Hdr);
538 if (RT_SUCCESS(rc))
539 {
540 m_hIf = OpenReq.hIf;
541 debugPrint(1, false, "successfully opened/created \"%s\" - hIf=%#x", OpenReq.szNetwork, m_hIf);
542
543 /*
544 * Get the ring-3 address of the shared interface buffer.
545 */
546 INTNETIFGETRING3BUFFERREQ GetRing3BufferReq;
547 GetRing3BufferReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
548 GetRing3BufferReq.Hdr.cbReq = sizeof(GetRing3BufferReq);
549 GetRing3BufferReq.pSession = m_pSession;
550 GetRing3BufferReq.hIf = m_hIf;
551 GetRing3BufferReq.pRing3Buf = NULL;
552 rc = SUPCallVMMR0Ex(NIL_RTR0PTR, VMMR0_DO_INTNET_IF_GET_RING3_BUFFER, 0, &GetRing3BufferReq.Hdr);
553 if (RT_SUCCESS(rc))
554 {
555 PINTNETBUF pBuf = GetRing3BufferReq.pRing3Buf;
556 debugPrint(1, false, "pBuf=%p cbBuf=%d cbSend=%d cbRecv=%d",
557 pBuf, pBuf->cbBuf, pBuf->cbSend, pBuf->cbRecv);
558 m_pIfBuf = pBuf;
559
560 /*
561 * Activate the interface.
562 */
563 INTNETIFSETACTIVEREQ ActiveReq;
564 ActiveReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
565 ActiveReq.Hdr.cbReq = sizeof(ActiveReq);
566 ActiveReq.pSession = m_pSession;
567 ActiveReq.hIf = m_hIf;
568 ActiveReq.fActive = true;
569 rc = SUPCallVMMR0Ex(NIL_RTR0PTR, VMMR0_DO_INTNET_IF_SET_ACTIVE, 0, &ActiveReq.Hdr);
570 if (RT_SUCCESS(rc))
571 return 0;
572
573 /* bail out */
574 RTStrmPrintf(g_pStdErr, "VBoxNetDHCP: SUPCallVMMR0Ex(,VMMR0_DO_INTNET_IF_SET_PROMISCUOUS_MODE,) failed, rc=%Rrc\n", rc);
575 }
576 else
577 RTStrmPrintf(g_pStdErr, "VBoxNetDHCP: SUPCallVMMR0Ex(,VMMR0_DO_INTNET_IF_GET_RING3_BUFFER,) failed, rc=%Rrc\n", rc);
578 }
579 else
580 RTStrmPrintf(g_pStdErr, "VBoxNetDHCP: SUPCallVMMR0Ex(,VMMR0_DO_INTNET_OPEN,) failed, rc=%Rrc\n", rc);
581
582 return RT_SUCCESS(rc) ? 0 : 1;
583}
584
585
586/**
587 * Runs the DHCP server.
588 *
589 * @returns exit code + error message to stderr on failure, won't return on
590 * success (you must kill this process).
591 */
592int VBoxNetDhcp::run(void)
593{
594 /*
595 * The loop.
596 */
597 PINTNETRINGBUF pRingBuf = &m_pIfBuf->Recv;
598 for (;;)
599 {
600 /*
601 * Wait for a packet to become available.
602 */
603 INTNETIFWAITREQ WaitReq;
604 WaitReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
605 WaitReq.Hdr.cbReq = sizeof(WaitReq);
606 WaitReq.pSession = m_pSession;
607 WaitReq.hIf = m_hIf;
608 WaitReq.cMillies = 2000; /* 2 secs - the sleep is for some reason uninterruptible... */ /** @todo fix interruptability in SrvIntNet! */
609 int rc = SUPCallVMMR0Ex(NIL_RTR0PTR, VMMR0_DO_INTNET_IF_WAIT, 0, &WaitReq.Hdr);
610 if (RT_FAILURE(rc))
611 {
612 if (rc == VERR_TIMEOUT)
613 continue;
614 RTStrmPrintf(g_pStdErr, "VBoxNetDHCP: VMMR0_DO_INTNET_IF_WAIT returned %Rrc\n", rc);
615 return 1;
616 }
617
618 /*
619 * Process the receive buffer.
620 */
621 while (INTNETRingGetReadable(pRingBuf) > 0)
622 {
623 size_t cb;
624 void *pv = VBoxNetUDPMatch(m_pIfBuf, 67 /* bootps */, &m_MacAddress,
625 VBOXNETUDP_MATCH_UNICAST | VBOXNETUDP_MATCH_BROADCAST | VBOXNETUDP_MATCH_CHECKSUM
626 | (m_cVerbosity > 2 ? VBOXNETUDP_MATCH_PRINT_STDERR : 0),
627 &m_CurHdrs, &cb);
628 if (pv && cb)
629 {
630 PCRTNETBOOTP pDhcpMsg = (PCRTNETBOOTP)pv;
631 m_pCurMsg = pDhcpMsg;
632 m_cbCurMsg = cb;
633
634 uint8_t uMsgType;
635 if (RTNetIPv4IsDHCPValid(NULL /* why is this here? */, pDhcpMsg, cb, &uMsgType))
636 {
637 m_uCurMsgType = uMsgType;
638 handleDhcpMsg(uMsgType, pDhcpMsg, cb);
639 m_uCurMsgType = UINT8_MAX;
640 }
641 else
642 debugPrint(1, true, "VBoxNetDHCP: Skipping invalid DHCP packet.\n"); /** @todo handle pure bootp clients too? */
643
644 m_pCurMsg = NULL;
645 m_cbCurMsg = 0;
646 }
647
648 /* Advance to the next frame. */
649 INTNETRingSkipFrame(m_pIfBuf, pRingBuf);
650 }
651 }
652
653 return 0;
654}
655
656
657/**
658 * Handles a DHCP message.
659 *
660 * @returns true if handled, false if not.
661 * @param uMsgType The message type.
662 * @param pDhcpMsg The DHCP message.
663 * @param cb The size of the DHCP message.
664 */
665bool VBoxNetDhcp::handleDhcpMsg(uint8_t uMsgType, PCRTNETBOOTP pDhcpMsg, size_t cb)
666{
667 if (pDhcpMsg->bp_op == RTNETBOOTP_OP_REQUEST)
668 {
669 switch (uMsgType)
670 {
671 case RTNET_DHCP_MT_DISCOVER:
672 return handleDhcpReqDiscover(pDhcpMsg, cb);
673
674 case RTNET_DHCP_MT_REQUEST:
675 return handleDhcpReqRequest(pDhcpMsg, cb);
676
677 case RTNET_DHCP_MT_DECLINE:
678 return handleDhcpReqDecline(pDhcpMsg, cb);
679
680 case RTNET_DHCP_MT_RELEASE:
681 return handleDhcpReqRelease(pDhcpMsg, cb);
682
683 case RTNET_DHCP_MT_INFORM:
684 debugPrint(0, true, "Should we handle this?");
685 break;
686
687 default:
688 debugPrint(0, true, "Unexpected.");
689 break;
690 }
691 }
692 return false;
693}
694
695
696/**
697 * The client is requesting an offer.
698 *
699 * @returns true.
700 *
701 * @param pDhcpMsg The message.
702 * @param cb The message size.
703 */
704bool VBoxNetDhcp::handleDhcpReqDiscover(PCRTNETBOOTP pDhcpMsg, size_t cb)
705{
706 /*
707 * First, see if there is already a lease for this client. It may have rebooted,
708 * crashed or whatever that have caused it to forget its existing lease.
709 * If none was found, create a new lease for it and then construct a reply.
710 */
711 VBoxNetDhcpLease *pLease = findLeaseByMacAddress(pDhcpMsg->bp_chaddr.Mac,
712 true /* fEnsureUpToDateConfig */);
713 if (!pLease)
714 pLease = newLease(pDhcpMsg, cb);
715 if (!pLease)
716 return false;
717 pLease->setState(pLease::kState_Discover);
718
719 handleDhcpReply(RTNET_DHCP_MT_OFFER, pLease, pDhcpMsg, cb);
720 return true;
721}
722
723
724/** The requested address. */
725#define RTNET_DHCP_OPT_REQUESTED_ADDRESS 50
726
727/**
728 * The client is requesting an offer.
729 *
730 * @returns true.
731 *
732 * @param pDhcpMsg The message.
733 * @param cb The message size.
734 */
735bool VBoxNetDhcp::handleDhcpReqRequest(PCRTNETBOOTP pDhcpMsg, size_t cb)
736{
737 /*
738 * Windows will reissue these requests when rejoining a network if it thinks it
739 * already has an address on the network. If we cannot find a valid lease,
740 * make a new one and return NAC.
741 */
742 uint8_t uReplyType = RTNET_DHCP_MT_NAC;
743 VBoxNetDhcpLease *pLease = NULL;
744 RTNETADDRIPV4 IPv4Addr;
745 if (findOptionIPv4Addr(RTNET_DHCP_OPT_REQUESTED_ADDRESS, pDhcpMsg, cb, &Ipv4Addr))
746 {
747 pLease = findLeaseByIpv4AndMacAddresses(Ipv4Addr, pDhcpMsg->bp_chaddr);
748 if (pLease)
749 {
750 /** @todo check how windows treats bp_xid here, it should match I think. If
751 * it doesn't we've no way of filtering out broadcast replies to other
752 * DHCP servers. */
753 if (pDhcpMsg->bp_xid == )
754 {
755 }
756
757 }
758 }
759
760 if (pLease)
761 {
762 }
763
764
765 /** @todo this code IS required. */
766 return true;
767}
768
769
770/**
771 * The client is declining an offer we've made.
772 *
773 * @returns true.
774 *
775 * @param pDhcpMsg The message.
776 * @param cb The message size.
777 */
778bool VBoxNetDhcp::handleDhcpReqDecline(PCRTNETBOOTP pDhcpMsg, size_t cb)
779{
780 /*
781 * The client is supposed to pass us option 50, requested address,
782 * from the offer. We also match the lease state. Apparently the
783 * MAC address is not supposed to be checked here.
784 */
785
786 /** @todo this is not required in the initial implementation, do it later. */
787 return true;
788}
789
790
791/**
792 * The client is releasing its lease - good boy.
793 *
794 * @returns true.
795 *
796 * @param pDhcpMsg The message.
797 * @param cb The message size.
798 */
799bool VBoxNetDhcp::handleDhcpReqRelease(PCRTNETBOOTP pDhcpMsg, size_t cb)
800{
801 /*
802 * The client may pass us option 61, client identifier, which we should
803 * use to find the lease by.
804 *
805 * We're matching MAC address and lease state as well.
806 */
807
808 /*
809 * If no client identifier or if we couldn't find a lease by using it,
810 * we will try look it up by the client IP address.
811 */
812
813
814 /*
815 * If found, release it.
816 */
817
818
819 /** @todo this is not required in the initial implementation, do it later. */
820 return true;
821}
822
823
824/**
825 * Constructs and sends a reply to a client.
826 *
827 * @returns
828 * @param uMsgType The DHCP message type.
829 * @param pLease The lease. This can be NULL for some replies.
830 * @param pDhcpMsg The client message. We will dig out the MAC address,
831 * transaction ID, and requested options from this.
832 * @param cb The size of the client message.
833 */
834void VBoxNetDhcp::handleDhcpReply(uint8_t uMsgType, VBoxNetDhcpLease *pLease, PCRTNETBOOTP pDhcpMsg, size_t cb)
835{
836 /** @todo this is required. :-) */
837}
838
839
840
841/**
842 * Print debug message depending on the m_cVerbosity level.
843 *
844 * @param iMinLevel The minimum m_cVerbosity level for this message.
845 * @param fMsg Whether to dump parts for the current DHCP message.
846 * @param pszFmt The message format string.
847 * @param ... Optional arguments.
848 */
849inline void VBoxNetDhcp::debugPrint(int32_t iMinLevel, bool fMsg, const char *pszFmt, ...) const
850{
851 if (iMinLevel <= m_cVerbosity)
852 {
853 va_list va;
854 va_start(va, pszFmt);
855 debugPrintV(iMinLevel, fMsg, pszFmt, va);
856 va_end(va);
857 }
858}
859
860
861/**
862 * Print debug message depending on the m_cVerbosity level.
863 *
864 * @param iMinLevel The minimum m_cVerbosity level for this message.
865 * @param fMsg Whether to dump parts for the current DHCP message.
866 * @param pszFmt The message format string.
867 * @param va Optional arguments.
868 */
869void VBoxNetDhcp::debugPrintV(int iMinLevel, bool fMsg, const char *pszFmt, va_list va) const
870{
871 if (iMinLevel <= m_cVerbosity)
872 {
873 va_list vaCopy; /* This dude is *very* special, thus the copy. */
874 va_copy(vaCopy, va);
875 RTStrmPrintf(g_pStdErr, "VBoxNetDHCP: %s: %N\n", iMinLevel >= 2 ? "debug" : "info", pszFmt, &vaCopy);
876 va_end(vaCopy);
877
878 if ( fMsg
879 && m_cVerbosity >= 2
880 && m_pCurMsg)
881 {
882 const char *pszMsg = m_uCurMsgType != UINT8_MAX ? debugDhcpName(m_uCurMsgType) : "";
883 RTStrmPrintf(g_pStdErr, "VBoxNetDHCP: debug: %8s chaddr=%.6Rhxs ciaddr=%d.%d.%d.%d yiaddr=%d.%d.%d.%d siaddr=%d.%d.%d.%d\n",
884 pszMsg,
885 &m_pCurMsg->bp_chaddr,
886 m_pCurMsg->bp_ciaddr.au8[0], m_pCurMsg->bp_ciaddr.au8[1], m_pCurMsg->bp_ciaddr.au8[2], m_pCurMsg->bp_ciaddr.au8[3],
887 m_pCurMsg->bp_yiaddr.au8[0], m_pCurMsg->bp_yiaddr.au8[1], m_pCurMsg->bp_yiaddr.au8[2], m_pCurMsg->bp_yiaddr.au8[3],
888 m_pCurMsg->bp_siaddr.au8[0], m_pCurMsg->bp_siaddr.au8[1], m_pCurMsg->bp_siaddr.au8[2], m_pCurMsg->bp_siaddr.au8[3]);
889 }
890 }
891}
892
893
894/**
895 * Gets the name of given DHCP message type.
896 *
897 * @returns Readonly name.
898 * @param uMsgType The message number.
899 */
900/* static */ const char *VBoxNetDhcp::debugDhcpName(uint8_t uMsgType)
901{
902 switch (uMsgType)
903 {
904 case 0: return "MT_00";
905 case RTNET_DHCP_MT_DISCOVER: return "DISCOVER";
906 case RTNET_DHCP_MT_OFFER: return "OFFER";
907 case RTNET_DHCP_MT_REQUEST: return "REQUEST";
908 case RTNET_DHCP_MT_DECLINE: return "DECLINE";
909 case RTNET_DHCP_MT_ACK: return "ACK";
910 case RTNET_DHCP_MT_NAC: return "NAC";
911 case RTNET_DHCP_MT_RELEASE: return "RELEASE";
912 case RTNET_DHCP_MT_INFORM: return "INFORM";
913 case 9: return "MT_09";
914 case 10: return "MT_0a";
915 case 11: return "MT_0b";
916 case 12: return "MT_0c";
917 case 13: return "MT_0d";
918 case 14: return "MT_0e";
919 case 15: return "MT_0f";
920 case 16: return "MT_10";
921 case 17: return "MT_11";
922 case 18: return "MT_12";
923 case 19: return "MT_13";
924 case UINT8_MAX: return "MT_ff";
925 default: return "UNKNOWN";
926 }
927}
928
929
930
931/**
932 * Entry point.
933 */
934extern "C" DECLEXPORT(int) TrustedMain(int argc, char **argv, char **envp)
935{
936 /*
937 * Instantiate the DHCP server and hand it the options.
938 */
939 VBoxNetDhcp *pDhcp = new VBoxNetDhcp();
940 if (!pDhcp)
941 {
942 RTStrmPrintf(g_pStdErr, "VBoxNetDHCP: new VBoxNetDhcp failed!\n");
943 return 1;
944 }
945 int rc = pDhcp->parseArgs(argc - 1, argv + 1);
946 if (rc)
947 return rc;
948
949 /*
950 * Try connect the server to the network.
951 */
952 rc = pDhcp->tryGoOnline();
953 if (rc)
954 {
955 delete pDhcp;
956 return rc;
957 }
958
959 /*
960 * Process requests.
961 */
962 g_pDhcp = pDhcp;
963 rc = pDhcp->run();
964 g_pDhcp = NULL;
965 delete pDhcp;
966
967 return rc;
968}
969
970
971
972#ifndef VBOX_WITH_HARDENING
973
974int main(int argc, char **argv, char **envp)
975{
976 int rc = RTR3InitAndSUPLib();
977 if (RT_FAILURE(rc))
978 {
979 RTStrmPrintf(g_pStdErr, "VBoxNetDHCP: RTR3InitAndSupLib failed, rc=%Rrc\n", rc);
980 return 1;
981 }
982
983 return TrustedMain(argc, argv, envp);
984}
985
986#endif /* !VBOX_WITH_HARDENING */
987
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