VirtualBox

source: vbox/trunk/src/VBox/HostDrivers/VBoxNetAdp/linux/VBoxNetAdp-linux.c@ 58464

Last change on this file since 58464 was 58408, checked in by vboxsync, 10 years ago

warning

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 16.2 KB
Line 
1/* $Id: VBoxNetAdp-linux.c 58408 2015-10-26 08:04:31Z vboxsync $ */
2/** @file
3 * VBoxNetAdp - Virtual Network Adapter Driver (Host), Linux Specific Code.
4 */
5
6/*
7 * Copyright (C) 2009-2015 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/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#include "the-linux-kernel.h"
23#include "version-generated.h"
24#include "product-generated.h"
25#include <linux/ethtool.h>
26#include <linux/netdevice.h>
27#include <linux/etherdevice.h>
28#include <linux/miscdevice.h>
29
30#define LOG_GROUP LOG_GROUP_NET_ADP_DRV
31#include <VBox/log.h>
32#include <VBox/err.h>
33#include <iprt/process.h>
34#include <iprt/initterm.h>
35#include <iprt/mem.h>
36#include <iprt/string.h>
37
38/*
39#include <iprt/assert.h>
40#include <iprt/semaphore.h>
41#include <iprt/spinlock.h>
42#include <iprt/string.h>
43#include <iprt/uuid.h>
44#include <iprt/alloca.h>
45*/
46
47#define VBOXNETADP_OS_SPECFIC 1
48#include "../VBoxNetAdpInternal.h"
49
50
51/*********************************************************************************************************************************
52* Defined Constants And Macros *
53*********************************************************************************************************************************/
54#define VBOXNETADP_LINUX_NAME "vboxnet%d"
55#define VBOXNETADP_CTL_DEV_NAME "vboxnetctl"
56
57#define VBOXNETADP_FROM_IFACE(iface) ((PVBOXNETADP) ifnet_softc(iface))
58
59
60/*********************************************************************************************************************************
61* Internal Functions *
62*********************************************************************************************************************************/
63static int VBoxNetAdpLinuxInit(void);
64static void VBoxNetAdpLinuxUnload(void);
65
66static int VBoxNetAdpLinuxOpen(struct inode *pInode, struct file *pFilp);
67static int VBoxNetAdpLinuxClose(struct inode *pInode, struct file *pFilp);
68#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
69static int VBoxNetAdpLinuxIOCtl(struct inode *pInode, struct file *pFilp,
70 unsigned int uCmd, unsigned long ulArg);
71#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) */
72static long VBoxNetAdpLinuxIOCtlUnlocked(struct file *pFilp,
73 unsigned int uCmd, unsigned long ulArg);
74#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) */
75
76static void vboxNetAdpEthGetDrvinfo(struct net_device *dev, struct ethtool_drvinfo *info);
77static int vboxNetAdpEthGetSettings(struct net_device *dev, struct ethtool_cmd *cmd);
78
79
80/*********************************************************************************************************************************
81* Global Variables *
82*********************************************************************************************************************************/
83module_init(VBoxNetAdpLinuxInit);
84module_exit(VBoxNetAdpLinuxUnload);
85
86MODULE_AUTHOR(VBOX_VENDOR);
87MODULE_DESCRIPTION(VBOX_PRODUCT " Network Adapter Driver");
88MODULE_LICENSE("GPL");
89#ifdef MODULE_VERSION
90MODULE_VERSION(VBOX_VERSION_STRING " (" RT_XSTR(INTNETTRUNKIFPORT_VERSION) ")");
91#endif
92
93/**
94 * The (common) global data.
95 */
96static struct file_operations gFileOpsVBoxNetAdp =
97{
98 owner: THIS_MODULE,
99 open: VBoxNetAdpLinuxOpen,
100 release: VBoxNetAdpLinuxClose,
101#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
102 ioctl: VBoxNetAdpLinuxIOCtl,
103#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) */
104 unlocked_ioctl: VBoxNetAdpLinuxIOCtlUnlocked,
105#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) */
106};
107
108/** The miscdevice structure. */
109static struct miscdevice g_CtlDev =
110{
111 minor: MISC_DYNAMIC_MINOR,
112 name: VBOXNETADP_CTL_DEV_NAME,
113 fops: &gFileOpsVBoxNetAdp,
114# if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 17)
115 devfs_name: VBOXNETADP_CTL_DEV_NAME
116# endif
117};
118
119static const struct ethtool_ops gEthToolOpsVBoxNetAdp =
120{
121 .get_drvinfo = vboxNetAdpEthGetDrvinfo,
122 .get_settings = vboxNetAdpEthGetSettings,
123 .get_link = ethtool_op_get_link,
124};
125
126
127struct VBoxNetAdpPriv
128{
129 struct net_device_stats Stats;
130};
131
132typedef struct VBoxNetAdpPriv VBOXNETADPPRIV;
133typedef VBOXNETADPPRIV *PVBOXNETADPPRIV;
134
135static int vboxNetAdpLinuxOpen(struct net_device *pNetDev)
136{
137 netif_start_queue(pNetDev);
138 return 0;
139}
140
141static int vboxNetAdpLinuxStop(struct net_device *pNetDev)
142{
143 netif_stop_queue(pNetDev);
144 return 0;
145}
146
147static int vboxNetAdpLinuxXmit(struct sk_buff *pSkb, struct net_device *pNetDev)
148{
149 PVBOXNETADPPRIV pPriv = netdev_priv(pNetDev);
150
151 /* Update the stats. */
152 pPriv->Stats.tx_packets++;
153 pPriv->Stats.tx_bytes += pSkb->len;
154 /* Update transmission time stamp. */
155 pNetDev->trans_start = jiffies;
156 /* Nothing else to do, just free the sk_buff. */
157 dev_kfree_skb(pSkb);
158 return 0;
159}
160
161struct net_device_stats *vboxNetAdpLinuxGetStats(struct net_device *pNetDev)
162{
163 PVBOXNETADPPRIV pPriv = netdev_priv(pNetDev);
164 return &pPriv->Stats;
165}
166
167
168/* ethtool_ops::get_drvinfo */
169static void vboxNetAdpEthGetDrvinfo(struct net_device *pNetDev, struct ethtool_drvinfo *info)
170{
171 PVBOXNETADPPRIV pPriv = netdev_priv(pNetDev);
172 NOREF(pPriv);
173
174 RTStrPrintf(info->driver, sizeof(info->driver),
175 "%s", VBOXNETADP_NAME);
176
177 /*
178 * Would be nice to include VBOX_SVN_REV, but it's not available
179 * here. Use file's svn revision via svn keyword?
180 */
181 RTStrPrintf(info->version, sizeof(info->version),
182 "%s", VBOX_VERSION_STRING);
183
184 RTStrPrintf(info->fw_version, sizeof(info->fw_version),
185 "0x%08X", INTNETTRUNKIFPORT_VERSION);
186
187 RTStrPrintf(info->bus_info, sizeof(info->driver),
188 "N/A");
189}
190
191
192/* ethtool_ops::get_settings */
193static int vboxNetAdpEthGetSettings(struct net_device *pNetDev, struct ethtool_cmd *cmd)
194{
195 cmd->supported = 0;
196 cmd->advertising = 0;
197#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)
198 ethtool_cmd_speed_set(cmd, SPEED_10);
199#else
200 cmd->speed = SPEED_10;
201#endif
202 cmd->duplex = DUPLEX_FULL;
203 cmd->port = PORT_TP;
204 cmd->phy_address = 0;
205 cmd->transceiver = XCVR_INTERNAL;
206 cmd->autoneg = AUTONEG_DISABLE;
207 cmd->maxtxpkt = 0;
208 cmd->maxrxpkt = 0;
209 return 0;
210}
211
212
213#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29)
214static const struct net_device_ops vboxNetAdpNetdevOps = {
215 .ndo_open = vboxNetAdpLinuxOpen,
216 .ndo_stop = vboxNetAdpLinuxStop,
217 .ndo_start_xmit = vboxNetAdpLinuxXmit,
218 .ndo_get_stats = vboxNetAdpLinuxGetStats
219};
220#endif
221
222static void vboxNetAdpNetDevInit(struct net_device *pNetDev)
223{
224 PVBOXNETADPPRIV pPriv;
225
226 ether_setup(pNetDev);
227#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29)
228 pNetDev->netdev_ops = &vboxNetAdpNetdevOps;
229#else /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29) */
230 pNetDev->open = vboxNetAdpLinuxOpen;
231 pNetDev->stop = vboxNetAdpLinuxStop;
232 pNetDev->hard_start_xmit = vboxNetAdpLinuxXmit;
233 pNetDev->get_stats = vboxNetAdpLinuxGetStats;
234#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29) */
235
236 pNetDev->ethtool_ops = &gEthToolOpsVBoxNetAdp;
237
238 pPriv = netdev_priv(pNetDev);
239 memset(pPriv, 0, sizeof(*pPriv));
240}
241
242
243int vboxNetAdpOsCreate(PVBOXNETADP pThis, PCRTMAC pMACAddress)
244{
245 int rc = VINF_SUCCESS;
246 struct net_device *pNetDev;
247
248 /* No need for private data. */
249 pNetDev = alloc_netdev(sizeof(VBOXNETADPPRIV),
250 pThis->szName[0] ? pThis->szName : VBOXNETADP_LINUX_NAME,
251#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 17, 0)
252 NET_NAME_UNKNOWN,
253#endif
254 vboxNetAdpNetDevInit);
255 if (pNetDev)
256 {
257 int err;
258
259 if (pNetDev->dev_addr)
260 {
261 memcpy(pNetDev->dev_addr, pMACAddress, ETH_ALEN);
262 Log2(("vboxNetAdpOsCreate: pNetDev->dev_addr = %.6Rhxd\n", pNetDev->dev_addr));
263
264 /*
265 * We treat presence of VBoxNetFlt filter as our "carrier",
266 * see vboxNetFltSetLinkState().
267 *
268 * operstates.txt: "On device allocation, networking core
269 * sets the flags equivalent to netif_carrier_ok() and
270 * !netif_dormant()" - so turn carrier off here.
271 */
272 netif_carrier_off(pNetDev);
273
274 err = register_netdev(pNetDev);
275 if (!err)
276 {
277 strncpy(pThis->szName, pNetDev->name, sizeof(pThis->szName));
278 pThis->szName[sizeof(pThis->szName) - 1] = '\0';
279 pThis->u.s.pNetDev = pNetDev;
280 Log2(("vboxNetAdpOsCreate: pThis=%p pThis->szName = %p\n", pThis, pThis->szName));
281 return VINF_SUCCESS;
282 }
283 }
284 else
285 {
286 LogRel(("VBoxNetAdp: failed to set MAC address (dev->dev_addr == NULL)\n"));
287 err = EFAULT;
288 }
289 free_netdev(pNetDev);
290 rc = RTErrConvertFromErrno(err);
291 }
292 return rc;
293}
294
295void vboxNetAdpOsDestroy(PVBOXNETADP pThis)
296{
297 struct net_device *pNetDev = pThis->u.s.pNetDev;
298 AssertPtr(pThis->u.s.pNetDev);
299
300 pThis->u.s.pNetDev = NULL;
301 unregister_netdev(pNetDev);
302 free_netdev(pNetDev);
303}
304
305/**
306 * Device open. Called on open /dev/vboxnetctl
307 *
308 * @param pInode Pointer to inode info structure.
309 * @param pFilp Associated file pointer.
310 */
311static int VBoxNetAdpLinuxOpen(struct inode *pInode, struct file *pFilp)
312{
313 Log(("VBoxNetAdpLinuxOpen: pid=%d/%d %s\n", RTProcSelf(), current->pid, current->comm));
314
315#ifdef VBOX_WITH_HARDENING
316 /*
317 * Only root is allowed to access the device, enforce it!
318 */
319 if (!capable(CAP_SYS_ADMIN))
320 {
321 Log(("VBoxNetAdpLinuxOpen: admin privileges required!\n"));
322 return -EPERM;
323 }
324#endif
325
326 return 0;
327}
328
329
330/**
331 * Close device.
332 *
333 * @param pInode Pointer to inode info structure.
334 * @param pFilp Associated file pointer.
335 */
336static int VBoxNetAdpLinuxClose(struct inode *pInode, struct file *pFilp)
337{
338 Log(("VBoxNetAdpLinuxClose: pid=%d/%d %s\n",
339 RTProcSelf(), current->pid, current->comm));
340 pFilp->private_data = NULL;
341 return 0;
342}
343
344/**
345 * Device I/O Control entry point.
346 *
347 * @param pFilp Associated file pointer.
348 * @param uCmd The function specified to ioctl().
349 * @param ulArg The argument specified to ioctl().
350 */
351#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
352static int VBoxNetAdpLinuxIOCtl(struct inode *pInode, struct file *pFilp,
353 unsigned int uCmd, unsigned long ulArg)
354#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) */
355static long VBoxNetAdpLinuxIOCtlUnlocked(struct file *pFilp,
356 unsigned int uCmd, unsigned long ulArg)
357#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) */
358{
359 VBOXNETADPREQ Req;
360 PVBOXNETADP pAdp;
361 int rc;
362 char *pszName = NULL;
363
364 Log(("VBoxNetAdpLinuxIOCtl: param len %#x; uCmd=%#x; add=%#x\n", _IOC_SIZE(uCmd), uCmd, VBOXNETADP_CTL_ADD));
365 if (RT_UNLIKELY(_IOC_SIZE(uCmd) != sizeof(Req))) /* paranoia */
366 {
367 Log(("VBoxNetAdpLinuxIOCtl: bad ioctl sizeof(Req)=%#x _IOC_SIZE=%#x; uCmd=%#x.\n", sizeof(Req), _IOC_SIZE(uCmd), uCmd));
368 return -EINVAL;
369 }
370
371 switch (uCmd)
372 {
373 case VBOXNETADP_CTL_ADD:
374 Log(("VBoxNetAdpLinuxIOCtl: _IOC_DIR(uCmd)=%#x; IOC_OUT=%#x\n", _IOC_DIR(uCmd), IOC_OUT));
375 if (RT_UNLIKELY(copy_from_user(&Req, (void *)ulArg, sizeof(Req))))
376 {
377 Log(("VBoxNetAdpLinuxIOCtl: copy_from_user(,%#lx,) failed; uCmd=%#x.\n", ulArg, uCmd));
378 return -EFAULT;
379 }
380 Log(("VBoxNetAdpLinuxIOCtl: Add %s\n", Req.szName));
381
382 if (Req.szName[0])
383 {
384 pAdp = vboxNetAdpFindByName(Req.szName);
385 if (pAdp)
386 {
387 Log(("VBoxNetAdpLinuxIOCtl: '%s' already exists\n", Req.szName));
388 return -EINVAL;
389 }
390 pszName = Req.szName;
391 }
392 rc = vboxNetAdpCreate(&pAdp, pszName);
393 if (RT_FAILURE(rc))
394 {
395 Log(("VBoxNetAdpLinuxIOCtl: vboxNetAdpCreate -> %Rrc\n", rc));
396 return -(rc == VERR_OUT_OF_RESOURCES ? ENOMEM : EINVAL);
397 }
398
399 Assert(strlen(pAdp->szName) < sizeof(Req.szName));
400 strncpy(Req.szName, pAdp->szName, sizeof(Req.szName) - 1);
401 Req.szName[sizeof(Req.szName) - 1] = '\0';
402
403 if (RT_UNLIKELY(copy_to_user((void *)ulArg, &Req, sizeof(Req))))
404 {
405 /* this is really bad! */
406 /** @todo remove the adapter again? */
407 printk(KERN_ERR "VBoxNetAdpLinuxIOCtl: copy_to_user(%#lx,,%#zx); uCmd=%#x!\n", ulArg, sizeof(Req), uCmd);
408 return -EFAULT;
409 }
410 Log(("VBoxNetAdpLinuxIOCtl: Successfully added '%s'\n", Req.szName));
411 break;
412
413 case VBOXNETADP_CTL_REMOVE:
414 if (RT_UNLIKELY(copy_from_user(&Req, (void *)ulArg, sizeof(Req))))
415 {
416 Log(("VBoxNetAdpLinuxIOCtl: copy_from_user(,%#lx,) failed; uCmd=%#x.\n", ulArg, uCmd));
417 return -EFAULT;
418 }
419 Log(("VBoxNetAdpLinuxIOCtl: Remove %s\n", Req.szName));
420
421 pAdp = vboxNetAdpFindByName(Req.szName);
422 if (!pAdp)
423 {
424 Log(("VBoxNetAdpLinuxIOCtl: '%s' not found\n", Req.szName));
425 return -EINVAL;
426 }
427
428 rc = vboxNetAdpDestroy(pAdp);
429 if (RT_FAILURE(rc))
430 {
431 Log(("VBoxNetAdpLinuxIOCtl: vboxNetAdpDestroy('%s') -> %Rrc\n", Req.szName, rc));
432 return -EINVAL;
433 }
434 Log(("VBoxNetAdpLinuxIOCtl: Successfully removed '%s'\n", Req.szName));
435 break;
436
437 default:
438 printk(KERN_ERR "VBoxNetAdpLinuxIOCtl: unknown command %x.\n", uCmd);
439 return -EINVAL;
440 }
441
442 return 0;
443}
444
445int vboxNetAdpOsInit(PVBOXNETADP pThis)
446{
447 /*
448 * Init linux-specific members.
449 */
450 pThis->u.s.pNetDev = NULL;
451
452 return VINF_SUCCESS;
453}
454
455
456
457/**
458 * Initialize module.
459 *
460 * @returns appropriate status code.
461 */
462static int __init VBoxNetAdpLinuxInit(void)
463{
464 int rc;
465 /*
466 * Initialize IPRT.
467 */
468 rc = RTR0Init(0);
469 if (RT_SUCCESS(rc))
470 {
471 Log(("VBoxNetAdpLinuxInit\n"));
472
473 rc = vboxNetAdpInit();
474 if (RT_SUCCESS(rc))
475 {
476 rc = misc_register(&g_CtlDev);
477 if (rc)
478 {
479 printk(KERN_ERR "VBoxNetAdp: Can't register " VBOXNETADP_CTL_DEV_NAME " device! rc=%d\n", rc);
480 return rc;
481 }
482 LogRel(("VBoxNetAdp: Successfully started.\n"));
483 return 0;
484 }
485 else
486 LogRel(("VBoxNetAdp: failed to register vboxnet0 device (rc=%d)\n", rc));
487 }
488 else
489 LogRel(("VBoxNetAdp: failed to initialize IPRT (rc=%d)\n", rc));
490
491 return -RTErrConvertToErrno(rc);
492}
493
494
495/**
496 * Unload the module.
497 *
498 * @todo We have to prevent this if we're busy!
499 */
500static void __exit VBoxNetAdpLinuxUnload(void)
501{
502 Log(("VBoxNetAdpLinuxUnload\n"));
503
504 /*
505 * Undo the work done during start (in reverse order).
506 */
507
508 vboxNetAdpShutdown();
509 /* Remove control device */
510 misc_deregister(&g_CtlDev);
511
512 RTR0Term();
513
514 Log(("VBoxNetAdpLinuxUnload - done\n"));
515}
516
Note: See TracBrowser for help on using the repository browser.

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