/** * 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); }
/** * 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 */ } }