UDP之数据报校验和


协议设计上,UDP的校验和功能是可选的,Linux实现时,UDP的校验和功能默认是开启的,不过应用程序可以通过选项SO_NO_CHECK设置该能力。

校验和的计算本身是协议自己的事情,和硬件无关,但是往往为了更加的高效,可能硬件提供了部分或者全部的校验和功能,这就导致代码实现中,校验和相关的逻辑显得有些复杂。这篇笔记分析了UDP的校验和实现细节。

关于校验和API的使用见笔记linux网络校验和计算API

数据结构

在sk_buff和net_device两个结构中,为校验和的计算增加了特定的字段。

sk_buff校验和字段

#define CHECKSUM_NONE 0
#define CHECKSUM_UNNECESSARY 1
#define CHECKSUM_COMPLETE 2
#define CHECKSUM_PARTIAL 3

struct sk_buff
{
   
	union {
   
		__wsum		csum;
		struct {
   
			__u16	csum_start;
			__u16	csum_offset;
		};
	};
    __u8 ip_summed:2,
}

联合体中哪个成员有效取决于ip_summed的值,ip_summed共两个bit,可取四个值,它们在发送和接收过程中表示的含义还有所不同。

接收过程中,ip_summed字段包含了网络设备硬件告诉L4软件当前校验和的状态,各值含义如下:

  • CHECKSUM_NONE:硬件没有提供校验和,可能是硬件不支持,也可能是硬件校验出错但是并未丢弃数据包,这时L4软件需要自己进行校验和计算;
  • CHECKSUM_UNNECESSARY:硬件已经进行了完整的校验,软件无需再进行检查。这时L4软件会跳过校验和检查;
  • CHECKSUM_COMPLETE:硬件已经计算了L4报头和其payload部分的校验和,并将计算结果保存在了skb->csum中,L4软件只需要再计算伪报头即可;

发送过程中,ip_summed字段记录了L4软件想要告诉网络设备硬件关于当前数据包的校验和状态信心。各值含义如下:

  • CHECKSUM_NONE:L4软件已经对数据包进行了完整的校验,或者该数据包不需要校验。总之这种情况下网络设备硬件无需做任何校验和计算;
  • CHECKSUM_PARTIAL:L4软件计算了伪报头的校验和,并且将值保存在了数据报的L4层首部的check字段中,网络设备硬件需要计算其余部分的校验和(报文首部+数据部分)。硬件需要计算的报文范围是从skb->csum_start到报文最后一个字节,计算结果需要填写到(skb->csum_start + skb->csum_offset)处。

net_device校验和字段

net_device的feature字段定义了如下和校验和相关的标记,这些标记表明了硬件计算校验和的能力。

feature 含义
NETIF_F_IP_CSUM 2 网络设备可以提供对基于IPv4的TCP和UDP数据包进行校验,其它协议报文不支持
NETIF_F_NO_CSUM 4 网络设备的传输非常可靠,无需L4执行任何校验,环回设备一般设置该标记
NETIF_F_HW_CSUM 8 网络设备可以对任何L4协议的数据包进行校验,基本很少有硬件能够实现
NETIF_F_IPV6_CSUM 16 网络设备可以对基于IPv6的TCP和UDP数据包进行校验,其它协议报文不支持

根据上述基础值重新定义了如下几个flag:

#define NETIF_F_GEN_CSUM	(NETIF_F_NO_CSUM | NETIF_F_HW_CSUM)
#define NETIF_F_V4_CSUM		(NETIF_F_GEN_CSUM | NETIF_F_IP_CSUM)
#define NETIF_F_V6_CSUM		(NETIF_F_GEN_CSUM | NETIF_F_IPV6_CSUM)
#define NETIF_F_ALL_CSUM	(NETIF_F_V4_CSUM | NETIF_F_V6_CSUM)

注:这些概念和字段的含义同样适用于TCP校验和处理过程

接收报文的校验和计算

udp4_csum_init()

UDP接收到报文后,首先会调用该函数进行校验和检查。

/* Initialize UDP checksum. If exited with zero value (success),
 * CHECKSUM_UNNECESSARY means, that no more checks are required.
 * Otherwise, csum completion requires chacksumming packet body,
 * including udp header and folding it to skb->csum.
 */
static inline int udp4_csum_init(struct sk_buff *skb, struct udphdr *uh, int proto)
{
   
	const struct iphdr *iph;
	int err;

	// 这两个字段用于指示对报文的哪些部分进行校验,cov指coverage,
	// 只有UDPLite使用,对于UDP,会对整个报文进行校验
	UDP_SKB_CB(skb)->partial_cov = 0;
	UDP_SKB_CB(skb)->cscov = skb->len;

	// UDPLITE,忽略
	if (proto == IPPROTO_UDPLITE) {
   
		err = udplite_checksum_init(skb, uh)<
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值