Cloud_12虚拟网络设备tap tun veth-pair实践

tap/tun

创建tun设备

ip tuntap add dev tun0 mod tun

配置ip地址

ifconfig tun0 192.168.0.254 up

为接口添加路由

route add -host 192.168.0.1 dev tun0

删除tun设备

ip tuntap del dev tun0 mod tun

通过用户应用程序使用tun(先删除通过ip tuntap命令添加的tun设备)

+----------------------------------------------------------------+
|                                                                |
|  +--------------------+      +--------------------+            |
|  | User Application A |      | User Application B |<-----+     |
|  +--------------------+      +--------------------+      |     |
|               | 1                    | 5                 |     |
|...............|......................|...................|.....|
|               ↓                      ↓                   |     |
|         +----------+           +----------+              |     |
|         | socket A |           | socket B |              |     |
|         +----------+           +----------+              |     |
|                 | 2               | 6                    |     |
|.................|.................|......................|.....|
|                 ↓                 ↓                      |     |
|             +------------------------+                 4 |     |
|             | Newwork Protocol Stack |                   |     |
|             +------------------------+                   |     |
|                | 7                 | 3                   |     |
|................|...................|.....................|.....|
|                ↓                   ↓                     |     |
|        +----------------+    +----------------+          |     |
|        |      eth0      |    |      tun0      |          |     |
|        +----------------+    +----------------+          |     |
| 192.168.80.134 |                   |   192.168.1.134     |     |
|                | 8                 +---------------------+     |
|                |                                               |
+----------------|-----------------------------------------------+
                 ↓
         Physical Network

tun0是连着内核协议栈和用户应用B(通过open该设备)的管道,当协议栈往tun0发送数据时,用户应用B能收到对应的数据。
User Application A:
能够触发协议栈往tun0发送数据(通过设备文件实现内核态和用户态通信)就行,这里直接使用ping
User Application B:
打开tun设备,接收协议栈发往tun0的数据

/*tun.c*/
#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, 0, sizeof(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;
}

步骤:
1.运行tun,打开字符设备文件
./tun
2.配置tun设备的IP地址192.168.1.134和接口状态,并在tun接口抓包
ip addr add 192.168.1.134/24 dev tun0
ip link set tun0 up
tcpdump -i tun0
3.触发数据发往tun0
ping 192.168.1.100(协议栈查路由,发现该icmp req包发往tun0)
通过抓包程序和应用B的打印,可以看到数据发送到了tun的另一端应用B,但是B没做处理,包到此终结。
如果修改应用B,使其返回icmp rep,则完成了一个ICMP应答功能。

#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(char *dev, int flags)
{

    struct ifreq ifr;
    int fd, err;
    char *clonedev = "/dev/net/tun";

    if ((fd = open(clonedev, O_RDWR)) < 0) {
        return fd;
    }

    memset(&ifr, 0, sizeof(ifr));
    ifr.ifr_flags = flags;

	if (*dev != '\0')
    {
		strncpy(ifr.ifr_name, dev, IFNAMSIZ);
    }
    if ((err = ioctl(fd, TUNSETIFF, (void *) &ifr)) < 0) {
        close(fd);
        return err;
    }

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

	// 一旦设备开启成功,系统会给设备分配一个名称,对于tun设备,一般为tunX,X为从0开始的编号
	strcpy(dev, ifr.ifr_name);

    return fd;
}

int main()
{

    int tun_fd, nread;
    char buffer[1500];
	char tun_name[IFNAMSIZ];

	tun_name[0] = '\0';

    /* Flags: IFF_TUN   - TUN device (no Ethernet headers)
     *        IFF_TAP   - TAP device
     *        IFF_NO_PI - Do not provide packet information
     */
    tun_fd = tun_alloc(tun_name, IFF_TUN | IFF_NO_PI);

    if (tun_fd < 0) {
        perror("Allocating interface");
        exit(1);
    }

    while (1) {
		unsigned char ip[4];

		//收包
        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);

		//构造icmp rep包
		memcpy(ip, &buffer[12], 4);
		memcpy(&buffer[12], &buffer[16], 4);
		memcpy(&buffer[16], ip, 4);

		buffer[20] = 0;
		*((unsigned short *)&buffer[22]) += 8;

		//发包
        nread = write(tun_fd, buffer, nread);

		printf("Write %d bytes to tun/tap device, that's %s\n", nread, buffer);
    }
    return 0;
}

