VirtualBox

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

Last change on this file since 69206 was 69107, checked in by vboxsync, 7 years ago

include/VBox/: (C) year

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