2005-04-17 05:20:36 +07:00
|
|
|
/*
|
|
|
|
* TUN - Universal TUN/TAP device driver.
|
|
|
|
* Copyright (C) 1999-2002 Maxim Krasnyansky <maxk@qualcomm.com>
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* $Id: tun.c,v 1.15 2002/03/01 02:44:24 maxk Exp $
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Changes:
|
|
|
|
*
|
2005-09-02 07:40:05 +07:00
|
|
|
* Mike Kershaw <dragorn@kismetwireless.net> 2005/08/14
|
|
|
|
* Add TUNSETLINK ioctl to set the link encapsulation
|
|
|
|
*
|
2005-04-17 05:20:36 +07:00
|
|
|
* Mark Smith <markzzzsmith@yahoo.com.au>
|
2008-07-15 12:18:19 +07:00
|
|
|
* Use random_ether_addr() for tap MAC address.
|
2005-04-17 05:20:36 +07:00
|
|
|
*
|
|
|
|
* Harald Roelle <harald.roelle@ifi.lmu.de> 2004/04/20
|
|
|
|
* Fixes in packet dropping, queue length setting and queue wakeup.
|
|
|
|
* Increased default tx queue length.
|
|
|
|
* Added ethtool API.
|
|
|
|
* Minor cleanups
|
|
|
|
*
|
|
|
|
* Daniel Podlejski <underley@underley.eu.org>
|
|
|
|
* Modifications for 2.3.99-pre5 kernel.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define DRV_NAME "tun"
|
|
|
|
#define DRV_VERSION "1.6"
|
|
|
|
#define DRV_DESCRIPTION "Universal TUN/TAP device driver"
|
|
|
|
#define DRV_COPYRIGHT "(C) 1999-2004 Max Krasnyansky <maxk@qualcomm.com>"
|
|
|
|
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/errno.h>
|
|
|
|
#include <linux/kernel.h>
|
|
|
|
#include <linux/major.h>
|
|
|
|
#include <linux/slab.h>
|
|
|
|
#include <linux/poll.h>
|
|
|
|
#include <linux/fcntl.h>
|
|
|
|
#include <linux/init.h>
|
|
|
|
#include <linux/skbuff.h>
|
|
|
|
#include <linux/netdevice.h>
|
|
|
|
#include <linux/etherdevice.h>
|
|
|
|
#include <linux/miscdevice.h>
|
|
|
|
#include <linux/ethtool.h>
|
|
|
|
#include <linux/rtnetlink.h>
|
2009-11-07 13:52:32 +07:00
|
|
|
#include <linux/compat.h>
|
2005-04-17 05:20:36 +07:00
|
|
|
#include <linux/if.h>
|
|
|
|
#include <linux/if_arp.h>
|
|
|
|
#include <linux/if_ether.h>
|
|
|
|
#include <linux/if_tun.h>
|
|
|
|
#include <linux/crc32.h>
|
2008-04-16 14:41:16 +07:00
|
|
|
#include <linux/nsproxy.h>
|
2008-07-03 17:48:02 +07:00
|
|
|
#include <linux/virtio_net.h>
|
2010-02-14 08:01:10 +07:00
|
|
|
#include <linux/rcupdate.h>
|
2007-09-18 01:56:21 +07:00
|
|
|
#include <net/net_namespace.h>
|
2008-04-16 14:40:46 +07:00
|
|
|
#include <net/netns/generic.h>
|
2009-01-22 07:02:16 +07:00
|
|
|
#include <net/rtnetlink.h>
|
2009-02-06 12:25:32 +07:00
|
|
|
#include <net/sock.h>
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
#include <asm/system.h>
|
|
|
|
#include <asm/uaccess.h>
|
|
|
|
|
2008-04-13 08:48:58 +07:00
|
|
|
/* Uncomment to enable debugging */
|
|
|
|
/* #define TUN_DEBUG 1 */
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
#ifdef TUN_DEBUG
|
|
|
|
static int debug;
|
2008-04-13 08:48:58 +07:00
|
|
|
|
|
|
|
#define DBG if(tun->debug)printk
|
|
|
|
#define DBG1 if(debug==2)printk
|
|
|
|
#else
|
|
|
|
#define DBG( a... )
|
|
|
|
#define DBG1( a... )
|
|
|
|
#endif
|
|
|
|
|
2008-07-15 12:18:19 +07:00
|
|
|
#define FLT_EXACT_COUNT 8
|
|
|
|
struct tap_filter {
|
|
|
|
unsigned int count; /* Number of addrs. Zero means disabled */
|
|
|
|
u32 mask[2]; /* Mask of the hashed addrs */
|
|
|
|
unsigned char addr[FLT_EXACT_COUNT][ETH_ALEN];
|
|
|
|
};
|
|
|
|
|
2009-01-20 18:00:40 +07:00
|
|
|
struct tun_file {
|
2009-01-20 18:07:17 +07:00
|
|
|
atomic_t count;
|
2009-01-20 18:00:40 +07:00
|
|
|
struct tun_struct *tun;
|
2009-01-20 18:01:48 +07:00
|
|
|
struct net *net;
|
2009-01-20 18:00:40 +07:00
|
|
|
};
|
|
|
|
|
2009-02-06 12:25:32 +07:00
|
|
|
struct tun_sock;
|
|
|
|
|
2008-04-13 08:48:58 +07:00
|
|
|
struct tun_struct {
|
2009-01-20 18:00:40 +07:00
|
|
|
struct tun_file *tfile;
|
2008-07-15 12:18:19 +07:00
|
|
|
unsigned int flags;
|
2008-04-13 08:48:58 +07:00
|
|
|
uid_t owner;
|
|
|
|
gid_t group;
|
|
|
|
|
|
|
|
struct net_device *dev;
|
2008-07-15 12:18:19 +07:00
|
|
|
struct fasync_struct *fasync;
|
2008-04-13 08:48:58 +07:00
|
|
|
|
2008-07-15 12:18:19 +07:00
|
|
|
struct tap_filter txflt;
|
2009-02-06 12:25:32 +07:00
|
|
|
struct socket socket;
|
2010-04-29 18:01:49 +07:00
|
|
|
struct socket_wq wq;
|
2010-03-17 22:45:01 +07:00
|
|
|
|
|
|
|
int vnet_hdr_sz;
|
|
|
|
|
2008-04-13 08:48:58 +07:00
|
|
|
#ifdef TUN_DEBUG
|
|
|
|
int debug;
|
2005-04-17 05:20:36 +07:00
|
|
|
#endif
|
2008-04-13 08:48:58 +07:00
|
|
|
};
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2009-02-06 12:25:32 +07:00
|
|
|
struct tun_sock {
|
|
|
|
struct sock sk;
|
|
|
|
struct tun_struct *tun;
|
|
|
|
};
|
|
|
|
|
|
|
|
static inline struct tun_sock *tun_sk(struct sock *sk)
|
|
|
|
{
|
|
|
|
return container_of(sk, struct tun_sock, sk);
|
|
|
|
}
|
|
|
|
|
2009-01-20 17:57:48 +07:00
|
|
|
static int tun_attach(struct tun_struct *tun, struct file *file)
|
|
|
|
{
|
2009-01-20 18:00:40 +07:00
|
|
|
struct tun_file *tfile = file->private_data;
|
2009-01-20 18:02:28 +07:00
|
|
|
int err;
|
2009-01-20 17:57:48 +07:00
|
|
|
|
|
|
|
ASSERT_RTNL();
|
|
|
|
|
2009-01-20 18:02:28 +07:00
|
|
|
netif_tx_lock_bh(tun->dev);
|
|
|
|
|
|
|
|
err = -EINVAL;
|
|
|
|
if (tfile->tun)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
err = -EBUSY;
|
|
|
|
if (tun->tfile)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
err = 0;
|
2009-01-20 18:00:40 +07:00
|
|
|
tfile->tun = tun;
|
|
|
|
tun->tfile = tfile;
|
2010-01-14 13:17:09 +07:00
|
|
|
tun->socket.file = file;
|
2010-07-27 20:53:43 +07:00
|
|
|
netif_carrier_on(tun->dev);
|
2009-01-20 18:07:17 +07:00
|
|
|
dev_hold(tun->dev);
|
2009-08-30 14:04:42 +07:00
|
|
|
sock_hold(tun->socket.sk);
|
2009-01-20 18:07:17 +07:00
|
|
|
atomic_inc(&tfile->count);
|
2009-01-20 17:57:48 +07:00
|
|
|
|
2009-01-20 18:02:28 +07:00
|
|
|
out:
|
|
|
|
netif_tx_unlock_bh(tun->dev);
|
|
|
|
return err;
|
2009-01-20 17:57:48 +07:00
|
|
|
}
|
|
|
|
|
2009-01-20 18:00:40 +07:00
|
|
|
static void __tun_detach(struct tun_struct *tun)
|
|
|
|
{
|
|
|
|
/* Detach from net device */
|
2009-01-20 18:02:28 +07:00
|
|
|
netif_tx_lock_bh(tun->dev);
|
2010-07-27 20:53:43 +07:00
|
|
|
netif_carrier_off(tun->dev);
|
2009-01-20 18:00:40 +07:00
|
|
|
tun->tfile = NULL;
|
2010-01-14 13:17:09 +07:00
|
|
|
tun->socket.file = NULL;
|
2009-01-20 18:02:28 +07:00
|
|
|
netif_tx_unlock_bh(tun->dev);
|
2009-01-20 18:00:40 +07:00
|
|
|
|
|
|
|
/* Drop read queue */
|
2009-08-30 14:04:42 +07:00
|
|
|
skb_queue_purge(&tun->socket.sk->sk_receive_queue);
|
2009-01-20 18:07:17 +07:00
|
|
|
|
|
|
|
/* Drop the extra count on the net device */
|
|
|
|
dev_put(tun->dev);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void tun_detach(struct tun_struct *tun)
|
|
|
|
{
|
|
|
|
rtnl_lock();
|
|
|
|
__tun_detach(tun);
|
|
|
|
rtnl_unlock();
|
2009-01-20 18:00:40 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static struct tun_struct *__tun_get(struct tun_file *tfile)
|
|
|
|
{
|
2009-01-20 18:07:17 +07:00
|
|
|
struct tun_struct *tun = NULL;
|
|
|
|
|
|
|
|
if (atomic_inc_not_zero(&tfile->count))
|
|
|
|
tun = tfile->tun;
|
|
|
|
|
|
|
|
return tun;
|
2009-01-20 18:00:40 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static struct tun_struct *tun_get(struct file *file)
|
|
|
|
{
|
|
|
|
return __tun_get(file->private_data);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void tun_put(struct tun_struct *tun)
|
|
|
|
{
|
2009-01-20 18:07:17 +07:00
|
|
|
struct tun_file *tfile = tun->tfile;
|
|
|
|
|
|
|
|
if (atomic_dec_and_test(&tfile->count))
|
|
|
|
tun_detach(tfile->tun);
|
2009-01-20 18:00:40 +07:00
|
|
|
}
|
|
|
|
|
2008-07-15 12:18:19 +07:00
|
|
|
/* TAP filterting */
|
|
|
|
static void addr_hash_set(u32 *mask, const u8 *addr)
|
|
|
|
{
|
|
|
|
int n = ether_crc(ETH_ALEN, addr) >> 26;
|
|
|
|
mask[n >> 5] |= (1 << (n & 31));
|
|
|
|
}
|
|
|
|
|
|
|
|
static unsigned int addr_hash_test(const u32 *mask, const u8 *addr)
|
|
|
|
{
|
|
|
|
int n = ether_crc(ETH_ALEN, addr) >> 26;
|
|
|
|
return mask[n >> 5] & (1 << (n & 31));
|
|
|
|
}
|
|
|
|
|
|
|
|
static int update_filter(struct tap_filter *filter, void __user *arg)
|
|
|
|
{
|
|
|
|
struct { u8 u[ETH_ALEN]; } *addr;
|
|
|
|
struct tun_filter uf;
|
|
|
|
int err, alen, n, nexact;
|
|
|
|
|
|
|
|
if (copy_from_user(&uf, arg, sizeof(uf)))
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
if (!uf.count) {
|
|
|
|
/* Disabled */
|
|
|
|
filter->count = 0;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
alen = ETH_ALEN * uf.count;
|
|
|
|
addr = kmalloc(alen, GFP_KERNEL);
|
|
|
|
if (!addr)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
if (copy_from_user(addr, arg + sizeof(uf), alen)) {
|
|
|
|
err = -EFAULT;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* The filter is updated without holding any locks. Which is
|
|
|
|
* perfectly safe. We disable it first and in the worst
|
|
|
|
* case we'll accept a few undesired packets. */
|
|
|
|
filter->count = 0;
|
|
|
|
wmb();
|
|
|
|
|
|
|
|
/* Use first set of addresses as an exact filter */
|
|
|
|
for (n = 0; n < uf.count && n < FLT_EXACT_COUNT; n++)
|
|
|
|
memcpy(filter->addr[n], addr[n].u, ETH_ALEN);
|
|
|
|
|
|
|
|
nexact = n;
|
|
|
|
|
2009-02-09 08:49:17 +07:00
|
|
|
/* Remaining multicast addresses are hashed,
|
|
|
|
* unicast will leave the filter disabled. */
|
2008-07-15 12:18:19 +07:00
|
|
|
memset(filter->mask, 0, sizeof(filter->mask));
|
2009-02-09 08:49:17 +07:00
|
|
|
for (; n < uf.count; n++) {
|
|
|
|
if (!is_multicast_ether_addr(addr[n].u)) {
|
|
|
|
err = 0; /* no filter */
|
|
|
|
goto done;
|
|
|
|
}
|
2008-07-15 12:18:19 +07:00
|
|
|
addr_hash_set(filter->mask, addr[n].u);
|
2009-02-09 08:49:17 +07:00
|
|
|
}
|
2008-07-15 12:18:19 +07:00
|
|
|
|
|
|
|
/* For ALLMULTI just set the mask to all ones.
|
|
|
|
* This overrides the mask populated above. */
|
|
|
|
if ((uf.flags & TUN_FLT_ALLMULTI))
|
|
|
|
memset(filter->mask, ~0, sizeof(filter->mask));
|
|
|
|
|
|
|
|
/* Now enable the filter */
|
|
|
|
wmb();
|
|
|
|
filter->count = nexact;
|
|
|
|
|
|
|
|
/* Return the number of exact filters */
|
|
|
|
err = nexact;
|
|
|
|
|
|
|
|
done:
|
|
|
|
kfree(addr);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Returns: 0 - drop, !=0 - accept */
|
|
|
|
static int run_filter(struct tap_filter *filter, const struct sk_buff *skb)
|
|
|
|
{
|
|
|
|
/* Cannot use eth_hdr(skb) here because skb_mac_hdr() is incorrect
|
|
|
|
* at this point. */
|
|
|
|
struct ethhdr *eh = (struct ethhdr *) skb->data;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/* Exact match */
|
|
|
|
for (i = 0; i < filter->count; i++)
|
|
|
|
if (!compare_ether_addr(eh->h_dest, filter->addr[i]))
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
/* Inexact match (multicast only) */
|
|
|
|
if (is_multicast_ether_addr(eh->h_dest))
|
|
|
|
return addr_hash_test(filter->mask, eh->h_dest);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Checks whether the packet is accepted or not.
|
|
|
|
* Returns: 0 - drop, !=0 - accept
|
|
|
|
*/
|
|
|
|
static int check_filter(struct tap_filter *filter, const struct sk_buff *skb)
|
|
|
|
{
|
|
|
|
if (!filter->count)
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
return run_filter(filter, skb);
|
|
|
|
}
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
/* Network device part of the driver */
|
|
|
|
|
2006-09-14 01:30:00 +07:00
|
|
|
static const struct ethtool_ops tun_ethtool_ops;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2009-01-20 18:07:17 +07:00
|
|
|
/* Net device detach from fd. */
|
|
|
|
static void tun_net_uninit(struct net_device *dev)
|
|
|
|
{
|
|
|
|
struct tun_struct *tun = netdev_priv(dev);
|
|
|
|
struct tun_file *tfile = tun->tfile;
|
|
|
|
|
|
|
|
/* Inform the methods they need to stop using the dev.
|
|
|
|
*/
|
|
|
|
if (tfile) {
|
2010-04-29 18:01:49 +07:00
|
|
|
wake_up_all(&tun->wq.wait);
|
2009-01-20 18:07:17 +07:00
|
|
|
if (atomic_dec_and_test(&tfile->count))
|
|
|
|
__tun_detach(tun);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-04-18 21:15:52 +07:00
|
|
|
static void tun_free_netdev(struct net_device *dev)
|
|
|
|
{
|
|
|
|
struct tun_struct *tun = netdev_priv(dev);
|
|
|
|
|
2009-08-30 14:04:42 +07:00
|
|
|
sock_put(tun->socket.sk);
|
2009-04-18 21:15:52 +07:00
|
|
|
}
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
/* Net device open. */
|
|
|
|
static int tun_net_open(struct net_device *dev)
|
|
|
|
{
|
|
|
|
netif_start_queue(dev);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Net device close. */
|
|
|
|
static int tun_net_close(struct net_device *dev)
|
|
|
|
{
|
|
|
|
netif_stop_queue(dev);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Net device start xmit */
|
2009-09-01 02:50:51 +07:00
|
|
|
static netdev_tx_t tun_net_xmit(struct sk_buff *skb, struct net_device *dev)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
struct tun_struct *tun = netdev_priv(dev);
|
|
|
|
|
|
|
|
DBG(KERN_INFO "%s: tun_net_xmit %d\n", tun->dev->name, skb->len);
|
|
|
|
|
|
|
|
/* Drop packet if interface is not attached */
|
2009-01-20 18:00:40 +07:00
|
|
|
if (!tun->tfile)
|
2005-04-17 05:20:36 +07:00
|
|
|
goto drop;
|
|
|
|
|
2008-07-15 12:18:19 +07:00
|
|
|
/* Drop if the filter does not like it.
|
|
|
|
* This is a noop if the filter is disabled.
|
|
|
|
* Filter can be enabled only for the TAP devices. */
|
|
|
|
if (!check_filter(&tun->txflt, skb))
|
|
|
|
goto drop;
|
|
|
|
|
2010-02-14 08:01:10 +07:00
|
|
|
if (tun->socket.sk->sk_filter &&
|
|
|
|
sk_filter(tun->socket.sk, skb))
|
|
|
|
goto drop;
|
|
|
|
|
2009-08-30 14:04:42 +07:00
|
|
|
if (skb_queue_len(&tun->socket.sk->sk_receive_queue) >= dev->tx_queue_len) {
|
2005-04-17 05:20:36 +07:00
|
|
|
if (!(tun->flags & TUN_ONE_QUEUE)) {
|
|
|
|
/* Normal queueing mode. */
|
|
|
|
/* Packet scheduler handles dropping of further packets. */
|
|
|
|
netif_stop_queue(dev);
|
|
|
|
|
|
|
|
/* We won't see all dropped packets individually, so overrun
|
|
|
|
* error is more appropriate. */
|
2007-10-04 07:41:50 +07:00
|
|
|
dev->stats.tx_fifo_errors++;
|
2005-04-17 05:20:36 +07:00
|
|
|
} else {
|
|
|
|
/* Single queue mode.
|
|
|
|
* Driver handles dropping of all packets itself. */
|
|
|
|
goto drop;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
tun: orphan an skb on tx
The following situation was observed in the field:
tap1 sends packets, tap2 does not consume them, as a result
tap1 can not be closed. This happens because
tun/tap devices can hang on to skbs undefinitely.
As noted by Herbert, possible solutions include a timeout followed by a
copy/change of ownership of the skb, or always copying/changing
ownership if we're going into a hostile device.
This patch implements the second approach.
Note: one issue still remaining is that since skbs
keep reference to tun socket and tun socket has a
reference to tun device, we won't flush backlog,
instead simply waiting for all skbs to get transmitted.
At least this is not user-triggerable, and
this was not reported in practice, my assumption is
other devices besides tap complete an skb
within finite time after it has been queued.
A possible solution for the second issue
would not to have socket reference the device,
instead, implement dev->destructor for tun, and
wait for all skbs to complete there, but this
needs some thought, probably too risky for 2.6.34.
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Tested-by: Yan Vugenfirer <yvugenfi@redhat.com>
Acked-by: Herbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
2010-04-13 11:59:44 +07:00
|
|
|
/* Orphan the skb - required as we might hang on to it
|
|
|
|
* for indefinite time. */
|
|
|
|
skb_orphan(skb);
|
|
|
|
|
2008-07-15 12:18:19 +07:00
|
|
|
/* Enqueue packet */
|
2009-08-30 14:04:42 +07:00
|
|
|
skb_queue_tail(&tun->socket.sk->sk_receive_queue, skb);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
/* Notify and wake up reader process */
|
|
|
|
if (tun->flags & TUN_FASYNC)
|
|
|
|
kill_fasync(&tun->fasync, SIGIO, POLL_IN);
|
2010-04-29 18:01:49 +07:00
|
|
|
wake_up_interruptible_poll(&tun->wq.wait, POLLIN |
|
2010-01-14 13:17:09 +07:00
|
|
|
POLLRDNORM | POLLRDBAND);
|
2009-06-23 13:03:08 +07:00
|
|
|
return NETDEV_TX_OK;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
drop:
|
2007-10-04 07:41:50 +07:00
|
|
|
dev->stats.tx_dropped++;
|
2005-04-17 05:20:36 +07:00
|
|
|
kfree_skb(skb);
|
2009-06-23 13:03:08 +07:00
|
|
|
return NETDEV_TX_OK;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2008-07-15 12:18:19 +07:00
|
|
|
static void tun_net_mclist(struct net_device *dev)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2008-07-15 12:18:19 +07:00
|
|
|
/*
|
|
|
|
* This callback is supposed to deal with mc filter in
|
|
|
|
* _rx_ path and has nothing to do with the _tx_ path.
|
|
|
|
* In rx path we always accept everything userspace gives us.
|
|
|
|
*/
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2007-09-17 02:21:38 +07:00
|
|
|
#define MIN_MTU 68
|
|
|
|
#define MAX_MTU 65535
|
|
|
|
|
|
|
|
static int
|
|
|
|
tun_net_change_mtu(struct net_device *dev, int new_mtu)
|
|
|
|
{
|
|
|
|
if (new_mtu < MIN_MTU || new_mtu + dev->hard_header_len > MAX_MTU)
|
|
|
|
return -EINVAL;
|
|
|
|
dev->mtu = new_mtu;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-11-20 13:10:37 +07:00
|
|
|
static const struct net_device_ops tun_netdev_ops = {
|
2009-01-20 18:07:17 +07:00
|
|
|
.ndo_uninit = tun_net_uninit,
|
2008-11-20 13:10:37 +07:00
|
|
|
.ndo_open = tun_net_open,
|
|
|
|
.ndo_stop = tun_net_close,
|
2008-11-21 11:14:53 +07:00
|
|
|
.ndo_start_xmit = tun_net_xmit,
|
2008-11-20 13:10:37 +07:00
|
|
|
.ndo_change_mtu = tun_net_change_mtu,
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct net_device_ops tap_netdev_ops = {
|
2009-01-20 18:07:17 +07:00
|
|
|
.ndo_uninit = tun_net_uninit,
|
2008-11-20 13:10:37 +07:00
|
|
|
.ndo_open = tun_net_open,
|
|
|
|
.ndo_stop = tun_net_close,
|
2008-11-21 11:14:53 +07:00
|
|
|
.ndo_start_xmit = tun_net_xmit,
|
2008-11-20 13:10:37 +07:00
|
|
|
.ndo_change_mtu = tun_net_change_mtu,
|
|
|
|
.ndo_set_multicast_list = tun_net_mclist,
|
|
|
|
.ndo_set_mac_address = eth_mac_addr,
|
|
|
|
.ndo_validate_addr = eth_validate_addr,
|
|
|
|
};
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
/* Initialize net device. */
|
|
|
|
static void tun_net_init(struct net_device *dev)
|
|
|
|
{
|
|
|
|
struct tun_struct *tun = netdev_priv(dev);
|
2006-09-14 00:24:59 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
switch (tun->flags & TUN_TYPE_MASK) {
|
|
|
|
case TUN_TUN_DEV:
|
2008-11-20 13:10:37 +07:00
|
|
|
dev->netdev_ops = &tun_netdev_ops;
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
/* Point-to-Point TUN Device */
|
|
|
|
dev->hard_header_len = 0;
|
|
|
|
dev->addr_len = 0;
|
|
|
|
dev->mtu = 1500;
|
|
|
|
|
|
|
|
/* Zero header length */
|
2006-09-14 00:24:59 +07:00
|
|
|
dev->type = ARPHRD_NONE;
|
2005-04-17 05:20:36 +07:00
|
|
|
dev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST;
|
|
|
|
dev->tx_queue_len = TUN_READQ_SIZE; /* We prefer our own queue length */
|
|
|
|
break;
|
|
|
|
|
|
|
|
case TUN_TAP_DEV:
|
2008-12-30 09:23:28 +07:00
|
|
|
dev->netdev_ops = &tap_netdev_ops;
|
2005-04-17 05:20:36 +07:00
|
|
|
/* Ethernet TAP Device */
|
|
|
|
ether_setup(dev);
|
2007-04-26 15:00:55 +07:00
|
|
|
|
2008-07-15 12:18:19 +07:00
|
|
|
random_ether_addr(dev->dev_addr);
|
2007-04-26 15:00:55 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
dev->tx_queue_len = TUN_READQ_SIZE; /* We prefer our own queue length */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Character device part */
|
|
|
|
|
|
|
|
/* Poll */
|
|
|
|
static unsigned int tun_chr_poll(struct file *file, poll_table * wait)
|
2006-09-14 00:24:59 +07:00
|
|
|
{
|
2009-01-20 18:03:21 +07:00
|
|
|
struct tun_file *tfile = file->private_data;
|
|
|
|
struct tun_struct *tun = __tun_get(tfile);
|
2009-07-06 02:48:35 +07:00
|
|
|
struct sock *sk;
|
2009-02-06 12:25:32 +07:00
|
|
|
unsigned int mask = 0;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
if (!tun)
|
2009-01-20 17:59:05 +07:00
|
|
|
return POLLERR;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2009-08-30 14:04:42 +07:00
|
|
|
sk = tun->socket.sk;
|
2009-07-06 02:48:35 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
DBG(KERN_INFO "%s: tun_chr_poll\n", tun->dev->name);
|
|
|
|
|
2010-04-29 18:01:49 +07:00
|
|
|
poll_wait(file, &tun->wq.wait, wait);
|
2006-09-14 00:24:59 +07:00
|
|
|
|
2009-08-30 14:04:42 +07:00
|
|
|
if (!skb_queue_empty(&sk->sk_receive_queue))
|
2005-04-17 05:20:36 +07:00
|
|
|
mask |= POLLIN | POLLRDNORM;
|
|
|
|
|
2009-02-06 12:25:32 +07:00
|
|
|
if (sock_writeable(sk) ||
|
|
|
|
(!test_and_set_bit(SOCK_ASYNC_NOSPACE, &sk->sk_socket->flags) &&
|
|
|
|
sock_writeable(sk)))
|
|
|
|
mask |= POLLOUT | POLLWRNORM;
|
|
|
|
|
2009-01-20 18:07:17 +07:00
|
|
|
if (tun->dev->reg_state != NETREG_REGISTERED)
|
|
|
|
mask = POLLERR;
|
|
|
|
|
2009-01-20 18:00:40 +07:00
|
|
|
tun_put(tun);
|
2005-04-17 05:20:36 +07:00
|
|
|
return mask;
|
|
|
|
}
|
|
|
|
|
2008-08-16 05:15:10 +07:00
|
|
|
/* prepad is the amount to reserve at front. len is length after that.
|
|
|
|
* linear is a hint as to how much to copy (usually headers). */
|
2009-02-06 12:25:32 +07:00
|
|
|
static inline struct sk_buff *tun_alloc_skb(struct tun_struct *tun,
|
|
|
|
size_t prepad, size_t len,
|
|
|
|
size_t linear, int noblock)
|
2008-08-16 05:15:10 +07:00
|
|
|
{
|
2009-08-30 14:04:42 +07:00
|
|
|
struct sock *sk = tun->socket.sk;
|
2008-08-16 05:15:10 +07:00
|
|
|
struct sk_buff *skb;
|
2009-02-06 12:25:32 +07:00
|
|
|
int err;
|
2008-08-16 05:15:10 +07:00
|
|
|
|
2010-05-24 14:14:10 +07:00
|
|
|
sock_update_classid(sk);
|
|
|
|
|
2008-08-16 05:15:10 +07:00
|
|
|
/* Under a page? Don't bother with paged skb. */
|
2009-04-14 16:09:43 +07:00
|
|
|
if (prepad + len < PAGE_SIZE || !linear)
|
2009-02-06 12:25:32 +07:00
|
|
|
linear = len;
|
2008-08-16 05:15:10 +07:00
|
|
|
|
2009-02-06 12:25:32 +07:00
|
|
|
skb = sock_alloc_send_pskb(sk, prepad + linear, len - linear, noblock,
|
|
|
|
&err);
|
2008-08-16 05:15:10 +07:00
|
|
|
if (!skb)
|
2009-02-06 12:25:32 +07:00
|
|
|
return ERR_PTR(err);
|
2008-08-16 05:15:10 +07:00
|
|
|
|
|
|
|
skb_reserve(skb, prepad);
|
|
|
|
skb_put(skb, linear);
|
2009-02-06 12:25:32 +07:00
|
|
|
skb->data_len = len - linear;
|
|
|
|
skb->len += len - linear;
|
2008-08-16 05:15:10 +07:00
|
|
|
|
|
|
|
return skb;
|
|
|
|
}
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
/* Get packet from user space buffer */
|
2009-02-06 12:25:32 +07:00
|
|
|
static __inline__ ssize_t tun_get_user(struct tun_struct *tun,
|
2009-04-20 08:26:11 +07:00
|
|
|
const struct iovec *iv, size_t count,
|
2009-02-06 12:25:32 +07:00
|
|
|
int noblock)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2009-02-01 15:45:17 +07:00
|
|
|
struct tun_pi pi = { 0, cpu_to_be16(ETH_P_IP) };
|
2005-04-17 05:20:36 +07:00
|
|
|
struct sk_buff *skb;
|
|
|
|
size_t len = count, align = 0;
|
2008-07-03 17:48:02 +07:00
|
|
|
struct virtio_net_hdr gso = { 0 };
|
2009-04-20 08:26:11 +07:00
|
|
|
int offset = 0;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
if (!(tun->flags & TUN_NO_PI)) {
|
|
|
|
if ((len -= sizeof(pi)) > count)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2009-04-20 08:26:11 +07:00
|
|
|
if (memcpy_fromiovecend((void *)&pi, iv, 0, sizeof(pi)))
|
2005-04-17 05:20:36 +07:00
|
|
|
return -EFAULT;
|
2009-04-20 08:26:11 +07:00
|
|
|
offset += sizeof(pi);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2008-07-03 17:48:02 +07:00
|
|
|
if (tun->flags & TUN_VNET_HDR) {
|
2010-03-17 22:45:01 +07:00
|
|
|
if ((len -= tun->vnet_hdr_sz) > count)
|
2008-07-03 17:48:02 +07:00
|
|
|
return -EINVAL;
|
|
|
|
|
2009-04-20 08:26:11 +07:00
|
|
|
if (memcpy_fromiovecend((void *)&gso, iv, offset, sizeof(gso)))
|
2008-07-03 17:48:02 +07:00
|
|
|
return -EFAULT;
|
|
|
|
|
2009-06-08 14:20:01 +07:00
|
|
|
if ((gso.flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) &&
|
|
|
|
gso.csum_start + gso.csum_offset + 2 > gso.hdr_len)
|
|
|
|
gso.hdr_len = gso.csum_start + gso.csum_offset + 2;
|
|
|
|
|
2008-07-03 17:48:02 +07:00
|
|
|
if (gso.hdr_len > len)
|
|
|
|
return -EINVAL;
|
2010-03-17 22:45:01 +07:00
|
|
|
offset += tun->vnet_hdr_sz;
|
2008-07-03 17:48:02 +07:00
|
|
|
}
|
|
|
|
|
2008-04-13 08:49:30 +07:00
|
|
|
if ((tun->flags & TUN_TYPE_MASK) == TUN_TAP_DEV) {
|
2005-04-17 05:20:36 +07:00
|
|
|
align = NET_IP_ALIGN;
|
2009-04-14 16:09:43 +07:00
|
|
|
if (unlikely(len < ETH_HLEN ||
|
|
|
|
(gso.hdr_len && gso.hdr_len < ETH_HLEN)))
|
2008-04-13 08:49:30 +07:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
2006-09-14 00:24:59 +07:00
|
|
|
|
2009-02-06 12:25:32 +07:00
|
|
|
skb = tun_alloc_skb(tun, align, len, gso.hdr_len, noblock);
|
|
|
|
if (IS_ERR(skb)) {
|
|
|
|
if (PTR_ERR(skb) != -EAGAIN)
|
|
|
|
tun->dev->stats.rx_dropped++;
|
|
|
|
return PTR_ERR(skb);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2009-04-20 08:26:11 +07:00
|
|
|
if (skb_copy_datagram_from_iovec(skb, 0, iv, offset, len)) {
|
2007-10-04 07:41:50 +07:00
|
|
|
tun->dev->stats.rx_dropped++;
|
2006-03-12 09:49:13 +07:00
|
|
|
kfree_skb(skb);
|
2005-04-17 05:20:36 +07:00
|
|
|
return -EFAULT;
|
2006-03-12 09:49:13 +07:00
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2008-07-03 17:48:02 +07:00
|
|
|
if (gso.flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) {
|
|
|
|
if (!skb_partial_csum_set(skb, gso.csum_start,
|
|
|
|
gso.csum_offset)) {
|
|
|
|
tun->dev->stats.rx_frame_errors++;
|
|
|
|
kfree_skb(skb);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
} else if (tun->flags & TUN_NOCHECKSUM)
|
|
|
|
skb->ip_summed = CHECKSUM_UNNECESSARY;
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
switch (tun->flags & TUN_TYPE_MASK) {
|
|
|
|
case TUN_TUN_DEV:
|
2008-06-18 11:10:33 +07:00
|
|
|
if (tun->flags & TUN_NO_PI) {
|
|
|
|
switch (skb->data[0] & 0xf0) {
|
|
|
|
case 0x40:
|
|
|
|
pi.proto = htons(ETH_P_IP);
|
|
|
|
break;
|
|
|
|
case 0x60:
|
|
|
|
pi.proto = htons(ETH_P_IPV6);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
tun->dev->stats.rx_dropped++;
|
|
|
|
kfree_skb(skb);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-03-20 05:30:44 +07:00
|
|
|
skb_reset_mac_header(skb);
|
2005-04-17 05:20:36 +07:00
|
|
|
skb->protocol = pi.proto;
|
2007-04-26 07:40:23 +07:00
|
|
|
skb->dev = tun->dev;
|
2005-04-17 05:20:36 +07:00
|
|
|
break;
|
|
|
|
case TUN_TAP_DEV:
|
|
|
|
skb->protocol = eth_type_trans(skb, tun->dev);
|
|
|
|
break;
|
|
|
|
};
|
|
|
|
|
2008-07-03 17:48:02 +07:00
|
|
|
if (gso.gso_type != VIRTIO_NET_HDR_GSO_NONE) {
|
|
|
|
pr_debug("GSO!\n");
|
|
|
|
switch (gso.gso_type & ~VIRTIO_NET_HDR_GSO_ECN) {
|
|
|
|
case VIRTIO_NET_HDR_GSO_TCPV4:
|
|
|
|
skb_shinfo(skb)->gso_type = SKB_GSO_TCPV4;
|
|
|
|
break;
|
|
|
|
case VIRTIO_NET_HDR_GSO_TCPV6:
|
|
|
|
skb_shinfo(skb)->gso_type = SKB_GSO_TCPV6;
|
|
|
|
break;
|
2009-07-14 21:21:04 +07:00
|
|
|
case VIRTIO_NET_HDR_GSO_UDP:
|
|
|
|
skb_shinfo(skb)->gso_type = SKB_GSO_UDP;
|
|
|
|
break;
|
2008-07-03 17:48:02 +07:00
|
|
|
default:
|
|
|
|
tun->dev->stats.rx_frame_errors++;
|
|
|
|
kfree_skb(skb);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (gso.gso_type & VIRTIO_NET_HDR_GSO_ECN)
|
|
|
|
skb_shinfo(skb)->gso_type |= SKB_GSO_TCP_ECN;
|
|
|
|
|
|
|
|
skb_shinfo(skb)->gso_size = gso.gso_size;
|
|
|
|
if (skb_shinfo(skb)->gso_size == 0) {
|
|
|
|
tun->dev->stats.rx_frame_errors++;
|
|
|
|
kfree_skb(skb);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Header must be checked, and gso_segs computed. */
|
|
|
|
skb_shinfo(skb)->gso_type |= SKB_GSO_DODGY;
|
|
|
|
skb_shinfo(skb)->gso_segs = 0;
|
|
|
|
}
|
2006-09-14 00:24:59 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
netif_rx_ni(skb);
|
2006-09-14 00:24:59 +07:00
|
|
|
|
2007-10-04 07:41:50 +07:00
|
|
|
tun->dev->stats.rx_packets++;
|
|
|
|
tun->dev->stats.rx_bytes += len;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
return count;
|
2006-09-14 00:24:59 +07:00
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2006-10-01 13:28:47 +07:00
|
|
|
static ssize_t tun_chr_aio_write(struct kiocb *iocb, const struct iovec *iv,
|
|
|
|
unsigned long count, loff_t pos)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2009-02-06 12:25:32 +07:00
|
|
|
struct file *file = iocb->ki_filp;
|
2009-02-15 11:46:39 +07:00
|
|
|
struct tun_struct *tun = tun_get(file);
|
2009-01-20 18:00:40 +07:00
|
|
|
ssize_t result;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
if (!tun)
|
|
|
|
return -EBADFD;
|
|
|
|
|
|
|
|
DBG(KERN_INFO "%s: tun_chr_write %ld\n", tun->dev->name, count);
|
|
|
|
|
2009-04-20 08:26:11 +07:00
|
|
|
result = tun_get_user(tun, iv, iov_length(iv, count),
|
2009-02-06 12:25:32 +07:00
|
|
|
file->f_flags & O_NONBLOCK);
|
2009-01-20 18:00:40 +07:00
|
|
|
|
|
|
|
tun_put(tun);
|
|
|
|
return result;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Put packet to the user space buffer */
|
|
|
|
static __inline__ ssize_t tun_put_user(struct tun_struct *tun,
|
|
|
|
struct sk_buff *skb,
|
2009-04-20 08:25:59 +07:00
|
|
|
const struct iovec *iv, int len)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
struct tun_pi pi = { 0, skb->protocol };
|
|
|
|
ssize_t total = 0;
|
|
|
|
|
|
|
|
if (!(tun->flags & TUN_NO_PI)) {
|
|
|
|
if ((len -= sizeof(pi)) < 0)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (len < skb->len) {
|
|
|
|
/* Packet will be striped */
|
|
|
|
pi.flags |= TUN_PKT_STRIP;
|
|
|
|
}
|
2006-09-14 00:24:59 +07:00
|
|
|
|
2009-04-20 08:25:59 +07:00
|
|
|
if (memcpy_toiovecend(iv, (void *) &pi, 0, sizeof(pi)))
|
2005-04-17 05:20:36 +07:00
|
|
|
return -EFAULT;
|
|
|
|
total += sizeof(pi);
|
2006-09-14 00:24:59 +07:00
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2008-07-03 17:48:02 +07:00
|
|
|
if (tun->flags & TUN_VNET_HDR) {
|
|
|
|
struct virtio_net_hdr gso = { 0 }; /* no info leak */
|
2010-03-17 22:45:01 +07:00
|
|
|
if ((len -= tun->vnet_hdr_sz) < 0)
|
2008-07-03 17:48:02 +07:00
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (skb_is_gso(skb)) {
|
|
|
|
struct skb_shared_info *sinfo = skb_shinfo(skb);
|
|
|
|
|
|
|
|
/* This is a hint as to how much should be linear. */
|
|
|
|
gso.hdr_len = skb_headlen(skb);
|
|
|
|
gso.gso_size = sinfo->gso_size;
|
|
|
|
if (sinfo->gso_type & SKB_GSO_TCPV4)
|
|
|
|
gso.gso_type = VIRTIO_NET_HDR_GSO_TCPV4;
|
|
|
|
else if (sinfo->gso_type & SKB_GSO_TCPV6)
|
|
|
|
gso.gso_type = VIRTIO_NET_HDR_GSO_TCPV6;
|
2009-07-14 21:21:04 +07:00
|
|
|
else if (sinfo->gso_type & SKB_GSO_UDP)
|
|
|
|
gso.gso_type = VIRTIO_NET_HDR_GSO_UDP;
|
2010-07-21 11:32:45 +07:00
|
|
|
else {
|
|
|
|
printk(KERN_ERR "tun: unexpected GSO type: "
|
|
|
|
"0x%x, gso_size %d, hdr_len %d\n",
|
|
|
|
sinfo->gso_type, gso.gso_size,
|
|
|
|
gso.hdr_len);
|
|
|
|
print_hex_dump(KERN_ERR, "tun: ",
|
|
|
|
DUMP_PREFIX_NONE,
|
|
|
|
16, 1, skb->head,
|
|
|
|
min((int)gso.hdr_len, 64), true);
|
|
|
|
WARN_ON_ONCE(1);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2008-07-03 17:48:02 +07:00
|
|
|
if (sinfo->gso_type & SKB_GSO_TCP_ECN)
|
|
|
|
gso.gso_type |= VIRTIO_NET_HDR_GSO_ECN;
|
|
|
|
} else
|
|
|
|
gso.gso_type = VIRTIO_NET_HDR_GSO_NONE;
|
|
|
|
|
|
|
|
if (skb->ip_summed == CHECKSUM_PARTIAL) {
|
|
|
|
gso.flags = VIRTIO_NET_HDR_F_NEEDS_CSUM;
|
|
|
|
gso.csum_start = skb->csum_start - skb_headroom(skb);
|
|
|
|
gso.csum_offset = skb->csum_offset;
|
|
|
|
} /* else everything is zero */
|
|
|
|
|
2009-04-20 08:25:59 +07:00
|
|
|
if (unlikely(memcpy_toiovecend(iv, (void *)&gso, total,
|
|
|
|
sizeof(gso))))
|
2008-07-03 17:48:02 +07:00
|
|
|
return -EFAULT;
|
2010-03-17 22:45:01 +07:00
|
|
|
total += tun->vnet_hdr_sz;
|
2008-07-03 17:48:02 +07:00
|
|
|
}
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
len = min_t(int, skb->len, len);
|
|
|
|
|
2009-04-20 08:25:59 +07:00
|
|
|
skb_copy_datagram_const_iovec(skb, 0, iv, total, len);
|
2010-01-14 13:17:09 +07:00
|
|
|
total += skb->len;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2007-10-04 07:41:50 +07:00
|
|
|
tun->dev->stats.tx_packets++;
|
|
|
|
tun->dev->stats.tx_bytes += len;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
return total;
|
|
|
|
}
|
|
|
|
|
2010-01-14 13:17:09 +07:00
|
|
|
static ssize_t tun_do_read(struct tun_struct *tun,
|
|
|
|
struct kiocb *iocb, const struct iovec *iv,
|
|
|
|
ssize_t len, int noblock)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
DECLARE_WAITQUEUE(wait, current);
|
|
|
|
struct sk_buff *skb;
|
2010-01-14 13:17:09 +07:00
|
|
|
ssize_t ret = 0;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
DBG(KERN_INFO "%s: tun_chr_read\n", tun->dev->name);
|
|
|
|
|
2010-04-29 18:01:49 +07:00
|
|
|
add_wait_queue(&tun->wq.wait, &wait);
|
2005-04-17 05:20:36 +07:00
|
|
|
while (len) {
|
|
|
|
current->state = TASK_INTERRUPTIBLE;
|
|
|
|
|
|
|
|
/* Read frames from the queue */
|
2009-08-30 14:04:42 +07:00
|
|
|
if (!(skb=skb_dequeue(&tun->socket.sk->sk_receive_queue))) {
|
2010-01-14 13:17:09 +07:00
|
|
|
if (noblock) {
|
2005-04-17 05:20:36 +07:00
|
|
|
ret = -EAGAIN;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (signal_pending(current)) {
|
|
|
|
ret = -ERESTARTSYS;
|
|
|
|
break;
|
|
|
|
}
|
2009-01-20 18:07:17 +07:00
|
|
|
if (tun->dev->reg_state != NETREG_REGISTERED) {
|
|
|
|
ret = -EIO;
|
|
|
|
break;
|
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
/* Nothing to read, let's sleep */
|
|
|
|
schedule();
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
netif_wake_queue(tun->dev);
|
|
|
|
|
2009-04-20 08:25:59 +07:00
|
|
|
ret = tun_put_user(tun, skb, iv, len);
|
2008-07-15 12:18:19 +07:00
|
|
|
kfree_skb(skb);
|
|
|
|
break;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
current->state = TASK_RUNNING;
|
2010-04-29 18:01:49 +07:00
|
|
|
remove_wait_queue(&tun->wq.wait, &wait);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2010-01-14 13:17:09 +07:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t tun_chr_aio_read(struct kiocb *iocb, const struct iovec *iv,
|
|
|
|
unsigned long count, loff_t pos)
|
|
|
|
{
|
|
|
|
struct file *file = iocb->ki_filp;
|
|
|
|
struct tun_file *tfile = file->private_data;
|
|
|
|
struct tun_struct *tun = __tun_get(tfile);
|
|
|
|
ssize_t len, ret;
|
|
|
|
|
|
|
|
if (!tun)
|
|
|
|
return -EBADFD;
|
|
|
|
len = iov_length(iv, count);
|
|
|
|
if (len < 0) {
|
|
|
|
ret = -EINVAL;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = tun_do_read(tun, iocb, iv, len, file->f_flags & O_NONBLOCK);
|
|
|
|
ret = min_t(ssize_t, ret, len);
|
2009-01-20 18:00:40 +07:00
|
|
|
out:
|
|
|
|
tun_put(tun);
|
2005-04-17 05:20:36 +07:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void tun_setup(struct net_device *dev)
|
|
|
|
{
|
|
|
|
struct tun_struct *tun = netdev_priv(dev);
|
|
|
|
|
|
|
|
tun->owner = -1;
|
2007-07-03 12:50:25 +07:00
|
|
|
tun->group = -1;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
dev->ethtool_ops = &tun_ethtool_ops;
|
2009-04-18 21:15:52 +07:00
|
|
|
dev->destructor = tun_free_netdev;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2009-01-22 07:02:16 +07:00
|
|
|
/* Trivial set of netlink ops to allow deleting tun or tap
|
|
|
|
* device with netlink.
|
|
|
|
*/
|
|
|
|
static int tun_validate(struct nlattr *tb[], struct nlattr *data[])
|
|
|
|
{
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct rtnl_link_ops tun_link_ops __read_mostly = {
|
|
|
|
.kind = DRV_NAME,
|
|
|
|
.priv_size = sizeof(struct tun_struct),
|
|
|
|
.setup = tun_setup,
|
|
|
|
.validate = tun_validate,
|
|
|
|
};
|
|
|
|
|
2009-02-06 12:25:32 +07:00
|
|
|
static void tun_sock_write_space(struct sock *sk)
|
|
|
|
{
|
|
|
|
struct tun_struct *tun;
|
2010-04-29 18:01:49 +07:00
|
|
|
wait_queue_head_t *wqueue;
|
2009-02-06 12:25:32 +07:00
|
|
|
|
|
|
|
if (!sock_writeable(sk))
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (!test_and_clear_bit(SOCK_ASYNC_NOSPACE, &sk->sk_socket->flags))
|
|
|
|
return;
|
|
|
|
|
2010-04-29 18:01:49 +07:00
|
|
|
wqueue = sk_sleep(sk);
|
|
|
|
if (wqueue && waitqueue_active(wqueue))
|
|
|
|
wake_up_interruptible_sync_poll(wqueue, POLLOUT |
|
2010-01-14 13:17:09 +07:00
|
|
|
POLLWRNORM | POLLWRBAND);
|
2009-06-04 11:45:55 +07:00
|
|
|
|
2009-12-25 14:17:43 +07:00
|
|
|
tun = tun_sk(sk)->tun;
|
2009-02-06 12:25:32 +07:00
|
|
|
kill_fasync(&tun->fasync, SIGIO, POLL_OUT);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void tun_sock_destruct(struct sock *sk)
|
|
|
|
{
|
2009-12-25 14:17:43 +07:00
|
|
|
free_netdev(tun_sk(sk)->tun->dev);
|
2009-02-06 12:25:32 +07:00
|
|
|
}
|
|
|
|
|
2010-01-14 13:17:09 +07:00
|
|
|
static int tun_sendmsg(struct kiocb *iocb, struct socket *sock,
|
|
|
|
struct msghdr *m, size_t total_len)
|
|
|
|
{
|
|
|
|
struct tun_struct *tun = container_of(sock, struct tun_struct, socket);
|
|
|
|
return tun_get_user(tun, m->msg_iov, total_len,
|
|
|
|
m->msg_flags & MSG_DONTWAIT);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int tun_recvmsg(struct kiocb *iocb, struct socket *sock,
|
|
|
|
struct msghdr *m, size_t total_len,
|
|
|
|
int flags)
|
|
|
|
{
|
|
|
|
struct tun_struct *tun = container_of(sock, struct tun_struct, socket);
|
|
|
|
int ret;
|
|
|
|
if (flags & ~(MSG_DONTWAIT|MSG_TRUNC))
|
|
|
|
return -EINVAL;
|
|
|
|
ret = tun_do_read(tun, iocb, m->msg_iov, total_len,
|
|
|
|
flags & MSG_DONTWAIT);
|
|
|
|
if (ret > total_len) {
|
|
|
|
m->msg_flags |= MSG_TRUNC;
|
|
|
|
ret = flags & MSG_TRUNC ? ret : total_len;
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Ops structure to mimic raw sockets with tun */
|
|
|
|
static const struct proto_ops tun_socket_ops = {
|
|
|
|
.sendmsg = tun_sendmsg,
|
|
|
|
.recvmsg = tun_recvmsg,
|
|
|
|
};
|
|
|
|
|
2009-02-06 12:25:32 +07:00
|
|
|
static struct proto tun_proto = {
|
|
|
|
.name = "tun",
|
|
|
|
.owner = THIS_MODULE,
|
|
|
|
.obj_size = sizeof(struct tun_sock),
|
|
|
|
};
|
2009-01-22 07:02:16 +07:00
|
|
|
|
2009-05-10 12:54:21 +07:00
|
|
|
static int tun_flags(struct tun_struct *tun)
|
|
|
|
{
|
|
|
|
int flags = 0;
|
|
|
|
|
|
|
|
if (tun->flags & TUN_TUN_DEV)
|
|
|
|
flags |= IFF_TUN;
|
|
|
|
else
|
|
|
|
flags |= IFF_TAP;
|
|
|
|
|
|
|
|
if (tun->flags & TUN_NO_PI)
|
|
|
|
flags |= IFF_NO_PI;
|
|
|
|
|
|
|
|
if (tun->flags & TUN_ONE_QUEUE)
|
|
|
|
flags |= IFF_ONE_QUEUE;
|
|
|
|
|
|
|
|
if (tun->flags & TUN_VNET_HDR)
|
|
|
|
flags |= IFF_VNET_HDR;
|
|
|
|
|
|
|
|
return flags;
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t tun_show_flags(struct device *dev, struct device_attribute *attr,
|
|
|
|
char *buf)
|
|
|
|
{
|
|
|
|
struct tun_struct *tun = netdev_priv(to_net_dev(dev));
|
|
|
|
return sprintf(buf, "0x%x\n", tun_flags(tun));
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t tun_show_owner(struct device *dev, struct device_attribute *attr,
|
|
|
|
char *buf)
|
|
|
|
{
|
|
|
|
struct tun_struct *tun = netdev_priv(to_net_dev(dev));
|
|
|
|
return sprintf(buf, "%d\n", tun->owner);
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t tun_show_group(struct device *dev, struct device_attribute *attr,
|
|
|
|
char *buf)
|
|
|
|
{
|
|
|
|
struct tun_struct *tun = netdev_priv(to_net_dev(dev));
|
|
|
|
return sprintf(buf, "%d\n", tun->group);
|
|
|
|
}
|
|
|
|
|
|
|
|
static DEVICE_ATTR(tun_flags, 0444, tun_show_flags, NULL);
|
|
|
|
static DEVICE_ATTR(owner, 0444, tun_show_owner, NULL);
|
|
|
|
static DEVICE_ATTR(group, 0444, tun_show_group, NULL);
|
|
|
|
|
2008-04-16 14:41:16 +07:00
|
|
|
static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2009-02-06 12:25:32 +07:00
|
|
|
struct sock *sk;
|
2005-04-17 05:20:36 +07:00
|
|
|
struct tun_struct *tun;
|
|
|
|
struct net_device *dev;
|
|
|
|
int err;
|
|
|
|
|
2009-01-20 17:56:20 +07:00
|
|
|
dev = __dev_get_by_name(net, ifr->ifr_name);
|
|
|
|
if (dev) {
|
2009-08-29 05:12:43 +07:00
|
|
|
const struct cred *cred = current_cred();
|
|
|
|
|
2009-04-27 17:23:54 +07:00
|
|
|
if (ifr->ifr_flags & IFF_TUN_EXCL)
|
|
|
|
return -EBUSY;
|
2009-01-20 17:56:20 +07:00
|
|
|
if ((ifr->ifr_flags & IFF_TUN) && dev->netdev_ops == &tun_netdev_ops)
|
|
|
|
tun = netdev_priv(dev);
|
|
|
|
else if ((ifr->ifr_flags & IFF_TAP) && dev->netdev_ops == &tap_netdev_ops)
|
|
|
|
tun = netdev_priv(dev);
|
|
|
|
else
|
|
|
|
return -EINVAL;
|
|
|
|
|
2009-08-29 05:12:43 +07:00
|
|
|
if (((tun->owner != -1 && cred->euid != tun->owner) ||
|
|
|
|
(tun->group != -1 && !in_egroup_p(tun->group))) &&
|
|
|
|
!capable(CAP_NET_ADMIN))
|
|
|
|
return -EPERM;
|
2009-09-15 00:37:28 +07:00
|
|
|
err = security_tun_dev_attach(tun->socket.sk);
|
2009-08-29 05:12:43 +07:00
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
|
2009-01-20 17:57:48 +07:00
|
|
|
err = tun_attach(tun, file);
|
|
|
|
if (err < 0)
|
|
|
|
return err;
|
2006-09-14 00:24:59 +07:00
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
else {
|
|
|
|
char *name;
|
|
|
|
unsigned long flags = 0;
|
|
|
|
|
2006-06-23 06:07:52 +07:00
|
|
|
if (!capable(CAP_NET_ADMIN))
|
|
|
|
return -EPERM;
|
2009-08-29 05:12:43 +07:00
|
|
|
err = security_tun_dev_create();
|
|
|
|
if (err < 0)
|
|
|
|
return err;
|
2006-06-23 06:07:52 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
/* Set dev type */
|
|
|
|
if (ifr->ifr_flags & IFF_TUN) {
|
|
|
|
/* TUN device */
|
|
|
|
flags |= TUN_TUN_DEV;
|
|
|
|
name = "tun%d";
|
|
|
|
} else if (ifr->ifr_flags & IFF_TAP) {
|
|
|
|
/* TAP device */
|
|
|
|
flags |= TUN_TAP_DEV;
|
|
|
|
name = "tap%d";
|
2006-09-14 00:24:59 +07:00
|
|
|
} else
|
2009-09-17 04:36:13 +07:00
|
|
|
return -EINVAL;
|
2006-09-14 00:24:59 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
if (*ifr->ifr_name)
|
|
|
|
name = ifr->ifr_name;
|
|
|
|
|
|
|
|
dev = alloc_netdev(sizeof(struct tun_struct), name,
|
|
|
|
tun_setup);
|
|
|
|
if (!dev)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2008-04-16 14:41:53 +07:00
|
|
|
dev_net_set(dev, net);
|
2009-01-22 07:02:16 +07:00
|
|
|
dev->rtnl_link_ops = &tun_link_ops;
|
2008-11-20 13:10:37 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
tun = netdev_priv(dev);
|
|
|
|
tun->dev = dev;
|
|
|
|
tun->flags = flags;
|
2008-07-15 12:18:19 +07:00
|
|
|
tun->txflt.count = 0;
|
2010-03-17 22:45:01 +07:00
|
|
|
tun->vnet_hdr_sz = sizeof(struct virtio_net_hdr);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2009-02-06 12:25:32 +07:00
|
|
|
err = -ENOMEM;
|
|
|
|
sk = sk_alloc(net, AF_UNSPEC, GFP_KERNEL, &tun_proto);
|
|
|
|
if (!sk)
|
|
|
|
goto err_free_dev;
|
|
|
|
|
2010-04-29 18:01:49 +07:00
|
|
|
tun->socket.wq = &tun->wq;
|
|
|
|
init_waitqueue_head(&tun->wq.wait);
|
2010-01-14 13:17:09 +07:00
|
|
|
tun->socket.ops = &tun_socket_ops;
|
2009-02-06 12:25:32 +07:00
|
|
|
sock_init_data(&tun->socket, sk);
|
|
|
|
sk->sk_write_space = tun_sock_write_space;
|
|
|
|
sk->sk_sndbuf = INT_MAX;
|
|
|
|
|
2009-12-25 14:17:43 +07:00
|
|
|
tun_sk(sk)->tun = tun;
|
2009-02-06 12:25:32 +07:00
|
|
|
|
2009-08-29 05:12:43 +07:00
|
|
|
security_tun_dev_post_create(sk);
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
tun_net_init(dev);
|
|
|
|
|
|
|
|
if (strchr(dev->name, '%')) {
|
|
|
|
err = dev_alloc_name(dev, dev->name);
|
|
|
|
if (err < 0)
|
2009-02-06 12:25:32 +07:00
|
|
|
goto err_free_sk;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
err = register_netdevice(tun->dev);
|
|
|
|
if (err < 0)
|
2009-04-18 21:15:52 +07:00
|
|
|
goto err_free_sk;
|
|
|
|
|
2009-05-10 12:54:21 +07:00
|
|
|
if (device_create_file(&tun->dev->dev, &dev_attr_tun_flags) ||
|
|
|
|
device_create_file(&tun->dev->dev, &dev_attr_owner) ||
|
|
|
|
device_create_file(&tun->dev->dev, &dev_attr_group))
|
|
|
|
printk(KERN_ERR "Failed to create tun sysfs files\n");
|
|
|
|
|
2009-04-18 21:15:52 +07:00
|
|
|
sk->sk_destruct = tun_sock_destruct;
|
2009-01-20 17:57:48 +07:00
|
|
|
|
|
|
|
err = tun_attach(tun, file);
|
|
|
|
if (err < 0)
|
2009-04-18 21:15:52 +07:00
|
|
|
goto failed;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
DBG(KERN_INFO "%s: tun_set_iff\n", tun->dev->name);
|
|
|
|
|
|
|
|
if (ifr->ifr_flags & IFF_NO_PI)
|
|
|
|
tun->flags |= TUN_NO_PI;
|
2008-02-05 18:05:07 +07:00
|
|
|
else
|
|
|
|
tun->flags &= ~TUN_NO_PI;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
if (ifr->ifr_flags & IFF_ONE_QUEUE)
|
|
|
|
tun->flags |= TUN_ONE_QUEUE;
|
2008-02-05 18:05:07 +07:00
|
|
|
else
|
|
|
|
tun->flags &= ~TUN_ONE_QUEUE;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2008-07-03 17:48:02 +07:00
|
|
|
if (ifr->ifr_flags & IFF_VNET_HDR)
|
|
|
|
tun->flags |= TUN_VNET_HDR;
|
|
|
|
else
|
|
|
|
tun->flags &= ~TUN_VNET_HDR;
|
|
|
|
|
2008-07-11 06:59:11 +07:00
|
|
|
/* Make sure persistent devices do not get stuck in
|
|
|
|
* xoff state.
|
|
|
|
*/
|
|
|
|
if (netif_running(tun->dev))
|
|
|
|
netif_wake_queue(tun->dev);
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
strcpy(ifr->ifr_name, tun->dev->name);
|
|
|
|
return 0;
|
|
|
|
|
2009-02-06 12:25:32 +07:00
|
|
|
err_free_sk:
|
|
|
|
sock_put(sk);
|
2005-04-17 05:20:36 +07:00
|
|
|
err_free_dev:
|
|
|
|
free_netdev(dev);
|
|
|
|
failed:
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2009-08-06 21:22:44 +07:00
|
|
|
static int tun_get_iff(struct net *net, struct tun_struct *tun,
|
|
|
|
struct ifreq *ifr)
|
2008-08-16 05:09:56 +07:00
|
|
|
{
|
|
|
|
DBG(KERN_INFO "%s: tun_get_iff\n", tun->dev->name);
|
|
|
|
|
|
|
|
strcpy(ifr->ifr_name, tun->dev->name);
|
|
|
|
|
2009-05-10 12:54:21 +07:00
|
|
|
ifr->ifr_flags = tun_flags(tun);
|
2008-08-16 05:09:56 +07:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-07-03 17:46:16 +07:00
|
|
|
/* This is like a cut-down ethtool ops, except done via tun fd so no
|
|
|
|
* privs required. */
|
|
|
|
static int set_offload(struct net_device *dev, unsigned long arg)
|
|
|
|
{
|
|
|
|
unsigned int old_features, features;
|
|
|
|
|
|
|
|
old_features = dev->features;
|
|
|
|
/* Unset features, set them as we chew on the arg. */
|
|
|
|
features = (old_features & ~(NETIF_F_HW_CSUM|NETIF_F_SG|NETIF_F_FRAGLIST
|
2009-07-14 21:21:04 +07:00
|
|
|
|NETIF_F_TSO_ECN|NETIF_F_TSO|NETIF_F_TSO6
|
|
|
|
|NETIF_F_UFO));
|
2008-07-03 17:46:16 +07:00
|
|
|
|
|
|
|
if (arg & TUN_F_CSUM) {
|
|
|
|
features |= NETIF_F_HW_CSUM|NETIF_F_SG|NETIF_F_FRAGLIST;
|
|
|
|
arg &= ~TUN_F_CSUM;
|
|
|
|
|
|
|
|
if (arg & (TUN_F_TSO4|TUN_F_TSO6)) {
|
|
|
|
if (arg & TUN_F_TSO_ECN) {
|
|
|
|
features |= NETIF_F_TSO_ECN;
|
|
|
|
arg &= ~TUN_F_TSO_ECN;
|
|
|
|
}
|
|
|
|
if (arg & TUN_F_TSO4)
|
|
|
|
features |= NETIF_F_TSO;
|
|
|
|
if (arg & TUN_F_TSO6)
|
|
|
|
features |= NETIF_F_TSO6;
|
|
|
|
arg &= ~(TUN_F_TSO4|TUN_F_TSO6);
|
|
|
|
}
|
2009-07-14 21:21:04 +07:00
|
|
|
|
|
|
|
if (arg & TUN_F_UFO) {
|
|
|
|
features |= NETIF_F_UFO;
|
|
|
|
arg &= ~TUN_F_UFO;
|
|
|
|
}
|
2008-07-03 17:46:16 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
/* This gives the user a way to test for new features in future by
|
|
|
|
* trying to set them. */
|
|
|
|
if (arg)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
dev->features = features;
|
|
|
|
if (old_features != dev->features)
|
|
|
|
netdev_features_change(dev);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-11-07 13:52:32 +07:00
|
|
|
static long __tun_chr_ioctl(struct file *file, unsigned int cmd,
|
|
|
|
unsigned long arg, int ifreq_len)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2009-01-20 18:01:48 +07:00
|
|
|
struct tun_file *tfile = file->private_data;
|
2009-01-20 18:00:40 +07:00
|
|
|
struct tun_struct *tun;
|
2005-04-17 05:20:36 +07:00
|
|
|
void __user* argp = (void __user*)arg;
|
2010-02-14 08:01:10 +07:00
|
|
|
struct sock_fprog fprog;
|
2005-04-17 05:20:36 +07:00
|
|
|
struct ifreq ifr;
|
2009-02-06 12:25:32 +07:00
|
|
|
int sndbuf;
|
2010-03-17 22:45:01 +07:00
|
|
|
int vnet_hdr_sz;
|
2008-07-15 12:18:19 +07:00
|
|
|
int ret;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
if (cmd == TUNSETIFF || _IOC_TYPE(cmd) == 0x89)
|
2009-11-07 13:52:32 +07:00
|
|
|
if (copy_from_user(&ifr, argp, ifreq_len))
|
2005-04-17 05:20:36 +07:00
|
|
|
return -EFAULT;
|
|
|
|
|
2009-01-20 18:00:40 +07:00
|
|
|
if (cmd == TUNGETFEATURES) {
|
|
|
|
/* Currently this just means: "what IFF flags are valid?".
|
|
|
|
* This is needed because we never checked for invalid flags on
|
|
|
|
* TUNSETIFF. */
|
|
|
|
return put_user(IFF_TUN | IFF_TAP | IFF_NO_PI | IFF_ONE_QUEUE |
|
|
|
|
IFF_VNET_HDR,
|
|
|
|
(unsigned int __user*)argp);
|
|
|
|
}
|
|
|
|
|
2009-08-06 21:22:44 +07:00
|
|
|
rtnl_lock();
|
|
|
|
|
2009-01-20 18:01:48 +07:00
|
|
|
tun = __tun_get(tfile);
|
2005-04-17 05:20:36 +07:00
|
|
|
if (cmd == TUNSETIFF && !tun) {
|
|
|
|
ifr.ifr_name[IFNAMSIZ-1] = '\0';
|
|
|
|
|
2009-08-06 21:22:44 +07:00
|
|
|
ret = tun_set_iff(tfile->net, file, &ifr);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2009-08-06 21:22:44 +07:00
|
|
|
if (ret)
|
|
|
|
goto unlock;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2009-11-07 13:52:32 +07:00
|
|
|
if (copy_to_user(argp, &ifr, ifreq_len))
|
2009-08-06 21:22:44 +07:00
|
|
|
ret = -EFAULT;
|
|
|
|
goto unlock;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2009-08-06 21:22:44 +07:00
|
|
|
ret = -EBADFD;
|
2005-04-17 05:20:36 +07:00
|
|
|
if (!tun)
|
2009-08-06 21:22:44 +07:00
|
|
|
goto unlock;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
DBG(KERN_INFO "%s: tun_chr_ioctl cmd %d\n", tun->dev->name, cmd);
|
|
|
|
|
2009-01-20 18:00:40 +07:00
|
|
|
ret = 0;
|
2005-04-17 05:20:36 +07:00
|
|
|
switch (cmd) {
|
2008-08-16 05:09:56 +07:00
|
|
|
case TUNGETIFF:
|
2009-08-06 21:22:44 +07:00
|
|
|
ret = tun_get_iff(current->nsproxy->net_ns, tun, &ifr);
|
2008-08-16 05:09:56 +07:00
|
|
|
if (ret)
|
2009-01-20 18:00:40 +07:00
|
|
|
break;
|
2008-08-16 05:09:56 +07:00
|
|
|
|
2009-11-07 13:52:32 +07:00
|
|
|
if (copy_to_user(argp, &ifr, ifreq_len))
|
2009-01-20 18:00:40 +07:00
|
|
|
ret = -EFAULT;
|
2008-08-16 05:09:56 +07:00
|
|
|
break;
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
case TUNSETNOCSUM:
|
|
|
|
/* Disable/Enable checksum */
|
|
|
|
if (arg)
|
|
|
|
tun->flags |= TUN_NOCHECKSUM;
|
|
|
|
else
|
|
|
|
tun->flags &= ~TUN_NOCHECKSUM;
|
|
|
|
|
|
|
|
DBG(KERN_INFO "%s: checksum %s\n",
|
|
|
|
tun->dev->name, arg ? "disabled" : "enabled");
|
|
|
|
break;
|
|
|
|
|
|
|
|
case TUNSETPERSIST:
|
|
|
|
/* Disable/Enable persist mode */
|
|
|
|
if (arg)
|
|
|
|
tun->flags |= TUN_PERSIST;
|
|
|
|
else
|
|
|
|
tun->flags &= ~TUN_PERSIST;
|
|
|
|
|
|
|
|
DBG(KERN_INFO "%s: persist %s\n",
|
2007-12-25 12:29:35 +07:00
|
|
|
tun->dev->name, arg ? "enabled" : "disabled");
|
2005-04-17 05:20:36 +07:00
|
|
|
break;
|
|
|
|
|
|
|
|
case TUNSETOWNER:
|
|
|
|
/* Set owner of the device */
|
|
|
|
tun->owner = (uid_t) arg;
|
|
|
|
|
|
|
|
DBG(KERN_INFO "%s: owner set to %d\n", tun->dev->name, tun->owner);
|
|
|
|
break;
|
|
|
|
|
2007-07-03 12:50:25 +07:00
|
|
|
case TUNSETGROUP:
|
|
|
|
/* Set group of the device */
|
|
|
|
tun->group= (gid_t) arg;
|
|
|
|
|
|
|
|
DBG(KERN_INFO "%s: group set to %d\n", tun->dev->name, tun->group);
|
|
|
|
break;
|
|
|
|
|
2005-09-02 07:40:05 +07:00
|
|
|
case TUNSETLINK:
|
|
|
|
/* Only allow setting the type when the interface is down */
|
|
|
|
if (tun->dev->flags & IFF_UP) {
|
|
|
|
DBG(KERN_INFO "%s: Linktype set failed because interface is up\n",
|
|
|
|
tun->dev->name);
|
2008-04-24 09:37:58 +07:00
|
|
|
ret = -EBUSY;
|
2005-09-02 07:40:05 +07:00
|
|
|
} else {
|
|
|
|
tun->dev->type = (int) arg;
|
|
|
|
DBG(KERN_INFO "%s: linktype set to %d\n", tun->dev->name, tun->dev->type);
|
2008-04-24 09:37:58 +07:00
|
|
|
ret = 0;
|
2005-09-02 07:40:05 +07:00
|
|
|
}
|
2009-01-20 18:00:40 +07:00
|
|
|
break;
|
2005-09-02 07:40:05 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
#ifdef TUN_DEBUG
|
|
|
|
case TUNSETDEBUG:
|
|
|
|
tun->debug = arg;
|
|
|
|
break;
|
|
|
|
#endif
|
2008-07-03 17:46:16 +07:00
|
|
|
case TUNSETOFFLOAD:
|
|
|
|
ret = set_offload(tun->dev, arg);
|
2009-01-20 18:00:40 +07:00
|
|
|
break;
|
2008-07-03 17:46:16 +07:00
|
|
|
|
2008-07-15 12:18:19 +07:00
|
|
|
case TUNSETTXFILTER:
|
|
|
|
/* Can be set only for TAPs */
|
2009-01-20 18:00:40 +07:00
|
|
|
ret = -EINVAL;
|
2008-07-15 12:18:19 +07:00
|
|
|
if ((tun->flags & TUN_TYPE_MASK) != TUN_TAP_DEV)
|
2009-01-20 18:00:40 +07:00
|
|
|
break;
|
2008-07-17 02:45:34 +07:00
|
|
|
ret = update_filter(&tun->txflt, (void __user *)arg);
|
2009-01-20 18:00:40 +07:00
|
|
|
break;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
case SIOCGIFHWADDR:
|
2008-07-15 12:18:19 +07:00
|
|
|
/* Get hw addres */
|
|
|
|
memcpy(ifr.ifr_hwaddr.sa_data, tun->dev->dev_addr, ETH_ALEN);
|
|
|
|
ifr.ifr_hwaddr.sa_family = tun->dev->type;
|
2009-11-07 13:52:32 +07:00
|
|
|
if (copy_to_user(argp, &ifr, ifreq_len))
|
2009-01-20 18:00:40 +07:00
|
|
|
ret = -EFAULT;
|
|
|
|
break;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
case SIOCSIFHWADDR:
|
2008-07-15 12:18:19 +07:00
|
|
|
/* Set hw address */
|
2008-10-28 05:59:26 +07:00
|
|
|
DBG(KERN_DEBUG "%s: set hw address: %pM\n",
|
|
|
|
tun->dev->name, ifr.ifr_hwaddr.sa_data);
|
2008-03-01 03:26:21 +07:00
|
|
|
|
|
|
|
ret = dev_set_mac_address(tun->dev, &ifr.ifr_hwaddr);
|
2009-01-20 18:00:40 +07:00
|
|
|
break;
|
2009-02-06 12:25:32 +07:00
|
|
|
|
|
|
|
case TUNGETSNDBUF:
|
2009-08-30 14:04:42 +07:00
|
|
|
sndbuf = tun->socket.sk->sk_sndbuf;
|
2009-02-06 12:25:32 +07:00
|
|
|
if (copy_to_user(argp, &sndbuf, sizeof(sndbuf)))
|
|
|
|
ret = -EFAULT;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case TUNSETSNDBUF:
|
|
|
|
if (copy_from_user(&sndbuf, argp, sizeof(sndbuf))) {
|
|
|
|
ret = -EFAULT;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2009-08-30 14:04:42 +07:00
|
|
|
tun->socket.sk->sk_sndbuf = sndbuf;
|
2009-02-06 12:25:32 +07:00
|
|
|
break;
|
|
|
|
|
2010-03-17 22:45:01 +07:00
|
|
|
case TUNGETVNETHDRSZ:
|
|
|
|
vnet_hdr_sz = tun->vnet_hdr_sz;
|
|
|
|
if (copy_to_user(argp, &vnet_hdr_sz, sizeof(vnet_hdr_sz)))
|
|
|
|
ret = -EFAULT;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case TUNSETVNETHDRSZ:
|
|
|
|
if (copy_from_user(&vnet_hdr_sz, argp, sizeof(vnet_hdr_sz))) {
|
|
|
|
ret = -EFAULT;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (vnet_hdr_sz < (int)sizeof(struct virtio_net_hdr)) {
|
|
|
|
ret = -EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
tun->vnet_hdr_sz = vnet_hdr_sz;
|
|
|
|
break;
|
|
|
|
|
2010-02-14 08:01:10 +07:00
|
|
|
case TUNATTACHFILTER:
|
|
|
|
/* Can be set only for TAPs */
|
|
|
|
ret = -EINVAL;
|
|
|
|
if ((tun->flags & TUN_TYPE_MASK) != TUN_TAP_DEV)
|
|
|
|
break;
|
|
|
|
ret = -EFAULT;
|
|
|
|
if (copy_from_user(&fprog, argp, sizeof(fprog)))
|
|
|
|
break;
|
|
|
|
|
|
|
|
ret = sk_attach_filter(&fprog, tun->socket.sk);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case TUNDETACHFILTER:
|
|
|
|
/* Can be set only for TAPs */
|
|
|
|
ret = -EINVAL;
|
|
|
|
if ((tun->flags & TUN_TYPE_MASK) != TUN_TAP_DEV)
|
|
|
|
break;
|
|
|
|
ret = sk_detach_filter(tun->socket.sk);
|
|
|
|
break;
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
default:
|
2009-01-20 18:00:40 +07:00
|
|
|
ret = -EINVAL;
|
|
|
|
break;
|
2010-05-18 12:47:34 +07:00
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2009-08-06 21:22:44 +07:00
|
|
|
unlock:
|
|
|
|
rtnl_unlock();
|
|
|
|
if (tun)
|
|
|
|
tun_put(tun);
|
2009-01-20 18:00:40 +07:00
|
|
|
return ret;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2009-11-07 13:52:32 +07:00
|
|
|
static long tun_chr_ioctl(struct file *file,
|
|
|
|
unsigned int cmd, unsigned long arg)
|
|
|
|
{
|
|
|
|
return __tun_chr_ioctl(file, cmd, arg, sizeof (struct ifreq));
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef CONFIG_COMPAT
|
|
|
|
static long tun_chr_compat_ioctl(struct file *file,
|
|
|
|
unsigned int cmd, unsigned long arg)
|
|
|
|
{
|
|
|
|
switch (cmd) {
|
|
|
|
case TUNSETIFF:
|
|
|
|
case TUNGETIFF:
|
|
|
|
case TUNSETTXFILTER:
|
|
|
|
case TUNGETSNDBUF:
|
|
|
|
case TUNSETSNDBUF:
|
|
|
|
case SIOCGIFHWADDR:
|
|
|
|
case SIOCSIFHWADDR:
|
|
|
|
arg = (unsigned long)compat_ptr(arg);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
arg = (compat_ulong_t)arg;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* compat_ifreq is shorter than ifreq, so we must not access beyond
|
|
|
|
* the end of that structure. All fields that are used in this
|
|
|
|
* driver are compatible though, we don't need to convert the
|
|
|
|
* contents.
|
|
|
|
*/
|
|
|
|
return __tun_chr_ioctl(file, cmd, arg, sizeof(struct compat_ifreq));
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_COMPAT */
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
static int tun_chr_fasync(int fd, struct file *file, int on)
|
|
|
|
{
|
2009-01-20 18:00:40 +07:00
|
|
|
struct tun_struct *tun = tun_get(file);
|
2005-04-17 05:20:36 +07:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (!tun)
|
|
|
|
return -EBADFD;
|
|
|
|
|
|
|
|
DBG(KERN_INFO "%s: tun_chr_fasync %d\n", tun->dev->name, on);
|
|
|
|
|
|
|
|
if ((ret = fasync_helper(fd, file, on, &tun->fasync)) < 0)
|
2008-06-20 04:50:37 +07:00
|
|
|
goto out;
|
2006-09-14 00:24:59 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
if (on) {
|
2006-10-02 16:17:15 +07:00
|
|
|
ret = __f_setown(file, task_pid(current), PIDTYPE_PID, 0);
|
2005-04-17 05:20:36 +07:00
|
|
|
if (ret)
|
2008-06-20 04:50:37 +07:00
|
|
|
goto out;
|
2005-04-17 05:20:36 +07:00
|
|
|
tun->flags |= TUN_FASYNC;
|
2006-09-14 00:24:59 +07:00
|
|
|
} else
|
2005-04-17 05:20:36 +07:00
|
|
|
tun->flags &= ~TUN_FASYNC;
|
2008-06-20 04:50:37 +07:00
|
|
|
ret = 0;
|
|
|
|
out:
|
2009-01-20 18:00:40 +07:00
|
|
|
tun_put(tun);
|
2008-06-20 04:50:37 +07:00
|
|
|
return ret;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static int tun_chr_open(struct inode *inode, struct file * file)
|
|
|
|
{
|
2009-01-20 18:00:40 +07:00
|
|
|
struct tun_file *tfile;
|
2009-10-14 15:19:46 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
DBG1(KERN_INFO "tunX: tun_chr_open\n");
|
2009-01-20 18:00:40 +07:00
|
|
|
|
|
|
|
tfile = kmalloc(sizeof(*tfile), GFP_KERNEL);
|
|
|
|
if (!tfile)
|
|
|
|
return -ENOMEM;
|
2009-01-20 18:07:17 +07:00
|
|
|
atomic_set(&tfile->count, 0);
|
2009-01-20 18:00:40 +07:00
|
|
|
tfile->tun = NULL;
|
2009-01-20 18:01:48 +07:00
|
|
|
tfile->net = get_net(current->nsproxy->net_ns);
|
2009-01-20 18:00:40 +07:00
|
|
|
file->private_data = tfile;
|
2005-04-17 05:20:36 +07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int tun_chr_close(struct inode *inode, struct file *file)
|
|
|
|
{
|
2009-01-20 18:00:40 +07:00
|
|
|
struct tun_file *tfile = file->private_data;
|
2009-06-08 14:44:31 +07:00
|
|
|
struct tun_struct *tun;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2009-06-08 14:44:31 +07:00
|
|
|
tun = __tun_get(tfile);
|
2009-01-20 18:00:40 +07:00
|
|
|
if (tun) {
|
2009-07-03 06:03:55 +07:00
|
|
|
struct net_device *dev = tun->dev;
|
|
|
|
|
|
|
|
DBG(KERN_INFO "%s: tun_chr_close\n", dev->name);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2009-01-20 18:00:40 +07:00
|
|
|
__tun_detach(tun);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
tree-wide: Assorted spelling fixes
In particular, several occurances of funny versions of 'success',
'unknown', 'therefore', 'acknowledge', 'argument', 'achieve', 'address',
'beginning', 'desirable', 'separate' and 'necessary' are fixed.
Signed-off-by: Daniel Mack <daniel@caiaq.de>
Cc: Joe Perches <joe@perches.com>
Cc: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
2010-02-03 07:01:28 +07:00
|
|
|
/* If desirable, unregister the netdevice. */
|
2009-07-03 06:03:55 +07:00
|
|
|
if (!(tun->flags & TUN_PERSIST)) {
|
|
|
|
rtnl_lock();
|
|
|
|
if (dev->reg_state == NETREG_REGISTERED)
|
|
|
|
unregister_netdevice(dev);
|
|
|
|
rtnl_unlock();
|
|
|
|
}
|
2009-01-20 18:00:40 +07:00
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2009-04-18 21:15:52 +07:00
|
|
|
tun = tfile->tun;
|
|
|
|
if (tun)
|
2009-08-30 14:04:42 +07:00
|
|
|
sock_put(tun->socket.sk);
|
2009-04-18 21:15:52 +07:00
|
|
|
|
2009-01-20 18:01:48 +07:00
|
|
|
put_net(tfile->net);
|
2009-01-20 18:00:40 +07:00
|
|
|
kfree(tfile);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-02-12 15:55:34 +07:00
|
|
|
static const struct file_operations tun_fops = {
|
2006-09-14 00:24:59 +07:00
|
|
|
.owner = THIS_MODULE,
|
2005-04-17 05:20:36 +07:00
|
|
|
.llseek = no_llseek,
|
2006-10-01 13:28:47 +07:00
|
|
|
.read = do_sync_read,
|
|
|
|
.aio_read = tun_chr_aio_read,
|
|
|
|
.write = do_sync_write,
|
|
|
|
.aio_write = tun_chr_aio_write,
|
2005-04-17 05:20:36 +07:00
|
|
|
.poll = tun_chr_poll,
|
2009-11-07 13:52:32 +07:00
|
|
|
.unlocked_ioctl = tun_chr_ioctl,
|
|
|
|
#ifdef CONFIG_COMPAT
|
|
|
|
.compat_ioctl = tun_chr_compat_ioctl,
|
|
|
|
#endif
|
2005-04-17 05:20:36 +07:00
|
|
|
.open = tun_chr_open,
|
|
|
|
.release = tun_chr_close,
|
2006-09-14 00:24:59 +07:00
|
|
|
.fasync = tun_chr_fasync
|
2005-04-17 05:20:36 +07:00
|
|
|
};
|
|
|
|
|
|
|
|
static struct miscdevice tun_miscdev = {
|
|
|
|
.minor = TUN_MINOR,
|
|
|
|
.name = "tun",
|
2009-09-19 04:01:12 +07:00
|
|
|
.nodename = "net/tun",
|
2005-04-17 05:20:36 +07:00
|
|
|
.fops = &tun_fops,
|
|
|
|
};
|
|
|
|
|
|
|
|
/* ethtool interface */
|
|
|
|
|
|
|
|
static int tun_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
|
|
|
|
{
|
|
|
|
cmd->supported = 0;
|
|
|
|
cmd->advertising = 0;
|
|
|
|
cmd->speed = SPEED_10;
|
|
|
|
cmd->duplex = DUPLEX_FULL;
|
|
|
|
cmd->port = PORT_TP;
|
|
|
|
cmd->phy_address = 0;
|
|
|
|
cmd->transceiver = XCVR_INTERNAL;
|
|
|
|
cmd->autoneg = AUTONEG_DISABLE;
|
|
|
|
cmd->maxtxpkt = 0;
|
|
|
|
cmd->maxrxpkt = 0;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void tun_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
|
|
|
|
{
|
|
|
|
struct tun_struct *tun = netdev_priv(dev);
|
|
|
|
|
|
|
|
strcpy(info->driver, DRV_NAME);
|
|
|
|
strcpy(info->version, DRV_VERSION);
|
|
|
|
strcpy(info->fw_version, "N/A");
|
|
|
|
|
|
|
|
switch (tun->flags & TUN_TYPE_MASK) {
|
|
|
|
case TUN_TUN_DEV:
|
|
|
|
strcpy(info->bus_info, "tun");
|
|
|
|
break;
|
|
|
|
case TUN_TAP_DEV:
|
|
|
|
strcpy(info->bus_info, "tap");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static u32 tun_get_msglevel(struct net_device *dev)
|
|
|
|
{
|
|
|
|
#ifdef TUN_DEBUG
|
|
|
|
struct tun_struct *tun = netdev_priv(dev);
|
|
|
|
return tun->debug;
|
|
|
|
#else
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
static void tun_set_msglevel(struct net_device *dev, u32 value)
|
|
|
|
{
|
|
|
|
#ifdef TUN_DEBUG
|
|
|
|
struct tun_struct *tun = netdev_priv(dev);
|
|
|
|
tun->debug = value;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
static u32 tun_get_rx_csum(struct net_device *dev)
|
|
|
|
{
|
|
|
|
struct tun_struct *tun = netdev_priv(dev);
|
|
|
|
return (tun->flags & TUN_NOCHECKSUM) == 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int tun_set_rx_csum(struct net_device *dev, u32 data)
|
|
|
|
{
|
|
|
|
struct tun_struct *tun = netdev_priv(dev);
|
|
|
|
if (data)
|
|
|
|
tun->flags &= ~TUN_NOCHECKSUM;
|
|
|
|
else
|
|
|
|
tun->flags |= TUN_NOCHECKSUM;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2006-09-14 01:30:00 +07:00
|
|
|
static const struct ethtool_ops tun_ethtool_ops = {
|
2005-04-17 05:20:36 +07:00
|
|
|
.get_settings = tun_get_settings,
|
|
|
|
.get_drvinfo = tun_get_drvinfo,
|
|
|
|
.get_msglevel = tun_get_msglevel,
|
|
|
|
.set_msglevel = tun_set_msglevel,
|
2010-07-27 20:53:43 +07:00
|
|
|
.get_link = ethtool_op_get_link,
|
2005-04-17 05:20:36 +07:00
|
|
|
.get_rx_csum = tun_get_rx_csum,
|
|
|
|
.set_rx_csum = tun_set_rx_csum
|
|
|
|
};
|
|
|
|
|
2008-04-16 14:40:46 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
static int __init tun_init(void)
|
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
printk(KERN_INFO "tun: %s, %s\n", DRV_DESCRIPTION, DRV_VERSION);
|
|
|
|
printk(KERN_INFO "tun: %s\n", DRV_COPYRIGHT);
|
|
|
|
|
2009-01-22 07:02:16 +07:00
|
|
|
ret = rtnl_link_register(&tun_link_ops);
|
2008-04-16 14:40:46 +07:00
|
|
|
if (ret) {
|
2009-01-22 07:02:16 +07:00
|
|
|
printk(KERN_ERR "tun: Can't register link_ops\n");
|
|
|
|
goto err_linkops;
|
2008-04-16 14:40:46 +07:00
|
|
|
}
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
ret = misc_register(&tun_miscdev);
|
2008-04-16 14:40:46 +07:00
|
|
|
if (ret) {
|
2005-04-17 05:20:36 +07:00
|
|
|
printk(KERN_ERR "tun: Can't register misc device %d\n", TUN_MINOR);
|
2008-04-16 14:40:46 +07:00
|
|
|
goto err_misc;
|
|
|
|
}
|
2009-01-22 07:02:16 +07:00
|
|
|
return 0;
|
2008-04-16 14:40:46 +07:00
|
|
|
err_misc:
|
2009-01-22 07:02:16 +07:00
|
|
|
rtnl_link_unregister(&tun_link_ops);
|
|
|
|
err_linkops:
|
2005-04-17 05:20:36 +07:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void tun_cleanup(void)
|
|
|
|
{
|
2006-09-14 00:24:59 +07:00
|
|
|
misc_deregister(&tun_miscdev);
|
2009-01-22 07:02:16 +07:00
|
|
|
rtnl_link_unregister(&tun_link_ops);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2010-01-14 13:17:09 +07:00
|
|
|
/* Get an underlying socket object from tun file. Returns error unless file is
|
|
|
|
* attached to a device. The returned object works like a packet socket, it
|
|
|
|
* can be used for sock_sendmsg/sock_recvmsg. The caller is responsible for
|
|
|
|
* holding a reference to the file for as long as the socket is in use. */
|
|
|
|
struct socket *tun_get_socket(struct file *file)
|
|
|
|
{
|
|
|
|
struct tun_struct *tun;
|
|
|
|
if (file->f_op != &tun_fops)
|
|
|
|
return ERR_PTR(-EINVAL);
|
|
|
|
tun = tun_get(file);
|
|
|
|
if (!tun)
|
|
|
|
return ERR_PTR(-EBADFD);
|
|
|
|
tun_put(tun);
|
|
|
|
return &tun->socket;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(tun_get_socket);
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
module_init(tun_init);
|
|
|
|
module_exit(tun_cleanup);
|
|
|
|
MODULE_DESCRIPTION(DRV_DESCRIPTION);
|
|
|
|
MODULE_AUTHOR(DRV_COPYRIGHT);
|
|
|
|
MODULE_LICENSE("GPL");
|
|
|
|
MODULE_ALIAS_MISCDEV(TUN_MINOR);
|
driver core: add devname module aliases to allow module on-demand auto-loading
This adds:
alias: devname:<name>
to some common kernel modules, which will allow the on-demand loading
of the kernel module when the device node is accessed.
Ideally all these modules would be compiled-in, but distros seems too
much in love with their modularization that we need to cover the common
cases with this new facility. It will allow us to remove a bunch of pretty
useless init scripts and modprobes from init scripts.
The static device node aliases will be carried in the module itself. The
program depmod will extract this information to a file in the module directory:
$ cat /lib/modules/2.6.34-00650-g537b60d-dirty/modules.devname
# Device nodes to trigger on-demand module loading.
microcode cpu/microcode c10:184
fuse fuse c10:229
ppp_generic ppp c108:0
tun net/tun c10:200
dm_mod mapper/control c10:235
Udev will pick up the depmod created file on startup and create all the
static device nodes which the kernel modules specify, so that these modules
get automatically loaded when the device node is accessed:
$ /sbin/udevd --debug
...
static_dev_create_from_modules: mknod '/dev/cpu/microcode' c10:184
static_dev_create_from_modules: mknod '/dev/fuse' c10:229
static_dev_create_from_modules: mknod '/dev/ppp' c108:0
static_dev_create_from_modules: mknod '/dev/net/tun' c10:200
static_dev_create_from_modules: mknod '/dev/mapper/control' c10:235
udev_rules_apply_static_dev_perms: chmod '/dev/net/tun' 0666
udev_rules_apply_static_dev_perms: chmod '/dev/fuse' 0666
A few device nodes are switched to statically allocated numbers, to allow
the static nodes to work. This might also useful for systems which still run
a plain static /dev, which is completely unsafe to use with any dynamic minor
numbers.
Note:
The devname aliases must be limited to the *common* and *single*instance*
device nodes, like the misc devices, and never be used for conceptually limited
systems like the loop devices, which should rather get fixed properly and get a
control node for losetup to talk to, instead of creating a random number of
device nodes in advance, regardless if they are ever used.
This facility is to hide the mess distros are creating with too modualized
kernels, and just to hide that these modules are not compiled-in, and not to
paper-over broken concepts. Thanks! :)
Cc: Greg Kroah-Hartman <gregkh@suse.de>
Cc: David S. Miller <davem@davemloft.net>
Cc: Miklos Szeredi <miklos@szeredi.hu>
Cc: Chris Mason <chris.mason@oracle.com>
Cc: Alasdair G Kergon <agk@redhat.com>
Cc: Tigran Aivazian <tigran@aivazian.fsnet.co.uk>
Cc: Ian Kent <raven@themaw.net>
Signed-Off-By: Kay Sievers <kay.sievers@vrfy.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2010-05-20 23:07:20 +07:00
|
|
|
MODULE_ALIAS("devname:net/tun");
|