~ubuntu-branches/ubuntu/trusty/virtualbox-lts-xenial/trusty-proposed

« back to all changes in this revision

Viewing changes to src/VBox/HostDrivers/VBoxNetAdp/linux/VBoxNetAdp-linux.c

  • Committer: Package Import Robot
  • Author(s): Gianfranco Costamagna
  • Date: 2016-02-23 14:28:26 UTC
  • Revision ID: package-import@ubuntu.com-20160223142826-bdu69el2z6wa2a44
Tags: upstream-4.3.36-dfsg
ImportĀ upstreamĀ versionĀ 4.3.36-dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* $Id: VBoxNetAdp-linux.c $ */
 
2
/** @file
 
3
 * VBoxNetAdp - Virtual Network Adapter Driver (Host), Linux Specific Code.
 
4
 */
 
5
 
 
6
/*
 
7
 * Copyright (C) 2009-2012 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
#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>
 
27
 
 
28
#define LOG_GROUP LOG_GROUP_NET_ADP_DRV
 
29
#include <VBox/log.h>
 
30
#include <VBox/err.h>
 
31
#include <iprt/process.h>
 
32
#include <iprt/initterm.h>
 
33
#include <iprt/mem.h>
 
34
 
 
35
/*
 
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>
 
42
*/
 
43
 
 
44
#define VBOXNETADP_OS_SPECFIC 1
 
45
#include "../VBoxNetAdpInternal.h"
 
46
 
 
47
/*******************************************************************************
 
48
*   Defined Constants And Macros                                               *
 
49
*******************************************************************************/
 
50
#define VBOXNETADP_LINUX_NAME      "vboxnet%d"
 
51
#define VBOXNETADP_CTL_DEV_NAME    "vboxnetctl"
 
52
 
 
53
#define VBOXNETADP_FROM_IFACE(iface) ((PVBOXNETADP) ifnet_softc(iface))
 
54
 
 
55
/*******************************************************************************
 
56
*   Internal Functions                                                         *
 
57
*******************************************************************************/
 
58
static int  VBoxNetAdpLinuxInit(void);
 
59
static void VBoxNetAdpLinuxUnload(void);
 
60
 
 
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) */
 
70
 
 
71
/*******************************************************************************
 
72
*   Global Variables                                                           *
 
73
*******************************************************************************/
 
74
module_init(VBoxNetAdpLinuxInit);
 
75
module_exit(VBoxNetAdpLinuxUnload);
 
76
 
 
77
MODULE_AUTHOR(VBOX_VENDOR);
 
78
MODULE_DESCRIPTION(VBOX_PRODUCT " Network Adapter Driver");
 
79
MODULE_LICENSE("GPL");
 
80
#ifdef MODULE_VERSION
 
81
MODULE_VERSION(VBOX_VERSION_STRING " (" RT_XSTR(INTNETTRUNKIFPORT_VERSION) ")");
 
82
#endif
 
83
 
 
84
/**
 
85
 * The (common) global data.
 
86
 */
 
87
static struct file_operations gFileOpsVBoxNetAdp =
 
88
{
 
89
    owner:      THIS_MODULE,
 
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) */
 
97
};
 
98
 
 
99
/** The miscdevice structure. */
 
100
static struct miscdevice g_CtlDev =
 
101
{
 
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
 
107
# endif
 
108
};
 
109
 
 
110
struct VBoxNetAdpPriv
 
111
{
 
112
    struct net_device_stats Stats;
 
113
};
 
114
 
 
115
typedef struct VBoxNetAdpPriv VBOXNETADPPRIV;
 
116
typedef VBOXNETADPPRIV *PVBOXNETADPPRIV;
 
117
 
 
118
static int vboxNetAdpLinuxOpen(struct net_device *pNetDev)
 
119
{
 
120
    netif_start_queue(pNetDev);
 
121
    return 0;
 
122
}
 
123
 
 
124
static int vboxNetAdpLinuxStop(struct net_device *pNetDev)
 
125
{
 
126
    netif_stop_queue(pNetDev);
 
127
    return 0;
 
128
}
 
129
 
 
130
static int vboxNetAdpLinuxXmit(struct sk_buff *pSkb, struct net_device *pNetDev)
 
131
{
 
132
    PVBOXNETADPPRIV pPriv = netdev_priv(pNetDev);
 
133
 
 
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. */
 
140
    dev_kfree_skb(pSkb);
 
141
    return 0;
 
142
}
 
143
 
 
144
struct net_device_stats *vboxNetAdpLinuxGetStats(struct net_device *pNetDev)
 
145
{
 
146
    PVBOXNETADPPRIV pPriv = netdev_priv(pNetDev);
 
147
    return &pPriv->Stats;
 
148
}
 
149
 
 
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
 
156
};
 
157
#endif
 
158
 
 
159
static void vboxNetAdpNetDevInit(struct net_device *pNetDev)
 
160
{
 
161
    PVBOXNETADPPRIV pPriv;
 
162
 
 
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) */
 
172
 
 
173
    pPriv = netdev_priv(pNetDev);
 
