1
/* $Id: VBoxNetAdp-linux.c $ */
3
* VBoxNetAdp - Virtual Network Adapter Driver (Host), Linux Specific Code.
7
* Copyright (C) 2009-2012 Oracle Corporation
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.
18
/*******************************************************************************
20
*******************************************************************************/
21
#include "the-linux-kernel.h"
22
#include "version-generated.h"
23
#include "product-generated.h"
24
#include <linux/netdevice.h>
25
#include <linux/etherdevice.h>
26
#include <linux/miscdevice.h>
28
#define LOG_GROUP LOG_GROUP_NET_ADP_DRV
31
#include <iprt/process.h>
32
#include <iprt/initterm.h>
36
#include <iprt/assert.h>
37
#include <iprt/semaphore.h>
38
#include <iprt/spinlock.h>
39
#include <iprt/string.h>
40
#include <iprt/uuid.h>
41
#include <iprt/alloca.h>
44
#define VBOXNETADP_OS_SPECFIC 1
45
#include "../VBoxNetAdpInternal.h"
47
/*******************************************************************************
48
* Defined Constants And Macros *
49
*******************************************************************************/
50
#define VBOXNETADP_LINUX_NAME "vboxnet%d"
51
#define VBOXNETADP_CTL_DEV_NAME "vboxnetctl"
53
#define VBOXNETADP_FROM_IFACE(iface) ((PVBOXNETADP) ifnet_softc(iface))
55
/*******************************************************************************
56
* Internal Functions *
57
*******************************************************************************/
58
static int VBoxNetAdpLinuxInit(void);
59
static void VBoxNetAdpLinuxUnload(void);
61
static int VBoxNetAdpLinuxOpen(struct inode *pInode, struct file *pFilp);
62
static int VBoxNetAdpLinuxClose(struct inode *pInode, struct file *pFilp);
63
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
64
static int VBoxNetAdpLinuxIOCtl(struct inode *pInode, struct file *pFilp,
65
unsigned int uCmd, unsigned long ulArg);
66
#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) */
67
static long VBoxNetAdpLinuxIOCtlUnlocked(struct file *pFilp,
68
unsigned int uCmd, unsigned long ulArg);
69
#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) */
71
/*******************************************************************************
73
*******************************************************************************/
74
module_init(VBoxNetAdpLinuxInit);
75
module_exit(VBoxNetAdpLinuxUnload);
77
MODULE_AUTHOR(VBOX_VENDOR);
78
MODULE_DESCRIPTION(VBOX_PRODUCT " Network Adapter Driver");
79
MODULE_LICENSE("GPL");
81
MODULE_VERSION(VBOX_VERSION_STRING " (" RT_XSTR(INTNETTRUNKIFPORT_VERSION) ")");
85
* The (common) global data.
87
static struct file_operations gFileOpsVBoxNetAdp =
90
open: VBoxNetAdpLinuxOpen,
91
release: VBoxNetAdpLinuxClose,
92
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
93
ioctl: VBoxNetAdpLinuxIOCtl,
94
#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) */
95
unlocked_ioctl: VBoxNetAdpLinuxIOCtlUnlocked,
96
#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) */
99
/** The miscdevice structure. */
100
static struct miscdevice g_CtlDev =
102
minor: MISC_DYNAMIC_MINOR,
103
name: VBOXNETADP_CTL_DEV_NAME,
104
fops: &gFileOpsVBoxNetAdp,
105
# if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 17)
106
devfs_name: VBOXNETADP_CTL_DEV_NAME
110
struct VBoxNetAdpPriv
112
struct net_device_stats Stats;
115
typedef struct VBoxNetAdpPriv VBOXNETADPPRIV;
116
typedef VBOXNETADPPRIV *PVBOXNETADPPRIV;
118
static int vboxNetAdpLinuxOpen(struct net_device *pNetDev)
120
netif_start_queue(pNetDev);
124
static int vboxNetAdpLinuxStop(struct net_device *pNetDev)
126
netif_stop_queue(pNetDev);
130
static int vboxNetAdpLinuxXmit(struct sk_buff *pSkb, struct net_device *pNetDev)
132
PVBOXNETADPPRIV pPriv = netdev_priv(pNetDev);
134
/* Update the stats. */
135
pPriv->Stats.tx_packets++;
136
pPriv->Stats.tx_bytes += pSkb->len;
137
/* Update transmission time stamp. */
138
pNetDev->trans_start = jiffies;
139
/* Nothing else to do, just free the sk_buff. */
144
struct net_device_stats *vboxNetAdpLinuxGetStats(struct net_device *pNetDev)
146
PVBOXNETADPPRIV pPriv = netdev_priv(pNetDev);
147
return &pPriv->Stats;
150
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29)
151
static const struct net_device_ops vboxNetAdpNetdevOps = {
152
.ndo_open = vboxNetAdpLinuxOpen,
153
.ndo_stop = vboxNetAdpLinuxStop,
154
.ndo_start_xmit = vboxNetAdpLinuxXmit,
155
.ndo_get_stats = vboxNetAdpLinuxGetStats
159
static void vboxNetAdpNetDevInit(struct net_device *pNetDev)
161
PVBOXNETADPPRIV pPriv;
163
ether_setup(pNetDev);
164
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29)
165
pNetDev->netdev_ops = &vboxNetAdpNetdevOps;
166
#else /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29) */
167
pNetDev->open = vboxNetAdpLinuxOpen;
168
pNetDev->stop = vboxNetAdpLinuxStop;
169
pNetDev->hard_start_xmit = vboxNetAdpLinuxXmit;
170
pNetDev->get_stats = vboxNetAdpLinuxGetStats;
171
#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29) */
173
pPriv = netdev_priv(pNetDev);
174
memset(pPriv, 0, sizeof(*pPriv));
178
int vboxNetAdpOsCreate(PVBOXNETADP pThis, PCRTMAC pMACAddress)
180
int rc = VINF_SUCCESS;
181
struct net_device *pNetDev;
183
/* No need for private data. */
184
pNetDev = alloc_netdev(sizeof(VBOXNETADPPRIV),
185
pThis->szName[0] ? pThis->szName : VBOXNETADP_LINUX_NAME,
186
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 17, 0)
189
vboxNetAdpNetDevInit);
194
if (pNetDev->dev_addr)
196
memcpy(pNetDev->dev_addr, pMACAddress, ETH_ALEN);
197
Log2(("vboxNetAdpOsCreate: pNetDev->dev_addr = %.6Rhxd\n", pNetDev->dev_addr));
198
err = register_netdev(pNetDev);
201
strncpy(pThis->szName, pNetDev->name, sizeof(pThis->szName));
202
pThis->szName[sizeof(pThis->szName) - 1] = '\0';
203
pThis->u.s.pNetDev = pNetDev;
204
Log2(("vboxNetAdpOsCreate: pThis=%p pThis->szName = %p\n", pThis, pThis->szName));
210
LogRel(("VBoxNetAdp: failed to set MAC address (dev->dev_addr == NULL)\n"));
213
free_netdev(pNetDev);
214
rc = RTErrConvertFromErrno(err);
219
void vboxNetAdpOsDestroy(PVBOXNETADP pThis)
221
struct net_device *pNetDev = pThis->u.s.pNetDev;
222
AssertPtr(pThis->u.s.pNetDev);
224
pThis->u.s.pNetDev = NULL;
225
unregister_netdev(pNetDev);
226
free_netdev(pNetDev);
230
* Device open. Called on open /dev/vboxnetctl
232
* @param pInode Pointer to inode info structure.
233
* @param pFilp Associated file pointer.
235
static int VBoxNetAdpLinuxOpen(struct inode *pInode, struct file *pFilp)
237
Log(("VBoxNetAdpLinuxOpen: pid=%d/%d %s\n", RTProcSelf(), current->pid, current->comm));
239
#ifdef VBOX_WITH_HARDENING
241
* Only root is allowed to access the device, enforce it!
243
if (!capable(CAP_SYS_ADMIN))
245
Log(("VBoxNetAdpLinuxOpen: admin privileges required!\n"));
257
* @param pInode Pointer to inode info structure.
258
* @param pFilp Associated file pointer.
260
static int VBoxNetAdpLinuxClose(struct inode *pInode, struct file *pFilp)
262
Log(("VBoxNetAdpLinuxClose: pid=%d/%d %s\n",
263
RTProcSelf(), current->pid, current->comm));
264
pFilp->private_data = NULL;
269
* Device I/O Control entry point.
271
* @param pFilp Associated file pointer.
272
* @param uCmd The function specified to ioctl().
273
* @param ulArg The argument specified to ioctl().
275
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
276
static int VBoxNetAdpLinuxIOCtl(struct inode *pInode, struct file *pFilp,
277
unsigned int uCmd, unsigned long ulArg)
278
#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) */
279
static long VBoxNetAdpLinuxIOCtlUnlocked(struct file *pFilp,
280
unsigned int uCmd, unsigned long ulArg)
281
#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) */
286
char *pszName = NULL;
288
Log(("VBoxNetAdpLinuxIOCtl: param len %#x; uCmd=%#x; add=%#x\n", _IOC_SIZE(uCmd), uCmd, VBOXNETADP_CTL_ADD));
289
if (RT_UNLIKELY(_IOC_SIZE(uCmd) != sizeof(Req))) /* paranoia */
291
Log(("VBoxNetAdpLinuxIOCtl: bad ioctl sizeof(Req)=%#x _IOC_SIZE=%#x; uCmd=%#x.\n", sizeof(Req), _IOC_SIZE(uCmd), uCmd));
297
case VBOXNETADP_CTL_ADD:
298
Log(("VBoxNetAdpLinuxIOCtl: _IOC_DIR(uCmd)=%#x; IOC_OUT=%#x\n", _IOC_DIR(uCmd), IOC_OUT));
299
if (RT_UNLIKELY(copy_from_user(&Req, (void *)ulArg, sizeof(Req))))
301
Log(("VBoxNetAdpLinuxIOCtl: copy_from_user(,%#lx,) failed; uCmd=%#x.\n", ulArg, uCmd));
304
Log(("VBoxNetAdpLinuxIOCtl: Add %s\n", Req.szName));
308
pAdp = vboxNetAdpFindByName(Req.szName);
311
Log(("VBoxNetAdpLinuxIOCtl: '%s' already exists\n", Req.szName));
314
pszName = Req.szName;
316
rc = vboxNetAdpCreate(&pAdp, pszName);
319
Log(("VBoxNetAdpLinuxIOCtl: vboxNetAdpCreate -> %Rrc\n", rc));
320
return -(rc == VERR_OUT_OF_RESOURCES ? ENOMEM : EINVAL);
323
Assert(strlen(pAdp->szName) < sizeof(Req.szName));
324
strncpy(Req.szName, pAdp->szName, sizeof(Req.szName) - 1);
325
Req.szName[sizeof(Req.szName) - 1] = '\0';
327
if (RT_UNLIKELY(copy_to_user((void *)ulArg, &Req, sizeof(Req))))
329
/* this is really bad! */
330
/** @todo remove the adapter again? */
331
printk(KERN_ERR "VBoxNetAdpLinuxIOCtl: copy_to_user(%#lx,,%#zx); uCmd=%#x!\n", ulArg, sizeof(Req), uCmd);
334
Log(("VBoxNetAdpLinuxIOCtl: Successfully added '%s'\n", Req.szName));
337
case VBOXNETADP_CTL_REMOVE:
338
if (RT_UNLIKELY(copy_from_user(&Req, (void *)ulArg, sizeof(Req))))
340
Log(("VBoxNetAdpLinuxIOCtl: copy_from_user(,%#lx,) failed; uCmd=%#x.\n", ulArg, uCmd));
343
Log(("VBoxNetAdpLinuxIOCtl: Remove %s\n", Req.szName));
345
pAdp = vboxNetAdpFindByName(Req.szName);
348
Log(("VBoxNetAdpLinuxIOCtl: '%s' not found\n", Req.szName));
352
rc = vboxNetAdpDestroy(pAdp);
355
Log(("VBoxNetAdpLinuxIOCtl: vboxNetAdpDestroy('%s') -> %Rrc\n", Req.szName, rc));
358
Log(("VBoxNetAdpLinuxIOCtl: Successfully removed '%s'\n", Req.szName));
362
printk(KERN_ERR "VBoxNetAdpLinuxIOCtl: unknown command %x.\n", uCmd);
369
int vboxNetAdpOsInit(PVBOXNETADP pThis)
372
* Init linux-specific members.
374
pThis->u.s.pNetDev = NULL;
384
* @returns appropriate status code.
386
static int __init VBoxNetAdpLinuxInit(void)
395
Log(("VBoxNetAdpLinuxInit\n"));
397
rc = vboxNetAdpInit();
400
rc = misc_register(&g_CtlDev);
403
printk(KERN_ERR "VBoxNetAdp: Can't register " VBOXNETADP_CTL_DEV_NAME " device! rc=%d\n", rc);
406
LogRel(("VBoxNetAdp: Successfully started.\n"));
410
LogRel(("VBoxNetAdp: failed to register vboxnet0 device (rc=%d)\n", rc));
413
LogRel(("VBoxNetAdp: failed to initialize IPRT (rc=%d)\n", rc));
415
return -RTErrConvertToErrno(rc);
422
* @todo We have to prevent this if we're busy!
424
static void __exit VBoxNetAdpLinuxUnload(void)
426
Log(("VBoxNetAdpLinuxUnload\n"));
429
* Undo the work done during start (in reverse order).
432
vboxNetAdpShutdown();
433
/* Remove control device */
434
misc_deregister(&g_CtlDev);
438
Log(("VBoxNetAdpLinuxUnload - done\n"));