VirtualBox

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

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