VirtualBox

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

Last change on this file since 94967 was 94967, checked in by vboxsync, 3 years ago

HostDrivers/{VBoxNetAdp,VBoxNetFlt}: Advertise TSO support to increase performance, bugref:9723

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