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就是把接收到的数据写进虚拟网卡。

  • 相关阅读:
    BestCoder17 1001.Chessboard(hdu 5100) 解题报告
    codeforces 485A.Factory 解题报告
    codeforces 485B Valuable Resources 解题报告
    BestCoder16 1002.Revenge of LIS II(hdu 5087) 解题报告
    codeforces 374A Inna and Pink Pony 解题报告
    codeforces 483B Friends and Presents 解题报告
    BestCoder15 1002.Instruction(hdu 5083) 解题报告
    codeforces 483C.Diverse Permutation 解题报告
    codeforces 483A. Counterexample 解题报告
    NSArray中地内存管理 理解
  • 原文地址:https://www.cnblogs.com/helloweworld/p/2697950.html
Copyright © 2011-2022 走看看