UDP-Lite 协议 (RFC 3828)

UDP-Lite 是一种标准跟踪 IETF 传输协议,其特点是可变长度的校验和。 这对于通过无线网络传输多媒体(视频,VoIP)具有优势,因为部分损坏的数据包仍然可以馈送到编解码器中,而不会因校验和测试失败而被丢弃。

此文件简要描述了现有的内核支持和套接字 API。 有关深入信息,您可以参考

1. 应用

一些应用程序已成功移植到 UDP-Lite。 Ethereal(现在称为 wireshark)默认情况下支持 UDP-Litev4/v6。

将应用程序移植到 UDP-Lite 非常简单:只需更改套接字级别和 IPPROTO;发送方还需要设置校验和覆盖长度(默认值 = 标头长度 = 8)。 详细信息在下一节中。

2. 编程 API

UDP-Lite 提供了一种无连接、不可靠的数据报服务,因此使用与 UDP 相同的套接字类型。 事实上,从 UDP 移植到 UDP-Lite 非常容易:只需添加 IPPROTO_UDPLITE 作为 socket(2) 调用的最后一个参数,以便语句看起来像

s = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDPLITE);

或者,分别为

s = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDPLITE);

只需进行上述更改,您就可以运行 UDP-Lite 服务或连接到 UDP-Lite 服务器。 内核将假定您对使用部分校验和覆盖不感兴趣,因此会模拟 UDP 模式(完全覆盖)。

要利用部分校验和覆盖工具,需要设置一个套接字选项,该选项采用一个整数来指定覆盖长度

  • 发送方校验和覆盖:UDPLITE_SEND_CSCOV

    例如

    int val = 20;
    setsockopt(s, SOL_UDPLITE, UDPLITE_SEND_CSCOV, &val, sizeof(int));
    

    将校验和覆盖长度设置为 20 字节(12b 数据 + 8b 标头)。 每个数据包只有前 20 个字节(加上伪标头)将被校验和。 这对于具有 12 字节基本标头的 RTP 应用程序很有用。

  • 接收方校验和覆盖:UDPLITE_RECV_CSCOV

    此选项是接收方类似的选项。 它是真正可选的,即不需要启用具有部分校验和覆盖的流量。 它的功能是作为流量过滤器:启用后,它指示内核丢弃所有覆盖范围_小于_此值的数据包。 例如,如果要保护 RTP 和 UDP 标头,则接收方可以强制仅允许最小覆盖范围为 20 的数据包

    int min = 20;
    setsockopt(s, SOL_UDPLITE, UDPLITE_RECV_CSCOV, &min, sizeof(int));
    

对 getsockopt(2) 的调用是类似的。 作为一种扩展而不是独立的协议,所有从 UDP 已知的套接字选项都可以与以前完全相同的方式使用,例如 UDP_CORK 或 UDP_ENCAP。

UDP-Lite 校验和覆盖选项的详细讨论在第 IV 节中。

3. 头文件

套接字 API 需要通过 /usr/include 中的头文件提供支持

  • /usr/include/netinet/in.h 定义 IPPROTO_UDPLITE

  • /usr/include/netinet/udplite.h 用于 UDP-Lite 标头字段和协议常量

出于测试目的,以下内容可以用作 mini 头文件

#define IPPROTO_UDPLITE       136
#define SOL_UDPLITE           136
#define UDPLITE_SEND_CSCOV     10
#define UDPLITE_RECV_CSCOV     11

适用于各种发行版的现成头文件位于 UDP-Lite tarball 中。

4. 内核关于各种套接字选项的行为

要启用调试消息,需要将日志级别设置为 8,因为大多数消息都使用 KERN_DEBUG 级别 (7)。

  1. 发送方套接字选项

如果发送方指定值 0 作为覆盖长度,则模块假定完全覆盖,传输覆盖长度为 0 且校验和正确的数据包。 如果发送方指定的覆盖范围 < 8 且与 0 不同,则内核假定 8 作为默认值。 最后,如果指定的覆盖长度超过数据包长度,则使用数据包长度代替覆盖长度。

  1. 接收方套接字选项

接收方指定它愿意接受的覆盖长度的最小值。 此处的值 0 表示接收方始终希望覆盖整个数据包。 在这种情况下,所有部分覆盖的数据包都将被丢弃并记录错误。

无法指定非法值 (<0 和 <8);在这些情况下,假定默认值为 8。

所有覆盖值小于指定阈值的数据包都将被丢弃,这些事件也会被记录。

  1. 禁用校验和计算

在发送方和接收方,始终会执行校验和,并且无法使用 SO_NO_CHECK 禁用。 因此

setsockopt(sockfd, SOL_SOCKET, SO_NO_CHECK,  ... );

将始终被忽略,而

getsockopt(sockfd, SOL_SOCKET, SO_NO_CHECK, &value, ...);

的值没有意义(如 TCP 中)。 校验和字段为零的数据包是非法的(参见 RFC 3828,第 3.1 节),并且将被静默丢弃。

  1. 分片

