2005-04-17 02:20:36 +04: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 04:40:05 +04:00
* Mike Kershaw < dragorn @ kismetwireless . net > 2005 / 08 / 14
* Add TUNSETLINK ioctl to set the link encapsulation
*
2005-04-17 02:20:36 +04:00
* Mark Smith < markzzzsmith @ yahoo . com . au >
2008-07-15 09:18:19 +04:00
* Use random_ether_addr ( ) for tap MAC address .
2005-04-17 02:20:36 +04: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>
2008-05-20 21:16:24 +04:00
# include <linux/smp_lock.h>
2005-04-17 02:20:36 +04:00
# 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>
# 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 11:41:16 +04:00
# include <linux/nsproxy.h>
2008-07-03 14:48:02 +04:00
# include <linux/virtio_net.h>
2007-09-17 22:56:21 +04:00
# include <net/net_namespace.h>
2008-04-16 11:40:46 +04:00
# include <net/netns/generic.h>
2009-01-22 03:02:16 +03:00
# include <net/rtnetlink.h>
2005-04-17 02:20:36 +04:00
# include <asm/system.h>
# include <asm/uaccess.h>
2008-04-13 05:48:58 +04:00
/* Uncomment to enable debugging */
/* #define TUN_DEBUG 1 */
2005-04-17 02:20:36 +04:00
# ifdef TUN_DEBUG
static int debug ;
2008-04-13 05:48:58 +04:00
# define DBG if(tun->debug)printk
# define DBG1 if(debug==2)printk
# else
# define DBG( a... )
# define DBG1( a... )
# endif
2008-07-15 09:18:19 +04: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 14:00:40 +03:00
struct tun_file {
2009-01-20 14:07:17 +03:00
atomic_t count ;
2009-01-20 14:00:40 +03:00
struct tun_struct * tun ;
2009-01-20 14:01:48 +03:00
struct net * net ;
2009-01-20 14:03:21 +03:00
wait_queue_head_t read_wait ;
2009-01-20 14:00:40 +03:00
} ;
2008-04-13 05:48:58 +04:00
struct tun_struct {
2009-01-20 14:00:40 +03:00
struct tun_file * tfile ;
2008-07-15 09:18:19 +04:00
unsigned int flags ;
2008-04-13 05:48:58 +04:00
uid_t owner ;
gid_t group ;
struct sk_buff_head readq ;
struct net_device * dev ;
2008-07-15 09:18:19 +04:00
struct fasync_struct * fasync ;
2008-04-13 05:48:58 +04:00
2008-07-15 09:18:19 +04:00
struct tap_filter txflt ;
2008-04-13 05:48:58 +04:00
# ifdef TUN_DEBUG
int debug ;
2005-04-17 02:20:36 +04:00
# endif
2008-04-13 05:48:58 +04:00
} ;
2005-04-17 02:20:36 +04:00
2009-01-20 13:57:48 +03:00
static int tun_attach ( struct tun_struct * tun , struct file * file )
{
2009-01-20 14:00:40 +03:00
struct tun_file * tfile = file - > private_data ;
2009-01-20 13:57:48 +03:00
const struct cred * cred = current_cred ( ) ;
2009-01-20 14:02:28 +03:00
int err ;
2009-01-20 13:57:48 +03:00
ASSERT_RTNL ( ) ;
/* Check permissions */
if ( ( ( tun - > owner ! = - 1 & & cred - > euid ! = tun - > owner ) | |
2009-02-03 10:34:56 +03:00
( tun - > group ! = - 1 & & ! in_egroup_p ( tun - > group ) ) ) & &
2009-01-20 13:57:48 +03:00
! capable ( CAP_NET_ADMIN ) )
return - EPERM ;
2009-01-20 14:02:28 +03: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 14:00:40 +03:00
tfile - > tun = tun ;
tun - > tfile = tfile ;
2009-01-20 14:07:17 +03:00
dev_hold ( tun - > dev ) ;
atomic_inc ( & tfile - > count ) ;
2009-01-20 13:57:48 +03:00
2009-01-20 14:02:28 +03:00
out :
netif_tx_unlock_bh ( tun - > dev ) ;
return err ;
2009-01-20 13:57:48 +03:00
}
2009-01-20 14:00:40 +03:00
static void __tun_detach ( struct tun_struct * tun )
{
struct tun_file * tfile = tun - > tfile ;
/* Detach from net device */
2009-01-20 14:02:28 +03:00
netif_tx_lock_bh ( tun - > dev ) ;
2009-01-20 14:00:40 +03:00
tfile - > tun = NULL ;
tun - > tfile = NULL ;
2009-01-20 14:02:28 +03:00
netif_tx_unlock_bh ( tun - > dev ) ;
2009-01-20 14:00:40 +03:00
/* Drop read queue */
skb_queue_purge ( & tun - > readq ) ;
2009-01-20 14:07:17 +03: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 14:00:40 +03:00
}
static struct tun_struct * __tun_get ( struct tun_file * tfile )
{
2009-01-20 14:07:17 +03:00
struct tun_struct * tun = NULL ;
if ( atomic_inc_not_zero ( & tfile - > count ) )
tun = tfile - > tun ;
return tun ;
2009-01-20 14:00:40 +03: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 14:07:17 +03:00
struct tun_file * tfile = tun - > tfile ;
if ( atomic_dec_and_test ( & tfile - > count ) )
tun_detach ( tfile - > tun ) ;
2009-01-20 14:00:40 +03:00
}
2008-07-15 09:18:19 +04: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 ;
/* The rest is hashed */
memset ( filter - > mask , 0 , sizeof ( filter - > mask ) ) ;
for ( ; n < uf . count ; n + + )
addr_hash_set ( filter - > mask , addr [ n ] . u ) ;
/* 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 02:20:36 +04:00
/* Network device part of the driver */
2006-09-13 22:30:00 +04:00
static const struct ethtool_ops tun_ethtool_ops ;
2005-04-17 02:20:36 +04:00
2009-01-20 14:07:17 +03: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 ) {
wake_up_all ( & tfile - > read_wait ) ;
if ( atomic_dec_and_test ( & tfile - > count ) )
__tun_detach ( tun ) ;
}
}
2005-04-17 02:20:36 +04: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 */
static int tun_net_xmit ( struct sk_buff * skb , struct net_device * dev )
{
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 14:00:40 +03:00
if ( ! tun - > tfile )
2005-04-17 02:20:36 +04:00
goto drop ;
2008-07-15 09:18:19 +04: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 ;
2005-04-17 02:20:36 +04:00
if ( skb_queue_len ( & tun - > readq ) > = dev - > tx_queue_len ) {
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 04:41:50 +04:00
dev - > stats . tx_fifo_errors + + ;
2005-04-17 02:20:36 +04:00
} else {
/* Single queue mode.
* Driver handles dropping of all packets itself . */
goto drop ;
}
}
2008-07-15 09:18:19 +04:00
/* Enqueue packet */
2005-04-17 02:20:36 +04:00
skb_queue_tail ( & tun - > readq , skb ) ;
dev - > trans_start = jiffies ;
/* Notify and wake up reader process */
if ( tun - > flags & TUN_FASYNC )
kill_fasync ( & tun - > fasync , SIGIO , POLL_IN ) ;
2009-01-20 14:03:21 +03:00
wake_up_interruptible ( & tun - > tfile - > read_wait ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
drop :
2007-10-04 04:41:50 +04:00
dev - > stats . tx_dropped + + ;
2005-04-17 02:20:36 +04:00
kfree_skb ( skb ) ;
return 0 ;
}
2008-07-15 09:18:19 +04:00
static void tun_net_mclist ( struct net_device * dev )
2005-04-17 02:20:36 +04:00
{
2008-07-15 09:18:19 +04: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 .
*/
return ;
2005-04-17 02:20:36 +04:00
}
2007-09-16 23:21:38 +04: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 09:10:37 +03:00
static const struct net_device_ops tun_netdev_ops = {
2009-01-20 14:07:17 +03:00
. ndo_uninit = tun_net_uninit ,
2008-11-20 09:10:37 +03:00
. ndo_open = tun_net_open ,
. ndo_stop = tun_net_close ,
2008-11-21 07:14:53 +03:00
. ndo_start_xmit = tun_net_xmit ,
2008-11-20 09:10:37 +03:00
. ndo_change_mtu = tun_net_change_mtu ,
} ;
static const struct net_device_ops tap_netdev_ops = {
2009-01-20 14:07:17 +03:00
. ndo_uninit = tun_net_uninit ,
2008-11-20 09:10:37 +03:00
. ndo_open = tun_net_open ,
. ndo_stop = tun_net_close ,
2008-11-21 07:14:53 +03:00
. ndo_start_xmit = tun_net_xmit ,
2008-11-20 09:10:37 +03: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 02:20:36 +04:00
/* Initialize net device. */
static void tun_net_init ( struct net_device * dev )
{
struct tun_struct * tun = netdev_priv ( dev ) ;
2006-09-13 21:24:59 +04:00
2005-04-17 02:20:36 +04:00
switch ( tun - > flags & TUN_TYPE_MASK ) {
case TUN_TUN_DEV :
2008-11-20 09:10:37 +03:00
dev - > netdev_ops = & tun_netdev_ops ;
2005-04-17 02:20:36 +04:00
/* Point-to-Point TUN Device */
dev - > hard_header_len = 0 ;
dev - > addr_len = 0 ;
dev - > mtu = 1500 ;
/* Zero header length */
2006-09-13 21:24:59 +04:00
dev - > type = ARPHRD_NONE ;
2005-04-17 02:20:36 +04: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 05:23:28 +03:00
dev - > netdev_ops = & tap_netdev_ops ;
2005-04-17 02:20:36 +04:00
/* Ethernet TAP Device */
ether_setup ( dev ) ;
2007-04-26 12:00:55 +04:00
2008-07-15 09:18:19 +04:00
random_ether_addr ( dev - > dev_addr ) ;
2007-04-26 12:00:55 +04:00
2005-04-17 02:20:36 +04: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-13 21:24:59 +04:00
{
2009-01-20 14:03:21 +03:00
struct tun_file * tfile = file - > private_data ;
struct tun_struct * tun = __tun_get ( tfile ) ;
2005-04-17 02:20:36 +04:00
unsigned int mask = POLLOUT | POLLWRNORM ;
if ( ! tun )
2009-01-20 13:59:05 +03:00
return POLLERR ;
2005-04-17 02:20:36 +04:00
DBG ( KERN_INFO " %s: tun_chr_poll \n " , tun - > dev - > name ) ;
2009-01-20 14:03:21 +03:00
poll_wait ( file , & tfile - > read_wait , wait ) ;
2006-09-13 21:24:59 +04:00
2005-07-09 01:57:23 +04:00
if ( ! skb_queue_empty ( & tun - > readq ) )
2005-04-17 02:20:36 +04:00
mask | = POLLIN | POLLRDNORM ;
2009-01-20 14:07:17 +03:00
if ( tun - > dev - > reg_state ! = NETREG_REGISTERED )
mask = POLLERR ;
2009-01-20 14:00:40 +03:00
tun_put ( tun ) ;
2005-04-17 02:20:36 +04:00
return mask ;
}
2008-08-16 02:15:10 +04: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 ) . */
static struct sk_buff * tun_alloc_skb ( size_t prepad , size_t len , size_t linear ,
gfp_t gfp )
{
struct sk_buff * skb ;
unsigned int i ;
skb = alloc_skb ( prepad + len , gfp | __GFP_NOWARN ) ;
if ( skb ) {
skb_reserve ( skb , prepad ) ;
skb_put ( skb , len ) ;
return skb ;
}
/* Under a page? Don't bother with paged skb. */
if ( prepad + len < PAGE_SIZE )
return NULL ;
/* Start with a normal skb, and add pages. */
skb = alloc_skb ( prepad + linear , gfp ) ;
if ( ! skb )
return NULL ;
skb_reserve ( skb , prepad ) ;
skb_put ( skb , linear ) ;
len - = linear ;
for ( i = 0 ; i < MAX_SKB_FRAGS ; i + + ) {
skb_frag_t * f = & skb_shinfo ( skb ) - > frags [ i ] ;
f - > page = alloc_page ( gfp | __GFP_ZERO ) ;
if ( ! f - > page )
break ;
f - > page_offset = 0 ;
f - > size = PAGE_SIZE ;
skb - > data_len + = PAGE_SIZE ;
skb - > len + = PAGE_SIZE ;
skb - > truesize + = PAGE_SIZE ;
skb_shinfo ( skb ) - > nr_frags + + ;
if ( len < PAGE_SIZE ) {
len = 0 ;
break ;
}
len - = PAGE_SIZE ;
}
/* Too large, or alloc fail? */
if ( unlikely ( len ) ) {
kfree_skb ( skb ) ;
skb = NULL ;
}
return skb ;
}
2005-04-17 02:20:36 +04:00
/* Get packet from user space buffer */
static __inline__ ssize_t tun_get_user ( struct tun_struct * tun , struct iovec * iv , size_t count )
{
2009-02-01 11:45:17 +03:00
struct tun_pi pi = { 0 , cpu_to_be16 ( ETH_P_IP ) } ;
2005-04-17 02:20:36 +04:00
struct sk_buff * skb ;
size_t len = count , align = 0 ;
2008-07-03 14:48:02 +04:00
struct virtio_net_hdr gso = { 0 } ;
2005-04-17 02:20:36 +04:00
if ( ! ( tun - > flags & TUN_NO_PI ) ) {
if ( ( len - = sizeof ( pi ) ) > count )
return - EINVAL ;
if ( memcpy_fromiovec ( ( void * ) & pi , iv , sizeof ( pi ) ) )
return - EFAULT ;
}
2008-07-03 14:48:02 +04:00
if ( tun - > flags & TUN_VNET_HDR ) {
if ( ( len - = sizeof ( gso ) ) > count )
return - EINVAL ;
if ( memcpy_fromiovec ( ( void * ) & gso , iv , sizeof ( gso ) ) )
return - EFAULT ;
if ( gso . hdr_len > len )
return - EINVAL ;
}
2008-04-13 05:49:30 +04:00
if ( ( tun - > flags & TUN_TYPE_MASK ) = = TUN_TAP_DEV ) {
2005-04-17 02:20:36 +04:00
align = NET_IP_ALIGN ;
2008-04-13 05:49:30 +04:00
if ( unlikely ( len < ETH_HLEN ) )
return - EINVAL ;
}
2006-09-13 21:24:59 +04:00
2008-08-16 02:15:10 +04:00
if ( ! ( skb = tun_alloc_skb ( align , len , gso . hdr_len , GFP_KERNEL ) ) ) {
2007-10-04 04:41:50 +04:00
tun - > dev - > stats . rx_dropped + + ;
2005-04-17 02:20:36 +04:00
return - ENOMEM ;
}
2008-08-16 02:15:10 +04:00
if ( skb_copy_datagram_from_iovec ( skb , 0 , iv , len ) ) {
2007-10-04 04:41:50 +04:00
tun - > dev - > stats . rx_dropped + + ;
2006-03-12 05:49:13 +03:00
kfree_skb ( skb ) ;
2005-04-17 02:20:36 +04:00
return - EFAULT ;
2006-03-12 05:49:13 +03:00
}
2005-04-17 02:20:36 +04:00
2008-07-03 14:48:02 +04: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 02:20:36 +04:00
switch ( tun - > flags & TUN_TYPE_MASK ) {
case TUN_TUN_DEV :
2008-06-18 08:10:33 +04: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 01:30:44 +03:00
skb_reset_mac_header ( skb ) ;
2005-04-17 02:20:36 +04:00
skb - > protocol = pi . proto ;
2007-04-26 04:40:23 +04:00
skb - > dev = tun - > dev ;
2005-04-17 02:20:36 +04:00
break ;
case TUN_TAP_DEV :
skb - > protocol = eth_type_trans ( skb , tun - > dev ) ;
break ;
} ;
2008-07-03 14:48:02 +04: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 ;
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-13 21:24:59 +04:00
2005-04-17 02:20:36 +04:00
netif_rx_ni ( skb ) ;
2006-09-13 21:24:59 +04:00
2007-10-04 04:41:50 +04:00
tun - > dev - > stats . rx_packets + + ;
tun - > dev - > stats . rx_bytes + = len ;
2005-04-17 02:20:36 +04:00
return count ;
2006-09-13 21:24:59 +04:00
}
2005-04-17 02:20:36 +04:00
2006-10-01 10:28:47 +04:00
static ssize_t tun_chr_aio_write ( struct kiocb * iocb , const struct iovec * iv ,
unsigned long count , loff_t pos )
2005-04-17 02:20:36 +04:00
{
2009-01-20 14:00:40 +03:00
struct tun_struct * tun = tun_get ( iocb - > ki_filp ) ;
ssize_t result ;
2005-04-17 02:20:36 +04:00
if ( ! tun )
return - EBADFD ;
DBG ( KERN_INFO " %s: tun_chr_write %ld \n " , tun - > dev - > name , count ) ;
2009-01-20 14:00:40 +03:00
result = tun_get_user ( tun , ( struct iovec * ) iv , iov_length ( iv , count ) ) ;
tun_put ( tun ) ;
return result ;
2005-04-17 02:20:36 +04:00
}
/* Put packet to the user space buffer */
static __inline__ ssize_t tun_put_user ( struct tun_struct * tun ,
struct sk_buff * skb ,
struct iovec * iv , int len )
{
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-13 21:24:59 +04:00
2005-04-17 02:20:36 +04:00
if ( memcpy_toiovec ( iv , ( void * ) & pi , sizeof ( pi ) ) )
return - EFAULT ;
total + = sizeof ( pi ) ;
2006-09-13 21:24:59 +04:00
}
2005-04-17 02:20:36 +04:00
2008-07-03 14:48:02 +04:00
if ( tun - > flags & TUN_VNET_HDR ) {
struct virtio_net_hdr gso = { 0 } ; /* no info leak */
if ( ( len - = sizeof ( gso ) ) < 0 )
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 ;
else
BUG ( ) ;
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 */
if ( unlikely ( memcpy_toiovec ( iv , ( void * ) & gso , sizeof ( gso ) ) ) )
return - EFAULT ;
total + = sizeof ( gso ) ;
}
2005-04-17 02:20:36 +04:00
len = min_t ( int , skb - > len , len ) ;
skb_copy_datagram_iovec ( skb , 0 , iv , len ) ;
total + = len ;
2007-10-04 04:41:50 +04:00
tun - > dev - > stats . tx_packets + + ;
tun - > dev - > stats . tx_bytes + = len ;
2005-04-17 02:20:36 +04:00
return total ;
}
2006-10-01 10:28:47 +04:00
static ssize_t tun_chr_aio_read ( struct kiocb * iocb , const struct iovec * iv ,
unsigned long count , loff_t pos )
2005-04-17 02:20:36 +04:00
{
2006-10-01 10:28:47 +04:00
struct file * file = iocb - > ki_filp ;
2009-01-20 14:03:21 +03:00
struct tun_file * tfile = file - > private_data ;
struct tun_struct * tun = __tun_get ( tfile ) ;
2005-04-17 02:20:36 +04:00
DECLARE_WAITQUEUE ( wait , current ) ;
struct sk_buff * skb ;
ssize_t len , ret = 0 ;
if ( ! tun )
return - EBADFD ;
DBG ( KERN_INFO " %s: tun_chr_read \n " , tun - > dev - > name ) ;
2007-11-20 09:46:51 +03:00
len = iov_length ( iv , count ) ;
2009-01-20 14:00:40 +03:00
if ( len < 0 ) {
ret = - EINVAL ;
goto out ;
}
2005-04-17 02:20:36 +04:00
2009-01-20 14:03:21 +03:00
add_wait_queue ( & tfile - > read_wait , & wait ) ;
2005-04-17 02:20:36 +04:00
while ( len ) {
current - > state = TASK_INTERRUPTIBLE ;
/* Read frames from the queue */
if ( ! ( skb = skb_dequeue ( & tun - > readq ) ) ) {
if ( file - > f_flags & O_NONBLOCK ) {
ret = - EAGAIN ;
break ;
}
if ( signal_pending ( current ) ) {
ret = - ERESTARTSYS ;
break ;
}
2009-01-20 14:07:17 +03:00
if ( tun - > dev - > reg_state ! = NETREG_REGISTERED ) {
ret = - EIO ;
break ;
}
2005-04-17 02:20:36 +04:00
/* Nothing to read, let's sleep */
schedule ( ) ;
continue ;
}
netif_wake_queue ( tun - > dev ) ;
2008-07-15 09:18:19 +04:00
ret = tun_put_user ( tun , skb , ( struct iovec * ) iv , len ) ;
kfree_skb ( skb ) ;
break ;
2005-04-17 02:20:36 +04:00
}
current - > state = TASK_RUNNING ;
2009-01-20 14:03:21 +03:00
remove_wait_queue ( & tfile - > read_wait , & wait ) ;
2005-04-17 02:20:36 +04:00
2009-01-20 14:00:40 +03:00
out :
tun_put ( tun ) ;
2005-04-17 02:20:36 +04:00
return ret ;
}
static void tun_setup ( struct net_device * dev )
{
struct tun_struct * tun = netdev_priv ( dev ) ;
skb_queue_head_init ( & tun - > readq ) ;
tun - > owner = - 1 ;
2007-07-03 09:50:25 +04:00
tun - > group = - 1 ;
2005-04-17 02:20:36 +04:00
dev - > ethtool_ops = & tun_ethtool_ops ;
dev - > destructor = free_netdev ;
}
2009-01-22 03:02:16 +03: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 ,
} ;
2008-04-16 11:41:16 +04:00
static int tun_set_iff ( struct net * net , struct file * file , struct ifreq * ifr )
2005-04-17 02:20:36 +04:00
{
struct tun_struct * tun ;
struct net_device * dev ;
int err ;
2009-01-20 13:56:20 +03:00
dev = __dev_get_by_name ( net , ifr - > ifr_name ) ;
if ( dev ) {
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-01-20 13:57:48 +03:00
err = tun_attach ( tun , file ) ;
if ( err < 0 )
return err ;
2006-09-13 21:24:59 +04:00
}
2005-04-17 02:20:36 +04:00
else {
char * name ;
unsigned long flags = 0 ;
err = - EINVAL ;
2006-06-23 03:07:52 +04:00
if ( ! capable ( CAP_NET_ADMIN ) )
return - EPERM ;
2005-04-17 02:20:36 +04: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-13 21:24:59 +04:00
} else
2005-04-17 02:20:36 +04:00
goto failed ;
2006-09-13 21:24:59 +04:00
2005-04-17 02:20:36 +04: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 11:41:53 +04:00
dev_net_set ( dev , net ) ;
2009-01-22 03:02:16 +03:00
dev - > rtnl_link_ops = & tun_link_ops ;
2008-11-20 09:10:37 +03:00
2005-04-17 02:20:36 +04:00
tun = netdev_priv ( dev ) ;
tun - > dev = dev ;
tun - > flags = flags ;
2008-07-15 09:18:19 +04:00
tun - > txflt . count = 0 ;
2005-04-17 02:20:36 +04:00
tun_net_init ( dev ) ;
if ( strchr ( dev - > name , ' % ' ) ) {
err = dev_alloc_name ( dev , dev - > name ) ;
if ( err < 0 )
goto err_free_dev ;
}
err = register_netdevice ( tun - > dev ) ;
if ( err < 0 )
goto err_free_dev ;
2009-01-20 13:57:48 +03:00
err = tun_attach ( tun , file ) ;
if ( err < 0 )
goto err_free_dev ;
2005-04-17 02:20:36 +04: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 14:05:07 +03:00
else
tun - > flags & = ~ TUN_NO_PI ;
2005-04-17 02:20:36 +04:00
if ( ifr - > ifr_flags & IFF_ONE_QUEUE )
tun - > flags | = TUN_ONE_QUEUE ;
2008-02-05 14:05:07 +03:00
else
tun - > flags & = ~ TUN_ONE_QUEUE ;
2005-04-17 02:20:36 +04:00
2008-07-03 14:48:02 +04:00
if ( ifr - > ifr_flags & IFF_VNET_HDR )
tun - > flags | = TUN_VNET_HDR ;
else
tun - > flags & = ~ TUN_VNET_HDR ;
2008-07-11 03:59:11 +04: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 02:20:36 +04:00
strcpy ( ifr - > ifr_name , tun - > dev - > name ) ;
return 0 ;
err_free_dev :
free_netdev ( dev ) ;
failed :
return err ;
}
2008-08-16 02:09:56 +04:00
static int tun_get_iff ( struct net * net , struct file * file , struct ifreq * ifr )
{
2009-01-20 14:00:40 +03:00
struct tun_struct * tun = tun_get ( file ) ;
2008-08-16 02:09:56 +04:00
if ( ! tun )
return - EBADFD ;
DBG ( KERN_INFO " %s: tun_get_iff \n " , tun - > dev - > name ) ;
strcpy ( ifr - > ifr_name , tun - > dev - > name ) ;
ifr - > ifr_flags = 0 ;
if ( ifr - > ifr_flags & TUN_TUN_DEV )
ifr - > ifr_flags | = IFF_TUN ;
else
ifr - > ifr_flags | = IFF_TAP ;
if ( tun - > flags & TUN_NO_PI )
ifr - > ifr_flags | = IFF_NO_PI ;
if ( tun - > flags & TUN_ONE_QUEUE )
ifr - > ifr_flags | = IFF_ONE_QUEUE ;
if ( tun - > flags & TUN_VNET_HDR )
ifr - > ifr_flags | = IFF_VNET_HDR ;
2009-01-20 14:00:40 +03:00
tun_put ( tun ) ;
2008-08-16 02:09:56 +04:00
return 0 ;
}
2008-07-03 14:46:16 +04: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
| NETIF_F_TSO_ECN | NETIF_F_TSO | NETIF_F_TSO6 ) ) ;
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 ) ;
}
}
/* 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 ;
}
2006-09-13 21:24:59 +04:00
static int tun_chr_ioctl ( struct inode * inode , struct file * file ,
2005-04-17 02:20:36 +04:00
unsigned int cmd , unsigned long arg )
{
2009-01-20 14:01:48 +03:00
struct tun_file * tfile = file - > private_data ;
2009-01-20 14:00:40 +03:00
struct tun_struct * tun ;
2005-04-17 02:20:36 +04:00
void __user * argp = ( void __user * ) arg ;
struct ifreq ifr ;
2008-07-15 09:18:19 +04:00
int ret ;
2005-04-17 02:20:36 +04:00
if ( cmd = = TUNSETIFF | | _IOC_TYPE ( cmd ) = = 0x89 )
if ( copy_from_user ( & ifr , argp , sizeof ifr ) )
return - EFAULT ;
2009-01-20 14:00:40 +03: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-01-20 14:01:48 +03:00
tun = __tun_get ( tfile ) ;
2005-04-17 02:20:36 +04:00
if ( cmd = = TUNSETIFF & & ! tun ) {
int err ;
ifr . ifr_name [ IFNAMSIZ - 1 ] = ' \0 ' ;
rtnl_lock ( ) ;
2009-01-20 14:01:48 +03:00
err = tun_set_iff ( tfile - > net , file , & ifr ) ;
2005-04-17 02:20:36 +04:00
rtnl_unlock ( ) ;
if ( err )
return err ;
if ( copy_to_user ( argp , & ifr , sizeof ( ifr ) ) )
return - EFAULT ;
return 0 ;
}
tun: Interface to query tun/tap features.
The problem with introducing checksum offload and gso to tun is they
need to set dev->features to enable GSO and/or checksumming, which is
supposed to be done before register_netdevice(), ie. as part of
TUNSETIFF.
Unfortunately, TUNSETIFF has always just ignored flags it doesn't
understand, so there's no good way of detecting whether the kernel
supports new IFF_ flags.
This patch implements a TUNGETFEATURES ioctl which returns all the valid IFF
flags. It could be extended later to include other features.
Here's an example program which uses it:
#include <linux/if_tun.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <err.h>
#include <stdio.h>
static struct {
unsigned int flag;
const char *name;
} known_flags[] = {
{ IFF_TUN, "TUN" },
{ IFF_TAP, "TAP" },
{ IFF_NO_PI, "NO_PI" },
{ IFF_ONE_QUEUE, "ONE_QUEUE" },
};
int main()
{
unsigned int features, i;
int netfd = open("/dev/net/tun", O_RDWR);
if (netfd < 0)
err(1, "Opening /dev/net/tun");
if (ioctl(netfd, TUNGETFEATURES, &features) != 0) {
printf("Kernel does not support TUNGETFEATURES, guessing\n");
features = (IFF_TUN|IFF_TAP|IFF_NO_PI|IFF_ONE_QUEUE);
}
printf("Available features are: ");
for (i = 0; i < sizeof(known_flags)/sizeof(known_flags[0]); i++) {
if (features & known_flags[i].flag) {
features &= ~known_flags[i].flag;
printf("%s ", known_flags[i].name);
}
}
if (features)
printf("(UNKNOWN %#x)", features);
printf("\n");
return 0;
}
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Acked-by: Max Krasnyansky <maxk@qualcomm.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2008-07-03 14:45:32 +04:00
2005-04-17 02:20:36 +04:00
if ( ! tun )
return - EBADFD ;
DBG ( KERN_INFO " %s: tun_chr_ioctl cmd %d \n " , tun - > dev - > name , cmd ) ;
2009-01-20 14:00:40 +03:00
ret = 0 ;
2005-04-17 02:20:36 +04:00
switch ( cmd ) {
2008-08-16 02:09:56 +04:00
case TUNGETIFF :
ret = tun_get_iff ( current - > nsproxy - > net_ns , file , & ifr ) ;
if ( ret )
2009-01-20 14:00:40 +03:00
break ;
2008-08-16 02:09:56 +04:00
if ( copy_to_user ( argp , & ifr , sizeof ( ifr ) ) )
2009-01-20 14:00:40 +03:00
ret = - EFAULT ;
2008-08-16 02:09:56 +04:00
break ;
2005-04-17 02:20:36 +04: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 08:29:35 +03:00
tun - > dev - > name , arg ? " enabled " : " disabled " ) ;
2005-04-17 02:20:36 +04: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 09:50:25 +04: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 04:40:05 +04:00
case TUNSETLINK :
/* Only allow setting the type when the interface is down */
2008-04-24 06:37:58 +04:00
rtnl_lock ( ) ;
2005-09-02 04:40:05 +04:00
if ( tun - > dev - > flags & IFF_UP ) {
DBG ( KERN_INFO " %s: Linktype set failed because interface is up \n " ,
tun - > dev - > name ) ;
2008-04-24 06:37:58 +04:00
ret = - EBUSY ;
2005-09-02 04:40:05 +04: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 06:37:58 +04:00
ret = 0 ;
2005-09-02 04:40:05 +04:00
}
2008-04-24 06:37:58 +04:00
rtnl_unlock ( ) ;
2009-01-20 14:00:40 +03:00
break ;
2005-09-02 04:40:05 +04:00
2005-04-17 02:20:36 +04:00
# ifdef TUN_DEBUG
case TUNSETDEBUG :
tun - > debug = arg ;
break ;
# endif
2008-07-03 14:46:16 +04:00
case TUNSETOFFLOAD :
rtnl_lock ( ) ;
ret = set_offload ( tun - > dev , arg ) ;
rtnl_unlock ( ) ;
2009-01-20 14:00:40 +03:00
break ;
2008-07-03 14:46:16 +04:00
2008-07-15 09:18:19 +04:00
case TUNSETTXFILTER :
/* Can be set only for TAPs */
2009-01-20 14:00:40 +03:00
ret = - EINVAL ;
2008-07-15 09:18:19 +04:00
if ( ( tun - > flags & TUN_TYPE_MASK ) ! = TUN_TAP_DEV )
2009-01-20 14:00:40 +03:00
break ;
2008-07-15 09:18:19 +04:00
rtnl_lock ( ) ;
2008-07-16 23:45:34 +04:00
ret = update_filter ( & tun - > txflt , ( void __user * ) arg ) ;
2008-07-15 09:18:19 +04:00
rtnl_unlock ( ) ;
2009-01-20 14:00:40 +03:00
break ;
2005-04-17 02:20:36 +04:00
case SIOCGIFHWADDR :
2008-07-15 09:18:19 +04:00
/* Get hw addres */
memcpy ( ifr . ifr_hwaddr . sa_data , tun - > dev - > dev_addr , ETH_ALEN ) ;
ifr . ifr_hwaddr . sa_family = tun - > dev - > type ;
if ( copy_to_user ( argp , & ifr , sizeof ifr ) )
2009-01-20 14:00:40 +03:00
ret = - EFAULT ;
break ;
2005-04-17 02:20:36 +04:00
case SIOCSIFHWADDR :
2008-07-15 09:18:19 +04:00
/* Set hw address */
2008-10-28 01:59:26 +03:00
DBG ( KERN_DEBUG " %s: set hw address: %pM \n " ,
tun - > dev - > name , ifr . ifr_hwaddr . sa_data ) ;
2008-02-29 23:26:21 +03:00
rtnl_lock ( ) ;
ret = dev_set_mac_address ( tun - > dev , & ifr . ifr_hwaddr ) ;
rtnl_unlock ( ) ;
2009-01-20 14:00:40 +03:00
break ;
2005-04-17 02:20:36 +04:00
default :
2009-01-20 14:00:40 +03:00
ret = - EINVAL ;
break ;
2005-04-17 02:20:36 +04:00
} ;
2009-01-20 14:00:40 +03:00
tun_put ( tun ) ;
return ret ;
2005-04-17 02:20:36 +04:00
}
static int tun_chr_fasync ( int fd , struct file * file , int on )
{
2009-01-20 14:00:40 +03:00
struct tun_struct * tun = tun_get ( file ) ;
2005-04-17 02:20:36 +04:00
int ret ;
if ( ! tun )
return - EBADFD ;
DBG ( KERN_INFO " %s: tun_chr_fasync %d \n " , tun - > dev - > name , on ) ;
2008-06-20 01:50:37 +04:00
lock_kernel ( ) ;
2005-04-17 02:20:36 +04:00
if ( ( ret = fasync_helper ( fd , file , on , & tun - > fasync ) ) < 0 )
2008-06-20 01:50:37 +04:00
goto out ;
2006-09-13 21:24:59 +04:00
2005-04-17 02:20:36 +04:00
if ( on ) {
2006-10-02 13:17:15 +04:00
ret = __f_setown ( file , task_pid ( current ) , PIDTYPE_PID , 0 ) ;
2005-04-17 02:20:36 +04:00
if ( ret )
2008-06-20 01:50:37 +04:00
goto out ;
2005-04-17 02:20:36 +04:00
tun - > flags | = TUN_FASYNC ;
2006-09-13 21:24:59 +04:00
} else
2005-04-17 02:20:36 +04:00
tun - > flags & = ~ TUN_FASYNC ;
2008-06-20 01:50:37 +04:00
ret = 0 ;
out :
unlock_kernel ( ) ;
2009-01-20 14:00:40 +03:00
tun_put ( tun ) ;
2008-06-20 01:50:37 +04:00
return ret ;
2005-04-17 02:20:36 +04:00
}
static int tun_chr_open ( struct inode * inode , struct file * file )
{
2009-01-20 14:00:40 +03:00
struct tun_file * tfile ;
2008-05-20 21:16:24 +04:00
cycle_kernel_lock ( ) ;
2005-04-17 02:20:36 +04:00
DBG1 ( KERN_INFO " tunX: tun_chr_open \n " ) ;
2009-01-20 14:00:40 +03:00
tfile = kmalloc ( sizeof ( * tfile ) , GFP_KERNEL ) ;
if ( ! tfile )
return - ENOMEM ;
2009-01-20 14:07:17 +03:00
atomic_set ( & tfile - > count , 0 ) ;
2009-01-20 14:00:40 +03:00
tfile - > tun = NULL ;
2009-01-20 14:01:48 +03:00
tfile - > net = get_net ( current - > nsproxy - > net_ns ) ;
2009-01-20 14:03:21 +03:00
init_waitqueue_head ( & tfile - > read_wait ) ;
2009-01-20 14:00:40 +03:00
file - > private_data = tfile ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
static int tun_chr_close ( struct inode * inode , struct file * file )
{
2009-01-20 14:00:40 +03:00
struct tun_file * tfile = file - > private_data ;
struct tun_struct * tun = __tun_get ( tfile ) ;
2005-04-17 02:20:36 +04:00
2009-01-20 14:00:40 +03:00
if ( tun ) {
DBG ( KERN_INFO " %s: tun_chr_close \n " , tun - > dev - > name ) ;
2005-04-17 02:20:36 +04:00
2009-01-20 14:00:40 +03:00
rtnl_lock ( ) ;
__tun_detach ( tun ) ;
2005-04-17 02:20:36 +04:00
2009-01-20 14:00:40 +03:00
/* If desireable, unregister the netdevice. */
if ( ! ( tun - > flags & TUN_PERSIST ) )
unregister_netdevice ( tun - > dev ) ;
2005-04-17 02:20:36 +04:00
2009-01-20 14:00:40 +03:00
rtnl_unlock ( ) ;
}
2005-04-17 02:20:36 +04:00
2009-01-20 14:01:48 +03:00
put_net ( tfile - > net ) ;
2009-01-20 14:00:40 +03:00
kfree ( tfile ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2007-02-12 11:55:34 +03:00
static const struct file_operations tun_fops = {
2006-09-13 21:24:59 +04:00
. owner = THIS_MODULE ,
2005-04-17 02:20:36 +04:00
. llseek = no_llseek ,
2006-10-01 10:28:47 +04:00
. read = do_sync_read ,
. aio_read = tun_chr_aio_read ,
. write = do_sync_write ,
. aio_write = tun_chr_aio_write ,
2005-04-17 02:20:36 +04:00
. poll = tun_chr_poll ,
. ioctl = tun_chr_ioctl ,
. open = tun_chr_open ,
. release = tun_chr_close ,
2006-09-13 21:24:59 +04:00
. fasync = tun_chr_fasync
2005-04-17 02:20:36 +04:00
} ;
static struct miscdevice tun_miscdev = {
. minor = TUN_MINOR ,
. name = " tun " ,
. 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_link ( struct net_device * dev )
{
struct tun_struct * tun = netdev_priv ( dev ) ;
2009-01-20 14:00:40 +03:00
return ! ! tun - > tfile ;
2005-04-17 02:20:36 +04:00
}
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-13 22:30:00 +04:00
static const struct ethtool_ops tun_ethtool_ops = {
2005-04-17 02:20:36 +04:00
. get_settings = tun_get_settings ,
. get_drvinfo = tun_get_drvinfo ,
. get_msglevel = tun_get_msglevel ,
. set_msglevel = tun_set_msglevel ,
. get_link = tun_get_link ,
. get_rx_csum = tun_get_rx_csum ,
. set_rx_csum = tun_set_rx_csum
} ;
2008-04-16 11:40:46 +04:00
2005-04-17 02:20:36 +04: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 03:02:16 +03:00
ret = rtnl_link_register ( & tun_link_ops ) ;
2008-04-16 11:40:46 +04:00
if ( ret ) {
2009-01-22 03:02:16 +03:00
printk ( KERN_ERR " tun: Can't register link_ops \n " ) ;
goto err_linkops ;
2008-04-16 11:40:46 +04:00
}
2005-04-17 02:20:36 +04:00
ret = misc_register ( & tun_miscdev ) ;
2008-04-16 11:40:46 +04:00
if ( ret ) {
2005-04-17 02:20:36 +04:00
printk ( KERN_ERR " tun: Can't register misc device %d \n " , TUN_MINOR ) ;
2008-04-16 11:40:46 +04:00
goto err_misc ;
}
2009-01-22 03:02:16 +03:00
return 0 ;
2008-04-16 11:40:46 +04:00
err_misc :
2009-01-22 03:02:16 +03:00
rtnl_link_unregister ( & tun_link_ops ) ;
err_linkops :
2005-04-17 02:20:36 +04:00
return ret ;
}
static void tun_cleanup ( void )
{
2006-09-13 21:24:59 +04:00
misc_deregister ( & tun_miscdev ) ;
2009-01-22 03:02:16 +03:00
rtnl_link_unregister ( & tun_link_ops ) ;
2005-04-17 02:20:36 +04: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 ) ;