VirtualBox

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

Last change on this file since 67937 was 63267, checked in by vboxsync, 8 years ago

NetworkServices: warnings

  • 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 return pGso->cbMaxSeg - (iSeg ? 0 : pGso->cbHdrsTotal - pGso->cbHdrsSeg);
183}
184
185/**
186 * Calculates the number of segments a GSO frame will be segmented into.
187 *
188 * @returns Segment count.
189 * @param pGso The GSO context.
190 * @param cbFrame The GSO frame size (header proto + payload).
191 */
192DECLINLINE(uint32_t) PDMNetGsoCalcSegmentCount(PCPDMNETWORKGSO pGso, size_t cbFrame)
193{
194 size_t cbPayload;
195 Assert(PDMNetGsoIsValid(pGso, sizeof(*pGso), cbFrame));
196 cbPayload = cbFrame - pGso->cbHdrsSeg;
197 return (uint32_t)((cbPayload + pGso->cbMaxSeg - 1) / pGso->cbMaxSeg);
198}
199
200
201/**
202 * Used to find the IPv6 header when handling 4to6 tunneling.
203 *
204 * @returns Offset of the IPv6 header.
205 * @param pbSegHdrs The headers / frame start.
206 * @param offIPv4Hdr The offset of the IPv4 header.
207 */
208DECLINLINE(uint8_t) pgmNetGsoCalcIpv6Offset(uint8_t *pbSegHdrs, uint8_t offIPv4Hdr)
209{
210 PCRTNETIPV4 pIPv4Hdr = (PCRTNETIPV4)&pbSegHdrs[offIPv4Hdr];
211 return offIPv4Hdr + pIPv4Hdr->ip_hl * 4;
212}
213
214
215/**
216 * Update an UDP header after carving out a segment
217 *
218 * @param u32PseudoSum The pseudo checksum.
219 * @param pbSegHdrs Pointer to the header bytes / frame start.
220 * @param offUdpHdr The offset into @a pbSegHdrs of the UDP header.
221 * @param pbPayload Pointer to the payload bytes.
222 * @param cbPayload The amount of payload.
223 * @param cbHdrs The size of all the headers.
224 * @param enmCsumType Whether to checksum the payload, the pseudo
225 * header or nothing.
226 * @internal
227 */
228DECLINLINE(void) pdmNetGsoUpdateUdpHdr(uint32_t u32PseudoSum, uint8_t *pbSegHdrs, uint8_t offUdpHdr,
229 uint8_t const *pbPayload, uint32_t cbPayload, uint8_t cbHdrs,
230 PDMNETCSUMTYPE enmCsumType)
231{
232 PRTNETUDP pUdpHdr = (PRTNETUDP)&pbSegHdrs[offUdpHdr];
233 pUdpHdr->uh_ulen = RT_H2N_U16(cbPayload + cbHdrs - offUdpHdr);
234 switch (enmCsumType)
235 {
236 case PDMNETCSUMTYPE_NONE:
237 pUdpHdr->uh_sum = 0;
238 break;
239 case PDMNETCSUMTYPE_COMPLETE:
240 pUdpHdr->uh_sum = RTNetUDPChecksum(u32PseudoSum, pUdpHdr);
241 break;
242 case PDMNETCSUMTYPE_PSEUDO:
243 pUdpHdr->uh_sum = ~RTNetIPv4FinalizeChecksum(u32PseudoSum);
244 break;
245 default:
246 NOREF(pbPayload);
247 AssertFailed();
248 break;
249 }
250}
251
252
253/**
254 * Update an UDP header after carving out an IP fragment
255 *
256 * @param u32PseudoSum The pseudo checksum.
257 * @param pbSegHdrs Pointer to the header bytes copy
258 * @param pbFrame Pointer to the frame start.
259 * @param offUdpHdr The offset into @a pbSegHdrs of the UDP header.
260 *
261 * @internal
262 */
263DECLINLINE(void) pdmNetGsoUpdateUdpHdrUfo(uint32_t u32PseudoSum, uint8_t *pbSegHdrs, const uint8_t *pbFrame, uint8_t offUdpHdr)
264{
265 PCRTNETUDP pcUdpHdrOrig = (PCRTNETUDP)&pbFrame[offUdpHdr];
266 PRTNETUDP pUdpHdr = (PRTNETUDP)&pbSegHdrs[offUdpHdr];
267 pUdpHdr->uh_sum = RTNetUDPChecksum(u32PseudoSum, pcUdpHdrOrig);
268}
269
270
271/**
272 * Update a TCP header after carving out a segment.
273 *
274 * @param u32PseudoSum The pseudo checksum.
275 * @param pbSegHdrs Pointer to the header bytes / frame start.
276 * @param offTcpHdr The offset into @a pbSegHdrs of the TCP header.
277 * @param pbPayload Pointer to the payload bytes.
278 * @param cbPayload The amount of payload.
279 * @param offPayload The offset into the payload that we're splitting
280 * up. We're ASSUMING that the payload follows
281 * immediately after the TCP header w/ options.
282 * @param cbHdrs The size of all the headers.
283 * @param fLastSeg Set if this is the last segment.
284 * @param enmCsumType Whether to checksum the payload, the pseudo
285 * header or nothing.
286 * @internal
287 */
288DECLINLINE(void) pdmNetGsoUpdateTcpHdr(uint32_t u32PseudoSum, uint8_t *pbSegHdrs, uint8_t offTcpHdr,
289 uint8_t const *pbPayload, uint32_t cbPayload, uint32_t offPayload, uint8_t cbHdrs,
290 bool fLastSeg, PDMNETCSUMTYPE enmCsumType)
291{
292 PRTNETTCP pTcpHdr = (PRTNETTCP)&pbSegHdrs[offTcpHdr];
293 pTcpHdr->th_seq = RT_H2N_U32(RT_N2H_U32(pTcpHdr->th_seq) + offPayload);
294 if (!fLastSeg)
295 pTcpHdr->th_flags &= ~(RTNETTCP_F_FIN | RTNETTCP_F_PSH);
296 switch (enmCsumType)
297 {
298 case PDMNETCSUMTYPE_NONE:
299 pTcpHdr->th_sum = 0;
300 break;
301 case PDMNETCSUMTYPE_COMPLETE:
302 pTcpHdr->th_sum = RTNetTCPChecksum(u32PseudoSum, pTcpHdr, pbPayload, cbPayload);
303 break;
304 case PDMNETCSUMTYPE_PSEUDO:
305 pTcpHdr->th_sum = ~RTNetIPv4FinalizeChecksum(u32PseudoSum);
306 break;
307 default:
308 NOREF(cbHdrs);
309 AssertFailed();
310 break;
311 }
312}
313
314
315/**
316 * Updates a IPv6 header after carving out a segment.
317 *
318 * @returns 32-bit intermediary checksum value for the pseudo header.
319 * @param pbSegHdrs Pointer to the header bytes.
320 * @param offIpHdr The offset into @a pbSegHdrs of the IP header.
321 * @param cbSegPayload The amount of segmented payload. Not to be
322 * confused with the IP payload.
323 * @param cbHdrs The size of all the headers.
324 * @param offPktHdr Offset of the protocol packet header. For the
325 * pseudo header checksum calulation.
326 * @param bProtocol The protocol type. For the pseudo header.
327 * @internal
328 */
329DECLINLINE(uint32_t) pdmNetGsoUpdateIPv6Hdr(uint8_t *pbSegHdrs, uint8_t offIpHdr, uint32_t cbSegPayload, uint8_t cbHdrs,
330 uint8_t offPktHdr, uint8_t bProtocol)
331{
332 PRTNETIPV6 pIpHdr = (PRTNETIPV6)&pbSegHdrs[offIpHdr];
333 uint16_t cbPayload = (uint16_t)(cbHdrs - (offIpHdr + sizeof(RTNETIPV6)) + cbSegPayload);
334 pIpHdr->ip6_plen = RT_H2N_U16(cbPayload);
335 return RTNetIPv6PseudoChecksumEx(pIpHdr, bProtocol, (uint16_t)(cbHdrs - offPktHdr + cbSegPayload));
336}
337
338
339/**
340 * Updates a IPv4 header after carving out a segment.
341 *
342 * @returns 32-bit intermediary checksum value for the pseudo header.
343 * @param pbSegHdrs Pointer to the header bytes.
344 * @param offIpHdr The offset into @a pbSegHdrs of the IP header.
345 * @param cbSegPayload The amount of segmented payload.
346 * @param iSeg The segment index.
347 * @param cbHdrs The size of all the headers.
348 * @internal
349 */
350DECLINLINE(uint32_t) pdmNetGsoUpdateIPv4Hdr(uint8_t *pbSegHdrs, uint8_t offIpHdr, uint32_t cbSegPayload,
351 uint32_t iSeg, uint8_t cbHdrs)
352{
353 PRTNETIPV4 pIpHdr = (PRTNETIPV4)&pbSegHdrs[offIpHdr];
354 pIpHdr->ip_len = RT_H2N_U16(cbHdrs - offIpHdr + cbSegPayload);
355 pIpHdr->ip_id = RT_H2N_U16(RT_N2H_U16(pIpHdr->ip_id) + iSeg);
356 pIpHdr->ip_sum = RTNetIPv4HdrChecksum(pIpHdr);
357 return RTNetIPv4PseudoChecksum(pIpHdr);
358}
359
360
361/**
362 * Updates a IPv4 header after carving out an IP fragment.
363 *
364 * @param pbSegHdrs Pointer to the header bytes.
365 * @param offIpHdr The offset into @a pbSegHdrs of the IP header.
366 * @param cbSegPayload The amount of segmented payload.
367 * @param offFragment The offset of this fragment for reassembly.
368 * @param cbHdrs The size of all the headers.
369 * @param fLastFragment True if this is the last fragment of datagram.
370 * @internal
371 */
372DECLINLINE(void) pdmNetGsoUpdateIPv4HdrUfo(uint8_t *pbSegHdrs, uint8_t offIpHdr, uint32_t cbSegPayload,
373 uint32_t offFragment, uint8_t cbHdrs, bool fLastFragment)
374{
375 PRTNETIPV4 pIpHdr = (PRTNETIPV4)&pbSegHdrs[offIpHdr];
376 pIpHdr->ip_len = RT_H2N_U16(cbHdrs - offIpHdr + cbSegPayload);
377 pIpHdr->ip_off = RT_H2N_U16((offFragment / 8) | (fLastFragment ? 0 : RTNETIPV4_FLAGS_MF));
378 pIpHdr->ip_sum = RTNetIPv4HdrChecksum(pIpHdr);
379}
380
381
382/**
383 * Carves out the specified segment in a destructive manner.
384 *
385 * This is for sequentially carving out segments and pushing them along for
386 * processing or sending. To avoid allocating a temporary buffer for
387 * constructing the segment in, we trash the previous frame by putting the
388 * header at the end of it.
389 *
390 * @returns Pointer to the segment frame that we've carved out.
391 * @param pGso The GSO context data.
392 * @param pbFrame Pointer to the GSO frame.
393 * @param cbFrame The size of the GSO frame.
394 * @param pbHdrScatch Pointer to a pGso->cbHdrs sized area where we
395 * can save the original header prototypes on the
396 * first call (@a iSeg is 0) and retrieve it on
397 * susequent calls. (Just use a 256 bytes
398 * buffer to make life easy.)
399 * @param iSeg The segment that we're carving out (0-based).
400 * @param cSegs The number of segments in the GSO frame. Use
401 * PDMNetGsoCalcSegmentCount to find this.
402 * @param pcbSegFrame Where to return the size of the returned segment
403 * frame.
404 */
405DECLINLINE(void *) PDMNetGsoCarveSegmentQD(PCPDMNETWORKGSO pGso, uint8_t *pbFrame, size_t cbFrame, uint8_t *pbHdrScatch,
406 uint32_t iSeg, uint32_t cSegs, uint32_t *pcbSegFrame)
407{
408 /*
409 * Figure out where the payload is and where the header starts before we
410 * do the protocol specific carving.
411 */
412 uint8_t * const pbSegHdrs = pbFrame + pGso->cbMaxSeg * iSeg;
413 uint8_t * const pbSegPayload = pbSegHdrs + pGso->cbHdrsSeg;
414 uint32_t const cbSegPayload = pdmNetSegPayloadLen(pGso, iSeg, cSegs, (uint32_t)cbFrame);
415 uint32_t const cbSegFrame = cbSegPayload + pGso->cbHdrsSeg;
416
417 /*
418 * Check assumptions (doing it after declaring the variables because of C).
419 */
420 Assert(iSeg < cSegs);
421 Assert(cSegs == PDMNetGsoCalcSegmentCount(pGso, cbFrame));
422 Assert(PDMNetGsoIsValid(pGso, sizeof(*pGso), cbFrame));
423
424 /*
425 * Copy the header and do the protocol specific massaging of it.
426 */
427 if (iSeg != 0)
428 memcpy(pbSegHdrs, pbHdrScatch, pGso->cbHdrsSeg);
429 else
430 memcpy(pbHdrScatch, pbSegHdrs, pGso->cbHdrsSeg); /* There is no need to save UDP header */
431
432 switch ((PDMNETWORKGSOTYPE)pGso->u8Type)
433 {
434 case PDMNETWORKGSOTYPE_IPV4_TCP:
435 pdmNetGsoUpdateTcpHdr(pdmNetGsoUpdateIPv4Hdr(pbSegHdrs, pGso->offHdr1, cbSegPayload, iSeg, pGso->cbHdrsSeg),
436 pbSegHdrs, pGso->offHdr2, pbSegPayload, cbSegPayload, iSeg * pGso->cbMaxSeg,
437 pGso->cbHdrsSeg, iSeg + 1 == cSegs, PDMNETCSUMTYPE_COMPLETE);
438 break;
439 case PDMNETWORKGSOTYPE_IPV4_UDP:
440 if (iSeg == 0)
441 pdmNetGsoUpdateUdpHdrUfo(RTNetIPv4PseudoChecksum((PRTNETIPV4)&pbFrame[pGso->offHdr1]),
442 pbSegHdrs, pbFrame, pGso->offHdr2);
443 pdmNetGsoUpdateIPv4HdrUfo(pbSegHdrs, pGso->offHdr1, cbSegPayload, iSeg * pGso->cbMaxSeg,
444 pdmNetSegHdrLen(pGso, iSeg), iSeg + 1 == cSegs);
445 break;
446 case PDMNETWORKGSOTYPE_IPV6_TCP:
447 pdmNetGsoUpdateTcpHdr(pdmNetGsoUpdateIPv6Hdr(pbSegHdrs, pGso->offHdr1, cbSegPayload, pGso->cbHdrsSeg,
448 pGso->offHdr2, RTNETIPV4_PROT_TCP),
449 pbSegHdrs, pGso->offHdr2, pbSegPayload, cbSegPayload, iSeg * pGso->cbMaxSeg,
450 pGso->cbHdrsSeg, iSeg + 1 == cSegs, PDMNETCSUMTYPE_COMPLETE);
451 break;
452 case PDMNETWORKGSOTYPE_IPV6_UDP:
453 pdmNetGsoUpdateUdpHdr(pdmNetGsoUpdateIPv6Hdr(pbSegHdrs, pGso->offHdr1, cbSegPayload, pGso->cbHdrsSeg,
454 pGso->offHdr2, RTNETIPV4_PROT_UDP),
455 pbSegHdrs, pGso->offHdr2, pbSegPayload, cbSegPayload, pGso->cbHdrsSeg, PDMNETCSUMTYPE_COMPLETE);
456 break;
457 case PDMNETWORKGSOTYPE_IPV4_IPV6_TCP:
458 pdmNetGsoUpdateIPv4Hdr(pbSegHdrs, pGso->offHdr1, cbSegPayload, iSeg, pGso->cbHdrsSeg);
459 pdmNetGsoUpdateTcpHdr(pdmNetGsoUpdateIPv6Hdr(pbSegHdrs, pgmNetGsoCalcIpv6Offset(pbSegHdrs, pGso->offHdr1),
460 cbSegPayload, pGso->cbHdrsSeg, pGso->offHdr2, RTNETIPV4_PROT_TCP),
461 pbSegHdrs, pGso->offHdr2, pbSegPayload, cbSegPayload, iSeg * pGso->cbMaxSeg,
462 pGso->cbHdrsSeg, iSeg + 1 == cSegs, PDMNETCSUMTYPE_COMPLETE);
463 break;
464 case PDMNETWORKGSOTYPE_IPV4_IPV6_UDP:
465 pdmNetGsoUpdateIPv4Hdr(pbSegHdrs, pGso->offHdr1, cbSegPayload, iSeg, pGso->cbHdrsSeg);
466 pdmNetGsoUpdateUdpHdr(pdmNetGsoUpdateIPv6Hdr(pbSegHdrs, pgmNetGsoCalcIpv6Offset(pbSegHdrs, pGso->offHdr1),
467 cbSegPayload, pGso->cbHdrsSeg, pGso->offHdr2, RTNETIPV4_PROT_UDP),
468 pbSegHdrs, pGso->offHdr2, pbSegPayload, cbSegPayload, pGso->cbHdrsSeg, PDMNETCSUMTYPE_COMPLETE);
469 break;
470 case PDMNETWORKGSOTYPE_INVALID:
471 case PDMNETWORKGSOTYPE_END:
472 /* no default! wnat gcc warnings. */
473 break;
474 }
475
476 *pcbSegFrame = cbSegFrame;
477 return pbSegHdrs;
478}
479
480
481/**
482 * Carves out the specified segment in a non-destructive manner.
483 *
484 * The segment headers and segment payload is kept separate here. The GSO frame
485 * is still expected to be one linear chunk of data, but we don't modify any of
486 * it.
487 *
488 * @returns The offset into the GSO frame of the payload.
489 * @param pGso The GSO context data.
490 * @param pbFrame Pointer to the GSO frame. Used for retrieving
491 * the header prototype and for checksumming the
492 * payload. The buffer is not modified.
493 * @param cbFrame The size of the GSO frame.
494 * @param iSeg The segment that we're carving out (0-based).
495 * @param cSegs The number of segments in the GSO frame. Use
496 * PDMNetGsoCalcSegmentCount to find this.
497 * @param pbSegHdrs Where to return the headers for the segment
498 * that's been carved out. The buffer must be at
499 * least pGso->cbHdrs in size, using a 256 byte
500 * buffer is a recommended simplification.
501 * @param pcbSegHdrs Where to return the size of the returned
502 * segment headers.
503 * @param pcbSegPayload Where to return the size of the returned
504 * segment payload.
505 */
506DECLINLINE(uint32_t) PDMNetGsoCarveSegment(PCPDMNETWORKGSO pGso, const uint8_t *pbFrame, size_t cbFrame,
507 uint32_t iSeg, uint32_t cSegs, uint8_t *pbSegHdrs,
508 uint32_t *pcbSegHdrs, uint32_t *pcbSegPayload)
509{
510 /*
511 * Figure out where the payload is and where the header starts before we
512 * do the protocol specific carving.
513 */
514 uint32_t const cbSegHdrs = pdmNetSegHdrLen(pGso, iSeg);
515 uint8_t const * const pbSegPayload = pbFrame + cbSegHdrs + iSeg * pGso->cbMaxSeg;
516 uint32_t const cbSegPayload = pdmNetSegPayloadLen(pGso, iSeg, cSegs, (uint32_t)cbFrame);
517
518 /*
519 * Check assumptions (doing it after declaring the variables because of C).
520 */
521 Assert(iSeg < cSegs);
522 Assert(cSegs == PDMNetGsoCalcSegmentCount(pGso, cbFrame));
523 Assert(PDMNetGsoIsValid(pGso, sizeof(*pGso), cbFrame));
524
525 /*
526 * Copy the header and do the protocol specific massaging of it.
527 */
528 memcpy(pbSegHdrs, pbFrame, pGso->cbHdrsTotal); /* include UDP header */
529
530 switch ((PDMNETWORKGSOTYPE)pGso->u8Type)
531 {
532 case PDMNETWORKGSOTYPE_IPV4_TCP:
533 pdmNetGsoUpdateTcpHdr(pdmNetGsoUpdateIPv4Hdr(pbSegHdrs, pGso->offHdr1, cbSegPayload, iSeg, cbSegHdrs),
534 pbSegHdrs, pGso->offHdr2, pbSegPayload, cbSegPayload, iSeg * pGso->cbMaxSeg,
535 cbSegHdrs, iSeg + 1 == cSegs, PDMNETCSUMTYPE_COMPLETE);
536 break;
537 case PDMNETWORKGSOTYPE_IPV4_UDP:
538 if (iSeg == 0)
539 pdmNetGsoUpdateUdpHdrUfo(RTNetIPv4PseudoChecksum((PRTNETIPV4)&pbFrame[pGso->offHdr1]),
540 pbSegHdrs, pbFrame, pGso->offHdr2);
541 pdmNetGsoUpdateIPv4HdrUfo(pbSegHdrs, pGso->offHdr1, cbSegPayload, iSeg * pGso->cbMaxSeg,
542 cbSegHdrs, iSeg + 1 == cSegs);
543 break;
544 case PDMNETWORKGSOTYPE_IPV6_TCP:
545 pdmNetGsoUpdateTcpHdr(pdmNetGsoUpdateIPv6Hdr(pbSegHdrs, pGso->offHdr1, cbSegPayload, cbSegHdrs,
546 pGso->offHdr2, RTNETIPV4_PROT_TCP),
547 pbSegHdrs, pGso->offHdr2, pbSegPayload, cbSegPayload, iSeg * pGso->cbMaxSeg,
548 cbSegHdrs, iSeg + 1 == cSegs, PDMNETCSUMTYPE_COMPLETE);
549 break;
550 case PDMNETWORKGSOTYPE_IPV6_UDP:
551 pdmNetGsoUpdateUdpHdr(pdmNetGsoUpdateIPv6Hdr(pbSegHdrs, pGso->offHdr1, cbSegPayload, cbSegHdrs,
552 pGso->offHdr2, RTNETIPV4_PROT_UDP),
553 pbSegHdrs, pGso->offHdr2, pbSegPayload, cbSegPayload, cbSegHdrs, PDMNETCSUMTYPE_COMPLETE);
554 break;
555 case PDMNETWORKGSOTYPE_IPV4_IPV6_TCP:
556 pdmNetGsoUpdateIPv4Hdr(pbSegHdrs, pGso->offHdr1, cbSegPayload, iSeg, cbSegHdrs);
557 pdmNetGsoUpdateTcpHdr(pdmNetGsoUpdateIPv6Hdr(pbSegHdrs, pgmNetGsoCalcIpv6Offset(pbSegHdrs, pGso->offHdr1),
558 cbSegPayload, cbSegHdrs, pGso->offHdr2, RTNETIPV4_PROT_TCP),
559 pbSegHdrs, pGso->offHdr2, pbSegPayload, cbSegPayload, iSeg * pGso->cbMaxSeg,
560 cbSegHdrs, iSeg + 1 == cSegs, PDMNETCSUMTYPE_COMPLETE);
561 break;
562 case PDMNETWORKGSOTYPE_IPV4_IPV6_UDP:
563 pdmNetGsoUpdateIPv4Hdr(pbSegHdrs, pGso->offHdr1, cbSegPayload, iSeg, cbSegHdrs);
564 pdmNetGsoUpdateUdpHdr(pdmNetGsoUpdateIPv6Hdr(pbSegHdrs, pgmNetGsoCalcIpv6Offset(pbSegHdrs, pGso->offHdr1),
565 cbSegPayload, cbSegHdrs, pGso->offHdr2, RTNETIPV4_PROT_UDP),
566 pbSegHdrs, pGso->offHdr2, pbSegPayload, cbSegPayload, cbSegHdrs, PDMNETCSUMTYPE_COMPLETE);
567 break;
568 case PDMNETWORKGSOTYPE_INVALID:
569 case PDMNETWORKGSOTYPE_END:
570 /* no default! wnat gcc warnings. */
571 break;
572 }
573
574 *pcbSegHdrs = cbSegHdrs;
575 *pcbSegPayload = cbSegPayload;
576 return cbSegHdrs + iSeg * pGso->cbMaxSeg;
577}
578
579
580/**
581 * Prepares the GSO frame for direct use without any segmenting.
582 *
583 * @param pGso The GSO context.
584 * @param pvFrame The frame to prepare.
585 * @param cbFrame The frame size.
586 * @param enmCsumType Whether to checksum the payload, the pseudo
587 * header or nothing.
588 */
589DECLINLINE(void) PDMNetGsoPrepForDirectUse(PCPDMNETWORKGSO pGso, void *pvFrame, size_t cbFrame, PDMNETCSUMTYPE enmCsumType)
590{
591 /*
592 * Figure out where the payload is and where the header starts before we
593 * do the protocol bits.
594 */
595 uint8_t * const pbHdrs = (uint8_t *)pvFrame;
596 uint8_t * const pbPayload = pbHdrs + pGso->cbHdrsTotal;
597 uint32_t const cbFrame32 = (uint32_t)cbFrame;
598 uint32_t const cbPayload = cbFrame32 - pGso->cbHdrsTotal;
599
600 /*
601 * Check assumptions (doing it after declaring the variables because of C).
602 */
603 Assert(PDMNetGsoIsValid(pGso, sizeof(*pGso), cbFrame));
604
605 /*
606 * Get down to busienss.
607 */
608 switch ((PDMNETWORKGSOTYPE)pGso->u8Type)
609 {
610 case PDMNETWORKGSOTYPE_IPV4_TCP:
611 pdmNetGsoUpdateTcpHdr(pdmNetGsoUpdateIPv4Hdr(pbHdrs, pGso->offHdr1, cbFrame32 - pGso->cbHdrsTotal, 0, pGso->cbHdrsTotal),
612 pbHdrs, pGso->offHdr2, pbPayload, cbPayload, 0, pGso->cbHdrsTotal, true, enmCsumType);
613 break;
614 case PDMNETWORKGSOTYPE_IPV4_UDP:
615 pdmNetGsoUpdateUdpHdr(pdmNetGsoUpdateIPv4Hdr(pbHdrs, pGso->offHdr1, cbFrame32 - pGso->cbHdrsTotal, 0, pGso->cbHdrsTotal),
616 pbHdrs, pGso->offHdr2, pbPayload, cbPayload, pGso->cbHdrsTotal, enmCsumType);
617 break;
618 case PDMNETWORKGSOTYPE_IPV6_TCP:
619 pdmNetGsoUpdateTcpHdr(pdmNetGsoUpdateIPv6Hdr(pbHdrs, pGso->offHdr1, cbPayload, pGso->cbHdrsTotal,
620 pGso->offHdr2, RTNETIPV4_PROT_TCP),
621 pbHdrs, pGso->offHdr2, pbPayload, cbPayload, 0, pGso->cbHdrsTotal, true, enmCsumType);
622 break;
623 case PDMNETWORKGSOTYPE_IPV6_UDP:
624 pdmNetGsoUpdateUdpHdr(pdmNetGsoUpdateIPv6Hdr(pbHdrs, pGso->offHdr1, cbPayload, pGso->cbHdrsTotal,
625 pGso->offHdr2, RTNETIPV4_PROT_UDP),
626 pbHdrs, pGso->offHdr2, pbPayload, cbPayload, pGso->cbHdrsTotal, enmCsumType);
627 break;
628 case PDMNETWORKGSOTYPE_IPV4_IPV6_TCP:
629 pdmNetGsoUpdateIPv4Hdr(pbHdrs, pGso->offHdr1, cbPayload, 0, pGso->cbHdrsTotal);
630 pdmNetGsoUpdateTcpHdr(pdmNetGsoUpdateIPv6Hdr(pbHdrs, pgmNetGsoCalcIpv6Offset(pbHdrs, pGso->offHdr1),
631 cbPayload, pGso->cbHdrsTotal, pGso->offHdr2, RTNETIPV4_PROT_TCP),
632 pbHdrs, pGso->offHdr2, pbPayload, cbPayload, 0, pGso->cbHdrsTotal, true, enmCsumType);
633 break;
634 case PDMNETWORKGSOTYPE_IPV4_IPV6_UDP:
635 pdmNetGsoUpdateIPv4Hdr(pbHdrs, pGso->offHdr1, cbPayload, 0, pGso->cbHdrsTotal);
636 pdmNetGsoUpdateUdpHdr(pdmNetGsoUpdateIPv6Hdr(pbHdrs, pgmNetGsoCalcIpv6Offset(pbHdrs, pGso->offHdr1),
637 cbPayload, pGso->cbHdrsTotal, pGso->offHdr2, RTNETIPV4_PROT_UDP),
638 pbHdrs, pGso->offHdr2, pbPayload, cbPayload, pGso->cbHdrsTotal, enmCsumType);
639 break;
640 case PDMNETWORKGSOTYPE_INVALID:
641 case PDMNETWORKGSOTYPE_END:
642 /* no default! wnat gcc warnings. */
643 break;
644 }
645}
646
647
648/**
649 * Gets the GSO type name string.
650 *
651 * @returns Pointer to read only name string.
652 * @param enmType The type.
653 */
654DECLINLINE(const char *) PDMNetGsoTypeName(PDMNETWORKGSOTYPE enmType)
655{
656 switch (enmType)
657 {
658 case PDMNETWORKGSOTYPE_IPV4_TCP: return "TCPv4";
659 case PDMNETWORKGSOTYPE_IPV6_TCP: return "TCPv6";
660 case PDMNETWORKGSOTYPE_IPV4_UDP: return "UDPv4";
661 case PDMNETWORKGSOTYPE_IPV6_UDP: return "UDPv6";
662 case PDMNETWORKGSOTYPE_IPV4_IPV6_TCP: return "4to6TCP";
663 case PDMNETWORKGSOTYPE_IPV4_IPV6_UDP: return "4to6UDP";
664 case PDMNETWORKGSOTYPE_INVALID: return "invalid";
665 case PDMNETWORKGSOTYPE_END: return "end";
666 }
667 return "bad-gso-type";
668}
669
670/** @} */
671
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