VirtualBox

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

Last change on this file since 15061 was 14968, checked in by vboxsync, 16 years ago

Linux hostif: Added dev_get_flags() support for pre-2.6.12 kernels.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 33.8 KB
Line 
1/* $Id: VBoxNetFlt-linux.c 14968 2008-12-04 09:24:33Z vboxsync $ */
2/** @file
3 * VBoxNetFlt - Network Filter Driver (Host), Linux Specific Code.
4 */
5
6/*
7 * Copyright (C) 2006-2008 Sun Microsystems, Inc.
8 *
9 * Sun Microsystems, Inc. confidential
10 * All rights reserved
11 */
12
13/*******************************************************************************
14* Header Files *
15*******************************************************************************/
16#include "the-linux-kernel.h"
17#include "version-generated.h"
18#include <linux/netdevice.h>
19#include <linux/etherdevice.h>
20#include <linux/rtnetlink.h>
21
22#define LOG_GROUP LOG_GROUP_NET_FLT_DRV
23#include <VBox/log.h>
24#include <VBox/err.h>
25#include <iprt/alloca.h>
26#include <iprt/assert.h>
27#include <iprt/spinlock.h>
28#include <iprt/semaphore.h>
29#include <iprt/initterm.h>
30#include <iprt/process.h>
31#include <iprt/mem.h>
32#include <iprt/log.h>
33#include <iprt/mp.h>
34#include <iprt/mem.h>
35#include <iprt/time.h>
36
37#define VBOXNETFLT_OS_SPECFIC 1
38#include "../VBoxNetFltInternal.h"
39
40#define VBOX_FLT_NB_TO_INST(pNB) ((PVBOXNETFLTINS)((uint8_t *)pNB - \
41 RT_OFFSETOF(VBOXNETFLTINS, u.s.Notifier)))
42#define VBOX_FLT_PT_TO_INST(pPT) ((PVBOXNETFLTINS)((uint8_t *)pPT - \
43 RT_OFFSETOF(VBOXNETFLTINS, u.s.PacketType)))
44#define VBOX_FLT_XT_TO_INST(pXT) ((PVBOXNETFLTINS)((uint8_t *)pXT - \
45 RT_OFFSETOF(VBOXNETFLTINS, u.s.XmitTask)))
46
47#define VBOX_GET_PCOUNT(pDev) (pDev->promiscuity)
48
49#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22)
50# define VBOX_SKB_TRANSPORT_HDR(skb) skb->transport_header
51# define VBOX_SKB_NETWORK_HDR(skb) skb->network_header
52# define VBOX_SKB_MAC_HDR(skb) skb->mac_header
53#else /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22) */
54# define VBOX_SKB_TRANSPORT_HDR(skb) skb->h.raw
55# define VBOX_SKB_NETWORK_HDR(skb) skb->nh.raw
56# define VBOX_SKB_MAC_HDR(skb) skb->mac.raw
57#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22) */
58
59#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)
60# define VBOX_SKB_CHECKSUM_HELP(skb) skb_checksum_help(skb)
61#else /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) */
62# define CHECKSUM_PARTIAL CHECKSUM_HW
63# if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 10)
64# define VBOX_SKB_CHECKSUM_HELP(skb) skb_checksum_help(skb, 0)
65# else /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 10) */
66# define VBOX_SKB_CHECKSUM_HELP(skb) skb_checksum_help(&skb, 0)
67# endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 10) */
68#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) */
69
70#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 18)
71# define VBOX_SKB_IS_GSO(skb) skb_is_gso(skb)
72 /* No features, very dumb device */
73# define VBOX_SKB_GSO_SEGMENT(skb) skb_gso_segment(skb, 0)
74#else /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18) */
75# define VBOX_SKB_IS_GSO(skb) false
76# define VBOX_SKB_GSO_SEGMENT(skb) NULL
77#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18) */
78
79#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 12)
80unsigned dev_get_flags(const struct net_device *dev)
81{
82 unsigned flags;
83
84 flags = (dev->flags & ~(IFF_PROMISC |
85 IFF_ALLMULTI |
86 IFF_RUNNING)) |
87 (dev->gflags & (IFF_PROMISC |
88 IFF_ALLMULTI));
89
90 if (netif_running(dev) && netif_carrier_ok(dev))
91 flags |= IFF_RUNNING;
92
93 return flags;
94}
95#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 12) */
96
97/*******************************************************************************
98* Internal Functions *
99*******************************************************************************/
100static int VBoxNetFltLinuxInit(void);
101static void VBoxNetFltLinuxUnload(void);
102
103
104/*******************************************************************************
105* Global Variables *
106*******************************************************************************/
107/**
108 * The (common) global data.
109 */
110static VBOXNETFLTGLOBALS g_VBoxNetFltGlobals;
111
112module_init(VBoxNetFltLinuxInit);
113module_exit(VBoxNetFltLinuxUnload);
114
115MODULE_AUTHOR("Sun Microsystems, Inc.");
116MODULE_DESCRIPTION("VirtualBox Network Filter Driver");
117MODULE_LICENSE("GPL");
118#ifdef MODULE_VERSION
119# define xstr(s) str(s)
120# define str(s) #s
121MODULE_VERSION(VBOX_VERSION_STRING " (" xstr(INTNETTRUNKIFPORT_VERSION) ")");
122#endif
123
124/**
125 * The (common) global data.
126 */
127static VBOXNETFLTGLOBALS g_VBoxNetFltGlobals;
128
129
130/**
131 * Initialize module.
132 *
133 * @returns appropriate status code.
134 */
135static int __init VBoxNetFltLinuxInit(void)
136{
137 int rc;
138 Log(("VBoxNetFltLinuxInit\n"));
139
140 /*
141 * Initialize IPRT.
142 */
143 rc = RTR0Init(0);
144 if (RT_SUCCESS(rc))
145 {
146 /*
147 * Initialize the globals and connect to the support driver.
148 *
149 * This will call back vboxNetFltOsOpenSupDrv (and maybe vboxNetFltOsCloseSupDrv)
150 * for establishing the connect to the support driver.
151 */
152 memset(&g_VBoxNetFltGlobals, 0, sizeof(g_VBoxNetFltGlobals));
153 rc = vboxNetFltInitGlobals(&g_VBoxNetFltGlobals);
154 if (RT_SUCCESS(rc))
155 {
156 LogRel(("VBoxNetFlt: Successfully started.\n"));
157 return 0;
158 }
159
160 LogRel(("VBoxNetFlt: failed to initialize device extension (rc=%d)\n", rc));
161 RTR0Term();
162 }
163 else
164 LogRel(("VBoxNetFlt: failed to initialize IPRT (rc=%d)\n", rc));
165
166 memset(&g_VBoxNetFltGlobals, 0, sizeof(g_VBoxNetFltGlobals));
167 return RTErrConvertToErrno(rc);
168}
169
170
171/**
172 * Unload the module.
173 *
174 * @todo We have to prevent this if we're busy!
175 */
176static void __exit VBoxNetFltLinuxUnload(void)
177{
178 int rc;
179 Log(("VBoxNetFltLinuxUnload\n"));
180 Assert(vboxNetFltCanUnload(&g_VBoxNetFltGlobals));
181
182 /*
183 * Undo the work done during start (in reverse order).
184 */
185 rc = vboxNetFltTryDeleteGlobals(&g_VBoxNetFltGlobals);
186 AssertRC(rc); NOREF(rc);
187
188 RTR0Term();
189
190 memset(&g_VBoxNetFltGlobals, 0, sizeof(g_VBoxNetFltGlobals));
191
192 Log(("VBoxNetFltLinuxUnload - done\n"));
193}
194
195
196/**
197 * Reads and retains the host interface handle.
198 *
199 * @returns The handle, NULL if detached.
200 * @param pThis
201 */
202DECLINLINE(struct net_device *) vboxNetFltLinuxRetainNetDev(PVBOXNETFLTINS pThis)
203{
204#if 0
205 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
206 struct net_device *pDev = NULL;
207
208 Log(("vboxNetFltLinuxRetainNetDev\n"));
209 /*
210 * Be careful here to avoid problems racing the detached callback.
211 */
212 RTSpinlockAcquire(pThis->hSpinlock, &Tmp);
213 if (!ASMAtomicUoReadBool(&pThis->fDisconnectedFromHost))
214 {
215 pDev = (struct net_device *)ASMAtomicUoReadPtr((void * volatile *)&pThis->u.s.pDev);
216 if (pDev)
217 {
218 dev_hold(pDev);
219 Log(("vboxNetFltLinuxRetainNetDev: Device %p(%s) retained. ref=%d\n", pDev, pDev->name, atomic_read(&pDev->refcnt)));
220 }
221 }
222 RTSpinlockRelease(pThis->hSpinlock, &Tmp);
223
224 Log(("vboxNetFltLinuxRetainNetDev - done\n"));
225 return pDev;
226#else
227 return (struct net_device *)ASMAtomicUoReadPtr((void * volatile *)&pThis->u.s.pDev);
228#endif
229}
230
231
232/**
233 * Release the host interface handle previously retained
234 * by vboxNetFltLinuxRetainNetDev.
235 *
236 * @param pThis The instance.
237 * @param pDev The vboxNetFltLinuxRetainNetDev
238 * return value, NULL is fine.
239 */
240DECLINLINE(void) vboxNetFltLinuxReleaseNetDev(PVBOXNETFLTINS pThis, struct net_device *pDev)
241{
242#if 0
243 Log(("vboxNetFltLinuxReleaseNetDev\n"));
244 NOREF(pThis);
245 if (pDev)
246 {
247 dev_put(pDev);
248 Log(("vboxNetFltLinuxReleaseNetDev: Device %p(%s) released. ref=%d\n", pDev, pDev->name, atomic_read(&pDev->refcnt)));
249 }
250 Log(("vboxNetFltLinuxReleaseNetDev - done\n"));
251#endif
252}
253
254#define VBOXNETFLT_CB_TAG 0xA1C9D7C3
255#define VBOXNETFLT_SKB_CB(skb) (*(uint32_t*)&((skb)->cb[0]))
256
257/**
258 * Checks whether this is an mbuf created by vboxNetFltLinuxMBufFromSG,
259 * i.e. a buffer which we're pushing and should be ignored by the filter callbacks.
260 *
261 * @returns true / false accordingly.
262 * @param pBuf The sk_buff.
263 */
264DECLINLINE(bool) vboxNetFltLinuxSkBufIsOur(struct sk_buff *pBuf)
265{
266 return VBOXNETFLT_SKB_CB(pBuf) == VBOXNETFLT_CB_TAG ;
267}
268
269
270/**
271 * Internal worker that create a linux sk_buff for a
272 * (scatter/)gather list.
273 *
274 * @returns Pointer to the sk_buff.
275 * @param pThis The instance.
276 * @param pSG The (scatter/)gather list.
277 */
278static struct sk_buff *vboxNetFltLinuxSkBufFromSG(PVBOXNETFLTINS pThis, PINTNETSG pSG, bool fDstWire)
279{
280 struct sk_buff *pPkt;
281 struct net_device *pDev;
282 /*
283 * Because we're lazy, we will ASSUME that all SGs coming from INTNET
284 * will only contain one single segment.
285 */
286 if (pSG->cSegsUsed != 1 || pSG->cbTotal != pSG->aSegs[0].cb)
287 {
288 LogRel(("VBoxNetFlt: Dropped multi-segment(%d) packet coming from internal network.\n", pSG->cSegsUsed));
289 return NULL;
290 }
291 if (pSG->cbTotal == 0)
292 {
293 LogRel(("VBoxNetFlt: Dropped empty packet coming from internal network.\n"));
294 return NULL;
295 }
296
297 /*
298 * Allocate a packet and copy over the data.
299 *
300 */
301 pDev = (struct net_device *)ASMAtomicUoReadPtr((void * volatile *)&pThis->u.s.pDev);
302 pPkt = dev_alloc_skb(pSG->cbTotal + NET_IP_ALIGN);
303 if (pPkt)
304 {
305 pPkt->dev = pDev;
306 /* Align IP header on 16-byte boundary: 2 + 14 (ethernet hdr size). */
307 skb_reserve(pPkt, NET_IP_ALIGN);
308 skb_put(pPkt, pSG->cbTotal);
309 memcpy(pPkt->data, pSG->aSegs[0].pv, pSG->cbTotal);
310 /* Set protocol and packet_type fields. */
311 pPkt->protocol = eth_type_trans(pPkt, pDev);
312 pPkt->ip_summed = CHECKSUM_NONE;
313 if (fDstWire)
314 {
315 VBOX_SKB_NETWORK_HDR(pPkt) = pPkt->data;
316 /* Restore ethernet header back. */
317 skb_push(pPkt, ETH_HLEN);
318 }
319 VBOXNETFLT_SKB_CB(pPkt) = VBOXNETFLT_CB_TAG;
320
321 return pPkt;
322 }
323 else
324 Log(("vboxNetFltLinuxSkBufFromSG: Failed to allocate sk_buff(%u).\n", pSG->cbTotal));
325 pSG->pvUserData = NULL;
326
327 return NULL;
328}
329
330
331/**
332 * Initializes a SG list from an sk_buff.
333 *
334 * @returns Number of segments.
335 * @param pThis The instance.
336 * @param pBuf The sk_buff.
337 * @param pSG The SG.
338 * @param pvFrame The frame pointer, optional.
339 * @param cSegs The number of segments allocated for the SG.
340 * This should match the number in the mbuf exactly!
341 * @param fSrc The source of the frame.
342 */
343DECLINLINE(void) vboxNetFltLinuxSkBufToSG(PVBOXNETFLTINS pThis, struct sk_buff *pBuf, PINTNETSG pSG, unsigned cSegs, uint32_t fSrc)
344{
345 int i;
346 NOREF(pThis);
347
348 Assert(!skb_shinfo(pBuf)->frag_list);
349 pSG->pvOwnerData = NULL;
350 pSG->pvUserData = NULL;
351 pSG->pvUserData2 = NULL;
352 pSG->cUsers = 1;
353 pSG->fFlags = INTNETSG_FLAGS_TEMP;
354 pSG->cSegsAlloc = cSegs;
355
356 if (fSrc & INTNETTRUNKDIR_WIRE)
357 {
358 /*
359 * The packet came from wire, ethernet header was removed by device driver.
360 * Restore it.
361 */
362 skb_push(pBuf, ETH_HLEN);
363 }
364 pSG->cbTotal = pBuf->len;
365#ifdef VBOXNETFLT_SG_SUPPORT
366 pSG->aSegs[0].cb = skb_headlen(pBuf);
367 pSG->aSegs[0].pv = pBuf->data;
368 pSG->aSegs[0].Phys = NIL_RTHCPHYS;
369
370 for (i = 0; i < skb_shinfo(pBuf)->nr_frags; i++)
371 {
372 skb_frag_t *pFrag = &skb_shinfo(pBuf)->frags[i];
373 pSG->aSegs[i+1].cb = pFrag->size;
374 pSG->aSegs[i+1].pv = kmap(pFrag->page);
375 printk("%p = kmap()\n", pSG->aSegs[i+1].pv);
376 pSG->aSegs[i+1].Phys = NIL_RTHCPHYS;
377 }
378 pSG->cSegsUsed = ++i;
379#else
380 pSG->aSegs[0].cb = pBuf->len;
381 pSG->aSegs[0].pv = pBuf->data;
382 pSG->aSegs[0].Phys = NIL_RTHCPHYS;
383 pSG->cSegsUsed = i = 1;
384#endif
385
386
387#ifdef PADD_RUNT_FRAMES_FROM_HOST
388 /*
389 * Add a trailer if the frame is too small.
390 *
391 * Since we're getting to the packet before it is framed, it has not
392 * yet been padded. The current solution is to add a segment pointing
393 * to a buffer containing all zeros and pray that works for all frames...
394 */
395 if (pSG->cbTotal < 60 && (fSrc & INTNETTRUNKDIR_HOST))
396 {
397 static uint8_t const s_abZero[128] = {0};
398
399 AssertReturnVoid(i < cSegs);
400
401 pSG->aSegs[i].Phys = NIL_RTHCPHYS;
402 pSG->aSegs[i].pv = (void *)&s_abZero[0];
403 pSG->aSegs[i].cb = 60 - pSG->cbTotal;
404 pSG->cbTotal = 60;
405 pSG->cSegsUsed++;
406 }
407#endif
408 Log2(("vboxNetFltLinuxSkBufToSG: allocated=%d, segments=%d frags=%d next=%p frag_list=%p pkt_type=%x fSrc=%x\n",
409 pSG->cSegsAlloc, pSG->cSegsUsed, skb_shinfo(pBuf)->nr_frags, pBuf->next, skb_shinfo(pBuf)->frag_list, pBuf->pkt_type, fSrc));
410 for (i = 0; i < pSG->cSegsUsed; i++)
411 Log2(("vboxNetFltLinuxSkBufToSG: #%d: cb=%d pv=%p\n",
412 i, pSG->aSegs[i].cb, pSG->aSegs[i].pv));
413}
414
415/**
416 * Packet handler,
417 *
418 * @returns 0 or EJUSTRETURN.
419 * @param pThis The instance.
420 * @param pMBuf The mbuf.
421 * @param pvFrame The start of the frame, optional.
422 * @param fSrc Where the packet (allegedly) comes from, one INTNETTRUNKDIR_* value.
423 * @param eProtocol The protocol.
424 */
425static int vboxNetFltLinuxPacketHandler(struct sk_buff *pBuf,
426 struct net_device *pSkbDev,
427 struct packet_type *pPacketType,
428 struct net_device *pOrigDev)
429{
430 PVBOXNETFLTINS pThis;
431 struct net_device *pDev;
432 /*
433 * Drop it immediately?
434 */
435 Log2(("vboxNetFltLinuxPacketHandler: pBuf=%p pSkbDev=%p pPacketType=%p pOrigDev=%p\n",
436 pBuf, pSkbDev, pPacketType, pOrigDev));
437 if (!pBuf)
438 return 0;
439 pThis = VBOX_FLT_PT_TO_INST(pPacketType);
440 pDev = (struct net_device *)ASMAtomicUoReadPtr((void * volatile *)&pThis->u.s.pDev);
441 if (pThis->u.s.pDev != pSkbDev)
442 {
443 Log(("vboxNetFltLinuxPacketHandler: Devices do not match, pThis may be wrong! pThis=%p\n", pThis));
444 return 0;
445 }
446
447 if (vboxNetFltLinuxSkBufIsOur(pBuf))
448 {
449 dev_kfree_skb(pBuf);
450 return 0;
451 }
452
453 /* Add the packet to transmit queue and schedule the bottom half. */
454 skb_queue_tail(&pThis->u.s.XmitQueue, pBuf);
455 schedule_work(&pThis->u.s.XmitTask);
456 Log2(("vboxNetFltLinuxPacketHandler: scheduled work %p for sk_buff %p\n",
457 &pThis->u.s.XmitTask, pBuf));
458 /* It does not really matter what we return, it is ignored by the kernel. */
459 return 0;
460}
461
462static unsigned vboxNetFltLinuxSGSegments(PVBOXNETFLTINS pThis, struct sk_buff *pBuf)
463{
464#ifdef VBOXNETFLT_SG_SUPPORT
465 unsigned cSegs = 1 + skb_shinfo(pBuf)->nr_frags;
466#else
467 unsigned cSegs = 1;
468#endif
469#ifdef PADD_RUNT_FRAMES_FROM_HOST
470 /*
471 * Add a trailer if the frame is too small.
472 */
473 if (pBuf->len < 60)
474 cSegs++;
475#endif
476 return cSegs;
477}
478
479/* WARNING! This function should only be called after vboxNetFltLinuxSkBufToSG()! */
480static void vboxNetFltLinuxFreeSkBuff(struct sk_buff *pBuf, PINTNETSG pSG)
481{
482#ifdef VBOXNETFLT_SG_SUPPORT
483 int i;
484
485 for (i = 0; i < skb_shinfo(pBuf)->nr_frags; i++)
486 {
487 printk("kunmap(%p)\n", pSG->aSegs[i+1].pv);
488 kunmap(pSG->aSegs[i+1].pv);
489 }
490#endif
491
492 dev_kfree_skb(pBuf);
493}
494
495static int vboxNetFltLinuxForwardSegment(PVBOXNETFLTINS pThis, struct sk_buff *pBuf, uint32_t fSrc)
496{
497 unsigned cSegs = vboxNetFltLinuxSGSegments(pThis, pBuf);
498 if (cSegs < MAX_SKB_FRAGS)
499 {
500 uint8_t *pTmp;
501 PINTNETSG pSG = (PINTNETSG)alloca(RT_OFFSETOF(INTNETSG, aSegs[cSegs]));
502 if (!pSG)
503 {
504 Log(("VBoxNetFlt: Failed to allocate SG buffer.\n"));
505 return VERR_NO_MEMORY;
506 }
507 vboxNetFltLinuxSkBufToSG(pThis, pBuf, pSG, cSegs, fSrc);
508
509 pTmp = pSG->aSegs[0].pv;
510 Log(("VBoxNetFlt: (int)%02x:%02x:%02x:%02x:%02x:%02x"
511 " <-- (%s)%02x:%02x:%02x:%02x:%02x:%02x (%u bytes)\n",
512 pTmp[0], pTmp[1], pTmp[2], pTmp[3], pTmp[4], pTmp[5],
513 (fSrc & INTNETTRUNKDIR_HOST) ? "host" : "wire",
514 pTmp[6], pTmp[7], pTmp[8], pTmp[9], pTmp[10], pTmp[11],
515 pSG->cbTotal));
516 pThis->pSwitchPort->pfnRecv(pThis->pSwitchPort, pSG, fSrc);
517 Log2(("VBoxNetFlt: Dropping the sk_buff.\n"));
518 vboxNetFltLinuxFreeSkBuff(pBuf, pSG);
519 }
520
521 return VINF_SUCCESS;
522}
523
524static void vboxNetFltLinuxForwardToIntNet(PVBOXNETFLTINS pThis, struct sk_buff *pBuf)
525{
526 uint32_t fSrc = pBuf->pkt_type == PACKET_OUTGOING ? INTNETTRUNKDIR_HOST : INTNETTRUNKDIR_WIRE;
527
528#ifndef VBOXNETFLT_SG_SUPPORT
529 /*
530 * Get rid of fragmented packets, they cause too much trouble.
531 */
532 struct sk_buff *pCopy = skb_copy(pBuf, GFP_KERNEL);
533 kfree_skb(pBuf);
534 if (!pCopy)
535 {
536 LogRel(("VBoxNetFlt: Failed to allocate packet buffer, dropping the packet.\n"));
537 return;
538 }
539 pBuf = pCopy;
540#endif
541
542 if (VBOX_SKB_IS_GSO(pBuf))
543 {
544 /* Need to segment the packet */
545 struct sk_buff *pNext, *pSegment;
546 //Log2(("vboxNetFltLinuxForwardToIntNet: cb=%u gso_size=%u gso_segs=%u gso_type=%u\n",
547 // pBuf->len, skb_shinfo(pBuf)->gso_size, skb_shinfo(pBuf)->gso_segs, skb_shinfo(pBuf)->gso_type));
548
549 for (pSegment = VBOX_SKB_GSO_SEGMENT(pBuf); pSegment; pSegment = pNext)
550 {
551 pNext = pSegment->next;
552 pSegment->next = 0;
553 vboxNetFltLinuxForwardSegment(pThis, pSegment, fSrc);
554 }
555 dev_kfree_skb(pBuf);
556 }
557 else
558 {
559 if (pBuf->ip_summed == CHECKSUM_PARTIAL)
560 if (VBOX_SKB_CHECKSUM_HELP(pBuf))
561 {
562 LogRel(("VBoxNetFlt: Failed to compute checksum, dropping the packet.\n"));
563 dev_kfree_skb(pBuf);
564 return;
565 }
566 vboxNetFltLinuxForwardSegment(pThis, pBuf, fSrc);
567 }
568 /*
569 * Create a (scatter/)gather list for the sk_buff and feed it to the internal network.
570 */
571}
572
573static void vboxNetFltLinuxXmitTask(struct work_struct *pWork)
574{
575 struct sk_buff *pBuf;
576 bool fActive;
577 PVBOXNETFLTINS pThis;
578 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
579
580 Log2(("vboxNetFltLinuxXmitTask: Got work %p.\n", pWork));
581 pThis = VBOX_FLT_XT_TO_INST(pWork);
582 /*
583 * Active? Retain the instance and increment the busy counter.
584 */
585 RTSpinlockAcquire(pThis->hSpinlock, &Tmp);
586 fActive = ASMAtomicUoReadBool(&pThis->fActive);
587 if (fActive)
588 vboxNetFltRetain(pThis, true /* fBusy */);
589 RTSpinlockRelease(pThis->hSpinlock, &Tmp);
590 if (!fActive)
591 return;
592
593 while ((pBuf = skb_dequeue(&pThis->u.s.XmitQueue)) != 0)
594 vboxNetFltLinuxForwardToIntNet(pThis, pBuf);
595
596 vboxNetFltRelease(pThis, true /* fBusy */);
597}
598
599/**
600 * Internal worker for vboxNetFltOsInitInstance and vboxNetFltOsMaybeRediscovered.
601 *
602 * @returns VBox status code.
603 * @param pThis The instance.
604 * @param fRediscovery If set we're doing a rediscovery attempt, so, don't
605 * flood the release log.
606 */
607static int vboxNetFltLinuxAttachToInterface(PVBOXNETFLTINS pThis, struct net_device *pDev)
608{
609 struct packet_type *pt;
610 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
611
612 LogFlow(("vboxNetFltLinuxAttachToInterface: pThis=%p (%s)\n", pThis, pThis->szName));
613
614 if (!pDev)
615 {
616 Log(("VBoxNetFlt: failed to find device '%s'\n", pThis->szName));
617 return VERR_INTNET_FLT_IF_NOT_FOUND;
618 }
619
620 dev_hold(pDev);
621 RTSpinlockAcquire(pThis->hSpinlock, &Tmp);
622 ASMAtomicUoWritePtr((void * volatile *)&pThis->u.s.pDev, pDev);
623 RTSpinlockRelease(pThis->hSpinlock, &Tmp);
624
625 Log(("vboxNetFltLinuxAttachToInterface: Device %p(%s) retained. ref=%d\n", pDev, pDev->name, atomic_read(&pDev->refcnt)));
626 Log(("vboxNetFltLinuxAttachToInterface: Got pDev=%p pThis=%p pThis->u.s.pDev=%p\n", pDev, pThis, ASMAtomicUoReadPtr((void * volatile *)&pThis->u.s.pDev)));
627 /*
628 * Get the mac address while we still have a valid ifnet reference.
629 */
630 memcpy(&pThis->u.s.Mac, pDev->dev_addr, sizeof(pThis->u.s.Mac));
631
632 pt = &pThis->u.s.PacketType;
633 pt->type = __constant_htons(ETH_P_ALL);
634 pt->dev = pDev;
635 pt->func = vboxNetFltLinuxPacketHandler;
636 dev_add_pack(pt);
637 RTSpinlockAcquire(pThis->hSpinlock, &Tmp);
638 pDev = (struct net_device *)ASMAtomicUoReadPtr((void * volatile *)&pThis->u.s.pDev);
639 if (pDev)
640 {
641 ASMAtomicUoWriteBool(&pThis->fDisconnectedFromHost, false);
642 ASMAtomicUoWriteBool(&pThis->u.s.fRegistered, true);
643 pDev = NULL; /* don't dereference it */
644 }
645 RTSpinlockRelease(pThis->hSpinlock, &Tmp);
646 Log(("vboxNetFltLinuxAttachToInterface: this=%p: Packet handler installed.\n", pThis));
647
648 /* Release the interface on failure. */
649 if (pDev)
650 {
651 RTSpinlockAcquire(pThis->hSpinlock, &Tmp);
652 ASMAtomicUoWritePtr((void * volatile *)&pThis->u.s.pDev, NULL);
653 RTSpinlockRelease(pThis->hSpinlock, &Tmp);
654 dev_put(pDev);
655 Log(("vboxNetFltLinuxAttachToInterface: Device %p(%s) released. ref=%d\n", pDev, pDev->name, atomic_read(&pDev->refcnt)));
656 }
657
658 LogRel(("VBoxNetFlt: attached to '%s' / %.*Rhxs\n", pThis->szName, sizeof(pThis->u.s.Mac), &pThis->u.s.Mac));
659 return VINF_SUCCESS;
660}
661
662
663static int vboxNetFltLinuxUnregisterDevice(PVBOXNETFLTINS pThis, struct net_device *pDev)
664{
665 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
666
667 Assert(!pThis->fDisconnectedFromHost);
668 RTSpinlockAcquire(pThis->hSpinlock, &Tmp);
669 ASMAtomicWriteBool(&pThis->u.s.fRegistered, false);
670 ASMAtomicWriteBool(&pThis->fDisconnectedFromHost, true);
671 ASMAtomicUoWritePtr((void * volatile *)&pThis->u.s.pDev, NULL);
672 RTSpinlockRelease(pThis->hSpinlock, &Tmp);
673
674 dev_remove_pack(&pThis->u.s.PacketType);
675 skb_queue_purge(&pThis->u.s.XmitQueue);
676 Log(("vboxNetFltLinuxUnregisterDevice: this=%p: Packet handler removed, xmit queue purged.\n", pThis));
677 Log(("vboxNetFltLinuxUnregisterDevice: Device %p(%s) released. ref=%d\n", pDev, pDev->name, atomic_read(&pDev->refcnt)));
678 dev_put(pDev);
679
680 return NOTIFY_OK;
681}
682
683static int vboxNetFltLinuxDeviceIsUp(PVBOXNETFLTINS pThis, struct net_device *pDev)
684{
685 /* Check if we are not suspended and promiscuous mode has not been set. */
686 if (ASMAtomicUoReadBool(&pThis->fActive) && !ASMAtomicUoReadBool(&pThis->u.s.fPromiscuousSet))
687 {
688 /* Note that there is no need for locking as the kernel got hold of the lock already. */
689 dev_set_promiscuity(pDev, 1);
690 ASMAtomicWriteBool(&pThis->u.s.fPromiscuousSet, true);
691 Log(("vboxNetFltLinuxDeviceIsUp: enabled promiscuous mode on %s (%d)\n", pThis->szName, VBOX_GET_PCOUNT(pDev)));
692 }
693 else
694 Log(("vboxNetFltLinuxDeviceIsUp: no need to enable promiscuous mode on %s (%d)\n", pThis->szName, VBOX_GET_PCOUNT(pDev)));
695 return NOTIFY_OK;
696}
697
698static int vboxNetFltLinuxDeviceGoingDown(PVBOXNETFLTINS pThis, struct net_device *pDev)
699{
700 /* Undo promiscuous mode if we has set it. */
701 if (ASMAtomicUoReadBool(&pThis->u.s.fPromiscuousSet))
702 {
703 /* Note that there is no need for locking as the kernel got hold of the lock already. */
704 dev_set_promiscuity(pDev, -1);
705 ASMAtomicWriteBool(&pThis->u.s.fPromiscuousSet, false);
706 Log(("vboxNetFltLinuxDeviceGoingDown: disabled promiscuous mode on %s (%d)\n", pThis->szName, VBOX_GET_PCOUNT(pDev)));
707 }
708 else
709 Log(("vboxNetFltLinuxDeviceGoingDown: no need to disable promiscuous mode on %s (%d)\n", pThis->szName, VBOX_GET_PCOUNT(pDev)));
710 return NOTIFY_OK;
711}
712
713static int vboxNetFltLinuxNotifierCallback(struct notifier_block *self, unsigned long ulEventType, void *ptr)
714
715{
716 int rc;
717#ifdef DEBUG
718 char *pszEvent = "<unknown>";
719#endif
720 struct net_device *pDev = (struct net_device *)ptr;
721 PVBOXNETFLTINS pThis = VBOX_FLT_NB_TO_INST(self);
722
723#ifdef DEBUG
724 switch (ulEventType)
725 {
726 case NETDEV_REGISTER: pszEvent = "NETDEV_REGISTER"; break;
727 case NETDEV_UNREGISTER: pszEvent = "NETDEV_UNREGISTER"; break;
728 case NETDEV_UP: pszEvent = "NETDEV_UP"; break;
729 case NETDEV_DOWN: pszEvent = "NETDEV_DOWN"; break;
730 case NETDEV_REBOOT: pszEvent = "NETDEV_REBOOT"; break;
731 case NETDEV_CHANGENAME: pszEvent = "NETDEV_CHANGENAME"; break;
732 case NETDEV_CHANGE: pszEvent = "NETDEV_CHANGE"; break;
733 case NETDEV_CHANGEMTU: pszEvent = "NETDEV_CHANGEMTU"; break;
734 case NETDEV_CHANGEADDR: pszEvent = "NETDEV_CHANGEADDR"; break;
735 case NETDEV_GOING_DOWN: pszEvent = "NETDEV_GOING_DOWN"; break;
736 }
737 Log(("VBoxNetFlt: got event %s(0x%lx) on %s, pDev=%p pThis=%p pThis->u.s.pDev=%p\n",
738 pszEvent, ulEventType, pDev->name, pDev, pThis, ASMAtomicUoReadPtr((void * volatile *)&pThis->u.s.pDev)));
739#endif
740 if (ulEventType == NETDEV_REGISTER && !strcmp(pDev->name, pThis->szName))
741 {
742 vboxNetFltLinuxAttachToInterface(pThis, pDev);
743 }
744 else
745 {
746 pDev = (struct net_device *)ASMAtomicUoReadPtr((void * volatile *)&pThis->u.s.pDev);
747 if (pDev != ptr)
748 return NOTIFY_OK;
749 rc = NOTIFY_OK;
750 switch (ulEventType)
751 {
752 case NETDEV_UNREGISTER:
753 rc = vboxNetFltLinuxUnregisterDevice(pThis, pDev);
754 break;
755 case NETDEV_UP:
756 rc = vboxNetFltLinuxDeviceIsUp(pThis, pDev);
757 break;
758 case NETDEV_GOING_DOWN:
759 rc = vboxNetFltLinuxDeviceGoingDown(pThis, pDev);
760 break;
761 case NETDEV_CHANGENAME:
762 break;
763 }
764 }
765
766 return rc;
767}
768
769bool vboxNetFltOsMaybeRediscovered(PVBOXNETFLTINS pThis)
770{
771 return !ASMAtomicUoReadBool(&pThis->fDisconnectedFromHost);
772}
773
774
775int vboxNetFltPortOsXmit(PVBOXNETFLTINS pThis, PINTNETSG pSG, uint32_t fDst)
776{
777 uint8_t *pTmp;
778 struct net_device * pDev;
779 int err;
780 int rc = VINF_SUCCESS;
781
782 LogFlow(("vboxNetFltPortOsXmit: pThis=%p (%s)\n", pThis, pThis->szName));
783
784 pTmp = pSG->aSegs[0].pv;
785
786 pDev = vboxNetFltLinuxRetainNetDev(pThis);
787 if (pDev)
788 {
789 /*
790 * Create a sk_buff for the gather list and push it onto the wire.
791 */
792 if (fDst & INTNETTRUNKDIR_WIRE)
793 {
794 struct sk_buff *pBuf = vboxNetFltLinuxSkBufFromSG(pThis, pSG, true);
795 if (pBuf)
796 {
797 Log(("VBoxNetFlt: (int)%02x:%02x:%02x:%02x:%02x:%02x"
798 " --> (wire)%02x:%02x:%02x:%02x:%02x:%02x (%u bytes)\n",
799 pTmp[6], pTmp[7], pTmp[8], pTmp[9], pTmp[10], pTmp[11],
800 pTmp[0], pTmp[1], pTmp[2], pTmp[3], pTmp[4], pTmp[5],
801 pSG->cbTotal));
802 err = dev_queue_xmit(pBuf);
803 if (err)
804 rc = RTErrConvertFromErrno(err);
805 }
806 else
807 rc = VERR_NO_MEMORY;
808 }
809
810 /*
811 * Create a sk_buff for the gather list and push it onto the host stack.
812 */
813 if (fDst & INTNETTRUNKDIR_HOST)
814 {
815 struct sk_buff *pBuf = vboxNetFltLinuxSkBufFromSG(pThis, pSG, false);
816 if (pBuf)
817 {
818 Log(("VBoxNetFlt: (int)%02x:%02x:%02x:%02x:%02x:%02x"
819 " --> (host)%02x:%02x:%02x:%02x:%02x:%02x (%u bytes)\n",
820 pTmp[6], pTmp[7], pTmp[8], pTmp[9], pTmp[10], pTmp[11],
821 pTmp[0], pTmp[1], pTmp[2], pTmp[3], pTmp[4], pTmp[5],
822 pSG->cbTotal));
823 err = netif_rx_ni(pBuf);
824 if (err)
825 rc = RTErrConvertFromErrno(err);
826 }
827 else
828 rc = VERR_NO_MEMORY;
829 }
830
831 vboxNetFltLinuxReleaseNetDev(pThis, pDev);
832 }
833
834 return rc;
835}
836
837
838bool vboxNetFltPortOsIsPromiscuous(PVBOXNETFLTINS pThis)
839{
840 bool fRc = false;
841 struct net_device * pDev = vboxNetFltLinuxRetainNetDev(pThis);
842 if (pDev)
843 {
844 fRc = !!(pDev->promiscuity - (ASMAtomicUoReadBool(&pThis->u.s.fPromiscuousSet) & 1));
845 vboxNetFltLinuxReleaseNetDev(pThis, pDev);
846 }
847 return fRc;
848}
849
850
851void vboxNetFltPortOsGetMacAddress(PVBOXNETFLTINS pThis, PRTMAC pMac)
852{
853 *pMac = pThis->u.s.Mac;
854}
855
856
857bool vboxNetFltPortOsIsHostMac(PVBOXNETFLTINS pThis, PCRTMAC pMac)
858{
859 /* ASSUMES that the MAC address never changes. */
860 return pThis->u.s.Mac.au16[0] == pMac->au16[0]
861 && pThis->u.s.Mac.au16[1] == pMac->au16[1]
862 && pThis->u.s.Mac.au16[2] == pMac->au16[2];
863}
864
865
866void vboxNetFltPortOsSetActive(PVBOXNETFLTINS pThis, bool fActive)
867{
868 struct net_device * pDev;
869
870 LogFlow(("vboxNetFltPortOsSetActive: pThis=%p (%s), fActive=%s\n",
871 pThis, pThis->szName, fActive?"true":"false"));
872
873 pDev = vboxNetFltLinuxRetainNetDev(pThis);
874 if (pDev)
875 {
876 /*
877 * This api is a bit weird, the best reference is the code.
878 *
879 * Also, we have a bit or race conditions wrt the maintance of
880 * host the interface promiscuity for vboxNetFltPortOsIsPromiscuous.
881 */
882 u_int16_t fIf;
883 unsigned const cPromiscBefore = VBOX_GET_PCOUNT(pDev);
884 if (fActive)
885 {
886 int err = 0;
887 Assert(!pThis->u.s.fPromiscuousSet);
888
889#if 0
890 /*
891 * Try bring the interface up and running if it's down.
892 */
893 fIf = dev_get_flags(pDev);
894 if ((fIf & (IFF_UP | IFF_RUNNING)) != (IFF_UP | IFF_RUNNING))
895 {
896 rtnl_lock();
897 err = dev_change_flags(pDev, fIf | IFF_UP);
898 rtnl_unlock();
899 fIf = dev_get_flags(pDev);
900 }
901
902 /*
903 * Is it already up? If it isn't, leave it to the link event or
904 * we'll upset if_pcount (as stated above, ifnet_set_promiscuous is weird).
905 */
906 if ((fIf & (IFF_UP | IFF_RUNNING)) == (IFF_UP | IFF_RUNNING)
907 && !ASMAtomicReadBool(&pThis->u.s.fPromiscuousSet))
908 {
909#endif
910 rtnl_lock();
911 dev_set_promiscuity(pDev, 1);
912 rtnl_unlock();
913 pThis->u.s.fPromiscuousSet = true;
914 Log(("vboxNetFltPortOsSetActive: enabled promiscuous mode on %s (%d)\n", pThis->szName, VBOX_GET_PCOUNT(pDev)));
915#if 0
916 /* check if it actually worked, this stuff is not always behaving well. */
917 if (!(dev_get_flags(pDev) & IFF_PROMISC))
918 {
919 err = dev_change_flags(pDev, fIf | IFF_PROMISC);
920 if (!err)
921 Log(("vboxNetFlt: fixed IFF_PROMISC on %s (%d->%d)\n", pThis->szName, cPromiscBefore, VBOX_GET_PCOUNT(pDev)));
922 else
923 Log(("VBoxNetFlt: failed to fix IFF_PROMISC on %s, err=%d (%d->%d)\n",
924 pThis->szName, err, cPromiscBefore, VBOX_GET_PCOUNT(pDev)));
925 }
926#endif
927#if 0
928 }
929 else if (!err)
930 Log(("VBoxNetFlt: Waiting for the link to come up... (%d->%d)\n", cPromiscBefore, VBOX_GET_PCOUNT(pDev)));
931 if (err)
932 LogRel(("VBoxNetFlt: Failed to put '%s' into promiscuous mode, err=%d (%d->%d)\n", pThis->szName, err, cPromiscBefore, VBOX_GET_PCOUNT(pDev)));
933#endif
934 }
935 else
936 {
937 if (pThis->u.s.fPromiscuousSet)
938 {
939 rtnl_lock();
940 dev_set_promiscuity(pDev, -1);
941 rtnl_unlock();
942 Log(("vboxNetFltPortOsSetActive: disabled promiscuous mode on %s (%d)\n", pThis->szName, VBOX_GET_PCOUNT(pDev)));
943 }
944 pThis->u.s.fPromiscuousSet = false;
945
946 fIf = dev_get_flags(pDev);
947 Log(("VBoxNetFlt: fIf=%#x; %d->%d\n", fIf, cPromiscBefore, VBOX_GET_PCOUNT(pDev)));
948 }
949
950 vboxNetFltLinuxReleaseNetDev(pThis, pDev);
951 }
952}
953
954
955int vboxNetFltOsDisconnectIt(PVBOXNETFLTINS pThis)
956{
957 /* Nothing to do here. */
958 return VINF_SUCCESS;
959}
960
961
962int vboxNetFltOsConnectIt(PVBOXNETFLTINS pThis)
963{
964 /* Nothing to do here. */
965 return VINF_SUCCESS;
966}
967
968
969void vboxNetFltOsDeleteInstance(PVBOXNETFLTINS pThis)
970{
971 struct net_device *pDev;
972 bool fRegistered;
973 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
974
975 RTSpinlockAcquire(pThis->hSpinlock, &Tmp);
976 pDev = (struct net_device *)ASMAtomicUoReadPtr((void * volatile *)&pThis->u.s.pDev);
977 fRegistered = ASMAtomicUoReadBool(&pThis->u.s.fRegistered);
978 RTSpinlockRelease(pThis->hSpinlock, &Tmp);
979 if (fRegistered)
980 {
981 dev_remove_pack(&pThis->u.s.PacketType);
982 skb_queue_purge(&pThis->u.s.XmitQueue);
983 Log(("vboxNetFltOsDeleteInstance: this=%p: Packet handler removed, xmit queue purged.\n", pThis));
984 Log(("vboxNetFltOsDeleteInstance: Device %p(%s) released. ref=%d\n", pDev, pDev->name, atomic_read(&pDev->refcnt)));
985 dev_put(pDev);
986 }
987 Log(("vboxNetFltOsDeleteInstance: this=%p: Notifier removed.\n", pThis));
988 unregister_netdevice_notifier(&pThis->u.s.Notifier);
989}
990
991
992int vboxNetFltOsInitInstance(PVBOXNETFLTINS pThis)
993{
994 int err;
995 pThis->u.s.Notifier.notifier_call = vboxNetFltLinuxNotifierCallback;
996 err = register_netdevice_notifier(&pThis->u.s.Notifier);
997 if (err)
998 return VERR_INTNET_FLT_IF_FAILED;
999 if (!pThis->u.s.fRegistered)
1000 {
1001 unregister_netdevice_notifier(&pThis->u.s.Notifier);
1002 LogRel(("VBoxNetFlt: failed to find %s.\n", pThis->szName));
1003 return VERR_INTNET_FLT_IF_NOT_FOUND;
1004 }
1005 Log(("vboxNetFltOsInitInstance: this=%p: Notifier installed.\n", pThis));
1006 return pThis->fDisconnectedFromHost ? VERR_INTNET_FLT_IF_FAILED : VINF_SUCCESS;
1007}
1008
1009int vboxNetFltOsPreInitInstance(PVBOXNETFLTINS pThis)
1010{
1011 /*
1012 * Init the linux specific members.
1013 */
1014 pThis->u.s.pDev = NULL;
1015 pThis->u.s.fRegistered = false;
1016 pThis->u.s.fPromiscuousSet = false;
1017 memset(&pThis->u.s.PacketType, 0, sizeof(pThis->u.s.PacketType));
1018 skb_queue_head_init(&pThis->u.s.XmitQueue);
1019#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20)
1020 INIT_WORK(&pThis->u.s.XmitTask, vboxNetFltLinuxXmitTask);
1021#else
1022 INIT_WORK(&pThis->u.s.XmitTask, vboxNetFltLinuxXmitTask, NULL);
1023#endif
1024
1025 return VINF_SUCCESS;
1026}
1027
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