VirtualBox

source: vbox/trunk/src/VBox/NetworkServices/Dhcpd/VBoxNetDhcpd.cpp@ 97071

Last change on this file since 97071 was 97071, checked in by vboxsync, 2 years ago

NetworkServices: Implement support for communicating over the R3 internal network service in driverless mode, bugref:10297

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 17.1 KB
Line 
1/* $Id: VBoxNetDhcpd.cpp 97071 2022-10-10 16:30:56Z vboxsync $ */
2/** @file
3 * VBoxNetDhcpd - DHCP server for host-only and NAT networks.
4 */
5
6/*
7 * Copyright (C) 2009-2022 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28
29/** @page pg_net_dhcp VBoxNetDHCP
30 *
31 * Write a few words...
32 *
33 */
34
35
36/*********************************************************************************************************************************
37* Header Files *
38*********************************************************************************************************************************/
39#include <iprt/cdefs.h>
40
41/*
42 * Need to get host/network order conversion stuff from Windows headers,
43 * so we do not define them in LWIP and then try to re-define them in
44 * Windows headers.
45 */
46#ifdef RT_OS_WINDOWS
47# include <iprt/win/winsock2.h>
48#endif
49
50#include "DhcpdInternal.h"
51#include <iprt/param.h>
52#include <iprt/errcore.h>
53
54#include <iprt/initterm.h>
55#include <iprt/message.h>
56
57#include <iprt/net.h>
58#include <iprt/path.h>
59#include <iprt/stream.h>
60
61#include <VBox/sup.h>
62#include <VBox/vmm/vmm.h>
63#include <VBox/vmm/pdmnetinline.h>
64#include <VBox/intnet.h>
65#include <VBox/intnetinline.h>
66
67#include "VBoxLwipCore.h"
68#include "Config.h"
69#include "DHCPD.h"
70#include "DhcpMessage.h"
71
72extern "C"
73{
74#include "lwip/sys.h"
75#include "lwip/pbuf.h"
76#include "lwip/netif.h"
77#include "lwip/tcpip.h"
78#include "lwip/udp.h"
79#include "netif/etharp.h"
80}
81
82#include <iprt/sanitized/string>
83#include <vector>
84#include <memory>
85
86#ifdef RT_OS_WINDOWS
87# include <iprt/win/windows.h>
88#endif
89
90#include "IntNetIf.h"
91
92struct delete_pbuf
93{
94 delete_pbuf() {}
95 void operator()(struct pbuf *p) const { pbuf_free(p); }
96};
97
98typedef std::unique_ptr<pbuf, delete_pbuf> unique_ptr_pbuf;
99
100
101class VBoxNetDhcpd
102{
103 DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP(VBoxNetDhcpd);
104
105private:
106 PRTLOGGER m_pStderrReleaseLogger;
107
108 /* intnet plumbing */
109 INTNETIFCTX m_hIf;
110 PINTNETBUF m_pIfBuf;
111
112 /* lwip stack connected to the intnet */
113 struct netif m_LwipNetif;
114
115 Config *m_Config;
116
117 /* listening pcb */
118 struct udp_pcb *m_Dhcp4Pcb;
119
120 DHCPD m_server;
121
122public:
123 VBoxNetDhcpd();
124 ~VBoxNetDhcpd();
125
126 int main(int argc, char **argv);
127
128private:
129 int logInitStderr();
130
131 /*
132 * Boilerplate code.
133 */
134 int r3Init();
135 void r3Fini();
136
137 int vmmInit();
138
139 int ifInit(const RTCString &strNetwork,
140 const RTCString &strTrunk = RTCString(),
141 INTNETTRUNKTYPE enmTrunkType = kIntNetTrunkType_WhateverNone);
142 int ifOpen(const RTCString &strNetwork,
143 const RTCString &strTrunk,
144 INTNETTRUNKTYPE enmTrunkType);
145 int ifGetBuf();
146 int ifActivate();
147
148 int ifProcessInput();
149 int ifFlush();
150
151 int ifClose();
152
153 void ifPump();
154 int ifInput(void *pvSegFrame, uint32_t cbSegFrame);
155
156
157 /*
158 * lwIP callbacks
159 */
160 static DECLCALLBACK(void) lwipInitCB(void *pvArg);
161 void lwipInit();
162
163 static err_t netifInitCB(netif *pNetif) RT_NOTHROW_PROTO;
164 err_t netifInit(netif *pNetif);
165
166 static err_t netifLinkOutputCB(netif *pNetif, pbuf *pPBuf) RT_NOTHROW_PROTO;
167 err_t netifLinkOutput(pbuf *pPBuf);
168
169 static void dhcp4RecvCB(void *arg, struct udp_pcb *pcb, struct pbuf *p,
170 ip_addr_t *addr, u16_t port) RT_NOTHROW_PROTO;
171 void dhcp4Recv(struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *addr, u16_t port);
172};
173
174
175VBoxNetDhcpd::VBoxNetDhcpd()
176 : m_pStderrReleaseLogger(NULL),
177 m_hIf(INTNET_HANDLE_INVALID),
178 m_pIfBuf(NULL),
179 m_LwipNetif(),
180 m_Config(NULL),
181 m_Dhcp4Pcb(NULL)
182{
183 logInitStderr();
184}
185
186
187VBoxNetDhcpd::~VBoxNetDhcpd()
188{
189 ifClose();
190}
191
192
193/*
194 * We don't know the name of the release log file until we parse our
195 * configuration because we use network name as basename. To get
196 * early logging to work, start with stderr-only release logger.
197 *
198 * We disable "sup" for this logger to avoid spam from SUPR3Init().
199 */
200int VBoxNetDhcpd::logInitStderr()
201{
202 static const char * const s_apszGroups[] = VBOX_LOGGROUP_NAMES;
203
204 PRTLOGGER pLogger;
205 int rc;
206
207 uint32_t fFlags = 0;
208#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
209 fFlags |= RTLOGFLAGS_USECRLF;
210#endif
211
212 rc = RTLogCreate(&pLogger, fFlags,
213 "all -sup all.restrict -default.restrict",
214 NULL, /* environment base */
215 RT_ELEMENTS(s_apszGroups), s_apszGroups,
216 RTLOGDEST_STDERR, NULL);
217 if (RT_FAILURE(rc))
218 {
219 RTPrintf("Failed to init stderr logger: %Rrs\n", rc);
220 return rc;
221 }
222
223 m_pStderrReleaseLogger = pLogger;
224 RTLogRelSetDefaultInstance(m_pStderrReleaseLogger);
225
226 return VINF_SUCCESS;
227}
228
229
230int VBoxNetDhcpd::ifInit(const RTCString &strNetwork,
231 const RTCString &strTrunk,
232 INTNETTRUNKTYPE enmTrunkType)
233{
234 int rc;
235
236 rc = ifOpen(strNetwork, strTrunk, enmTrunkType);
237 if (RT_FAILURE(rc))
238 return rc;
239
240 rc = ifGetBuf();
241 if (RT_FAILURE(rc))
242 return rc;
243
244 rc = ifActivate();
245 if (RT_FAILURE(rc))
246 return rc;
247
248 return VINF_SUCCESS;
249}
250
251
252int VBoxNetDhcpd::ifOpen(const RTCString &strNetwork,
253 const RTCString &strTrunk,
254 INTNETTRUNKTYPE enmTrunkType)
255{
256 AssertReturn(m_hIf == NULL, VERR_GENERAL_FAILURE);
257
258 if (enmTrunkType == kIntNetTrunkType_Invalid)
259 enmTrunkType = kIntNetTrunkType_WhateverNone;
260
261 return IntNetR3IfCtxCreate(&m_hIf, strNetwork.c_str(), enmTrunkType,
262 strTrunk.c_str(), _128K /*cbSend*/, _256K /*cbRecv*/,
263 0 /*fFlags*/);
264}
265
266
267int VBoxNetDhcpd::ifGetBuf()
268{
269 AssertReturn(m_hIf != NULL, VERR_GENERAL_FAILURE);
270 AssertReturn(m_pIfBuf == NULL, VERR_GENERAL_FAILURE);
271
272 return IntNetR3IfCtxQueryBufferPtr(m_hIf, &m_pIfBuf);
273}
274
275
276int VBoxNetDhcpd::ifActivate()
277{
278 AssertReturn(m_hIf != NULL, VERR_GENERAL_FAILURE);
279 AssertReturn(m_pIfBuf != NULL, VERR_GENERAL_FAILURE);
280
281 return IntNetR3IfCtxSetActive(m_hIf, true /*fActive*/);
282}
283
284
285/**
286 * Process incoming packages forever.
287 *
288 * @note This function normally never returns, given that the process is
289 * typically just killed when shutting down a network.
290 */
291void VBoxNetDhcpd::ifPump()
292{
293 for (;;)
294 {
295 /*
296 * Wait for input:
297 */
298 int rc = IntNetR3IfWait(m_hIf, RT_INDEFINITE_WAIT);
299 /*
300 * Process any pending input before we wait again:
301 */
302 if ( RT_SUCCESS(rc)
303 || rc == VERR_INTERRUPTED
304 || rc == VERR_TIMEOUT /* paranoia */)
305 ifProcessInput();
306 else
307 {
308 DHCP_LOG_MSG_ERROR(("ifWait failed: %Rrc\n", rc));
309 return;
310 }
311 }
312}
313
314
315int VBoxNetDhcpd::ifProcessInput()
316{
317 AssertReturn(m_hIf != NULL, VERR_GENERAL_FAILURE);
318 AssertReturn(m_pIfBuf != NULL, VERR_GENERAL_FAILURE);
319
320 PCINTNETHDR pHdr = IntNetRingGetNextFrameToRead(&m_pIfBuf->Recv);
321 while (pHdr)
322 {
323 const uint8_t u8Type = pHdr->u8Type;
324 void *pvSegFrame;
325 uint32_t cbSegFrame;
326
327 if (u8Type == INTNETHDR_TYPE_FRAME)
328 {
329 pvSegFrame = IntNetHdrGetFramePtr(pHdr, m_pIfBuf);
330 cbSegFrame = pHdr->cbFrame;
331
332 ifInput(pvSegFrame, cbSegFrame);
333 }
334 else if (u8Type == INTNETHDR_TYPE_GSO)
335 {
336 size_t cbGso = pHdr->cbFrame;
337 size_t cbFrame = cbGso - sizeof(PDMNETWORKGSO);
338
339 PCPDMNETWORKGSO pGso = IntNetHdrGetGsoContext(pHdr, m_pIfBuf);
340 if (PDMNetGsoIsValid(pGso, cbGso, cbFrame))
341 {
342 const uint32_t cSegs = PDMNetGsoCalcSegmentCount(pGso, cbFrame);
343 for (uint32_t i = 0; i < cSegs; ++i)
344 {
345 uint8_t abHdrScratch[256];
346 pvSegFrame = PDMNetGsoCarveSegmentQD(pGso, (uint8_t *)(pGso + 1), cbFrame,
347 abHdrScratch,
348 i, cSegs,
349 &cbSegFrame);
350 ifInput(pvSegFrame, (uint32_t)cbFrame);
351 }
352 }
353 }
354
355 /* Advance: */
356 IntNetRingSkipFrame(&m_pIfBuf->Recv);
357 pHdr = IntNetRingGetNextFrameToRead(&m_pIfBuf->Recv);
358 }
359
360 return VINF_SUCCESS;
361}
362
363
364/**
365 * Got a frame from the internal network, feed it to the lwIP stack.
366 */
367int VBoxNetDhcpd::ifInput(void *pvFrame, uint32_t cbFrame)
368{
369 if (pvFrame == NULL)
370 return VERR_INVALID_PARAMETER;
371
372 if ( cbFrame <= sizeof(RTNETETHERHDR)
373 || cbFrame > UINT16_MAX - ETH_PAD_SIZE)
374 return VERR_INVALID_PARAMETER;
375
376 struct pbuf *p = pbuf_alloc(PBUF_RAW, (u16_t)cbFrame + ETH_PAD_SIZE, PBUF_POOL);
377 if (RT_UNLIKELY(p == NULL))
378 return VERR_NO_MEMORY;
379
380 /*
381 * The code below is inlined version of:
382 *
383 * pbuf_header(p, -ETH_PAD_SIZE); // hide padding
384 * pbuf_take(p, pvFrame, cbFrame);
385 * pbuf_header(p, ETH_PAD_SIZE); // reveal padding
386 */
387 struct pbuf *q = p;
388 uint8_t *pbChunk = (uint8_t *)pvFrame;
389 do
390 {
391 uint8_t *payload = (uint8_t *)q->payload;
392 size_t len = q->len;
393
394#if ETH_PAD_SIZE
395 if (RT_LIKELY(q == p)) /* single pbuf is large enough */
396 {
397 payload += ETH_PAD_SIZE;
398 len -= ETH_PAD_SIZE;
399 }
400#endif
401 memcpy(payload, pbChunk, len);
402 pbChunk += len;
403 q = q->next;
404 } while (RT_UNLIKELY(q != NULL));
405
406 m_LwipNetif.input(p, &m_LwipNetif);
407 return VINF_SUCCESS;
408}
409
410
411/**
412 * Got a frame from the lwIP stack, feed it to the internal network.
413 */
414err_t VBoxNetDhcpd::netifLinkOutput(pbuf *pPBuf)
415{
416 if (pPBuf->tot_len < sizeof(struct eth_hdr)) /* includes ETH_PAD_SIZE */
417 return ERR_ARG;
418
419 PINTNETHDR pHdr;
420 void *pvFrame;
421 u16_t cbFrame = pPBuf->tot_len - ETH_PAD_SIZE;
422 int rc = IntNetRingAllocateFrame(&m_pIfBuf->Send, cbFrame, &pHdr, &pvFrame);
423 if (RT_FAILURE(rc))
424 return ERR_MEM;
425
426 pbuf_copy_partial(pPBuf, pvFrame, cbFrame, ETH_PAD_SIZE);
427 IntNetRingCommitFrameEx(&m_pIfBuf->Send, pHdr, cbFrame);
428
429 ifFlush();
430 return ERR_OK;
431}
432
433
434int VBoxNetDhcpd::ifFlush()
435{
436 return IntNetR3IfSend(m_hIf);
437}
438
439
440int VBoxNetDhcpd::ifClose()
441{
442 if (m_hIf == NULL)
443 return VINF_SUCCESS;
444
445 int rc = IntNetR3IfCtxDestroy(m_hIf);
446 m_hIf = NULL;
447 return rc;
448}
449
450
451/* static */ DECLCALLBACK(void) VBoxNetDhcpd::lwipInitCB(void *pvArg)
452{
453 AssertPtrReturnVoid(pvArg);
454
455 VBoxNetDhcpd *self = static_cast<VBoxNetDhcpd *>(pvArg);
456 self->lwipInit();
457}
458
459
460/* static */ err_t VBoxNetDhcpd::netifInitCB(netif *pNetif) RT_NOTHROW_DEF
461{
462 AssertPtrReturn(pNetif, ERR_ARG);
463
464 VBoxNetDhcpd *self = static_cast<VBoxNetDhcpd *>(pNetif->state);
465 return self->netifInit(pNetif);
466}
467
468
469/* static */ err_t VBoxNetDhcpd::netifLinkOutputCB(netif *pNetif, pbuf *pPBuf) RT_NOTHROW_DEF
470{
471 AssertPtrReturn(pNetif, ERR_ARG);
472 AssertPtrReturn(pPBuf, ERR_ARG);
473
474 VBoxNetDhcpd *self = static_cast<VBoxNetDhcpd *>(pNetif->state);
475 AssertPtrReturn(self, ERR_IF);
476
477 return self->netifLinkOutput(pPBuf);
478}
479
480
481/* static */ void VBoxNetDhcpd::dhcp4RecvCB(void *arg, struct udp_pcb *pcb,
482 struct pbuf *p,
483 ip_addr_t *addr, u16_t port) RT_NOTHROW_DEF
484{
485 AssertPtrReturnVoid(arg);
486
487 VBoxNetDhcpd *self = static_cast<VBoxNetDhcpd *>(arg);
488 self->dhcp4Recv(pcb, p, addr, port);
489 pbuf_free(p);
490}
491
492
493
494
495
496int VBoxNetDhcpd::main(int argc, char **argv)
497{
498 /*
499 * Register string format types.
500 */
501 ClientId::registerFormat();
502 Binding::registerFormat();
503
504 /*
505 * Parse the command line into a configuration object.
506 */
507 m_Config = Config::create(argc, argv);
508 if (m_Config == NULL)
509 return VERR_GENERAL_FAILURE;
510
511 /*
512 * Initialize the server.
513 */
514 int rc = m_server.init(m_Config);
515 if (RT_SUCCESS(rc))
516 {
517 /* connect to the intnet */
518 rc = ifInit(m_Config->getNetwork(), m_Config->getTrunk(), m_Config->getTrunkType());
519 if (RT_SUCCESS(rc))
520 {
521 /* setup lwip */
522 rc = vboxLwipCoreInitialize(lwipInitCB, this);
523 if (RT_SUCCESS(rc))
524 {
525 /*
526 * Pump packets more or less for ever.
527 */
528 ifPump();
529 }
530 else
531 DHCP_LOG_MSG_ERROR(("Terminating - vboxLwipCoreInitialize failed: %Rrc\n", rc));
532 }
533 else
534 DHCP_LOG_MSG_ERROR(("Terminating - ifInit failed: %Rrc\n", rc));
535 }
536 else
537 DHCP_LOG_MSG_ERROR(("Terminating - Dhcpd::init failed: %Rrc\n", rc));
538 return rc;
539}
540
541
542void VBoxNetDhcpd::lwipInit()
543{
544 err_t error;
545
546 ip_addr_t addr, mask;
547 ip4_addr_set_u32(&addr, m_Config->getIPv4Address().u);
548 ip4_addr_set_u32(&mask, m_Config->getIPv4Netmask().u);
549
550 netif *pNetif = netif_add(&m_LwipNetif,
551 &addr, &mask,
552 IP_ADDR_ANY, /* gateway */
553 this, /* state */
554 VBoxNetDhcpd::netifInitCB, /* netif_init_fn */
555 tcpip_input); /* netif_input_fn */
556 if (pNetif == NULL)
557 return;
558
559 netif_set_up(pNetif);
560 netif_set_link_up(pNetif);
561
562 m_Dhcp4Pcb = udp_new();
563 if (RT_UNLIKELY(m_Dhcp4Pcb == NULL))
564 return; /* XXX? */
565
566 ip_set_option(m_Dhcp4Pcb, SOF_BROADCAST);
567 udp_recv(m_Dhcp4Pcb, dhcp4RecvCB, this);
568
569 error = udp_bind(m_Dhcp4Pcb, IP_ADDR_ANY, RTNETIPV4_PORT_BOOTPS);
570 if (error != ERR_OK)
571 {
572 udp_remove(m_Dhcp4Pcb);
573 m_Dhcp4Pcb = NULL;
574 return; /* XXX? */
575 }
576}
577
578
579err_t VBoxNetDhcpd::netifInit(netif *pNetif)
580{
581 pNetif->hwaddr_len = sizeof(RTMAC);
582 memcpy(pNetif->hwaddr, &m_Config->getMacAddress(), sizeof(RTMAC));
583
584 pNetif->mtu = 1500;
585
586 pNetif->flags = NETIF_FLAG_BROADCAST
587 | NETIF_FLAG_ETHARP
588 | NETIF_FLAG_ETHERNET;
589
590 pNetif->linkoutput = netifLinkOutputCB;
591 pNetif->output = etharp_output;
592
593 netif_set_default(pNetif);
594 return ERR_OK;
595}
596
597
598void VBoxNetDhcpd::dhcp4Recv(struct udp_pcb *pcb, struct pbuf *p,
599 ip_addr_t *addr, u16_t port)
600{
601 RT_NOREF(pcb, addr, port);
602
603 if (RT_UNLIKELY(p->next != NULL))
604 return; /* XXX: we want it in one chunk */
605
606 bool broadcasted = ip_addr_cmp(ip_current_dest_addr(), &ip_addr_broadcast)
607 || ip_addr_cmp(ip_current_dest_addr(), &ip_addr_any);
608
609 try
610 {
611 DhcpClientMessage *msgIn = DhcpClientMessage::parse(broadcasted, p->payload, p->len);
612 if (msgIn == NULL)
613 return;
614
615 std::unique_ptr<DhcpClientMessage> autoFreeMsgIn(msgIn);
616
617 DhcpServerMessage *msgOut = m_server.process(*msgIn);
618 if (msgOut == NULL)
619 return;
620
621 std::unique_ptr<DhcpServerMessage> autoFreeMsgOut(msgOut);
622
623 ip_addr_t dst = { msgOut->dst().u };
624 if (ip_addr_cmp(&dst, &ip_addr_any))
625 ip_addr_copy(dst, ip_addr_broadcast);
626
627 octets_t data;
628 int rc = msgOut->encode(data);
629 if (RT_FAILURE(rc))
630 return;
631
632 unique_ptr_pbuf q ( pbuf_alloc(PBUF_RAW, (u16_t)data.size(), PBUF_RAM) );
633 if (!q)
634 return;
635
636 err_t error = pbuf_take(q.get(), &data.front(), (u16_t)data.size());
637 if (error != ERR_OK)
638 return;
639
640 error = udp_sendto(pcb, q.get(), &dst, RTNETIPV4_PORT_BOOTPC);
641 if (error != ERR_OK)
642 return;
643 }
644 catch (std::bad_alloc &)
645 {
646 LogRel(("VBoxNetDhcpd::dhcp4Recv: Caught std::bad_alloc!\n"));
647 }
648}
649
650
651
652
653/*
654 * Entry point.
655 */
656extern "C" DECLEXPORT(int) TrustedMain(int argc, char **argv)
657{
658 VBoxNetDhcpd Dhcpd;
659 int rc = Dhcpd.main(argc, argv);
660 return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
661}
662
663
664#ifndef VBOX_WITH_HARDENING
665
666int main(int argc, char **argv)
667{
668 int rc = RTR3InitExe(argc, &argv, RTR3INIT_FLAGS_SUPLIB);
669 if (RT_SUCCESS(rc))
670 return TrustedMain(argc, argv);
671 return RTMsgInitFailure(rc);
672}
673
674
675# ifdef RT_OS_WINDOWS
676/** (We don't want a console usually.) */
677int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
678{
679 RT_NOREF(hInstance, hPrevInstance, lpCmdLine, nCmdShow);
680
681 return main(__argc, __argv);
682}
683# endif /* RT_OS_WINDOWS */
684
685#endif /* !VBOX_WITH_HARDENING */
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