Example #1
0
/**
 * Checks if the head of the receive ring is a UDP packet matching the given
 * criteria.
 *
 * @returns Pointer to the data if it matches.
 * @param   pBuf            The IntNet buffers.
 * @param   uDstPort        The destination port to match.
 * @param   pDstMac         The destination address to match if
 *                          VBOXNETUDP_MATCH_UNICAST is specied.
 * @param   fFlags          Flags indicating what to match and some debug stuff.
 *                          See VBOXNETUDP_MATCH_*.
 * @param   pHdrs           Where to return the pointers to the headers.
 *                          Optional.
 * @param   pcb             Where to return the size of the data on success.
 */
void *VBoxNetUDPMatch(PINTNETBUF pBuf, unsigned uDstPort, PCRTMAC pDstMac, uint32_t fFlags, PVBOXNETUDPHDRS pHdrs, size_t *pcb)
{
    /*
     * Clear return values so we can return easier on mismatch.
     */
    *pcb = 0;
    if (pHdrs)
    {
        pHdrs->pEth  = NULL;
        pHdrs->pIpv4 = NULL;
        pHdrs->pUdp  = NULL;
    }

    /*
     * Valid IntNet Ethernet frame?
     */
    PCINTNETHDR pHdr = IntNetRingGetNextFrameToRead(&pBuf->Recv);
    if (    !pHdr
        ||  (   pHdr->u16Type != INTNETHDR_TYPE_FRAME
             && pHdr->u16Type != INTNETHDR_TYPE_GSO))
        return NULL;

    size_t          cbFrame = pHdr->cbFrame;
    const void     *pvFrame = IntNetHdrGetFramePtr(pHdr, pBuf);
    PCPDMNETWORKGSO pGso    = NULL;
    if (pHdr->u16Type == INTNETHDR_TYPE_GSO)
    {
        pGso = (PCPDMNETWORKGSO)pvFrame;
        if (!PDMNetGsoIsValid(pGso, cbFrame, cbFrame - sizeof(*pGso)))
            return NULL;
        /** @todo IPv6 UDP support, goes for this entire function really.  Not really
         *        important yet since this is currently only used by the DHCP server. */
        if (pGso->u8Type != PDMNETWORKGSOTYPE_IPV4_UDP)
            return NULL;
        pvFrame  = pGso + 1;
        cbFrame -= sizeof(*pGso);
    }

    PCRTNETETHERHDR pEthHdr = (PCRTNETETHERHDR)pvFrame;
    if (pHdrs)
        pHdrs->pEth = pEthHdr;

#ifdef IN_RING3
    /* Dump if to stderr/log if that's wanted. */
    if (fFlags & VBOXNETUDP_MATCH_PRINT_STDERR)
    {
        RTStrmPrintf(g_pStdErr, "frame: cb=%04x dst=%.6Rhxs src=%.6Rhxs type=%04x%s\n",
                     cbFrame, &pEthHdr->DstMac, &pEthHdr->SrcMac, RT_BE2H_U16(pEthHdr->EtherType),
                     !memcmp(&pEthHdr->DstMac, pDstMac, sizeof(*pDstMac)) ? " Mine!" : "");
    }
#endif

    /*
     * Ethernet matching.
     */

    /* Ethernet min frame size. */
    if (cbFrame < 64)
        return NULL;

    /* Match Ethertype: IPV4? */
    /** @todo VLAN tagging? */
    if (pEthHdr->EtherType != RT_H2BE_U16_C(RTNET_ETHERTYPE_IPV4))
        return NULL;

    /* Match destination address (ethernet) */
    if (    (   !(fFlags & VBOXNETUDP_MATCH_UNICAST)
             || memcmp(&pEthHdr->DstMac, pDstMac, sizeof(pEthHdr->DstMac)))
        &&  (   !(fFlags & VBOXNETUDP_MATCH_BROADCAST)
             || pEthHdr->DstMac.au16[0] != 0xffff
             || pEthHdr->DstMac.au16[1] != 0xffff
             || pEthHdr->DstMac.au16[2] != 0xffff))
        return NULL;

    /*
     * If we're working on a GSO frame, we need to make sure the length fields
     * are set correctly (they are usually set to 0).
     */
    if (pGso)
        PDMNetGsoPrepForDirectUse(pGso, (void *)pvFrame, cbFrame, PDMNETCSUMTYPE_NONE);

    /*
     * IP validation and matching.
     */
    PCRTNETIPV4 pIpHdr = (PCRTNETIPV4)(pEthHdr + 1);
    if (pHdrs)
        pHdrs->pIpv4 = pIpHdr;

    /* Protocol: UDP */
    if (pIpHdr->ip_p != RTNETIPV4_PROT_UDP)
        return NULL;

    /* Valid IPv4 header? */
    size_t const offIpHdr = (uintptr_t)pIpHdr - (uintptr_t)pEthHdr;
    if (!RTNetIPv4IsHdrValid(pIpHdr, cbFrame - offIpHdr, cbFrame - offIpHdr, !pGso /*fChecksum*/))
        return NULL;

    /*
     * UDP matching and validation.
     */
    PCRTNETUDP  pUdpHdr   = (PCRTNETUDP)((uint32_t *)pIpHdr + pIpHdr->ip_hl);
    if (pHdrs)
        pHdrs->pUdp = pUdpHdr;

    /* Destination port */
    if (RT_BE2H_U16(pUdpHdr->uh_dport) != uDstPort)
        return NULL;

    if (!pGso)
    {
        /* Validate the UDP header according to flags. */
        size_t      offUdpHdr = (uintptr_t)pUdpHdr - (uintptr_t)pEthHdr;
        if (fFlags & (VBOXNETUDP_MATCH_CHECKSUM | VBOXNETUDP_MATCH_REQUIRE_CHECKSUM))
        {
            if (!RTNetIPv4IsUDPValid(pIpHdr, pUdpHdr, pUdpHdr + 1, cbFrame - offUdpHdr, true /*fChecksum*/))
                return NULL;
            if (    (fFlags & VBOXNETUDP_MATCH_REQUIRE_CHECKSUM)
                &&  !pUdpHdr->uh_sum)
                return NULL;
        }
        else
        {
            if (!RTNetIPv4IsUDPSizeValid(pIpHdr, pUdpHdr, cbFrame - offUdpHdr))
                return NULL;
        }
    }

    /*
     * We've got a match!
     */
    *pcb = pUdpHdr->uh_ulen - sizeof(*pUdpHdr);
    return (void *)(pUdpHdr + 1);
}
Example #2
0
/**
 * Deal with ARP queries.
 *
 * @returns true if ARP.
 *
 * @param   pSession        The support driver session.
 * @param   hIf             The internal network interface handle.
 * @param   pBuf            The internal network interface buffer.
 * @param   pMacAddr        Our MAC address.
 * @param   IPv4Addr        Our IPv4 address.
 */
