VirtualBox

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

Last change on this file since 69500 was 69500, checked in by vboxsync, 7 years ago

*: scm --update-copyright-year

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