VirtualBox

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

Last change on this file was 106061, checked in by vboxsync, 3 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 14.0 KB
Line 
1/* $Id: VBoxNetDhcpd.cpp 106061 2024-09-16 14:03:52Z vboxsync $ */
2/** @file
3 * VBoxNetDhcpd - DHCP server for host-only and NAT networks.
4 */
5
6/*
7 * Copyright (C) 2009-2024 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);
104public:
105 VBoxNetDhcpd();
106 ~VBoxNetDhcpd();
107
108 int main(int argc, char **argv);
109
110private:
111 /** The logger instance. */
112 PRTLOGGER m_pStderrReleaseLogger;
113 /** Internal network interface handle. */
114 INTNETIFCTX m_hIf;
115 /** lwip stack connected to the intnet */
116 struct netif m_LwipNetif;
117 /** The DHCP server config. */
118 Config *m_Config;
119 /** Listening pcb */
120 struct udp_pcb *m_Dhcp4Pcb;
121 /** DHCP server instance. */
122 DHCPD m_server;
123
124 int logInitStderr();
125
126 /*
127 * Internal network plumbing.
128 */
129 int ifInit(const RTCString &strNetwork,
130 const RTCString &strTrunk = RTCString(),
131 INTNETTRUNKTYPE enmTrunkType = kIntNetTrunkType_WhateverNone);
132
133 static DECLCALLBACK(void) ifInput(void *pvUser, void *pvFrame, uint32_t cbFrame);
134 void ifInputWorker(void *pvFrame, uint32_t cbFrame);
135
136 /*
137 * lwIP callbacks
138 */
139 static DECLCALLBACK(void) lwipInitCB(void *pvArg);
140 void lwipInit();
141
142 static err_t netifInitCB(netif *pNetif) RT_NOTHROW_PROTO;
143 err_t netifInit(netif *pNetif);
144
145 static err_t netifLinkOutputCB(netif *pNetif, pbuf *pPBuf) RT_NOTHROW_PROTO;
146 err_t netifLinkOutput(pbuf *pPBuf);
147
148 static void dhcp4RecvCB(void *arg, struct udp_pcb *pcb, struct pbuf *p,
149 ip_addr_t *addr, u16_t port) RT_NOTHROW_PROTO;
150 void dhcp4Recv(struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *addr, u16_t port);
151};
152
153
154VBoxNetDhcpd::VBoxNetDhcpd()
155 : m_pStderrReleaseLogger(NULL),
156 m_hIf(NULL),
157 m_LwipNetif(),
158 m_Config(NULL),
159 m_Dhcp4Pcb(NULL)
160{
161 logInitStderr();
162}
163
164
165VBoxNetDhcpd::~VBoxNetDhcpd()
166{
167 if (m_hIf != NULL)
168 {
169 int rc = IntNetR3IfDestroy(m_hIf);
170 AssertRC(rc);
171 m_hIf = NULL;
172 }
173}
174
175
176/*
177 * We don't know the name of the release log file until we parse our
178 * configuration because we use network name as basename. To get
179 * early logging to work, start with stderr-only release logger.
180 *
181 * We disable "sup" for this logger to avoid spam from SUPR3Init().
182 */
183int VBoxNetDhcpd::logInitStderr()
184{
185 static const char * const s_apszGroups[] = VBOX_LOGGROUP_NAMES;
186
187 uint32_t fFlags = 0;
188#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
189 fFlags |= RTLOGFLAGS_USECRLF;
190#endif
191
192 PRTLOGGER pLogger;
193 int rc = RTLogCreate(&pLogger, fFlags,
194 "all -sup all.restrict -default.restrict",
195 NULL, /* environment base */
196 RT_ELEMENTS(s_apszGroups), s_apszGroups,
197 RTLOGDEST_STDERR, NULL);
198 if (RT_FAILURE(rc))
199 {
200 RTPrintf("Failed to init stderr logger: %Rrs\n", rc);
201 return rc;
202 }
203
204 m_pStderrReleaseLogger = pLogger;
205 RTLogRelSetDefaultInstance(m_pStderrReleaseLogger);
206
207 return VINF_SUCCESS;
208}
209
210
211int VBoxNetDhcpd::ifInit(const RTCString &strNetwork,
212 const RTCString &strTrunk,
213 INTNETTRUNKTYPE enmTrunkType)
214{
215 if (enmTrunkType == kIntNetTrunkType_Invalid)
216 enmTrunkType = kIntNetTrunkType_WhateverNone;
217
218 int rc = IntNetR3IfCreateEx(&m_hIf, strNetwork.c_str(), enmTrunkType,
219 strTrunk.c_str(), _128K /*cbSend*/, _256K /*cbRecv*/,
220 0 /*fFlags*/);
221 if (RT_SUCCESS(rc))
222 rc = IntNetR3IfSetActive(m_hIf, true /*fActive*/);
223
224 return rc;
225}
226
227
228void VBoxNetDhcpd::ifInputWorker(void *pvFrame, uint32_t cbFrame)
229{
230 struct pbuf *p = pbuf_alloc(PBUF_RAW, (u16_t)cbFrame + ETH_PAD_SIZE, PBUF_POOL);
231 AssertPtrReturnVoid(p);
232
233 /*
234 * The code below is inlined version of:
235 *
236 * pbuf_header(p, -ETH_PAD_SIZE); // hide padding
237 * pbuf_take(p, pvFrame, cbFrame);
238 * pbuf_header(p, ETH_PAD_SIZE); // reveal padding
239 */
240 struct pbuf *q = p;
241 uint8_t *pbChunk = (uint8_t *)pvFrame;
242 do
243 {
244 uint8_t *payload = (uint8_t *)q->payload;
245 size_t len = q->len;
246
247#if ETH_PAD_SIZE
248 if (RT_LIKELY(q == p)) /* single pbuf is large enough */
249 {
250 payload += ETH_PAD_SIZE;
251 len -= ETH_PAD_SIZE;
252 }
253#endif
254 memcpy(payload, pbChunk, len);
255 pbChunk += len;
256 q = q->next;
257 } while (RT_UNLIKELY(q != NULL));
258
259 m_LwipNetif.input(p, &m_LwipNetif);
260}
261
262
263/**
264 * Got a frame from the internal network, feed it to the lwIP stack.
265 */
266/*static*/
267DECLCALLBACK(void) VBoxNetDhcpd::ifInput(void *pvUser, void *pvFrame, uint32_t cbFrame)
268{
269 AssertReturnVoid(pvFrame);
270 AssertReturnVoid( cbFrame > sizeof(RTNETETHERHDR)
271 && cbFrame <= UINT16_MAX - ETH_PAD_SIZE);
272
273 VBoxNetDhcpd *self = static_cast<VBoxNetDhcpd *>(pvUser);
274 self->ifInputWorker(pvFrame, cbFrame);
275}
276
277
278/**
279 * Got a frame from the lwIP stack, feed it to the internal network.
280 */
281err_t VBoxNetDhcpd::netifLinkOutput(pbuf *pPBuf)
282{
283 if (pPBuf->tot_len < sizeof(struct eth_hdr)) /* includes ETH_PAD_SIZE */
284 return ERR_ARG;
285
286 u16_t cbFrame = pPBuf->tot_len - ETH_PAD_SIZE;
287 INTNETFRAME Frame;
288 int rc = IntNetR3IfQueryOutputFrame(m_hIf, cbFrame, &Frame);
289 if (RT_FAILURE(rc))
290 return ERR_MEM;
291
292 pbuf_copy_partial(pPBuf, Frame.pvFrame, cbFrame, ETH_PAD_SIZE);
293 IntNetR3IfOutputFrameCommit(m_hIf, &Frame);
294 return ERR_OK;
295}
296
297
298/* static */ DECLCALLBACK(void) VBoxNetDhcpd::lwipInitCB(void *pvArg)
299{
300 AssertPtrReturnVoid(pvArg);
301
302 VBoxNetDhcpd *self = static_cast<VBoxNetDhcpd *>(pvArg);
303 self->lwipInit();
304}
305
306
307/* static */ err_t VBoxNetDhcpd::netifInitCB(netif *pNetif) RT_NOTHROW_DEF
308{
309 AssertPtrReturn(pNetif, ERR_ARG);
310
311 VBoxNetDhcpd *self = static_cast<VBoxNetDhcpd *>(pNetif->state);
312 return self->netifInit(pNetif);
313}
314
315
316/* static */ err_t VBoxNetDhcpd::netifLinkOutputCB(netif *pNetif, pbuf *pPBuf) RT_NOTHROW_DEF
317{
318 AssertPtrReturn(pNetif, ERR_ARG);
319 AssertPtrReturn(pPBuf, ERR_ARG);
320
321 VBoxNetDhcpd *self = static_cast<VBoxNetDhcpd *>(pNetif->state);
322 AssertPtrReturn(self, ERR_IF);
323
324 return self->netifLinkOutput(pPBuf);
325}
326
327
328/* static */ void VBoxNetDhcpd::dhcp4RecvCB(void *arg, struct udp_pcb *pcb,
329 struct pbuf *p,
330 ip_addr_t *addr, u16_t port) RT_NOTHROW_DEF
331{
332 AssertPtrReturnVoid(arg);
333
334 VBoxNetDhcpd *self = static_cast<VBoxNetDhcpd *>(arg);
335 self->dhcp4Recv(pcb, p, addr, port);
336 pbuf_free(p);
337}
338
339
340
341
342
343int VBoxNetDhcpd::main(int argc, char **argv)
344{
345 /*
346 * Register string format types.
347 */
348 ClientId::registerFormat();
349 Binding::registerFormat();
350
351 /*
352 * Parse the command line into a configuration object.
353 */
354 m_Config = Config::create(argc, argv);
355 if (m_Config == NULL)
356 return VERR_GENERAL_FAILURE;
357
358 /*
359 * Initialize the server.
360 */
361 int rc = m_server.init(m_Config);
362 if (RT_SUCCESS(rc))
363 {
364 /* connect to the intnet */
365 rc = ifInit(m_Config->getNetwork(), m_Config->getTrunk(), m_Config->getTrunkType());
366 if (RT_SUCCESS(rc))
367 {
368 /* setup lwip */
369 rc = vboxLwipCoreInitialize(lwipInitCB, this);
370 if (RT_SUCCESS(rc))
371 {
372 /*
373 * Pump packets more or less for ever.
374 */
375 rc = IntNetR3IfPumpPkts(m_hIf, ifInput, this,
376 NULL /*pfnInputGso*/, NULL /*pvUserGso*/);
377 }
378 else
379 DHCP_LOG_MSG_ERROR(("Terminating - vboxLwipCoreInitialize failed: %Rrc\n", rc));
380 }
381 else
382 DHCP_LOG_MSG_ERROR(("Terminating - ifInit failed: %Rrc\n", rc));
383 }
384 else
385 DHCP_LOG_MSG_ERROR(("Terminating - Dhcpd::init failed: %Rrc\n", rc));
386 return rc;
387}
388
389
390void VBoxNetDhcpd::lwipInit()
391{
392 err_t error;
393
394 ip_addr_t addr, mask;
395 ip4_addr_set_u32(&addr, m_Config->getIPv4Address().u);
396 ip4_addr_set_u32(&mask, m_Config->getIPv4Netmask().u);
397
398 netif *pNetif = netif_add(&m_LwipNetif,
399 &addr, &mask,
400 IP_ADDR_ANY, /* gateway */
401 this, /* state */
402 VBoxNetDhcpd::netifInitCB, /* netif_init_fn */
403 tcpip_input); /* netif_input_fn */
404 if (pNetif == NULL)
405 return;
406
407 netif_set_up(pNetif);
408 netif_set_link_up(pNetif);
409
410 m_Dhcp4Pcb = udp_new();
411 if (RT_UNLIKELY(m_Dhcp4Pcb == NULL))
412 return; /* XXX? */
413
414 ip_set_option(m_Dhcp4Pcb, SOF_BROADCAST);
415 udp_recv(m_Dhcp4Pcb, dhcp4RecvCB, this);
416
417 error = udp_bind(m_Dhcp4Pcb, IP_ADDR_ANY, RTNETIPV4_PORT_BOOTPS);
418 if (error != ERR_OK)
419 {
420 udp_remove(m_Dhcp4Pcb);
421 m_Dhcp4Pcb = NULL;
422 return; /* XXX? */
423 }
424}
425
426
427err_t VBoxNetDhcpd::netifInit(netif *pNetif)
428{
429 pNetif->hwaddr_len = sizeof(RTMAC);
430 memcpy(pNetif->hwaddr, &m_Config->getMacAddress(), sizeof(RTMAC));
431
432 pNetif->mtu = 1500;
433
434 pNetif->flags = NETIF_FLAG_BROADCAST
435 | NETIF_FLAG_ETHARP
436 | NETIF_FLAG_ETHERNET;
437
438 pNetif->linkoutput = netifLinkOutputCB;
439 pNetif->output = etharp_output;
440
441 netif_set_default(pNetif);
442 return ERR_OK;
443}
444
445
446void VBoxNetDhcpd::dhcp4Recv(struct udp_pcb *pcb, struct pbuf *p,
447 ip_addr_t *addr, u16_t port)
448{
449 RT_NOREF(pcb, addr, port);
450
451 if (RT_UNLIKELY(p->next != NULL))
452 return; /* XXX: we want it in one chunk */
453
454 bool broadcasted = ip_addr_cmp(ip_current_dest_addr(), &ip_addr_broadcast)
455 || ip_addr_cmp(ip_current_dest_addr(), &ip_addr_any);
456
457 try
458 {
459 DhcpClientMessage *msgIn = DhcpClientMessage::parse(broadcasted, p->payload, p->len);
460 if (msgIn == NULL)
461 return;
462
463 std::unique_ptr<DhcpClientMessage> autoFreeMsgIn(msgIn);
464
465 DhcpServerMessage *msgOut = m_server.process(*msgIn);
466 if (msgOut == NULL)
467 return;
468
469 std::unique_ptr<DhcpServerMessage> autoFreeMsgOut(msgOut);
470
471 ip_addr_t dst = { msgOut->dst().u };
472 if (ip_addr_cmp(&dst, &ip_addr_any))
473 ip_addr_copy(dst, ip_addr_broadcast);
474
475 octets_t data;
476 int rc = msgOut->encode(data);
477 if (RT_FAILURE(rc))
478 return;
479
480 unique_ptr_pbuf q ( pbuf_alloc(PBUF_RAW, (u16_t)data.size(), PBUF_RAM) );
481 if (!q)
482 return;
483
484 err_t error = pbuf_take(q.get(), &data.front(), (u16_t)data.size());
485 if (error != ERR_OK)
486 return;
487
488 error = udp_sendto(pcb, q.get(), &dst, RTNETIPV4_PORT_BOOTPC);
489 if (error != ERR_OK)
490 return;
491 }
492 catch (std::bad_alloc &)
493 {
494 LogRel(("VBoxNetDhcpd::dhcp4Recv: Caught std::bad_alloc!\n"));
495 }
496}
497
498
499
500
501/*
502 * Entry point.
503 */
504extern "C" DECLEXPORT(int) TrustedMain(int argc, char **argv)
505{
506 VBoxNetDhcpd Dhcpd;
507 int rc = Dhcpd.main(argc, argv);
508 return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
509}
510
511
512#ifndef VBOX_WITH_HARDENING
513
514int main(int argc, char **argv)
515{
516 int rc = RTR3InitExe(argc, &argv, RTR3INIT_FLAGS_SUPLIB);
517 if (RT_SUCCESS(rc))
518 return TrustedMain(argc, argv);
519 return RTMsgInitFailure(rc);
520}
521
522
523# ifdef RT_OS_WINDOWS
524/** (We don't want a console usually.) */
525int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
526{
527 RT_NOREF(hInstance, hPrevInstance, lpCmdLine, nCmdShow);
528
529 return main(__argc, __argv);
530}
531# endif /* RT_OS_WINDOWS */
532
533#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