鲲鹏社区首页
中文
注册
开发者
一次Keepalived的coredump问题定位

一次Keepalived的coredump问题定位

coredump

发表于 2025/04/22

0

作者|张久同


问题现象

在某个项目中,Keepalived工作异常,无法正常启动。Keepalived的第三个进程pid一直在不停更换,查看messages日志:

会不断打印keepalived进程core dump信息。


应用信息

软件名称

版本

说明

麒麟

v10 sp1

操作系统

Keepalived 

1.2.9

Keepalived 

  Keepalived是用来业务组件的做心跳探活,集群检查等工作的开源组件。

对比分析

*  X86+centos运行正常

*   鲲鹏+centos运行正常

*   鲲鹏+kylin运行core dump

推断是内核差异导致的core dump,centos为4k页表,麒麟为64k页表。

实验室重新编译4k内核(openEuler 20.03)后运行正常。

推断是程序中存在与内核页表大小相关的变量导致core dump,同时对比。


问题定位

1. 分析core dump文件

打印函数调用栈,看到core dump位置


是在vrrp_parser.c文件的176行报错。

查看源码: https://github.com/acassen/keepalived/blob/v1.2.9/keepalived/vrrp/vrrp_parser.c

访问vrrp->ifp->ifindex时报错。

该变量时从if_get_by_name函数返回。同时gdb打印vrrp->ifp为空,所以应该是访问了空指针导致的core dump。

该if_get_by_ifname函数逻辑就是遍历if_queue队列,如果找到了该name的对象,就返回该ifp,如果没找到就返回NULL。

在if_get_by_ifname中添加测试代码,打印出来if_queue的所有数据:


在实验室环境,指定的网口为enp4s0,但是只是循环打印以上内容,定位到原因是没找到enp4s0网口导致的core dump。


2. 定位为什么if_queue中缺少网卡数据

在源码中只有这部分是往if_queue队列中添加




该函数为netlink_if_link_filter,继续向上查看:


调用该函数的是netlink_interface_lookup,且该函数没有变量输入,为最上层函数,查看netlink_parse_info函数,结合message出现的报错:

该netlink_parse_info会有两种情况下出现该ERROR。如下所示:



3. 定位Netlink:error的原因

在两部分中添加printf函数分别打印两个不同的信息,来确认到底是那处报错,如下:


这里将不同触发条件写到指定文件中,运行后打印该文件:

发现是第二部分出现的error,也就是msg.msg_flags的标记为被标记为了MSG_TRUNC。

查看msg的赋值以及初始化:


msg变量是由recvmsg函数进行赋值修改的。通过(man recvmsg)查看recvmsg函数介绍

该标志位表示:由于数据报大于提供的缓冲区,因此丢弃了数据报的尾部。而buf是硬编码的4096。也就是说是由于buf太小导致recvmsg函数丢弃了数据包的尾部。导致网口信息解析失败。

4. core dump的根因

    该进程core dump根因为buf数组设置过小,导致recvmsg函数丢弃了socket 数据包的尾部,导致部分网口信息解析失败,读不到指定网口,访问了空指针引起的core dump。


问题解决

查找相关patch,在debian的1.2.24版本中有一个对应的patch。#846292 - keepalived: Fix netlink message truncation with large page sizes - Debian Bug report logs

这个patch的核心思想就是把这个4096通过netlink_set_recv_buf_size函数来初始化,初始化的逻辑:通过getpagesize函数得到当前系统的页表大小,如果超过8k,就设置成8k。可以将其patch的代码在1.2.9版本中实现重新编译后:

 

可以读到指定网卡。

本页内容