其他分享
首页 > 其他分享> > 网络虚拟化基本原理

网络虚拟化基本原理

作者:互联网

文章目录

背景

  1. 虚拟机
  1. 报文收发
  1. 报文转发

基本组件

tun/tap device

基本原理

基本用法

网卡创建

  #include <linux/if.h>
  #include <linux/if_tun.h>

  int tun_alloc(char *dev)
  {
      struct ifreq ifr;
      int fd, err;
	  /* 获得文件描述符,用户态程序对虚拟网卡的读写通过次描述符完成 */
      if( (fd = open("/dev/net/tun", O_RDWR)) < 0 )
         return tun_alloc_old(dev);

      memset(&ifr, 0, sizeof(ifr));
	  /* 设置创建设备的标志,IFF_TUN表示创建三层网卡,只能收发IP报文 
	  * IFF_TAP表示创建二层网卡,报文中包含以太网帧头部
	  */
      /* Flags: IFF_TUN   - TUN device (no Ethernet headers) 
       *        IFF_TAP   - TAP device  
       *
       *        IFF_NO_PI - Do not provide packet information  
       */ 
      ifr.ifr_flags = IFF_TUN; 
      if( *dev )
         strncpy(ifr.ifr_name, dev, IFNAMSIZ);
      /* 调用TUNSETIFF ioctl命令字,传入ifreq信息,创建虚拟网卡 */
      if( (err = ioctl(fd, TUNSETIFF, (void *) &ifr)) < 0 ){
         close(fd);
         return err;
      }
      strcpy(dev, ifr.ifr_name);
      return fd;
  } 

持久化

  /* "delete" is set if the user wants to delete (ie, make nonpersistent)
     an existing interface; otherwise, the user is creating a new
     interface 
     用户态程序可以设置网卡持久化,也可以反向操作,禁用其持久化的模式
     这些操作通过TUNSETPERSIST命令字完成
   */
     
  if(delete) {
    /* remove persistent status */
    if(ioctl(tap_fd, TUNSETPERSIST, 0) < 0){
      perror("disabling TUNSETPERSIST");
      exit(1);
    }
    printf("Set '%s' nonpersistent\n", ifr.ifr_name);
  }
  else {
    /* emulate behaviour prior to TUNSETGROUP */
    if(owner == -1 && group == -1) {
      owner = geteuid();
    }
    /* 设置虚拟化网卡的用户 */
    if(owner != -1) {
      if(ioctl(tap_fd, TUNSETOWNER, owner) < 0){
        perror("TUNSETOWNER");
        exit(1);
      }
    }
    /* 设置虚拟化网卡的组 */
    if(group != -1) {
      if(ioctl(tap_fd, TUNSETGROUP, group) < 0){
        perror("TUNSETGROUP");
        exit(1);
      }
    }
     /* 使能虚拟网卡持久化 */
    if(ioctl(tap_fd, TUNSETPERSIST, 1) < 0){
      perror("enabling TUNSETPERSIST");
      exit(1);
    }

    if(brief)
      printf("%s\n", ifr.ifr_name);
    else {
      printf("Set '%s' persistent and owned by", ifr.ifr_name);
      if(owner != -1)
          printf(" uid %d", owner);
      if(group != -1)
          printf(" gid %d", group);
      printf("\n");
    }
  }

数据收发

  char tun_name[IFNAMSIZ];
  
  /* Connect to the device */
  strcpy(tun_name, "tun77");
  /* 创建或者连接一个虚拟化网卡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);
  }

openvswitch

基本原理

基本用法

端口转发

cookie: 用于识别一组流表项,可以方便地进行批量流表操作,控制器通常会使用该字段,如果不设置默认为0
duration: 记录流表项在ovs桥上的存活时间,秒为单位
table: 流表项所在的流表
n_packets: 统计该流表项匹配的网络包个数
n_bytes: 统计该流表项匹配的网络包字节数
idle_age: 上一次流表项匹配成功到现在的时间,秒为单位
prority: 优先级,同一个流表内,数字越大优先级越高,高优先级的流表项优先被匹配到
arp: 同dl_type = 0x0806,表示L2层协议的payload为ARP包。dl_type即data link type,2层协议
arp_tpa: ARP是以太网的payload,当dl_type(ether_type)为0x0806时,arp_tpa表示ARP请求的目标IP地址
action: 如果有网络包与该流表匹配,将其从端口2发送出去

IP路由

ip: 同dl_type = 0x0800,表示L2层协议的payload为IP包
nw_dst: IP协议中目的端的IP地址

测试验证

报文收发

  1. 通过openvpn工具创建一个持久化模式的tun设备tun77,配置IP地址10.0.0.1,当dst IP是10.0.0.1的icmp包到达内核时,内核datapath会将其转发到tun77设备
openvpn --mktun --dev tun77 --user hyman
ip link set tun77 up
ip addr add 10.0.0.1/24 dev tun77
ping 10.0.0.1
  1. 编写一个操作tun/tap设备的程序,打开tun77设备并循环读,打印其内容,如果tun77上有流量,将会被打印出来,代码如下:
int main(int argc, char *argv[]) {
  int tun_fd;
  unsigned int nread;
  char tun_name[IFNAMSIZ];
  char buffer[BUFSIZE];
  int i;
 /* 打开使用openvpn工具创建的tun/tap设备tun77 */
  /* 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("\nRead %d bytes from device %s\n Raw data:\n", nread, tun_name);

    for (i = 0; i < nread; i++) {
        printf("%x", buffer[i]);
    }
  }

  return(0);
}
gcc tunclient.c -o tunclient
./tunclient
  1. 主机上重开一个终端运行ping命令,测试10.0.0.2的连通性
ping 10.0.0.2

报文转发

虚机1网卡配置:
    <interface type='bridge'>
      <mac address='24:42:54:20:50:46'/>
      <source bridge='br0'/>
      <virtualport type='openvswitch'/>
      <model type='virtio'/>
    </interface>
虚机2网卡配置:
    <interface type='bridge'>
      <mac address='24:42:53:21:52:4e'/>
      <source bridge='br0'/>
      <virtualport type='openvswitch'/>
      <model type='virtio'/>
    </interface>

vlan隔离

标签:tun,虚拟化,基本原理,报文,网络,网卡,虚机,转发,icmp
来源: https://blog.csdn.net/huang987246510/article/details/119208365