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

posted @ 2012-09-22 13:17  helloweworld  阅读(1200)  评论(0编辑  收藏  举报