174
    memset(pPriv, 0, sizeof(*pPriv));
 
175
}
 
176
 
 
177
 
 
178
int vboxNetAdpOsCreate(PVBOXNETADP pThis, PCRTMAC pMACAddress)
 
179
{
 
180
    int rc = VINF_SUCCESS;
 
181
    struct net_device *pNetDev;
 
182
 
 
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)
 
187
                           NET_NAME_UNKNOWN,
 
188
#endif
 
189
                           vboxNetAdpNetDevInit);
 
190
    if (pNetDev)
 
191
    {
 
192
        int err;
 
193
 
 
194
        if (pNetDev->dev_addr)
 
195
        {
 
196
            memcpy(pNetDev->dev_addr, pMACAddress, ETH_ALEN);
 
197
            Log2(("vboxNetAdpOsCreate: pNetDev->dev_addr = %.6Rhxd\n", pNetDev->dev_addr));
 
198
            err = register_netdev(pNetDev);
 
199
            if (!err)
 
200
            {
 
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));
 
205
                return VINF_SUCCESS;
 
206
            }
 
207
        }
 
208
        else
 
209
        {
 
210
            LogRel(("VBoxNetAdp: failed to set MAC address (dev->dev_addr == NULL)\n"));
 
211
            err = EFAULT;
 
212
        }
 
213
        free_netdev(pNetDev);
 
214
        rc = RTErrConvertFromErrno(err);
 
215
    }
 
216
    return rc;
 
217
}
 
218
 
 
219
void vboxNetAdpOsDestroy(PVBOXNETADP pThis)
 
220
{
 
221
    struct net_device *pNetDev = pThis->u.s.pNetDev;
 
222
    AssertPtr(pThis->u.s.pNetDev);
 
223
 
 
224
    pThis->u.s.pNetDev = NULL;
 
225
    unregister_netdev(pNetDev);
 
226
    free_netdev(pNetDev);
 
227
}
 
228
 
 
229
/**
 
230
 * Device open. Called on open /dev/vboxnetctl
 
231
 *
 
232
 * @param   pInode      Pointer to inode info structure.
 
233
 * @param   pFilp       Associated file pointer.
 
234
 */
 
235
static int VBoxNetAdpLinuxOpen(struct inode *pInode, struct file *pFilp)
 
236
{
 
237
    Log(("VBoxNetAdpLinuxOpen: pid=%d/%d %s\n", RTProcSelf(), current->pid, current->comm));
 
238
 
 
239
#ifdef VBOX_WITH_HARDENING
 
240
    /*
 
241
     * Only root is allowed to access the device, enforce it!
 
242
     */
 
243
    if (!capable(CAP_SYS_ADMIN))
 
244
    {
 
245
        Log(("VBoxNetAdpLinuxOpen: admin privileges required!\n"));
 
246
        return -EPERM;
 
247
    }
 
248
#endif
 
249
 
 
250
    return 0;
 
251
}
 
252
 
 
253
 
 
254
/**
 
255
 * Close device.
 
256
 *
 
257
 * @param   pInode      Pointer to inode info structure.
 
258
 * @param   pFilp       Associated file pointer.
 
259
 */
 
260
static int VBoxNetAdpLinuxClose(struct inode *pInode, struct file *pFilp)
 
261
{
 
262
    Log(("VBoxNetAdpLinuxClose: pid=%d/%d %s\n",
 
263
         RTProcSelf(), current->pid, current->comm));
 
264
    pFilp->private_data = NULL;
 
265
    return 0;
 
266
}
 
267
 
 
268
/**
 
269
 * Device I/O Control entry point.
 
270
 *
 
271
 * @param   pFilp       Associated file pointer.
 
272
 * @param   uCmd        The function specified to ioctl().
 
273
 * @param   ulArg       The argument specified to ioctl().
 
274
 */
 
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) */
 
282
{
 
283
    VBOXNETADPREQ Req;
 
284
    PVBOXNETADP pAdp;
 
285
    int rc;
 
286
    char *pszName = NULL;
 
287
 
 
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 */
 
290
    {
 
291
        Log(("VBoxNetAdpLinuxIOCtl: bad ioctl sizeof(Req)=%#x _IOC_SIZE=%#x; uCmd=%#x.\n", sizeof(Req), _IOC_SIZE(uCmd), uCmd));
 
292
        return -EINVAL;
 
293
    }
 
294
 
 
295
    switch (uCmd)
 
296
    {
 
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))))
 
300
            {
 
301
                Log(("VBoxNetAdpLinuxIOCtl: copy_from_user(,%#lx,) failed; uCmd=%#x.\n", ulArg, uCmd));
 
302
                return -EFAULT;
 
303
            }
 
304
            Log(("VBoxNetAdpLinuxIOCtl: Add %s\n", Req.szName));
 
305
 
 
306
            if (Req.szName[0])
 
307
            {
 
308
                pAdp = vboxNetAdpFindByName(Req.szName);
 
309
                if (pAdp)
 
310
                {
 
311
                    Log(("VBoxNetAdpLinuxIOCtl: '%s' already exists\n", Req.szName));
 
312
                    return -EINVAL;
 
313
                }
 
314
                pszName = Req.szName;
 
315
            }
 
