summaryrefslogtreecommitdiff
path: root/src/VBox/HostDrivers/VBoxNetFlt/linux/VBoxNetFlt-linux.c
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@baserock.org>2014-03-26 19:21:20 +0000
committer <>2014-05-08 15:03:54 +0000
commitfb123f93f9f5ce42c8e5785d2f8e0edaf951740e (patch)
treec2103d76aec5f1f10892cd1d3a38e24f665ae5db /src/VBox/HostDrivers/VBoxNetFlt/linux/VBoxNetFlt-linux.c
parent58ed4748338f9466599adfc8a9171280ed99e23f (diff)
downloadVirtualBox-master.tar.gz
Imported from /home/lorry/working-area/delta_VirtualBox/VirtualBox-4.3.10.tar.bz2.HEADVirtualBox-4.3.10master
Diffstat (limited to 'src/VBox/HostDrivers/VBoxNetFlt/linux/VBoxNetFlt-linux.c')
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/linux/VBoxNetFlt-linux.c99
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