Linux Tun/Tap网口(/dev/net/tun)的读写方法

我的代码
https://github.com/liuqun/demo-linux-tun-virtual-netcard/blob/master/demo-1-ioctl/src/main.c

代码片段1

以下代码节选自"openV屁N"的源码tun.c文件:

openV屁N封装了一组读写函数如下:

  • open_tun(dev_name, dev_type, dev_node, tuntap上下文)打开Tun虚拟网卡
  • close_tun(tuntap上下文)
  • n_bytes = write_tun(tuntap上下文, buf, len)
  • n_bytes = read_tun(tuntap上下文, buf, len)
int write_tun(struct tuntap *tt, uint8_t *buf, int len)
{
    return write(tt->fd, buf, len);
}
int read_tun(struct tuntap *tt, uint8_t *buf, int len)
{
    return read(tt->fd, buf, len);
}
void open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
{
    struct ifreq ifr;

    /*
     * We handle --dev null specially, we do not open /dev/null for this.
     */
    if (tt->type == DEV_TYPE_NULL)
    {
        open_null(tt);
    }
    else
    {
        /*
         * Process --dev-node
         */
        const char *node = dev_node;
        if (!node)
        {
            node = "/dev/net/tun";
        }

        /*
         * Open the interface
         */
        if ((tt->fd = open(node, O_RDWR)) < 0)
        {
            msg(M_ERR, "ERROR: Cannot open TUN/TAP dev %s", node);
        }

        /*
         * Process --tun-ipv6
         */
        CLEAR(ifr);
        ifr.ifr_flags = IFF_NO_PI;

#if defined(IFF_ONE_QUEUE) && defined(SIOCSIFTXQLEN)
        ifr.ifr_flags |= IFF_ONE_QUEUE;
#endif

        /*
         * Figure out if tun or tap device
         */
        if (tt->type == DEV_TYPE_TUN)
        {
            ifr.ifr_flags |= IFF_TUN;
        }
        else if (tt->type == DEV_TYPE_TAP)
        {
            ifr.ifr_flags |= IFF_TAP;
        }
        else
        {
            msg(M_FATAL, "I don't recognize device %s as a tun or tap device",
                dev);
        }

        /*
         * Set an explicit name, if --dev is not tun or tap
         */
        if (strcmp(dev, "tun") && strcmp(dev, "tap"))
        {
            strncpynt(ifr.ifr_name, dev, IFNAMSIZ);
        }

        /*
         * Use special ioctl that configures tun/tap device with the parms
         * we set in ifr
         */
        if (ioctl(tt->fd, TUNSETIFF, (void *) &ifr) < 0)
        {
            msg(M_ERR, "ERROR: Cannot ioctl TUNSETIFF %s", dev);
        }

        msg(M_INFO, "TUN/TAP device %s opened", ifr.ifr_name);

        /*
         * Try making the TX send queue bigger
         */
#if defined(IFF_ONE_QUEUE) && defined(SIOCSIFTXQLEN)
        if (tt->options.txqueuelen)
        {
            struct ifreq netifr;
            int ctl_fd;

            if ((ctl_fd = socket(AF_INET, SOCK_DGRAM, 0)) >= 0)
            {
                CLEAR(netifr);
                strncpynt(netifr.ifr_name, ifr.ifr_name, IFNAMSIZ);
                netifr.ifr_qlen = tt->options.txqueuelen;
                if (ioctl(ctl_fd, SIOCSIFTXQLEN, (void *) &netifr) >= 0)
                {
                    msg(D_OSBUF, "TUN/TAP TX queue length set to %d", tt->options.txqueuelen);
                }
                else
                {
                    msg(M_WARN | M_ERRNO, "Note: Cannot set tx queue length on %s", ifr.ifr_name);
                }
                close(ctl_fd);
            }
            else
            {
                msg(M_WARN | M_ERRNO, "Note: Cannot open control socket on %s", ifr.ifr_name);
            }
        }
#endif /* if defined(IFF_ONE_QUEUE) && defined(SIOCSIFTXQLEN) */

        set_nonblock(tt->fd);
        set_cloexec(tt->fd);
        tt->actual_name = string_alloc(ifr.ifr_name, NULL);
    }
    return;
}
void close_tun(struct tuntap *tt)
{
    if (tt)
    {
        if (tt->type != DEV_TYPE_NULL && tt->did_ifconfig)
        {
            struct argv argv = argv_new();
            struct gc_arena gc = gc_new();

#ifdef ENABLE_IPROUTE
            if (is_tun_p2p(tt))
            {
                argv_printf(&argv,
                            "%s addr del dev %s local %s peer %s",
                            iproute_path,
                            tt->actual_name,
                            print_in_addr_t(tt->local, 0, &gc),
                            print_in_addr_t(tt->remote_netmask, 0, &gc)
                            );
            }
            else
            {
                argv_printf(&argv,
                            "%s addr del dev %s %s/%d",
                            iproute_path,
                            tt->actual_name,
                            print_in_addr_t(tt->local, 0, &gc),
                            netmask_to_netbits2(tt->remote_netmask)
                            );
            }
#else  /* ifdef ENABLE_IPROUTE */
            argv_printf(&argv,
                        "%s %s 0.0.0.0",
                        IFCONFIG_PATH,
                        tt->actual_name
                        );
#endif /* ifdef ENABLE_IPROUTE */

            argv_msg(M_INFO, &argv);
            open_execve_check(&argv, NULL, 0, "Linux ip addr del failed");

            if (tt->did_ifconfig_ipv6_setup)
            {
                const char *ifconfig_ipv6_local = print_in6_addr(tt->local_ipv6, 0, &gc);

#ifdef ENABLE_IPROUTE
                argv_printf(&argv, "%s -6 addr del %s/%d dev %s",
                            iproute_path,
                            ifconfig_ipv6_local,
                            tt->netbits_ipv6,
                            tt->actual_name
                            );
                argv_msg(M_INFO, &argv);
                open_execve_check(&argv, NULL, 0, "Linux ip -6 addr del failed");
#else  /* ifdef ENABLE_IPROUTE */
                argv_printf(&argv,
                            "%s %s del %s/%d",
                            IFCONFIG_PATH,
                            tt->actual_name,
                            ifconfig_ipv6_local,
                            tt->netbits_ipv6
                            );
                argv_msg(M_INFO, &argv);
                open_execve_check(&argv, NULL, 0, "Linux ifconfig inet6 del failed");
#endif
            }

            argv_reset(&argv);
            gc_free(&gc);
        }
        close_tun_generic(tt);
        free(tt);
    }
}
#define TUNNEL_TYPE(tt) ((tt) ? ((tt)->type) : DEV_TYPE_UNDEF)
#define TUNNEL_TOPOLOGY(tt) ((tt) ? ((tt)->topology) : TOP_UNDEF)