veth-pair

veth-pair 就是一对的虚拟设备接口,和 tap/tun 设备不同的是,它都是成对出现的。一端连着协议栈,一端彼此相连着。

创建一对 veth-pair veth0 veth1

ip link add veth0 type veth peer name veth1

给veth-pair分别配置ip地址

ip addr add 10.1.1.1/24 dev veth0
ip addr add 10.1.1.2/24 dev veth1

设置状态up

ip link set veth0 up
ip link set veth1 up

veth-pair连通性

[root@ ~]# ping -I veth0 10.1.1.2 -c 2
PING 10.1.1.2 (10.1.1.2) from 10.1.1.1 veth0: 56(84) bytes of data.
64 bytes from 10.1.1.2: icmp_seq=1 ttl=64 time=0.021 ms
64 bytes from 10.1.1.2: icmp_seq=2 ttl=64 time=0.033 ms

如果ping不通,是由于ARP没学到,Ubuntu 系统内核中一些 ARP 相关的默认配置限制所导致,需要修改一下配置项:
echo 1 > /proc/sys/net/ipv4/conf/veth1/accept_local
echo 1 > /proc/sys/net/ipv4/conf/veth0/accept_local
echo 0 > /proc/sys/net/ipv4/conf/all/rp_filter
echo 0 > /proc/sys/net/ipv4/conf/veth0/rp_filter
echo 0 > /proc/sys/net/ipv4/conf/veth1/rp_filter

抓包

veth0:
tcpdump -nnt -i veth0
IP 10.1.1.2 > 10.1.1.1: ICMP echo request, id 8499, seq 1, length 64
IP 10.1.1.2 > 10.1.1.1: ICMP echo request, id 8499, seq 2, length 64
IP 10.1.1.2 > 10.1.1.1: ICMP echo request, id 8499, seq 3, length 64
IP 10.1.1.2 > 10.1.1.1: ICMP echo request, id 8499, seq 4, length 64
veth1:
tcpdump -nnt -i veth1
IP 10.1.1.2 > 10.1.1.1: ICMP echo request, id 8499, seq 19, length 64
IP 10.1.1.2 > 10.1.1.1: ICMP echo request, id 8499, seq 20, length 64
IP 10.1.1.2 > 10.1.1.1: ICMP echo request, id 8499, seq 21, length 64
IP 10.1.1.2 > 10.1.1.1: ICMP echo request, id 8499, seq 22, length 64
都只有echo request包
在loopback抓包:
tcpdump -nnt -i lo
IP 10.1.1.1 > 10.1.1.2: ICMP echo reply, id 8499, seq 79, length 64
IP 10.1.1.1 > 10.1.1.2: ICMP echo reply, id 8499, seq 80, length 64
IP 10.1.1.1 > 10.1.1.2: ICMP echo reply, id 8499, seq 81, length 64
IP 10.1.1.1 > 10.1.1.2: ICMP echo reply, id 8499, seq 82, length 64
其实这里 echo reply 走的是 localback 口

整个ping流程

由于 veth0 连着 veth1,所以 ICMP request 直接发给 veth1。
veth1 收到请求后,交给另一端的协议栈。
协议栈看本地有 10.1.1.3 这个 IP,于是构造 ICMP reply 包,查看路由表,发现回给 10.1.1.0 网段的数据包应该走 localback 口,于是将 reply 包交给 lo 口(会优先查看路由表的 0 号表,ip route show table 0 查看)。
lo 收到协议栈的 reply 包后,啥都没干,转手又回给协议栈。
协议栈收到 reply 包之后,发现有 socket 在等待包,于是将包给 socket。
等待在用户态的 ping 程序发现 socket 返回,于是就收到 ICMP 的 reply 包。
cloud_dev_veth_pair_ping.jpg
cloud_dev_veth_pair_ping.jpg

删除veth-pair设备

ip link del veth0

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值