VirtualBox

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

Last change on this file since 77124 was 76553, checked in by vboxsync, 6 years ago

scm --update-copyright-year

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