VirtualBox

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

Last change on this file since 55998 was 52420, checked in by vboxsync, 10 years ago

Runtime, VBoxNetAdp: Linux 3.17 fixes

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