云时代下服务器主要都是虚拟机和容器,其背后的网络都离不开虚拟网络设备,了解虚拟网络设备有利于更好的理解云计算的网络结构。我们一起来看下Linux下的虚拟网络设备。

对于Linux内核网络设备管理模块来说,虚拟设备和物理设备没有区别,都是网络设备,都能配置IP,从网络设备来的数据,都会转发给协议栈,协议栈过来的数据,也会交由网络设备发送出去,怎么发送出去的,发到哪里去取决于虚拟网络设备的驱动实现。

我们来看下图:


其中tun0是一个Tun/Tap虚拟设备。

图中可以看到eth0的另一端是物理网络,这个物理网络可能就是一个交换机,而tun0的另一端是一个用户层的程序,协议栈发给tun0的数据包能被这个应用程序读取到,并且应用程序能直接向tun0写数据。

l   应用程序A通过socket A发送了一个数据包,假设这个数据包的目的IP地址是192.168.x.x

l   协议栈根据数据包的目的IP地址,匹配本地路由规则,数据包由tun0出去交给tun0

l   tun0收到数据包之后,发现另一端被进程B打开了,于是丢给了进程B

l   进程B收到数据包之后,做相关的处理后构造一个新的数据包,通过socket B将数据包转发出去,这时候新数据包的源地址变成了eth0的地址,而目的IP地址变成了一个其它的地址,比如是10.33.x.x.

l   协议栈根据本地路由,发现这个数据包应该要通过eth0发送出去,于是将数据包交给eth0,通过物理网络将数据包发送出去

实现了数据的传输,不过注意的是数据包走哪个网络设备由路由表控制,如果我们想某些网络流量走应用程序B的转发流程,就需要配置路由表。

       tun/tap设备的用处是将协议栈中的部分数据包转发给用户空间的应用程序,给用户空间的程序一个处理数据包的机会。比如数据压缩,加密等功能,最常用的场景是VPN,包括tunnel以及应用层的IPSec等.

1.   关于tun和tap

       tun和tap设备存在一些差异,就是用户层程序通过tun设备只能读写IP数据包,而通过tap设备能读写链路层数据包,类似于普通socket和raw socket的差别,处理数据包的格式不一样。

       示例代码:

#include <net/if.h>

#include <sys/ioctl.h>

#include <sys/stat.h>

#include <fcntl.h>

#include <string.h>

#include <sys/types.h>

#include <linux/if_tun.h>

#include <stdlib.h>

#include <stdio.h>

int

tun_alloc (int flags)

{

  struct ifreq ifr;

  int fd, err;

  char *clonedev = "/dev/net/tun";

 

  if ((fd = open (clonedev, O_RDWR)) < 0)

    {

      return fd;

    }

 

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

  ifr.ifr_flags = flags;

 

  if ((err = ioctl (fd, TUNSETIFF, (void *) &ifr)) < 0)

    {

      close (fd);

      return err;

    }

 

  printf ("Open tun/tap device: %s for reading...\n", ifr.ifr_name);

 

  return fd;

}

int

main ()

{

 

  int tun_fd, nread;

  char buffer[1500];

 

  /* Flags: IFF_TUN   - TUN device (no Ethernet headers)

   *        IFF_TAP   - TAP device

   *        IFF_NO_PI - Do not provide packet information

   */

  tun_fd = tun_alloc (IFF_TUN | IFF_NO_PI);

 

  if (tun_fd < 0)

    {

      perror ("Allocating interface");

      exit (1);

    }

 

  while (1)

    {

      nread = read (tun_fd, buffer, sizeof (buffer));

      if (nread < 0)

        {

          perror ("Reading from interface");

          close (tun_fd);

          exit (1);

        }

 

      printf ("Read %d bytes from tun/tap device\n", nread);

    }

  return 0;

}

编译运行后,提示设备tun0在阻塞等待数据

# ./tun

Open tun/tap device: tun0 for reading...

然后使用命令查看,多出了tun0设备

# ip link

配置IP地址

# ip addr add 192.168.99.102/24 dev tun0

# ip link set tun0 up

然后就可以去ping 192.168.99.102。包会到达tun程序中但是并不会进行处理,所以包算丢了。不过tun用的比较少的,常见的还是veth,特别是在容器时代。

2.   veth设备

Virtual Ethernet Pair简称veth pair,是一个成对的端口,所有从这对端口一 端进入的数据包都将从另一端出来。veth是一种新的设备,专门为 container 所建。


示例:

通过ip link添加veth0和veth1.

#ip link add veth0 type veth peer name veth1

创建成功后,可以通过ip link看到一对端口

ip addr add 192.168.1.10/24 dev veth0

ip link set veth0 up

删除设备只需要执行如下即可

#ip link delete veth1

仅有veth-pair设备,容器是无法访问网络的。因为容器发出的数据包,实质上直接进入了veth1设备的协议栈里。如果容器需要访问网络,需要使用bridge等技术,将veth1接收到的数据包通过某种方式转发出去 

3.   网桥

3.1     Linux网桥

当2个以上命名空间或者KVM/LXC实例需要通过链接的时候,网桥必不可少,linux提供了一个linux网桥。使用命令为brctl。


3.2     OVS网桥

另外一个解决2个以上命名空间或者KVM/LXC实例链接的办法是使用OVS网桥。OVS网桥全称是openvswitch网桥,使用逻辑和linux网桥一样,命令有些差异,命令为ovs-vsctl.

4.   参考

http://backreference.org/2010/03/26/tuntap-interface-tutorial/

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

LinuxSwitching-Interconnecting Namespaces

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