VirtualBox

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

Last change on this file since 89363 was 86535, checked in by vboxsync, 4 years ago

pdmnetinline.h: Some adjustments to bugref:9764

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