VirtualBox

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

Last change on this file since 62958 was 62476, checked in by vboxsync, 8 years ago

(C) 2016

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