VirtualBox

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

Last change on this file since 58207 was 58207, checked in by vboxsync, 9 years ago

VBoxNetAdp/linux: do the ritual netif_carrier_off/netif_carrier_on
song and dance so that when the interface goes IFF_UP it ends up in
operstate IF_OPER_UP, not unknown.

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