/*
 * Define a TUN/TAP dev.
 */
struct tuntap
{
    int type; /* DEV_TYPE_x as defined in proto.h */

    int topology; /* one of the TOP_x values */

    bool did_ifconfig_setup;
    bool did_ifconfig_ipv6_setup;
    bool did_ifconfig;

    bool persistent_if;         /* if existed before, keep on program end */

    struct tuntap_options options; /* options set on command line */

    char *actual_name; /* actual name of TUN/TAP dev, usually including unit number */

    /* number of TX buffers */
    int txqueuelen;

    /* ifconfig parameters */
    in_addr_t local;
    in_addr_t remote_netmask;
    in_addr_t broadcast;

    struct in6_addr local_ipv6;
    struct in6_addr remote_ipv6;
    int netbits_ipv6;

#ifdef _WIN32
    HANDLE hand;
    struct overlapped_io reads;
    struct overlapped_io writes;
    struct rw_handle rw_handle;

    /* used for setting interface address via IP Helper API
     * or DHCP masquerade */
    bool ipapi_context_defined;
    ULONG ipapi_context;
    ULONG ipapi_instance;
    in_addr_t adapter_netmask;

    /* Windows adapter index for TAP-Windows adapter,
     * ~0 if undefined */
    DWORD adapter_index;

    int standby_iter;
#else  /* ifdef _WIN32 */
    int fd; /* file descriptor for TUN/TAP dev */
#endif

#ifdef TARGET_SOLARIS
    int ip_fd;
#endif

#ifdef HAVE_NET_IF_UTUN_H
    bool is_utun;
#endif
    /* used for printing status info only */
    unsigned int rwflags_debug;

    /* Some TUN/TAP drivers like to be ioctled for mtu
     * after open */
    int post_open_mtu;
};

代码片段2

节选自Linux内核文档提供的一段Tun/Tap用户层样例代码(多处细节已经调整)
https://www.kernel.org/doc/Documentation/networking/tuntap.txt

// 样例代码
#include 
#include 

  int open_tun_and_select_type(char devName[IFNAMSIZ], int devType)//devName字符串指针不能为NULL,但可以是全0x00即'\0'
  {
      struct ifreq ifr;
      int fd;
      int err;

      if( (fd = open("/dev/net/tun", O_RDWR)) < 0 )
         return old_style__open_tun_and_select_type(devName, devType);

      memset(&ifr, 0, sizeof(ifr));

      /* Flags: IFF_TUN   - TUN device (no Ethernet headers) 
       *        IFF_TAP   - TAP device  
       *
       *        IFF_NO_PI - Do not provide packet information  
       */ 
      ifr.ifr_flags = (short) devType; 
      if( *devName ) {
         memcpy(ifr.ifr_name, devName, IFNAMSIZ);
         ifr.ifr_name[IFNAMSIZ-1] = '\0';
      }

      if( (err = ioctl(fd, TUNSETIFF, (void *) &ifr)) < 0 ){
         close(fd);
         return err;
      }
      memcpy(devName, ifr.ifr_name, IFNAMSIZ);
      return fd;
  }              
  • Linux标准struct ifreq结构体的定义位于, 细节如下:
    http://man7.org/linux/man-pages/man7/netdevice.7.html
    https://github.com/torvalds/linux/blob/master/include/uapi/linux/if.h
#define IFNAMSIZ    16
// 该结构体用于修改网卡的各种参数, 包括MAC地址/IP地址/MTU最大值/Metric跃点数等
// 之前的程序我们仅仅用到 ifr.ifr_flags=IFF_TUN切换Tun/Tap网卡的类型
           struct ifreq {
               char ifr_name[IFNAMSIZ]; /* Interface name */
               union {
                   struct sockaddr ifr_addr;
                   struct sockaddr ifr_dstaddr;
                   struct sockaddr ifr_broadaddr;
                   struct sockaddr ifr_netmask;
                   struct sockaddr ifr_hwaddr;
                   short           ifr_flags;
                   int             ifr_ifindex;
                   int             ifr_metric;
                   int             ifr_mtu;
                   struct ifmap    ifr_map;
                   char            ifr_slave[IFNAMSIZ];
                   char            ifr_newname[IFNAMSIZ];
                   char           *ifr_data;
               };
           };

数据格式

tun_read() / tun_write()读写操作的数据包结构如下(摘自Linux内核文档):

frame_format: 
   - Flags [2 bytes]
   - Proto [2 bytes]
   - Raw protocol(IP, IPv6, etc) frame.

https://www.kernel.org/doc/Documentation/networking/tuntap.txt

你可能感兴趣的:(Linux Tun/Tap网口(/dev/net/tun)的读写方法)