VirtualBox

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

Last change on this file was 103005, checked in by vboxsync, 4 months ago

iprt/asm.h,*: Split out the ASMMem* and related stuff into a separate header, asm-mem.h, so that we can get the RT_ASM_PAGE_SIZE stuff out of the way.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use