fd = open("/dev/net/tun", O_RDWR);
struct ifreq ifr;
ifr.ifr_flags = IFF_TUN; // or IFF_TAP
strncpy(ifr.ifr_name, (char*)name, IFNAMSIZ-1);
int res = ioctl(fd, TUNSETIFF, &ifr);
// If successful, now `fd` is used to access the tun interface.
// A tun interface may be created, or can be an existing one.
Recap: Each process has an associated file descriptors to struct file
mapping
(not 100% accurate).
Note that if you call ioctl on some fd, the corresponding struct file *
will be
passed to internal ioctl function (Spoiler alert: Linux will replace struct file *
for /dev/net/tun
to a certain tuntap interface to/from which you can write/read.).
Plus, a struct file *
has a versatile void *private_data
field, which is used as
struct tun_file
for tun files (struct tun_file *tfile = file->private_data
).
/* A tun_file connects an open character device to a tuntap netdevice. It
* also contains all socket related structures (except sock_fprog and tap_filter)
* 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
* netdevice not for a specific queue (at least I didn't see the requirement for
* this).
*
* RCU usage:
* The tun_file and tun_struct are loosely coupled, the pointer from one to the
* other can only be read while rcu_read_lock or rtnl_lock is held.
*/
struct tun_file {
struct sock sk;
struct socket socket;
struct tun_struct __rcu *tun;
struct fasync_struct *fasync;
/* only used for fasnyc */
unsigned int flags;
union {
u16 queue_index;
unsigned int ifindex;
};
struct napi_struct napi;
bool napi_enabled;
bool napi_frags_enabled;
struct mutex napi_mutex; /* Protects access to the above napi */
struct list_head next;
struct tun_struct *detached;
struct ptr_ring tx_ring;
struct xdp_rxq_info xdp_rxq;
};
vs.
/* Since the socket were moved to tun_file, to preserve the behavior of persist
* device, socket filter, sndbuf and vnet header size were restore when the
* file were attached to a persist device.
*/
struct tun_struct {
struct tun_file __rcu *tfiles[MAX_TAP_QUEUES];
unsigned int numqueues;
unsigned int flags;
kuid_t owner;
kgid_t group;
struct net_device *dev;
netdev_features_t set_features;
#define TUN_USER_FEATURES (NETIF_F_HW_CSUM|NETIF_F_TSO_ECN|NETIF_F_TSO| \
NETIF_F_TSO6)
int align;
int vnet_hdr_sz;
int sndbuf;
struct tap_filter txflt;
struct sock_fprog fprog;
/* protected by rtnl lock */
bool filter_attached;
u32 msg_enable;
spinlock_t lock;
struct hlist_head flows[TUN_NUM_FLOW_ENTRIES];
struct timer_list flow_gc_timer;
unsigned long ageing_time;
unsigned int numdisabled;
struct list_head disabled;
void *security;
u32 flow_count;
u32 rx_batched;
atomic_long_t rx_frame_errors;
struct bpf_prog __rcu *xdp_prog;
struct tun_prog __rcu *steering_prog;
struct tun_prog __rcu *filter_prog;
struct ethtool_link_ksettings link_ksettings;
};
Every time a process opens a tun, a tun_file is created.
tfile = (struct tun_file*)sk_alloc(net, AF_UNSPEC, GFP_KERNEL, &tun_proto, 0);
...
tfile->socket.file = file;
tfile->socket.ops = &tun_socket_ops;
RCU_INIT_POINTER(tfile->tun, NULL); // XXX: set by tun_attach()
...
tfile->sk.sk_write_space = tun_sock_write_space // ???
tfile->sk.sk_sndbuf = INT_MAX;
file->private_data = tfile;
INIT_LIST_HEAD(&tfile->next);
Summary:
- Configures
file->tfile
. Without properly setting upfile->tfile
, you cannot read() or write() (See definitions of tun_do_read() and tun_get_user()). - Link b/w tun (the glboal? tun struct) and tfile (tfile of current file).
tun_file = file->private_data;
dev = tun->dev;