第15章 UDP:用户数据报协议
所属:TCP/IP详解 卷2:实现 来源:TCP/IP详解 卷2:实现
本章描述UDP协议的内核实现,包括UDP首部、UDP输入处理、UDP输出处理、UDP控制块和UDP校验和等。
15.1 引言
UDP的特点
UDP(User Datagram Protocol):
- 用户数据报协议
- 无连接
- 不保证可靠交付
- 不保证顺序
- 开销小,效率高
- 适合一次性、少量数据的传输
UDP是简单的无连接传输层协议。
UDP的用途
UDP的常见用途:
- DNS:域名解析
- TFTP:简单文件传输
- SNMP:网络管理
- 实时应用:视频、语音
- 广播和多播
- 等等
UDP适合对实时性要求高、能容忍少量丢失的场景。
本章讨论的内容
本章讨论的内容:
- UDP首部格式
- UDP控制块(inpcb)
- UDP输入处理(udp_input)
- UDP输出处理(udp_output)
- UDP校验和
- UDP与应用的接口
本章讨论UDP的内核实现。
15.2 UDP首部
UDP首部格式
UDP首部的格式:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 源端口 | 目的端口 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 长度 | 检验和 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
| 数据(可变) |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
字段说明
| 字段 | 大小 | 说明 |
|---|---|---|
| 源端口 | 16位 | 发送方端口号,可选 |
| 目的端口 | 16位 | 接收方端口号,必须 |
| 长度 | 16位 | UDP首部+数据的总长度 |
| 检验和 | 16位 | 可选,0表示不校验 |
UDP首部只有8字节,很简单。
udphdr结构
内核中的UDP首部结构:
struct udphdr {
u_short uh_sport; /* 源端口 */
u_short uh_dport; /* 目的端口 */
u_short uh_ulen; /* 长度 */
u_short uh_sum; /* 检验和 */
};对应UDP首部的C结构。
15.3 UDP控制块
inpcb结构
UDP使用inpcb(Internet PCB)作为控制块:
struct inpcb {
struct inpcb *inp_next; /* 下一个PCB */
struct inpcb *inp_prev; /* 前一个PCB */
struct inpcb *inp_head; /* 链表头 */
struct socket *inp_socket; /* 对应的套接字 */
u_long inp_faddr; /* 外部地址(对端) */
u_short inp_fport; /* 外部端口 */
u_long inp_laddr; /* 本地地址 */
u_short inp_lport; /* 本地端口 */
/* ... 其他字段 ... */
};字段说明
| 字段 | 说明 |
|---|---|
| inp_next / inp_prev | 链表指针 |
| inp_socket | 对应的socket结构 |
| inp_faddr | 外部(对端)IP地址 |
| inp_fport | 外部(对端)端口号 |
| inp_laddr | 本地IP地址 |
| inp_lport | 本地端口号 |
inpcb保存UDP套接字的状态。
PCB的组织
PCB的组织方式:
- 全局链表:所有UDP的PCB链接在一起
- 按本地端口号排序或哈希
- 方便查找
查找方式:
- 根据目的端口查找
- 找到对应的PCB
- 然后把数据交给它
用链表或哈希表组织PCB。
15.4 UDP输入处理
udp_input函数
UDP输入处理(udp_input):
- IP层分用后调用
- 处理收到的UDP数据报
- 验证UDP首部
- 查找对应的PCB
- 把数据放入接收缓冲区
- 唤醒等待的进程
udp_input是UDP输入的入口。
输入处理流程
UDP输入的处理流程:
收到UDP数据报
|
v
验证长度
|
v
验证校验和(如果有)
|
v
查找PCB(根据目的端口)
|
+---> 找到?
| |
| +---> 是 → 放入接收缓冲区
| | |
| | +---> 唤醒进程
| |
| +---> 否 → 发送ICMP端口不可达
|
+---> 校验和错误?
|
+---> 是 → 丢弃,不发ICMP
校验和验证
UDP校验和的验证:
- 如果校验和为0,表示不校验
- 否则验证校验和
- 校验和错误则丢弃
- 不发送ICMP错误(防止放大攻击)
校验和错误直接丢弃。
PCB查找
PCB的查找:
- 先找精确匹配(本地地址+本地端口+外部地址+外部端口)
- 再找只匹配本地的
- 找通配地址的
- 都没找到就发ICMP端口不可达
查找优先级:
- 完全匹配 > 部分匹配 > 通配
找最匹配的PCB。
数据交付
数据的交付:
- 找到PCB后
- 把数据封装成mbuf
- 添加到套接字的接收缓冲区
- 唤醒等待的进程
- 进程用recvfrom等读取
数据放入接收缓冲区,进程读取。
15.5 UDP输出处理
udp_output函数
UDP输出处理(udp_output):
- 应用调用sendto等
- 内核构造UDP首部
- 计算校验和
- 调用IP层发送
udp_output是UDP输出的入口。
输出处理流程
UDP输出的处理流程:
应用调用sendto
|
v
构造UDP首部
|
v
计算校验和(如果启用)
|
v
调用ip_output发送
源端口选择
源端口的选择:
- 如果应用bind了端口,用bind的
- 如果没bind,自动分配一个
- 从临时端口范围选
- 确保不冲突
没绑定就自动分配端口。
源地址选择
源地址的选择:
- 如果应用指定了源地址,用指定的
- 如果bind了地址,用bind的
- 否则根据输出接口选
- 用输出接口的地址
源地址通常是输出接口的地址。
15.6 UDP校验和
校验和的计算
UDP校验和的计算:
- 计算范围:伪首部 + UDP首部 + 数据
- 伪首部包含:源地址、目的地址、协议、UDP长度
- 16位二进制反码求和
- 结果取反作为校验和
伪首部的作用:
- 验证数据到达了正确的主机和端口
- 包含IP层的信息
- 端到端的校验
UDP校验和包含伪首部。
校验和的可选性
UDP校验和是可选的:
- 校验和为0表示不校验
- 可以提高性能
- 但降低了可靠性
建议:
- 最好开启校验和
- 现在网络速度快,开销不大
- 可靠性更重要
校验和可选,但建议开启。
15.7 UDP与应用的接口
系统调用
UDP相关的系统调用:
- socket:创建套接字
- bind:绑定本地地址和端口
- sendto:发送数据(指定目的地址)
- recvfrom:接收数据(获取源地址)
- connect:连接(指定对端地址)
- close:关闭
UDP用sendto和recvfrom比较多。
connect的作用
UDP的connect:
- 不发送任何数据
- 只是记录对端地址
- 之后可以用send/recv
- 可以收到异步错误(ICMP)
为什么用connect:
- 简化编程
- 每次不用指定地址
- 可以收到ICMP错误
UDP的connect只是记录地址。
15.8 小结
UDP概述
-
特点
- 无连接
- 不保证可靠
- 开销小
- 效率高
-
用途
- DNS、TFTP、SNMP
- 实时应用
- 广播多播
UDP首部
-
格式
- 源端口、目的端口
- 长度、检验和
- 8字节首部
-
字段
- 源端口可选
- 目的端口必须
- 检验和可选
UDP控制块
-
inpcb结构
- 本地地址/端口
- 外部地址/端口
- 对应套接字
-
组织方式
- 链表或哈希
- 按端口查找
UDP输入处理
-
udp_input
- 输入入口
- 验证长度和校验和
- 查找PCB
- 交付数据
-
PCB查找
- 精确匹配优先
- 找不到发ICMP不可达
-
数据交付
- 放入接收缓冲区
- 唤醒进程
UDP输出处理
-
udp_output
- 输出入口
- 构造首部
- 计算校验和
- 调用IP发送
-
端口选择
- 绑定的或自动分配
-
地址选择
- 指定的或接口地址
UDP校验和
-
计算方式
- 伪首部 + UDP首部 + 数据
- 反码求和
-
可选性
- 0表示不校验
- 建议开启
应用接口
-
系统调用
- socket、bind
- sendto、recvfrom
- connect、close
-
connect的作用
- 记录地址
- 简化编程
- 收异步错误
关键概念
-
UDP
- 无连接
- 简单高效
-
inpcb
- UDP控制块
- 保存状态
-
udp_input
- 输入处理
- 查找PCB
- 交付数据
-
udp_output
- 输出处理
- 构造首部
- 调用IP
-
校验和
- 包含伪首部
- 可选但建议开启