Skip to content

Instantly share code, notes, and snippets.

@shpark
Last active November 3, 2021 05:34
Show Gist options
  • Save shpark/258040755f1b20b10b60f3586610254a to your computer and use it in GitHub Desktop.
Save shpark/258040755f1b20b10b60f3586610254a to your computer and use it in GitHub Desktop.

How to open/create a TUN interface

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.

Internals

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;
};

tun_file

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);

tun_attach(tun, file, ...)

Summary:

  • Configures file->tfile. Without properly setting up file->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;

tun_fops

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment