VirtualBox

source: vbox/trunk/src/VBox/HostDrivers/VBoxNetFlt/linux/VBoxNetFlt-linux.c@ 46904

Last change on this file since 46904 was 46904, checked in by vboxsync, 11 years ago

IntNet, VirtioNet, NetFilter: Large frame support + drop oversized + UFO fix (#6821)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 73.5 KB
Line 
1/* $Id: VBoxNetFlt-linux.c 46904 2013-07-02 12:59:56Z vboxsync $ */
2/** @file
3 * VBoxNetFlt - Network Filter Driver (Host), Linux Specific Code.
4 */
5
6/*
7 * Copyright (C) 2006-2013 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/*******************************************************************************
19* Header Files *
20*******************************************************************************/
21#define LOG_GROUP LOG_GROUP_NET_FLT_DRV
22#define VBOXNETFLT_LINUX_NO_XMIT_QUEUE
23#include "the-linux-kernel.h"
24#include "version-generated.h"
25#include "product-generated.h"
26#include <linux/netdevice.h>
27#include <linux/etherdevice.h>
28#include <linux/rtnetlink.h>
29#include <linux/miscdevice.h>
30#include <linux/ip.h>
31#include <linux/if_vlan.h>
32
33#include <VBox/log.h>
34#include <VBox/err.h>
35#include <VBox/intnetinline.h>
36#include <VBox/vmm/pdmnetinline.h>
37#include <VBox/param.h>
38#include <iprt/alloca.h>
39#include <iprt/assert.h>
40#include <iprt/spinlock.h>
41#include <iprt/semaphore.h>
42#include <iprt/initterm.h>
43#include <iprt/process.h>
44#include <iprt/mem.h>
45#include <iprt/net.h>
46#include <iprt/log.h>
47#include <iprt/mp.h>
48#include <iprt/mem.h>
49#include <iprt/time.h>
50
51#define VBOXNETFLT_OS_SPECFIC 1
52#include "../VBoxNetFltInternal.h"
53
54
55/*******************************************************************************
56* Defined Constants And Macros *
57*******************************************************************************/
58#define VBOX_FLT_NB_TO_INST(pNB) RT_FROM_MEMBER(pNB, VBOXNETFLTINS, u.s.Notifier)
59#define VBOX_FLT_PT_TO_INST(pPT) RT_FROM_MEMBER(pPT, VBOXNETFLTINS, u.s.PacketType)
60#ifndef VBOXNETFLT_LINUX_NO_XMIT_QUEUE
61# define VBOX_FLT_XT_TO_INST(pXT) RT_FROM_MEMBER(pXT, VBOXNETFLTINS, u.s.XmitTask)
62#endif
63
64#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22)
65# define VBOX_SKB_RESET_NETWORK_HDR(skb) skb_reset_network_header(skb)
66# define VBOX_SKB_RESET_MAC_HDR(skb) skb_reset_mac_header(skb)
67#else
68# define VBOX_SKB_RESET_NETWORK_HDR(skb) skb->nh.raw = skb->data
69# define VBOX_SKB_RESET_MAC_HDR(skb) skb->mac.raw = skb->data
70#endif
71
72#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)
73# define VBOX_SKB_CHECKSUM_HELP(skb) skb_checksum_help(skb)
74#else
75# define CHECKSUM_PARTIAL CHECKSUM_HW
76# if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 10)
77# define VBOX_SKB_CHECKSUM_HELP(skb) skb_checksum_help(skb, 0)
78# else
79# if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 7)
80# define VBOX_SKB_CHECKSUM_HELP(skb) skb_checksum_help(&skb, 0)
81# else
82# define VBOX_SKB_CHECKSUM_HELP(skb) (!skb_checksum_help(skb))
83# endif
84/* Versions prior 2.6.10 use stats for both bstats and qstats */
85# define bstats stats
86# define qstats stats
87# endif
88#endif
89
90#ifndef NET_IP_ALIGN
91# define NET_IP_ALIGN 2
92#endif
93
94#if 0
95/** Create scatter / gather segments for fragments. When not used, we will
96 * linearize the socket buffer before creating the internal networking SG. */
97# define VBOXNETFLT_SG_SUPPORT 1
98#endif
99
100#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 18)
101/** Indicates that the linux kernel may send us GSO frames. */
102# define VBOXNETFLT_WITH_GSO 1
103
104/** This enables or disables the transmitting of GSO frame from the internal
105 * network and to the host. */
106# define VBOXNETFLT_WITH_GSO_XMIT_HOST 1
107
108# if 0 /** @todo This is currently disable because it causes performance loss of 5-10%. */
109/** This enables or disables the transmitting of GSO frame from the internal
110 * network and to the wire. */
111# define VBOXNETFLT_WITH_GSO_XMIT_WIRE 1
112# endif
113
114/** This enables or disables the forwarding/flooding of GSO frame from the host
115 * to the internal network. */
116# define VBOXNETFLT_WITH_GSO_RECV 1
117
118#endif
119
120#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29)
121/** This enables or disables handling of GSO frames coming from the wire (GRO). */
122# define VBOXNETFLT_WITH_GRO 1
123#endif
124/*
125 * GRO support was backported to RHEL 5.4
126 */
127#ifdef RHEL_RELEASE_CODE
128# if RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(5, 4)
129# define VBOXNETFLT_WITH_GRO 1
130# endif
131#endif
132
133/*******************************************************************************
134* Internal Functions *
135*******************************************************************************/
136static int VBoxNetFltLinuxInit(void);
137static void VBoxNetFltLinuxUnload(void);
138static void vboxNetFltLinuxForwardToIntNet(PVBOXNETFLTINS pThis, struct sk_buff *pBuf);
139
140
141/*******************************************************************************
142* Global Variables *
143*******************************************************************************/
144/**
145 * The (common) global data.
146 */
147static VBOXNETFLTGLOBALS g_VBoxNetFltGlobals;
148
149module_init(VBoxNetFltLinuxInit);
150module_exit(VBoxNetFltLinuxUnload);
151
152MODULE_AUTHOR(VBOX_VENDOR);
153MODULE_DESCRIPTION(VBOX_PRODUCT " Network Filter Driver");
154MODULE_LICENSE("GPL");
155#ifdef MODULE_VERSION
156MODULE_VERSION(VBOX_VERSION_STRING " (" RT_XSTR(INTNETTRUNKIFPORT_VERSION) ")");
157#endif
158
159
160#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 12) && defined(LOG_ENABLED)
161unsigned dev_get_flags(const struct net_device *dev)
162{
163 unsigned flags;
164
165 flags = (dev->flags & ~(IFF_PROMISC |
166 IFF_ALLMULTI |
167 IFF_RUNNING)) |
168 (dev->gflags & (IFF_PROMISC |
169 IFF_ALLMULTI));
170
171 if (netif_running(dev) && netif_carrier_ok(dev))
172 flags |= IFF_RUNNING;
173
174 return flags;
175}
176#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 12) */
177
178
179/**
180 * Initialize module.
181 *
182 * @returns appropriate status code.
183 */
184static int __init VBoxNetFltLinuxInit(void)
185{
186 int rc;
187 /*
188 * Initialize IPRT.
189 */
190 rc = RTR0Init(0);
191 if (RT_SUCCESS(rc))
192 {
193 Log(("VBoxNetFltLinuxInit\n"));
194
195 /*
196 * Initialize the globals and connect to the support driver.
197 *
198 * This will call back vboxNetFltOsOpenSupDrv (and maybe vboxNetFltOsCloseSupDrv)
199 * for establishing the connect to the support driver.
200 */
201 memset(&g_VBoxNetFltGlobals, 0, sizeof(g_VBoxNetFltGlobals));
202 rc = vboxNetFltInitGlobalsAndIdc(&g_VBoxNetFltGlobals);
203 if (RT_SUCCESS(rc))
204 {
205 LogRel(("VBoxNetFlt: Successfully started.\n"));
206 return 0;
207 }
208
209 LogRel(("VBoxNetFlt: failed to initialize device extension (rc=%d)\n", rc));
210 RTR0Term();
211 }
212 else
213 LogRel(("VBoxNetFlt: failed to initialize IPRT (rc=%d)\n", rc));
214
215 memset(&g_VBoxNetFltGlobals, 0, sizeof(g_VBoxNetFltGlobals));
216 return -RTErrConvertToErrno(rc);
217}
218
219
220/**
221 * Unload the module.
222 *
223 * @todo We have to prevent this if we're busy!
224 */
225static void __exit VBoxNetFltLinuxUnload(void)
226{
227 int rc;
228 Log(("VBoxNetFltLinuxUnload\n"));
229 Assert(vboxNetFltCanUnload(&g_VBoxNetFltGlobals));
230
231 /*
232 * Undo the work done during start (in reverse order).
233 */
234 rc = vboxNetFltTryDeleteIdcAndGlobals(&g_VBoxNetFltGlobals);
235 AssertRC(rc); NOREF(rc);
236
237 RTR0Term();
238
239 memset(&g_VBoxNetFltGlobals, 0, sizeof(g_VBoxNetFltGlobals));
240
241 Log(("VBoxNetFltLinuxUnload - done\n"));
242}
243
244
245/**
246 * We filter traffic from the host to the internal network
247 * before it reaches the NIC driver.
248 *
249 * The current code uses a very ugly hack overriding hard_start_xmit
250 * callback in the device structure, but it has been shown to give us a
251 * performance boost of 60-100% though. Eventually we have to find some
252 * less hacky way of getting this job done.
253 */
254#define VBOXNETFLT_WITH_HOST2WIRE_FILTER
255
256#ifdef VBOXNETFLT_WITH_HOST2WIRE_FILTER
257
258# if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29)
259
260# include <linux/ethtool.h>
261
262typedef struct ethtool_ops OVR_OPSTYPE;
263# define OVR_OPS ethtool_ops
264# define OVR_XMIT pfnStartXmit
265
266# else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29) */
267
268typedef struct net_device_ops OVR_OPSTYPE;
269# define OVR_OPS netdev_ops
270# define OVR_XMIT pOrgOps->ndo_start_xmit
271
272# endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29) */
273
274/**
275 * The overridden net_device_ops of the device we're attached to.
276 *
277 * As there is no net_device_ops structure in pre-2.6.29 kernels we override
278 * ethtool_ops instead along with hard_start_xmit callback in net_device
279 * structure.
280 *
281 * This is a very dirty hack that was created to explore how much we can improve
282 * the host to guest transfers by not CC'ing the NIC. It turns out to be
283 * the only way to filter outgoing packets for devices without TX queue.
284 */
285typedef struct VBoxNetDeviceOpsOverride
286{
287 /** Our overridden ops. */
288 OVR_OPSTYPE Ops;
289 /** Magic word. */
290 uint32_t u32Magic;
291 /** Pointer to the original ops. */
292 OVR_OPSTYPE const *pOrgOps;
293# if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29)
294 /** Pointer to the original hard_start_xmit function. */
295 int (*pfnStartXmit)(struct sk_buff *pSkb, struct net_device *pDev);
296# endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29) */
297 /** Pointer to the net filter instance. */
298 PVBOXNETFLTINS pVBoxNetFlt;
299 /** The number of filtered packages. */
300 uint64_t cFiltered;
301 /** The total number of packets */
302 uint64_t cTotal;
303} VBOXNETDEVICEOPSOVERRIDE, *PVBOXNETDEVICEOPSOVERRIDE;
304/** VBOXNETDEVICEOPSOVERRIDE::u32Magic value. */
305#define VBOXNETDEVICEOPSOVERRIDE_MAGIC UINT32_C(0x00c0ffee)
306
307/**
308 * ndo_start_xmit wrapper that drops packets that shouldn't go to the wire
309 * because they belong on the internal network.
310 *
311 * @returns NETDEV_TX_XXX.
312 * @param pSkb The socket buffer to transmit.
313 * @param pDev The net device.
314 */
315static int vboxNetFltLinuxStartXmitFilter(struct sk_buff *pSkb, struct net_device *pDev)
316{
317 PVBOXNETDEVICEOPSOVERRIDE pOverride = (PVBOXNETDEVICEOPSOVERRIDE)pDev->OVR_OPS;
318 uint8_t abHdrBuf[sizeof(RTNETETHERHDR) + sizeof(uint32_t) + RTNETIPV4_MIN_LEN];
319 PCRTNETETHERHDR pEtherHdr;
320 PINTNETTRUNKSWPORT pSwitchPort;
321 uint32_t cbHdrs;
322
323
324 /*
325 * Validate the override structure.
326 *
327 * Note! We're racing vboxNetFltLinuxUnhookDev here. If this was supposed
328 * to be production quality code, we would have to be much more
329 * careful here and avoid the race.
330 */
331 if ( !VALID_PTR(pOverride)
332 || pOverride->u32Magic != VBOXNETDEVICEOPSOVERRIDE_MAGIC
333# if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29)
334 || !VALID_PTR(pOverride->pOrgOps)
335# endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29) */
336 )
337 {
338 printk("vboxNetFltLinuxStartXmitFilter: bad override %p\n", pOverride);
339 dev_kfree_skb(pSkb);
340 return NETDEV_TX_OK;
341 }
342 pOverride->cTotal++;
343
344 /*
345 * Do the filtering base on the default OUI of our virtual NICs
346 *
347 * Note! In a real solution, we would ask the switch whether the
348 * destination MAC is 100% to be on the internal network and then
349 * drop it.
350 */
351 cbHdrs = skb_headlen(pSkb);
352 cbHdrs = RT_MIN(cbHdrs, sizeof(abHdrBuf));
353 pEtherHdr = (PCRTNETETHERHDR)skb_header_pointer(pSkb, 0, cbHdrs, &abHdrBuf[0]);
354 if ( pEtherHdr
355 && VALID_PTR(pOverride->pVBoxNetFlt)
356 && (pSwitchPort = pOverride->pVBoxNetFlt->pSwitchPort) != NULL
357 && VALID_PTR(pSwitchPort)
358 && cbHdrs >= 6)
359 {
360 INTNETSWDECISION enmDecision;
361
362 /** @todo consider reference counting, etc. */
363 enmDecision = pSwitchPort->pfnPreRecv(pSwitchPort, pEtherHdr, cbHdrs, INTNETTRUNKDIR_HOST);
364 if (enmDecision == INTNETSWDECISION_INTNET)
365 {
366 dev_kfree_skb(pSkb);
367 pOverride->cFiltered++;
368 return NETDEV_TX_OK;
369 }
370 }
371
372 return pOverride->OVR_XMIT(pSkb, pDev);
373}
374
375/**
376 * Hooks the device ndo_start_xmit operation of the device.
377 *
378 * @param pThis The net filter instance.
379 * @param pDev The net device.
380 */
381static void vboxNetFltLinuxHookDev(PVBOXNETFLTINS pThis, struct net_device *pDev)
382{
383 PVBOXNETDEVICEOPSOVERRIDE pOverride;
384
385 /* Cancel override if ethtool_ops is missing (host-only case, @bugref{5712}) */
386 if (!VALID_PTR(pDev->OVR_OPS))
387 return;
388 pOverride = RTMemAlloc(sizeof(*pOverride));
389 if (!pOverride)
390 return;
391 pOverride->pOrgOps = pDev->OVR_OPS;
392 pOverride->Ops = *pDev->OVR_OPS;
393# if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29)
394 pOverride->pfnStartXmit = pDev->hard_start_xmit;
395# else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29) */
396 pOverride->Ops.ndo_start_xmit = vboxNetFltLinuxStartXmitFilter;
397# endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29) */
398 pOverride->u32Magic = VBOXNETDEVICEOPSOVERRIDE_MAGIC;
399 pOverride->cTotal = 0;
400 pOverride->cFiltered = 0;
401 pOverride->pVBoxNetFlt = pThis;
402
403 RTSpinlockAcquire(pThis->hSpinlock); /* (this isn't necessary, but so what) */
404 ASMAtomicWritePtr((void * volatile *)&pDev->OVR_OPS, pOverride);
405# if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29)
406 ASMAtomicXchgPtr((void * volatile *)&pDev->hard_start_xmit, vboxNetFltLinuxStartXmitFilter);
407# endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29) */
408 RTSpinlockReleaseNoInts(pThis->hSpinlock);
409}
410
411/**
412 * Undos what vboxNetFltLinuxHookDev did.
413 *
414 * @param pThis The net filter instance.
415 * @param pDev The net device. Can be NULL, in which case
416 * we'll try retrieve it from @a pThis.
417 */
418static void vboxNetFltLinuxUnhookDev(PVBOXNETFLTINS pThis, struct net_device *pDev)
419{
420 PVBOXNETDEVICEOPSOVERRIDE pOverride;
421
422 RTSpinlockAcquire(pThis->hSpinlock);
423 if (!pDev)
424 pDev = ASMAtomicUoReadPtrT(&pThis->u.s.pDev, struct net_device *);
425 if (VALID_PTR(pDev))
426 {
427 pOverride = (PVBOXNETDEVICEOPSOVERRIDE)pDev->OVR_OPS;
428 if ( VALID_PTR(pOverride)
429 && pOverride->u32Magic == VBOXNETDEVICEOPSOVERRIDE_MAGIC
430 && VALID_PTR(pOverride->pOrgOps)
431 )
432 {
433# if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29)
434 ASMAtomicWritePtr((void * volatile *)&pDev->hard_start_xmit, pOverride->pfnStartXmit);
435# endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29) */
436 ASMAtomicWritePtr((void const * volatile *)&pDev->OVR_OPS, pOverride->pOrgOps);
437 ASMAtomicWriteU32(&pOverride->u32Magic, 0);
438 }
439 else
440 pOverride = NULL;
441 }
442 else
443 pOverride = NULL;
444 RTSpinlockReleaseNoInts(pThis->hSpinlock);
445
446 if (pOverride)
447 {
448 printk("vboxnetflt: %llu out of %llu packets were not sent (directed to host)\n", pOverride->cFiltered, pOverride->cTotal);
449 RTMemFree(pOverride);
450 }
451}
452
453#endif /* VBOXNETFLT_WITH_HOST2WIRE_FILTER */
454
455
456/**
457 * Reads and retains the host interface handle.
458 *
459 * @returns The handle, NULL if detached.
460 * @param pThis
461 */
462DECLINLINE(struct net_device *) vboxNetFltLinuxRetainNetDev(PVBOXNETFLTINS pThis)
463{
464#if 0
465 struct net_device *pDev = NULL;
466
467 Log(("vboxNetFltLinuxRetainNetDev\n"));
468 /*
469 * Be careful here to avoid problems racing the detached callback.
470 */
471 RTSpinlockAcquire(pThis->hSpinlock);
472 if (!ASMAtomicUoReadBool(&pThis->fDisconnectedFromHost))
473 {
474 pDev = (struct net_device *)ASMAtomicUoReadPtr((void * volatile *)&pThis->u.s.pDev);
475 if (pDev)
476 {
477 dev_hold(pDev);
478 Log(("vboxNetFltLinuxRetainNetDev: Device %p(%s) retained. ref=%d\n",
479 pDev, pDev->name,
480#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)
481 netdev_refcnt_read(pDev)
482#else
483 atomic_read(&pDev->refcnt)
484#endif
485 ));
486 }
487 }
488 RTSpinlockRelease(pThis->hSpinlock);
489
490 Log(("vboxNetFltLinuxRetainNetDev - done\n"));
491 return pDev;
492#else
493 return ASMAtomicUoReadPtrT(&pThis->u.s.pDev, struct net_device *);
494#endif
495}
496
497
498/**
499 * Release the host interface handle previously retained
500 * by vboxNetFltLinuxRetainNetDev.
501 *
502 * @param pThis The instance.
503 * @param pDev The vboxNetFltLinuxRetainNetDev
504 * return value, NULL is fine.
505 */
506DECLINLINE(void) vboxNetFltLinuxReleaseNetDev(PVBOXNETFLTINS pThis, struct net_device *pDev)
507{
508#if 0
509 Log(("vboxNetFltLinuxReleaseNetDev\n"));
510 NOREF(pThis);
511 if (pDev)
512 {
513 dev_put(pDev);
514 Log(("vboxNetFltLinuxReleaseNetDev: Device %p(%s) released. ref=%d\n",
515 pDev, pDev->name,
516#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)
517 netdev_refcnt_read(pDev)
518#else
519 atomic_read(&pDev->refcnt)
520#endif
521 ));
522 }
523 Log(("vboxNetFltLinuxReleaseNetDev - done\n"));
524#endif
525}
526
527#define VBOXNETFLT_CB_TAG(skb) (0xA1C90000 | (skb->dev->ifindex & 0xFFFF))
528#define VBOXNETFLT_SKB_TAG(skb) (*(uint32_t*)&((skb)->cb[sizeof((skb)->cb)-sizeof(uint32_t)]))
529
530/**
531 * Checks whether this is an mbuf created by vboxNetFltLinuxMBufFromSG,
532 * i.e. a buffer which we're pushing and should be ignored by the filter callbacks.
533 *
534 * @returns true / false accordingly.
535 * @param pBuf The sk_buff.
536 */
537DECLINLINE(bool) vboxNetFltLinuxSkBufIsOur(struct sk_buff *pBuf)
538{
539 return VBOXNETFLT_SKB_TAG(pBuf) == VBOXNETFLT_CB_TAG(pBuf);
540}
541
542
543/**
544 * Checks whether this SG list contains a GSO packet.
545 *
546 * @returns true / false accordingly.
547 * @param pSG The (scatter/)gather list.
548 */
549DECLINLINE(bool) vboxNetFltLinuxIsGso(PINTNETSG pSG)
550{
551#if defined(VBOXNETFLT_WITH_GSO_XMIT_WIRE) || defined(VBOXNETFLT_WITH_GSO_XMIT_HOST)
552 return !((PDMNETWORKGSOTYPE)pSG->GsoCtx.u8Type == PDMNETWORKGSOTYPE_INVALID);
553#else /* !VBOXNETFLT_WITH_GSO_XMIT_WIRE && !VBOXNETFLT_WITH_GSO_XMIT_HOST */
554 return false;
555#endif /* !VBOXNETFLT_WITH_GSO_XMIT_WIRE && !VBOXNETFLT_WITH_GSO_XMIT_HOST */
556}
557
558
559/**
560 * Find out the frame size (of a single segment in case of GSO frames).
561 *
562 * @returns the frame size.
563 * @param pSG The (scatter/)gather list.
564 */
565DECLINLINE(uint32_t) vboxNetFltLinuxFrameSize(PINTNETSG pSG)
566{
567 RTNETETHERHDR EthHdr;
568 uint16_t u16Type = 0;
569 uint32_t cbVlanTag = 0;
570 if (pSG->aSegs[0].cb >= sizeof(EthHdr))
571 u16Type = RT_BE2H_U16(((PCRTNETETHERHDR)pSG->aSegs[0].pv)->EtherType);
572 else if (pSG->cbTotal >= sizeof(EthHdr))
573 {
574 uint32_t i, uOffset = RT_OFFSETOF(RTNETETHERHDR, EtherType);
575 for (i = 0; i < pSG->cSegsUsed; ++i)
576 {
577 if (uOffset > pSG->aSegs[i].cb)
578 {
579 uOffset -= pSG->aSegs[i].cb;
580 continue;
581 }
582 if (uOffset + sizeof(uint16_t) > pSG->aSegs[i].cb)
583 {
584 if (i + 1 < pSG->cSegsUsed)
585 u16Type = RT_BE2H_U16( ((uint16_t)( ((uint8_t*)pSG->aSegs[i].pv)[uOffset] ) << 8)
586 + *(uint8_t*)pSG->aSegs[i + 1].pv);
587 }
588 else
589 u16Type = RT_BE2H_U16(*(uint16_t*)((uint8_t*)pSG->aSegs[i].pv + uOffset));
590 }
591 }
592 if (u16Type == RTNET_ETHERTYPE_VLAN)
593 cbVlanTag = 4;
594 return (vboxNetFltLinuxIsGso(pSG) ? (uint32_t)pSG->GsoCtx.cbMaxSeg + pSG->GsoCtx.cbHdrsTotal : pSG->cbTotal) - cbVlanTag;
595}
596
597
598/**
599 * Internal worker that create a linux sk_buff for a
600 * (scatter/)gather list.
601 *
602 * @returns Pointer to the sk_buff.
603 * @param pThis The instance.
604 * @param pSG The (scatter/)gather list.
605 * @param fDstWire Set if the destination is the wire.
606 */
607static struct sk_buff *vboxNetFltLinuxSkBufFromSG(PVBOXNETFLTINS pThis, PINTNETSG pSG, bool fDstWire)
608{
609 struct sk_buff *pPkt;
610 struct net_device *pDev;
611 unsigned fGsoType = 0;
612
613 if (pSG->cbTotal == 0)
614 {
615 LogRel(("VBoxNetFlt: Dropped empty packet coming from internal network.\n"));
616 return NULL;
617 }
618 Log5(("VBoxNetFlt: Packet to %s of %d bytes (frame=%d).\n", fDstWire?"wire":"host", pSG->cbTotal, vboxNetFltLinuxFrameSize(pSG)));
619 if (fDstWire && (vboxNetFltLinuxFrameSize(pSG) > ASMAtomicReadU32(&pThis->u.s.cbMtu) + 14))
620 {
621 static bool fOnce = true;
622 if (fOnce)
623 {
624 fOnce = false;
625 printk("VBoxNetFlt: Dropped over-sized packet (%d bytes) coming from internal network.\n", vboxNetFltLinuxFrameSize(pSG));
626 }
627 return NULL;
628 }
629
630 /** @todo We should use fragments mapping the SG buffers with large packets.
631 * 256 bytes seems to be the a threshold used a lot for this. It
632 * requires some nasty work on the intnet side though... */
633 /*
634 * Allocate a packet and copy over the data.
635 */
636 pDev = ASMAtomicUoReadPtrT(&pThis->u.s.pDev, struct net_device *);
637 pPkt = dev_alloc_skb(pSG->cbTotal + NET_IP_ALIGN);
638 if (RT_UNLIKELY(!pPkt))
639 {
640 Log(("vboxNetFltLinuxSkBufFromSG: Failed to allocate sk_buff(%u).\n", pSG->cbTotal));
641 pSG->pvUserData = NULL;
642 return NULL;
643 }
644 pPkt->dev = pDev;
645 pPkt->ip_summed = CHECKSUM_NONE;
646
647 /* Align IP header on 16-byte boundary: 2 + 14 (ethernet hdr size). */
648 skb_reserve(pPkt, NET_IP_ALIGN);
649
650 /* Copy the segments. */
651 skb_put(pPkt, pSG->cbTotal);
652 IntNetSgRead(pSG, pPkt->data);
653
654#if defined(VBOXNETFLT_WITH_GSO_XMIT_WIRE) || defined(VBOXNETFLT_WITH_GSO_XMIT_HOST)
655 /*
656 * Setup GSO if used by this packet.
657 */
658 switch ((PDMNETWORKGSOTYPE)pSG->GsoCtx.u8Type)
659 {
660 default:
661 AssertMsgFailed(("%u (%s)\n", pSG->GsoCtx.u8Type, PDMNetGsoTypeName((PDMNETWORKGSOTYPE)pSG->GsoCtx.u8Type) ));
662 /* fall thru */
663 case PDMNETWORKGSOTYPE_INVALID:
664 fGsoType = 0;
665 break;
666 case PDMNETWORKGSOTYPE_IPV4_TCP:
667 fGsoType = SKB_GSO_TCPV4;
668 break;
669 case PDMNETWORKGSOTYPE_IPV4_UDP:
670 fGsoType = SKB_GSO_UDP;
671 break;
672 case PDMNETWORKGSOTYPE_IPV6_TCP:
673 fGsoType = SKB_GSO_TCPV6;
674 break;
675 }
676 if (fGsoType)
677 {
678 struct skb_shared_info *pShInfo = skb_shinfo(pPkt);
679
680 pShInfo->gso_type = fGsoType | SKB_GSO_DODGY;
681 pShInfo->gso_size = pSG->GsoCtx.cbMaxSeg;
682 pShInfo->gso_segs = PDMNetGsoCalcSegmentCount(&pSG->GsoCtx, pSG->cbTotal);
683
684 /*
685 * We need to set checksum fields even if the packet goes to the host
686 * directly as it may be immediately forwarded by IP layer @bugref{5020}.
687 */
688 Assert(skb_headlen(pPkt) >= pSG->GsoCtx.cbHdrsTotal);
689 pPkt->ip_summed = CHECKSUM_PARTIAL;
690# if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22)
691 pPkt->csum_start = skb_headroom(pPkt) + pSG->GsoCtx.offHdr2;
692 if (fGsoType & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6))
693 pPkt->csum_offset = RT_OFFSETOF(RTNETTCP, th_sum);
694 else
695 pPkt->csum_offset = RT_OFFSETOF(RTNETUDP, uh_sum);
696# else
697 pPkt->h.raw = pPkt->data + pSG->GsoCtx.offHdr2;
698 if (fGsoType & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6))
699 pPkt->csum = RT_OFFSETOF(RTNETTCP, th_sum);
700 else
701 pPkt->csum = RT_OFFSETOF(RTNETUDP, uh_sum);
702# endif
703 if (!fDstWire)
704 PDMNetGsoPrepForDirectUse(&pSG->GsoCtx, pPkt->data, pSG->cbTotal, PDMNETCSUMTYPE_PSEUDO);
705 }
706#endif /* VBOXNETFLT_WITH_GSO_XMIT_WIRE || VBOXNETFLT_WITH_GSO_XMIT_HOST */
707
708 /*
709 * Finish up the socket buffer.
710 */
711 pPkt->protocol = eth_type_trans(pPkt, pDev);
712 if (fDstWire)
713 {
714 VBOX_SKB_RESET_NETWORK_HDR(pPkt);
715
716 /* Restore ethernet header back. */
717 skb_push(pPkt, ETH_HLEN); /** @todo VLAN: +4 if VLAN? */
718 VBOX_SKB_RESET_MAC_HDR(pPkt);
719 }
720 VBOXNETFLT_SKB_TAG(pPkt) = VBOXNETFLT_CB_TAG(pPkt);
721
722 return pPkt;
723}
724
725
726/**
727 * Initializes a SG list from an sk_buff.
728 *
729 * @returns Number of segments.
730 * @param pThis The instance.
731 * @param pBuf The sk_buff.
732 * @param pSG The SG.
733 * @param pvFrame The frame pointer, optional.
734 * @param cSegs The number of segments allocated for the SG.
735 * This should match the number in the mbuf exactly!
736 * @param fSrc The source of the frame.
737 * @param pGso Pointer to the GSO context if it's a GSO
738 * internal network frame. NULL if regular frame.
739 */
740DECLINLINE(void) vboxNetFltLinuxSkBufToSG(PVBOXNETFLTINS pThis, struct sk_buff *pBuf, PINTNETSG pSG,
741 unsigned cSegs, uint32_t fSrc, PCPDMNETWORKGSO pGsoCtx)
742{
743 int i;
744 NOREF(pThis);
745
746 Assert(!skb_shinfo(pBuf)->frag_list);
747
748 if (!pGsoCtx)
749 IntNetSgInitTempSegs(pSG, pBuf->len, cSegs, 0 /*cSegsUsed*/);
750 else
751 IntNetSgInitTempSegsGso(pSG, pBuf->len, cSegs, 0 /*cSegsUsed*/, pGsoCtx);
752
753#ifdef VBOXNETFLT_SG_SUPPORT
754 pSG->aSegs[0].cb = skb_headlen(pBuf);
755 pSG->aSegs[0].pv = pBuf->data;
756 pSG->aSegs[0].Phys = NIL_RTHCPHYS;
757
758 for (i = 0; i < skb_shinfo(pBuf)->nr_frags; i++)
759 {
760 skb_frag_t *pFrag = &skb_shinfo(pBuf)->frags[i];
761 pSG->aSegs[i+1].cb = pFrag->size;
762 pSG->aSegs[i+1].pv = kmap(pFrag->page);
763 printk("%p = kmap()\n", pSG->aSegs[i+1].pv);
764 pSG->aSegs[i+1].Phys = NIL_RTHCPHYS;
765 }
766 ++i;
767
768#else
769 pSG->aSegs[0].cb = pBuf->len;
770 pSG->aSegs[0].pv = pBuf->data;
771 pSG->aSegs[0].Phys = NIL_RTHCPHYS;
772 i = 1;
773#endif
774
775 pSG->cSegsUsed = i;
776
777#ifdef PADD_RUNT_FRAMES_FROM_HOST
778 /*
779 * Add a trailer if the frame is too small.
780 *
781 * Since we're getting to the packet before it is framed, it has not
782 * yet been padded. The current solution is to add a segment pointing
783 * to a buffer containing all zeros and pray that works for all frames...
784 */
785 if (pSG->cbTotal < 60 && (fSrc & INTNETTRUNKDIR_HOST))
786 {
787 static uint8_t const s_abZero[128] = {0};
788
789 AssertReturnVoid(i < cSegs);
790
791 pSG->aSegs[i].Phys = NIL_RTHCPHYS;
792 pSG->aSegs[i].pv = (void *)&s_abZero[0];
793 pSG->aSegs[i].cb = 60 - pSG->cbTotal;
794 pSG->cbTotal = 60;
795 pSG->cSegsUsed++;
796 Assert(i + 1 <= pSG->cSegsAlloc)
797 }
798#endif
799
800 Log4(("vboxNetFltLinuxSkBufToSG: allocated=%d, segments=%d frags=%d next=%p frag_list=%p pkt_type=%x fSrc=%x\n",
801 pSG->cSegsAlloc, pSG->cSegsUsed, skb_shinfo(pBuf)->nr_frags, pBuf->next, skb_shinfo(pBuf)->frag_list, pBuf->pkt_type, fSrc));
802 for (i = 0; i < pSG->cSegsUsed; i++)
803 Log4(("vboxNetFltLinuxSkBufToSG: #%d: cb=%d pv=%p\n",
804 i, pSG->aSegs[i].cb, pSG->aSegs[i].pv));
805}
806
807/**
808 * Packet handler,
809 *
810 * @returns 0 or EJUSTRETURN.
811 * @param pThis The instance.
812 * @param pMBuf The mbuf.
813 * @param pvFrame The start of the frame, optional.
814 * @param fSrc Where the packet (allegedly) comes from, one INTNETTRUNKDIR_* value.
815 * @param eProtocol The protocol.
816 */
817#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 14)
818static int vboxNetFltLinuxPacketHandler(struct sk_buff *pBuf,
819 struct net_device *pSkbDev,
820 struct packet_type *pPacketType,
821 struct net_device *pOrigDev)
822#else
823static int vboxNetFltLinuxPacketHandler(struct sk_buff *pBuf,
824 struct net_device *pSkbDev,
825 struct packet_type *pPacketType)
826#endif
827{
828 PVBOXNETFLTINS pThis;
829 struct net_device *pDev;
830 LogFlow(("vboxNetFltLinuxPacketHandler: pBuf=%p pSkbDev=%p pPacketType=%p\n",
831 pBuf, pSkbDev, pPacketType));
832#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 18)
833 Log3(("vboxNetFltLinuxPacketHandler: skb len=%u data_len=%u truesize=%u next=%p nr_frags=%u gso_size=%u gso_seqs=%u gso_type=%x frag_list=%p pkt_type=%x\n",
834 pBuf->len, pBuf->data_len, pBuf->truesize, pBuf->next, skb_shinfo(pBuf)->nr_frags, skb_shinfo(pBuf)->gso_size, skb_shinfo(pBuf)->gso_segs, skb_shinfo(pBuf)->gso_type, skb_shinfo(pBuf)->frag_list, pBuf->pkt_type));
835# if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22)
836 Log4(("vboxNetFltLinuxPacketHandler: packet dump follows:\n%.*Rhxd\n", pBuf->len-pBuf->data_len, skb_mac_header(pBuf)));
837# endif
838#else
839 Log3(("vboxNetFltLinuxPacketHandler: skb len=%u data_len=%u truesize=%u next=%p nr_frags=%u tso_size=%u tso_seqs=%u frag_list=%p pkt_type=%x\n",
840 pBuf->len, pBuf->data_len, pBuf->truesize, pBuf->next, skb_shinfo(pBuf)->nr_frags, skb_shinfo(pBuf)->tso_size, skb_shinfo(pBuf)->tso_segs, skb_shinfo(pBuf)->frag_list, pBuf->pkt_type));
841#endif
842 /*
843 * Drop it immediately?
844 */
845 if (!pBuf)
846 return 0;
847
848 if (pBuf->pkt_type == PACKET_LOOPBACK)
849 {
850 /*
851 * We are not interested in loopbacked packets as they will always have
852 * another copy going to the wire.
853 */
854 Log2(("vboxNetFltLinuxPacketHandler: dropped loopback packet (cb=%u)\n", pBuf->len));
855 dev_kfree_skb(pBuf); /* We must 'consume' all packets we get (@bugref{6539})! */
856 return 0;
857 }
858
859 pThis = VBOX_FLT_PT_TO_INST(pPacketType);
860 pDev = ASMAtomicUoReadPtrT(&pThis->u.s.pDev, struct net_device *);
861 if (pDev != pSkbDev)
862 {
863 Log(("vboxNetFltLinuxPacketHandler: Devices do not match, pThis may be wrong! pThis=%p\n", pThis));
864 kfree_skb(pBuf); /* This is a failure, so we use kfree_skb instead of dev_kfree_skb. */
865 return 0;
866 }
867
868 Log4(("vboxNetFltLinuxPacketHandler: pBuf->cb dump:\n%.*Rhxd\n", sizeof(pBuf->cb), pBuf->cb));
869 if (vboxNetFltLinuxSkBufIsOur(pBuf))
870 {
871 Log2(("vboxNetFltLinuxPacketHandler: got our own sk_buff, drop it.\n"));
872 dev_kfree_skb(pBuf);
873 return 0;
874 }
875
876#ifndef VBOXNETFLT_SG_SUPPORT
877 {
878 /*
879 * Get rid of fragmented packets, they cause too much trouble.
880 */
881 unsigned int uMacLen = pBuf->mac_len;
882 struct sk_buff *pCopy = skb_copy(pBuf, GFP_ATOMIC);
883 dev_kfree_skb(pBuf);
884 if (!pCopy)
885 {
886 LogRel(("VBoxNetFlt: Failed to allocate packet buffer, dropping the packet.\n"));
887 return 0;
888 }
889 pBuf = pCopy;
890 /* Somehow skb_copy ignores mac_len */
891 pBuf->mac_len = uMacLen;
892# if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)
893 /* Restore VLAN tag stripped by host hardware */
894 if (vlan_tx_tag_present(pBuf) && skb_headroom(pBuf) >= VLAN_ETH_HLEN)
895 {
896 uint8_t *pMac = (uint8_t*)skb_mac_header(pBuf);
897 struct vlan_ethhdr *pVHdr = (struct vlan_ethhdr *)(pMac - VLAN_HLEN);
898# if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)
899 memmove(pVHdr, pMac, ETH_ALEN * 2);
900# else
901 memmove(pVHdr, pMac, VLAN_ETH_ALEN * 2);
902# endif
903 pVHdr->h_vlan_proto = RT_H2N_U16(ETH_P_8021Q);
904 pVHdr->h_vlan_TCI = RT_H2N_U16(vlan_tx_tag_get(pBuf));
905 pBuf->mac_header -= VLAN_HLEN;
906 pBuf->mac_len += VLAN_HLEN;
907 }
908# endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27) */
909
910# if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 18)
911 Log3(("vboxNetFltLinuxPacketHandler: skb copy len=%u data_len=%u truesize=%u next=%p nr_frags=%u gso_size=%u gso_seqs=%u gso_type=%x frag_list=%p pkt_type=%x\n",
912 pBuf->len, pBuf->data_len, pBuf->truesize, pBuf->next, skb_shinfo(pBuf)->nr_frags, skb_shinfo(pBuf)->gso_size, skb_shinfo(pBuf)->gso_segs, skb_shinfo(pBuf)->gso_type, skb_shinfo(pBuf)->frag_list, pBuf->pkt_type));
913# if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22)
914 Log4(("vboxNetFltLinuxPacketHandler: packet dump follows:\n%.*Rhxd\n", pBuf->len-pBuf->data_len, skb_mac_header(pBuf)));
915# endif
916# else
917 Log3(("vboxNetFltLinuxPacketHandler: skb copy len=%u data_len=%u truesize=%u next=%p nr_frags=%u tso_size=%u tso_seqs=%u frag_list=%p pkt_type=%x\n",
918 pBuf->len, pBuf->data_len, pBuf->truesize, pBuf->next, skb_shinfo(pBuf)->nr_frags, skb_shinfo(pBuf)->tso_size, skb_shinfo(pBuf)->tso_segs, skb_shinfo(pBuf)->frag_list, pBuf->pkt_type));
919# endif
920 }
921#endif
922
923#ifdef VBOXNETFLT_LINUX_NO_XMIT_QUEUE
924 /* Forward it to the internal network. */
925 vboxNetFltLinuxForwardToIntNet(pThis, pBuf);
926#else
927 /* Add the packet to transmit queue and schedule the bottom half. */
928 skb_queue_tail(&pThis->u.s.XmitQueue, pBuf);
929 schedule_work(&pThis->u.s.XmitTask);
930 Log4(("vboxNetFltLinuxPacketHandler: scheduled work %p for sk_buff %p\n",
931 &pThis->u.s.XmitTask, pBuf));
932#endif
933
934 /* It does not really matter what we return, it is ignored by the kernel. */
935 return 0;
936}
937
938/**
939 * Calculate the number of INTNETSEG segments the socket buffer will need.
940 *
941 * @returns Segment count.
942 * @param pBuf The socket buffer.
943 */
944DECLINLINE(unsigned) vboxNetFltLinuxCalcSGSegments(struct sk_buff *pBuf)
945{
946#ifdef VBOXNETFLT_SG_SUPPORT
947 unsigned cSegs = 1 + skb_shinfo(pBuf)->nr_frags;
948#else
949 unsigned cSegs = 1;
950#endif
951#ifdef PADD_RUNT_FRAMES_FROM_HOST
952 /* vboxNetFltLinuxSkBufToSG adds a padding segment if it's a runt. */
953 if (pBuf->len < 60)
954 cSegs++;
955#endif
956 return cSegs;
957}
958
959/**
960 * Destroy the intnet scatter / gather buffer created by
961 * vboxNetFltLinuxSkBufToSG.
962 */
963static void vboxNetFltLinuxDestroySG(PINTNETSG pSG)
964{
965#ifdef VBOXNETFLT_SG_SUPPORT
966 int i;
967
968 for (i = 0; i < skb_shinfo(pBuf)->nr_frags; i++)
969 {
970 printk("kunmap(%p)\n", pSG->aSegs[i+1].pv);
971 kunmap(pSG->aSegs[i+1].pv);
972 }
973#endif
974 NOREF(pSG);
975}
976
977#ifdef LOG_ENABLED
978/**
979 * Logging helper.
980 */
981static void vboxNetFltDumpPacket(PINTNETSG pSG, bool fEgress, const char *pszWhere, int iIncrement)
982{
983 int i, offSeg;
984 uint8_t *pInt, *pExt;
985 static int iPacketNo = 1;
986 iPacketNo += iIncrement;
987 if (fEgress)
988 {
989 pExt = pSG->aSegs[0].pv;
990 pInt = pExt + 6;
991 }
992 else
993 {
994 pInt = pSG->aSegs[0].pv;
995 pExt = pInt + 6;
996 }
997 Log(("VBoxNetFlt: (int)%02x:%02x:%02x:%02x:%02x:%02x"
998 " %s (%s)%02x:%02x:%02x:%02x:%02x:%02x (%u bytes) packet #%u\n",
999 pInt[0], pInt[1], pInt[2], pInt[3], pInt[4], pInt[5],
1000 fEgress ? "-->" : "<--", pszWhere,
1001 pExt[0], pExt[1], pExt[2], pExt[3], pExt[4], pExt[5],
1002 pSG->cbTotal, iPacketNo));
1003 if (pSG->cSegsUsed == 1)
1004 {
1005 Log3(("%.*Rhxd\n", pSG->aSegs[0].cb, pSG->aSegs[0].pv));
1006 }
1007 else
1008 {
1009 for (i = 0, offSeg = 0; i < pSG->cSegsUsed; i++)
1010 {
1011 Log3(("-- segment %d at 0x%x (%d bytes) --\n%.*Rhxd\n",
1012 i, offSeg, pSG->aSegs[i].cb, pSG->aSegs[i].cb, pSG->aSegs[i].pv));
1013 offSeg += pSG->aSegs[i].cb;
1014 }
1015 }
1016
1017}
1018#else
1019# define vboxNetFltDumpPacket(a, b, c, d) do {} while (0)
1020#endif
1021
1022#ifdef VBOXNETFLT_WITH_GSO_RECV
1023
1024/**
1025 * Worker for vboxNetFltLinuxForwardToIntNet that checks if we can forwards a
1026 * GSO socket buffer without having to segment it.
1027 *
1028 * @returns true on success, false if needs segmenting.
1029 * @param pThis The net filter instance.
1030 * @param pSkb The GSO socket buffer.
1031 * @param fSrc The source.
1032 * @param pGsoCtx Where to return the GSO context on success.
1033 */
1034static bool vboxNetFltLinuxCanForwardAsGso(PVBOXNETFLTINS pThis, struct sk_buff *pSkb, uint32_t fSrc,
1035 PPDMNETWORKGSO pGsoCtx)
1036{
1037 PDMNETWORKGSOTYPE enmGsoType;
1038 uint16_t uEtherType;
1039 unsigned int cbTransport;
1040 unsigned int offTransport;
1041 unsigned int cbTransportHdr;
1042 unsigned uProtocol;
1043 union
1044 {
1045 RTNETIPV4 IPv4;
1046 RTNETIPV6 IPv6;
1047 RTNETTCP Tcp;
1048 uint8_t ab[40];
1049 uint16_t au16[40/2];
1050 uint32_t au32[40/4];
1051 } Buf;
1052
1053 /*
1054 * Check the GSO properties of the socket buffer and make sure it fits.
1055 */
1056 /** @todo Figure out how to handle SKB_GSO_TCP_ECN! */
1057 if (RT_UNLIKELY( skb_shinfo(pSkb)->gso_type & ~(SKB_GSO_UDP | SKB_GSO_DODGY | SKB_GSO_TCPV6 | SKB_GSO_TCPV4) ))
1058 {
1059 Log5(("vboxNetFltLinuxCanForwardAsGso: gso_type=%#x\n", skb_shinfo(pSkb)->gso_type));
1060 return false;
1061 }
1062 if (RT_UNLIKELY( skb_shinfo(pSkb)->gso_size < 1
1063 || pSkb->len > VBOX_MAX_GSO_SIZE ))
1064 {
1065 Log5(("vboxNetFltLinuxCanForwardAsGso: gso_size=%#x skb_len=%#x (max=%#x)\n", skb_shinfo(pSkb)->gso_size, pSkb->len, VBOX_MAX_GSO_SIZE));
1066 return false;
1067 }
1068 /*
1069 * It is possible to receive GSO packets from wire if GRO is enabled.
1070 */
1071 if (RT_UNLIKELY(fSrc & INTNETTRUNKDIR_WIRE))
1072 {
1073 Log5(("vboxNetFltLinuxCanForwardAsGso: fSrc=wire\n"));
1074#ifdef VBOXNETFLT_WITH_GRO
1075 /*
1076 * The packet came from the wire and the driver has already consumed
1077 * mac header. We need to restore it back.
1078 */
1079 pSkb->mac_len = skb_network_header(pSkb) - skb_mac_header(pSkb);
1080 skb_push(pSkb, pSkb->mac_len);
1081 Log5(("vboxNetFltLinuxCanForwardAsGso: mac_len=%d data=%p mac_header=%p network_header=%p\n",
1082 pSkb->mac_len, pSkb->data, skb_mac_header(pSkb), skb_network_header(pSkb)));
1083#else /* !VBOXNETFLT_WITH_GRO */
1084 /* Older kernels didn't have GRO. */
1085 return false;
1086#endif /* !VBOXNETFLT_WITH_GRO */
1087 }
1088 else
1089 {
1090 /*
1091 * skb_gso_segment does the following. Do we need to do it as well?
1092 */
1093#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22)
1094 skb_reset_mac_header(pSkb);
1095 pSkb->mac_len = pSkb->network_header - pSkb->mac_header;
1096#else
1097 pSkb->mac.raw = pSkb->data;
1098 pSkb->mac_len = pSkb->nh.raw - pSkb->data;
1099#endif
1100 }
1101
1102 /*
1103 * Switch on the ethertype.
1104 */
1105 uEtherType = pSkb->protocol;
1106 if ( uEtherType == RT_H2N_U16_C(RTNET_ETHERTYPE_VLAN)
1107 && pSkb->mac_len == sizeof(RTNETETHERHDR) + sizeof(uint32_t))
1108 {
1109 uint16_t const *puEtherType = skb_header_pointer(pSkb, sizeof(RTNETETHERHDR) + sizeof(uint16_t), sizeof(uint16_t), &Buf);
1110 if (puEtherType)
1111 uEtherType = *puEtherType;
1112 }
1113 switch (uEtherType)
1114 {
1115 case RT_H2N_U16_C(RTNET_ETHERTYPE_IPV4):
1116 {
1117 unsigned int cbHdr;
1118 PCRTNETIPV4 pIPv4 = (PCRTNETIPV4)skb_header_pointer(pSkb, pSkb->mac_len, sizeof(Buf.IPv4), &Buf);
1119 if (RT_UNLIKELY(!pIPv4))
1120 {
1121 Log5(("vboxNetFltLinuxCanForwardAsGso: failed to access IPv4 hdr\n"));
1122 return false;
1123 }
1124
1125 cbHdr = pIPv4->ip_hl * 4;
1126 cbTransport = RT_N2H_U16(pIPv4->ip_len);
1127 if (RT_UNLIKELY( cbHdr < RTNETIPV4_MIN_LEN
1128 || cbHdr > cbTransport ))
1129 {
1130 Log5(("vboxNetFltLinuxCanForwardAsGso: invalid IPv4 lengths: ip_hl=%u ip_len=%u\n", pIPv4->ip_hl, RT_N2H_U16(pIPv4->ip_len)));
1131 return false;
1132 }
1133 cbTransport -= cbHdr;
1134 offTransport = pSkb->mac_len + cbHdr;
1135 uProtocol = pIPv4->ip_p;
1136 if (uProtocol == RTNETIPV4_PROT_TCP)
1137 enmGsoType = PDMNETWORKGSOTYPE_IPV4_TCP;
1138 else if (uProtocol == RTNETIPV4_PROT_UDP)
1139 enmGsoType = PDMNETWORKGSOTYPE_IPV4_UDP;
1140 else /** @todo IPv6: 4to6 tunneling */
1141 enmGsoType = PDMNETWORKGSOTYPE_INVALID;
1142 break;
1143 }
1144
1145 case RT_H2N_U16_C(RTNET_ETHERTYPE_IPV6):
1146 {
1147 PCRTNETIPV6 pIPv6 = (PCRTNETIPV6)skb_header_pointer(pSkb, pSkb->mac_len, sizeof(Buf.IPv6), &Buf);
1148 if (RT_UNLIKELY(!pIPv6))
1149 {
1150 Log5(("vboxNetFltLinuxCanForwardAsGso: failed to access IPv6 hdr\n"));
1151 return false;
1152 }
1153
1154 cbTransport = RT_N2H_U16(pIPv6->ip6_plen);
1155 offTransport = pSkb->mac_len + sizeof(RTNETIPV6);
1156 uProtocol = pIPv6->ip6_nxt;
1157 /** @todo IPv6: Dig our way out of the other headers. */
1158 if (uProtocol == RTNETIPV4_PROT_TCP)
1159 enmGsoType = PDMNETWORKGSOTYPE_IPV6_TCP;
1160 else if (uProtocol == RTNETIPV4_PROT_UDP)
1161 enmGsoType = PDMNETWORKGSOTYPE_IPV4_UDP;
1162 else
1163 enmGsoType = PDMNETWORKGSOTYPE_INVALID;
1164 break;
1165 }
1166
1167 default:
1168 Log5(("vboxNetFltLinuxCanForwardAsGso: uEtherType=%#x\n", RT_H2N_U16(uEtherType)));
1169 return false;
1170 }
1171
1172 if (enmGsoType == PDMNETWORKGSOTYPE_INVALID)
1173 {
1174 Log5(("vboxNetFltLinuxCanForwardAsGso: Unsupported protocol %d\n", uProtocol));
1175 return false;
1176 }
1177
1178 if (RT_UNLIKELY( offTransport + cbTransport <= offTransport
1179 || offTransport + cbTransport > pSkb->len
1180 || cbTransport < (uProtocol == RTNETIPV4_PROT_TCP ? RTNETTCP_MIN_LEN : RTNETUDP_MIN_LEN)) )
1181 {
1182 Log5(("vboxNetFltLinuxCanForwardAsGso: Bad transport length; off=%#x + cb=%#x => %#x; skb_len=%#x (%s)\n",
1183 offTransport, cbTransport, offTransport + cbTransport, pSkb->len, PDMNetGsoTypeName(enmGsoType) ));
1184 return false;
1185 }
1186
1187 /*
1188 * Check the TCP/UDP bits.
1189 */
1190 if (uProtocol == RTNETIPV4_PROT_TCP)
1191 {
1192 PCRTNETTCP pTcp = (PCRTNETTCP)skb_header_pointer(pSkb, offTransport, sizeof(Buf.Tcp), &Buf);
1193 if (RT_UNLIKELY(!pTcp))
1194 {
1195 Log5(("vboxNetFltLinuxCanForwardAsGso: failed to access TCP hdr\n"));
1196 return false;
1197 }
1198
1199 cbTransportHdr = pTcp->th_off * 4;
1200 pGsoCtx->cbHdrsSeg = offTransport + cbTransportHdr;
1201 if (RT_UNLIKELY( cbTransportHdr < RTNETTCP_MIN_LEN
1202 || cbTransportHdr > cbTransport
1203 || offTransport + cbTransportHdr >= UINT8_MAX
1204 || offTransport + cbTransportHdr >= pSkb->len ))
1205 {
1206 Log5(("vboxNetFltLinuxCanForwardAsGso: No space for TCP header; off=%#x cb=%#x skb_len=%#x\n", offTransport, cbTransportHdr, pSkb->len));
1207 return false;
1208 }
1209
1210 }
1211 else
1212 {
1213 Assert(uProtocol == RTNETIPV4_PROT_UDP);
1214 cbTransportHdr = sizeof(RTNETUDP);
1215 pGsoCtx->cbHdrsSeg = offTransport; /* Exclude UDP header */
1216 if (RT_UNLIKELY( offTransport + cbTransportHdr >= UINT8_MAX
1217 || offTransport + cbTransportHdr >= pSkb->len ))
1218 {
1219 Log5(("vboxNetFltLinuxCanForwardAsGso: No space for UDP header; off=%#x skb_len=%#x\n", offTransport, pSkb->len));
1220 return false;
1221 }
1222 }
1223
1224 /*
1225 * We're good, init the GSO context.
1226 */
1227 pGsoCtx->u8Type = enmGsoType;
1228 pGsoCtx->cbHdrsTotal = offTransport + cbTransportHdr;
1229 pGsoCtx->cbMaxSeg = skb_shinfo(pSkb)->gso_size;
1230 pGsoCtx->offHdr1 = pSkb->mac_len;
1231 pGsoCtx->offHdr2 = offTransport;
1232 pGsoCtx->u8Unused = 0;
1233
1234 return true;
1235}
1236
1237/**
1238 * Forward the socket buffer as a GSO internal network frame.
1239 *
1240 * @returns IPRT status code.
1241 * @param pThis The net filter instance.
1242 * @param pSkb The GSO socket buffer.
1243 * @param fSrc The source.
1244 * @param pGsoCtx Where to return the GSO context on success.
1245 */
1246static int vboxNetFltLinuxForwardAsGso(PVBOXNETFLTINS pThis, struct sk_buff *pSkb, uint32_t fSrc, PCPDMNETWORKGSO pGsoCtx)
1247{
1248 int rc;
1249 unsigned cSegs = vboxNetFltLinuxCalcSGSegments(pSkb);
1250 if (RT_LIKELY(cSegs <= MAX_SKB_FRAGS + 1))
1251 {
1252 PINTNETSG pSG = (PINTNETSG)alloca(RT_OFFSETOF(INTNETSG, aSegs[cSegs]));
1253 if (RT_LIKELY(pSG))
1254 {
1255 vboxNetFltLinuxSkBufToSG(pThis, pSkb, pSG, cSegs, fSrc, pGsoCtx);
1256
1257 vboxNetFltDumpPacket(pSG, false, (fSrc & INTNETTRUNKDIR_HOST) ? "host" : "wire", 1);
1258 pThis->pSwitchPort->pfnRecv(pThis->pSwitchPort, NULL /* pvIf */, pSG, fSrc);
1259
1260 vboxNetFltLinuxDestroySG(pSG);
1261 rc = VINF_SUCCESS;
1262 }
1263 else
1264 {
1265 Log(("VBoxNetFlt: Dropping the sk_buff (failure case).\n"));
1266 rc = VERR_NO_MEMORY;
1267 }
1268 }
1269 else
1270 {
1271 Log(("VBoxNetFlt: Bad sk_buff? cSegs=%#x.\n", cSegs));
1272 rc = VERR_INTERNAL_ERROR_3;
1273 }
1274
1275 Log4(("VBoxNetFlt: Dropping the sk_buff.\n"));
1276 dev_kfree_skb(pSkb);
1277 return rc;
1278}
1279
1280#endif /* VBOXNETFLT_WITH_GSO_RECV */
1281
1282/**
1283 * Worker for vboxNetFltLinuxForwardToIntNet.
1284 *
1285 * @returns VINF_SUCCESS or VERR_NO_MEMORY.
1286 * @param pThis The net filter instance.
1287 * @param pBuf The socket buffer.
1288 * @param fSrc The source.
1289 */
1290static int vboxNetFltLinuxForwardSegment(PVBOXNETFLTINS pThis, struct sk_buff *pBuf, uint32_t fSrc)
1291{
1292 int rc;
1293 unsigned cSegs = vboxNetFltLinuxCalcSGSegments(pBuf);
1294 if (cSegs <= MAX_SKB_FRAGS + 1)
1295 {
1296 PINTNETSG pSG = (PINTNETSG)alloca(RT_OFFSETOF(INTNETSG, aSegs[cSegs]));
1297 if (RT_LIKELY(pSG))
1298 {
1299 if (fSrc & INTNETTRUNKDIR_WIRE)
1300 {
1301 /*
1302 * The packet came from wire, ethernet header was removed by device driver.
1303 * Restore it using mac_len field. This takes into account VLAN headers too.
1304 */
1305 skb_push(pBuf, pBuf->mac_len);
1306 }
1307
1308 vboxNetFltLinuxSkBufToSG(pThis, pBuf, pSG, cSegs, fSrc, NULL /*pGsoCtx*/);
1309
1310 vboxNetFltDumpPacket(pSG, false, (fSrc & INTNETTRUNKDIR_HOST) ? "host" : "wire", 1);
1311 pThis->pSwitchPort->pfnRecv(pThis->pSwitchPort, NULL /* pvIf */, pSG, fSrc);
1312
1313 vboxNetFltLinuxDestroySG(pSG);
1314 rc = VINF_SUCCESS;
1315 }
1316 else
1317 {
1318 Log(("VBoxNetFlt: Failed to allocate SG buffer.\n"));
1319 rc = VERR_NO_MEMORY;
1320 }
1321 }
1322 else
1323 {
1324 Log(("VBoxNetFlt: Bad sk_buff? cSegs=%#x.\n", cSegs));
1325 rc = VERR_INTERNAL_ERROR_3;
1326 }
1327
1328 Log4(("VBoxNetFlt: Dropping the sk_buff.\n"));
1329 dev_kfree_skb(pBuf);
1330 return rc;
1331}
1332
1333/**
1334 *
1335 * @param pBuf The socket buffer. This is consumed by this function.
1336 */
1337static void vboxNetFltLinuxForwardToIntNet(PVBOXNETFLTINS pThis, struct sk_buff *pBuf)
1338{
1339 uint32_t fSrc = pBuf->pkt_type == PACKET_OUTGOING ? INTNETTRUNKDIR_HOST : INTNETTRUNKDIR_WIRE;
1340
1341#ifdef VBOXNETFLT_WITH_GSO
1342 if (skb_is_gso(pBuf))
1343 {
1344 PDMNETWORKGSO GsoCtx;
1345 Log3(("vboxNetFltLinuxForwardToIntNet: skb len=%u data_len=%u truesize=%u next=%p nr_frags=%u gso_size=%u gso_seqs=%u gso_type=%x frag_list=%p pkt_type=%x ip_summed=%d\n",
1346 pBuf->len, pBuf->data_len, pBuf->truesize, pBuf->next, skb_shinfo(pBuf)->nr_frags, skb_shinfo(pBuf)->gso_size, skb_shinfo(pBuf)->gso_segs, skb_shinfo(pBuf)->gso_type, skb_shinfo(pBuf)->frag_list, pBuf->pkt_type, pBuf->ip_summed));
1347# ifdef VBOXNETFLT_WITH_GSO_RECV
1348 if ( (skb_shinfo(pBuf)->gso_type & (SKB_GSO_UDP | SKB_GSO_TCPV6 | SKB_GSO_TCPV4))
1349 && vboxNetFltLinuxCanForwardAsGso(pThis, pBuf, fSrc, &GsoCtx) )
1350 vboxNetFltLinuxForwardAsGso(pThis, pBuf, fSrc, &GsoCtx);
1351 else
1352# endif
1353 {
1354 /* Need to segment the packet */
1355 struct sk_buff *pNext;
1356 struct sk_buff *pSegment = skb_gso_segment(pBuf, 0 /*supported features*/);
1357 if (IS_ERR(pSegment))
1358 {
1359 dev_kfree_skb(pBuf);
1360 LogRel(("VBoxNetFlt: Failed to segment a packet (%d).\n", PTR_ERR(pSegment)));
1361 return;
1362 }
1363
1364 for (; pSegment; pSegment = pNext)
1365 {
1366 Log3(("vboxNetFltLinuxForwardToIntNet: segment len=%u data_len=%u truesize=%u next=%p nr_frags=%u gso_size=%u gso_seqs=%u gso_type=%x frag_list=%p pkt_type=%x\n",
1367 pSegment->len, pSegment->data_len, pSegment->truesize, pSegment->next, skb_shinfo(pSegment)->nr_frags, skb_shinfo(pSegment)->gso_size, skb_shinfo(pSegment)->gso_segs, skb_shinfo(pSegment)->gso_type, skb_shinfo(pSegment)->frag_list, pSegment->pkt_type));
1368 pNext = pSegment->next;
1369 pSegment->next = 0;
1370 vboxNetFltLinuxForwardSegment(pThis, pSegment, fSrc);
1371 }
1372 dev_kfree_skb(pBuf);
1373 }
1374 }
1375 else
1376#endif /* VBOXNETFLT_WITH_GSO */
1377 {
1378 if (pBuf->ip_summed == CHECKSUM_PARTIAL && pBuf->pkt_type == PACKET_OUTGOING)
1379 {
1380#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 18)
1381 /*
1382 * Try to work around the problem with CentOS 4.7 and 5.2 (2.6.9
1383 * and 2.6.18 kernels), they pass wrong 'h' pointer down. We take IP
1384 * header length from the header itself and reconstruct 'h' pointer
1385 * to TCP (or whatever) header.
1386 */
1387 unsigned char *tmp = pBuf->h.raw;
1388 if (pBuf->h.raw == pBuf->nh.raw && pBuf->protocol == htons(ETH_P_IP))
1389 pBuf->h.raw = pBuf->nh.raw + pBuf->nh.iph->ihl * 4;
1390#endif /* LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 18) */
1391 if (VBOX_SKB_CHECKSUM_HELP(pBuf))
1392 {
1393 LogRel(("VBoxNetFlt: Failed to compute checksum, dropping the packet.\n"));
1394 dev_kfree_skb(pBuf);
1395 return;
1396 }
1397#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 18)
1398 /* Restore the original (wrong) pointer. */
1399 pBuf->h.raw = tmp;
1400#endif /* LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 18) */
1401 }
1402 vboxNetFltLinuxForwardSegment(pThis, pBuf, fSrc);
1403 }
1404}
1405
1406#ifndef VBOXNETFLT_LINUX_NO_XMIT_QUEUE
1407/**
1408 * Work queue handler that forwards the socket buffers queued by
1409 * vboxNetFltLinuxPacketHandler to the internal network.
1410 *
1411 * @param pWork The work queue.
1412 */
1413# if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20)
1414static void vboxNetFltLinuxXmitTask(struct work_struct *pWork)
1415# else
1416static void vboxNetFltLinuxXmitTask(void *pWork)
1417# endif
1418{
1419 PVBOXNETFLTINS pThis = VBOX_FLT_XT_TO_INST(pWork);
1420 struct sk_buff *pBuf;
1421
1422 Log4(("vboxNetFltLinuxXmitTask: Got work %p.\n", pWork));
1423
1424 /*
1425 * Active? Retain the instance and increment the busy counter.
1426 */
1427 if (vboxNetFltTryRetainBusyActive(pThis))
1428 {
1429 while ((pBuf = skb_dequeue(&pThis->u.s.XmitQueue)) != NULL)
1430 vboxNetFltLinuxForwardToIntNet(pThis, pBuf);
1431
1432 vboxNetFltRelease(pThis, true /* fBusy */);
1433 }
1434 else
1435 {
1436 /** @todo Shouldn't we just drop the packets here? There is little point in
1437 * making them accumulate when the VM is paused and it'll only waste
1438 * kernel memory anyway... Hmm. maybe wait a short while (2-5 secs)
1439 * before start draining the packets (goes for the intnet ring buf
1440 * too)? */
1441 }
1442}
1443#endif /* !VBOXNETFLT_LINUX_NO_XMIT_QUEUE */
1444
1445/**
1446 * Reports the GSO capabilities of the hardware NIC.
1447 *
1448 * @param pThis The net filter instance. The caller hold a
1449 * reference to this.
1450 */
1451static void vboxNetFltLinuxReportNicGsoCapabilities(PVBOXNETFLTINS pThis)
1452{
1453#ifdef VBOXNETFLT_WITH_GSO_XMIT_WIRE
1454 if (vboxNetFltTryRetainBusyNotDisconnected(pThis))
1455 {
1456 struct net_device *pDev;
1457 PINTNETTRUNKSWPORT pSwitchPort;
1458 unsigned int fFeatures;
1459
1460 RTSpinlockAcquire(pThis->hSpinlock);
1461
1462 pSwitchPort = pThis->pSwitchPort; /* this doesn't need to be here, but it doesn't harm. */
1463 pDev = ASMAtomicUoReadPtrT(&pThis->u.s.pDev, struct net_device *);
1464 if (pDev)
1465 fFeatures = pDev->features;
1466 else
1467 fFeatures = 0;
1468
1469 RTSpinlockReleaseNoInts(pThis->hSpinlock);
1470
1471 if (pThis->pSwitchPort)
1472 {
1473 /* Set/update the GSO capabilities of the NIC. */
1474 uint32_t fGsoCapabilites = 0;
1475 if (fFeatures & NETIF_F_TSO)
1476 fGsoCapabilites |= RT_BIT_32(PDMNETWORKGSOTYPE_IPV4_TCP);
1477 if (fFeatures & NETIF_F_TSO6)
1478 fGsoCapabilites |= RT_BIT_32(PDMNETWORKGSOTYPE_IPV6_TCP);
1479# if 0 /** @todo GSO: Test UDP offloading (UFO) on linux. */
1480 if (fFeatures & NETIF_F_UFO)
1481 fGsoCapabilites |= RT_BIT_32(PDMNETWORKGSOTYPE_IPV4_UDP);
1482 if (fFeatures & NETIF_F_UFO)
1483 fGsoCapabilites |= RT_BIT_32(PDMNETWORKGSOTYPE_IPV6_UDP);
1484# endif
1485 Log3(("vboxNetFltLinuxReportNicGsoCapabilities: reporting wire %s%s%s%s\n",
1486 (fGsoCapabilites & RT_BIT_32(PDMNETWORKGSOTYPE_IPV4_TCP)) ? "tso " : "",
1487 (fGsoCapabilites & RT_BIT_32(PDMNETWORKGSOTYPE_IPV6_TCP)) ? "tso6 " : "",
1488 (fGsoCapabilites & RT_BIT_32(PDMNETWORKGSOTYPE_IPV4_UDP)) ? "ufo " : "",
1489 (fGsoCapabilites & RT_BIT_32(PDMNETWORKGSOTYPE_IPV6_UDP)) ? "ufo6 " : ""));
1490 pThis->pSwitchPort->pfnReportGsoCapabilities(pThis->pSwitchPort, fGsoCapabilites, INTNETTRUNKDIR_WIRE);
1491 }
1492
1493 vboxNetFltRelease(pThis, true /*fBusy*/);
1494 }
1495#endif /* VBOXNETFLT_WITH_GSO_XMIT_WIRE */
1496}
1497
1498/**
1499 * Helper that determines whether the host (ignoreing us) is operating the
1500 * interface in promiscuous mode or not.
1501 */
1502static bool vboxNetFltLinuxPromiscuous(PVBOXNETFLTINS pThis)
1503{
1504 bool fRc = false;
1505 struct net_device * pDev = vboxNetFltLinuxRetainNetDev(pThis);
1506 if (pDev)
1507 {
1508 fRc = !!(pDev->promiscuity - (ASMAtomicUoReadBool(&pThis->u.s.fPromiscuousSet) & 1));
1509 LogFlow(("vboxNetFltPortOsIsPromiscuous: returns %d, pDev->promiscuity=%d, fPromiscuousSet=%d\n",
1510 fRc, pDev->promiscuity, pThis->u.s.fPromiscuousSet));
1511 vboxNetFltLinuxReleaseNetDev(pThis, pDev);
1512 }
1513 return fRc;
1514}
1515
1516#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36)
1517/**
1518 * Helper for detecting TAP devices.
1519 */
1520static bool vboxNetFltIsTapDevice(PVBOXNETFLTINS pThis, struct net_device *pDev)
1521{
1522 if (pDev->ethtool_ops && pDev->ethtool_ops->get_drvinfo)
1523 {
1524 struct ethtool_drvinfo Info;
1525
1526 memset(&Info, 0, sizeof(Info));
1527 Info.cmd = ETHTOOL_GDRVINFO;
1528 pDev->ethtool_ops->get_drvinfo(pDev, &Info);
1529 Log3(("vboxNetFltIsTapDevice: driver=%s version=%s bus_info=%s\n",
1530 Info.driver, Info.version, Info.bus_info));
1531
1532 return !strncmp(Info.driver, "tun", 4)
1533 && !strncmp(Info.bus_info, "tap", 4);
1534 }
1535
1536 return false;
1537}
1538
1539/**
1540 * Helper for updating the link state of TAP devices.
1541 * Only TAP devices are affected.
1542 */
1543static void vboxNetFltSetTapLinkState(PVBOXNETFLTINS pThis, struct net_device *pDev, bool fLinkUp)
1544{
1545 if (vboxNetFltIsTapDevice(pThis, pDev))
1546 {
1547 Log3(("vboxNetFltSetTapLinkState: bringing %s tap device link state\n",
1548 fLinkUp ? "up" : "down"));
1549 netif_tx_lock_bh(pDev);
1550 if (fLinkUp)
1551 netif_carrier_on(pDev);
1552 else
1553 netif_carrier_off(pDev);
1554 netif_tx_unlock_bh(pDev);
1555 }
1556}
1557#else /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) */
1558DECLINLINE(void) vboxNetFltSetTapLinkState(PVBOXNETFLTINS pThis, struct net_device *pDev, bool fLinkUp)
1559{
1560 /* Nothing to do for pre-2.6.36 kernels. */
1561}
1562#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) */
1563
1564/**
1565 * Internal worker for vboxNetFltLinuxNotifierCallback.
1566 *
1567 * @returns VBox status code.
1568 * @param pThis The instance.
1569 * @param fRediscovery If set we're doing a rediscovery attempt, so, don't
1570 * flood the release log.
1571 */
1572static int vboxNetFltLinuxAttachToInterface(PVBOXNETFLTINS pThis, struct net_device *pDev)
1573{
1574 LogFlow(("vboxNetFltLinuxAttachToInterface: pThis=%p (%s)\n", pThis, pThis->szName));
1575
1576 /*
1577 * Retain and store the device.
1578 */
1579 dev_hold(pDev);
1580
1581 RTSpinlockAcquire(pThis->hSpinlock);
1582 ASMAtomicUoWritePtr(&pThis->u.s.pDev, pDev);
1583 RTSpinlockReleaseNoInts(pThis->hSpinlock);
1584
1585 Log(("vboxNetFltLinuxAttachToInterface: Device %p(%s) retained. ref=%d\n",
1586 pDev, pDev->name,
1587#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)
1588 netdev_refcnt_read(pDev)
1589#else
1590 atomic_read(&pDev->refcnt)
1591#endif
1592 ));
1593 Log(("vboxNetFltLinuxAttachToInterface: Got pDev=%p pThis=%p pThis->u.s.pDev=%p\n",
1594 pDev, pThis, ASMAtomicUoReadPtrT(&pThis->u.s.pDev, struct net_device *)));
1595
1596 /* Get the mac address while we still have a valid net_device reference. */
1597 memcpy(&pThis->u.s.MacAddr, pDev->dev_addr, sizeof(pThis->u.s.MacAddr));
1598 /* Initialize MTU */
1599 pThis->u.s.cbMtu = pDev->mtu;
1600
1601 /*
1602 * Install a packet filter for this device with a protocol wildcard (ETH_P_ALL).
1603 */
1604 pThis->u.s.PacketType.type = __constant_htons(ETH_P_ALL);
1605 pThis->u.s.PacketType.dev = pDev;
1606 pThis->u.s.PacketType.func = vboxNetFltLinuxPacketHandler;
1607 dev_add_pack(&pThis->u.s.PacketType);
1608 ASMAtomicUoWriteBool(&pThis->u.s.fPacketHandler, true);
1609 Log(("vboxNetFltLinuxAttachToInterface: this=%p: Packet handler installed.\n", pThis));
1610
1611#ifdef VBOXNETFLT_WITH_HOST2WIRE_FILTER
1612 vboxNetFltLinuxHookDev(pThis, pDev);
1613#endif
1614
1615 /*
1616 * If attaching to TAP interface we need to bring the link state up
1617 * starting from 2.6.36 kernel.
1618 */
1619 vboxNetFltSetTapLinkState(pThis, pDev, true);
1620
1621 /*
1622 * Set indicators that require the spinlock. Be abit paranoid about racing
1623 * the device notification handle.
1624 */
1625 RTSpinlockAcquire(pThis->hSpinlock);
1626 pDev = ASMAtomicUoReadPtrT(&pThis->u.s.pDev, struct net_device *);
1627 if (pDev)
1628 {
1629 ASMAtomicUoWriteBool(&pThis->fDisconnectedFromHost, false);
1630 ASMAtomicUoWriteBool(&pThis->u.s.fRegistered, true);
1631 pDev = NULL; /* don't dereference it */
1632 }
1633 RTSpinlockReleaseNoInts(pThis->hSpinlock);
1634
1635 /*
1636 * If the above succeeded report GSO capabilities, if not undo and
1637 * release the device.
1638 */
1639 if (!pDev)
1640 {
1641 Assert(pThis->pSwitchPort);
1642 if (vboxNetFltTryRetainBusyNotDisconnected(pThis))
1643 {
1644 vboxNetFltLinuxReportNicGsoCapabilities(pThis);
1645 pThis->pSwitchPort->pfnReportMacAddress(pThis->pSwitchPort, &pThis->u.s.MacAddr);
1646 pThis->pSwitchPort->pfnReportPromiscuousMode(pThis->pSwitchPort, vboxNetFltLinuxPromiscuous(pThis));
1647 pThis->pSwitchPort->pfnReportNoPreemptDsts(pThis->pSwitchPort, INTNETTRUNKDIR_WIRE | INTNETTRUNKDIR_HOST);
1648 vboxNetFltRelease(pThis, true /*fBusy*/);
1649 }
1650 }
1651 else
1652 {
1653#ifdef VBOXNETFLT_WITH_HOST2WIRE_FILTER
1654 vboxNetFltLinuxUnhookDev(pThis, pDev);
1655#endif
1656 RTSpinlockAcquire(pThis->hSpinlock);
1657 ASMAtomicUoWriteNullPtr(&pThis->u.s.pDev);
1658 RTSpinlockReleaseNoInts(pThis->hSpinlock);
1659 dev_put(pDev);
1660 Log(("vboxNetFltLinuxAttachToInterface: Device %p(%s) released. ref=%d\n",
1661 pDev, pDev->name,
1662#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)
1663 netdev_refcnt_read(pDev)
1664#else
1665 atomic_read(&pDev->refcnt)
1666#endif
1667 ));
1668 }
1669
1670 LogRel(("VBoxNetFlt: attached to '%s' / %.*Rhxs\n", pThis->szName, sizeof(pThis->u.s.MacAddr), &pThis->u.s.MacAddr));
1671 return VINF_SUCCESS;
1672}
1673
1674
1675static int vboxNetFltLinuxUnregisterDevice(PVBOXNETFLTINS pThis, struct net_device *pDev)
1676{
1677 bool fRegistered;
1678 Assert(!pThis->fDisconnectedFromHost);
1679
1680#ifdef VBOXNETFLT_WITH_HOST2WIRE_FILTER
1681 vboxNetFltLinuxUnhookDev(pThis, pDev);
1682#endif
1683
1684 if (ASMAtomicCmpXchgBool(&pThis->u.s.fPacketHandler, false, true))
1685 {
1686 dev_remove_pack(&pThis->u.s.PacketType);
1687 Log(("vboxNetFltLinuxUnregisterDevice: this=%p: packet handler removed.\n", pThis));
1688 }
1689
1690 RTSpinlockAcquire(pThis->hSpinlock);
1691 fRegistered = ASMAtomicXchgBool(&pThis->u.s.fRegistered, false);
1692 if (fRegistered)
1693 {
1694 ASMAtomicWriteBool(&pThis->fDisconnectedFromHost, true);
1695 ASMAtomicUoWriteNullPtr(&pThis->u.s.pDev);
1696 }
1697 RTSpinlockReleaseNoInts(pThis->hSpinlock);
1698
1699 if (fRegistered)
1700 {
1701#ifndef VBOXNETFLT_LINUX_NO_XMIT_QUEUE
1702 skb_queue_purge(&pThis->u.s.XmitQueue);
1703#endif
1704 Log(("vboxNetFltLinuxUnregisterDevice: this=%p: xmit queue purged.\n", pThis));
1705 Log(("vboxNetFltLinuxUnregisterDevice: Device %p(%s) released. ref=%d\n",
1706 pDev, pDev->name,
1707#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)
1708 netdev_refcnt_read(pDev)
1709#else
1710 atomic_read(&pDev->refcnt)
1711#endif
1712 ));
1713 dev_put(pDev);
1714 }
1715
1716 return NOTIFY_OK;
1717}
1718
1719static int vboxNetFltLinuxDeviceIsUp(PVBOXNETFLTINS pThis, struct net_device *pDev)
1720{
1721 /* Check if we are not suspended and promiscuous mode has not been set. */
1722 if ( pThis->enmTrunkState == INTNETTRUNKIFSTATE_ACTIVE
1723 && !ASMAtomicUoReadBool(&pThis->u.s.fPromiscuousSet))
1724 {
1725 /* Note that there is no need for locking as the kernel got hold of the lock already. */
1726 dev_set_promiscuity(pDev, 1);
1727 ASMAtomicWriteBool(&pThis->u.s.fPromiscuousSet, true);
1728 Log(("vboxNetFltLinuxDeviceIsUp: enabled promiscuous mode on %s (%d)\n", pThis->szName, pDev->promiscuity));
1729 }
1730 else
1731 Log(("vboxNetFltLinuxDeviceIsUp: no need to enable promiscuous mode on %s (%d)\n", pThis->szName, pDev->promiscuity));
1732 return NOTIFY_OK;
1733}
1734
1735static int vboxNetFltLinuxDeviceGoingDown(PVBOXNETFLTINS pThis, struct net_device *pDev)
1736{
1737 /* Undo promiscuous mode if we has set it. */
1738 if (ASMAtomicUoReadBool(&pThis->u.s.fPromiscuousSet))
1739 {
1740 /* Note that there is no need for locking as the kernel got hold of the lock already. */
1741 dev_set_promiscuity(pDev, -1);
1742 ASMAtomicWriteBool(&pThis->u.s.fPromiscuousSet, false);
1743 Log(("vboxNetFltLinuxDeviceGoingDown: disabled promiscuous mode on %s (%d)\n", pThis->szName, pDev->promiscuity));
1744 }
1745 else
1746 Log(("vboxNetFltLinuxDeviceGoingDown: no need to disable promiscuous mode on %s (%d)\n", pThis->szName, pDev->promiscuity));
1747 return NOTIFY_OK;
1748}
1749
1750/**
1751 * Callback for listening to MTU change event.
1752 *
1753 * We need to track changes of host's inteface MTU to discard over-sized frames
1754 * coming from the internal network as they may hang the TX queue of host's
1755 * adapter.
1756 *
1757 * @returns NOTIFY_OK
1758 * @param pThis The netfilter instance.
1759 * @param pDev Pointer to device structure of host's interface.
1760 */
1761static int vboxNetFltLinuxDeviceMtuChange(PVBOXNETFLTINS pThis, struct net_device *pDev)
1762{
1763 ASMAtomicWriteU32(&pThis->u.s.cbMtu, pDev->mtu);
1764 Log(("vboxNetFltLinuxDeviceMtuChange: set MTU for %s to %d\n", pThis->szName, pDev->mtu));
1765 return NOTIFY_OK;
1766}
1767
1768#ifdef LOG_ENABLED
1769/** Stringify the NETDEV_XXX constants. */
1770static const char *vboxNetFltLinuxGetNetDevEventName(unsigned long ulEventType)
1771{
1772 const char *pszEvent = "NETDRV_<unknown>";
1773 switch (ulEventType)
1774 {
1775 case NETDEV_REGISTER: pszEvent = "NETDEV_REGISTER"; break;
1776 case NETDEV_UNREGISTER: pszEvent = "NETDEV_UNREGISTER"; break;
1777 case NETDEV_UP: pszEvent = "NETDEV_UP"; break;
1778 case NETDEV_DOWN: pszEvent = "NETDEV_DOWN"; break;
1779 case NETDEV_REBOOT: pszEvent = "NETDEV_REBOOT"; break;
1780 case NETDEV_CHANGENAME: pszEvent = "NETDEV_CHANGENAME"; break;
1781 case NETDEV_CHANGE: pszEvent = "NETDEV_CHANGE"; break;
1782 case NETDEV_CHANGEMTU: pszEvent = "NETDEV_CHANGEMTU"; break;
1783 case NETDEV_CHANGEADDR: pszEvent = "NETDEV_CHANGEADDR"; break;
1784 case NETDEV_GOING_DOWN: pszEvent = "NETDEV_GOING_DOWN"; break;
1785# ifdef NETDEV_FEAT_CHANGE
1786 case NETDEV_FEAT_CHANGE: pszEvent = "NETDEV_FEAT_CHANGE"; break;
1787# endif
1788 }
1789 return pszEvent;
1790}
1791#endif /* LOG_ENABLED */
1792
1793/**
1794 * Callback for listening to netdevice events.
1795 *
1796 * This works the rediscovery, clean up on unregistration, promiscuity on
1797 * up/down, and GSO feature changes from ethtool.
1798 *
1799 * @returns NOTIFY_OK
1800 * @param self Pointer to our notifier registration block.
1801 * @param ulEventType The event.
1802 * @param ptr Event specific, but it is usually the device it
1803 * relates to.
1804 */
1805static int vboxNetFltLinuxNotifierCallback(struct notifier_block *self, unsigned long ulEventType, void *ptr)
1806
1807{
1808 PVBOXNETFLTINS pThis = VBOX_FLT_NB_TO_INST(self);
1809 struct net_device *pDev = (struct net_device *)ptr;
1810 int rc = NOTIFY_OK;
1811
1812 Log(("VBoxNetFlt: got event %s(0x%lx) on %s, pDev=%p pThis=%p pThis->u.s.pDev=%p\n",
1813 vboxNetFltLinuxGetNetDevEventName(ulEventType), ulEventType, pDev->name, pDev, pThis, ASMAtomicUoReadPtrT(&pThis->u.s.pDev, struct net_device *)));
1814 if ( ulEventType == NETDEV_REGISTER
1815 && !strcmp(pDev->name, pThis->szName))
1816 {
1817 vboxNetFltLinuxAttachToInterface(pThis, pDev);
1818 }
1819 else
1820 {
1821 pDev = ASMAtomicUoReadPtrT(&pThis->u.s.pDev, struct net_device *);
1822 if (pDev == ptr)
1823 {
1824 switch (ulEventType)
1825 {
1826 case NETDEV_UNREGISTER:
1827 rc = vboxNetFltLinuxUnregisterDevice(pThis, pDev);
1828 break;
1829 case NETDEV_UP:
1830 rc = vboxNetFltLinuxDeviceIsUp(pThis, pDev);
1831 break;
1832 case NETDEV_GOING_DOWN:
1833 rc = vboxNetFltLinuxDeviceGoingDown(pThis, pDev);
1834 break;
1835 case NETDEV_CHANGEMTU:
1836 rc = vboxNetFltLinuxDeviceMtuChange(pThis, pDev);
1837 break;
1838 case NETDEV_CHANGENAME:
1839 break;
1840#ifdef NETDEV_FEAT_CHANGE
1841 case NETDEV_FEAT_CHANGE:
1842 vboxNetFltLinuxReportNicGsoCapabilities(pThis);
1843 break;
1844#endif
1845 }
1846 }
1847 }
1848
1849 return rc;
1850}
1851
1852bool vboxNetFltOsMaybeRediscovered(PVBOXNETFLTINS pThis)
1853{
1854 return !ASMAtomicUoReadBool(&pThis->fDisconnectedFromHost);
1855}
1856
1857int vboxNetFltPortOsXmit(PVBOXNETFLTINS pThis, void *pvIfData, PINTNETSG pSG, uint32_t fDst)
1858{
1859 struct net_device * pDev;
1860 int err;
1861 int rc = VINF_SUCCESS;
1862 NOREF(pvIfData);
1863
1864 LogFlow(("vboxNetFltPortOsXmit: pThis=%p (%s)\n", pThis, pThis->szName));
1865
1866 pDev = vboxNetFltLinuxRetainNetDev(pThis);
1867 if (pDev)
1868 {
1869 /*
1870 * Create a sk_buff for the gather list and push it onto the wire.
1871 */
1872 if (fDst & INTNETTRUNKDIR_WIRE)
1873 {
1874 struct sk_buff *pBuf = vboxNetFltLinuxSkBufFromSG(pThis, pSG, true);
1875 if (pBuf)
1876 {
1877 vboxNetFltDumpPacket(pSG, true, "wire", 1);
1878 Log4(("vboxNetFltPortOsXmit: pBuf->cb dump:\n%.*Rhxd\n", sizeof(pBuf->cb), pBuf->cb));
1879 Log4(("vboxNetFltPortOsXmit: dev_queue_xmit(%p)\n", pBuf));
1880 err = dev_queue_xmit(pBuf);
1881 if (err)
1882 rc = RTErrConvertFromErrno(err);
1883 }
1884 else
1885 rc = VERR_NO_MEMORY;
1886 }
1887
1888 /*
1889 * Create a sk_buff for the gather list and push it onto the host stack.
1890 */
1891 if (fDst & INTNETTRUNKDIR_HOST)
1892 {
1893 struct sk_buff *pBuf = vboxNetFltLinuxSkBufFromSG(pThis, pSG, false);
1894 if (pBuf)
1895 {
1896 vboxNetFltDumpPacket(pSG, true, "host", (fDst & INTNETTRUNKDIR_WIRE) ? 0 : 1);
1897 Log4(("vboxNetFltPortOsXmit: pBuf->cb dump:\n%.*Rhxd\n", sizeof(pBuf->cb), pBuf->cb));
1898 Log4(("vboxNetFltPortOsXmit: netif_rx_ni(%p)\n", pBuf));
1899 err = netif_rx_ni(pBuf);
1900 if (err)
1901 rc = RTErrConvertFromErrno(err);
1902 }
1903 else
1904 rc = VERR_NO_MEMORY;
1905 }
1906
1907 vboxNetFltLinuxReleaseNetDev(pThis, pDev);
1908 }
1909
1910 return rc;
1911}
1912
1913
1914void vboxNetFltPortOsSetActive(PVBOXNETFLTINS pThis, bool fActive)
1915{
1916 struct net_device * pDev;
1917
1918 LogFlow(("vboxNetFltPortOsSetActive: pThis=%p (%s), fActive=%s, fDisablePromiscuous=%s\n",
1919 pThis, pThis->szName, fActive?"true":"false",
1920 pThis->fDisablePromiscuous?"true":"false"));
1921
1922 if (pThis->fDisablePromiscuous)
1923 return;
1924
1925 pDev = vboxNetFltLinuxRetainNetDev(pThis);
1926 if (pDev)
1927 {
1928 /*
1929 * This api is a bit weird, the best reference is the code.
1930 *
1931 * Also, we have a bit or race conditions wrt the maintenance of
1932 * host the interface promiscuity for vboxNetFltPortOsIsPromiscuous.
1933 */
1934#ifdef LOG_ENABLED
1935 u_int16_t fIf;
1936 unsigned const cPromiscBefore = pDev->promiscuity;
1937#endif
1938 if (fActive)
1939 {
1940 Assert(!pThis->u.s.fPromiscuousSet);
1941
1942 rtnl_lock();
1943 dev_set_promiscuity(pDev, 1);
1944 rtnl_unlock();
1945 pThis->u.s.fPromiscuousSet = true;
1946 Log(("vboxNetFltPortOsSetActive: enabled promiscuous mode on %s (%d)\n", pThis->szName, pDev->promiscuity));
1947 }
1948 else
1949 {
1950 if (pThis->u.s.fPromiscuousSet)
1951 {
1952 rtnl_lock();
1953 dev_set_promiscuity(pDev, -1);
1954 rtnl_unlock();
1955 Log(("vboxNetFltPortOsSetActive: disabled promiscuous mode on %s (%d)\n", pThis->szName, pDev->promiscuity));
1956 }
1957 pThis->u.s.fPromiscuousSet = false;
1958
1959#ifdef LOG_ENABLED
1960 fIf = dev_get_flags(pDev);
1961 Log(("VBoxNetFlt: fIf=%#x; %d->%d\n", fIf, cPromiscBefore, pDev->promiscuity));
1962#endif
1963 }
1964
1965 vboxNetFltLinuxReleaseNetDev(pThis, pDev);
1966 }
1967}
1968
1969
1970int vboxNetFltOsDisconnectIt(PVBOXNETFLTINS pThis)
1971{
1972 /*
1973 * Remove packet handler when we get disconnected from internal switch as
1974 * we don't want the handler to forward packets to disconnected switch.
1975 */
1976 if (ASMAtomicCmpXchgBool(&pThis->u.s.fPacketHandler, false, true))
1977 {
1978 dev_remove_pack(&pThis->u.s.PacketType);
1979 Log(("vboxNetFltOsDisconnectIt: this=%p: Packet handler removed.\n", pThis));
1980 }
1981 return VINF_SUCCESS;
1982}
1983
1984
1985int vboxNetFltOsConnectIt(PVBOXNETFLTINS pThis)
1986{
1987 /*
1988 * Report the GSO capabilities of the host and device (if connected).
1989 * Note! No need to mark ourselves busy here.
1990 */
1991 /** @todo duplicate work here now? Attach */
1992#if defined(VBOXNETFLT_WITH_GSO_XMIT_HOST)
1993 Log3(("vboxNetFltOsConnectIt: reporting host tso tso6 ufo\n"));
1994 pThis->pSwitchPort->pfnReportGsoCapabilities(pThis->pSwitchPort,
1995 0
1996 | RT_BIT_32(PDMNETWORKGSOTYPE_IPV4_TCP)
1997 | RT_BIT_32(PDMNETWORKGSOTYPE_IPV6_TCP)
1998 | RT_BIT_32(PDMNETWORKGSOTYPE_IPV4_UDP)
1999# if 0 /** @todo GSO: Test UDP offloading (UFO) on linux. */
2000 | RT_BIT_32(PDMNETWORKGSOTYPE_IPV6_UDP)
2001# endif
2002 , INTNETTRUNKDIR_HOST);
2003
2004#endif
2005 vboxNetFltLinuxReportNicGsoCapabilities(pThis);
2006
2007 return VINF_SUCCESS;
2008}
2009
2010
2011void vboxNetFltOsDeleteInstance(PVBOXNETFLTINS pThis)
2012{
2013 struct net_device *pDev;
2014 bool fRegistered;
2015
2016#ifdef VBOXNETFLT_WITH_HOST2WIRE_FILTER
2017 vboxNetFltLinuxUnhookDev(pThis, NULL);
2018#endif
2019
2020 /** @todo This code may race vboxNetFltLinuxUnregisterDevice (very very
2021 * unlikely, but none the less). Since it doesn't actually update the
2022 * state (just reads it), it is likely to panic in some interesting
2023 * ways. */
2024
2025 RTSpinlockAcquire(pThis->hSpinlock);
2026 pDev = ASMAtomicUoReadPtrT(&pThis->u.s.pDev, struct net_device *);
2027 fRegistered = ASMAtomicXchgBool(&pThis->u.s.fRegistered, false);
2028 RTSpinlockReleaseNoInts(pThis->hSpinlock);
2029
2030 if (fRegistered)
2031 {
2032 vboxNetFltSetTapLinkState(pThis, pDev, false);
2033
2034#ifndef VBOXNETFLT_LINUX_NO_XMIT_QUEUE
2035 skb_queue_purge(&pThis->u.s.XmitQueue);
2036#endif
2037 Log(("vboxNetFltOsDeleteInstance: this=%p: xmit queue purged.\n", pThis));
2038 Log(("vboxNetFltOsDeleteInstance: Device %p(%s) released. ref=%d\n",
2039 pDev, pDev->name,
2040#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)
2041 netdev_refcnt_read(pDev)
2042#else
2043 atomic_read(&pDev->refcnt)
2044#endif
2045 ));
2046 dev_put(pDev);
2047 }
2048 Log(("vboxNetFltOsDeleteInstance: this=%p: Notifier removed.\n", pThis));
2049 unregister_netdevice_notifier(&pThis->u.s.Notifier);
2050 module_put(THIS_MODULE);
2051}
2052
2053
2054int vboxNetFltOsInitInstance(PVBOXNETFLTINS pThis, void *pvContext)
2055{
2056 int err;
2057 NOREF(pvContext);
2058
2059 pThis->u.s.Notifier.notifier_call = vboxNetFltLinuxNotifierCallback;
2060 err = register_netdevice_notifier(&pThis->u.s.Notifier);
2061 if (err)
2062 return VERR_INTNET_FLT_IF_FAILED;
2063 if (!pThis->u.s.fRegistered)
2064 {
2065 unregister_netdevice_notifier(&pThis->u.s.Notifier);
2066 LogRel(("VBoxNetFlt: failed to find %s.\n", pThis->szName));
2067 return VERR_INTNET_FLT_IF_NOT_FOUND;
2068 }
2069
2070 Log(("vboxNetFltOsInitInstance: this=%p: Notifier installed.\n", pThis));
2071 if ( pThis->fDisconnectedFromHost
2072 || !try_module_get(THIS_MODULE))
2073 return VERR_INTNET_FLT_IF_FAILED;
2074
2075 return VINF_SUCCESS;
2076}
2077
2078int vboxNetFltOsPreInitInstance(PVBOXNETFLTINS pThis)
2079{
2080 /*
2081 * Init the linux specific members.
2082 */
2083 ASMAtomicUoWriteNullPtr(&pThis->u.s.pDev);
2084 pThis->u.s.fRegistered = false;
2085 pThis->u.s.fPromiscuousSet = false;
2086 pThis->u.s.fPacketHandler = false;
2087 memset(&pThis->u.s.PacketType, 0, sizeof(pThis->u.s.PacketType));
2088#ifndef VBOXNETFLT_LINUX_NO_XMIT_QUEUE
2089 skb_queue_head_init(&pThis->u.s.XmitQueue);
2090# if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20)
2091 INIT_WORK(&pThis->u.s.XmitTask, vboxNetFltLinuxXmitTask);
2092# else
2093 INIT_WORK(&pThis->u.s.XmitTask, vboxNetFltLinuxXmitTask, &pThis->u.s.XmitTask);
2094# endif
2095#endif
2096
2097 return VINF_SUCCESS;
2098}
2099
2100
2101void vboxNetFltPortOsNotifyMacAddress(PVBOXNETFLTINS pThis, void *pvIfData, PCRTMAC pMac)
2102{
2103 NOREF(pThis); NOREF(pvIfData); NOREF(pMac);
2104}
2105
2106
2107int vboxNetFltPortOsConnectInterface(PVBOXNETFLTINS pThis, void *pvIf, void **pvIfData)
2108{
2109 /* Nothing to do */
2110 NOREF(pThis); NOREF(pvIf); NOREF(pvIfData);
2111 return VINF_SUCCESS;
2112}
2113
2114
2115int vboxNetFltPortOsDisconnectInterface(PVBOXNETFLTINS pThis, void *pvIfData)
2116{
2117 /* Nothing to do */
2118 NOREF(pThis); NOREF(pvIfData);
2119 return VINF_SUCCESS;
2120}
2121
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette