diff options
Diffstat (limited to 'src/VBox/HostDrivers/VBoxNetFlt/linux/VBoxNetFlt-linux.c')
-rw-r--r-- | src/VBox/HostDrivers/VBoxNetFlt/linux/VBoxNetFlt-linux.c | 99 |
1 files changed, 96 insertions, 3 deletions
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/linux/VBoxNetFlt-linux.c b/src/VBox/HostDrivers/VBoxNetFlt/linux/VBoxNetFlt-linux.c index 82be076d..ff62274f 100644 --- a/src/VBox/HostDrivers/VBoxNetFlt/linux/VBoxNetFlt-linux.c +++ b/src/VBox/HostDrivers/VBoxNetFlt/linux/VBoxNetFlt-linux.c @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2008 Oracle Corporation + * Copyright (C) 2006-2013 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -445,7 +445,7 @@ static void vboxNetFltLinuxUnhookDev(PVBOXNETFLTINS pThis, struct net_device *pD if (pOverride) { - printk("vboxnetflt: dropped %llu out of %llu packets\n", pOverride->cFiltered, pOverride->cTotal); + printk("vboxnetflt: %llu out of %llu packets were not sent (directed to host)\n", pOverride->cFiltered, pOverride->cTotal); RTMemFree(pOverride); } } @@ -541,6 +541,59 @@ DECLINLINE(bool) vboxNetFltLinuxSkBufIsOur(struct sk_buff *pBuf) /** + * Checks whether this SG list contains a GSO packet. + * + * @returns true / false accordingly. + * @param pSG The (scatter/)gather list. + */ +DECLINLINE(bool) vboxNetFltLinuxIsGso(PINTNETSG pSG) +{ +#if defined(VBOXNETFLT_WITH_GSO_XMIT_WIRE) || defined(VBOXNETFLT_WITH_GSO_XMIT_HOST) + return !((PDMNETWORKGSOTYPE)pSG->GsoCtx.u8Type == PDMNETWORKGSOTYPE_INVALID); +#else /* !VBOXNETFLT_WITH_GSO_XMIT_WIRE && !VBOXNETFLT_WITH_GSO_XMIT_HOST */ + return false; +#endif /* !VBOXNETFLT_WITH_GSO_XMIT_WIRE && !VBOXNETFLT_WITH_GSO_XMIT_HOST */ +} + + +/** + * Find out the frame size (of a single segment in case of GSO frames). + * + * @returns the frame size. + * @param pSG The (scatter/)gather list. + */ +DECLINLINE(uint32_t) vboxNetFltLinuxFrameSize(PINTNETSG pSG) +{ + uint16_t u16Type = 0; + uint32_t cbVlanTag = 0; + if (pSG->aSegs[0].cb >= sizeof(RTNETETHERHDR)) + u16Type = RT_BE2H_U16(((PCRTNETETHERHDR)pSG->aSegs[0].pv)->EtherType); + else if (pSG->cbTotal >= sizeof(RTNETETHERHDR)) + { + uint32_t off = RT_OFFSETOF(RTNETETHERHDR, EtherType); + uint32_t i; + for (i = 0; i < pSG->cSegsUsed; ++i) + { + if (off <= pSG->aSegs[i].cb) + { + if (off + sizeof(uint16_t) <= pSG->aSegs[i].cb) + u16Type = RT_BE2H_U16(*(uint16_t *)((uintptr_t)pSG->aSegs[i].pv + off)); + else if (i + 1 < pSG->cSegsUsed) + u16Type = RT_BE2H_U16( ((uint16_t)( ((uint8_t *)pSG->aSegs[i].pv)[off] ) << 8) + + *(uint8_t *)pSG->aSegs[i + 1].pv); /* ASSUMES no empty segments! */ + /* else: frame is too short. */ + break; + } + off -= pSG->aSegs[i].cb; + } + } + if (u16Type == RTNET_ETHERTYPE_VLAN) + cbVlanTag = 4; + return (vboxNetFltLinuxIsGso(pSG) ? (uint32_t)pSG->GsoCtx.cbMaxSeg + pSG->GsoCtx.cbHdrsTotal : pSG->cbTotal) - cbVlanTag; +} + + +/** * Internal worker that create a linux sk_buff for a * (scatter/)gather list. * @@ -560,6 +613,17 @@ static struct sk_buff *vboxNetFltLinuxSkBufFromSG(PVBOXNETFLTINS pThis, PINTNETS LogRel(("VBoxNetFlt: Dropped empty packet coming from internal network.\n")); return NULL; } + Log5(("VBoxNetFlt: Packet to %s of %d bytes (frame=%d).\n", fDstWire?"wire":"host", pSG->cbTotal, vboxNetFltLinuxFrameSize(pSG))); + if (fDstWire && (vboxNetFltLinuxFrameSize(pSG) > ASMAtomicReadU32(&pThis->u.s.cbMtu) + 14)) + { + static bool s_fOnce = true; + if (s_fOnce) + { + s_fOnce = false; + printk("VBoxNetFlt: Dropped over-sized packet (%d bytes) coming from internal network.\n", vboxNetFltLinuxFrameSize(pSG)); + } + return NULL; + } /** @todo We should use fragments mapping the SG buffers with large packets. * 256 bytes seems to be the a threshold used a lot for this. It @@ -786,6 +850,7 @@ static int vboxNetFltLinuxPacketHandler(struct sk_buff *pBuf, * another copy going to the wire. */ Log2(("vboxNetFltLinuxPacketHandler: dropped loopback packet (cb=%u)\n", pBuf->len)); + dev_kfree_skb(pBuf); /* We must 'consume' all packets we get (@bugref{6539})! */ return 0; } @@ -794,6 +859,7 @@ static int vboxNetFltLinuxPacketHandler(struct sk_buff *pBuf, if (pDev != pSkbDev) { Log(("vboxNetFltLinuxPacketHandler: Devices do not match, pThis may be wrong! pThis=%p\n", pThis)); + kfree_skb(pBuf); /* This is a failure, so we use kfree_skb instead of dev_kfree_skb. */ return 0; } @@ -812,7 +878,7 @@ static int vboxNetFltLinuxPacketHandler(struct sk_buff *pBuf, */ unsigned int uMacLen = pBuf->mac_len; struct sk_buff *pCopy = skb_copy(pBuf, GFP_ATOMIC); - kfree_skb(pBuf); + dev_kfree_skb(pBuf); if (!pCopy) { LogRel(("VBoxNetFlt: Failed to allocate packet buffer, dropping the packet.\n")); @@ -1527,6 +1593,8 @@ static int vboxNetFltLinuxAttachToInterface(PVBOXNETFLTINS pThis, struct net_dev /* Get the mac address while we still have a valid net_device reference. */ memcpy(&pThis->u.s.MacAddr, pDev->dev_addr, sizeof(pThis->u.s.MacAddr)); + /* Initialize MTU */ + pThis->u.s.cbMtu = pDev->mtu; /* * Install a packet filter for this device with a protocol wildcard (ETH_P_ALL). @@ -1677,6 +1745,24 @@ static int vboxNetFltLinuxDeviceGoingDown(PVBOXNETFLTINS pThis, struct net_devic return NOTIFY_OK; } +/** + * Callback for listening to MTU change event. + * + * We need to track changes of host's inteface MTU to discard over-sized frames + * coming from the internal network as they may hang the TX queue of host's + * adapter. + * + * @returns NOTIFY_OK + * @param pThis The netfilter instance. + * @param pDev Pointer to device structure of host's interface. + */ +static int vboxNetFltLinuxDeviceMtuChange(PVBOXNETFLTINS pThis, struct net_device *pDev) +{ + ASMAtomicWriteU32(&pThis->u.s.cbMtu, pDev->mtu); + Log(("vboxNetFltLinuxDeviceMtuChange: set MTU for %s to %d\n", pThis->szName, pDev->mtu)); + return NOTIFY_OK; +} + #ifdef LOG_ENABLED /** Stringify the NETDEV_XXX constants. */ static const char *vboxNetFltLinuxGetNetDevEventName(unsigned long ulEventType) @@ -1718,7 +1804,11 @@ static int vboxNetFltLinuxNotifierCallback(struct notifier_block *self, unsigned { PVBOXNETFLTINS pThis = VBOX_FLT_NB_TO_INST(self); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0) + struct net_device *pDev = netdev_notifier_info_to_dev(ptr); +#else struct net_device *pDev = (struct net_device *)ptr; +#endif int rc = NOTIFY_OK; Log(("VBoxNetFlt: got event %s(0x%lx) on %s, pDev=%p pThis=%p pThis->u.s.pDev=%p\n", @@ -1744,6 +1834,9 @@ static int vboxNetFltLinuxNotifierCallback(struct notifier_block *self, unsigned case NETDEV_GOING_DOWN: rc = vboxNetFltLinuxDeviceGoingDown(pThis, pDev); break; + case NETDEV_CHANGEMTU: + rc = vboxNetFltLinuxDeviceMtuChange(pThis, pDev); + break; case NETDEV_CHANGENAME: break; #ifdef NETDEV_FEAT_CHANGE |