VirtualBox

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

Last change on this file since 98272 was 98103, checked in by vboxsync, 23 months ago

Copyright year updates by scm.

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