VirtualBox

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

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

Networkservices: Further cleanup and consolidation, get rid of the almost useless IntNetIf helper class and add methods to the low level code offering the missing functionality there, bugref:10297

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 16.1 KB
Line 
1/* $Id: VBoxNetDhcpd.cpp 97078 2022-10-10 19:32:33Z 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 ifInit(const RTCString &strNetwork,
135 const RTCString &strTrunk = RTCString(),
136 INTNETTRUNKTYPE enmTrunkType = kIntNetTrunkType_WhateverNone);
137
138 int ifProcessInput();
139
140 void ifPump();
141 int ifInput(void *pvSegFrame, uint32_t cbSegFrame);
142
143
144 /*
145 * lwIP callbacks
146 */
147 static DECLCALLBACK(void) lwipInitCB(void *pvArg);
148 void lwipInit();
149
150 static err_t netifInitCB(netif *pNetif) RT_NOTHROW_PROTO;
151 err_t netifInit(netif *pNetif);
152
153 static err_t netifLinkOutputCB(netif *pNetif, pbuf *pPBuf) RT_NOTHROW_PROTO;
154 err_t netifLinkOutput(pbuf *pPBuf);
155
156 static void dhcp4RecvCB(void *arg, struct udp_pcb *pcb, struct pbuf *p,
157 ip_addr_t *addr, u16_t port) RT_NOTHROW_PROTO;
158 void dhcp4Recv(struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *addr, u16_t port);
159};
160
161
162VBoxNetDhcpd::VBoxNetDhcpd()
163 : m_pStderrReleaseLogger(NULL),
164 m_hIf(INTNET_HANDLE_INVALID),
165 m_pIfBuf(NULL),
166 m_LwipNetif(),
167 m_Config(NULL),
168 m_Dhcp4Pcb(NULL)
169{
170 logInitStderr();
171}
172
173
174VBoxNetDhcpd::~VBoxNetDhcpd()
175{
176 if (m_hIf != NULL)
177 {
178 int rc = IntNetR3IfDestroy(m_hIf);
179 AssertRC(rc);
180 m_hIf = NULL;
181 }
182}
183
184
185/*
186 * We don't know the name of the release log file until we parse our
187 * configuration because we use network name as basename. To get
188 * early logging to work, start with stderr-only release logger.
189 *
190 * We disable "sup" for this logger to avoid spam from SUPR3Init().
191 */
192int VBoxNetDhcpd::logInitStderr()
193{
194 static const char * const s_apszGroups[] = VBOX_LOGGROUP_NAMES;
195
196 PRTLOGGER pLogger;
197 int rc;
198
199 uint32_t fFlags = 0;
200#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
201 fFlags |= RTLOGFLAGS_USECRLF;
202#endif
203
204 rc = RTLogCreate(&pLogger, fFlags,
205 "all -sup all.restrict -default.restrict",
206 NULL, /* environment base */
207 RT_ELEMENTS(s_apszGroups), s_apszGroups,
208 RTLOGDEST_STDERR, NULL);
209 if (RT_FAILURE(rc))
210 {
211 RTPrintf("Failed to init stderr logger: %Rrs\n", rc);
212 return rc;
213 }
214
215 m_pStderrReleaseLogger = pLogger;
216 RTLogRelSetDefaultInstance(m_pStderrReleaseLogger);
217
218 return VINF_SUCCESS;
219}
220
221
222int VBoxNetDhcpd::ifInit(const RTCString &strNetwork,
223 const RTCString &strTrunk,
224 INTNETTRUNKTYPE enmTrunkType)
225{
226 if (enmTrunkType == kIntNetTrunkType_Invalid)
227 enmTrunkType = kIntNetTrunkType_WhateverNone;
228
229 int rc = IntNetR3IfCreateEx(&m_hIf, strNetwork.c_str(), enmTrunkType,
230 strTrunk.c_str(), _128K /*cbSend*/, _256K /*cbRecv*/,
231 0 /*fFlags*/);
232 if (RT_SUCCESS(rc))
233 {
234 rc = IntNetR3IfQueryBufferPtr(m_hIf, &m_pIfBuf);
235 if (RT_SUCCESS(rc))
236 rc = IntNetR3IfSetActive(m_hIf, true /*fActive*/);
237 }
238
239 return rc;
240}
241
242
243/**
244 * Process incoming packages forever.
245 *
246 * @note This function normally never returns, given that the process is
247 * typically just killed when shutting down a network.
248 */
249void VBoxNetDhcpd::ifPump()
250{
251 for (;;)
252 {
253 /*
254 * Wait for input:
255 */
256 int rc = IntNetR3IfWait(m_hIf, RT_INDEFINITE_WAIT);
257 /*
258 * Process any pending input before we wait again:
259 */
260 if ( RT_SUCCESS(rc)
261 || rc == VERR_INTERRUPTED
262 || rc == VERR_TIMEOUT /* paranoia */)
263 ifProcessInput();
264 else
265 {
266 DHCP_LOG_MSG_ERROR(("ifWait failed: %Rrc\n", rc));
267 return;
268 }
269 }
270}
271
272
273int VBoxNetDhcpd::ifProcessInput()
274{
275 AssertReturn(m_hIf != NULL, VERR_GENERAL_FAILURE);
276 AssertReturn(m_pIfBuf != NULL, VERR_GENERAL_FAILURE);
277
278 PCINTNETHDR pHdr = IntNetRingGetNextFrameToRead(&m_pIfBuf->Recv);
279 while (pHdr)
280 {
281 const uint8_t u8Type = pHdr->u8Type;
282 void *pvSegFrame;
283 uint32_t cbSegFrame;
284
285 if (u8Type == INTNETHDR_TYPE_FRAME)
286 {
287 pvSegFrame = IntNetHdrGetFramePtr(pHdr, m_pIfBuf);
288 cbSegFrame = pHdr->cbFrame;
289
290 ifInput(pvSegFrame, cbSegFrame);
291 }
292 else if (u8Type == INTNETHDR_TYPE_GSO)
293 {
294 size_t cbGso = pHdr->cbFrame;
295 size_t cbFrame = cbGso - sizeof(PDMNETWORKGSO);
296
297 PCPDMNETWORKGSO pGso = IntNetHdrGetGsoContext(pHdr, m_pIfBuf);
298 if (PDMNetGsoIsValid(pGso, cbGso, cbFrame))
299 {
300 const uint32_t cSegs = PDMNetGsoCalcSegmentCount(pGso, cbFrame);
301 for (uint32_t i = 0; i < cSegs; ++i)
302 {
303 uint8_t abHdrScratch[256];
304 pvSegFrame = PDMNetGsoCarveSegmentQD(pGso, (uint8_t *)(pGso + 1), cbFrame,
305 abHdrScratch,
306 i, cSegs,
307 &cbSegFrame);
308 ifInput(pvSegFrame, (uint32_t)cbFrame);
309 }
310 }
311 }
312
313 /* Advance: */
314 IntNetRingSkipFrame(&m_pIfBuf->Recv);
315 pHdr = IntNetRingGetNextFrameToRead(&m_pIfBuf->Recv);
316 }
317
318 return VINF_SUCCESS;
319}
320
321
322/**
323 * Got a frame from the internal network, feed it to the lwIP stack.
324 */
325int VBoxNetDhcpd::ifInput(void *pvFrame, uint32_t cbFrame)
326{
327 if (pvFrame == NULL)
328 return VERR_INVALID_PARAMETER;
329
330 if ( cbFrame <= sizeof(RTNETETHERHDR)
331 || cbFrame > UINT16_MAX - ETH_PAD_SIZE)
332 return VERR_INVALID_PARAMETER;
333
334 struct pbuf *p = pbuf_alloc(PBUF_RAW, (u16_t)cbFrame + ETH_PAD_SIZE, PBUF_POOL);
335 if (RT_UNLIKELY(p == NULL))
336 return VERR_NO_MEMORY;
337
338 /*
339 * The code below is inlined version of:
340 *
341 * pbuf_header(p, -ETH_PAD_SIZE); // hide padding
342 * pbuf_take(p, pvFrame, cbFrame);
343 * pbuf_header(p, ETH_PAD_SIZE); // reveal padding
344 */
345 struct pbuf *q = p;
346 uint8_t *pbChunk = (uint8_t *)pvFrame;
347 do
348 {
349 uint8_t *payload = (uint8_t *)q->payload;
350 size_t len = q->len;
351
352#if ETH_PAD_SIZE
353 if (RT_LIKELY(q == p)) /* single pbuf is large enough */
354 {
355 payload += ETH_PAD_SIZE;
356 len -= ETH_PAD_SIZE;
357 }
358#endif
359 memcpy(payload, pbChunk, len);
360 pbChunk += len;
361 q = q->next;
362 } while (RT_UNLIKELY(q != NULL));
363
364 m_LwipNetif.input(p, &m_LwipNetif);
365 return VINF_SUCCESS;
366}
367
368
369/**
370 * Got a frame from the lwIP stack, feed it to the internal network.
371 */
372err_t VBoxNetDhcpd::netifLinkOutput(pbuf *pPBuf)
373{
374 if (pPBuf->tot_len < sizeof(struct eth_hdr)) /* includes ETH_PAD_SIZE */
375 return ERR_ARG;
376
377 PINTNETHDR pHdr;
378 void *pvFrame;
379 u16_t cbFrame = pPBuf->tot_len - ETH_PAD_SIZE;
380 int rc = IntNetRingAllocateFrame(&m_pIfBuf->Send, cbFrame, &pHdr, &pvFrame);
381 if (RT_FAILURE(rc))
382 return ERR_MEM;
383
384 pbuf_copy_partial(pPBuf, pvFrame, cbFrame, ETH_PAD_SIZE);
385 IntNetRingCommitFrameEx(&m_pIfBuf->Send, pHdr, cbFrame);
386
387 IntNetR3IfSend(m_hIf);
388 return ERR_OK;
389}
390
391
392/* static */ DECLCALLBACK(void) VBoxNetDhcpd::lwipInitCB(void *pvArg)
393{
394 AssertPtrReturnVoid(pvArg);
395
396 VBoxNetDhcpd *self = static_cast<VBoxNetDhcpd *>(pvArg);
397 self->lwipInit();
398}
399
400
401/* static */ err_t VBoxNetDhcpd::netifInitCB(netif *pNetif) RT_NOTHROW_DEF
402{
403 AssertPtrReturn(pNetif, ERR_ARG);
404
405 VBoxNetDhcpd *self = static_cast<VBoxNetDhcpd *>(pNetif->state);
406 return self->netifInit(pNetif);
407}
408
409
410/* static */ err_t VBoxNetDhcpd::netifLinkOutputCB(netif *pNetif, pbuf *pPBuf) RT_NOTHROW_DEF
411{
412 AssertPtrReturn(pNetif, ERR_ARG);
413 AssertPtrReturn(pPBuf, ERR_ARG);
414
415 VBoxNetDhcpd *self = static_cast<VBoxNetDhcpd *>(pNetif->state);
416 AssertPtrReturn(self, ERR_IF);
417
418 return self->netifLinkOutput(pPBuf);
419}
420
421
422/* static */ void VBoxNetDhcpd::dhcp4RecvCB(void *arg, struct udp_pcb *pcb,
423 struct pbuf *p,
424 ip_addr_t *addr, u16_t port) RT_NOTHROW_DEF
425{
426 AssertPtrReturnVoid(arg);
427
428 VBoxNetDhcpd *self = static_cast<VBoxNetDhcpd *>(arg);
429 self->dhcp4Recv(pcb, p, addr, port);
430 pbuf_free(p);
431}
432
433
434
435
436
437int VBoxNetDhcpd::main(int argc, char **argv)
438{
439 /*
440 * Register string format types.
441 */
442 ClientId::registerFormat();
443 Binding::registerFormat();
444
445 /*
446 * Parse the command line into a configuration object.
447 */
448 m_Config = Config::create(argc, argv);
449 if (m_Config == NULL)
450 return VERR_GENERAL_FAILURE;
451
452 /*
453 * Initialize the server.
454 */
455 int rc = m_server.init(m_Config);
456 if (RT_SUCCESS(rc))
457 {
458 /* connect to the intnet */
459 rc = ifInit(m_Config->getNetwork(), m_Config->getTrunk(), m_Config->getTrunkType());
460 if (RT_SUCCESS(rc))
461 {
462 /* setup lwip */
463 rc = vboxLwipCoreInitialize(lwipInitCB, this);
464 if (RT_SUCCESS(rc))
465 {
466 /*
467 * Pump packets more or less for ever.
468 */
469 ifPump();
470 }
471 else
472 DHCP_LOG_MSG_ERROR(("Terminating - vboxLwipCoreInitialize failed: %Rrc\n", rc));
473 }
474 else
475 DHCP_LOG_MSG_ERROR(("Terminating - ifInit failed: %Rrc\n", rc));
476 }
477 else
478 DHCP_LOG_MSG_ERROR(("Terminating - Dhcpd::init failed: %Rrc\n", rc));
479 return rc;
480}
481
482
483void VBoxNetDhcpd::lwipInit()
484{
485 err_t error;
486
487 ip_addr_t addr, mask;
488 ip4_addr_set_u32(&addr, m_Config->getIPv4Address().u);
489 ip4_addr_set_u32(&mask, m_Config->getIPv4Netmask().u);
490
491 netif *pNetif = netif_add(&m_LwipNetif,
492 &addr, &mask,
493 IP_ADDR_ANY, /* gateway */
494 this, /* state */
495 VBoxNetDhcpd::netifInitCB, /* netif_init_fn */
496 tcpip_input); /* netif_input_fn */
497 if (pNetif == NULL)
498 return;
499
500 netif_set_up(pNetif);
501 netif_set_link_up(pNetif);
502
503 m_Dhcp4Pcb = udp_new();
504 if (RT_UNLIKELY(m_Dhcp4Pcb == NULL))
505 return; /* XXX? */
506
507 ip_set_option(m_Dhcp4Pcb, SOF_BROADCAST);
508 udp_recv(m_Dhcp4Pcb, dhcp4RecvCB, this);
509
510 error = udp_bind(m_Dhcp4Pcb, IP_ADDR_ANY, RTNETIPV4_PORT_BOOTPS);
511 if (error != ERR_OK)
512 {
513 udp_remove(m_Dhcp4Pcb);
514 m_Dhcp4Pcb = NULL;
515 return; /* XXX? */
516 }
517}
518
519
520err_t VBoxNetDhcpd::netifInit(netif *pNetif)
521{
522 pNetif->hwaddr_len = sizeof(RTMAC);
523 memcpy(pNetif->hwaddr, &m_Config->getMacAddress(), sizeof(RTMAC));
524
525 pNetif->mtu = 1500;
526
527 pNetif->flags = NETIF_FLAG_BROADCAST
528 | NETIF_FLAG_ETHARP
529 | NETIF_FLAG_ETHERNET;
530
531 pNetif->linkoutput = netifLinkOutputCB;
532 pNetif->output = etharp_output;
533
534 netif_set_default(pNetif);
535 return ERR_OK;
536}
537
538
539void VBoxNetDhcpd::dhcp4Recv(struct udp_pcb *pcb, struct pbuf *p,
540 ip_addr_t *addr, u16_t port)
541{
542 RT_NOREF(pcb, addr, port);
543
544 if (RT_UNLIKELY(p->next != NULL))
545 return; /* XXX: we want it in one chunk */
546
547 bool broadcasted = ip_addr_cmp(ip_current_dest_addr(), &ip_addr_broadcast)
548 || ip_addr_cmp(ip_current_dest_addr(), &ip_addr_any);
549
550 try
551 {
552 DhcpClientMessage *msgIn = DhcpClientMessage::parse(broadcasted, p->payload, p->len);
553 if (msgIn == NULL)
554 return;
555
556 std::unique_ptr<DhcpClientMessage> autoFreeMsgIn(msgIn);
557
558 DhcpServerMessage *msgOut = m_server.process(*msgIn);
559 if (msgOut == NULL)
560 return;
561
562 std::unique_ptr<DhcpServerMessage> autoFreeMsgOut(msgOut);
563
564 ip_addr_t dst = { msgOut->dst().u };
565 if (ip_addr_cmp(&dst, &ip_addr_any))
566 ip_addr_copy(dst, ip_addr_broadcast);
567
568 octets_t data;
569 int rc = msgOut->encode(data);
570 if (RT_FAILURE(rc))
571 return;
572
573 unique_ptr_pbuf q ( pbuf_alloc(PBUF_RAW, (u16_t)data.size(), PBUF_RAM) );
574 if (!q)
575 return;
576
577 err_t error = pbuf_take(q.get(), &data.front(), (u16_t)data.size());
578 if (error != ERR_OK)
579 return;
580
581 error = udp_sendto(pcb, q.get(), &dst, RTNETIPV4_PORT_BOOTPC);
582 if (error != ERR_OK)
583 return;
584 }
585 catch (std::bad_alloc &)
586 {
587 LogRel(("VBoxNetDhcpd::dhcp4Recv: Caught std::bad_alloc!\n"));
588 }
589}
590
591
592
593
594/*
595 * Entry point.
596 */
597extern "C" DECLEXPORT(int) TrustedMain(int argc, char **argv)
598{
599 VBoxNetDhcpd Dhcpd;
600 int rc = Dhcpd.main(argc, argv);
601 return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
602}
603
604
605#ifndef VBOX_WITH_HARDENING
606
607int main(int argc, char **argv)
608{
609 int rc = RTR3InitExe(argc, &argv, RTR3INIT_FLAGS_SUPLIB);
610 if (RT_SUCCESS(rc))
611 return TrustedMain(argc, argv);
612 return RTMsgInitFailure(rc);
613}
614
615
616# ifdef RT_OS_WINDOWS
617/** (We don't want a console usually.) */
618int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
619{
620 RT_NOREF(hInstance, hPrevInstance, lpCmdLine, nCmdShow);
621
622 return main(__argc, __argv);
623}
624# endif /* RT_OS_WINDOWS */
625
626#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