最近需要连接Quagga和SDN Controller,由于Quagga原因,目前没办法直接在两者之间建立连接,所以只能曲线救国,采用tun/tap搞个虚拟网卡中转一下。本文主要介绍以下tun/tap的机制,关于具体连接部分将在以后进行介绍。
tun和tap的区别在于tun是三层设备,用于ip转发,无法与物理网卡做 bridge,但是可以通过三层交换(如 ip_forward)与物理网卡连通;tap是二层设备,用于mac转发。
以下几个图来自kghost的博客。
首先是正常网卡接收报文是从物理链路接收的:
而虚拟网卡(tun/tap)是从用户态接收数据,在用户态和协议栈之间进行中转数据。数据从用户态读进来,穿透/dev/net/tun
字符设备抵达内核,然后交给虚拟网卡,虚拟网卡将报文通过协议栈交给用户态进程tun/tap驱动进程,反之亦然。具体在字符设备如何在用户态和内核态传递数据可以参考这篇博客,介绍的比较详细。
当然,tun/tap的用途可以用来搞VPN,数据包两次经过协议栈,在App地方实现数据包的封装加密,从而达到目的。
以下代码是介绍如何创建一个虚拟网卡的过程,具体全部代码可以查看simpletun。
int tun_alloc(char *dev, int flags) {
struct ifreq ifr;
int fd, err;
if( (fd = open("/dev/net/tun", O_RDWR)) < 0 ) {
perror("Opening /dev/net/tun");
return fd;
}
memset(&ifr, 0, sizeof(ifr));
ifr.ifr_flags = flags;
if (*dev) {
strncpy(ifr.ifr_name, dev, IFNAMSIZ);
}
if( (err = ioctl(fd, TUNSETIFF, (void *)&ifr)) < 0 ) {
perror("ioctl(TUNSETIFF)");
close(fd);
return err;
}
strcpy(dev, ifr.ifr_name);
return fd;
}
tun_alloc
函数创建一个虚拟网卡设备,并返回一个文件描述符,我们可以通过对该描述符的read/write
达到数据交互的目的。创建完之后可以通过ifconfig -a
查看到设备的存在,但是目前的状态的down的,我们需要把它分配个ip并up起来,假设文件名为abc:
ifconfig abc 20.1.1.1/24
,ok,这时候查看ifconfig
发现设备已经起来了。这个时候我们当然可以ping这个ip,但是由于环回地址lo
的存在,所有本地的ip都不会经过协议栈到达网卡,而是直接返回了。正确的做法是我们先检查路由表:route -n
,发现存在这样一条路由表项:Destination=20.1.1.0/24 Iface=abc
,表示发往20.1.1.0/24网段的都从abc网卡出去,所以ping这个网段的主机就会到达这个虚拟网卡。然后可以ping这个20.1.1.0/24
网段内的主机地址,这样就会从abc网卡上发出报文了:ping 20.1.1.2
。报文就会穿过虚拟网卡到达用户态的App,我们就可以read读取这个报文了。虚拟网卡在内核态传递给用户态时的做法是缓存报文,用户态调用read时才会传递给用户态。我们可以按如下代码实现最简单的读取:
...
/* tunclient.c */
char tun_name[IFNAMSIZ];
/* Connect to the device */
strcpy(tun_name, "tun77");
tun_fd = tun_alloc(tun_name, IFF_TUN | IFF_NO_PI); /* tun interface */
if(tun_fd < 0){
perror("Allocating interface");
exit(1);
}
/* Now read data coming from the kernel */
while(1) {
/* Note that "buffer" should be at least the MTU size of the interface, eg 1500 bytes */
nread = read(tun_fd,buffer,sizeof(buffer));
if(nread < 0) {
perror("Reading from interface");
close(tun_fd);
exit(1);
}
/* Do whatever with the data */
printf("Read %d bytes from device %s\n", nread, tun_name);
}
我们此时ping 20.1.1.2
会发现不断显示读取到报文。
写到这里,tun/tap实现中转部分已经差不多介绍完毕了,那么剩下的连接部分我们就可以在用户态完成,比如左边的App连接Controller,右边的socket api连接Quagga,两者都可以建立socket连接,采用epoll/select/poll
方式监听多个描述符,就比如simpletun中所使用的poll
建立vpn连接,都是一个道理。
之后的博客我将会介绍具体如何连接Controller和Quagga。
参考:
http://www.ibm.com/developerworks/cn/linux/l-tuntap/
https://blog.kghost.info/2013/03/27/linux-network-tun/
http://stackoverflow.com/questions/9374165/tuntap-interface-in-c-linux-cant-capture-udp-packets-sent-on-the-tuntap-wit
http://www.cnblogs.com/heidsoft/p/3525788.html