VirtualBox

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

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

More code.

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