316
            rc = vboxNetAdpCreate(&pAdp, pszName);
 
317
            if (RT_FAILURE(rc))
 
318
            {
 
319
                Log(("VBoxNetAdpLinuxIOCtl: vboxNetAdpCreate -> %Rrc\n", rc));
 
320
                return -(rc == VERR_OUT_OF_RESOURCES ? ENOMEM : EINVAL);
 
321
            }
 
322
 
 
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';
 
326
 
 
327
            if (RT_UNLIKELY(copy_to_user((void *)ulArg, &Req, sizeof(Req))))
 
328
            {
 
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);
 
332
                return -EFAULT;
 
333
            }
 
334
            Log(("VBoxNetAdpLinuxIOCtl: Successfully added '%s'\n", Req.szName));
 
335
            break;
 
336
 
 
337
        case VBOXNETADP_CTL_REMOVE:
 
338
            if (RT_UNLIKELY(copy_from_user(&Req, (void *)ulArg, sizeof(Req))))
 
339
            {
 
340
                Log(("VBoxNetAdpLinuxIOCtl: copy_from_user(,%#lx,) failed; uCmd=%#x.\n", ulArg, uCmd));
 
341
                return -EFAULT;
 
342
            }
 
343
            Log(("VBoxNetAdpLinuxIOCtl: Remove %s\n", Req.szName));
 
344
 
 
345
            pAdp = vboxNetAdpFindByName(Req.szName);
 
346
            if (!pAdp)
 
347
            {
 
348
                Log(("VBoxNetAdpLinuxIOCtl: '%s' not found\n", Req.szName));
 
349
                return -EINVAL;
 
350
            }
 
351
 
 
352
            rc = vboxNetAdpDestroy(pAdp);
 
353
            if (RT_FAILURE(rc))
 
354
            {
 
355
                Log(("VBoxNetAdpLinuxIOCtl: vboxNetAdpDestroy('%s') -> %Rrc\n", Req.szName, rc));
 
356
                return -EINVAL;
 
357
            }
 
358
            Log(("VBoxNetAdpLinuxIOCtl: Successfully removed '%s'\n", Req.szName));
 
359
            break;
 
360
 
 
361
        default:
 
362
            printk(KERN_ERR "VBoxNetAdpLinuxIOCtl: unknown command %x.\n", uCmd);
 
363
            return -EINVAL;
 
364
    }
 
365
 
 
366
    return 0;
 
367
}
 
368
 
 
369
int  vboxNetAdpOsInit(PVBOXNETADP pThis)
 
370
{
 
371
    /*
 
372
     * Init linux-specific members.
 
373
     */
 
374
    pThis->u.s.pNetDev = NULL;
 
375
 
 
376
    return VINF_SUCCESS;
 
377
}
 
378
 
 
379
 
 
380
 
 
381
/**
 
382
 * Initialize module.
 
383
 *
 
384
 * @returns appropriate status code.
 
385
 */
 
386
static int __init VBoxNetAdpLinuxInit(void)
 
387
{
 
388
    int rc;
 
389
    /*
 
390
     * Initialize IPRT.
 
391
     */
 
392
    rc = RTR0Init(0);
 
393
    if (RT_SUCCESS(rc))
 
394
    {
 
395
        Log(("VBoxNetAdpLinuxInit\n"));
 
396
 
 
397
        rc = vboxNetAdpInit();
 
398
        if (RT_SUCCESS(rc))
 
399
        {
 
400
            rc = misc_register(&g_CtlDev);
 
401
            if (rc)
 
402
            {
 
403
                printk(KERN_ERR "VBoxNetAdp: Can't register " VBOXNETADP_CTL_DEV_NAME " device! rc=%d\n", rc);
 
404
                return rc;
 
405
            }
 
406
            LogRel(("VBoxNetAdp: Successfully started.\n"));
 
407
            return 0;
 
408
        }
 
409
        else
 
410
            LogRel(("VBoxNetAdp: failed to register vboxnet0 device (rc=%d)\n", rc));
 
411
    }
 
412
    else
 
413
        LogRel(("VBoxNetAdp: failed to initialize IPRT (rc=%d)\n", rc));
 
414
 
 
415
    return -RTErrConvertToErrno(rc);
 
416
}
 
417
 
 
418
 
 
419
/**
 
420
 * Unload the module.
 
421
 *
 
422
 * @todo We have to prevent this if we're busy!
 
423
 */
 
424
static void __exit VBoxNetAdpLinuxUnload(void)
 
425
{
 
426
    Log(("VBoxNetAdpLinuxUnload\n"));
 
427
 
 
428
    /*
 
429
     * Undo the work done during start (in reverse order).
 
430
     */
 
431
 
 
432
    vboxNetAdpShutdown();
 
433
    /* Remove control device */
 
434
    misc_deregister(&g_CtlDev);
 
435
 
 
436
    RTR0Term();
 
437
 
 
438
    Log(("VBoxNetAdpLinuxUnload - done\n"));
 
439
}
 
440