/** * 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); }
/** * Internal worker for vboxNetFltDarwinIffInput and vboxNetFltDarwinIffOutput, * * @returns 0 or EJUSTRETURN. * @param pThis The instance. * @param pMBuf The mbuf. * @param pvFrame The start of the frame, optional. * @param fSrc Where the packet (allegedly) comes from, one INTNETTRUNKDIR_* value. * @param eProtocol The protocol. */ static errno_t vboxNetFltDarwinIffInputOutputWorker(PVBOXNETFLTINS pThis, mbuf_t pMBuf, void *pvFrame, uint32_t fSrc, protocol_family_t eProtocol) { /* * Drop it immediately? */ Log2(("vboxNetFltDarwinIffInputOutputWorker: pThis=%p pMBuf=%p pvFrame=%p fSrc=%#x cbPkt=%x\n", pThis, pMBuf, pvFrame, fSrc, pMBuf ? mbuf_pkthdr_len(pMBuf) : -1)); if (!pMBuf) return 0; #if 0 /* debugging lost icmp packets */ if (mbuf_pkthdr_len(pMBuf) > 0x300) { uint8_t *pb = (uint8_t *)(pvFrame ? pvFrame : mbuf_data(pMBuf)); Log3(("D=%.6Rhxs S=%.6Rhxs T=%04x IFF\n", pb, pb + 6, RT_BE2H_U16(*(uint16_t *)(pb + 12)))); } #endif if (vboxNetFltDarwinMBufIsOur(pThis, pMBuf, pvFrame)) return 0; /* * Active? Retain the instance and increment the busy counter. */ if (!vboxNetFltTryRetainBusyActive(pThis)) return 0; /* * Finalize out-bound packets since the stack puts off finalizing * TCP/IP checksums as long as possible. * ASSUMES this only applies to outbound IP packets. */ if ( (fSrc & INTNETTRUNKDIR_HOST) && eProtocol == PF_INET) { Assert(!pvFrame); mbuf_outbound_finalize(pMBuf, eProtocol, sizeof(RTNETETHERHDR)); } /* * Create a (scatter/)gather list for the mbuf and feed it to the internal network. */ bool fDropIt = false; unsigned cSegs = vboxNetFltDarwinMBufCalcSGSegs(pThis, pMBuf, pvFrame); if (cSegs < VBOXNETFLT_DARWIN_MAX_SEGS) { PINTNETSG pSG = (PINTNETSG)alloca(RT_OFFSETOF(INTNETSG, aSegs[cSegs])); vboxNetFltDarwinMBufToSG(pThis, pMBuf, pvFrame, pSG, cSegs, fSrc); fDropIt = pThis->pSwitchPort->pfnRecv(pThis->pSwitchPort, NULL /* pvIf */, pSG, fSrc); if (fDropIt) { /* * Check if this interface is in promiscuous mode. We should not drop * any packets before they get to the driver as it passes them to tap * callbacks in order for BPF to work properly. */ if (vboxNetFltDarwinIsPromiscuous(pThis)) fDropIt = false; else mbuf_freem(pMBuf); } } vboxNetFltRelease(pThis, true /* fBusy */); return fDropIt ? EJUSTRETURN : 0; }