diff options
Diffstat (limited to 'include/VBox/vmm/pdmnetinline.h')
-rw-r--r-- | include/VBox/vmm/pdmnetinline.h | 724 |
1 files changed, 724 insertions, 0 deletions
diff --git a/include/VBox/vmm/pdmnetinline.h b/include/VBox/vmm/pdmnetinline.h new file mode 100644 index 00000000..0acd5fb9 --- /dev/null +++ b/include/VBox/vmm/pdmnetinline.h @@ -0,0 +1,724 @@ +/** @file + * PDM - Networking Helpers, Inlined Code. (DEV,++) + * + * This is all inlined because it's too tedious to create 2-3 libraries to + * contain it all (same bad excuse as for intnetinline.h). + */ + +/* + * Copyright (C) 2010-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + +#ifndef VBOX_INCLUDED_vmm_pdmnetinline_h +#define VBOX_INCLUDED_vmm_pdmnetinline_h +#ifndef RT_WITHOUT_PRAGMA_ONCE +# pragma once +#endif + + +/******************************************************************************* +* Header Files * +*******************************************************************************/ +#include <VBox/log.h> +#include <VBox/types.h> +#include <iprt/asm.h> +#include <iprt/assert.h> +#include <iprt/net.h> +#include <iprt/string.h> + + +/** @defgroup grp_pdm_net_inline The PDM Networking Helper APIs + * @ingroup grp_pdm + * @{ + */ + + +/** + * Checksum type. + */ +typedef enum PDMNETCSUMTYPE +{ + /** No checksum. */ + PDMNETCSUMTYPE_NONE = 0, + /** Normal TCP checksum. */ + PDMNETCSUMTYPE_COMPLETE, + /** Checksum on pseudo header (used with GSO). */ + PDMNETCSUMTYPE_PSEUDO, + /** The usual 32-bit hack. */ + PDMNETCSUMTYPE_32_BIT_HACK = 0x7fffffff +} PDMNETCSUMTYPE; + + +/** + * Validates the GSO context. + * + * @returns true if valid, false if not (not asserted or logged). + * @param pGso The GSO context. + * @param cbGsoMax The max size of the GSO context. + * @param cbFrame The max size of the GSO frame (use to validate + * the MSS). + */ +DECLINLINE(bool) PDMNetGsoIsValid(PCPDMNETWORKGSO pGso, size_t cbGsoMax, size_t cbFrame) +{ +#define CHECK_COND_RETURN_FALSE(expr) if (RT_LIKELY(expr)) { /* likely */ } else return false + PDMNETWORKGSOTYPE enmType; + + CHECK_COND_RETURN_FALSE(cbGsoMax >= sizeof(*pGso)); + + enmType = (PDMNETWORKGSOTYPE)pGso->u8Type; + CHECK_COND_RETURN_FALSE(enmType > PDMNETWORKGSOTYPE_INVALID && enmType < PDMNETWORKGSOTYPE_END); + + /* all types requires both headers. */ + CHECK_COND_RETURN_FALSE(pGso->offHdr1 >= sizeof(RTNETETHERHDR)); + CHECK_COND_RETURN_FALSE(pGso->offHdr2 > pGso->offHdr1); + CHECK_COND_RETURN_FALSE(pGso->cbHdrsTotal > pGso->offHdr2); + + /* min size of the 1st header(s). */ + switch (enmType) + { + case PDMNETWORKGSOTYPE_IPV4_TCP: + case PDMNETWORKGSOTYPE_IPV4_UDP: + CHECK_COND_RETURN_FALSE((unsigned)pGso->offHdr2 - pGso->offHdr1 >= RTNETIPV4_MIN_LEN); + break; + case PDMNETWORKGSOTYPE_IPV6_TCP: + case PDMNETWORKGSOTYPE_IPV6_UDP: + CHECK_COND_RETURN_FALSE((unsigned)pGso->offHdr2 - pGso->offHdr1 >= RTNETIPV6_MIN_LEN); + break; + case PDMNETWORKGSOTYPE_IPV4_IPV6_TCP: + case PDMNETWORKGSOTYPE_IPV4_IPV6_UDP: + CHECK_COND_RETURN_FALSE((unsigned)pGso->offHdr2 - pGso->offHdr1 >= RTNETIPV4_MIN_LEN + RTNETIPV6_MIN_LEN); + break; + /* These two have been rejected above already, but we need to include them to avoid gcc warnings. */ + case PDMNETWORKGSOTYPE_INVALID: + case PDMNETWORKGSOTYPE_END: + break; + /* No default case! Want gcc warnings. */ + } + + /* min size of the 2nd header. */ + switch (enmType) + { + case PDMNETWORKGSOTYPE_IPV4_TCP: + case PDMNETWORKGSOTYPE_IPV6_TCP: + case PDMNETWORKGSOTYPE_IPV4_IPV6_TCP: + CHECK_COND_RETURN_FALSE((unsigned)pGso->cbHdrsTotal - pGso->offHdr2 >= RTNETTCP_MIN_LEN); + break; + case PDMNETWORKGSOTYPE_IPV4_UDP: + case PDMNETWORKGSOTYPE_IPV6_UDP: + case PDMNETWORKGSOTYPE_IPV4_IPV6_UDP: + CHECK_COND_RETURN_FALSE((unsigned)pGso->cbHdrsTotal - pGso->offHdr2 >= RTNETUDP_MIN_LEN); + break; + /* These two have been rejected above already, but we need to include them to avoid gcc warnings. */ + case PDMNETWORKGSOTYPE_INVALID: + case PDMNETWORKGSOTYPE_END: + break; + /* No default case! Want gcc warnings. */ + } + + /* There must be at more than one segment. */ + CHECK_COND_RETURN_FALSE(cbFrame > pGso->cbHdrsTotal); + CHECK_COND_RETURN_FALSE(cbFrame - pGso->cbHdrsTotal >= pGso->cbMaxSeg); + + /* Make sure the segment size is enough to fit a UDP header. */ + CHECK_COND_RETURN_FALSE(enmType != PDMNETWORKGSOTYPE_IPV4_UDP || pGso->cbMaxSeg >= RTNETUDP_MIN_LEN); + + /* Make sure the segment size is not zero. */ + CHECK_COND_RETURN_FALSE(pGso->cbMaxSeg > 0); + + return true; +#undef CHECK_COND_RETURN_FALSE +} + + +/** + * Returns the length of header for a particular segment/fragment. + * + * We cannot simply treat UDP header as a part of payload because we do not + * want to modify the payload but still need to modify the checksum field in + * UDP header. So we want to include UDP header when calculating the length + * of headers in the first segment getting it copied to a temporary buffer + * along with other headers. + * + * @returns Length of headers (including UDP header for the first fragment). + * @param pGso The GSO context. + * @param iSeg The segment index. + */ +DECLINLINE(uint8_t) pdmNetSegHdrLen(PCPDMNETWORKGSO pGso, uint32_t iSeg) +{ + return iSeg ? pGso->cbHdrsSeg : pGso->cbHdrsTotal; +} + +/** + * Returns the length of payload for a particular segment/fragment. + * + * The first segment does not contain UDP header. The size of UDP header is + * determined as the difference between the total headers size and the size + * used during segmentation. + * + * @returns Length of payload (including UDP header for the first fragment). + * @param pGso The GSO context. + * @param iSeg The segment that we're carving out (0-based). + * @param cSegs The number of segments in the GSO frame. + * @param cbFrame The size of the GSO frame. + */ +DECLINLINE(uint32_t) pdmNetSegPayloadLen(PCPDMNETWORKGSO pGso, uint32_t iSeg, uint32_t cSegs, uint32_t cbFrame) +{ + if (iSeg + 1 == cSegs) + return cbFrame - iSeg * pGso->cbMaxSeg - pdmNetSegHdrLen(pGso, iSeg); + return pGso->cbMaxSeg - (iSeg ? 0 : pGso->cbHdrsTotal - pGso->cbHdrsSeg); +} + +/** + * Calculates the number of segments a GSO frame will be segmented into. + * + * @returns Segment count. + * @param pGso The GSO context. + * @param cbFrame The GSO frame size (header proto + payload). + */ +DECLINLINE(uint32_t) PDMNetGsoCalcSegmentCount(PCPDMNETWORKGSO pGso, size_t cbFrame) +{ + size_t cbPayload; + Assert(PDMNetGsoIsValid(pGso, sizeof(*pGso), cbFrame)); + cbPayload = cbFrame - pGso->cbHdrsSeg; + return (uint32_t)((cbPayload + pGso->cbMaxSeg - 1) / pGso->cbMaxSeg); +} + + +/** + * Used to find the IPv6 header when handling 4to6 tunneling. + * + * @returns Offset of the IPv6 header. + * @param pbSegHdrs The headers / frame start. + * @param offIPv4Hdr The offset of the IPv4 header. + */ +DECLINLINE(uint8_t) pgmNetGsoCalcIpv6Offset(uint8_t *pbSegHdrs, uint8_t offIPv4Hdr) +{ + PCRTNETIPV4 pIPv4Hdr = (PCRTNETIPV4)&pbSegHdrs[offIPv4Hdr]; + return offIPv4Hdr + pIPv4Hdr->ip_hl * 4; +} + + +/** + * Update an UDP header after carving out a segment + * + * @param u32PseudoSum The pseudo checksum. + * @param pbSegHdrs Pointer to the header bytes / frame start. + * @param offUdpHdr The offset into @a pbSegHdrs of the UDP header. + * @param pbPayload Pointer to the payload bytes. + * @param cbPayload The amount of payload. + * @param cbHdrs The size of all the headers. + * @param enmCsumType Whether to checksum the payload, the pseudo + * header or nothing. + * @internal + */ +DECLINLINE(void) pdmNetGsoUpdateUdpHdr(uint32_t u32PseudoSum, uint8_t *pbSegHdrs, uint8_t offUdpHdr, + uint8_t const *pbPayload, uint32_t cbPayload, uint8_t cbHdrs, + PDMNETCSUMTYPE enmCsumType) +{ + PRTNETUDP pUdpHdr = (PRTNETUDP)&pbSegHdrs[offUdpHdr]; + pUdpHdr->uh_ulen = RT_H2N_U16(cbPayload + cbHdrs - offUdpHdr); + switch (enmCsumType) + { + case PDMNETCSUMTYPE_NONE: + pUdpHdr->uh_sum = 0; + break; + case PDMNETCSUMTYPE_COMPLETE: + pUdpHdr->uh_sum = RTNetUDPChecksum(u32PseudoSum, pUdpHdr); + break; + case PDMNETCSUMTYPE_PSEUDO: + pUdpHdr->uh_sum = ~RTNetIPv4FinalizeChecksum(u32PseudoSum); + break; + default: + NOREF(pbPayload); + AssertFailed(); + break; + } +} + + +/** + * Update an UDP header after carving out an IP fragment + * + * @param u32PseudoSum The pseudo checksum. + * @param pbSegHdrs Pointer to the header bytes copy + * @param pbFrame Pointer to the frame start. + * @param offUdpHdr The offset into @a pbSegHdrs of the UDP header. + * + * @internal + */ +DECLINLINE(void) pdmNetGsoUpdateUdpHdrUfo(uint32_t u32PseudoSum, uint8_t *pbSegHdrs, const uint8_t *pbFrame, uint8_t offUdpHdr) +{ + PCRTNETUDP pcUdpHdrOrig = (PCRTNETUDP)&pbFrame[offUdpHdr]; + PRTNETUDP pUdpHdr = (PRTNETUDP)&pbSegHdrs[offUdpHdr]; + pUdpHdr->uh_sum = RTNetUDPChecksum(u32PseudoSum, pcUdpHdrOrig); +} + + +/** + * Update a TCP header after carving out a segment. + * + * @param u32PseudoSum The pseudo checksum. + * @param pbSegHdrs Pointer to the header bytes / frame start. + * @param offTcpHdr The offset into @a pbSegHdrs of the TCP header. + * @param pbPayload Pointer to the payload bytes. + * @param cbPayload The amount of payload. + * @param offPayload The offset into the payload that we're splitting + * up. We're ASSUMING that the payload follows + * immediately after the TCP header w/ options. + * @param cbHdrs The size of all the headers. + * @param fLastSeg Set if this is the last segment. + * @param enmCsumType Whether to checksum the payload, the pseudo + * header or nothing. + * @internal + */ +DECLINLINE(void) pdmNetGsoUpdateTcpHdr(uint32_t u32PseudoSum, uint8_t *pbSegHdrs, uint8_t offTcpHdr, + uint8_t const *pbPayload, uint32_t cbPayload, uint32_t offPayload, uint8_t cbHdrs, + bool fLastSeg, PDMNETCSUMTYPE enmCsumType) +{ + PRTNETTCP pTcpHdr = (PRTNETTCP)&pbSegHdrs[offTcpHdr]; + pTcpHdr->th_seq = RT_H2N_U32(RT_N2H_U32(pTcpHdr->th_seq) + offPayload); + if (!fLastSeg) + pTcpHdr->th_flags &= ~(RTNETTCP_F_FIN | RTNETTCP_F_PSH); + switch (enmCsumType) + { + case PDMNETCSUMTYPE_NONE: + pTcpHdr->th_sum = 0; + break; + case PDMNETCSUMTYPE_COMPLETE: + pTcpHdr->th_sum = RTNetTCPChecksum(u32PseudoSum, pTcpHdr, pbPayload, cbPayload); + break; + case PDMNETCSUMTYPE_PSEUDO: + pTcpHdr->th_sum = ~RTNetIPv4FinalizeChecksum(u32PseudoSum); + break; + default: + NOREF(cbHdrs); + AssertFailed(); + break; + } +} + + +/** + * Updates a IPv6 header after carving out a segment. + * + * @returns 32-bit intermediary checksum value for the pseudo header. + * @param pbSegHdrs Pointer to the header bytes. + * @param offIpHdr The offset into @a pbSegHdrs of the IP header. + * @param cbSegPayload The amount of segmented payload. Not to be + * confused with the IP payload. + * @param cbHdrs The size of all the headers. + * @param offPktHdr Offset of the protocol packet header. For the + * pseudo header checksum calulation. + * @param bProtocol The protocol type. For the pseudo header. + * @internal + */ +DECLINLINE(uint32_t) pdmNetGsoUpdateIPv6Hdr(uint8_t *pbSegHdrs, uint8_t offIpHdr, uint32_t cbSegPayload, uint8_t cbHdrs, + uint8_t offPktHdr, uint8_t bProtocol) +{ + PRTNETIPV6 pIpHdr = (PRTNETIPV6)&pbSegHdrs[offIpHdr]; + uint16_t cbPayload = (uint16_t)(cbHdrs - (offIpHdr + sizeof(RTNETIPV6)) + cbSegPayload); + pIpHdr->ip6_plen = RT_H2N_U16(cbPayload); + return RTNetIPv6PseudoChecksumEx(pIpHdr, bProtocol, (uint16_t)(cbHdrs - offPktHdr + cbSegPayload)); +} + + +/** + * Updates a IPv4 header after carving out a segment. + * + * @returns 32-bit intermediary checksum value for the pseudo header. + * @param pbSegHdrs Pointer to the header bytes. + * @param offIpHdr The offset into @a pbSegHdrs of the IP header. + * @param cbSegPayload The amount of segmented payload. + * @param iSeg The segment index. + * @param cbHdrs The size of all the headers. + * @internal + */ +DECLINLINE(uint32_t) pdmNetGsoUpdateIPv4Hdr(uint8_t *pbSegHdrs, uint8_t offIpHdr, uint32_t cbSegPayload, + uint32_t iSeg, uint8_t cbHdrs) +{ + PRTNETIPV4 pIpHdr = (PRTNETIPV4)&pbSegHdrs[offIpHdr]; + pIpHdr->ip_len = RT_H2N_U16(cbHdrs - offIpHdr + cbSegPayload); + pIpHdr->ip_id = RT_H2N_U16(RT_N2H_U16(pIpHdr->ip_id) + iSeg); + pIpHdr->ip_sum = RTNetIPv4HdrChecksum(pIpHdr); + return RTNetIPv4PseudoChecksum(pIpHdr); +} + + +/** + * Updates a IPv4 header after carving out an IP fragment. + * + * @param pbSegHdrs Pointer to the header bytes. + * @param offIpHdr The offset into @a pbSegHdrs of the IP header. + * @param cbSegPayload The amount of segmented payload. + * @param offFragment The offset of this fragment for reassembly. + * @param cbHdrs The size of all the headers. + * @param fLastFragment True if this is the last fragment of datagram. + * @internal + */ +DECLINLINE(void) pdmNetGsoUpdateIPv4HdrUfo(uint8_t *pbSegHdrs, uint8_t offIpHdr, uint32_t cbSegPayload, + uint32_t offFragment, uint8_t cbHdrs, bool fLastFragment) +{ + PRTNETIPV4 pIpHdr = (PRTNETIPV4)&pbSegHdrs[offIpHdr]; + pIpHdr->ip_len = RT_H2N_U16(cbHdrs - offIpHdr + cbSegPayload); + pIpHdr->ip_off = RT_H2N_U16((offFragment / 8) | (fLastFragment ? 0 : RTNETIPV4_FLAGS_MF)); + pIpHdr->ip_sum = RTNetIPv4HdrChecksum(pIpHdr); +} + + +/** + * Carves out the specified segment in a destructive manner. + * + * This is for sequentially carving out segments and pushing them along for + * processing or sending. To avoid allocating a temporary buffer for + * constructing the segment in, we trash the previous frame by putting the + * header at the end of it. + * + * @returns Pointer to the segment frame that we've carved out. + * @param pGso The GSO context data. + * @param pbFrame Pointer to the GSO frame. + * @param cbFrame The size of the GSO frame. + * @param pbHdrScatch Pointer to a pGso->cbHdrs sized area where we + * can save the original header prototypes on the + * first call (@a iSeg is 0) and retrieve it on + * susequent calls. (Just use a 256 bytes + * buffer to make life easy.) + * @param iSeg The segment that we're carving out (0-based). + * @param cSegs The number of segments in the GSO frame. Use + * PDMNetGsoCalcSegmentCount to find this. + * @param pcbSegFrame Where to return the size of the returned segment + * frame. + */ +DECLINLINE(void *) PDMNetGsoCarveSegmentQD(PCPDMNETWORKGSO pGso, uint8_t *pbFrame, size_t cbFrame, uint8_t *pbHdrScatch, + uint32_t iSeg, uint32_t cSegs, uint32_t *pcbSegFrame) +{ + /* + * Figure out where the payload is and where the header starts before we + * do the protocol specific carving. + * + * UDP GSO uses IPv4 fragmentation, meaning that UDP header is present in + * the first fragment only. When computing the total frame size of the + * first fragment we need to use PDMNETWORKGSO::cbHdrsTotal instead of + * PDMNETWORKGSO::cbHdrsSeg. In case of TCP GSO both cbHdrsTotal and + * cbHdrsSeg have the same value, so it will work as well. + */ + uint8_t * const pbSegHdrs = pbFrame + pGso->cbMaxSeg * iSeg; + uint8_t * const pbSegPayload = pbSegHdrs + pGso->cbHdrsSeg; + uint32_t const cbSegPayload = pdmNetSegPayloadLen(pGso, iSeg, cSegs, (uint32_t)cbFrame); + uint32_t const cbSegFrame = cbSegPayload + (iSeg ? pGso->cbHdrsSeg : pGso->cbHdrsTotal); + + /* + * Check assumptions (doing it after declaring the variables because of C). + */ + Assert(iSeg < cSegs); + Assert(cSegs == PDMNetGsoCalcSegmentCount(pGso, cbFrame)); + Assert(PDMNetGsoIsValid(pGso, sizeof(*pGso), cbFrame)); + + /* + * Copy the header and do the protocol specific massaging of it. + */ + if (iSeg != 0) + memcpy(pbSegHdrs, pbHdrScatch, pGso->cbHdrsSeg); + else + memcpy(pbHdrScatch, pbSegHdrs, pGso->cbHdrsSeg); /* There is no need to save UDP header */ + + switch ((PDMNETWORKGSOTYPE)pGso->u8Type) + { + case PDMNETWORKGSOTYPE_IPV4_TCP: + pdmNetGsoUpdateTcpHdr(pdmNetGsoUpdateIPv4Hdr(pbSegHdrs, pGso->offHdr1, cbSegPayload, iSeg, pGso->cbHdrsSeg), + pbSegHdrs, pGso->offHdr2, pbSegPayload, cbSegPayload, iSeg * pGso->cbMaxSeg, + pGso->cbHdrsSeg, iSeg + 1 == cSegs, PDMNETCSUMTYPE_COMPLETE); + break; + case PDMNETWORKGSOTYPE_IPV4_UDP: + if (iSeg == 0) + { + /* uh_ulen shall not exceed cbFrame - pGso->offHdr2 (offset of UDP header) */ + PRTNETUDP pUdpHdr = (PRTNETUDP)&pbFrame[pGso->offHdr2]; + Assert(pGso->offHdr2 + RT_UOFFSET_AFTER(RTNETUDP, uh_ulen) <= cbFrame); + if ((unsigned)(pGso->offHdr2 + RT_BE2H_U16(pUdpHdr->uh_ulen)) > cbFrame) + { + size_t cbUdp = cbFrame - pGso->offHdr2; + if (cbUdp >= UINT16_MAX) + pUdpHdr->uh_ulen = UINT16_MAX; + else + pUdpHdr->uh_ulen = RT_H2BE_U16((uint16_t)cbUdp); + } + /* uh_ulen shall be at least the size of UDP header */ + if (RT_BE2H_U16(pUdpHdr->uh_ulen) < sizeof(RTNETUDP)) + pUdpHdr->uh_ulen = RT_H2BE_U16(sizeof(RTNETUDP)); + pdmNetGsoUpdateUdpHdrUfo(RTNetIPv4PseudoChecksum((PRTNETIPV4)&pbFrame[pGso->offHdr1]), + pbSegHdrs, pbFrame, pGso->offHdr2); + } + pdmNetGsoUpdateIPv4HdrUfo(pbSegHdrs, pGso->offHdr1, cbSegPayload, iSeg * pGso->cbMaxSeg, + pdmNetSegHdrLen(pGso, iSeg), iSeg + 1 == cSegs); + break; + case PDMNETWORKGSOTYPE_IPV6_TCP: + pdmNetGsoUpdateTcpHdr(pdmNetGsoUpdateIPv6Hdr(pbSegHdrs, pGso->offHdr1, cbSegPayload, pGso->cbHdrsSeg, + pGso->offHdr2, RTNETIPV4_PROT_TCP), + pbSegHdrs, pGso->offHdr2, pbSegPayload, cbSegPayload, iSeg * pGso->cbMaxSeg, + pGso->cbHdrsSeg, iSeg + 1 == cSegs, PDMNETCSUMTYPE_COMPLETE); + break; + case PDMNETWORKGSOTYPE_IPV6_UDP: + pdmNetGsoUpdateUdpHdr(pdmNetGsoUpdateIPv6Hdr(pbSegHdrs, pGso->offHdr1, cbSegPayload, pGso->cbHdrsSeg, + pGso->offHdr2, RTNETIPV4_PROT_UDP), + pbSegHdrs, pGso->offHdr2, pbSegPayload, cbSegPayload, pGso->cbHdrsSeg, PDMNETCSUMTYPE_COMPLETE); + break; + case PDMNETWORKGSOTYPE_IPV4_IPV6_TCP: + pdmNetGsoUpdateIPv4Hdr(pbSegHdrs, pGso->offHdr1, cbSegPayload, iSeg, pGso->cbHdrsSeg); + pdmNetGsoUpdateTcpHdr(pdmNetGsoUpdateIPv6Hdr(pbSegHdrs, pgmNetGsoCalcIpv6Offset(pbSegHdrs, pGso->offHdr1), + cbSegPayload, pGso->cbHdrsSeg, pGso->offHdr2, RTNETIPV4_PROT_TCP), + pbSegHdrs, pGso->offHdr2, pbSegPayload, cbSegPayload, iSeg * pGso->cbMaxSeg, + pGso->cbHdrsSeg, iSeg + 1 == cSegs, PDMNETCSUMTYPE_COMPLETE); + break; + case PDMNETWORKGSOTYPE_IPV4_IPV6_UDP: + pdmNetGsoUpdateIPv4Hdr(pbSegHdrs, pGso->offHdr1, cbSegPayload, iSeg, pGso->cbHdrsSeg); + pdmNetGsoUpdateUdpHdr(pdmNetGsoUpdateIPv6Hdr(pbSegHdrs, pgmNetGsoCalcIpv6Offset(pbSegHdrs, pGso->offHdr1), + cbSegPayload, pGso->cbHdrsSeg, pGso->offHdr2, RTNETIPV4_PROT_UDP), + pbSegHdrs, pGso->offHdr2, pbSegPayload, cbSegPayload, pGso->cbHdrsSeg, PDMNETCSUMTYPE_COMPLETE); + break; + case PDMNETWORKGSOTYPE_INVALID: + case PDMNETWORKGSOTYPE_END: + /* no default! wnat gcc warnings. */ + break; + } + + *pcbSegFrame = cbSegFrame; + return pbSegHdrs; +} + + +/** + * Carves out the specified segment in a non-destructive manner. + * + * The segment headers and segment payload is kept separate here. The GSO frame + * is still expected to be one linear chunk of data, but we don't modify any of + * it. + * + * @returns The offset into the GSO frame of the payload. + * @param pGso The GSO context data. + * @param pbFrame Pointer to the GSO frame. Used for retrieving + * the header prototype and for checksumming the + * payload. The buffer is not modified. + * @param cbFrame The size of the GSO frame. + * @param iSeg The segment that we're carving out (0-based). + * @param cSegs The number of segments in the GSO frame. Use + * PDMNetGsoCalcSegmentCount to find this. + * @param pbSegHdrs Where to return the headers for the segment + * that's been carved out. The buffer must be at + * least pGso->cbHdrs in size, using a 256 byte + * buffer is a recommended simplification. + * @param pcbSegHdrs Where to return the size of the returned + * segment headers. + * @param pcbSegPayload Where to return the size of the returned + * segment payload. + */ +DECLINLINE(uint32_t) PDMNetGsoCarveSegment(PCPDMNETWORKGSO pGso, const uint8_t *pbFrame, size_t cbFrame, + uint32_t iSeg, uint32_t cSegs, uint8_t *pbSegHdrs, + uint32_t *pcbSegHdrs, uint32_t *pcbSegPayload) +{ + /* + * Figure out where the payload is and where the header starts before we + * do the protocol specific carving. + */ + uint32_t const cbSegHdrs = pdmNetSegHdrLen(pGso, iSeg); + uint8_t const * const pbSegPayload = pbFrame + cbSegHdrs + iSeg * pGso->cbMaxSeg; + uint32_t const cbSegPayload = pdmNetSegPayloadLen(pGso, iSeg, cSegs, (uint32_t)cbFrame); + + /* + * Check assumptions (doing it after declaring the variables because of C). + */ + Assert(iSeg < cSegs); + Assert(cSegs == PDMNetGsoCalcSegmentCount(pGso, cbFrame)); + Assert(PDMNetGsoIsValid(pGso, sizeof(*pGso), cbFrame)); + + /* + * Copy the header and do the protocol specific massaging of it. + */ + memcpy(pbSegHdrs, pbFrame, pGso->cbHdrsTotal); /* include UDP header */ + + switch ((PDMNETWORKGSOTYPE)pGso->u8Type) + { + case PDMNETWORKGSOTYPE_IPV4_TCP: + pdmNetGsoUpdateTcpHdr(pdmNetGsoUpdateIPv4Hdr(pbSegHdrs, pGso->offHdr1, cbSegPayload, iSeg, cbSegHdrs), + pbSegHdrs, pGso->offHdr2, pbSegPayload, cbSegPayload, iSeg * pGso->cbMaxSeg, + cbSegHdrs, iSeg + 1 == cSegs, PDMNETCSUMTYPE_COMPLETE); + break; + case PDMNETWORKGSOTYPE_IPV4_UDP: + if (iSeg == 0) + { + /* uh_ulen shall not exceed cbFrame - pGso->offHdr2 (offset of UDP header) */ + PRTNETUDP pUdpHdr = (PRTNETUDP)&pbFrame[pGso->offHdr2]; + Assert(pGso->offHdr2 + RT_UOFFSET_AFTER(RTNETUDP, uh_ulen) <= cbFrame); + if ((unsigned)(pGso->offHdr2 + RT_BE2H_U16(pUdpHdr->uh_ulen)) > cbFrame) + { + size_t cbUdp = cbFrame - pGso->offHdr2; + if (cbUdp >= UINT16_MAX) + pUdpHdr->uh_ulen = UINT16_MAX; + else + pUdpHdr->uh_ulen = RT_H2BE_U16((uint16_t)cbUdp); + } + /* uh_ulen shall be at least the size of UDP header */ + if (RT_BE2H_U16(pUdpHdr->uh_ulen) < sizeof(RTNETUDP)) + pUdpHdr->uh_ulen = RT_H2BE_U16(sizeof(RTNETUDP)); + pdmNetGsoUpdateUdpHdrUfo(RTNetIPv4PseudoChecksum((PRTNETIPV4)&pbFrame[pGso->offHdr1]), + pbSegHdrs, pbFrame, pGso->offHdr2); + } + pdmNetGsoUpdateIPv4HdrUfo(pbSegHdrs, pGso->offHdr1, cbSegPayload, iSeg * pGso->cbMaxSeg, + cbSegHdrs, iSeg + 1 == cSegs); + break; + case PDMNETWORKGSOTYPE_IPV6_TCP: + pdmNetGsoUpdateTcpHdr(pdmNetGsoUpdateIPv6Hdr(pbSegHdrs, pGso->offHdr1, cbSegPayload, cbSegHdrs, + pGso->offHdr2, RTNETIPV4_PROT_TCP), + pbSegHdrs, pGso->offHdr2, pbSegPayload, cbSegPayload, iSeg * pGso->cbMaxSeg, + cbSegHdrs, iSeg + 1 == cSegs, PDMNETCSUMTYPE_COMPLETE); + break; + case PDMNETWORKGSOTYPE_IPV6_UDP: + pdmNetGsoUpdateUdpHdr(pdmNetGsoUpdateIPv6Hdr(pbSegHdrs, pGso->offHdr1, cbSegPayload, cbSegHdrs, + pGso->offHdr2, RTNETIPV4_PROT_UDP), + pbSegHdrs, pGso->offHdr2, pbSegPayload, cbSegPayload, cbSegHdrs, PDMNETCSUMTYPE_COMPLETE); + break; + case PDMNETWORKGSOTYPE_IPV4_IPV6_TCP: + pdmNetGsoUpdateIPv4Hdr(pbSegHdrs, pGso->offHdr1, cbSegPayload, iSeg, cbSegHdrs); + pdmNetGsoUpdateTcpHdr(pdmNetGsoUpdateIPv6Hdr(pbSegHdrs, pgmNetGsoCalcIpv6Offset(pbSegHdrs, pGso->offHdr1), + cbSegPayload, cbSegHdrs, pGso->offHdr2, RTNETIPV4_PROT_TCP), + pbSegHdrs, pGso->offHdr2, pbSegPayload, cbSegPayload, iSeg * pGso->cbMaxSeg, + cbSegHdrs, iSeg + 1 == cSegs, PDMNETCSUMTYPE_COMPLETE); + break; + case PDMNETWORKGSOTYPE_IPV4_IPV6_UDP: + pdmNetGsoUpdateIPv4Hdr(pbSegHdrs, pGso->offHdr1, cbSegPayload, iSeg, cbSegHdrs); + pdmNetGsoUpdateUdpHdr(pdmNetGsoUpdateIPv6Hdr(pbSegHdrs, pgmNetGsoCalcIpv6Offset(pbSegHdrs, pGso->offHdr1), + cbSegPayload, cbSegHdrs, pGso->offHdr2, RTNETIPV4_PROT_UDP), + pbSegHdrs, pGso->offHdr2, pbSegPayload, cbSegPayload, cbSegHdrs, PDMNETCSUMTYPE_COMPLETE); + break; + case PDMNETWORKGSOTYPE_INVALID: + case PDMNETWORKGSOTYPE_END: + /* no default! wnat gcc warnings. */ + break; + } + + *pcbSegHdrs = cbSegHdrs; + *pcbSegPayload = cbSegPayload; + return cbSegHdrs + iSeg * pGso->cbMaxSeg; +} + + +/** + * Prepares the GSO frame for direct use without any segmenting. + * + * @param pGso The GSO context. + * @param pvFrame The frame to prepare. + * @param cbFrame The frame size. + * @param enmCsumType Whether to checksum the payload, the pseudo + * header or nothing. + */ +DECLINLINE(void) PDMNetGsoPrepForDirectUse(PCPDMNETWORKGSO pGso, void *pvFrame, size_t cbFrame, PDMNETCSUMTYPE enmCsumType) +{ + /* + * Figure out where the payload is and where the header starts before we + * do the protocol bits. + */ + uint8_t * const pbHdrs = (uint8_t *)pvFrame; + uint8_t * const pbPayload = pbHdrs + pGso->cbHdrsTotal; + uint32_t const cbFrame32 = (uint32_t)cbFrame; + uint32_t const cbPayload = cbFrame32 - pGso->cbHdrsTotal; + + /* + * Check assumptions (doing it after declaring the variables because of C). + */ + Assert(PDMNetGsoIsValid(pGso, sizeof(*pGso), cbFrame)); + + /* + * Get down to busienss. + */ + switch ((PDMNETWORKGSOTYPE)pGso->u8Type) + { + case PDMNETWORKGSOTYPE_IPV4_TCP: + pdmNetGsoUpdateTcpHdr(pdmNetGsoUpdateIPv4Hdr(pbHdrs, pGso->offHdr1, cbFrame32 - pGso->cbHdrsTotal, 0, pGso->cbHdrsTotal), + pbHdrs, pGso->offHdr2, pbPayload, cbPayload, 0, pGso->cbHdrsTotal, true, enmCsumType); + break; + case PDMNETWORKGSOTYPE_IPV4_UDP: + pdmNetGsoUpdateUdpHdr(pdmNetGsoUpdateIPv4Hdr(pbHdrs, pGso->offHdr1, cbFrame32 - pGso->cbHdrsTotal, 0, pGso->cbHdrsTotal), + pbHdrs, pGso->offHdr2, pbPayload, cbPayload, pGso->cbHdrsTotal, enmCsumType); + break; + case PDMNETWORKGSOTYPE_IPV6_TCP: + pdmNetGsoUpdateTcpHdr(pdmNetGsoUpdateIPv6Hdr(pbHdrs, pGso->offHdr1, cbPayload, pGso->cbHdrsTotal, + pGso->offHdr2, RTNETIPV4_PROT_TCP), + pbHdrs, pGso->offHdr2, pbPayload, cbPayload, 0, pGso->cbHdrsTotal, true, enmCsumType); + break; + case PDMNETWORKGSOTYPE_IPV6_UDP: + pdmNetGsoUpdateUdpHdr(pdmNetGsoUpdateIPv6Hdr(pbHdrs, pGso->offHdr1, cbPayload, pGso->cbHdrsTotal, + pGso->offHdr2, RTNETIPV4_PROT_UDP), + pbHdrs, pGso->offHdr2, pbPayload, cbPayload, pGso->cbHdrsTotal, enmCsumType); + break; + case PDMNETWORKGSOTYPE_IPV4_IPV6_TCP: + pdmNetGsoUpdateIPv4Hdr(pbHdrs, pGso->offHdr1, cbPayload, 0, pGso->cbHdrsTotal); + pdmNetGsoUpdateTcpHdr(pdmNetGsoUpdateIPv6Hdr(pbHdrs, pgmNetGsoCalcIpv6Offset(pbHdrs, pGso->offHdr1), + cbPayload, pGso->cbHdrsTotal, pGso->offHdr2, RTNETIPV4_PROT_TCP), + pbHdrs, pGso->offHdr2, pbPayload, cbPayload, 0, pGso->cbHdrsTotal, true, enmCsumType); + break; + case PDMNETWORKGSOTYPE_IPV4_IPV6_UDP: + pdmNetGsoUpdateIPv4Hdr(pbHdrs, pGso->offHdr1, cbPayload, 0, pGso->cbHdrsTotal); + pdmNetGsoUpdateUdpHdr(pdmNetGsoUpdateIPv6Hdr(pbHdrs, pgmNetGsoCalcIpv6Offset(pbHdrs, pGso->offHdr1), + cbPayload, pGso->cbHdrsTotal, pGso->offHdr2, RTNETIPV4_PROT_UDP), + pbHdrs, pGso->offHdr2, pbPayload, cbPayload, pGso->cbHdrsTotal, enmCsumType); + break; + case PDMNETWORKGSOTYPE_INVALID: + case PDMNETWORKGSOTYPE_END: + /* no default! wnat gcc warnings. */ + break; + } +} + + +/** + * Gets the GSO type name string. + * + * @returns Pointer to read only name string. + * @param enmType The type. + */ +DECLINLINE(const char *) PDMNetGsoTypeName(PDMNETWORKGSOTYPE enmType) +{ + switch (enmType) + { + case PDMNETWORKGSOTYPE_IPV4_TCP: return "TCPv4"; + case PDMNETWORKGSOTYPE_IPV6_TCP: return "TCPv6"; + case PDMNETWORKGSOTYPE_IPV4_UDP: return "UDPv4"; + case PDMNETWORKGSOTYPE_IPV6_UDP: return "UDPv6"; + case PDMNETWORKGSOTYPE_IPV4_IPV6_TCP: return "4to6TCP"; + case PDMNETWORKGSOTYPE_IPV4_IPV6_UDP: return "4to6UDP"; + case PDMNETWORKGSOTYPE_INVALID: return "invalid"; + case PDMNETWORKGSOTYPE_END: return "end"; + } + return "bad-gso-type"; +} + +/** @} */ + +#endif /* !VBOX_INCLUDED_vmm_pdmnetinline_h */ + |