zoukankan      html  css  js  c++  java
  • vtun 对虚拟网卡的读写操作

    一、对虚拟网卡的读写操作都在哪里?

    对虚拟网卡写操作函数tun_write在tun_dev.c中定义;

    函数指针dev_write在tunnel.c中指向tun_write函数;

    函数指针dev_write在linkfd.c中对虚拟网卡进行写操作。

    因此实际对虚拟网卡的写操作在linkfd.c中。

    涉及到写操作的linkfd.c中的代码:

    if( len && dev_write(fd2,out,len) < 0 ){
                 if( errno != EAGAIN && errno != EINTR )
                    break;
                 else
                    continue;
    }

     

    简单的理解dev_write函数是向虚拟网卡fd2中写入out数据。

    二、dev_write(fd2,out,len) 中fd2 是啥?

    那么我们就搞清楚fd2   out   len这三个参数到底是什么!尤其是fd2和out!

    猜测fd2是虚拟设备文件描述符,out是截获到的数据?

    1、在linkfd.c中有如下定义:

    int fd2 = lfd_host->loc_fd;

    猜测:loc应该是local的缩写,loc_fd应该是本地的描述符?

    struct vtun_host *lfd_host;

     

    vtun_host结构体都是在vtun.h中定义的。

    到此,貌似陷入死局了!已经找到了fd2对应的最原始变量lfd_host,但lfd_host到底是哪个设备文件的描述符?!应该是host的虚拟网卡设备描述符,但是根源在哪里,也就是说,在哪里将host的设备描述符赋予lfd_host的?!

    请相信,山重水复疑无路!注意到lfd_host的定义上面有两行注释,相当重要:

    /* Host we are working with.
    * Used by signal handlers that's why it is global.
    */

    也就是说lfd_host是信号处理函数调用的,是全局变量!

    全局变量就说明其他地方可以使用该变量!那么在哪里被使用的呢?

    于是查找lfd_host出现的地方,发现了依然子linkfd.c中定义这么一句:

    lfd_host=host

    int linkfd(struct vtun_host *host){…}


    好了,下面就看linkfd函数在哪里被调用了,即host获取的是谁的值。

    找啊找 。。。

    找到了,在tunnel.c的函数int tunnel(struct vtun_host *host)中出现了:

    opt = linkfd(host);

    尼玛,这里的host又是啥!看host的定义处:

    int tunnel(struct vtun_host *host){...}

    我去,又得看tunnel在哪被调用的,继续找!

    找到了,在client.c和server.c中都有调用,先看client.c:

    client_term = tunnel(host);

    再看此处的host定义的地方:

    void client(struct vtun_host *host){…}

    哎,又要找client被调用的地方了。。。。

    我去,终于有点眉目了,在main.c中出现了!

    client(host);

    好,再看此处host的定义:

    struct vtun_host *host = NULL;

    初始值是空,那么我们看host被赋值的地方:

    依然在main.c中,

    hst = argv[optind++];//vtund server [ip]的第二个参数给hst

    if( !(host = find_host(hst)) )
            {
                vtun_syslog(LOG_ERR,"Host %s not found in %s", hst, vtun.cfg_file);
                exit(1);
    }

    好了,下面就看find_host(hst)的返回值了!

     

    找到find_host()函数定义的地方:

    在vtun.h中,有原型说明,
    struct vtun_host * find_host(char *host);

    find_host在cfg_file.y文件中定义

    /* Find host in the hosts list.
    * NOTE: This function can be called only once since it deallocates hosts list.
    */
    inline struct vtun_host* find_host(char *host)
    {
       return (struct vtun_host *)llist_free(&host_list, free_host, host);
    }

    host_list是一个llist型的全局变量,其值是从配置文件中获取的。

    下面分析llist_free函数,

    在llist.c中定义:

    /* Travel list from head to tail, deallocate each element */
    void * llist_free(llist *l, int (*f)(void *d, void *u), void *u)
    {
        llist_elm *i = l->head, *n;
        void *ff = NULL;

        while( i )
        {
            n = i->next;    
            if( f(i->data,u) )
                ff = i->data;
            else
              free(i);
           i = n;
        }
        l->head = l->tail = NULL;
        return ff;
    }

    其中llist_elm在llist.h中定义:

    struct llist_element {
        struct llist_element * next;
        void * data;
    };
    typedef struct llist_element llist_elm;

    typedef struct {
        llist_elm * head;
        llist_elm * tail;
    } llist;

     

    llist_element是单项链表;typedef重定义了llist_element,所以llist_elm就是llist_element单项链表;而llist是一个自定义结构体类型名有两个llist_elm成员head和tail

    下面分析llist_free函数

    void * llist_free(llist *l, int (*f)(void *d, void *u), void *u)
    {
        llist_elm *i = l->head, *n;  //定义两个节点指针i和n;初始化i
        void *ff = NULL;

        while( i )
        {
            n = i->next;    
            if( f(i->data,u) )            //简单理解为判断命令行host和配置文件中host是否相等。

                ff = i->data;
            else
              free(i);
           i = n;
        }
        l->head = l->tail = NULL;
        return ff;
    }

     

    free_host在cfg_file.y中定义:

    功能简单理解为判断命令行参数和配置文件中获取的参数是否相同。

    int free_host(void *d, void *u)
    {
       struct vtun_host *h = d;

       if (u && !strcmp(h->host, u))
          return 1;

       free(h->host);  
       free(h->passwd);  
       llist_free(&h->up, free_cmd, NULL);  
       llist_free(&h->down, free_cmd, NULL);

       free_addr(h);

       /* releases only host struct instances which were
        * allocated in the case of K_HOST except default_host */
       if( h->passwd )
          free(h);

       return 0;  
    }

    综上所述,fd2来源于配置文件,但是配置文件中具体哪个参数与fd2有关?

    那就要看host_list的值是什么了,因为host决定了fd2,而llist_free的返回值决定了host,而该返回值与host_list有关,所以要看host_list是啥!

     

    下面分析host_list:

    在cfg_file.y中定义了host_list全局变量:llist host_list;

    有一段代码是给host_list赋值的:

    '{' host_options '}'
            {
              /* Check if session definition is complete */
              if (!parse_host->passwd) {
                  cfg_error("Ignored incomplete session definition '%s'", parse_host->host);
                free_host(parse_host, NULL);           
                free(parse_host);
              } else {
                  /* Add host to the list */
                 llist_add(&host_list, (void *)parse_host);
              }
            }

     

    下面分析llist_add:

    在llist.c中定义,

    int llist_add(llist *l, void * d)
    {
        llist_elm *e;

        if( !(e=malloc(sizeof(llist_elm))) )
           return -1;    

        if( !l->head )
           l->head = l->tail = e;
        else
           l->tail->next = e;
        l->tail = e;

        e->next = NULL;
        e->data = d;

        return 0;
    }

    llist_add函数就是把节点d增加到链表l上。

     

    那么parse_host是什么?

    在cfg_file.y中定义,

    struct vtun_host *parse_host;

    cfg_file.y中对parse_host进行了赋值,这部分是提取操作,不是很懂。。。

    找到了vtun_host成员passwd等等在此处获取值了,但是没有找到loc_fd!!!!

    重新回顾思路,终于发现问题啦!

    在tunnel.c中已经对loc_fd进行赋值了!是赋完值再对将参数host传给linkfd函数的:

    int fd[2]={-1, -1};//初试值,下面对fd进行了赋值

    ………

    if( ! interface_already_open )
        {
            switch( host->flags & VTUN_TYPE_MASK ){
                case VTUN_TTY:
                    if( (fd[0]=pty_open(dev)) < 0 )
                    {
                        vtun_syslog(LOG_ERR,"Can't allocate pseudo tty. %s(%d)", strerror(errno), errno);
                        return -1;
                    }
                break;

                case VTUN_PIPE:
                    if( pipe_open(fd) < 0 )
                    {
                        vtun_syslog(LOG_ERR,"Can't create pipe. %s(%d)", strerror(errno), errno);
                        return -1;
                    }
                    break;

                case VTUN_ETHER:
                    if( (fd[0]=tap_open(dev)) < 0 )
                    {
                        vtun_syslog(LOG_ERR,"Can't allocate tap device %s. %s(%d)", dev, strerror(errno), errno);
                        return -1;
                    }
                    break;

                case VTUN_TUN:
                    if( (fd[0]=tun_open(dev)) < 0 )
                    {
                        vtun_syslog(LOG_ERR,"Can't allocate tun device %s. %s(%d)", dev, strerror(errno), errno);
                        return -1;
                    }
                    break;
            }//end switch

    …………

    host->loc_fd = fd[0];

    ………

    opt = linkfd(host);

     

    最新总结,调用dev_write(fd2,out,len) 时fd2在tunnel.c中定义,而fd2就是tun_open(dev)返回的设备描述符!

     

    下面分析tun_open函数:

    我们来看generic文件夹下的定义,

    int tun_open(char *dev)
    {
        char tunname[14];
        int i, fd;

        if( *dev ) {
           sprintf(tunname, "/dev/%s", dev);
           return open(tunname, O_RDWR);
        }

        for(i=0; i < 255; i++){
           sprintf(tunname, "/dev/tun%d", i);
           /* Open device */
           if( (fd=open(tunname, O_RDWR)) > 0 ){
              sprintf(dev, "tun%d", i);
              return fd;
           }
        }
        return -1;
    }

    找到打开虚拟设备根源了就是open(tunname, O_RDWR),tunname是虚拟设备名,O_RDWR是以读写方式打开。

    这里tunname可以从配置文件中获取:

    tunnel.c中有下面代码可以说明:

    /* Initialize device. */
        if( host->dev )
        {
            strncpy(dev, host->dev, VTUN_DEV_LEN);
            dev[VTUN_DEV_LEN-1]='\0';
        }

    也可以是默认的即

    从sprintf(tunname, "/dev/tun%d", i);可以看出,默认为tun0一直到tun255.

    最终总结:在tunnel.c定义了fd2,fd2就是虚拟网卡的设备描述符。且tunnel.c对虚拟网卡进行了打开操作。

    对虚拟网卡的操作基本清楚了,
    1、tunnel.c调用tun_open打开虚拟网卡;
    2、实际的打开动作在generic文件中,generic中使用系统函数open打开虚拟网卡,返回虚拟设备描述符;
    3、tunnel.c中,获取描述符,该描述符被赋予变量host的成员loc_fd中;
    4、linkfd.c中,调用dev_write dev_read对虚拟网卡进行读写操作。

     

    三、dev_write(fd2,out,len) 中out 是啥?


    我们回到dev_write函数被调用的地方:

    linkfd.c中的一段代码,
           if( len && dev_write(fd2,out,len) < 0 ){
                  if( errno != EAGAIN && errno != EINTR )
                     break;
                  else
                     continue;
               }

      if( (len=lfd_run_up(len,buf,&out)) == -1 )
                   break;
               if( len && dev_write(fd2,out,len) < 0 )
               {
                   if( errno != EAGAIN && errno != EINTR )
                       break;
                   else
                       continue;
               }

    lfd_run_up简单的理解是buf数据给out了。

    那么buf是什么?找到下面代码:

    if( (len=proto_read(fd1, buf)) <= 0 )
    可以看出buf是接收到的数据。

    综述所述,dev_write就是把接收到的数据写进虚拟网卡。

  • 相关阅读:
    merge into语句的使用
    mybatis框架下解决数据库中表的列的字段名和实体类属性不相同的问题
    Mybatis框架基于映射文件和配置文件的方式,实现增删改查,可以打印日志信息
    Mybatis框架基于注解的方式,实对数据现增删改查
    java中的泛型和sql中的索引
    SpringMVC框架下的异常处理
    SpringMVC框架下的拦截器
    在SpringMVC框架下实现数据的国际化(即数据实现多国文字之间的转换)
    Java 随机数生成工具RandomUtils
    Java 获取客服端ip地址
  • 原文地址:https://www.cnblogs.com/helloweworld/p/2697950.html
Copyright © 2011-2022 走看看