VirtualBox

source: vbox/trunk/src/VBox/Devices/Network/testcase/tstIntNet-1.cpp

Last change on this file was 103431, checked in by vboxsync, 7 months ago

tstIntNet-1: RTStrCat won't fail because we already subtracted the size of the string-to-append from the buffer size in the RTPathExecDir call. Just use memcpy to avoid upsetting anyone. bugref:3409

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 36.9 KB
Line 
1/* $Id: tstIntNet-1.cpp 103431 2024-02-19 12:06:37Z vboxsync $ */
2/** @file
3 * VBox - Testcase for internal networking, simple NetFlt trunk creation.
4 */
5
6/*
7 * Copyright (C) 2006-2023 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/*********************************************************************************************************************************
30* Header Files *
31*********************************************************************************************************************************/
32#include <VBox/intnet.h>
33#include <VBox/intnetinline.h>
34#include <VBox/vmm/pdmnetinline.h>
35#include <VBox/sup.h>
36#include <VBox/vmm/vmm.h>
37#include <iprt/errcore.h>
38#include <iprt/initterm.h>
39#include <iprt/alloc.h>
40#include <iprt/path.h>
41#include <iprt/stream.h>
42#include <iprt/string.h>
43#include <iprt/thread.h>
44#include <iprt/param.h>
45#include <iprt/getopt.h>
46#include <iprt/rand.h>
47#include <iprt/log.h>
48#include <iprt/crc.h>
49#include <iprt/net.h>
50
51#include "../Pcap.h"
52
53
54/*********************************************************************************************************************************
55* Global Variables *
56*********************************************************************************************************************************/
57static int g_cErrors = 0;
58static uint64_t g_StartTS = 0;
59static uint32_t g_DhcpXID = 0;
60static bool g_fDhcpReply = false;
61static bool g_fPingReply = false;
62static uint32_t g_cOtherPkts = 0;
63static uint32_t g_cArpPkts = 0;
64static uint32_t g_cIpv4Pkts = 0;
65static uint32_t g_cUdpPkts = 0;
66static uint32_t g_cDhcpPkts = 0;
67static uint32_t g_cTcpPkts = 0;
68
69
70/**
71 * Error reporting wrapper.
72 *
73 * @param pErrStrm The stream to write the error message to. Can be NULL.
74 * @param pszFormat The message format string.
75 * @param ... Format arguments.
76 */
77static void tstIntNetError(PRTSTREAM pErrStrm, const char *pszFormat, ...)
78{
79 if (!pErrStrm)
80 pErrStrm = g_pStdOut;
81
82 va_list va;
83 va_start(va, pszFormat);
84 RTStrmPrintf(pErrStrm, "tstIntNet-1: ERROR - ");
85 RTStrmPrintfV(pErrStrm, pszFormat, va);
86 va_end(va);
87
88 g_cErrors++;
89}
90
91
92/**
93 * Parses a frame an runs in thru the RTNet validation code so it gets
94 * some exercise.
95 *
96 * @param pvFrame Pointer to the ethernet frame.
97 * @param cbFrame The size of the ethernet frame.
98 * @param pErrStrm The error stream.
99 */
100static void tstIntNetTestFrame(void const *pvFrame, size_t cbFrame, PRTSTREAM pErrStrm, bool fGso)
101{
102 /*
103 * Ethernet header.
104 */
105 PCRTNETETHERHDR pEtherHdr = (PCRTNETETHERHDR)pvFrame;
106 if (cbFrame <= sizeof(*pEtherHdr))
107 return tstIntNetError(pErrStrm, "cbFrame=%#x <= %#x (ether)\n", cbFrame, sizeof(*pEtherHdr));
108 ssize_t cbLeft = cbFrame - sizeof(*pEtherHdr);
109 uint8_t const *pbCur = (uint8_t const *)(pEtherHdr + 1);
110
111 switch (RT_BE2H_U16(pEtherHdr->EtherType))
112 {
113 case RTNET_ETHERTYPE_ARP:
114 {
115 g_cArpPkts++;
116 break;
117 }
118
119 case RTNET_ETHERTYPE_IPV4:
120 {
121 g_cIpv4Pkts++;
122
123 PCRTNETIPV4 pIpHdr = (PCRTNETIPV4)pbCur;
124 if (!RTNetIPv4IsHdrValid(pIpHdr, cbLeft, cbLeft, !fGso /*fChecksum*/))
125 return tstIntNetError(pErrStrm, "RTNetIPv4IsHdrValid failed\n");
126 pbCur += pIpHdr->ip_hl * 4;
127 cbLeft -= pIpHdr->ip_hl * 4;
128 AssertFatal(cbLeft >= 0);
129
130 switch (pIpHdr->ip_p)
131 {
132 case RTNETIPV4_PROT_ICMP:
133 {
134 /** @todo ICMP? */
135 break;
136 }
137
138 case RTNETIPV4_PROT_UDP:
139 {
140 g_cUdpPkts++;
141 PCRTNETUDP pUdpHdr = (PCRTNETUDP)pbCur;
142 if (!RTNetIPv4IsUDPValid(pIpHdr, pUdpHdr, pUdpHdr + 1, cbLeft, !fGso /*fChecksum*/))
143 return tstIntNetError(pErrStrm, "RTNetIPv4IsUDPValid failed\n");
144 pbCur += sizeof(*pUdpHdr);
145 cbLeft -= sizeof(*pUdpHdr);
146
147 if (RT_BE2H_U16(pUdpHdr->uh_dport) == RTNETIPV4_PORT_BOOTPS)
148 {
149 g_cDhcpPkts++;
150 PCRTNETBOOTP pDhcp = (PCRTNETBOOTP)pbCur;
151 if (!RTNetIPv4IsDHCPValid(pUdpHdr, pDhcp, cbLeft, NULL))
152 return tstIntNetError(pErrStrm, "RTNetIPv4IsDHCPValid failed\n");
153 }
154 break;
155 }
156
157 case RTNETIPV4_PROT_TCP:
158 {
159 g_cTcpPkts++;
160 PCRTNETTCP pTcpHdr = (PCRTNETTCP)pbCur;
161 if (!RTNetIPv4IsTCPValid(pIpHdr, pTcpHdr, cbLeft, NULL, cbLeft, !fGso /*fChecksum*/))
162 return tstIntNetError(pErrStrm, "RTNetIPv4IsTCPValid failed\n");
163 break;
164 }
165 }
166 break;
167 }
168
169 //case RTNET_ETHERTYPE_IPV6:
170 default:
171 g_cOtherPkts++;
172 break;
173 }
174}
175
176
177/**
178 * Transmits one frame after appending the CRC.
179 *
180 * @param hIf The interface handle.
181 * @param pSession The session.
182 * @param pBuf The shared interface buffer.
183 * @param pvFrame The frame without a crc.
184 * @param cbFrame The size of it.
185 * @param pFileRaw The file to write the raw data to (optional).
186 * @param pFileText The file to write a textual packet summary to (optional).
187 */
188static void doXmitFrame(INTNETIFHANDLE hIf, PSUPDRVSESSION pSession, PINTNETBUF pBuf, void *pvFrame, size_t cbFrame, PRTSTREAM pFileRaw, PRTSTREAM pFileText)
189{
190 /*
191 * Log it.
192 */
193 if (pFileText)
194 {
195 PCRTNETETHERHDR pEthHdr = (PCRTNETETHERHDR)pvFrame;
196 uint64_t NanoTS = RTTimeNanoTS() - g_StartTS;
197 RTStrmPrintf(pFileText, "%3RU64.%09u: cb=%04x dst=%.6Rhxs src=%.6Rhxs type=%04x Send!\n",
198 NanoTS / 1000000000, (uint32_t)(NanoTS % 1000000000),
199 cbFrame, &pEthHdr->SrcMac, &pEthHdr->DstMac, RT_BE2H_U16(pEthHdr->EtherType));
200 }
201
202 /*
203 * Run in thru the frame validator to test the RTNet code.
204 */
205 tstIntNetTestFrame(pvFrame, cbFrame, pFileText, false /*fGso*/);
206
207 /*
208 * Write the frame and push the queue.
209 *
210 * Don't bother with dealing with overflows like DrvIntNet does, because
211 * it's not supposed to happen here in this testcase.
212 */
213 int rc = IntNetRingWriteFrame(&pBuf->Send, pvFrame, cbFrame);
214 if (RT_SUCCESS(rc))
215 {
216 if (pFileRaw)
217 PcapStreamFrame(pFileRaw, g_StartTS, pvFrame, cbFrame, 0xffff);
218 }
219 else
220 {
221 RTPrintf("tstIntNet-1: IntNetRingWriteFrame failed, %Rrc; cbFrame=%d pBuf->cbSend=%d\n", rc, cbFrame, pBuf->cbSend);
222 g_cErrors++;
223 }
224
225 INTNETIFSENDREQ SendReq;
226 SendReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
227 SendReq.Hdr.cbReq = sizeof(SendReq);
228 SendReq.pSession = pSession;
229 SendReq.hIf = hIf;
230 rc = SUPR3CallVMMR0Ex(NIL_RTR0PTR, NIL_VMCPUID, VMMR0_DO_INTNET_IF_SEND, 0, &SendReq.Hdr);
231 if (RT_FAILURE(rc))
232 {
233 RTPrintf("tstIntNet-1: SUPR3CallVMMR0Ex(,VMMR0_DO_INTNET_IF_SEND,) failed, rc=%Rrc\n", rc);
234 g_cErrors++;
235 }
236
237}
238
239
240/**
241 * Does the transmit test.
242 *
243 * @param hIf The interface handle.
244 * @param pSession The session.
245 * @param pBuf The shared interface buffer.
246 * @param pSrcMac The mac address to use as source.
247 * @param pFileRaw The file to write the raw data to (optional).
248 * @param pFileText The file to write a textual packet summary to (optional).
249 */
250static void doXmitTest(INTNETIFHANDLE hIf, PSUPDRVSESSION pSession, PINTNETBUF pBuf, PCRTMAC pSrcMac, PRTSTREAM pFileRaw, PRTSTREAM pFileText)
251{
252 uint8_t abFrame[4096];
253 PRTNETETHERHDR pEthHdr = (PRTNETETHERHDR)&abFrame[0];
254 PRTNETIPV4 pIpHdr = (PRTNETIPV4) (pEthHdr + 1);
255 PRTNETUDP pUdpHdr = (PRTNETUDP) (pIpHdr + 1);
256 PRTNETDHCP pDhcpMsg = (PRTNETDHCP) (pUdpHdr + 1);
257
258 /*
259 * Create a simple DHCP broadcast request.
260 */
261 memset(&abFrame, 0, sizeof(abFrame));
262
263 pDhcpMsg->Op = 1; /* request */
264 pDhcpMsg->HType = 1; /* ethernet */
265 pDhcpMsg->HLen = sizeof(RTMAC);
266 pDhcpMsg->Hops = 0;
267 pDhcpMsg->XID = g_DhcpXID = RTRandU32();
268 pDhcpMsg->Secs = 0;
269 pDhcpMsg->Flags = 0; /* unicast */ //RT_H2BE_U16(0x8000); /* broadcast */
270 pDhcpMsg->CIAddr.u = 0;
271 pDhcpMsg->YIAddr.u = 0;
272 pDhcpMsg->SIAddr.u = 0;
273 pDhcpMsg->GIAddr.u = 0;
274 memset(&pDhcpMsg->CHAddr[0], '\0', sizeof(pDhcpMsg->CHAddr));
275 memcpy(&pDhcpMsg->CHAddr[0], pSrcMac, sizeof(*pSrcMac));
276 memset(&pDhcpMsg->SName[0], '\0', sizeof(pDhcpMsg->SName));
277 memset(&pDhcpMsg->File[0], '\0', sizeof(pDhcpMsg->File));
278 pDhcpMsg->abMagic[0] = 99;
279 pDhcpMsg->abMagic[1] = 130;
280 pDhcpMsg->abMagic[2] = 83;
281 pDhcpMsg->abMagic[3] = 99;
282
283 pDhcpMsg->DhcpOpt = 53; /* DHCP Msssage Type option */
284 pDhcpMsg->DhcpLen = 1;
285 pDhcpMsg->DhcpReq = 1; /* DHCPDISCOVER */
286
287 memset(&pDhcpMsg->abOptions[0], '\0', sizeof(pDhcpMsg->abOptions));
288 uint8_t *pbOpt = &pDhcpMsg->abOptions[0];
289
290 *pbOpt++ = 116; /* DHCP Auto-Configure */
291 *pbOpt++ = 1;
292 *pbOpt++ = 1;
293
294 *pbOpt++ = 61; /* Client identifier */
295 *pbOpt++ = 1 + sizeof(*pSrcMac);
296 *pbOpt++ = 1; /* hw type: ethernet */
297 memcpy(pbOpt, pSrcMac, sizeof(*pSrcMac));
298 pbOpt += sizeof(*pSrcMac);
299
300 *pbOpt++ = 12; /* Host name */
301 *pbOpt++ = sizeof("tstIntNet-1") - 1;
302 memcpy(pbOpt, "tstIntNet-1", sizeof("tstIntNet-1") - 1);
303 pbOpt += sizeof("tstIntNet-1") - 1;
304
305 *pbOpt = 0xff; /* the end */
306
307 /* UDP */
308 pUdpHdr->uh_sport = RT_H2BE_U16(68); /* bootp */
309 pUdpHdr->uh_dport = RT_H2BE_U16(67); /* bootps */
310 pUdpHdr->uh_ulen = RT_H2BE_U16(sizeof(*pDhcpMsg) + sizeof(*pUdpHdr));
311 pUdpHdr->uh_sum = 0; /* pretend checksumming is disabled */
312
313 /* IP */
314 pIpHdr->ip_v = 4;
315 pIpHdr->ip_hl = sizeof(*pIpHdr) / sizeof(uint32_t);
316 pIpHdr->ip_tos = 0;
317 pIpHdr->ip_len = RT_H2BE_U16(sizeof(*pDhcpMsg) + sizeof(*pUdpHdr) + sizeof(*pIpHdr));
318 pIpHdr->ip_id = (uint16_t)RTRandU32();
319 pIpHdr->ip_off = 0;
320 pIpHdr->ip_ttl = 255;
321 pIpHdr->ip_p = 0x11; /* UDP */
322 pIpHdr->ip_sum = 0;
323 pIpHdr->ip_src.u = 0;
324 pIpHdr->ip_dst.u = UINT32_C(0xffffffff); /* broadcast */
325 pIpHdr->ip_sum = RTNetIPv4HdrChecksum(pIpHdr);
326
327 /* calc the UDP checksum. */
328 pUdpHdr->uh_sum = RTNetIPv4UDPChecksum(pIpHdr, pUdpHdr, pUdpHdr + 1);
329
330 /* Ethernet */
331 memset(&pEthHdr->DstMac, 0xff, sizeof(pEthHdr->DstMac)); /* broadcast */
332 pEthHdr->SrcMac = *pSrcMac;
333 pEthHdr->EtherType = RT_H2BE_U16(RTNET_ETHERTYPE_IPV4); /* IP */
334
335 doXmitFrame(hIf, pSession, pBuf, &abFrame[0], (uint8_t *)(pDhcpMsg + 1) - (uint8_t *)&abFrame[0], pFileRaw, pFileText);
336}
337
338
339static uint16_t icmpChecksum(PRTNETICMPV4HDR pHdr, size_t cbHdr)
340{
341 size_t cbLeft = cbHdr;
342 uint16_t *pbSrc = (uint16_t *)pHdr;
343 uint16_t oddByte = 0;
344 int cSum = 0;
345
346 while (cbLeft > 1)
347 {
348 cSum += *pbSrc++;
349 cbLeft -= 2;
350 }
351
352 if (cbLeft == 1)
353 {
354 *(uint16_t *)(&oddByte) = *(uint16_t *)pbSrc;
355 cSum += oddByte;
356 }
357
358 cSum = (cSum >> 16) + (cSum & 0xffff);
359 cSum += (cSum >> 16);
360 uint16_t Result = ~cSum;
361 return Result;
362}
363
364
365/**
366 * Does the rudimentary ping test with fixed destination and source IPs.
367 *
368 * @param hIf The interface handle.
369 * @param pSession The session.
370 * @param pBuf The shared interface buffer.
371 * @param pSrcMac The mac address to use as source.
372 * @param pFileRaw The file to write the raw data to (optional).
373 * @param pFileText The file to write a textual packet summary to (optional).
374 */
375static void doPingTest(INTNETIFHANDLE hIf, PSUPDRVSESSION pSession, PINTNETBUF pBuf, PCRTMAC pSrcMac, PRTSTREAM pFileRaw, PRTSTREAM pFileText)
376{
377 uint8_t abFrame[4096];
378 PRTNETETHERHDR pEthHdr = (PRTNETETHERHDR)&abFrame[0];
379 PRTNETIPV4 pIpHdr = (PRTNETIPV4) (pEthHdr + 1);
380 PRTNETICMPV4ECHO pIcmpEcho = (PRTNETICMPV4ECHO) (pIpHdr + 1);
381
382 /*
383 * Create a simple ping request.
384 */
385 memset(&abFrame, 0, sizeof(abFrame));
386
387 pIcmpEcho->Hdr.icmp_type = RTNETICMPV4_TYPE_ECHO_REQUEST;
388 pIcmpEcho->Hdr.icmp_code = 0;
389 pIcmpEcho->icmp_id = 0x06;
390 pIcmpEcho->icmp_seq = 0x05;
391 size_t cbPad = 56;
392 memset(&pIcmpEcho->icmp_data, '\0', cbPad);
393 pIcmpEcho->Hdr.icmp_cksum = icmpChecksum(&pIcmpEcho->Hdr, cbPad + 8);
394
395 /* IP */
396 pIpHdr->ip_v = 4;
397 pIpHdr->ip_hl = sizeof(*pIpHdr) / sizeof(uint32_t);
398 pIpHdr->ip_tos = 0;
399 pIpHdr->ip_len = RT_H2BE_U16((uint16_t)(sizeof(*pIcmpEcho) + cbPad + sizeof(*pIpHdr)));
400 pIpHdr->ip_id = (uint16_t)RTRandU32();
401 pIpHdr->ip_off = 0;
402 pIpHdr->ip_ttl = 255;
403 pIpHdr->ip_p = 0x01; /*ICMP */
404 pIpHdr->ip_sum = 0;
405 pIpHdr->ip_src.u = UINT32_C(0x9701A8C0); /* 192.168.1.151 */
406 pIpHdr->ip_dst.u = UINT32_C(0xF9A344D0); /* 208.68.163.249 */
407 pIpHdr->ip_sum = RTNetIPv4HdrChecksum(pIpHdr);
408
409 /* Ethernet */
410 memset(&pEthHdr->DstMac, 0xff, sizeof(pEthHdr->DstMac)); /* broadcast */
411
412 pEthHdr->SrcMac = *pSrcMac;
413#if 0 /* Enable with host's real Mac address for testing of the testcase. */
414 pEthHdr->SrcMac.au8[0] = 0x00;
415 pEthHdr->SrcMac.au8[1] = 0x1b;
416 pEthHdr->SrcMac.au8[2] = 0x24;
417 pEthHdr->SrcMac.au8[3] = 0xa0;
418 pEthHdr->SrcMac.au8[4] = 0x2f;
419 pEthHdr->SrcMac.au8[5] = 0xce;
420#endif
421
422 pEthHdr->EtherType = RT_H2BE_U16(RTNET_ETHERTYPE_IPV4); /* IP */
423
424 doXmitFrame(hIf, pSession, pBuf, &abFrame[0], (uint8_t *)(pIcmpEcho + 1) + cbPad - (uint8_t *)&abFrame[0], pFileRaw, pFileText);
425}
426
427
428/**
429 * Does packet sniffing for a given period of time.
430 *
431 * @param hIf The interface handle.
432 * @param pSession The session.
433 * @param pBuf The shared interface buffer.
434 * @param cMillies The time period, ms.
435 * @param pFileRaw The file to write the raw data to (optional).
436 * @param pFileText The file to write a textual packet summary to (optional).
437 * @param pSrcMac Out MAC address.
438 */
439static void doPacketSniffing(INTNETIFHANDLE hIf, PSUPDRVSESSION pSession, PINTNETBUF pBuf, uint32_t cMillies,
440 PRTSTREAM pFileRaw, PRTSTREAM pFileText, PCRTMAC pSrcMac)
441{
442 /*
443 * The loop.
444 */
445 PINTNETRINGBUF pRingBuf = &pBuf->Recv;
446 for (;;)
447 {
448 /*
449 * Wait for a packet to become available.
450 */
451 uint64_t cElapsedMillies = (RTTimeNanoTS() - g_StartTS) / 1000000;
452 if (cElapsedMillies >= cMillies)
453 break;
454 INTNETIFWAITREQ WaitReq;
455 WaitReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
456 WaitReq.Hdr.cbReq = sizeof(WaitReq);
457 WaitReq.pSession = pSession;
458 WaitReq.hIf = hIf;
459 WaitReq.cMillies = cMillies - (uint32_t)cElapsedMillies;
460 int rc = SUPR3CallVMMR0Ex(NIL_RTR0PTR, NIL_VMCPUID, VMMR0_DO_INTNET_IF_WAIT, 0, &WaitReq.Hdr);
461 if (rc == VERR_TIMEOUT || rc == VERR_INTERRUPTED)
462 break;
463 if (RT_FAILURE(rc))
464 {
465 g_cErrors++;
466 RTPrintf("tstIntNet-1: VMMR0_DO_INTNET_IF_WAIT returned %Rrc\n", rc);
467 break;
468 }
469
470 /*
471 * Process the receive buffer.
472 */
473 PINTNETHDR pHdr;
474 while ((pHdr = IntNetRingGetNextFrameToRead(pRingBuf)))
475 {
476 if (pHdr->u8Type == INTNETHDR_TYPE_FRAME)
477 {
478 size_t cbFrame = pHdr->cbFrame;
479 const void *pvFrame = IntNetHdrGetFramePtr(pHdr, pBuf);
480 uint64_t NanoTS = RTTimeNanoTS() - g_StartTS;
481
482 if (pFileRaw)
483 PcapStreamFrame(pFileRaw, g_StartTS, pvFrame, cbFrame, 0xffff);
484
485 PCRTNETETHERHDR pEthHdr = (PCRTNETETHERHDR)pvFrame;
486 if (pFileText)
487 RTStrmPrintf(pFileText, "%3RU64.%09u: cb=%04x dst=%.6Rhxs src=%.6Rhxs type=%04x%s\n",
488 NanoTS / 1000000000, (uint32_t)(NanoTS % 1000000000),
489 cbFrame, &pEthHdr->DstMac, &pEthHdr->SrcMac, RT_BE2H_U16(pEthHdr->EtherType),
490 !memcmp(&pEthHdr->DstMac, pSrcMac, sizeof(*pSrcMac)) ? " Mine!" : "");
491 tstIntNetTestFrame(pvFrame, cbFrame, pFileText, false /*fGso*/);
492
493 /* Loop for the DHCP reply. */
494 if ( cbFrame > 64
495 && RT_BE2H_U16(pEthHdr->EtherType) == 0x0800 /* EtherType == IP */)
496 {
497 PCRTNETIPV4 pIpHdr = (PCRTNETIPV4)(pEthHdr + 1);
498 PCRTNETUDP pUdpHdr = (PCRTNETUDP)((uint32_t *)pIpHdr + pIpHdr->ip_hl);
499 if ( pIpHdr->ip_p == 0x11 /*UDP*/
500 && RT_BE2H_U16(pUdpHdr->uh_dport) == 68 /* bootp */
501 && RT_BE2H_U16(pUdpHdr->uh_sport) == 67 /* bootps */)
502 {
503 PCRTNETDHCP pDhcpMsg = (PCRTNETDHCP)(pUdpHdr + 1);
504 if ( pDhcpMsg->Op == 2 /* boot reply */
505 && pDhcpMsg->HType == 1 /* ethernet */
506 && pDhcpMsg->HLen == sizeof(RTMAC)
507 && (pDhcpMsg->XID == g_DhcpXID || !g_DhcpXID)
508 && !memcmp(&pDhcpMsg->CHAddr[0], pSrcMac, sizeof(*pSrcMac)))
509 {
510 g_fDhcpReply = true;
511 RTPrintf("tstIntNet-1: DHCP server reply! My IP: %d.%d.%d.%d\n",
512 pDhcpMsg->YIAddr.au8[0],
513 pDhcpMsg->YIAddr.au8[1],
514 pDhcpMsg->YIAddr.au8[2],
515 pDhcpMsg->YIAddr.au8[3]);
516 }
517 }
518 else if (pIpHdr->ip_p == 0x01) /* ICMP */
519 {
520 PRTNETICMPV4HDR pIcmpHdr = (PRTNETICMPV4HDR)(pIpHdr + 1);
521 PRTNETICMPV4ECHO pIcmpEcho = (PRTNETICMPV4ECHO)(pIpHdr + 1);
522 if ( pIcmpHdr->icmp_type == RTNETICMPV4_TYPE_ECHO_REPLY
523 && pIcmpEcho->icmp_seq == 0x05
524 && pIpHdr->ip_dst.u == UINT32_C(0x9701A8C0)
525#if 0
526 /** Enable with the host's real Mac address for testing of the testcase.*/
527 && pEthHdr->DstMac.au8[0] == 0x00
528 && pEthHdr->DstMac.au8[1] == 0x1b
529 && pEthHdr->DstMac.au8[2] == 0x24
530 && pEthHdr->DstMac.au8[3] == 0xa0
531 && pEthHdr->DstMac.au8[4] == 0x2f
532 && pEthHdr->DstMac.au8[5] == 0xce
533#else
534 && pEthHdr->DstMac.au16[0] == pSrcMac->au16[0]
535 && pEthHdr->DstMac.au16[1] == pSrcMac->au16[1]
536 && pEthHdr->DstMac.au16[2] == pSrcMac->au16[2]
537#endif
538 )
539 {
540 g_fPingReply = true;
541 RTPrintf("tstIntNet-1: Ping reply! From %d.%d.%d.%d\n",
542 pIpHdr->ip_src.au8[0],
543 pIpHdr->ip_src.au8[1],
544 pIpHdr->ip_src.au8[2],
545 pIpHdr->ip_src.au8[3]);
546 }
547 else
548 RTPrintf("type=%d seq=%d dstmac=%.6Rhxs ip=%d.%d.%d.%d\n", pIcmpHdr->icmp_type, pIcmpEcho->icmp_seq,
549 &pEthHdr->DstMac, pIpHdr->ip_dst.au8[0], pIpHdr->ip_dst.au8[1], pIpHdr->ip_dst.au8[2], pIpHdr->ip_dst.au8[3]);
550 }
551 }
552 }
553 else if (pHdr->u8Type == INTNETHDR_TYPE_GSO)
554 {
555 PCPDMNETWORKGSO pGso = IntNetHdrGetGsoContext(pHdr, pBuf);
556 size_t cbFrame = pHdr->cbFrame;
557 if (PDMNetGsoIsValid(pGso, cbFrame, cbFrame - sizeof(*pGso)))
558 {
559 const void *pvFrame = pGso + 1;
560 uint64_t NanoTS = RTTimeNanoTS() - g_StartTS;
561 cbFrame -= sizeof(pGso);
562
563 if (pFileRaw)
564 PcapStreamGsoFrame(pFileRaw, g_StartTS, pGso, pvFrame, cbFrame, 0xffff);
565
566 PCRTNETETHERHDR pEthHdr = (PCRTNETETHERHDR)pvFrame;
567 if (pFileText)
568 RTStrmPrintf(pFileText, "%3RU64.%09u: cb=%04x dst=%.6Rhxs src=%.6Rhxs type=%04x%s [GSO]\n",
569 NanoTS / 1000000000, (uint32_t)(NanoTS % 1000000000),
570 cbFrame, &pEthHdr->DstMac, &pEthHdr->SrcMac, RT_BE2H_U16(pEthHdr->EtherType),
571 !memcmp(&pEthHdr->DstMac, pSrcMac, sizeof(*pSrcMac)) ? " Mine!" : "");
572 tstIntNetTestFrame(pvFrame, cbFrame, pFileText, true /*fGso*/);
573 }
574 else
575 {
576 RTPrintf("tstIntNet-1: Bad GSO frame: %.*Rhxs\n", sizeof(*pGso), pGso);
577 STAM_REL_COUNTER_INC(&pBuf->cStatBadFrames);
578 g_cErrors++;
579 }
580 }
581 else if (pHdr->u8Type != INTNETHDR_TYPE_PADDING)
582 {
583 RTPrintf("tstIntNet-1: Unknown frame type %d\n", pHdr->u8Type);
584 STAM_REL_COUNTER_INC(&pBuf->cStatBadFrames);
585 g_cErrors++;
586 }
587
588 /* Advance to the next frame. */
589 IntNetRingSkipFrame(pRingBuf);
590 }
591 }
592
593 uint64_t NanoTS = RTTimeNanoTS() - g_StartTS;
594 RTStrmPrintf(pFileText ? pFileText : g_pStdOut,
595 "%3RU64.%09u: stopped. cRecvs=%RU64 cbRecv=%RU64 cLost=%RU64 cOYs=%RU64 cNYs=%RU64\n",
596 NanoTS / 1000000000, (uint32_t)(NanoTS % 1000000000),
597 pBuf->Recv.cStatFrames.c,
598 pBuf->Recv.cbStatWritten.c,
599 pBuf->cStatLost.c,
600 pBuf->cStatYieldsOk.c,
601 pBuf->cStatYieldsNok.c
602 );
603 RTStrmPrintf(pFileText ? pFileText : g_pStdOut,
604 "%3RU64.%09u: cOtherPkts=%RU32 cArpPkts=%RU32 cIpv4Pkts=%RU32 cTcpPkts=%RU32 cUdpPkts=%RU32 cDhcpPkts=%RU32\n",
605 NanoTS / 1000000000, (uint32_t)(NanoTS % 1000000000),
606 g_cOtherPkts, g_cArpPkts, g_cIpv4Pkts, g_cTcpPkts, g_cUdpPkts, g_cDhcpPkts);
607}
608
609#ifdef RT_OS_LINUX
610#include <stdio.h>
611#include <net/if.h>
612#include <net/route.h>
613/**
614 * Obtain the name of the interface used for default routing.
615 *
616 * NOTE: Copied from Main/src-server/linux/NetIf-linux.cpp
617 *
618 * @returns VBox status code.
619 *
620 * @param pszName The buffer where to put the name.
621 * @param cbName The buffer length.
622 */
623static int getDefaultIfaceName(char *pszName, size_t cbName)
624{
625 FILE *fp = fopen("/proc/net/route", "r");
626 char szBuf[1024];
627 char szIfName[17];
628 uint32_t uAddr;
629 uint32_t uGateway;
630 uint32_t uMask;
631 int iTmp;
632 unsigned uFlags;
633
634 if (fp)
635 {
636 while (fgets(szBuf, sizeof(szBuf)-1, fp))
637 {
638 int n = sscanf(szBuf, "%16s %x %x %x %d %d %d %x %d %d %d\n",
639 szIfName, &uAddr, &uGateway, &uFlags, &iTmp, &iTmp, &iTmp,
640 &uMask, &iTmp, &iTmp, &iTmp);
641 if (n < 10 || !(uFlags & RTF_UP))
642 continue;
643
644 if (uAddr == 0 && uMask == 0)
645 {
646 fclose(fp);
647 szIfName[sizeof(szIfName) - 1] = '\0';
648 return RTStrCopy(pszName, cbName, szIfName);
649 }
650 }
651 fclose(fp);
652 }
653 return VERR_INTERNAL_ERROR;
654}
655#endif /* RT_OS_LINUX */
656
657
658/**
659 * Entry point.
660 */
661extern "C" DECLEXPORT(int) TrustedMain(int argc, char **argv, char **envp)
662{
663 RT_NOREF(envp);
664
665 /*
666 * Init the runtime and parse the arguments.
667 */
668 RTR3InitExe(argc, &argv, 0);
669
670 static RTGETOPTDEF const s_aOptions[] =
671 {
672 { "--duration", 'd', RTGETOPT_REQ_UINT32 },
673 { "--file", 'f', RTGETOPT_REQ_STRING },
674 { "--interface", 'i', RTGETOPT_REQ_STRING },
675 { "--mac-sharing", 'm', RTGETOPT_REQ_NOTHING },
676 { "--network", 'n', RTGETOPT_REQ_STRING },
677 { "--promiscuous", 'p', RTGETOPT_REQ_NOTHING },
678 { "--recv-buffer", 'r', RTGETOPT_REQ_UINT32 },
679 { "--send-buffer", 's', RTGETOPT_REQ_UINT32 },
680 { "--sniffer", 'S', RTGETOPT_REQ_NOTHING },
681 { "--text-file", 't', RTGETOPT_REQ_STRING },
682 { "--xmit-test", 'x', RTGETOPT_REQ_NOTHING },
683 { "--ping-test", 'P', RTGETOPT_REQ_NOTHING },
684 };
685
686 uint32_t cMillies = 1000;
687 PRTSTREAM pFileRaw = NULL;
688#ifdef RT_OS_DARWIN
689 const char *pszIf = "en0";
690#elif defined(RT_OS_LINUX)
691 char szIf[IFNAMSIZ+1] = "eth0"; /* Reasonable default */
692 /*
693 * Try to update the default interface by consulting the routing table.
694 * If we fail we still have our reasonable default.
695 */
696 getDefaultIfaceName(szIf, sizeof(szIf));
697 const char *pszIf = szIf;
698#elif defined(RT_OS_SOLARIS)
699 const char* pszIf = "rge0";
700#else
701 const char *pszIf = "em0";
702#endif
703 bool fMacSharing = false;
704 const char *pszNetwork = "tstIntNet-1";
705 bool fPromiscuous = false;
706 uint32_t cbRecv = 0;
707 uint32_t cbSend = 0;
708 bool fSniffer = false;
709 PRTSTREAM pFileText = g_pStdOut;
710 bool fXmitTest = false;
711 bool fPingTest = false;
712 RTMAC SrcMac;
713 SrcMac.au8[0] = 0x08;
714 SrcMac.au8[1] = 0x03;
715 SrcMac.au8[2] = 0x86;
716 RTRandBytes(&SrcMac.au8[3], sizeof(SrcMac) - 3);
717
718 int rc;
719 int ch;
720 RTGETOPTUNION Value;
721 RTGETOPTSTATE GetState;
722 RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0 /* fFlags */);
723 while ((ch = RTGetOpt(&GetState, &Value)))
724 switch (ch)
725 {
726 case 'd':
727 cMillies = Value.u32 * 1000;
728 if (cMillies / 1000 != Value.u32)
729 {
730 RTPrintf("tstIntNet-1: warning duration overflowed\n");
731 cMillies = UINT32_MAX - 1;
732 }
733 break;
734
735 case 'f':
736 rc = RTStrmOpen(Value.psz, "w+b", &pFileRaw);
737 if (RT_FAILURE(rc))
738 {
739 RTPrintf("tstIntNet-1: Failed to creating \"%s\" for writing: %Rrc\n", Value.psz, rc);
740 return 1;
741 }
742 break;
743
744 case 'i':
745 pszIf = Value.psz;
746 if (strlen(pszIf) >= INTNET_MAX_TRUNK_NAME)
747 {
748 RTPrintf("tstIntNet-1: Interface name is too long (max %d chars): %s\n", INTNET_MAX_TRUNK_NAME - 1, pszIf);
749 return 1;
750 }
751 break;
752
753 case 'm':
754 fMacSharing = true;
755 break;
756
757 case 'n':
758 pszNetwork = Value.psz;
759 if (strlen(pszNetwork) >= INTNET_MAX_NETWORK_NAME)
760 {
761 RTPrintf("tstIntNet-1: Network name is too long (max %d chars): %s\n", INTNET_MAX_NETWORK_NAME - 1, pszNetwork);
762 return 1;
763 }
764 break;
765
766 case 'p':
767 fPromiscuous = true;
768 break;
769
770 case 'r':
771 cbRecv = Value.u32;
772 break;
773
774 case 's':
775 cbSend = Value.u32;
776 break;
777
778 case 'S':
779 fSniffer = true;
780 break;
781
782 case 't':
783 if (!*Value.psz)
784 pFileText = NULL;
785 else if (!strcmp(Value.psz, "-"))
786 pFileText = g_pStdOut;
787 else if (!strcmp(Value.psz, "!"))
788 pFileText = g_pStdErr;
789 else
790 {
791 rc = RTStrmOpen(Value.psz, "w", &pFileText);
792 if (RT_FAILURE(rc))
793 {
794 RTPrintf("tstIntNet-1: Failed to creating \"%s\" for writing: %Rrc\n", Value.psz, rc);
795 return 1;
796 }
797 }
798 break;
799
800 case 'x':
801 fXmitTest = true;
802 break;
803
804 case 'P':
805 fPingTest = true;
806 break;
807
808 case 'h':
809 RTPrintf("syntax: tstIntNet-1 <options>\n"
810 "\n"
811 "Options:\n");
812 for (size_t i = 0; i < RT_ELEMENTS(s_aOptions); i++)
813 RTPrintf(" -%c,%s\n", s_aOptions[i].iShort, s_aOptions[i].pszLong);
814 RTPrintf("\n"
815 "Examples:\n"
816 " tstIntNet-1 -r 8192 -s 4096 -xS\n"
817 " tstIntNet-1 -n VBoxNetDhcp -r 4096 -s 4096 -i \"\" -xS\n");
818 return 1;
819
820 case 'V':
821 RTPrintf("$Revision: 103431 $\n");
822 return 0;
823
824 default:
825 return RTGetOptPrintError(ch, &Value);
826 }
827
828 RTPrintf("tstIntNet-1: TESTING...\n");
829
830 /*
831 * Open the session, load ring-0 and issue the request.
832 */
833 PSUPDRVSESSION pSession;
834 rc = SUPR3Init(&pSession);
835 if (RT_FAILURE(rc))
836 {
837 RTPrintf("tstIntNet-1: SUPR3Init -> %Rrc\n", rc);
838 return 1;
839 }
840
841 static const char s_szVmmR0[] = "/../VMMR0.r0";
842 char szPath[RTPATH_MAX];
843 rc = RTPathExecDir(szPath, sizeof(szPath) - sizeof(s_szVmmR0));
844 if (RT_FAILURE(rc))
845 {
846 RTPrintf("tstIntNet-1: RTPathExecDir -> %Rrc\n", rc);
847 return 1;
848 }
849 memcpy(strchr(szPath, '\0'), s_szVmmR0, sizeof(s_szVmmR0));
850
851 char szAbsPath[RTPATH_MAX];
852 rc = RTPathAbs(szPath, szAbsPath, sizeof(szAbsPath));
853 if (RT_FAILURE(rc))
854 {
855 RTPrintf("tstIntNet-1: RTPathAbs -> %Rrc\n", rc);
856 return 1;
857 }
858
859 rc = SUPR3LoadVMM(szAbsPath, NULL);
860 if (RT_FAILURE(rc))
861 {
862 RTPrintf("tstIntNet-1: SUPR3LoadVMM(\"%s\") -> %Rrc\n", szAbsPath, rc);
863 return 1;
864 }
865
866 /*
867 * Create the request, picking the network and trunk names from argv[2]
868 * and argv[1] if present.
869 */
870 INTNETOPENREQ OpenReq;
871 OpenReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
872 OpenReq.Hdr.cbReq = sizeof(OpenReq);
873 OpenReq.pSession = pSession;
874 RTStrCopy(OpenReq.szNetwork, sizeof(OpenReq.szNetwork), pszNetwork);
875 RTStrCopy(OpenReq.szTrunk, sizeof(OpenReq.szTrunk), pszIf);
876 OpenReq.enmTrunkType = *pszIf ? kIntNetTrunkType_NetFlt : kIntNetTrunkType_WhateverNone;
877 OpenReq.fFlags = fMacSharing ? INTNET_OPEN_FLAGS_SHARED_MAC_ON_WIRE : 0;
878 OpenReq.cbSend = cbSend;
879 OpenReq.cbRecv = cbRecv;
880 OpenReq.hIf = INTNET_HANDLE_INVALID;
881
882 /*
883 * Issue the request.
884 */
885 RTPrintf("tstIntNet-1: attempting to open/create network \"%s\" with NetFlt trunk \"%s\"...\n",
886 OpenReq.szNetwork, OpenReq.szTrunk);
887 RTStrmFlush(g_pStdOut);
888 rc = SUPR3CallVMMR0Ex(NIL_RTR0PTR, NIL_VMCPUID, VMMR0_DO_INTNET_OPEN, 0, &OpenReq.Hdr);
889 if (RT_SUCCESS(rc))
890 {
891 RTPrintf("tstIntNet-1: successfully opened/created \"%s\" with NetFlt trunk \"%s\" - hIf=%#x\n",
892 OpenReq.szNetwork, OpenReq.szTrunk, OpenReq.hIf);
893 RTStrmFlush(g_pStdOut);
894
895 /*
896 * Get the ring-3 address of the shared interface buffer.
897 */
898 INTNETIFGETBUFFERPTRSREQ GetBufferPtrsReq;
899 GetBufferPtrsReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
900 GetBufferPtrsReq.Hdr.cbReq = sizeof(GetBufferPtrsReq);
901 GetBufferPtrsReq.pSession = pSession;
902 GetBufferPtrsReq.hIf = OpenReq.hIf;
903 GetBufferPtrsReq.pRing3Buf = NULL;
904 GetBufferPtrsReq.pRing0Buf = NIL_RTR0PTR;
905 rc = SUPR3CallVMMR0Ex(NIL_RTR0PTR, NIL_VMCPUID, VMMR0_DO_INTNET_IF_GET_BUFFER_PTRS, 0, &GetBufferPtrsReq.Hdr);
906 if (RT_SUCCESS(rc))
907 {
908 PINTNETBUF pBuf = GetBufferPtrsReq.pRing3Buf;
909 RTPrintf("tstIntNet-1: pBuf=%p cbBuf=%d cbSend=%d cbRecv=%d\n",
910 pBuf, pBuf->cbBuf, pBuf->cbSend, pBuf->cbRecv);
911 RTStrmFlush(g_pStdOut);
912 if (fPromiscuous)
913 {
914 INTNETIFSETPROMISCUOUSMODEREQ PromiscReq;
915 PromiscReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
916 PromiscReq.Hdr.cbReq = sizeof(PromiscReq);
917 PromiscReq.pSession = pSession;
918 PromiscReq.hIf = OpenReq.hIf;
919 PromiscReq.fPromiscuous = true;
920 rc = SUPR3CallVMMR0Ex(NIL_RTR0PTR, NIL_VMCPUID, VMMR0_DO_INTNET_IF_SET_PROMISCUOUS_MODE, 0, &PromiscReq.Hdr);
921 if (RT_SUCCESS(rc))
922 RTPrintf("tstIntNet-1: interface in promiscuous mode\n");
923 }
924 if (RT_SUCCESS(rc))
925 {
926 /*
927 * Activate the interface.
928 */
929 INTNETIFSETACTIVEREQ ActiveReq;
930 ActiveReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
931 ActiveReq.Hdr.cbReq = sizeof(ActiveReq);
932 ActiveReq.pSession = pSession;
933 ActiveReq.hIf = OpenReq.hIf;
934 ActiveReq.fActive = true;
935 rc = SUPR3CallVMMR0Ex(NIL_RTR0PTR, NIL_VMCPUID, VMMR0_DO_INTNET_IF_SET_ACTIVE, 0, &ActiveReq.Hdr);
936 if (RT_SUCCESS(rc))
937 {
938 /*
939 * Start the stop watch, init the pcap file.
940 */
941 g_StartTS = RTTimeNanoTS();
942 if (pFileRaw)
943 PcapStreamHdr(pFileRaw, g_StartTS);
944
945 /*
946 * Do the transmit test first and so we can sniff for the response.
947 */
948 if (fXmitTest)
949 doXmitTest(OpenReq.hIf, pSession, pBuf, &SrcMac, pFileRaw, pFileText);
950
951 if (fPingTest)
952 doPingTest(OpenReq.hIf, pSession, pBuf, &SrcMac, pFileRaw, pFileText);
953
954 /*
955 * Either enter sniffing mode or do a timeout thing.
956 */
957 if (fSniffer)
958 {
959 doPacketSniffing(OpenReq.hIf, pSession, pBuf, cMillies, pFileRaw, pFileText, &SrcMac);
960 if ( fXmitTest
961 && !g_fDhcpReply)
962 {
963 RTPrintf("tstIntNet-1: Error! The DHCP server didn't reply... (Perhaps you don't have one?)\n");
964 g_cErrors++;
965 }
966
967 if ( fPingTest
968 && !g_fPingReply)
969 {
970 RTPrintf("tstIntNet-1: Error! No reply for ping request...\n");
971 g_cErrors++;
972 }
973 }
974 else
975 RTThreadSleep(cMillies);
976 }
977 else
978 {
979 RTPrintf("tstIntNet-1: SUPR3CallVMMR0Ex(,VMMR0_DO_INTNET_IF_SET_PROMISCUOUS_MODE,) failed, rc=%Rrc\n", rc);
980 g_cErrors++;
981 }
982 }
983 else
984 {
985 RTPrintf("tstIntNet-1: SUPR3CallVMMR0Ex(,VMMR0_DO_INTNET_IF_SET_PROMISCUOUS_MODE,) failed, rc=%Rrc\n", rc);
986 g_cErrors++;
987 }
988 }
989 else
990 {
991 RTPrintf("tstIntNet-1: SUPR3CallVMMR0Ex(,VMMR0_DO_INTNET_IF_GET_BUFFER_PTRS,) failed, rc=%Rrc\n", rc);
992 g_cErrors++;
993 }
994 }
995 else
996 {
997 RTPrintf("tstIntNet-1: SUPR3CallVMMR0Ex(,VMMR0_DO_INTNET_OPEN,) failed, rc=%Rrc\n", rc);
998 g_cErrors++;
999 }
1000
1001 SUPR3Term(false /*fForced*/);
1002
1003 /* close open files */
1004 if (pFileRaw)
1005 RTStrmClose(pFileRaw);
1006 if (pFileText && pFileText != g_pStdErr && pFileText != g_pStdOut)
1007 RTStrmClose(pFileText);
1008
1009 /*
1010 * Summary.
1011 */
1012 if (!g_cErrors)
1013 RTPrintf("tstIntNet-1: SUCCESS\n");
1014 else
1015 RTPrintf("tstIntNet-1: FAILURE - %d errors\n", g_cErrors);
1016
1017 return !!g_cErrors;
1018}
1019
1020
1021#if !defined(VBOX_WITH_HARDENING) || !defined(RT_OS_WINDOWS)
1022/**
1023 * Main entry point.
1024 */
1025int main(int argc, char **argv, char **envp)
1026{
1027 return TrustedMain(argc, argv, envp);
1028}
1029#endif
1030
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle
ContactPrivacy/Do Not Sell My InfoTerms of Use