如何在两台机器之间通过 IP 建立非静态 L2TPv3 套接字连接
Posted
技术标签:
【中文标题】如何在两台机器之间通过 IP 建立非静态 L2TPv3 套接字连接【英文标题】:How to esteblish NOT static L2TPv3 socket connection over IP between two machines 【发布时间】:2019-11-09 15:04:35 【问题描述】:我没有找到任何如何通过 IP 实现 L2TPv3 的示例
我只发现了几个 L2TP 的开源实现,它们是:
openL2TP: https://github.com/breed/openl2tp 不支持 L2TPv3 并使用 UDP tunneldigger:https://github.com/wlanslovenija/tunneldigger 支持 L2TPv3 但也使用 UDPL2TPv3 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)。阅读内核文档后,我只有自己的理解,这可能不正确,但就是这样。
服务器端:
-
准备隧道 0 套接字
为 l2tp 创建 genl 套接字
创建标准套接字填充 l2tpip 结构(套接字创建 + 绑定),其中隧道 ID 为 0
使用 genl socket 创建隧道 0
等待 SCCRQ(据我了解,标头的控制连接 ID 等于零)
在隧道 0 套接字上收到 SCCRQ
从收到的 SCCRQ 中获取 AVL 的分配控制连接 ID,该 ID 在另一侧是 received_tunnel_id 为我方生成 local_tunnel_id 创建标准套接字填充 l2tpip 结构(套接字创建 + 绑定),其中 tunnel_id 为 local_tunnel_id 使用 genl 套接字创建带有 local_tunnel_id 的隧道 发送 SCCRP,其标头的控制连接 ID 等于 received_tunnel_id 和 AVL 分配的控制连接 ID 等于 local_tunnel_id 在该套接字上等待 SCCN客户端
-
不知道那边是怎么做的
据我了解,进一步的通信将通过第二个标准套接字进行,并且隧道 id 零仅接收 SCCRQ
很高兴了解这些细节
【问题讨论】:
【参考方案1】:抱歉这么晚才回复!但希望它仍然对您或其他人有用。
我将尝试依次解决您问题的不同方面。
但我不明白为什么我们必须将 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 and L2TP -- a peek under the hood。它涵盖了 netlink API 中的命令并概述了如何使用它们。
以及L2TPv3控制交换建立的最后部分(SCCRQ、SCCRP、SCCCN)。阅读内核文档后,我只有自己的理解,这可能不正确,但就是这样。
在服务器端,您大体上是正确的,但需要注意以下几点:
步骤 4 是不必要的。您不需要为隧道 ID 0 创建内核上下文,因为您只希望控制数据包(特别是 SCCRQ,在某些极端情况下为 STOPCCN)到隧道 ID 0。 第 9 步也应该connect(2)
到对等方的地址和隧道 ID。
可以认为步骤 10 过早完成,因为控制协议当然会在隧道建立之前失败。从服务器的角度来看,一旦 SCCCN 消息已被传输确认,就可以认为隧道已建立。这就是说,只要确保在控制协议失败的情况下将其拆除,尽早创建内核隧道上下文是没有害处的。
客户端...我不知道那端是怎么做的
这与服务器端的基本过程相同。
用户空间应该执行控制协议消息交换 (SCCRQ/SCCRP/SCCCN),一旦成功完成,使用 netlink 消息在内核中实例化 L2TP 隧道上下文。
-
为 L2TP 创建一个 genl 套接字。
生成本地隧道 ID。
创建一个应该绑定到本地地址和本地隧道 ID 的隧道套接字。暂时不要连接套接字。
准备 SCCRQ 消息。这应该使用 L2TP 标头中的隧道 ID 0 发送给对等方。您需要使用
sendto(2)
语义来执行此操作。
等待来自对等方的 SCCRP。收到 SCCRP 后,您需要将套接字连接到 SCCRP 消息中分配的控制连接 AVP 中调用的隧道 ID。
发送 SCCCN。
等待 SCCCN 被确认。收到 SCCCN ACK 后,使用 L2TP genl 套接字使用来自控制协议交换的隧道 ID 创建内核隧道上下文。
据我了解,进一步的通信将通过第二个标准套接字进行,并且隧道 id 零仅接收 SCCRQ
是的。考虑隧道 ID 零套接字的一种方法是类似于您为 HTTP 服务器使用 listen(2)
的套接字。套接字的目的是接收对已知端口的请求,随后的消息交换通过不同端口上的特定连接套接字进行。
当然 IP 没有端口,但零隧道 ID 对 L2TPv3 IP-encap 服务器起着相同的作用。
【讨论】:
以上是关于如何在两台机器之间通过 IP 建立非静态 L2TPv3 套接字连接的主要内容,如果未能解决你的问题,请参考以下文章