UDP-Lite 协议 (RFC 3828)¶
UDP-Lite 是一种标准跟踪 IETF 传输协议,其特点是可变长度的校验和。这对于通过无线网络传输多媒体(视频、VoIP)具有优势,因为部分损坏的数据包仍然可以被送入编解码器,而不是因为校验和测试失败而被丢弃。
本文简要描述了现有的内核支持和套接字 API。有关深入信息,您可以查阅
UDP-Lite 主页:http://web.archive.org/web/%2E/http://www.erg.abdn.ac.uk/users/gerrit/udp-lite/
从这里您还可以下载一些示例应用程序源代码。
UDP-Lite HOWTO:http://web.archive.org/web/%2E/http://www.erg.abdn.ac.uk/users/gerrit/udp-lite/files/UDP-Lite-HOWTO.txt
Wireshark UDP-Lite WiKi(带有捕获文件):https://wiki.wireshark.org/Lightweight_User_Datagram_Protocol
协议规范,RFC 3828:http://www.ietf.org/rfc/rfc3828.txt
1. 应用程序¶
一些应用程序已成功移植到 UDP-Lite。Ethereal(现在称为 wireshark)默认支持 UDP-Litev4/v6。
将应用程序移植到 UDP-Lite 非常简单:只需更改套接字级别和 IPPROTO;发送方还需要设置校验和覆盖长度(默认为头部长度 = 8)。详细信息在下一节中。
2. 编程 API¶
UDP-Lite 提供无连接的、不可靠的数据报服务,因此使用与 UDP 相同的套接字类型。实际上,从 UDP 移植到 UDP-Lite 非常容易:只需在 socket(2) 调用的最后一个参数中添加
IPPROTO_UDPLITE
,使语句看起来像这样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 个字节(12 字节数据 + 8 字节头部)。每个数据包仅校验前 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 校验和覆盖选项的详细讨论在第四节中。
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)。
发送方套接字选项
如果发送方指定 0 作为覆盖长度值,则模块假定完全覆盖,发送覆盖长度为 0 的数据包并进行相应的校验和。如果发送方指定覆盖范围 < 8 且不等于 0,则内核假定 8 为默认值。最后,如果指定的覆盖长度超过数据包长度,则使用数据包长度代替覆盖长度。
接收方套接字选项
接收方指定其愿意接受的覆盖长度的最小值。此处的 0 值表示接收方始终希望覆盖整个数据包。在这种情况下,所有部分覆盖的数据包都将被丢弃,并记录一个错误。
不可能指定非法值(<0 和 <8);在这些情况下,将假定默认值为 8。
所有覆盖值小于指定阈值的数据包都将被丢弃,这些事件也会被记录。
禁用校验和计算
在发送方和接收方,始终会执行校验和,并且无法使用 SO_NO_CHECK 禁用。因此
setsockopt(sockfd, SOL_SOCKET, SO_NO_CHECK, ... );将始终被忽略,而
getsockopt(sockfd, SOL_SOCKET, SO_NO_CHECK, &value, ...);的值是无意义的(如 TCP 中一样)。校验和字段为零的数据包是非法的(参见 RFC 3828,第 3.1 节),将被静默丢弃。
分片
校验和计算同时考虑缓冲区大小和 MTU。UDP-Lite 数据包的大小由发送缓冲区的大小决定。发送缓冲区的最小大小为 2048(在 include/net/sock.h 中定义为 SOCK_MIN_SNDBUF),默认值可配置为 net.core.wmem_default 或通过设置 SO_SNDBUF 套接字 (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 bytesUDP-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 分片的类似情况,请考虑 1280 字节的链路 MTU 和 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 补丁是在以下地点开发的
阿伯丁大学电子研究小组工程系 Fraser Noble Building Aberdeen AB24 3UE; 英国
当前的维护者是 Gerrit Renker,<gerrit@erg.abdn.ac.uk>。初始代码由 William Stanislaus 开发,<william@erg.abdn.ac.uk>。