RFC 2661 的 L2TP 头里,隧道号和会话号各是 2 字节,填的是接收方的号。控制消息必须带长度和序号,版本必须是 2。长度字段原文只写消息的总八位组数。L2TPv3 的数据头换成 32 位会话号,不是这一套。
两个号都是对方分配的
一个可独立引用的定义句:隧道号标识控制连接,会话号标识隧道里的一个会话。两者都只有本地意义:同一条隧道或会话,两端各给一个不同的号。每条消息里填写的是预定接收方的号,不是发送方自己的号(RFC 2661 第 3.1 节)。
RFC 2661 是 1999 年 8 月的 Proposed Standard。第 1.1 节把关键词指向 RFC 2119。这份文本没有加上后来常见的「只在全大写时才算」。下文的「必须」对应所引原文的大写词。多八位组的字段按网络序发送,高八位组在前。
图 3.1 里,隧道号和会话号没有标成可选。可选的是长度、Ns、Nr 和偏移。控制消息上,长度、Ns 和 Nr 虽然在图里标了可选,正文写明它们必须出现。
分配隧道号的 AVP 是属性类型 9。属性值是 2 个八位组的非零无符号整数。对端随后发出的全部控制消息和数据消息,都必须在头里的隧道号填上这个值。还没收到对端的这份 AVP 时,发给该对端的控制消息必须把隧道号设为 0(第 4.4.3 节)。会话号有对应的分配 AVP,属性类型 14,同样是 2 个八位组的非零整数。
第 5.3 节写明,0 不得用作已分配的会话号或隧道号。会话号尚未由对端分配时,头里的会话号必须发送为 0,靠消息里的分配会话号 AVP 来认出这次会话。隧道号尚未分配时同样必须发送 0。HELLO 的会话号必须为 0,因为这条保活属于整条隧道(第 6.5 节)。
AVP 可以把属性值隐藏起来。本文不写隐藏用的填充和散列步骤。
控制消息上 L 和 S 必须为 1
一个可独立引用的定义句:T 位为 0 是数据消息,为 1 是控制消息。L 位为 1 时长度字段才出现,控制消息必须把 L 置 1。S 位为 1 时 Ns 和 Nr 才出现,控制消息必须把 S 置 1。O 位和 P 位在控制消息上必须为 0。版本必须为 2(RFC 2661 第 3.1 节)。
图的第一行从左到右是 T、L、两位保留、S、一位保留、O、P、四位保留,然后 4 位版本,再然后可选的长度。原文用这张图,没有在散文里再给每一位一个号码。保留位发出时必须为 0,收到时必须忽略。
| 位 | 为 1 时 | 控制消息 |
|---|---|---|
| T | 控制消息。为 0 则是数据消息 | 控制消息置 1 |
| L | 长度字段出现 | 必须置 1 |
| S | Ns 和 Nr 出现 | 必须置 1 |
| O | 偏移量字段出现 | 必须置 0 |
| P | 这条数据消息在本地排队和发送时优先 | 必须置 0 |
P 位只用于数据消息。原文举的例子是用作链路保活的 LCP 回显请求。本文不写如何设置这一位。
版本必须为 2,表示本文这份数据消息头。值 1 留给 L2F,以便和混在一起的 L2F 包区分。收到未知版本必须丢弃。
长度字段表示消息的总八位组数。第 3.1 节只有这一句,没有写从 T 位起算,也没有写是否含头。控制消息的 O 位必须为 0,因此没有偏移字段。按图数,标志与版本、长度、隧道号、会话号、Ns、Nr 合起来是 12 个八位组。数据消息若四个可选字段都不出现,则是 6 个八位组。原文没有印出 12 或 6。
Ns 是这条数据或控制消息的序号,从 0 起,每发送一条加 1,模 2^16。Nr 是期望收到的下一条控制消息的序号,等于最后一条按序收到的 Ns 加 1,同样取模。数据消息里的 Nr 是保留的;S 位使它出现时,收到必须忽略。序号如何重传,第 3.1 节指向第 5.4 节和第 5.8 节。本文没有把这两节的窗口算法读完,因此不引用一个窗口大小。
偏移量若出现,表示载荷数据应当从 L2TP 头之后多少个八位组处开始。填充里的实际数据没有定义。偏移字段出现时,L2TP 头在填充的最后一个八位组之后结束。
1701 是发起时的目的端口
一个可独立引用的定义句:用 UDP 承载时,整份 L2TP 包,包括载荷和 L2TP 头,放在一个 UDP 数据报里。发起方把目的端口设为 1701,源端口可以是、也可以不是 1701。接收方选择自己的源端口,可以是、也可以不是 1701,并把应答送回发起方的地址和端口。两端端口一旦确定,在隧道存续期间必须保持不变(RFC 2661 第 8.1 节)。
1701 同时用于 L2F 和 L2TP。版本 1 是 L2F,本文的 L2TP 是 2。不支持 L2F 的实现必须静默丢弃全部 L2F 包。2026 年 10 月 25 日没有下载 IANA 端口表。端口号只引自这一节对 RFC 1700 的引用,没有另核当天的登记。
原文写:让接收方自选源端口,而不是沿用发起包的目的端口 1701,可能使 L2TP 更难穿过某些 NAT。这句没有给出改写映射的步骤。地址和端口会被改写的概念,见 NAT 穿越。
默认情况下,控制和数据消息的 UDP 校验和都必须打开。实现可以为数据消息提供关闭校验和的选项。原文建议控制包始终打开校验和。何种取值表示没有计算,见 UDP 校验和。IP 分片可能在途中发生,L2TP 不为它做专门优化,见 IP 分片。
版本 3 不是这两个两字节
RFC 3931 第 4.1 节把会话号定义为 32 位、非零,并且只有本地意义。直接走 IP 时,数据头以这 32 位开头,后面可以有最长 64 位的 Cookie(第 4.1.1.1 节)。同一路径上的控制消息改用 32 位全 0 的会话号来区分,不再靠 T 位。那张控制头的长度从 T 位起算,不含前面那 32 位 0(第 4.1.1.2 节)。「仍然从 T 位起算」出现在这一节,没有点名 RFC 2661 第 3.1 节。本文不把这句回写成 1999 年文本已经印出的定义。
走 UDP 时,数据头的 T 位必须为 0,版本必须为 3,随后才是 32 位会话号(第 4.1.2.1 节)。图里没有 16 位隧道号。该节写明,位 1、4、6、7 在 L2TPv2 里有含义,在 L2TPv3 里改为保留。对照 RFC 2661 的图,这四位分别是 L、S、O、P。这是两张图的对照,不是 RFC 2661 散文里的位号。第 4.1.2.2 节写,UDP 端口的选法和 L2TPv2 相同,控制连接协商时控制消息必须发往 1701。第 4.7.2 节又把 UDP 头的格式指回第 4.1.2.1 节。本文没有读第 4.7.3 节的双版本回退步骤。
RFC 9601 更新 RFC 2661,把一段话追加到第 5.3 节,而不是第 3.1 节。不支持其 ECN 扩展的一端,必须按 RFC 9601 第 4 节配置,以便在外层是 IP 时把外层 ECN 清成 0b00。2026 年 10 月 25 日没有重读第 4 节,因此不复述清零的条件。它另定义属性类型 103 的 ECN Capability AVP,长度在隐藏前是 6 个八位组,可以出现在 SCCRQ 和 SCCRP。这不改变隧道号或会话号的宽度。显式拥塞通知在 IP 头里怎样传递,见 显式拥塞通知。
L2TP/IPsec 写的是由内到外有 PPP、L2TP、UDP 和 ESP。两个 2 字节的号在本文,不在那张层次表里。PPPoE 发现阶段得到的会话号是以太网头里的另一个字段,见 PPPoE。它不是 L2TP 头里的会话号。
2026 年 10 月 25 日的勘误页上,已验证的 401 把第 3 节里的 SSHTRESH 改为 SSTHRESH,出现三次;5632 改正第 6.1 节的一个词。二者都不改第 3.1 节的头。2049 是 Held,改的是 DSLAM 的全称。
现象对照
| 现象 | 更可能先看什么 |
|---|---|
| 版本为 2,头里两个 16 位标识 | RFC 2661。填的是接收方的号 |
| 控制消息的 L 或 S 为 0 | 与第 3.1 节不符。这两位在控制消息上必须为 1 |
| 隧道号为 0 | 对端的分配隧道号 AVP 还没到。0 不得当作已分配的号 |
| HELLO 的会话号不是 0 | 与第 6.5 节不符 |
| 版本为 1 | 按 L2F 区分。不支持 L2F 时必须静默丢弃 |
| 版本为 3,后面是 32 位会话号 | L2TPv3 的 UDP 数据头。不是 RFC 2661 的两个 2 字节 |
| 目的端口不是 1701,隧道却已经建立 | 第 8.1 节允许接收方自选源端口。建立之后两端端口必须保持不变 |
核对清单
- 两个号各 2 字节,填接收方:尚未分配时必须为 0。已分配的值不得为 0。HELLO 的会话号必须为 0;
- 控制消息:T、L、S 为 1,O、P 为 0,版本为 2。数据消息上 Nr 若出现必须忽略;
- 长度只写了总八位组数:原文没有印出 12 或 6,也没有写从 T 位起算。RFC 3931 的「仍然」不回写进这句;
- 1701 是发起时的目的端口:源端口可以不是 1701。建立之后必须保持不变。当天没有核 IANA;
- 版本 3 的会话号是 32 位。RFC 9601 追加的是第 5.3 节和一条 AVP,不改号的宽度。第 5.4 节、第 5.8 节和第 4.7.3 节没有读完。