VirtualBox

source: vbox/trunk/include/VBox/vmm/pdmnetinline.h@ 97150

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

scm copyright and license note update

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 32.7 KB
Line 
1/** @file
2 * PDM - Networking Helpers, Inlined Code. (DEV,++)
3 *
4 * This is all inlined because it's too tedious to create 2-3 libraries to
5 * contain it all (same bad excuse as for intnetinline.h).
6 */
7
8/*
9 * Copyright (C) 2010-2022 Oracle and/or its affiliates.
10 *
11 * This file is part of VirtualBox base platform packages, as
12 * available from https://www.virtualbox.org.
13 *
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * as published by the Free Software Foundation, in version 3 of the
17 * License.
18 *
19 * This program is distributed in the hope that it will be useful, but
20 * WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with this program; if not, see <https://www.gnu.org/licenses>.
26 *
27 * The contents of this file may alternatively be used under the terms
28 * of the Common Development and Distribution License Version 1.0
29 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
30 * in the VirtualBox distribution, in which case the provisions of the
31 * CDDL are applicable instead of those of the GPL.
32 *
33 * You may elect to license modified versions of this file under the
34 * terms and conditions of either the GPL or the CDDL or both.
35 *
36 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
37 */
38
39#ifndef VBOX_INCLUDED_vmm_pdmnetinline_h
40#define VBOX_INCLUDED_vmm_pdmnetinline_h
41#ifndef RT_WITHOUT_PRAGMA_ONCE
42# pragma once
43#endif
44
45
46/*******************************************************************************
47* Header Files *
48*******************************************************************************/
49#include <VBox/log.h>
50#include <VBox/types.h>
51#include <iprt/asm.h>
52#include <iprt/assert.h>
53#include <iprt/net.h>
54#include <iprt/string.h>
55
56
57/** @defgroup grp_pdm_net_inline The PDM Networking Helper APIs
58 * @ingroup grp_pdm
59 * @{
60 */
61
62
63/**
64 * Checksum type.
65 */
66typedef enum PDMNETCSUMTYPE
67{
68 /** No checksum. */
69 PDMNETCSUMTYPE_NONE = 0,
70 /** Normal TCP checksum. */
71 PDMNETCSUMTYPE_COMPLETE,
72 /** Checksum on pseudo header (used with GSO). */
73 PDMNETCSUMTYPE_PSEUDO,
74 /** The usual 32-bit hack. */
75 PDMNETCSUMTYPE_32_BIT_HACK = 0x7fffffff
76} PDMNETCSUMTYPE;
77
78
79/**
80 * Validates the GSO context.
81 *
82 * @returns true if valid, false if not (not asserted or logged).
83 * @param pGso The GSO context.
84 * @param cbGsoMax The max size of the GSO context.
85 * @param cbFrame The max size of the GSO frame (use to validate
86 * the MSS).
87 */
88DECLINLINE(bool) PDMNetGsoIsValid(PCPDMNETWORKGSO pGso, size_t cbGsoMax, size_t cbFrame)
89{
90 PDMNETWORKGSOTYPE enmType;
91
92 if (RT_LIKELY(cbGsoMax >= sizeof(*pGso)))
93 { /* likely */ } else return false;
94
95 enmType = (PDMNETWORKGSOTYPE)pGso->u8Type;
96 if (RT_LIKELY( enmType > PDMNETWORKGSOTYPE_INVALID && enmType < PDMNETWORKGSOTYPE_END ))
97 { /* likely */ } else return false;
98
99 /* all types requires both headers. */
100 if (RT_LIKELY( pGso->offHdr1 >= sizeof(RTNETETHERHDR) ))
101 { /* likely */ } else return false;
102 if (RT_LIKELY( pGso->offHdr2 > pGso->offHdr1 ))
103 { /* likely */ } else return false;
104 if (RT_LIKELY( pGso->cbHdrsTotal > pGso->offHdr2 ))
105 { /* likely */ } else return false;
106
107 /* min size of the 1st header(s). */
108 switch (enmType)
109 {
110 case PDMNETWORKGSOTYPE_IPV4_TCP:
111 case PDMNETWORKGSOTYPE_IPV4_UDP:
112 if (RT_LIKELY( (unsigned)pGso->offHdr2 - pGso->offHdr1 >= RTNETIPV4_MIN_LEN ))
113 { /* likely */ } else return false;
114 break;
115 case PDMNETWORKGSOTYPE_IPV6_TCP:
116 case PDMNETWORKGSOTYPE_IPV6_UDP:
117 if (RT_LIKELY( (unsigned)pGso->offHdr2 - pGso->offHdr1 >= RTNETIPV6_MIN_LEN ))
118 { /* likely */ } else return false;
119 break;
120 case PDMNETWORKGSOTYPE_IPV4_IPV6_TCP:
121 case PDMNETWORKGSOTYPE_IPV4_IPV6_UDP:
122 if (RT_LIKELY( (unsigned)pGso->offHdr2 - pGso->offHdr1 >= RTNETIPV4_MIN_LEN + RTNETIPV6_MIN_LEN ))
123 { /* likely */ } else return false;
124 break;
125 case PDMNETWORKGSOTYPE_INVALID:
126 case PDMNETWORKGSOTYPE_END:
127 break;
128 /* no default case! want gcc warnings. */
129 }
130
131 /* min size of the 2nd header. */
132 switch (enmType)
133 {
134 case PDMNETWORKGSOTYPE_IPV4_TCP:
135 case PDMNETWORKGSOTYPE_IPV6_TCP:
136 case PDMNETWORKGSOTYPE_IPV4_IPV6_TCP:
137 if (RT_LIKELY( (unsigned)pGso->cbHdrsTotal - pGso->offHdr2 >= RTNETTCP_MIN_LEN ))
138 { /* likely */ } else return false;
139 break;
140 case PDMNETWORKGSOTYPE_IPV4_UDP:
141 case PDMNETWORKGSOTYPE_IPV6_UDP:
142 case PDMNETWORKGSOTYPE_IPV4_IPV6_UDP:
143 if (RT_LIKELY( (unsigned)pGso->cbHdrsTotal - pGso->offHdr2 >= RTNETUDP_MIN_LEN ))
144 { /* likely */ } else return false;
145 break;
146 case PDMNETWORKGSOTYPE_INVALID:
147 case PDMNETWORKGSOTYPE_END:
148 break;
149 /* no default case! want gcc warnings. */
150 }
151
152 /* There must be at more than one segment. */
153 if (RT_LIKELY( cbFrame > pGso->cbHdrsTotal ))
154 { /* likely */ } else return false;
155 if (RT_LIKELY( cbFrame - pGso->cbHdrsTotal >= pGso->cbMaxSeg ))
156 { /* likely */ } else return false;
157
158 /* Make sure the segment size is enough to fit a UDP header. */
159 if (RT_LIKELY(enmType != PDMNETWORKGSOTYPE_IPV4_UDP || pGso->cbMaxSeg >= RTNETUDP_MIN_LEN))
160 { /* likely */ } else return false;
161
162 /* Make sure the segment size is not zero. */
163 if (RT_LIKELY(pGso->cbMaxSeg > 0))
164 { /* likely */ } else return false;
165
166 return true;
167}
168
169
170/**
171 * Returns the length of header for a particular segment/fragment.
172 *
173 * We cannot simply treat UDP header as a part of payload because we do not
174 * want to modify the payload but still need to modify the checksum field in
175 * UDP header. So we want to include UDP header when calculating the length
176 * of headers in the first segment getting it copied to a temporary buffer
177 * along with other headers.
178 *
179 * @returns Length of headers (including UDP header for the first fragment).
180 * @param pGso The GSO context.
181 * @param iSeg The segment index.
182 */
183DECLINLINE(uint8_t) pdmNetSegHdrLen(PCPDMNETWORKGSO pGso, uint32_t iSeg)
184{
185 return iSeg ? pGso->cbHdrsSeg : pGso->cbHdrsTotal;
186}
187
188/**
189 * Returns the length of payload for a particular segment/fragment.
190 *
191 * The first segment does not contain UDP header. The size of UDP header is
192 * determined as the difference between the total headers size and the size
193 * used during segmentation.
194 *
195 * @returns Length of payload (including UDP header for the first fragment).
196 * @param pGso The GSO context.
197 * @param iSeg The segment that we're carving out (0-based).
198 * @param cSegs The number of segments in the GSO frame.
199 * @param cbFrame The size of the GSO frame.
200 */
201DECLINLINE(uint32_t) pdmNetSegPayloadLen(PCPDMNETWORKGSO pGso, uint32_t iSeg, uint32_t cSegs, uint32_t cbFrame)
202{
203 if (iSeg + 1 == cSegs)
204 return cbFrame - iSeg * pGso->cbMaxSeg - pdmNetSegHdrLen(pGso, iSeg);
205 return pGso->cbMaxSeg - (iSeg ? 0 : pGso->cbHdrsTotal - pGso->cbHdrsSeg);
206}
207
208/**
209 * Calculates the number of segments a GSO frame will be segmented into.
210 *
211 * @returns Segment count.
212 * @param pGso The GSO context.
213 * @param cbFrame The GSO frame size (header proto + payload).
214 */
215DECLINLINE(uint32_t) PDMNetGsoCalcSegmentCount(PCPDMNETWORKGSO pGso, size_t cbFrame)
216{
217 size_t cbPayload;
218 Assert(PDMNetGsoIsValid(pGso, sizeof(*pGso), cbFrame));
219 cbPayload = cbFrame - pGso->cbHdrsSeg;
220 return (uint32_t)((cbPayload + pGso->cbMaxSeg - 1) / pGso->cbMaxSeg);
221}
222
223
224/**
225 * Used to find the IPv6 header when handling 4to6 tunneling.
226 *
227 * @returns Offset of the IPv6 header.
228 * @param pbSegHdrs The headers / frame start.
229 * @param offIPv4Hdr The offset of the IPv4 header.
230 */
231DECLINLINE(uint8_t) pgmNetGsoCalcIpv6Offset(uint8_t *pbSegHdrs, uint8_t offIPv4Hdr)
232{
233 PCRTNETIPV4 pIPv4Hdr = (PCRTNETIPV4)&pbSegHdrs[offIPv4Hdr];
234 return offIPv4Hdr + pIPv4Hdr->ip_hl * 4;
235}
236
237
238/**
239 * Update an UDP header after carving out a segment
240 *
241 * @param u32PseudoSum The pseudo checksum.
242 * @param pbSegHdrs Pointer to the header bytes / frame start.
243 * @param offUdpHdr The offset into @a pbSegHdrs of the UDP header.
244 * @param pbPayload Pointer to the payload bytes.
245 * @param cbPayload The amount of payload.
246 * @param cbHdrs The size of all the headers.
247 * @param enmCsumType Whether to checksum the payload, the pseudo
248 * header or nothing.
249 * @internal
250 */
251DECLINLINE(void) pdmNetGsoUpdateUdpHdr(uint32_t u32PseudoSum, uint8_t *pbSegHdrs, uint8_t offUdpHdr,
252 uint8_t const *pbPayload, uint32_t cbPayload, uint8_t cbHdrs,
253 PDMNETCSUMTYPE enmCsumType)
254{
255 PRTNETUDP pUdpHdr = (PRTNETUDP)&pbSegHdrs[offUdpHdr];
256 pUdpHdr->uh_ulen = RT_H2N_U16(cbPayload + cbHdrs - offUdpHdr);
257 switch (enmCsumType)
258 {
259 case PDMNETCSUMTYPE_NONE:
260 pUdpHdr->uh_sum = 0;
261 break;
262 case PDMNETCSUMTYPE_COMPLETE:
263 pUdpHdr->uh_sum = RTNetUDPChecksum(u32PseudoSum, pUdpHdr);
264 break;
265 case PDMNETCSUMTYPE_PSEUDO:
266 pUdpHdr->uh_sum = ~RTNetIPv4FinalizeChecksum(u32PseudoSum);
267 break;
268 default:
269 NOREF(pbPayload);
270 AssertFailed();
271 break;
272 }
273}
274
275
276/**
277 * Update an UDP header after carving out an IP fragment
278 *
279 * @param u32PseudoSum The pseudo checksum.
280 * @param pbSegHdrs Pointer to the header bytes copy
281 * @param pbFrame Pointer to the frame start.
282 * @param offUdpHdr The offset into @a pbSegHdrs of the UDP header.
283 *
284 * @internal
285 */
286DECLINLINE(void) pdmNetGsoUpdateUdpHdrUfo(uint32_t u32PseudoSum, uint8_t *pbSegHdrs, const uint8_t *pbFrame, uint8_t offUdpHdr)
287{
288 PCRTNETUDP pcUdpHdrOrig = (PCRTNETUDP)&pbFrame[offUdpHdr];
289 PRTNETUDP pUdpHdr = (PRTNETUDP)&pbSegHdrs[offUdpHdr];
290 pUdpHdr->uh_sum = RTNetUDPChecksum(u32PseudoSum, pcUdpHdrOrig);
291}
292
293
294/**
295 * Update a TCP header after carving out a segment.
296 *
297 * @param u32PseudoSum The pseudo checksum.
298 * @param pbSegHdrs Pointer to the header bytes / frame start.
299 * @param offTcpHdr The offset into @a pbSegHdrs of the TCP header.
300 * @param pbPayload Pointer to the payload bytes.
301 * @param cbPayload The amount of payload.
302 * @param offPayload The offset into the payload that we're splitting
303 * up. We're ASSUMING that the payload follows
304 * immediately after the TCP header w/ options.
305 * @param cbHdrs The size of all the headers.
306 * @param fLastSeg Set if this is the last segment.
307 * @param enmCsumType Whether to checksum the payload, the pseudo
308 * header or nothing.
309 * @internal
310 */
311DECLINLINE(void) pdmNetGsoUpdateTcpHdr(uint32_t u32PseudoSum, uint8_t *pbSegHdrs, uint8_t offTcpHdr,
312 uint8_t const *pbPayload, uint32_t cbPayload, uint32_t offPayload, uint8_t cbHdrs,
313 bool fLastSeg, PDMNETCSUMTYPE enmCsumType)
314{
315 PRTNETTCP pTcpHdr = (PRTNETTCP)&pbSegHdrs[offTcpHdr];
316 pTcpHdr->th_seq = RT_H2N_U32(RT_N2H_U32(pTcpHdr->th_seq) + offPayload);
317 if (!fLastSeg)
318 pTcpHdr->th_flags &= ~(RTNETTCP_F_FIN | RTNETTCP_F_PSH);
319 switch (enmCsumType)
320 {
321 case PDMNETCSUMTYPE_NONE:
322 pTcpHdr->th_sum = 0;
323 break;
324 case PDMNETCSUMTYPE_COMPLETE:
325 pTcpHdr->th_sum = RTNetTCPChecksum(u32PseudoSum, pTcpHdr, pbPayload, cbPayload);
326 break;
327 case PDMNETCSUMTYPE_PSEUDO:
328 pTcpHdr->th_sum = ~RTNetIPv4FinalizeChecksum(u32PseudoSum);
329 break;
330 default:
331 NOREF(cbHdrs);
332 AssertFailed();
333 break;
334 }
335}
336
337
338/**
339 * Updates a IPv6 header after carving out a segment.
340 *
341 * @returns 32-bit intermediary checksum value for the pseudo header.
342 * @param pbSegHdrs Pointer to the header bytes.
343 * @param offIpHdr The offset into @a pbSegHdrs of the IP header.
344 * @param cbSegPayload The amount of segmented payload. Not to be
345 * confused with the IP payload.
346 * @param cbHdrs The size of all the headers.
347 * @param offPktHdr Offset of the protocol packet header. For the
348 * pseudo header checksum calulation.
349 * @param bProtocol The protocol type. For the pseudo header.
350 * @internal
351 */
352DECLINLINE(uint32_t) pdmNetGsoUpdateIPv6Hdr(uint8_t *pbSegHdrs, uint8_t offIpHdr, uint32_t cbSegPayload, uint8_t cbHdrs,
353 uint8_t offPktHdr, uint8_t bProtocol)
354{
355 PRTNETIPV6 pIpHdr = (PRTNETIPV6)&pbSegHdrs[offIpHdr];
356 uint16_t cbPayload = (uint16_t)(cbHdrs - (offIpHdr + sizeof(RTNETIPV6)) + cbSegPayload);
357 pIpHdr->ip6_plen = RT_H2N_U16(cbPayload);
358 return RTNetIPv6PseudoChecksumEx(pIpHdr, bProtocol, (uint16_t)(cbHdrs - offPktHdr + cbSegPayload));
359}
360
361
362/**
363 * Updates a IPv4 header after carving out a segment.
364 *
365 * @returns 32-bit intermediary checksum value for the pseudo header.
366 * @param pbSegHdrs Pointer to the header bytes.
367 * @param offIpHdr The offset into @a pbSegHdrs of the IP header.
368 * @param cbSegPayload The amount of segmented payload.
369 * @param iSeg The segment index.
370 * @param cbHdrs The size of all the headers.
371 * @internal
372 */
373DECLINLINE(uint32_t) pdmNetGsoUpdateIPv4Hdr(uint8_t *pbSegHdrs, uint8_t offIpHdr, uint32_t cbSegPayload,
374 uint32_t iSeg, uint8_t cbHdrs)
375{
376 PRTNETIPV4 pIpHdr = (PRTNETIPV4)&pbSegHdrs[offIpHdr];
377 pIpHdr->ip_len = RT_H2N_U16(cbHdrs - offIpHdr + cbSegPayload);
378 pIpHdr->ip_id = RT_H2N_U16(RT_N2H_U16(pIpHdr->ip_id) + iSeg);
379 pIpHdr->ip_sum = RTNetIPv4HdrChecksum(pIpHdr);
380 return RTNetIPv4PseudoChecksum(pIpHdr);
381}
382
383
384/**
385 * Updates a IPv4 header after carving out an IP fragment.
386 *
387 * @param pbSegHdrs Pointer to the header bytes.
388 * @param offIpHdr The offset into @a pbSegHdrs of the IP header.
389 * @param cbSegPayload The amount of segmented payload.
390 * @param offFragment The offset of this fragment for reassembly.
391 * @param cbHdrs The size of all the headers.
392 * @param fLastFragment True if this is the last fragment of datagram.
393 * @internal
394 */
395DECLINLINE(void) pdmNetGsoUpdateIPv4HdrUfo(uint8_t *pbSegHdrs, uint8_t offIpHdr, uint32_t cbSegPayload,
396 uint32_t offFragment, uint8_t cbHdrs, bool fLastFragment)
397{
398 PRTNETIPV4 pIpHdr = (PRTNETIPV4)&pbSegHdrs[offIpHdr];
399 pIpHdr->ip_len = RT_H2N_U16(cbHdrs - offIpHdr + cbSegPayload);
400 pIpHdr->ip_off = RT_H2N_U16((offFragment / 8) | (fLastFragment ? 0 : RTNETIPV4_FLAGS_MF));
401 pIpHdr->ip_sum = RTNetIPv4HdrChecksum(pIpHdr);
402}
403
404
405/**
406 * Carves out the specified segment in a destructive manner.
407 *
408 * This is for sequentially carving out segments and pushing them along for
409 * processing or sending. To avoid allocating a temporary buffer for
410 * constructing the segment in, we trash the previous frame by putting the
411 * header at the end of it.
412 *
413 * @returns Pointer to the segment frame that we've carved out.
414 * @param pGso The GSO context data.
415 * @param pbFrame Pointer to the GSO frame.
416 * @param cbFrame The size of the GSO frame.
417 * @param pbHdrScatch Pointer to a pGso->cbHdrs sized area where we
418 * can save the original header prototypes on the
419 * first call (@a iSeg is 0) and retrieve it on
420 * susequent calls. (Just use a 256 bytes
421 * buffer to make life easy.)
422 * @param iSeg The segment that we're carving out (0-based).
423 * @param cSegs The number of segments in the GSO frame. Use
424 * PDMNetGsoCalcSegmentCount to find this.
425 * @param pcbSegFrame Where to return the size of the returned segment
426 * frame.
427 */
428DECLINLINE(void *) PDMNetGsoCarveSegmentQD(PCPDMNETWORKGSO pGso, uint8_t *pbFrame, size_t cbFrame, uint8_t *pbHdrScatch,
429 uint32_t iSeg, uint32_t cSegs, uint32_t *pcbSegFrame)
430{
431 /*
432 * Figure out where the payload is and where the header starts before we
433 * do the protocol specific carving.
434 *
435 * UDP GSO uses IPv4 fragmentation, meaning that UDP header is present in
436 * the first fragment only. When computing the total frame size of the
437 * first fragment we need to use PDMNETWORKGSO::cbHdrsTotal instead of
438 * PDMNETWORKGSO::cbHdrsSeg. In case of TCP GSO both cbHdrsTotal and
439 * cbHdrsSeg have the same value, so it will work as well.
440 */
441 uint8_t * const pbSegHdrs = pbFrame + pGso->cbMaxSeg * iSeg;
442 uint8_t * const pbSegPayload = pbSegHdrs + pGso->cbHdrsSeg;
443 uint32_t const cbSegPayload = pdmNetSegPayloadLen(pGso, iSeg, cSegs, (uint32_t)cbFrame);
444 uint32_t const cbSegFrame = cbSegPayload + (iSeg ? pGso->cbHdrsSeg : pGso->cbHdrsTotal);
445
446 /*
447 * Check assumptions (doing it after declaring the variables because of C).
448 */
449 Assert(iSeg < cSegs);
450 Assert(cSegs == PDMNetGsoCalcSegmentCount(pGso, cbFrame));
451 Assert(PDMNetGsoIsValid(pGso, sizeof(*pGso), cbFrame));
452
453 /*
454 * Copy the header and do the protocol specific massaging of it.
455 */
456 if (iSeg != 0)
457 memcpy(pbSegHdrs, pbHdrScatch, pGso->cbHdrsSeg);
458 else
459 memcpy(pbHdrScatch, pbSegHdrs, pGso->cbHdrsSeg); /* There is no need to save UDP header */
460
461 switch ((PDMNETWORKGSOTYPE)pGso->u8Type)
462 {
463 case PDMNETWORKGSOTYPE_IPV4_TCP:
464 pdmNetGsoUpdateTcpHdr(pdmNetGsoUpdateIPv4Hdr(pbSegHdrs, pGso->offHdr1, cbSegPayload, iSeg, pGso->cbHdrsSeg),
465 pbSegHdrs, pGso->offHdr2, pbSegPayload, cbSegPayload, iSeg * pGso->cbMaxSeg,
466 pGso->cbHdrsSeg, iSeg + 1 == cSegs, PDMNETCSUMTYPE_COMPLETE);
467 break;
468 case PDMNETWORKGSOTYPE_IPV4_UDP:
469 if (iSeg == 0)
470 {
471 /* uh_ulen shall not exceed cbFrame - pGso->offHdr2 (offset of UDP header) */
472 PRTNETUDP pUdpHdr = (PRTNETUDP)&pbFrame[pGso->offHdr2];
473 Assert(pGso->offHdr2 + RT_UOFFSET_AFTER(RTNETUDP, uh_ulen) <= cbFrame);
474 if ((unsigned)(pGso->offHdr2 + RT_BE2H_U16(pUdpHdr->uh_ulen)) > cbFrame)
475 {
476 size_t cbUdp = cbFrame - pGso->offHdr2;
477 if (cbUdp >= UINT16_MAX)
478 pUdpHdr->uh_ulen = UINT16_MAX;
479 else
480 pUdpHdr->uh_ulen = RT_H2BE_U16((uint16_t)cbUdp);
481 }
482 /* uh_ulen shall be at least the size of UDP header */
483 if (RT_BE2H_U16(pUdpHdr->uh_ulen) < sizeof(RTNETUDP))
484 pUdpHdr->uh_ulen = RT_H2BE_U16(sizeof(RTNETUDP));
485 pdmNetGsoUpdateUdpHdrUfo(RTNetIPv4PseudoChecksum((PRTNETIPV4)&pbFrame[pGso->offHdr1]),
486 pbSegHdrs, pbFrame, pGso->offHdr2);
487 }
488 pdmNetGsoUpdateIPv4HdrUfo(pbSegHdrs, pGso->offHdr1, cbSegPayload, iSeg * pGso->cbMaxSeg,
489 pdmNetSegHdrLen(pGso, iSeg), iSeg + 1 == cSegs);
490 break;
491 case PDMNETWORKGSOTYPE_IPV6_TCP:
492 pdmNetGsoUpdateTcpHdr(pdmNetGsoUpdateIPv6Hdr(pbSegHdrs, pGso->offHdr1, cbSegPayload, pGso->cbHdrsSeg,
493 pGso->offHdr2, RTNETIPV4_PROT_TCP),
494 pbSegHdrs, pGso->offHdr2, pbSegPayload, cbSegPayload, iSeg * pGso->cbMaxSeg,
495 pGso->cbHdrsSeg, iSeg + 1 == cSegs, PDMNETCSUMTYPE_COMPLETE);
496 break;
497 case PDMNETWORKGSOTYPE_IPV6_UDP:
498 pdmNetGsoUpdateUdpHdr(pdmNetGsoUpdateIPv6Hdr(pbSegHdrs, pGso->offHdr1, cbSegPayload, pGso->cbHdrsSeg,
499 pGso->offHdr2, RTNETIPV4_PROT_UDP),
500 pbSegHdrs, pGso->offHdr2, pbSegPayload, cbSegPayload, pGso->cbHdrsSeg, PDMNETCSUMTYPE_COMPLETE);
501 break;
502 case PDMNETWORKGSOTYPE_IPV4_IPV6_TCP:
503 pdmNetGsoUpdateIPv4Hdr(pbSegHdrs, pGso->offHdr1, cbSegPayload, iSeg, pGso->cbHdrsSeg);
504 pdmNetGsoUpdateTcpHdr(pdmNetGsoUpdateIPv6Hdr(pbSegHdrs, pgmNetGsoCalcIpv6Offset(pbSegHdrs, pGso->offHdr1),
505 cbSegPayload, pGso->cbHdrsSeg, pGso->offHdr2, RTNETIPV4_PROT_TCP),
506 pbSegHdrs, pGso->offHdr2, pbSegPayload, cbSegPayload, iSeg * pGso->cbMaxSeg,
507 pGso->cbHdrsSeg, iSeg + 1 == cSegs, PDMNETCSUMTYPE_COMPLETE);
508 break;
509 case PDMNETWORKGSOTYPE_IPV4_IPV6_UDP:
510 pdmNetGsoUpdateIPv4Hdr(pbSegHdrs, pGso->offHdr1, cbSegPayload, iSeg, pGso->cbHdrsSeg);
511 pdmNetGsoUpdateUdpHdr(pdmNetGsoUpdateIPv6Hdr(pbSegHdrs, pgmNetGsoCalcIpv6Offset(pbSegHdrs, pGso->offHdr1),
512 cbSegPayload, pGso->cbHdrsSeg, pGso->offHdr2, RTNETIPV4_PROT_UDP),
513 pbSegHdrs, pGso->offHdr2, pbSegPayload, cbSegPayload, pGso->cbHdrsSeg, PDMNETCSUMTYPE_COMPLETE);
514 break;
515 case PDMNETWORKGSOTYPE_INVALID:
516 case PDMNETWORKGSOTYPE_END:
517 /* no default! wnat gcc warnings. */
518 break;
519 }
520
521 *pcbSegFrame = cbSegFrame;
522 return pbSegHdrs;
523}
524
525
526/**
527 * Carves out the specified segment in a non-destructive manner.
528 *
529 * The segment headers and segment payload is kept separate here. The GSO frame
530 * is still expected to be one linear chunk of data, but we don't modify any of
531 * it.
532 *
533 * @returns The offset into the GSO frame of the payload.
534 * @param pGso The GSO context data.
535 * @param pbFrame Pointer to the GSO frame. Used for retrieving
536 * the header prototype and for checksumming the
537 * payload. The buffer is not modified.
538 * @param cbFrame The size of the GSO frame.
539 * @param iSeg The segment that we're carving out (0-based).
540 * @param cSegs The number of segments in the GSO frame. Use
541 * PDMNetGsoCalcSegmentCount to find this.
542 * @param pbSegHdrs Where to return the headers for the segment
543 * that's been carved out. The buffer must be at
544 * least pGso->cbHdrs in size, using a 256 byte
545 * buffer is a recommended simplification.
546 * @param pcbSegHdrs Where to return the size of the returned
547 * segment headers.
548 * @param pcbSegPayload Where to return the size of the returned
549 * segment payload.
550 */
551DECLINLINE(uint32_t) PDMNetGsoCarveSegment(PCPDMNETWORKGSO pGso, const uint8_t *pbFrame, size_t cbFrame,
552 uint32_t iSeg, uint32_t cSegs, uint8_t *pbSegHdrs,
553 uint32_t *pcbSegHdrs, uint32_t *pcbSegPayload)
554{
555 /*
556 * Figure out where the payload is and where the header starts before we
557 * do the protocol specific carving.
558 */
559 uint32_t const cbSegHdrs = pdmNetSegHdrLen(pGso, iSeg);
560 uint8_t const * const pbSegPayload = pbFrame + cbSegHdrs + iSeg * pGso->cbMaxSeg;
561 uint32_t const cbSegPayload = pdmNetSegPayloadLen(pGso, iSeg, cSegs, (uint32_t)cbFrame);
562
563 /*
564 * Check assumptions (doing it after declaring the variables because of C).
565 */
566 Assert(iSeg < cSegs);
567 Assert(cSegs == PDMNetGsoCalcSegmentCount(pGso, cbFrame));
568 Assert(PDMNetGsoIsValid(pGso, sizeof(*pGso), cbFrame));
569
570 /*
571 * Copy the header and do the protocol specific massaging of it.
572 */
573 memcpy(pbSegHdrs, pbFrame, pGso->cbHdrsTotal); /* include UDP header */
574
575 switch ((PDMNETWORKGSOTYPE)pGso->u8Type)
576 {
577 case PDMNETWORKGSOTYPE_IPV4_TCP:
578 pdmNetGsoUpdateTcpHdr(pdmNetGsoUpdateIPv4Hdr(pbSegHdrs, pGso->offHdr1, cbSegPayload, iSeg, cbSegHdrs),
579 pbSegHdrs, pGso->offHdr2, pbSegPayload, cbSegPayload, iSeg * pGso->cbMaxSeg,
580 cbSegHdrs, iSeg + 1 == cSegs, PDMNETCSUMTYPE_COMPLETE);
581 break;
582 case PDMNETWORKGSOTYPE_IPV4_UDP:
583 if (iSeg == 0)
584 {
585 /* uh_ulen shall not exceed cbFrame - pGso->offHdr2 (offset of UDP header) */
586 PRTNETUDP pUdpHdr = (PRTNETUDP)&pbFrame[pGso->offHdr2];
587 Assert(pGso->offHdr2 + RT_UOFFSET_AFTER(RTNETUDP, uh_ulen) <= cbFrame);
588 if ((unsigned)(pGso->offHdr2 + RT_BE2H_U16(pUdpHdr->uh_ulen)) > cbFrame)
589 {
590 size_t cbUdp = cbFrame - pGso->offHdr2;
591 if (cbUdp >= UINT16_MAX)
592 pUdpHdr->uh_ulen = UINT16_MAX;
593 else
594 pUdpHdr->uh_ulen = RT_H2BE_U16((uint16_t)cbUdp);
595 }
596 /* uh_ulen shall be at least the size of UDP header */
597 if (RT_BE2H_U16(pUdpHdr->uh_ulen) < sizeof(RTNETUDP))
598 pUdpHdr->uh_ulen = RT_H2BE_U16(sizeof(RTNETUDP));
599 pdmNetGsoUpdateUdpHdrUfo(RTNetIPv4PseudoChecksum((PRTNETIPV4)&pbFrame[pGso->offHdr1]),
600 pbSegHdrs, pbFrame, pGso->offHdr2);
601 }
602 pdmNetGsoUpdateIPv4HdrUfo(pbSegHdrs, pGso->offHdr1, cbSegPayload, iSeg * pGso->cbMaxSeg,
603 cbSegHdrs, iSeg + 1 == cSegs);
604 break;
605 case PDMNETWORKGSOTYPE_IPV6_TCP:
606 pdmNetGsoUpdateTcpHdr(pdmNetGsoUpdateIPv6Hdr(pbSegHdrs, pGso->offHdr1, cbSegPayload, cbSegHdrs,
607 pGso->offHdr2, RTNETIPV4_PROT_TCP),
608 pbSegHdrs, pGso->offHdr2, pbSegPayload, cbSegPayload, iSeg * pGso->cbMaxSeg,
609 cbSegHdrs, iSeg + 1 == cSegs, PDMNETCSUMTYPE_COMPLETE);
610 break;
611 case PDMNETWORKGSOTYPE_IPV6_UDP:
612 pdmNetGsoUpdateUdpHdr(pdmNetGsoUpdateIPv6Hdr(pbSegHdrs, pGso->offHdr1, cbSegPayload, cbSegHdrs,
613 pGso->offHdr2, RTNETIPV4_PROT_UDP),
614 pbSegHdrs, pGso->offHdr2, pbSegPayload, cbSegPayload, cbSegHdrs, PDMNETCSUMTYPE_COMPLETE);
615 break;
616 case PDMNETWORKGSOTYPE_IPV4_IPV6_TCP:
617 pdmNetGsoUpdateIPv4Hdr(pbSegHdrs, pGso->offHdr1, cbSegPayload, iSeg, cbSegHdrs);
618 pdmNetGsoUpdateTcpHdr(pdmNetGsoUpdateIPv6Hdr(pbSegHdrs, pgmNetGsoCalcIpv6Offset(pbSegHdrs, pGso->offHdr1),
619 cbSegPayload, cbSegHdrs, pGso->offHdr2, RTNETIPV4_PROT_TCP),
620 pbSegHdrs, pGso->offHdr2, pbSegPayload, cbSegPayload, iSeg * pGso->cbMaxSeg,
621 cbSegHdrs, iSeg + 1 == cSegs, PDMNETCSUMTYPE_COMPLETE);
622 break;
623 case PDMNETWORKGSOTYPE_IPV4_IPV6_UDP:
624 pdmNetGsoUpdateIPv4Hdr(pbSegHdrs, pGso->offHdr1, cbSegPayload, iSeg, cbSegHdrs);
625 pdmNetGsoUpdateUdpHdr(pdmNetGsoUpdateIPv6Hdr(pbSegHdrs, pgmNetGsoCalcIpv6Offset(pbSegHdrs, pGso->offHdr1),
626 cbSegPayload, cbSegHdrs, pGso->offHdr2, RTNETIPV4_PROT_UDP),
627 pbSegHdrs, pGso->offHdr2, pbSegPayload, cbSegPayload, cbSegHdrs, PDMNETCSUMTYPE_COMPLETE);
628 break;
629 case PDMNETWORKGSOTYPE_INVALID:
630 case PDMNETWORKGSOTYPE_END:
631 /* no default! wnat gcc warnings. */
632 break;
633 }
634
635 *pcbSegHdrs = cbSegHdrs;
636 *pcbSegPayload = cbSegPayload;
637 return cbSegHdrs + iSeg * pGso->cbMaxSeg;
638}
639
640
641/**
642 * Prepares the GSO frame for direct use without any segmenting.
643 *
644 * @param pGso The GSO context.
645 * @param pvFrame The frame to prepare.
646 * @param cbFrame The frame size.
647 * @param enmCsumType Whether to checksum the payload, the pseudo
648 * header or nothing.
649 */
650DECLINLINE(void) PDMNetGsoPrepForDirectUse(PCPDMNETWORKGSO pGso, void *pvFrame, size_t cbFrame, PDMNETCSUMTYPE enmCsumType)
651{
652 /*
653 * Figure out where the payload is and where the header starts before we
654 * do the protocol bits.
655 */
656 uint8_t * const pbHdrs = (uint8_t *)pvFrame;
657 uint8_t * const pbPayload = pbHdrs + pGso->cbHdrsTotal;
658 uint32_t const cbFrame32 = (uint32_t)cbFrame;
659 uint32_t const cbPayload = cbFrame32 - pGso->cbHdrsTotal;
660
661 /*
662 * Check assumptions (doing it after declaring the variables because of C).
663 */
664 Assert(PDMNetGsoIsValid(pGso, sizeof(*pGso), cbFrame));
665
666 /*
667 * Get down to busienss.
668 */
669 switch ((PDMNETWORKGSOTYPE)pGso->u8Type)
670 {
671 case PDMNETWORKGSOTYPE_IPV4_TCP:
672 pdmNetGsoUpdateTcpHdr(pdmNetGsoUpdateIPv4Hdr(pbHdrs, pGso->offHdr1, cbFrame32 - pGso->cbHdrsTotal, 0, pGso->cbHdrsTotal),
673 pbHdrs, pGso->offHdr2, pbPayload, cbPayload, 0, pGso->cbHdrsTotal, true, enmCsumType);
674 break;
675 case PDMNETWORKGSOTYPE_IPV4_UDP:
676 pdmNetGsoUpdateUdpHdr(pdmNetGsoUpdateIPv4Hdr(pbHdrs, pGso->offHdr1, cbFrame32 - pGso->cbHdrsTotal, 0, pGso->cbHdrsTotal),
677 pbHdrs, pGso->offHdr2, pbPayload, cbPayload, pGso->cbHdrsTotal, enmCsumType);
678 break;
679 case PDMNETWORKGSOTYPE_IPV6_TCP:
680 pdmNetGsoUpdateTcpHdr(pdmNetGsoUpdateIPv6Hdr(pbHdrs, pGso->offHdr1, cbPayload, pGso->cbHdrsTotal,
681 pGso->offHdr2, RTNETIPV4_PROT_TCP),
682 pbHdrs, pGso->offHdr2, pbPayload, cbPayload, 0, pGso->cbHdrsTotal, true, enmCsumType);
683 break;
684 case PDMNETWORKGSOTYPE_IPV6_UDP:
685 pdmNetGsoUpdateUdpHdr(pdmNetGsoUpdateIPv6Hdr(pbHdrs, pGso->offHdr1, cbPayload, pGso->cbHdrsTotal,
686 pGso->offHdr2, RTNETIPV4_PROT_UDP),
687 pbHdrs, pGso->offHdr2, pbPayload, cbPayload, pGso->cbHdrsTotal, enmCsumType);
688 break;
689 case PDMNETWORKGSOTYPE_IPV4_IPV6_TCP:
690 pdmNetGsoUpdateIPv4Hdr(pbHdrs, pGso->offHdr1, cbPayload, 0, pGso->cbHdrsTotal);
691 pdmNetGsoUpdateTcpHdr(pdmNetGsoUpdateIPv6Hdr(pbHdrs, pgmNetGsoCalcIpv6Offset(pbHdrs, pGso->offHdr1),
692 cbPayload, pGso->cbHdrsTotal, pGso->offHdr2, RTNETIPV4_PROT_TCP),
693 pbHdrs, pGso->offHdr2, pbPayload, cbPayload, 0, pGso->cbHdrsTotal, true, enmCsumType);
694 break;
695 case PDMNETWORKGSOTYPE_IPV4_IPV6_UDP:
696 pdmNetGsoUpdateIPv4Hdr(pbHdrs, pGso->offHdr1, cbPayload, 0, pGso->cbHdrsTotal);
697 pdmNetGsoUpdateUdpHdr(pdmNetGsoUpdateIPv6Hdr(pbHdrs, pgmNetGsoCalcIpv6Offset(pbHdrs, pGso->offHdr1),
698 cbPayload, pGso->cbHdrsTotal, pGso->offHdr2, RTNETIPV4_PROT_UDP),
699 pbHdrs, pGso->offHdr2, pbPayload, cbPayload, pGso->cbHdrsTotal, enmCsumType);
700 break;
701 case PDMNETWORKGSOTYPE_INVALID:
702 case PDMNETWORKGSOTYPE_END:
703 /* no default! wnat gcc warnings. */
704 break;
705 }
706}
707
708
709/**
710 * Gets the GSO type name string.
711 *
712 * @returns Pointer to read only name string.
713 * @param enmType The type.
714 */
715DECLINLINE(const char *) PDMNetGsoTypeName(PDMNETWORKGSOTYPE enmType)
716{
717 switch (enmType)
718 {
719 case PDMNETWORKGSOTYPE_IPV4_TCP: return "TCPv4";
720 case PDMNETWORKGSOTYPE_IPV6_TCP: return "TCPv6";
721 case PDMNETWORKGSOTYPE_IPV4_UDP: return "UDPv4";
722 case PDMNETWORKGSOTYPE_IPV6_UDP: return "UDPv6";
723 case PDMNETWORKGSOTYPE_IPV4_IPV6_TCP: return "4to6TCP";
724 case PDMNETWORKGSOTYPE_IPV4_IPV6_UDP: return "4to6UDP";
725 case PDMNETWORKGSOTYPE_INVALID: return "invalid";
726 case PDMNETWORKGSOTYPE_END: return "end";
727 }
728 return "bad-gso-type";
729}
730
731/** @} */
732
733#endif /* !VBOX_INCLUDED_vmm_pdmnetinline_h */
734
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