VirtualBox

Ignore:
Timestamp:
Apr 9, 2010 5:58:51 PM (15 years ago)
Author:
vboxsync
Message:

VBoxNetFlt-linux: Receive GSO frames from the host, save calls into SrvIntNet as well as ring-0 <-> ring-3 context switches on the DrvIntNet receive thread.

Location:
trunk/src/VBox/HostDrivers/VBoxNetFlt/linux
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/HostDrivers/VBoxNetFlt/linux/VBoxNetFlt-linux.c

    r28140 r28153  
    3535#include <VBox/err.h>
    3636#include <VBox/intnetinline.h>
     37#include <VBox/pdmnetinline.h>
     38#include <VBox/param.h>
    3739#include <iprt/alloca.h>
    3840#include <iprt/assert.h>
     
    4244#include <iprt/process.h>
    4345#include <iprt/mem.h>
     46#include <iprt/net.h>
    4447#include <iprt/log.h>
    4548#include <iprt/mp.h>
     
    349352 *                              This should match the number in the mbuf exactly!
    350353 * @param   fSrc                The source of the frame.
    351  */
    352 DECLINLINE(void) vboxNetFltLinuxSkBufToSG(PVBOXNETFLTINS pThis, struct sk_buff *pBuf, PINTNETSG pSG, unsigned cSegs, uint32_t fSrc)
     354 * @param   pGso                Pointer to the GSO context if it's a GSO
     355 *                              internal network frame.  NULL if regular frame.
     356 */
     357DECLINLINE(void) vboxNetFltLinuxSkBufToSG(PVBOXNETFLTINS pThis, struct sk_buff *pBuf, PINTNETSG pSG,
     358                                          unsigned cSegs, uint32_t fSrc, PCPDMNETWORKGSO pGsoCtx)
    353359{
    354360    int i;
     
    366372    }
    367373
    368     INTNETSgInitTempSegs(pSG, pBuf->len, cSegs, 0 /*cSegsUsed*/);
     374    if (!pGsoCtx)
     375        INTNETSgInitTempSegs(pSG, pBuf->len, cSegs, 0 /*cSegsUsed*/);
     376    else
     377        INTNETSgInitTempSegsGso(pSG, pBuf->len, cSegs, 0 /*cSegsUsed*/, pGsoCtx);
    369378
    370379#ifdef VBOXNETFLT_SG_SUPPORT
     
    532541/**
    533542 * Destroy the intnet scatter / gather buffer created by
    534  * vboxNetFltLinuxSkBufToSG and free the associated socket buffer.
    535  */
    536 static void  vboxNetFltLinuxFreeSkBuff(struct sk_buff *pBuf, PINTNETSG pSG)
     543 * vboxNetFltLinuxSkBufToSG.
     544 */
     545static void vboxNetFltLinuxDestroySG(PINTNETSG pSG)
    537546{
    538547#ifdef VBOXNETFLT_SG_SUPPORT
     
    545554    }
    546555#endif
    547 
    548     dev_kfree_skb(pBuf);
     556    NOREF(pSG);
    549557}
    550558
    551559#ifndef LOG_ENABLED
    552 # define vboxNetFltDumpPacket(a, b, c, d) do {} while (0)
     560# define VBOXNETFLT_DUMP_PACKET(a, b, c, d) do {} while (0)
    553561#else
    554562static void vboxNetFltDumpPacket(PINTNETSG pSG, bool fEgress, const char *pszWhere, int iIncrement)
     
    575583    Log3(("%.*Rhxd\n", pSG->aSegs[0].cb, pSG->aSegs[0].pv));
    576584}
    577 #endif
     585#endif /* LOG_ENABLED */
     586
     587#ifdef VBOXNETFLT_WITH_GSO
     588
     589/**
     590 * Worker for vboxNetFltLinuxForwardToIntNet that checks if we can forwards a
     591 * GSO socket buffer without having to segment it.
     592 *
     593 * @returns true on success, false if needs segmenting.
     594 * @param   pThis               The net filter instance.
     595 * @param   pSkb                The GSO socket buffer.
     596 * @param   fSrc                The source.
     597 * @param   pGsoCtx             Where to return the GSO context on success.
     598 */
     599static bool vboxNetFltLinuxCanForwardAsGso(PVBOXNETFLTINS pThis, struct sk_buff *pSkb, uint32_t fSrc,
     600                                           PPDMNETWORKGSO pGsoCtx)
     601{
     602    PDMNETWORKGSOTYPE   enmGsoType;
     603    uint16_t            uEtherType;
     604    unsigned int        cbTransport;
     605    unsigned int        offTransport;
     606    unsigned int        cbTransportHdr;
     607    unsigned            uProtocol;
     608    union
     609    {
     610        RTNETIPV4           IPv4;
     611        RTNETIPV6           IPv6;
     612        RTNETTCP            Tcp;
     613        uint8_t             ab[40];
     614        uint16_t            au16[40/2];
     615        uint32_t            au32[40/4];
     616    }                   Buf;
     617
     618    /*
     619     * Check the GSO properties of the socket buffer and make sure it fits.
     620     */
     621    /** @todo Figure out how to handle SKB_GSO_TCP_ECN! */
     622    if (RT_UNLIKELY( skb_shinfo(pSkb)->gso_type & ~(SKB_GSO_UDP | SKB_GSO_DODGY | SKB_GSO_TCPV6 | SKB_GSO_TCPV4) ))
     623    {
     624        Log5(("vboxNetFltLinuxCanForwardAsGso: gso_type=%#x\n", skb_shinfo(pSkb)->gso_type));
     625        return false;
     626    }
     627    if (RT_UNLIKELY(   skb_shinfo(pSkb)->gso_size < 1
     628                    || pSkb->len > VBOX_MAX_GSO_SIZE ))
     629    {
     630        Log5(("vboxNetFltLinuxCanForwardAsGso: gso_size=%#x skb_len=%#x (max=%#x)\n", skb_shinfo(pSkb)->gso_size, pSkb->len, VBOX_MAX_GSO_SIZE));
     631        return false;
     632    }
     633    if (RT_UNLIKELY(fSrc & INTNETTRUNKDIR_WIRE))
     634    {
     635        Log5(("vboxNetFltLinuxCanForwardAsGso: fSrc=wire\n"));
     636        return false;
     637    }
     638
     639    /*
     640     * skb_gso_segment does the following. Do we need to do it as well?
     641     */
     642    skb_reset_mac_header(pSkb);
     643    pSkb->mac_len = pSkb->network_header - pSkb->mac_header;
     644
     645    /*
     646     * Switch on the ethertype.
     647     */
     648    uEtherType = pSkb->protocol;
     649    if (   uEtherType    == RT_H2N_U16_C(RTNET_ETHERTYPE_VLAN)
     650        && pSkb->mac_len == sizeof(RTNETETHERHDR) + sizeof(uint32_t))
     651    {
     652        uint16_t const *puEtherType = skb_header_pointer(pSkb, sizeof(RTNETETHERHDR) + sizeof(uint16_t), sizeof(uint16_t), &Buf);
     653        if (puEtherType)
     654            uEtherType = *puEtherType;
     655    }
     656    switch (uEtherType)
     657    {
     658        case RT_H2N_U16_C(RTNET_ETHERTYPE_IPV4):
     659        {
     660            unsigned int cbHdr;
     661            PCRTNETIPV4  pIPv4 = (PCRTNETIPV4)skb_header_pointer(pSkb, pSkb->mac_len, sizeof(Buf.IPv4), &Buf);
     662            if (RT_UNLIKELY(!pIPv4))
     663            {
     664                Log5(("vboxNetFltLinuxCanForwardAsGso: failed to access IPv4 hdr\n"));
     665                return false;
     666            }
     667
     668            cbHdr       = pIPv4->ip_hl * 4;
     669            cbTransport = RT_N2H_U16(pIPv4->ip_len);
     670            if (RT_UNLIKELY(   cbHdr < RTNETIPV4_MIN_LEN
     671                            || cbHdr > cbTransport ))
     672            {
     673                Log5(("vboxNetFltLinuxCanForwardAsGso: invalid IPv4 lengths: ip_hl=%u ip_len=%u\n", pIPv4->ip_hl, RT_N2H_U16(pIPv4->ip_len)));
     674                return false;
     675            }
     676            cbTransport -= cbHdr;
     677            offTransport = pSkb->mac_len + cbHdr;
     678            uProtocol    = pIPv4->ip_p;
     679            if (uProtocol == RTNETIPV4_PROT_TCP)
     680                enmGsoType = PDMNETWORKGSOTYPE_IPV4_TCP;
     681            else if (uProtocol == RTNETIPV4_PROT_UDP)
     682                enmGsoType = PDMNETWORKGSOTYPE_IPV4_UDP;
     683            else /** @todo IPv6: 4to6 tunneling */
     684                enmGsoType = PDMNETWORKGSOTYPE_INVALID;
     685            break;
     686        }
     687
     688        case RT_H2N_U16_C(RTNET_ETHERTYPE_IPV6):
     689        {
     690            PCRTNETIPV6 pIPv6 = (PCRTNETIPV6)skb_header_pointer(pSkb, pSkb->mac_len, sizeof(Buf.IPv6), &Buf);
     691            if (RT_UNLIKELY(!pIPv6))
     692            {
     693                Log5(("vboxNetFltLinuxCanForwardAsGso: failed to access IPv6 hdr\n"));
     694                return false;
     695            }
     696
     697            cbTransport  = RT_N2H_U16(pIPv6->ip6_plen);
     698            offTransport = pSkb->mac_len + sizeof(RTNETIPV6);
     699            uProtocol    = pIPv6->ip6_nxt;
     700            /** @todo IPv6: Dig our way out of the other headers. */
     701            if (uProtocol == RTNETIPV4_PROT_TCP)
     702                enmGsoType = PDMNETWORKGSOTYPE_IPV6_TCP;
     703            else if (uProtocol == RTNETIPV4_PROT_UDP)
     704                enmGsoType = PDMNETWORKGSOTYPE_IPV4_UDP;
     705            else
     706                enmGsoType = PDMNETWORKGSOTYPE_INVALID;
     707            break;
     708        }
     709
     710        default:
     711            Log5(("vboxNetFltLinuxCanForwardAsGso: uEtherType=%#x\n", RT_H2N_U16(uEtherType)));
     712            return false;
     713    }
     714
     715    if (enmGsoType == PDMNETWORKGSOTYPE_INVALID)
     716    {
     717        Log5(("vboxNetFltLinuxCanForwardAsGso: Unsupported protocol %d\n", uProtocol));
     718        return false;
     719    }
     720
     721    if (RT_UNLIKELY(   offTransport + cbTransport <= offTransport
     722                    || offTransport + cbTransport > pSkb->len
     723                    || cbTransport < (uProtocol == RTNETIPV4_PROT_TCP ? RTNETTCP_MIN_LEN : RTNETUDP_MIN_LEN)) )
     724    {
     725        Log5(("vboxNetFltLinuxCanForwardAsGso: Bad transport length; off=%#x + cb=%#x => %#x; skb_len=%#x (%s)\n",
     726              offTransport, cbTransport, offTransport + cbTransport, pSkb->len, PDMNetGsoTypeName(enmGsoType) ));
     727        return false;
     728    }
     729
     730    /*
     731     * Check the TCP/UDP bits.
     732     */
     733    if (uProtocol == RTNETIPV4_PROT_TCP)
     734    {
     735        PCRTNETTCP pTcp = (PCRTNETTCP)skb_header_pointer(pSkb, offTransport, sizeof(Buf.Tcp), &Buf);
     736        if (RT_UNLIKELY(!pTcp))
     737        {
     738            Log5(("vboxNetFltLinuxCanForwardAsGso: failed to access TCP hdr\n"));
     739            return false;
     740        }
     741
     742        cbTransportHdr = pTcp->th_off * 4;
     743        if (RT_UNLIKELY(   cbTransportHdr < RTNETTCP_MIN_LEN
     744                        || cbTransportHdr > cbTransport
     745                        || offTransport + cbTransportHdr >= UINT8_MAX
     746                        || offTransport + cbTransportHdr >= pSkb->len ))
     747        {
     748            Log5(("vboxNetFltLinuxCanForwardAsGso: No space for TCP header; off=%#x cb=%#x skb_len=%#x\n", offTransport, cbTransportHdr, pSkb->len));
     749            return false;
     750        }
     751
     752    }
     753    else
     754    {
     755        Assert(uProtocol == RTNETIPV4_PROT_UDP);
     756        cbTransportHdr = sizeof(RTNETUDP);
     757        if (RT_UNLIKELY(   offTransport + cbTransportHdr >= UINT8_MAX
     758                        || offTransport + cbTransportHdr >= pSkb->len ))
     759        {
     760            Log5(("vboxNetFltLinuxCanForwardAsGso: No space for UDP header; off=%#x skb_len=%#x\n", offTransport, pSkb->len));
     761            return false;
     762        }
     763    }
     764
     765    /*
     766     * We're good, init the GSO context.
     767     */
     768    pGsoCtx->u8Type       = enmGsoType;
     769    pGsoCtx->cbHdrs       = offTransport + cbTransportHdr;
     770    pGsoCtx->cbMaxSeg     = skb_shinfo(pSkb)->gso_size;
     771    pGsoCtx->offHdr1      = pSkb->mac_len;
     772    pGsoCtx->offHdr2      = offTransport;
     773    pGsoCtx->au8Unused[0] = 0;
     774    pGsoCtx->au8Unused[1] = 0;
     775
     776    return true;
     777}
     778
     779/**
     780 * Forward the socket buffer as a GSO internal network frame.
     781 *
     782 * @returns IPRT status code.
     783 * @param   pThis               The net filter instance.
     784 * @param   pSkb                The GSO socket buffer.
     785 * @param   fSrc                The source.
     786 * @param   pGsoCtx             Where to return the GSO context on success.
     787 */
     788static int vboxNetFltLinuxForwardAsGso(PVBOXNETFLTINS pThis, struct sk_buff *pSkb, uint32_t fSrc, PCPDMNETWORKGSO pGsoCtx)
     789{
     790    int         rc;
     791    unsigned    cSegs = vboxNetFltLinuxCalcSGSegments(pSkb);
     792    if (RT_LIKELY(cSegs <= MAX_SKB_FRAGS + 1))
     793    {
     794        PINTNETSG pSG = (PINTNETSG)alloca(RT_OFFSETOF(INTNETSG, aSegs[cSegs]));
     795        if (RT_LIKELY(pSG))
     796        {
     797            vboxNetFltLinuxSkBufToSG(pThis, pSkb, pSG, cSegs, fSrc, pGsoCtx);
     798
     799            vboxNetFltDumpPacket(pSG, false, (fSrc & INTNETTRUNKDIR_HOST) ? "host" : "wire", 1);
     800            pThis->pSwitchPort->pfnRecv(pThis->pSwitchPort, pSG, fSrc);
     801
     802            vboxNetFltLinuxDestroySG(pSG);
     803            rc = VINF_SUCCESS;
     804        }
     805        else
     806        {
     807            Log(("VBoxNetFlt: Dropping the sk_buff (failure case).\n"));
     808            rc = VERR_NO_MEMORY;
     809        }
     810    }
     811    else
     812    {
     813        Log(("VBoxNetFlt: Bad sk_buff? cSegs=%#x.\n", cSegs));
     814        rc = VERR_INTERNAL_ERROR_3;
     815    }
     816
     817    Log4(("VBoxNetFlt: Dropping the sk_buff.\n"));
     818    dev_kfree_skb(pSkb);
     819    return rc;
     820}
     821
     822#endif /* VBOXNETFLT_WITH_GSO */
    578823
    579824/**
     
    587832static int vboxNetFltLinuxForwardSegment(PVBOXNETFLTINS pThis, struct sk_buff *pBuf, uint32_t fSrc)
    588833{
    589     unsigned cSegs = vboxNetFltLinuxCalcSGSegments(pBuf);
    590     if (cSegs < MAX_SKB_FRAGS + 1)
     834    int         rc;
     835    unsigned    cSegs = vboxNetFltLinuxCalcSGSegments(pBuf);
     836    if (cSegs <= MAX_SKB_FRAGS + 1)
    591837    {
    592838        PINTNETSG pSG = (PINTNETSG)alloca(RT_OFFSETOF(INTNETSG, aSegs[cSegs]));
    593         if (!pSG)
     839        if (RT_LIKELY(pSG))
     840        {
     841            vboxNetFltLinuxSkBufToSG(pThis, pBuf, pSG, cSegs, fSrc, NULL /*pGsoCtx*/);
     842
     843            vboxNetFltDumpPacket(pSG, false, (fSrc & INTNETTRUNKDIR_HOST) ? "host" : "wire", 1);
     844            pThis->pSwitchPort->pfnRecv(pThis->pSwitchPort, pSG, fSrc);
     845
     846            vboxNetFltLinuxDestroySG(pSG);
     847            rc = VINF_SUCCESS;
     848        }
     849        else
    594850        {
    595851            Log(("VBoxNetFlt: Failed to allocate SG buffer.\n"));
    596             return VERR_NO_MEMORY;
    597         }
    598         vboxNetFltLinuxSkBufToSG(pThis, pBuf, pSG, cSegs, fSrc);
    599 
    600         vboxNetFltDumpPacket(pSG, false, (fSrc & INTNETTRUNKDIR_HOST) ? "host" : "wire", 1);
    601         pThis->pSwitchPort->pfnRecv(pThis->pSwitchPort, pSG, fSrc);
    602         Log4(("VBoxNetFlt: Dropping the sk_buff.\n"));
    603         vboxNetFltLinuxFreeSkBuff(pBuf, pSG);
    604     }
    605 
    606     return VINF_SUCCESS;
     852            rc = VERR_NO_MEMORY;
     853        }
     854    }
     855    else
     856    {
     857        Log(("VBoxNetFlt: Bad sk_buff? cSegs=%#x.\n", cSegs));
     858        rc = VERR_INTERNAL_ERROR_3;
     859    }
     860
     861    Log4(("VBoxNetFlt: Dropping the sk_buff.\n"));
     862    dev_kfree_skb(pBuf);
     863    return rc;
    607864}
    608865
     
    614871    if (skb_is_gso(pBuf))
    615872    {
     873        PDMNETWORKGSO GsoCtx;
    616874        Log3(("vboxNetFltLinuxForwardToIntNet: skb len=%u data_len=%u truesize=%u next=%p nr_frags=%u gso_size=%u gso_seqs=%u gso_type=%x frag_list=%p pkt_type=%x ip_summed=%d\n",
    617875              pBuf->len, pBuf->data_len, pBuf->truesize, pBuf->next, skb_shinfo(pBuf)->nr_frags, skb_shinfo(pBuf)->gso_size, skb_shinfo(pBuf)->gso_segs, skb_shinfo(pBuf)->gso_type, skb_shinfo(pBuf)->frag_list, pBuf->pkt_type, pBuf->ip_summed));
    618 # if 0 /** @todo receive -> GSO SGs. */
    619         if ()
    620         {
    621             Log3(("vboxNetFltLinuxForwardToIntNet: GSO SG\n"));
    622             vboxNetFltLinuxForwardGso(pThis, pBuf, fSrc);
    623         }
     876        if (   (skb_shinfo(pBuf)->gso_type & (SKB_GSO_UDP | SKB_GSO_TCPV6 | SKB_GSO_TCPV4))
     877            && vboxNetFltLinuxCanForwardAsGso(pThis, pBuf, fSrc, &GsoCtx) )
     878            vboxNetFltLinuxForwardAsGso(pThis, pBuf, fSrc, &GsoCtx);
    624879        else
    625 # endif
    626880        {
    627881            /* Need to segment the packet */
     
    634888                return;
    635889            }
     890
    636891            for (; pSegment; pSegment = pNext)
    637892            {
  • trunk/src/VBox/HostDrivers/VBoxNetFlt/linux/files_vboxnetflt

    r28025 r28153  
    3737    ${PATH_ROOT}/include/iprt/memobj.h=>include/iprt/memobj.h \
    3838    ${PATH_ROOT}/include/iprt/mp.h=>include/iprt/mp.h \
     39    ${PATH_ROOT}/include/iprt/net.h=>include/iprt/net.h \
    3940    ${PATH_ROOT}/include/iprt/param.h=>include/iprt/param.h \
    4041    ${PATH_ROOT}/include/iprt/power.h=>include/iprt/power.h \
     
    5657    ${PATH_ROOT}/include/VBox/intnet.h=>include/VBox/intnet.h \
    5758    ${PATH_ROOT}/include/VBox/intnetinline.h=>include/VBox/intnetinline.h \
     59    ${PATH_ROOT}/include/VBox/pdmnetinline.h=>include/VBox/pdmnetinline.h \
     60    ${PATH_ROOT}/include/VBox/param.h=>include/VBox/param.h \
    5861    ${PATH_ROOT}/include/VBox/stam.h=>include/VBox/stam.h \
    5962    ${PATH_ROOT}/include/VBox/sup.h=>include/VBox/sup.h \
Note: See TracChangeset for help on using the changeset viewer.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette