Be careful kernel 4.15.x, stuck in the getsockopt
失败访问情况分析
先说结论,怀疑主机默认内核4.15.6-1.el7.elrepo.x86_64 (centos7) 存在调用系统内核函数getsockopt的严重bug!!!
失败连接通信抓包
环境:
mkmaster1 10.200.50.118 国贸机房 edgemesh-server, edgemesh-agent role: master kernel: 5.2.14-1.el7.elrepo.x86_64
mkworker1 10.201.82.139 天坛机房 edgemesh-agent role: cloudnode kernel: 5.2.14-1.el7.elrepo.x86_64
mkworker2 10.201.83.74 天坛机房 edgemesh-agent role: cloudnode kernel: 5.2.14-1.el7.elrepo.x86_64
mkedge1 10.201.82.148 天坛机房 edgemesh-agent role: edgenode kernel: 4.15.6-1.el7.elrepo.x86_64
mkedge2 10.201.82.131 天坛机房 edgemesh-agent role: edgenode kernel: 4.15.6-1.el7.elrepo.x86_64
mkedge3 10.136.77.2 北四环办公室边缘盒子 edgemesh-agent role: edgenode kernel: 3.10.0-514.el7.x86_64
用例:
在mkworker1、mkworker2、mkedge1、mkedge2上启动同时启动tcp-echo-cloud容器,通过mkedge1上的busybox容器进行远程访问这些服务,发现一个都不通;
现状:
从实际流量抓包看来看,从业务容器发送请求给到edgemesh-agent:40001端口,之后流量就不见了踪影;
- 在反覆启停这些容器,经过多次测试,的确无法进行正常通信;
- 怀疑是iptables的问题,因丢失了DOCKER链之后,重启docker服务也无法创建规则链;
- 重启edgemesh-agent服务,发现其容器无法被清除,一直是Running状态,如果docker rm -f强制停止,会导致edgemesh-agent在宿主机上留下僵尸进程;

初始排查阶段
究竟是Go的版本不对?容器镜像问题?逐个进行了排查:
给edgemesh-agent:proxy.go的Run()函数中,realServerAddress(&conn)调用之前,增加了打印日志,同时在这个函数调用中,增加了一些日志输出标记位,重新制作容器进行测试,发现输出日志555,但未输出666,同时进程状态异常变为了D:
func (proxy *EdgeProxy) Run() {
// ensure ipatbles
proxy.Proxier.Start()
// start tcp proxy
for {
conn, err := proxy.TCPProxy.Listener.Accept()
if err != nil {
klog.Warningf("get tcp conn error: %v", err)
continue
}
klog.Info("!!! has workload !!!")
ip, port, err := realServerAddress(&conn)
...
}
}
// realServerAddress returns an intercepted connection's original destination.
func realServerAddress(conn *net.Conn) (string, int, error) {
tcpConn, ok := (*conn).(*net.TCPConn)
if !ok {
return "", -1, fmt.Errorf("not a TCPConn")
}
klog.Info("111")
file, err := tcpConn.File()
if err != nil {
return "", -1, err
}
defer file.Close()
klog.Info("222")
// To avoid potential problems from making the socket non-blocking.
tcpConn.Close()
*conn, err = net.FileConn(file)
if err != nil {
return "", -1, err
}
klog.Info("333")
fd := file.Fd()
klog.Info("444")
var addr sockAddr
size := uint32(unsafe.Sizeof(addr))
err = getSockOpt(int(fd), syscall.SOL_IP, SoOriginalDst, uintptr(unsafe.Pointer(&addr)), &size)
if err != nil {
return "", -1, err
}
klog.Info("777")
var ip net.IP
switch addr.family {
case syscall.AF_INET:
ip = addr.data[2:6]
default:
return "", -1, fmt.Errorf("unrecognized address family")
}
klog.Info("888")
port := int(addr.data[0])<<8 + int(addr.data[1])
if err := syscall.SetNonblock(int(fd), true); err != nil {
return "", -1, nil
}
return ip.String(), port, nil
}
func getSockOpt(s int, level int, name int, val uintptr, vallen *uint32) (err error) {
klog.Info("555")
_, _, e1 := syscall.Syscall6(syscall.SYS_GETSOCKOPT, uintptr(s), uintptr(level), uintptr(name), uintptr(val), uintptr(unsafe.Pointer(vallen)), 0)
if e1 != 0 {
err = e1
}
klog.Info("666")
return
}
然后就开始怀疑是这个内核调用出现了问题,先编写了一个go程序:https://gitee.com/Hu-Lyndon/gogetsockopt.git
如下是在mkedge3宿主机上,kernel: 3.10.0-514.el7.x86_64,一个终端上先将程序启动监听18011端口,然后在另外一个连接mkedge3终端上,直接telnet这个端口,程序能够正确通信,并输出打印日志address: 192.168.127.1:18011

如下是在mkedge1上测试,kernel: 4.15.6-1.el7.elrepo.x86_64, 发现telnet之后,无法输出打印日志,并且程序在第三个终端窗口里也无法kill -9,并且进程状态变为了D;

不死心,程序在mkedge2上依然如上;
不死心,找了台内核升级为kernel: 4.19.12-1.el7.elrepo.x86_64的主机,结果是正确的;

再次排查阶段
不死心:虽然严重怀疑了kernel: 4.15.6-1.el7.elrepo.x86_64,那么究竟是Go的问题,还是系统内核的问题呢?于是又写了一个C程序来测试:https://gitee.com/Hu-Lyndon/unix-network-programming,在compile-on-kernel4.15.6分支的/sockopt/sockoptchk.c
依然出现了上面出现的问题,说明应该就是kernel的问题了,需要推荐运维以后不要安装这个版本的内核了,目前看4.19.12以上的版本或者干脆就是3.10的内核都是打过patch的。

相关资料
https://go.dev/play/p/GMAaKucHOr
https://elixir.bootlin.com/linux/latest/C/ident/sys_getsockopt
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/socket.c
https://bugzilla.kernel.org/show_bug.cgi?id=198791
http://patchwork.ozlabs.org/project/netfilter-devel/patch/a4752d0887579496c5db267d9db7ff77719436d8.1518088560.git.pabeni@redhat.com/
https://github.com/cybozu-go/transocks/pull/14
Good job, thank you for your work. And we will continue to pay attention to this issue.