===============================================================================================================
@@ 简单背景介绍
eBPF 中有2个 prog_type:
prog_type = BPF_PROG_TYPE_SK_SKB # 之后,简称为 prog_sk_skb
prog_type = BPF_PROG_TYPE_SK_MSG # 之后,简称为 prog_sk_msg
可以用来实现在内核态中的 packet 在不同的 socket 之间的 redirect。
在这之前,如果需要实现 packet 在不同的 socket 之间的 redirect,可能是需要在用户态执行这样的逻辑:
recvmsg( socket_0, buffer ) # 包含一次 kernel-user-level memcpy,将包内容,从内核态的 socket RXQ 拷贝到 用户态 buffer 中。
sendmsg( socket_1, buffer ) # 包含另一次 kernel-user-level memcpy,将包内容,从 用户态 buffer 拷贝到 内核态 socket TXQ 中。
对一个包,涉及到 2次 memcpy。
#
# --- 这就,好像很浪费呀。
#
而 eBPF 的这两个 prog_type,可以实现在内核态下的,包 从一个 socket 的 RXQ,直接进入 另外一个 socket 的 TXQ。
#
# --- 于是,节省了 2次 memcpy。
#
大致的逻辑,简单介绍一下: # 并不是很精确,大致如此。 详细可见: https://mp.weixin.qq.com/s/VL6oKW1m0PXmuuE1v8h0iw
创建一个 eBPF socketmap,实际上是一个 array,向里面填充是 socket 的 FD (__从用户态的角度看是如此,但在内核态逻辑中,FD 会转换为 socket instance )。
将 prog_sk_skb 和 prog_skb_msg “安装”到 socketmap 上。 # 两个 prog 里面,是自己写的代码逻辑。 也可只安装一个。
prog_sk_skb 是对 socket 的 ingress traffic 进行处理:
从底层网卡和协议栈接收到一个ingress packet,在放进 socket RXQ 之前,执行 prog_sk_skb。
如果 prog_sk_skb 对该 packet 进行 redirect 操作(__重定向到 socketmap 中另外一个 socket),则将该 packet 放进 另一个 socket 的 TXQ(__也可以放进它RXQ)。
否则,不进行redirect操作的话,则正常进入原来socket的 RXQ。
prog_sk_msg 是对 socket 的 egress traffic 进行处理:
从用户态 sendmsg() 发送下来的 egress packet,在放进 socket TXQ 之前,执行 prog_sk_msg。
prog_sk_msg 可以将该 egress packet,redirect 到 socketmap 中另外一个 socket 的 RXQ 或者 TXQ。
否则,不进行 redirect 的话,则正常进入原来socket 的 TXQ。
===============================================================================================================
@@ 看起来好好,不是吗? --- 但是,可能没有想象得那么好。有很多限制。
===============================================================================================================
@@ 限制 #1. 只支持 TCP socket,不支持 UDP socket
虽然,看起来支持 UDP socket 也能够做到,但是 **我个人的猜测理解**,不支持 UDP socket 是有原因的。
TCP socket 是 有连接 的。只有 alive TCP socket,才能够被加入到 socketmap 中去,当 TCP connection 断开时,TCP socket 自动从 socketmap 移除。
并且,TCP socket 是两端都会有反馈, 一端 发送包过去,另一段会返回 ACK,表明TCP connection仍然存在。
只要 TCP connection 仍然存在,就能够继续愉快地 redirect packet。
而 UDP socket 是无连接的 connectionless 。一端发送包,另一端不一定会返回任何包。甚至都不知道,对端是不是真的存在。
这样的话,如果在2个 UDP sockets 之间 一直 redirect packet, 就,可能看不到反馈,发送到黑洞里去了,都不知道。 --- 就感觉不太好。
--- 一个办法,在用户态在 UDP sockets 上 实现某种两端反馈机制。 但这是很自定义的机制,没什么通用性。 --- 不通用的东西,可能 kernel 也懒得去考虑。
另外一些原因,是和 UDP socket 的收发包的一些细节有关(__实际上,也还是因为 UDP 是 connection less)。
当然,要强行去实现支持UDP socket,好像也不是不可以,那就需要改kernel了,并且,也是很私人的修改,上不了台面,不产生问题就不错了。
===============================================================================================================
@@ 限制 #2. 只支持 “单播”的 redirect,不支持 “多播”的 redirect
可能会有,将一个packet,同时 redirect 到多个 socket 的想法。 --- “多播”嘛。
但是,当前不支持。
当前,只支持,将一个 packet,从一个 socket,“单播”地 redirect 到另一个 socket。
原因嘛? 我现在想想,也想不出来自己说得通的原因,但总感觉这样不好。
同样,要强行去实现“多播”,好像也不是不可以,那就需要改kernel了,并且,也是很私人的修改,上不了台面,不产生问题就不错了。
类似之前想过的,
在 eBPF/XDP 中实现 L2 的转发“多播” # https://blog.csdn.net/xzhao28/article/details/110247359
也是这样的上不了台面。
===============================================================================================================
@@ <extra tip>: prog_type = BPF_PROG_TYPE_SK_SKB 实际上有2个 attach_type = BPF_SK_SKB_STREAM_PARSER 和 BPF_SK_SKB_STREAM_VERDICT,前者 是基于 stream_parser 框架进行 parsing,后者才是做 redirect,一前一后先后执行的关系。 --- 但好像( 个人觉得), parsing / redirect 之间,好像当前并没有太符合 stream_parser(strparser) 原本的框架的设计期望。 --- 细节太多了,无法展开说 --- 但是,感觉 之后 eBPF 的这两个 prog_type 肯定还会继续完善和进化吧。
===============================================================================================================