第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的查找:

  1. 先找精确匹配(本地地址+本地端口+外部地址+外部端口)
  2. 再找只匹配本地的
  3. 找通配地址的
  4. 都没找到就发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发送

源端口选择

源端口的选择:

  1. 如果应用bind了端口,用bind的
  2. 如果没bind,自动分配一个
    • 从临时端口范围选
    • 确保不冲突

没绑定就自动分配端口。


源地址选择

源地址的选择:

  1. 如果应用指定了源地址,用指定的
  2. 如果bind了地址,用bind的
  3. 否则根据输出接口选
    • 用输出接口的地址

源地址通常是输出接口的地址。


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概述

  1. 特点

    • 无连接
    • 不保证可靠
    • 开销小
    • 效率高
  2. 用途

    • DNS、TFTP、SNMP
    • 实时应用
    • 广播多播

UDP首部

  1. 格式

    • 源端口、目的端口
    • 长度、检验和
    • 8字节首部
  2. 字段

    • 源端口可选
    • 目的端口必须
    • 检验和可选

UDP控制块

  1. inpcb结构

    • 本地地址/端口
    • 外部地址/端口
    • 对应套接字
  2. 组织方式

    • 链表或哈希
    • 按端口查找

UDP输入处理

  1. udp_input

    • 输入入口
    • 验证长度和校验和
    • 查找PCB
    • 交付数据
  2. PCB查找

    • 精确匹配优先
    • 找不到发ICMP不可达
  3. 数据交付

    • 放入接收缓冲区
    • 唤醒进程

UDP输出处理

  1. udp_output

    • 输出入口
    • 构造首部
    • 计算校验和
    • 调用IP发送
  2. 端口选择

    • 绑定的或自动分配
  3. 地址选择

    • 指定的或接口地址

UDP校验和

  1. 计算方式

    • 伪首部 + UDP首部 + 数据
    • 反码求和
  2. 可选性

    • 0表示不校验
    • 建议开启

应用接口

  1. 系统调用

    • socket、bind
    • sendto、recvfrom
    • connect、close
  2. connect的作用

    • 记录地址
    • 简化编程
    • 收异步错误

关键概念

  1. UDP

    • 无连接
    • 简单高效
  2. inpcb

    • UDP控制块
    • 保存状态
  3. udp_input

    • 输入处理
    • 查找PCB
    • 交付数据
  4. udp_output

    • 输出处理
    • 构造首部
    • 调用IP
  5. 校验和

    • 包含伪首部
    • 可选但建议开启

用户数据报协议