如何用ebpf开启tun网卡的TUNSETSTEERINGEBPF功能

第一部分 前置: 

        大概两个月前, 有幸读到了csdn大神dog250写的一篇有关tun的文章, 里面详细介绍了如何演变tun读写的架构, 感觉非常厉害, 但是又有很多迷惑, 尤其是他提到了需要使用ebpf来开启tun的steering的功能, 那就更是一无所知了. 感兴趣的同学可以看下他的原文, 链接如下.

        tun虚拟网卡该怎么玩不该怎么玩_Netfilter,iptables/OpenVPN/TCP guard:-(-CSDN博客

        虽然不是很懂, 但我还是硬着头皮在高版本linux内核环境中ioctl TUNSETSTEERINGEBPF功能, 虽然编译不会报错, 但是启动tun测试程序的时候会失败. 好吧, 只能搜索ebpf的相关资料了. 断断续续搜索了一个多月, 一直无法入门, 那就只能看linux内核里的ebpf测试用例了. 

        下载内核源码, 编译bpf测试用例. 当然这个也花了不少时间, 为了后来的同学能有个参考, 同样也写了一篇博客, 见以下链接:

        如何使用linux源码编译bpf_c_cppcoder的博客-CSDN博客

        前几天又大概看了下tun的源码, 发现里面有个函数(见下图), 突然就有点顿悟了, 那就根据测试用例写ebpf的测试程序吧. 

第二部分 源码:

        代码分为两个文件, 一个是tun_steer_user.c, 一个是tun_steer_kern.c, 详细代码如下:

        tun_steer_user.c:

/*
tun_steer_user.c
打开两个tun队列
两个线程轮询read(fd, data, data_len),  成功后write(fd, data, data_len)
数据就是ping数据
*/

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <linux/bpf.h>
#include <bpf/bpf.h>
#include <bpf/libbpf.h>
#include <linux/if_tun.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>

typedef struct mixer
{
	int index;
	int fd;
	char *msg;
}mixer_t;

static int create_tun(const char *tun_name, int *fds, int fds_size, const int *prog)
{
        int i = 0;
        for(; i < fds_size; ++i)
        {
                struct ifreq ifr;
                int fd = 0;
                if ((fd = open("/dev/net/tun", O_RDWR)) == -1)
                {
                        printf("open tun failed: %s\n", strerror(errno));
                        return -1;
                }

                ifr.ifr_flags = IFF_NO_PI | IFF_MULTI_QUEUE | IFF_TUN;
                strcpy(ifr.ifr_name, tun_name);
                if (ioctl(fd, TUNSETIFF, (void *) &ifr) == -1)
                {
                        printf("ioctl tun failed: %s\n", strerror(errno));
                        return -1;
                }

                if (ioctl(fd, TUNSETSTEERINGEBPF, (void *)prog) == -1)
                {
                        printf("ioctl tun TUNSETSTEERINGEBPF failed: %s\n", strerror(errno));
                        return -1;
                }             
                fds[i] = fd;
        }
        return 0;
}

static void *ping_pong(void *arg)
{
	mixer_t *m = (mixer_t *)arg;

	int ret = 0, tun = m->fd, index = m->index;
	unsigned char buf[256] = {};

	for(;;) 
	{
		unsigned char ip[4];
		ret = read(tun, buf, sizeof(buf));
		if (ret < 0)
		{
			printf("exit\n");
			break;
		}
		memcpy(ip, &buf[12], 4);
		memcpy(&buf[12], &buf[16], 4);
		memcpy(&buf[16], ip, 4);
		buf[20] = 0;
		*((unsigned short*)&buf[22]) += 8;

		if (buf[0] != 0x60)
		{
			printf("%s read from fd: %d, index: %d\n", m->msg, tun, index);
		}
		ret = write(tun, buf, ret);
	}
	return NULL;
}

int main(int ac, char **argv)
{
	struct bpf_object *obj;
	int prog_fd;
	char filename[256];
	int fds[2];

	snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]);

	if (bpf_prog_load(filename, BPF_PROG_TYPE_SOCKET_FILTER, &obj, &prog_fd))
	{
		return -1;
	}

	if (create_tun("tun0", fds, sizeof(fds)/sizeof(int), &prog_fd))
	{
		return -1;
	}

	FILE *f = popen("/usr/sbin/ifconfig tun0 10.10.0.1 pointopoint 10.10.0.2", "r");
	fclose(f);
	f = popen("/usr/sbin/route add -net 10.10.0.0 netmask 255.255.0.0 gw 10.10.0.2", "r");
	fclose(f);

	mixer_t sub;
	sub.index = 0;
	sub.fd = fds[0];
	sub.msg = "sub thread";
	pthread_t pid;
	pthread_create(&pid, NULL, ping_pong, (void *)&sub);

	mixer_t mai;
	mai.index = 1;
	mai.fd = fds[1];
	mai.msg = "main thread";
	ping_pong((void *)&mai);
	return 0;
}

        tun_steer_kern.c:

/*
tun_steer_kern.c
获取目的地址, 并根据最后两个字节的值返回不同的值;
比如: 10.10.0.6, 最后两个字节是6, 返回0.
*/

#include <uapi/linux/bpf.h>
#include <uapi/linux/if_ether.h>
#include <uapi/linux/if_packet.h>
#include <uapi/linux/ip.h>
#include "bpf_helpers.h"

SEC("tun_steer")
int bpf_prog1(struct __sk_buff *skb)
{
	u32 nhoff = skb->cb[0];
	u32 daddr = load_word(skb, nhoff + offsetof(struct iphdr, daddr));
	u32 ret = daddr & 0x0000ffff;

	if (ret < 0x0010)
	{
		return 0;
	}	
	return 1;
}
char _license[] SEC("license") = "GPL";

第三部分 编译:

        在bpf的Makefile里添加4行代码

hostprogs-y += tracex6
hostprogs-y += tracex7
hostprogs-y += tun_steer // 第一行, 可以根据上面两行找到位置


tracex6-objs := bpf_load.o tracex6_user.o
tracex7-objs := bpf_load.o tracex7_user.o
tun_steer-objs := bpf_load.o tun_steer_user.o // 第二行


always += tracex6_kern.o
always += tracex7_kern.o
always += tun_steer_kern.o // 第三行


HOSTLOADLIBES_test_overhead	+= -lrt
HOSTLOADLIBES_xdpsock		+= -pthread
HOSTLOADLIBES_tun_steer		+= -pthread // 第四行

        Makefile规则已更改, 可以编译了.

第四部分 测试:

        在本机打开另一个终端

        1, 先ping 10.10.0.6, 查看打印

        2, 再ping 10.10.0.15, 查看打印

        3, 再ping 10.10.0.16, 查看打印

        4, 再ping 10.10.0.30, 查看打印

        结果如下:

         可以看到, 开启tun steer功能后, 内核代码返回的值, 可以决定tun的哪个队列有数据. 

结束.

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值