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>
|
2012-07-12 23:33:09 +04:00
|
|
|
* Use eth_random_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.
|
|
|
|
*/
|
|
|
|
|
2011-03-02 10:18:10 +03:00
|
|
|
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
#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 09:52:32 +03:00
|
|
|
#include <linux/compat.h>
|
2005-04-17 02:20:36 +04:00
|
|
|
#include <linux/if.h>
|
|
|
|
#include <linux/if_arp.h>
|
|
|
|
#include <linux/if_ether.h>
|
|
|
|
#include <linux/if_tun.h>
|
2013-07-25 09:00:33 +04:00
|
|
|
#include <linux/if_vlan.h>
|
2005-04-17 02:20:36 +04:00
|
|
|
#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>
|
2010-02-14 04:01:10 +03:00
|
|
|
#include <linux/rcupdate.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>
|
2009-02-06 08:25:32 +03:00
|
|
|
#include <net/sock.h>
|
2014-01-29 11:43:31 +04:00
|
|
|
#include <linux/seq_file.h>
|
2014-11-07 16:22:23 +03:00
|
|
|
#include <linux/uio.h>
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
#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
|
|
|
|
2011-03-02 10:18:10 +03:00
|
|
|
#define tun_debug(level, tun, fmt, args...) \
|
|
|
|
do { \
|
|
|
|
if (tun->debug) \
|
|
|
|
netdev_printk(level, tun->dev, fmt, ##args); \
|
|
|
|
} while (0)
|
|
|
|
#define DBG1(level, fmt, args...) \
|
|
|
|
do { \
|
|
|
|
if (debug == 2) \
|
|
|
|
printk(level fmt, ##args); \
|
|
|
|
} while (0)
|
2008-04-13 05:48:58 +04:00
|
|
|
#else
|
2011-03-02 10:18:10 +03:00
|
|
|
#define tun_debug(level, tun, fmt, args...) \
|
|
|
|
do { \
|
|
|
|
if (0) \
|
|
|
|
netdev_printk(level, tun->dev, fmt, ##args); \
|
|
|
|
} while (0)
|
|
|
|
#define DBG1(level, fmt, args...) \
|
|
|
|
do { \
|
|
|
|
if (0) \
|
|
|
|
printk(level fmt, ##args); \
|
|
|
|
} while (0)
|
2008-04-13 05:48:58 +04:00
|
|
|
#endif
|
|
|
|
|
2014-11-19 15:44:40 +03:00
|
|
|
/* TUN device flags */
|
|
|
|
|
|
|
|
/* IFF_ATTACH_QUEUE is never stored in device flags,
|
|
|
|
* overload it to mean fasync when stored there.
|
|
|
|
*/
|
|
|
|
#define TUN_FASYNC IFF_ATTACH_QUEUE
|
2014-12-16 16:05:06 +03:00
|
|
|
/* High bits in flags field are unused. */
|
|
|
|
#define TUN_VNET_LE 0x80000000
|
2015-04-24 15:50:36 +03:00
|
|
|
#define TUN_VNET_BE 0x40000000
|
2014-11-19 15:44:40 +03:00
|
|
|
|
|
|
|
#define TUN_FEATURES (IFF_NO_PI | IFF_ONE_QUEUE | IFF_VNET_HDR | \
|
2014-12-16 16:05:06 +03:00
|
|
|
IFF_MULTI_QUEUE)
|
2012-07-20 13:23:23 +04:00
|
|
|
#define GOODCOPY_LEN 128
|
|
|
|
|
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];
|
|
|
|
};
|
|
|
|
|
2015-01-12 09:11:29 +03:00
|
|
|
/* MAX_TAP_QUEUES 256 is chosen to allow rx/tx queues to be equal
|
|
|
|
* to max number of VCPUs in guest. */
|
|
|
|
#define MAX_TAP_QUEUES 256
|
2013-01-23 07:59:13 +04:00
|
|
|
#define MAX_TAP_FLOWS 4096
|
2012-10-31 23:46:00 +04:00
|
|
|
|
2012-10-31 23:46:02 +04:00
|
|
|
#define TUN_FLOW_EXPIRE (3 * HZ)
|
|
|
|
|
2016-04-13 11:52:20 +03:00
|
|
|
struct tun_pcpu_stats {
|
|
|
|
u64 rx_packets;
|
|
|
|
u64 rx_bytes;
|
|
|
|
u64 tx_packets;
|
|
|
|
u64 tx_bytes;
|
|
|
|
struct u64_stats_sync syncp;
|
|
|
|
u32 rx_dropped;
|
|
|
|
u32 tx_dropped;
|
|
|
|
u32 rx_frame_errors;
|
|
|
|
};
|
|
|
|
|
2012-10-31 23:45:57 +04:00
|
|
|
/* A tun_file connects an open character device to a tuntap netdevice. It
|
2013-12-06 08:42:58 +04:00
|
|
|
* also contains all socket related structures (except sock_fprog and tap_filter)
|
2012-10-31 23:45:57 +04:00
|
|
|
* to serve as one transmit queue for tuntap device. The sock_fprog and
|
|
|
|
* tap_filter were kept in tun_struct since they were used for filtering for the
|
2012-11-26 02:07:40 +04:00
|
|
|
* netdevice not for a specific queue (at least I didn't see the requirement for
|
2012-10-31 23:45:57 +04:00
|
|
|
* this).
|
2012-10-31 23:45:58 +04:00
|
|
|
*
|
|
|
|
* RCU usage:
|
2012-11-26 02:07:40 +04:00
|
|
|
* The tun_file and tun_struct are loosely coupled, the pointer from one to the
|
2012-10-31 23:45:58 +04:00
|
|
|
* other can only be read while rcu_read_lock or rtnl_lock is held.
|
2012-10-31 23:45:57 +04:00
|
|
|
*/
|
2009-01-20 14:00:40 +03:00
|
|
|
struct tun_file {
|
2012-10-31 23:45:57 +04:00
|
|
|
struct sock sk;
|
|
|
|
struct socket socket;
|
|
|
|
struct socket_wq wq;
|
2012-10-31 23:45:58 +04:00
|
|
|
struct tun_struct __rcu *tun;
|
2012-10-31 23:45:57 +04:00
|
|
|
struct fasync_struct *fasync;
|
|
|
|
/* only used for fasnyc */
|
|
|
|
unsigned int flags;
|
tun: Add ability to create tun device with given index
Tun devices cannot be created with ifidex user wants, but it's
required by checkpoint-restore project.
Long time ago such ability was implemented for rtnl_ops-based
interface for creating links (9c7dafbf net: Allow to create links
with given ifindex), but the only API for creating and managing
tuntap devices is ioctl-based and is evolving with adding new ones
(cde8b15f tuntap: add ioctl to attach or detach a file form tuntap
device).
Following that trend, here's how a new ioctl that sets the ifindex
for device, that _will_ be created by TUNSETIFF ioctl looks like.
So those who want a tuntap device with the ifindex N, should open
the tun device, call ioctl(fd, TUNSETIFINDEX, &N), then call TUNSETIFF.
If the index N is busy, then the register_netdev will find this out
and the ioctl would be failed with -EBUSY.
If setifindex is not called, then it will be generated as before.
Signed-off-by: Pavel Emelyanov <xemul@parallels.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2013-08-21 14:31:38 +04:00
|
|
|
union {
|
|
|
|
u16 queue_index;
|
|
|
|
unsigned int ifindex;
|
|
|
|
};
|
2012-12-14 03:53:30 +04:00
|
|
|
struct list_head next;
|
|
|
|
struct tun_struct *detached;
|
2009-01-20 14:00:40 +03:00
|
|
|
};
|
|
|
|
|
2012-10-31 23:46:02 +04:00
|
|
|
struct tun_flow_entry {
|
|
|
|
struct hlist_node hash_link;
|
|
|
|
struct rcu_head rcu;
|
|
|
|
struct tun_struct *tun;
|
|
|
|
|
|
|
|
u32 rxhash;
|
2013-12-22 14:54:32 +04:00
|
|
|
u32 rps_rxhash;
|
2012-10-31 23:46:02 +04:00
|
|
|
int queue_index;
|
|
|
|
unsigned long updated;
|
|
|
|
};
|
|
|
|
|
|
|
|
#define TUN_NUM_FLOW_ENTRIES 1024
|
|
|
|
|
2012-10-31 23:45:57 +04:00
|
|
|
/* Since the socket were moved to tun_file, to preserve the behavior of persist
|
2012-11-26 02:07:40 +04:00
|
|
|
* device, socket filter, sndbuf and vnet header size were restore when the
|
2012-10-31 23:45:57 +04:00
|
|
|
* file were attached to a persist device.
|
|
|
|
*/
|
2008-04-13 05:48:58 +04:00
|
|
|
struct tun_struct {
|
2012-10-31 23:46:00 +04:00
|
|
|
struct tun_file __rcu *tfiles[MAX_TAP_QUEUES];
|
|
|
|
unsigned int numqueues;
|
2008-07-15 09:18:19 +04:00
|
|
|
unsigned int flags;
|
2012-02-08 04:48:55 +04:00
|
|
|
kuid_t owner;
|
|
|
|
kgid_t group;
|
2008-04-13 05:48:58 +04:00
|
|
|
|
|
|
|
struct net_device *dev;
|
2011-11-15 19:29:55 +04:00
|
|
|
netdev_features_t set_features;
|
2011-04-19 10:13:10 +04:00
|
|
|
#define TUN_USER_FEATURES (NETIF_F_HW_CSUM|NETIF_F_TSO_ECN|NETIF_F_TSO| \
|
2015-02-04 00:36:17 +03:00
|
|
|
NETIF_F_TSO6|NETIF_F_UFO)
|
2010-03-17 18:45:01 +03:00
|
|
|
|
2016-02-26 12:45:40 +03:00
|
|
|
int align;
|
2010-03-17 18:45:01 +03:00
|
|
|
int vnet_hdr_sz;
|
2012-10-31 23:45:57 +04:00
|
|
|
int sndbuf;
|
|
|
|
struct tap_filter txflt;
|
|
|
|
struct sock_fprog fprog;
|
|
|
|
/* protected by rtnl lock */
|
|
|
|
bool filter_attached;
|
2008-04-13 05:48:58 +04:00
|
|
|
#ifdef TUN_DEBUG
|
|
|
|
int debug;
|
2005-04-17 02:20:36 +04:00
|
|
|
#endif
|
2012-10-31 23:46:02 +04:00
|
|
|
spinlock_t lock;
|
|
|
|
struct hlist_head flows[TUN_NUM_FLOW_ENTRIES];
|
|
|
|
struct timer_list flow_gc_timer;
|
|
|
|
unsigned long ageing_time;
|
2012-12-14 03:53:30 +04:00
|
|
|
unsigned int numdisabled;
|
|
|
|
struct list_head disabled;
|
2013-01-14 11:12:19 +04:00
|
|
|
void *security;
|
2013-01-23 07:59:13 +04:00
|
|
|
u32 flow_count;
|
2016-04-13 11:52:20 +03:00
|
|
|
struct tun_pcpu_stats __percpu *pcpu_stats;
|
2008-04-13 05:48:58 +04:00
|
|
|
};
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2015-04-24 15:50:36 +03:00
|
|
|
#ifdef CONFIG_TUN_VNET_CROSS_LE
|
|
|
|
static inline bool tun_legacy_is_little_endian(struct tun_struct *tun)
|
|
|
|
{
|
|
|
|
return tun->flags & TUN_VNET_BE ? false :
|
|
|
|
virtio_legacy_is_little_endian();
|
|
|
|
}
|
|
|
|
|
|
|
|
static long tun_get_vnet_be(struct tun_struct *tun, int __user *argp)
|
|
|
|
{
|
|
|
|
int be = !!(tun->flags & TUN_VNET_BE);
|
|
|
|
|
|
|
|
if (put_user(be, argp))
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static long tun_set_vnet_be(struct tun_struct *tun, int __user *argp)
|
|
|
|
{
|
|
|
|
int be;
|
|
|
|
|
|
|
|
if (get_user(be, argp))
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
if (be)
|
|
|
|
tun->flags |= TUN_VNET_BE;
|
|
|
|
else
|
|
|
|
tun->flags &= ~TUN_VNET_BE;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
static inline bool tun_legacy_is_little_endian(struct tun_struct *tun)
|
|
|
|
{
|
|
|
|
return virtio_legacy_is_little_endian();
|
|
|
|
}
|
|
|
|
|
|
|
|
static long tun_get_vnet_be(struct tun_struct *tun, int __user *argp)
|
|
|
|
{
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static long tun_set_vnet_be(struct tun_struct *tun, int __user *argp)
|
|
|
|
{
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_TUN_VNET_CROSS_LE */
|
|
|
|
|
2015-04-24 15:24:38 +03:00
|
|
|
static inline bool tun_is_little_endian(struct tun_struct *tun)
|
|
|
|
{
|
2015-04-24 15:26:24 +03:00
|
|
|
return tun->flags & TUN_VNET_LE ||
|
2015-04-24 15:50:36 +03:00
|
|
|
tun_legacy_is_little_endian(tun);
|
2015-04-24 15:24:38 +03:00
|
|
|
}
|
|
|
|
|
2014-10-23 23:59:31 +04:00
|
|
|
static inline u16 tun16_to_cpu(struct tun_struct *tun, __virtio16 val)
|
|
|
|
{
|
2015-04-24 15:24:38 +03:00
|
|
|
return __virtio16_to_cpu(tun_is_little_endian(tun), val);
|
2014-10-23 23:59:31 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline __virtio16 cpu_to_tun16(struct tun_struct *tun, u16 val)
|
|
|
|
{
|
2015-04-24 15:24:38 +03:00
|
|
|
return __cpu_to_virtio16(tun_is_little_endian(tun), val);
|
2014-10-23 23:59:31 +04:00
|
|
|
}
|
|
|
|
|
2012-10-31 23:46:02 +04:00
|
|
|
static inline u32 tun_hashfn(u32 rxhash)
|
|
|
|
{
|
|
|
|
return rxhash & 0x3ff;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct tun_flow_entry *tun_flow_find(struct hlist_head *head, u32 rxhash)
|
|
|
|
{
|
|
|
|
struct tun_flow_entry *e;
|
|
|
|
|
hlist: drop the node parameter from iterators
I'm not sure why, but the hlist for each entry iterators were conceived
list_for_each_entry(pos, head, member)
The hlist ones were greedy and wanted an extra parameter:
hlist_for_each_entry(tpos, pos, head, member)
Why did they need an extra pos parameter? I'm not quite sure. Not only
they don't really need it, it also prevents the iterator from looking
exactly like the list iterator, which is unfortunate.
Besides the semantic patch, there was some manual work required:
- Fix up the actual hlist iterators in linux/list.h
- Fix up the declaration of other iterators based on the hlist ones.
- A very small amount of places were using the 'node' parameter, this
was modified to use 'obj->member' instead.
- Coccinelle didn't handle the hlist_for_each_entry_safe iterator
properly, so those had to be fixed up manually.
The semantic patch which is mostly the work of Peter Senna Tschudin is here:
@@
iterator name hlist_for_each_entry, hlist_for_each_entry_continue, hlist_for_each_entry_from, hlist_for_each_entry_rcu, hlist_for_each_entry_rcu_bh, hlist_for_each_entry_continue_rcu_bh, for_each_busy_worker, ax25_uid_for_each, ax25_for_each, inet_bind_bucket_for_each, sctp_for_each_hentry, sk_for_each, sk_for_each_rcu, sk_for_each_from, sk_for_each_safe, sk_for_each_bound, hlist_for_each_entry_safe, hlist_for_each_entry_continue_rcu, nr_neigh_for_each, nr_neigh_for_each_safe, nr_node_for_each, nr_node_for_each_safe, for_each_gfn_indirect_valid_sp, for_each_gfn_sp, for_each_host;
type T;
expression a,c,d,e;
identifier b;
statement S;
@@
-T b;
<+... when != b
(
hlist_for_each_entry(a,
- b,
c, d) S
|
hlist_for_each_entry_continue(a,
- b,
c) S
|
hlist_for_each_entry_from(a,
- b,
c) S
|
hlist_for_each_entry_rcu(a,
- b,
c, d) S
|
hlist_for_each_entry_rcu_bh(a,
- b,
c, d) S
|
hlist_for_each_entry_continue_rcu_bh(a,
- b,
c) S
|
for_each_busy_worker(a, c,
- b,
d) S
|
ax25_uid_for_each(a,
- b,
c) S
|
ax25_for_each(a,
- b,
c) S
|
inet_bind_bucket_for_each(a,
- b,
c) S
|
sctp_for_each_hentry(a,
- b,
c) S
|
sk_for_each(a,
- b,
c) S
|
sk_for_each_rcu(a,
- b,
c) S
|
sk_for_each_from
-(a, b)
+(a)
S
+ sk_for_each_from(a) S
|
sk_for_each_safe(a,
- b,
c, d) S
|
sk_for_each_bound(a,
- b,
c) S
|
hlist_for_each_entry_safe(a,
- b,
c, d, e) S
|
hlist_for_each_entry_continue_rcu(a,
- b,
c) S
|
nr_neigh_for_each(a,
- b,
c) S
|
nr_neigh_for_each_safe(a,
- b,
c, d) S
|
nr_node_for_each(a,
- b,
c) S
|
nr_node_for_each_safe(a,
- b,
c, d) S
|
- for_each_gfn_sp(a, c, d, b) S
+ for_each_gfn_sp(a, c, d) S
|
- for_each_gfn_indirect_valid_sp(a, c, d, b) S
+ for_each_gfn_indirect_valid_sp(a, c, d) S
|
for_each_host(a,
- b,
c) S
|
for_each_host_safe(a,
- b,
c, d) S
|
for_each_mesh_entry(a,
- b,
c, d) S
)
...+>
[akpm@linux-foundation.org: drop bogus change from net/ipv4/raw.c]
[akpm@linux-foundation.org: drop bogus hunk from net/ipv6/raw.c]
[akpm@linux-foundation.org: checkpatch fixes]
[akpm@linux-foundation.org: fix warnings]
[akpm@linux-foudnation.org: redo intrusive kvm changes]
Tested-by: Peter Senna Tschudin <peter.senna@gmail.com>
Acked-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Signed-off-by: Sasha Levin <sasha.levin@oracle.com>
Cc: Wu Fengguang <fengguang.wu@intel.com>
Cc: Marcelo Tosatti <mtosatti@redhat.com>
Cc: Gleb Natapov <gleb@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2013-02-28 05:06:00 +04:00
|
|
|
hlist_for_each_entry_rcu(e, head, hash_link) {
|
2012-10-31 23:46:02 +04:00
|
|
|
if (e->rxhash == rxhash)
|
|
|
|
return e;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct tun_flow_entry *tun_flow_create(struct tun_struct *tun,
|
|
|
|
struct hlist_head *head,
|
|
|
|
u32 rxhash, u16 queue_index)
|
|
|
|
{
|
2012-12-21 11:17:21 +04:00
|
|
|
struct tun_flow_entry *e = kmalloc(sizeof(*e), GFP_ATOMIC);
|
|
|
|
|
2012-10-31 23:46:02 +04:00
|
|
|
if (e) {
|
|
|
|
tun_debug(KERN_INFO, tun, "create flow: hash %u index %u\n",
|
|
|
|
rxhash, queue_index);
|
|
|
|
e->updated = jiffies;
|
|
|
|
e->rxhash = rxhash;
|
2013-12-22 14:54:32 +04:00
|
|
|
e->rps_rxhash = 0;
|
2012-10-31 23:46:02 +04:00
|
|
|
e->queue_index = queue_index;
|
|
|
|
e->tun = tun;
|
|
|
|
hlist_add_head_rcu(&e->hash_link, head);
|
2013-01-23 07:59:13 +04:00
|
|
|
++tun->flow_count;
|
2012-10-31 23:46:02 +04:00
|
|
|
}
|
|
|
|
return e;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void tun_flow_delete(struct tun_struct *tun, struct tun_flow_entry *e)
|
|
|
|
{
|
|
|
|
tun_debug(KERN_INFO, tun, "delete flow: hash %u index %u\n",
|
|
|
|
e->rxhash, e->queue_index);
|
|
|
|
hlist_del_rcu(&e->hash_link);
|
2012-12-21 11:17:21 +04:00
|
|
|
kfree_rcu(e, rcu);
|
2013-01-23 07:59:13 +04:00
|
|
|
--tun->flow_count;
|
2012-10-31 23:46:02 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
static void tun_flow_flush(struct tun_struct *tun)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
spin_lock_bh(&tun->lock);
|
|
|
|
for (i = 0; i < TUN_NUM_FLOW_ENTRIES; i++) {
|
|
|
|
struct tun_flow_entry *e;
|
hlist: drop the node parameter from iterators
I'm not sure why, but the hlist for each entry iterators were conceived
list_for_each_entry(pos, head, member)
The hlist ones were greedy and wanted an extra parameter:
hlist_for_each_entry(tpos, pos, head, member)
Why did they need an extra pos parameter? I'm not quite sure. Not only
they don't really need it, it also prevents the iterator from looking
exactly like the list iterator, which is unfortunate.
Besides the semantic patch, there was some manual work required:
- Fix up the actual hlist iterators in linux/list.h
- Fix up the declaration of other iterators based on the hlist ones.
- A very small amount of places were using the 'node' parameter, this
was modified to use 'obj->member' instead.
- Coccinelle didn't handle the hlist_for_each_entry_safe iterator
properly, so those had to be fixed up manually.
The semantic patch which is mostly the work of Peter Senna Tschudin is here:
@@
iterator name hlist_for_each_entry, hlist_for_each_entry_continue, hlist_for_each_entry_from, hlist_for_each_entry_rcu, hlist_for_each_entry_rcu_bh, hlist_for_each_entry_continue_rcu_bh, for_each_busy_worker, ax25_uid_for_each, ax25_for_each, inet_bind_bucket_for_each, sctp_for_each_hentry, sk_for_each, sk_for_each_rcu, sk_for_each_from, sk_for_each_safe, sk_for_each_bound, hlist_for_each_entry_safe, hlist_for_each_entry_continue_rcu, nr_neigh_for_each, nr_neigh_for_each_safe, nr_node_for_each, nr_node_for_each_safe, for_each_gfn_indirect_valid_sp, for_each_gfn_sp, for_each_host;
type T;
expression a,c,d,e;
identifier b;
statement S;
@@
-T b;
<+... when != b
(
hlist_for_each_entry(a,
- b,
c, d) S
|
hlist_for_each_entry_continue(a,
- b,
c) S
|
hlist_for_each_entry_from(a,
- b,
c) S
|
hlist_for_each_entry_rcu(a,
- b,
c, d) S
|
hlist_for_each_entry_rcu_bh(a,
- b,
c, d) S
|
hlist_for_each_entry_continue_rcu_bh(a,
- b,
c) S
|
for_each_busy_worker(a, c,
- b,
d) S
|
ax25_uid_for_each(a,
- b,
c) S
|
ax25_for_each(a,
- b,
c) S
|
inet_bind_bucket_for_each(a,
- b,
c) S
|
sctp_for_each_hentry(a,
- b,
c) S
|
sk_for_each(a,
- b,
c) S
|
sk_for_each_rcu(a,
- b,
c) S
|
sk_for_each_from
-(a, b)
+(a)
S
+ sk_for_each_from(a) S
|
sk_for_each_safe(a,
- b,
c, d) S
|
sk_for_each_bound(a,
- b,
c) S
|
hlist_for_each_entry_safe(a,
- b,
c, d, e) S
|
hlist_for_each_entry_continue_rcu(a,
- b,
c) S
|
nr_neigh_for_each(a,
- b,
c) S
|
nr_neigh_for_each_safe(a,
- b,
c, d) S
|
nr_node_for_each(a,
- b,
c) S
|
nr_node_for_each_safe(a,
- b,
c, d) S
|
- for_each_gfn_sp(a, c, d, b) S
+ for_each_gfn_sp(a, c, d) S
|
- for_each_gfn_indirect_valid_sp(a, c, d, b) S
+ for_each_gfn_indirect_valid_sp(a, c, d) S
|
for_each_host(a,
- b,
c) S
|
for_each_host_safe(a,
- b,
c, d) S
|
for_each_mesh_entry(a,
- b,
c, d) S
)
...+>
[akpm@linux-foundation.org: drop bogus change from net/ipv4/raw.c]
[akpm@linux-foundation.org: drop bogus hunk from net/ipv6/raw.c]
[akpm@linux-foundation.org: checkpatch fixes]
[akpm@linux-foundation.org: fix warnings]
[akpm@linux-foudnation.org: redo intrusive kvm changes]
Tested-by: Peter Senna Tschudin <peter.senna@gmail.com>
Acked-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Signed-off-by: Sasha Levin <sasha.levin@oracle.com>
Cc: Wu Fengguang <fengguang.wu@intel.com>
Cc: Marcelo Tosatti <mtosatti@redhat.com>
Cc: Gleb Natapov <gleb@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2013-02-28 05:06:00 +04:00
|
|
|
struct hlist_node *n;
|
2012-10-31 23:46:02 +04:00
|
|
|
|
hlist: drop the node parameter from iterators
I'm not sure why, but the hlist for each entry iterators were conceived
list_for_each_entry(pos, head, member)
The hlist ones were greedy and wanted an extra parameter:
hlist_for_each_entry(tpos, pos, head, member)
Why did they need an extra pos parameter? I'm not quite sure. Not only
they don't really need it, it also prevents the iterator from looking
exactly like the list iterator, which is unfortunate.
Besides the semantic patch, there was some manual work required:
- Fix up the actual hlist iterators in linux/list.h
- Fix up the declaration of other iterators based on the hlist ones.
- A very small amount of places were using the 'node' parameter, this
was modified to use 'obj->member' instead.
- Coccinelle didn't handle the hlist_for_each_entry_safe iterator
properly, so those had to be fixed up manually.
The semantic patch which is mostly the work of Peter Senna Tschudin is here:
@@
iterator name hlist_for_each_entry, hlist_for_each_entry_continue, hlist_for_each_entry_from, hlist_for_each_entry_rcu, hlist_for_each_entry_rcu_bh, hlist_for_each_entry_continue_rcu_bh, for_each_busy_worker, ax25_uid_for_each, ax25_for_each, inet_bind_bucket_for_each, sctp_for_each_hentry, sk_for_each, sk_for_each_rcu, sk_for_each_from, sk_for_each_safe, sk_for_each_bound, hlist_for_each_entry_safe, hlist_for_each_entry_continue_rcu, nr_neigh_for_each, nr_neigh_for_each_safe, nr_node_for_each, nr_node_for_each_safe, for_each_gfn_indirect_valid_sp, for_each_gfn_sp, for_each_host;
type T;
expression a,c,d,e;
identifier b;
statement S;
@@
-T b;
<+... when != b
(
hlist_for_each_entry(a,
- b,
c, d) S
|
hlist_for_each_entry_continue(a,
- b,
c) S
|
hlist_for_each_entry_from(a,
- b,
c) S
|
hlist_for_each_entry_rcu(a,
- b,
c, d) S
|
hlist_for_each_entry_rcu_bh(a,
- b,
c, d) S
|
hlist_for_each_entry_continue_rcu_bh(a,
- b,
c) S
|
for_each_busy_worker(a, c,
- b,
d) S
|
ax25_uid_for_each(a,
- b,
c) S
|
ax25_for_each(a,
- b,
c) S
|
inet_bind_bucket_for_each(a,
- b,
c) S
|
sctp_for_each_hentry(a,
- b,
c) S
|
sk_for_each(a,
- b,
c) S
|
sk_for_each_rcu(a,
- b,
c) S
|
sk_for_each_from
-(a, b)
+(a)
S
+ sk_for_each_from(a) S
|
sk_for_each_safe(a,
- b,
c, d) S
|
sk_for_each_bound(a,
- b,
c) S
|
hlist_for_each_entry_safe(a,
- b,
c, d, e) S
|
hlist_for_each_entry_continue_rcu(a,
- b,
c) S
|
nr_neigh_for_each(a,
- b,
c) S
|
nr_neigh_for_each_safe(a,
- b,
c, d) S
|
nr_node_for_each(a,
- b,
c) S
|
nr_node_for_each_safe(a,
- b,
c, d) S
|
- for_each_gfn_sp(a, c, d, b) S
+ for_each_gfn_sp(a, c, d) S
|
- for_each_gfn_indirect_valid_sp(a, c, d, b) S
+ for_each_gfn_indirect_valid_sp(a, c, d) S
|
for_each_host(a,
- b,
c) S
|
for_each_host_safe(a,
- b,
c, d) S
|
for_each_mesh_entry(a,
- b,
c, d) S
)
...+>
[akpm@linux-foundation.org: drop bogus change from net/ipv4/raw.c]
[akpm@linux-foundation.org: drop bogus hunk from net/ipv6/raw.c]
[akpm@linux-foundation.org: checkpatch fixes]
[akpm@linux-foundation.org: fix warnings]
[akpm@linux-foudnation.org: redo intrusive kvm changes]
Tested-by: Peter Senna Tschudin <peter.senna@gmail.com>
Acked-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Signed-off-by: Sasha Levin <sasha.levin@oracle.com>
Cc: Wu Fengguang <fengguang.wu@intel.com>
Cc: Marcelo Tosatti <mtosatti@redhat.com>
Cc: Gleb Natapov <gleb@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2013-02-28 05:06:00 +04:00
|
|
|
hlist_for_each_entry_safe(e, n, &tun->flows[i], hash_link)
|
2012-10-31 23:46:02 +04:00
|
|
|
tun_flow_delete(tun, e);
|
|
|
|
}
|
|
|
|
spin_unlock_bh(&tun->lock);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void tun_flow_delete_by_queue(struct tun_struct *tun, u16 queue_index)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
spin_lock_bh(&tun->lock);
|
|
|
|
for (i = 0; i < TUN_NUM_FLOW_ENTRIES; i++) {
|
|
|
|
struct tun_flow_entry *e;
|
hlist: drop the node parameter from iterators
I'm not sure why, but the hlist for each entry iterators were conceived
list_for_each_entry(pos, head, member)
The hlist ones were greedy and wanted an extra parameter:
hlist_for_each_entry(tpos, pos, head, member)
Why did they need an extra pos parameter? I'm not quite sure. Not only
they don't really need it, it also prevents the iterator from looking
exactly like the list iterator, which is unfortunate.
Besides the semantic patch, there was some manual work required:
- Fix up the actual hlist iterators in linux/list.h
- Fix up the declaration of other iterators based on the hlist ones.
- A very small amount of places were using the 'node' parameter, this
was modified to use 'obj->member' instead.
- Coccinelle didn't handle the hlist_for_each_entry_safe iterator
properly, so those had to be fixed up manually.
The semantic patch which is mostly the work of Peter Senna Tschudin is here:
@@
iterator name hlist_for_each_entry, hlist_for_each_entry_continue, hlist_for_each_entry_from, hlist_for_each_entry_rcu, hlist_for_each_entry_rcu_bh, hlist_for_each_entry_continue_rcu_bh, for_each_busy_worker, ax25_uid_for_each, ax25_for_each, inet_bind_bucket_for_each, sctp_for_each_hentry, sk_for_each, sk_for_each_rcu, sk_for_each_from, sk_for_each_safe, sk_for_each_bound, hlist_for_each_entry_safe, hlist_for_each_entry_continue_rcu, nr_neigh_for_each, nr_neigh_for_each_safe, nr_node_for_each, nr_node_for_each_safe, for_each_gfn_indirect_valid_sp, for_each_gfn_sp, for_each_host;
type T;
expression a,c,d,e;
identifier b;
statement S;
@@
-T b;
<+... when != b
(
hlist_for_each_entry(a,
- b,
c, d) S
|
hlist_for_each_entry_continue(a,
- b,
c) S
|
hlist_for_each_entry_from(a,
- b,
c) S
|
hlist_for_each_entry_rcu(a,
- b,
c, d) S
|
hlist_for_each_entry_rcu_bh(a,
- b,
c, d) S
|
hlist_for_each_entry_continue_rcu_bh(a,
- b,
c) S
|
for_each_busy_worker(a, c,
- b,
d) S
|
ax25_uid_for_each(a,
- b,
c) S
|
ax25_for_each(a,
- b,
c) S
|
inet_bind_bucket_for_each(a,
- b,
c) S
|
sctp_for_each_hentry(a,
- b,
c) S
|
sk_for_each(a,
- b,
c) S
|
sk_for_each_rcu(a,
- b,
c) S
|
sk_for_each_from
-(a, b)
+(a)
S
+ sk_for_each_from(a) S
|
sk_for_each_safe(a,
- b,
c, d) S
|
sk_for_each_bound(a,
- b,
c) S
|
hlist_for_each_entry_safe(a,
- b,
c, d, e) S
|
hlist_for_each_entry_continue_rcu(a,
- b,
c) S
|
nr_neigh_for_each(a,
- b,
c) S
|
nr_neigh_for_each_safe(a,
- b,
c, d) S
|
nr_node_for_each(a,
- b,
c) S
|
nr_node_for_each_safe(a,
- b,
c, d) S
|
- for_each_gfn_sp(a, c, d, b) S
+ for_each_gfn_sp(a, c, d) S
|
- for_each_gfn_indirect_valid_sp(a, c, d, b) S
+ for_each_gfn_indirect_valid_sp(a, c, d) S
|
for_each_host(a,
- b,
c) S
|
for_each_host_safe(a,
- b,
c, d) S
|
for_each_mesh_entry(a,
- b,
c, d) S
)
...+>
[akpm@linux-foundation.org: drop bogus change from net/ipv4/raw.c]
[akpm@linux-foundation.org: drop bogus hunk from net/ipv6/raw.c]
[akpm@linux-foundation.org: checkpatch fixes]
[akpm@linux-foundation.org: fix warnings]
[akpm@linux-foudnation.org: redo intrusive kvm changes]
Tested-by: Peter Senna Tschudin <peter.senna@gmail.com>
Acked-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Signed-off-by: Sasha Levin <sasha.levin@oracle.com>
Cc: Wu Fengguang <fengguang.wu@intel.com>
Cc: Marcelo Tosatti <mtosatti@redhat.com>
Cc: Gleb Natapov <gleb@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2013-02-28 05:06:00 +04:00
|
|
|
struct hlist_node *n;
|
2012-10-31 23:46:02 +04:00
|
|
|
|
hlist: drop the node parameter from iterators
I'm not sure why, but the hlist for each entry iterators were conceived
list_for_each_entry(pos, head, member)
The hlist ones were greedy and wanted an extra parameter:
hlist_for_each_entry(tpos, pos, head, member)
Why did they need an extra pos parameter? I'm not quite sure. Not only
they don't really need it, it also prevents the iterator from looking
exactly like the list iterator, which is unfortunate.
Besides the semantic patch, there was some manual work required:
- Fix up the actual hlist iterators in linux/list.h
- Fix up the declaration of other iterators based on the hlist ones.
- A very small amount of places were using the 'node' parameter, this
was modified to use 'obj->member' instead.
- Coccinelle didn't handle the hlist_for_each_entry_safe iterator
properly, so those had to be fixed up manually.
The semantic patch which is mostly the work of Peter Senna Tschudin is here:
@@
iterator name hlist_for_each_entry, hlist_for_each_entry_continue, hlist_for_each_entry_from, hlist_for_each_entry_rcu, hlist_for_each_entry_rcu_bh, hlist_for_each_entry_continue_rcu_bh, for_each_busy_worker, ax25_uid_for_each, ax25_for_each, inet_bind_bucket_for_each, sctp_for_each_hentry, sk_for_each, sk_for_each_rcu, sk_for_each_from, sk_for_each_safe, sk_for_each_bound, hlist_for_each_entry_safe, hlist_for_each_entry_continue_rcu, nr_neigh_for_each, nr_neigh_for_each_safe, nr_node_for_each, nr_node_for_each_safe, for_each_gfn_indirect_valid_sp, for_each_gfn_sp, for_each_host;
type T;
expression a,c,d,e;
identifier b;
statement S;
@@
-T b;
<+... when != b
(
hlist_for_each_entry(a,
- b,
c, d) S
|
hlist_for_each_entry_continue(a,
- b,
c) S
|
hlist_for_each_entry_from(a,
- b,
c) S
|
hlist_for_each_entry_rcu(a,
- b,
c, d) S
|
hlist_for_each_entry_rcu_bh(a,
- b,
c, d) S
|
hlist_for_each_entry_continue_rcu_bh(a,
- b,
c) S
|
for_each_busy_worker(a, c,
- b,
d) S
|
ax25_uid_for_each(a,
- b,
c) S
|
ax25_for_each(a,
- b,
c) S
|
inet_bind_bucket_for_each(a,
- b,
c) S
|
sctp_for_each_hentry(a,
- b,
c) S
|
sk_for_each(a,
- b,
c) S
|
sk_for_each_rcu(a,
- b,
c) S
|
sk_for_each_from
-(a, b)
+(a)
S
+ sk_for_each_from(a) S
|
sk_for_each_safe(a,
- b,
c, d) S
|
sk_for_each_bound(a,
- b,
c) S
|
hlist_for_each_entry_safe(a,
- b,
c, d, e) S
|
hlist_for_each_entry_continue_rcu(a,
- b,
c) S
|
nr_neigh_for_each(a,
- b,
c) S
|
nr_neigh_for_each_safe(a,
- b,
c, d) S
|
nr_node_for_each(a,
- b,
c) S
|
nr_node_for_each_safe(a,
- b,
c, d) S
|
- for_each_gfn_sp(a, c, d, b) S
+ for_each_gfn_sp(a, c, d) S
|
- for_each_gfn_indirect_valid_sp(a, c, d, b) S
+ for_each_gfn_indirect_valid_sp(a, c, d) S
|
for_each_host(a,
- b,
c) S
|
for_each_host_safe(a,
- b,
c, d) S
|
for_each_mesh_entry(a,
- b,
c, d) S
)
...+>
[akpm@linux-foundation.org: drop bogus change from net/ipv4/raw.c]
[akpm@linux-foundation.org: drop bogus hunk from net/ipv6/raw.c]
[akpm@linux-foundation.org: checkpatch fixes]
[akpm@linux-foundation.org: fix warnings]
[akpm@linux-foudnation.org: redo intrusive kvm changes]
Tested-by: Peter Senna Tschudin <peter.senna@gmail.com>
Acked-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Signed-off-by: Sasha Levin <sasha.levin@oracle.com>
Cc: Wu Fengguang <fengguang.wu@intel.com>
Cc: Marcelo Tosatti <mtosatti@redhat.com>
Cc: Gleb Natapov <gleb@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2013-02-28 05:06:00 +04:00
|
|
|
hlist_for_each_entry_safe(e, n, &tun->flows[i], hash_link) {
|
2012-10-31 23:46:02 +04:00
|
|
|
if (e->queue_index == queue_index)
|
|
|
|
tun_flow_delete(tun, e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
spin_unlock_bh(&tun->lock);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void tun_flow_cleanup(unsigned long data)
|
|
|
|
{
|
|
|
|
struct tun_struct *tun = (struct tun_struct *)data;
|
|
|
|
unsigned long delay = tun->ageing_time;
|
|
|
|
unsigned long next_timer = jiffies + delay;
|
|
|
|
unsigned long count = 0;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
tun_debug(KERN_INFO, tun, "tun_flow_cleanup\n");
|
|
|
|
|
|
|
|
spin_lock_bh(&tun->lock);
|
|
|
|
for (i = 0; i < TUN_NUM_FLOW_ENTRIES; i++) {
|
|
|
|
struct tun_flow_entry *e;
|
hlist: drop the node parameter from iterators
I'm not sure why, but the hlist for each entry iterators were conceived
list_for_each_entry(pos, head, member)
The hlist ones were greedy and wanted an extra parameter:
hlist_for_each_entry(tpos, pos, head, member)
Why did they need an extra pos parameter? I'm not quite sure. Not only
they don't really need it, it also prevents the iterator from looking
exactly like the list iterator, which is unfortunate.
Besides the semantic patch, there was some manual work required:
- Fix up the actual hlist iterators in linux/list.h
- Fix up the declaration of other iterators based on the hlist ones.
- A very small amount of places were using the 'node' parameter, this
was modified to use 'obj->member' instead.
- Coccinelle didn't handle the hlist_for_each_entry_safe iterator
properly, so those had to be fixed up manually.
The semantic patch which is mostly the work of Peter Senna Tschudin is here:
@@
iterator name hlist_for_each_entry, hlist_for_each_entry_continue, hlist_for_each_entry_from, hlist_for_each_entry_rcu, hlist_for_each_entry_rcu_bh, hlist_for_each_entry_continue_rcu_bh, for_each_busy_worker, ax25_uid_for_each, ax25_for_each, inet_bind_bucket_for_each, sctp_for_each_hentry, sk_for_each, sk_for_each_rcu, sk_for_each_from, sk_for_each_safe, sk_for_each_bound, hlist_for_each_entry_safe, hlist_for_each_entry_continue_rcu, nr_neigh_for_each, nr_neigh_for_each_safe, nr_node_for_each, nr_node_for_each_safe, for_each_gfn_indirect_valid_sp, for_each_gfn_sp, for_each_host;
type T;
expression a,c,d,e;
identifier b;
statement S;
@@
-T b;
<+... when != b
(
hlist_for_each_entry(a,
- b,
c, d) S
|
hlist_for_each_entry_continue(a,
- b,
c) S
|
hlist_for_each_entry_from(a,
- b,
c) S
|
hlist_for_each_entry_rcu(a,
- b,
c, d) S
|
hlist_for_each_entry_rcu_bh(a,
- b,
c, d) S
|
hlist_for_each_entry_continue_rcu_bh(a,
- b,
c) S
|
for_each_busy_worker(a, c,
- b,
d) S
|
ax25_uid_for_each(a,
- b,
c) S
|
ax25_for_each(a,
- b,
c) S
|
inet_bind_bucket_for_each(a,
- b,
c) S
|
sctp_for_each_hentry(a,
- b,
c) S
|
sk_for_each(a,
- b,
c) S
|
sk_for_each_rcu(a,
- b,
c) S
|
sk_for_each_from
-(a, b)
+(a)
S
+ sk_for_each_from(a) S
|
sk_for_each_safe(a,
- b,
c, d) S
|
sk_for_each_bound(a,
- b,
c) S
|
hlist_for_each_entry_safe(a,
- b,
c, d, e) S
|
hlist_for_each_entry_continue_rcu(a,
- b,
c) S
|
nr_neigh_for_each(a,
- b,
c) S
|
nr_neigh_for_each_safe(a,
- b,
c, d) S
|
nr_node_for_each(a,
- b,
c) S
|
nr_node_for_each_safe(a,
- b,
c, d) S
|
- for_each_gfn_sp(a, c, d, b) S
+ for_each_gfn_sp(a, c, d) S
|
- for_each_gfn_indirect_valid_sp(a, c, d, b) S
+ for_each_gfn_indirect_valid_sp(a, c, d) S
|
for_each_host(a,
- b,
c) S
|
for_each_host_safe(a,
- b,
c, d) S
|
for_each_mesh_entry(a,
- b,
c, d) S
)
...+>
[akpm@linux-foundation.org: drop bogus change from net/ipv4/raw.c]
[akpm@linux-foundation.org: drop bogus hunk from net/ipv6/raw.c]
[akpm@linux-foundation.org: checkpatch fixes]
[akpm@linux-foundation.org: fix warnings]
[akpm@linux-foudnation.org: redo intrusive kvm changes]
Tested-by: Peter Senna Tschudin <peter.senna@gmail.com>
Acked-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Signed-off-by: Sasha Levin <sasha.levin@oracle.com>
Cc: Wu Fengguang <fengguang.wu@intel.com>
Cc: Marcelo Tosatti <mtosatti@redhat.com>
Cc: Gleb Natapov <gleb@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2013-02-28 05:06:00 +04:00
|
|
|
struct hlist_node *n;
|
2012-10-31 23:46:02 +04:00
|
|
|
|
hlist: drop the node parameter from iterators
I'm not sure why, but the hlist for each entry iterators were conceived
list_for_each_entry(pos, head, member)
The hlist ones were greedy and wanted an extra parameter:
hlist_for_each_entry(tpos, pos, head, member)
Why did they need an extra pos parameter? I'm not quite sure. Not only
they don't really need it, it also prevents the iterator from looking
exactly like the list iterator, which is unfortunate.
Besides the semantic patch, there was some manual work required:
- Fix up the actual hlist iterators in linux/list.h
- Fix up the declaration of other iterators based on the hlist ones.
- A very small amount of places were using the 'node' parameter, this
was modified to use 'obj->member' instead.
- Coccinelle didn't handle the hlist_for_each_entry_safe iterator
properly, so those had to be fixed up manually.
The semantic patch which is mostly the work of Peter Senna Tschudin is here:
@@
iterator name hlist_for_each_entry, hlist_for_each_entry_continue, hlist_for_each_entry_from, hlist_for_each_entry_rcu, hlist_for_each_entry_rcu_bh, hlist_for_each_entry_continue_rcu_bh, for_each_busy_worker, ax25_uid_for_each, ax25_for_each, inet_bind_bucket_for_each, sctp_for_each_hentry, sk_for_each, sk_for_each_rcu, sk_for_each_from, sk_for_each_safe, sk_for_each_bound, hlist_for_each_entry_safe, hlist_for_each_entry_continue_rcu, nr_neigh_for_each, nr_neigh_for_each_safe, nr_node_for_each, nr_node_for_each_safe, for_each_gfn_indirect_valid_sp, for_each_gfn_sp, for_each_host;
type T;
expression a,c,d,e;
identifier b;
statement S;
@@
-T b;
<+... when != b
(
hlist_for_each_entry(a,
- b,
c, d) S
|
hlist_for_each_entry_continue(a,
- b,
c) S
|
hlist_for_each_entry_from(a,
- b,
c) S
|
hlist_for_each_entry_rcu(a,
- b,
c, d) S
|
hlist_for_each_entry_rcu_bh(a,
- b,
c, d) S
|
hlist_for_each_entry_continue_rcu_bh(a,
- b,
c) S
|
for_each_busy_worker(a, c,
- b,
d) S
|
ax25_uid_for_each(a,
- b,
c) S
|
ax25_for_each(a,
- b,
c) S
|
inet_bind_bucket_for_each(a,
- b,
c) S
|
sctp_for_each_hentry(a,
- b,
c) S
|
sk_for_each(a,
- b,
c) S
|
sk_for_each_rcu(a,
- b,
c) S
|
sk_for_each_from
-(a, b)
+(a)
S
+ sk_for_each_from(a) S
|
sk_for_each_safe(a,
- b,
c, d) S
|
sk_for_each_bound(a,
- b,
c) S
|
hlist_for_each_entry_safe(a,
- b,
c, d, e) S
|
hlist_for_each_entry_continue_rcu(a,
- b,
c) S
|
nr_neigh_for_each(a,
- b,
c) S
|
nr_neigh_for_each_safe(a,
- b,
c, d) S
|
nr_node_for_each(a,
- b,
c) S
|
nr_node_for_each_safe(a,
- b,
c, d) S
|
- for_each_gfn_sp(a, c, d, b) S
+ for_each_gfn_sp(a, c, d) S
|
- for_each_gfn_indirect_valid_sp(a, c, d, b) S
+ for_each_gfn_indirect_valid_sp(a, c, d) S
|
for_each_host(a,
- b,
c) S
|
for_each_host_safe(a,
- b,
c, d) S
|
for_each_mesh_entry(a,
- b,
c, d) S
)
...+>
[akpm@linux-foundation.org: drop bogus change from net/ipv4/raw.c]
[akpm@linux-foundation.org: drop bogus hunk from net/ipv6/raw.c]
[akpm@linux-foundation.org: checkpatch fixes]
[akpm@linux-foundation.org: fix warnings]
[akpm@linux-foudnation.org: redo intrusive kvm changes]
Tested-by: Peter Senna Tschudin <peter.senna@gmail.com>
Acked-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Signed-off-by: Sasha Levin <sasha.levin@oracle.com>
Cc: Wu Fengguang <fengguang.wu@intel.com>
Cc: Marcelo Tosatti <mtosatti@redhat.com>
Cc: Gleb Natapov <gleb@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2013-02-28 05:06:00 +04:00
|
|
|
hlist_for_each_entry_safe(e, n, &tun->flows[i], hash_link) {
|
2012-10-31 23:46:02 +04:00
|
|
|
unsigned long this_timer;
|
|
|
|
count++;
|
|
|
|
this_timer = e->updated + delay;
|
|
|
|
if (time_before_eq(this_timer, jiffies))
|
|
|
|
tun_flow_delete(tun, e);
|
|
|
|
else if (time_before(this_timer, next_timer))
|
|
|
|
next_timer = this_timer;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (count)
|
|
|
|
mod_timer(&tun->flow_gc_timer, round_jiffies_up(next_timer));
|
|
|
|
spin_unlock_bh(&tun->lock);
|
|
|
|
}
|
|
|
|
|
2012-12-12 23:22:57 +04:00
|
|
|
static void tun_flow_update(struct tun_struct *tun, u32 rxhash,
|
2013-01-28 05:05:19 +04:00
|
|
|
struct tun_file *tfile)
|
2012-10-31 23:46:02 +04:00
|
|
|
{
|
|
|
|
struct hlist_head *head;
|
|
|
|
struct tun_flow_entry *e;
|
|
|
|
unsigned long delay = tun->ageing_time;
|
2013-01-28 05:05:19 +04:00
|
|
|
u16 queue_index = tfile->queue_index;
|
2012-10-31 23:46:02 +04:00
|
|
|
|
|
|
|
if (!rxhash)
|
|
|
|
return;
|
|
|
|
else
|
|
|
|
head = &tun->flows[tun_hashfn(rxhash)];
|
|
|
|
|
|
|
|
rcu_read_lock();
|
|
|
|
|
2013-01-28 05:05:19 +04:00
|
|
|
/* We may get a very small possibility of OOO during switching, not
|
|
|
|
* worth to optimize.*/
|
|
|
|
if (tun->numqueues == 1 || tfile->detached)
|
2012-10-31 23:46:02 +04:00
|
|
|
goto unlock;
|
|
|
|
|
|
|
|
e = tun_flow_find(head, rxhash);
|
|
|
|
if (likely(e)) {
|
|
|
|
/* TODO: keep queueing to old queue until it's empty? */
|
|
|
|
e->queue_index = queue_index;
|
|
|
|
e->updated = jiffies;
|
2013-12-22 14:54:32 +04:00
|
|
|
sock_rps_record_flow_hash(e->rps_rxhash);
|
2012-10-31 23:46:02 +04:00
|
|
|
} else {
|
|
|
|
spin_lock_bh(&tun->lock);
|
2013-01-23 07:59:13 +04:00
|
|
|
if (!tun_flow_find(head, rxhash) &&
|
|
|
|
tun->flow_count < MAX_TAP_FLOWS)
|
2012-10-31 23:46:02 +04:00
|
|
|
tun_flow_create(tun, head, rxhash, queue_index);
|
|
|
|
|
|
|
|
if (!timer_pending(&tun->flow_gc_timer))
|
|
|
|
mod_timer(&tun->flow_gc_timer,
|
|
|
|
round_jiffies_up(jiffies + delay));
|
|
|
|
spin_unlock_bh(&tun->lock);
|
|
|
|
}
|
|
|
|
|
|
|
|
unlock:
|
|
|
|
rcu_read_unlock();
|
|
|
|
}
|
|
|
|
|
2013-12-22 14:54:32 +04:00
|
|
|
/**
|
|
|
|
* Save the hash received in the stack receive path and update the
|
|
|
|
* flow_hash table accordingly.
|
|
|
|
*/
|
|
|
|
static inline void tun_flow_save_rps_rxhash(struct tun_flow_entry *e, u32 hash)
|
|
|
|
{
|
net: rfs: add hash collision detection
Receive Flow Steering is a nice solution but suffers from
hash collisions when a mix of connected and unconnected traffic
is received on the host, when flow hash table is populated.
Also, clearing flow in inet_release() makes RFS not very good
for short lived flows, as many packets can follow close().
(FIN , ACK packets, ...)
This patch extends the information stored into global hash table
to not only include cpu number, but upper part of the hash value.
I use a 32bit value, and dynamically split it in two parts.
For host with less than 64 possible cpus, this gives 6 bits for the
cpu number, and 26 (32-6) bits for the upper part of the hash.
Since hash bucket selection use low order bits of the hash, we have
a full hash match, if /proc/sys/net/core/rps_sock_flow_entries is big
enough.
If the hash found in flow table does not match, we fallback to RPS (if
it is enabled for the rxqueue).
This means that a packet for an non connected flow can avoid the
IPI through a unrelated/victim CPU.
This also means we no longer have to clear the table at socket
close time, and this helps short lived flows performance.
Signed-off-by: Eric Dumazet <edumazet@google.com>
Acked-by: Tom Herbert <therbert@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2015-02-06 23:59:01 +03:00
|
|
|
if (unlikely(e->rps_rxhash != hash))
|
2013-12-22 14:54:32 +04:00
|
|
|
e->rps_rxhash = hash;
|
|
|
|
}
|
|
|
|
|
2012-10-31 23:46:00 +04:00
|
|
|
/* We try to identify a flow through its rxhash first. The reason that
|
2013-12-06 08:42:58 +04:00
|
|
|
* we do not check rxq no. is because some cards(e.g 82599), chooses
|
2012-10-31 23:46:00 +04:00
|
|
|
* the rxq based on the txq where the last packet of the flow comes. As
|
|
|
|
* the userspace application move between processors, we may get a
|
|
|
|
* different rxq no. here. If we could not get rxhash, then we would
|
|
|
|
* hope the rxq no. may help here.
|
|
|
|
*/
|
2014-01-10 12:18:26 +04:00
|
|
|
static u16 tun_select_queue(struct net_device *dev, struct sk_buff *skb,
|
2014-02-16 18:55:20 +04:00
|
|
|
void *accel_priv, select_queue_fallback_t fallback)
|
2012-10-31 23:46:00 +04:00
|
|
|
{
|
|
|
|
struct tun_struct *tun = netdev_priv(dev);
|
2012-10-31 23:46:02 +04:00
|
|
|
struct tun_flow_entry *e;
|
2012-10-31 23:46:00 +04:00
|
|
|
u32 txq = 0;
|
|
|
|
u32 numqueues = 0;
|
|
|
|
|
|
|
|
rcu_read_lock();
|
2013-06-05 12:44:57 +04:00
|
|
|
numqueues = ACCESS_ONCE(tun->numqueues);
|
2012-10-31 23:46:00 +04:00
|
|
|
|
2013-12-16 10:12:06 +04:00
|
|
|
txq = skb_get_hash(skb);
|
2012-10-31 23:46:00 +04:00
|
|
|
if (txq) {
|
2012-10-31 23:46:02 +04:00
|
|
|
e = tun_flow_find(&tun->flows[tun_hashfn(txq)], txq);
|
2013-12-22 14:54:32 +04:00
|
|
|
if (e) {
|
|
|
|
tun_flow_save_rps_rxhash(e, txq);
|
2014-01-02 09:24:28 +04:00
|
|
|
txq = e->queue_index;
|
2013-12-22 14:54:32 +04:00
|
|
|
} else
|
2012-10-31 23:46:02 +04:00
|
|
|
/* use multiply and shift instead of expensive divide */
|
|
|
|
txq = ((u64)txq * numqueues) >> 32;
|
2012-10-31 23:46:00 +04:00
|
|
|
} else if (likely(skb_rx_queue_recorded(skb))) {
|
|
|
|
txq = skb_get_rx_queue(skb);
|
|
|
|
while (unlikely(txq >= numqueues))
|
|
|
|
txq -= numqueues;
|
|
|
|
}
|
|
|
|
|
|
|
|
rcu_read_unlock();
|
|
|
|
return txq;
|
|
|
|
}
|
|
|
|
|
2012-10-31 23:46:01 +04:00
|
|
|
static inline bool tun_not_capable(struct tun_struct *tun)
|
|
|
|
{
|
|
|
|
const struct cred *cred = current_cred();
|
2012-11-19 01:34:11 +04:00
|
|
|
struct net *net = dev_net(tun->dev);
|
2012-10-31 23:46:01 +04:00
|
|
|
|
|
|
|
return ((uid_valid(tun->owner) && !uid_eq(cred->euid, tun->owner)) ||
|
|
|
|
(gid_valid(tun->group) && !in_egroup_p(tun->group))) &&
|
2012-11-19 01:34:11 +04:00
|
|
|
!ns_capable(net->user_ns, CAP_NET_ADMIN);
|
2012-10-31 23:46:01 +04:00
|
|
|
}
|
|
|
|
|
2012-10-31 23:46:00 +04:00
|
|
|
static void tun_set_real_num_queues(struct tun_struct *tun)
|
|
|
|
{
|
|
|
|
netif_set_real_num_tx_queues(tun->dev, tun->numqueues);
|
|
|
|
netif_set_real_num_rx_queues(tun->dev, tun->numqueues);
|
|
|
|
}
|
|
|
|
|
2012-12-14 03:53:30 +04:00
|
|
|
static void tun_disable_queue(struct tun_struct *tun, struct tun_file *tfile)
|
|
|
|
{
|
|
|
|
tfile->detached = tun;
|
|
|
|
list_add_tail(&tfile->next, &tun->disabled);
|
|
|
|
++tun->numdisabled;
|
|
|
|
}
|
|
|
|
|
2012-12-18 07:00:27 +04:00
|
|
|
static struct tun_struct *tun_enable_queue(struct tun_file *tfile)
|
2012-12-14 03:53:30 +04:00
|
|
|
{
|
|
|
|
struct tun_struct *tun = tfile->detached;
|
|
|
|
|
|
|
|
tfile->detached = NULL;
|
|
|
|
list_del_init(&tfile->next);
|
|
|
|
--tun->numdisabled;
|
|
|
|
return tun;
|
|
|
|
}
|
|
|
|
|
2013-09-05 13:53:59 +04:00
|
|
|
static void tun_queue_purge(struct tun_file *tfile)
|
|
|
|
{
|
|
|
|
skb_queue_purge(&tfile->sk.sk_receive_queue);
|
|
|
|
skb_queue_purge(&tfile->sk.sk_error_queue);
|
|
|
|
}
|
|
|
|
|
2012-10-31 23:46:00 +04:00
|
|
|
static void __tun_detach(struct tun_file *tfile, bool clean)
|
|
|
|
{
|
|
|
|
struct tun_file *ntfile;
|
|
|
|
struct tun_struct *tun;
|
|
|
|
|
2013-01-11 20:59:32 +04:00
|
|
|
tun = rtnl_dereference(tfile->tun);
|
|
|
|
|
2013-01-28 05:05:19 +04:00
|
|
|
if (tun && !tfile->detached) {
|
2012-10-31 23:46:00 +04:00
|
|
|
u16 index = tfile->queue_index;
|
|
|
|
BUG_ON(index >= tun->numqueues);
|
|
|
|
|
|
|
|
rcu_assign_pointer(tun->tfiles[index],
|
|
|
|
tun->tfiles[tun->numqueues - 1]);
|
2013-01-11 20:59:32 +04:00
|
|
|
ntfile = rtnl_dereference(tun->tfiles[index]);
|
2012-10-31 23:46:00 +04:00
|
|
|
ntfile->queue_index = index;
|
|
|
|
|
|
|
|
--tun->numqueues;
|
2013-01-28 05:05:19 +04:00
|
|
|
if (clean) {
|
2014-03-23 22:32:32 +04:00
|
|
|
RCU_INIT_POINTER(tfile->tun, NULL);
|
2012-12-14 03:53:30 +04:00
|
|
|
sock_put(&tfile->sk);
|
2013-01-28 05:05:19 +04:00
|
|
|
} else
|
2012-12-14 03:53:30 +04:00
|
|
|
tun_disable_queue(tun, tfile);
|
2012-10-31 23:46:00 +04:00
|
|
|
|
|
|
|
synchronize_net();
|
2012-10-31 23:46:02 +04:00
|
|
|
tun_flow_delete_by_queue(tun, tun->numqueues + 1);
|
2012-10-31 23:46:00 +04:00
|
|
|
/* Drop read queue */
|
2013-09-05 13:53:59 +04:00
|
|
|
tun_queue_purge(tfile);
|
2012-10-31 23:46:00 +04:00
|
|
|
tun_set_real_num_queues(tun);
|
2013-01-11 20:59:34 +04:00
|
|
|
} else if (tfile->detached && clean) {
|
2012-12-14 03:53:30 +04:00
|
|
|
tun = tun_enable_queue(tfile);
|
2013-01-11 20:59:34 +04:00
|
|
|
sock_put(&tfile->sk);
|
|
|
|
}
|
2012-10-31 23:46:00 +04:00
|
|
|
|
|
|
|
if (clean) {
|
2013-01-28 04:38:02 +04:00
|
|
|
if (tun && tun->numqueues == 0 && tun->numdisabled == 0) {
|
|
|
|
netif_carrier_off(tun->dev);
|
|
|
|
|
2014-11-19 16:17:31 +03:00
|
|
|
if (!(tun->flags & IFF_PERSIST) &&
|
2013-01-28 04:38:02 +04:00
|
|
|
tun->dev->reg_state == NETREG_REGISTERED)
|
2012-12-14 03:53:30 +04:00
|
|
|
unregister_netdevice(tun->dev);
|
2013-01-28 04:38:02 +04:00
|
|
|
}
|
2015-05-09 05:07:08 +03:00
|
|
|
sock_put(&tfile->sk);
|
2012-10-31 23:46:00 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void tun_detach(struct tun_file *tfile, bool clean)
|
|
|
|
{
|
|
|
|
rtnl_lock();
|
|
|
|
__tun_detach(tfile, clean);
|
|
|
|
rtnl_unlock();
|
|
|
|
}
|
|
|
|
|
|
|
|
static void tun_detach_all(struct net_device *dev)
|
|
|
|
{
|
|
|
|
struct tun_struct *tun = netdev_priv(dev);
|
2012-12-14 03:53:30 +04:00
|
|
|
struct tun_file *tfile, *tmp;
|
2012-10-31 23:46:00 +04:00
|
|
|
int i, n = tun->numqueues;
|
|
|
|
|
|
|
|
for (i = 0; i < n; i++) {
|
2013-01-11 20:59:32 +04:00
|
|
|
tfile = rtnl_dereference(tun->tfiles[i]);
|
2012-10-31 23:46:00 +04:00
|
|
|
BUG_ON(!tfile);
|
2014-05-17 02:11:48 +04:00
|
|
|
tfile->socket.sk->sk_data_ready(tfile->socket.sk);
|
2014-03-23 22:32:32 +04:00
|
|
|
RCU_INIT_POINTER(tfile->tun, NULL);
|
2012-10-31 23:46:00 +04:00
|
|
|
--tun->numqueues;
|
|
|
|
}
|
2013-01-28 05:05:19 +04:00
|
|
|
list_for_each_entry(tfile, &tun->disabled, next) {
|
2014-05-17 02:11:48 +04:00
|
|
|
tfile->socket.sk->sk_data_ready(tfile->socket.sk);
|
2014-03-23 22:32:32 +04:00
|
|
|
RCU_INIT_POINTER(tfile->tun, NULL);
|
2013-01-28 05:05:19 +04:00
|
|
|
}
|
2012-10-31 23:46:00 +04:00
|
|
|
BUG_ON(tun->numqueues != 0);
|
|
|
|
|
|
|
|
synchronize_net();
|
|
|
|
for (i = 0; i < n; i++) {
|
2013-01-11 20:59:32 +04:00
|
|
|
tfile = rtnl_dereference(tun->tfiles[i]);
|
2012-10-31 23:46:00 +04:00
|
|
|
/* Drop read queue */
|
2013-09-05 13:53:59 +04:00
|
|
|
tun_queue_purge(tfile);
|
2012-10-31 23:46:00 +04:00
|
|
|
sock_put(&tfile->sk);
|
|
|
|
}
|
2012-12-14 03:53:30 +04:00
|
|
|
list_for_each_entry_safe(tfile, tmp, &tun->disabled, next) {
|
|
|
|
tun_enable_queue(tfile);
|
2013-09-05 13:53:59 +04:00
|
|
|
tun_queue_purge(tfile);
|
2012-12-14 03:53:30 +04:00
|
|
|
sock_put(&tfile->sk);
|
|
|
|
}
|
|
|
|
BUG_ON(tun->numdisabled != 0);
|
2013-01-11 20:59:34 +04:00
|
|
|
|
2014-11-19 16:17:31 +03:00
|
|
|
if (tun->flags & IFF_PERSIST)
|
2013-01-11 20:59:34 +04:00
|
|
|
module_put(THIS_MODULE);
|
2012-10-31 23:46:00 +04:00
|
|
|
}
|
|
|
|
|
tun: Allow to skip filter on attach
There's a small problem with sk-filters on tun devices. Consider
an application doing this sequence of steps:
fd = open("/dev/net/tun");
ioctl(fd, TUNSETIFF, { .ifr_name = "tun0" });
ioctl(fd, TUNATTACHFILTER, &my_filter);
ioctl(fd, TUNSETPERSIST, 1);
close(fd);
At that point the tun0 will remain in the system and will keep in
mind that there should be a socket filter at address '&my_filter'.
If after that we do
fd = open("/dev/net/tun");
ioctl(fd, TUNSETIFF, { .ifr_name = "tun0" });
we most likely receive the -EFAULT error, since tun_attach() would
try to connect the filter back. But (!) if we provide a filter at
address &my_filter, then tun0 will be created and the "new" filter
would be attached, but application may not know about that.
This may create certain problems to anyone using tun-s, but it's
critical problem for c/r -- if we meet a persistent tun device
with a filter in mind, we will not be able to attach to it to dump
its state (flags, owner, address, vnethdr size, etc.).
The proposal is to allow to attach to tun device (with TUNSETIFF)
w/o attaching the filter to the tun-file's socket. After this
attach app may e.g clean the device by dropping the filter, it
doesn't want to have one, or (in case of c/r) get information
about the device with tun ioctls.
Signed-off-by: Pavel Emelyanov <xemul@parallels.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2013-08-21 14:32:21 +04:00
|
|
|
static int tun_attach(struct tun_struct *tun, struct file *file, bool skip_filter)
|
2009-01-20 13:57:48 +03:00
|
|
|
{
|
2009-01-20 14:00:40 +03:00
|
|
|
struct tun_file *tfile = file->private_data;
|
2009-01-20 14:02:28 +03:00
|
|
|
int err;
|
2009-01-20 13:57:48 +03:00
|
|
|
|
2013-01-14 11:12:19 +04:00
|
|
|
err = security_tun_dev_attach(tfile->socket.sk, tun->security);
|
|
|
|
if (err < 0)
|
|
|
|
goto out;
|
|
|
|
|
2009-01-20 14:02:28 +03:00
|
|
|
err = -EINVAL;
|
2013-01-28 05:05:19 +04:00
|
|
|
if (rtnl_dereference(tfile->tun) && !tfile->detached)
|
2009-01-20 14:02:28 +03:00
|
|
|
goto out;
|
|
|
|
|
|
|
|
err = -EBUSY;
|
2014-11-19 16:17:31 +03:00
|
|
|
if (!(tun->flags & IFF_MULTI_QUEUE) && tun->numqueues == 1)
|
2012-10-31 23:46:00 +04:00
|
|
|
goto out;
|
|
|
|
|
|
|
|
err = -E2BIG;
|
2012-12-14 03:53:30 +04:00
|
|
|
if (!tfile->detached &&
|
|
|
|
tun->numqueues + tun->numdisabled == MAX_TAP_QUEUES)
|
2009-01-20 14:02:28 +03:00
|
|
|
goto out;
|
|
|
|
|
|
|
|
err = 0;
|
2012-10-31 23:45:57 +04:00
|
|
|
|
2013-12-06 08:42:58 +04:00
|
|
|
/* Re-attach the filter to persist device */
|
tun: Allow to skip filter on attach
There's a small problem with sk-filters on tun devices. Consider
an application doing this sequence of steps:
fd = open("/dev/net/tun");
ioctl(fd, TUNSETIFF, { .ifr_name = "tun0" });
ioctl(fd, TUNATTACHFILTER, &my_filter);
ioctl(fd, TUNSETPERSIST, 1);
close(fd);
At that point the tun0 will remain in the system and will keep in
mind that there should be a socket filter at address '&my_filter'.
If after that we do
fd = open("/dev/net/tun");
ioctl(fd, TUNSETIFF, { .ifr_name = "tun0" });
we most likely receive the -EFAULT error, since tun_attach() would
try to connect the filter back. But (!) if we provide a filter at
address &my_filter, then tun0 will be created and the "new" filter
would be attached, but application may not know about that.
This may create certain problems to anyone using tun-s, but it's
critical problem for c/r -- if we meet a persistent tun device
with a filter in mind, we will not be able to attach to it to dump
its state (flags, owner, address, vnethdr size, etc.).
The proposal is to allow to attach to tun device (with TUNSETIFF)
w/o attaching the filter to the tun-file's socket. After this
attach app may e.g clean the device by dropping the filter, it
doesn't want to have one, or (in case of c/r) get information
about the device with tun ioctls.
Signed-off-by: Pavel Emelyanov <xemul@parallels.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2013-08-21 14:32:21 +04:00
|
|
|
if (!skip_filter && (tun->filter_attached == true)) {
|
2016-04-05 18:10:16 +03:00
|
|
|
lock_sock(tfile->socket.sk);
|
|
|
|
err = sk_attach_filter(&tun->fprog, tfile->socket.sk);
|
|
|
|
release_sock(tfile->socket.sk);
|
2012-10-31 23:45:57 +04:00
|
|
|
if (!err)
|
|
|
|
goto out;
|
|
|
|
}
|
2012-10-31 23:46:00 +04:00
|
|
|
tfile->queue_index = tun->numqueues;
|
2012-10-31 23:45:58 +04:00
|
|
|
rcu_assign_pointer(tfile->tun, tun);
|
2012-10-31 23:46:00 +04:00
|
|
|
rcu_assign_pointer(tun->tfiles[tun->numqueues], tfile);
|
|
|
|
tun->numqueues++;
|
2009-01-20 13:57:48 +03:00
|
|
|
|
2012-12-14 03:53:30 +04:00
|
|
|
if (tfile->detached)
|
|
|
|
tun_enable_queue(tfile);
|
|
|
|
else
|
|
|
|
sock_hold(&tfile->sk);
|
|
|
|
|
2012-10-31 23:46:00 +04:00
|
|
|
tun_set_real_num_queues(tun);
|
2009-01-20 13:57:48 +03:00
|
|
|
|
2012-10-31 23:46:00 +04:00
|
|
|
/* device is allowed to go away first, so no need to hold extra
|
|
|
|
* refcnt.
|
|
|
|
*/
|
|
|
|
|
|
|
|
out:
|
|
|
|
return err;
|
2009-01-20 14:00:40 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static struct tun_struct *__tun_get(struct tun_file *tfile)
|
|
|
|
{
|
2012-10-31 23:45:58 +04:00
|
|
|
struct tun_struct *tun;
|
2009-01-20 14:07:17 +03:00
|
|
|
|
2012-10-31 23:45:58 +04:00
|
|
|
rcu_read_lock();
|
|
|
|
tun = rcu_dereference(tfile->tun);
|
|
|
|
if (tun)
|
|
|
|
dev_hold(tun->dev);
|
|
|
|
rcu_read_unlock();
|
2009-01-20 14:07:17 +03:00
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
2012-10-31 23:45:58 +04:00
|
|
|
dev_put(tun->dev);
|
2009-01-20 14:00:40 +03:00
|
|
|
}
|
|
|
|
|
2011-03-02 10:18:10 +03:00
|
|
|
/* TAP filtering */
|
2008-07-15 09:18:19 +04:00
|
|
|
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 04:49:17 +03:00
|
|
|
/* Remaining multicast addresses are hashed,
|
|
|
|
* unicast will leave the filter disabled. */
|
2008-07-15 09:18:19 +04:00
|
|
|
memset(filter->mask, 0, sizeof(filter->mask));
|
2009-02-09 04:49:17 +03:00
|
|
|
for (; n < uf.count; n++) {
|
|
|
|
if (!is_multicast_ether_addr(addr[n].u)) {
|
|
|
|
err = 0; /* no filter */
|
|
|
|
goto done;
|
|
|
|
}
|
2008-07-15 09:18:19 +04:00
|
|
|
addr_hash_set(filter->mask, addr[n].u);
|
2009-02-09 04:49:17 +03:00
|
|
|
}
|
2008-07-15 09:18:19 +04: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++)
|
drivers/net: Convert compare_ether_addr to ether_addr_equal
Use the new bool function ether_addr_equal to add
some clarity and reduce the likelihood for misuse
of compare_ether_addr for sorting.
Done via cocci script:
$ cat compare_ether_addr.cocci
@@
expression a,b;
@@
- !compare_ether_addr(a, b)
+ ether_addr_equal(a, b)
@@
expression a,b;
@@
- compare_ether_addr(a, b)
+ !ether_addr_equal(a, b)
@@
expression a,b;
@@
- !ether_addr_equal(a, b) == 0
+ ether_addr_equal(a, b)
@@
expression a,b;
@@
- !ether_addr_equal(a, b) != 0
+ !ether_addr_equal(a, b)
@@
expression a,b;
@@
- ether_addr_equal(a, b) == 0
+ !ether_addr_equal(a, b)
@@
expression a,b;
@@
- ether_addr_equal(a, b) != 0
+ ether_addr_equal(a, b)
@@
expression a,b;
@@
- !!ether_addr_equal(a, b)
+ ether_addr_equal(a, b)
Signed-off-by: Joe Perches <joe@perches.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2012-05-09 21:17:46 +04:00
|
|
|
if (ether_addr_equal(eh->h_dest, filter->addr[i]))
|
2008-07-15 09:18:19 +04:00
|
|
|
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)
|
|
|
|
{
|
2012-10-31 23:46:00 +04:00
|
|
|
tun_detach_all(dev);
|
2009-01-20 14:07:17 +03:00
|
|
|
}
|
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
/* Net device open. */
|
|
|
|
static int tun_net_open(struct net_device *dev)
|
|
|
|
{
|
2012-10-31 23:46:00 +04:00
|
|
|
netif_tx_start_all_queues(dev);
|
2005-04-17 02:20:36 +04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Net device close. */
|
|
|
|
static int tun_net_close(struct net_device *dev)
|
|
|
|
{
|
2012-10-31 23:46:00 +04:00
|
|
|
netif_tx_stop_all_queues(dev);
|
2005-04-17 02:20:36 +04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Net device start xmit */
|
2009-08-31 23:50:51 +04:00
|
|
|
static netdev_tx_t tun_net_xmit(struct sk_buff *skb, struct net_device *dev)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
|
|
|
struct tun_struct *tun = netdev_priv(dev);
|
2012-10-31 23:46:00 +04:00
|
|
|
int txq = skb->queue_mapping;
|
2012-10-31 23:45:58 +04:00
|
|
|
struct tun_file *tfile;
|
2014-01-22 07:03:23 +04:00
|
|
|
u32 numqueues = 0;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2012-10-31 23:45:58 +04:00
|
|
|
rcu_read_lock();
|
2012-10-31 23:46:00 +04:00
|
|
|
tfile = rcu_dereference(tun->tfiles[txq]);
|
2014-01-22 07:03:23 +04:00
|
|
|
numqueues = ACCESS_ONCE(tun->numqueues);
|
2012-10-31 23:46:00 +04:00
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
/* Drop packet if interface is not attached */
|
2014-01-22 07:03:23 +04:00
|
|
|
if (txq >= numqueues)
|
2005-04-17 02:20:36 +04:00
|
|
|
goto drop;
|
|
|
|
|
2014-01-22 07:03:23 +04:00
|
|
|
if (numqueues == 1) {
|
2013-12-22 14:54:32 +04:00
|
|
|
/* Select queue was not called for the skbuff, so we extract the
|
|
|
|
* RPS hash and save it into the flow_table here.
|
|
|
|
*/
|
|
|
|
__u32 rxhash;
|
|
|
|
|
|
|
|
rxhash = skb_get_hash(skb);
|
|
|
|
if (rxhash) {
|
|
|
|
struct tun_flow_entry *e;
|
|
|
|
e = tun_flow_find(&tun->flows[tun_hashfn(rxhash)],
|
|
|
|
rxhash);
|
|
|
|
if (e)
|
|
|
|
tun_flow_save_rps_rxhash(e, rxhash);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-10-31 23:45:58 +04:00
|
|
|
tun_debug(KERN_INFO, tun, "tun_net_xmit %d\n", skb->len);
|
|
|
|
|
2012-10-31 23:46:00 +04:00
|
|
|
BUG_ON(!tfile);
|
|
|
|
|
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;
|
|
|
|
|
2012-10-31 23:45:57 +04:00
|
|
|
if (tfile->socket.sk->sk_filter &&
|
|
|
|
sk_filter(tfile->socket.sk, skb))
|
2010-02-14 04:01:10 +03:00
|
|
|
goto drop;
|
|
|
|
|
2012-11-26 02:07:40 +04:00
|
|
|
/* Limit the number of packets queued by dividing txq length with the
|
2012-10-31 23:46:00 +04:00
|
|
|
* number of queues.
|
|
|
|
*/
|
2014-01-22 07:03:23 +04:00
|
|
|
if (skb_queue_len(&tfile->socket.sk->sk_receive_queue) * numqueues
|
|
|
|
>= dev->tx_queue_len)
|
2012-12-03 14:07:14 +04:00
|
|
|
goto drop;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2013-09-05 13:54:00 +04:00
|
|
|
if (unlikely(skb_orphan_frags(skb, GFP_ATOMIC)))
|
|
|
|
goto drop;
|
|
|
|
|
2015-10-10 01:42:21 +03:00
|
|
|
if (skb->sk && sk_fullsock(skb->sk)) {
|
2016-04-03 06:08:12 +03:00
|
|
|
sock_tx_timestamp(skb->sk, skb->sk->sk_tsflags,
|
|
|
|
&skb_shinfo(skb)->tx_flags);
|
2013-07-19 21:40:10 +04:00
|
|
|
sw_tx_timestamp(skb);
|
|
|
|
}
|
|
|
|
|
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 08:59:44 +04:00
|
|
|
/* Orphan the skb - required as we might hang on to it
|
2013-09-05 13:54:00 +04:00
|
|
|
* for indefinite time.
|
|
|
|
*/
|
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 08:59:44 +04:00
|
|
|
skb_orphan(skb);
|
|
|
|
|
2013-03-06 15:02:37 +04:00
|
|
|
nf_reset(skb);
|
|
|
|
|
2008-07-15 09:18:19 +04:00
|
|
|
/* Enqueue packet */
|
2012-10-31 23:45:57 +04:00
|
|
|
skb_queue_tail(&tfile->socket.sk->sk_receive_queue, skb);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
/* Notify and wake up reader process */
|
2012-10-31 23:45:57 +04:00
|
|
|
if (tfile->flags & TUN_FASYNC)
|
|
|
|
kill_fasync(&tfile->fasync, SIGIO, POLL_IN);
|
2014-05-17 02:11:48 +04:00
|
|
|
tfile->socket.sk->sk_data_ready(tfile->socket.sk);
|
2012-10-31 23:45:58 +04:00
|
|
|
|
|
|
|
rcu_read_unlock();
|
2009-06-23 10:03:08 +04:00
|
|
|
return NETDEV_TX_OK;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
drop:
|
2016-04-13 11:52:20 +03:00
|
|
|
this_cpu_inc(tun->pcpu_stats->tx_dropped);
|
2012-11-01 13:16:32 +04:00
|
|
|
skb_tx_error(skb);
|
2005-04-17 02:20:36 +04:00
|
|
|
kfree_skb(skb);
|
2012-10-31 23:45:58 +04:00
|
|
|
rcu_read_unlock();
|
2014-11-18 08:20:41 +03:00
|
|
|
return NET_XMIT_DROP;
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
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.
|
|
|
|
*/
|
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;
|
|
|
|
}
|
|
|
|
|
2011-11-15 19:29:55 +04:00
|
|
|
static netdev_features_t tun_net_fix_features(struct net_device *dev,
|
|
|
|
netdev_features_t features)
|
2011-04-19 10:13:10 +04:00
|
|
|
{
|
|
|
|
struct tun_struct *tun = netdev_priv(dev);
|
|
|
|
|
|
|
|
return (features & tun->set_features) | (features & ~TUN_USER_FEATURES);
|
|
|
|
}
|
2011-06-15 09:25:01 +04:00
|
|
|
#ifdef CONFIG_NET_POLL_CONTROLLER
|
|
|
|
static void tun_poll_controller(struct net_device *dev)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Tun only receives frames when:
|
|
|
|
* 1) the char device endpoint gets data from user space
|
|
|
|
* 2) the tun socket gets a sendmsg call from user space
|
2013-12-06 08:42:58 +04:00
|
|
|
* Since both of those are synchronous operations, we are guaranteed
|
2011-06-15 09:25:01 +04:00
|
|
|
* never to have pending data when we poll for it
|
2013-12-06 08:42:58 +04:00
|
|
|
* so there is nothing to do here but return.
|
2011-06-15 09:25:01 +04:00
|
|
|
* We need this though so netpoll recognizes us as an interface that
|
|
|
|
* supports polling, which enables bridge devices in virt setups to
|
|
|
|
* still use netconsole
|
|
|
|
*/
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
#endif
|
2016-02-26 12:45:40 +03:00
|
|
|
|
|
|
|
static void tun_set_headroom(struct net_device *dev, int new_hr)
|
|
|
|
{
|
|
|
|
struct tun_struct *tun = netdev_priv(dev);
|
|
|
|
|
|
|
|
if (new_hr < NET_SKB_PAD)
|
|
|
|
new_hr = NET_SKB_PAD;
|
|
|
|
|
|
|
|
tun->align = new_hr;
|
|
|
|
}
|
|
|
|
|
2016-04-13 11:52:20 +03:00
|
|
|
static struct rtnl_link_stats64 *
|
|
|
|
tun_net_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
|
|
|
|
{
|
|
|
|
u32 rx_dropped = 0, tx_dropped = 0, rx_frame_errors = 0;
|
|
|
|
struct tun_struct *tun = netdev_priv(dev);
|
|
|
|
struct tun_pcpu_stats *p;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for_each_possible_cpu(i) {
|
|
|
|
u64 rxpackets, rxbytes, txpackets, txbytes;
|
|
|
|
unsigned int start;
|
|
|
|
|
|
|
|
p = per_cpu_ptr(tun->pcpu_stats, i);
|
|
|
|
do {
|
|
|
|
start = u64_stats_fetch_begin(&p->syncp);
|
|
|
|
rxpackets = p->rx_packets;
|
|
|
|
rxbytes = p->rx_bytes;
|
|
|
|
txpackets = p->tx_packets;
|
|
|
|
txbytes = p->tx_bytes;
|
|
|
|
} while (u64_stats_fetch_retry(&p->syncp, start));
|
|
|
|
|
|
|
|
stats->rx_packets += rxpackets;
|
|
|
|
stats->rx_bytes += rxbytes;
|
|
|
|
stats->tx_packets += txpackets;
|
|
|
|
stats->tx_bytes += txbytes;
|
|
|
|
|
|
|
|
/* u32 counters */
|
|
|
|
rx_dropped += p->rx_dropped;
|
|
|
|
rx_frame_errors += p->rx_frame_errors;
|
|
|
|
tx_dropped += p->tx_dropped;
|
|
|
|
}
|
|
|
|
stats->rx_dropped = rx_dropped;
|
|
|
|
stats->rx_frame_errors = rx_frame_errors;
|
|
|
|
stats->tx_dropped = tx_dropped;
|
|
|
|
return stats;
|
|
|
|
}
|
|
|
|
|
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,
|
2011-04-19 10:13:10 +04:00
|
|
|
.ndo_fix_features = tun_net_fix_features,
|
2012-10-31 23:46:00 +04:00
|
|
|
.ndo_select_queue = tun_select_queue,
|
2011-06-15 09:25:01 +04:00
|
|
|
#ifdef CONFIG_NET_POLL_CONTROLLER
|
|
|
|
.ndo_poll_controller = tun_poll_controller,
|
|
|
|
#endif
|
2016-02-26 12:45:40 +03:00
|
|
|
.ndo_set_rx_headroom = tun_set_headroom,
|
2016-04-13 11:52:20 +03:00
|
|
|
.ndo_get_stats64 = tun_net_get_stats64,
|
2008-11-20 09:10:37 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
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,
|
2011-04-19 10:13:10 +04:00
|
|
|
.ndo_fix_features = tun_net_fix_features,
|
2011-08-16 10:29:01 +04:00
|
|
|
.ndo_set_rx_mode = tun_net_mclist,
|
2008-11-20 09:10:37 +03:00
|
|
|
.ndo_set_mac_address = eth_mac_addr,
|
|
|
|
.ndo_validate_addr = eth_validate_addr,
|
2012-10-31 23:46:00 +04:00
|
|
|
.ndo_select_queue = tun_select_queue,
|
2011-06-15 09:25:01 +04:00
|
|
|
#ifdef CONFIG_NET_POLL_CONTROLLER
|
|
|
|
.ndo_poll_controller = tun_poll_controller,
|
|
|
|
#endif
|
2015-07-31 09:03:27 +03:00
|
|
|
.ndo_features_check = passthru_features_check,
|
2016-02-26 12:45:40 +03:00
|
|
|
.ndo_set_rx_headroom = tun_set_headroom,
|
2016-04-13 11:52:20 +03:00
|
|
|
.ndo_get_stats64 = tun_net_get_stats64,
|
2008-11-20 09:10:37 +03:00
|
|
|
};
|
|
|
|
|
2013-06-11 17:01:08 +04:00
|
|
|
static void tun_flow_init(struct tun_struct *tun)
|
2012-10-31 23:46:02 +04:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < TUN_NUM_FLOW_ENTRIES; i++)
|
|
|
|
INIT_HLIST_HEAD(&tun->flows[i]);
|
|
|
|
|
|
|
|
tun->ageing_time = TUN_FLOW_EXPIRE;
|
|
|
|
setup_timer(&tun->flow_gc_timer, tun_flow_cleanup, (unsigned long)tun);
|
|
|
|
mod_timer(&tun->flow_gc_timer,
|
|
|
|
round_jiffies_up(jiffies + tun->ageing_time));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void tun_flow_uninit(struct tun_struct *tun)
|
|
|
|
{
|
|
|
|
del_timer_sync(&tun->flow_gc_timer);
|
|
|
|
tun_flow_flush(tun);
|
|
|
|
}
|
|
|
|
|
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) {
|
2014-11-19 16:17:31 +03:00
|
|
|
case IFF_TUN:
|
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;
|
|
|
|
break;
|
|
|
|
|
2014-11-19 16:17:31 +03:00
|
|
|
case IFF_TAP:
|
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);
|
2011-07-26 10:05:38 +04:00
|
|
|
dev->priv_flags &= ~IFF_TX_SKB_SHARING;
|
2012-12-10 19:16:00 +04:00
|
|
|
dev->priv_flags |= IFF_LIVE_ADDR_CHANGE;
|
2007-04-26 12:00:55 +04:00
|
|
|
|
2012-02-15 10:45:39 +04:00
|
|
|
eth_hw_addr_random(dev);
|
2007-04-26 12:00:55 +04:00
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Character device part */
|
|
|
|
|
|
|
|
/* Poll */
|
2012-10-31 23:46:00 +04:00
|
|
|
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);
|
2009-07-05 23:48:35 +04:00
|
|
|
struct sock *sk;
|
2009-02-06 08:25:32 +03:00
|
|
|
unsigned int mask = 0;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
if (!tun)
|
2009-01-20 13:59:05 +03:00
|
|
|
return POLLERR;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2012-10-31 23:45:57 +04:00
|
|
|
sk = tfile->socket.sk;
|
2009-07-05 23:48:35 +04:00
|
|
|
|
2011-03-02 10:18:10 +03:00
|
|
|
tun_debug(KERN_INFO, tun, "tun_chr_poll\n");
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2014-05-17 02:11:48 +04:00
|
|
|
poll_wait(file, sk_sleep(sk), wait);
|
2006-09-13 21:24:59 +04:00
|
|
|
|
2009-08-30 11:04:42 +04:00
|
|
|
if (!skb_queue_empty(&sk->sk_receive_queue))
|
2005-04-17 02:20:36 +04:00
|
|
|
mask |= POLLIN | POLLRDNORM;
|
|
|
|
|
2009-02-06 08:25:32 +03:00
|
|
|
if (sock_writeable(sk) ||
|
2015-11-30 07:03:10 +03:00
|
|
|
(!test_and_set_bit(SOCKWQ_ASYNC_NOSPACE, &sk->sk_socket->flags) &&
|
2009-02-06 08:25:32 +03:00
|
|
|
sock_writeable(sk)))
|
|
|
|
mask |= POLLOUT | POLLWRNORM;
|
|
|
|
|
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). */
|
2012-10-31 23:45:57 +04:00
|
|
|
static struct sk_buff *tun_alloc_skb(struct tun_file *tfile,
|
2011-06-08 18:33:08 +04:00
|
|
|
size_t prepad, size_t len,
|
|
|
|
size_t linear, int noblock)
|
2008-08-16 02:15:10 +04:00
|
|
|
{
|
2012-10-31 23:45:57 +04:00
|
|
|
struct sock *sk = tfile->socket.sk;
|
2008-08-16 02:15:10 +04:00
|
|
|
struct sk_buff *skb;
|
2009-02-06 08:25:32 +03:00
|
|
|
int err;
|
2008-08-16 02:15:10 +04:00
|
|
|
|
|
|
|
/* Under a page? Don't bother with paged skb. */
|
2009-04-14 13:09:43 +04:00
|
|
|
if (prepad + len < PAGE_SIZE || !linear)
|
2009-02-06 08:25:32 +03:00
|
|
|
linear = len;
|
2008-08-16 02:15:10 +04:00
|
|
|
|
2009-02-06 08:25:32 +03:00
|
|
|
skb = sock_alloc_send_pskb(sk, prepad + linear, len - linear, noblock,
|
2013-08-09 01:38:47 +04:00
|
|
|
&err, 0);
|
2008-08-16 02:15:10 +04:00
|
|
|
if (!skb)
|
2009-02-06 08:25:32 +03:00
|
|
|
return ERR_PTR(err);
|
2008-08-16 02:15:10 +04:00
|
|
|
|
|
|
|
skb_reserve(skb, prepad);
|
|
|
|
skb_put(skb, linear);
|
2009-02-06 08:25:32 +03:00
|
|
|
skb->data_len = len - linear;
|
|
|
|
skb->len += len - linear;
|
2008-08-16 02:15:10 +04:00
|
|
|
|
|
|
|
return skb;
|
|
|
|
}
|
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
/* Get packet from user space buffer */
|
2012-10-31 23:45:57 +04:00
|
|
|
static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile,
|
2014-06-19 23:36:49 +04:00
|
|
|
void *msg_control, struct iov_iter *from,
|
|
|
|
int noblock)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
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;
|
2014-06-19 23:36:49 +04:00
|
|
|
size_t total_len = iov_iter_count(from);
|
2016-02-26 12:45:40 +03:00
|
|
|
size_t len = total_len, align = tun->align, linear;
|
2008-07-03 14:48:02 +04:00
|
|
|
struct virtio_net_hdr gso = { 0 };
|
2016-04-13 11:52:20 +03:00
|
|
|
struct tun_pcpu_stats *stats;
|
2013-11-13 10:00:39 +04:00
|
|
|
int good_linear;
|
2012-07-20 13:23:23 +04:00
|
|
|
int copylen;
|
|
|
|
bool zerocopy = false;
|
|
|
|
int err;
|
2012-12-12 23:22:57 +04:00
|
|
|
u32 rxhash;
|
2014-06-19 23:36:49 +04:00
|
|
|
ssize_t n;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2015-12-16 19:57:37 +03:00
|
|
|
if (!(tun->dev->flags & IFF_UP))
|
|
|
|
return -EIO;
|
|
|
|
|
2014-11-19 16:17:31 +03:00
|
|
|
if (!(tun->flags & IFF_NO_PI)) {
|
2013-08-15 16:52:57 +04:00
|
|
|
if (len < sizeof(pi))
|
2005-04-17 02:20:36 +04:00
|
|
|
return -EINVAL;
|
2013-08-15 16:52:57 +04:00
|
|
|
len -= sizeof(pi);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2014-06-19 23:36:49 +04:00
|
|
|
n = copy_from_iter(&pi, sizeof(pi), from);
|
|
|
|
if (n != sizeof(pi))
|
2005-04-17 02:20:36 +04:00
|
|
|
return -EFAULT;
|
|
|
|
}
|
|
|
|
|
2014-11-19 16:17:31 +03:00
|
|
|
if (tun->flags & IFF_VNET_HDR) {
|
2013-08-15 16:52:57 +04:00
|
|
|
if (len < tun->vnet_hdr_sz)
|
2008-07-03 14:48:02 +04:00
|
|
|
return -EINVAL;
|
2013-08-15 16:52:57 +04:00
|
|
|
len -= tun->vnet_hdr_sz;
|
2008-07-03 14:48:02 +04:00
|
|
|
|
2014-06-19 23:36:49 +04:00
|
|
|
n = copy_from_iter(&gso, sizeof(gso), from);
|
|
|
|
if (n != sizeof(gso))
|
2008-07-03 14:48:02 +04:00
|
|
|
return -EFAULT;
|
|
|
|
|
2009-06-08 11:20:01 +04:00
|
|
|
if ((gso.flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) &&
|
2014-10-23 23:59:31 +04:00
|
|
|
tun16_to_cpu(tun, gso.csum_start) + tun16_to_cpu(tun, gso.csum_offset) + 2 > tun16_to_cpu(tun, gso.hdr_len))
|
|
|
|
gso.hdr_len = cpu_to_tun16(tun, tun16_to_cpu(tun, gso.csum_start) + tun16_to_cpu(tun, gso.csum_offset) + 2);
|
2009-06-08 11:20:01 +04:00
|
|
|
|
2014-10-23 23:59:31 +04:00
|
|
|
if (tun16_to_cpu(tun, gso.hdr_len) > len)
|
2008-07-03 14:48:02 +04:00
|
|
|
return -EINVAL;
|
2014-11-30 13:03:31 +03:00
|
|
|
iov_iter_advance(from, tun->vnet_hdr_sz - sizeof(gso));
|
2008-07-03 14:48:02 +04:00
|
|
|
}
|
|
|
|
|
2014-11-19 16:17:31 +03:00
|
|
|
if ((tun->flags & TUN_TYPE_MASK) == IFF_TAP) {
|
2011-06-08 18:33:07 +04:00
|
|
|
align += NET_IP_ALIGN;
|
2009-04-14 13:09:43 +04:00
|
|
|
if (unlikely(len < ETH_HLEN ||
|
2014-10-23 23:59:31 +04:00
|
|
|
(gso.hdr_len && tun16_to_cpu(tun, gso.hdr_len) < ETH_HLEN)))
|
2008-04-13 05:49:30 +04:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
2006-09-13 21:24:59 +04:00
|
|
|
|
2013-11-13 10:00:39 +04:00
|
|
|
good_linear = SKB_MAX_HEAD(align);
|
|
|
|
|
2013-07-18 06:55:15 +04:00
|
|
|
if (msg_control) {
|
2014-06-19 23:36:49 +04:00
|
|
|
struct iov_iter i = *from;
|
|
|
|
|
2013-07-18 06:55:15 +04:00
|
|
|
/* There are 256 bytes to be copied in skb, so there is
|
|
|
|
* enough room for skb expand head in case it is used.
|
2012-07-20 13:23:23 +04:00
|
|
|
* The rest of the buffer is mapped from userspace.
|
|
|
|
*/
|
2014-10-23 23:59:31 +04:00
|
|
|
copylen = gso.hdr_len ? tun16_to_cpu(tun, gso.hdr_len) : GOODCOPY_LEN;
|
2013-11-13 10:00:39 +04:00
|
|
|
if (copylen > good_linear)
|
|
|
|
copylen = good_linear;
|
2013-07-10 09:43:27 +04:00
|
|
|
linear = copylen;
|
2014-06-19 23:36:49 +04:00
|
|
|
iov_iter_advance(&i, copylen);
|
|
|
|
if (iov_iter_npages(&i, INT_MAX) <= MAX_SKB_FRAGS)
|
2013-07-18 06:55:15 +04:00
|
|
|
zerocopy = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!zerocopy) {
|
2012-07-20 13:23:23 +04:00
|
|
|
copylen = len;
|
2014-10-23 23:59:31 +04:00
|
|
|
if (tun16_to_cpu(tun, gso.hdr_len) > good_linear)
|
2013-11-13 10:00:39 +04:00
|
|
|
linear = good_linear;
|
|
|
|
else
|
2014-10-23 23:59:31 +04:00
|
|
|
linear = tun16_to_cpu(tun, gso.hdr_len);
|
2013-07-10 09:43:27 +04:00
|
|
|
}
|
2012-07-20 13:23:23 +04:00
|
|
|
|
2013-07-10 09:43:27 +04:00
|
|
|
skb = tun_alloc_skb(tfile, align, copylen, linear, noblock);
|
2009-02-06 08:25:32 +03:00
|
|
|
if (IS_ERR(skb)) {
|
|
|
|
if (PTR_ERR(skb) != -EAGAIN)
|
2016-04-13 11:52:20 +03:00
|
|
|
this_cpu_inc(tun->pcpu_stats->rx_dropped);
|
2009-02-06 08:25:32 +03:00
|
|
|
return PTR_ERR(skb);
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
2012-07-20 13:23:23 +04:00
|
|
|
if (zerocopy)
|
2014-06-19 23:36:49 +04:00
|
|
|
err = zerocopy_sg_from_iter(skb, from);
|
2013-07-18 06:55:15 +04:00
|
|
|
else {
|
2014-06-19 23:36:49 +04:00
|
|
|
err = skb_copy_datagram_from_iter(skb, 0, from, len);
|
2013-07-18 06:55:15 +04:00
|
|
|
if (!err && msg_control) {
|
|
|
|
struct ubuf_info *uarg = msg_control;
|
|
|
|
uarg->callback(uarg, false);
|
|
|
|
}
|
|
|
|
}
|
2012-07-20 13:23:23 +04:00
|
|
|
|
|
|
|
if (err) {
|
2016-04-13 11:52:20 +03:00
|
|
|
this_cpu_inc(tun->pcpu_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) {
|
2014-10-23 23:59:31 +04:00
|
|
|
if (!skb_partial_csum_set(skb, tun16_to_cpu(tun, gso.csum_start),
|
|
|
|
tun16_to_cpu(tun, gso.csum_offset))) {
|
2016-04-13 11:52:20 +03:00
|
|
|
this_cpu_inc(tun->pcpu_stats->rx_frame_errors);
|
2008-07-03 14:48:02 +04:00
|
|
|
kfree_skb(skb);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2011-04-19 10:13:10 +04:00
|
|
|
}
|
2008-07-03 14:48:02 +04:00
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
switch (tun->flags & TUN_TYPE_MASK) {
|
2014-11-19 16:17:31 +03:00
|
|
|
case IFF_TUN:
|
|
|
|
if (tun->flags & IFF_NO_PI) {
|
2008-06-18 08:10:33 +04:00
|
|
|
switch (skb->data[0] & 0xf0) {
|
|
|
|
case 0x40:
|
|
|
|
pi.proto = htons(ETH_P_IP);
|
|
|
|
break;
|
|
|
|
case 0x60:
|
|
|
|
pi.proto = htons(ETH_P_IPV6);
|
|
|
|
break;
|
|
|
|
default:
|
2016-04-13 11:52:20 +03:00
|
|
|
this_cpu_inc(tun->pcpu_stats->rx_dropped);
|
2008-06-18 08:10:33 +04:00
|
|
|
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;
|
2014-11-19 16:17:31 +03:00
|
|
|
case IFF_TAP:
|
2005-04-17 02:20:36 +04:00
|
|
|
skb->protocol = eth_type_trans(skb, tun->dev);
|
|
|
|
break;
|
2011-06-03 15:51:20 +04:00
|
|
|
}
|
2005-04-17 02:20:36 +04:00
|
|
|
|
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:
|
2013-02-11 13:27:41 +04:00
|
|
|
skb_shinfo(skb)->gso_type = SKB_GSO_TCPV4;
|
2008-07-03 14:48:02 +04:00
|
|
|
break;
|
|
|
|
case VIRTIO_NET_HDR_GSO_TCPV6:
|
2013-02-11 13:27:41 +04:00
|
|
|
skb_shinfo(skb)->gso_type = SKB_GSO_TCPV6;
|
2008-07-03 14:48:02 +04:00
|
|
|
break;
|
2009-07-14 18:21:04 +04:00
|
|
|
case VIRTIO_NET_HDR_GSO_UDP:
|
2013-02-11 13:27:41 +04:00
|
|
|
skb_shinfo(skb)->gso_type = SKB_GSO_UDP;
|
2009-07-14 18:21:04 +04:00
|
|
|
break;
|
2008-07-03 14:48:02 +04:00
|
|
|
default:
|
2016-04-13 11:52:20 +03:00
|
|
|
this_cpu_inc(tun->pcpu_stats->rx_frame_errors);
|
2008-07-03 14:48:02 +04:00
|
|
|
kfree_skb(skb);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (gso.gso_type & VIRTIO_NET_HDR_GSO_ECN)
|
2013-02-11 13:27:41 +04:00
|
|
|
skb_shinfo(skb)->gso_type |= SKB_GSO_TCP_ECN;
|
2008-07-03 14:48:02 +04:00
|
|
|
|
2014-10-23 23:59:31 +04:00
|
|
|
skb_shinfo(skb)->gso_size = tun16_to_cpu(tun, gso.gso_size);
|
2008-07-03 14:48:02 +04:00
|
|
|
if (skb_shinfo(skb)->gso_size == 0) {
|
2016-04-13 11:52:20 +03:00
|
|
|
this_cpu_inc(tun->pcpu_stats->rx_frame_errors);
|
2008-07-03 14:48:02 +04:00
|
|
|
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
|
|
|
|
2012-07-20 13:23:23 +04:00
|
|
|
/* copy skb_ubuf_info for callback when skb has no error */
|
|
|
|
if (zerocopy) {
|
|
|
|
skb_shinfo(skb)->destructor_arg = msg_control;
|
|
|
|
skb_shinfo(skb)->tx_flags |= SKBTX_DEV_ZEROCOPY;
|
2013-02-11 13:27:41 +04:00
|
|
|
skb_shinfo(skb)->tx_flags |= SKBTX_SHARED_FRAG;
|
2012-07-20 13:23:23 +04:00
|
|
|
}
|
|
|
|
|
2015-02-04 00:36:16 +03:00
|
|
|
skb_reset_network_header(skb);
|
2013-03-27 03:11:22 +04:00
|
|
|
skb_probe_transport_header(skb, 0);
|
2013-03-26 00:19:56 +04:00
|
|
|
|
2013-12-16 10:12:06 +04:00
|
|
|
rxhash = skb_get_hash(skb);
|
2005-04-17 02:20:36 +04:00
|
|
|
netif_rx_ni(skb);
|
2006-09-13 21:24:59 +04:00
|
|
|
|
2016-04-13 11:52:20 +03:00
|
|
|
stats = get_cpu_ptr(tun->pcpu_stats);
|
|
|
|
u64_stats_update_begin(&stats->syncp);
|
|
|
|
stats->rx_packets++;
|
|
|
|
stats->rx_bytes += len;
|
|
|
|
u64_stats_update_end(&stats->syncp);
|
|
|
|
put_cpu_ptr(stats);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2013-01-28 05:05:19 +04:00
|
|
|
tun_flow_update(tun, rxhash, tfile);
|
2012-07-20 13:23:23 +04:00
|
|
|
return total_len;
|
2006-09-13 21:24:59 +04:00
|
|
|
}
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2014-06-19 23:36:49 +04:00
|
|
|
static ssize_t tun_chr_write_iter(struct kiocb *iocb, struct iov_iter *from)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
2009-02-06 08:25:32 +03:00
|
|
|
struct file *file = iocb->ki_filp;
|
2009-02-15 07:46:39 +03:00
|
|
|
struct tun_struct *tun = tun_get(file);
|
2012-10-31 23:45:57 +04:00
|
|
|
struct tun_file *tfile = file->private_data;
|
2009-01-20 14:00:40 +03:00
|
|
|
ssize_t result;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
if (!tun)
|
|
|
|
return -EBADFD;
|
|
|
|
|
2014-06-19 23:36:49 +04:00
|
|
|
result = tun_get_user(tun, tfile, NULL, from, file->f_flags & O_NONBLOCK);
|
2009-01-20 14:00:40 +03:00
|
|
|
|
|
|
|
tun_put(tun);
|
|
|
|
return result;
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Put packet to the user space buffer */
|
2011-06-08 18:33:08 +04:00
|
|
|
static ssize_t tun_put_user(struct tun_struct *tun,
|
2012-10-31 23:45:57 +04:00
|
|
|
struct tun_file *tfile,
|
2011-06-08 18:33:08 +04:00
|
|
|
struct sk_buff *skb,
|
2014-11-07 16:22:23 +03:00
|
|
|
struct iov_iter *iter)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
|
|
|
struct tun_pi pi = { 0, skb->protocol };
|
2016-04-13 11:52:20 +03:00
|
|
|
struct tun_pcpu_stats *stats;
|
2014-11-07 16:22:23 +03:00
|
|
|
ssize_t total;
|
2014-11-13 11:54:14 +03:00
|
|
|
int vlan_offset = 0;
|
2014-11-02 23:30:13 +03:00
|
|
|
int vlan_hlen = 0;
|
2014-11-02 23:30:14 +03:00
|
|
|
int vnet_hdr_sz = 0;
|
2014-11-02 23:30:13 +03:00
|
|
|
|
2015-01-13 19:13:44 +03:00
|
|
|
if (skb_vlan_tag_present(skb))
|
2014-11-02 23:30:13 +03:00
|
|
|
vlan_hlen = VLAN_HLEN;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2014-11-19 16:17:31 +03:00
|
|
|
if (tun->flags & IFF_VNET_HDR)
|
2014-11-02 23:30:14 +03:00
|
|
|
vnet_hdr_sz = tun->vnet_hdr_sz;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2014-11-07 16:22:23 +03:00
|
|
|
total = skb->len + vlan_hlen + vnet_hdr_sz;
|
|
|
|
|
2014-11-19 16:17:31 +03:00
|
|
|
if (!(tun->flags & IFF_NO_PI)) {
|
2014-11-07 16:22:23 +03:00
|
|
|
if (iov_iter_count(iter) < sizeof(pi))
|
2005-04-17 02:20:36 +04:00
|
|
|
return -EINVAL;
|
|
|
|
|
2014-11-07 16:22:23 +03:00
|
|
|
total += sizeof(pi);
|
|
|
|
if (iov_iter_count(iter) < total) {
|
2005-04-17 02:20:36 +04:00
|
|
|
/* Packet will be striped */
|
|
|
|
pi.flags |= TUN_PKT_STRIP;
|
|
|
|
}
|
2006-09-13 21:24:59 +04:00
|
|
|
|
2014-11-07 16:22:23 +03:00
|
|
|
if (copy_to_iter(&pi, sizeof(pi), iter) != sizeof(pi))
|
2005-04-17 02:20:36 +04:00
|
|
|
return -EFAULT;
|
2006-09-13 21:24:59 +04:00
|
|
|
}
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2014-11-02 23:30:14 +03:00
|
|
|
if (vnet_hdr_sz) {
|
2008-07-03 14:48:02 +04:00
|
|
|
struct virtio_net_hdr gso = { 0 }; /* no info leak */
|
2014-11-07 16:22:23 +03:00
|
|
|
if (iov_iter_count(iter) < vnet_hdr_sz)
|
2008-07-03 14:48:02 +04: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. */
|
2014-10-23 23:59:31 +04:00
|
|
|
gso.hdr_len = cpu_to_tun16(tun, skb_headlen(skb));
|
|
|
|
gso.gso_size = cpu_to_tun16(tun, sinfo->gso_size);
|
2008-07-03 14:48:02 +04:00
|
|
|
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;
|
2015-02-04 00:36:17 +03:00
|
|
|
else if (sinfo->gso_type & SKB_GSO_UDP)
|
|
|
|
gso.gso_type = VIRTIO_NET_HDR_GSO_UDP;
|
2010-07-21 08:32:45 +04:00
|
|
|
else {
|
2011-03-02 10:18:10 +03:00
|
|
|
pr_err("unexpected GSO type: "
|
2010-07-21 08:32:45 +04:00
|
|
|
"0x%x, gso_size %d, hdr_len %d\n",
|
2014-10-23 23:59:31 +04:00
|
|
|
sinfo->gso_type, tun16_to_cpu(tun, gso.gso_size),
|
|
|
|
tun16_to_cpu(tun, gso.hdr_len));
|
2010-07-21 08:32:45 +04:00
|
|
|
print_hex_dump(KERN_ERR, "tun: ",
|
|
|
|
DUMP_PREFIX_NONE,
|
|
|
|
16, 1, skb->head,
|
2014-10-23 23:59:31 +04:00
|
|
|
min((int)tun16_to_cpu(tun, gso.hdr_len), 64), true);
|
2010-07-21 08:32:45 +04:00
|
|
|
WARN_ON_ONCE(1);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2008-07-03 14:48:02 +04: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;
|
2014-10-23 23:59:31 +04:00
|
|
|
gso.csum_start = cpu_to_tun16(tun, skb_checksum_start_offset(skb) +
|
|
|
|
vlan_hlen);
|
|
|
|
gso.csum_offset = cpu_to_tun16(tun, skb->csum_offset);
|
2011-06-10 04:56:17 +04:00
|
|
|
} else if (skb->ip_summed == CHECKSUM_UNNECESSARY) {
|
|
|
|
gso.flags = VIRTIO_NET_HDR_F_DATA_VALID;
|
2008-07-03 14:48:02 +04:00
|
|
|
} /* else everything is zero */
|
|
|
|
|
2014-11-07 16:22:23 +03:00
|
|
|
if (copy_to_iter(&gso, sizeof(gso), iter) != sizeof(gso))
|
2008-07-03 14:48:02 +04:00
|
|
|
return -EFAULT;
|
2014-11-13 11:54:14 +03:00
|
|
|
|
|
|
|
iov_iter_advance(iter, vnet_hdr_sz - sizeof(gso));
|
2008-07-03 14:48:02 +04:00
|
|
|
}
|
|
|
|
|
2014-11-02 23:30:13 +03:00
|
|
|
if (vlan_hlen) {
|
2014-11-07 16:22:23 +03:00
|
|
|
int ret;
|
2013-07-25 09:00:33 +04:00
|
|
|
struct {
|
|
|
|
__be16 h_vlan_proto;
|
|
|
|
__be16 h_vlan_TCI;
|
|
|
|
} veth;
|
|
|
|
|
|
|
|
veth.h_vlan_proto = skb->vlan_proto;
|
2015-01-13 19:13:44 +03:00
|
|
|
veth.h_vlan_TCI = htons(skb_vlan_tag_get(skb));
|
2013-07-25 09:00:33 +04:00
|
|
|
|
|
|
|
vlan_offset = offsetof(struct vlan_ethhdr, h_vlan_proto);
|
|
|
|
|
2014-11-07 16:22:23 +03:00
|
|
|
ret = skb_copy_datagram_iter(skb, 0, iter, vlan_offset);
|
|
|
|
if (ret || !iov_iter_count(iter))
|
2013-07-25 09:00:33 +04:00
|
|
|
goto done;
|
|
|
|
|
2014-11-07 16:22:23 +03:00
|
|
|
ret = copy_to_iter(&veth, sizeof(veth), iter);
|
|
|
|
if (ret != sizeof(veth) || !iov_iter_count(iter))
|
2013-07-25 09:00:33 +04:00
|
|
|
goto done;
|
|
|
|
}
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2014-11-07 16:22:23 +03:00
|
|
|
skb_copy_datagram_iter(skb, vlan_offset, iter, skb->len - vlan_offset);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2013-07-25 09:00:33 +04:00
|
|
|
done:
|
2016-04-13 11:52:20 +03:00
|
|
|
/* caller is in process context, */
|
|
|
|
stats = get_cpu_ptr(tun->pcpu_stats);
|
|
|
|
u64_stats_update_begin(&stats->syncp);
|
|
|
|
stats->tx_packets++;
|
|
|
|
stats->tx_bytes += skb->len + vlan_hlen;
|
|
|
|
u64_stats_update_end(&stats->syncp);
|
|
|
|
put_cpu_ptr(tun->pcpu_stats);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
return total;
|
|
|
|
}
|
|
|
|
|
2012-10-31 23:45:57 +04:00
|
|
|
static ssize_t tun_do_read(struct tun_struct *tun, struct tun_file *tfile,
|
2014-11-07 21:52:07 +03:00
|
|
|
struct iov_iter *to,
|
|
|
|
int noblock)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
|
|
|
struct sk_buff *skb;
|
2014-11-07 21:52:07 +03:00
|
|
|
ssize_t ret;
|
2014-05-17 02:11:48 +04:00
|
|
|
int peeked, err, off = 0;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2012-11-26 02:07:41 +04:00
|
|
|
tun_debug(KERN_INFO, tun, "tun_do_read\n");
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2014-11-07 21:52:07 +03:00
|
|
|
if (!iov_iter_count(to))
|
|
|
|
return 0;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2014-05-17 02:11:48 +04:00
|
|
|
if (tun->dev->reg_state != NETREG_REGISTERED)
|
|
|
|
return -EIO;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2014-05-17 02:11:48 +04:00
|
|
|
/* Read frames from queue */
|
|
|
|
skb = __skb_recv_datagram(tfile->socket.sk, noblock ? MSG_DONTWAIT : 0,
|
|
|
|
&peeked, &off, &err);
|
2014-11-07 16:22:23 +03:00
|
|
|
if (!skb)
|
2014-12-26 10:22:49 +03:00
|
|
|
return err;
|
2014-11-07 16:22:23 +03:00
|
|
|
|
2014-11-07 21:52:07 +03:00
|
|
|
ret = tun_put_user(tun, tfile, skb, to);
|
2014-12-01 11:53:15 +03:00
|
|
|
if (unlikely(ret < 0))
|
2008-07-15 09:18:19 +04:00
|
|
|
kfree_skb(skb);
|
2014-12-01 11:53:15 +03:00
|
|
|
else
|
|
|
|
consume_skb(skb);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2010-01-14 09:17:09 +03:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2014-11-07 21:52:07 +03:00
|
|
|
static ssize_t tun_chr_read_iter(struct kiocb *iocb, struct iov_iter *to)
|
2010-01-14 09:17:09 +03:00
|
|
|
{
|
|
|
|
struct file *file = iocb->ki_filp;
|
|
|
|
struct tun_file *tfile = file->private_data;
|
|
|
|
struct tun_struct *tun = __tun_get(tfile);
|
2014-11-07 21:52:07 +03:00
|
|
|
ssize_t len = iov_iter_count(to), ret;
|
2010-01-14 09:17:09 +03:00
|
|
|
|
|
|
|
if (!tun)
|
|
|
|
return -EBADFD;
|
2014-11-07 21:52:07 +03:00
|
|
|
ret = tun_do_read(tun, tfile, to, file->f_flags & O_NONBLOCK);
|
2013-12-11 07:05:45 +04:00
|
|
|
ret = min_t(ssize_t, ret, len);
|
2013-12-06 10:16:51 +04:00
|
|
|
if (ret > 0)
|
|
|
|
iocb->ki_pos = ret;
|
2009-01-20 14:00:40 +03:00
|
|
|
tun_put(tun);
|
2005-04-17 02:20:36 +04:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2012-10-31 23:46:02 +04:00
|
|
|
static void tun_free_netdev(struct net_device *dev)
|
|
|
|
{
|
|
|
|
struct tun_struct *tun = netdev_priv(dev);
|
|
|
|
|
2012-12-14 03:53:30 +04:00
|
|
|
BUG_ON(!(list_empty(&tun->disabled)));
|
2016-04-13 11:52:20 +03:00
|
|
|
free_percpu(tun->pcpu_stats);
|
2012-10-31 23:46:02 +04:00
|
|
|
tun_flow_uninit(tun);
|
2013-01-14 11:12:19 +04:00
|
|
|
security_tun_dev_free_security(tun->security);
|
2012-10-31 23:46:02 +04:00
|
|
|
free_netdev(dev);
|
|
|
|
}
|
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
static void tun_setup(struct net_device *dev)
|
|
|
|
{
|
|
|
|
struct tun_struct *tun = netdev_priv(dev);
|
|
|
|
|
2012-02-08 04:48:55 +04:00
|
|
|
tun->owner = INVALID_UID;
|
|
|
|
tun->group = INVALID_GID;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
dev->ethtool_ops = &tun_ethtool_ops;
|
2012-10-31 23:46:02 +04:00
|
|
|
dev->destructor = tun_free_netdev;
|
2016-04-08 08:26:48 +03:00
|
|
|
/* We prefer our own queue length */
|
|
|
|
dev->tx_queue_len = TUN_READQ_SIZE;
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
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,
|
|
|
|
};
|
|
|
|
|
2009-02-06 08:25:32 +03:00
|
|
|
static void tun_sock_write_space(struct sock *sk)
|
|
|
|
{
|
2012-10-31 23:45:57 +04:00
|
|
|
struct tun_file *tfile;
|
2010-04-29 15:01:49 +04:00
|
|
|
wait_queue_head_t *wqueue;
|
2009-02-06 08:25:32 +03:00
|
|
|
|
|
|
|
if (!sock_writeable(sk))
|
|
|
|
return;
|
|
|
|
|
2015-11-30 07:03:10 +03:00
|
|
|
if (!test_and_clear_bit(SOCKWQ_ASYNC_NOSPACE, &sk->sk_socket->flags))
|
2009-02-06 08:25:32 +03:00
|
|
|
return;
|
|
|
|
|
2010-04-29 15:01:49 +04:00
|
|
|
wqueue = sk_sleep(sk);
|
|
|
|
if (wqueue && waitqueue_active(wqueue))
|
|
|
|
wake_up_interruptible_sync_poll(wqueue, POLLOUT |
|
2010-01-14 09:17:09 +03:00
|
|
|
POLLWRNORM | POLLWRBAND);
|
2009-06-04 08:45:55 +04:00
|
|
|
|
2012-10-31 23:45:57 +04:00
|
|
|
tfile = container_of(sk, struct tun_file, sk);
|
|
|
|
kill_fasync(&tfile->fasync, SIGIO, POLL_OUT);
|
2009-02-06 08:25:32 +03:00
|
|
|
}
|
|
|
|
|
2015-03-02 10:37:48 +03:00
|
|
|
static int tun_sendmsg(struct socket *sock, struct msghdr *m, size_t total_len)
|
2010-01-14 09:17:09 +03:00
|
|
|
{
|
2012-10-31 23:45:57 +04:00
|
|
|
int ret;
|
|
|
|
struct tun_file *tfile = container_of(sock, struct tun_file, socket);
|
|
|
|
struct tun_struct *tun = __tun_get(tfile);
|
|
|
|
|
|
|
|
if (!tun)
|
|
|
|
return -EBADFD;
|
2014-06-19 23:36:49 +04:00
|
|
|
|
2014-11-24 18:42:55 +03:00
|
|
|
ret = tun_get_user(tun, tfile, m->msg_control, &m->msg_iter,
|
2014-06-19 23:36:49 +04:00
|
|
|
m->msg_flags & MSG_DONTWAIT);
|
2012-10-31 23:45:57 +04:00
|
|
|
tun_put(tun);
|
|
|
|
return ret;
|
2010-01-14 09:17:09 +03:00
|
|
|
}
|
|
|
|
|
2015-03-02 10:37:48 +03:00
|
|
|
static int tun_recvmsg(struct socket *sock, struct msghdr *m, size_t total_len,
|
2010-01-14 09:17:09 +03:00
|
|
|
int flags)
|
|
|
|
{
|
2012-10-31 23:45:57 +04:00
|
|
|
struct tun_file *tfile = container_of(sock, struct tun_file, socket);
|
|
|
|
struct tun_struct *tun = __tun_get(tfile);
|
2010-01-14 09:17:09 +03:00
|
|
|
int ret;
|
2012-10-31 23:45:57 +04:00
|
|
|
|
|
|
|
if (!tun)
|
|
|
|
return -EBADFD;
|
|
|
|
|
2013-07-19 21:40:10 +04:00
|
|
|
if (flags & ~(MSG_DONTWAIT|MSG_TRUNC|MSG_ERRQUEUE)) {
|
2013-04-25 01:59:23 +04:00
|
|
|
ret = -EINVAL;
|
|
|
|
goto out;
|
|
|
|
}
|
2013-07-19 21:40:10 +04:00
|
|
|
if (flags & MSG_ERRQUEUE) {
|
|
|
|
ret = sock_recv_errqueue(sock->sk, m, total_len,
|
|
|
|
SOL_PACKET, TUN_TX_TIMESTAMP);
|
|
|
|
goto out;
|
|
|
|
}
|
2014-11-24 18:42:55 +03:00
|
|
|
ret = tun_do_read(tun, tfile, &m->msg_iter, flags & MSG_DONTWAIT);
|
2014-12-26 10:05:03 +03:00
|
|
|
if (ret > (ssize_t)total_len) {
|
2013-12-11 07:05:45 +04:00
|
|
|
m->msg_flags |= MSG_TRUNC;
|
|
|
|
ret = flags & MSG_TRUNC ? ret : total_len;
|
|
|
|
}
|
2013-04-25 01:59:23 +04:00
|
|
|
out:
|
2012-10-31 23:45:57 +04:00
|
|
|
tun_put(tun);
|
2010-01-14 09:17:09 +03:00
|
|
|
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 08:25:32 +03:00
|
|
|
static struct proto tun_proto = {
|
|
|
|
.name = "tun",
|
|
|
|
.owner = THIS_MODULE,
|
2012-10-31 23:45:57 +04:00
|
|
|
.obj_size = sizeof(struct tun_file),
|
2009-02-06 08:25:32 +03:00
|
|
|
};
|
2009-01-22 03:02:16 +03:00
|
|
|
|
2009-05-10 09:54:21 +04:00
|
|
|
static int tun_flags(struct tun_struct *tun)
|
|
|
|
{
|
2014-11-19 15:44:40 +03:00
|
|
|
return tun->flags & (TUN_FEATURES | IFF_PERSIST | IFF_TUN | IFF_TAP);
|
2009-05-10 09:54:21 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
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));
|
2012-02-08 04:48:55 +04:00
|
|
|
return uid_valid(tun->owner)?
|
|
|
|
sprintf(buf, "%u\n",
|
|
|
|
from_kuid_munged(current_user_ns(), tun->owner)):
|
|
|
|
sprintf(buf, "-1\n");
|
2009-05-10 09:54:21 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
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));
|
2012-02-08 04:48:55 +04:00
|
|
|
return gid_valid(tun->group) ?
|
|
|
|
sprintf(buf, "%u\n",
|
|
|
|
from_kgid_munged(current_user_ns(), tun->group)):
|
|
|
|
sprintf(buf, "-1\n");
|
2009-05-10 09:54:21 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
2015-02-04 16:37:34 +03:00
|
|
|
static struct attribute *tun_dev_attrs[] = {
|
|
|
|
&dev_attr_tun_flags.attr,
|
|
|
|
&dev_attr_owner.attr,
|
|
|
|
&dev_attr_group.attr,
|
|
|
|
NULL
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct attribute_group tun_attr_group = {
|
|
|
|
.attrs = tun_dev_attrs
|
|
|
|
};
|
|
|
|
|
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;
|
2012-10-31 23:45:57 +04:00
|
|
|
struct tun_file *tfile = file->private_data;
|
2005-04-17 02:20:36 +04:00
|
|
|
struct net_device *dev;
|
|
|
|
int err;
|
|
|
|
|
2013-01-11 20:59:33 +04:00
|
|
|
if (tfile->detached)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2009-01-20 13:56:20 +03:00
|
|
|
dev = __dev_get_by_name(net, ifr->ifr_name);
|
|
|
|
if (dev) {
|
2009-04-27 14:23:54 +04:00
|
|
|
if (ifr->ifr_flags & IFF_TUN_EXCL)
|
|
|
|
return -EBUSY;
|
2009-01-20 13:56:20 +03: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;
|
|
|
|
|
2013-05-28 22:32:11 +04:00
|
|
|
if (!!(ifr->ifr_flags & IFF_MULTI_QUEUE) !=
|
2014-11-19 16:17:31 +03:00
|
|
|
!!(tun->flags & IFF_MULTI_QUEUE))
|
2013-05-28 22:32:11 +04:00
|
|
|
return -EINVAL;
|
|
|
|
|
2012-10-31 23:46:01 +04:00
|
|
|
if (tun_not_capable(tun))
|
2009-08-29 02:12:43 +04:00
|
|
|
return -EPERM;
|
2013-01-14 11:12:19 +04:00
|
|
|
err = security_tun_dev_open(tun->security);
|
2009-08-29 02:12:43 +04:00
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
|
tun: Allow to skip filter on attach
There's a small problem with sk-filters on tun devices. Consider
an application doing this sequence of steps:
fd = open("/dev/net/tun");
ioctl(fd, TUNSETIFF, { .ifr_name = "tun0" });
ioctl(fd, TUNATTACHFILTER, &my_filter);
ioctl(fd, TUNSETPERSIST, 1);
close(fd);
At that point the tun0 will remain in the system and will keep in
mind that there should be a socket filter at address '&my_filter'.
If after that we do
fd = open("/dev/net/tun");
ioctl(fd, TUNSETIFF, { .ifr_name = "tun0" });
we most likely receive the -EFAULT error, since tun_attach() would
try to connect the filter back. But (!) if we provide a filter at
address &my_filter, then tun0 will be created and the "new" filter
would be attached, but application may not know about that.
This may create certain problems to anyone using tun-s, but it's
critical problem for c/r -- if we meet a persistent tun device
with a filter in mind, we will not be able to attach to it to dump
its state (flags, owner, address, vnethdr size, etc.).
The proposal is to allow to attach to tun device (with TUNSETIFF)
w/o attaching the filter to the tun-file's socket. After this
attach app may e.g clean the device by dropping the filter, it
doesn't want to have one, or (in case of c/r) get information
about the device with tun ioctls.
Signed-off-by: Pavel Emelyanov <xemul@parallels.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2013-08-21 14:32:21 +04:00
|
|
|
err = tun_attach(tun, file, ifr->ifr_flags & IFF_NOFILTER);
|
2009-01-20 13:57:48 +03:00
|
|
|
if (err < 0)
|
|
|
|
return err;
|
2012-12-14 03:53:30 +04:00
|
|
|
|
2014-11-19 16:17:31 +03:00
|
|
|
if (tun->flags & IFF_MULTI_QUEUE &&
|
2013-04-23 00:40:39 +04:00
|
|
|
(tun->numqueues + tun->numdisabled > 1)) {
|
|
|
|
/* One or more queue has already been attached, no need
|
|
|
|
* to initialize the device again.
|
|
|
|
*/
|
|
|
|
return 0;
|
|
|
|
}
|
2006-09-13 21:24:59 +04:00
|
|
|
}
|
2005-04-17 02:20:36 +04:00
|
|
|
else {
|
|
|
|
char *name;
|
|
|
|
unsigned long flags = 0;
|
2013-01-23 07:59:12 +04:00
|
|
|
int queues = ifr->ifr_flags & IFF_MULTI_QUEUE ?
|
|
|
|
MAX_TAP_QUEUES : 1;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2012-11-19 01:34:11 +04:00
|
|
|
if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
|
2006-06-23 03:07:52 +04:00
|
|
|
return -EPERM;
|
2009-08-29 02:12:43 +04:00
|
|
|
err = security_tun_dev_create();
|
|
|
|
if (err < 0)
|
|
|
|
return err;
|
2006-06-23 03:07:52 +04:00
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
/* Set dev type */
|
|
|
|
if (ifr->ifr_flags & IFF_TUN) {
|
|
|
|
/* TUN device */
|
2014-11-19 16:17:31 +03:00
|
|
|
flags |= IFF_TUN;
|
2005-04-17 02:20:36 +04:00
|
|
|
name = "tun%d";
|
|
|
|
} else if (ifr->ifr_flags & IFF_TAP) {
|
|
|
|
/* TAP device */
|
2014-11-19 16:17:31 +03:00
|
|
|
flags |= IFF_TAP;
|
2005-04-17 02:20:36 +04:00
|
|
|
name = "tap%d";
|
2006-09-13 21:24:59 +04:00
|
|
|
} else
|
2009-09-17 01:36:13 +04:00
|
|
|
return -EINVAL;
|
2006-09-13 21:24:59 +04:00
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
if (*ifr->ifr_name)
|
|
|
|
name = ifr->ifr_name;
|
|
|
|
|
2012-10-31 23:46:00 +04:00
|
|
|
dev = alloc_netdev_mqs(sizeof(struct tun_struct), name,
|
net: set name_assign_type in alloc_netdev()
Extend alloc_netdev{,_mq{,s}}() to take name_assign_type as argument, and convert
all users to pass NET_NAME_UNKNOWN.
Coccinelle patch:
@@
expression sizeof_priv, name, setup, txqs, rxqs, count;
@@
(
-alloc_netdev_mqs(sizeof_priv, name, setup, txqs, rxqs)
+alloc_netdev_mqs(sizeof_priv, name, NET_NAME_UNKNOWN, setup, txqs, rxqs)
|
-alloc_netdev_mq(sizeof_priv, name, setup, count)
+alloc_netdev_mq(sizeof_priv, name, NET_NAME_UNKNOWN, setup, count)
|
-alloc_netdev(sizeof_priv, name, setup)
+alloc_netdev(sizeof_priv, name, NET_NAME_UNKNOWN, setup)
)
v9: move comments here from the wrong commit
Signed-off-by: Tom Gundersen <teg@jklm.no>
Reviewed-by: David Herrmann <dh.herrmann@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2014-07-14 18:37:24 +04:00
|
|
|
NET_NAME_UNKNOWN, tun_setup, queues,
|
|
|
|
queues);
|
2013-01-23 07:59:12 +04:00
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
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;
|
tun: Add ability to create tun device with given index
Tun devices cannot be created with ifidex user wants, but it's
required by checkpoint-restore project.
Long time ago such ability was implemented for rtnl_ops-based
interface for creating links (9c7dafbf net: Allow to create links
with given ifindex), but the only API for creating and managing
tuntap devices is ioctl-based and is evolving with adding new ones
(cde8b15f tuntap: add ioctl to attach or detach a file form tuntap
device).
Following that trend, here's how a new ioctl that sets the ifindex
for device, that _will_ be created by TUNSETIFF ioctl looks like.
So those who want a tuntap device with the ifindex N, should open
the tun device, call ioctl(fd, TUNSETIFINDEX, &N), then call TUNSETIFF.
If the index N is busy, then the register_netdev will find this out
and the ioctl would be failed with -EBUSY.
If setifindex is not called, then it will be generated as before.
Signed-off-by: Pavel Emelyanov <xemul@parallels.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2013-08-21 14:31:38 +04:00
|
|
|
dev->ifindex = tfile->ifindex;
|
2015-02-04 16:37:34 +03:00
|
|
|
dev->sysfs_groups[0] = &tun_attr_group;
|
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;
|
2010-03-17 18:45:01 +03:00
|
|
|
tun->vnet_hdr_sz = sizeof(struct virtio_net_hdr);
|
2009-02-06 08:25:32 +03:00
|
|
|
|
2016-02-26 12:45:40 +03:00
|
|
|
tun->align = NET_SKB_PAD;
|
2012-10-31 23:45:57 +04:00
|
|
|
tun->filter_attached = false;
|
|
|
|
tun->sndbuf = tfile->socket.sk->sk_sndbuf;
|
2009-02-06 08:25:32 +03:00
|
|
|
|
2016-04-13 11:52:20 +03:00
|
|
|
tun->pcpu_stats = netdev_alloc_pcpu_stats(struct tun_pcpu_stats);
|
|
|
|
if (!tun->pcpu_stats) {
|
|
|
|
err = -ENOMEM;
|
|
|
|
goto err_free_dev;
|
|
|
|
}
|
|
|
|
|
2012-10-31 23:46:02 +04:00
|
|
|
spin_lock_init(&tun->lock);
|
|
|
|
|
2013-01-14 11:12:19 +04:00
|
|
|
err = security_tun_dev_alloc_security(&tun->security);
|
|
|
|
if (err < 0)
|
2016-04-13 11:52:20 +03:00
|
|
|
goto err_free_stat;
|
2009-08-29 02:12:43 +04:00
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
tun_net_init(dev);
|
2013-06-11 17:01:08 +04:00
|
|
|
tun_flow_init(tun);
|
2012-10-31 23:46:02 +04:00
|
|
|
|
2011-04-19 10:13:10 +04:00
|
|
|
dev->hw_features = NETIF_F_SG | NETIF_F_FRAGLIST |
|
2013-07-25 09:00:33 +04:00
|
|
|
TUN_USER_FEATURES | NETIF_F_HW_VLAN_CTAG_TX |
|
|
|
|
NETIF_F_HW_VLAN_STAG_TX;
|
2011-04-19 10:13:10 +04:00
|
|
|
dev->features = dev->hw_features;
|
2014-02-18 16:20:09 +04:00
|
|
|
dev->vlan_features = dev->features &
|
|
|
|
~(NETIF_F_HW_VLAN_CTAG_TX |
|
|
|
|
NETIF_F_HW_VLAN_STAG_TX);
|
2011-04-19 10:13:10 +04:00
|
|
|
|
2012-12-14 03:53:30 +04:00
|
|
|
INIT_LIST_HEAD(&tun->disabled);
|
tun: Allow to skip filter on attach
There's a small problem with sk-filters on tun devices. Consider
an application doing this sequence of steps:
fd = open("/dev/net/tun");
ioctl(fd, TUNSETIFF, { .ifr_name = "tun0" });
ioctl(fd, TUNATTACHFILTER, &my_filter);
ioctl(fd, TUNSETPERSIST, 1);
close(fd);
At that point the tun0 will remain in the system and will keep in
mind that there should be a socket filter at address '&my_filter'.
If after that we do
fd = open("/dev/net/tun");
ioctl(fd, TUNSETIFF, { .ifr_name = "tun0" });
we most likely receive the -EFAULT error, since tun_attach() would
try to connect the filter back. But (!) if we provide a filter at
address &my_filter, then tun0 will be created and the "new" filter
would be attached, but application may not know about that.
This may create certain problems to anyone using tun-s, but it's
critical problem for c/r -- if we meet a persistent tun device
with a filter in mind, we will not be able to attach to it to dump
its state (flags, owner, address, vnethdr size, etc.).
The proposal is to allow to attach to tun device (with TUNSETIFF)
w/o attaching the filter to the tun-file's socket. After this
attach app may e.g clean the device by dropping the filter, it
doesn't want to have one, or (in case of c/r) get information
about the device with tun ioctls.
Signed-off-by: Pavel Emelyanov <xemul@parallels.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2013-08-21 14:32:21 +04:00
|
|
|
err = tun_attach(tun, file, false);
|
2012-12-02 21:19:45 +04:00
|
|
|
if (err < 0)
|
2013-09-11 14:09:48 +04:00
|
|
|
goto err_free_flow;
|
2012-12-02 21:19:45 +04:00
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
err = register_netdevice(tun->dev);
|
|
|
|
if (err < 0)
|
2013-09-11 14:09:48 +04:00
|
|
|
goto err_detach;
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
2013-01-28 04:38:02 +04:00
|
|
|
netif_carrier_on(tun->dev);
|
|
|
|
|
2011-03-02 10:18:10 +03:00
|
|
|
tun_debug(KERN_INFO, tun, "tun_set_iff\n");
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2014-11-19 15:44:40 +03:00
|
|
|
tun->flags = (tun->flags & ~TUN_FEATURES) |
|
|
|
|
(ifr->ifr_flags & TUN_FEATURES);
|
2012-10-31 23:46:00 +04:00
|
|
|
|
2008-07-11 03:59:11 +04:00
|
|
|
/* Make sure persistent devices do not get stuck in
|
|
|
|
* xoff state.
|
|
|
|
*/
|
|
|
|
if (netif_running(tun->dev))
|
2012-10-31 23:46:00 +04:00
|
|
|
netif_tx_wake_all_queues(tun->dev);
|
2008-07-11 03:59:11 +04:00
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
strcpy(ifr->ifr_name, tun->dev->name);
|
|
|
|
return 0;
|
|
|
|
|
2013-09-11 14:09:48 +04:00
|
|
|
err_detach:
|
|
|
|
tun_detach_all(dev);
|
|
|
|
err_free_flow:
|
|
|
|
tun_flow_uninit(tun);
|
|
|
|
security_tun_dev_free_security(tun->security);
|
2016-04-13 11:52:20 +03:00
|
|
|
err_free_stat:
|
|
|
|
free_percpu(tun->pcpu_stats);
|
2013-09-11 14:09:48 +04:00
|
|
|
err_free_dev:
|
2005-04-17 02:20:36 +04:00
|
|
|
free_netdev(dev);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2012-11-23 07:58:10 +04:00
|
|
|
static void tun_get_iff(struct net *net, struct tun_struct *tun,
|
2009-08-06 18:22:44 +04:00
|
|
|
struct ifreq *ifr)
|
2008-08-16 02:09:56 +04:00
|
|
|
{
|
2011-03-02 10:18:10 +03:00
|
|
|
tun_debug(KERN_INFO, tun, "tun_get_iff\n");
|
2008-08-16 02:09:56 +04:00
|
|
|
|
|
|
|
strcpy(ifr->ifr_name, tun->dev->name);
|
|
|
|
|
2009-05-10 09:54:21 +04:00
|
|
|
ifr->ifr_flags = tun_flags(tun);
|
2008-08-16 02:09:56 +04:00
|
|
|
|
|
|
|
}
|
|
|
|
|
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. */
|
2011-04-19 10:13:10 +04:00
|
|
|
static int set_offload(struct tun_struct *tun, unsigned long arg)
|
2008-07-03 14:46:16 +04:00
|
|
|
{
|
2011-11-15 19:29:55 +04:00
|
|
|
netdev_features_t features = 0;
|
2008-07-03 14:46:16 +04:00
|
|
|
|
|
|
|
if (arg & TUN_F_CSUM) {
|
2011-04-19 10:13:10 +04:00
|
|
|
features |= NETIF_F_HW_CSUM;
|
2008-07-03 14:46:16 +04:00
|
|
|
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);
|
|
|
|
}
|
2015-02-04 00:36:17 +03:00
|
|
|
|
|
|
|
if (arg & TUN_F_UFO) {
|
|
|
|
features |= NETIF_F_UFO;
|
|
|
|
arg &= ~TUN_F_UFO;
|
|
|
|
}
|
2008-07-03 14:46:16 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* This gives the user a way to test for new features in future by
|
|
|
|
* trying to set them. */
|
|
|
|
if (arg)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2011-04-19 10:13:10 +04:00
|
|
|
tun->set_features = features;
|
|
|
|
netdev_update_features(tun->dev);
|
2008-07-03 14:46:16 +04:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-10-31 23:46:00 +04:00
|
|
|
static void tun_detach_filter(struct tun_struct *tun, int n)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
struct tun_file *tfile;
|
|
|
|
|
|
|
|
for (i = 0; i < n; i++) {
|
2013-01-11 20:59:32 +04:00
|
|
|
tfile = rtnl_dereference(tun->tfiles[i]);
|
2016-04-05 18:10:16 +03:00
|
|
|
lock_sock(tfile->socket.sk);
|
|
|
|
sk_detach_filter(tfile->socket.sk);
|
|
|
|
release_sock(tfile->socket.sk);
|
2012-10-31 23:46:00 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
tun->filter_attached = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int tun_attach_filter(struct tun_struct *tun)
|
|
|
|
{
|
|
|
|
int i, ret = 0;
|
|
|
|
struct tun_file *tfile;
|
|
|
|
|
|
|
|
for (i = 0; i < tun->numqueues; i++) {
|
2013-01-11 20:59:32 +04:00
|
|
|
tfile = rtnl_dereference(tun->tfiles[i]);
|
2016-04-05 18:10:16 +03:00
|
|
|
lock_sock(tfile->socket.sk);
|
|
|
|
ret = sk_attach_filter(&tun->fprog, tfile->socket.sk);
|
|
|
|
release_sock(tfile->socket.sk);
|
2012-10-31 23:46:00 +04:00
|
|
|
if (ret) {
|
|
|
|
tun_detach_filter(tun, i);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
tun->filter_attached = true;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void tun_set_sndbuf(struct tun_struct *tun)
|
|
|
|
{
|
|
|
|
struct tun_file *tfile;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < tun->numqueues; i++) {
|
2013-01-11 20:59:32 +04:00
|
|
|
tfile = rtnl_dereference(tun->tfiles[i]);
|
2012-10-31 23:46:00 +04:00
|
|
|
tfile->socket.sk->sk_sndbuf = tun->sndbuf;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-10-31 23:46:01 +04:00
|
|
|
static int tun_set_queue(struct file *file, struct ifreq *ifr)
|
|
|
|
{
|
|
|
|
struct tun_file *tfile = file->private_data;
|
|
|
|
struct tun_struct *tun;
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
rtnl_lock();
|
|
|
|
|
|
|
|
if (ifr->ifr_flags & IFF_ATTACH_QUEUE) {
|
2012-12-14 03:53:30 +04:00
|
|
|
tun = tfile->detached;
|
2013-01-14 11:12:19 +04:00
|
|
|
if (!tun) {
|
2012-10-31 23:46:01 +04:00
|
|
|
ret = -EINVAL;
|
2013-01-14 11:12:19 +04:00
|
|
|
goto unlock;
|
|
|
|
}
|
|
|
|
ret = security_tun_dev_attach_queue(tun->security);
|
|
|
|
if (ret < 0)
|
|
|
|
goto unlock;
|
tun: Allow to skip filter on attach
There's a small problem with sk-filters on tun devices. Consider
an application doing this sequence of steps:
fd = open("/dev/net/tun");
ioctl(fd, TUNSETIFF, { .ifr_name = "tun0" });
ioctl(fd, TUNATTACHFILTER, &my_filter);
ioctl(fd, TUNSETPERSIST, 1);
close(fd);
At that point the tun0 will remain in the system and will keep in
mind that there should be a socket filter at address '&my_filter'.
If after that we do
fd = open("/dev/net/tun");
ioctl(fd, TUNSETIFF, { .ifr_name = "tun0" });
we most likely receive the -EFAULT error, since tun_attach() would
try to connect the filter back. But (!) if we provide a filter at
address &my_filter, then tun0 will be created and the "new" filter
would be attached, but application may not know about that.
This may create certain problems to anyone using tun-s, but it's
critical problem for c/r -- if we meet a persistent tun device
with a filter in mind, we will not be able to attach to it to dump
its state (flags, owner, address, vnethdr size, etc.).
The proposal is to allow to attach to tun device (with TUNSETIFF)
w/o attaching the filter to the tun-file's socket. After this
attach app may e.g clean the device by dropping the filter, it
doesn't want to have one, or (in case of c/r) get information
about the device with tun ioctls.
Signed-off-by: Pavel Emelyanov <xemul@parallels.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2013-08-21 14:32:21 +04:00
|
|
|
ret = tun_attach(tun, file, false);
|
2012-12-14 03:53:30 +04:00
|
|
|
} else if (ifr->ifr_flags & IFF_DETACH_QUEUE) {
|
2013-01-11 20:59:32 +04:00
|
|
|
tun = rtnl_dereference(tfile->tun);
|
2014-11-19 16:17:31 +03:00
|
|
|
if (!tun || !(tun->flags & IFF_MULTI_QUEUE) || tfile->detached)
|
2012-12-14 03:53:30 +04:00
|
|
|
ret = -EINVAL;
|
|
|
|
else
|
|
|
|
__tun_detach(tfile, false);
|
|
|
|
} else
|
2012-10-31 23:46:01 +04:00
|
|
|
ret = -EINVAL;
|
|
|
|
|
2013-01-14 11:12:19 +04:00
|
|
|
unlock:
|
2012-10-31 23:46:01 +04:00
|
|
|
rtnl_unlock();
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2009-11-07 09:52:32 +03:00
|
|
|
static long __tun_chr_ioctl(struct file *file, unsigned int cmd,
|
|
|
|
unsigned long arg, int ifreq_len)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
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;
|
2012-02-08 04:48:55 +04:00
|
|
|
kuid_t owner;
|
|
|
|
kgid_t group;
|
2009-02-06 08:25:32 +03:00
|
|
|
int sndbuf;
|
2010-03-17 18:45:01 +03:00
|
|
|
int vnet_hdr_sz;
|
tun: Add ability to create tun device with given index
Tun devices cannot be created with ifidex user wants, but it's
required by checkpoint-restore project.
Long time ago such ability was implemented for rtnl_ops-based
interface for creating links (9c7dafbf net: Allow to create links
with given ifindex), but the only API for creating and managing
tuntap devices is ioctl-based and is evolving with adding new ones
(cde8b15f tuntap: add ioctl to attach or detach a file form tuntap
device).
Following that trend, here's how a new ioctl that sets the ifindex
for device, that _will_ be created by TUNSETIFF ioctl looks like.
So those who want a tuntap device with the ifindex N, should open
the tun device, call ioctl(fd, TUNSETIFINDEX, &N), then call TUNSETIFF.
If the index N is busy, then the register_netdev will find this out
and the ioctl would be failed with -EBUSY.
If setifindex is not called, then it will be generated as before.
Signed-off-by: Pavel Emelyanov <xemul@parallels.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2013-08-21 14:31:38 +04:00
|
|
|
unsigned int ifindex;
|
2014-12-16 16:05:06 +03:00
|
|
|
int le;
|
2008-07-15 09:18:19 +04:00
|
|
|
int ret;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2012-10-31 23:46:01 +04:00
|
|
|
if (cmd == TUNSETIFF || cmd == TUNSETQUEUE || _IOC_TYPE(cmd) == 0x89) {
|
2009-11-07 09:52:32 +03:00
|
|
|
if (copy_from_user(&ifr, argp, ifreq_len))
|
2005-04-17 02:20:36 +04:00
|
|
|
return -EFAULT;
|
2012-07-31 01:52:48 +04:00
|
|
|
} else {
|
2012-07-29 23:45:14 +04:00
|
|
|
memset(&ifr, 0, sizeof(ifr));
|
2012-07-31 01:52:48 +04:00
|
|
|
}
|
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
|
2014-11-19 15:44:40 +03:00
|
|
|
* TUNSETIFF.
|
|
|
|
*/
|
|
|
|
return put_user(IFF_TUN | IFF_TAP | TUN_FEATURES,
|
2009-01-20 14:00:40 +03:00
|
|
|
(unsigned int __user*)argp);
|
2012-10-31 23:46:01 +04:00
|
|
|
} else if (cmd == TUNSETQUEUE)
|
|
|
|
return tun_set_queue(file, &ifr);
|
2009-01-20 14:00:40 +03:00
|
|
|
|
2012-10-31 23:46:00 +04:00
|
|
|
ret = 0;
|
2009-08-06 18:22:44 +04:00
|
|
|
rtnl_lock();
|
|
|
|
|
2009-01-20 14:01:48 +03:00
|
|
|
tun = __tun_get(tfile);
|
2005-04-17 02:20:36 +04:00
|
|
|
if (cmd == TUNSETIFF && !tun) {
|
|
|
|
ifr.ifr_name[IFNAMSIZ-1] = '\0';
|
|
|
|
|
2015-05-09 05:07:08 +03:00
|
|
|
ret = tun_set_iff(sock_net(&tfile->sk), file, &ifr);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2009-08-06 18:22:44 +04:00
|
|
|
if (ret)
|
|
|
|
goto unlock;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2009-11-07 09:52:32 +03:00
|
|
|
if (copy_to_user(argp, &ifr, ifreq_len))
|
2009-08-06 18:22:44 +04:00
|
|
|
ret = -EFAULT;
|
|
|
|
goto unlock;
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
tun: Add ability to create tun device with given index
Tun devices cannot be created with ifidex user wants, but it's
required by checkpoint-restore project.
Long time ago such ability was implemented for rtnl_ops-based
interface for creating links (9c7dafbf net: Allow to create links
with given ifindex), but the only API for creating and managing
tuntap devices is ioctl-based and is evolving with adding new ones
(cde8b15f tuntap: add ioctl to attach or detach a file form tuntap
device).
Following that trend, here's how a new ioctl that sets the ifindex
for device, that _will_ be created by TUNSETIFF ioctl looks like.
So those who want a tuntap device with the ifindex N, should open
the tun device, call ioctl(fd, TUNSETIFINDEX, &N), then call TUNSETIFF.
If the index N is busy, then the register_netdev will find this out
and the ioctl would be failed with -EBUSY.
If setifindex is not called, then it will be generated as before.
Signed-off-by: Pavel Emelyanov <xemul@parallels.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2013-08-21 14:31:38 +04:00
|
|
|
if (cmd == TUNSETIFINDEX) {
|
|
|
|
ret = -EPERM;
|
|
|
|
if (tun)
|
|
|
|
goto unlock;
|
|
|
|
|
|
|
|
ret = -EFAULT;
|
|
|
|
if (copy_from_user(&ifindex, argp, sizeof(ifindex)))
|
|
|
|
goto unlock;
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
tfile->ifindex = ifindex;
|
|
|
|
goto unlock;
|
|
|
|
}
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2009-08-06 18:22:44 +04:00
|
|
|
ret = -EBADFD;
|
2005-04-17 02:20:36 +04:00
|
|
|
if (!tun)
|
2009-08-06 18:22:44 +04:00
|
|
|
goto unlock;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2012-10-31 23:45:56 +04:00
|
|
|
tun_debug(KERN_INFO, tun, "tun_chr_ioctl cmd %u\n", cmd);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
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:
|
2012-11-23 07:58:10 +04:00
|
|
|
tun_get_iff(current->nsproxy->net_ns, tun, &ifr);
|
2008-08-16 02:09:56 +04:00
|
|
|
|
2013-08-21 14:32:00 +04:00
|
|
|
if (tfile->detached)
|
|
|
|
ifr.ifr_flags |= IFF_DETACH_QUEUE;
|
tun: Allow to skip filter on attach
There's a small problem with sk-filters on tun devices. Consider
an application doing this sequence of steps:
fd = open("/dev/net/tun");
ioctl(fd, TUNSETIFF, { .ifr_name = "tun0" });
ioctl(fd, TUNATTACHFILTER, &my_filter);
ioctl(fd, TUNSETPERSIST, 1);
close(fd);
At that point the tun0 will remain in the system and will keep in
mind that there should be a socket filter at address '&my_filter'.
If after that we do
fd = open("/dev/net/tun");
ioctl(fd, TUNSETIFF, { .ifr_name = "tun0" });
we most likely receive the -EFAULT error, since tun_attach() would
try to connect the filter back. But (!) if we provide a filter at
address &my_filter, then tun0 will be created and the "new" filter
would be attached, but application may not know about that.
This may create certain problems to anyone using tun-s, but it's
critical problem for c/r -- if we meet a persistent tun device
with a filter in mind, we will not be able to attach to it to dump
its state (flags, owner, address, vnethdr size, etc.).
The proposal is to allow to attach to tun device (with TUNSETIFF)
w/o attaching the filter to the tun-file's socket. After this
attach app may e.g clean the device by dropping the filter, it
doesn't want to have one, or (in case of c/r) get information
about the device with tun ioctls.
Signed-off-by: Pavel Emelyanov <xemul@parallels.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2013-08-21 14:32:21 +04:00
|
|
|
if (!tfile->socket.sk->sk_filter)
|
|
|
|
ifr.ifr_flags |= IFF_NOFILTER;
|
2013-08-21 14:32:00 +04:00
|
|
|
|
2009-11-07 09:52:32 +03:00
|
|
|
if (copy_to_user(argp, &ifr, ifreq_len))
|
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 */
|
|
|
|
|
2011-04-19 10:13:10 +04:00
|
|
|
/* [unimplemented] */
|
|
|
|
tun_debug(KERN_INFO, tun, "ignored: set checksum %s\n",
|
2011-03-02 10:18:10 +03:00
|
|
|
arg ? "disabled" : "enabled");
|
2005-04-17 02:20:36 +04:00
|
|
|
break;
|
|
|
|
|
|
|
|
case TUNSETPERSIST:
|
2012-10-31 23:45:57 +04:00
|
|
|
/* Disable/Enable persist mode. Keep an extra reference to the
|
|
|
|
* module to prevent the module being unprobed.
|
|
|
|
*/
|
2014-11-19 16:17:31 +03:00
|
|
|
if (arg && !(tun->flags & IFF_PERSIST)) {
|
|
|
|
tun->flags |= IFF_PERSIST;
|
2012-10-31 23:45:57 +04:00
|
|
|
__module_get(THIS_MODULE);
|
2013-01-11 20:59:34 +04:00
|
|
|
}
|
2014-11-19 16:17:31 +03:00
|
|
|
if (!arg && (tun->flags & IFF_PERSIST)) {
|
|
|
|
tun->flags &= ~IFF_PERSIST;
|
2012-10-31 23:45:57 +04:00
|
|
|
module_put(THIS_MODULE);
|
|
|
|
}
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2011-03-02 10:18:10 +03:00
|
|
|
tun_debug(KERN_INFO, tun, "persist %s\n",
|
|
|
|
arg ? "enabled" : "disabled");
|
2005-04-17 02:20:36 +04:00
|
|
|
break;
|
|
|
|
|
|
|
|
case TUNSETOWNER:
|
|
|
|
/* Set owner of the device */
|
2012-02-08 04:48:55 +04:00
|
|
|
owner = make_kuid(current_user_ns(), arg);
|
|
|
|
if (!uid_valid(owner)) {
|
|
|
|
ret = -EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
tun->owner = owner;
|
2012-10-31 23:45:56 +04:00
|
|
|
tun_debug(KERN_INFO, tun, "owner set to %u\n",
|
2012-02-08 04:48:55 +04:00
|
|
|
from_kuid(&init_user_ns, tun->owner));
|
2005-04-17 02:20:36 +04:00
|
|
|
break;
|
|
|
|
|
2007-07-03 09:50:25 +04:00
|
|
|
case TUNSETGROUP:
|
|
|
|
/* Set group of the device */
|
2012-02-08 04:48:55 +04:00
|
|
|
group = make_kgid(current_user_ns(), arg);
|
|
|
|
if (!gid_valid(group)) {
|
|
|
|
ret = -EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
tun->group = group;
|
2012-10-31 23:45:56 +04:00
|
|
|
tun_debug(KERN_INFO, tun, "group set to %u\n",
|
2012-02-08 04:48:55 +04:00
|
|
|
from_kgid(&init_user_ns, tun->group));
|
2007-07-03 09:50:25 +04:00
|
|
|
break;
|
|
|
|
|
2005-09-02 04:40:05 +04:00
|
|
|
case TUNSETLINK:
|
|
|
|
/* Only allow setting the type when the interface is down */
|
|
|
|
if (tun->dev->flags & IFF_UP) {
|
2011-03-02 10:18:10 +03:00
|
|
|
tun_debug(KERN_INFO, tun,
|
|
|
|
"Linktype set failed because interface is up\n");
|
2008-04-24 06:37:58 +04:00
|
|
|
ret = -EBUSY;
|
2005-09-02 04:40:05 +04:00
|
|
|
} else {
|
|
|
|
tun->dev->type = (int) arg;
|
2011-03-02 10:18:10 +03:00
|
|
|
tun_debug(KERN_INFO, tun, "linktype set to %d\n",
|
|
|
|
tun->dev->type);
|
2008-04-24 06:37:58 +04:00
|
|
|
ret = 0;
|
2005-09-02 04:40:05 +04:00
|
|
|
}
|
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:
|
2011-04-19 10:13:10 +04:00
|
|
|
ret = set_offload(tun, arg);
|
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;
|
2014-11-19 16:17:31 +03:00
|
|
|
if ((tun->flags & TUN_TYPE_MASK) != IFF_TAP)
|
2009-01-20 14:00:40 +03:00
|
|
|
break;
|
2008-07-16 23:45:34 +04:00
|
|
|
ret = update_filter(&tun->txflt, (void __user *)arg);
|
2009-01-20 14:00:40 +03:00
|
|
|
break;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
case SIOCGIFHWADDR:
|
tree-wide: fix comment/printk typos
"gadget", "through", "command", "maintain", "maintain", "controller", "address",
"between", "initiali[zs]e", "instead", "function", "select", "already",
"equal", "access", "management", "hierarchy", "registration", "interest",
"relative", "memory", "offset", "already",
Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
2010-11-01 22:38:34 +03:00
|
|
|
/* Get hw address */
|
2008-07-15 09:18:19 +04:00
|
|
|
memcpy(ifr.ifr_hwaddr.sa_data, tun->dev->dev_addr, ETH_ALEN);
|
|
|
|
ifr.ifr_hwaddr.sa_family = tun->dev->type;
|
2009-11-07 09:52:32 +03:00
|
|
|
if (copy_to_user(argp, &ifr, ifreq_len))
|
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 */
|
2011-03-02 10:18:10 +03:00
|
|
|
tun_debug(KERN_DEBUG, tun, "set hw address: %pM\n",
|
|
|
|
ifr.ifr_hwaddr.sa_data);
|
2008-02-29 23:26:21 +03:00
|
|
|
|
|
|
|
ret = dev_set_mac_address(tun->dev, &ifr.ifr_hwaddr);
|
2009-01-20 14:00:40 +03:00
|
|
|
break;
|
2009-02-06 08:25:32 +03:00
|
|
|
|
|
|
|
case TUNGETSNDBUF:
|
2012-10-31 23:45:57 +04:00
|
|
|
sndbuf = tfile->socket.sk->sk_sndbuf;
|
2009-02-06 08:25:32 +03: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;
|
|
|
|
}
|
|
|
|
|
2012-10-31 23:46:00 +04:00
|
|
|
tun->sndbuf = sndbuf;
|
|
|
|
tun_set_sndbuf(tun);
|
2009-02-06 08:25:32 +03:00
|
|
|
break;
|
|
|
|
|
2010-03-17 18:45:01 +03: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;
|
|
|
|
|
2014-12-16 16:05:06 +03:00
|
|
|
case TUNGETVNETLE:
|
|
|
|
le = !!(tun->flags & TUN_VNET_LE);
|
|
|
|
if (put_user(le, (int __user *)argp))
|
|
|
|
ret = -EFAULT;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case TUNSETVNETLE:
|
|
|
|
if (get_user(le, (int __user *)argp)) {
|
|
|
|
ret = -EFAULT;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (le)
|
|
|
|
tun->flags |= TUN_VNET_LE;
|
|
|
|
else
|
|
|
|
tun->flags &= ~TUN_VNET_LE;
|
|
|
|
break;
|
|
|
|
|
2015-04-24 15:50:36 +03:00
|
|
|
case TUNGETVNETBE:
|
|
|
|
ret = tun_get_vnet_be(tun, argp);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case TUNSETVNETBE:
|
|
|
|
ret = tun_set_vnet_be(tun, argp);
|
|
|
|
break;
|
|
|
|
|
2010-02-14 04:01:10 +03:00
|
|
|
case TUNATTACHFILTER:
|
|
|
|
/* Can be set only for TAPs */
|
|
|
|
ret = -EINVAL;
|
2014-11-19 16:17:31 +03:00
|
|
|
if ((tun->flags & TUN_TYPE_MASK) != IFF_TAP)
|
2010-02-14 04:01:10 +03:00
|
|
|
break;
|
|
|
|
ret = -EFAULT;
|
2012-10-31 23:45:57 +04:00
|
|
|
if (copy_from_user(&tun->fprog, argp, sizeof(tun->fprog)))
|
2010-02-14 04:01:10 +03:00
|
|
|
break;
|
|
|
|
|
2012-10-31 23:46:00 +04:00
|
|
|
ret = tun_attach_filter(tun);
|
2010-02-14 04:01:10 +03:00
|
|
|
break;
|
|
|
|
|
|
|
|
case TUNDETACHFILTER:
|
|
|
|
/* Can be set only for TAPs */
|
|
|
|
ret = -EINVAL;
|
2014-11-19 16:17:31 +03:00
|
|
|
if ((tun->flags & TUN_TYPE_MASK) != IFF_TAP)
|
2010-02-14 04:01:10 +03:00
|
|
|
break;
|
2012-10-31 23:46:00 +04:00
|
|
|
ret = 0;
|
|
|
|
tun_detach_filter(tun, tun->numqueues);
|
2010-02-14 04:01:10 +03:00
|
|
|
break;
|
|
|
|
|
2013-08-21 14:32:39 +04:00
|
|
|
case TUNGETFILTER:
|
|
|
|
ret = -EINVAL;
|
2014-11-19 16:17:31 +03:00
|
|
|
if ((tun->flags & TUN_TYPE_MASK) != IFF_TAP)
|
2013-08-21 14:32:39 +04:00
|
|
|
break;
|
|
|
|
ret = -EFAULT;
|
|
|
|
if (copy_to_user(argp, &tun->fprog, sizeof(tun->fprog)))
|
|
|
|
break;
|
|
|
|
ret = 0;
|
|
|
|
break;
|
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
default:
|
2009-01-20 14:00:40 +03:00
|
|
|
ret = -EINVAL;
|
|
|
|
break;
|
2010-05-18 09:47:34 +04:00
|
|
|
}
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2009-08-06 18:22:44 +04:00
|
|
|
unlock:
|
|
|
|
rtnl_unlock();
|
|
|
|
if (tun)
|
|
|
|
tun_put(tun);
|
2009-01-20 14:00:40 +03:00
|
|
|
return ret;
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
2009-11-07 09:52:32 +03: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 02:20:36 +04:00
|
|
|
static int tun_chr_fasync(int fd, struct file *file, int on)
|
|
|
|
{
|
2012-10-31 23:45:57 +04:00
|
|
|
struct tun_file *tfile = file->private_data;
|
2005-04-17 02:20:36 +04:00
|
|
|
int ret;
|
|
|
|
|
2012-10-31 23:45:57 +04:00
|
|
|
if ((ret = fasync_helper(fd, file, on, &tfile->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) {
|
2014-08-22 19:27:32 +04:00
|
|
|
__f_setown(file, task_pid(current), PIDTYPE_PID, 0);
|
2012-10-31 23:45:57 +04:00
|
|
|
tfile->flags |= TUN_FASYNC;
|
2006-09-13 21:24:59 +04:00
|
|
|
} else
|
2012-10-31 23:45:57 +04:00
|
|
|
tfile->flags &= ~TUN_FASYNC;
|
2008-06-20 01:50:37 +04:00
|
|
|
ret = 0;
|
|
|
|
out:
|
|
|
|
return ret;
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
static int tun_chr_open(struct inode *inode, struct file * file)
|
|
|
|
{
|
2015-05-09 05:07:08 +03:00
|
|
|
struct net *net = current->nsproxy->net_ns;
|
2009-01-20 14:00:40 +03:00
|
|
|
struct tun_file *tfile;
|
2009-10-14 12:19:46 +04:00
|
|
|
|
2011-03-02 10:18:10 +03:00
|
|
|
DBG1(KERN_INFO, "tunX: tun_chr_open\n");
|
2009-01-20 14:00:40 +03:00
|
|
|
|
2015-05-09 05:07:08 +03:00
|
|
|
tfile = (struct tun_file *)sk_alloc(net, AF_UNSPEC, GFP_KERNEL,
|
2015-05-09 05:09:13 +03:00
|
|
|
&tun_proto, 0);
|
2009-01-20 14:00:40 +03:00
|
|
|
if (!tfile)
|
|
|
|
return -ENOMEM;
|
2014-03-23 22:32:32 +04:00
|
|
|
RCU_INIT_POINTER(tfile->tun, NULL);
|
2012-10-31 23:45:57 +04:00
|
|
|
tfile->flags = 0;
|
tun: Add ability to create tun device with given index
Tun devices cannot be created with ifidex user wants, but it's
required by checkpoint-restore project.
Long time ago such ability was implemented for rtnl_ops-based
interface for creating links (9c7dafbf net: Allow to create links
with given ifindex), but the only API for creating and managing
tuntap devices is ioctl-based and is evolving with adding new ones
(cde8b15f tuntap: add ioctl to attach or detach a file form tuntap
device).
Following that trend, here's how a new ioctl that sets the ifindex
for device, that _will_ be created by TUNSETIFF ioctl looks like.
So those who want a tuntap device with the ifindex N, should open
the tun device, call ioctl(fd, TUNSETIFINDEX, &N), then call TUNSETIFF.
If the index N is busy, then the register_netdev will find this out
and the ioctl would be failed with -EBUSY.
If setifindex is not called, then it will be generated as before.
Signed-off-by: Pavel Emelyanov <xemul@parallels.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2013-08-21 14:31:38 +04:00
|
|
|
tfile->ifindex = 0;
|
2012-10-31 23:45:57 +04:00
|
|
|
|
|
|
|
init_waitqueue_head(&tfile->wq.wait);
|
2014-05-17 02:11:48 +04:00
|
|
|
RCU_INIT_POINTER(tfile->socket.wq, &tfile->wq);
|
2012-10-31 23:45:57 +04:00
|
|
|
|
|
|
|
tfile->socket.file = file;
|
|
|
|
tfile->socket.ops = &tun_socket_ops;
|
|
|
|
|
|
|
|
sock_init_data(&tfile->socket, &tfile->sk);
|
|
|
|
|
|
|
|
tfile->sk.sk_write_space = tun_sock_write_space;
|
|
|
|
tfile->sk.sk_sndbuf = INT_MAX;
|
|
|
|
|
2009-01-20 14:00:40 +03:00
|
|
|
file->private_data = tfile;
|
2012-12-14 03:53:30 +04:00
|
|
|
INIT_LIST_HEAD(&tfile->next);
|
2012-10-31 23:45:57 +04:00
|
|
|
|
2013-06-08 10:17:41 +04:00
|
|
|
sock_set_flag(&tfile->sk, SOCK_ZEROCOPY);
|
|
|
|
|
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;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2012-10-31 23:46:00 +04:00
|
|
|
tun_detach(tfile, true);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-01-29 11:43:31 +04:00
|
|
|
#ifdef CONFIG_PROC_FS
|
2014-09-30 03:08:25 +04:00
|
|
|
static void tun_chr_show_fdinfo(struct seq_file *m, struct file *f)
|
2014-01-29 11:43:31 +04:00
|
|
|
{
|
|
|
|
struct tun_struct *tun;
|
|
|
|
struct ifreq ifr;
|
|
|
|
|
|
|
|
memset(&ifr, 0, sizeof(ifr));
|
|
|
|
|
|
|
|
rtnl_lock();
|
|
|
|
tun = tun_get(f);
|
|
|
|
if (tun)
|
|
|
|
tun_get_iff(current->nsproxy->net_ns, tun, &ifr);
|
|
|
|
rtnl_unlock();
|
|
|
|
|
|
|
|
if (tun)
|
|
|
|
tun_put(tun);
|
|
|
|
|
2014-09-30 03:08:25 +04:00
|
|
|
seq_printf(m, "iff:\t%s\n", ifr.ifr_name);
|
2014-01-29 11:43:31 +04:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
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,
|
2014-11-07 21:52:07 +03:00
|
|
|
.read_iter = tun_chr_read_iter,
|
2014-06-19 23:36:49 +04:00
|
|
|
.write_iter = tun_chr_write_iter,
|
2005-04-17 02:20:36 +04:00
|
|
|
.poll = tun_chr_poll,
|
2009-11-07 09:52:32 +03:00
|
|
|
.unlocked_ioctl = tun_chr_ioctl,
|
|
|
|
#ifdef CONFIG_COMPAT
|
|
|
|
.compat_ioctl = tun_chr_compat_ioctl,
|
|
|
|
#endif
|
2005-04-17 02:20:36 +04:00
|
|
|
.open = tun_chr_open,
|
|
|
|
.release = tun_chr_close,
|
2014-01-29 11:43:31 +04:00
|
|
|
.fasync = tun_chr_fasync,
|
|
|
|
#ifdef CONFIG_PROC_FS
|
|
|
|
.show_fdinfo = tun_chr_show_fdinfo,
|
|
|
|
#endif
|
2005-04-17 02:20:36 +04:00
|
|
|
};
|
|
|
|
|
|
|
|
static struct miscdevice tun_miscdev = {
|
|
|
|
.minor = TUN_MINOR,
|
|
|
|
.name = "tun",
|
2009-09-19 01:01:12 +04:00
|
|
|
.nodename = "net/tun",
|
2005-04-17 02:20:36 +04: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;
|
2011-04-27 22:32:40 +04:00
|
|
|
ethtool_cmd_speed_set(cmd, SPEED_10);
|
2005-04-17 02:20:36 +04:00
|
|
|
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);
|
|
|
|
|
2011-11-15 18:59:53 +04:00
|
|
|
strlcpy(info->driver, DRV_NAME, sizeof(info->driver));
|
|
|
|
strlcpy(info->version, DRV_VERSION, sizeof(info->version));
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
switch (tun->flags & TUN_TYPE_MASK) {
|
2014-11-19 16:17:31 +03:00
|
|
|
case IFF_TUN:
|
2011-11-15 18:59:53 +04:00
|
|
|
strlcpy(info->bus_info, "tun", sizeof(info->bus_info));
|
2005-04-17 02:20:36 +04:00
|
|
|
break;
|
2014-11-19 16:17:31 +03:00
|
|
|
case IFF_TAP:
|
2011-11-15 18:59:53 +04:00
|
|
|
strlcpy(info->bus_info, "tap", sizeof(info->bus_info));
|
2005-04-17 02:20:36 +04:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
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,
|
2010-07-27 17:53:43 +04:00
|
|
|
.get_link = ethtool_op_get_link,
|
2013-07-19 21:40:10 +04:00
|
|
|
.get_ts_info = ethtool_op_get_ts_info,
|
2005-04-17 02:20:36 +04:00
|
|
|
};
|
|
|
|
|
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;
|
|
|
|
|
2011-03-02 10:18:10 +03:00
|
|
|
pr_info("%s, %s\n", DRV_DESCRIPTION, DRV_VERSION);
|
|
|
|
pr_info("%s\n", DRV_COPYRIGHT);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
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) {
|
2011-03-02 10:18:10 +03:00
|
|
|
pr_err("Can't register link_ops\n");
|
2009-01-22 03:02:16 +03:00
|
|
|
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) {
|
2011-03-02 10:18:10 +03:00
|
|
|
pr_err("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
|
|
|
}
|
|
|
|
|
2010-01-14 09:17:09 +03: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)
|
|
|
|
{
|
2012-10-31 23:45:58 +04:00
|
|
|
struct tun_file *tfile;
|
2010-01-14 09:17:09 +03:00
|
|
|
if (file->f_op != &tun_fops)
|
|
|
|
return ERR_PTR(-EINVAL);
|
2012-10-31 23:45:58 +04:00
|
|
|
tfile = file->private_data;
|
|
|
|
if (!tfile)
|
2010-01-14 09:17:09 +03:00
|
|
|
return ERR_PTR(-EBADFD);
|
2012-10-31 23:45:57 +04:00
|
|
|
return &tfile->socket;
|
2010-01-14 09:17:09 +03:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(tun_get_socket);
|
|
|
|
|
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);
|
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 20:07:20 +04:00
|
|
|
MODULE_ALIAS("devname:net/tun");
|