TUN/TAP provides the reception and transmission of packets to the user-space program, which can be viewed as a simple point-to-point device or
Ethernet devices. It receives the package from the user-space program instead of receiving the package from the physical device. It sends packets not through objects
To send packets to a user-space program.
In order to apply this driver, the application needs to open the/dev/net/tun device (character device) and then issue a control
System (IOCTL) to register a NIC device, a network device will be named Tunxx or Tapxx. Depends on the logo you set
-bit When the application closes the file descriptor, the network device and other related routes will disappear.
Depending on the type of device selected, the application for user space needs to read and write IP packets (with Tun devices) or Ethernet packets (with
Tap devices). As for the specific use of that device, it relies on the flag parameters passed to the IOCTL function.
The source package address of the TUN/TAP device is Http://vtun.sourceforge.net/tun
Contains two simple examples to show how to use Tun devices and tap devices. Two programs are like these two nets
Network bridge between the device interfaces.
Br_select.c‐bridge based on the select system call.
Br_sigio.c‐bridge based on async IO and Sigio signal.
Of course, the best example is the is Vtun http://vtun.sourceforge.net:))
Module_init (Tun_init);
Module_exit (Tun_cleanup);
/* Network Device part of the driver */
Static List_head (tun_dev_list);
static const struct ETHTOOL_OPS tun_ethtool_ops;
Main data structures
struct Miscdevice
struct Miscdevice {
int minor;
const char *name;
const struct File_operations *fops;
struct List_head list;
struct device *parent;
struct device *this_device;
};
struct TUN_STRUCT
struct Tun_struct {
struct List_head list;
unsigned long flags;////differentiate Tun and tap devices
int attached;
uid_t owner;
wait_queue_head_t read_wait;////Waiting queue
struct Sk_buff_head READQ; Network buffer queue
struct Net_device *dev; Linux Abstract Network device architecture (architecture is provided by the Linux kernel
Unified network equipment structure, defines the system unified access interface. )
struct Net_device_stats stats; Network card status information structure
struct fasync_struct *fasync;////file asynchronous notification structure
unsigned long if_flags;
U8 Dev_addr[eth_alen];
U32 chr_filter[2];
U32 net_filter[2];
#ifdef Tun_debug
int debug;
#endif
};
Struct Ifreq
/*
* INTERFACE request structure used for socket
* ioctl ' s. All interface ioctl ' s must has parameter
* Definitions which begin with Ifr_name. The
* Remainder may interface specific.
*/
struct IFREQ
{
#define Ifhwaddrlen 6
Union
{
Char Ifrn_name[ifnamsiz]; /* If name, e.g. "en0" */
} IFR_IFRN;
Union {
struct SOCKADDR ifru_addr;
struct SOCKADDR ifru_dstaddr;
struct SOCKADDR ifru_broadaddr;
struct SOCKADDR ifru_netmask;
struct SOCKADDR ifru_hwaddr;
Short ifru_flags;
int ifru_ivalue;
int IFRU_MTU;
struct Ifmap ifru_map;
Char Ifru_slave[ifnamsiz]; /* Just fits the size */
Char Ifru_newname[ifnamsiz];
void __user * IFRU_DATA;
struct If_settings ifru_settings;
} Ifr_ifru;
};
Initialization of the module (tun_init)
static int __init tun_init (void)
{
int ret = 0;
PRINTK (kern_info "Tun:%s,%s/n", Drv_description, drv_version);
PRINTK (kern_info "Tun:%s/n", drv_copyright);
ret = Misc_register (&tun_miscdev);
if (ret)
PRINTK (kern_err "Tun:can ' t Register misc device%d/n", Tun_minor);
return ret;
}
static struct Miscdevice Tun_miscdev = {
. minor = Tun_minor,
. Name = "Tun",
. FoPs = &tun_fops,
};
static const struct File_operations Tun_fops = {
. Owner = This_module,
. Llseek = No_llseek,
. Read = Do_sync_read,
. Aio_read = Tun_chr_aio_read,
. write = Do_sync_write,
. Aio_write = Tun_chr_aio_write,
. Poll = Tun_chr_poll,
. IOCTL = Tun_chr_ioctl,
. open = Tun_chr_open,
. Release = Tun_chr_close,
. Fasync = Tun_chr_fasync
};
Misc_register
Use the Misc_register () function in the kernel to register the driver as a non-standard character device driver, providing a character device
There are various program interfaces.
int Misc_register (struct Miscdevice * misc)
{
struct Miscdevice *c;
dev_t Dev;
int err = 0;
Init_list_head (&misc‐>list);
Mutex_lock (&MISC_MTX);
List_for_each_entry (c, &misc_list, list) {
if (C‐>minor = = Misc‐>minor) {
Mutex_unlock (&MISC_MTX);
Return‐ebusy;
}
}
if (Misc‐>minor = = Misc_dynamic_minor) {
int i = dynamic_minors;
while (‐‐i >= 0)
if ((Misc_minors[i>>3] & (1 << (i&7))) = = 0)
Break
if (i<0) {
Mutex_unlock (&MISC_MTX);
Return‐ebusy;
}
Misc‐>minor = i;
}
if (Misc‐>minor < dynamic_minors)
Misc_minors[misc‐>minor >> 3] |= 1 << (Misc‐>minor & 7);
dev = MKDEV (misc_major, Misc‐>minor);
Misc‐>this_device = Device_create (Misc_class, misc‐>parent, Dev,
"%s", misc‐>name);
if (Is_err (Misc‐>this_device)) {
Err = Ptr_err (Misc‐>this_device);
Goto out;
}
/*
* ADD it to the front, so that later devices can "override"
* Earlier defaults
*/
List_add (&misc‐>list, &misc_list);
Out
Mutex_unlock (&MISC_MTX);
return err;
}
Tun operation of the device (System call)
Tun_chr_open (called when the device is turned on)
When a TUN/TAP device is opened, the open function calls the Tun_chr_open () function, which completes some important initialization
Ride
Initialization functions and initialization of network buffer lists and waiting queue initialization
static int Tun_chr_open (struct inode *inode, struct file * file)
{
DBG1 (kern_info "tunx:tun_chr_open/n");
File‐>private_data = null;//Initialize the contents of the device file
return 0;
}
TUN_CHR_IOCTL (Control call interface of the device)
To control the calling interface:
Cmd=
.. Tunsetiff
.. _ioc_type (cmd) = = 0x89
.. Tunsetnocsum
.. Tunsetpersist
.. Tunsetowner
.. Tunsetlink
.. Tunsetdebug
.. Siocgifflags
.. Siocsifflags
.. Siocgifhwaddr
.. Siocsifhwaddr
.. Siocaddmulti
.. Siocdelmulti
The registration of the NIC in the Tun/tap driver is embedded in the character-driven IOCTL routine, which is used by the character device file descriptor
Define the IOCTL setting flag Tunsetiff to complete the registration of the NIC.
static int tun_chr_ioctl (struct inode *inode, struct file *file,unsigned int cmd, unsigned long arg)
{
struct Tun_struct *tun = file‐>private_data;
void __user* ARGP = (void __user*) arg;
struct Ifreq IFR;
if (cmd = = Tunsetiff | | _ioc_type (CMD) = = 0x89)
if (Copy_from_user (&IFR, ARGP, sizeof IFR))//Copy the network device configuration of the user area. The user area has been
The values and configuration values of the IFREQ structure are assigned,
Return‐efault;
if (cmd = = Tunsetiff &&!tun) {//character device file data is not empty then
int err;
Ifr.ifr_name[ifnamsiz‐] = '/0 ';
Rtnl_lock ();//defined in
Err = Tun_set_iff (file, &IFR);
Rtnl_unlock ();
if (ERR)
return err;
if (Copy_to_user (ARGP, &IFR, sizeof))//Copy the configuration data to the user area
Return‐efault;
return 0;
}
if (!tun)//tun device error
RETURN‐EBADFD;
DBG (kern_info "%s:tun_chr_ioctl cmd%d/n", Tun‐>dev‐>name, CMD);
Switch (CMD) {
Case Tunsetnocsum:
/* disable/enable Checksum */
if (ARG)
Tun‐>flags |= tun_nochecksum;
Else
Tun‐>flags &= ~tun_nochecksum;
DBG (kern_info "%s:checksum%s/n",
Tun‐>dev‐>name, Arg? "Disabled": "Enabled");
Break
Case Tunsetpersist:
/* disable/enable persist mode */
if (ARG)
Tun‐>flags |= tun_persist;
Else
Tun‐>flags &= ~tun_persist;
DBG (kern_info "%s:persist%s/n",
Tun‐>dev‐>name, Arg? "Disabled": "Enabled");
Break
Case Tunsetowner:
/* Set owner of the device */
Tun‐>owner = (uid_t) arg;
DBG (kern_info "%s:owner set to%d/n", Tun‐>dev‐>name, Tun‐>owner);
Break
Case Tunsetlink:
/* Setting the type when the interface are down */
if (Tun‐>dev‐>flags & iff_up) {
DBG (kern_info "%s:linktype set failed because interface is up/n",
Tun‐>dev‐>name);
Return‐ebusy;
} else {
Tun‐>dev‐>type = (int) arg;
DBG (kern_info "%s:linktype set to%d/n", Tun‐>dev‐>name, Tun‐>dev‐>type);
}
Break
#ifdef Tun_debug
Case Tunsetdebug:
Tun‐>debug = arg;
Break
#endif
Case Siocgifflags:
Ifr.ifr_flags = tun‐>if_flags;
if (Copy_to_user (ARGP, &IFR, sizeof IFR))
Return‐efault;
return 0;
Case Siocsifflags:
/** Set The character device ' s interface flags. Currently only
* Iff_promisc and Iff_allmulti are used. */
Tun‐>if_flags = Ifr.ifr_flags;
DBG (kern_info "%s:interface flags 0x%lx/n",
Tun‐>dev‐>name, Tun‐>if_flags);
return 0;
Case SIOCGIFHWADDR:
/* note:the actual net device ' s address may different */
memcpy (Ifr.ifr_hwaddr.sa_data, TUN‐>DEV_ADDR,
Min (sizeof ifr.ifr_hwaddr.sa_data, sizeof tun‐>dev_addr));
if (Copy_to_user (ARGP, &IFR, sizeof IFR))
Return‐efault;
return 0;
Case SIOCSIFHWADDR:
{
/* Try to set the actual net device ' HW address */
int ret = dev_set_mac_address (Tun‐>dev, &ifr.ifr_hwaddr);
if (ret = = 0) {
/** Set The character device ' s hardware address. This was used when
* Filtering packets being sent from the network device to the character
* Device. */
memcpy (Tun‐>dev_addr, Ifr.ifr_hwaddr.sa_data,
Min (sizeof ifr.ifr_hwaddr.sa_data, sizeof tun‐>dev_addr));
DBG (kern_debug "%s:set Hardware address:%x:%x:%x:%x:%x:%x/n",
Tun‐>dev‐>name,
Tun‐>dev_addr[0], tun‐>dev_addr[1], tun‐>dev_addr[2],
TUN‐>DEV_ADDR[3], tun‐>dev_addr[4], tun‐>dev_addr[5]);
}
return ret;
}
Case SIOCADDMULTI:
/** ADD The specified group to the character device ' s multicast filter
* list. */
Add_multi (Tun‐>chr_filter, ifr.ifr_hwaddr.sa_data);
DBG (kern_debug "%s:add Multi:%x:%x:%x:%x:%x:%x/n",
Tun‐>dev‐>name,
(U8) ifr.ifr_hwaddr.sa_data[0], (U8) ifr.ifr_hwaddr.sa_data[1],
(U8) ifr.ifr_hwaddr.sa_data[2], (U8) ifr.ifr_hwaddr.sa_data[3],
(U8) ifr.ifr_hwaddr.sa_data[4], (U8) ifr.ifr_hwaddr.sa_data[5]);
return 0;
Case SIOCDELMULTI:
/** Remove The specified group from the character device ' s multicast
* Filter list. */
Del_multi (Tun‐>chr_filter, ifr.ifr_hwaddr.sa_data);
DBG (kern_debug "%s:del Multi:%x:%x:%x:%x:%x:%x/n",
Tun‐>dev‐>name,
(U8) ifr.ifr_hwaddr.sa_data[0], (U8) ifr.ifr_hwaddr.sa_data[1],
(U8) ifr.ifr_hwaddr.sa_data[2], (U8) ifr.ifr_hwaddr.sa_data[3],
(U8) ifr.ifr_hwaddr.sa_data[4], (U8) ifr.ifr_hwaddr.sa_data[5]);
return 0;
Default
Return‐einval;
};
return 0;
}
Tun_chr_aio_read (asynchronous read) (reads data from the TUN device)
Static ssize_t tun_chr_aio_read (struct KIOCB *iocb, const struct IOVEC *iv,
unsigned long count, loff_t POS)
{
struct File *file = iocb‐>ki_filp;
struct Tun_struct *tun = file‐>private_data;
Declare_waitqueue (wait, current);
struct Sk_buff *skb;
ssize_t len, ret = 0;
if (!tun)
RETURN‐EBADFD;
DBG (kern_info "%s:tun_chr_read/n", tun‐>dev‐>name);
Len = Iov_total (iv, Count);
if (Len < 0)
Return‐einval;
Add_wait_queue (&tun‐>read_wait, &wait);
while (Len) {
Const U8 ones[Eth_alen] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xff};
U8 addr[Eth_alen];
int bit_nr;
Current‐>state = task_interruptible;
/* Read frames from the queue */
if (! ( Skb=skb_dequeue (&TUN‐>READQ))) {
if (File‐>f_flags & O_nonblock) {
RET =‐eagain;
Break
}
if (signal_pending (current)) {
RET =‐erestartsys;
Break
}
/* Nothing to read, let's sleep */
Schedule ();
Continue
}
Netif_wake_queue (Tun‐>dev);
/** Decide whether to accept this packet. This code was designed to
* Behave identically to an Ethernet interface. Accept the packet If
*‐we is promiscuous.
*‐the packet is addressed to us.
*‐the packet is broadcast.
*‐the packet is multicast and
*‐we is multicast promiscous.
*‐we belong to the multicast group.
*/
Skb_copy_from_linear_data (SKB, addr, min_t (size_t, sizeof addr,
Skb‐>len));
BIT_NR = ETHER_CRC (sizeof addr, addr) >> 26;
if ((Tun‐>if_flags & Iff_promisc) | |
memcmp (addr, tun‐>dev_addr, sizeof addr) = = 0 | |
memcmp (addr, ones, sizeof addr) = = 0 | |
((addr[0] = = 1 && addr[1] = = 0 && addr[2] = = 0x5e) | |
(Addr[0] = = 0x33 && addr[1] = = 0x33)) &&
((Tun‐>if_flags & Iff_allmulti) | |
(Tun‐>chr_filter[bit_nr >> 5] & (1 << (BIT_NR & 31))))) {
DBG (kern_debug "%s:tun_chr_readv:accepted:%x:%x:%x:%x:%x:%x/n",
Tun‐>dev‐>name, Addr[0], addr[1], addr[2],
ADDR[3], addr[4], addr[5]);
ret = Tun_put_user (tun, SKB, (struct Iovec *) IV, Len);
KFREE_SKB (SKB);
Break
} else {
DBG (kern_debug "%s:tun_chr_readv:rejected:%x:%x:%x:%x:%x:%x/n",
Tun‐>dev‐>name, Addr[0], addr[1], addr[2],
ADDR[3], addr[4], addr[5]);
KFREE_SKB (SKB);
Continue
}
}
Current‐>state = task_running;
Remove_wait_queue (&tun‐>read_wait, &wait);
return ret;
}
Skb_dequeue (SRC/NET/CORE/SKBUFF.C)
/**
* Skb_dequeue‐remove from the head of the queue
* @list: List to dequeue from
*
* Remove the head of the list. The list lock is taken so the function
* May is used safely with other locking list functions. The head item is
* Returned or%null if the list is empty.
*/
struct Sk_buff *skb_dequeue (struct sk_buff_head *list)
{
unsigned long flags;
struct Sk_buff *result;
Spin_lock_irqsave (&list‐>lock, flags);
result = __skb_dequeue (list);
Spin_unlock_irqrestore (&list‐>lock, flags);
return result;
}
__skb_dequeue
/**
* __skb_dequeue‐remove from the head of the queue
* @list: List to dequeue from
*
* Remove the head of the list. This function does not take any locks
* So must is used with appropriate locks held only. The head item is
* Returned or%null if the list is empty.
*/
extern struct Sk_buff *skb_dequeue (struct sk_buff_head *list);
Static inline struct Sk_buff *__skb_dequeue (struct sk_buff_head *list)
{
struct Sk_buff *next, *prev, *result;
Prev = (struct Sk_buff *) list;
Next = prev‐>next;
result = NULL;
if (next! = prev) {
result = Next;
Next = next‐>next;
list‐>qlen‐‐;
Next‐>prev = prev;
Prev‐>next = Next;
Result‐>next = Result‐>prev = NULL;
}
return result;
}
Tun_put_user
/* Put packet to the user space buffer */
Static __inline__ ssize_t tun_put_user (struct tun_struct *tun,
struct Sk_buff *skb,
struct Iovec *iv, int len)
{
struct Tun_pi pi = {0, skb‐>protocol};
ssize_t total = 0;
if (! ( Tun‐>flags & Tun_no_pi)) {
if ((len‐= sizeof (PI)) < 0)
Return‐einval;
if (Len < Skb‐>len) {
/* Packet'll be striped */
Pi.flags |= Tun_pkt_strip;
}
if (Memcpy_toiovec (iv, (void *) &pi, sizeof (PI)))
Return‐efault;
Total + = sizeof (PI);
}
Len = min_t (int, skb‐>len, Len);
Skb_copy_datagram_iovec (SKB, 0, IV, Len);
Total + = Len;
tun‐>stats.tx_packets++;
Tun‐>stats.tx_bytes + = Len;
return total;
}
Tun_chr_aio_write (writes data to the TUN device)
Static ssize_t tun_chr_aio_write (struct KIOCB *iocb, const struct IOVEC *iv,
unsigned long count, loff_t POS)
{
struct Tun_struct *tun = iocb‐>ki_filp‐>private_data;
if (!tun)
RETURN‐EBADFD;
DBG (kern_info "%s:tun_chr_write%ld/n", Tun‐>dev‐>name, Count);
Return Tun_get_user (Tun, (struct Iovec *) IV, Iov_total (iv, Count));
}
Tun_get_user
/* Get packet from User space buffer */
Static __inline__ ssize_t tun_get_user (struct tun_struct *tun, struct Iovec *iv, size_t count)
{
struct Tun_pi pi = {0, __constant_htons (ETH_P_IP)};
struct Sk_buff *skb;
size_t len = count, align = 0;
if (! ( Tun‐>flags & Tun_no_pi)) {
if ((len‐= sizeof (PI)) > Count)
Return‐einval;
if (Memcpy_fromiovec (void *) &pi, IV, sizeof (PI)))
Return‐efault;
}
if ((Tun‐>flags & tun_type_mask) = = Tun_tap_dev)
align = net_ip_align;
if (! ( SKB = ALLOC_SKB (len + align, gfp_kernel)) {
tun‐>stats.rx_dropped++;
Return‐enomem;
}
if (align)
Skb_reserve (SKB, align);
if (Memcpy_fromiovec (Skb_put (SKB, Len), IV, Len)) {
tun‐>stats.rx_dropped++;
KFREE_SKB (SKB);
Return‐efault;
}
Switch (Tun‐>flags & tun_type_mask) {
Case Tun_tun_dev:
Skb_reset_mac_header (SKB);
Skb‐>protocol = Pi.proto;
Skb‐>dev = tun‐>dev;
Break
Case Tun_tap_dev:
Skb‐>protocol = Eth_type_trans (SKB, Tun‐>dev);
Break
};
if (Tun‐>flags & Tun_nochecksum)
skb‐>ip_summed = checksum_unnecessary;
Netif_rx_ni (SKB);
Tun‐>dev‐>last_rx = jiffies;
tun‐>stats.rx_packets++;
Tun‐>stats.rx_bytes + = Len;
return count;
}
Linux virtual Nic Tun and tap