作者:张华 发表于:2015-11-13
版权声明:可以任意转载,转载时请务必以超链接形式标明文章原始出处和作者信息及本版权声明
(http://blog.csdn.net/quqi99 )
程序员所接触的应用项目大都是基于流式套接字(SOCK_STREAM)和数据报式套接字(SOCK_DGRAM)。
原始套接字IPPROTO_RAW允许用户旁路TCP/IP栈实现IP层及其以上协议层的通信;PF_PACKET则允许二层链路层的帧。
什么是BPF
Linux secomp (secure computing mode)可以限制用户进程可以使用的系统调用,它有两种工作模式:
- 一种是SECCOMP_MODE_STRICT模式,简单粗暴地规定只能调用read, write, exit, sigreturn四种系统调用,没有open, 意味着进程只能用已有文件描述符,除了读写之外,剩下的就只有退出了,这有点适合网格计算的场景,通过read获取指令,计算,通过write写回结果
- 另一种是SECCOMP_MODE_FILTER模式,它要优雅和灵活许多,它允许进程自定义对系统调用的限制,不仅可以限制系统调用号,还可以限制系统调用的参数。这种方式通过在seccomp.c的seccomp_filter结构体中引入sock_filter定义过滤指令运行在BPF(Berkeley Packet Filter)虚拟机上。BPF虚拟机有一个累加器,一个索引寄存器,大小为16*32bit的内存(Scratch Memory Store)和一个不可见的程序计算数。说它是虚拟机,不如说它是伪机(Rseudo Machine),因为它的某些指令要取网络包头或者系统调用之类的数据,这些东西都不在它可怜的16*32bit的内存中。所以BPF虚拟机能运行sock_filter定义的指令结构(sock_filter中有代码、跳转和立即数,sock_filter里装的就下面用tcpdump看到的伪代码)。有趣地是,内核开发者引入了JIT(Just In Time)编译器,将BPF指令编译为本机指令提高运行速度。
BPF(BerkeleyPacketFilter,伯克利数据包过滤器)允许在内核态下丢弃那些不需要的数据包,从而避免所有包都从内核态拷贝到用户态的网络监控工具而提升性能(也可采用LIBPCAP函数库实现过滤与捕捉)。BPF主要包括两部分:
- Network tap(分头器),是一个回调函数,当BPF在网卡上监听时,网卡驱动会首先调用BPF将数据传给过滤器
- Packet Filter(过滤器),决定是否接收该数据包,以及接收数据包的哪些部分
通过SO_ATTACH_FILTER允许用户空间程序attach到任意
socket, TC, kprobe, syscalls,tracepoints来过滤某种类型的流量(所以可用eBPF+tc代替OVS kernel dataplane)。设置BPF过滤器是通过setsockopt调用来完成的,格式如下:
setsockopt(sd, SOL_SOCKET, SO_ATTACH_FILTER, &Filter, sizeof(Filter))
设置过滤器的方法如下:
SetFilter(char *mac1, char *mac2)
{
struct sock_filter code[]={
{ 0×20, 0, 0, 0×00000008 },
{ 0×15, 0, 2, ntohl(*(unsigned int *)(mac1 + 2)) },
{ 0×28, 0, 0, 0×00000006 },
{ 0×15, 3, 4, ntohs(*(unsigned short *)mac1) },
{ 0×15, 0, 3, ntohl(*(unsigned int *)(mac2 + 2)) },
{ 0×28, 0, 0, 0×00000006 },
{ 0×15, 0, 1, ntohs(*(unsigned short *)mac2) },
{ 0×6, 0, 0, 0×00000060 },
{ 0×6, 0, 0, 0×00000000 }
};
…
}
其中过滤器里面的BPF代码(BPF代码是伪汇编程序,也可以使用C语言写,有工具将C变成伪汇编)可由tcpdump加-dd参数自动生成(用tcpdump生成的BPF代码只能用于SOCK_RAW的socket):
root@hua-ThinkPad-T440p:~# tcpdump ether src 01:02:03:04:05:06 or ether src 04:05:06:07:08:09 -dd
{ 0x20, 0, 0, 0x00000008 },
{ 0x15, 0, 2, 0x03040506 },
{ 0x28, 0, 0, 0x00000006 },
{ 0x15, 3, 4, 0x00000102 },
{ 0x15, 0, 3, 0x06070809 },
{ 0x28, 0, 0, 0x00000006 },
{ 0x15, 0, 1, 0x00000405 },
{ 0x6, 0, 0, 0x00040000 },
{ 0x6, 0, 0, 0x00000000 },
BPF也使用了in-kernel JIT compiler实时编译(使用LLVM编译,目前还不支持GCC)通过尽量将寄存器直接映射为 x86_64 的物理寄存器将虚拟机指令也尽可能直接地映射机器指令加速BPF代码,
什么是eBPF
Linux 3.15 开始引入 eBPF。其扩充了 BPF 的功能,丰富了指令集。到了 eBPF 后,虚拟机的功能并都更加强大,使得我们对数据包的操作都可以在内核灵活地实现,完全不需要加载重新编译,而且可以在线替换等,灵活性大大增加。数据平面也就变得更加灵活(可以随时改变对每个数据包的处理行为)。且在内核中完成该功能使得性能大大提高(避免从内核到用户态的拷贝)。当然,其安全性等问题也还是非常地重要的,毕竟内核几乎就有了绝对的权限。此内容以后再说。
什么是IO Visor
IO Visor的基本思路是使用一种灵活的方式在内核实现对网络数据包的处理,而不需要像传统的方法那样通过加载内核模块的方式来实现、或者通过繁重的系统调用在用户态计算。从而实现一个灵活的数据平面,加速NFV。
参考
1, http://www.cnblogs.com/rollenholt/articles/2585517.html
2, http://blog.sina.com.cn/s/blog_4e4ea8770100u22w.html
3, http://www.slideshare.net/PLUMgrid/ebpf-and-linux-networking
4, https://www.iovisor.org/technology/download