bool VBoxNetArpHandleIt(PSUPDRVSESSION pSession, INTNETIFHANDLE hIf, PINTNETBUF pBuf, PCRTMAC pMacAddr, RTNETADDRIPV4 IPv4Addr)
{
    /*
     * Valid IntNet Ethernet frame? Skip GSO, no ARP in there.
     */
    PCINTNETHDR pHdr = IntNetRingGetNextFrameToRead(&pBuf->Recv);
    if (   !pHdr
        || pHdr->u16Type != INTNETHDR_TYPE_FRAME)
        return false;

    size_t          cbFrame = pHdr->cbFrame;
    const void     *pvFrame = IntNetHdrGetFramePtr(pHdr, pBuf);
    PCRTNETETHERHDR pEthHdr = (PCRTNETETHERHDR)pvFrame;

    /*
     * Arp frame?
     */
    if (pEthHdr->EtherType != RT_H2N_U16_C(RTNET_ETHERTYPE_ARP))
        return false;
    if (   (   pEthHdr->DstMac.au16[0] != 0xffff
            || pEthHdr->DstMac.au16[1] != 0xffff
            || pEthHdr->DstMac.au16[2] != 0xffff)
        && (   pEthHdr->DstMac.au16[0] != pMacAddr->au16[0]
            || pEthHdr->DstMac.au16[1] != pMacAddr->au16[1]
            || pEthHdr->DstMac.au16[2] != pMacAddr->au16[2])
       )
        return false;
    if (cbFrame < sizeof(RTNETARPIPV4) + sizeof(RTNETETHERHDR))
        return false;

    PCRTNETARPHDR pArpHdr = (PCRTNETARPHDR)(pEthHdr + 1);
    if (pArpHdr->ar_htype != RT_H2N_U16_C(RTNET_ARP_ETHER))
        return false;
    if (pArpHdr->ar_hlen != sizeof(RTMAC))
        return false;
    if (pArpHdr->ar_ptype != RT_H2N_U16_C(RTNET_ETHERTYPE_IPV4))
        return false;
    if (pArpHdr->ar_plen != sizeof(RTNETADDRIPV4))
        return false;

    /* It's ARP, alright. Anything we need to do something about. */
    PCRTNETARPIPV4 pArp = (PCRTNETARPIPV4)pArpHdr;
    switch (pArp->Hdr.ar_oper)
    {
        case RT_H2N_U16_C(RTNET_ARPOP_REQUEST):
        case RT_H2N_U16_C(RTNET_ARPOP_REVREQUEST):
        case RT_H2N_U16_C(RTNET_ARPOP_INVREQUEST):
            break;
        default:
            return true;
    }

    /*
     * Deal with the queries.
     */
    RTNETARPIPV4 Reply;
    switch (pArp->Hdr.ar_oper)
    {
        /* 'Who has ar_tpa? Tell ar_spa.'  */
        case RT_H2N_U16_C(RTNET_ARPOP_REQUEST):
            if (pArp->ar_tpa.u != IPv4Addr.u)
                return true;
            Reply.Hdr.ar_oper = RT_H2N_U16_C(RTNET_ARPOP_REPLY);
            break;

        case RT_H2N_U16_C(RTNET_ARPOP_REVREQUEST):
            if (    pArp->ar_tha.au16[0] != pMacAddr->au16[0]
                ||  pArp->ar_tha.au16[1] != pMacAddr->au16[1]
                ||  pArp->ar_tha.au16[2] != pMacAddr->au16[2])
                return true;
            Reply.Hdr.ar_oper = RT_H2N_U16_C(RTNET_ARPOP_REVREPLY);
            break;

        case RT_H2N_U16_C(RTNET_ARPOP_INVREQUEST):
            /** @todo RTNET_ARPOP_INVREQUEST */
            return true;
            //Reply.Hdr.ar_oper = RT_H2N_U16_C(RTNET_ARPOP_INVREPLY);
            //break;
    }

    /*
     * Complete the reply and send it.
     */
    Reply.Hdr.ar_htype = RT_H2N_U16_C(RTNET_ARP_ETHER);
    Reply.Hdr.ar_ptype = RT_H2N_U16_C(RTNET_ETHERTYPE_IPV4);
    Reply.Hdr.ar_hlen  = sizeof(RTMAC);
    Reply.Hdr.ar_plen  = sizeof(RTNETADDRIPV4);
    Reply.ar_sha = *pMacAddr;
    Reply.ar_spa = IPv4Addr;
    Reply.ar_tha = pArp->ar_sha;
    Reply.ar_tpa = pArp->ar_spa;


    RTNETETHERHDR EthHdr;
    EthHdr.DstMac    = pArp->ar_sha;
    EthHdr.SrcMac    = *pMacAddr;
    EthHdr.EtherType = RT_H2N_U16_C(RTNET_ETHERTYPE_ARP);

    uint8_t abTrailer[60 - sizeof(Reply) - sizeof(EthHdr)];
    memset(abTrailer, '\0', sizeof(abTrailer));

    INTNETSEG aSegs[3];
    aSegs[0].cb = sizeof(EthHdr);
    aSegs[0].pv = &EthHdr;

    aSegs[1].pv = &Reply;
    aSegs[1].cb = sizeof(Reply);

    aSegs[2].pv = &abTrailer[0];
    aSegs[2].cb = sizeof(abTrailer);

    VBoxNetIntIfSend(pSession, hIf, pBuf, RT_ELEMENTS(aSegs), &aSegs[0], true /* fFlush */);

    return true;
}
void VBoxNetBaseService::doReceiveLoop()
{
    int rc;
    /* Well we're ready */
    PINTNETRINGBUF  pRingBuf = &m->m_pIfBuf->Recv;

    for (;;)
    {
        /*
         * Wait for a packet to become available.
         */
        rc = waitForIntNetEvent(2000);
        if (rc == VERR_SEM_DESTROYED)
            break;

        if (RT_FAILURE(rc))
        {
            if (rc == VERR_TIMEOUT || rc == VERR_INTERRUPTED)
            {
                /* do we want interrupt anyone ??? */
                continue;
            }
            LogRel(("VBoxNetBaseService: waitForIntNetEvent returned %Rrc\n", rc));
            AssertRCReturnVoid(rc);
        }

        /*
         * Process the receive buffer.
         */
        PCINTNETHDR pHdr;
        while ((pHdr = IntNetRingGetNextFrameToRead(pRingBuf)) != NULL)
        {
            uint8_t const u8Type = pHdr->u8Type;
            size_t        cbFrame = pHdr->cbFrame;
            switch (u8Type)
            {
                case INTNETHDR_TYPE_FRAME:
                {
                    void *pvFrame = IntNetHdrGetFramePtr(pHdr, m->m_pIfBuf);
                    rc = processFrame(pvFrame, cbFrame);
                    if (RT_FAILURE(rc) && rc == VERR_IGNORED)
                    {
                        /* XXX: UDP + ARP for DHCP */
                        VBOXNETUDPHDRS Hdrs;
                        size_t  cb;
                        void   *pv = VBoxNetUDPMatch(m->m_pIfBuf, RTNETIPV4_PORT_BOOTPS, &m->m_MacAddress,
                                                       VBOXNETUDP_MATCH_UNICAST
                                                     | VBOXNETUDP_MATCH_BROADCAST
                                                     | VBOXNETUDP_MATCH_CHECKSUM
                                                     | (m->m_cVerbosity > 2 ? VBOXNETUDP_MATCH_PRINT_STDERR : 0),
                                                     &Hdrs, &cb);
                        if (pv && cb)
                            processUDP(pv, cb);
                        else
                            VBoxNetArpHandleIt(m->m_pSession, m->m_hIf, m->m_pIfBuf, &m->m_MacAddress, m->m_Ipv4Address);
                    }
                    break;
                }
                case INTNETHDR_TYPE_GSO:
                {
                    PCPDMNETWORKGSO pGso = IntNetHdrGetGsoContext(pHdr, m->m_pIfBuf);
                    rc = processGSO(pGso, cbFrame);
                    if (RT_FAILURE(rc) && rc == VERR_IGNORED)
                        break;
                    break;
                }

                case INTNETHDR_TYPE_PADDING:
                    break;

                default:
                    break;
            }
            IntNetRingSkipFrame(&m->m_pIfBuf->Recv);
        } /* loop */
    }
}