校验和计算遵循缓冲区大小和 MTU。 UDP-Lite 数据包的大小由发送缓冲区的大小决定。 发送缓冲区的最小大小为 2048(在 include/net/sock.h 中定义为 SOCK_MIN_SNDBUF),默认值可配置为 net.core.wmem_default 或通过设置 SO_SNDBUF socket(7) 选项。 发送缓冲区的最大上限由 net.core.wmem_max 决定。

给定一个大于发送缓冲区大小的有效负载大小,UDP-Lite 将有效负载分成几个单独的数据包,在每种情况下都填充发送缓冲区大小。

精确的值还取决于接口 MTU。 接口 MTU 反过来可能会触发 IP 分片。 在这种情况下,生成的 UDP-Lite 数据包被分成几个 IP 数据包,其中只有第一个包含 L4 标头。

发送缓冲区大小对校验和覆盖长度有影响。 考虑以下示例

Payload: 1536 bytes          Send Buffer:     1024 bytes
MTU:     1500 bytes          Coverage Length:  856 bytes

UDP-Lite 将在两个单独的数据包中运送 1536 字节

Packet 1: 1024 payload + 8 byte header + 20 byte IP header = 1052 bytes
Packet 2:  512 payload + 8 byte header + 20 byte IP header =  540 bytes

覆盖数据包覆盖第一个数据包中的 UDP-Lite 标头和 848 字节的有效负载,第二个数据包完全覆盖。 请注意,对于第二个数据包,覆盖长度超过了数据包长度。 在这种情况下,内核始终会将覆盖长度重新调整为数据包长度。

作为将一个 UDP-Lite 数据包分成几个小片段时会发生什么的一个例子,考虑以下示例

Payload: 1024 bytes            Send buffer size: 1024 bytes
MTU:      300 bytes            Coverage length:   575 bytes

+-+-----------+--------------+--------------+--------------+
|8|    272    |      280     |     280      |     280      |
+-+-----------+--------------+--------------+--------------+
            280            560            840           1032
                                    ^
*****checksum coverage*************

UDP-Lite 模块生成一个 1032 字节的数据包(1024 + 8 字节标头)。 根据接口 MTU,这些被分成 4 个 IP 数据包(280 字节 IP 有效负载 + 20 字节 IP 标头)。 内核模块在将片段释放到 IP 模块之前,对前两个完整数据包的内容加上最后一个数据包的 15 个字节求和。

要查看 IPv6 分片的类似情况,请考虑链接 MTU 为 1280 字节,写入缓冲区为 3356 字节。 如果校验和覆盖率小于 1232 字节(MTU 减去 IPv6/片段标头长度),则只需要考虑第一个片段。 使用更大的校验和覆盖长度时,需要对每个符合条件的片段进行校验和。 假设我们的校验和覆盖率为 3062。3356 字节的缓冲区将分成以下片段

Fragment 1: 1280 bytes carrying  1232 bytes of UDP-Lite data
Fragment 2: 1280 bytes carrying  1232 bytes of UDP-Lite data
Fragment 3:  948 bytes carrying   900 bytes of UDP-Lite data

前两个片段必须完全进行校验和,最后一个片段只有 598 (= 3062 - 2*1232) 字节进行校验和。

虽然正确处理这些情况很重要,但它们(令人恼火地)很少见:UDP-Lite 旨在优化无线(或通常嘈杂的)链路上的多媒体性能,因此可能需要更小的覆盖长度。

5. UDP-Lite 运行时统计信息及其含义

异常和错误情况以 KERN_DEBUG 级别记录到 syslog。 有关 UDP-Lite 的实时统计信息可在 /proc/net/snmp 中找到,并且(使用较新版本的 netstat)可以使用以下命令查看

netstat -svu

这会显示 UDP-Lite 统计变量,其含义如下。

InDatagrams

传递给用户的总数据报数。

NoPorts

接收到未知端口的数据包数量。 这些情况是单独计数的(不作为 InErrors)。

InErrors

错误 UDP-Lite 数据包的数量。 错误包括

  • 内部套接字队列接收错误

  • 数据包太短(小于 8 个字节或声明的覆盖长度超过接收到的长度)

  • xfrm4_policy_check() 返回错误

  • 应用程序指定了比传入数据包更大的最小覆盖长度

  • 校验和覆盖违反

  • 错误的校验和

OutDatagrams

发送的数据报总数。

这些统计数据来自 UDP MIB(RFC 2013)。

6. IPtables

有针对 UDP-Lite 的数据包匹配支持以及对 LOG 目标的支持。 如果您将以下行复制并粘贴到 /etc/protocols 中

udplite 136     UDP-Lite        # UDP-Lite [RFC 3828]

然后

iptables -A INPUT -p udplite -j LOG

将生成到 syslog 的日志输出。 丢弃和拒绝数据包也有效。

7. 维护者地址

UDP-Lite 补丁是在以下地址开发的

University of Aberdeen Electronics Research Group Department of Engineering Fraser Noble Building Aberdeen AB24 3UE; UK

当前的维护者是 Gerrit Renker, <gerrit@erg.abdn.ac.uk>。 初始代码由 William Stanislaus, <william@erg.abdn.ac.uk> 开发。