VirtualBox

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

Last change on this file since 90175 was 90175, checked in by vboxsync, 3 years ago

Network: (bugref:10024) Fixed UDP header handling when carving segments destructively.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 31.0 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 * UDP GSO uses IPv4 fragmentation, meaning that UDP header is present in
418 * the first fragment only. When computing the total frame size of the
419 * first fragment we need to use PDMNETWORKGSO::cbHdrsTotal instead of
420 * PDMNETWORKGSO::cbHdrsSeg. In case of TCP GSO both cbHdrsTotal and
421 * cbHdrsSeg have the same value, so it will work as well.
422 */
423 uint8_t * const pbSegHdrs = pbFrame + pGso->cbMaxSeg * iSeg;
424 uint8_t * const pbSegPayload = pbSegHdrs + pGso->cbHdrsSeg;
425 uint32_t const cbSegPayload = pdmNetSegPayloadLen(pGso, iSeg, cSegs, (uint32_t)cbFrame);
426 uint32_t const cbSegFrame = cbSegPayload + (iSeg ? pGso->cbHdrsSeg : pGso->cbHdrsTotal);
427
428 /*
429 * Check assumptions (doing it after declaring the variables because of C).
430 */
431 Assert(iSeg < cSegs);
432 Assert(cSegs == PDMNetGsoCalcSegmentCount(pGso, cbFrame));
433 Assert(PDMNetGsoIsValid(pGso, sizeof(*pGso), cbFrame));
434
435 /*
436 * Copy the header and do the protocol specific massaging of it.
437 */
438 if (iSeg != 0)
439 memcpy(pbSegHdrs, pbHdrScatch, pGso->cbHdrsSeg);
440 else
441 memcpy(pbHdrScatch, pbSegHdrs, pGso->cbHdrsSeg); /* There is no need to save UDP header */
442
443 switch ((PDMNETWORKGSOTYPE)pGso->u8Type)
444 {
445 case PDMNETWORKGSOTYPE_IPV4_TCP:
446 pdmNetGsoUpdateTcpHdr(pdmNetGsoUpdateIPv4Hdr(pbSegHdrs, pGso->offHdr1, cbSegPayload, iSeg, pGso->cbHdrsSeg),
447 pbSegHdrs, pGso->offHdr2, pbSegPayload, cbSegPayload, iSeg * pGso->cbMaxSeg,
448 pGso->cbHdrsSeg, iSeg + 1 == cSegs, PDMNETCSUMTYPE_COMPLETE);
449 break;
450 case PDMNETWORKGSOTYPE_IPV4_UDP:
451 if (iSeg == 0)
452 pdmNetGsoUpdateUdpHdrUfo(RTNetIPv4PseudoChecksum((PRTNETIPV4)&pbFrame[pGso->offHdr1]),
453 pbSegHdrs, pbFrame, pGso->offHdr2);
454 pdmNetGsoUpdateIPv4HdrUfo(pbSegHdrs, pGso->offHdr1, cbSegPayload, iSeg * pGso->cbMaxSeg,
455 pdmNetSegHdrLen(pGso, iSeg), iSeg + 1 == cSegs);
456 break;
457 case PDMNETWORKGSOTYPE_IPV6_TCP:
458 pdmNetGsoUpdateTcpHdr(pdmNetGsoUpdateIPv6Hdr(pbSegHdrs, pGso->offHdr1, cbSegPayload, pGso->cbHdrsSeg,
459 pGso->offHdr2, RTNETIPV4_PROT_TCP),
460 pbSegHdrs, pGso->offHdr2, pbSegPayload, cbSegPayload, iSeg * pGso->cbMaxSeg,
461 pGso->cbHdrsSeg, iSeg + 1 == cSegs, PDMNETCSUMTYPE_COMPLETE);
462 break;
463 case PDMNETWORKGSOTYPE_IPV6_UDP:
464 pdmNetGsoUpdateUdpHdr(pdmNetGsoUpdateIPv6Hdr(pbSegHdrs, pGso->offHdr1, cbSegPayload, pGso->cbHdrsSeg,
465 pGso->offHdr2, RTNETIPV4_PROT_UDP),
466 pbSegHdrs, pGso->offHdr2, pbSegPayload, cbSegPayload, pGso->cbHdrsSeg, PDMNETCSUMTYPE_COMPLETE);
467 break;
468 case PDMNETWORKGSOTYPE_IPV4_IPV6_TCP:
469 pdmNetGsoUpdateIPv4Hdr(pbSegHdrs, pGso->offHdr1, cbSegPayload, iSeg, pGso->cbHdrsSeg);
470 pdmNetGsoUpdateTcpHdr(pdmNetGsoUpdateIPv6Hdr(pbSegHdrs, pgmNetGsoCalcIpv6Offset(pbSegHdrs, pGso->offHdr1),
471 cbSegPayload, pGso->cbHdrsSeg, pGso->offHdr2, RTNETIPV4_PROT_TCP),
472 pbSegHdrs, pGso->offHdr2, pbSegPayload, cbSegPayload, iSeg * pGso->cbMaxSeg,
473 pGso->cbHdrsSeg, iSeg + 1 == cSegs, PDMNETCSUMTYPE_COMPLETE);
474 break;
475 case PDMNETWORKGSOTYPE_IPV4_IPV6_UDP:
476 pdmNetGsoUpdateIPv4Hdr(pbSegHdrs, pGso->offHdr1, cbSegPayload, iSeg, pGso->cbHdrsSeg);
477 pdmNetGsoUpdateUdpHdr(pdmNetGsoUpdateIPv6Hdr(pbSegHdrs, pgmNetGsoCalcIpv6Offset(pbSegHdrs, pGso->offHdr1),
478 cbSegPayload, pGso->cbHdrsSeg, pGso->offHdr2, RTNETIPV4_PROT_UDP),
479 pbSegHdrs, pGso->offHdr2, pbSegPayload, cbSegPayload, pGso->cbHdrsSeg, PDMNETCSUMTYPE_COMPLETE);
480 break;
481 case PDMNETWORKGSOTYPE_INVALID:
482 case PDMNETWORKGSOTYPE_END:
483 /* no default! wnat gcc warnings. */
484 break;
485 }
486
487 *pcbSegFrame = cbSegFrame;
488 return pbSegHdrs;
489}
490
491
492/**
493 * Carves out the specified segment in a non-destructive manner.
494 *
495 * The segment headers and segment payload is kept separate here. The GSO frame
496 * is still expected to be one linear chunk of data, but we don't modify any of
497 * it.
498 *
499 * @returns The offset into the GSO frame of the payload.
500 * @param pGso The GSO context data.
501 * @param pbFrame Pointer to the GSO frame. Used for retrieving
502 * the header prototype and for checksumming the
503 * payload. The buffer is not modified.
504 * @param cbFrame The size of the GSO frame.
505 * @param iSeg The segment that we're carving out (0-based).
506 * @param cSegs The number of segments in the GSO frame. Use
507 * PDMNetGsoCalcSegmentCount to find this.
508 * @param pbSegHdrs Where to return the headers for the segment
509 * that's been carved out. The buffer must be at
510 * least pGso->cbHdrs in size, using a 256 byte
511 * buffer is a recommended simplification.
512 * @param pcbSegHdrs Where to return the size of the returned
513 * segment headers.
514 * @param pcbSegPayload Where to return the size of the returned
515 * segment payload.
516 */
517DECLINLINE(uint32_t) PDMNetGsoCarveSegment(PCPDMNETWORKGSO pGso, const uint8_t *pbFrame, size_t cbFrame,
518 uint32_t iSeg, uint32_t cSegs, uint8_t *pbSegHdrs,
519 uint32_t *pcbSegHdrs, uint32_t *pcbSegPayload)
520{
521 /*
522 * Figure out where the payload is and where the header starts before we
523 * do the protocol specific carving.
524 */
525 uint32_t const cbSegHdrs = pdmNetSegHdrLen(pGso, iSeg);
526 uint8_t const * const pbSegPayload = pbFrame + cbSegHdrs + iSeg * pGso->cbMaxSeg;
527 uint32_t const cbSegPayload = pdmNetSegPayloadLen(pGso, iSeg, cSegs, (uint32_t)cbFrame);
528
529 /*
530 * Check assumptions (doing it after declaring the variables because of C).
531 */
532 Assert(iSeg < cSegs);
533 Assert(cSegs == PDMNetGsoCalcSegmentCount(pGso, cbFrame));
534 Assert(PDMNetGsoIsValid(pGso, sizeof(*pGso), cbFrame));
535
536 /*
537 * Copy the header and do the protocol specific massaging of it.
538 */
539 memcpy(pbSegHdrs, pbFrame, pGso->cbHdrsTotal); /* include UDP header */
540
541 switch ((PDMNETWORKGSOTYPE)pGso->u8Type)
542 {
543 case PDMNETWORKGSOTYPE_IPV4_TCP:
544 pdmNetGsoUpdateTcpHdr(pdmNetGsoUpdateIPv4Hdr(pbSegHdrs, pGso->offHdr1, cbSegPayload, iSeg, cbSegHdrs),
545 pbSegHdrs, pGso->offHdr2, pbSegPayload, cbSegPayload, iSeg * pGso->cbMaxSeg,
546 cbSegHdrs, iSeg + 1 == cSegs, PDMNETCSUMTYPE_COMPLETE);
547 break;
548 case PDMNETWORKGSOTYPE_IPV4_UDP:
549 if (iSeg == 0)
550 {
551 /* uh_ulen shall not exceed cbFrame - pGso->offHdr2 (offset of UDP header) */
552 PRTNETUDP pUdpHdr = (PRTNETUDP)&pbFrame[pGso->offHdr2];
553 Assert(pGso->offHdr2 + RT_UOFFSET_AFTER(RTNETUDP, uh_ulen) <= cbFrame);
554 if ((unsigned)(pGso->offHdr2 + RT_BE2H_U16(pUdpHdr->uh_ulen)) > cbFrame)
555 {
556 size_t cbUdp = cbFrame - pGso->offHdr2;
557 if (cbUdp >= UINT16_MAX)
558 pUdpHdr->uh_ulen = UINT16_MAX;
559 else
560 pUdpHdr->uh_ulen = RT_H2BE_U16((uint16_t)cbUdp);
561 }
562 pdmNetGsoUpdateUdpHdrUfo(RTNetIPv4PseudoChecksum((PRTNETIPV4)&pbFrame[pGso->offHdr1]),
563 pbSegHdrs, pbFrame, pGso->offHdr2);
564 }
565 pdmNetGsoUpdateIPv4HdrUfo(pbSegHdrs, pGso->offHdr1, cbSegPayload, iSeg * pGso->cbMaxSeg,
566 cbSegHdrs, iSeg + 1 == cSegs);
567 break;
568 case PDMNETWORKGSOTYPE_IPV6_TCP:
569 pdmNetGsoUpdateTcpHdr(pdmNetGsoUpdateIPv6Hdr(pbSegHdrs, pGso->offHdr1, cbSegPayload, cbSegHdrs,
570 pGso->offHdr2, RTNETIPV4_PROT_TCP),
571 pbSegHdrs, pGso->offHdr2, pbSegPayload, cbSegPayload, iSeg * pGso->cbMaxSeg,
572 cbSegHdrs, iSeg + 1 == cSegs, PDMNETCSUMTYPE_COMPLETE);
573 break;
574 case PDMNETWORKGSOTYPE_IPV6_UDP:
575 pdmNetGsoUpdateUdpHdr(pdmNetGsoUpdateIPv6Hdr(pbSegHdrs, pGso->offHdr1, cbSegPayload, cbSegHdrs,
576 pGso->offHdr2, RTNETIPV4_PROT_UDP),
577 pbSegHdrs, pGso->offHdr2, pbSegPayload, cbSegPayload, cbSegHdrs, PDMNETCSUMTYPE_COMPLETE);
578 break;
579 case PDMNETWORKGSOTYPE_IPV4_IPV6_TCP:
580 pdmNetGsoUpdateIPv4Hdr(pbSegHdrs, pGso->offHdr1, cbSegPayload, iSeg, cbSegHdrs);
581 pdmNetGsoUpdateTcpHdr(pdmNetGsoUpdateIPv6Hdr(pbSegHdrs, pgmNetGsoCalcIpv6Offset(pbSegHdrs, pGso->offHdr1),
582 cbSegPayload, cbSegHdrs, pGso->offHdr2, RTNETIPV4_PROT_TCP),
583 pbSegHdrs, pGso->offHdr2, pbSegPayload, cbSegPayload, iSeg * pGso->cbMaxSeg,
584 cbSegHdrs, iSeg + 1 == cSegs, PDMNETCSUMTYPE_COMPLETE);
585 break;
586 case PDMNETWORKGSOTYPE_IPV4_IPV6_UDP:
587 pdmNetGsoUpdateIPv4Hdr(pbSegHdrs, pGso->offHdr1, cbSegPayload, iSeg, cbSegHdrs);
588 pdmNetGsoUpdateUdpHdr(pdmNetGsoUpdateIPv6Hdr(pbSegHdrs, pgmNetGsoCalcIpv6Offset(pbSegHdrs, pGso->offHdr1),
589 cbSegPayload, cbSegHdrs, pGso->offHdr2, RTNETIPV4_PROT_UDP),
590 pbSegHdrs, pGso->offHdr2, pbSegPayload, cbSegPayload, cbSegHdrs, PDMNETCSUMTYPE_COMPLETE);
591 break;
592 case PDMNETWORKGSOTYPE_INVALID:
593 case PDMNETWORKGSOTYPE_END:
594 /* no default! wnat gcc warnings. */
595 break;
596 }
597
598 *pcbSegHdrs = cbSegHdrs;
599 *pcbSegPayload = cbSegPayload;
600 return cbSegHdrs + iSeg * pGso->cbMaxSeg;
601}
602
603
604/**
605 * Prepares the GSO frame for direct use without any segmenting.
606 *
607 * @param pGso The GSO context.
608 * @param pvFrame The frame to prepare.
609 * @param cbFrame The frame size.
610 * @param enmCsumType Whether to checksum the payload, the pseudo
611 * header or nothing.
612 */
613DECLINLINE(void) PDMNetGsoPrepForDirectUse(PCPDMNETWORKGSO pGso, void *pvFrame, size_t cbFrame, PDMNETCSUMTYPE enmCsumType)
614{
615 /*
616 * Figure out where the payload is and where the header starts before we
617 * do the protocol bits.
618 */
619 uint8_t * const pbHdrs = (uint8_t *)pvFrame;
620 uint8_t * const pbPayload = pbHdrs + pGso->cbHdrsTotal;
621 uint32_t const cbFrame32 = (uint32_t)cbFrame;
622 uint32_t const cbPayload = cbFrame32 - pGso->cbHdrsTotal;
623
624 /*
625 * Check assumptions (doing it after declaring the variables because of C).
626 */
627 Assert(PDMNetGsoIsValid(pGso, sizeof(*pGso), cbFrame));
628
629 /*
630 * Get down to busienss.
631 */
632 switch ((PDMNETWORKGSOTYPE)pGso->u8Type)
633 {
634 case PDMNETWORKGSOTYPE_IPV4_TCP:
635 pdmNetGsoUpdateTcpHdr(pdmNetGsoUpdateIPv4Hdr(pbHdrs, pGso->offHdr1, cbFrame32 - pGso->cbHdrsTotal, 0, pGso->cbHdrsTotal),
636 pbHdrs, pGso->offHdr2, pbPayload, cbPayload, 0, pGso->cbHdrsTotal, true, enmCsumType);
637 break;
638 case PDMNETWORKGSOTYPE_IPV4_UDP:
639 pdmNetGsoUpdateUdpHdr(pdmNetGsoUpdateIPv4Hdr(pbHdrs, pGso->offHdr1, cbFrame32 - pGso->cbHdrsTotal, 0, pGso->cbHdrsTotal),
640 pbHdrs, pGso->offHdr2, pbPayload, cbPayload, pGso->cbHdrsTotal, enmCsumType);
641 break;
642 case PDMNETWORKGSOTYPE_IPV6_TCP:
643 pdmNetGsoUpdateTcpHdr(pdmNetGsoUpdateIPv6Hdr(pbHdrs, pGso->offHdr1, cbPayload, pGso->cbHdrsTotal,
644 pGso->offHdr2, RTNETIPV4_PROT_TCP),
645 pbHdrs, pGso->offHdr2, pbPayload, cbPayload, 0, pGso->cbHdrsTotal, true, enmCsumType);
646 break;
647 case PDMNETWORKGSOTYPE_IPV6_UDP:
648 pdmNetGsoUpdateUdpHdr(pdmNetGsoUpdateIPv6Hdr(pbHdrs, pGso->offHdr1, cbPayload, pGso->cbHdrsTotal,
649 pGso->offHdr2, RTNETIPV4_PROT_UDP),
650 pbHdrs, pGso->offHdr2, pbPayload, cbPayload, pGso->cbHdrsTotal, enmCsumType);
651 break;
652 case PDMNETWORKGSOTYPE_IPV4_IPV6_TCP:
653 pdmNetGsoUpdateIPv4Hdr(pbHdrs, pGso->offHdr1, cbPayload, 0, pGso->cbHdrsTotal);
654 pdmNetGsoUpdateTcpHdr(pdmNetGsoUpdateIPv6Hdr(pbHdrs, pgmNetGsoCalcIpv6Offset(pbHdrs, pGso->offHdr1),
655 cbPayload, pGso->cbHdrsTotal, pGso->offHdr2, RTNETIPV4_PROT_TCP),
656 pbHdrs, pGso->offHdr2, pbPayload, cbPayload, 0, pGso->cbHdrsTotal, true, enmCsumType);
657 break;
658 case PDMNETWORKGSOTYPE_IPV4_IPV6_UDP:
659 pdmNetGsoUpdateIPv4Hdr(pbHdrs, pGso->offHdr1, cbPayload, 0, pGso->cbHdrsTotal);
660 pdmNetGsoUpdateUdpHdr(pdmNetGsoUpdateIPv6Hdr(pbHdrs, pgmNetGsoCalcIpv6Offset(pbHdrs, pGso->offHdr1),
661 cbPayload, pGso->cbHdrsTotal, pGso->offHdr2, RTNETIPV4_PROT_UDP),
662 pbHdrs, pGso->offHdr2, pbPayload, cbPayload, pGso->cbHdrsTotal, enmCsumType);
663 break;
664 case PDMNETWORKGSOTYPE_INVALID:
665 case PDMNETWORKGSOTYPE_END:
666 /* no default! wnat gcc warnings. */
667 break;
668 }
669}
670
671
672/**
673 * Gets the GSO type name string.
674 *
675 * @returns Pointer to read only name string.
676 * @param enmType The type.
677 */
678DECLINLINE(const char *) PDMNetGsoTypeName(PDMNETWORKGSOTYPE enmType)
679{
680 switch (enmType)
681 {
682 case PDMNETWORKGSOTYPE_IPV4_TCP: return "TCPv4";
683 case PDMNETWORKGSOTYPE_IPV6_TCP: return "TCPv6";
684 case PDMNETWORKGSOTYPE_IPV4_UDP: return "UDPv4";
685 case PDMNETWORKGSOTYPE_IPV6_UDP: return "UDPv6";
686 case PDMNETWORKGSOTYPE_IPV4_IPV6_TCP: return "4to6TCP";
687 case PDMNETWORKGSOTYPE_IPV4_IPV6_UDP: return "4to6UDP";
688 case PDMNETWORKGSOTYPE_INVALID: return "invalid";
689 case PDMNETWORKGSOTYPE_END: return "end";
690 }
691 return "bad-gso-type";
692}
693
694/** @} */
695
696#endif /* !VBOX_INCLUDED_vmm_pdmnetinline_h */
697
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