Bootstrap

限制内核 udp bad checksum 失败告警信息

问题描述

某系统 dmesg 信息中有如下内容频繁打印,冲掉了其它相关的信息,需要限制打印。

UDP: bad checksum. From 10.66.245.93:61525 to 255.255.255.255:137 ulen 58

相关代码

内核源码树中的文件名:

net/ipv4/udp.c 

相关源码:

csum_error:
        /*
         * RFC1122: OK.  Discards the bad packet silently (as far as
         * the network is concerned, anyway) as per 4.1.3.4 (MUST).
         */
        net_dbg_ratelimited("UDP%s: bad checksum. From %pI4:%u to %pI4:%u ulen %d\n",
                            proto == IPPROTO_UDPLITE ? "Lite" : "",
                            &saddr, ntohs(uh->source), &daddr, ntohs(uh->dest),
                            ulen);

这部分功能为,内核检验报文的 checksum 失败,丢弃报文并打印警告。问题的原因可能是网络链路存在问题导致有错包出现。

从功能上说,这个属于异常告警,直接关闭显然不太合理,但是系统中一直打印这些信息加之 dmesg 使用环形缓冲区,一些有用的内核打印信息会被这个信息冲掉,为此需要进行处理。

如何限制打印?

从上文的源代码中可以看出,内核调用了 net_dbg_ratelimited 函数来打印这个告警信息,net_dbg_ratelimited 函数本身就有限速的功能,但是这个限速明显不符合我们的要求,如果能够提高限速,或许能够解决我们的问题。

从 net_dbg_ratelimited 函数的实现着手

linux-5.x 内核代码中,net_dbg_ratelimited 函数实现代码如下:

#if defined(CONFIG_DYNAMIC_DEBUG) || \
	(defined(CONFIG_DYNAMIC_DEBUG_CORE) && defined(DYNAMIC_DEBUG_MODULE))
#define net_dbg_ratelimited(fmt, ...)					\
do {									\
	DEFINE_DYNAMIC_DEBUG_METADATA(descriptor, fmt);			\
	if (DYNAMIC_DEBUG_BRANCH(descriptor) &&				\
	    net_ratelimit())						\
		__dynamic_pr_debug(&descriptor, pr_fmt(fmt),		\
		                   ##__VA_ARGS__);			\
} while (0)
#elif defined(DEBUG)
#define net_dbg_ratelimited(fmt, ...)				\
	net_ratelimited_function(pr_debug, fmt, ##__VA_ARGS__)
#else
#define net_dbg_ratelimited(fmt, ...)				\
	do {							\
		if (0)						\
			no_printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__); \
	} while (0)
#endif

我们的系统中开启了 CONFIG_DYNAMIC_DEBUG 内核配置,这里只需要考虑第一种情况。根据代码能够将问题缩小到 net_ratelimit 函数中。

net_ratelimit 函数源码如下:

DEFINE_RATELIMIT_STATE(net_ratelimit_state, 5 * HZ, 10);
/*
 * All net warning printk()s should be guarded by this function.
 */
int net_ratelimit(void)
{
        return __ratelimit(&net_ratelimit_state);
}
EXPORT_SYMBOL(net_ratelimit);

net_ratelimit_state 为内核向用户态导出的可配置接口,它在 net_core_table ctl_table 中的定义如下:

        {
                .procname       = "message_cost",
                .data           = &net_ratelimit_state.interval,
                .maxlen         = sizeof(int),
                .mode           = 0644,
                .proc_handler   = proc_dointvec_jiffies,
        },
        {
                .procname       = "message_burst",
                .data           = &net_ratelimit_state.burst,
                .maxlen         = sizeof(int),
                .mode           = 0644,
                .proc_handler   = proc_dointvec,
        },

我们可以通过修改 net.core.message_cost 与 net.core.message_burst 来完成限速功能。

实现限速的两种方式

上文已经描述了 net_dbg_ratelimited 的主要实现,根据内核代码,可以使用如下两种方式来配置限速:

  1. 使用 sysctl -w 来设置

    相关的项目如下:

    [root] #$ sysctl -a  | grep net.core.message 
    net.core.message_burst = 10
    net.core.message_cost = 5
    
  2. 通过 proc fs 来设置

    相关文件如下:

    /proc/sys/net/core/message_burst
    /proc/sys/net/core/message_cost
    
;