0

我没有找到任何如何通过 IP 实现 L2TPv3 的示例

我发现只有几个 L2TP 的开源实现,它们是:

  • openL2TP:https ://github.com/breed/openl2tp不支持 L2TPv3 并使用 UDP
  • tunneldigger:https ://github.com/wlanslovenija/tunneldigger支持 L2TPv3 但也使用 UDP

L2TPv3 rfc 没有说明它是如何在 linux 中实现的,这很明显

内核文档: https ://github.com/torvalds/linux/blob/master/Documentation/networking/l2tp.txt说我们必须使用 netlink + 标准套接字来告诉我们一些事情。但我不明白为什么我们必须使用 netlink 和标准套接字?

内核头文件: https ://github.com/torvalds/linux/blob/master/include/uapi/linux/l2tp.h它有大量用于 netlink + l2tpip 结构的枚举和命令。我对如何正确使用它们感到困惑。

以及L2TPv3控制交换建立的最后部分(SCCRQ、SCCRP、SCCCN)。阅读内核文档后,我只有自己的理解,这可能不正确,但在这里。

服务器端:

  1. 准备隧道 0 套接字
  2. 为 l2tp 创建 genl 套接字
  3. 创建标准套接字填充 l2tpip 结构(套接字创建 + 绑定),其中 tunnel_id 为 0
  4. 使用 genl socket 创建隧道 0
  5. 等待 SCCRQ(据我了解,标头的控制连接 ID 等于零)

  6. 在隧道 0 套接字上收到 SCCRQ

  7. 从收到的 SCCRQ 中获取 AVL 的分配控制连接 ID,该 ID 在另一侧是 received_tunnel_id
  8. 为我方生成 local_tunnel_id
  9. 创建标准套接字填充 l2tpip 结构(套接字创建 + 绑定),其中 tunnel_id 为 local_tunnel_id
  10. 使用 genl socket 创建带有 local_tunnel_id 的隧道
  11. 发送 SCCRP,其标头的控制连接 ID 等于 received_tunnel_id 和 AVL 分配的控制连接 ID 等于 local_tunnel_id
  12. 在该套接字上等待 SCCN

客户端

  1. 我不知道那一边是怎么做的

据我了解,进一步的通信将通过第二个标准套接字进行,并且隧道 id 零仅接收 SCCRQ

很高兴能澄清这些细节

4

1 回答 1

0

很抱歉这个回复来晚了!但希望它仍然对您或其他人有用。

我将尝试依次解决您问题的不同方面。

但我不明白为什么我们必须使用 netlink 和标准套接字?

Linux L2TP 子系统仅处理数据平面(即通过 L2TP 会话传输的用户数据包)。用户空间的任务是处理控制平面(L2TP 控制协议交换)。

所以广义上讲,用户空间创建了一个隧道套接字,以便与对等方交换 L2TP 控制消息。一旦建立了隧道 (SCCRQ/SCCRP/SCCCN),隧道 FD 在创建内核隧道上下文时使用 netlink API 传递给内核。然后,相同的隧道 FD 用于隧道中的会话数据包。

内核头文件:https ://github.com/torvalds/linux/blob/master/include/uapi/linux/l2tp.h它有大量用于 netlink + l2tpip 结构的枚举和命令。我对如何正确使用它们感到困惑。

这篇博文可能会对您有所帮助:Linux 和 L2TP——引擎盖下的一瞥。它涵盖了 netlink API 中的命令并概述了如何使用它们。

以及L2TPv3控制交换建立的最后部分(SCCRQ、SCCRP、SCCCN)。阅读内核文档后,我只有自己的理解,这可能不正确,但在这里。

在服务器端,您大体上是正确的,但需要注意以下几点:

  • 步骤 4 是不必要的。您不需要为隧道 ID 0 创建内核上下文,因为您只希望控制数据包(特别是 SCCRQ,在某些极端情况下为 STOPCCN)到隧道 ID 0。
  • 第 9 步还应该connect(2)给对等方的地址和隧道 ID。
  • 步骤 10 可能被认为做得太早了,因为控制协议当然会在隧道建立之前失败。从服务器的角度来看,一旦 SCCCN 消息已被传输确认,就可以认为隧道已建立。这就是说,只要您确保在控制协议失败的情况下将其拆除,尽早创建内核隧道上下文是没有害处的。

客户端......我不知道它是如何在那边完成的

这与服务器端的基本过程相同。

用户空间应该执行控制协议消息交换(SCCRQ/SCCRP/SCCCN),一旦成功完成,使用 netlink 消息在内核中实例化 L2TP 隧道上下文。

  1. 为 L2TP 创建一个 genl 套接字。
  2. 生成本地隧道 ID。
  3. 创建一个应该绑定到本地地址和本地隧道 ID 的隧道套接字。暂时不要连接插座。
  4. 准备 SCCRQ 消息。这应该使用 L2TP 标头中的隧道 ID 0 发送给对等方。您需要使用sendto(2)语义来执行此操作。
  5. 等待来自对等方的 SCCRP。收到 SCCRP 后,您需要将套接字连接到 SCCRP 消息中分配的控制连接 AVP 中调用的隧道 ID。
  6. 发送 SCCCN。
  7. 等待 SCCCN 被确认。收到 SCCCN ACK 后,使用 L2TP genl 套接字使用来自控制协议交换的隧道 ID 创建内核隧道上下文。

据我了解,进一步的通信将通过第二个标准套接字进行,并且隧道 id 零仅接收 SCCRQ

是的。将隧道 ID 零套接字视为类似于您listen(2)为 HTTP 服务器使用的套接字的一种方法。套接字的目的是接收对已知端口的请求,随后的消息交换通过不同端口上的特定连接套接字进行。

当然 IP 没有端口,但零隧道 ID 对 L2TPv3 IP-encap 服务器起着相同的作用。

于 2020-01-09T15:43:04.517 回答